mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 14:20:17 +00:00
[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
This commit is contained in:
parent
7783de7fe3
commit
86c90f9bfd
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
84
lld/test/wasm/unresolved-symbols-dynamic.s
Normal file
84
lld/test/wasm/unresolved-symbols-dynamic.s
Normal file
@ -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@GOT
|
||||
return
|
||||
end_function
|
||||
|
||||
.globl get_func_addr
|
||||
get_func_addr:
|
||||
.functype get_func_addr () -> (i32)
|
||||
global.get undef@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:...
|
@ -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 () -> ()
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -54,6 +54,12 @@ public:
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DylinkSection::isNeeded() const {
|
||||
return config->isPic ||
|
||||
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic ||
|
||||
!symtab->sharedFiles.empty();
|
||||
}
|
||||
|
||||
void DylinkSection::writeBody() {
|
||||
raw_ostream &os = bodyOutputStream;
|
||||
|
||||
|
@ -75,7 +75,7 @@ protected:
|
||||
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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user