[lld] r368719 - [lld][WebAssembly] Allow linking of pic code into static binaries

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 13 10:02:02 PDT 2019


Author: sbc
Date: Tue Aug 13 10:02:02 2019
New Revision: 368719

URL: http://llvm.org/viewvc/llvm-project?rev=368719&view=rev
Log:
[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

Added:
    lld/trunk/test/wasm/pic-static.ll
Modified:
    lld/trunk/wasm/Driver.cpp
    lld/trunk/wasm/Relocations.cpp
    lld/trunk/wasm/Symbols.cpp
    lld/trunk/wasm/Symbols.h
    lld/trunk/wasm/SyntheticSections.cpp
    lld/trunk/wasm/SyntheticSections.h
    lld/trunk/wasm/Writer.cpp

Added: lld/trunk/test/wasm/pic-static.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/wasm/pic-static.ll?rev=368719&view=auto
==============================================================================
--- lld/trunk/test/wasm/pic-static.ll (added)
+++ lld/trunk/test/wasm/pic-static.ll Tue Aug 13 10:02:02 2019
@@ -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)
+ at global_float = global float 1.0
+ at hidden_float = hidden global float 2.0
+
+ at 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

Modified: lld/trunk/wasm/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Driver.cpp?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/Driver.cpp (original)
+++ lld/trunk/wasm/Driver.cpp Tue Aug 13 10:02:02 2019
@@ -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");
   }
 }
 

Modified: lld/trunk/wasm/Relocations.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Relocations.cpp?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/Relocations.cpp (original)
+++ lld/trunk/wasm/Relocations.cpp Tue Aug 13 10:02:02 2019
@@ -40,6 +40,20 @@ static void reportUndefined(const Symbol
     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(InputChu
       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(InputChu
         // 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 {

Modified: lld/trunk/wasm/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.cpp?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.cpp (original)
+++ lld/trunk/wasm/Symbols.cpp Tue Aug 13 10:02:02 2019
@@ -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
 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;
 }
 

Modified: lld/trunk/wasm/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.h?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.h (original)
+++ lld/trunk/wasm/Symbols.h Tue Aug 13 10:02:02 2019
@@ -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

Modified: lld/trunk/wasm/SyntheticSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/SyntheticSections.cpp?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/SyntheticSections.cpp (original)
+++ lld/trunk/wasm/SyntheticSections.cpp Tue Aug 13 10:02:02 2019
@@ -103,6 +103,7 @@ uint32_t ImportSection::getNumImports()
 
 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() {

Modified: lld/trunk/wasm/SyntheticSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/SyntheticSections.h?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/SyntheticSections.h (original)
+++ lld/trunk/wasm/SyntheticSections.h Tue Aug 13 10:02:02 2019
@@ -52,6 +52,8 @@ public:
 
   virtual void writeBody() {}
 
+  virtual void assignIndexes() {}
+
   void finalizeContents() override {
     writeBody();
     bodyOutputStream.flush();
@@ -173,14 +175,17 @@ class GlobalSection : public SyntheticSe
 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

Modified: lld/trunk/wasm/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Writer.cpp?rev=368719&r1=368718&r2=368719&view=diff
==============================================================================
--- lld/trunk/wasm/Writer.cpp (original)
+++ lld/trunk/wasm/Writer.cpp Tue Aug 13 10:02:02 2019
@@ -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();




More information about the llvm-commits mailing list