From 6ea46cd7bc5148b6e7a4b861a4d16d1e37ed9259 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 25 Aug 2025 17:54:25 -0700 Subject: [PATCH] Implement --emit-symbol-map locally without calling out wasm-opt The binaryen version was outputting strange mangled C++ names. Doing this in python should be faster since we don't need to parse the whole binary. Still needs a test. Fixes: #24982 --- test/other/test_symbol_map.O2.symbols | 2 +- test/other/test_symbol_map.O3.symbols | 2 +- tools/building.py | 48 ++++++++++++++++++--------- tools/link.py | 5 +-- tools/webassembly.py | 15 +++++++++ 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/test/other/test_symbol_map.O2.symbols b/test/other/test_symbol_map.O2.symbols index 444b34e9ce5d9..f0f1142e38149 100644 --- a/test/other/test_symbol_map.O2.symbols +++ b/test/other/test_symbol_map.O2.symbols @@ -1,7 +1,7 @@ 0:run_js 1:emscripten_asm_const_int 2:__wasm_call_ctors -3:foo::cpp_func\28int\29 +3:foo::cpp_func(int) 4:middle 5:main 6:_emscripten_stack_restore diff --git a/test/other/test_symbol_map.O3.symbols b/test/other/test_symbol_map.O3.symbols index 8382f2e4a2fd9..4bf0271be9878 100644 --- a/test/other/test_symbol_map.O3.symbols +++ b/test/other/test_symbol_map.O3.symbols @@ -2,5 +2,5 @@ 1:emscripten_asm_const_int 2:middle 3:main -4:foo::cpp_func\28int\29 +4:foo::cpp_func(int) 5:__wasm_call_ctors diff --git a/tools/building.py b/tools/building.py index 05d8a0e7ebad3..c2058a976ad57 100644 --- a/tools/building.py +++ b/tools/building.py @@ -1081,24 +1081,40 @@ def instrument_js_for_safe_heap(js_file): return acorn_optimizer(js_file, ['safeHeap']) +def read_name_section(wasm_file): + with webassembly.Module(wasm_file) as module: + for section in module.sections(): + if section.type == webassembly.SecType.CUSTOM: + module.seek(section.offset) + if module.read_string() == 'name': + name_map = {} + # The name section is made up sub-section. + # We are looking for the function names sub-section + while module.tell() < section.offset + section.size: + name_type = module.read_uleb() + subsection_size = module.read_uleb() + subsection_end = module.tell() + subsection_size + if name_type == webassembly.NameType.FUNCTION: + # We found the function names sub-section + num_names = module.read_uleb() + for _ in range(num_names): + id = module.read_uleb() + name = module.read_string() + name_map[id] = name + return name_map + module.seek(subsection_end) + + return name_map + + @ToolchainProfiler.profile() -def handle_final_wasm_symbols(wasm_file, symbols_file, debug_info): +def write_symbol_map(wasm_file, symbols_file): logger.debug('handle_final_wasm_symbols') - args = [] - if symbols_file: - args += ['--print-function-map'] - else: - # suppress the wasm-opt warning regarding "no output file specified" - args += ['--quiet'] - output = run_wasm_opt(wasm_file, args=args, stdout=PIPE) - if symbols_file: - utils.write_file(symbols_file, output) - if not debug_info: - # strip the names section using llvm-objcopy. this is slightly slower than - # using wasm-opt (we could run wasm-opt without -g here and just tell it to - # write the file back out), but running wasm-opt would undo StackIR - # optimizations, if we did those. - strip(wasm_file, wasm_file, sections=['name']) + names = read_name_section(wasm_file) + assert(names) + strings = [f'{id}:{name}' for id, name in names.items()] + contents = '\n'.join(strings) + '\n' + utils.write_file(symbols_file, contents) def is_ar(filename): diff --git a/tools/link.py b/tools/link.py index ccaac6bae8106..61a1234a8fb18 100644 --- a/tools/link.py +++ b/tools/link.py @@ -2413,8 +2413,9 @@ def phase_binaryen(target, options, wasm_target): if options.emit_symbol_map: intermediate_debug_info -= 1 if generating_wasm: - building.handle_final_wasm_symbols(wasm_file=wasm_target, symbols_file=symbols_file, debug_info=intermediate_debug_info) - save_intermediate_with_wasm('symbolmap', wasm_target) + building.write_symbol_map(wasm_target, symbols_file) + if not intermediate_debug_info: + building.strip(wasm_target, wasm_target, sections=['name']) if settings.GENERATE_DWARF and settings.SEPARATE_DWARF and generating_wasm: # if the dwarf filename wasn't provided, use the default target + a suffix diff --git a/tools/webassembly.py b/tools/webassembly.py index 2ffbb1ab8e51e..d4c26c0cab15d 100644 --- a/tools/webassembly.py +++ b/tools/webassembly.py @@ -157,6 +157,21 @@ class TargetFeaturePrefix(IntEnum): DISALLOWED = 0x2d +class NameType(IntEnum): + MODULE = 0 + FUNCTION = 1 + LOCAL = 2 + LABEL = 3 + TYPE = 4 + TABLE = 5 + MEMORY = 6 + GLOBAL = 7 + ELEMSEGMENT = 8 + DATASEGMENT = 9 + FIELD = 10 + TAG = 11 + + class InvalidWasmError(BaseException): pass