authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-09-08 16:57:23+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-09-08 16:57:23+02:00
logab4b26d8a6a36ccf75af8e25f0a1f7b88063b76f
tree1a2922f3412bab348087e53c6962c8d7baa33b14
parent6a62a15ecde10300f6281e2d49fed34031d5f68a
parenta8d137d05ae36870d4e896cf5e37b591d9fa219c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12770 from Luukdegram/wasm-dwarf

wasm-linker: implement linking debug-information

5 files changed, 430 insertions(+), 197 deletions(-)

src/arch/wasm/CodeGen.zig+4
...@@ -666,6 +666,10 @@ pub fn deinit(self: *Self) void {...@@ -666,6 +666,10 @@ pub fn deinit(self: *Self) void {
666 self.locals.deinit(self.gpa);666 self.locals.deinit(self.gpa);
667 self.mir_instructions.deinit(self.gpa);667 self.mir_instructions.deinit(self.gpa);
668 self.mir_extra.deinit(self.gpa);668 self.mir_extra.deinit(self.gpa);
669 self.free_locals_i32.deinit(self.gpa);
670 self.free_locals_i64.deinit(self.gpa);
671 self.free_locals_f32.deinit(self.gpa);
672 self.free_locals_f64.deinit(self.gpa);
669 self.* = undefined;673 self.* = undefined;
670}674}
671675
src/link/Dwarf.zig+36-37
...@@ -861,7 +861,8 @@ pub fn commitDeclState(...@@ -861,7 +861,8 @@ pub fn commitDeclState(
861 },861 },
862 .wasm => {862 .wasm => {
863 const wasm_file = file.cast(File.Wasm).?;863 const wasm_file = file.cast(File.Wasm).?;
864 writeDbgLineNopsBuffered(wasm_file.debug_line.items, src_fn.off, 0, &.{}, src_fn.len);864 const debug_line = wasm_file.debug_line_atom.?.code;
865 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
865 },866 },
866 else => unreachable,867 else => unreachable,
867 }868 }
...@@ -972,23 +973,21 @@ pub fn commitDeclState(...@@ -972,23 +973,21 @@ pub fn commitDeclState(
972 },973 },
973 .wasm => {974 .wasm => {
974 const wasm_file = file.cast(File.Wasm).?;975 const wasm_file = file.cast(File.Wasm).?;
975 const segment_index = try wasm_file.getDebugLineIndex();976 const atom = wasm_file.debug_line_atom.?;
976 const segment = &wasm_file.segments.items[segment_index];977 const debug_line = &atom.code;
977 const debug_line = &wasm_file.debug_line;978 const segment_size = debug_line.items.len;
978 if (needed_size != segment.size) {979 if (needed_size != segment_size) {
979 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});980 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
980 if (needed_size > segment.size) {981 if (needed_size > segment_size) {
981 log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size});982 log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
982 try debug_line.resize(self.allocator, needed_size);983 try debug_line.resize(self.allocator, needed_size);
983 mem.set(u8, debug_line.items[segment.size..], 0);984 mem.set(u8, debug_line.items[segment_size..], 0);
984 }985 }
985 segment.size = needed_size;
986 debug_line.items.len = needed_size;986 debug_line.items.len = needed_size;
987 }987 }
988 const offset = segment.offset + src_fn.off;
989 writeDbgLineNopsBuffered(988 writeDbgLineNopsBuffered(
990 debug_line.items,989 debug_line.items,
991 offset,990 src_fn.off,
992 prev_padding_size,991 prev_padding_size,
993 dbg_line_buffer.items,992 dbg_line_buffer.items,
994 next_padding_size,993 next_padding_size,
...@@ -1146,10 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3...@@ -1146,10 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
1146 },1145 },
1147 .wasm => {1146 .wasm => {
1148 const wasm_file = file.cast(File.Wasm).?;1147 const wasm_file = file.cast(File.Wasm).?;
1149 const segment_index = try wasm_file.getDebugInfoIndex();1148 const debug_info = &wasm_file.debug_info_atom.?.code;
1150 const segment = &wasm_file.segments.items[segment_index];1149 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
1151 const offset = segment.offset + atom.off;
1152 try writeDbgInfoNopsToArrayList(gpa, &wasm_file.debug_info, offset, 0, &.{0}, atom.len, false);
1153 },1150 },
1154 else => unreachable,1151 else => unreachable,
1155 }1152 }
...@@ -1276,27 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1276,27 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1276 },1273 },
1277 .wasm => {1274 .wasm => {
1278 const wasm_file = file.cast(File.Wasm).?;1275 const wasm_file = file.cast(File.Wasm).?;
1279 const segment_index = try wasm_file.getDebugInfoIndex();1276 const info_atom = wasm_file.debug_info_atom.?;
1280 const segment = &wasm_file.segments.items[segment_index];1277 const debug_info = &info_atom.code;
1281 const debug_info = &wasm_file.debug_info;1278 const segment_size = debug_info.items.len;
1282 if (needed_size != segment.size) {1279 if (needed_size != segment_size) {
1283 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});1280 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1284 if (needed_size > segment.size) {1281 if (needed_size > segment_size) {
1285 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size});1282 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
1286 try debug_info.resize(self.allocator, needed_size);1283 try debug_info.resize(self.allocator, needed_size);
1287 mem.set(u8, debug_info.items[segment.size..], 0);1284 mem.set(u8, debug_info.items[segment_size..], 0);
1288 }1285 }
1289 segment.size = needed_size;
1290 debug_info.items.len = needed_size;1286 debug_info.items.len = needed_size;
1291 }1287 }
1292 const offset = segment.offset + atom.off;
1293 log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{1288 log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{
1294 debug_info.items.len, offset, dbg_info_buf.len, next_padding_size,1289 debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
1295 });1290 });
1296 try writeDbgInfoNopsToArrayList(1291 try writeDbgInfoNopsToArrayList(
1297 gpa,1292 gpa,
1298 debug_info,1293 debug_info,
1299 offset,1294 atom.off,
1300 prev_padding_size,1295 prev_padding_size,
1301 dbg_info_buf,1296 dbg_info_buf,
1302 next_padding_size,1297 next_padding_size,
...@@ -1337,10 +1332,9 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)...@@ -1337,10 +1332,9 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
1337 },1332 },
1338 .wasm => {1333 .wasm => {
1339 const wasm_file = file.cast(File.Wasm).?;1334 const wasm_file = file.cast(File.Wasm).?;
1340 const segment_index = wasm_file.getDebugLineIndex() catch unreachable;1335 const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
1341 const segment = wasm_file.segments.items[segment_index];1336 const atom = wasm_file.debug_line_atom.?;
1342 const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();1337 mem.copy(u8, atom.code.items[offset..], &data);
1343 mem.copy(u8, wasm_file.debug_line.items[offset..], &data);
1344 },1338 },
1345 else => unreachable,1339 else => unreachable,
1346 }1340 }
...@@ -1576,8 +1570,9 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {...@@ -1576,8 +1570,9 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
1576 },1570 },
1577 .wasm => {1571 .wasm => {
1578 const wasm_file = file.cast(File.Wasm).?;1572 const wasm_file = file.cast(File.Wasm).?;
1579 try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size);1573 const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code;
1580 mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf);1574 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);
1575 mem.copy(u8, debug_abbrev.items, &abbrev_buf);
1581 },1576 },
1582 else => unreachable,1577 else => unreachable,
1583 }1578 }
...@@ -1687,7 +1682,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6...@@ -1687,7 +1682,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
1687 },1682 },
1688 .wasm => {1683 .wasm => {
1689 const wasm_file = file.cast(File.Wasm).?;1684 const wasm_file = file.cast(File.Wasm).?;
1690 try writeDbgInfoNopsToArrayList(self.allocator, &wasm_file.debug_info, 0, 0, di_buf.items, jmp_amt, false);1685 const debug_info = &wasm_file.debug_info_atom.?.code;
1686 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
1691 },1687 },
1692 else => unreachable,1688 else => unreachable,
1693 }1689 }
...@@ -2016,8 +2012,9 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {...@@ -2016,8 +2012,9 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
2016 },2012 },
2017 .wasm => {2013 .wasm => {
2018 const wasm_file = file.cast(File.Wasm).?;2014 const wasm_file = file.cast(File.Wasm).?;
2019 try wasm_file.debug_aranges.resize(wasm_file.base.allocator, needed_size);2015 const debug_ranges = &wasm_file.debug_ranges_atom.?.code;
2020 mem.copy(u8, wasm_file.debug_aranges.items, di_buf.items);2016 try debug_ranges.resize(wasm_file.base.allocator, needed_size);
2017 mem.copy(u8, debug_ranges.items, di_buf.items);
2021 },2018 },
2022 else => unreachable,2019 else => unreachable,
2023 }2020 }
...@@ -2139,7 +2136,8 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2139,7 +2136,8 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
2139 },2136 },
2140 .wasm => {2137 .wasm => {
2141 const wasm_file = file.cast(File.Wasm).?;2138 const wasm_file = file.cast(File.Wasm).?;
2142 writeDbgLineNopsBuffered(wasm_file.debug_line.items, 0, 0, di_buf.items, jmp_amt);2139 const debug_line = wasm_file.debug_line_atom.?.code;
2140 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
2143 },2141 },
2144 else => unreachable,2142 else => unreachable,
2145 }2143 }
...@@ -2287,7 +2285,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2287,7 +2285,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
2287 },2285 },
2288 .wasm => {2286 .wasm => {
2289 const wasm_file = file.cast(File.Wasm).?;2287 const wasm_file = file.cast(File.Wasm).?;
2290 mem.copy(u8, wasm_file.debug_info.items[reloc.atom.off + reloc.offset ..], &buf);2288 const debug_info = wasm_file.debug_info_atom.?.code;
2289 mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
2291 },2290 },
2292 else => unreachable,2291 else => unreachable,
2293 }2292 }
src/link/Wasm.zig+280-119
...@@ -67,6 +67,18 @@ code_section_index: ?u32 = null,...@@ -67,6 +67,18 @@ code_section_index: ?u32 = null,
67debug_info_index: ?u32 = null,67debug_info_index: ?u32 = null,
68/// The index of the segment representing the custom '.debug_line' section.68/// The index of the segment representing the custom '.debug_line' section.
69debug_line_index: ?u32 = null,69debug_line_index: ?u32 = null,
70/// The index of the segment representing the custom '.debug_loc' section.
71debug_loc_index: ?u32 = null,
72/// The index of the segment representing the custom '.debug_ranges' section.
73debug_ranges_index: ?u32 = null,
74/// The index of the segment representing the custom '.debug_pubnames' section.
75debug_pubnames_index: ?u32 = null,
76/// The index of the segment representing the custom '.debug_pubtypes' section.
77debug_pubtypes_index: ?u32 = null,
78/// The index of the segment representing the custom '.debug_pubtypes' section.
79debug_str_index: ?u32 = null,
80/// The index of the segment representing the custom '.debug_pubtypes' section.
81debug_abbrev_index: ?u32 = null,
70/// The count of imported functions. This number will be appended82/// The count of imported functions. This number will be appended
71/// to the function indexes as their index starts at the lowest non-extern function.83/// to the function indexes as their index starts at the lowest non-extern function.
72imported_functions_count: u32 = 0,84imported_functions_count: u32 = 0,
...@@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{},...@@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{},
83segments: std.ArrayListUnmanaged(Segment) = .{},95segments: std.ArrayListUnmanaged(Segment) = .{},
84/// Maps a data segment key (such as .rodata) to the index into `segments`.96/// Maps a data segment key (such as .rodata) to the index into `segments`.
85data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},97data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},
86/// A list of `types.Segment` which provide meta data98/// A table of `types.Segment` which provide meta data
87/// about a data symbol such as its name99/// about a data symbol such as its name where the key is
88segment_info: std.ArrayListUnmanaged(types.Segment) = .{},100/// the segment index, which can be found from `data_segments`
101segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{},
89/// Deduplicated string table for strings used by symbols, imports and exports.102/// Deduplicated string table for strings used by symbols, imports and exports.
90string_table: StringTable = .{},103string_table: StringTable = .{},
91/// Debug information for wasm104/// Debug information for wasm
92dwarf: ?Dwarf = null,105dwarf: ?Dwarf = null,
93106
94// *debug information* //
95/// Contains all bytes for the '.debug_info' section
96debug_info: std.ArrayListUnmanaged(u8) = .{},
97/// Contains all bytes for the '.debug_line' section
98debug_line: std.ArrayListUnmanaged(u8) = .{},
99/// Contains all bytes for the '.debug_abbrev' section
100debug_abbrev: std.ArrayListUnmanaged(u8) = .{},
101/// Contains all bytes for the '.debug_ranges' section
102debug_aranges: std.ArrayListUnmanaged(u8) = .{},
103
104// Output sections107// Output sections
105/// Output type section108/// Output type section
106func_types: std.ArrayListUnmanaged(wasm.Type) = .{},109func_types: std.ArrayListUnmanaged(wasm.Type) = .{},
...@@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},...@@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
156/// The actual table is populated during `flush`.159/// The actual table is populated during `flush`.
157error_table_symbol: ?u32 = null,160error_table_symbol: ?u32 = null,
158161
162// Debug section atoms. These are only set when the current compilation
163// unit contains Zig code. The lifetime of these atoms are extended
164// until the end of the compiler's lifetime. Meaning they're not freed
165// during `flush()` in incremental-mode.
166debug_info_atom: ?*Atom = null,
167debug_line_atom: ?*Atom = null,
168debug_loc_atom: ?*Atom = null,
169debug_ranges_atom: ?*Atom = null,
170debug_abbrev_atom: ?*Atom = null,
171debug_str_atom: ?*Atom = null,
172debug_pubnames_atom: ?*Atom = null,
173debug_pubtypes_atom: ?*Atom = null,
174
159pub const Segment = struct {175pub const Segment = struct {
160 alignment: u32,176 alignment: u32,
161 size: u32,177 size: u32,
...@@ -209,6 +225,18 @@ pub const SymbolLoc = struct {...@@ -209,6 +225,18 @@ pub const SymbolLoc = struct {
209 }225 }
210 return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name);226 return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name);
211 }227 }
228
229 /// From a given symbol location, returns the final location.
230 /// e.g. when a symbol was resolved and replaced by the symbol
231 /// in a different file, this will return said location.
232 /// If the symbol wasn't replaced by another, this will return
233 /// the given location itself.
234 pub fn finalLoc(self: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc {
235 if (wasm_bin.discarded.get(self)) |new_loc| {
236 return new_loc.finalLoc(wasm_bin);
237 }
238 return self;
239 }
212};240};
213241
214/// Generic string table that duplicates strings242/// Generic string table that duplicates strings
...@@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
335 };363 };
336 }364 }
337365
366 try wasm_bin.initDebugSections();
338 return wasm_bin;367 return wasm_bin;
339}368}
340369
...@@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {...@@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
363 return self;392 return self;
364}393}
365394
395/// Initializes symbols and atoms for the debug sections
396/// Initialization is only done when compiling Zig code.
397/// When Zig is invoked as a linker instead, the atoms
398/// and symbols come from the object files instead.
399pub fn initDebugSections(self: *Wasm) !void {
400 if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
401 assert(self.debug_info_index == null);
402 // this will create an Atom and set the index for us.
403 self.debug_info_atom = try self.createDebugSectionForIndex(&self.debug_info_index, ".debug_info");
404 self.debug_line_atom = try self.createDebugSectionForIndex(&self.debug_line_index, ".debug_line");
405 self.debug_loc_atom = try self.createDebugSectionForIndex(&self.debug_loc_index, ".debug_loc");
406 self.debug_abbrev_atom = try self.createDebugSectionForIndex(&self.debug_abbrev_index, ".debug_abbrev");
407 self.debug_ranges_atom = try self.createDebugSectionForIndex(&self.debug_ranges_index, ".debug_ranges");
408 self.debug_str_atom = try self.createDebugSectionForIndex(&self.debug_str_index, ".debug_str");
409 self.debug_pubnames_atom = try self.createDebugSectionForIndex(&self.debug_pubnames_index, ".debug_pubnames");
410 self.debug_pubtypes_atom = try self.createDebugSectionForIndex(&self.debug_pubtypes_index, ".debug_pubtypes");
411}
412
366fn parseInputFiles(self: *Wasm, files: []const []const u8) !void {413fn parseInputFiles(self: *Wasm, files: []const []const u8) !void {
367 for (files) |path| {414 for (files) |path| {
368 if (try self.parseObjectFile(path)) continue;415 if (try self.parseObjectFile(path)) continue;
...@@ -644,7 +691,7 @@ pub fn deinit(self: *Wasm) void {...@@ -644,7 +691,7 @@ pub fn deinit(self: *Wasm) void {
644 for (self.func_types.items) |*func_type| {691 for (self.func_types.items) |*func_type| {
645 func_type.deinit(gpa);692 func_type.deinit(gpa);
646 }693 }
647 for (self.segment_info.items) |segment_info| {694 for (self.segment_info.values()) |segment_info| {
648 gpa.free(segment_info.name);695 gpa.free(segment_info.name);
649 }696 }
650 for (self.objects.items) |*object| {697 for (self.objects.items) |*object| {
...@@ -692,11 +739,6 @@ pub fn deinit(self: *Wasm) void {...@@ -692,11 +739,6 @@ pub fn deinit(self: *Wasm) void {
692 if (self.dwarf) |*dwarf| {739 if (self.dwarf) |*dwarf| {
693 dwarf.deinit();740 dwarf.deinit();
694 }741 }
695
696 self.debug_info.deinit(gpa);
697 self.debug_line.deinit(gpa);
698 self.debug_abbrev.deinit(gpa);
699 self.debug_aranges.deinit(gpa);
700}742}
701743
702pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void {744pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void {
...@@ -1337,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1337,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
1337 const index = gop.value_ptr.*;1379 const index = gop.value_ptr.*;
1338 self.segments.items[index].size += atom.size;1380 self.segments.items[index].size += atom.size;
13391381
1340 // segment indexes can be off by 1 due to also containing a segment1382 symbol.index = @intCast(u32, self.segment_info.getIndex(index).?);
1341 // for the code section, so we must check if the existing segment
1342 // is larger than that of the code section, and substract the index by 1 in such case.
1343 var info_add = if (self.code_section_index) |idx| blk: {
1344 if (idx < index) break :blk @as(u32, 1);
1345 break :blk 0;
1346 } else @as(u32, 0);
1347 if (self.debug_info_index != null) info_add += 1;
1348 if (self.debug_line_index != null) info_add += 1;
1349 symbol.index = index - info_add;
1350 // segment info already exists, so free its memory1383 // segment info already exists, so free its memory
1351 self.base.allocator.free(segment_name);1384 self.base.allocator.free(segment_name);
1352 break :result index;1385 break :result index;
...@@ -1359,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1359,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
1359 });1392 });
1360 gop.value_ptr.* = index;1393 gop.value_ptr.* = index;
13611394
1362 const info_index = @intCast(u32, self.segment_info.items.len);1395 const info_index = @intCast(u32, self.segment_info.count());
1363 try self.segment_info.append(self.base.allocator, segment_info);1396 try self.segment_info.put(self.base.allocator, index, segment_info);
1364 symbol.index = info_index;1397 symbol.index = info_index;
1365 break :result index;1398 break :result index;
1366 }1399 }
...@@ -1370,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1370,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
1370 const segment: *Segment = &self.segments.items[final_index];1403 const segment: *Segment = &self.segments.items[final_index];
1371 segment.alignment = std.math.max(segment.alignment, atom.alignment);1404 segment.alignment = std.math.max(segment.alignment, atom.alignment);
13721405
1373 if (self.atoms.getPtr(final_index)) |last| {1406 try self.appendAtomAtIndex(final_index, atom);
1407}
1408
1409/// From a given index, append the given `Atom` at the back of the linked list.
1410/// Simply inserts it into the map of atoms when it doesn't exist yet.
1411pub fn appendAtomAtIndex(self: *Wasm, index: u32, atom: *Atom) !void {
1412 if (self.atoms.getPtr(index)) |last| {
1374 last.*.next = atom;1413 last.*.next = atom;
1375 atom.prev = last.*;1414 atom.prev = last.*;
1376 last.* = atom;1415 last.* = atom;
1377 } else {1416 } else {
1378 try self.atoms.putNoClobber(self.base.allocator, final_index, atom);1417 try self.atoms.putNoClobber(self.base.allocator, index, atom);
1379 }1418 }
1380}1419}
13811420
1421/// Allocates debug atoms into their respective debug sections
1422/// to merge them with maybe-existing debug atoms from object files.
1423fn allocateDebugAtoms(self: *Wasm) !void {
1424 if (self.dwarf == null) return;
1425
1426 const allocAtom = struct {
1427 fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void {
1428 const index = maybe_index.* orelse idx: {
1429 const index = @intCast(u32, bin.segments.items.len);
1430 try bin.appendDummySegment();
1431 maybe_index.* = index;
1432 break :idx index;
1433 };
1434 atom.size = @intCast(u32, atom.code.items.len);
1435 bin.symbols.items[atom.sym_index].index = index;
1436 try bin.appendAtomAtIndex(index, atom);
1437 }
1438 }.f;
1439
1440 try allocAtom(self, &self.debug_info_index, self.debug_info_atom.?);
1441 try allocAtom(self, &self.debug_line_index, self.debug_line_atom.?);
1442 try allocAtom(self, &self.debug_loc_index, self.debug_loc_atom.?);
1443 try allocAtom(self, &self.debug_str_index, self.debug_str_atom.?);
1444 try allocAtom(self, &self.debug_ranges_index, self.debug_ranges_atom.?);
1445 try allocAtom(self, &self.debug_abbrev_index, self.debug_abbrev_atom.?);
1446 try allocAtom(self, &self.debug_pubnames_index, self.debug_pubnames_atom.?);
1447 try allocAtom(self, &self.debug_pubtypes_index, self.debug_pubtypes_atom.?);
1448}
1449
1382fn allocateAtoms(self: *Wasm) !void {1450fn allocateAtoms(self: *Wasm) !void {
1383 // first sort the data segments1451 // first sort the data segments
1384 try sortDataSegments(self);1452 try sortDataSegments(self);
1453 try allocateDebugAtoms(self);
13851454
1386 var it = self.atoms.iterator();1455 var it = self.atoms.iterator();
1387 while (it.next()) |entry| {1456 while (it.next()) |entry| {
...@@ -1399,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -1399,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void {
1399 atom.size,1468 atom.size,
1400 });1469 });
1401 offset += atom.size;1470 offset += atom.size;
1402 self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers1471 try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers
1403 atom = atom.next orelse break;1472 atom = atom.next orelse break;
1404 }1473 }
1405 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);1474 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
...@@ -1753,7 +1822,7 @@ fn setupMemory(self: *Wasm) !void {...@@ -1753,7 +1822,7 @@ fn setupMemory(self: *Wasm) !void {
1753/// From a given object's index and the index of the segment, returns the corresponding1822/// From a given object's index and the index of the segment, returns the corresponding
1754/// index of the segment within the final data section. When the segment does not yet1823/// index of the segment within the final data section. When the segment does not yet
1755/// exist, a new one will be initialized and appended. The new index will be returned in that case.1824/// exist, a new one will be initialized and appended. The new index will be returned in that case.
1756pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !u32 {1825pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !?u32 {
1757 const object: Object = self.objects.items[object_index];1826 const object: Object = self.objects.items[object_index];
1758 const relocatable_data = object.relocatable_data[relocatable_index];1827 const relocatable_data = object.relocatable_data[relocatable_index];
1759 const index = @intCast(u32, self.segments.items.len);1828 const index = @intCast(u32, self.segments.items.len);
...@@ -1765,27 +1834,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32...@@ -1765,27 +1834,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32
1765 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment));1834 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment));
1766 if (!result.found_existing) {1835 if (!result.found_existing) {
1767 result.value_ptr.* = index;1836 result.value_ptr.* = index;
1768 try self.segments.append(self.base.allocator, .{1837 try self.appendDummySegment();
1769 .alignment = 1,
1770 .size = 0,
1771 .offset = 0,
1772 });
1773 return index;1838 return index;
1774 } else return result.value_ptr.*;1839 } else return result.value_ptr.*;
1775 },1840 },
1776 .code => return self.code_section_index orelse blk: {1841 .code => return self.code_section_index orelse blk: {
1777 self.code_section_index = index;1842 self.code_section_index = index;
1778 try self.segments.append(self.base.allocator, .{1843 try self.appendDummySegment();
1779 .alignment = 1,
1780 .size = 0,
1781 .offset = 0,
1782 });
1783 break :blk index;1844 break :blk index;
1784 },1845 },
1785 .custom => return error.@"TODO: Custom section relocations for wasm",1846 .debug => {
1847 const debug_name = object.getDebugName(relocatable_data);
1848 if (mem.eql(u8, debug_name, ".debug_info")) {
1849 return self.debug_info_index orelse blk: {
1850 self.debug_info_index = index;
1851 try self.appendDummySegment();
1852 break :blk index;
1853 };
1854 } else if (mem.eql(u8, debug_name, ".debug_line")) {
1855 return self.debug_line_index orelse blk: {
1856 self.debug_line_index = index;
1857 try self.appendDummySegment();
1858 break :blk index;
1859 };
1860 } else if (mem.eql(u8, debug_name, ".debug_loc")) {
1861 return self.debug_loc_index orelse blk: {
1862 self.debug_loc_index = index;
1863 try self.appendDummySegment();
1864 break :blk index;
1865 };
1866 } else if (mem.eql(u8, debug_name, ".debug_ranges")) {
1867 return self.debug_line_index orelse blk: {
1868 self.debug_ranges_index = index;
1869 try self.appendDummySegment();
1870 break :blk index;
1871 };
1872 } else if (mem.eql(u8, debug_name, ".debug_pubnames")) {
1873 return self.debug_pubnames_index orelse blk: {
1874 self.debug_pubnames_index = index;
1875 try self.appendDummySegment();
1876 break :blk index;
1877 };
1878 } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) {
1879 return self.debug_pubtypes_index orelse blk: {
1880 self.debug_pubtypes_index = index;
1881 try self.appendDummySegment();
1882 break :blk index;
1883 };
1884 } else if (mem.eql(u8, debug_name, ".debug_abbrev")) {
1885 return self.debug_abbrev_index orelse blk: {
1886 self.debug_abbrev_index = index;
1887 try self.appendDummySegment();
1888 break :blk index;
1889 };
1890 } else if (mem.eql(u8, debug_name, ".debug_str")) {
1891 return self.debug_str_index orelse blk: {
1892 self.debug_str_index = index;
1893 try self.appendDummySegment();
1894 break :blk index;
1895 };
1896 } else {
1897 log.warn("found unknown debug section '{s}'", .{debug_name});
1898 log.warn(" debug section will be skipped", .{});
1899 return null;
1900 }
1901 },
1786 }1902 }
1787}1903}
17881904
1905/// Appends a new segment with default field values
1906fn appendDummySegment(self: *Wasm) !void {
1907 try self.segments.append(self.base.allocator, .{
1908 .alignment = 1,
1909 .size = 0,
1910 .offset = 0,
1911 });
1912}
1913
1789/// Returns the symbol index of the error name table.1914/// Returns the symbol index of the error name table.
1790///1915///
1791/// When the symbol does not yet exist, it will create a new one instead.1916/// When the symbol does not yet exist, it will create a new one instead.
...@@ -1903,50 +2028,52 @@ fn populateErrorNameTable(self: *Wasm) !void {...@@ -1903,50 +2028,52 @@ fn populateErrorNameTable(self: *Wasm) !void {
1903 try self.parseAtom(names_atom, .{ .data = .read_only });2028 try self.parseAtom(names_atom, .{ .data = .read_only });
1904}2029}
19052030
1906pub fn getDebugInfoIndex(self: *Wasm) !u32 {2031/// From a given index variable, creates a new debug section.
1907 assert(self.dwarf != null);2032/// This initializes the index, appends a new segment,
1908 return self.debug_info_index orelse {2033/// and finally, creates a managed `Atom`.
1909 self.debug_info_index = @intCast(u32, self.segments.items.len);2034pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom {
1910 const segment = try self.segments.addOne(self.base.allocator);2035 const new_index = @intCast(u32, self.segments.items.len);
1911 segment.* = .{2036 index.* = new_index;
1912 .size = 0,2037 try self.appendDummySegment();
1913 .offset = 0,2038 // _ = index;
1914 // debug sections always have alignment '1'2039
1915 .alignment = 1,2040 const sym_index = self.symbols_free_list.popOrNull() orelse idx: {
1916 };2041 const tmp_index = @intCast(u32, self.symbols.items.len);
1917 return self.debug_info_index.?;2042 _ = try self.symbols.addOne(self.base.allocator);
2043 break :idx tmp_index;
1918 };2044 };
1919}2045 self.symbols.items[sym_index] = .{
19202046 .tag = .section,
1921pub fn getDebugLineIndex(self: *Wasm) !u32 {2047 .name = try self.string_table.put(self.base.allocator, name),
1922 assert(self.dwarf != null);2048 .index = 0,
1923 return self.debug_line_index orelse {2049 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1924 self.debug_line_index = @intCast(u32, self.segments.items.len);
1925 const segment = try self.segments.addOne(self.base.allocator);
1926 segment.* = .{
1927 .size = 0,
1928 .offset = 0,
1929 .alignment = 1,
1930 };
1931 return self.debug_line_index.?;
1932 };2050 };
2051
2052 const atom = try self.base.allocator.create(Atom);
2053 atom.* = Atom.empty;
2054 atom.alignment = 1; // debug sections are always 1-byte-aligned
2055 atom.sym_index = sym_index;
2056 try self.managed_atoms.append(self.base.allocator, atom);
2057 try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom);
2058 return atom;
1933}2059}
19342060
1935fn resetState(self: *Wasm) void {2061fn resetState(self: *Wasm) void {
1936 for (self.segment_info.items) |*segment_info| {2062 for (self.segment_info.values()) |segment_info| {
1937 self.base.allocator.free(segment_info.name);2063 self.base.allocator.free(segment_info.name);
1938 }2064 }
1939 const mod = self.base.options.module.?;2065 if (self.base.options.module) |mod| {
1940 var decl_it = self.decls.keyIterator();2066 var decl_it = self.decls.keyIterator();
1941 while (decl_it.next()) |decl_index_ptr| {2067 while (decl_it.next()) |decl_index_ptr| {
1942 const decl = mod.declPtr(decl_index_ptr.*);2068 const decl = mod.declPtr(decl_index_ptr.*);
1943 const atom = &decl.link.wasm;2069 const atom = &decl.link.wasm;
1944 atom.next = null;2070 atom.next = null;
1945 atom.prev = null;2071 atom.prev = null;
19462072
1947 for (atom.locals.items) |*local_atom| {2073 for (atom.locals.items) |*local_atom| {
1948 local_atom.next = null;2074 local_atom.next = null;
1949 local_atom.prev = null;2075 local_atom.prev = null;
2076 }
1950 }2077 }
1951 }2078 }
1952 self.functions.clearRetainingCapacity();2079 self.functions.clearRetainingCapacity();
...@@ -1959,6 +2086,12 @@ fn resetState(self: *Wasm) void {...@@ -1959,6 +2086,12 @@ fn resetState(self: *Wasm) void {
1959 self.code_section_index = null;2086 self.code_section_index = null;
1960 self.debug_info_index = null;2087 self.debug_info_index = null;
1961 self.debug_line_index = null;2088 self.debug_line_index = null;
2089 self.debug_loc_index = null;
2090 self.debug_str_index = null;
2091 self.debug_ranges_index = null;
2092 self.debug_abbrev_index = null;
2093 self.debug_pubnames_index = null;
2094 self.debug_pubtypes_index = null;
1962}2095}
19632096
1964pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {2097pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -2036,29 +2169,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2036,29 +2169,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2036 defer self.resetState();2169 defer self.resetState();
2037 try self.setupStart();2170 try self.setupStart();
2038 try self.setupImports();2171 try self.setupImports();
2039 const mod = self.base.options.module.?;2172 if (self.base.options.module) |mod| {
2040 var decl_it = self.decls.keyIterator();2173 var decl_it = self.decls.keyIterator();
2041 while (decl_it.next()) |decl_index_ptr| {2174 while (decl_it.next()) |decl_index_ptr| {
2042 const decl = mod.declPtr(decl_index_ptr.*);2175 const decl = mod.declPtr(decl_index_ptr.*);
2043 if (decl.isExtern()) continue;2176 if (decl.isExtern()) continue;
2044 const atom = &decl.*.link.wasm;2177 const atom = &decl.*.link.wasm;
2045 if (decl.ty.zigTypeTag() == .Fn) {2178 if (decl.ty.zigTypeTag() == .Fn) {
2046 try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });2179 try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });
2047 } else if (decl.getVariable()) |variable| {2180 } else if (decl.getVariable()) |variable| {
2048 if (!variable.is_mutable) {2181 if (!variable.is_mutable) {
2049 try self.parseAtom(atom, .{ .data = .read_only });2182 try self.parseAtom(atom, .{ .data = .read_only });
2050 } else if (variable.init.isUndefDeep()) {2183 } else if (variable.init.isUndefDeep()) {
2051 try self.parseAtom(atom, .{ .data = .uninitialized });2184 try self.parseAtom(atom, .{ .data = .uninitialized });
2185 } else {
2186 try self.parseAtom(atom, .{ .data = .initialized });
2187 }
2052 } else {2188 } else {
2053 try self.parseAtom(atom, .{ .data = .initialized });2189 try self.parseAtom(atom, .{ .data = .read_only });
2190 }
2191
2192 // also parse atoms for a decl's locals
2193 for (atom.locals.items) |*local_atom| {
2194 try self.parseAtom(local_atom, .{ .data = .read_only });
2054 }2195 }
2055 } else {
2056 try self.parseAtom(atom, .{ .data = .read_only });
2057 }2196 }
20582197
2059 // also parse atoms for a decl's locals2198 if (self.dwarf) |*dwarf| {
2060 for (atom.locals.items) |*local_atom| {2199 try dwarf.flushModule(&self.base, self.base.options.module.?);
2061 try self.parseAtom(local_atom, .{ .data = .read_only });
2062 }2200 }
2063 }2201 }
20642202
...@@ -2066,9 +2204,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2066,9 +2204,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2066 try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self);2204 try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self);
2067 }2205 }
20682206
2069 if (self.dwarf) |*dwarf| {
2070 try dwarf.flushModule(&self.base, self.base.options.module.?);
2071 }
2072 try self.allocateAtoms();2207 try self.allocateAtoms();
2073 try self.setupMemory();2208 try self.setupMemory();
2074 self.mapFunctionTable();2209 self.mapFunctionTable();
...@@ -2424,19 +2559,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2424,19 +2559,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2424 }2559 }
2425 } else if (!self.base.options.strip) {2560 } else if (!self.base.options.strip) {
2426 if (self.dwarf) |*dwarf| {2561 if (self.dwarf) |*dwarf| {
2427 if (self.debug_info_index != null) {2562 const mod = self.base.options.module.?;
2428 try dwarf.writeDbgAbbrev(&self.base);2563 try dwarf.writeDbgAbbrev(&self.base);
2429 // for debug info and ranges, the address is always 0,2564 // for debug info and ranges, the address is always 0,
2430 // as locations are always offsets relative to 'code' section.2565 // as locations are always offsets relative to 'code' section.
2431 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);2566 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
2432 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);2567 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
2433 try dwarf.writeDbgLineHeader(&self.base, mod);2568 try dwarf.writeDbgLineHeader(&self.base, mod);
24342569 }
2435 try emitDebugSection(file, self.debug_info.items, ".debug_info");2570
2436 try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges");2571 var debug_bytes = std.ArrayList(u8).init(self.base.allocator);
2437 try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev");2572 defer debug_bytes.deinit();
2438 try emitDebugSection(file, self.debug_line.items, ".debug_line");2573
2439 try emitDebugSection(file, dwarf.strtab.items, ".debug_str");2574 const DebugSection = struct {
2575 name: []const u8,
2576 index: ?u32,
2577 };
2578
2579 const debug_sections: []const DebugSection = &.{
2580 .{ .name = ".debug_info", .index = self.debug_info_index },
2581 .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index },
2582 .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index },
2583 .{ .name = ".debug_line", .index = self.debug_line_index },
2584 .{ .name = ".debug_str", .index = self.debug_str_index },
2585 .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index },
2586 .{ .name = ".debug_loc", .index = self.debug_loc_index },
2587 .{ .name = ".debug_ranges", .index = self.debug_ranges_index },
2588 };
2589
2590 for (debug_sections) |item| {
2591 if (item.index) |index| {
2592 var atom = self.atoms.get(index).?.getFirst();
2593 while (true) {
2594 atom.resolveRelocs(self);
2595 try debug_bytes.appendSlice(atom.code.items);
2596 atom = atom.next orelse break;
2597 }
2598 try emitDebugSection(file, debug_bytes.items, item.name);
2599 debug_bytes.clearRetainingCapacity();
2440 }2600 }
2441 }2601 }
2442 try self.emitNameSection(file, arena);2602 try self.emitNameSection(file, arena);
...@@ -2444,6 +2604,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2444,6 +2604,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2444}2604}
24452605
2446fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {2606fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
2607 if (data.len == 0) return;
2447 const header_offset = try reserveCustomSectionHeader(file);2608 const header_offset = try reserveCustomSectionHeader(file);
2448 const writer = file.writer();2609 const writer = file.writer();
2449 try leb.writeULEB128(writer, @intCast(u32, name.len));2610 try leb.writeULEB128(writer, @intCast(u32, name.len));
...@@ -3149,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -3149,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {
3149 var payload = std.ArrayList(u8).init(arena);3310 var payload = std.ArrayList(u8).init(arena);
3150 const writer = payload.writer();3311 const writer = payload.writer();
3151 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO));3312 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO));
3152 try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len));3313 try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count()));
3153 for (self.segment_info.items) |segment_info| {3314 for (self.segment_info.values()) |segment_info| {
3154 log.debug("Emit segment: {s} align({d}) flags({b})", .{3315 log.debug("Emit segment: {s} align({d}) flags({b})", .{
3155 segment_info.name,3316 segment_info.name,
3156 @ctz(segment_info.alignment),3317 @ctz(segment_info.alignment),
src/link/Wasm/Atom.zig+35-7
...@@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom {...@@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom {
90 return tmp;90 return tmp;
91}91}
9292
93/// Unlike `getFirst` this returns the first `*Atom` that was
94/// produced from Zig code, rather than an object file.
95/// This is useful for debug sections where we want to extend
96/// the bytes, and don't want to overwrite existing Atoms.
97pub fn getFirstZigAtom(self: *Atom) *Atom {
98 if (self.file == null) return self;
99 var tmp = self;
100 return while (tmp.prev) |prev| {
101 if (prev.file == null) break prev;
102 tmp = prev;
103 } else unreachable; // must allocate an Atom first!
104}
105
93/// Returns the location of the symbol that represents this `Atom`106/// Returns the location of the symbol that represents this `Atom`
94pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {107pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
95 return .{ .file = self.file, .index = self.sym_index };108 return .{ .file = self.file, .index = self.sym_index };
...@@ -145,7 +158,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void {...@@ -145,7 +158,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void {
145/// All values will be represented as a `u64` as all values can fit within it.158/// All values will be represented as a `u64` as all values can fit within it.
146/// The final value must be casted to the correct size.159/// The final value must be casted to the correct size.
147fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {160fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
148 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };161 const target_loc = (Wasm.SymbolLoc{ .file = self.file, .index = relocation.index }).finalLoc(wasm_bin);
149 const symbol = target_loc.getSymbol(wasm_bin).*;162 const symbol = target_loc.getSymbol(wasm_bin).*;
150 switch (relocation.relocation_type) {163 switch (relocation.relocation_type) {
151 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,164 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
...@@ -174,19 +187,34 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -174,19 +187,34 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
174 => {187 => {
175 std.debug.assert(symbol.tag == .data and !symbol.isUndefined());188 std.debug.assert(symbol.tag == .data and !symbol.isUndefined());
176 const merge_segment = wasm_bin.base.options.output_mode != .Obj;189 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
177 const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc;190 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
178 const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?;
179 const segment_info = if (target_atom.file) |object_index| blk: {191 const segment_info = if (target_atom.file) |object_index| blk: {
180 break :blk wasm_bin.objects.items[object_index].segment_info;192 break :blk wasm_bin.objects.items[object_index].segment_info;
181 } else wasm_bin.segment_info.items;193 } else wasm_bin.segment_info.values();
182 const segment_name = segment_info[symbol.index].outputName(merge_segment);194 const segment_name = segment_info[symbol.index].outputName(merge_segment);
183 const segment_index = wasm_bin.data_segments.get(segment_name).?;195 const segment_index = wasm_bin.data_segments.get(segment_name).?;
184 const segment = wasm_bin.segments.items[segment_index];196 const segment = wasm_bin.segments.items[segment_index];
185 return target_atom.offset + segment.offset + (relocation.addend orelse 0);197 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
186 },198 },
187 .R_WASM_EVENT_INDEX_LEB => return symbol.index,199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
188 .R_WASM_SECTION_OFFSET_I32,200 .R_WASM_SECTION_OFFSET_I32 => {
189 .R_WASM_FUNCTION_OFFSET_I32,201 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
190 => return relocation.offset,202 return target_atom.offset + (relocation.addend orelse 0);
203 },
204 .R_WASM_FUNCTION_OFFSET_I32 => {
205 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
206 var atom = target_atom.getFirst();
207 var offset: u32 = 0;
208 // TODO: Calculate this during atom allocation, rather than
209 // this linear calculation. For now it's done here as atoms
210 // are being sorted after atom allocation, as functions aren't
211 // merged until later.
212 while (true) {
213 offset += 5; // each atom uses 5 bytes to store its body's size
214 if (atom == target_atom) break;
215 atom = atom.next.?;
216 }
217 return target_atom.offset + offset + (relocation.addend orelse 0);
218 },
191 }219 }
192}220}
src/link/Wasm/Object.zig+75-34
...@@ -63,16 +63,21 @@ relocatable_data: []const RelocatableData = &.{},...@@ -63,16 +63,21 @@ relocatable_data: []const RelocatableData = &.{},
63/// import name, module name and export names. Each string will be deduplicated63/// import name, module name and export names. Each string will be deduplicated
64/// and returns an offset into the table.64/// and returns an offset into the table.
65string_table: Wasm.StringTable = .{},65string_table: Wasm.StringTable = .{},
66/// All the names of each debug section found in the current object file.
67/// Each name is terminated by a null-terminator. The name can be found,
68/// from the `index` offset within the `RelocatableData`.
69debug_names: [:0]const u8,
6670
67/// Represents a single item within a section (depending on its `type`)71/// Represents a single item within a section (depending on its `type`)
68const RelocatableData = struct {72const RelocatableData = struct {
69 /// The type of the relocatable data73 /// The type of the relocatable data
70 type: enum { data, code, custom },74 type: enum { data, code, debug },
71 /// Pointer to the data of the segment, where its length is written to `size`75 /// Pointer to the data of the segment, where its length is written to `size`
72 data: [*]u8,76 data: [*]u8,
73 /// The size in bytes of the data representing the segment within the section77 /// The size in bytes of the data representing the segment within the section
74 size: u32,78 size: u32,
75 /// The index within the section itself79 /// The index within the section itself, or in case of a debug section,
80 /// the offset within the `string_table`.
76 index: u32,81 index: u32,
77 /// The offset within the section where the data starts82 /// The offset within the section where the data starts
78 offset: u32,83 offset: u32,
...@@ -96,9 +101,16 @@ const RelocatableData = struct {...@@ -96,9 +101,16 @@ const RelocatableData = struct {
96 return switch (self.type) {101 return switch (self.type) {
97 .data => .data,102 .data => .data,
98 .code => .function,103 .code => .function,
99 .custom => .section,104 .debug => .section,
100 };105 };
101 }106 }
107
108 /// Returns the index within a section itself, or in case of a debug section,
109 /// returns the section index within the object file.
110 pub fn getIndex(self: RelocatableData) u32 {
111 if (self.type == .debug) return self.section_index;
112 return self.index;
113 }
102};114};
103115
104pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;116pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;
...@@ -111,6 +123,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz...@@ -111,6 +123,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
111 var object: Object = .{123 var object: Object = .{
112 .file = file,124 .file = file,
113 .name = try gpa.dupe(u8, name),125 .name = try gpa.dupe(u8, name),
126 .debug_names = &.{},
114 };127 };
115128
116 var is_object_file: bool = false;129 var is_object_file: bool = false;
...@@ -197,6 +210,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32...@@ -197,6 +210,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
197 } else i;210 } else i;
198}211}
199212
213/// From a given `RelocatableDate`, find the corresponding debug section name
214pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 {
215 return self.string_table.get(relocatable_data.index);
216}
217
200/// Checks if the object file is an MVP version.218/// Checks if the object file is an MVP version.
201/// When that's the case, we check if there's an import table definiton with its name219/// When that's the case, we check if there's an import table definiton with its name
202/// set to '__indirect_function_table". When that's also the case,220/// set to '__indirect_function_table". When that's also the case,
...@@ -328,10 +346,15 @@ fn Parser(comptime ReaderType: type) type {...@@ -328,10 +346,15 @@ fn Parser(comptime ReaderType: type) type {
328346
329 self.object.version = version;347 self.object.version = version;
330 var relocatable_data = std.ArrayList(RelocatableData).init(gpa);348 var relocatable_data = std.ArrayList(RelocatableData).init(gpa);
331349 var debug_names = std.ArrayList(u8).init(gpa);
332 errdefer while (relocatable_data.popOrNull()) |rel_data| {350
333 gpa.free(rel_data.data[0..rel_data.size]);351 errdefer {
334 } else relocatable_data.deinit();352 while (relocatable_data.popOrNull()) |rel_data| {
353 gpa.free(rel_data.data[0..rel_data.size]);
354 } else relocatable_data.deinit();
355 gpa.free(debug_names.items);
356 debug_names.deinit();
357 }
335358
336 var section_index: u32 = 0;359 var section_index: u32 = 0;
337 while (self.reader.reader().readByte()) |byte| : (section_index += 1) {360 while (self.reader.reader().readByte()) |byte| : (section_index += 1) {
...@@ -347,11 +370,26 @@ fn Parser(comptime ReaderType: type) type {...@@ -347,11 +370,26 @@ fn Parser(comptime ReaderType: type) type {
347370
348 if (std.mem.eql(u8, name, "linking")) {371 if (std.mem.eql(u8, name, "linking")) {
349 is_object_file.* = true;372 is_object_file.* = true;
373 self.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them.
350 try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));374 try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));
351 } else if (std.mem.startsWith(u8, name, "reloc")) {375 } else if (std.mem.startsWith(u8, name, "reloc")) {
352 try self.parseRelocations(gpa);376 try self.parseRelocations(gpa);
353 } else if (std.mem.eql(u8, name, "target_features")) {377 } else if (std.mem.eql(u8, name, "target_features")) {
354 try self.parseFeatures(gpa);378 try self.parseFeatures(gpa);
379 } else if (std.mem.startsWith(u8, name, ".debug")) {
380 const debug_size = @intCast(u32, reader.context.bytes_left);
381 const debug_content = try gpa.alloc(u8, debug_size);
382 errdefer gpa.free(debug_content);
383 try reader.readNoEof(debug_content);
384
385 try relocatable_data.append(.{
386 .type = .debug,
387 .data = debug_content.ptr,
388 .size = debug_size,
389 .index = try self.object.string_table.put(gpa, name),
390 .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset
391 .section_index = section_index,
392 });
355 } else {393 } else {
356 try reader.skipBytes(reader.context.bytes_left, .{});394 try reader.skipBytes(reader.context.bytes_left, .{});
357 }395 }
...@@ -737,7 +775,12 @@ fn Parser(comptime ReaderType: type) type {...@@ -737,7 +775,12 @@ fn Parser(comptime ReaderType: type) type {
737 },775 },
738 .section => {776 .section => {
739 symbol.index = try leb.readULEB128(u32, reader);777 symbol.index = try leb.readULEB128(u32, reader);
740 symbol.name = try self.object.string_table.put(gpa, @tagName(symbol.tag));778 for (self.object.relocatable_data) |data| {
779 if (data.section_index == symbol.index) {
780 symbol.name = data.index;
781 break;
782 }
783 }
741 },784 },
742 else => {785 else => {
743 symbol.index = try leb.readULEB128(u32, reader);786 symbol.index = try leb.readULEB128(u32, reader);
...@@ -827,7 +870,6 @@ fn assertEnd(reader: anytype) !void {...@@ -827,7 +870,6 @@ fn assertEnd(reader: anytype) !void {
827870
828/// Parses an object file into atoms, for code and data sections871/// Parses an object file into atoms, for code and data sections
829pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {872pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {
830 log.debug("Parsing data section into atoms", .{});
831 const Key = struct {873 const Key = struct {
832 kind: Symbol.Tag,874 kind: Symbol.Tag,
833 index: u32,875 index: u32,
...@@ -839,7 +881,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -839,7 +881,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
839881
840 for (self.symtable) |symbol, symbol_index| {882 for (self.symtable) |symbol, symbol_index| {
841 switch (symbol.tag) {883 switch (symbol.tag) {
842 .function, .data => if (!symbol.isUndefined()) {884 .function, .data, .section => if (!symbol.isUndefined()) {
843 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });885 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
844 const sym_idx = @intCast(u32, symbol_index);886 const sym_idx = @intCast(u32, symbol_index);
845 if (!gop.found_existing) {887 if (!gop.found_existing) {
...@@ -852,12 +894,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -852,12 +894,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
852 }894 }
853895
854 for (self.relocatable_data) |relocatable_data, index| {896 for (self.relocatable_data) |relocatable_data, index| {
855 const symbols = symbol_for_segment.getPtr(.{897 const final_index = (try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index))) orelse {
856 .kind = relocatable_data.getSymbolKind(),898 continue; // found unknown section, so skip parsing into atom as we do not know how to handle it.
857 .index = @intCast(u32, relocatable_data.index),899 };
858 }) orelse continue; // encountered a segment we do not create an atom for
859 const sym_index = symbols.pop();
860 const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index));
861900
862 const atom = try gpa.create(Atom);901 const atom = try gpa.create(Atom);
863 atom.* = Atom.empty;902 atom.* = Atom.empty;
...@@ -870,7 +909,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -870,7 +909,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
870 atom.file = object_index;909 atom.file = object_index;
871 atom.size = relocatable_data.size;910 atom.size = relocatable_data.size;
872 atom.alignment = relocatable_data.getAlignment(self);911 atom.alignment = relocatable_data.getAlignment(self);
873 atom.sym_index = sym_index;
874912
875 const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{};913 const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{};
876 for (relocations) |relocation| {914 for (relocations) |relocation| {
...@@ -892,28 +930,31 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -892,28 +930,31 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
892930
893 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);931 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
894932
895 // symbols referencing the same atom will be added as alias933 if (symbol_for_segment.getPtr(.{
896 // or as 'parent' when they are global.934 .kind = relocatable_data.getSymbolKind(),
897 while (symbols.popOrNull()) |idx| {935 .index = relocatable_data.getIndex(),
898 const alias_symbol = self.symtable[idx];936 })) |symbols| {
899 const symbol = self.symtable[atom.sym_index];937 atom.sym_index = symbols.pop();
900 if (alias_symbol.isGlobal() and symbol.isLocal()) {938
901 atom.sym_index = idx;939 // symbols referencing the same atom will be added as alias
940 // or as 'parent' when they are global.
941 while (symbols.popOrNull()) |idx| {
942 const alias_symbol = self.symtable[idx];
943 const symbol = self.symtable[atom.sym_index];
944 if (alias_symbol.isGlobal() and symbol.isLocal()) {
945 atom.sym_index = idx;
946 }
902 }947 }
948 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
903 }949 }
904 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
905950
906 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];951 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
907 segment.alignment = std.math.max(segment.alignment, atom.alignment);952 if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned
908953 segment.alignment = std.math.max(segment.alignment, atom.alignment);
909 if (wasm_bin.atoms.getPtr(final_index)) |last| {
910 last.*.next = atom;
911 atom.prev = last.*;
912 last.* = atom;
913 } else {
914 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);
915 }954 }
916 log.debug("Parsed into atom: '{s}'", .{self.string_table.get(self.symtable[atom.sym_index].name)});955
956 try wasm_bin.appendAtomAtIndex(final_index, atom);
957 log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index });
917 }958 }
918}959}
919960