wasm linker: chase relocations for references

This commit is contained in:
Andrew Kelley 2024-12-29 20:21:26 -08:00
parent c5822879a1
commit 7d224516c4
5 changed files with 605 additions and 357 deletions

View File

@ -108,7 +108,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() },
.tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
.tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
.addend = 0,
});
code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
@ -162,7 +162,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) },
.tag = .FUNCTION_INDEX_LEB,
.tag = .function_index_leb,
.addend = 0,
});
code.appendNTimesAssumeCapacity(0, 5);
@ -182,7 +182,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .type_index = func_ty_index },
.tag = .TYPE_INDEX_LEB,
.tag = .type_index_leb,
.addend = 0,
});
code.appendNTimesAssumeCapacity(0, 5);
@ -202,7 +202,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) },
.tag = .FUNCTION_INDEX_LEB,
.tag = .function_index_leb,
.addend = 0,
});
code.appendNTimesAssumeCapacity(0, 5);
@ -226,7 +226,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) },
.tag = .FUNCTION_INDEX_LEB,
.tag = .function_index_leb,
.addend = 0,
});
code.appendNTimesAssumeCapacity(0, 5);
@ -245,7 +245,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() },
.tag = .GLOBAL_INDEX_LEB,
.tag = .global_index_leb,
.addend = 0,
});
code.appendNTimesAssumeCapacity(0, 5);
@ -922,7 +922,7 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) },
.tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
.tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
.addend = data.offset,
});
code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
@ -957,7 +957,7 @@ fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) },
.tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
.tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
.addend = data.offset,
});
code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);

View File

@ -670,7 +670,7 @@ fn lowerUavRef(
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) },
.tag = if (ptr_width_bytes == 4) .MEMORY_ADDR_I32 else .MEMORY_ADDR_I64,
.tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
.addend = @intCast(offset),
});
} else {
@ -742,7 +742,7 @@ fn lowerNavRef(
try wasm.out_relocs.append(gpa, .{
.offset = @intCast(code.items.len),
.pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
.tag = if (ptr_width_bytes == 4) .MEMORY_ADDR_I32 else .MEMORY_ADDR_I64,
.tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
.addend = @intCast(offset),
});
} else {

View File

@ -93,13 +93,13 @@ func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty,
/// Local functions may be unnamed.
object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty,
/// All functions for all objects.
object_functions: std.ArrayListUnmanaged(Function) = .empty,
object_functions: std.ArrayListUnmanaged(ObjectFunction) = .empty,
/// Provides a mapping of both imports and provided globals to symbol name.
/// Local globals may be unnamed.
object_global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImport) = .empty,
/// All globals for all objects.
object_globals: std.ArrayListUnmanaged(Global) = .empty,
object_globals: std.ArrayListUnmanaged(ObjectGlobal) = .empty,
/// All table imports for all objects.
object_table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport) = .empty,
@ -386,7 +386,7 @@ pub const GlobalIndex = enum(u32) {
pub const stack_pointer: GlobalIndex = @enumFromInt(0);
/// Same as `stack_pointer` but with a safety assertion.
pub fn stackPointer(wasm: *const Wasm) Global.Index {
pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index {
const comp = wasm.base.comp;
assert(comp.config.output_mode != .Obj);
assert(comp.zcu != null);
@ -513,26 +513,19 @@ pub const SymbolFlags = packed struct(u32) {
// Above here matches the tooling conventions ABI.
padding1: u5 = 0,
padding1: u13 = 0,
/// Zig-specific. Dead things are allowed to be garbage collected.
alive: bool = false,
/// Zig-specific. Segments only. Signals that the segment contains only
/// null terminated strings allowing the linker to perform merging.
strings: bool = false,
/// Zig-specific. This symbol comes from an object that must be included in
/// the final link.
must_link: bool = false,
/// Zig-specific. Data segments only.
is_passive: bool = false,
/// Zig-specific. Data segments only.
alignment: Alignment = .none,
/// Zig-specific. Globals only.
/// Zig-specific.
global_type: GlobalType4 = .zero,
/// Zig-specific. Tables only.
/// Zig-specific.
limits_has_max: bool = false,
/// Zig-specific. Tables only.
/// Zig-specific.
limits_is_shared: bool = false,
/// Zig-specific. Tables only.
/// Zig-specific.
ref_type: RefType1 = .funcref,
pub const Binding = enum(u2) {
@ -554,10 +547,7 @@ pub const SymbolFlags = packed struct(u32) {
pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void {
flags.no_strip = no_strip;
flags.alive = false;
flags.strings = false;
flags.must_link = must_link;
flags.is_passive = false;
flags.alignment = .none;
flags.global_type = .zero;
flags.limits_has_max = false;
flags.limits_is_shared = false;
@ -603,7 +593,7 @@ pub const GlobalType4 = packed struct(u4) {
pub const zero: GlobalType4 = @bitCast(@as(u4, 0));
pub fn to(gt: GlobalType4) Global.Type {
pub fn to(gt: GlobalType4) ObjectGlobal.Type {
return .{
.valtype = gt.valtype.to(),
.mutable = gt.mutable,
@ -1001,18 +991,24 @@ pub const FunctionImport = extern struct {
};
};
pub const Function = extern struct {
pub const ObjectFunction = extern struct {
flags: SymbolFlags,
/// `none` if this function has no symbol describing it.
name: OptionalString,
type_index: FunctionType.Index,
code: Code,
/// The offset within the section where the data starts.
/// The offset within the code section where the data starts.
offset: u32,
section_index: ObjectSectionIndex,
source_location: SourceLocation,
/// The object file whose code section contains this function.
object_index: ObjectIndex,
pub const Code = DataPayload;
fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice {
const code_section_index = of.object_index.ptr(wasm).code_section_index.?;
const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty;
return .init(relocs, of.offset, of.code.len, wasm);
}
};
pub const GlobalImport = extern struct {
@ -1101,6 +1097,10 @@ pub const GlobalImport = extern struct {
});
}
fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution {
return pack(wasm, .{ .object_global = object_global });
}
pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
return switch (unpack(r, wasm)) {
.unresolved => unreachable,
@ -1137,22 +1137,32 @@ pub const GlobalImport = extern struct {
return index.value(wasm).module_name;
}
pub fn globalType(index: Index, wasm: *const Wasm) Global.Type {
pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type {
return value(index, wasm).flags.global_type.to();
}
};
};
pub const Global = extern struct {
pub const ObjectGlobal = extern struct {
/// `none` if this function has no symbol describing it.
name: OptionalString,
flags: SymbolFlags,
expr: Expr,
/// The object file whose global section contains this global.
object_index: ObjectIndex,
offset: u32,
size: u32,
pub const Type = struct {
valtype: std.wasm.Valtype,
mutable: bool,
};
fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice {
const global_section_index = og.object_index.ptr(wasm).global_section_index.?;
const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty;
return .init(relocs, og.offset, og.size, wasm);
}
};
pub const RefType1 = enum(u1) {
@ -1205,6 +1215,18 @@ pub const TableImport = extern struct {
};
}
fn pack(unpacked: Unpacked) Resolution {
return switch (unpacked) {
.unresolved => .unresolved,
.__indirect_function_table => .__indirect_function_table,
.object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)),
};
}
fn fromObjectTable(object_table: ObjectTableIndex) Resolution {
return pack(.{ .object_table = object_table });
}
pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType {
return switch (unpack(r)) {
.unresolved => unreachable,
@ -1298,7 +1320,7 @@ pub const ObjectTableIndex = enum(u32) {
pub const ObjectGlobalIndex = enum(u32) {
_,
pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal {
return &wasm.object_globals.items[@intFromEnum(index)];
}
@ -1338,7 +1360,7 @@ pub const ObjectMemory = extern struct {
pub const ObjectFunctionIndex = enum(u32) {
_,
pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *Function {
pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction {
return &wasm.object_functions.items[@intFromEnum(index)];
}
@ -1363,8 +1385,28 @@ pub const OptionalObjectFunctionIndex = enum(u32) {
pub const ObjectDataSegment = extern struct {
/// `none` if segment info custom subsection is missing.
name: OptionalString,
flags: SymbolFlags,
flags: Flags,
payload: DataPayload,
offset: u32,
object_index: ObjectIndex,
pub const Flags = packed struct(u32) {
alive: bool = false,
is_passive: bool = false,
alignment: Alignment = .none,
/// Signals that the segment contains only null terminated strings allowing
/// the linker to perform merging.
strings: bool = false,
/// The segment contains thread-local data. This means that a unique copy
/// of this segment will be created for each thread.
tls: bool = false,
/// If the object file is included in the final link, the segment should be
/// retained in the final output regardless of whether it is used by the
/// program.
retain: bool = false,
_: u21 = 0,
};
/// Index into `Wasm.object_data_segments`.
pub const Index = enum(u32) {
@ -1374,6 +1416,12 @@ pub const ObjectDataSegment = extern struct {
return &wasm.object_data_segments.items[@intFromEnum(i)];
}
};
fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice {
const data_section_index = ods.object_index.ptr(wasm).data_section_index.?;
const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty;
return .init(relocs, ods.offset, ods.payload.len, wasm);
}
};
/// A local or exported global const from an object file.
@ -1383,8 +1431,7 @@ pub const ObjectData = extern struct {
offset: u32,
/// May be zero. `offset + size` must be <= the segment's size.
size: u32,
/// `none` if no symbol describes it.
name: OptionalString,
name: String,
flags: SymbolFlags,
/// Index into `Wasm.object_datas`.
@ -1845,7 +1892,7 @@ pub const ZcuImportIndex = enum(u32) {
return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
}
pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) Global.Type {
pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type {
_ = index;
_ = wasm;
unreachable; // Zig has no way to create Wasm globals yet.
@ -1997,7 +2044,7 @@ pub const GlobalImportId = enum(u32) {
};
}
pub fn globalType(id: GlobalImportId, wasm: *Wasm) Global.Type {
pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type {
return switch (unpack(id, wasm)) {
inline .object_global_import, .zcu_import => |i| i.globalType(wasm),
};
@ -2014,7 +2061,7 @@ pub const SymbolTableIndex = enum(u32) {
};
pub const OutReloc = struct {
tag: ObjectRelocation.Tag,
tag: Object.RelocationType,
offset: u32,
pointee: Pointee,
addend: i32,
@ -2041,15 +2088,144 @@ pub const ObjectRelocation = struct {
/// When `offset` is zero, its position is immediately after the id and size of the section.
offset: u32,
pointee: Pointee,
/// Populated only for `MEMORY_ADDR_*`, `FUNCTION_OFFSET_I32` and `SECTION_OFFSET_I32`.
/// Populated only for `memory_addr_*`, `function_offset_i32` and `section_offset_i32`.
addend: i32,
pub const Tag = enum(u8) {
// These use `Pointee.function`.
function_index_i32,
function_index_leb,
function_offset_i32,
function_offset_i64,
table_index_i32,
table_index_i64,
table_index_rel_sleb,
table_index_rel_sleb64,
table_index_sleb,
table_index_sleb64,
// These use `Pointee.symbol_name`.
function_import_index_i32,
function_import_index_leb,
function_import_offset_i32,
function_import_offset_i64,
table_import_index_i32,
table_import_index_i64,
table_import_index_rel_sleb,
table_import_index_rel_sleb64,
table_import_index_sleb,
table_import_index_sleb64,
// These use `Pointee.global`.
global_index_i32,
global_index_leb,
// These use `Pointee.symbol_name`.
global_import_index_i32,
global_import_index_leb,
// These use `Pointee.data`.
memory_addr_i32,
memory_addr_i64,
memory_addr_leb,
memory_addr_leb64,
memory_addr_locrel_i32,
memory_addr_rel_sleb,
memory_addr_rel_sleb64,
memory_addr_sleb,
memory_addr_sleb64,
memory_addr_tls_sleb,
memory_addr_tls_sleb64,
// These use `Pointee.symbol_name`.
memory_addr_import_i32,
memory_addr_import_i64,
memory_addr_import_leb,
memory_addr_import_leb64,
memory_addr_import_locrel_i32,
memory_addr_import_rel_sleb,
memory_addr_import_rel_sleb64,
memory_addr_import_sleb,
memory_addr_import_sleb64,
memory_addr_import_tls_sleb,
memory_addr_import_tls_sleb64,
/// Uses `Pointee.section`.
section_offset_i32,
/// Uses `Pointee.table`.
table_number_leb,
/// Uses `Pointee.symbol_name`.
table_import_number_leb,
/// Uses `Pointee.type_index`.
type_index_leb,
pub fn fromType(t: Object.RelocationType) Tag {
return switch (t) {
.event_index_leb => unreachable,
.function_index_i32 => .function_index_i32,
.function_index_leb => .function_index_leb,
.function_offset_i32 => .function_offset_i32,
.function_offset_i64 => .function_offset_i64,
.global_index_i32 => .global_index_i32,
.global_index_leb => .global_index_leb,
.memory_addr_i32 => .memory_addr_i32,
.memory_addr_i64 => .memory_addr_i64,
.memory_addr_leb => .memory_addr_leb,
.memory_addr_leb64 => .memory_addr_leb64,
.memory_addr_locrel_i32 => .memory_addr_locrel_i32,
.memory_addr_rel_sleb => .memory_addr_rel_sleb,
.memory_addr_rel_sleb64 => .memory_addr_rel_sleb64,
.memory_addr_sleb => .memory_addr_sleb,
.memory_addr_sleb64 => .memory_addr_sleb64,
.memory_addr_tls_sleb => .memory_addr_tls_sleb,
.memory_addr_tls_sleb64 => .memory_addr_tls_sleb64,
.section_offset_i32 => .section_offset_i32,
.table_index_i32 => .table_index_i32,
.table_index_i64 => .table_index_i64,
.table_index_rel_sleb => .table_index_rel_sleb,
.table_index_rel_sleb64 => .table_index_rel_sleb64,
.table_index_sleb => .table_index_sleb,
.table_index_sleb64 => .table_index_sleb64,
.table_number_leb => .table_number_leb,
.type_index_leb => .type_index_leb,
};
}
pub fn fromTypeImport(t: Object.RelocationType) Tag {
return switch (t) {
.event_index_leb => unreachable,
.function_index_i32 => .function_import_index_i32,
.function_index_leb => .function_import_index_leb,
.function_offset_i32 => .function_import_offset_i32,
.function_offset_i64 => .function_import_offset_i64,
.global_index_i32 => .global_import_index_i32,
.global_index_leb => .global_import_index_leb,
.memory_addr_i32 => .memory_addr_import_i32,
.memory_addr_i64 => .memory_addr_import_i64,
.memory_addr_leb => .memory_addr_import_leb,
.memory_addr_leb64 => .memory_addr_import_leb64,
.memory_addr_locrel_i32 => .memory_addr_import_locrel_i32,
.memory_addr_rel_sleb => .memory_addr_import_rel_sleb,
.memory_addr_rel_sleb64 => .memory_addr_import_rel_sleb64,
.memory_addr_sleb => .memory_addr_import_sleb,
.memory_addr_sleb64 => .memory_addr_import_sleb64,
.memory_addr_tls_sleb => .memory_addr_import_tls_sleb,
.memory_addr_tls_sleb64 => .memory_addr_import_tls_sleb64,
.section_offset_i32 => unreachable,
.table_index_i32 => .table_import_index_i32,
.table_index_i64 => .table_import_index_i64,
.table_index_rel_sleb => .table_import_index_rel_sleb,
.table_index_rel_sleb64 => .table_import_index_rel_sleb64,
.table_index_sleb => .table_import_index_sleb,
.table_index_sleb64 => .table_import_index_sleb64,
.table_number_leb => .table_import_number_leb,
.type_index_leb => unreachable,
};
}
};
pub const Pointee = union {
symbol_name: String,
data: ObjectData.Index,
type_index: FunctionType.Index,
section: ObjectSectionIndex,
data: ObjectData.Index,
function: Wasm.ObjectFunctionIndex,
function: ObjectFunctionIndex,
global: ObjectGlobalIndex,
table: ObjectTableIndex,
};
pub const Slice = extern struct {
@ -2057,63 +2233,47 @@ pub const ObjectRelocation = struct {
off: u32,
len: u32,
pub fn slice(s: Slice, wasm: *const Wasm) []ObjectRelocation {
return wasm.relocations.items[s.off..][0..s.len];
const empty: Slice = .{ .off = 0, .len = 0 };
fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag {
return wasm.object_relocations.items(.tag)[s.off..][0..s.len];
}
fn offsets(s: Slice, wasm: *const Wasm) []const u32 {
return wasm.object_relocations.items(.offset)[s.off..][0..s.len];
}
fn pointees(s: Slice, wasm: *const Wasm) []const Pointee {
return wasm.object_relocations.items(.pointee)[s.off..][0..s.len];
}
fn addends(s: Slice, wasm: *const Wasm) []const i32 {
return wasm.object_relocations.items(.addend)[s.off..][0..s.len];
}
};
pub const Tag = enum(u8) {
/// Uses `function`.
FUNCTION_INDEX_LEB = 0,
/// Uses `table_index`.
TABLE_INDEX_SLEB = 1,
/// Uses `table_index`.
TABLE_INDEX_I32 = 2,
/// Uses `data_segment`.
MEMORY_ADDR_LEB = 3,
/// Uses `data_segment`.
MEMORY_ADDR_SLEB = 4,
/// Uses `data_segment`.
MEMORY_ADDR_I32 = 5,
/// Uses `type_index`.
TYPE_INDEX_LEB = 6,
/// Uses `symbol_name`.
GLOBAL_INDEX_LEB = 7,
FUNCTION_OFFSET_I32 = 8,
SECTION_OFFSET_I32 = 9,
TAG_INDEX_LEB = 10,
/// Uses `data_segment`.
MEMORY_ADDR_REL_SLEB = 11,
TABLE_INDEX_REL_SLEB = 12,
/// Uses `symbol_name`.
GLOBAL_INDEX_I32 = 13,
/// Uses `data_segment`.
MEMORY_ADDR_LEB64 = 14,
/// Uses `data_segment`.
MEMORY_ADDR_SLEB64 = 15,
/// Uses `data_segment`.
MEMORY_ADDR_I64 = 16,
/// Uses `data_segment`.
MEMORY_ADDR_REL_SLEB64 = 17,
/// Uses `table_index`.
TABLE_INDEX_SLEB64 = 18,
/// Uses `table_index`.
TABLE_INDEX_I64 = 19,
TABLE_NUMBER_LEB = 20,
/// Uses `data_segment`.
MEMORY_ADDR_TLS_SLEB = 21,
FUNCTION_OFFSET_I64 = 22,
/// Uses `data_segment`.
MEMORY_ADDR_LOCREL_I32 = 23,
TABLE_INDEX_REL_SLEB64 = 24,
/// Uses `data_segment`.
MEMORY_ADDR_TLS_SLEB64 = 25,
/// Uses `symbol_name`.
FUNCTION_INDEX_I32 = 26,
pub const IterableSlice = struct {
slice: Slice,
/// Offset at which point to stop iterating.
end: u32,
// Above here, the tags correspond to symbol table ABI described in
// https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
// Below, the tags are compiler-internal.
const empty: IterableSlice = .{ .slice = .empty, .end = 0 };
fn init(relocs: Slice, offset: u32, size: u32, wasm: *const Wasm) IterableSlice {
const offsets = relocs.offsets(wasm);
const start = std.sort.lowerBound(u32, offsets, offset, order);
return .{
.slice = .{
.off = @intCast(relocs.off + start),
.len = @intCast(relocs.len - start),
},
.end = offset + size,
};
}
fn order(lhs: u32, rhs: u32) std.math.Order {
return std.math.order(lhs, rhs);
}
};
};
@ -2781,7 +2941,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
// At the end, output functions and globals will be populated.
for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {
if (import.flags.isIncluded(rdynamic)) {
try markFunction(wasm, name, import, @enumFromInt(i));
try markFunctionImport(wasm, name, import, @enumFromInt(i));
}
}
wasm.functions_end_prelink = @intCast(wasm.functions.entries.len);
@ -2789,7 +2949,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
if (import.flags.isIncluded(rdynamic)) {
try markGlobal(wasm, name, import, @enumFromInt(i));
try markGlobalImport(wasm, name, import, @enumFromInt(i));
}
}
wasm.globals_end_prelink = @intCast(wasm.globals.entries.len);
@ -2797,13 +2957,12 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| {
if (import.flags.isIncluded(rdynamic)) {
try markTable(wasm, name, import, @enumFromInt(i));
try markTableImport(wasm, name, import, @enumFromInt(i));
}
}
}
/// Recursively mark alive everything referenced by the function.
fn markFunction(
fn markFunctionImport(
wasm: *Wasm,
name: String,
import: *FunctionImport,
@ -2814,8 +2973,6 @@ fn markFunction(
const comp = wasm.base.comp;
const gpa = comp.gpa;
const rdynamic = comp.config.rdynamic;
const is_obj = comp.config.output_mode == .Obj;
try wasm.functions.ensureUnusedCapacity(gpa, 1);
@ -2836,20 +2993,31 @@ fn markFunction(
try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm));
}
} else {
const gop = wasm.functions.getOrPutAssumeCapacity(import.resolution);
if (!is_obj and import.flags.isExported(rdynamic)) try wasm.function_exports.append(gpa, .{
.name = name,
.function_index = @enumFromInt(gop.index),
});
for (try wasm.functionResolutionRelocSlice(import.resolution)) |reloc|
try wasm.markReloc(reloc);
try markFunction(wasm, import.resolution.unpack(wasm).object_function);
}
}
/// Recursively mark alive everything referenced by the function.
fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) Allocator.Error!void {
const comp = wasm.base.comp;
const gpa = comp.gpa;
const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i));
if (gop.found_existing) return;
const rdynamic = comp.config.rdynamic;
const is_obj = comp.config.output_mode == .Obj;
const function = i.ptr(wasm);
if (!is_obj and function.flags.isExported(rdynamic)) try wasm.function_exports.append(gpa, .{
.name = function.name.unwrap().?,
.function_index = @enumFromInt(gop.index),
});
try wasm.markRelocations(function.relocations(wasm));
}
/// Recursively mark alive everything referenced by the global.
fn markGlobal(
fn markGlobalImport(
wasm: *Wasm,
name: String,
import: *GlobalImport,
@ -2860,8 +3028,6 @@ fn markGlobal(
const comp = wasm.base.comp;
const gpa = comp.gpa;
const rdynamic = comp.config.rdynamic;
const is_obj = comp.config.output_mode == .Obj;
try wasm.globals.ensureUnusedCapacity(gpa, 1);
@ -2888,19 +3054,29 @@ fn markGlobal(
try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm));
}
} else {
const gop = wasm.globals.getOrPutAssumeCapacity(import.resolution);
if (!is_obj and import.flags.isExported(rdynamic)) try wasm.global_exports.append(gpa, .{
.name = name,
.global_index = @enumFromInt(gop.index),
});
for (try wasm.globalResolutionRelocSlice(import.resolution)) |reloc|
try wasm.markReloc(reloc);
try markGlobal(wasm, import.resolution.unpack(wasm).object_global);
}
}
fn markTable(
fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex) Allocator.Error!void {
const comp = wasm.base.comp;
const gpa = comp.gpa;
const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i));
if (gop.found_existing) return;
const rdynamic = comp.config.rdynamic;
const is_obj = comp.config.output_mode == .Obj;
const global = i.ptr(wasm);
if (!is_obj and global.flags.isExported(rdynamic)) try wasm.global_exports.append(gpa, .{
.name = global.name.unwrap().?,
.global_index = @enumFromInt(gop.index),
});
try wasm.markRelocations(global.relocations(wasm));
}
fn markTableImport(
wasm: *Wasm,
name: String,
import: *TableImport,
@ -2927,22 +3103,101 @@ fn markTable(
}
}
fn globalResolutionRelocSlice(wasm: *Wasm, resolution: GlobalImport.Resolution) ![]const ObjectRelocation {
assert(resolution != .unresolved);
_ = wasm;
@panic("TODO");
fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) Allocator.Error!void {
const segment = segment_index.ptr(wasm);
if (segment.flags.alive) return;
segment.flags.alive = true;
try wasm.markRelocations(segment.relocations(wasm));
}
fn functionResolutionRelocSlice(wasm: *Wasm, resolution: FunctionImport.Resolution) ![]const ObjectRelocation {
assert(resolution != .unresolved);
_ = wasm;
@panic("TODO");
}
fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) Allocator.Error!void {
for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, *pointee, offset| {
if (offset >= relocs.end) break;
switch (tag) {
.function_import_index_leb,
.function_import_index_i32,
.function_import_offset_i32,
.function_import_offset_i64,
.table_import_index_sleb,
.table_import_index_i32,
.table_import_index_sleb64,
.table_import_index_i64,
.table_import_index_rel_sleb,
.table_import_index_rel_sleb64,
=> {
const name = pointee.symbol_name;
const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);
try markFunctionImport(wasm, name, i.value(wasm), i);
},
.global_import_index_leb, .global_import_index_i32 => {
const name = pointee.symbol_name;
const i: GlobalImport.Index = @enumFromInt(wasm.object_global_imports.getIndex(name).?);
try markGlobalImport(wasm, name, i.value(wasm), i);
},
.table_import_number_leb => {
const name = pointee.symbol_name;
const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?);
try markTableImport(wasm, name, i.value(wasm), i);
},
fn markReloc(wasm: *Wasm, reloc: ObjectRelocation) !void {
_ = wasm;
_ = reloc;
@panic("TODO");
.function_index_leb,
.function_index_i32,
.function_offset_i32,
.function_offset_i64,
.table_index_sleb,
.table_index_i32,
.table_index_sleb64,
.table_index_i64,
.table_index_rel_sleb,
.table_index_rel_sleb64,
=> try markFunction(wasm, pointee.function),
.global_index_leb,
.global_index_i32,
=> try markGlobal(wasm, pointee.global),
.table_number_leb,
=> try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(pointee.table), {}),
.section_offset_i32 => {
log.warn("TODO: ensure section {d} is included in output", .{pointee.section});
},
.memory_addr_import_leb,
.memory_addr_import_sleb,
.memory_addr_import_i32,
.memory_addr_import_rel_sleb,
.memory_addr_import_leb64,
.memory_addr_import_sleb64,
.memory_addr_import_i64,
.memory_addr_import_rel_sleb64,
.memory_addr_import_tls_sleb,
.memory_addr_import_locrel_i32,
.memory_addr_import_tls_sleb64,
=> {
const name = pointee.symbol_name;
if (name == wasm.preloaded_strings.__heap_end or
name == wasm.preloaded_strings.__heap_base)
{
continue;
}
log.warn("TODO: ensure data symbol {s} is included in output", .{name.slice(wasm)});
},
.memory_addr_leb,
.memory_addr_sleb,
.memory_addr_i32,
.memory_addr_rel_sleb,
.memory_addr_leb64,
.memory_addr_sleb64,
.memory_addr_i64,
.memory_addr_rel_sleb64,
.memory_addr_tls_sleb,
.memory_addr_locrel_i32,
.memory_addr_tls_sleb64,
=> try markDataSegment(wasm, pointee.data.ptr(wasm).segment),
.type_index_leb => continue,
}
}
}
pub fn flushModule(

View File

@ -1398,148 +1398,6 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
// try binary_bytes.insertSlice(table_offset, &buf);
//}
///// Resolves the relocations within the atom, writing the new value
///// at the calculated offset.
//fn resolveAtomRelocs(wasm: *const Wasm, atom: *Atom) void {
// const symbol_name = wasm.symbolLocName(atom.symbolLoc());
// log.debug("resolving {d} relocs in atom '{s}'", .{ atom.relocs.len, symbol_name });
//
// for (atom.relocSlice(wasm)) |reloc| {
// const value = atomRelocationValue(wasm, atom, reloc);
// log.debug("relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
// wasm.symbolLocName(.{
// .file = atom.file,
// .index = @enumFromInt(reloc.index),
// }),
// symbol_name,
// reloc.offset,
// value,
// });
//
// switch (reloc.tag) {
// .TABLE_INDEX_I32,
// .FUNCTION_OFFSET_I32,
// .GLOBAL_INDEX_I32,
// .MEMORY_ADDR_I32,
// .SECTION_OFFSET_I32,
// => mem.writeInt(u32, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
//
// .TABLE_INDEX_I64,
// .MEMORY_ADDR_I64,
// => mem.writeInt(u64, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..8], value, .little),
//
// .GLOBAL_INDEX_LEB,
// .EVENT_INDEX_LEB,
// .FUNCTION_INDEX_LEB,
// .MEMORY_ADDR_LEB,
// .MEMORY_ADDR_SLEB,
// .TABLE_INDEX_SLEB,
// .TABLE_NUMBER_LEB,
// .TYPE_INDEX_LEB,
// .MEMORY_ADDR_TLS_SLEB,
// => leb.writeUnsignedFixed(5, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
//
// .MEMORY_ADDR_LEB64,
// .MEMORY_ADDR_SLEB64,
// .TABLE_INDEX_SLEB64,
// .MEMORY_ADDR_TLS_SLEB64,
// => leb.writeUnsignedFixed(10, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..10], value),
// }
// }
//}
///// From a given `relocation` will return the new value to be written.
///// All values will be represented as a `u64` as all values can fit within it.
///// The final value must be casted to the correct size.
//fn atomRelocationValue(wasm: *const Wasm, atom: *const Atom, relocation: *const Relocation) u64 {
// if (relocation.tag == .TYPE_INDEX_LEB) {
// // Eagerly resolved when parsing the object file.
// if (true) @panic("TODO the eager resolve when parsing");
// return relocation.index;
// }
// const target_loc = wasm.symbolLocFinalLoc(.{
// .file = atom.file,
// .index = @enumFromInt(relocation.index),
// });
// const symbol = wasm.finalSymbolByLoc(target_loc);
// if (symbol.tag != .section and !symbol.flags.alive) {
// const val = atom.tombstone(wasm) orelse relocation.addend;
// return @bitCast(val);
// }
// return switch (relocation.tag) {
// .FUNCTION_INDEX_LEB => if (symbol.flags.undefined)
// @intFromEnum(symbol.pointee.function_import)
// else
// @intFromEnum(symbol.pointee.function) + f.function_imports.items.len,
// .TABLE_NUMBER_LEB => if (symbol.flags.undefined)
// @intFromEnum(symbol.pointee.table_import)
// else
// @intFromEnum(symbol.pointee.table) + wasm.table_imports.items.len,
// .TABLE_INDEX_I32,
// .TABLE_INDEX_I64,
// .TABLE_INDEX_SLEB,
// .TABLE_INDEX_SLEB64,
// => wasm.indirect_function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
//
// .TYPE_INDEX_LEB => unreachable, // handled above
// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)
// @intFromEnum(symbol.pointee.global_import)
// else
// @intFromEnum(symbol.pointee.global) + f.global_imports.items.len,
//
// .MEMORY_ADDR_I32,
// .MEMORY_ADDR_I64,
// .MEMORY_ADDR_LEB,
// .MEMORY_ADDR_LEB64,
// .MEMORY_ADDR_SLEB,
// .MEMORY_ADDR_SLEB64,
// => {
// assert(symbol.tag == .data);
// if (symbol.flags.undefined) return 0;
// const va: i33 = symbol.virtual_address;
// return @intCast(va + relocation.addend);
// },
// .EVENT_INDEX_LEB => @panic("TODO: expose this as an error, events are unsupported"),
// .SECTION_OFFSET_I32 => {
// const target_atom_index = wasm.symbol_atom.get(target_loc).?;
// const target_atom = wasm.getAtom(target_atom_index);
// const rel_value: i33 = target_atom.offset;
// return @intCast(rel_value + relocation.addend);
// },
// .FUNCTION_OFFSET_I32 => {
// if (symbol.flags.undefined) {
// const val = atom.tombstone(wasm) orelse relocation.addend;
// return @bitCast(val);
// }
// const target_atom_index = wasm.symbol_atom.get(target_loc).?;
// const target_atom = wasm.getAtom(target_atom_index);
// const rel_value: i33 = target_atom.offset;
// return @intCast(rel_value + relocation.addend);
// },
// .MEMORY_ADDR_TLS_SLEB,
// .MEMORY_ADDR_TLS_SLEB64,
// => {
// const va: i33 = symbol.virtual_address;
// return @intCast(va + relocation.addend);
// },
// };
//}
///// For a given `Atom` returns whether it has a tombstone value or not.
///// This defines whether we want a specific value when a section is dead.
//fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 {
// const atom_name = wasm.finalSymbolByLoc(atom.symbolLoc()).name;
// if (atom_name == wasm.custom_sections.@".debug_ranges".name or
// atom_name == wasm.custom_sections.@".debug_loc".name)
// {
// return -2;
// } else if (mem.startsWith(u8, atom_name.slice(wasm), ".debug_")) {
// return -1;
// } else {
// return null;
// }
//}
fn uleb128size(x: u32) u32 {
var value = x;
var size: u32 = 0;

View File

@ -36,12 +36,16 @@ global_imports: RelativeSlice,
table_imports: RelativeSlice,
/// Points into Wasm object_custom_segments
custom_segments: RelativeSlice,
/// For calculating local section index from `Wasm.ObjectSectionIndex`.
local_section_index_base: u32,
/// Points into Wasm object_init_funcs
init_funcs: RelativeSlice,
/// Points into Wasm object_comdats
comdats: RelativeSlice,
/// Guaranteed to be non-null when functions has nonzero length.
code_section_index: ?Wasm.ObjectSectionIndex,
/// Guaranteed to be non-null when globals has nonzero length.
global_section_index: ?Wasm.ObjectSectionIndex,
/// Guaranteed to be non-null when data segments has nonzero length.
data_section_index: ?Wasm.ObjectSectionIndex,
pub const RelativeSlice = struct {
off: u32,
@ -52,7 +56,8 @@ pub const SegmentInfo = struct {
name: Wasm.String,
flags: Flags,
const Flags = packed struct(u32) {
/// Matches the ABI.
pub const Flags = packed struct(u32) {
/// Signals that the segment contains only null terminated strings allowing
/// the linker to perform merging.
strings: bool,
@ -101,6 +106,37 @@ pub const SubsectionType = enum(u8) {
symbol_table = 8,
};
/// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
pub const RelocationType = enum(u8) {
function_index_leb = 0,
table_index_sleb = 1,
table_index_i32 = 2,
memory_addr_leb = 3,
memory_addr_sleb = 4,
memory_addr_i32 = 5,
type_index_leb = 6,
global_index_leb = 7,
function_offset_i32 = 8,
section_offset_i32 = 9,
event_index_leb = 10,
memory_addr_rel_sleb = 11,
table_index_rel_sleb = 12,
global_index_i32 = 13,
memory_addr_leb64 = 14,
memory_addr_sleb64 = 15,
memory_addr_i64 = 16,
memory_addr_rel_sleb64 = 17,
table_index_sleb64 = 18,
table_index_i64 = 19,
table_number_leb = 20,
memory_addr_tls_sleb = 21,
function_offset_i64 = 22,
memory_addr_locrel_i32 = 23,
table_index_rel_sleb64 = 24,
memory_addr_tls_sleb64 = 25,
function_index_i32 = 26,
};
pub const Symbol = struct {
flags: Wasm.SymbolFlags,
name: Wasm.OptionalString,
@ -245,7 +281,8 @@ pub fn parse(
const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len);
const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len);
const local_section_index_base = wasm.object_total_sections;
const source_location: Wasm.SourceLocation = .fromObject(@enumFromInt(wasm.objects.items.len), wasm);
const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len);
const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm);
ss.clear();
@ -254,6 +291,9 @@ pub fn parse(
var saw_linking_section = false;
var has_tls = false;
var table_import_symbol_count: usize = 0;
var code_section_index: ?Wasm.ObjectSectionIndex = null;
var global_section_index: ?Wasm.ObjectSectionIndex = null;
var data_section_index: ?Wasm.ObjectSectionIndex = null;
while (pos < bytes.len) : (wasm.object_total_sections += 1) {
const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);
@ -365,7 +405,8 @@ pub fn parse(
switch (tag) {
.data => {
const name, pos = readBytes(bytes, pos);
symbol.name = (try wasm.internString(name)).toOptional();
const interned_name = try wasm.internString(name);
symbol.name = interned_name.toOptional();
if (symbol.flags.undefined) {
symbol.pointee = .data_import;
} else {
@ -376,7 +417,7 @@ pub fn parse(
.segment = @enumFromInt(data_segment_start + segment_index),
.offset = segment_offset,
.size = size,
.name = symbol.name,
.name = interned_name,
.flags = symbol.flags,
});
symbol.pointee = .{
@ -468,7 +509,7 @@ pub fn parse(
var prev_offset: u32 = 0;
try wasm.object_relocations.ensureUnusedCapacity(gpa, count);
for (0..count) |_| {
const tag: Wasm.ObjectRelocation.Tag = @enumFromInt(bytes[pos]);
const tag: RelocationType = @enumFromInt(bytes[pos]);
pos += 1;
const offset, pos = readLeb(u32, bytes, pos);
const index, pos = readLeb(u32, bytes, pos);
@ -477,75 +518,134 @@ pub fn parse(
return diags.failParse(path, "relocation entries not sorted by offset", .{});
prev_offset = offset;
const sym = &ss.symbol_table.items[index];
switch (tag) {
.MEMORY_ADDR_LEB,
.MEMORY_ADDR_SLEB,
.MEMORY_ADDR_I32,
.MEMORY_ADDR_REL_SLEB,
.MEMORY_ADDR_LEB64,
.MEMORY_ADDR_SLEB64,
.MEMORY_ADDR_I64,
.MEMORY_ADDR_REL_SLEB64,
.MEMORY_ADDR_TLS_SLEB,
.MEMORY_ADDR_LOCREL_I32,
.MEMORY_ADDR_TLS_SLEB64,
.memory_addr_leb,
.memory_addr_sleb,
.memory_addr_i32,
.memory_addr_rel_sleb,
.memory_addr_leb64,
.memory_addr_sleb64,
.memory_addr_i64,
.memory_addr_rel_sleb64,
.memory_addr_tls_sleb,
.memory_addr_locrel_i32,
.memory_addr_tls_sleb64,
=> {
const addend: i32, pos = readLeb(i32, bytes, pos);
wasm.object_relocations.appendAssumeCapacity(.{
.tag = tag,
.offset = offset,
.pointee = .{ .data = ss.symbol_table.items[index].pointee.data },
.addend = addend,
wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
.data => |data| .{
.tag = .fromType(tag),
.offset = offset,
.pointee = .{ .data = data },
.addend = addend,
},
.data_import => .{
.tag = .fromTypeImport(tag),
.offset = offset,
.pointee = .{ .symbol_name = sym.name.unwrap().? },
.addend = addend,
},
else => unreachable,
});
},
.FUNCTION_OFFSET_I32,
.FUNCTION_OFFSET_I64,
=> {
.function_offset_i32, .function_offset_i64 => {
const addend: i32, pos = readLeb(i32, bytes, pos);
wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
.function => .{
.tag = .fromType(tag),
.offset = offset,
.pointee = .{ .function = sym.pointee.function },
.addend = addend,
},
.function_import => .{
.tag = .fromTypeImport(tag),
.offset = offset,
.pointee = .{ .symbol_name = sym.name.unwrap().? },
.addend = addend,
},
else => unreachable,
});
},
.section_offset_i32 => {
const addend: i32, pos = readLeb(i32, bytes, pos);
wasm.object_relocations.appendAssumeCapacity(.{
.tag = tag,
.tag = .section_offset_i32,
.offset = offset,
.pointee = .{ .function = ss.symbol_table.items[index].pointee.function },
.pointee = .{ .section = sym.pointee.section },
.addend = addend,
});
},
.SECTION_OFFSET_I32 => {
const addend: i32, pos = readLeb(i32, bytes, pos);
.type_index_leb => {
wasm.object_relocations.appendAssumeCapacity(.{
.tag = tag,
.offset = offset,
.pointee = .{ .section = ss.symbol_table.items[index].pointee.section },
.addend = addend,
});
},
.TYPE_INDEX_LEB => {
wasm.object_relocations.appendAssumeCapacity(.{
.tag = tag,
.tag = .type_index_leb,
.offset = offset,
.pointee = .{ .type_index = ss.func_types.items[index] },
.addend = undefined,
});
},
.FUNCTION_INDEX_LEB,
.FUNCTION_INDEX_I32,
.GLOBAL_INDEX_LEB,
.GLOBAL_INDEX_I32,
.TABLE_INDEX_SLEB,
.TABLE_INDEX_I32,
.TABLE_INDEX_SLEB64,
.TABLE_INDEX_I64,
.TABLE_NUMBER_LEB,
.TABLE_INDEX_REL_SLEB,
.TABLE_INDEX_REL_SLEB64,
.TAG_INDEX_LEB,
.function_index_leb,
.function_index_i32,
.table_index_sleb,
.table_index_i32,
.table_index_sleb64,
.table_index_i64,
.table_index_rel_sleb,
.table_index_rel_sleb64,
=> {
wasm.object_relocations.appendAssumeCapacity(.{
.tag = tag,
.offset = offset,
.pointee = .{ .symbol_name = ss.symbol_table.items[index].name.unwrap().? },
.addend = undefined,
wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
.function => .{
.tag = .fromType(tag),
.offset = offset,
.pointee = .{ .function = sym.pointee.function },
.addend = undefined,
},
.function_import => .{
.tag = .fromTypeImport(tag),
.offset = offset,
.pointee = .{ .symbol_name = sym.name.unwrap().? },
.addend = undefined,
},
else => unreachable,
});
},
.global_index_leb, .global_index_i32 => {
wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
.global => .{
.tag = .fromType(tag),
.offset = offset,
.pointee = .{ .global = sym.pointee.global },
.addend = undefined,
},
.global_import => .{
.tag = .fromTypeImport(tag),
.offset = offset,
.pointee = .{ .symbol_name = sym.name.unwrap().? },
.addend = undefined,
},
else => unreachable,
});
},
.table_number_leb => {
wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
.table => .{
.tag = .fromType(tag),
.offset = offset,
.pointee = .{ .table = sym.pointee.table },
.addend = undefined,
},
.table_import => .{
.tag = .fromTypeImport(tag),
.offset = offset,
.pointee = .{ .symbol_name = sym.name.unwrap().? },
.addend = undefined,
},
else => unreachable,
});
},
.event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}),
}
}
@ -684,11 +784,17 @@ pub fn parse(
}
},
.global => {
if (global_section_index != null)
return diags.failParse(path, "object has more than one global section", .{});
global_section_index = section_index;
const section_start = pos;
const globals_len, pos = readLeb(u32, bytes, pos);
for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| {
const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
const mutable = bytes[pos] == 0x01;
pos += 1;
const init_start = pos;
const expr, pos = try readInit(wasm, bytes, pos);
global.* = .{
.name = .none,
@ -699,6 +805,9 @@ pub fn parse(
},
},
.expr = expr,
.object_index = object_index,
.offset = @intCast(init_start - section_start),
.size = @intCast(pos - init_start),
};
}
},
@ -728,10 +837,14 @@ pub fn parse(
start_function = @enumFromInt(functions_start + index);
},
.element => {
log.warn("unimplemented: element section in {}", .{path});
log.warn("unimplemented: element section in {} {s}", .{ path, archive_member_name.? });
pos = section_end;
},
.code => {
if (code_section_index != null)
return diags.failParse(path, "object has more than one code section", .{});
code_section_index = section_index;
const start = pos;
const count, pos = readLeb(u32, bytes, pos);
for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| {
@ -745,12 +858,16 @@ pub fn parse(
.type_index = undefined, // populated from func_types
.code = payload,
.offset = offset,
.section_index = section_index,
.source_location = source_location,
.object_index = object_index,
};
}
},
.data => {
if (data_section_index != null)
return diags.failParse(path, "object has more than one data section", .{});
data_section_index = section_index;
const section_start = pos;
const count, pos = readLeb(u32, bytes, pos);
for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| {
const flags, pos = readEnum(DataSegmentFlags, bytes, pos);
@ -761,12 +878,17 @@ pub fn parse(
//const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos };
if (flags != .passive) pos = try skipInit(bytes, pos);
const data_len, pos = readLeb(u32, bytes, pos);
const segment_start = pos;
const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]);
pos += data_len;
elem.* = .{
.payload = payload,
.name = .none, // Populated from symbol table
.flags = .{}, // Populated from symbol table and segment_info
.name = .none, // Populated from segment_info
.flags = .{
.is_passive = flags == .passive,
}, // Remainder populated from segment_info
.offset = @intCast(segment_start - section_start),
.object_index = object_index,
};
}
},
@ -1033,8 +1155,10 @@ pub fn parse(
}
},
.data_import => {
const name = symbol.name.unwrap().?;
log.warn("TODO data import '{s}'", .{name.slice(wasm)});
if (symbol.flags.undefined and symbol.flags.binding == .local) {
const name = symbol.name.slice(wasm).?;
diags.addParseError(path, "local symbol '{s}' references import", .{name});
}
},
.data => continue, // `wasm.object_datas` has already been populated.
};
@ -1055,16 +1179,21 @@ pub fn parse(
}
// Apply segment_info.
for (wasm.object_data_segments.items[data_segment_start..], ss.segment_info.items) |*data, info| {
const data_segments = wasm.object_data_segments.items[data_segment_start..];
if (data_segments.len != ss.segment_info.items.len) {
return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{
data_segments.len, ss.segment_info.items.len,
});
}
for (data_segments, ss.segment_info.items) |*data, info| {
data.name = info.name.toOptional();
data.flags.strings = info.flags.strings;
data.flags.tls = data.flags.tls or info.flags.tls;
data.flags.no_strip = info.flags.retain;
data.flags.alignment = info.flags.alignment;
if (data.flags.undefined and data.flags.binding == .local) {
const name = info.name.slice(wasm);
diags.addParseError(path, "local symbol '{s}' references import", .{name});
}
data.flags = .{
.is_passive = data.flags.is_passive,
.strings = info.flags.strings,
.tls = info.flags.tls,
.retain = info.flags.retain,
.alignment = info.flags.alignment,
};
}
// Check for indirect function table in case of an MVP object file.
@ -1106,6 +1235,10 @@ pub fn parse(
});
}
const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start);
if (functions_len > 0 and code_section_index == null)
return diags.failParse(path, "code section missing ({d} functions)", .{functions_len});
return .{
.version = version,
.path = path,
@ -1114,7 +1247,7 @@ pub fn parse(
.features = features,
.functions = .{
.off = functions_start,
.len = @intCast(wasm.object_functions.items.len - functions_start),
.len = functions_len,
},
.function_imports = .{
.off = function_imports_start,
@ -1140,7 +1273,9 @@ pub fn parse(
.off = custom_segment_start,
.len = @intCast(wasm.object_custom_segments.entries.len - custom_segment_start),
},
.local_section_index_base = local_section_index_base,
.code_section_index = code_section_index,
.global_section_index = global_section_index,
.data_section_index = data_section_index,
};
}