[lld] 86c90f9 - [lld][WebAssembly] Add --unresolved-symbols=import-dynamic
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 15 15:13:49 PDT 2022
Author: Sam Clegg
Date: 2022-03-15T15:10:21-07:00
New Revision: 86c90f9bfdc4e9f02533196396a6ae363449b6a3
URL: https://github.com/llvm/llvm-project/commit/86c90f9bfdc4e9f02533196396a6ae363449b6a3
DIFF: https://github.com/llvm/llvm-project/commit/86c90f9bfdc4e9f02533196396a6ae363449b6a3.diff
LOG: [lld][WebAssembly] Add --unresolved-symbols=import-dynamic
This is a new mode for handling unresolved symbols that allows all
symbols to be imported in the same that they would be in the case of
`-fpie` or `-shared`, but generting an otherwise fixed/non-relocatable
binary.
Code linked in this way should still be compiled with `-fPIC` so that
data symbols can be resolved via imports.
This essentially allows the building of static binaries that have
dynamic imports. See:
https://github.com/emscripten-core/emscripten/issues/12682
As with other uses of the experimental dynamic linking ABI, this
behaviour will produce a warning unless run with `--experimental-pic`.
Differential Revision: https://reviews.llvm.org/D91577
Added:
lld/test/wasm/unresolved-symbols-dynamic.s
Modified:
lld/docs/WebAssembly.rst
lld/test/wasm/shared.s
lld/test/wasm/shared64.s
lld/test/wasm/undefined-data.s
lld/test/wasm/unresolved-symbols.s
lld/wasm/Config.h
lld/wasm/Driver.cpp
lld/wasm/InputChunks.cpp
lld/wasm/Relocations.cpp
lld/wasm/SyntheticSections.cpp
lld/wasm/SyntheticSections.h
lld/wasm/Writer.cpp
Removed:
################################################################################
diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst
index ea8917ffc97d1..c40d4b322080a 100644
--- a/lld/docs/WebAssembly.rst
+++ b/lld/docs/WebAssembly.rst
@@ -92,6 +92,21 @@ WebAssembly-specific options:
this is trivial. For direct function calls, the linker will generate a
trapping stub function in place of the undefined function.
+ import-dynamic:
+
+ Undefined symbols generate WebAssembly imports, including undefined data
+ symbols. This is somewhat similar to the --import-undefined option but
+ works all symbol types. This options puts limitations on the type of
+ relocations that are allowed for imported data symbols. Relocations that
+ require absolute data addresses (i.e. All R_WASM_MEMORY_ADDR_I32) will
+ generate an error if they cannot be resolved statically. For clang/llvm
+ this means inputs should be compiled with `-fPIC` (i.e. `pic` or
+ `dynamic-no-pic` relocation models). This options is useful for linking
+ binaries that are themselves static (non-relocatable) but whose undefined
+ symbols are resolved by a dynamic linker. Since the dynamic linking API is
+ experimental, this option currently requires `--experimental-pic` to also
+ be specified.
+
.. option:: --import-memory
Import memory from the environment.
diff --git a/lld/test/wasm/shared.s b/lld/test/wasm/shared.s
index 01dc8d51474d9..086f228308de9 100644
--- a/lld/test/wasm/shared.s
+++ b/lld/test/wasm/shared.s
@@ -214,32 +214,32 @@ get_local_func_address:
# DIS: <__wasm_apply_data_relocs>:
# DIS-EMPTY:
-# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 4
+# DIS-NEXT: global.get 1
# DIS-NEXT: i32.add
# DIS-NEXT: global.get 4
# DIS-NEXT: i32.store 0
-# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 8
+# DIS-NEXT: global.get 1
# DIS-NEXT: i32.add
# DIS-NEXT: global.get 2
# DIS-NEXT: i32.const 1
# DIS-NEXT: i32.add
# DIS-NEXT: i32.store 0
-# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 12
+# DIS-NEXT: global.get 1
# DIS-NEXT: i32.add
# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 0
# DIS-NEXT: i32.add
# DIS-NEXT: i32.store 0
-# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 16
+# DIS-NEXT: global.get 1
# DIS-NEXT: i32.add
# DIS-NEXT: global.get 5
# DIS-NEXT: i32.store 0
-# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 20
+# DIS-NEXT: global.get 1
# DIS-NEXT: i32.add
# DIS-NEXT: global.get 6
# DIS-NEXT: i32.const 4
diff --git a/lld/test/wasm/shared64.s b/lld/test/wasm/shared64.s
index 080b05213a04a..c5a813ea98f34 100644
--- a/lld/test/wasm/shared64.s
+++ b/lld/test/wasm/shared64.s
@@ -221,32 +221,32 @@ get_local_func_address:
# DIS: <__wasm_apply_data_relocs>:
# DIS-EMPTY:
-# DIS-NEXT: global.get 1
# DIS-NEXT: i64.const 4
+# DIS-NEXT: global.get 1
# DIS-NEXT: i64.add
# DIS-NEXT: global.get 5
# DIS-NEXT: i64.store 0:p2align=2
-# DIS-NEXT: global.get 1
# DIS-NEXT: i64.const 12
+# DIS-NEXT: global.get 1
# DIS-NEXT: i64.add
# DIS-NEXT: global.get 2
# DIS-NEXT: i64.const 1
# DIS-NEXT: i64.add
# DIS-NEXT: i64.store 0:p2align=2
-# DIS-NEXT: global.get 1
# DIS-NEXT: i64.const 20
+# DIS-NEXT: global.get 1
# DIS-NEXT: i64.add
# DIS-NEXT: global.get 1
# DIS-NEXT: i32.const 0
# DIS-NEXT: i32.add
# DIS-NEXT: i32.store 0
-# DIS-NEXT: global.get 1
# DIS-NEXT: i64.const 24
+# DIS-NEXT: global.get 1
# DIS-NEXT: i64.add
# DIS-NEXT: global.get 6
# DIS-NEXT: i64.store 0:p2align=2
-# DIS-NEXT: global.get 1
# DIS-NEXT: i64.const 32
+# DIS-NEXT: global.get 1
# DIS-NEXT: i64.add
# DIS-NEXT: global.get 7
# DIS-NEXT: i32.const 4
diff --git a/lld/test/wasm/undefined-data.s b/lld/test/wasm/undefined-data.s
index edb8917bc125e..d63b667c4ea38 100644
--- a/lld/test/wasm/undefined-data.s
+++ b/lld/test/wasm/undefined-data.s
@@ -13,4 +13,4 @@ _start:
.size data_external, 4
# UNDEF: error: {{.*}}undefined-data.s.tmp.o: undefined symbol: data_external
-# SHARED: error: {{.*}}undefined-data.s.tmp.o: relocation R_WASM_MEMORY_ADDR_LEB cannot be used against symbol data_external; recompile with -fPIC
+# SHARED: error: {{.*}}undefined-data.s.tmp.o: relocation R_WASM_MEMORY_ADDR_LEB cannot be used against symbol `data_external`; recompile with -fPIC
diff --git a/lld/test/wasm/unresolved-symbols-dynamic.s b/lld/test/wasm/unresolved-symbols-dynamic.s
new file mode 100644
index 0000000000000..c3d4a753acf60
--- /dev/null
+++ b/lld/test/wasm/unresolved-symbols-dynamic.s
@@ -0,0 +1,84 @@
+# Unresolve data symbols are allowing under import-dynamic when GOT
+# relocations are used
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t-dynamic.o
+# RUN: wasm-ld %t-dynamic.o -o %t.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=WARN %s
+# WARN: wasm-ld: warning: dynamic imports are not yet stable (--unresolved-symbols=import-dynamic)
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+.functype undef () -> ()
+
+.globl get_data_addr
+get_data_addr:
+ .functype get_data_addr () -> (i32)
+ global.get undef_data at GOT
+ return
+ end_function
+
+.globl get_func_addr
+get_func_addr:
+ .functype get_func_addr () -> (i32)
+ global.get undef at GOT
+ return
+ end_function
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ call undef
+ call get_func_addr
+ drop
+ call get_data_addr
+ i32.load data_ptr
+ drop
+ end_function
+
+.section .data.data_ptr,"",@
+data_ptr:
+ .int32 data_external+42
+ .size data_ptr, 4
+
+.size data_external, 4
+
+# CHECK: - Type: IMPORT
+# CHECK-NEXT: Imports:
+# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: undef
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: SigIndex: 0
+# CHECK-NEXT: - Module: GOT.mem
+# CHECK-NEXT: Field: undef_data
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I32
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Module: GOT.func
+# CHECK-NEXT: Field: undef
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I32
+# CHECK-NEXT: GlobalMutable: true
+
+# CHECK: - Type: CUSTOM
+# CHECK-NEXT: Name: name
+# CHECK-NEXT: FunctionNames:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Name: undef
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Name: __wasm_apply_data_relocs
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Name: get_data_addr
+# CHECK-NEXT: - Index: 3
+# CHECK-NEXT: Name: get_func_addr
+# CHECK-NEXT: - Index: 4
+# CHECK-NEXT: Name: _start
+# CHECK-NEXT: GlobalNames:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Name: undef_data
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Name: undef
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Name: data_external
+# CHECK-NEXT: - Index: 3
+# CHECK-NEXT: Name: __stack_pointer
+# CHECK-NEXT: DataSegmentNames:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Name: .data
+# CHECK-NEXT:...
diff --git a/lld/test/wasm/unresolved-symbols.s b/lld/test/wasm/unresolved-symbols.s
index 81c94a32f3b2c..5de54d76c6de8 100644
--- a/lld/test/wasm/unresolved-symbols.s
+++ b/lld/test/wasm/unresolved-symbols.s
@@ -79,6 +79,11 @@
.functype get_data_addr () -> (i32)
.functype get_func_addr () -> (i32)
+## import-dynamic should fail due to incompatible relocations.
+# RUN: not wasm-ld %t1.o -o %t5.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=ERRNOPIC %s
+# ERRNOPIC: relocation R_WASM_MEMORY_ADDR_SLEB cannot be used against symbol `undef_data`; recompile with -fPIC
+# ERRNOPIC: relocation R_WASM_TABLE_INDEX_SLEB cannot be used against symbol `undef_func`; recompile with -fPIC
+
.globl _start
_start:
.functype _start () -> ()
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index b4172ff1737cb..2cde65144ce66 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -18,7 +18,7 @@ namespace lld {
namespace wasm {
// For --unresolved-symbols.
-enum class UnresolvedPolicy { ReportError, Warn, Ignore };
+enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic };
// This struct contains the global configuration for the linker.
// Most fields are direct mapping from the command line options
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 8ef86206a9b15..6d84b604796f0 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -335,6 +335,8 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
StringRef s = arg->getValue();
if (s == "ignore-all")
return UnresolvedPolicy::Ignore;
+ if (s == "import-dynamic")
+ return UnresolvedPolicy::ImportDynamic;
if (s == "report-all")
return errorOrWarn;
error("unknown --unresolved-symbols value: " + s);
@@ -528,6 +530,11 @@ static void checkOptions(opt::InputArgList &args) {
if (config->pie) {
warn("creating PIEs, with -pie, is not yet stable");
}
+
+ if (config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) {
+ warn("dynamic imports are not yet stable "
+ "(--unresolved-symbols=import-dynamic)");
+ }
}
if (config->bsymbolic && !config->shared) {
diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp
index 242378504c3b8..332382c907b7c 100644
--- a/lld/wasm/InputChunks.cpp
+++ b/lld/wasm/InputChunks.cpp
@@ -376,19 +376,26 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
for (const WasmRelocation &rel : relocations) {
uint64_t offset = getVA(rel.Offset) - getInputSectionOffset();
+ Symbol *sym = file->getSymbol(rel);
+ if (!config->isPic && sym->isDefined())
+ continue;
+
LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
<< " addend=" << rel.Addend << " index=" << rel.Index
<< " output offset=" << offset << "\n");
- // Get __memory_base
- writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
- writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
-
- // Add the offset of the relocation
+ // Calculate the address at which to apply the relocations
writeU8(os, opcode_ptr_const, "CONST");
writeSleb128(os, offset, "offset");
- writeU8(os, opcode_ptr_add, "ADD");
+ // In PIC mode we need to add the __memory_base
+ if (config->isPic) {
+ writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+ writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
+ writeU8(os, opcode_ptr_add, "ADD");
+ }
+
+ // Now figure out what we want to store at this location
bool is64 = relocIs64(rel.Type);
unsigned opcode_reloc_const =
is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST;
@@ -397,8 +404,6 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
unsigned opcode_reloc_store =
is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE;
- Symbol *sym = file->getSymbol(rel);
- // Now figure out what we want to store
if (sym->hasGOTIndex()) {
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, sym->getGOTIndex(), "global index");
@@ -408,6 +413,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
writeU8(os, opcode_reloc_add, "ADD");
}
} else {
+ assert(config->isPic);
const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
if (rel.Type == R_WASM_TABLE_INDEX_I32 ||
rel.Type == R_WASM_TABLE_INDEX_I64)
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 0109b6743f5c6..c7710a9baf320 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -20,7 +20,8 @@ namespace lld {
namespace wasm {
static bool requiresGOTAccess(const Symbol *sym) {
- if (!config->isPic)
+ if (!config->isPic &&
+ config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic)
return false;
if (sym->isHidden() || sym->isLocal())
return false;
@@ -66,6 +67,8 @@ static void reportUndefined(Symbol *sym) {
}
}
break;
+ case UnresolvedPolicy::ImportDynamic:
+ break;
}
}
}
@@ -139,7 +142,9 @@ void scanRelocations(InputChunk *chunk) {
break;
}
- if (config->isPic) {
+ if (config->isPic ||
+ (sym->isUndefined() &&
+ config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
switch (reloc.Type) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_SLEB64:
@@ -150,8 +155,8 @@ void scanRelocations(InputChunk *chunk) {
// Certain relocation types can't be used when building PIC output,
// since they would require absolute symbol addresses at link time.
error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
- " cannot be used against symbol " + toString(*sym) +
- "; recompile with -fPIC");
+ " cannot be used against symbol `" + toString(*sym) +
+ "`; recompile with -fPIC");
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_TABLE_INDEX_I64:
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index a0a8d597118d3..c47c58ce1961b 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -54,6 +54,12 @@ class SubSection {
} // namespace
+bool DylinkSection::isNeeded() const {
+ return config->isPic ||
+ config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic ||
+ !symtab->sharedFiles.empty();
+}
+
void DylinkSection::writeBody() {
raw_ostream &os = bodyOutputStream;
diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index 9a425ea4f6ec3..cb3b1888f1fa8 100644
--- a/lld/wasm/SyntheticSections.h
+++ b/lld/wasm/SyntheticSections.h
@@ -75,7 +75,7 @@ class SyntheticSection : public OutputSection {
class DylinkSection : public SyntheticSection {
public:
DylinkSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "dylink.0") {}
- bool isNeeded() const override { return config->isPic; }
+ bool isNeeded() const override;
void writeBody() override;
uint32_t memAlign = 0;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 621e48e7f273a..4465bb2e37b3d 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -597,7 +597,8 @@ static bool shouldImport(Symbol *sym) {
return false;
}
- if (config->isPic || config->relocatable || config->importUndefined)
+ if (config->isPic || config->relocatable || config->importUndefined ||
+ config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)
return true;
if (config->allowUndefinedSymbols.count(sym->getName()) != 0)
return true;
@@ -1004,21 +1005,22 @@ void Writer::createSyntheticInitFunctions() {
WasmSym::applyGlobalTLSRelocs->markLive();
}
- if (config->isPic) {
- // For PIC code we create synthetic functions that apply relocations.
- // These get called from __wasm_call_ctors before the user-level
- // constructors.
+ if (config->isPic ||
+ config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) {
+ // For PIC code, or when dynamically importing addresses, we create
+ // synthetic functions that apply relocations. These get called from
+ // __wasm_call_ctors before the user-level constructors.
WasmSym::applyDataRelocs = symtab->addSyntheticFunction(
"__wasm_apply_data_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_apply_data_relocs"));
WasmSym::applyDataRelocs->markLive();
+ }
- if (out.globalSec->needsRelocations()) {
- WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction(
- "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
- make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
- WasmSym::applyGlobalRelocs->markLive();
- }
+ if (config->isPic && out.globalSec->needsRelocations()) {
+ WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction(
+ "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+ make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
+ WasmSym::applyGlobalRelocs->markLive();
}
int startCount = 0;
More information about the llvm-commits
mailing list