[WebAssembly] Add symbol information for shared libraries (#75238)

The current (experimental) spec for WebAssembly shared libraries does
not include a full symbol table like the object format. This change
extracts symbol information from the normal wasm exports.

This is the first step in having the linker report undefined symbols
when linking with shared libraries. The current behaviour is to ignore
all undefined symbols when linking with `-pie` or `-shared`.

See https://github.com/emscripten-core/emscripten/issues/18198
This commit is contained in:
Sam Clegg 2023-12-20 11:13:09 -08:00 committed by GitHub
parent 2c27013fa9
commit 4e8cb01b01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 4 deletions

View file

@ -144,7 +144,6 @@ public:
ArrayRef<wasm::WasmGlobal> globals() const { return Globals; }
ArrayRef<wasm::WasmTag> tags() const { return Tags; }
ArrayRef<wasm::WasmExport> exports() const { return Exports; }
ArrayRef<WasmSymbol> syms() const { return Symbols; }
const wasm::WasmLinkingData &linkingData() const { return LinkingData; }
uint32_t getNumberOfSymbols() const { return Symbols.size(); }
ArrayRef<wasm::WasmElemSegment> elements() const { return ElemSegments; }

View file

@ -599,6 +599,10 @@ Error WasmObjectFile::parseLinkingSection(ReadContext &Ctx) {
Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
uint32_t Count = readVaruint32(Ctx);
// Clear out any symbol information that was derived from the exports
// section.
LinkingData.SymbolTable.clear();
Symbols.clear();
LinkingData.SymbolTable.reserve(Count);
Symbols.reserve(Count);
StringSet<> SymbolNames;
@ -1290,37 +1294,75 @@ Error WasmObjectFile::parseGlobalSection(ReadContext &Ctx) {
Error WasmObjectFile::parseExportSection(ReadContext &Ctx) {
uint32_t Count = readVaruint32(Ctx);
Exports.reserve(Count);
LinkingData.SymbolTable.reserve(Count);
Symbols.reserve(Count);
for (uint32_t I = 0; I < Count; I++) {
wasm::WasmExport Ex;
Ex.Name = readString(Ctx);
Ex.Kind = readUint8(Ctx);
Ex.Index = readVaruint32(Ctx);
const wasm::WasmSignature *Signature = nullptr;
const wasm::WasmGlobalType *GlobalType = nullptr;
const wasm::WasmTableType *TableType = nullptr;
wasm::WasmSymbolInfo Info;
Info.Name = Ex.Name;
Info.Flags = 0;
switch (Ex.Kind) {
case wasm::WASM_EXTERNAL_FUNCTION:
case wasm::WASM_EXTERNAL_FUNCTION: {
if (!isDefinedFunctionIndex(Ex.Index))
return make_error<GenericBinaryError>("invalid function export",
object_error::parse_failed);
getDefinedFunction(Ex.Index).ExportName = Ex.Name;
Info.Kind = wasm::WASM_SYMBOL_TYPE_FUNCTION;
Info.ElementIndex = Ex.Index;
unsigned FuncIndex = Info.ElementIndex - NumImportedFunctions;
wasm::WasmFunction &Function = Functions[FuncIndex];
Signature = &Signatures[Function.SigIndex];
break;
case wasm::WASM_EXTERNAL_GLOBAL:
}
case wasm::WASM_EXTERNAL_GLOBAL: {
if (!isValidGlobalIndex(Ex.Index))
return make_error<GenericBinaryError>("invalid global export",
object_error::parse_failed);
Info.Kind = wasm::WASM_SYMBOL_TYPE_DATA;
uint64_t Offset = 0;
if (isDefinedGlobalIndex(Ex.Index)) {
auto Global = getDefinedGlobal(Ex.Index);
if (!Global.InitExpr.Extended) {
auto Inst = Global.InitExpr.Inst;
if (Inst.Opcode == wasm::WASM_OPCODE_I32_CONST) {
Offset = Inst.Value.Int32;
} else if (Inst.Opcode == wasm::WASM_OPCODE_I64_CONST) {
Offset = Inst.Value.Int64;
}
}
}
Info.DataRef = wasm::WasmDataReference{0, Offset, 0};
break;
}
case wasm::WASM_EXTERNAL_TAG:
if (!isValidTagIndex(Ex.Index))
return make_error<GenericBinaryError>("invalid tag export",
object_error::parse_failed);
Info.Kind = wasm::WASM_SYMBOL_TYPE_TAG;
Info.ElementIndex = Ex.Index;
break;
case wasm::WASM_EXTERNAL_MEMORY:
break;
case wasm::WASM_EXTERNAL_TABLE:
Info.Kind = wasm::WASM_SYMBOL_TYPE_TABLE;
break;
default:
return make_error<GenericBinaryError>("unexpected export kind",
object_error::parse_failed);
}
Exports.push_back(Ex);
if (Ex.Kind != wasm::WASM_EXTERNAL_MEMORY) {
LinkingData.SymbolTable.emplace_back(Info);
Symbols.emplace_back(LinkingData.SymbolTable.back(), GlobalType,
TableType, Signature);
LLVM_DEBUG(dbgs() << "Adding symbol: " << Symbols.back() << "\n");
}
}
if (Ctx.Ptr != Ctx.End)
return make_error<GenericBinaryError>("export section ended prematurely",
@ -1644,6 +1686,8 @@ uint64_t WasmObjectFile::getWasmSymbolValue(const WasmSymbol &Sym) const {
return Segment.Offset.Inst.Value.Int32 + Sym.Info.DataRef.Offset;
} else if (Segment.Offset.Inst.Opcode == wasm::WASM_OPCODE_I64_CONST) {
return Segment.Offset.Inst.Value.Int64 + Sym.Info.DataRef.Offset;
} else if (Segment.Offset.Inst.Opcode == wasm::WASM_OPCODE_GLOBAL_GET) {
return Sym.Info.DataRef.Offset;
} else {
llvm_unreachable("unknown init expr opcode");
}

View file

@ -0,0 +1,69 @@
# RUN: yaml2obj %s -o %t.so
# RUN: llvm-nm %t.so | FileCheck %s
#
# CHECK: 00000001 T my_func_export
# CHECK: 0000002a D my_global_export
--- !WASM
FileHeader:
Version: 0x1
Sections:
- Type: CUSTOM
Name: dylink.0
MemorySize: 15
MemoryAlignment: 0
TableSize: 0
TableAlignment: 0
Needed: []
- Type: TYPE
Signatures:
- Index: 0
ParamTypes: []
ReturnTypes: []
- Type: IMPORT
Imports:
- Module: env
Field: foo
Kind: FUNCTION
SigIndex: 0
- Module: env
Field: bar
Kind: GLOBAL
GlobalType: I32
GlobalMutable: true
- Module: env
Field: memory
Kind: MEMORY
Memory:
Minimum: 0x1
- Type: FUNCTION
FunctionTypes: [ 0 ]
- Type: GLOBAL
Globals:
- Index: 1
Mutable: false
Type: I32
InitExpr:
Opcode: I32_CONST
Value: 42
- Type: EXPORT
Exports:
- Name: my_func_export
Kind: FUNCTION
Index: 1
- Name: my_global_export
Kind: GLOBAL
Index: 1
- Type: CODE
Functions:
- Index: 1
Locals:
Body: 00
- Type: DATA
Segments:
- SectionOffset: 0
InitFlags: 0
Offset:
Opcode: I32_CONST
Value: 0
Content: ''

View file

@ -0,0 +1,70 @@
# RUN: yaml2obj %s -o %t.so
# RUN: llvm-objdump -t %t.so | FileCheck %s
#
# CHECK: SYMBOL TABLE:
# CHECK-NEXT: 00000001 g F CODE my_func_export
# CHECK-NEXT: 0000002a g O DATA my_global_export
--- !WASM
FileHeader:
Version: 0x1
Sections:
- Type: CUSTOM
Name: dylink.0
MemorySize: 15
MemoryAlignment: 0
TableSize: 0
TableAlignment: 0
Needed: []
- Type: TYPE
Signatures:
- Index: 0
ParamTypes: []
ReturnTypes: []
- Type: IMPORT
Imports:
- Module: env
Field: foo
Kind: FUNCTION
SigIndex: 0
- Module: env
Field: bar
Kind: GLOBAL
GlobalType: I32
GlobalMutable: true
- Module: env
Field: memory
Kind: MEMORY
Memory:
Minimum: 0x1
- Type: FUNCTION
FunctionTypes: [ 0 ]
- Type: GLOBAL
Globals:
- Index: 1
Mutable: false
Type: I32
InitExpr:
Opcode: I32_CONST
Value: 42
- Type: EXPORT
Exports:
- Name: my_func_export
Kind: FUNCTION
Index: 1
- Name: my_global_export
Kind: GLOBAL
Index: 1
- Type: CODE
Functions:
- Index: 1
Locals:
Body: 00
- Type: DATA
Segments:
- SectionOffset: 0
InitFlags: 0
Offset:
Opcode: I32_CONST
Value: 0
Content: ''