summaryrefslogtreecommitdiff
path: root/src/wasm-linker.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-linker.h')
-rw-r--r--src/wasm-linker.h168
1 files changed, 98 insertions, 70 deletions
diff --git a/src/wasm-linker.h b/src/wasm-linker.h
index 9065c4b3e..023cf198c 100644
--- a/src/wasm-linker.h
+++ b/src/wasm-linker.h
@@ -29,7 +29,9 @@
namespace wasm {
-class Linker {
+// An "object file" for linking. Contains a wasm module, plus the associated
+// information needed for linking/layout.
+class LinkerObject {
public:
struct Relocation {
enum Kind { kData, kFunction };
@@ -43,50 +45,7 @@ class Linker {
kind(kind), data(data), symbol(symbol), addend(addend) {}
};
- Linker(Module& wasm, size_t globalBase, size_t stackAllocation,
- size_t userInitialMemory, size_t userMaxMemory,
- bool ignoreUnknownSymbols, Name startFunction,
- bool debug) :
- wasm(wasm),
- ignoreUnknownSymbols(ignoreUnknownSymbols),
- startFunction(startFunction),
- globalBase(globalBase),
- nextStatic(globalBase),
- userInitialMemory(userInitialMemory),
- userMaxMemory(userMaxMemory),
- stackAllocation(stackAllocation),
- debug(debug) {
- if (userMaxMemory && userMaxMemory < userInitialMemory) {
- Fatal() << "Specified max memory " << userMaxMemory <<
- " is < specified initial memory " << userInitialMemory;
- }
- if (roundUpToPageSize(userMaxMemory) != userMaxMemory) {
- Fatal() << "Specified max memory " << userMaxMemory <<
- " is not a multiple of 64k";
- }
- if (roundUpToPageSize(userInitialMemory) != userInitialMemory) {
- Fatal() << "Specified initial memory " << userInitialMemory <<
- " is not a multiple of 64k";
- }
- // Don't allow anything to be allocated at address 0
- if (globalBase == 0) nextStatic = 1;
- // Place the stack pointer at the bottom of the linear memory, to keep its
- // address small (and thus with a small encoding).
- placeStackPointer(stackAllocation);
- // Allocate __dso_handle. For asm.js, emscripten provides this in JS, but
- // wasm modules can't import data objects. Its value is 0 for the main
- // executable, which is all we have with static linking. In the future this
- // can go in a crtbegin or similar file.
- addStatic(4, 4, "__dso_handle");
- }
-
- // Allocate a static variable and return its address in linear memory
- size_t allocateStatic(size_t allocSize, size_t alignment, Name name) {
- size_t address = alignAddr(nextStatic, alignment);
- staticAddresses[name] = address;
- nextStatic = address + allocSize;
- return address;
- }
+ LinkerObject() {}
// Allocate a static object
void addStatic(size_t allocSize, size_t alignment, Name name) {
@@ -98,7 +57,7 @@ class Linker {
}
void addRelocation(Relocation::Kind kind, uint32_t* target, Name name, int addend) {
- relocations.emplace_back(make_unique<Relocation>(kind, target, name, addend));
+ relocations.emplace_back(new Relocation(kind, target, name, addend));
}
Relocation* getCurrentRelocation() {
return relocations.back().get();
@@ -132,14 +91,13 @@ class Linker {
assert(implementedFunctions.count(name));
}
- // Allocate the user stack, set up the initial memory size of the module, lay
- // out the linear memory, process the relocations, and set up the indirect
- // function table.
- void layout();
+ bool isEmpty() {
+ return wasm.functions.empty();
+ }
- // Support for emscripten integration: generates dyncall thunks, emits
- // metadata for asmConsts, staticBump and initializer functions.
- void emscriptenGlue(std::ostream& o);
+ friend class Linker;
+
+ Module wasm;
private:
struct StaticObject {
@@ -150,6 +108,87 @@ class Linker {
allocSize(allocSize), alignment(alignment), name(name) {}
};
+ std::vector<Name> globls;
+
+ std::vector<StaticObject> staticObjects;
+ std::vector<std::unique_ptr<Relocation>> relocations;
+
+ std::set<Name> implementedFunctions;
+ std::unordered_map<cashew::IString, Name> aliasedFunctions;
+
+ std::map<Name, size_t> segments; // name => segment index (in wasm module)
+
+ std::vector<Name> initializerFunctions;
+
+ LinkerObject(const LinkerObject&) = delete;
+ LinkerObject& operator=(const LinkerObject&) = delete;
+
+};
+
+// Class which performs some linker-like functionality; namely taking an object
+// file with relocations, laying out the linear memory and segments, and
+// applying the relocations, resulting in an executable wasm module.
+class Linker {
+ public:
+ Linker(size_t globalBase, size_t stackAllocation,
+ size_t userInitialMemory, size_t userMaxMemory,
+ bool ignoreUnknownSymbols, Name startFunction,
+ bool debug) :
+ ignoreUnknownSymbols(ignoreUnknownSymbols),
+ startFunction(startFunction),
+ globalBase(globalBase),
+ nextStatic(globalBase),
+ userInitialMemory(userInitialMemory),
+ userMaxMemory(userMaxMemory),
+ stackAllocation(stackAllocation),
+ debug(debug) {
+ if (userMaxMemory && userMaxMemory < userInitialMemory) {
+ Fatal() << "Specified max memory " << userMaxMemory <<
+ " is < specified initial memory " << userInitialMemory;
+ }
+ if (roundUpToPageSize(userMaxMemory) != userMaxMemory) {
+ Fatal() << "Specified max memory " << userMaxMemory <<
+ " is not a multiple of 64k";
+ }
+ if (roundUpToPageSize(userInitialMemory) != userInitialMemory) {
+ Fatal() << "Specified initial memory " << userInitialMemory <<
+ " is not a multiple of 64k";
+ }
+ // Don't allow anything to be allocated at address 0
+ if (globalBase == 0) nextStatic = 1;
+
+ // Place the stack pointer at the bottom of the linear memory, to keep its
+ // address small (and thus with a small encoding).
+ placeStackPointer(stackAllocation);
+ // Allocate __dso_handle. For asm.js, emscripten provides this in JS, but
+ // wasm modules can't import data objects. Its value is 0 for the main
+ // executable, which is all we have with static linking. In the future this
+ // can go in a crtbegin or similar file.
+ out.addStatic(4, 4, "__dso_handle");
+ }
+
+ // Return a reference to the LinkerObject for the main executable. If empty,
+ // it can be passed to an S2WasmBuilder and constructed.
+ LinkerObject& getOutput() { return out; }
+
+ // Allocate the user stack, set up the initial memory size of the module, lay
+ // out the linear memory, process the relocations, and set up the indirect
+ // function table.
+ void layout();
+
+ // Support for emscripten integration: generates dyncall thunks, emits
+ // metadata for asmConsts, staticBump and initializer functions.
+ void emscriptenGlue(std::ostream& o);
+
+ private:
+ // Allocate a static variable and return its address in linear memory
+ size_t allocateStatic(size_t allocSize, size_t alignment, Name name) {
+ size_t address = alignAddr(nextStatic, alignment);
+ staticAddresses[name] = address;
+ nextStatic = address + allocSize;
+ return address;
+ }
+
// Allocate space for a stack pointer and (if stackAllocation > 0) set up a
// relocation for it to point to the top of the stack.
void placeStackPointer(size_t stackAllocation);
@@ -175,21 +214,21 @@ class Linker {
}
void exportFunction(Name name, bool must_export) {
- if (!wasm.checkFunction(name)) {
+ if (!out.wasm.checkFunction(name)) {
assert(!must_export);
return;
}
- if (wasm.checkExport(name)) return; // Already exported
- auto exp = wasm.allocator.alloc<Export>();
+ if (out.wasm.checkExport(name)) return; // Already exported
+ auto exp = out.wasm.allocator.alloc<Export>();
exp->name = exp->value = name;
- wasm.addExport(exp);
+ out.wasm.addExport(exp);
}
+ // The output module (linked executable)
+ LinkerObject out;
- Module& wasm;
bool ignoreUnknownSymbols;
Name startFunction;
- std::vector<Name> globls;
// where globals can start to be statically allocated, i.e., the data segment
size_t globalBase;
@@ -200,21 +239,10 @@ class Linker {
size_t stackAllocation;
bool debug;
- std::vector<StaticObject> staticObjects;
std::unordered_map<cashew::IString, int32_t> staticAddresses; // name => address
-
- std::vector<std::unique_ptr<Relocation>> relocations;
-
- std::set<Name> implementedFunctions;
- std::unordered_map<cashew::IString, Name> aliasedFunctions;
-
- std::map<Name, size_t> segments; // name => segment index (in wasm module)
std::unordered_map<size_t, size_t> segmentsByAddress; // address => segment index
-
std::unordered_map<cashew::IString, size_t> functionIndexes;
- std::vector<Name> initializerFunctions;
-
};