diff --git a/lld/test/wasm/shared.ll b/lld/test/wasm/shared.ll --- a/lld/test/wasm/shared.ll +++ b/lld/test/wasm/shared.ll @@ -9,8 +9,14 @@ @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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; CHECK-NEXT: Offset: ; CHECK-NEXT: Opcode: GLOBAL_GET ; CHECK-NEXT: Index: 1 -; CHECK-NEXT: Content: '0200000001000000020000000000000000000000' +; CHECK-NEXT: Content: '020000000100000002000000000000000000000000000000' diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -301,10 +301,19 @@ // 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 @@ // 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 diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -97,6 +97,9 @@ 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; }