[WebAssembly] Always take into account added when applying runtime relocations

The code we generate for applying data relocations at runtime omitted
the symbols with GOT entries.

Also refactor the code to reduce duplication.

Differential Revision: https://reviews.llvm.org/D61111

llvm-svn: 359207
This commit is contained in:
Sam Clegg 2019-04-25 17:11:54 +00:00
parent a1309a3147
commit b685ddf288
3 changed files with 42 additions and 29 deletions

View File

@ -9,8 +9,14 @@ 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
; Test data relocations
@data_addr = local_unnamed_addr global i32* @data, align 4
; .. against external symbols
@data_addr_external = local_unnamed_addr global i32* @data_external, align 4
; .. including addends
%struct.s = type { i32, i32 }
@extern_struct = external global %struct.s
@extern_struct_internal_ptr = local_unnamed_addr global i32* getelementptr inbounds (%struct.s, %struct.s* @extern_struct, i32 0, i32 1), align 4
define hidden i32 @foo() {
entry:
@ -46,7 +52,7 @@ declare void @func_external()
; CHECK: Sections:
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: dylink
; CHECK-NEXT: MemorySize: 20
; CHECK-NEXT: MemorySize: 24
; CHECK-NEXT: MemoryAlignment: 2
; CHECK-NEXT: TableSize: 3
; CHECK-NEXT: TableAlignment: 0
@ -98,6 +104,11 @@ declare void @func_external()
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: GlobalType: I32
; CHECK-NEXT: GlobalMutable: true
; CHECK-NEXT: - Module: GOT.mem
; CHECK-NEXT: Field: extern_struct
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: GlobalType: I32
; CHECK-NEXT: GlobalMutable: true
; CHECK-NEXT: - Type: FUNCTION
; CHECK: - Type: EXPORT
@ -125,7 +136,7 @@ declare void @func_external()
; CHECK-NEXT: Body: 10020B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals: []
; CHECK-NEXT: Body: 230141046A230241016A360200230141086A230241026A3602002301410C6A230141006A360200230141106A23033602000B
; CHECK-NEXT: Body: 230141046A230241016A360200230141086A23043602002301410C6A230141006A360200230141106A2303360200230141146A230541046A3602000B
; check the data segment initialized with __memory_base global as offset
@ -136,4 +147,4 @@ declare void @func_external()
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: GLOBAL_GET
; CHECK-NEXT: Index: 1
; CHECK-NEXT: Content: '0200000001000000020000000000000000000000'
; CHECK-NEXT: Content: '020000000100000002000000000000000000000000000000'

View File

@ -301,10 +301,19 @@ void InputFunction::writeTo(uint8_t *Buf) const {
// 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 {
LLVM_DEBUG(dbgs() << "generating runtime relocations: " << getName()
<< " count=" << Relocations.size() << "\n");
// TODO(sbc): Encode the relocations in the data section and write a loop
// here to apply them.
uint32_t SegmentVA = OutputSeg->StartVA + OutputSegmentOffset;
for (const WasmRelocation &Rel : Relocations) {
uint32_t Offset = Rel.Offset - getInputSectionOffset();
uint32_t OutputVA = SegmentVA + Offset;
uint32_t OutputOffset = SegmentVA + Offset;
LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(Rel.Type)
<< " addend=" << Rel.Addend << " index=" << Rel.Index
<< " output offset=" << OutputOffset << "\n");
// Get __memory_base
writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
@ -312,38 +321,28 @@ void InputSegment::generateRelocationCode(raw_ostream &OS) const {
// Add the offset of the relocation
writeU8(OS, WASM_OPCODE_I32_CONST, "I32_CONST");
writeSleb128(OS, OutputVA, "offset");
writeSleb128(OS, OutputOffset, "offset");
writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
Symbol *Sym = File->getSymbol(Rel);
// 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
if (Sym->hasGOTIndex()) {
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->isLocal() || Sym->isHidden()) {
// Hidden/Local data symbols are accessed via known offset from
// __memory_base
writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(OS, WasmSym::MemoryBase->getGlobalIndex(), "memory_base");
writeUleb128(OS, Sym->getGOTIndex(), "global index");
if (Rel.Addend) {
writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
writeSleb128(OS, File->calcNewValue(Rel), "new memory offset");
writeSleb128(OS, Rel.Addend, "addend");
writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
} else {
// Default data symbols are accessed via imported GOT globals
writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(OS, Sym->getGOTIndex(), "global index");
}
break;
}
default:
llvm_unreachable("unexpected relocation type in data segment");
} else {
const GlobalSymbol* BaseSymbol = WasmSym::MemoryBase;
if (Rel.Type == R_WASM_TABLE_INDEX_I32)
BaseSymbol = WasmSym::TableBase;
writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(OS, BaseSymbol->getGlobalIndex(), "base");
writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
writeSleb128(OS, File->calcNewValue(Rel), "offset");
writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
}
// Store that value at the virtual address

View File

@ -97,6 +97,9 @@ 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;
GOTIndex = Index;
}