| ... | ... | @@ -44,9 +44,11 @@ abbrev_table_offset: ?u64 = null, |
| 44 | 44 | /// Table of debug symbol names. |
| 45 | 45 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 46 | 46 | |
| 47 | | di_files: std.ArrayListUnmanaged(DIFile) = .{}, |
| 48 | | di_files_free_list: std.ArrayListUnmanaged(u28) = .{}, |
| 49 | | di_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 |
| 51 | di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{}, |
| 50 | 52 | |
| 51 | 53 | /// List of atoms that are owned directly by the DWARF module. |
| 52 | 54 | /// TODO convert links in DebugInfoAtom into indices and make |
| ... | ... | @@ -55,17 +57,6 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, |
| 55 | 57 | |
| 56 | 58 | global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, |
| 57 | 59 | |
| 58 | | const 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 | | |
| 69 | 60 | pub const Atom = struct { |
| 70 | 61 | /// Previous/next linked list pointers. |
| 71 | 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 | 913 | self.atom_free_list.deinit(gpa); |
| 923 | 914 | self.strtab.deinit(gpa); |
| 924 | 915 | self.di_files.deinit(gpa); |
| 925 | | self.di_files_free_list.deinit(gpa); |
| 926 | | self.di_files_lookup.deinit(gpa); |
| 927 | 916 | self.global_abbrev_relocs.deinit(gpa); |
| 928 | 917 | |
| 929 | 918 | for (self.managed_atoms.items) |atom| { |
| ... | ... | @@ -1154,7 +1143,7 @@ pub fn commitDeclState( |
| 1154 | 1143 | self.dbg_line_fn_last = src_fn; |
| 1155 | 1144 | |
| 1156 | 1145 | // 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 | } |
| 1159 | 1148 | |
| 1160 | 1149 | const last_src_fn = self.dbg_line_fn_last.?; |
| ... | ... | @@ -1649,16 +1638,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { |
| 1649 | 1638 | if (self.dbg_line_fn_last == fn_link) { |
| 1650 | 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 | } |
| 1663 | 1642 | |
| 1664 | 1643 | pub fn writeDbgAbbrev(self: *Dwarf) !void { |
| ... | ... | @@ -1902,7 +1881,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1902 | 1881 | } |
| 1903 | 1882 | // Write the form for the compile unit, which must match the abbrev table above. |
| 1904 | 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 | 1886 | const producer_strp = try self.makeString(link.producer_string); |
| 1907 | 1887 | |
| 1908 | 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 | 1934 | } |
| 1955 | 1935 | } |
| 1956 | 1936 | |
| 1937 | fn 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 | |
| 1957 | 1952 | fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1958 | 1953 | const target_endian = self.target.cpu.arch.endian(); |
| 1959 | 1954 | switch (self.ptr_width) { |
| ... | ... | @@ -2299,10 +2294,15 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { |
| 2299 | 2294 | const dbg_line_prg_end = self.getDebugLineProgramEnd().?; |
| 2300 | 2295 | assert(dbg_line_prg_end != 0); |
| 2301 | 2296 | |
| 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 | 2302 | // The size of this header is variable, depending on the number of directories, |
| 2303 | 2303 | // files, and padding. We have a function to compute the upper bound size, however, |
| 2304 | 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 | 2306 | log.debug("dbg_line_prg_off = {x}, needed_bytes = {x}", .{ dbg_line_prg_off, needed_bytes }); |
| 2307 | 2307 | var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); |
| 2308 | 2308 | defer di_buf.deinit(); |
| ... | ... | @@ -2356,17 +2356,22 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { |
| 2356 | 2356 | 0, // `DW.LNS.set_prologue_end` |
| 2357 | 2357 | 0, // `DW.LNS.set_epilogue_begin` |
| 2358 | 2358 | 1, // `DW.LNS.set_isa` |
| 2359 | | 0, // include_directories (none except the compilation unit cwd) |
| 2360 | 2359 | }); |
| 2361 | 2360 | |
| 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); |
| 2367 | 2372 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| 2368 | 2373 | 0, // null byte for the relative path name |
| 2369 | | 0, // directory_index |
| 2374 | @intCast(u8, dir_index + 1), // directory_index |
| 2370 | 2375 | 0, // mtime (TODO supply this) |
| 2371 | 2376 | 0, // file size bytes (TODO supply this) |
| 2372 | 2377 | }); |
| ... | ... | @@ -2442,25 +2447,28 @@ fn ptrWidthBytes(self: Dwarf) u8 { |
| 2442 | 2447 | }; |
| 2443 | 2448 | } |
| 2444 | 2449 | |
| 2445 | | fn dbgLineNeededHeaderBytes(self: Dwarf, module: *Module) u32 { |
| 2450 | fn dbgLineNeededHeaderBytes(self: Dwarf, dirs: []const []const u8, files: []const []const u8) u32 { |
| 2451 | _ = self; |
| 2446 | 2452 | const directory_entry_format_count = 1; |
| 2447 | 2453 | 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 | } |
| 2451 | 2461 | |
| 2452 | 2462 | 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; |
| 2456 | 2465 | } |
| 2457 | 2466 | |
| 2458 | 2467 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + |
| 2459 | 2468 | directory_count * 8 + file_name_count * 8 + |
| 2460 | 2469 | // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like |
| 2461 | 2470 | // 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); |
| 2464 | 2472 | } |
| 2465 | 2473 | |
| 2466 | 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 | 2579 | } |
| 2572 | 2580 | } |
| 2573 | 2581 | |
| 2574 | | fn 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 | | |
| 2592 | 2582 | fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 { |
| 2593 | 2583 | const decl = mod.declPtr(decl_index); |
| 2594 | 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 | 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 | 2587 | switch (self.bin_file.tag) { |
| 2604 | 2588 | .elf => self.bin_file.cast(File.Elf).?.debug_line_header_dirty = true, |
| 2605 | 2589 | .macho => self.bin_file.cast(File.MachO).?.d_sym.?.debug_line_header_dirty = true, |
| 2606 | 2590 | .wasm => {}, |
| 2607 | 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 | |
| 2597 | fn 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 | } |
| 2615 | 2637 | |
| 2616 | 2638 | fn addDbgInfoErrorSet( |