mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
wasm linker: chase relocations for references
This commit is contained in:
parent
c5822879a1
commit
7d224516c4
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user