1# Copyright (C) 2019 The Android Open Source Project 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions 6# are met: 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in 11# the documentation and/or other materials provided with the 12# distribution. 13# 14# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 17# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 18# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 19# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 21# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 24# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26 27from enum import Enum 28from typing import Any, Dict, Iterator, List, Optional, Set, Tuple 29 30 31class SymKind(Enum): 32 Func = 1 33 Var = 2 34 35 def to_json(self) -> str: 36 return {SymKind.Func: 'func', SymKind.Var: 'var'}[self] 37 38 @staticmethod 39 def from_json(obj: str) -> 'SymKind': 40 return {'func': SymKind.Func, 'var': SymKind.Var}[obj] 41 42 43class SymBind(Enum): 44 Global = 1 45 Weak = 2 46 47 def to_json(self) -> str: 48 return {SymBind.Global: 'global', SymBind.Weak: 'weak'}[self] 49 50 @staticmethod 51 def from_json(obj: str) -> 'SymBind': 52 return {'global': SymBind.Global, 'weak': SymBind.Weak}[obj] 53 54 55class DynSymbol: 56 def __init__(self, name: str, kind: SymKind, bind: SymBind, defined: bool, 57 ver_type: Optional[str], ver_name: Optional[str]): 58 assert ver_type in {None, '@', '@@'} 59 self.name: str = name 60 self.kind: SymKind = kind 61 self.bind: SymBind = bind 62 self.defined: bool = defined 63 self.ver_type: Optional[str] = ver_type 64 self.ver_name: Optional[str] = ver_name 65 66 def to_json(self) -> Dict[str, Any]: 67 result: Dict[str, Any] = {} 68 result['name'] = self.name 69 result['kind'] = self.kind.to_json() 70 result['bind'] = self.bind.to_json() 71 result['defined'] = self.defined 72 result['ver_type'] = self.ver_type 73 result['ver_name'] = self.ver_name 74 return result 75 76 @staticmethod 77 def from_json(obj: Dict[str, Any]) -> 'DynSymbol': 78 return DynSymbol(obj['name'], 79 SymKind.from_json(obj['kind']), 80 SymBind.from_json(obj['bind']), 81 obj['defined'], 82 obj['ver_type'], 83 obj['ver_name']) 84 85 86DynSymbols = Dict[int, DynSymbol] 87 88 89class SymbolRef: 90 def __init__(self, name: str, is_weak: bool, ver: Optional[str]): 91 self.name: str = name 92 self.is_weak: bool = is_weak 93 self.ver: Optional[str] = ver 94 95 def to_json(self) -> Dict[str, Any]: 96 result: Dict[str, Any] = {} 97 result['name'] = self.name 98 result['is_weak'] = self.is_weak 99 if self.ver is not None: 100 result['ver'] = self.ver 101 return result 102 103 @staticmethod 104 def from_json(obj: Dict[str, Any]) -> 'SymbolRef': 105 return SymbolRef(obj['name'], obj['is_weak'], obj.get('ver')) 106 107 108class Relocations: 109 def __init__(self): 110 self.jump_slots: List[SymbolRef] = [] 111 self.got: List[SymbolRef] = [] 112 self.symbolic: List[Tuple[int, SymbolRef]] = [] 113 self.relative: List[int] = [] 114 115 def to_json(self) -> Dict[str, Any]: 116 result: Dict[str, Any] = {} 117 result['jump_slots'] = [sym.to_json() for sym in self.jump_slots] 118 result['got'] = [sym.to_json() for sym in self.got] 119 result['symbolic'] = [(off, sym.to_json()) for (off, sym) in self.symbolic] 120 result['relative'] = self.relative 121 return result 122 123 @staticmethod 124 def from_json(obj: Dict[str, Any]) -> 'Relocations': 125 result = Relocations() 126 result.jump_slots = [SymbolRef.from_json(sym) for sym in obj['jump_slots']] 127 result.got = [SymbolRef.from_json(sym) for sym in obj['got']] 128 result.symbolic = [(off, SymbolRef.from_json(sym)) for (off, sym) in obj['symbolic']] 129 result.relative = obj['relative'] 130 return result 131 132 133class LoadedLibrary: 134 def __init__(self): 135 self.soname: str = None 136 self.syms: DynSymbols = None 137 self.rels: Relocations = None 138 self.needed: List[LoadedLibrary] = [] 139 140 def to_json(self) -> Dict[str, Any]: 141 result: Dict[str, Any] = {} 142 result['soname'] = self.soname 143 result['syms'] = {name: sym.to_json() for name, sym in self.syms.items()} 144 result['rels'] = self.rels.to_json() 145 result['needed'] = [lib.soname for lib in self.needed] 146 return result 147 148 @staticmethod 149 def from_json(obj: Dict[str, Any]) -> Tuple['LoadedLibrary', List[str]]: 150 result = LoadedLibrary() 151 result.soname = obj['soname'] 152 result.syms = {name: DynSymbol.from_json(sym) for name, sym in obj['syms'].items()} 153 result.rels = Relocations.from_json(obj['rels']) 154 return result, obj['needed'] 155 156 157def elf_tree_to_json(tree: LoadedLibrary) -> Dict[str, Any]: 158 libraries: Dict[str, LoadedLibrary] = {} 159 result: Dict[str, Any] = {} 160 result['root'] = tree.soname 161 result['libs'] = [] 162 for lib in bfs_walk(tree): 163 result['libs'].append(lib.to_json()) 164 return result 165 166 167def json_to_elf_tree(obj: Dict[str, Any]) -> LoadedLibrary: 168 libraries: Dict[str, LoadedLibrary] = {} 169 all_needed: List[Tuple[LoadedLibrary, List[str]]] = [] 170 for lib_obj in obj['libs']: 171 lib, needed = LoadedLibrary.from_json(lib_obj) 172 libraries[lib.soname] = lib 173 all_needed.append((lib, needed)) 174 for lib, needed in all_needed: 175 lib.needed = [libraries[x] for x in needed] 176 return libraries[obj['root']] 177 178 179def bfs_walk(tree: LoadedLibrary) -> Iterator[LoadedLibrary]: 180 work_list = [tree] 181 seen: Set[LoadedLibrary] = set() 182 while len(work_list) > 0: 183 lib = work_list.pop(0) 184 if lib in seen: continue 185 seen.add(lib) 186 yield lib 187 work_list.extend(lib.needed) 188