| # Copyright (C) 2019 The Android Open Source Project |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in |
| # the documentation and/or other materials provided with the |
| # distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| # SUCH DAMAGE. |
| |
| from enum import Enum |
| from typing import Any, Dict, Iterator, List, Optional, Set, Tuple |
| |
| |
| class SymKind(Enum): |
| Func = 1 |
| Var = 2 |
| |
| def to_json(self) -> str: |
| return {SymKind.Func: 'func', SymKind.Var: 'var'}[self] |
| |
| @staticmethod |
| def from_json(obj: str) -> 'SymKind': |
| return {'func': SymKind.Func, 'var': SymKind.Var}[obj] |
| |
| |
| class SymBind(Enum): |
| Global = 1 |
| Weak = 2 |
| |
| def to_json(self) -> str: |
| return {SymBind.Global: 'global', SymBind.Weak: 'weak'}[self] |
| |
| @staticmethod |
| def from_json(obj: str) -> 'SymBind': |
| return {'global': SymBind.Global, 'weak': SymBind.Weak}[obj] |
| |
| |
| class DynSymbol: |
| def __init__(self, name: str, kind: SymKind, bind: SymBind, defined: bool, |
| ver_type: Optional[str], ver_name: Optional[str]): |
| assert ver_type in {None, '@', '@@'} |
| self.name: str = name |
| self.kind: SymKind = kind |
| self.bind: SymBind = bind |
| self.defined: bool = defined |
| self.ver_type: Optional[str] = ver_type |
| self.ver_name: Optional[str] = ver_name |
| |
| def to_json(self) -> Dict[str, Any]: |
| result: Dict[str, Any] = {} |
| result['name'] = self.name |
| result['kind'] = self.kind.to_json() |
| result['bind'] = self.bind.to_json() |
| result['defined'] = self.defined |
| result['ver_type'] = self.ver_type |
| result['ver_name'] = self.ver_name |
| return result |
| |
| @staticmethod |
| def from_json(obj: Dict[str, Any]) -> 'DynSymbol': |
| return DynSymbol(obj['name'], |
| SymKind.from_json(obj['kind']), |
| SymBind.from_json(obj['bind']), |
| obj['defined'], |
| obj['ver_type'], |
| obj['ver_name']) |
| |
| |
| DynSymbols = Dict[int, DynSymbol] |
| |
| |
| class SymbolRef: |
| def __init__(self, name: str, is_weak: bool, ver: Optional[str]): |
| self.name: str = name |
| self.is_weak: bool = is_weak |
| self.ver: Optional[str] = ver |
| |
| def to_json(self) -> Dict[str, Any]: |
| result: Dict[str, Any] = {} |
| result['name'] = self.name |
| result['is_weak'] = self.is_weak |
| if self.ver is not None: |
| result['ver'] = self.ver |
| return result |
| |
| @staticmethod |
| def from_json(obj: Dict[str, Any]) -> 'SymbolRef': |
| return SymbolRef(obj['name'], obj['is_weak'], obj.get('ver')) |
| |
| |
| class Relocations: |
| def __init__(self): |
| self.jump_slots: List[SymbolRef] = [] |
| self.got: List[SymbolRef] = [] |
| self.symbolic: List[Tuple[int, SymbolRef]] = [] |
| self.relative: List[int] = [] |
| |
| def to_json(self) -> Dict[str, Any]: |
| result: Dict[str, Any] = {} |
| result['jump_slots'] = [sym.to_json() for sym in self.jump_slots] |
| result['got'] = [sym.to_json() for sym in self.got] |
| result['symbolic'] = [(off, sym.to_json()) for (off, sym) in self.symbolic] |
| result['relative'] = self.relative |
| return result |
| |
| @staticmethod |
| def from_json(obj: Dict[str, Any]) -> 'Relocations': |
| result = Relocations() |
| result.jump_slots = [SymbolRef.from_json(sym) for sym in obj['jump_slots']] |
| result.got = [SymbolRef.from_json(sym) for sym in obj['got']] |
| result.symbolic = [(off, SymbolRef.from_json(sym)) for (off, sym) in obj['symbolic']] |
| result.relative = obj['relative'] |
| return result |
| |
| |
| class LoadedLibrary: |
| def __init__(self): |
| self.soname: str = None |
| self.syms: DynSymbols = None |
| self.rels: Relocations = None |
| self.needed: List[LoadedLibrary] = [] |
| |
| def to_json(self) -> Dict[str, Any]: |
| result: Dict[str, Any] = {} |
| result['soname'] = self.soname |
| result['syms'] = {name: sym.to_json() for name, sym in self.syms.items()} |
| result['rels'] = self.rels.to_json() |
| result['needed'] = [lib.soname for lib in self.needed] |
| return result |
| |
| @staticmethod |
| def from_json(obj: Dict[str, Any]) -> Tuple['LoadedLibrary', List[str]]: |
| result = LoadedLibrary() |
| result.soname = obj['soname'] |
| result.syms = {name: DynSymbol.from_json(sym) for name, sym in obj['syms'].items()} |
| result.rels = Relocations.from_json(obj['rels']) |
| return result, obj['needed'] |
| |
| |
| def elf_tree_to_json(tree: LoadedLibrary) -> Dict[str, Any]: |
| libraries: Dict[str, LoadedLibrary] = {} |
| result: Dict[str, Any] = {} |
| result['root'] = tree.soname |
| result['libs'] = [] |
| for lib in bfs_walk(tree): |
| result['libs'].append(lib.to_json()) |
| return result |
| |
| |
| def json_to_elf_tree(obj: Dict[str, Any]) -> LoadedLibrary: |
| libraries: Dict[str, LoadedLibrary] = {} |
| all_needed: List[Tuple[LoadedLibrary, List[str]]] = [] |
| for lib_obj in obj['libs']: |
| lib, needed = LoadedLibrary.from_json(lib_obj) |
| libraries[lib.soname] = lib |
| all_needed.append((lib, needed)) |
| for lib, needed in all_needed: |
| lib.needed = [libraries[x] for x in needed] |
| return libraries[obj['root']] |
| |
| |
| def bfs_walk(tree: LoadedLibrary) -> Iterator[LoadedLibrary]: |
| work_list = [tree] |
| seen: Set[LoadedLibrary] = set() |
| while len(work_list) > 0: |
| lib = work_list.pop(0) |
| if lib in seen: continue |
| seen.add(lib) |
| yield lib |
| work_list.extend(lib.needed) |