wasm linker: support export section as implicit symbols

This commit is contained in:
Andrew Kelley 2024-12-11 16:41:11 -08:00
parent 031c84c8cb
commit 26c38b2d42
2 changed files with 53 additions and 13 deletions

View File

@ -894,6 +894,15 @@ pub const ObjectGlobalIndex = enum(u32) {
_,
};
/// Index into `Wasm.object_memories`.
pub const ObjectMemoryIndex = enum(u32) {
_,
pub fn ptr(index: ObjectMemoryIndex, wasm: *const Wasm) *std.wasm.Memory {
return &wasm.object_memories.items[@intFromEnum(index)];
}
};
/// Index into `object_functions`.
pub const ObjectFunctionIndex = enum(u32) {
_,
@ -2609,7 +2618,10 @@ pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr {
pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error!DataSegment.Payload {
const gpa = wasm.base.comp.gpa;
try wasm.string_bytes.appendSlice(gpa, bytes);
return @enumFromInt(wasm.string_bytes.items.len - bytes.len);
return .{
.off = @intCast(wasm.string_bytes.items.len - bytes.len),
.len = @intCast(bytes.len),
};
}
pub fn uavSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex {

View File

@ -122,6 +122,19 @@ pub const ScratchSpace = struct {
func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,
symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,
segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,
exports: std.ArrayListUnmanaged(Export) = .empty,
const Export = struct {
name: Wasm.String,
pointee: Pointee,
const Pointee = union(std.wasm.ExternalKind) {
function: Wasm.ObjectFunctionIndex,
table: Wasm.ObjectTableIndex,
memory: Wasm.ObjectMemoryIndex,
global: Wasm.ObjectGlobalIndex,
};
};
/// Index into `func_imports`.
const FuncImportIndex = enum(u32) {
@ -142,6 +155,7 @@ pub const ScratchSpace = struct {
};
pub fn deinit(ss: *ScratchSpace, gpa: Allocator) void {
ss.exports.deinit(gpa);
ss.func_types.deinit(gpa);
ss.func_type_indexes.deinit(gpa);
ss.func_imports.deinit(gpa);
@ -151,6 +165,7 @@ pub const ScratchSpace = struct {
}
fn clear(ss: *ScratchSpace) void {
ss.exports.clearRetainingCapacity();
ss.func_types.clearRetainingCapacity();
ss.func_type_indexes.clearRetainingCapacity();
ss.func_imports.clearRetainingCapacity();
@ -622,24 +637,22 @@ pub fn parse(
},
.@"export" => {
const exports_len, pos = readLeb(u32, bytes, pos);
// TODO: instead, read into scratch space, and then later
// add this data as if it were extra symbol table entries,
// but allow merging with existing symbol table data if the name matches.
for (try wasm.object_exports.addManyAsSlice(gpa, exports_len)) |*exp| {
// Read into scratch space, and then later add this data as if
// it were extra symbol table entries, but allow merging with
// existing symbol table data if the name matches.
for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| {
const name, pos = readBytes(bytes, pos);
const kind: std.wasm.ExternalKind = @enumFromInt(bytes[pos]);
pos += 1;
const index, pos = readLeb(u32, bytes, pos);
const rebased_index = index + switch (kind) {
.function => functions_start,
.table => tables_start,
.memory => memories_start,
.global => globals_start,
};
exp.* = .{
.name = try wasm.internString(name),
.kind = kind,
.index = rebased_index,
.pointee = switch (kind) {
.function => .{ .function = @enumFromInt(functions_start + index) },
.table => .{ .table = @enumFromInt(tables_start + index) },
.memory => .{ .memory = @enumFromInt(memories_start + index) },
.global => .{ .global = @enumFromInt(globals_start + index) },
},
};
}
},
@ -828,6 +841,21 @@ pub fn parse(
},
};
// Apply export section info. This is done after the symbol table above so
// that the symbol table can take precedence, overriding the export name.
for (ss.exports.items) |*exp| {
switch (exp.pointee) {
inline .function, .table, .memory, .global => |index| {
const ptr = index.ptr(wasm);
if (ptr.name == .none) {
// Missng symbol table entry; use defaults for exported things.
ptr.name = exp.name.toOptional();
ptr.flags.exported = true;
}
},
}
}
// Apply segment_info.
for (wasm.object_data_segments.items[data_segment_start..], ss.segment_info.items) |*data, info| {
data.name = info.name.toOptional();