[lld] 2513407 - [lld][WebAssembly] Add support for -Bsymbolic flag

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 12 17:25:25 PDT 2020


Author: Sam Clegg
Date: 2020-10-12T17:25:04-07:00
New Revision: 2513407d39506edf2a98f647088a9e1789f8c418

URL: https://github.com/llvm/llvm-project/commit/2513407d39506edf2a98f647088a9e1789f8c418
DIFF: https://github.com/llvm/llvm-project/commit/2513407d39506edf2a98f647088a9e1789f8c418.diff

LOG: [lld][WebAssembly] Add support for -Bsymbolic flag

This flag works in a similar way to the ELF linker in that it
will resolve any defined symbols to their local definition with
a shared library or -pie executable.

This flag has no effect on static linking.

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

Added: 
    lld/test/wasm/bsymbolic.s

Modified: 
    lld/wasm/Config.h
    lld/wasm/Driver.cpp
    lld/wasm/Options.td
    lld/wasm/Relocations.cpp
    lld/wasm/SyntheticSections.cpp
    lld/wasm/SyntheticSections.h
    lld/wasm/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/test/wasm/bsymbolic.s b/lld/test/wasm/bsymbolic.s
new file mode 100644
index 000000000000..dc0e0ddcc773
--- /dev/null
+++ b/lld/test/wasm/bsymbolic.s
@@ -0,0 +1,79 @@
+// RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
+// RUN: wasm-ld --no-entry -Bsymbolic %t.o -o %t2.so 2>&1 | FileCheck -check-prefix=WARNING %s
+// WARNING: warning: -Bsymbolic is only meaningful when combined with -shared
+
+// RUN: wasm-ld --experimental-pic -shared %t.o -o %t0.so
+// RUN: obj2yaml %t0.so | FileCheck -check-prefix=NOOPTION %s
+
+// RUN: wasm-ld --experimental-pic -shared -Bsymbolic %t.o -o %t1.so
+// RUN: obj2yaml %t1.so | FileCheck -check-prefix=SYMBOLIC %s
+
+// NOOPTION  - Type:            IMPORT
+// NOOPTION:            - Module:          GOT.func
+// NOOPTION-NEXT:         Field:           foo
+// NOOPTION-NEXT:         Kind:            GLOBAL
+// NOOPTION-NEXT:         GlobalType:      I32
+// NOOPTION-NEXT:         GlobalMutable:   true
+// NOOPTION-NEXT:       - Module:          GOT.mem
+// NOOPTION-NEXT:         Field:           bar
+// NOOPTION-NEXT:         Kind:            GLOBAL
+// NOOPTION-NEXT:         GlobalType:      I32
+// NOOPTION-NEXT:         GlobalMutable:   true
+
+//      NOOPTION:  - Type:            GLOBAL
+// NOOPTION-NEXT:    Globals:
+// NOOPTION-NEXT:      - Index:           4
+// NOOPTION-NEXT:        Type:            I32
+// NOOPTION-NEXT:        Mutable:         false
+// NOOPTION-NEXT:        InitExpr:
+// NOOPTION-NEXT:          Opcode:          I32_CONST
+// NOOPTION-NEXT:          Value:           0
+// NOOPTION-NEXT:  - Type:            EXPORT
+
+// SYMBOLIC-NOT:   - Module:          GOT.mem
+// SYMBOLIC-NOT:   - Module:          GOT.func
+
+// SYMBOLIC:       - Type:            GLOBAL
+// SYMBOLIC-NEXT:    Globals:
+// SYMBOLIC-NEXT:      - Index:           2
+// SYMBOLIC-NEXT:        Type:            I32
+// SYMBOLIC-NEXT:        Mutable:         true
+// SYMBOLIC-NEXT:        InitExpr:
+// SYMBOLIC-NEXT:          Opcode:          I32_CONST
+// SYMBOLIC-NEXT:          Value:           0
+// SYMBOLIC-NEXT:      - Index:           3
+// SYMBOLIC-NEXT:        Type:            I32
+// SYMBOLIC-NEXT:        Mutable:         true
+// SYMBOLIC-NEXT:        InitExpr:
+// SYMBOLIC-NEXT:          Opcode:          I32_CONST
+// SYMBOLIC-NEXT:          Value:           0
+// SYMBOLIC-NEXT:      - Index:           4
+// SYMBOLIC-NEXT:        Type:            I32
+// SYMBOLIC-NEXT:        Mutable:         false
+// SYMBOLIC-NEXT:        InitExpr:
+// SYMBOLIC-NEXT:          Opcode:          I32_CONST
+// SYMBOLIC-NEXT:          Value:           0
+// SYMBOLIC-NEXT:  - Type:            EXPORT
+
+.globl foo
+foo:
+  .functype foo () -> ()
+  end_function
+
+.globl get_foo_address
+get_foo_address:
+  .functype get_foo_address () -> (i32)
+  global.get foo at GOT
+  end_function
+
+.globl get_bar_address
+get_bar_address:
+  .functype get_bar_address () -> (i32)
+  global.get bar at GOT
+  end_function
+
+.globl bar
+.section  .data.bar,"",@
+bar:
+  .int 42
+.size bar, 4

diff  --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index cd6d57333a21..4439098e65ba 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -23,6 +23,7 @@ namespace wasm {
 // Most fields are initialized by the driver.
 struct Configuration {
   bool allowUndefined;
+  bool bsymbolic;
   bool checkFeatures;
   bool compressRelocations;
   bool demangle;

diff  --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index a6d26dcfcc43..c00c7eb7522e 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -327,6 +327,7 @@ static StringRef getEntry(opt::InputArgList &args) {
 // Initializes Config members by the command line options.
 static void readConfigs(opt::InputArgList &args) {
   config->allowUndefined = args.hasArg(OPT_allow_undefined);
+  config->bsymbolic = args.hasArg(OPT_Bsymbolic);
   config->checkFeatures =
       args.hasFlag(OPT_check_features, OPT_no_check_features, true);
   config->compressRelocations = args.hasArg(OPT_compress_relocations);
@@ -490,6 +491,10 @@ static void checkOptions(opt::InputArgList &args) {
       warn("creating PIEs, with -pie, is not yet stable");
     }
   }
+
+  if (config->bsymbolic && !config->shared) {
+    warn("-Bsymbolic is only meaningful when combined with -shared");
+  }
 }
 
 // Force Sym to be entered in the output. Used for -u or equivalent.

diff  --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index 27d54c5cdc64..1a5cf3513fea 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -18,6 +18,8 @@ multiclass B<string name, string help1, string help2> {
 }
 
 // The following flags are shared with the ELF linker
+def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
+
 def color_diagnostics: F<"color-diagnostics">,
   HelpText<"Use colors in diagnostics">;
 

diff  --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 0a364d1a53ac..eec33b670127 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -16,8 +16,17 @@ using namespace llvm::wasm;
 
 namespace lld {
 namespace wasm {
+
 static bool requiresGOTAccess(const Symbol *sym) {
-  return config->isPic && !sym->isHidden() && !sym->isLocal();
+  if (!config->isPic)
+    return false;
+  if (sym->isHidden() || sym->isLocal())
+    return false;
+  // With `-Bsymbolic` (or when building an executable) as don't need to use
+  // the GOT for symbols that are defined within the current module.
+  if (sym->isDefined() && (!config->shared || config->bsymbolic))
+    return false;
+  return true;
 }
 
 static bool allowUndefined(const Symbol* sym) {
@@ -41,17 +50,10 @@ static void reportUndefined(const Symbol* 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)
+  if (requiresGOTAccess(sym))
     out.importSec->addGOTEntry(sym);
   else
-    out.globalSec->addStaticGOTEntry(sym);
+    out.globalSec->addInternalGOTEntry(sym);
 }
 
 void scanRelocations(InputChunk *chunk) {

diff  --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 753482fda410..ca4394e17212 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -268,21 +268,56 @@ void GlobalSection::assignIndexes() {
   uint32_t globalIndex = out.importSec->getNumImportedGlobals();
   for (InputGlobal *g : inputGlobals)
     g->setGlobalIndex(globalIndex++);
-  for (Symbol *sym : staticGotSymbols)
+  for (Symbol *sym : internalGotSymbols)
     sym->setGOTIndex(globalIndex++);
   isSealed = true;
 }
 
-void GlobalSection::addStaticGOTEntry(Symbol *sym) {
+void GlobalSection::addInternalGOTEntry(Symbol *sym) {
   assert(!isSealed);
   if (sym->requiresGOT)
     return;
-  LLVM_DEBUG(dbgs() << "addStaticGOTEntry: " << sym->getName() << " "
+  LLVM_DEBUG(dbgs() << "addInternalGOTEntry: " << sym->getName() << " "
                     << toString(sym->kind()) << "\n");
   sym->requiresGOT = true;
   if (auto *F = dyn_cast<FunctionSymbol>(sym))
     out.elemSec->addEntry(F);
-  staticGotSymbols.push_back(sym);
+  internalGotSymbols.push_back(sym);
+}
+
+void GlobalSection::generateRelocationCode(raw_ostream &os) const {
+  unsigned opcode_ptr_const = config->is64.getValueOr(false)
+                                  ? WASM_OPCODE_I64_CONST
+                                  : WASM_OPCODE_I32_CONST;
+  unsigned opcode_ptr_add = config->is64.getValueOr(false)
+                                ? WASM_OPCODE_I64_ADD
+                                : WASM_OPCODE_I32_ADD;
+
+  for (const Symbol *sym : internalGotSymbols) {
+    if (auto *d = dyn_cast<DefinedData>(sym)) {
+      // Get __memory_base
+      writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+      writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base");
+
+      // Add the virtual address of the data symbol
+      writeU8(os, opcode_ptr_const, "CONST");
+      writeSleb128(os, d->getVirtualAddress(), "offset");
+    } else if (auto *f = dyn_cast<FunctionSymbol>(sym)) {
+      // Get __table_base
+      writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+      writeUleb128(os, WasmSym::tableBase->getGlobalIndex(), "__table_base");
+
+      // Add the table index to __table_base
+      writeU8(os, opcode_ptr_const, "CONST");
+      writeSleb128(os, f->getTableIndex(), "offset");
+    } else {
+      assert(isa<UndefinedData>(sym));
+      continue;
+    }
+    writeU8(os, opcode_ptr_add, "ADD");
+    writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET");
+    writeUleb128(os, sym->getGOTIndex(), "got_entry");
+  }
 }
 
 void GlobalSection::writeBody() {
@@ -292,9 +327,9 @@ void GlobalSection::writeBody() {
   for (InputGlobal *g : inputGlobals)
     writeGlobal(os, g->global);
   // TODO(wvo): when do these need I64_CONST?
-  for (const Symbol *sym : staticGotSymbols) {
+  for (const Symbol *sym : internalGotSymbols) {
     WasmGlobal global;
-    global.Type = {WASM_TYPE_I32, false};
+    global.Type = {WASM_TYPE_I32, config->isPic};
     global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
     if (auto *d = dyn_cast<DefinedData>(sym))
       global.InitExpr.Value.Int32 = d->getVirtualAddress();

diff  --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index 079de93492f1..8e4019440171 100644
--- a/lld/wasm/SyntheticSections.h
+++ b/lld/wasm/SyntheticSections.h
@@ -197,21 +197,35 @@ class GlobalSection : public SyntheticSection {
   uint32_t numGlobals() const {
     assert(isSealed);
     return inputGlobals.size() + dataAddressGlobals.size() +
-           staticGotSymbols.size();
+           internalGotSymbols.size();
   }
   bool isNeeded() const override { return numGlobals() > 0; }
   void assignIndexes() override;
   void writeBody() override;
   void addGlobal(InputGlobal *global);
   void addDataAddressGlobal(DefinedData *global);
-  void addStaticGOTEntry(Symbol *sym);
+
+  // Add an internal GOT entry global that corresponds to the given symbol.
+  // Normally GOT entries are imported and assigned by the external dynamic
+  // linker.  However, when linking PIC code statically or when linking with
+  // -Bsymbolic we can internalize GOT entries by declaring globals the hold
+  // symbol addresses.
+  //
+  // For the static linking case these internal globals can be completely
+  // eliminated by a post-link optimizer such as wasm-opt.
+  //
+  // TODO(sbc): Another approach to optimizing these away could be to use
+  // specific relocation types combined with linker relaxation which could
+  // transform a `global.get` to an `i32.const`.
+  void addInternalGOTEntry(Symbol *sym);
+  void generateRelocationCode(raw_ostream &os) const;
 
   std::vector<const DefinedData *> dataAddressGlobals;
 
 protected:
   bool isSealed = false;
   std::vector<InputGlobal *> inputGlobals;
-  std::vector<Symbol *> staticGotSymbols;
+  std::vector<Symbol *> internalGotSymbols;
 };
 
 class ExportSection : public SyntheticSection {

diff  --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index fee87f292c90..31618314cf52 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -965,9 +965,17 @@ void Writer::createApplyRelocationsFunction() {
   {
     raw_string_ostream os(bodyContent);
     writeUleb128(os, 0, "num locals");
+
+    // First apply relocations to any internalized GOT entries.  These
+    // are the result of relaxation when building with -Bsymbolic.
+    out.globalSec->generateRelocationCode(os);
+
+    // Next apply any realocation to the data section by reading GOT entry
+    // globals.
     for (const OutputSegment *seg : segments)
       for (const InputSegment *inSeg : seg->inputSegments)
         inSeg->generateRelocationCode(os);
+
     writeU8(os, WASM_OPCODE_END, "END");
   }
 


        


More information about the llvm-commits mailing list