Skip to content

[lld][WebAssembly] Report undefined symbols in -shared/-pie builds #75242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lld/test/wasm/Inputs/ret32.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
.hidden ret32
.globl ret32
ret32:
.functype ret32 (f32) -> (i32)
Expand Down
31 changes: 31 additions & 0 deletions lld/test/wasm/dylink.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/ret32.s -o %t.ret32.o
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/libsearch-dyn.s -o %t.dyn.o
# RUN: wasm-ld --experimental-pic -shared %t.ret32.o %t.dyn.o -o %t.lib.so
# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.o 2>&1 | FileCheck --check-prefix=ERROR %s
# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.lib.so
# RUN: obj2yaml %t.wasm | FileCheck %s

# ERROR: error: {{.*}}: undefined symbol: ret32
# ERROR: error: {{.*}}: undefined symbol: _bar
.functype ret32 (f32) -> (i32)

.globl _start
_start:
.functype _start () -> ()
f32.const 0.0
call ret32
drop
i32.const _bar@GOT
drop
end_function

# CHECK: Sections:
# CHECK-NEXT: - Type: CUSTOM
# CHECK-NEXT: Name: dylink.0
# CHECK-NEXT: MemorySize: 0
# CHECK-NEXT: MemoryAlignment: 0
# CHECK-NEXT: TableSize: 0
# CHECK-NEXT: TableAlignment: 0
# CHECK-NEXT: Needed:
# CHECK-NEXT: - {{.*}}.lib.so
2 changes: 1 addition & 1 deletion lld/test/wasm/emit-relocs.s
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ foo:
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: ret32
# CHECK-NEXT: Flags: [ VISIBILITY_HIDDEN ]
# CHECK-NEXT: Flags: [ ]
# CHECK-NEXT: Function: 1
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Kind: DATA
Expand Down
39 changes: 39 additions & 0 deletions lld/test/wasm/no-shlib-sigcheck.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/ret32.s -o %t.ret32.o
# RUN: wasm-ld --experimental-pic -shared %t.ret32.o -o %t.lib.so

## Fails with signature mismatch by default
# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.lib.so 2>&1 | FileCheck --check-prefix=ERROR %s
## Same again with shared library first.
# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.lib.so %t.o 2>&1 | FileCheck --check-prefix=ERROR %s

## Succeeds with --no-shlib-sigcheck added
# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.lib.so --no-shlib-sigcheck
# RUN: obj2yaml %t.wasm | FileCheck %s
## Same again with shared library first.
# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.lib.so %t.o --no-shlib-sigcheck
# RUN: obj2yaml %t.wasm | FileCheck %s

.functype ret32 (f32) -> (i64)

.globl _start
_start:
.functype _start () -> ()
f32.const 0.0
call ret32
drop
end_function

# ERROR: wasm-ld: error: function signature mismatch: ret32
# ERROR: >>> defined as (f32) -> i64 in {{.*}}.o

# CHECK: - Type: TYPE
# CHECK-NEXT: Signatures:
# CHECK-NEXT: - Index: 0
# CHECK-NEXT: ParamTypes:
# CHECK-NEXT: - F32
# CHECK-NEXT: ReturnTypes:
# CHECK-NEXT: - I64
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: ParamTypes: []
# CHECK-NEXT: ReturnTypes: []
6 changes: 3 additions & 3 deletions lld/test/wasm/pie.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %S/Inputs/internal_func.s -o %t.internal_func.o
# RUN: wasm-ld --no-gc-sections --experimental-pic -pie -o %t.wasm %t.o %t.internal_func.o
# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --unresolved-symbols=import-dynamic -o %t.wasm %t.o %t.internal_func.o
# RUN: obj2yaml %t.wasm | FileCheck %s
# 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

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

# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --extra-features=extended-const -o %t.extended.wasm %t.o %t.internal_func.o
# 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
# RUN: obj2yaml %t.extended.wasm | FileCheck %s --check-prefix=EXTENDED-CONST

# EXTENDED-CONST-NOT: __wasm_apply_global_relocs
Expand Down Expand Up @@ -207,7 +207,7 @@ _start:
# to be generated along with __wasm_start as the start
# function.

# RUN: wasm-ld --no-gc-sections --shared-memory --experimental-pic -pie -o %t.shmem.wasm %t.o %t.internal_func.o
# 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
# RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM
# RUN: llvm-objdump --disassemble-symbols=__wasm_start --no-show-raw-insn --no-leading-addr %t.shmem.wasm | FileCheck %s --check-prefix DISASSEM-SHMEM

Expand Down
31 changes: 21 additions & 10 deletions lld/test/wasm/shared-needed.s
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/ret32.s -o %t.ret32.o

# RUN: wasm-ld -shared --experimental-pic -o %t1.so %t.o
# RUN: obj2yaml %t1.so | FileCheck %s -check-prefix=SO1
# RUN: wasm-ld -shared --experimental-pic -o %t.ret32.so %t.ret32.o
# RUN: obj2yaml %t.ret32.so | FileCheck %s -check-prefix=SO1

# Without linking against the ret32.so shared object we expect an undefined
# symbol error

# RUN: not wasm-ld -shared --experimental-pic -o %t.so %t.o 2>&1 | FileCheck %s --check-prefix=ERROR
# ERROR: undefined symbol: ret32

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

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

.globl foo
.globl data

.functype ret32 (f32) -> (i32)

foo:
.functype foo () -> ()
.functype foo (f32) -> (i32)
local.get 0
call ret32
end_function

.section .data,"",@
Expand All @@ -24,8 +35,8 @@ data:
# SO1: Sections:
# SO1-NEXT: - Type: CUSTOM
# SO1-NEXT: Name: dylink.0
# SO1-NEXT: MemorySize: 4
# SO1-NEXT: MemoryAlignment: 2
# SO1-NEXT: MemorySize: 0
# SO1-NEXT: MemoryAlignment: 0
# SO1-NEXT: TableSize: 0
# SO1-NEXT: TableAlignment: 0
# SO1-NEXT: Needed: []
Expand All @@ -34,10 +45,10 @@ data:
# SO2: Sections:
# SO2-NEXT: - Type: CUSTOM
# SO2-NEXT: Name: dylink.0
# SO2-NEXT: MemorySize: 0
# SO2-NEXT: MemoryAlignment: 0
# SO2-NEXT: MemorySize: 4
# SO2-NEXT: MemoryAlignment: 2
# SO2-NEXT: TableSize: 0
# SO2-NEXT: TableAlignment: 0
# SO2-NEXT: Needed:
# SO2-NEXT: - shared-needed.s.tmp1.so
# SO2-NEXT: - shared-needed.s.tmp.ret32.so
# SO2-NEXT: - Type: TYPE
6 changes: 3 additions & 3 deletions lld/test/wasm/shared.s
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o
# RUN: wasm-ld --experimental-pic --unresolved-symbols=import-dynamic -shared -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
# 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

.functype func_external () -> ()

# Linker-synthesized globals
.globaltype __stack_pointer, i32
.globaltype __table_base, i32, immutable
.globaltype __memory_base, i32, immutable
.globaltype __table_base, i32, immutable
.globaltype __memory_base, i32, immutable

.section .data.data,"",@
data:
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/shared64.s
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s
# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o
# RUN: wasm-ld -mwasm64 --experimental-pic --unresolved-symbols=import-dynamic -shared -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
# 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

Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/signature-mismatch.s
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ ret32_address_main:
# RELOC-NEXT: - Index: 1
# RELOC-NEXT: Kind: FUNCTION
# RELOC-NEXT: Name: ret32
# RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
# RELOC-NEXT: Flags: [ ]
# RELOC-NEXT: Function: 2
# RELOC-NEXT: - Index: 2
# RELOC-NEXT: Kind: DATA
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/tag-section.ll
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section1.ll -o %t1.o
; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section2.ll -o %t2.o
; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %s -o %t.o
; RUN: wasm-ld --import-undefined --experimental-pic -pie -o %t.wasm %t.o %t1.o %t2.o
; RUN: wasm-ld --import-undefined --experimental-pic --unresolved-symbols=import-dynamic -pie -o %t.wasm %t.o %t1.o %t2.o
; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=PIC

target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
Expand Down
12 changes: 12 additions & 0 deletions lld/test/wasm/undef-shared.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
# RUN: not wasm-ld --experimental-pic %t.o -o /dev/null -shared 2>&1 | FileCheck %s

# CHECK: error: {{.*}}: undefined symbol: hidden
.global hidden
.hidden hidden

.global foo
.section .data,"",@
foo:
.int32 hidden
.size foo,4
2 changes: 1 addition & 1 deletion lld/test/wasm/undefined-data.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
# RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=UNDEF
# RUN: wasm-ld --allow-undefined -o %t.wasm %t.o
# RUN: not wasm-ld --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED
# RUN: not wasm-ld --experimental-pic -shared --unresolved-symbols=import-dynamic -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED

.globl _start
_start:
Expand Down
2 changes: 1 addition & 1 deletion lld/test/wasm/unresolved-symbols.s
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
# RUN: llvm-readobj %t4.wasm > /dev/null 2>&1

## import-dynamic should fail due to incompatible relocations.
# RUN: not wasm-ld %t/main.o -o %t5.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=ERRNOPIC %s
# RUN: not wasm-ld %t/main.o -o %t5.wasm --experimental-pic --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

Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct Configuration {
bool relocatable;
bool saveTemps;
bool shared;
bool shlibSigCheck;
bool stripAll;
bool stripDebug;
bool stackFirst;
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ static void readConfigs(opt::InputArgList &args) {
config->saveTemps = args.hasArg(OPT_save_temps);
config->searchPaths = args::getStrings(args, OPT_library_path);
config->shared = args.hasArg(OPT_shared);
config->shlibSigCheck = !args.hasArg(OPT_no_shlib_sigcheck);
config->stripAll = args.hasArg(OPT_strip_all);
config->stripDebug = args.hasArg(OPT_strip_debug);
config->stackFirst = args.hasArg(OPT_stack_first);
Expand Down
48 changes: 43 additions & 5 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
case R_WASM_MEMORY_ADDR_TLS_SLEB:
case R_WASM_MEMORY_ADDR_TLS_SLEB64:
case R_WASM_MEMORY_ADDR_LOCREL_I32: {
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
if (isa<UndefinedData>(sym) || sym->isShared() || sym->isUndefWeak())
return 0;
auto D = cast<DefinedData>(sym);
uint64_t value = D->getVA() + reloc.Addend;
Expand Down Expand Up @@ -388,7 +388,8 @@ static bool shouldMerge(const WasmSegment &seg) {
}

void ObjFile::parseLazy() {
LLVM_DEBUG(dbgs() << "ObjFile::parseLazy: " << toString(this) << "\n");
LLVM_DEBUG(dbgs() << "ObjFile::parseLazy: " << toString(this) << " "
<< wasmObj.get() << "\n");
for (const SymbolRef &sym : wasmObj->symbols()) {
const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl());
if (!wasmSym.isDefined())
Expand All @@ -403,21 +404,55 @@ void ObjFile::parseLazy() {
}

ObjFile::ObjFile(MemoryBufferRef m, StringRef archiveName, bool lazy)
: InputFile(ObjectKind, m) {
: WasmFileBase(ObjectKind, m) {
this->lazy = lazy;
this->archiveName = std::string(archiveName);

// If this isn't part of an archive, it's eagerly linked, so mark it live.
if (archiveName.empty())
markLive();
}

void SharedFile::parse() {
assert(wasmObj->isSharedObject());

for (const SymbolRef &sym : wasmObj->symbols()) {
const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl());
if (wasmSym.isDefined()) {
StringRef name = wasmSym.Info.Name;
// Certain shared library exports are known to be DSO-local so we
// don't want to add them to the symbol table.
// TODO(sbc): Instead of hardcoding these here perhaps we could add
// this as extra metadata in the `dylink` section.
if (name == "__wasm_apply_data_relocs" || name == "__wasm_call_ctors" ||
name.starts_with("__start_") || name.starts_with("__stop_"))
continue;
uint32_t flags = wasmSym.Info.Flags;
Symbol *s;
LLVM_DEBUG(dbgs() << "shared symbol: " << name << "\n");
switch (wasmSym.Info.Kind) {
case WASM_SYMBOL_TYPE_FUNCTION:
s = symtab->addSharedFunction(name, flags, this, wasmSym.Signature);
break;
case WASM_SYMBOL_TYPE_DATA:
s = symtab->addSharedData(name, flags, this);
break;
default:
continue;
}
symbols.push_back(s);
}
}
}

WasmFileBase::WasmFileBase(Kind k, MemoryBufferRef m) : InputFile(k, m) {
// Parse a memory buffer as a wasm file.
LLVM_DEBUG(dbgs() << "Reading object: " << toString(this) << "\n");
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), toString(this));

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

bin.release();
wasmObj.reset(obj);
Expand All @@ -429,6 +464,9 @@ void ObjFile::parse(bool ignoreComdats) {
// Parse a memory buffer as a wasm file.
LLVM_DEBUG(dbgs() << "ObjFile::parse: " << toString(this) << "\n");

if (!wasmObj->isRelocatableObject())
fatal(toString(this) + ": not a relocatable wasm file");

// Build up a map of function indices to table indices for use when
// verifying the existing table index relocations
uint32_t totalFunctions =
Expand Down
25 changes: 17 additions & 8 deletions lld/wasm/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,26 @@ class InputFile {
bool live;
};

class WasmFileBase : public InputFile {
public:
explicit WasmFileBase(Kind k, MemoryBufferRef m);

// Returns the underlying wasm file.
const WasmObjectFile *getWasmObj() const { return wasmObj.get(); }

protected:
std::unique_ptr<WasmObjectFile> wasmObj;
};

// .o file (wasm object file)
class ObjFile : public InputFile {
class ObjFile : public WasmFileBase {
public:
ObjFile(MemoryBufferRef m, StringRef archiveName, bool lazy = false);
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }

void parse(bool ignoreComdats = false);
void parseLazy();

// Returns the underlying wasm file.
const WasmObjectFile *getWasmObj() const { return wasmObj.get(); }

uint32_t calcNewIndex(const WasmRelocation &reloc) const;
uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
const InputChunk *chunk) const;
Expand Down Expand Up @@ -139,14 +147,15 @@ class ObjFile : public InputFile {

bool isExcludedByComdat(const InputChunk *chunk) const;
void addLegacyIndirectFunctionTableIfNeeded(uint32_t tableSymbolCount);

std::unique_ptr<WasmObjectFile> wasmObj;
};

// .so file.
class SharedFile : public InputFile {
class SharedFile : public WasmFileBase {
public:
explicit SharedFile(MemoryBufferRef m) : InputFile(SharedKind, m) {}
explicit SharedFile(MemoryBufferRef m) : WasmFileBase(SharedKind, m) {}

void parse();

static bool classof(const InputFile *f) { return f->kind() == SharedKind; }
};

Expand Down
Loading
Loading