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(...@@ -862,7 +862,8 @@ pub fn commitDeclState(
862 .wasm => {862 .wasm => {
863 const wasm_file = file.cast(File.Wasm).?;863 const wasm_file = file.cast(File.Wasm).?;
864 const segment_index = wasm_file.debug_line_index.?;864 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;
866 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);867 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
867 },868 },
868 else => unreachable,869 else => unreachable,
...@@ -974,9 +975,10 @@ pub fn commitDeclState(...@@ -974,9 +975,10 @@ pub fn commitDeclState(
974 },975 },
975 .wasm => {976 .wasm => {
976 const wasm_file = file.cast(File.Wasm).?;977 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.?;
978 const segment = &wasm_file.segments.items[segment_index];979 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;
980 if (needed_size != segment.size) {982 if (needed_size != segment.size) {
981 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});983 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
982 if (needed_size > segment.size) {984 if (needed_size > segment.size) {
...@@ -1148,9 +1150,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3...@@ -1148,9 +1150,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
1148 },1150 },
1149 .wasm => {1151 .wasm => {
1150 const wasm_file = file.cast(File.Wasm).?;1152 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.?;
1152 const segment = &wasm_file.segments.items[segment_index];1154 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;
1154 const offset = segment.offset + atom.off;1157 const offset = segment.offset + atom.off;
1155 try writeDbgInfoNopsToArrayList(gpa, debug_info, offset, 0, &.{0}, atom.len, false);1158 try writeDbgInfoNopsToArrayList(gpa, debug_info, offset, 0, &.{0}, atom.len, false);
1156 },1159 },
...@@ -1279,9 +1282,10 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1279,9 +1282,10 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1279 },1282 },
1280 .wasm => {1283 .wasm => {
1281 const wasm_file = file.cast(File.Wasm).?;1284 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.?;
1283 const segment = &wasm_file.segments.items[segment_index];1286 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;
1285 if (needed_size != segment.size) {1289 if (needed_size != segment.size) {
1286 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});1290 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1287 if (needed_size > segment.size) {1291 if (needed_size > segment.size) {
...@@ -1343,7 +1347,8 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)...@@ -1343,7 +1347,8 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
1343 const segment_index = wasm_file.debug_line_index.?;1347 const segment_index = wasm_file.debug_line_index.?;
1344 const segment = wasm_file.segments.items[segment_index];1348 const segment = wasm_file.segments.items[segment_index];
1345 const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();1349 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);
1347 },1352 },
1348 else => unreachable,1353 else => unreachable,
1349 }1354 }
...@@ -1579,8 +1584,8 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {...@@ -1579,8 +1584,8 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
1579 },1584 },
1580 .wasm => {1585 .wasm => {
1581 const wasm_file = file.cast(File.Wasm).?;1586 const wasm_file = file.cast(File.Wasm).?;
1582 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_abbrev_index);1587 const segment_index = wasm_file.debug_abbrev_index.?;
1583 const debug_abbrev = &wasm_file.atoms.get(segment_index).?.code;1588 const debug_abbrev = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
1584 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);1589 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);
1585 mem.copy(u8, debug_abbrev.items, &abbrev_buf);1590 mem.copy(u8, debug_abbrev.items, &abbrev_buf);
1586 },1591 },
...@@ -1693,7 +1698,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6...@@ -1693,7 +1698,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
1693 .wasm => {1698 .wasm => {
1694 const wasm_file = file.cast(File.Wasm).?;1699 const wasm_file = file.cast(File.Wasm).?;
1695 const segment_index = wasm_file.debug_info_index.?;1700 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;
1697 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);1702 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
1698 },1703 },
1699 else => unreachable,1704 else => unreachable,
...@@ -2023,8 +2028,8 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {...@@ -2023,8 +2028,8 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
2023 },2028 },
2024 .wasm => {2029 .wasm => {
2025 const wasm_file = file.cast(File.Wasm).?;2030 const wasm_file = file.cast(File.Wasm).?;
2026 const segment_index = try wasm_file.getOrSetDebugIndex(&wasm_file.debug_ranges_index);2031 const segment_index = wasm_file.debug_ranges_index.?;
2027 const debug_ranges = &wasm_file.atoms.get(segment_index).?.code;2032 const debug_ranges = &wasm_file.atoms.get(segment_index).?.getFirstZigAtom().code;
2028 try debug_ranges.resize(wasm_file.base.allocator, needed_size);2033 try debug_ranges.resize(wasm_file.base.allocator, needed_size);
2029 mem.copy(u8, debug_ranges.items, di_buf.items);2034 mem.copy(u8, debug_ranges.items, di_buf.items);
2030 },2035 },
...@@ -2149,7 +2154,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2149,7 +2154,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
2149 .wasm => {2154 .wasm => {
2150 const wasm_file = file.cast(File.Wasm).?;2155 const wasm_file = file.cast(File.Wasm).?;
2151 const segment_index = wasm_file.debug_line_index.?;2156 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;
2153 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);2158 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
2154 },2159 },
2155 else => unreachable,2160 else => unreachable,
...@@ -2299,7 +2304,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {...@@ -2299,7 +2304,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
2299 .wasm => {2304 .wasm => {
2300 const wasm_file = file.cast(File.Wasm).?;2305 const wasm_file = file.cast(File.Wasm).?;
2301 const segment_index = wasm_file.debug_info_index.?;2306 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;
2303 mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);2308 mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
2304 },2309 },
2305 else => unreachable,2310 else => unreachable,
src/link/Wasm.zig+31-17
...@@ -349,6 +349,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -349,6 +349,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
349 };349 };
350 }350 }
351351
352 try wasm_bin.initDebugSections();
352 return wasm_bin;353 return wasm_bin;
353}354}
354355
...@@ -377,6 +378,23 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {...@@ -377,6 +378,23 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
377 return self;378 return self;
378}379}
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
380fn parseInputFiles(self: *Wasm, files: []const []const u8) !void {398fn parseInputFiles(self: *Wasm, files: []const []const u8) !void {
381 for (files) |path| {399 for (files) |path| {
382 if (try self.parseObjectFile(path)) continue;400 if (try self.parseObjectFile(path)) continue;
...@@ -1968,23 +1986,19 @@ fn populateErrorNameTable(self: *Wasm) !void {...@@ -1968,23 +1986,19 @@ fn populateErrorNameTable(self: *Wasm) !void {
1968 try self.parseAtom(names_atom, .{ .data = .read_only });1986 try self.parseAtom(names_atom, .{ .data = .read_only });
1969}1987}
19701988
1971/// From a given index variable, returns it value if set.1989/// From a given index variable, creates a new debug section.
1972/// When not set, initialises a new segment, sets the index,1990/// This initializes the index, appends a new segment,
1973/// and returns it value.1991/// and finally, creates a managed `Atom`.
1974/// When a new segment is initialised. It also creates an atom.1992pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32) !void {
1975pub fn getOrSetDebugIndex(self: *Wasm, index: *?u32) !u32 {1993 const new_index = @intCast(u32, self.segments.items.len);
1976 return (index.*) orelse {1994 index.* = new_index;
1977 const new_index = @intCast(u32, self.segments.items.len);1995 try self.appendDummySegment();
1978 index.* = new_index;1996
1979 try self.appendDummySegment();1997 const atom = try self.base.allocator.create(Atom);
19801998 atom.* = Atom.empty;
1981 const atom = try self.base.allocator.create(Atom);1999 atom.alignment = 1; // debug sections are always 1-byte-aligned
1982 atom.* = Atom.empty;2000 try self.managed_atoms.append(self.base.allocator, atom);
1983 atom.alignment = 1; // debug sections are always 1-byte-aligned2001 try self.atoms.put(self.base.allocator, new_index, atom);
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 };
1988}2002}
19892003
1990fn resetState(self: *Wasm) void {2004fn resetState(self: *Wasm) void {
src/link/Wasm/Atom.zig+32-3
...@@ -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 };
...@@ -184,8 +197,24 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -184,8 +197,24 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
184 return target_atom.offset + segment.offset + (relocation.addend orelse 0);197 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
185 },198 },
186 .R_WASM_EVENT_INDEX_LEB => return symbol.index,199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
187 .R_WASM_SECTION_OFFSET_I32,200 .R_WASM_SECTION_OFFSET_I32 => {
188 .R_WASM_FUNCTION_OFFSET_I32,201 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
189 => return relocation.addend orelse 0,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 },
190 }219 }
191}220}