예제 #1
0
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
예제 #2
0
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')
예제 #3
0
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
예제 #4
0
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'])
예제 #5
0
 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]))
예제 #6
0
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
예제 #7
0
 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]))