-
Notifications
You must be signed in to change notification settings - Fork 0
/
aux_builder.py
196 lines (161 loc) · 6.83 KB
/
aux_builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""Build GTIRB AuxData tables from Binary Ninja."""
from enum import Flag, auto
from collections import defaultdict
from typing import Mapping, Optional, Set
from uuid import UUID
import binaryninja as bn
import gtirb
from . import utils
class AuxTables(Flag):
"""Determine which auxiliary tables to export."""
FUNCTION_NAMES = auto()
FUNCTION_ENTRIES = auto()
FUNCTION_BLOCKS = auto()
COMMENTS = auto()
TYPES = auto()
LIBRARIES = auto()
LIBRARY_PATHS = auto()
class AuxDataBuilder:
"""Builds several auxiliary data tables."""
def __init__(
self, bv: bn.BinaryView, mod: gtirb.Module, tables_to_export: AuxTables
):
self._bv = bv
self._mod = mod
self._tables_to_export = tables_to_export
def build(self) -> Mapping[str, gtirb.AuxData]:
"""Build a set of auxiliary data tables."""
aux_tables = {}
if self._tables_to_export & (
AuxTables.FUNCTION_NAMES
| AuxTables.FUNCTION_ENTRIES
| AuxTables.FUNCTION_BLOCKS
):
aux_tables |= self._export_function_tables()
if (AuxTables.COMMENTS in self._tables_to_export) and (
comments := self._export_comments()
):
aux_tables["comments"] = comments
if (AuxTables.TYPES in self._tables_to_export) and (
types := self._export_types()
):
aux_tables["types"] = types
if (AuxTables.LIBRARIES in self._tables_to_export) and (
libs := self._export_libraries()
):
aux_tables["libraries"] = libs
if (AuxTables.LIBRARY_PATHS in self._tables_to_export) and (
lib_paths := self._export_library_paths()
):
aux_tables["libraryPaths"] = lib_paths
return aux_tables
def _find_first_symbol(self, name: str) -> Optional[gtirb.Symbol]:
"""Find the first symbol with the given name."""
return next(self._mod.symbols_named(name), None)
def _export_function_tables(self) -> Mapping[str, gtirb.AuxData]:
"""
Export the ``functionNames``, ``funtionEntries``, and ``functionBlocks``
auxiliary tables.
"""
function_names: Mapping[UUID, UUID] = {}
function_entries: Mapping[UUID, Set[UUID]] = defaultdict(set)
function_blocks: Mapping[UUID, Set[UUID]] = {}
for func in self._bv.functions:
func_node = gtirb.Node()
# Function name
if (AuxTables.FUNCTION_NAMES in self._tables_to_export) and (
sym := self._find_first_symbol(func.name)
):
function_names[func_node.uuid] = sym.uuid
# Function entry
if AuxTables.FUNCTION_ENTRIES in self._tables_to_export:
for block in self._mod.code_blocks_at(func.start):
function_entries[func_node.uuid].add(block.uuid)
# Function blocks
if AuxTables.FUNCTION_BLOCKS in self._tables_to_export:
blocks = set(
block.uuid
for bb in func.basic_blocks
for block in self._mod.code_blocks_at(bb.start)
)
if blocks:
function_blocks[func_node.uuid] = blocks
# Make auxiliary tables
ret: Mapping[str, gtirb.AuxData] = {}
if function_names:
utils.log_debug(
f"Exported functionNames table with {len(function_names)} entries"
)
ret["functionNames"] = gtirb.AuxData(
data=function_names, type_name="mapping<UUID,UUID>"
)
if function_entries:
utils.log_debug(
f"Exported functionEntries table with {len(function_entries)} entries"
)
ret["functionEntries"] = gtirb.AuxData(
data=function_entries, type_name="mapping<UUID,set<UUID>>"
)
if function_blocks:
utils.log_debug(
f"Exported functionBlocks table with {len(function_blocks)} entries"
)
ret["functionBlocks"] = gtirb.AuxData(
data=function_blocks, type_name="mapping<UUID,set<UUID>>"
)
return ret
def _export_comments(self) -> Optional[gtirb.AuxData]:
"""Export the ``comments`` auxiliary table."""
comments: Mapping[int, str] = {
# Comments attached to data
**self._bv.address_comments,
# Comments attached to code (i.e., functions)
**{
addr: comment
for func in self._bv.functions
for addr, comment in func.comments.items()
},
}
addr_to_blocks = utils.address_to_block_map(self._mod)
gcomments: Mapping[gtirb.Offset, str] = {}
for addr, comment in comments.items():
block = addr_to_blocks.get(addr)
if block is None or not block.address:
continue
displacement = addr - block.address
offset = gtirb.Offset(element_id=block, displacement=displacement)
gcomments[offset] = comment
if not gcomments:
return None
utils.log_debug(f"Exported comments aux table with {len(gcomments)} entries")
return gtirb.AuxData(data=gcomments, type_name="mapping<Offset,string>")
def _export_types(self) -> Optional[gtirb.AuxData]:
"""Export the ``types`` auxiliary table."""
types: Mapping[UUID, str] = {}
type_printer = bn.TypePrinter.default
for sym in self._bv.get_symbols():
v = self._bv.get_data_var_at(sym.address)
if v is None:
continue
for block in self._mod.data_blocks_at(sym.address):
types[block.uuid] = type_printer.get_type_string(v.type)
if not types:
return None
utils.log_debug(f"Exported types aux table with {len(types)} entries")
return gtirb.AuxData(data=types, type_name="mapping<UUID,string>")
def _export_libraries(self) -> Optional[gtirb.AuxData]:
"""Export the ``libraries`` auxiliary table."""
libs = [lib.name for lib in self._bv.get_external_libraries()]
if not libs:
return None
utils.log_debug(f"Exported libraries aux table with {len(libs)} entries")
return gtirb.AuxData(data=libs, type_name="sequence<string>")
def _export_library_paths(self) -> Optional[gtirb.AuxData]:
"""Export the ``libraryPaths`` auxiliary table."""
lib_paths = [lib.backing_file for lib in self._bv.get_external_libraries()]
if not lib_paths or all(p is None for p in lib_paths):
return None
utils.log_debug(
f"Exported libraryPaths aux table with {len(lib_paths)} entries"
)
return gtirb.AuxData(data=lib_paths, type_name="sequence<string>")