diff options
-rw-r--r-- | src/s2wasm.h | 103 | ||||
-rw-r--r-- | src/support/file.cpp | 2 | ||||
-rw-r--r-- | src/wasm-linker.cpp | 17 | ||||
-rw-r--r-- | src/wasm-linker.h | 29 | ||||
-rw-r--r-- | src/wasm.h | 3 | ||||
-rw-r--r-- | test/dot_s/alias.s | 27 | ||||
-rw-r--r-- | test/dot_s/alias.wast | 18 |
7 files changed, 158 insertions, 41 deletions
diff --git a/src/s2wasm.h b/src/s2wasm.h index bf66f4407..8f429f96c 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -101,6 +101,14 @@ class S2WasmBuilder { return true; } + bool skipEqual() { + skipWhitespace(); + if (*s != '=') return false; + s++; + skipWhitespace(); + return true; + } + #define abort_on(why) { \ dump(why ":"); \ abort(); \ @@ -370,22 +378,60 @@ class S2WasmBuilder { s = inputStart; while (*s) { skipWhitespace(); - s = strstr(s, ".type"); - if (!s) break; - mustMatch(".type"); - Name name = getCommaSeparated(); - skipComma(); - if (!match("@function")) continue; - if (match(".hidden")) mustMatch(name.str); - mustMatch(name.str); - if (match(":")) { - info->implementedFunctions.insert(name); - } else if (match("=")) { - Name alias = getAtSeparated(); - mustMatch("@FUNCTION"); - info->aliasedFunctions.insert({name, alias}); + + // add function definitions and aliases + if (match(".type")) { + Name name = getCommaSeparated(); + skipComma(); + if (!match("@function")) continue; + if (match(".hidden")) mustMatch(name.str); + mustMatch(name.str); + if (match(":")) { + info->implementedFunctions.insert(name); + } else if (match("=")) { + Name alias = getAtSeparated(); + mustMatch("@FUNCTION"); + info->aliasedSymbols.insert({name, LinkerObject::SymbolAlias(alias, LinkerObject::Relocation::kFunction, 0)}); + } else { + abort_on("unknown directive"); + } + // add data aliases } else { - abort_on("unknown directive"); + Name lhs = getStrToSep(); + if (!skipEqual()){ + s = strchr(s, '\n'); + if (!s) break; + continue; + } + + // get the original name + Name rhs = getStrToSep(); + assert(!isFunctionName(rhs)); + Offset offset = 0; + if (*s == '+') { + s++; + offset = getInt(); + } + skipWhitespace(); + + // get the data size + mustMatch(".size"); + mustMatch(lhs.str); + mustMatch(","); + wasm::Address size = atoi(getStr().str); + WASM_UNUSED(size); + skipWhitespace(); + + // check if the rhs is already an alias + const auto alias = symbolInfo->aliasedSymbols.find(rhs); + if (alias != symbolInfo->aliasedSymbols.end() && alias->second.kind == LinkerObject::Relocation::kData) { + offset += alias->second.offset; + rhs = alias->second.symbol; + } + + // add the new alias + symbolInfo->aliasedSymbols.insert({lhs, LinkerObject::SymbolAlias(rhs, + LinkerObject::Relocation::kData, offset)}); } } } @@ -395,7 +441,7 @@ class S2WasmBuilder { skipWhitespace(); if (debug) dump("process"); if (!*s) break; - if (*s != '.') break; + if (*s != '.') skipObjectAlias(false); s++; if (match("text")) parseText(); else if (match("type")) parseType(); @@ -407,10 +453,31 @@ class S2WasmBuilder { else if (match("align") || match("p2align")) skipToEOL(); else if (match("globl")) parseGlobl(); else if (match("functype")) parseFuncType(); - else abort_on("process"); + else skipObjectAlias(true); } } + void skipObjectAlias(bool prefix) { + if (debug) dump("object_alias"); + + // grab the dot that was consumed earlier + if (prefix) s--; + Name lhs = getStrToSep(); + WASM_UNUSED(lhs); + if (!skipEqual()) abort_on("object_alias"); + + Name rhs = getStr(); + WASM_UNUSED(rhs); + skipWhitespace(); + + mustMatch(".size"); + mustMatch(lhs.str); + mustMatch(","); + Name size = getStr(); + WASM_UNUSED(size); + skipWhitespace(); + } + void parseToplevelSection() { auto section = getCommaSeparated(); // Skipping .debug_ sections @@ -804,7 +871,7 @@ class S2WasmBuilder { } else { // non-indirect call Name assign = getAssign(); - Name target = linkerObj->resolveAlias(cleanFunction(getCommaSeparated())); + Name target = linkerObj->resolveAlias(cleanFunction(getCommaSeparated()), LinkerObject::Relocation::kFunction); Call* curr = allocator->alloc<Call>(); curr->target = target; diff --git a/src/support/file.cpp b/src/support/file.cpp index 296fd6dbe..666c07b87 100644 --- a/src/support/file.cpp +++ b/src/support/file.cpp @@ -52,7 +52,7 @@ wasm::Output::Output(const std::string &filename, Flags::BinaryOption binary, Fl : outfile(), out([this, filename, binary, debug]() { std::streambuf *buffer; if (filename.size()) { - if (debug == Flags::Debug) std::cerr << "Opening '" << filename << std::endl; + if (debug == Flags::Debug) std::cerr << "Opening '" << filename << "'" << std::endl; auto flags = std::ofstream::out | std::ofstream::trunc; if (binary == Flags::Binary) flags |= std::ofstream::binary; outfile.open(filename, flags); diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp index 34dbc7f9c..a2e88cfb3 100644 --- a/src/wasm-linker.cpp +++ b/src/wasm-linker.cpp @@ -123,9 +123,16 @@ void Linker::layout() { } }; for (auto& relocation : out.relocations) { + 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'; @@ -133,7 +140,6 @@ void Linker::layout() { if (debug) std::cerr << " ==> " << *(relocation->data) << '\n'; } else { // function address - name = out.resolveAlias(name); if (!out.wasm.checkFunction(name)) { if (FunctionType* f = out.getExternType(name)) { // Address of an imported function is taken, but imports do not have addresses in wasm. @@ -208,11 +214,12 @@ bool Linker::linkObject(S2WasmBuilder& builder) { // 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->aliasedFunctions) { - if (out.symbolInfo.aliasedFunctions.count(alias.first) && - out.symbolInfo.aliasedFunctions[alias.first] != alias.second) { + 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() << "\n"; + << alias.first.c_str() << "of type " << alias.second.kind << "\n"; return false; } } diff --git a/src/wasm-linker.h b/src/wasm-linker.h index 97e1c9cd5..535b5dc55 100644 --- a/src/wasm-linker.h +++ b/src/wasm-linker.h @@ -47,12 +47,19 @@ class LinkerObject { Relocation(Kind kind, uint32_t* data, Name symbol, int addend) : kind(kind), data(data), symbol(symbol), addend(addend) {} }; + struct SymbolAlias { + Name symbol; + Relocation::Kind kind; + Offset offset; + SymbolAlias(Name symbol, Relocation::Kind kind, Offset offset) : + symbol(symbol), kind(kind), offset(offset) {} + }; // Information about symbols struct SymbolInfo { std::unordered_set<cashew::IString> implementedFunctions; std::unordered_set<cashew::IString> undefinedFunctions; // TODO: it's not clear that this really belongs here. - std::unordered_map<cashew::IString, Name> aliasedFunctions; + std::unordered_map<cashew::IString, SymbolAlias> aliasedSymbols; // For now, do not support weak symbols or anything special. Just directly // merge the functions together, and remove any newly-defined functions @@ -63,8 +70,8 @@ class LinkerObject { } implementedFunctions.insert(other.implementedFunctions.begin(), other.implementedFunctions.end()); - aliasedFunctions.insert(other.aliasedFunctions.begin(), - other.aliasedFunctions.end()); + aliasedSymbols.insert(other.aliasedSymbols.begin(), + other.aliasedSymbols.end()); } }; @@ -82,22 +89,28 @@ class LinkerObject { void addRelocation(Relocation::Kind kind, uint32_t* target, Name name, int addend) { relocations.emplace_back(new Relocation(kind, target, name, addend)); } + Relocation* getCurrentRelocation() { return relocations.back().get(); } - bool isFunctionImplemented(Name name) { return symbolInfo.implementedFunctions.count(name) != 0; } - // If name is an alias, return what it points to. Otherwise return name - Name resolveAlias(Name name) { - auto aliased = symbolInfo.aliasedFunctions.find(name); - if (aliased != symbolInfo.aliasedFunctions.end()) return aliased->second; + // If name is an alias, return what it points to. Otherwise return name. + Name resolveAlias(Name name, Relocation::Kind kind) { + auto aliased = symbolInfo.aliasedSymbols.find(name); + if (aliased != symbolInfo.aliasedSymbols.end() && aliased->second.kind == kind) return aliased->second.symbol; return name; } + SymbolAlias *getAlias(Name name, Relocation::Kind kind) { + auto aliased = symbolInfo.aliasedSymbols.find(name); + if (aliased != symbolInfo.aliasedSymbols.end() && aliased->second.kind == kind) return &aliased->second; + return nullptr; + } + // Add an initializer segment for the named static variable. void addSegment(Name name, const char* data, Address size) { segments[name] = wasm.memory.segments.size(); diff --git a/src/wasm.h b/src/wasm.h index a71e09d19..40728de50 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -108,6 +108,9 @@ struct Address { Address& operator++() { ++addr; return *this; } }; +// An offset into memory +typedef int32_t Offset; + // Types enum WasmType { diff --git a/test/dot_s/alias.s b/test/dot_s/alias.s index 85a89a16f..449e33f0e 100644 --- a/test/dot_s/alias.s +++ b/test/dot_s/alias.s @@ -5,9 +5,13 @@ .globl __exit .type __exit,@function __exit: # @__exit - .local i32 + i32.const $push0=, _A + i32.load $push1=, 0($pop0) + i32.const $push2=, ._B + i32.load $push3=, 0($pop2) + i32.add $push4=, $pop1, $pop3 # BB#0: # %entry - return + return $pop4 .endfunc .Lfunc_end0: .size __exit, .Lfunc_end0-__exit @@ -16,10 +20,10 @@ __exit: # @__exit .globl __needs_exit .type __needs_exit,@function __needs_exit: # @__needs_exit - .result i32 + .result i32 # BB#0: # %entry - call __exit_needed@FUNCTION - i32.const $push0=, __exit_needed@FUNCTION + call __exit_needed@FUNCTION + i32.const $push0=, __exit_needed@FUNCTION return $pop0 .endfunc .Lfunc_end1: @@ -29,3 +33,16 @@ __needs_exit: # @__needs_exit .type __exit_needed,@function .hidden __exit_needed __exit_needed = __exit@FUNCTION + + .type .L__unnamed_1,@object + .p2align 4 +.L__unnamed_1: + .int32 1234 + .skip 4 + .int32 2345 + .size .L__unnamed_1, 12 + +_A = .L__unnamed_1 + .size _A, 12 +._B = _A+8 + .size ._B, 4 diff --git a/test/dot_s/alias.wast b/test/dot_s/alias.wast index d1e08e136..6221a2232 100644 --- a/test/dot_s/alias.wast +++ b/test/dot_s/alias.wast @@ -1,5 +1,7 @@ (module - (memory 1) + (memory 1 + (segment 16 "\d2\04\00\00\00\00\00\00)\t\00\00") + ) (export "memory" memory) (type $FUNCSIG$v (func)) (export "__exit" $__exit) @@ -7,8 +9,16 @@ (export "dynCall_v" $dynCall_v) (table $__exit) (func $__exit (type $FUNCSIG$v) - (local $0 i32) - (return) + (return + (i32.add + (i32.load + (i32.const 16) + ) + (i32.load + (i32.const 24) + ) + ) + ) ) (func $__needs_exit (result i32) (call $__exit) @@ -22,4 +32,4 @@ ) ) ) -;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] } +;; METADATA: { "asmConsts": {},"staticBump": 28, "initializers": [] } |