[WebAssembly] Initial support for shared objects (-shared)
Based on the initial spec proposal:
https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
The llvm/codegen side of this is still missing but I believe this change is
still worth landing as an incremental step
Differential Revision: https://reviews.llvm.org/D54249
llvm-svn: 346918
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index cb413de..6048508 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -39,7 +39,6 @@
using namespace lld::wasm;
static constexpr int kStackAlignment = 16;
-static constexpr int kInitialTableOffset = 1;
static constexpr const char *kFunctionTableName = "__indirect_function_table";
namespace {
@@ -90,6 +89,7 @@
void createCustomSections();
// Custom sections
+ void createDylinkSection();
void createRelocSections();
void createLinkingSection();
void createNameSection();
@@ -98,8 +98,13 @@
void writeSections();
uint64_t FileSize = 0;
+ uint32_t TableBase = 0;
uint32_t NumMemoryPages = 0;
uint32_t MaxMemoryPages = 0;
+ // Memory size and aligment. Written to the "dylink" section
+ // when build with -shared or -pie.
+ uint32_t MemAlign = 0;
+ uint32_t MemSize = 0;
std::vector<const WasmSignature *> Types;
DenseMap<WasmSignature, int32_t> TypeIndices;
@@ -161,7 +166,7 @@
}
if (Config->ImportTable) {
- uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+ uint32_t TableSize = TableBase + IndirectFunctions.size();
WasmImport Import;
Import.Module = "env";
Import.Field = kFunctionTableName;
@@ -259,7 +264,7 @@
// no address-taken function will fail at validation time since it is
// a validation error to include a call_indirect instruction if there
// is not table.
- uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+ uint32_t TableSize = TableBase + IndirectFunctions.size();
SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE);
raw_ostream &OS = Section->getStream();
@@ -325,12 +330,18 @@
writeUleb128(OS, 1, "segment count");
writeUleb128(OS, 0, "table index");
WasmInitExpr InitExpr;
- InitExpr.Opcode = WASM_OPCODE_I32_CONST;
- InitExpr.Value.Int32 = kInitialTableOffset;
+ if (Config->Pic) {
+ InitExpr.Opcode = WASM_OPCODE_GET_GLOBAL;
+ InitExpr.Value.Global =
+ cast<GlobalSymbol>(WasmSym::TableBase)->getGlobalIndex();
+ } else {
+ InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ InitExpr.Value.Int32 = TableBase;
+ }
writeInitExpr(OS, InitExpr);
writeUleb128(OS, IndirectFunctions.size(), "elem count");
- uint32_t TableIndex = kInitialTableOffset;
+ uint32_t TableIndex = TableBase;
for (const FunctionSymbol *Sym : IndirectFunctions) {
assert(Sym->getTableIndex() == TableIndex);
writeUleb128(OS, Sym->getFunctionIndex(), "function index");
@@ -425,6 +436,20 @@
raw_string_ostream OS{Body};
};
+// Create the custom "dylink" section containing information for the dynamic
+// linker.
+// See
+// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+void Writer::createDylinkSection() {
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, "dylink");
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, MemSize, "MemSize");
+ writeUleb128(OS, int(log2(MemAlign)), "MemAlign");
+ writeUleb128(OS, IndirectFunctions.size(), "TableSize");
+ writeUleb128(OS, 0, "TableAlign");
+}
+
// Create the custom "linking" section containing linker metadata.
// This is only created when relocatable output is requested.
void Writer::createLinkingSection() {
@@ -599,7 +624,7 @@
uint32_t MemoryPtr = 0;
auto PlaceStack = [&]() {
- if (Config->Relocatable)
+ if (Config->Relocatable || Config->Shared)
return;
MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
@@ -625,7 +650,9 @@
if (WasmSym::DsoHandle)
WasmSym::DsoHandle->setVirtualAddress(DataStart);
+ MemAlign = 0;
for (OutputSegment *Seg : Segments) {
+ MemAlign = std::max(MemAlign, Seg->Alignment);
MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
Seg->StartVA = MemoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", Seg->Name,
@@ -658,8 +685,8 @@
else
MemoryPtr = Config->InitialMemory;
}
- uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize);
- NumMemoryPages = MemSize / WasmPageSize;
+ MemSize = MemoryPtr;
+ NumMemoryPages = alignTo(MemoryPtr, WasmPageSize) / WasmPageSize;
log("mem: total pages = " + Twine(NumMemoryPages));
if (Config->MaxMemory != 0) {
@@ -682,6 +709,8 @@
void Writer::createSections() {
// Known sections
+ if (Config->Pic)
+ createDylinkSection();
createTypeSection();
createImportSection();
createFunctionSection();
@@ -874,7 +903,7 @@
AddDefinedFunction(Func);
}
- uint32_t TableIndex = kInitialTableOffset;
+ uint32_t TableIndex = TableBase;
auto HandleRelocs = [&](InputChunk *Chunk) {
if (!Chunk->Live)
return;
@@ -926,6 +955,10 @@
}
static StringRef getOutputDataSegmentName(StringRef Name) {
+ // With PIC code we currently only support a single data segment since
+ // we only have a single __memory_base to use as our base address.
+ if (Config->Pic)
+ return "data";
if (!Config->MergeDataSegments)
return Name;
if (Name.startswith(".text."))
@@ -1010,9 +1043,14 @@
}
void Writer::run() {
- if (Config->Relocatable)
+ if (Config->Relocatable || Config->Pic)
Config->GlobalBase = 0;
+ // For PIC code the table base is assigned dynamically by the loader.
+ // For non-PIC, we start at 1 so that accessing table index 0 always traps.
+ if (!Config->Pic)
+ TableBase = 1;
+
log("-- calculateImports");
calculateImports();
log("-- assignIndexes");