authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-07 21:38:38+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-09 09:24:25+01:00
log6817219e275a739794006d242bfa13840757b768
tree1df274e0e8fc6aa90694ccb9dfce19521870f5d9
parent6ec34edb9a2e45226bcedbae896ba2aa11b4ed7b

dwarf: generate list of include dirs and file names like LLVM backend


1 files changed, 96 insertions(+), 74 deletions(-)

src/link/Dwarf.zig+96-74
...@@ -44,9 +44,11 @@ abbrev_table_offset: ?u64 = null,...@@ -44,9 +44,11 @@ abbrev_table_offset: ?u64 = null,
44/// Table of debug symbol names.44/// Table of debug symbol names.
45strtab: std.ArrayListUnmanaged(u8) = .{},45strtab: std.ArrayListUnmanaged(u8) = .{},
4646
47di_files: std.ArrayListUnmanaged(DIFile) = .{},47/// Quick lookup array of all defined source files referenced by at least one Decl.
48di_files_free_list: std.ArrayListUnmanaged(u28) = .{},48/// They will end up in the DWARF debug_line header as two lists:
49di_files_lookup: std.AutoHashMapUnmanaged(*const Module.File, u28) = .{},49/// * []include_directory
50/// * []file_names
51di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{},
5052
51/// List of atoms that are owned directly by the DWARF module.53/// List of atoms that are owned directly by the DWARF module.
52/// TODO convert links in DebugInfoAtom into indices and make54/// TODO convert links in DebugInfoAtom into indices and make
...@@ -55,17 +57,6 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},...@@ -55,17 +57,6 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
5557
56global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},58global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
5759
58const DIFile = struct {
59 file_source: *const Module.File,
60 ref_count: u32,
61
62 fn getFullyResolvedPath(dif: DIFile, allocator: Allocator) ![]const u8 {
63 const path = try dif.file_source.fullPath(allocator);
64 defer allocator.free(path);
65 return fs.realpathAlloc(allocator, path);
66 }
67};
68
69pub const Atom = struct {60pub const Atom = struct {
70 /// Previous/next linked list pointers.61 /// Previous/next linked list pointers.
71 /// This is the linked list node for this Decl's corresponding .debug_info tag.62 /// This is the linked list node for this Decl's corresponding .debug_info tag.
...@@ -922,8 +913,6 @@ pub fn deinit(self: *Dwarf) void {...@@ -922,8 +913,6 @@ pub fn deinit(self: *Dwarf) void {
922 self.atom_free_list.deinit(gpa);913 self.atom_free_list.deinit(gpa);
923 self.strtab.deinit(gpa);914 self.strtab.deinit(gpa);
924 self.di_files.deinit(gpa);915 self.di_files.deinit(gpa);
925 self.di_files_free_list.deinit(gpa);
926 self.di_files_lookup.deinit(gpa);
927 self.global_abbrev_relocs.deinit(gpa);916 self.global_abbrev_relocs.deinit(gpa);
928917
929 for (self.managed_atoms.items) |atom| {918 for (self.managed_atoms.items) |atom| {
...@@ -1154,7 +1143,7 @@ pub fn commitDeclState(...@@ -1154,7 +1143,7 @@ pub fn commitDeclState(
1154 self.dbg_line_fn_last = src_fn;1143 self.dbg_line_fn_last = src_fn;
11551144
1156 // TODO TEXME JK1145 // TODO TEXME JK
1157 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module) * 100);1146 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}) * 100);
1158 }1147 }
11591148
1160 const last_src_fn = self.dbg_line_fn_last.?;1149 const last_src_fn = self.dbg_line_fn_last.?;
...@@ -1649,16 +1638,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void {...@@ -1649,16 +1638,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void {
1649 if (self.dbg_line_fn_last == fn_link) {1638 if (self.dbg_line_fn_last == fn_link) {
1650 self.dbg_line_fn_last = fn_link.prev;1639 self.dbg_line_fn_last = fn_link.prev;
1651 }1640 }
1652
1653 const file_source = decl.getFileScope();
1654 if (self.di_files_lookup.get(file_source)) |dif_index| {
1655 const dif = &self.di_files.items[dif_index];
1656 dif.ref_count -= 1;
1657 if (dif.ref_count == 0) {
1658 self.di_files_free_list.append(gpa, dif_index) catch {};
1659 }
1660 _ = self.di_files_lookup.remove(file_source);
1661 }
1662}1641}
16631642
1664pub fn writeDbgAbbrev(self: *Dwarf) !void {1643pub fn writeDbgAbbrev(self: *Dwarf) !void {
...@@ -1902,7 +1881,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1902,7 +1881,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1902 }1881 }
1903 // Write the form for the compile unit, which must match the abbrev table above.1882 // Write the form for the compile unit, which must match the abbrev table above.
1904 const name_strp = try self.makeString(module.root_pkg.root_src_path);1883 const name_strp = try self.makeString(module.root_pkg.root_src_path);
1905 const comp_dir_strp = try self.makeString(module.root_pkg.root_src_directory.path orelse ".");1884 const compile_unit_dir = self.getCompDir(module);
1885 const comp_dir_strp = try self.makeString(compile_unit_dir);
1906 const producer_strp = try self.makeString(link.producer_string);1886 const producer_strp = try self.makeString(link.producer_string);
19071887
1908 di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit));1888 di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit));
...@@ -1954,6 +1934,21 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1954,6 +1934,21 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1954 }1934 }
1955}1935}
19561936
1937fn getCompDir(self: Dwarf, module: *Module) []const u8 {
1938 // For macOS stack traces, we want to avoid having to parse the compilation unit debug
1939 // info. As long as each debug info file has a path independent of the compilation unit
1940 // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug
1941 // info. If we provide an absolute path to LLVM here for the compilation unit debug
1942 // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we
1943 // pass "." for the compilation unit directory. This forces each debug file to have a
1944 // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug
1945 // files will no longer reference DW_AT_comp_dir, for the purpose of being able to
1946 // support the common practice of stripping all but the line number sections from an
1947 // executable.
1948 if (self.bin_file.tag == .macho) return ".";
1949 return module.root_pkg.root_src_directory.path orelse ".";
1950}
1951
1957fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void {1952fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void {
1958 const target_endian = self.target.cpu.arch.endian();1953 const target_endian = self.target.cpu.arch.endian();
1959 switch (self.ptr_width) {1954 switch (self.ptr_width) {
...@@ -2299,10 +2294,15 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void {...@@ -2299,10 +2294,15 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void {
2299 const dbg_line_prg_end = self.getDebugLineProgramEnd().?;2294 const dbg_line_prg_end = self.getDebugLineProgramEnd().?;
2300 assert(dbg_line_prg_end != 0);2295 assert(dbg_line_prg_end != 0);
23012296
2297 // Convert all input DI files into a set of include dirs and file names.
2298 var arena = std.heap.ArenaAllocator.init(self.allocator);
2299 defer arena.deinit();
2300 const paths = try self.genIncludeDirsAndFileNames(arena.allocator(), module);
2301
2302 // The size of this header is variable, depending on the number of directories,2302 // The size of this header is variable, depending on the number of directories,
2303 // files, and padding. We have a function to compute the upper bound size, however,2303 // files, and padding. We have a function to compute the upper bound size, however,
2304 // because it's needed for determining where to put the offset of the first `SrcFn`.2304 // because it's needed for determining where to put the offset of the first `SrcFn`.
2305 const needed_bytes = self.dbgLineNeededHeaderBytes(module);2305 const needed_bytes = self.dbgLineNeededHeaderBytes(paths.dirs, paths.files);
2306 log.debug("dbg_line_prg_off = {x}, needed_bytes = {x}", .{ dbg_line_prg_off, needed_bytes });2306 log.debug("dbg_line_prg_off = {x}, needed_bytes = {x}", .{ dbg_line_prg_off, needed_bytes });
2307 var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes);2307 var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes);
2308 defer di_buf.deinit();2308 defer di_buf.deinit();
...@@ -2356,17 +2356,22 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void {...@@ -2356,17 +2356,22 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void {
2356 0, // `DW.LNS.set_prologue_end`2356 0, // `DW.LNS.set_prologue_end`
2357 0, // `DW.LNS.set_epilogue_begin`2357 0, // `DW.LNS.set_epilogue_begin`
2358 1, // `DW.LNS.set_isa`2358 1, // `DW.LNS.set_isa`
2359 0, // include_directories (none except the compilation unit cwd)
2360 });2359 });
23612360
2362 for (self.di_files.items) |dif, i| {2361 for (paths.dirs) |dir, i| {
2363 const full_path = try dif.getFullyResolvedPath(self.allocator);2362 log.debug("adding new include dir at {d} of '{s}'", .{ i + 1, dir });
2364 defer self.allocator.free(full_path);2363 di_buf.appendSliceAssumeCapacity(dir);
2365 log.debug("adding new file name at {d} of '{s}'", .{ i + 1, full_path });2364 di_buf.appendAssumeCapacity(0);
2366 di_buf.appendSliceAssumeCapacity(full_path);2365 }
2366 di_buf.appendAssumeCapacity(0); // include directories sentinel
2367
2368 for (paths.files) |file, i| {
2369 const dir_index = paths.files_dirs_indexes[i];
2370 log.debug("adding new file name at {d} of '{s}' referencing directory {d}", .{ i + 1, file, dir_index + 1 });
2371 di_buf.appendSliceAssumeCapacity(file);
2367 di_buf.appendSliceAssumeCapacity(&[_]u8{2372 di_buf.appendSliceAssumeCapacity(&[_]u8{
2368 0, // null byte for the relative path name2373 0, // null byte for the relative path name
2369 0, // directory_index2374 @intCast(u8, dir_index + 1), // directory_index
2370 0, // mtime (TODO supply this)2375 0, // mtime (TODO supply this)
2371 0, // file size bytes (TODO supply this)2376 0, // file size bytes (TODO supply this)
2372 });2377 });
...@@ -2442,25 +2447,28 @@ fn ptrWidthBytes(self: Dwarf) u8 {...@@ -2442,25 +2447,28 @@ fn ptrWidthBytes(self: Dwarf) u8 {
2442 };2447 };
2443}2448}
24442449
2445fn dbgLineNeededHeaderBytes(self: Dwarf, module: *Module) u32 {2450fn dbgLineNeededHeaderBytes(self: Dwarf, dirs: []const []const u8, files: []const []const u8) u32 {
2451 _ = self;
2446 const directory_entry_format_count = 1;2452 const directory_entry_format_count = 1;
2447 const file_name_entry_format_count = 1;2453 const file_name_entry_format_count = 1;
2448 const directory_count = 1;2454 const directory_count = dirs.len + 1;
2449 const file_name_count = self.di_files.items.len;2455 const file_name_count = files.len;
2450 const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "."2456
2457 var dir_names_len: usize = 0;
2458 for (dirs) |dir| {
2459 dir_names_len += dir.len + 1;
2460 }
24512461
2452 var file_names_len: usize = 0;2462 var file_names_len: usize = 0;
2453 for (self.di_files.items) |dif| {2463 for (files) |file| {
2454 const dir_path = dif.file_source.pkg.root_src_directory.path orelse ".";2464 file_names_len += file.len + 1;
2455 file_names_len += dir_path.len + dif.file_source.sub_file_path.len + 1;
2456 }2465 }
24572466
2458 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +2467 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
2459 directory_count * 8 + file_name_count * 8 +2468 directory_count * 8 + file_name_count * 8 +
2460 // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like2469 // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like
2461 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.2470 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2462 root_src_dir_path_len +2471 dir_names_len + file_names_len);
2463 file_names_len);
2464}2472}
24652473
2466/// The reloc offset for the line offset of a function from the previous function's line.2474/// The reloc offset for the line offset of a function from the previous function's line.
...@@ -2571,46 +2579,60 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {...@@ -2571,46 +2579,60 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
2571 }2579 }
2572}2580}
25732581
2574fn allocateDIFileIndex(self: *Dwarf) !u28 {
2575 try self.di_files.ensureUnusedCapacity(self.allocator, 1);
2576
2577 const index = blk: {
2578 if (self.di_files_free_list.popOrNull()) |index| {
2579 log.debug(" (reusing DIFile index {d})", .{index});
2580 break :blk index;
2581 } else {
2582 const index = @intCast(u28, self.di_files.items.len);
2583 log.debug(" (allocating DIFile index {d})", .{index});
2584 _ = self.di_files.addOneAssumeCapacity();
2585 break :blk index;
2586 }
2587 };
2588
2589 return index;
2590}
2591
2592fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 {2582fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 {
2593 const decl = mod.declPtr(decl_index);2583 const decl = mod.declPtr(decl_index);
2594 const file_scope = decl.getFileScope();2584 const file_scope = decl.getFileScope();
2595 const gop = try self.di_files_lookup.getOrPut(self.allocator, file_scope);2585 const gop = try self.di_files.getOrPut(self.allocator, file_scope);
2596 if (!gop.found_existing) {2586 if (!gop.found_existing) {
2597 gop.value_ptr.* = try self.allocateDIFileIndex();
2598 self.di_files.items[gop.value_ptr.*] = .{
2599 .file_source = file_scope,
2600 .ref_count = 1,
2601 };
2602
2603 switch (self.bin_file.tag) {2587 switch (self.bin_file.tag) {
2604 .elf => self.bin_file.cast(File.Elf).?.debug_line_header_dirty = true,2588 .elf => self.bin_file.cast(File.Elf).?.debug_line_header_dirty = true,
2605 .macho => self.bin_file.cast(File.MachO).?.d_sym.?.debug_line_header_dirty = true,2589 .macho => self.bin_file.cast(File.MachO).?.d_sym.?.debug_line_header_dirty = true,
2606 .wasm => {},2590 .wasm => {},
2607 else => unreachable,2591 else => unreachable,
2608 }2592 }
2609 } else {
2610 const dif = &self.di_files.items[gop.value_ptr.*];
2611 dif.ref_count += 1;
2612 }2593 }
2613 return gop.value_ptr.*;2594 return @intCast(u28, gop.index);
2595}
2596
2597fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator, module: *Module) !struct {
2598 dirs: []const []const u8,
2599 files: []const []const u8,
2600 files_dirs_indexes: []u28,
2601} {
2602 var dirs = std.StringArrayHashMap(void).init(arena);
2603 try dirs.ensureTotalCapacity(self.di_files.count());
2604
2605 var files = std.ArrayList([]const u8).init(arena);
2606 try files.ensureTotalCapacityPrecise(self.di_files.count());
2607
2608 var files_dir_indexes = std.ArrayList(u28).init(arena);
2609 try files_dir_indexes.ensureTotalCapacity(self.di_files.count());
2610
2611 const comp_dir = self.getCompDir(module);
2612
2613 for (self.di_files.keys()) |dif| {
2614 const full_path = try dif.fullPath(arena);
2615 const dir_path = std.fs.path.dirname(full_path) orelse ".";
2616 const sub_file_path = std.fs.path.basename(full_path);
2617
2618 const dir_index: u28 = blk: {
2619 const actual_dir_path = if (mem.indexOf(u8, dir_path, comp_dir)) |_| inner: {
2620 if (comp_dir.len == dir_path.len) break :blk 0;
2621 break :inner dir_path[comp_dir.len + 1 ..];
2622 } else dir_path;
2623 const dirs_gop = dirs.getOrPutAssumeCapacity(actual_dir_path);
2624 break :blk @intCast(u28, dirs_gop.index);
2625 };
2626
2627 files_dir_indexes.appendAssumeCapacity(dir_index);
2628 files.appendAssumeCapacity(sub_file_path);
2629 }
2630
2631 return .{
2632 .dirs = dirs.keys(),
2633 .files = files.items,
2634 .files_dirs_indexes = files_dir_indexes.items,
2635 };
2614}2636}
26152637
2616fn addDbgInfoErrorSet(2638fn addDbgInfoErrorSet(