Skip to content

Commit 6803545

Browse files
dschuffcwoffenden
authored andcommitted
Add support for symbol maps to emsymbolizer (#24735)
Read symbol information from the symbol map, and function offset info from the binary, and match them up.
1 parent c97a935 commit 6803545

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

emsymbolizer.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# If there is an emscripten symbol map, we can use that to get the symbol name
1212
# If there is a name section or symbol table, llvm-symbolizer can show the
1313
# symbol name.
14-
# Separate DWARF and emscripten symbol maps are not supported yet.
14+
# Separate DWARF is not supported yet.
1515

1616
import argparse
1717
import json
@@ -22,6 +22,7 @@
2222
from tools import shared
2323
from tools import webassembly
2424

25+
2526
LLVM_SYMBOLIZER = os.path.expanduser(
2627
shared.build_llvm_tool_path(shared.exe_suffix('llvm-symbolizer')))
2728

@@ -70,6 +71,8 @@ def symbolize_address_symbolizer(module, address, is_dwarf):
7071
vma_adjust = 0
7172
cmd = [LLVM_SYMBOLIZER, '-e', module.filename, f'--adjust-vma={vma_adjust}',
7273
str(address)]
74+
if shared.DEBUG:
75+
print(f'Running {" ".join(cmd)}')
7376
out = shared.run_process(cmd, stdout=subprocess.PIPE).stdout.strip()
7477
out_lines = out.splitlines()
7578

@@ -215,6 +218,31 @@ def symbolize_address_sourcemap(module, address, force_file):
215218
sm.lookup(address).print()
216219

217220

221+
def symbolize_address_symbolmap(module, address, symbol_map_file):
222+
"""Symbolize using a symbol map file."""
223+
func_names = {}
224+
225+
with open(symbol_map_file) as f:
226+
lines = f.read().splitlines()
227+
for line in lines:
228+
index, name = line.split(':')
229+
func_names[int(index)] = name
230+
231+
func_index = -1
232+
for i, func in module.iter_functions_by_index():
233+
if shared.DEBUG:
234+
print(f'Func {i}: {hex(func.offset)}, {func_names[i]}')
235+
if func.offset > address:
236+
if i > 0:
237+
func_index = i - 1
238+
break
239+
else:
240+
print("Address is before the first function")
241+
return
242+
243+
LocationInfo(func=func_names[func_index]).print()
244+
245+
218246
def main(args):
219247
with webassembly.Module(args.wasm_file) as module:
220248
base = 16 if args.address.lower().startswith('0x') else 10
@@ -235,6 +263,8 @@ def main(args):
235263
elif ((has_linking_section(module) and not args.source) or
236264
'symtab' in args.source):
237265
symbolize_address_symbolizer(module, address, is_dwarf=False)
266+
elif (args.source == 'symbolmap'):
267+
symbolize_address_symbolmap(module, address, args.file)
238268
else:
239269
raise Error('No .debug_line or sourceMappingURL section found in '
240270
f'{module.filename}.'
@@ -244,7 +274,7 @@ def main(args):
244274
def get_args():
245275
parser = argparse.ArgumentParser()
246276
parser.add_argument('-s', '--source', choices=['dwarf', 'sourcemap',
247-
'names', 'symtab'],
277+
'names', 'symtab', 'symbolmap'],
248278
help='Force debug info source type', default=())
249279
parser.add_argument('-f', '--file', action='store',
250280
help='Force debug info source file')

test/test_other.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10950,6 +10950,24 @@ def check_func_info(filename, address, func):
1095010950
# The name section will not show bar, as it's inlined into main
1095110951
check_func_info('test_dwarf.wasm', unreachable_addr, '__original_main')
1095210952

10953+
# 2. Test symbol map
10954+
self.run_process([EMCC, test_file('core/test_dwarf.c'),
10955+
'-O1', '--emit-symbol-map', '-o', 'test_dwarf.js'])
10956+
self.assertExists('test_dwarf.js.symbols')
10957+
10958+
def check_symbolmap_info(address, func):
10959+
out = self.run_process([emsymbolizer, '--source=symbolmap', '-f', 'test_dwarf.js.symbols', 'test_dwarf.wasm', address], stdout=PIPE).stdout
10960+
self.assertIn(func, out)
10961+
10962+
# Same tests as name section above.
10963+
# Address of out_to_js(0) within foo(), uninlined
10964+
out_to_js_call_addr = self.get_instr_addr('call\t0', 'test_dwarf.wasm')
10965+
# Address of __builtin_trap() within bar(), inlined into main()
10966+
unreachable_addr = self.get_instr_addr('unreachable', 'test_dwarf.wasm')
10967+
check_symbolmap_info(out_to_js_call_addr, 'foo')
10968+
# The name section will not show bar, as it's inlined into main
10969+
check_symbolmap_info(unreachable_addr, '__original_main')
10970+
1095310971
def test_separate_dwarf(self):
1095410972
self.run_process([EMCC, test_file('hello_world.c'), '-g'])
1095510973
self.assertExists('a.out.wasm')

tools/webassembly.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,12 @@ def get_function(self, idx):
542542
assert idx >= self.num_imported_funcs()
543543
return self.get_functions()[idx - self.num_imported_funcs()]
544544

545+
def iter_functions_by_index(self):
546+
self._calc_indexes()
547+
for idx in range(self.num_imported_funcs(),
548+
self.num_imported_funcs() + len(self.get_functions())):
549+
yield idx, self.get_function(idx)
550+
545551
def get_global(self, idx):
546552
self._calc_indexes()
547553
assert idx >= self.num_imported_globals()

0 commit comments

Comments
 (0)