aboutsummaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2019-08-13 17:02:02 +0000
committerSam Clegg <sbc@chromium.org>2019-08-13 17:02:02 +0000
commit7185a7301e860a2c7c35a2013ac27f6a8673f067 (patch)
tree5655cadffe5519e4536d0d04ba755ef9a35f1675 /lld
parent57ae300562f826e3fee81017d6473a5dec516383 (diff)
downloadllvm-7185a7301e860a2c7c35a2013ac27f6a8673f067.zip
llvm-7185a7301e860a2c7c35a2013ac27f6a8673f067.tar.gz
llvm-7185a7301e860a2c7c35a2013ac27f6a8673f067.tar.bz2
[lld][WebAssembly] Allow linking of pic code into static binaries
Summary: See https://github.com/emscripten-core/emscripten/issues/9013 Subscribers: dschuff, jgravelle-google, aheejin, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65922 llvm-svn: 368719
Diffstat (limited to 'lld')
-rw-r--r--lld/test/wasm/pic-static.ll95
-rw-r--r--lld/wasm/Driver.cpp2
-rw-r--r--lld/wasm/Relocations.cpp18
-rw-r--r--lld/wasm/Symbols.cpp10
-rw-r--r--lld/wasm/Symbols.h2
-rw-r--r--lld/wasm/SyntheticSections.cpp34
-rw-r--r--lld/wasm/SyntheticSections.h7
-rw-r--r--lld/wasm/Writer.cpp11
8 files changed, 165 insertions, 14 deletions
diff --git a/lld/test/wasm/pic-static.ll b/lld/test/wasm/pic-static.ll
new file mode 100644
index 0000000..f0187ec
--- /dev/null
+++ b/lld/test/wasm/pic-static.ll
@@ -0,0 +1,95 @@
+; Test that PIC code can be linked into static binaries.
+; In this case the GOT entries will end up as internalized wasm globals with
+; fixed values.
+; RUN: llc -relocation-model=pic -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o
+; RUN: wasm-ld -o %t.wasm %t.o %t.ret32.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target triple = "wasm32-unknown-emscripten"
+
+declare i32 @ret32(float)
+@global_float = global float 1.0
+@hidden_float = hidden global float 2.0
+
+@ret32_ptr = global i32 (float)* @ret32, align 4
+
+define i32 (float)* @getaddr_external() {
+ ret i32 (float)* @ret32;
+}
+
+define i32 ()* @getaddr_hidden() {
+ ret i32 ()* @hidden_func;
+}
+
+define hidden i32 @hidden_func() {
+ ret i32 1
+}
+
+define void @_start() {
+entry:
+ %f = load float, float* @hidden_float, align 4
+ %addr = load i32 (float)*, i32 (float)** @ret32_ptr, align 4
+ %arg = load float, float* @global_float, align 4
+ call i32 %addr(float %arg)
+
+ %addr2 = call i32 (float)* @getaddr_external()
+ %arg2 = load float, float* @hidden_float, align 4
+ call i32 %addr2(float %arg2)
+
+ %addr3 = call i32 ()* @getaddr_hidden()
+ call i32 %addr3()
+
+ ret void
+}
+
+; CHECK: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+
+; __stack_pointer
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66576
+
+; GOT.func.ret32
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 2
+
+; __table_base
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1
+
+; GOT.mem.global_float
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+
+; GOT.mem.ret32_ptr
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1032
+
+; __memory_base
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index a7b5f0f..13b9e3c 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -536,6 +536,8 @@ static void createOptionalSymbols() {
if (!config->isPic) {
WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base");
WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
+ WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
+ WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
}
}
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index e39f698..ed160a5 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -40,6 +40,20 @@ static void reportUndefined(const Symbol* sym) {
error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
}
+static void addGOTEntry(Symbol *sym) {
+ // In PIC mode a GOT entry is an imported global that the dynamic linker
+ // will assign.
+ // In non-PIC mode (i.e. when code compiled as fPIC is linked into a static
+ // binary) we create an internal wasm global with a fixed value that takes the
+ // place of th GOT entry and effectivly acts as an i32 const. This can
+ // potentially be optimized away at runtime or with a post-link tool.
+ // TODO(sbc): Linker relaxation might also be able to optimize this away.
+ if (config->isPic)
+ out.importSec->addGOTEntry(sym);
+ else
+ out.globalSec->addDummyGOTEntry(sym);
+}
+
void lld::wasm::scanRelocations(InputChunk *chunk) {
if (!chunk->live)
return;
@@ -67,7 +81,7 @@ void lld::wasm::scanRelocations(InputChunk *chunk) {
break;
case R_WASM_GLOBAL_INDEX_LEB:
if (!isa<GlobalSymbol>(sym))
- out.importSec->addGOTEntry(sym);
+ addGOTEntry(sym);
break;
}
@@ -88,7 +102,7 @@ void lld::wasm::scanRelocations(InputChunk *chunk) {
// will be converted into code by `generateRelocationCode`. This code
// requires the symbols to have GOT entires.
if (requiresGOTAccess(sym))
- out.importSec->addGOTEntry(sym);
+ addGOTEntry(sym);
break;
}
} else {
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 05cf2ef..957b81b 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -37,7 +37,9 @@ GlobalSymbol *WasmSym::tlsBase;
GlobalSymbol *WasmSym::tlsSize;
GlobalSymbol *WasmSym::tlsAlign;
UndefinedGlobal *WasmSym::tableBase;
+DefinedData *WasmSym::definedTableBase;
UndefinedGlobal *WasmSym::memoryBase;
+DefinedData *WasmSym::definedMemoryBase;
WasmSymbolType Symbol::getWasmType() const {
if (isa<FunctionSymbol>(this))
@@ -111,9 +113,11 @@ void Symbol::setOutputSymbolIndex(uint32_t index) {
void Symbol::setGOTIndex(uint32_t index) {
LLVM_DEBUG(dbgs() << "setGOTIndex " << name << " -> " << index << "\n");
assert(gotIndex == INVALID_INDEX);
- // Any symbol that is assigned a GOT entry must be exported othewise the
- // dynamic linker won't be able create the entry that contains it.
- forceExport = true;
+ if (config->isPic) {
+ // Any symbol that is assigned a GOT entry must be exported othewise the
+ // dynamic linker won't be able create the entry that contains it.
+ forceExport = true;
+ }
gotIndex = index;
}
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 83d3926..777f776 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -472,10 +472,12 @@ struct WasmSym {
// __table_base
// Used in PIC code for offset of indirect function table
static UndefinedGlobal *tableBase;
+ static DefinedData *definedTableBase;
// __memory_base
// Used in PIC code for offset of global data
static UndefinedGlobal *memoryBase;
+ static DefinedData *definedMemoryBase;
};
// A buffer class that is large enough to hold any Symbol-derived
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 6d5d14f..02b9b81 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -103,6 +103,7 @@ uint32_t ImportSection::getNumImports() const {
void ImportSection::addGOTEntry(Symbol *sym) {
assert(!isSealed);
+ LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n");
if (sym->hasGOTIndex())
return;
sym->setGOTIndex(numImportedGlobals++);
@@ -235,11 +236,26 @@ void MemorySection::writeBody() {
writeUleb128(os, maxMemoryPages, "max pages");
}
+void GlobalSection::assignIndexes() {
+ uint32_t globalIndex = out.importSec->getNumImportedGlobals();
+ for (InputGlobal *g : inputGlobals)
+ g->setGlobalIndex(globalIndex++);
+ for (Symbol *sym : gotSymbols)
+ sym->setGOTIndex(globalIndex++);
+}
+
+void GlobalSection::addDummyGOTEntry(Symbol *sym) {
+ LLVM_DEBUG(dbgs() << "addDummyGOTEntry: " << toString(*sym) << "\n");
+ if (sym->hasGOTIndex())
+ return;
+ gotSymbols.push_back(sym);
+}
+
void GlobalSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, numGlobals(), "global count");
- for (const InputGlobal *g : inputGlobals)
+ for (InputGlobal *g : inputGlobals)
writeGlobal(os, g->global);
for (const DefinedData *sym : definedFakeGlobals) {
WasmGlobal global;
@@ -248,16 +264,22 @@ void GlobalSection::writeBody() {
global.InitExpr.Value.Int32 = sym->getVirtualAddress();
writeGlobal(os, global);
}
+ for (const Symbol *sym : gotSymbols) {
+ WasmGlobal global;
+ global.Type = {WASM_TYPE_I32, false};
+ global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ if (auto *d = dyn_cast<DefinedData>(sym))
+ global.InitExpr.Value.Int32 = d->getVirtualAddress();
+ else if (auto *f = cast<DefinedFunction>(sym))
+ global.InitExpr.Value.Int32 = f->getTableIndex();
+ writeGlobal(os, global);
+ }
}
void GlobalSection::addGlobal(InputGlobal *global) {
if (!global->live)
return;
- uint32_t globalIndex =
- out.importSec->getNumImportedGlobals() + inputGlobals.size();
- LLVM_DEBUG(dbgs() << "addGlobal: " << globalIndex << "\n");
- global->setGlobalIndex(globalIndex);
- out.globalSec->inputGlobals.push_back(global);
+ inputGlobals.push_back(global);
}
void EventSection::writeBody() {
diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index 1d3b8b7..63fab24 100644
--- a/lld/wasm/SyntheticSections.h
+++ b/lld/wasm/SyntheticSections.h
@@ -52,6 +52,8 @@ public:
virtual void writeBody() {}
+ virtual void assignIndexes() {}
+
void finalizeContents() override {
writeBody();
bodyOutputStream.flush();
@@ -173,14 +175,17 @@ class GlobalSection : public SyntheticSection {
public:
GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {}
uint32_t numGlobals() const {
- return inputGlobals.size() + definedFakeGlobals.size();
+ return inputGlobals.size() + definedFakeGlobals.size() + gotSymbols.size();
}
bool isNeeded() const override { return numGlobals() > 0; }
+ void assignIndexes() override;
void writeBody() override;
void addGlobal(InputGlobal *global);
+ void addDummyGOTEntry(Symbol *sym);
std::vector<const DefinedData *> definedFakeGlobals;
std::vector<InputGlobal *> inputGlobals;
+ std::vector<Symbol *> gotSymbols;
};
// The event section contains a list of declared wasm events associated with the
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 2e1af22..5a3b629 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -226,7 +226,9 @@ void Writer::layoutMemory() {
}
if (WasmSym::globalBase)
- WasmSym::globalBase->setVirtualAddress(config->globalBase);
+ WasmSym::globalBase->setVirtualAddress(memoryPtr);
+ if (WasmSym::definedMemoryBase)
+ WasmSym::definedMemoryBase->setVirtualAddress(memoryPtr);
uint32_t dataStart = memoryPtr;
@@ -617,6 +619,8 @@ void Writer::assignIndexes() {
for (InputEvent *event : file->events)
out.eventSec->addEvent(event);
}
+
+ out.globalSec->assignIndexes();
}
static StringRef getOutputDataSegmentName(StringRef name) {
@@ -862,8 +866,11 @@ void Writer::run() {
// 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->isPic)
+ if (!config->isPic) {
tableBase = 1;
+ if (WasmSym::definedTableBase)
+ WasmSym::definedTableBase->setVirtualAddress(tableBase);
+ }
log("-- createOutputSegments");
createOutputSegments();