blob: 94168b07c633ce531ec480b05f5e2934811f6e6f [file] [log] [blame]
# 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)