[WebAssembly] Always take into account added when applying runtime relocations
authorSam Clegg <sbc@chromium.org>
Thu, 25 Apr 2019 17:11:54 +0000 (17:11 +0000)
committerSam Clegg <sbc@chromium.org>
Thu, 25 Apr 2019 17:11:54 +0000 (17:11 +0000)
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

lld/test/wasm/shared.ll
lld/wasm/InputChunks.cpp
lld/wasm/Symbols.cpp

index 678d628..50a4c8b 100644 (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'
index b609069..735eb77 100644 (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
index c2bd003..74fa723 100644 (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;
 }