def create_asm_consts(metadata): asm_consts = {} for k, v in metadata['asmConsts'].items(): const, sigs, call_types = v const = asstr(const) const = trim_asm_const_body(const) args = [] max_arity = 16 arity = 0 for i in range(max_arity): if ('$' + str(i)) in const: arity = i + 1 for i in range(arity): args.append('$' + str(i)) const = 'function(' + ', '.join(args) + ') {' + const + '}' asm_consts[int(k)] = const asm_consts = [(key, value) for key, value in asm_consts.items()] asm_consts.sort() return asm_consts
def report_missing_symbols(all_implemented, pre): required_symbols = set(shared.Settings.USER_EXPORTED_FUNCTIONS) # In standalone mode a request for `_main` is interpreted as a request for `_start` # so don't warn about mossing main. if shared.Settings.STANDALONE_WASM and '_main' in required_symbols: required_symbols.discard('_main') # the initial list of missing functions are that the user explicitly exported # but were not implemented in compiled code missing = list(required_symbols - all_implemented) for requested in missing: if ('function ' + asstr(requested)) in pre: continue # special-case malloc, EXPORTED by default for internal use, but we bake in a # trivial allocator and warn at runtime if used in ASSERTIONS if missing == '_malloc': continue diagnostics.warning('undefined', 'undefined exported function: "%s"', requested) # Special hanlding for the `_main` symbol if shared.Settings.STANDALONE_WASM: # standalone mode doesn't use main, and it always reports missing entry point at link time. # In this mode we never expect _main in the export list. return if shared.Settings.IGNORE_MISSING_MAIN: # The default mode for emscripten is to ignore the missing main function allowing # maximum compatibility. return if shared.Settings.EXPECT_MAIN and '_main' not in all_implemented: # For compatibility with the output of wasm-ld we use the same wording here in our # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode). exit_with_error('entry symbol not defined (pass --no-entry to suppress): main')
def load_metadata_wasm(metadata_raw, DEBUG): try: metadata_json = json.loads(metadata_raw) except Exception: logger.error( 'emscript: failure to parse metadata output from wasm-emscripten-finalize. raw output is: \n' + metadata_raw) raise metadata = { 'aliases': {}, 'declares': [], 'implementedFunctions': [], 'externs': [], 'simd': False, # Obsolete, always False 'maxGlobalAlign': 0, 'staticBump': 0, 'tableSize': 0, 'initializers': [], 'exports': [], 'namedGlobals': {}, 'emJsFuncs': {}, 'asmConsts': {}, 'invokeFuncs': [], 'features': [], 'mainReadsParams': 1, } assert 'tableSize' in metadata_json.keys() for key, value in metadata_json.items(): # json.loads returns `unicode` for strings but other code in this file # generally works with utf8 encoded `str` objects, and they don't alwasy # mix well. e.g. s.replace(x, y) will blow up is `s` a uts8 str containing # non-ascii and either x or y are unicode objects. # TODO(sbc): Remove this encoding if we switch to unicode elsewhere # (specifically the glue returned from compile_settings) if type(value) == list: value = [asstr(v) for v in value] if key not in metadata: exit_with_error( 'unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value if not shared.Settings.MINIMAL_RUNTIME: # In regular runtime initializers call the global var version of the export, so they get the mangled name. # In MINIMAL_RUNTIME, the initializers are called directly off the export object for minimal code size. metadata['initializers'] = [ asmjs_mangle(i) for i in metadata['initializers'] ] if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in shared.Settings.EXPORTED_FUNCTIONS ] building.user_requested_exports += unexpected_exports # With the wasm backend the set of implemented functions is identical to the set of exports # Set this key here simply so that the shared code that handle it. metadata['implementedFunctions'] = [ asmjs_mangle(x) for x in metadata['exports'] ] return metadata
def read_dwarf_entries(wasm, options): if options.dwarfdump_output: output = open(options.dwarfdump_output, 'r').read() elif options.dwarfdump: logger.debug('Reading DWARF information from %s' % wasm) if not os.path.exists(options.dwarfdump): logger.error('llvm-dwarfdump not found: ' + options.dwarfdump) sys.exit(1) process = Popen([ options.dwarfdump, '-debug-info', '-debug-line', '--recurse-depth=0', wasm ], stdout=PIPE) output, err = process.communicate() exit_code = process.wait() if exit_code != 0: logger.error('Error during llvm-dwarfdump execution (%s)' % exit_code) sys.exit(1) else: logger.error('Please specify either --dwarfdump or --dwarfdump-output') sys.exit(1) entries = [] debug_line_chunks = re.split(r"debug_line\[(0x[0-9a-f]*)\]", asstr(output)) maybe_debug_info_content = debug_line_chunks[0] for i in range(1, len(debug_line_chunks), 2): stmt_list = debug_line_chunks[i] comp_dir_match = re.search( r"DW_AT_stmt_list\s+\(" + stmt_list + r"\)\s+" + r"DW_AT_comp_dir\s+\(\"([^\"]+)", maybe_debug_info_content) comp_dir = comp_dir_match.group( 1) if comp_dir_match is not None else "" line_chunk = debug_line_chunks[i + 1] # include_directories[ 1] = "/Users/yury/Work/junk/sqlite-playground/src" # file_names[ 1]: # name: "playground.c" # dir_index: 1 # mod_time: 0x00000000 # length: 0x00000000 # # Address Line Column File ISA Discriminator Flags # ------------------ ------ ------ ------ --- ------------- ------------- # 0x0000000000000006 22 0 1 0 0 is_stmt # 0x0000000000000007 23 10 1 0 0 is_stmt prologue_end # 0x000000000000000f 23 3 1 0 0 # 0x0000000000000010 23 3 1 0 0 end_sequence # 0x0000000000000011 28 0 1 0 0 is_stmt include_directories = {'0': comp_dir} for dir in re.finditer(r"include_directories\[\s*(\d+)\] = \"([^\"]*)", line_chunk): include_directories[dir.group(1)] = dir.group(2) files = {} for file in re.finditer( r"file_names\[\s*(\d+)\]:\s+name: \"([^\"]*)\"\s+dir_index: (\d+)", line_chunk): dir = include_directories[file.group(3)] file_path = (dir + '/' if file.group(2)[0] != '/' else '') + file.group(2) files[file.group(1)] = file_path for line in re.finditer( r"\n0x([0-9a-f]+)\s+(\d+)\s+(\d+)\s+(\d+)(.*?end_sequence)?", line_chunk): entry = { 'address': int(line.group(1), 16), 'line': int(line.group(2)), 'column': int(line.group(3)), 'file': files[line.group(4)], 'eos': line.group(5) is not None } if not entry['eos']: entries.append(entry) else: # move end of function to the last END operator entry['address'] -= 1 if entries[-1]['address'] == entry['address']: # last entry has the same address, reusing entries[-1]['eos'] = True else: entries.append(entry) remove_dead_entries(entries) # return entries sorted by the address field return sorted(entries, key=lambda entry: entry['address'])
def show_build_errors(outs, errs): for i in range(len(outs)): shared.logging.debug('output from attempt ' + str(i) + ':\n' + shared.asstr(outs[i]) + '\n===========\n' + shared.asstr(errs[i]))
def load_metadata_wasm(metadata_raw, DEBUG): try: metadata_json = json.loads(metadata_raw) except Exception: logger.error( 'emscript: failure to parse metadata output from wasm-emscripten-finalize. raw output is: \n' + metadata_raw) raise metadata = { 'declares': [], 'externs': [], 'staticBump': 0, 'tableSize': 0, 'exports': [], 'namedGlobals': {}, 'emJsFuncs': {}, 'asmConsts': {}, 'invokeFuncs': [], 'features': [], 'mainReadsParams': 1, } legacy_keys = set(['implementedFunctions', 'initializers', 'simd']) assert 'tableSize' in metadata_json.keys() for key, value in metadata_json.items(): if key in legacy_keys: continue # json.loads returns `unicode` for strings but other code in this file # generally works with utf8 encoded `str` objects, and they don't alwasy # mix well. e.g. s.replace(x, y) will blow up is `s` a uts8 str containing # non-ascii and either x or y are unicode objects. # TODO(sbc): Remove this encoding if we switch to unicode elsewhere # (specifically the glue returned from compile_settings) if type(value) == list: value = [asstr(v) for v in value] if key not in metadata: exit_with_error( 'unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value # Support older metadata when asmConsts values were lists. We only use the first element # nowadays # TODO(sbc): remove this once binaryen has been changed to only emit the single element metadata['asmConsts'] = { k: v[0] if type(v) is list else v for k, v in metadata['asmConsts'].items() } if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in shared.Settings.EXPORTED_FUNCTIONS ] building.user_requested_exports += unexpected_exports return metadata
def show_build_errors(outs, errs): for i in range(len(outs)): shared.logging.debug('output from attempt ' + str(i) + ':\n' + shared.asstr(outs[i]) + '\n===========\n' + shared.asstr(errs[i]))