authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-09-04 21:00:44+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-09-07 18:53:16+02:00
logf2c8d09c4f743f4172e0a0a6f0fd59a96e56386d
treeb6d34647df04407a67339920cae7c986788c2c0a
parentb2718e213ed7e7cd8bcd85bdf49d7ae33c857c58
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: Mix Zig -and Object debug atoms

When linking a Zig-compilation with an object file, we allow mixing the debug atoms to make sure debug information is preserved from object files. By default, we now always initialize all debug sections if the `strip` flag is unset. This also fixes relocations for debug information as previously the offset of an atom wasn't calculated, and neither was the code size itself which meant that debug lines were off and file names from other object files were missing.

3 files changed, 83 insertions(+), 35 deletions(-)

src/link/Dwarf.zig+20-15
......@@ -862,7 +862,8 @@ pub fn commitDeclState(
862862 .wasm => {
863863 const wasm_file = file.cast(File.Wasm).?;
864864 const segment_index = wasm_file.debug_line_index.?;
865 const debug_line = wasm_file.atoms.get(segment_index).?.code;
865 const atom = wasm_file.atoms.get(segment_index).?;
866 const debug_line = atom.getFirstZigAtom().code;
866867 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
867868 },
868869 else => unreachable,
......@@ -974,9 +975,10 @@ pub fn commitDeclState(
974975 },
975976 .wasm => {
976977 const wasm_file = file.cast(File.Wasm).?;
977 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_line_index);
978 const segment_index = wasm_file.debug_line_index.?;
978979 const segment = &wasm_file.segments.items[segment_index];
979 const debug_line = &wasm_file.atoms.get(segment_index).?.code;
980 const atom = wasm_file.atoms.get(segment_index).?;
981 const debug_line = &atom.getFirstZigAtom().code;
980982 if (needed_size != segment.size) {
981983 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
982984 if (needed_size > segment.size) {
......@@ -1148,9 +1150,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
11481150 },
11491151 .wasm => {
11501152 const wasm_file = file.cast(File.Wasm).?;
1151 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_info_index);
1153 const segment_index = wasm_file.debug_info_index.?;
11521154 const segment = &wasm_file.segments.items[segment_index];
1153 const debug_info = &wasm_file.atoms.get(segment_index).?.code;
1155 const info_atom = wasm_file.atoms.get(segment_index).?;
1156 const debug_info = &info_atom.getFirstZigAtom().code;
11541157 const offset = segment.offset + atom.off;
11551158 try writeDbgInfoNopsToArrayList(gpa, debug_info, offset, 0, &.{0}, atom.len, false);
11561159 },
......@@ -1279,9 +1282,10 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12791282 },
12801283 .wasm => {
12811284 const wasm_file = file.cast(File.Wasm).?;
1282 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_info_index);
1285 const segment_index = wasm_file.debug_info_index.?;
12831286 const segment = &wasm_file.segments.items[segment_index];
1284 const debug_info = &wasm_file.atoms.get(segment_index).?.code;
1287 const info_atom = wasm_file.atoms.get(segment_index).?;
1288 const debug_info = &info_atom.getFirstZigAtom().code;
12851289 if (needed_size != segment.size) {
12861290 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
12871291 if (needed_size > segment.size) {
......@@ -1343,7 +1347,8 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
13431347 const segment_index = wasm_file.debug_line_index.?;
13441348 const segment = wasm_file.segments.items[segment_index];
13451349 const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
1346 mem.copy(u8, wasm_file.atoms.get(segment_index).?.code.items[offset..], &data);
1350 const atom = wasm_file.atoms.get(segment_index).?.getFirstZigAtom();
1351 mem.copy(u8, atom.code.items[offset..], &data);
13471352 },
13481353 else => unreachable,
13491354 }
......@@ -1579,8 +1584,8 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
15791584 },
15801585 .wasm => {
15811586 const wasm_file = file.cast(File.Wasm).?;
1582 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_abbrev_index);
1583 const debug_abbrev = &wasm_file.atoms.get(segment_index).?.code;
1587 const segment_index = wasm_file.debug_abbrev_index.?;
1588 const debug_abbrev = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
15841589 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);
15851590 mem.copy(u8, debug_abbrev.items, &abbrev_buf);
15861591 },
......@@ -1693,7 +1698,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
16931698 .wasm => {
16941699 const wasm_file = file.cast(File.Wasm).?;
16951700 const segment_index = wasm_file.debug_info_index.?;
1696 const debug_info = &wasm_file.atoms.get(segment_index).?.code;
1701 const debug_info = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
16971702 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
16981703 },
16991704 else => unreachable,
......@@ -2023,8 +2028,8 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
20232028 },
20242029 .wasm => {
20252030 const wasm_file = file.cast(File.Wasm).?;
2026 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_ranges_index);
2027 const debug_ranges = &wasm_file.atoms.get(segment_index).?.code;
2031 const segment_index = wasm_file.debug_ranges_index.?;
2032 const debug_ranges = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
20282033 try debug_ranges.resize(wasm_file.base.allocator, needed_size);
20292034 mem.copy(u8, debug_ranges.items, di_buf.items);
20302035 },
......@@ -2149,7 +2154,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
21492154 .wasm => {
21502155 const wasm_file = file.cast(File.Wasm).?;
21512156 const segment_index = wasm_file.debug_line_index.?;
2152 const debug_line = wasm_file.atoms.get(segment_index).?.code;
2157 const debug_line = wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
21532158 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
21542159 },
21552160 else => unreachable,
......@@ -2299,7 +2304,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
22992304 .wasm => {
23002305 const wasm_file = file.cast(File.Wasm).?;
23012306 const segment_index = wasm_file.debug_info_index.?;
2302 const debug_info = wasm_file.atoms.get(segment_index).?.code;
2307 const debug_info = wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
23032308 mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
23042309 },
23052310 else => unreachable,
src/link/Wasm.zig+31-17
......@@ -349,6 +349,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
349349 };
350350 }
351351
352 try wasm_bin.initDebugSections();
352353 return wasm_bin;
353354}
354355
......@@ -377,6 +378,23 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
377378 return self;
378379}
379380
381/// Initializes symbols and atoms for the debug sections
382/// Initialization is only done when compiling Zig code.
383/// When Zig is invoked as a linker instead, the atoms
384/// and symbols come from the object files instead.
385pub fn initDebugSections(self: *Wasm) !void {
386 if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections
387 // this will create an Atom and set the index for us.
388 try self.createDebugSectionForIndex(&self.debug_info_index);
389 try self.createDebugSectionForIndex(&self.debug_line_index);
390 try self.createDebugSectionForIndex(&self.debug_loc_index);
391 try self.createDebugSectionForIndex(&self.debug_abbrev_index);
392 try self.createDebugSectionForIndex(&self.debug_ranges_index);
393 try self.createDebugSectionForIndex(&self.debug_str_index);
394 try self.createDebugSectionForIndex(&self.debug_pubnames_index);
395 try self.createDebugSectionForIndex(&self.debug_pubtypes_index);
396}
397
380398fn parseInputFiles(self: *Wasm, files: []const []const u8) !void {
381399 for (files) |path| {
382400 if (try self.parseObjectFile(path)) continue;
......@@ -1968,23 +1986,19 @@ fn populateErrorNameTable(self: *Wasm) !void {
19681986 try self.parseAtom(names_atom, .{ .data = .read_only });
19691987}
19701988
1971/// From a given index variable, returns it value if set.
1972/// When not set, initialises a new segment, sets the index,
1973/// and returns it value.
1974/// When a new segment is initialised. It also creates an atom.
1975pub fn getOrSetDebugIndex(self: *Wasm, index: *?u32) !u32 {
1976 return (index.*) orelse {
1977 const new_index = @intCast(u32, self.segments.items.len);
1978 index.* = new_index;
1979 try self.appendDummySegment();
1980
1981 const atom = try self.base.allocator.create(Atom);
1982 atom.* = Atom.empty;
1983 atom.alignment = 1; // debug sections are always 1-byte-aligned
1984 try self.managed_atoms.append(self.base.allocator, atom);
1985 try self.atoms.put(self.base.allocator, new_index, atom);
1986 return new_index;
1987 };
1989/// From a given index variable, creates a new debug section.
1990/// This initializes the index, appends a new segment,
1991/// and finally, creates a managed `Atom`.
1992pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32) !void {
1993 const new_index = @intCast(u32, self.segments.items.len);
1994 index.* = new_index;
1995 try self.appendDummySegment();
1996
1997 const atom = try self.base.allocator.create(Atom);
1998 atom.* = Atom.empty;
1999 atom.alignment = 1; // debug sections are always 1-byte-aligned
2000 try self.managed_atoms.append(self.base.allocator, atom);
2001 try self.atoms.put(self.base.allocator, new_index, atom);
19882002}
19892003
19902004fn resetState(self: *Wasm) void {
src/link/Wasm/Atom.zig+32-3
......@@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom {
9090 return tmp;
9191}
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
93106/// Returns the location of the symbol that represents this `Atom`
94107pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
95108 return .{ .file = self.file, .index = self.sym_index };
......@@ -184,8 +197,24 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
184197 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
185198 },
186199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
187 .R_WASM_SECTION_OFFSET_I32,
188 .R_WASM_FUNCTION_OFFSET_I32,
189 => return relocation.addend orelse 0,
200 .R_WASM_SECTION_OFFSET_I32 => {
201 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
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 },
190219 }
191220}