Skip to content

Commit 39538fc

Browse files
committed
Read metadata using python code
1 parent ff75baa commit 39538fc

File tree

2 files changed

+261
-75
lines changed

2 files changed

+261
-75
lines changed

emscripten.py

Lines changed: 128 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,19 @@ def update_settings_glue(metadata, DEBUG):
123123
# -s DECLARE_ASM_MODULE_EXPORTS=0 builds.
124124
settings.WASM_FUNCTION_EXPORTS = metadata['exports']
125125

126-
# start with the MVP features, and add any detected features.
127-
settings.BINARYEN_FEATURES = ['--mvp-features'] + metadata['features']
128-
if settings.USE_PTHREADS:
129-
assert '--enable-threads' in settings.BINARYEN_FEATURES
130-
if settings.MEMORY64:
131-
assert '--enable-memory64' in settings.BINARYEN_FEATURES
132-
133-
if settings.RELOCATABLE:
126+
if 'features' in metadata:
127+
# start with the MVP features, and add any detected features.
128+
settings.BINARYEN_FEATURES = ['--mvp-features'] + metadata['features']
129+
if settings.USE_PTHREADS:
130+
assert '--enable-threads' in settings.BINARYEN_FEATURES
131+
if settings.MEMORY64:
132+
assert '--enable-memory64' in settings.BINARYEN_FEATURES
133+
134+
if settings.RELOCATABLE and settings.INITIAL_TABLE == -1:
134135
# When building relocatable output (e.g. MAIN_MODULE) the reported table
135136
# size does not include the reserved slot at zero for the null pointer.
136137
# Instead we use __table_base to offset the elements by 1.
137-
if settings.INITIAL_TABLE == -1:
138-
settings.INITIAL_TABLE = metadata['tableSize'] + 1
138+
settings.INITIAL_TABLE = metadata['tableSize'] + 1
139139

140140
settings.HAS_MAIN = settings.MAIN_MODULE or settings.STANDALONE_WASM or 'main' in settings.WASM_EXPORTS
141141

@@ -380,6 +380,99 @@ def remove_trailing_zeros(memfile):
380380
f.write(mem_data[:end])
381381

382382

383+
def read_metadata(wasm_file):
384+
module = webassembly.Module(wasm_file)
385+
imports = module.imports()
386+
exports = module.exports()
387+
globals_ = module.globals()
388+
num_global_imports = len([i for i in imports if i.kind == webassembly.ExternType.GLOBAL])
389+
390+
metadata = {
391+
'exports': [],
392+
'declares': [],
393+
'invokeFuncs': [],
394+
'namedGlobals': {},
395+
'emJsFuncs': {},
396+
'asmConsts': {},
397+
}
398+
399+
em_asm_start = None
400+
em_asm_stop = None
401+
402+
for export in exports:
403+
if export.kind == webassembly.ExternType.FUNC:
404+
metadata['exports'].append(export.name)
405+
elif export.kind == webassembly.ExternType.GLOBAL:
406+
assert export.index >= num_global_imports
407+
glob = globals_[export.index - num_global_imports]
408+
if export.name == '__start_em_asm':
409+
em_asm_start = glob
410+
elif export.name == '__stop_em_asm':
411+
em_asm_stop = glob
412+
else:
413+
metadata['namedGlobals'][export.name] = glob.init[1]
414+
415+
if em_asm_start and em_asm_stop:
416+
asm_start = em_asm_start.init[1]
417+
asm_stop = em_asm_stop.init[1]
418+
419+
def read_asm_strings(seg_data):
420+
str_start = 0
421+
while str_start < len(seg_data):
422+
str_end = seg_data.find(b'\0', str_start)
423+
string = seg_data[str_start:str_end].decode('utf-8')
424+
address = asm_start + str_start
425+
metadata['asmConsts'][address] = string
426+
str_start = str_end + 1
427+
428+
asm_len = asm_stop - asm_start
429+
total_offset = 1024
430+
for seg in module.data_segments():
431+
if seg.init and seg.init[1] == asm_start and seg.init[1] + len(seg.data) == asm_stop:
432+
read_asm_strings(seg.data)
433+
break
434+
elif total_offset == asm_start and len(seg.data) == asm_len:
435+
print("FOUND")
436+
read_asm_strings(seg.data)
437+
break
438+
total_offset += len(seg.data)
439+
if not metadata['asmConsts']:
440+
exit_with_error('em_asm section not found (%s - %s) %d' % (asm_start, asm_stop, asm_len))
441+
442+
for imp in imports:
443+
if imp.kind == webassembly.ExternType.FUNC:
444+
if imp.field.startswith('invoke_'):
445+
metadata['invokeFuncs'].append(imp.field)
446+
else:
447+
metadata['declares'].append(imp.field)
448+
449+
for sec in module.sections():
450+
if sec.type == webassembly.SecType.CUSTOM:
451+
sec_name = module.readString()
452+
if sec_name == 'target_features':
453+
metadata['features'] = []
454+
while module.tell() < sec.offset + sec.size:
455+
state = module.readString()
456+
name = module.readString()
457+
if state == '+':
458+
metadata['features'].append('--enable-' + name)
459+
else:
460+
assert False
461+
break
462+
463+
metadata['globalImports'] = [i.field for i in imports if i.kind == webassembly.ExternType.GLOBAL]
464+
metadata['mainReadsParams'] = 1
465+
466+
tables = [i for i in imports if i.kind == webassembly.ExternType.TABLE]
467+
if tables:
468+
assert len(tables) == 1
469+
metadata['tableSize'] = tables[0].info[1].initial
470+
else:
471+
metadata['tableSize'] = 0
472+
473+
return metadata
474+
475+
383476
def finalize_wasm(infile, outfile, memfile, DEBUG):
384477
building.save_intermediate(infile, 'base.wasm')
385478
# tell binaryen to look at the features section, and if there isn't one, to use MVP
@@ -435,26 +528,33 @@ def finalize_wasm(infile, outfile, memfile, DEBUG):
435528

436529
if settings.DEBUG_LEVEL >= 3:
437530
args.append('--dwarf')
438-
stdout = building.run_binaryen_command('wasm-emscripten-finalize',
439-
infile=infile,
440-
outfile=outfile if modify_wasm else None,
441-
args=args,
442-
stdout=subprocess.PIPE)
443-
if modify_wasm:
444-
building.save_intermediate(infile, 'post_finalize.wasm')
445-
elif infile != outfile:
446-
shutil.copy(infile, outfile)
447-
if settings.GENERATE_SOURCE_MAP:
448-
building.save_intermediate(infile + '.map', 'post_finalize.map')
449531

450-
if memfile:
451-
# we have a separate .mem file. binaryen did not strip any trailing zeros,
452-
# because it's an ABI question as to whether it is valid to do so or not.
453-
# we can do so here, since we make sure to zero out that memory (even in
454-
# the dynamic linking case, our loader zeros it out)
455-
remove_trailing_zeros(memfile)
532+
if modify_wasm or settings.GENERATE_SOURCE_MAP or memfile:
533+
stdout = building.run_binaryen_command('wasm-emscripten-finalize',
534+
infile=infile,
535+
outfile=outfile if modify_wasm else None,
536+
args=args,
537+
stdout=subprocess.PIPE)
538+
if modify_wasm:
539+
building.save_intermediate(infile, 'post_finalize.wasm')
540+
elif infile != outfile:
541+
shutil.copy(infile, outfile)
542+
if settings.GENERATE_SOURCE_MAP:
543+
building.save_intermediate(infile + '.map', 'post_finalize.map')
544+
545+
if memfile:
546+
# we have a separate .mem file. binaryen did not strip any trailing zeros,
547+
# because it's an ABI question as to whether it is valid to do so or not.
548+
# we can do so here, since we make sure to zero out that memory (even in
549+
# the dynamic linking case, our loader zeros it out)
550+
remove_trailing_zeros(memfile)
551+
552+
metadata = load_metadata_wasm(stdout, DEBUG)
553+
else:
554+
metadata = read_metadata(infile)
456555

457-
return load_metadata_wasm(stdout, DEBUG)
556+
logger.debug("Metadata parsed: " + pprint.pformat(metadata))
557+
return metadata
458558

459559

460560
def create_asm_consts(metadata):
@@ -758,7 +858,6 @@ def load_metadata_wasm(metadata_raw, DEBUG):
758858
metadata = {
759859
'declares': [],
760860
'globalImports': [],
761-
'staticBump': 0,
762861
'tableSize': 0,
763862
'exports': [],
764863
'namedGlobals': {},
@@ -783,9 +882,6 @@ def load_metadata_wasm(metadata_raw, DEBUG):
783882
# TODO(sbc): remove this once binaryen has been changed to only emit the single element
784883
metadata['asmConsts'] = {k: v[0] if type(v) is list else v for k, v in metadata['asmConsts'].items()}
785884

786-
if DEBUG:
787-
logger.debug("Metadata parsed: " + pprint.pformat(metadata))
788-
789885
# Calculate the subset of exports that were explicitly marked with llvm.used.
790886
# These are any exports that were not requested on the command line and are
791887
# not known auto-generated system functions.

0 commit comments

Comments
 (0)