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