authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-29 20:21:26-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log7d224516c4414015186bc1497ca6641b7390bf92
treeaa703daf59107ff10601ad1c626ff9c18ea11a0c
parentc5822879a1da97e6c381c6684f4b9281400e5de0

wasm linker: chase relocations for references


5 files changed, 601 insertions(+), 353 deletions(-)

src/arch/wasm/Emit.zig+8-8
......@@ -108,7 +108,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
108108 try wasm.out_relocs.append(gpa, .{
109109 .offset = @intCast(code.items.len),
110110 .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() },
111 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
111 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
112112 .addend = 0,
113113 });
114114 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
......@@ -162,7 +162,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
162162 try wasm.out_relocs.append(gpa, .{
163163 .offset = @intCast(code.items.len),
164164 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) },
165 .tag = .FUNCTION_INDEX_LEB,
165 .tag = .function_index_leb,
166166 .addend = 0,
167167 });
168168 code.appendNTimesAssumeCapacity(0, 5);
......@@ -182,7 +182,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
182182 try wasm.out_relocs.append(gpa, .{
183183 .offset = @intCast(code.items.len),
184184 .pointee = .{ .type_index = func_ty_index },
185 .tag = .TYPE_INDEX_LEB,
185 .tag = .type_index_leb,
186186 .addend = 0,
187187 });
188188 code.appendNTimesAssumeCapacity(0, 5);
......@@ -202,7 +202,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
202202 try wasm.out_relocs.append(gpa, .{
203203 .offset = @intCast(code.items.len),
204204 .pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) },
205 .tag = .FUNCTION_INDEX_LEB,
205 .tag = .function_index_leb,
206206 .addend = 0,
207207 });
208208 code.appendNTimesAssumeCapacity(0, 5);
......@@ -226,7 +226,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
226226 try wasm.out_relocs.append(gpa, .{
227227 .offset = @intCast(code.items.len),
228228 .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) },
229 .tag = .FUNCTION_INDEX_LEB,
229 .tag = .function_index_leb,
230230 .addend = 0,
231231 });
232232 code.appendNTimesAssumeCapacity(0, 5);
......@@ -245,7 +245,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
245245 try wasm.out_relocs.append(gpa, .{
246246 .offset = @intCast(code.items.len),
247247 .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() },
248 .tag = .GLOBAL_INDEX_LEB,
248 .tag = .global_index_leb,
249249 .addend = 0,
250250 });
251251 code.appendNTimesAssumeCapacity(0, 5);
......@@ -922,7 +922,7 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef
922922 try wasm.out_relocs.append(gpa, .{
923923 .offset = @intCast(code.items.len),
924924 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) },
925 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
925 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
926926 .addend = data.offset,
927927 });
928928 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
......@@ -957,7 +957,7 @@ fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff
957957 try wasm.out_relocs.append(gpa, .{
958958 .offset = @intCast(code.items.len),
959959 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) },
960 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
960 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
961961 .addend = data.offset,
962962 });
963963 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
src/codegen.zig+2-2
......@@ -670,7 +670,7 @@ fn lowerUavRef(
670670 try wasm.out_relocs.append(gpa, .{
671671 .offset = @intCast(code.items.len),
672672 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) },
673 .tag = if (ptr_width_bytes == 4) .MEMORY_ADDR_I32 else .MEMORY_ADDR_I64,
673 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
674674 .addend = @intCast(offset),
675675 });
676676 } else {
......@@ -742,7 +742,7 @@ fn lowerNavRef(
742742 try wasm.out_relocs.append(gpa, .{
743743 .offset = @intCast(code.items.len),
744744 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
745 .tag = if (ptr_width_bytes == 4) .MEMORY_ADDR_I32 else .MEMORY_ADDR_I64,
745 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
746746 .addend = @intCast(offset),
747747 });
748748 } else {
src/link/Wasm.zig+385-130
......@@ -93,13 +93,13 @@ func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty,
9393/// Local functions may be unnamed.
9494object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty,
9595/// All functions for all objects.
96object_functions: std.ArrayListUnmanaged(Function) = .empty,
96object_functions: std.ArrayListUnmanaged(ObjectFunction) = .empty,
9797
9898/// Provides a mapping of both imports and provided globals to symbol name.
9999/// Local globals may be unnamed.
100100object_global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImport) = .empty,
101101/// All globals for all objects.
102object_globals: std.ArrayListUnmanaged(Global) = .empty,
102object_globals: std.ArrayListUnmanaged(ObjectGlobal) = .empty,
103103
104104/// All table imports for all objects.
105105object_table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport) = .empty,
......@@ -386,7 +386,7 @@ pub const GlobalIndex = enum(u32) {
386386 pub const stack_pointer: GlobalIndex = @enumFromInt(0);
387387
388388 /// Same as `stack_pointer` but with a safety assertion.
389 pub fn stackPointer(wasm: *const Wasm) Global.Index {
389 pub fn stackPointer(wasm: *const Wasm) ObjectGlobal.Index {
390390 const comp = wasm.base.comp;
391391 assert(comp.config.output_mode != .Obj);
392392 assert(comp.zcu != null);
......@@ -513,26 +513,19 @@ pub const SymbolFlags = packed struct(u32) {
513513
514514 // Above here matches the tooling conventions ABI.
515515
516 padding1: u5 = 0,
516 padding1: u13 = 0,
517517 /// Zig-specific. Dead things are allowed to be garbage collected.
518518 alive: bool = false,
519 /// Zig-specific. Segments only. Signals that the segment contains only
520 /// null terminated strings allowing the linker to perform merging.
521 strings: bool = false,
522519 /// Zig-specific. This symbol comes from an object that must be included in
523520 /// the final link.
524521 must_link: bool = false,
525 /// Zig-specific. Data segments only.
526 is_passive: bool = false,
527 /// Zig-specific. Data segments only.
528 alignment: Alignment = .none,
529 /// Zig-specific. Globals only.
522 /// Zig-specific.
530523 global_type: GlobalType4 = .zero,
531 /// Zig-specific. Tables only.
524 /// Zig-specific.
532525 limits_has_max: bool = false,
533 /// Zig-specific. Tables only.
526 /// Zig-specific.
534527 limits_is_shared: bool = false,
535 /// Zig-specific. Tables only.
528 /// Zig-specific.
536529 ref_type: RefType1 = .funcref,
537530
538531 pub const Binding = enum(u2) {
......@@ -554,10 +547,7 @@ pub const SymbolFlags = packed struct(u32) {
554547 pub fn initZigSpecific(flags: *SymbolFlags, must_link: bool, no_strip: bool) void {
555548 flags.no_strip = no_strip;
556549 flags.alive = false;
557 flags.strings = false;
558550 flags.must_link = must_link;
559 flags.is_passive = false;
560 flags.alignment = .none;
561551 flags.global_type = .zero;
562552 flags.limits_has_max = false;
563553 flags.limits_is_shared = false;
......@@ -603,7 +593,7 @@ pub const GlobalType4 = packed struct(u4) {
603593
604594 pub const zero: GlobalType4 = @bitCast(@as(u4, 0));
605595
606 pub fn to(gt: GlobalType4) Global.Type {
596 pub fn to(gt: GlobalType4) ObjectGlobal.Type {
607597 return .{
608598 .valtype = gt.valtype.to(),
609599 .mutable = gt.mutable,
......@@ -1001,18 +991,24 @@ pub const FunctionImport = extern struct {
1001991 };
1002992};
1003993
1004pub const Function = extern struct {
994pub const ObjectFunction = extern struct {
1005995 flags: SymbolFlags,
1006996 /// `none` if this function has no symbol describing it.
1007997 name: OptionalString,
1008998 type_index: FunctionType.Index,
1009999 code: Code,
1010 /// The offset within the section where the data starts.
1000 /// The offset within the code section where the data starts.
10111001 offset: u32,
1012 section_index: ObjectSectionIndex,
1013 source_location: SourceLocation,
1002 /// The object file whose code section contains this function.
1003 object_index: ObjectIndex,
10141004
10151005 pub const Code = DataPayload;
1006
1007 fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1008 const code_section_index = of.object_index.ptr(wasm).code_section_index.?;
1009 const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty;
1010 return .init(relocs, of.offset, of.code.len, wasm);
1011 }
10161012};
10171013
10181014pub const GlobalImport = extern struct {
......@@ -1101,6 +1097,10 @@ pub const GlobalImport = extern struct {
11011097 });
11021098 }
11031099
1100 fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution {
1101 return pack(wasm, .{ .object_global = object_global });
1102 }
1103
11041104 pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 {
11051105 return switch (unpack(r, wasm)) {
11061106 .unresolved => unreachable,
......@@ -1137,22 +1137,32 @@ pub const GlobalImport = extern struct {
11371137 return index.value(wasm).module_name;
11381138 }
11391139
1140 pub fn globalType(index: Index, wasm: *const Wasm) Global.Type {
1140 pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type {
11411141 return value(index, wasm).flags.global_type.to();
11421142 }
11431143 };
11441144};
11451145
1146pub const Global = extern struct {
1146pub const ObjectGlobal = extern struct {
11471147 /// `none` if this function has no symbol describing it.
11481148 name: OptionalString,
11491149 flags: SymbolFlags,
11501150 expr: Expr,
1151 /// The object file whose global section contains this global.
1152 object_index: ObjectIndex,
1153 offset: u32,
1154 size: u32,
11511155
11521156 pub const Type = struct {
11531157 valtype: std.wasm.Valtype,
11541158 mutable: bool,
11551159 };
1160
1161 fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1162 const global_section_index = og.object_index.ptr(wasm).global_section_index.?;
1163 const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty;
1164 return .init(relocs, og.offset, og.size, wasm);
1165 }
11561166};
11571167
11581168pub const RefType1 = enum(u1) {
......@@ -1205,6 +1215,18 @@ pub const TableImport = extern struct {
12051215 };
12061216 }
12071217
1218 fn pack(unpacked: Unpacked) Resolution {
1219 return switch (unpacked) {
1220 .unresolved => .unresolved,
1221 .__indirect_function_table => .__indirect_function_table,
1222 .object_table => |i| @enumFromInt(first_object_table + @intFromEnum(i)),
1223 };
1224 }
1225
1226 fn fromObjectTable(object_table: ObjectTableIndex) Resolution {
1227 return pack(.{ .object_table = object_table });
1228 }
1229
12081230 pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType {
12091231 return switch (unpack(r)) {
12101232 .unresolved => unreachable,
......@@ -1298,7 +1320,7 @@ pub const ObjectTableIndex = enum(u32) {
12981320pub const ObjectGlobalIndex = enum(u32) {
12991321 _,
13001322
1301 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
1323 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *ObjectGlobal {
13021324 return &wasm.object_globals.items[@intFromEnum(index)];
13031325 }
13041326
......@@ -1338,7 +1360,7 @@ pub const ObjectMemory = extern struct {
13381360pub const ObjectFunctionIndex = enum(u32) {
13391361 _,
13401362
1341 pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *Function {
1363 pub fn ptr(index: ObjectFunctionIndex, wasm: *const Wasm) *ObjectFunction {
13421364 return &wasm.object_functions.items[@intFromEnum(index)];
13431365 }
13441366
......@@ -1363,8 +1385,28 @@ pub const OptionalObjectFunctionIndex = enum(u32) {
13631385pub const ObjectDataSegment = extern struct {
13641386 /// `none` if segment info custom subsection is missing.
13651387 name: OptionalString,
1366 flags: SymbolFlags,
1388 flags: Flags,
13671389 payload: DataPayload,
1390 offset: u32,
1391 object_index: ObjectIndex,
1392
1393 pub const Flags = packed struct(u32) {
1394 alive: bool = false,
1395 is_passive: bool = false,
1396 alignment: Alignment = .none,
1397 /// Signals that the segment contains only null terminated strings allowing
1398 /// the linker to perform merging.
1399 strings: bool = false,
1400 /// The segment contains thread-local data. This means that a unique copy
1401 /// of this segment will be created for each thread.
1402 tls: bool = false,
1403 /// If the object file is included in the final link, the segment should be
1404 /// retained in the final output regardless of whether it is used by the
1405 /// program.
1406 retain: bool = false,
1407
1408 _: u21 = 0,
1409 };
13681410
13691411 /// Index into `Wasm.object_data_segments`.
13701412 pub const Index = enum(u32) {
......@@ -1374,6 +1416,12 @@ pub const ObjectDataSegment = extern struct {
13741416 return &wasm.object_data_segments.items[@intFromEnum(i)];
13751417 }
13761418 };
1419
1420 fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice {
1421 const data_section_index = ods.object_index.ptr(wasm).data_section_index.?;
1422 const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty;
1423 return .init(relocs, ods.offset, ods.payload.len, wasm);
1424 }
13771425};
13781426
13791427/// A local or exported global const from an object file.
......@@ -1383,8 +1431,7 @@ pub const ObjectData = extern struct {
13831431 offset: u32,
13841432 /// May be zero. `offset + size` must be <= the segment's size.
13851433 size: u32,
1386 /// `none` if no symbol describes it.
1387 name: OptionalString,
1434 name: String,
13881435 flags: SymbolFlags,
13891436
13901437 /// Index into `Wasm.object_datas`.
......@@ -1845,7 +1892,7 @@ pub const ZcuImportIndex = enum(u32) {
18451892 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
18461893 }
18471894
1848 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) Global.Type {
1895 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type {
18491896 _ = index;
18501897 _ = wasm;
18511898 unreachable; // Zig has no way to create Wasm globals yet.
......@@ -1997,7 +2044,7 @@ pub const GlobalImportId = enum(u32) {
19972044 };
19982045 }
19992046
2000 pub fn globalType(id: GlobalImportId, wasm: *Wasm) Global.Type {
2047 pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type {
20012048 return switch (unpack(id, wasm)) {
20022049 inline .object_global_import, .zcu_import => |i| i.globalType(wasm),
20032050 };
......@@ -2014,7 +2061,7 @@ pub const SymbolTableIndex = enum(u32) {
20142061};
20152062
20162063pub const OutReloc = struct {
2017 tag: ObjectRelocation.Tag,
2064 tag: Object.RelocationType,
20182065 offset: u32,
20192066 pointee: Pointee,
20202067 addend: i32,
......@@ -2041,15 +2088,144 @@ pub const ObjectRelocation = struct {
20412088 /// When `offset` is zero, its position is immediately after the id and size of the section.
20422089 offset: u32,
20432090 pointee: Pointee,
2044 /// Populated only for `MEMORY_ADDR_*`, `FUNCTION_OFFSET_I32` and `SECTION_OFFSET_I32`.
2091 /// Populated only for `memory_addr_*`, `function_offset_i32` and `section_offset_i32`.
20452092 addend: i32,
20462093
2094 pub const Tag = enum(u8) {
2095 // These use `Pointee.function`.
2096 function_index_i32,
2097 function_index_leb,
2098 function_offset_i32,
2099 function_offset_i64,
2100 table_index_i32,
2101 table_index_i64,
2102 table_index_rel_sleb,
2103 table_index_rel_sleb64,
2104 table_index_sleb,
2105 table_index_sleb64,
2106 // These use `Pointee.symbol_name`.
2107 function_import_index_i32,
2108 function_import_index_leb,
2109 function_import_offset_i32,
2110 function_import_offset_i64,
2111 table_import_index_i32,
2112 table_import_index_i64,
2113 table_import_index_rel_sleb,
2114 table_import_index_rel_sleb64,
2115 table_import_index_sleb,
2116 table_import_index_sleb64,
2117 // These use `Pointee.global`.
2118 global_index_i32,
2119 global_index_leb,
2120 // These use `Pointee.symbol_name`.
2121 global_import_index_i32,
2122 global_import_index_leb,
2123 // These use `Pointee.data`.
2124 memory_addr_i32,
2125 memory_addr_i64,
2126 memory_addr_leb,
2127 memory_addr_leb64,
2128 memory_addr_locrel_i32,
2129 memory_addr_rel_sleb,
2130 memory_addr_rel_sleb64,
2131 memory_addr_sleb,
2132 memory_addr_sleb64,
2133 memory_addr_tls_sleb,
2134 memory_addr_tls_sleb64,
2135 // These use `Pointee.symbol_name`.
2136 memory_addr_import_i32,
2137 memory_addr_import_i64,
2138 memory_addr_import_leb,
2139 memory_addr_import_leb64,
2140 memory_addr_import_locrel_i32,
2141 memory_addr_import_rel_sleb,
2142 memory_addr_import_rel_sleb64,
2143 memory_addr_import_sleb,
2144 memory_addr_import_sleb64,
2145 memory_addr_import_tls_sleb,
2146 memory_addr_import_tls_sleb64,
2147 /// Uses `Pointee.section`.
2148 section_offset_i32,
2149 /// Uses `Pointee.table`.
2150 table_number_leb,
2151 /// Uses `Pointee.symbol_name`.
2152 table_import_number_leb,
2153 /// Uses `Pointee.type_index`.
2154 type_index_leb,
2155
2156 pub fn fromType(t: Object.RelocationType) Tag {
2157 return switch (t) {
2158 .event_index_leb => unreachable,
2159 .function_index_i32 => .function_index_i32,
2160 .function_index_leb => .function_index_leb,
2161 .function_offset_i32 => .function_offset_i32,
2162 .function_offset_i64 => .function_offset_i64,
2163 .global_index_i32 => .global_index_i32,
2164 .global_index_leb => .global_index_leb,
2165 .memory_addr_i32 => .memory_addr_i32,
2166 .memory_addr_i64 => .memory_addr_i64,
2167 .memory_addr_leb => .memory_addr_leb,
2168 .memory_addr_leb64 => .memory_addr_leb64,
2169 .memory_addr_locrel_i32 => .memory_addr_locrel_i32,
2170 .memory_addr_rel_sleb => .memory_addr_rel_sleb,
2171 .memory_addr_rel_sleb64 => .memory_addr_rel_sleb64,
2172 .memory_addr_sleb => .memory_addr_sleb,
2173 .memory_addr_sleb64 => .memory_addr_sleb64,
2174 .memory_addr_tls_sleb => .memory_addr_tls_sleb,
2175 .memory_addr_tls_sleb64 => .memory_addr_tls_sleb64,
2176 .section_offset_i32 => .section_offset_i32,
2177 .table_index_i32 => .table_index_i32,
2178 .table_index_i64 => .table_index_i64,
2179 .table_index_rel_sleb => .table_index_rel_sleb,
2180 .table_index_rel_sleb64 => .table_index_rel_sleb64,
2181 .table_index_sleb => .table_index_sleb,
2182 .table_index_sleb64 => .table_index_sleb64,
2183 .table_number_leb => .table_number_leb,
2184 .type_index_leb => .type_index_leb,
2185 };
2186 }
2187
2188 pub fn fromTypeImport(t: Object.RelocationType) Tag {
2189 return switch (t) {
2190 .event_index_leb => unreachable,
2191 .function_index_i32 => .function_import_index_i32,
2192 .function_index_leb => .function_import_index_leb,
2193 .function_offset_i32 => .function_import_offset_i32,
2194 .function_offset_i64 => .function_import_offset_i64,
2195 .global_index_i32 => .global_import_index_i32,
2196 .global_index_leb => .global_import_index_leb,
2197 .memory_addr_i32 => .memory_addr_import_i32,
2198 .memory_addr_i64 => .memory_addr_import_i64,
2199 .memory_addr_leb => .memory_addr_import_leb,
2200 .memory_addr_leb64 => .memory_addr_import_leb64,
2201 .memory_addr_locrel_i32 => .memory_addr_import_locrel_i32,
2202 .memory_addr_rel_sleb => .memory_addr_import_rel_sleb,
2203 .memory_addr_rel_sleb64 => .memory_addr_import_rel_sleb64,
2204 .memory_addr_sleb => .memory_addr_import_sleb,
2205 .memory_addr_sleb64 => .memory_addr_import_sleb64,
2206 .memory_addr_tls_sleb => .memory_addr_import_tls_sleb,
2207 .memory_addr_tls_sleb64 => .memory_addr_import_tls_sleb64,
2208 .section_offset_i32 => unreachable,
2209 .table_index_i32 => .table_import_index_i32,
2210 .table_index_i64 => .table_import_index_i64,
2211 .table_index_rel_sleb => .table_import_index_rel_sleb,
2212 .table_index_rel_sleb64 => .table_import_index_rel_sleb64,
2213 .table_index_sleb => .table_import_index_sleb,
2214 .table_index_sleb64 => .table_import_index_sleb64,
2215 .table_number_leb => .table_import_number_leb,
2216 .type_index_leb => unreachable,
2217 };
2218 }
2219 };
2220
20472221 pub const Pointee = union {
20482222 symbol_name: String,
2223 data: ObjectData.Index,
20492224 type_index: FunctionType.Index,
20502225 section: ObjectSectionIndex,
2051 data: ObjectData.Index,
2052 function: Wasm.ObjectFunctionIndex,
2226 function: ObjectFunctionIndex,
2227 global: ObjectGlobalIndex,
2228 table: ObjectTableIndex,
20532229 };
20542230
20552231 pub const Slice = extern struct {
......@@ -2057,63 +2233,47 @@ pub const ObjectRelocation = struct {
20572233 off: u32,
20582234 len: u32,
20592235
2060 pub fn slice(s: Slice, wasm: *const Wasm) []ObjectRelocation {
2061 return wasm.relocations.items[s.off..][0..s.len];
2236 const empty: Slice = .{ .off = 0, .len = 0 };
2237
2238 fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag {
2239 return wasm.object_relocations.items(.tag)[s.off..][0..s.len];
2240 }
2241
2242 fn offsets(s: Slice, wasm: *const Wasm) []const u32 {
2243 return wasm.object_relocations.items(.offset)[s.off..][0..s.len];
2244 }
2245
2246 fn pointees(s: Slice, wasm: *const Wasm) []const Pointee {
2247 return wasm.object_relocations.items(.pointee)[s.off..][0..s.len];
2248 }
2249
2250 fn addends(s: Slice, wasm: *const Wasm) []const i32 {
2251 return wasm.object_relocations.items(.addend)[s.off..][0..s.len];
20622252 }
20632253 };
20642254
2065 pub const Tag = enum(u8) {
2066 /// Uses `function`.
2067 FUNCTION_INDEX_LEB = 0,
2068 /// Uses `table_index`.
2069 TABLE_INDEX_SLEB = 1,
2070 /// Uses `table_index`.
2071 TABLE_INDEX_I32 = 2,
2072 /// Uses `data_segment`.
2073 MEMORY_ADDR_LEB = 3,
2074 /// Uses `data_segment`.
2075 MEMORY_ADDR_SLEB = 4,
2076 /// Uses `data_segment`.
2077 MEMORY_ADDR_I32 = 5,
2078 /// Uses `type_index`.
2079 TYPE_INDEX_LEB = 6,
2080 /// Uses `symbol_name`.
2081 GLOBAL_INDEX_LEB = 7,
2082 FUNCTION_OFFSET_I32 = 8,
2083 SECTION_OFFSET_I32 = 9,
2084 TAG_INDEX_LEB = 10,
2085 /// Uses `data_segment`.
2086 MEMORY_ADDR_REL_SLEB = 11,
2087 TABLE_INDEX_REL_SLEB = 12,
2088 /// Uses `symbol_name`.
2089 GLOBAL_INDEX_I32 = 13,
2090 /// Uses `data_segment`.
2091 MEMORY_ADDR_LEB64 = 14,
2092 /// Uses `data_segment`.
2093 MEMORY_ADDR_SLEB64 = 15,
2094 /// Uses `data_segment`.
2095 MEMORY_ADDR_I64 = 16,
2096 /// Uses `data_segment`.
2097 MEMORY_ADDR_REL_SLEB64 = 17,
2098 /// Uses `table_index`.
2099 TABLE_INDEX_SLEB64 = 18,
2100 /// Uses `table_index`.
2101 TABLE_INDEX_I64 = 19,
2102 TABLE_NUMBER_LEB = 20,
2103 /// Uses `data_segment`.
2104 MEMORY_ADDR_TLS_SLEB = 21,
2105 FUNCTION_OFFSET_I64 = 22,
2106 /// Uses `data_segment`.
2107 MEMORY_ADDR_LOCREL_I32 = 23,
2108 TABLE_INDEX_REL_SLEB64 = 24,
2109 /// Uses `data_segment`.
2110 MEMORY_ADDR_TLS_SLEB64 = 25,
2111 /// Uses `symbol_name`.
2112 FUNCTION_INDEX_I32 = 26,
2113
2114 // Above here, the tags correspond to symbol table ABI described in
2115 // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
2116 // Below, the tags are compiler-internal.
2255 pub const IterableSlice = struct {
2256 slice: Slice,
2257 /// Offset at which point to stop iterating.
2258 end: u32,
2259
2260 const empty: IterableSlice = .{ .slice = .empty, .end = 0 };
2261
2262 fn init(relocs: Slice, offset: u32, size: u32, wasm: *const Wasm) IterableSlice {
2263 const offsets = relocs.offsets(wasm);
2264 const start = std.sort.lowerBound(u32, offsets, offset, order);
2265 return .{
2266 .slice = .{
2267 .off = @intCast(relocs.off + start),
2268 .len = @intCast(relocs.len - start),
2269 },
2270 .end = offset + size,
2271 };
2272 }
2273
2274 fn order(lhs: u32, rhs: u32) std.math.Order {
2275 return std.math.order(lhs, rhs);
2276 }
21172277 };
21182278};
21192279
......@@ -2781,7 +2941,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
27812941 // At the end, output functions and globals will be populated.
27822942 for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {
27832943 if (import.flags.isIncluded(rdynamic)) {
2784 try markFunction(wasm, name, import, @enumFromInt(i));
2944 try markFunctionImport(wasm, name, import, @enumFromInt(i));
27852945 }
27862946 }
27872947 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
27892949
27902950 for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
27912951 if (import.flags.isIncluded(rdynamic)) {
2792 try markGlobal(wasm, name, import, @enumFromInt(i));
2952 try markGlobalImport(wasm, name, import, @enumFromInt(i));
27932953 }
27942954 }
27952955 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
27972957
27982958 for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| {
27992959 if (import.flags.isIncluded(rdynamic)) {
2800 try markTable(wasm, name, import, @enumFromInt(i));
2960 try markTableImport(wasm, name, import, @enumFromInt(i));
28012961 }
28022962 }
28032963}
28042964
2805/// Recursively mark alive everything referenced by the function.
2806fn markFunction(
2965fn markFunctionImport(
28072966 wasm: *Wasm,
28082967 name: String,
28092968 import: *FunctionImport,
......@@ -2814,8 +2973,6 @@ fn markFunction(
28142973
28152974 const comp = wasm.base.comp;
28162975 const gpa = comp.gpa;
2817 const rdynamic = comp.config.rdynamic;
2818 const is_obj = comp.config.output_mode == .Obj;
28192976
28202977 try wasm.functions.ensureUnusedCapacity(gpa, 1);
28212978
......@@ -2836,20 +2993,31 @@ fn markFunction(
28362993 try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm));
28372994 }
28382995 } else {
2839 const gop = wasm.functions.getOrPutAssumeCapacity(import.resolution);
2996 try markFunction(wasm, import.resolution.unpack(wasm).object_function);
2997 }
2998}
28402999
2841 if (!is_obj and import.flags.isExported(rdynamic)) try wasm.function_exports.append(gpa, .{
2842 .name = name,
2843 .function_index = @enumFromInt(gop.index),
2844 });
3000/// Recursively mark alive everything referenced by the function.
3001fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) Allocator.Error!void {
3002 const comp = wasm.base.comp;
3003 const gpa = comp.gpa;
3004 const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i));
3005 if (gop.found_existing) return;
28453006
2846 for (try wasm.functionResolutionRelocSlice(import.resolution)) |reloc|
2847 try wasm.markReloc(reloc);
2848 }
3007 const rdynamic = comp.config.rdynamic;
3008 const is_obj = comp.config.output_mode == .Obj;
3009 const function = i.ptr(wasm);
3010
3011 if (!is_obj and function.flags.isExported(rdynamic)) try wasm.function_exports.append(gpa, .{
3012 .name = function.name.unwrap().?,
3013 .function_index = @enumFromInt(gop.index),
3014 });
3015
3016 try wasm.markRelocations(function.relocations(wasm));
28493017}
28503018
28513019/// Recursively mark alive everything referenced by the global.
2852fn markGlobal(
3020fn markGlobalImport(
28533021 wasm: *Wasm,
28543022 name: String,
28553023 import: *GlobalImport,
......@@ -2860,8 +3028,6 @@ fn markGlobal(
28603028
28613029 const comp = wasm.base.comp;
28623030 const gpa = comp.gpa;
2863 const rdynamic = comp.config.rdynamic;
2864 const is_obj = comp.config.output_mode == .Obj;
28653031
28663032 try wasm.globals.ensureUnusedCapacity(gpa, 1);
28673033
......@@ -2888,19 +3054,29 @@ fn markGlobal(
28883054 try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm));
28893055 }
28903056 } else {
2891 const gop = wasm.globals.getOrPutAssumeCapacity(import.resolution);
3057 try markGlobal(wasm, import.resolution.unpack(wasm).object_global);
3058 }
3059}
28923060
2893 if (!is_obj and import.flags.isExported(rdynamic)) try wasm.global_exports.append(gpa, .{
2894 .name = name,
2895 .global_index = @enumFromInt(gop.index),
2896 });
3061fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex) Allocator.Error!void {
3062 const comp = wasm.base.comp;
3063 const gpa = comp.gpa;
3064 const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i));
3065 if (gop.found_existing) return;
28973066
2898 for (try wasm.globalResolutionRelocSlice(import.resolution)) |reloc|
2899 try wasm.markReloc(reloc);
2900 }
3067 const rdynamic = comp.config.rdynamic;
3068 const is_obj = comp.config.output_mode == .Obj;
3069 const global = i.ptr(wasm);
3070
3071 if (!is_obj and global.flags.isExported(rdynamic)) try wasm.global_exports.append(gpa, .{
3072 .name = global.name.unwrap().?,
3073 .global_index = @enumFromInt(gop.index),
3074 });
3075
3076 try wasm.markRelocations(global.relocations(wasm));
29013077}
29023078
2903fn markTable(
3079fn markTableImport(
29043080 wasm: *Wasm,
29053081 name: String,
29063082 import: *TableImport,
......@@ -2927,22 +3103,101 @@ fn markTable(
29273103 }
29283104}
29293105
2930fn globalResolutionRelocSlice(wasm: *Wasm, resolution: GlobalImport.Resolution) ![]const ObjectRelocation {
2931 assert(resolution != .unresolved);
2932 _ = wasm;
2933 @panic("TODO");
2934}
3106fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) Allocator.Error!void {
3107 const segment = segment_index.ptr(wasm);
3108 if (segment.flags.alive) return;
3109 segment.flags.alive = true;
29353110
2936fn functionResolutionRelocSlice(wasm: *Wasm, resolution: FunctionImport.Resolution) ![]const ObjectRelocation {
2937 assert(resolution != .unresolved);
2938 _ = wasm;
2939 @panic("TODO");
3111 try wasm.markRelocations(segment.relocations(wasm));
29403112}
29413113
2942fn markReloc(wasm: *Wasm, reloc: ObjectRelocation) !void {
2943 _ = wasm;
2944 _ = reloc;
2945 @panic("TODO");
3114fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) Allocator.Error!void {
3115 for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, *pointee, offset| {
3116 if (offset >= relocs.end) break;
3117 switch (tag) {
3118 .function_import_index_leb,
3119 .function_import_index_i32,
3120 .function_import_offset_i32,
3121 .function_import_offset_i64,
3122 .table_import_index_sleb,
3123 .table_import_index_i32,
3124 .table_import_index_sleb64,
3125 .table_import_index_i64,
3126 .table_import_index_rel_sleb,
3127 .table_import_index_rel_sleb64,
3128 => {
3129 const name = pointee.symbol_name;
3130 const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);
3131 try markFunctionImport(wasm, name, i.value(wasm), i);
3132 },
3133 .global_import_index_leb, .global_import_index_i32 => {
3134 const name = pointee.symbol_name;
3135 const i: GlobalImport.Index = @enumFromInt(wasm.object_global_imports.getIndex(name).?);
3136 try markGlobalImport(wasm, name, i.value(wasm), i);
3137 },
3138 .table_import_number_leb => {
3139 const name = pointee.symbol_name;
3140 const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?);
3141 try markTableImport(wasm, name, i.value(wasm), i);
3142 },
3143
3144 .function_index_leb,
3145 .function_index_i32,
3146 .function_offset_i32,
3147 .function_offset_i64,
3148 .table_index_sleb,
3149 .table_index_i32,
3150 .table_index_sleb64,
3151 .table_index_i64,
3152 .table_index_rel_sleb,
3153 .table_index_rel_sleb64,
3154 => try markFunction(wasm, pointee.function),
3155 .global_index_leb,
3156 .global_index_i32,
3157 => try markGlobal(wasm, pointee.global),
3158 .table_number_leb,
3159 => try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(pointee.table), {}),
3160
3161 .section_offset_i32 => {
3162 log.warn("TODO: ensure section {d} is included in output", .{pointee.section});
3163 },
3164 .memory_addr_import_leb,
3165 .memory_addr_import_sleb,
3166 .memory_addr_import_i32,
3167 .memory_addr_import_rel_sleb,
3168 .memory_addr_import_leb64,
3169 .memory_addr_import_sleb64,
3170 .memory_addr_import_i64,
3171 .memory_addr_import_rel_sleb64,
3172 .memory_addr_import_tls_sleb,
3173 .memory_addr_import_locrel_i32,
3174 .memory_addr_import_tls_sleb64,
3175 => {
3176 const name = pointee.symbol_name;
3177 if (name == wasm.preloaded_strings.__heap_end or
3178 name == wasm.preloaded_strings.__heap_base)
3179 {
3180 continue;
3181 }
3182 log.warn("TODO: ensure data symbol {s} is included in output", .{name.slice(wasm)});
3183 },
3184
3185 .memory_addr_leb,
3186 .memory_addr_sleb,
3187 .memory_addr_i32,
3188 .memory_addr_rel_sleb,
3189 .memory_addr_leb64,
3190 .memory_addr_sleb64,
3191 .memory_addr_i64,
3192 .memory_addr_rel_sleb64,
3193 .memory_addr_tls_sleb,
3194 .memory_addr_locrel_i32,
3195 .memory_addr_tls_sleb64,
3196 => try markDataSegment(wasm, pointee.data.ptr(wasm).segment),
3197
3198 .type_index_leb => continue,
3199 }
3200 }
29463201}
29473202
29483203pub fn flushModule(
src/link/Wasm/Flush.zig-142
......@@ -1398,148 +1398,6 @@ fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {
13981398// try binary_bytes.insertSlice(table_offset, &buf);
13991399//}
14001400
1401///// Resolves the relocations within the atom, writing the new value
1402///// at the calculated offset.
1403//fn resolveAtomRelocs(wasm: *const Wasm, atom: *Atom) void {
1404// const symbol_name = wasm.symbolLocName(atom.symbolLoc());
1405// log.debug("resolving {d} relocs in atom '{s}'", .{ atom.relocs.len, symbol_name });
1406//
1407// for (atom.relocSlice(wasm)) |reloc| {
1408// const value = atomRelocationValue(wasm, atom, reloc);
1409// log.debug("relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
1410// wasm.symbolLocName(.{
1411// .file = atom.file,
1412// .index = @enumFromInt(reloc.index),
1413// }),
1414// symbol_name,
1415// reloc.offset,
1416// value,
1417// });
1418//
1419// switch (reloc.tag) {
1420// .TABLE_INDEX_I32,
1421// .FUNCTION_OFFSET_I32,
1422// .GLOBAL_INDEX_I32,
1423// .MEMORY_ADDR_I32,
1424// .SECTION_OFFSET_I32,
1425// => mem.writeInt(u32, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..4], @as(u32, @truncate(value)), .little),
1426//
1427// .TABLE_INDEX_I64,
1428// .MEMORY_ADDR_I64,
1429// => mem.writeInt(u64, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..8], value, .little),
1430//
1431// .GLOBAL_INDEX_LEB,
1432// .EVENT_INDEX_LEB,
1433// .FUNCTION_INDEX_LEB,
1434// .MEMORY_ADDR_LEB,
1435// .MEMORY_ADDR_SLEB,
1436// .TABLE_INDEX_SLEB,
1437// .TABLE_NUMBER_LEB,
1438// .TYPE_INDEX_LEB,
1439// .MEMORY_ADDR_TLS_SLEB,
1440// => leb.writeUnsignedFixed(5, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..5], @as(u32, @truncate(value))),
1441//
1442// .MEMORY_ADDR_LEB64,
1443// .MEMORY_ADDR_SLEB64,
1444// .TABLE_INDEX_SLEB64,
1445// .MEMORY_ADDR_TLS_SLEB64,
1446// => leb.writeUnsignedFixed(10, atom.code.slice(wasm)[reloc.offset - atom.original_offset ..][0..10], value),
1447// }
1448// }
1449//}
1450
1451///// From a given `relocation` will return the new value to be written.
1452///// All values will be represented as a `u64` as all values can fit within it.
1453///// The final value must be casted to the correct size.
1454//fn atomRelocationValue(wasm: *const Wasm, atom: *const Atom, relocation: *const Relocation) u64 {
1455// if (relocation.tag == .TYPE_INDEX_LEB) {
1456// // Eagerly resolved when parsing the object file.
1457// if (true) @panic("TODO the eager resolve when parsing");
1458// return relocation.index;
1459// }
1460// const target_loc = wasm.symbolLocFinalLoc(.{
1461// .file = atom.file,
1462// .index = @enumFromInt(relocation.index),
1463// });
1464// const symbol = wasm.finalSymbolByLoc(target_loc);
1465// if (symbol.tag != .section and !symbol.flags.alive) {
1466// const val = atom.tombstone(wasm) orelse relocation.addend;
1467// return @bitCast(val);
1468// }
1469// return switch (relocation.tag) {
1470// .FUNCTION_INDEX_LEB => if (symbol.flags.undefined)
1471// @intFromEnum(symbol.pointee.function_import)
1472// else
1473// @intFromEnum(symbol.pointee.function) + f.function_imports.items.len,
1474// .TABLE_NUMBER_LEB => if (symbol.flags.undefined)
1475// @intFromEnum(symbol.pointee.table_import)
1476// else
1477// @intFromEnum(symbol.pointee.table) + wasm.table_imports.items.len,
1478// .TABLE_INDEX_I32,
1479// .TABLE_INDEX_I64,
1480// .TABLE_INDEX_SLEB,
1481// .TABLE_INDEX_SLEB64,
1482// => wasm.indirect_function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
1483//
1484// .TYPE_INDEX_LEB => unreachable, // handled above
1485// .GLOBAL_INDEX_I32, .GLOBAL_INDEX_LEB => if (symbol.flags.undefined)
1486// @intFromEnum(symbol.pointee.global_import)
1487// else
1488// @intFromEnum(symbol.pointee.global) + f.global_imports.items.len,
1489//
1490// .MEMORY_ADDR_I32,
1491// .MEMORY_ADDR_I64,
1492// .MEMORY_ADDR_LEB,
1493// .MEMORY_ADDR_LEB64,
1494// .MEMORY_ADDR_SLEB,
1495// .MEMORY_ADDR_SLEB64,
1496// => {
1497// assert(symbol.tag == .data);
1498// if (symbol.flags.undefined) return 0;
1499// const va: i33 = symbol.virtual_address;
1500// return @intCast(va + relocation.addend);
1501// },
1502// .EVENT_INDEX_LEB => @panic("TODO: expose this as an error, events are unsupported"),
1503// .SECTION_OFFSET_I32 => {
1504// const target_atom_index = wasm.symbol_atom.get(target_loc).?;
1505// const target_atom = wasm.getAtom(target_atom_index);
1506// const rel_value: i33 = target_atom.offset;
1507// return @intCast(rel_value + relocation.addend);
1508// },
1509// .FUNCTION_OFFSET_I32 => {
1510// if (symbol.flags.undefined) {
1511// const val = atom.tombstone(wasm) orelse relocation.addend;
1512// return @bitCast(val);
1513// }
1514// const target_atom_index = wasm.symbol_atom.get(target_loc).?;
1515// const target_atom = wasm.getAtom(target_atom_index);
1516// const rel_value: i33 = target_atom.offset;
1517// return @intCast(rel_value + relocation.addend);
1518// },
1519// .MEMORY_ADDR_TLS_SLEB,
1520// .MEMORY_ADDR_TLS_SLEB64,
1521// => {
1522// const va: i33 = symbol.virtual_address;
1523// return @intCast(va + relocation.addend);
1524// },
1525// };
1526//}
1527
1528///// For a given `Atom` returns whether it has a tombstone value or not.
1529///// This defines whether we want a specific value when a section is dead.
1530//fn tombstone(atom: Atom, wasm: *const Wasm) ?i64 {
1531// const atom_name = wasm.finalSymbolByLoc(atom.symbolLoc()).name;
1532// if (atom_name == wasm.custom_sections.@".debug_ranges".name or
1533// atom_name == wasm.custom_sections.@".debug_loc".name)
1534// {
1535// return -2;
1536// } else if (mem.startsWith(u8, atom_name.slice(wasm), ".debug_")) {
1537// return -1;
1538// } else {
1539// return null;
1540// }
1541//}
1542
15431401fn uleb128size(x: u32) u32 {
15441402 var value = x;
15451403 var size: u32 = 0;
src/link/Wasm/Object.zig+206-71
......@@ -36,12 +36,16 @@ global_imports: RelativeSlice,
3636table_imports: RelativeSlice,
3737/// Points into Wasm object_custom_segments
3838custom_segments: RelativeSlice,
39/// For calculating local section index from `Wasm.ObjectSectionIndex`.
40local_section_index_base: u32,
4139/// Points into Wasm object_init_funcs
4240init_funcs: RelativeSlice,
4341/// Points into Wasm object_comdats
4442comdats: RelativeSlice,
43/// Guaranteed to be non-null when functions has nonzero length.
44code_section_index: ?Wasm.ObjectSectionIndex,
45/// Guaranteed to be non-null when globals has nonzero length.
46global_section_index: ?Wasm.ObjectSectionIndex,
47/// Guaranteed to be non-null when data segments has nonzero length.
48data_section_index: ?Wasm.ObjectSectionIndex,
4549
4650pub const RelativeSlice = struct {
4751 off: u32,
......@@ -52,7 +56,8 @@ pub const SegmentInfo = struct {
5256 name: Wasm.String,
5357 flags: Flags,
5458
55 const Flags = packed struct(u32) {
59 /// Matches the ABI.
60 pub const Flags = packed struct(u32) {
5661 /// Signals that the segment contains only null terminated strings allowing
5762 /// the linker to perform merging.
5863 strings: bool,
......@@ -101,6 +106,37 @@ pub const SubsectionType = enum(u8) {
101106 symbol_table = 8,
102107};
103108
109/// Specified by https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
110pub const RelocationType = enum(u8) {
111 function_index_leb = 0,
112 table_index_sleb = 1,
113 table_index_i32 = 2,
114 memory_addr_leb = 3,
115 memory_addr_sleb = 4,
116 memory_addr_i32 = 5,
117 type_index_leb = 6,
118 global_index_leb = 7,
119 function_offset_i32 = 8,
120 section_offset_i32 = 9,
121 event_index_leb = 10,
122 memory_addr_rel_sleb = 11,
123 table_index_rel_sleb = 12,
124 global_index_i32 = 13,
125 memory_addr_leb64 = 14,
126 memory_addr_sleb64 = 15,
127 memory_addr_i64 = 16,
128 memory_addr_rel_sleb64 = 17,
129 table_index_sleb64 = 18,
130 table_index_i64 = 19,
131 table_number_leb = 20,
132 memory_addr_tls_sleb = 21,
133 function_offset_i64 = 22,
134 memory_addr_locrel_i32 = 23,
135 table_index_rel_sleb64 = 24,
136 memory_addr_tls_sleb64 = 25,
137 function_index_i32 = 26,
138};
139
104140pub const Symbol = struct {
105141 flags: Wasm.SymbolFlags,
106142 name: Wasm.OptionalString,
......@@ -245,7 +281,8 @@ pub fn parse(
245281 const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len);
246282 const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len);
247283 const local_section_index_base = wasm.object_total_sections;
248 const source_location: Wasm.SourceLocation = .fromObject(@enumFromInt(wasm.objects.items.len), wasm);
284 const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len);
285 const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm);
249286
250287 ss.clear();
251288
......@@ -254,6 +291,9 @@ pub fn parse(
254291 var saw_linking_section = false;
255292 var has_tls = false;
256293 var table_import_symbol_count: usize = 0;
294 var code_section_index: ?Wasm.ObjectSectionIndex = null;
295 var global_section_index: ?Wasm.ObjectSectionIndex = null;
296 var data_section_index: ?Wasm.ObjectSectionIndex = null;
257297 while (pos < bytes.len) : (wasm.object_total_sections += 1) {
258298 const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);
259299
......@@ -365,7 +405,8 @@ pub fn parse(
365405 switch (tag) {
366406 .data => {
367407 const name, pos = readBytes(bytes, pos);
368 symbol.name = (try wasm.internString(name)).toOptional();
408 const interned_name = try wasm.internString(name);
409 symbol.name = interned_name.toOptional();
369410 if (symbol.flags.undefined) {
370411 symbol.pointee = .data_import;
371412 } else {
......@@ -376,7 +417,7 @@ pub fn parse(
376417 .segment = @enumFromInt(data_segment_start + segment_index),
377418 .offset = segment_offset,
378419 .size = size,
379 .name = symbol.name,
420 .name = interned_name,
380421 .flags = symbol.flags,
381422 });
382423 symbol.pointee = .{
......@@ -468,7 +509,7 @@ pub fn parse(
468509 var prev_offset: u32 = 0;
469510 try wasm.object_relocations.ensureUnusedCapacity(gpa, count);
470511 for (0..count) |_| {
471 const tag: Wasm.ObjectRelocation.Tag = @enumFromInt(bytes[pos]);
512 const tag: RelocationType = @enumFromInt(bytes[pos]);
472513 pos += 1;
473514 const offset, pos = readLeb(u32, bytes, pos);
474515 const index, pos = readLeb(u32, bytes, pos);
......@@ -477,75 +518,134 @@ pub fn parse(
477518 return diags.failParse(path, "relocation entries not sorted by offset", .{});
478519 prev_offset = offset;
479520
521 const sym = &ss.symbol_table.items[index];
522
480523 switch (tag) {
481 .MEMORY_ADDR_LEB,
482 .MEMORY_ADDR_SLEB,
483 .MEMORY_ADDR_I32,
484 .MEMORY_ADDR_REL_SLEB,
485 .MEMORY_ADDR_LEB64,
486 .MEMORY_ADDR_SLEB64,
487 .MEMORY_ADDR_I64,
488 .MEMORY_ADDR_REL_SLEB64,
489 .MEMORY_ADDR_TLS_SLEB,
490 .MEMORY_ADDR_LOCREL_I32,
491 .MEMORY_ADDR_TLS_SLEB64,
524 .memory_addr_leb,
525 .memory_addr_sleb,
526 .memory_addr_i32,
527 .memory_addr_rel_sleb,
528 .memory_addr_leb64,
529 .memory_addr_sleb64,
530 .memory_addr_i64,
531 .memory_addr_rel_sleb64,
532 .memory_addr_tls_sleb,
533 .memory_addr_locrel_i32,
534 .memory_addr_tls_sleb64,
492535 => {
493536 const addend: i32, pos = readLeb(i32, bytes, pos);
494 wasm.object_relocations.appendAssumeCapacity(.{
495 .tag = tag,
496 .offset = offset,
497 .pointee = .{ .data = ss.symbol_table.items[index].pointee.data },
498 .addend = addend,
537 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
538 .data => |data| .{
539 .tag = .fromType(tag),
540 .offset = offset,
541 .pointee = .{ .data = data },
542 .addend = addend,
543 },
544 .data_import => .{
545 .tag = .fromTypeImport(tag),
546 .offset = offset,
547 .pointee = .{ .symbol_name = sym.name.unwrap().? },
548 .addend = addend,
549 },
550 else => unreachable,
499551 });
500552 },
501 .FUNCTION_OFFSET_I32,
502 .FUNCTION_OFFSET_I64,
503 => {
553 .function_offset_i32, .function_offset_i64 => {
504554 const addend: i32, pos = readLeb(i32, bytes, pos);
505 wasm.object_relocations.appendAssumeCapacity(.{
506 .tag = tag,
507 .offset = offset,
508 .pointee = .{ .function = ss.symbol_table.items[index].pointee.function },
509 .addend = addend,
555 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
556 .function => .{
557 .tag = .fromType(tag),
558 .offset = offset,
559 .pointee = .{ .function = sym.pointee.function },
560 .addend = addend,
561 },
562 .function_import => .{
563 .tag = .fromTypeImport(tag),
564 .offset = offset,
565 .pointee = .{ .symbol_name = sym.name.unwrap().? },
566 .addend = addend,
567 },
568 else => unreachable,
510569 });
511570 },
512 .SECTION_OFFSET_I32 => {
571 .section_offset_i32 => {
513572 const addend: i32, pos = readLeb(i32, bytes, pos);
514573 wasm.object_relocations.appendAssumeCapacity(.{
515 .tag = tag,
574 .tag = .section_offset_i32,
516575 .offset = offset,
517 .pointee = .{ .section = ss.symbol_table.items[index].pointee.section },
576 .pointee = .{ .section = sym.pointee.section },
518577 .addend = addend,
519578 });
520579 },
521 .TYPE_INDEX_LEB => {
580 .type_index_leb => {
522581 wasm.object_relocations.appendAssumeCapacity(.{
523 .tag = tag,
582 .tag = .type_index_leb,
524583 .offset = offset,
525584 .pointee = .{ .type_index = ss.func_types.items[index] },
526585 .addend = undefined,
527586 });
528587 },
529 .FUNCTION_INDEX_LEB,
530 .FUNCTION_INDEX_I32,
531 .GLOBAL_INDEX_LEB,
532 .GLOBAL_INDEX_I32,
533 .TABLE_INDEX_SLEB,
534 .TABLE_INDEX_I32,
535 .TABLE_INDEX_SLEB64,
536 .TABLE_INDEX_I64,
537 .TABLE_NUMBER_LEB,
538 .TABLE_INDEX_REL_SLEB,
539 .TABLE_INDEX_REL_SLEB64,
540 .TAG_INDEX_LEB,
588 .function_index_leb,
589 .function_index_i32,
590 .table_index_sleb,
591 .table_index_i32,
592 .table_index_sleb64,
593 .table_index_i64,
594 .table_index_rel_sleb,
595 .table_index_rel_sleb64,
541596 => {
542 wasm.object_relocations.appendAssumeCapacity(.{
543 .tag = tag,
544 .offset = offset,
545 .pointee = .{ .symbol_name = ss.symbol_table.items[index].name.unwrap().? },
546 .addend = undefined,
597 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
598 .function => .{
599 .tag = .fromType(tag),
600 .offset = offset,
601 .pointee = .{ .function = sym.pointee.function },
602 .addend = undefined,
603 },
604 .function_import => .{
605 .tag = .fromTypeImport(tag),
606 .offset = offset,
607 .pointee = .{ .symbol_name = sym.name.unwrap().? },
608 .addend = undefined,
609 },
610 else => unreachable,
611 });
612 },
613 .global_index_leb, .global_index_i32 => {
614 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
615 .global => .{
616 .tag = .fromType(tag),
617 .offset = offset,
618 .pointee = .{ .global = sym.pointee.global },
619 .addend = undefined,
620 },
621 .global_import => .{
622 .tag = .fromTypeImport(tag),
623 .offset = offset,
624 .pointee = .{ .symbol_name = sym.name.unwrap().? },
625 .addend = undefined,
626 },
627 else => unreachable,
628 });
629 },
630
631 .table_number_leb => {
632 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
633 .table => .{
634 .tag = .fromType(tag),
635 .offset = offset,
636 .pointee = .{ .table = sym.pointee.table },
637 .addend = undefined,
638 },
639 .table_import => .{
640 .tag = .fromTypeImport(tag),
641 .offset = offset,
642 .pointee = .{ .symbol_name = sym.name.unwrap().? },
643 .addend = undefined,
644 },
645 else => unreachable,
547646 });
548647 },
648 .event_index_leb => return diags.failParse(path, "unsupported relocation: R_WASM_EVENT_INDEX_LEB", .{}),
549649 }
550650 }
551651
......@@ -684,11 +784,17 @@ pub fn parse(
684784 }
685785 },
686786 .global => {
787 if (global_section_index != null)
788 return diags.failParse(path, "object has more than one global section", .{});
789 global_section_index = section_index;
790
791 const section_start = pos;
687792 const globals_len, pos = readLeb(u32, bytes, pos);
688793 for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| {
689794 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
690795 const mutable = bytes[pos] == 0x01;
691796 pos += 1;
797 const init_start = pos;
692798 const expr, pos = try readInit(wasm, bytes, pos);
693799 global.* = .{
694800 .name = .none,
......@@ -699,6 +805,9 @@ pub fn parse(
699805 },
700806 },
701807 .expr = expr,
808 .object_index = object_index,
809 .offset = @intCast(init_start - section_start),
810 .size = @intCast(pos - init_start),
702811 };
703812 }
704813 },
......@@ -728,10 +837,14 @@ pub fn parse(
728837 start_function = @enumFromInt(functions_start + index);
729838 },
730839 .element => {
731 log.warn("unimplemented: element section in {}", .{path});
840 log.warn("unimplemented: element section in {} {s}", .{ path, archive_member_name.? });
732841 pos = section_end;
733842 },
734843 .code => {
844 if (code_section_index != null)
845 return diags.failParse(path, "object has more than one code section", .{});
846 code_section_index = section_index;
847
735848 const start = pos;
736849 const count, pos = readLeb(u32, bytes, pos);
737850 for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| {
......@@ -745,12 +858,16 @@ pub fn parse(
745858 .type_index = undefined, // populated from func_types
746859 .code = payload,
747860 .offset = offset,
748 .section_index = section_index,
749 .source_location = source_location,
861 .object_index = object_index,
750862 };
751863 }
752864 },
753865 .data => {
866 if (data_section_index != null)
867 return diags.failParse(path, "object has more than one data section", .{});
868 data_section_index = section_index;
869
870 const section_start = pos;
754871 const count, pos = readLeb(u32, bytes, pos);
755872 for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| {
756873 const flags, pos = readEnum(DataSegmentFlags, bytes, pos);
......@@ -761,12 +878,17 @@ pub fn parse(
761878 //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos };
762879 if (flags != .passive) pos = try skipInit(bytes, pos);
763880 const data_len, pos = readLeb(u32, bytes, pos);
881 const segment_start = pos;
764882 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]);
765883 pos += data_len;
766884 elem.* = .{
767885 .payload = payload,
768 .name = .none, // Populated from symbol table
769 .flags = .{}, // Populated from symbol table and segment_info
886 .name = .none, // Populated from segment_info
887 .flags = .{
888 .is_passive = flags == .passive,
889 }, // Remainder populated from segment_info
890 .offset = @intCast(segment_start - section_start),
891 .object_index = object_index,
770892 };
771893 }
772894 },
......@@ -1033,8 +1155,10 @@ pub fn parse(
10331155 }
10341156 },
10351157 .data_import => {
1036 const name = symbol.name.unwrap().?;
1037 log.warn("TODO data import '{s}'", .{name.slice(wasm)});
1158 if (symbol.flags.undefined and symbol.flags.binding == .local) {
1159 const name = symbol.name.slice(wasm).?;
1160 diags.addParseError(path, "local symbol '{s}' references import", .{name});
1161 }
10381162 },
10391163 .data => continue, // `wasm.object_datas` has already been populated.
10401164 };
......@@ -1055,16 +1179,21 @@ pub fn parse(
10551179 }
10561180
10571181 // Apply segment_info.
1058 for (wasm.object_data_segments.items[data_segment_start..], ss.segment_info.items) |*data, info| {
1182 const data_segments = wasm.object_data_segments.items[data_segment_start..];
1183 if (data_segments.len != ss.segment_info.items.len) {
1184 return diags.failParse(path, "expected {d} segment_info entries; found {d}", .{
1185 data_segments.len, ss.segment_info.items.len,
1186 });
1187 }
1188 for (data_segments, ss.segment_info.items) |*data, info| {
10591189 data.name = info.name.toOptional();
1060 data.flags.strings = info.flags.strings;
1061 data.flags.tls = data.flags.tls or info.flags.tls;
1062 data.flags.no_strip = info.flags.retain;
1063 data.flags.alignment = info.flags.alignment;
1064 if (data.flags.undefined and data.flags.binding == .local) {
1065 const name = info.name.slice(wasm);
1066 diags.addParseError(path, "local symbol '{s}' references import", .{name});
1067 }
1190 data.flags = .{
1191 .is_passive = data.flags.is_passive,
1192 .strings = info.flags.strings,
1193 .tls = info.flags.tls,
1194 .retain = info.flags.retain,
1195 .alignment = info.flags.alignment,
1196 };
10681197 }
10691198
10701199 // Check for indirect function table in case of an MVP object file.
......@@ -1106,6 +1235,10 @@ pub fn parse(
11061235 });
11071236 }
11081237
1238 const functions_len: u32 = @intCast(wasm.object_functions.items.len - functions_start);
1239 if (functions_len > 0 and code_section_index == null)
1240 return diags.failParse(path, "code section missing ({d} functions)", .{functions_len});
1241
11091242 return .{
11101243 .version = version,
11111244 .path = path,
......@@ -1114,7 +1247,7 @@ pub fn parse(
11141247 .features = features,
11151248 .functions = .{
11161249 .off = functions_start,
1117 .len = @intCast(wasm.object_functions.items.len - functions_start),
1250 .len = functions_len,
11181251 },
11191252 .function_imports = .{
11201253 .off = function_imports_start,
......@@ -1140,7 +1273,9 @@ pub fn parse(
11401273 .off = custom_segment_start,
11411274 .len = @intCast(wasm.object_custom_segments.entries.len - custom_segment_start),
11421275 },
1143 .local_section_index_base = local_section_index_base,
1276 .code_section_index = code_section_index,
1277 .global_section_index = global_section_index,
1278 .data_section_index = data_section_index,
11441279 };
11451280}
11461281