diff options
author | Derek Schuff <dschuff@chromium.org> | 2016-04-25 13:16:18 -0700 |
---|---|---|
committer | Derek Schuff <dschuff@chromium.org> | 2016-04-25 13:16:18 -0700 |
commit | 27ef6de772ca90824018819b91b8a230136f56c3 (patch) | |
tree | 265bcdc03489f1f6f844422cc03f6a0528cafa93 | |
parent | 0d17b75113d4f50d4b05ef3b243dcad962483def (diff) | |
download | binaryen-27ef6de772ca90824018819b91b8a230136f56c3.tar.gz binaryen-27ef6de772ca90824018819b91b8a230136f56c3.tar.bz2 binaryen-27ef6de772ca90824018819b91b8a230136f56c3.zip |
Separate LinkerObject from Linker (#383)
Create the LinkerObject class, which has a wasm Module and represents the module plus the information needed to relocate and lay it out. Each Linker owns a "main executable" LinkerObject, and S2WasmBuilder requires a LinkerObject instead of just a Module because LLVM asm files require relocation/linking before they represent a full wasm module.
No merging support yet, but the real functionality for #370 is coming soon.
-rw-r--r-- | src/s2wasm-main.cpp | 14 | ||||
-rw-r--r-- | src/s2wasm.h | 41 | ||||
-rw-r--r-- | src/wasm-linker.cpp | 89 | ||||
-rw-r--r-- | src/wasm-linker.h | 168 |
4 files changed, 171 insertions, 141 deletions
diff --git a/src/s2wasm-main.cpp b/src/s2wasm-main.cpp index c6a77c583..435eb560f 100644 --- a/src/s2wasm-main.cpp +++ b/src/s2wasm-main.cpp @@ -84,7 +84,6 @@ int main(int argc, const char *argv[]) { auto input(read_file<std::string>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); if (options.debug) std::cerr << "Parsing and wasming..." << std::endl; - Module wasm; uint64_t globalBase = options.extra.find("global-base") != options.extra.end() ? std::stoull(options.extra["global-base"]) : 0; @@ -101,21 +100,24 @@ int main(int argc, const char *argv[]) { ? std::stoull(options.extra["max-memory"]) : 0; if (options.debug) std::cerr << "Global base " << globalBase << '\n'; - Linker lm(wasm, globalBase, stackAllocation, initialMem, maxMem, - ignoreUnknownSymbols, startFunction, options.debug); - S2WasmBuilder s2wasm(wasm, input.c_str(), options.debug, lm); + Linker linker(globalBase, stackAllocation, initialMem, maxMem, + ignoreUnknownSymbols, startFunction, options.debug); + + S2WasmBuilder s2wasm(linker.getOutput(), input.c_str(), options.debug); + + linker.layout(); std::stringstream meta; if (generateEmscriptenGlue) { if (options.debug) std::cerr << "Emscripten gluing..." << std::endl; // dyncall thunks - lm.emscriptenGlue(meta); + linker.emscriptenGlue(meta); } if (options.debug) std::cerr << "Printing..." << std::endl; Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release); - WasmPrinter::printModule(&wasm, output.getStream()); + WasmPrinter::printModule(&linker.getOutput().wasm, output.getStream()); output << meta.str() << std::endl; if (options.debug) std::cerr << "Done." << std::endl; diff --git a/src/s2wasm.h b/src/s2wasm.h index 2e979f589..f70246d4f 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -41,23 +41,20 @@ class S2WasmBuilder { MixedArena& allocator; const char* s; bool debug; - Linker& linker; + LinkerObject& linkerObj; public: - S2WasmBuilder(Module& wasm, const char* input, bool debug, - Linker& linker) - : wasm(wasm), + S2WasmBuilder(LinkerObject& linkerObj, const char* input, bool debug) + : wasm(linkerObj.wasm), allocator(wasm.allocator), debug(debug), - linker(linker) { - + linkerObj(linkerObj) { + if (!linkerObj.isEmpty()) Fatal() << "Cannot construct an S2WasmBuilder in an non-empty LinkerObject"; s = input; scan(); s = input; process(); - - linker.layout(); } private: @@ -201,7 +198,9 @@ class S2WasmBuilder { } else { // a global constant, we need to fix it up later Name name = getStrToSep(); - Linker::Relocation::Kind kind = isFunctionName(name) ? Linker::Relocation::kFunction : Linker::Relocation::kData; + LinkerObject::Relocation::Kind kind = isFunctionName(name) ? + LinkerObject::Relocation::kFunction : + LinkerObject::Relocation::kData; int offset = 0; if (*s == '+') { s++; @@ -210,7 +209,7 @@ class S2WasmBuilder { s++; offset = -getInt(); } - linker.addRelocation(kind, target, cleanFunction(name), offset); + linkerObj.addRelocation(kind, target, cleanFunction(name), offset); return true; } } @@ -347,11 +346,11 @@ class S2WasmBuilder { if (match(".hidden")) mustMatch(name.str); mustMatch(name.str); if (match(":")) { - linker.addImplementedFunction(name); + linkerObj.addImplementedFunction(name); } else if (match("=")) { Name alias = getAtSeparated(); mustMatch("@FUNCTION"); - linker.addAliasedFunction(name, alias); + linkerObj.addAliasedFunction(name, alias); } else { abort_on("unknown directive"); } @@ -403,7 +402,7 @@ class S2WasmBuilder { } mustMatch(".int32"); do { - linker.addInitializerFunction(cleanFunction(getStr())); + linkerObj.addInitializerFunction(cleanFunction(getStr())); skipWhitespace(); } while (match(".int32")); } @@ -438,7 +437,7 @@ class S2WasmBuilder { } void parseGlobl() { - linker.addGlobal(getStr()); + linkerObj.addGlobal(getStr()); skipWhitespace(); } @@ -699,8 +698,8 @@ class S2WasmBuilder { // non-indirect call CallBase* curr; Name assign = getAssign(); - Name target = linker.resolveAlias(cleanFunction(getCommaSeparated())); - if (linker.isFunctionImplemented(target)) { + Name target = linkerObj.resolveAlias(cleanFunction(getCommaSeparated())); + if (linkerObj.isFunctionImplemented(target)) { auto specific = allocator.alloc<Call>(); specific->target = target; curr = specific; @@ -1037,7 +1036,7 @@ class S2WasmBuilder { mustMatch(":"); auto raw = new std::vector<char>(); // leaked intentionally, no new allocation in Memory bool zero = true; - std::vector<std::pair<Linker::Relocation*, size_t>> currRelocations; // [relocation, offset in raw] + std::vector<std::pair<LinkerObject::Relocation*, size_t>> currRelocations; // [relocation, offset in raw] while (1) { skipWhitespace(); if (match(".asci")) { @@ -1079,7 +1078,7 @@ class S2WasmBuilder { size_t size = raw->size(); raw->resize(size + 4); if (getConst((uint32_t*)&(*raw)[size])) { // just the size, as we may reallocate; we must fix this later, if it's a relocation - currRelocations.emplace_back(linker.getCurrentRelocation(), size); + currRelocations.emplace_back(linkerObj.getCurrentRelocation(), size); } zero = false; } else if (match(".int64")) { @@ -1110,9 +1109,9 @@ class S2WasmBuilder { r->data = (uint32_t*)&(*raw)[i]; } // assign the address, add to memory - linker.addStatic(size, align, name); + linkerObj.addStatic(size, align, name); if (!zero) { - linker.addSegment(name, (const char*)&(*raw)[0], size); + linkerObj.addSegment(name, (const char*)&(*raw)[0], size); } } @@ -1124,7 +1123,7 @@ class S2WasmBuilder { skipComma(); getInt(); } - linker.addStatic(size, align, name); + linkerObj.addStatic(size, align, name); } void skipImports() { diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp index 2c5e0003d..2e5787670 100644 --- a/src/wasm-linker.cpp +++ b/src/wasm-linker.cpp @@ -37,26 +37,27 @@ void Linker::placeStackPointer(size_t stackAllocation) { const size_t pointerSize = 4; // Unconditionally allocate space for the stack pointer. Emscripten // allocates the stack itself, and initializes the stack pointer itself. - addStatic(pointerSize, pointerSize, "__stack_pointer"); + out.addStatic(pointerSize, pointerSize, "__stack_pointer"); 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. auto* raw = new uint32_t; - addRelocation(Relocation::kData, raw, ".stack", stackAllocation); - assert(wasm.memory.segments.size() == 0); - addSegment("__stack_pointer", reinterpret_cast<char*>(raw), pointerSize); + out.addRelocation(LinkerObject::Relocation::kData, raw, ".stack", stackAllocation); + assert(out.wasm.memory.segments.size() == 0); + out.addSegment("__stack_pointer", reinterpret_cast<char*>(raw), pointerSize); } } void Linker::layout() { // Allocate all user statics - for (const auto& obj : staticObjects) { + 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 (auto& seg : segments) { + for (auto& seg : out.segments) { size_t address = staticAddresses[seg.first]; - wasm.memory.segments[seg.second].offset = address; + out.wasm.memory.segments[seg.second].offset = address; segmentsByAddress[address] = seg.second; } @@ -73,41 +74,41 @@ void Linker::layout() { Fatal() << "Specified initial memory size " << userInitialMemory << " is smaller than required size " << initialMem; } - wasm.memory.initial = userInitialMemory / Memory::kPageSize; + out.wasm.memory.initial = userInitialMemory / Memory::kPageSize; } else { - wasm.memory.initial = initialMem / Memory::kPageSize; + out.wasm.memory.initial = initialMem / Memory::kPageSize; } - if (userMaxMemory) wasm.memory.max = userMaxMemory / Memory::kPageSize; - wasm.memory.exportName = MEMORY; + if (userMaxMemory) out.wasm.memory.max = userMaxMemory / Memory::kPageSize; + out.wasm.memory.exportName = MEMORY; // XXX For now, export all functions marked .globl. - for (Name name : globls) exportFunction(name, false); - for (Name name : initializerFunctions) exportFunction(name, true); + for (Name name : out.globls) exportFunction(name, false); + for (Name name : out.initializerFunctions) exportFunction(name, true); auto ensureFunctionIndex = [this](Name name) { if (functionIndexes.count(name) == 0) { - functionIndexes[name] = wasm.table.names.size(); - wasm.table.names.push_back(name); + functionIndexes[name] = out.wasm.table.names.size(); + out.wasm.table.names.push_back(name); if (debug) { std::cerr << "function index: " << name << ": " << functionIndexes[name] << '\n'; } } }; - for (auto& relocation : relocations) { + for (auto& relocation : out.relocations) { Name name = relocation->symbol; if (debug) std::cerr << "fix relocation " << name << '\n'; - if (relocation->kind == Relocation::kData) { + if (relocation->kind == LinkerObject::Relocation::kData) { const auto& symbolAddress = staticAddresses.find(name); assert(symbolAddress != staticAddresses.end()); *(relocation->data) = symbolAddress->second + relocation->addend; if (debug) std::cerr << " ==> " << *(relocation->data) << '\n'; } else { // function address - name = resolveAlias(name); - if (!wasm.checkFunction(name)) { + name = out.resolveAlias(name); + if (!out.wasm.checkFunction(name)) { std::cerr << "Unknown symbol: " << name << '\n'; if (!ignoreUnknownSymbols) Fatal() << "undefined reference\n"; *(relocation->data) = 0; @@ -118,31 +119,31 @@ void Linker::layout() { } } if (!!startFunction) { - if (implementedFunctions.count(startFunction) == 0) { + if (out.implementedFunctions.count(startFunction) == 0) { Fatal() << "Unknown start function: `" << startFunction << "`\n"; } - const auto *target = wasm.getFunction(startFunction); + const auto *target = out.wasm.getFunction(startFunction); Name start("_start"); - if (implementedFunctions.count(start) != 0) { + if (out.implementedFunctions.count(start) != 0) { Fatal() << "Start function already present: `" << start << "`\n"; } - auto* func = wasm.allocator.alloc<Function>(); + auto* func = out.wasm.allocator.alloc<Function>(); func->name = start; - wasm.addFunction(func); + out.wasm.addFunction(func); exportFunction(start, true); - wasm.addStart(start); - auto* block = wasm.allocator.alloc<Block>(); + out.wasm.addStart(start); + auto* block = out.wasm.allocator.alloc<Block>(); func->body = block; { // Create the call, matching its parameters. // TODO allow calling with non-default values. - auto* call = wasm.allocator.alloc<Call>(); + auto* call = out.wasm.allocator.alloc<Call>(); call->target = startFunction; size_t paramNum = 0; for (WasmType type : target->params) { Name name = Name::fromInt(paramNum++); Builder::addVar(func, name, type); - auto* param = wasm.allocator.alloc<GetLocal>(); + auto* param = out.wasm.allocator.alloc<GetLocal>(); param->index = func->getLocalIndex(name); param->type = type; call->operands.push_back(param); @@ -153,18 +154,18 @@ void Linker::layout() { } // ensure an explicit function type for indirect call targets - for (auto& name : wasm.table.names) { - auto* func = wasm.getFunction(name); - func->type = ensureFunctionType(getSig(func), &wasm, wasm.allocator)->name; + for (auto& name : out.wasm.table.names) { + auto* func = out.wasm.getFunction(name); + func->type = ensureFunctionType(getSig(func), &out.wasm, out.wasm.allocator)->name; } } void Linker::emscriptenGlue(std::ostream& o) { if (debug) { - WasmPrinter::printModule(&wasm, std::cerr); + WasmPrinter::printModule(&out.wasm, std::cerr); } - wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions + out.wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions makeDynCallThunks(); @@ -181,7 +182,7 @@ void Linker::emscriptenGlue(std::ostream& o) { if (curr->target == EMSCRIPTEN_ASM_CONST) { auto arg = curr->operands[0]->cast<Const>(); size_t segmentIndex = parent->segmentsByAddress[arg->value.geti32()]; - std::string code = escape(parent->wasm.memory.segments[segmentIndex].data); + std::string code = escape(parent->out.wasm.memory.segments[segmentIndex].data); int32_t id; if (ids.count(code) == 0) { id = ids.size(); @@ -197,11 +198,11 @@ void Linker::emscriptenGlue(std::ostream& o) { // add import, if necessary if (allSigs.count(sig) == 0) { allSigs.insert(sig); - auto import = parent->wasm.allocator.alloc<Import>(); + auto import = parent->out.wasm.allocator.alloc<Import>(); import->name = import->base = curr->target; import->module = ENV; - import->type = ensureFunctionType(getSig(curr), &parent->wasm, parent->wasm.allocator); - parent->wasm.addImport(import); + import->type = ensureFunctionType(getSig(curr), &parent->out.wasm, parent->out.wasm.allocator); + parent->out.wasm.addImport(import); } } } @@ -230,7 +231,7 @@ void Linker::emscriptenGlue(std::ostream& o) { }; AsmConstWalker walker; walker.parent = this; - walker.startWalk(&wasm); + walker.startWalk(&out.wasm); // print o << "\"asmConsts\": {"; bool first = true; @@ -249,7 +250,7 @@ void Linker::emscriptenGlue(std::ostream& o) { o << "\"initializers\": ["; first = true; - for (const auto& func : initializerFunctions) { + for (const auto& func : out.initializerFunctions) { if (first) first = false; else o << ", "; o << "\"" << func.c_str() << "\""; @@ -261,10 +262,10 @@ void Linker::emscriptenGlue(std::ostream& o) { void Linker::makeDynCallThunks() { std::unordered_set<std::string> sigs; - wasm::Builder wasmBuilder(wasm); - for (const auto& indirectFunc : wasm.table.names) { - std::string sig(getSig(wasm.getFunction(indirectFunc))); - auto* funcType = ensureFunctionType(sig, &wasm, wasm.allocator); + wasm::Builder wasmBuilder(out.wasm); + for (const auto& indirectFunc : out.wasm.table.names) { + std::string sig(getSig(out.wasm.getFunction(indirectFunc))); + auto* funcType = ensureFunctionType(sig, &out.wasm, out.wasm.allocator); if (!sigs.insert(sig).second) continue; // Sig is already in the set std::vector<NameType> params; params.emplace_back("fptr", i32); // function pointer param @@ -278,7 +279,7 @@ void Linker::makeDynCallThunks() { } Expression* call = wasmBuilder.makeCallIndirect(funcType, fptr, std::move(args)); f->body = funcType->result == none ? call : wasmBuilder.makeReturn(call); - wasm.addFunction(f); + out.wasm.addFunction(f); exportFunction(f->name, true); } } 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; - }; |