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,
4444/// Table of debug symbol names.
4545strtab: std.ArrayListUnmanaged(u8) = .{},
4646
47di_files: std.ArrayListUnmanaged(DIFile) = .{},
48di_files_free_list: std.ArrayListUnmanaged(u28) = .{},
49di_files_lookup: std.AutoHashMapUnmanaged(*const Module.File, u28) = .{},
47/// Quick lookup array of all defined source files referenced by at least one Decl.
48/// They will end up in the DWARF debug_line header as two lists:
49/// * []include_directory
50/// * []file_names
51di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{},
5052
5153/// List of atoms that are owned directly by the DWARF module.
5254/// TODO convert links in DebugInfoAtom into indices and make
......@@ -55,17 +57,6 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
5557
5658global_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
6960pub const Atom = struct {
7061 /// Previous/next linked list pointers.
7162 /// This is the linked list node for this Decl's corresponding .debug_info tag.
......@@ -922,8 +913,6 @@ pub fn deinit(self: *Dwarf) void {
922913 self.atom_free_list.deinit(gpa);
923914 self.strtab.deinit(gpa);
924915 self.di_files.deinit(gpa);
925 self.di_files_free_list.deinit(gpa);
926 self.di_files_lookup.deinit(gpa);
927916 self.global_abbrev_relocs.deinit(gpa);
928917
929918 for (self.managed_atoms.items) |atom| {
......@@ -1154,7 +1143,7 @@ pub fn commitDeclState(
11541143 self.dbg_line_fn_last = src_fn;
11551144
11561145 // TODO TEXME JK
1157 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module) * 100);
1146 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}) * 100);
11581147 }
11591148
11601149 const last_src_fn = self.dbg_line_fn_last.?;
......@@ -1649,16 +1638,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void {
16491638 if (self.dbg_line_fn_last == fn_link) {
16501639 self.dbg_line_fn_last = fn_link.prev;
16511640 }
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 }
16621641}
16631642
16641643pub fn writeDbgAbbrev(self: *Dwarf) !void {
......@@ -1902,7 +1881,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
19021881 }
19031882 // Write the form for the compile unit, which must match the abbrev table above.
19041883 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);
19061886 const producer_strp = try self.makeString(link.producer_string);
19071887
19081888 di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit));
......@@ -1954,6 +1934,21 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
19541934 }
19551935}
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
19571952fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void {
19581953 const target_endian = self.target.cpu.arch.endian();
19591954 switch (self.ptr_width) {
......@@ -2299,10 +2294,15 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void {
22992294 const dbg_line_prg_end = self.getDebugLineProgramEnd().?;
23002295 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
23022302 // The size of this header is variable, depending on the number of directories,
23032303 // files, and padding. We have a function to compute the upper bound size, however,
23042304 // 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);
23062306 log.debug("dbg_line_prg_off = {x}, needed_bytes = {x}", .{ dbg_line_prg_off, needed_bytes });
23072307 var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes);
23082308 defer di_buf.deinit();
......@@ -2356,17 +2356,22 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void {
23562356 0, // `DW.LNS.set_prologue_end`
23572357 0, // `DW.LNS.set_epilogue_begin`
23582358 1, // `DW.LNS.set_isa`
2359 0, // include_directories (none except the compilation unit cwd)
23602359 });
23612360
2362 for (self.di_files.items) |dif, i| {
2363 const full_path = try dif.getFullyResolvedPath(self.allocator);
2364 defer self.allocator.free(full_path);
2365 log.debug("adding new file name at {d} of '{s}'", .{ i + 1, full_path });
2366 di_buf.appendSliceAssumeCapacity(full_path);
2361 for (paths.dirs) |dir, i| {
2362 log.debug("adding new include dir at {d} of '{s}'", .{ i + 1, dir });
2363 di_buf.appendSliceAssumeCapacity(dir);
2364 di_buf.appendAssumeCapacity(0);
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);
23672372 di_buf.appendSliceAssumeCapacity(&[_]u8{
23682373 0, // null byte for the relative path name
2369 0, // directory_index
2374 @intCast(u8, dir_index + 1), // directory_index
23702375 0, // mtime (TODO supply this)
23712376 0, // file size bytes (TODO supply this)
23722377 });
......@@ -2442,25 +2447,28 @@ fn ptrWidthBytes(self: Dwarf) u8 {
24422447 };
24432448}
24442449
2445fn dbgLineNeededHeaderBytes(self: Dwarf, module: *Module) u32 {
2450fn dbgLineNeededHeaderBytes(self: Dwarf, dirs: []const []const u8, files: []const []const u8) u32 {
2451 _ = self;
24462452 const directory_entry_format_count = 1;
24472453 const file_name_entry_format_count = 1;
2448 const directory_count = 1;
2449 const file_name_count = self.di_files.items.len;
2450 const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
2454 const directory_count = dirs.len + 1;
2455 const file_name_count = files.len;
2456
2457 var dir_names_len: usize = 0;
2458 for (dirs) |dir| {
2459 dir_names_len += dir.len + 1;
2460 }
24512461
24522462 var file_names_len: usize = 0;
2453 for (self.di_files.items) |dif| {
2454 const dir_path = dif.file_source.pkg.root_src_directory.path orelse ".";
2455 file_names_len += dir_path.len + dif.file_source.sub_file_path.len + 1;
2463 for (files) |file| {
2464 file_names_len += file.len + 1;
24562465 }
24572466
24582467 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
24592468 directory_count * 8 + file_name_count * 8 +
24602469 // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like
24612470 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2462 root_src_dir_path_len +
2463 file_names_len);
2471 dir_names_len + file_names_len);
24642472}
24652473
24662474/// 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 {
25712579 }
25722580}
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
25922582fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 {
25932583 const decl = mod.declPtr(decl_index);
25942584 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);
25962586 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
26032587 switch (self.bin_file.tag) {
26042588 .elf => self.bin_file.cast(File.Elf).?.debug_line_header_dirty = true,
26052589 .macho => self.bin_file.cast(File.MachO).?.d_sym.?.debug_line_header_dirty = true,
26062590 .wasm => {},
26072591 else => unreachable,
26082592 }
2609 } else {
2610 const dif = &self.di_files.items[gop.value_ptr.*];
2611 dif.ref_count += 1;
26122593 }
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 };
26142636}
26152637
26162638fn addDbgInfoErrorSet(