summaryrefslogtreecommitdiff
path: root/src/wasm-linker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-linker.cpp')
-rw-r--r--src/wasm-linker.cpp417
1 files changed, 0 insertions, 417 deletions
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
deleted file mode 100644
index df51d85c6..000000000
--- a/src/wasm-linker.cpp
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-
-#include "wasm-linker.h"
-#include "asm_v_wasm.h"
-#include "ir/utils.h"
-#include "s2wasm.h"
-#include "support/utilities.h"
-#include "wasm-builder.h"
-#include "wasm-emscripten.h"
-#include "wasm-printing.h"
-
-using namespace wasm;
-
-// Name of the dummy function to prevent erroneous nullptr comparisons.
-static constexpr const char* dummyFunction = "__wasm_nullptr";
-static constexpr const char* stackPointer = "__stack_pointer";
-
-void Linker::placeStackPointer(Address stackAllocation) {
- // ensure this is the first allocation
- assert(nextStatic == globalBase || nextStatic == 1);
- const Address pointerSize = 4;
- // Unconditionally allocate space for the stack pointer. Emscripten
- // allocates the stack itself, and initializes the stack pointer itself.
- out.addStatic(pointerSize, pointerSize, stackPointer);
- if (stackAllocation) {
- // If we are allocating the stack, set up a relocation to initialize the
- // stack pointer to point to one past-the-end of the stack allocation.
- std::vector<char> raw;
- raw.resize(pointerSize);
- auto relocation = new LinkerObject::Relocation(
- LinkerObject::Relocation::kData, (uint32_t*)&raw[0], ".stack", stackAllocation);
- out.addRelocation(relocation);
- assert(out.wasm.memory.segments.empty());
- out.addSegment(stackPointer, raw);
- }
-}
-
-void Linker::ensureFunctionImport(Name target, std::string signature) {
- if (!out.wasm.getImportOrNull(target)) {
- auto import = new Import;
- import->name = import->base = target;
- import->module = ENV;
- import->functionType = ensureFunctionType(signature, &out.wasm)->name;
- import->kind = ExternalKind::Function;
- out.wasm.addImport(import);
- }
-}
-
-void Linker::ensureObjectImport(Name target) {
- if (!out.wasm.getImportOrNull(target)) {
- auto import = new Import;
- import->name = import->base = target;
- import->module = ENV;
- import->kind = ExternalKind::Global;
- import->globalType = i32;
- out.wasm.addImport(import);
- }
-}
-
-void Linker::layout() {
- // Convert calls to undefined functions to call_imports
- for (const auto& f : out.undefinedFunctionCalls) {
- Name target = f.first;
- if (!out.symbolInfo.undefinedFunctions.count(target)) continue;
- // Create an import for the target if necessary.
- ensureFunctionImport(target, getSig(*f.second.begin()));
- // Change each call. The target is the same since it's still the name.
- // Delete and re-allocate the Expression as CallImport to avoid undefined
- // behavior.
- for (auto* call : f.second) {
- auto type = call->type;
- ExpressionList operands(out.wasm.allocator);
- operands.swap(call->operands);
- auto target = call->target;
- CallImport* newCall = ExpressionManipulator::convert<Call, CallImport>(call, out.wasm.allocator);
- newCall->type = type;
- newCall->operands.swap(operands);
- newCall->target = target;
- }
- }
-
- // Allocate all user statics
- for (const auto& obj : out.staticObjects) {
- allocateStatic(obj.allocSize, obj.alignment, obj.name);
- }
-
- // Update the segments with their addresses now that they have been allocated.
- for (const auto& seg : out.segments) {
- Address address = staticAddresses[seg.first];
- out.wasm.memory.segments[seg.second].offset = out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(address)));
- segmentsByAddress[address] = seg.second;
- }
-
- // Place the stack after the user's static data, to keep those addresses
- // small.
- if (stackAllocation) allocateStatic(stackAllocation, 16, ".stack");
-
- // The minimum initial memory size is the amount of static variables we have
- // allocated. Round it up to a page, and update the page-increment versions
- // of initial and max
- Address initialMem = roundUpToPageSize(nextStatic);
- if (userInitialMemory) {
- if (initialMem > userInitialMemory) {
- Fatal() << "Specified initial memory size " << userInitialMemory <<
- " is smaller than required size " << initialMem;
- }
- out.wasm.memory.initial = userInitialMemory / Memory::kPageSize;
- } else {
- out.wasm.memory.initial = initialMem / Memory::kPageSize;
- }
- out.wasm.memory.exists = true;
-
- if (userMaxMemory) out.wasm.memory.max = userMaxMemory / Memory::kPageSize;
-
- if (importMemory) {
- auto memoryImport = make_unique<Import>();
- memoryImport->name = MEMORY;
- memoryImport->module = ENV;
- memoryImport->base = MEMORY;
- memoryImport->kind = ExternalKind::Memory;
- out.wasm.memory.imported = true;
- out.wasm.addImport(memoryImport.release());
- } else {
- auto memoryExport = make_unique<Export>();
- memoryExport->name = MEMORY;
- memoryExport->value = Name::fromInt(0);
- memoryExport->kind = ExternalKind::Memory;
- out.wasm.addExport(memoryExport.release());
- }
-
- // Add imports for any imported objects
- for (const auto& obj : out.symbolInfo.importedObjects) {
- ensureObjectImport(obj);
- }
-
- // XXX For now, export all functions marked .globl.
- for (Name name : out.globls) exportFunction(out.wasm, name, false);
- for (Name name : out.initializerFunctions) exportFunction(out.wasm, name, true);
-
- // Pad the indirect function table with a dummy function
- makeDummyFunction();
- // Always create a table, even if it's empty.
- out.wasm.table.exists = true;
-
- // Pre-assign the function indexes
- for (auto& pair : out.indirectIndexes) {
- if (functionIndexes.count(pair.first) != 0 ||
- functionNames.count(pair.second) != 0) {
- Fatal() << "Function " << pair.first << " already has an index " <<
- functionIndexes[pair.first] << " while setting index " << pair.second;
- }
- if (debug) {
- std::cerr << "pre-assigned function index: " << pair.first << ": "
- << pair.second << '\n';
- }
- functionIndexes[pair.first] = pair.second;
- functionNames[pair.second] = pair.first;
- }
-
- // Emit the pre-assigned function names in sorted order
- for (const auto& P : functionNames) {
- ensureTableSegment();
- getTableDataRef().push_back(P.second);
- }
-
- for (auto& relocation : out.relocations) {
- // TODO: Handle weak symbols properly, instead of always taking the weak definition.
- auto *alias = out.getAlias(relocation->symbol, relocation->kind);
- Name name = relocation->symbol;
-
- if (debug) std::cerr << "fix relocation " << name << '\n';
-
- if (alias) {
- name = alias->symbol;
- relocation->addend += alias->offset;
- }
-
- if (relocation->kind == LinkerObject::Relocation::kData) {
- const auto& symbolAddress = staticAddresses.find(name);
- if (symbolAddress == staticAddresses.end()) Fatal() << "Unknown relocation: " << name << '\n';
- *(relocation->data) = symbolAddress->second + relocation->addend;
- if (debug) std::cerr << " ==> " << *(relocation->data) << '\n';
- } else {
- // function address
- if (!out.wasm.getFunctionOrNull(name)) {
- if (FunctionType* f = out.getExternType(name)) {
- // Address of an imported function is taken, but imports do not have addresses in wasm.
- // Generate a thunk to forward to the call_import.
- Function* thunk = getImportThunk(name, f);
- *(relocation->data) = getFunctionIndex(thunk->name) + relocation->addend;
- } else {
- std::cerr << "Unknown symbol: " << name << '\n';
- if (!ignoreUnknownSymbols) Fatal() << "undefined reference\n";
- *(relocation->data) = 0;
- }
- } else {
- *(relocation->data) = getFunctionIndex(name) + relocation->addend;
- }
- }
- }
- out.relocations.clear();
-
- if (!!startFunction) {
- if (out.symbolInfo.implementedFunctions.count(startFunction) == 0) {
- Fatal() << "Unknown start function: `" << startFunction << "`\n";
- }
- const auto *target = out.wasm.getFunction(startFunction);
- Name start("_start");
- if (out.symbolInfo.implementedFunctions.count(start) != 0) {
- Fatal() << "Start function already present: `" << start << "`\n";
- }
- auto* func = new Function;
- func->name = start;
- out.wasm.addFunction(func);
- out.wasm.addStart(start);
- Builder builder(out.wasm);
- auto* block = builder.makeBlock();
- func->body = block;
- {
- // Create the call, matching its parameters.
- // TODO allow calling with non-default values.
- std::vector<Expression*> args;
- Index paramNum = 0;
- for (Type type : target->params) {
- Name name = Name::fromInt(paramNum++);
- Builder::addVar(func, name, type);
- auto* param = builder.makeGetLocal(func->getLocalIndex(name), type);
- args.push_back(param);
- }
- auto* call = builder.makeCall(startFunction, args, target->result);
- Expression* e = call;
- if (target->result != none) e = builder.makeDrop(call);
- block->list.push_back(e);
- block->finalize();
- }
- }
-
- // ensure an explicit function type for indirect call targets
- for (auto& segment : out.wasm.table.segments) {
- for (auto& name : segment.data) {
- auto* func = out.wasm.getFunction(name);
- func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
- }
- }
-
- // Export malloc/realloc/free/memalign whenever availble. JavsScript version of
- // malloc has some issues and it cannot be called once _sbrk() is called, and
- // JS glue code does not have realloc(). TODO This should get the list of
- // exported functions from emcc.py - it has EXPORTED_FUNCTION metadata to keep
- // track of this. Get the list of exported functions using a command-line
- // argument from emcc.py and export all of them.
- for (auto function : {"malloc", "free", "realloc", "memalign"}) {
- if (out.symbolInfo.implementedFunctions.count(function)) {
- exportFunction(out.wasm, function, true);
- }
- }
-
- // finalize function table
- unsigned int tableSize = getTableData().size();
- if (tableSize > 0) {
- out.wasm.table.initial = out.wasm.table.max = tableSize;
- }
-}
-
-bool Linker::linkObject(S2WasmBuilder& builder) {
- LinkerObject::SymbolInfo *newSymbols = builder.getSymbolInfo();
- // check for multiple definitions
- for (const Name& symbol : newSymbols->implementedFunctions) {
- if (out.symbolInfo.implementedFunctions.count(symbol)) {
- // TODO: Figure out error handling for library-style pieces
- // TODO: give LinkerObjects (or builders) names for better errors.
- std::cerr << "Error: multiple definition of symbol " << symbol << "\n";
- return false;
- }
- }
- // Allow duplicate aliases only if they refer to the same name. For now we
- // do not expect aliases in compiler-rt files.
- // TODO: figure out what the semantics of merging aliases should be.
- for (const auto& alias : newSymbols->aliasedSymbols) {
- if (out.symbolInfo.aliasedSymbols.count(alias.first) &&
- (out.symbolInfo.aliasedSymbols.at(alias.first).symbol != alias.second.symbol ||
- out.symbolInfo.aliasedSymbols.at(alias.first).kind != alias.second.kind)) {
- std::cerr << "Error: conflicting definitions for alias "
- << alias.first.c_str() << "of type " << alias.second.kind << "\n";
- return false;
- }
- }
- out.symbolInfo.merge(*newSymbols);
- builder.build(&out);
- return true;
-}
-
-bool Linker::linkArchive(Archive& archive) {
- bool selected;
- do {
- selected = false;
- for (auto child = archive.child_begin(), end = archive.child_end();
- child != end; ++child) {
- Archive::SubBuffer memberBuf = child->getBuffer();
- // S2WasmBuilder expects its input to be NUL-terminated. Archive members
- // are
- // not NUL-terminated. So we have to copy the contents out before parsing.
- std::vector<char> memberString(memberBuf.len + 1);
- memcpy(memberString.data(), memberBuf.data, memberBuf.len);
- memberString[memberBuf.len] = '\0';
- S2WasmBuilder memberBuilder(memberString.data(), false);
- auto* memberSymbols = memberBuilder.getSymbolInfo();
- for (const Name& symbol : memberSymbols->implementedFunctions) {
- if (out.symbolInfo.undefinedFunctions.count(symbol)) {
- if (!linkObject(memberBuilder)) return false;
- selected = true;
- break;
- }
- }
- }
- // If we selected an archive member, it may depend on another archive member
- // so continue to make passes over the members until no more are added.
- } while (selected);
- return true;
-}
-
-Address Linker::getStaticBump() const {
- return nextStatic - globalBase;
-}
-
-void Linker::ensureTableSegment() {
- if (out.wasm.table.segments.size() == 0) {
- auto emptySegment = out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0)));
- out.wasm.table.segments.emplace_back(emptySegment);
- }
-}
-
-std::vector<Name>& Linker::getTableDataRef() {
- assert(out.wasm.table.segments.size() == 1);
- return out.wasm.table.segments[0].data;
-}
-
-std::vector<Name> Linker::getTableData() {
- if (out.wasm.table.segments.size() > 0) {
- return getTableDataRef();
- }
- return {};
-}
-
-Index Linker::getFunctionIndex(Name name) {
- if (!functionIndexes.count(name)) {
- ensureTableSegment();
- functionIndexes[name] = getTableData().size();
- getTableDataRef().push_back(name);
- if (debug) {
- std::cerr << "function index: " << name << ": "
- << functionIndexes[name] << '\n';
- }
- }
- return functionIndexes[name];
-}
-
-void Linker::makeDummyFunction() {
- bool create = false;
- // Check if there are address-taken functions
- for (auto& relocation : out.relocations) {
- if (relocation->kind == LinkerObject::Relocation::kFunction) {
- create = true;
- break;
- }
- }
-
- if (!create) return;
- wasm::Builder wasmBuilder(out.wasm);
- Expression *unreachable = wasmBuilder.makeUnreachable();
- Function *dummy = wasmBuilder.makeFunction(
- Name(dummyFunction),
- std::vector<Type>{},
- Type::none,
- std::vector<Type>{},
- unreachable
- );
- out.wasm.addFunction(dummy);
- getFunctionIndex(dummy->name);
-}
-
-Function* Linker::getImportThunk(Name name, const FunctionType* funcType) {
- Name thunkName = std::string("__importThunk_") + name.c_str();
- if (Function* thunk = out.wasm.getFunctionOrNull(thunkName)) return thunk;
- ensureFunctionImport(name, getSig(funcType));
- wasm::Builder wasmBuilder(out.wasm);
- std::vector<NameType> params;
- Index p = 0;
- for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
- Function *f = wasmBuilder.makeFunction(thunkName, std::move(params), funcType->result, {});
- std::vector<Expression*> args;
- for (Index i = 0; i < funcType->params.size(); ++i) {
- args.push_back(wasmBuilder.makeGetLocal(i, funcType->params[i]));
- }
- Expression* call = wasmBuilder.makeCallImport(name, args, funcType->result);
- f->body = call;
- out.wasm.addFunction(f);
- return f;
-}
-
-Address Linker::getStackPointerAddress() const {
- return Address(staticAddresses.at(stackPointer));
-}