Skip to content

Commit 7185a73

Browse files
committed
[lld][WebAssembly] Allow linking of pic code into static binaries
Summary: See emscripten-core/emscripten#9013 Subscribers: dschuff, jgravelle-google, aheejin, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65922 llvm-svn: 368719
1 parent 57ae300 commit 7185a73

File tree

8 files changed

+165
-14
lines changed

8 files changed

+165
-14
lines changed

lld/test/wasm/pic-static.ll

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
; Test that PIC code can be linked into static binaries.
2+
; In this case the GOT entries will end up as internalized wasm globals with
3+
; fixed values.
4+
; RUN: llc -relocation-model=pic -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
5+
; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o
6+
; RUN: wasm-ld -o %t.wasm %t.o %t.ret32.o
7+
; RUN: obj2yaml %t.wasm | FileCheck %s
8+
9+
target triple = "wasm32-unknown-emscripten"
10+
11+
declare i32 @ret32(float)
12+
@global_float = global float 1.0
13+
@hidden_float = hidden global float 2.0
14+
15+
@ret32_ptr = global i32 (float)* @ret32, align 4
16+
17+
define i32 (float)* @getaddr_external() {
18+
ret i32 (float)* @ret32;
19+
}
20+
21+
define i32 ()* @getaddr_hidden() {
22+
ret i32 ()* @hidden_func;
23+
}
24+
25+
define hidden i32 @hidden_func() {
26+
ret i32 1
27+
}
28+
29+
define void @_start() {
30+
entry:
31+
%f = load float, float* @hidden_float, align 4
32+
%addr = load i32 (float)*, i32 (float)** @ret32_ptr, align 4
33+
%arg = load float, float* @global_float, align 4
34+
call i32 %addr(float %arg)
35+
36+
%addr2 = call i32 (float)* @getaddr_external()
37+
%arg2 = load float, float* @hidden_float, align 4
38+
call i32 %addr2(float %arg2)
39+
40+
%addr3 = call i32 ()* @getaddr_hidden()
41+
call i32 %addr3()
42+
43+
ret void
44+
}
45+
46+
; CHECK: - Type: GLOBAL
47+
; CHECK-NEXT: Globals:
48+
49+
; __stack_pointer
50+
; CHECK-NEXT: - Index: 0
51+
; CHECK-NEXT: Type: I32
52+
; CHECK-NEXT: Mutable: true
53+
; CHECK-NEXT: InitExpr:
54+
; CHECK-NEXT: Opcode: I32_CONST
55+
; CHECK-NEXT: Value: 66576
56+
57+
; GOT.func.ret32
58+
; CHECK-NEXT: - Index: 1
59+
; CHECK-NEXT: Type: I32
60+
; CHECK-NEXT: Mutable: false
61+
; CHECK-NEXT: InitExpr:
62+
; CHECK-NEXT: Opcode: I32_CONST
63+
; CHECK-NEXT: Value: 2
64+
65+
; __table_base
66+
; CHECK-NEXT: - Index: 2
67+
; CHECK-NEXT: Type: I32
68+
; CHECK-NEXT: Mutable: false
69+
; CHECK-NEXT: InitExpr:
70+
; CHECK-NEXT: Opcode: I32_CONST
71+
; CHECK-NEXT: Value: 1
72+
73+
; GOT.mem.global_float
74+
; CHECK-NEXT: - Index: 3
75+
; CHECK-NEXT: Type: I32
76+
; CHECK-NEXT: Mutable: false
77+
; CHECK-NEXT: InitExpr:
78+
; CHECK-NEXT: Opcode: I32_CONST
79+
; CHECK-NEXT: Value: 1024
80+
81+
; GOT.mem.ret32_ptr
82+
; CHECK-NEXT: - Index: 4
83+
; CHECK-NEXT: Type: I32
84+
; CHECK-NEXT: Mutable: false
85+
; CHECK-NEXT: InitExpr:
86+
; CHECK-NEXT: Opcode: I32_CONST
87+
; CHECK-NEXT: Value: 1032
88+
89+
; __memory_base
90+
; CHECK-NEXT: - Index: 5
91+
; CHECK-NEXT: Type: I32
92+
; CHECK-NEXT: Mutable: false
93+
; CHECK-NEXT: InitExpr:
94+
; CHECK-NEXT: Opcode: I32_CONST
95+
; CHECK-NEXT: Value: 1024

lld/wasm/Driver.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,8 @@ static void createOptionalSymbols() {
536536
if (!config->isPic) {
537537
WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base");
538538
WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
539+
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
540+
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
539541
}
540542
}
541543

lld/wasm/Relocations.cpp

+16-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ static void reportUndefined(const Symbol* sym) {
4040
error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
4141
}
4242

43+
static void addGOTEntry(Symbol *sym) {
44+
// In PIC mode a GOT entry is an imported global that the dynamic linker
45+
// will assign.
46+
// In non-PIC mode (i.e. when code compiled as fPIC is linked into a static
47+
// binary) we create an internal wasm global with a fixed value that takes the
48+
// place of th GOT entry and effectivly acts as an i32 const. This can
49+
// potentially be optimized away at runtime or with a post-link tool.
50+
// TODO(sbc): Linker relaxation might also be able to optimize this away.
51+
if (config->isPic)
52+
out.importSec->addGOTEntry(sym);
53+
else
54+
out.globalSec->addDummyGOTEntry(sym);
55+
}
56+
4357
void lld::wasm::scanRelocations(InputChunk *chunk) {
4458
if (!chunk->live)
4559
return;
@@ -67,7 +81,7 @@ void lld::wasm::scanRelocations(InputChunk *chunk) {
6781
break;
6882
case R_WASM_GLOBAL_INDEX_LEB:
6983
if (!isa<GlobalSymbol>(sym))
70-
out.importSec->addGOTEntry(sym);
84+
addGOTEntry(sym);
7185
break;
7286
}
7387

@@ -88,7 +102,7 @@ void lld::wasm::scanRelocations(InputChunk *chunk) {
88102
// will be converted into code by `generateRelocationCode`. This code
89103
// requires the symbols to have GOT entires.
90104
if (requiresGOTAccess(sym))
91-
out.importSec->addGOTEntry(sym);
105+
addGOTEntry(sym);
92106
break;
93107
}
94108
} else {

lld/wasm/Symbols.cpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ GlobalSymbol *WasmSym::tlsBase;
3737
GlobalSymbol *WasmSym::tlsSize;
3838
GlobalSymbol *WasmSym::tlsAlign;
3939
UndefinedGlobal *WasmSym::tableBase;
40+
DefinedData *WasmSym::definedTableBase;
4041
UndefinedGlobal *WasmSym::memoryBase;
42+
DefinedData *WasmSym::definedMemoryBase;
4143

4244
WasmSymbolType Symbol::getWasmType() const {
4345
if (isa<FunctionSymbol>(this))
@@ -111,9 +113,11 @@ void Symbol::setOutputSymbolIndex(uint32_t index) {
111113
void Symbol::setGOTIndex(uint32_t index) {
112114
LLVM_DEBUG(dbgs() << "setGOTIndex " << name << " -> " << index << "\n");
113115
assert(gotIndex == INVALID_INDEX);
114-
// Any symbol that is assigned a GOT entry must be exported othewise the
115-
// dynamic linker won't be able create the entry that contains it.
116-
forceExport = true;
116+
if (config->isPic) {
117+
// Any symbol that is assigned a GOT entry must be exported othewise the
118+
// dynamic linker won't be able create the entry that contains it.
119+
forceExport = true;
120+
}
117121
gotIndex = index;
118122
}
119123

lld/wasm/Symbols.h

+2
Original file line numberDiff line numberDiff line change
@@ -472,10 +472,12 @@ struct WasmSym {
472472
// __table_base
473473
// Used in PIC code for offset of indirect function table
474474
static UndefinedGlobal *tableBase;
475+
static DefinedData *definedTableBase;
475476

476477
// __memory_base
477478
// Used in PIC code for offset of global data
478479
static UndefinedGlobal *memoryBase;
480+
static DefinedData *definedMemoryBase;
479481
};
480482

481483
// A buffer class that is large enough to hold any Symbol-derived

lld/wasm/SyntheticSections.cpp

+28-6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ uint32_t ImportSection::getNumImports() const {
103103

104104
void ImportSection::addGOTEntry(Symbol *sym) {
105105
assert(!isSealed);
106+
LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n");
106107
if (sym->hasGOTIndex())
107108
return;
108109
sym->setGOTIndex(numImportedGlobals++);
@@ -235,11 +236,26 @@ void MemorySection::writeBody() {
235236
writeUleb128(os, maxMemoryPages, "max pages");
236237
}
237238

239+
void GlobalSection::assignIndexes() {
240+
uint32_t globalIndex = out.importSec->getNumImportedGlobals();
241+
for (InputGlobal *g : inputGlobals)
242+
g->setGlobalIndex(globalIndex++);
243+
for (Symbol *sym : gotSymbols)
244+
sym->setGOTIndex(globalIndex++);
245+
}
246+
247+
void GlobalSection::addDummyGOTEntry(Symbol *sym) {
248+
LLVM_DEBUG(dbgs() << "addDummyGOTEntry: " << toString(*sym) << "\n");
249+
if (sym->hasGOTIndex())
250+
return;
251+
gotSymbols.push_back(sym);
252+
}
253+
238254
void GlobalSection::writeBody() {
239255
raw_ostream &os = bodyOutputStream;
240256

241257
writeUleb128(os, numGlobals(), "global count");
242-
for (const InputGlobal *g : inputGlobals)
258+
for (InputGlobal *g : inputGlobals)
243259
writeGlobal(os, g->global);
244260
for (const DefinedData *sym : definedFakeGlobals) {
245261
WasmGlobal global;
@@ -248,16 +264,22 @@ void GlobalSection::writeBody() {
248264
global.InitExpr.Value.Int32 = sym->getVirtualAddress();
249265
writeGlobal(os, global);
250266
}
267+
for (const Symbol *sym : gotSymbols) {
268+
WasmGlobal global;
269+
global.Type = {WASM_TYPE_I32, false};
270+
global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
271+
if (auto *d = dyn_cast<DefinedData>(sym))
272+
global.InitExpr.Value.Int32 = d->getVirtualAddress();
273+
else if (auto *f = cast<DefinedFunction>(sym))
274+
global.InitExpr.Value.Int32 = f->getTableIndex();
275+
writeGlobal(os, global);
276+
}
251277
}
252278

253279
void GlobalSection::addGlobal(InputGlobal *global) {
254280
if (!global->live)
255281
return;
256-
uint32_t globalIndex =
257-
out.importSec->getNumImportedGlobals() + inputGlobals.size();
258-
LLVM_DEBUG(dbgs() << "addGlobal: " << globalIndex << "\n");
259-
global->setGlobalIndex(globalIndex);
260-
out.globalSec->inputGlobals.push_back(global);
282+
inputGlobals.push_back(global);
261283
}
262284

263285
void EventSection::writeBody() {

lld/wasm/SyntheticSections.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class SyntheticSection : public OutputSection {
5252

5353
virtual void writeBody() {}
5454

55+
virtual void assignIndexes() {}
56+
5557
void finalizeContents() override {
5658
writeBody();
5759
bodyOutputStream.flush();
@@ -173,14 +175,17 @@ class GlobalSection : public SyntheticSection {
173175
public:
174176
GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {}
175177
uint32_t numGlobals() const {
176-
return inputGlobals.size() + definedFakeGlobals.size();
178+
return inputGlobals.size() + definedFakeGlobals.size() + gotSymbols.size();
177179
}
178180
bool isNeeded() const override { return numGlobals() > 0; }
181+
void assignIndexes() override;
179182
void writeBody() override;
180183
void addGlobal(InputGlobal *global);
184+
void addDummyGOTEntry(Symbol *sym);
181185

182186
std::vector<const DefinedData *> definedFakeGlobals;
183187
std::vector<InputGlobal *> inputGlobals;
188+
std::vector<Symbol *> gotSymbols;
184189
};
185190

186191
// The event section contains a list of declared wasm events associated with the

lld/wasm/Writer.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ void Writer::layoutMemory() {
226226
}
227227

228228
if (WasmSym::globalBase)
229-
WasmSym::globalBase->setVirtualAddress(config->globalBase);
229+
WasmSym::globalBase->setVirtualAddress(memoryPtr);
230+
if (WasmSym::definedMemoryBase)
231+
WasmSym::definedMemoryBase->setVirtualAddress(memoryPtr);
230232

231233
uint32_t dataStart = memoryPtr;
232234

@@ -617,6 +619,8 @@ void Writer::assignIndexes() {
617619
for (InputEvent *event : file->events)
618620
out.eventSec->addEvent(event);
619621
}
622+
623+
out.globalSec->assignIndexes();
620624
}
621625

622626
static StringRef getOutputDataSegmentName(StringRef name) {
@@ -862,8 +866,11 @@ void Writer::run() {
862866

863867
// For PIC code the table base is assigned dynamically by the loader.
864868
// For non-PIC, we start at 1 so that accessing table index 0 always traps.
865-
if (!config->isPic)
869+
if (!config->isPic) {
866870
tableBase = 1;
871+
if (WasmSym::definedTableBase)
872+
WasmSym::definedTableBase->setVirtualAddress(tableBase);
873+
}
867874

868875
log("-- createOutputSegments");
869876
createOutputSegments();

0 commit comments

Comments
 (0)