diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst --- a/lld/docs/WebAssembly.rst +++ b/lld/docs/WebAssembly.rst @@ -97,6 +97,18 @@ symbols are resolved to zero as in ``ignore-all``. This corresponds the legacy ``--allow-undefined`` flag. + import-dynamic: + + Like import-functions but also import the addresses of unresolved data + symbols. This puts limitations on the type of relocations that are allowed + for imported data symbols. Any 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 is useful for linking binaries are themselves static but are compiled + with either `pic` or `dynamic-no-pic` reocation models, allowing undefined + symbols to be resolved dynamically. + .. option:: --import-memory Import memory from the environment. diff --git a/lld/test/wasm/Inputs/unresolved-symbols-dynamic.s b/lld/test/wasm/Inputs/unresolved-symbols-dynamic.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/Inputs/unresolved-symbols-dynamic.s @@ -0,0 +1,23 @@ +.globl _start +_start: + .functype _start () -> () + call undef + call get_data_addr + call get_func_addr + end_function + +.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 + +.functype undef () -> () diff --git a/lld/test/wasm/undefined-data.ll b/lld/test/wasm/undefined-data.ll --- a/lld/test/wasm/undefined-data.ll +++ b/lld/test/wasm/undefined-data.ll @@ -14,4 +14,4 @@ } ; UNDEF: error: {{.*}}undefined-data.ll.tmp.o: undefined symbol: data_external -; SHARED: error: {{.*}}undefined-data.ll.tmp.o: relocation R_WASM_MEMORY_ADDR_LEB cannot be used against symbol data_external; recompile with -fPIC +; SHARED: error: {{.*}}undefined-data.ll.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.s b/lld/test/wasm/unresolved-symbols.s --- a/lld/test/wasm/unresolved-symbols.s +++ b/lld/test/wasm/unresolved-symbols.s @@ -65,9 +65,37 @@ # IMPORT-NEXT: SigIndex: 0 # IMPORT-NEXT: - Type: FUNCTION +## import-dynamic should fail due to incompatible relocations. +# RUN: not wasm-ld %t1.o -o %t4.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`; recompile with -fPIC + +## import-dynamic should succeed if that input file contains GOT +# relocations for external sybmols. +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/unresolved-symbols-dynamic.s -o %t-dynamic.o +# RUN: wasm-ld %t-dynamic.o -o %t5.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 %t5.wasm | FileCheck -check-prefix=DYNAMIC %s +# DYNAMIC: - Type: IMPORT +# DYNAMIC-NEXT: Imports: +# DYNAMIC-NEXT: - Module: env +# DYNAMIC-NEXT: Field: undef +# DYNAMIC-NEXT: Kind: FUNCTION +# DYNAMIC-NEXT: SigIndex: 0 +# DYNAMIC-NEXT: - Module: GOT.mem +# DYNAMIC-NEXT: Field: undef_data +# DYNAMIC-NEXT: Kind: GLOBAL +# DYNAMIC-NEXT: GlobalType: I32 +# DYNAMIC-NEXT: GlobalMutable: true +# DYNAMIC-NEXT: - Module: GOT.func +# DYNAMIC-NEXT: Field: undef +# DYNAMIC-NEXT: Kind: GLOBAL +# DYNAMIC-NEXT: GlobalType: I32 +# DYNAMIC-NEXT: GlobalMutable: true + ## Do not report undefines if linking relocatable. -# RUN: wasm-ld -r %t1.o -o %t4.wasm --unresolved-symbols=report-all -# RUN: llvm-readobj %t4.wasm > /dev/null 2>&1 +# RUN: wasm-ld -r %t1.o -o %t6.wasm --unresolved-symbols=report-all +# RUN: llvm-readobj %t6.wasm > /dev/null 2>&1 .globl _start _start: diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -21,7 +21,13 @@ // The `ImportFuncs` mode is an additional mode that corresponds to the // --allow-undefined flag which turns undefined functions in imports // as opposed ed to Ignore or Warn which turn them into unreachables. -enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportFuncs }; +enum class UnresolvedPolicy { + ReportError, + Warn, + Ignore, + ImportFuncs, + 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 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -345,6 +345,8 @@ return UnresolvedPolicy::Ignore; if (s == "import-functions") return UnresolvedPolicy::ImportFuncs; + if (s == "import-dynamic") + return UnresolvedPolicy::ImportDynamic; if (s == "report-all") return errorOrWarn; error("unknown --unresolved-symbols value: " + s); @@ -463,12 +465,11 @@ if (config->exportTable) error("-shared/-pie is incompatible with --export-table"); config->importTable = true; + config->unresolvedSymbols = UnresolvedPolicy::ImportDynamic; } - if (config->shared) { + if (config->shared) config->importMemory = true; - config->unresolvedSymbols = UnresolvedPolicy::ImportFuncs; - } } // Some command line options or some combinations of them are not allowed. @@ -516,14 +517,16 @@ // Also, warn about flags which request explicit exports. if (!config->experimentalPic) { // -shared will change meaning when Module Linking is implemented. - if (config->shared) { + if (config->shared) warn("creating shared libraries, with -shared, is not yet stable"); - } // -pie will change meaning when Module Linking is implemented. - if (config->pie) { + 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/Relocations.cpp b/lld/wasm/Relocations.cpp --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -20,7 +20,8 @@ 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; @@ -67,6 +68,7 @@ } break; case UnresolvedPolicy::ImportFuncs: + case UnresolvedPolicy::ImportDynamic: break; } } @@ -123,7 +125,9 @@ 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: @@ -134,8 +138,8 @@ // 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/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -546,7 +546,8 @@ return false; if (config->relocatable || - config->unresolvedSymbols == UnresolvedPolicy::ImportFuncs) + config->unresolvedSymbols == UnresolvedPolicy::ImportFuncs || + config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) return true; if (config->allowUndefinedSymbols.count(sym->getName()) != 0) return true;