[lld] r357715 - [WebAssembly] Apply data relocations at runtime in shared objects
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 4 11:40:51 PDT 2019
Author: sbc
Date: Thu Apr 4 11:40:51 2019
New Revision: 357715
URL: http://llvm.org/viewvc/llvm-project?rev=357715&view=rev
Log:
[WebAssembly] Apply data relocations at runtime in shared objects
See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
Data section relocations in wasm shared libraries are applied by the
library itself at static constructor time. This change adds a new
synthetic function that applies relocations to relevant memory locations
on startup.
Differential Revision: https://reviews.llvm.org/D59278
Modified:
lld/trunk/test/wasm/shared.ll
lld/trunk/test/wasm/undefined-data.ll
lld/trunk/wasm/Driver.cpp
lld/trunk/wasm/InputChunks.cpp
lld/trunk/wasm/InputChunks.h
lld/trunk/wasm/InputFiles.h
lld/trunk/wasm/MarkLive.cpp
lld/trunk/wasm/Symbols.cpp
lld/trunk/wasm/Symbols.h
lld/trunk/wasm/Writer.cpp
Modified: lld/trunk/test/wasm/shared.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/wasm/shared.ll?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/test/wasm/shared.ll (original)
+++ lld/trunk/test/wasm/shared.ll Thu Apr 4 11:40:51 2019
@@ -9,6 +9,9 @@ target triple = "wasm32-unknown-unknown"
@indirect_func = local_unnamed_addr global i32 ()* @foo, align 4
@indirect_func_external = local_unnamed_addr global void ()* @func_external, align 4
+ at data_addr = local_unnamed_addr global i32* @data, align 4
+ at data_addr_external = local_unnamed_addr global i32* @data_external, align 4
+
define default i32 @foo() {
entry:
; To ensure we use __stack_pointer
@@ -36,7 +39,7 @@ declare void @func_external()
; CHECK: Sections:
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: dylink
-; CHECK-NEXT: MemorySize: 12
+; CHECK-NEXT: MemorySize: 20
; CHECK-NEXT: MemoryAlignment: 2
; CHECK-NEXT: TableSize: 2
; CHECK-NEXT: TableAlignment: 0
@@ -90,6 +93,12 @@ declare void @func_external()
; CHECK-NEXT: GlobalMutable: true
; CHECK-NEXT: - Type: FUNCTION
+; CHECK: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: __wasm_call_ctors
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+
; check for elem segment initialized with __table_base global as offset
; CHECK: - Type: ELEM
@@ -97,7 +106,19 @@ declare void @func_external()
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: GLOBAL_GET
; CHECK-NEXT: Index: 2
-; CHECK-NEXT: Functions: [ 1, 0 ]
+; CHECK-NEXT: Functions: [ 3, 0 ]
+
+; check the generated code in __wasm_call_ctors and __wasm_apply_relocs functions
+; TODO(sbc): Disassemble and verify instructions.
+
+; CHECK: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Locals: []
+; CHECK-NEXT: Body: 10020B
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Locals: []
+; CHECK-NEXT: Body: 230141046A230241006A360200230141086A230241016A3602002301410C6A230141006A360200230141106A23033602000B
; check the data segment initialized with __memory_base global as offset
@@ -108,4 +129,4 @@ declare void @func_external()
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: GLOBAL_GET
; CHECK-NEXT: Index: 1
-; CHECK-NEXT: Content: '020000000000000001000000'
+; CHECK-NEXT: Content: '0200000000000000010000000000000000000000'
Modified: lld/trunk/test/wasm/undefined-data.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/wasm/undefined-data.ll?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/test/wasm/undefined-data.ll (original)
+++ lld/trunk/test/wasm/undefined-data.ll Thu Apr 4 11:40:51 2019
@@ -1,6 +1,6 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=UNDEF
-; RUN: not wasm-ld --allow-undefined -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=BADRELOC
+; RUN: not wasm-ld --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=BADRELOC
target triple = "wasm32-unknown-unknown"
Modified: lld/trunk/wasm/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Driver.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Driver.cpp (original)
+++ lld/trunk/wasm/Driver.cpp Thu Apr 4 11:40:51 2019
@@ -278,10 +278,15 @@ void LinkerDriver::createFiles(opt::Inpu
}
}
-static StringRef getEntry(opt::InputArgList &Args, StringRef Default) {
+static StringRef getEntry(opt::InputArgList &Args) {
auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
- if (!Arg)
- return Default;
+ if (!Arg) {
+ if (Args.hasArg(OPT_relocatable))
+ return "";
+ if (Args.hasArg(OPT_shared))
+ return "__wasm_call_ctors";
+ return "_start";
+ }
if (Arg->getOption().getID() == OPT_no_entry)
return "";
return Arg->getValue();
@@ -298,7 +303,7 @@ static void setConfigs(opt::InputArgList
Config->CompressRelocations = Args.hasArg(OPT_compress_relocations);
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
- Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
+ Config->Entry = getEntry(Args);
Config->ExportAll = Args.hasArg(OPT_export_all);
Config->ExportDynamic = Args.hasFlag(OPT_export_dynamic,
OPT_no_export_dynamic, false);
@@ -422,11 +427,21 @@ static void createSyntheticSymbols() {
static llvm::wasm::WasmGlobalType MutableGlobalTypeI32 = {WASM_TYPE_I32,
true};
- if (!Config->Relocatable)
+ if (!Config->Relocatable) {
WasmSym::CallCtors = Symtab->addSyntheticFunction(
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
+ if (Config->Pic) {
+ // For PIC code we create a synthetic function call __wasm_apply_relocs
+ // and add this as the first call in __wasm_call_ctors.
+ // We also unconditionally export
+ WasmSym::ApplyRelocs = Symtab->addSyntheticFunction(
+ "__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+ make<SyntheticFunction>(NullSignature, "__wasm_apply_relocs"));
+ }
+ }
+
// The __stack_pointer is imported in the shared library case, and exported
// in the non-shared (executable) case.
if (Config->Shared) {
@@ -518,12 +533,6 @@ void LinkerDriver::link(ArrayRef<const c
Config->ImportTable = true;
}
- if (Config->Shared) {
- Config->ImportMemory = true;
- Config->ExportDynamic = true;
- Config->AllowUndefined = true;
- }
-
// Handle --trace-symbol.
for (auto *Arg : Args.filtered(OPT_trace_symbol))
Symtab->trace(Arg->getValue());
@@ -531,6 +540,12 @@ void LinkerDriver::link(ArrayRef<const c
if (!Config->Relocatable)
createSyntheticSymbols();
+ if (Config->Shared) {
+ Config->ImportMemory = true;
+ Config->ExportDynamic = true;
+ Config->AllowUndefined = true;
+ }
+
createFiles(Args);
if (errorCount())
return;
@@ -547,15 +562,13 @@ void LinkerDriver::link(ArrayRef<const c
handleUndefined(Arg->getValue());
Symbol *EntrySym = nullptr;
- if (!Config->Relocatable) {
- if (!Config->Shared && !Config->Entry.empty()) {
- EntrySym = handleUndefined(Config->Entry);
- if (EntrySym && EntrySym->isDefined())
- EntrySym->ForceExport = true;
- else
- error("entry symbol not defined (pass --no-entry to supress): " +
- Config->Entry);
- }
+ if (!Config->Relocatable && !Config->Entry.empty()) {
+ EntrySym = handleUndefined(Config->Entry);
+ if (EntrySym && EntrySym->isDefined())
+ EntrySym->ForceExport = true;
+ else
+ error("entry symbol not defined (pass --no-entry to supress): " +
+ Config->Entry);
}
if (errorCount())
Modified: lld/trunk/wasm/InputChunks.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/InputChunks.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/InputChunks.cpp (original)
+++ lld/trunk/wasm/InputChunks.cpp Thu Apr 4 11:40:51 2019
@@ -296,3 +296,58 @@ void InputFunction::writeTo(uint8_t *Buf
memcpy(Buf, LastRelocEnd, ChunkSize);
LLVM_DEBUG(dbgs() << " total: " << (Buf + ChunkSize - Orig) << "\n");
}
+
+// Generate code to apply relocations to the data section at runtime.
+// This is only called when generating shared libaries (PIC) where address are
+// not known at static link time.
+void InputSegment::generateRelocationCode(raw_ostream &OS) const {
+ uint32_t SegmentVA = OutputSeg->StartVA + OutputSegmentOffset;
+ for (const WasmRelocation &Rel : Relocations) {
+ uint32_t Offset = Rel.Offset - getInputSectionOffset();
+ uint32_t OutputVA = SegmentVA + Offset;
+
+ // Get __memory_base
+ writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+ writeUleb128(OS, WasmSym::MemoryBase->getGlobalIndex(), "memory_base");
+
+ // Add the offset of the relocation
+ writeU8(OS, WASM_OPCODE_I32_CONST, "I32_CONST");
+ writeSleb128(OS, OutputVA, "offset");
+ writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
+
+ // Now figure out what we want to store
+ switch (Rel.Type) {
+ case R_WASM_TABLE_INDEX_I32:
+ // Add the table index to the __table_base
+ writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+ writeUleb128(OS, WasmSym::TableBase->getGlobalIndex(), "table_base");
+ writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
+ writeSleb128(OS, File->calcNewValue(Rel), "new table index");
+ writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
+ break;
+ case R_WASM_MEMORY_ADDR_I32: {
+ Symbol *Sym = File->getSymbol(Rel);
+ if (Sym->isUndefined()) {
+ // Undefined addresses are accessed via imported GOT globals
+ writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+ writeUleb128(OS, Sym->getGOTIndex(), "global index");
+ } else {
+ // Defined global data is accessed via known offset from __memory_base
+ writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+ writeUleb128(OS, WasmSym::MemoryBase->getGlobalIndex(), "memory_base");
+ writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
+ writeSleb128(OS, File->calcNewValue(Rel), "new memory offset");
+ writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
+ }
+ break;
+ }
+ default:
+ llvm_unreachable("unexpected relocation type in data segment");
+ }
+
+ // Store that value at the virtual address
+ writeU8(OS, WASM_OPCODE_I32_STORE, "I32_STORE");
+ writeUleb128(OS, 2, "align");
+ writeUleb128(OS, 0, "offset");
+ }
+}
Modified: lld/trunk/wasm/InputChunks.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/InputChunks.h?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/InputChunks.h (original)
+++ lld/trunk/wasm/InputChunks.h Thu Apr 4 11:40:51 2019
@@ -92,6 +92,8 @@ public:
static bool classof(const InputChunk *C) { return C->kind() == DataSegment; }
+ void generateRelocationCode(raw_ostream &OS) const;
+
uint32_t getAlignment() const { return Segment.Data.Alignment; }
StringRef getName() const override { return Segment.Data.Name; }
StringRef getDebugName() const override { return StringRef(); }
Modified: lld/trunk/wasm/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/InputFiles.h?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/InputFiles.h (original)
+++ lld/trunk/wasm/InputFiles.h Thu Apr 4 11:40:51 2019
@@ -96,6 +96,9 @@ public:
uint32_t calcNewValue(const WasmRelocation &Reloc) const;
uint32_t calcNewAddend(const WasmRelocation &Reloc) const;
uint32_t calcExpectedValue(const WasmRelocation &Reloc) const;
+ Symbol *getSymbol(const WasmRelocation &Reloc) const {
+ return Symbols[Reloc.Index];
+ };
const WasmSection *CodeSection = nullptr;
const WasmSection *DataSection = nullptr;
Modified: lld/trunk/wasm/MarkLive.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/MarkLive.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/MarkLive.cpp (original)
+++ lld/trunk/wasm/MarkLive.cpp Thu Apr 4 11:40:51 2019
@@ -76,6 +76,11 @@ void lld::wasm::markLive() {
}
}
+ if (Config->Pic) {
+ Enqueue(WasmSym::CallCtors);
+ Enqueue(WasmSym::ApplyRelocs);
+ }
+
// Follow relocations to mark all reachable chunks.
while (!Q.empty()) {
InputChunk *C = Q.pop_back_val();
Modified: lld/trunk/wasm/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.cpp (original)
+++ lld/trunk/wasm/Symbols.cpp Thu Apr 4 11:40:51 2019
@@ -24,6 +24,7 @@ using namespace lld;
using namespace lld::wasm;
DefinedFunction *WasmSym::CallCtors;
+DefinedFunction *WasmSym::ApplyRelocs;
DefinedData *WasmSym::DsoHandle;
DefinedData *WasmSym::DataEnd;
DefinedData *WasmSym::HeapBase;
Modified: lld/trunk/wasm/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.h?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.h (original)
+++ lld/trunk/wasm/Symbols.h Thu Apr 4 11:40:51 2019
@@ -406,6 +406,10 @@ struct WasmSym {
// Function that directly calls all ctors in priority order.
static DefinedFunction *CallCtors;
+ // __wasm_apply_relocs
+ // Function that applies relocations to data segment post-instantiation.
+ static DefinedFunction *ApplyRelocs;
+
// __dso_handle
// Symbol used in calls to __cxa_atexit to determine current DLL
static DefinedData *DsoHandle;
Modified: lld/trunk/wasm/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Writer.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Writer.cpp (original)
+++ lld/trunk/wasm/Writer.cpp Thu Apr 4 11:40:51 2019
@@ -65,7 +65,9 @@ private:
uint32_t lookupType(const WasmSignature &Sig);
uint32_t registerType(const WasmSignature &Sig);
- void createCtorFunction();
+ void createApplyRelocationsFunction();
+ void createCallCtorsFunction();
+
void calculateInitFunctions();
void processRelocations(InputChunk *Chunk);
void assignIndexes();
@@ -1149,17 +1151,6 @@ void Writer::processRelocations(InputChu
File->TypeMap[Reloc.Index] = registerType(Types[Reloc.Index]);
File->TypeIsUsed[Reloc.Index] = true;
break;
- case R_WASM_MEMORY_ADDR_SLEB:
- case R_WASM_MEMORY_ADDR_I32:
- case R_WASM_MEMORY_ADDR_LEB: {
- DataSymbol *Sym = File->getDataSymbol(Reloc.Index);
- if (!Config->Relocatable && !isa<DefinedData>(Sym) && !Sym->isWeak())
- error(File->getName() + ": relocation " +
- relocTypeToString(Reloc.Type) + " cannot be used againt symbol " +
- Sym->getName() + "; recompile with -fPIC");
-
- break;
- }
case R_WASM_GLOBAL_INDEX_LEB: {
auto* Sym = File->getSymbols()[Reloc.Index];
if (!isa<GlobalSymbol>(Sym) && !Sym->isInGOT()) {
@@ -1168,7 +1159,24 @@ void Writer::processRelocations(InputChu
}
}
}
+
+ if (Config->Pic) {
+ // Certain relocation types can't be used when building PIC output, since
+ // they would require absolute symbol addresses at link time.
+ switch (Reloc.Type) {
+ case R_WASM_TABLE_INDEX_SLEB:
+ case R_WASM_MEMORY_ADDR_SLEB:
+ case R_WASM_MEMORY_ADDR_LEB: {
+ Symbol *Sym = File->getSymbols()[Reloc.Index];
+ error(toString(File) + ": relocation " +
+ relocTypeToString(Reloc.Type) + " cannot be used againt symbol " +
+ toString(*Sym) + "; recompile with -fPIC");
+ break;
+ }
+ }
+ }
}
+
}
void Writer::assignIndexes() {
@@ -1272,12 +1280,38 @@ void Writer::createOutputSegments() {
}
}
-static const int OPCODE_CALL = 0x10;
-static const int OPCODE_END = 0xb;
+// For -shared (PIC) output, we create create a synthetic function which will
+// apply any relocations to the data segments on startup. This function is
+// called __wasm_apply_relocs and is added at the very beginning of
+// __wasm_call_ctors before any of the constructors run.
+void Writer::createApplyRelocationsFunction() {
+ LLVM_DEBUG(dbgs() << "createApplyRelocationsFunction\n");
+ // First write the body's contents to a string.
+ std::string BodyContent;
+ {
+ raw_string_ostream OS(BodyContent);
+ writeUleb128(OS, 0, "num locals");
+ for (const OutputSegment *Seg : Segments)
+ for (const InputSegment *InSeg : Seg->InputSegments)
+ InSeg->generateRelocationCode(OS);
+ writeU8(OS, WASM_OPCODE_END, "END");
+ }
+
+ // Once we know the size of the body we can create the final function body
+ std::string FunctionBody;
+ {
+ raw_string_ostream OS(FunctionBody);
+ writeUleb128(OS, BodyContent.size(), "function size");
+ OS << BodyContent;
+ }
+
+ ArrayRef<uint8_t> Body = arrayRefFromStringRef(Saver.save(FunctionBody));
+ cast<SyntheticFunction>(WasmSym::ApplyRelocs->Function)->setBody(Body);
+}
// Create synthetic "__wasm_call_ctors" function based on ctor functions
// in input object.
-void Writer::createCtorFunction() {
+void Writer::createCallCtorsFunction() {
if (!WasmSym::CallCtors->isLive())
return;
@@ -1286,11 +1320,16 @@ void Writer::createCtorFunction() {
{
raw_string_ostream OS(BodyContent);
writeUleb128(OS, 0, "num locals");
+ if (Config->Pic) {
+ writeU8(OS, WASM_OPCODE_CALL, "CALL");
+ writeUleb128(OS, WasmSym::ApplyRelocs->getFunctionIndex(),
+ "function index");
+ }
for (const WasmInitEntry &F : InitFunctions) {
- writeU8(OS, OPCODE_CALL, "CALL");
+ writeU8(OS, WASM_OPCODE_CALL, "CALL");
writeUleb128(OS, F.Sym->getFunctionIndex(), "function index");
}
- writeU8(OS, OPCODE_END, "END");
+ writeU8(OS, WASM_OPCODE_END, "END");
}
// Once we know the size of the body we can create the final function body
@@ -1348,12 +1387,15 @@ void Writer::run() {
assignIndexes();
log("-- calculateInitFunctions");
calculateInitFunctions();
- if (!Config->Relocatable)
- createCtorFunction();
log("-- calculateTypes");
calculateTypes();
log("-- layoutMemory");
layoutMemory();
+ if (!Config->Relocatable) {
+ if (Config->Pic)
+ createApplyRelocationsFunction();
+ createCallCtorsFunction();
+ }
log("-- calculateExports");
calculateExports();
log("-- calculateCustomSections");
More information about the llvm-commits
mailing list