| /* |
| * Copyright 2017 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // |
| // wasm-emscripten-finalize console tool |
| // Performs Emscripten-specific transforms on .wasm files |
| // |
| |
| #include <exception> |
| |
| #include "ir/trapping.h" |
| #include "support/colors.h" |
| #include "support/command-line.h" |
| #include "support/file.h" |
| #include "wasm-binary.h" |
| #include "wasm-emscripten.h" |
| #include "wasm-io.h" |
| #include "wasm-printing.h" |
| #include "wasm-validator.h" |
| #include "abi/js.h" |
| |
| using namespace cashew; |
| using namespace wasm; |
| |
| int main(int argc, const char *argv[]) { |
| const uint64_t INVALID_BASE = -1; |
| |
| std::string infile; |
| std::string outfile; |
| std::string inputSourceMapFilename; |
| std::string outputSourceMapFilename; |
| std::string outputSourceMapUrl; |
| std::string dataSegmentFile; |
| bool emitBinary = true; |
| bool debugInfo = false; |
| bool legalizeJavaScriptFFI = true; |
| unsigned numReservedFunctionPointers = 0; |
| uint64_t globalBase = INVALID_BASE; |
| uint64_t initialStackPointer = INVALID_BASE; |
| Options options("wasm-emscripten-finalize", |
| "Performs Emscripten-specific transforms on .wasm files"); |
| options |
| .add("--output", "-o", "Output file", |
| Options::Arguments::One, |
| [&outfile](Options*, const std::string& argument) { |
| outfile = argument; |
| Colors::disable(); |
| }) |
| .add("--debuginfo", "-g", |
| "Emit names section in wasm binary (or full debuginfo in wast)", |
| Options::Arguments::Zero, |
| [&debugInfo](Options *, const std::string &) { |
| debugInfo = true; |
| }) |
| .add("--emit-text", "-S", "Emit text instead of binary for the output file", |
| Options::Arguments::Zero, |
| [&emitBinary](Options*, const std::string& ) { |
| emitBinary = false; |
| }) |
| .add("--emscripten-reserved-function-pointers", "", |
| "Number of reserved function pointers for emscripten addFunction " |
| "support", |
| Options::Arguments::One, |
| [&numReservedFunctionPointers](Options *, |
| const std::string &argument) { |
| numReservedFunctionPointers = std::stoi(argument); |
| }) |
| .add("--global-base", "", "The address at which static globals were placed", |
| Options::Arguments::One, |
| [&globalBase](Options*, const std::string&argument ) { |
| globalBase = std::stoull(argument); |
| }) |
| .add("--initial-stack-pointer", "", "The initial location of the stack pointer", |
| Options::Arguments::One, |
| [&initialStackPointer](Options*, const std::string&argument ) { |
| initialStackPointer = std::stoull(argument); |
| }) |
| |
| .add("--input-source-map", "-ism", "Consume source map from the specified file", |
| Options::Arguments::One, |
| [&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; }) |
| .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, " |
| "f32->f64) the imports and exports for interfacing with JS", |
| Options::Arguments::Zero, |
| [&legalizeJavaScriptFFI](Options *o, const std::string& ) { |
| legalizeJavaScriptFFI = false; |
| }) |
| .add("--output-source-map", "-osm", "Emit source map to the specified file", |
| Options::Arguments::One, |
| [&outputSourceMapFilename](Options *o, const std::string& argument) { outputSourceMapFilename = argument; }) |
| .add("--output-source-map-url", "-osu", "Emit specified string as source map URL", |
| Options::Arguments::One, |
| [&outputSourceMapUrl](Options *o, const std::string& argument) { outputSourceMapUrl = argument; }) |
| .add("--separate-data-segments", "", "Separate data segments to a file", |
| Options::Arguments::One, |
| [&dataSegmentFile](Options *o, const std::string& argument) { dataSegmentFile = argument;}) |
| .add_positional("INFILE", Options::Arguments::One, |
| [&infile](Options *o, const std::string& argument) { |
| infile = argument; |
| }); |
| options.parse(argc, argv); |
| |
| if (infile == "") { |
| Fatal() << "Need to specify an infile\n"; |
| } |
| if (outfile == "" && emitBinary) { |
| Fatal() << "Need to specify an outfile, or use text output\n"; |
| } |
| |
| Module wasm; |
| ModuleReader reader; |
| try { |
| reader.read(infile, wasm, inputSourceMapFilename); |
| } catch (ParseException& p) { |
| p.dump(std::cerr); |
| std::cerr << '\n'; |
| Fatal() << "error in parsing input"; |
| } catch (MapParseException& p) { |
| p.dump(std::cerr); |
| std::cerr << '\n'; |
| Fatal() << "error in parsing wasm source map"; |
| } |
| |
| if (options.debug) { |
| std::cerr << "Module before:\n"; |
| WasmPrinter::printModule(&wasm, std::cerr); |
| } |
| |
| bool isSideModule = false; |
| for (const UserSection& section : wasm.userSections) { |
| if (section.name == BinaryConsts::UserSections::Dylink) { |
| isSideModule = true; |
| } |
| } |
| |
| uint32_t dataSize = 0; |
| |
| if (!isSideModule) { |
| if (globalBase == INVALID_BASE) { |
| Fatal() << "globalBase must be set"; |
| } |
| if (initialStackPointer == INVALID_BASE) { |
| Fatal() << "initialStackPointer must be set"; |
| } |
| Export* dataEndExport = wasm.getExport("__data_end"); |
| if (dataEndExport == nullptr) { |
| Fatal() << "__data_end export not found"; |
| } |
| Global* dataEnd = wasm.getGlobal(dataEndExport->value); |
| if (dataEnd == nullptr) { |
| Fatal() << "__data_end global not found"; |
| } |
| if (dataEnd->type != Type::i32) { |
| Fatal() << "__data_end global has wrong type"; |
| } |
| Const* dataEndConst = dataEnd->init->cast<Const>(); |
| dataSize = dataEndConst->value.geti32() - globalBase; |
| } |
| |
| EmscriptenGlueGenerator generator(wasm); |
| generator.fixInvokeFunctionNames(); |
| |
| PassRunner passRunner(&wasm); |
| passRunner.setDebug(options.debug); |
| passRunner.setDebugInfo(debugInfo); |
| passRunner.add(ABI::getLegalizationPass( |
| legalizeJavaScriptFFI ? ABI::LegalizationLevel::Full |
| : ABI::LegalizationLevel::Minimal |
| )); |
| passRunner.run(); |
| |
| std::vector<Name> initializerFunctions; |
| |
| // The names of standard imports/exports used by lld doesn't quite match that |
| // expected by emscripten. |
| // TODO(sbc): Unify these |
| if (Export* ex = wasm.getExportOrNull("__wasm_call_ctors")) { |
| ex->name = "__post_instantiate"; |
| } |
| if (wasm.table.imported()) { |
| if (wasm.table.base != "table") wasm.table.base = Name("table"); |
| } |
| if (wasm.memory.imported()) { |
| if (wasm.table.base != "memory") wasm.memory.base = Name("memory"); |
| } |
| wasm.updateMaps(); |
| |
| if (isSideModule) { |
| generator.replaceStackPointerGlobal(); |
| } else { |
| generator.generateRuntimeFunctions(); |
| generator.generateMemoryGrowthFunction(); |
| generator.generateStackInitialization(initialStackPointer); |
| // emscripten calls this by default for side libraries so we only need |
| // to include in as a static ctor for main module case. |
| if (wasm.getExportOrNull("__post_instantiate")) { |
| initializerFunctions.push_back("__post_instantiate"); |
| } |
| } |
| |
| generator.generateDynCallThunks(); |
| generator.generateJSCallThunks(numReservedFunctionPointers); |
| std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions, numReservedFunctionPointers); |
| if (!dataSegmentFile.empty()) { |
| Output memInitFile(dataSegmentFile, Flags::Binary, Flags::Release); |
| generator.separateDataSegments(&memInitFile); |
| } |
| |
| if (options.debug) { |
| std::cerr << "Module after:\n"; |
| WasmPrinter::printModule(&wasm, std::cerr); |
| } |
| |
| auto outputBinaryFlag = emitBinary ? Flags::Binary : Flags::Text; |
| Output output(outfile, outputBinaryFlag, Flags::Release); |
| ModuleWriter writer; |
| writer.setDebug(options.debug); |
| writer.setDebugInfo(debugInfo); |
| // writer.setSymbolMap(symbolMap); |
| writer.setBinary(emitBinary); |
| if (outputSourceMapFilename.size()) { |
| writer.setSourceMapFilename(outputSourceMapFilename); |
| writer.setSourceMapUrl(outputSourceMapUrl); |
| } |
| writer.write(wasm, output); |
| if (emitBinary) { |
| std::cout << metadata; |
| } else { |
| output << "(;\n"; |
| output << "--BEGIN METADATA --\n" << metadata << "-- END METADATA --\n"; |
| output << ";)\n"; |
| } |
| |
| return 0; |
| } |