Skip to content

Commit 9b5bbe6

Browse files
committed
[lld][WebAssembly] Report undefined symbols by default -shared/-pie builds
Previously we would ignore/import all undefined symbols when using `-shared` or `-pie`. With this change we now track symbol in shared libraries and report undefined symbols by default. This can be a breaking change for any users of dyanmic linking. However, the old behaviour is still available using `--unresolved-symbols=import-dynamic`. This rationale for allowing this type of breaking change is that `-pie` and `-shared` are both still experimental will warn as such, unless `--experimental-pic` is passed. See emscripten-core/emscripten#18198
1 parent 8928622 commit 9b5bbe6

22 files changed

+264
-48
lines changed

lld/test/wasm/Inputs/ret32.s

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
.hidden ret32
21
.globl ret32
32
ret32:
43
.functype ret32 (f32) -> (i32)

lld/test/wasm/dylink.s

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s
2+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/ret32.s -o %t.ret32.o
3+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/libsearch-dyn.s -o %t.dyn.o
4+
# RUN: wasm-ld --experimental-pic -shared %t.ret32.o %t.dyn.o -o %t.lib.so
5+
# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.o 2>&1 | FileCheck --check-prefix=ERROR %s
6+
# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.lib.so
7+
# RUN: obj2yaml %t.wasm | FileCheck %s
8+
9+
# ERROR: error: {{.*}}: undefined symbol: ret32
10+
.functype ret32 (f32) -> (i32)
11+
12+
.globl _start
13+
_start:
14+
.functype _start () -> ()
15+
f32.const 0.0
16+
call ret32
17+
drop
18+
i32.const _dynamic@GOT
19+
drop
20+
end_function
21+
22+
# CHECK: Sections:
23+
# CHECK-NEXT: - Type: CUSTOM
24+
# CHECK-NEXT: Name: dylink.0
25+
# CHECK-NEXT: MemorySize: 0
26+
# CHECK-NEXT: MemoryAlignment: 0
27+
# CHECK-NEXT: TableSize: 0
28+
# CHECK-NEXT: TableAlignment: 0
29+
# CHECK-NEXT: Needed:
30+
# CHECK-NEXT: - {{.*}}.lib.so

lld/test/wasm/emit-relocs.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ foo:
5454
# CHECK-NEXT: - Index: 1
5555
# CHECK-NEXT: Kind: FUNCTION
5656
# CHECK-NEXT: Name: ret32
57-
# CHECK-NEXT: Flags: [ VISIBILITY_HIDDEN ]
57+
# CHECK-NEXT: Flags: [ ]
5858
# CHECK-NEXT: Function: 1
5959
# CHECK-NEXT: - Index: 2
6060
# CHECK-NEXT: Kind: DATA

lld/test/wasm/pie.s

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s
22
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %S/Inputs/internal_func.s -o %t.internal_func.o
3-
# RUN: wasm-ld --no-gc-sections --experimental-pic -pie -o %t.wasm %t.o %t.internal_func.o
3+
# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --unresolved-symbols=import-dynamic -o %t.wasm %t.o %t.internal_func.o
44
# RUN: obj2yaml %t.wasm | FileCheck %s
55
# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DISASSEM
66

@@ -150,7 +150,7 @@ _start:
150150
# instruction in the InitExpr. We also, therefore, do not need these globals
151151
# to be mutable.
152152

153-
# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --extra-features=extended-const -o %t.extended.wasm %t.o %t.internal_func.o
153+
# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --unresolved-symbols=import-dynamic --extra-features=extended-const -o %t.extended.wasm %t.o %t.internal_func.o
154154
# RUN: obj2yaml %t.extended.wasm | FileCheck %s --check-prefix=EXTENDED-CONST
155155

156156
# EXTENDED-CONST-NOT: __wasm_apply_global_relocs
@@ -207,7 +207,7 @@ _start:
207207
# to be generated along with __wasm_start as the start
208208
# function.
209209

210-
# RUN: wasm-ld --no-gc-sections --shared-memory --experimental-pic -pie -o %t.shmem.wasm %t.o %t.internal_func.o
210+
# RUN: wasm-ld --no-gc-sections --shared-memory --experimental-pic -pie --unresolved-symbols=import-dynamic -o %t.shmem.wasm %t.o %t.internal_func.o
211211
# RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM
212212
# RUN: llvm-objdump --disassemble-symbols=__wasm_start --no-show-raw-insn --no-leading-addr %t.shmem.wasm | FileCheck %s --check-prefix DISASSEM-SHMEM
213213

lld/test/wasm/shared-needed.s

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
11
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
22
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/ret32.s -o %t.ret32.o
33

4-
# RUN: wasm-ld -shared --experimental-pic -o %t1.so %t.o
5-
# RUN: obj2yaml %t1.so | FileCheck %s -check-prefix=SO1
4+
# RUN: wasm-ld -shared --experimental-pic -o %t.ret32.so %t.ret32.o
5+
# RUN: obj2yaml %t.ret32.so | FileCheck %s -check-prefix=SO1
6+
7+
# Without linking against the ret32.so shared object we expect and undefined
8+
# symbol error
9+
10+
# RUN: not wasm-ld -shared --experimental-pic -o %t.so %t.o 2>&1 | FileCheck %s --check-prefix=ERROR
11+
# ERROR: undefined symbol: ret32
12+
13+
# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o %t.ret32.so
14+
# RUN: obj2yaml %t.so | FileCheck %s -check-prefix=SO2
615

7-
# RUN: wasm-ld -shared --experimental-pic -o %t2.so %t1.so %t.ret32.o
8-
# RUN: obj2yaml %t2.so | FileCheck %s -check-prefix=SO2
916

1017
.globl foo
1118
.globl data
1219

20+
.functype ret32 (f32) -> (i32)
21+
1322
foo:
14-
.functype foo () -> ()
23+
.functype foo (f32) -> (i32)
24+
local.get 0
25+
call ret32
1526
end_function
1627

1728
.section .data,"",@
@@ -24,8 +35,8 @@ data:
2435
# SO1: Sections:
2536
# SO1-NEXT: - Type: CUSTOM
2637
# SO1-NEXT: Name: dylink.0
27-
# SO1-NEXT: MemorySize: 4
28-
# SO1-NEXT: MemoryAlignment: 2
38+
# SO1-NEXT: MemorySize: 0
39+
# SO1-NEXT: MemoryAlignment: 0
2940
# SO1-NEXT: TableSize: 0
3041
# SO1-NEXT: TableAlignment: 0
3142
# SO1-NEXT: Needed: []
@@ -34,10 +45,10 @@ data:
3445
# SO2: Sections:
3546
# SO2-NEXT: - Type: CUSTOM
3647
# SO2-NEXT: Name: dylink.0
37-
# SO2-NEXT: MemorySize: 0
38-
# SO2-NEXT: MemoryAlignment: 0
48+
# SO2-NEXT: MemorySize: 4
49+
# SO2-NEXT: MemoryAlignment: 2
3950
# SO2-NEXT: TableSize: 0
4051
# SO2-NEXT: TableAlignment: 0
4152
# SO2-NEXT: Needed:
42-
# SO2-NEXT: - shared-needed.s.tmp1.so
53+
# SO2-NEXT: - shared-needed.s.tmp.ret32.so
4354
# SO2-NEXT: - Type: TYPE

lld/test/wasm/shared.s

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
2-
# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o
2+
# RUN: wasm-ld --experimental-pic --unresolved-symbols=import-dynamic -shared -o %t.wasm %t.o
33
# RUN: obj2yaml %t.wasm | FileCheck %s
44
# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS
55

66
.functype func_external () -> ()
77

88
# Linker-synthesized globals
99
.globaltype __stack_pointer, i32
10-
.globaltype __table_base, i32, immutable
11-
.globaltype __memory_base, i32, immutable
10+
.globaltype __table_base, i32, immutable
11+
.globaltype __memory_base, i32, immutable
1212

1313
.section .data.data,"",@
1414
data:

lld/test/wasm/shared64.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s
2-
# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o
2+
# RUN: wasm-ld -mwasm64 --experimental-pic --unresolved-symbols=import-dynamic -shared -o %t.wasm %t.o
33
# RUN: obj2yaml %t.wasm | FileCheck %s
44
# RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS
55

lld/test/wasm/signature-mismatch.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ ret32_address_main:
8484
# RELOC-NEXT: - Index: 1
8585
# RELOC-NEXT: Kind: FUNCTION
8686
# RELOC-NEXT: Name: ret32
87-
# RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
87+
# RELOC-NEXT: Flags: [ ]
8888
# RELOC-NEXT: Function: 2
8989
# RELOC-NEXT: - Index: 2
9090
# RELOC-NEXT: Kind: DATA

lld/test/wasm/tag-section.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section1.ll -o %t1.o
1212
; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section2.ll -o %t2.o
1313
; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %s -o %t.o
14-
; RUN: wasm-ld --import-undefined --experimental-pic -pie -o %t.wasm %t.o %t1.o %t2.o
14+
; RUN: wasm-ld --import-undefined --experimental-pic --unresolved-symbols=import-dynamic -pie -o %t.wasm %t.o %t1.o %t2.o
1515
; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=PIC
1616

1717
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"

lld/test/wasm/undef-shared.s

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
2+
# RUN: not wasm-ld --experimental-pic %t.o -o /dev/null -shared 2>&1 | FileCheck %s
3+
4+
# CHECK: error: {{.*}}: undefined symbol: hidden
5+
.global hidden
6+
.hidden hidden
7+
8+
.global foo
9+
.section .data,"",@
10+
foo:
11+
.int32 hidden
12+
.size foo,4

lld/test/wasm/undefined-data.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
22
# RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=UNDEF
33
# RUN: wasm-ld --allow-undefined -o %t.wasm %t.o
4-
# RUN: not wasm-ld --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED
4+
# RUN: not wasm-ld --experimental-pic -shared --unresolved-symbols=import-dynamic -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED
55

66
.globl _start
77
_start:

lld/test/wasm/unresolved-symbols.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
.functype get_func_addr () -> (i32)
8181

8282
## import-dynamic should fail due to incompatible relocations.
83-
# RUN: not wasm-ld %t1.o -o %t5.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=ERRNOPIC %s
83+
# RUN: not wasm-ld %t1.o -o %t5.wasm --experimental-pic --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=ERRNOPIC %s
8484
# ERRNOPIC: relocation R_WASM_MEMORY_ADDR_SLEB cannot be used against symbol `undef_data`; recompile with -fPIC
8585
# ERRNOPIC: relocation R_WASM_TABLE_INDEX_SLEB cannot be used against symbol `undef_func`; recompile with -fPIC
8686

lld/wasm/InputFiles.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
178178
case R_WASM_MEMORY_ADDR_TLS_SLEB:
179179
case R_WASM_MEMORY_ADDR_TLS_SLEB64:
180180
case R_WASM_MEMORY_ADDR_LOCREL_I32: {
181-
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
181+
if (isa<UndefinedData>(sym) || sym->isShared() || sym->isUndefWeak())
182182
return 0;
183183
auto D = cast<DefinedData>(sym);
184184
uint64_t value = D->getVA() + reloc.Addend;
@@ -391,21 +391,55 @@ static bool shouldMerge(const WasmSegment &seg) {
391391
return true;
392392
}
393393

394-
void ObjFile::parse(bool ignoreComdats) {
394+
void SharedFile::parse() {
395+
WasmFileBase::parse();
396+
assert(wasmObj->isSharedObject());
397+
398+
for (const SymbolRef &sym : wasmObj->symbols()) {
399+
const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl());
400+
if (wasmSym.isDefined()) {
401+
StringRef name = wasmSym.Info.Name;
402+
uint32_t flags = wasmSym.Info.Flags;
403+
Symbol *s;
404+
LLVM_DEBUG(dbgs() << "shared symbol: " << name << "\n");
405+
switch (wasmSym.Info.Kind) {
406+
case WASM_SYMBOL_TYPE_FUNCTION:
407+
if (name == "__wasm_apply_data_relocs" || name == "__wasm_call_ctors") {
408+
continue;
409+
}
410+
s = symtab->addSharedFunction(name, flags, this, wasmSym.Signature);
411+
break;
412+
case WASM_SYMBOL_TYPE_DATA:
413+
s = symtab->addSharedData(name, flags, this);
414+
break;
415+
default:
416+
continue;
417+
}
418+
symbols.push_back(s);
419+
}
420+
}
421+
}
422+
423+
void WasmFileBase::parse() {
395424
// Parse a memory buffer as a wasm file.
396425
LLVM_DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n");
397426
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), toString(this));
398427

399428
auto *obj = dyn_cast<WasmObjectFile>(bin.get());
400429
if (!obj)
401430
fatal(toString(this) + ": not a wasm file");
402-
if (!obj->isRelocatableObject())
403-
fatal(toString(this) + ": not a relocatable wasm file");
404431

405432
bin.release();
406433
wasmObj.reset(obj);
407434

408435
checkArch(obj->getArch());
436+
}
437+
438+
void ObjFile::parse(bool ignoreComdats) {
439+
WasmFileBase::parse();
440+
441+
if (!wasmObj->isRelocatableObject())
442+
fatal(toString(this) + ": not a relocatable wasm file");
409443

410444
// Build up a map of function indices to table indices for use when
411445
// verifying the existing table index relocations
@@ -548,6 +582,7 @@ bool ObjFile::isExcludedByComdat(const InputChunk *chunk) const {
548582
}
549583

550584
FunctionSymbol *ObjFile::getFunctionSymbol(uint32_t index) const {
585+
assert(isa<FunctionSymbol>(symbols[index]));
551586
return cast<FunctionSymbol>(symbols[index]);
552587
}
553588

lld/wasm/InputFiles.h

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,33 @@ class ArchiveFile : public InputFile {
100100
llvm::DenseSet<uint64_t> seen;
101101
};
102102

103+
class WasmFileBase : public InputFile {
104+
public:
105+
explicit WasmFileBase(Kind k, MemoryBufferRef m) : InputFile(k, m) {}
106+
107+
// Returns the underlying wasm file.
108+
const WasmObjectFile *getWasmObj() const { return wasmObj.get(); }
109+
110+
protected:
111+
void parse();
112+
std::unique_ptr<WasmObjectFile> wasmObj;
113+
};
114+
103115
// .o file (wasm object file)
104-
class ObjFile : public InputFile {
116+
class ObjFile : public WasmFileBase {
105117
public:
106118
explicit ObjFile(MemoryBufferRef m, StringRef archiveName)
107-
: InputFile(ObjectKind, m) {
119+
: WasmFileBase(ObjectKind, m) {
108120
this->archiveName = std::string(archiveName);
109121

110122
// If this isn't part of an archive, it's eagerly linked, so mark it live.
111123
if (archiveName.empty())
112124
markLive();
113125
}
114-
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
115126

116127
void parse(bool ignoreComdats = false);
117128

118-
// Returns the underlying wasm file.
119-
const WasmObjectFile *getWasmObj() const { return wasmObj.get(); }
129+
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
120130

121131
uint32_t calcNewIndex(const WasmRelocation &reloc) const;
122132
uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
@@ -158,14 +168,15 @@ class ObjFile : public InputFile {
158168

159169
bool isExcludedByComdat(const InputChunk *chunk) const;
160170
void addLegacyIndirectFunctionTableIfNeeded(uint32_t tableSymbolCount);
161-
162-
std::unique_ptr<WasmObjectFile> wasmObj;
163171
};
164172

165173
// .so file.
166-
class SharedFile : public InputFile {
174+
class SharedFile : public WasmFileBase {
167175
public:
168-
explicit SharedFile(MemoryBufferRef m) : InputFile(SharedKind, m) {}
176+
explicit SharedFile(MemoryBufferRef m) : WasmFileBase(SharedKind, m) {}
177+
178+
void parse();
179+
169180
static bool classof(const InputFile *f) { return f->kind() == SharedKind; }
170181
};
171182

lld/wasm/MarkLive.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@ void MarkLive::enqueue(Symbol *sym) {
6060

6161
sym->markLive();
6262

63-
// Mark ctor functions in the object that defines this symbol live.
63+
// Mark as live the ctor functions in the object that defines this symbol.
6464
// The ctor functions are all referenced by the synthetic callCtors
6565
// function. However, this function does not contain relocations so we
6666
// have to manually mark the ctors as live.
6767
if (needInitFunctions)
68-
enqueueInitFunctions(cast<ObjFile>(file));
68+
if (auto obj = dyn_cast<ObjFile>(file))
69+
enqueueInitFunctions(obj);
6970

7071
if (InputChunk *chunk = sym->getChunk())
7172
queue.push_back(chunk);

lld/wasm/Relocations.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ using namespace llvm::wasm;
1919
namespace lld::wasm {
2020

2121
static bool requiresGOTAccess(const Symbol *sym) {
22+
if (sym->isShared())
23+
return true;
2224
if (!config->isPic &&
2325
config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic)
2426
return false;
@@ -141,9 +143,12 @@ void scanRelocations(InputChunk *chunk) {
141143
break;
142144
}
143145

144-
if (config->isPic ||
146+
bool shouldImport =
147+
sym->isShared() ||
145148
(sym->isUndefined() &&
146-
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
149+
config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic);
150+
151+
if (shouldImport) {
147152
switch (reloc.Type) {
148153
case R_WASM_TABLE_INDEX_SLEB:
149154
case R_WASM_TABLE_INDEX_SLEB64:
@@ -162,8 +167,8 @@ void scanRelocations(InputChunk *chunk) {
162167
case R_WASM_MEMORY_ADDR_I32:
163168
case R_WASM_MEMORY_ADDR_I64:
164169
// These relocation types are only present in the data section and
165-
// will be converted into code by `generateRelocationCode`. This code
166-
// requires the symbols to have GOT entries.
170+
// will be converted into code by `generateRelocationCode`. This
171+
// code requires the symbols to have GOT entries.
167172
if (requiresGOTAccess(sym))
168173
addGOTEntry(sym);
169174
break;

0 commit comments

Comments
 (0)