| author | |
| committer | |
| log | bd5a8f86a120093b126cfbde41e002dae161e883 |
| tree | e15e4dfd7c82542bac7b472b9ff13c2b12369147 |
| parent | d0172488b2aa6490dcb983c24cac76e72fa972c0 |
| parent | 182751ba27edf64304d2ae78043576b78c45fc1c |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
debug info: resolve relative paths to source files into absolute paths8 files changed, 55 insertions(+), 82 deletions(-)
src/Compilation.zig-25| ... | ... | @@ -1035,9 +1035,6 @@ pub const InitOptions = struct { |
| 1035 | 1035 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols |
| 1036 | 1036 | dead_strip_dylibs: bool = false, |
| 1037 | 1037 | libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, |
| 1038 | /// (Windows) PDB source path prefix to instruct the linker how to resolve relative | |
| 1039 | /// paths when consolidating CodeView streams into a single PDB file. | |
| 1040 | pdb_source_path: ?[]const u8 = null, | |
| 1041 | 1038 | }; |
| 1042 | 1039 | |
| 1043 | 1040 | fn addPackageTableToCacheHash( |
| ... | ... | @@ -1740,27 +1737,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1740 | 1737 | }; |
| 1741 | 1738 | }; |
| 1742 | 1739 | |
| 1743 | const pdb_source_path: ?[]const u8 = options.pdb_source_path orelse blk: { | |
| 1744 | if (builtin.target.os.tag == .windows) { | |
| 1745 | // PDB requires all file paths to be fully resolved, and it is really the | |
| 1746 | // linker's responsibility to canonicalize any path extracted from the CodeView | |
| 1747 | // in the object file. However, LLD-link has some very questionable defaults, and | |
| 1748 | // in particular, it purposely bakes in path separator of the host system it was | |
| 1749 | // built on rather than the targets, or just throw an error. Thankfully, they have | |
| 1750 | // left a backdoor we can use via -PDBSOURCEPATH. | |
| 1751 | const mod = module orelse break :blk null; | |
| 1752 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1753 | const resolved_path = if (mod.main_pkg.root_src_directory.path) |base_path| p: { | |
| 1754 | if (std.fs.path.isAbsolute(base_path)) break :blk base_path; | |
| 1755 | const resolved_path = std.os.realpath(base_path, &buffer) catch break :blk null; | |
| 1756 | const pos = std.mem.lastIndexOfLinear(u8, resolved_path, base_path) orelse resolved_path.len; | |
| 1757 | break :p resolved_path[0..pos]; | |
| 1758 | } else std.os.realpath(".", &buffer) catch break :blk null; | |
| 1759 | break :blk try arena.dupe(u8, resolved_path); | |
| 1760 | } | |
| 1761 | break :blk null; | |
| 1762 | }; | |
| 1763 | ||
| 1764 | 1740 | const implib_emit: ?link.Emit = blk: { |
| 1765 | 1741 | const emit_implib = options.emit_implib orelse break :blk null; |
| 1766 | 1742 | |
| ... | ... | @@ -1906,7 +1882,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1906 | 1882 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 1907 | 1883 | .dead_strip_dylibs = options.dead_strip_dylibs, |
| 1908 | 1884 | .force_undefined_symbols = .{}, |
| 1909 | .pdb_source_path = pdb_source_path, | |
| 1910 | 1885 | }); |
| 1911 | 1886 | errdefer bin_file.destroy(); |
| 1912 | 1887 | comp.* = .{ |
src/codegen/llvm.zig+25-19| ... | ... | @@ -443,20 +443,19 @@ pub const Object = struct { |
| 443 | 443 | }); |
| 444 | 444 | defer gpa.free(producer); |
| 445 | 445 | |
| 446 | // For macOS stack traces, we want to avoid having to parse the compilation unit debug | |
| 447 | // info. As long as each debug info file has a path independent of the compilation unit | |
| 448 | // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug | |
| 449 | // info. If we provide an absolute path to LLVM here for the compilation unit debug | |
| 450 | // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we | |
| 451 | // pass "." for the compilation unit directory. This forces each debug file to have a | |
| 452 | // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug | |
| 453 | // files will no longer reference DW_AT_comp_dir, for the purpose of being able to | |
| 454 | // support the common practice of stripping all but the line number sections from an | |
| 455 | // executable. | |
| 456 | const compile_unit_dir = d: { | |
| 457 | if (options.target.isDarwin()) break :d "."; | |
| 458 | const mod = options.module orelse break :d "."; | |
| 459 | break :d mod.root_pkg.root_src_directory.path orelse "."; | |
| 446 | // We fully resolve all paths at this point to avoid lack of source line info in stack | |
| 447 | // traces or lack of debugging information which, if relative paths were used, would | |
| 448 | // be very location dependent. | |
| 449 | // TODO: the only concern I have with this is WASI as either host or target, should | |
| 450 | // we leave the paths as relative then? | |
| 451 | var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 452 | const compile_unit_dir = blk: { | |
| 453 | const path = d: { | |
| 454 | const mod = options.module orelse break :d "."; | |
| 455 | break :d mod.root_pkg.root_src_directory.path orelse "."; | |
| 456 | }; | |
| 457 | if (std.fs.path.isAbsolute(path)) break :blk path; | |
| 458 | break :blk std.os.realpath(path, &buf) catch path; // If realpath fails, fallback to whatever path was | |
| 460 | 459 | }; |
| 461 | 460 | const compile_unit_dir_z = try gpa.dupeZ(u8, compile_unit_dir); |
| 462 | 461 | defer gpa.free(compile_unit_dir_z); |
| ... | ... | @@ -1389,13 +1388,20 @@ pub const Object = struct { |
| 1389 | 1388 | if (gop.found_existing) { |
| 1390 | 1389 | return @ptrCast(*llvm.DIFile, gop.value_ptr.*); |
| 1391 | 1390 | } |
| 1392 | const dir_path = file.pkg.root_src_directory.path orelse "."; | |
| 1391 | const dir_path_z = d: { | |
| 1392 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1393 | const dir_path = file.pkg.root_src_directory.path orelse "."; | |
| 1394 | const resolved_dir_path = if (std.fs.path.isAbsolute(dir_path)) | |
| 1395 | dir_path | |
| 1396 | else | |
| 1397 | std.os.realpath(dir_path, &buffer) catch dir_path; // If realpath fails, fallback to whatever dir_path was | |
| 1398 | break :d try std.fs.path.joinZ(gpa, &.{ | |
| 1399 | resolved_dir_path, std.fs.path.dirname(file.sub_file_path) orelse "", | |
| 1400 | }); | |
| 1401 | }; | |
| 1402 | defer gpa.free(dir_path_z); | |
| 1393 | 1403 | const sub_file_path_z = try gpa.dupeZ(u8, std.fs.path.basename(file.sub_file_path)); |
| 1394 | 1404 | defer gpa.free(sub_file_path_z); |
| 1395 | const dir_path_z = try std.fs.path.joinZ(gpa, &.{ | |
| 1396 | dir_path, std.fs.path.dirname(file.sub_file_path) orelse "", | |
| 1397 | }); | |
| 1398 | defer gpa.free(dir_path_z); | |
| 1399 | 1405 | const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z); |
| 1400 | 1406 | gop.value_ptr.* = di_file.toNode(); |
| 1401 | 1407 | return di_file; |
src/link.zig-4| ... | ... | @@ -217,10 +217,6 @@ pub const Options = struct { |
| 217 | 217 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols |
| 218 | 218 | dead_strip_dylibs: bool = false, |
| 219 | 219 | |
| 220 | /// (Windows) PDB source path prefix to instruct the linker how to resolve relative | |
| 221 | /// paths when consolidating CodeView streams into a single PDB file. | |
| 222 | pdb_source_path: ?[]const u8 = null, | |
| 223 | ||
| 224 | 220 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { |
| 225 | 221 | return if (options.use_lld) .Obj else options.output_mode; |
| 226 | 222 | } |
src/link/Coff/lld.zig-4| ... | ... | @@ -171,10 +171,6 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 171 | 171 | }); |
| 172 | 172 | try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); |
| 173 | 173 | try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb})); |
| 174 | ||
| 175 | if (self.base.options.pdb_source_path) |path| { | |
| 176 | try argv.append(try std.fmt.allocPrint(arena, "-PDBSOURCEPATH:{s}", .{path})); | |
| 177 | } | |
| 178 | 174 | } |
| 179 | 175 | if (self.base.options.lto) { |
| 180 | 176 | switch (self.base.options.optimize_mode) { |
src/link/Dwarf.zig+27-27| ... | ... | @@ -1771,7 +1771,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1771 | 1771 | } |
| 1772 | 1772 | // Write the form for the compile unit, which must match the abbrev table above. |
| 1773 | 1773 | const name_strp = try self.makeString(module.root_pkg.root_src_path); |
| 1774 | const compile_unit_dir = self.getCompDir(module); | |
| 1774 | var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1775 | const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer); | |
| 1775 | 1776 | const comp_dir_strp = try self.makeString(compile_unit_dir); |
| 1776 | 1777 | const producer_strp = try self.makeString(link.producer_string); |
| 1777 | 1778 | |
| ... | ... | @@ -1823,19 +1824,15 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1823 | 1824 | } |
| 1824 | 1825 | } |
| 1825 | 1826 | |
| 1826 | fn getCompDir(self: Dwarf, module: *Module) []const u8 { | |
| 1827 | // For macOS stack traces, we want to avoid having to parse the compilation unit debug | |
| 1828 | // info. As long as each debug info file has a path independent of the compilation unit | |
| 1829 | // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug | |
| 1830 | // info. If we provide an absolute path to LLVM here for the compilation unit debug | |
| 1831 | // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we | |
| 1832 | // pass "." for the compilation unit directory. This forces each debug file to have a | |
| 1833 | // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug | |
| 1834 | // files will no longer reference DW_AT_comp_dir, for the purpose of being able to | |
| 1835 | // support the common practice of stripping all but the line number sections from an | |
| 1836 | // executable. | |
| 1837 | if (self.bin_file.tag == .macho) return "."; | |
| 1838 | return module.root_pkg.root_src_directory.path orelse "."; | |
| 1827 | fn resolveCompilationDir(module: *Module, buffer: *[std.fs.MAX_PATH_BYTES]u8) []const u8 { | |
| 1828 | // We fully resolve all paths at this point to avoid lack of source line info in stack | |
| 1829 | // traces or lack of debugging information which, if relative paths were used, would | |
| 1830 | // be very location dependent. | |
| 1831 | // TODO: the only concern I have with this is WASI as either host or target, should | |
| 1832 | // we leave the paths as relative then? | |
| 1833 | const comp_dir_path = module.root_pkg.root_src_directory.path orelse "."; | |
| 1834 | if (std.fs.path.isAbsolute(comp_dir_path)) return comp_dir_path; | |
| 1835 | return std.os.realpath(comp_dir_path, buffer) catch comp_dir_path; // If realpath fails, fallback to whatever comp_dir_path was | |
| 1839 | 1836 | } |
| 1840 | 1837 | |
| 1841 | 1838 | fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { |
| ... | ... | @@ -2149,7 +2146,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { |
| 2149 | 2146 | } |
| 2150 | 2147 | } |
| 2151 | 2148 | |
| 2152 | pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { | |
| 2149 | pub fn writeDbgLineHeader(self: *Dwarf) !void { | |
| 2153 | 2150 | const gpa = self.allocator; |
| 2154 | 2151 | |
| 2155 | 2152 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); |
| ... | ... | @@ -2167,7 +2164,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { |
| 2167 | 2164 | // Convert all input DI files into a set of include dirs and file names. |
| 2168 | 2165 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 2169 | 2166 | defer arena.deinit(); |
| 2170 | const paths = try self.genIncludeDirsAndFileNames(arena.allocator(), module); | |
| 2167 | const paths = try self.genIncludeDirsAndFileNames(arena.allocator()); | |
| 2171 | 2168 | |
| 2172 | 2169 | // The size of this header is variable, depending on the number of directories, |
| 2173 | 2170 | // files, and padding. We have a function to compute the upper bound size, however, |
| ... | ... | @@ -2551,7 +2548,7 @@ fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 { |
| 2551 | 2548 | return @intCast(u28, gop.index + 1); |
| 2552 | 2549 | } |
| 2553 | 2550 | |
| 2554 | fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator, module: *Module) !struct { | |
| 2551 | fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct { | |
| 2555 | 2552 | dirs: []const []const u8, |
| 2556 | 2553 | files: []const []const u8, |
| 2557 | 2554 | files_dirs_indexes: []u28, |
| ... | ... | @@ -2565,19 +2562,22 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator, module: *Module) ! |
| 2565 | 2562 | var files_dir_indexes = std.ArrayList(u28).init(arena); |
| 2566 | 2563 | try files_dir_indexes.ensureTotalCapacity(self.di_files.count()); |
| 2567 | 2564 | |
| 2568 | const comp_dir = self.getCompDir(module); | |
| 2569 | ||
| 2570 | 2565 | for (self.di_files.keys()) |dif| { |
| 2571 | const full_path = try dif.fullPath(arena); | |
| 2572 | const dir_path = std.fs.path.dirname(full_path) orelse "."; | |
| 2573 | const sub_file_path = std.fs.path.basename(full_path); | |
| 2566 | const dir_path = d: { | |
| 2567 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 2568 | const dir_path = dif.pkg.root_src_directory.path orelse "."; | |
| 2569 | const abs_dir_path = if (std.fs.path.isAbsolute(dir_path)) | |
| 2570 | dir_path | |
| 2571 | else | |
| 2572 | std.os.realpath(dir_path, &buffer) catch dir_path; // If realpath fails, fallback to whatever dir_path was | |
| 2573 | break :d try std.fs.path.join(arena, &.{ | |
| 2574 | abs_dir_path, std.fs.path.dirname(dif.sub_file_path) orelse "", | |
| 2575 | }); | |
| 2576 | }; | |
| 2577 | const sub_file_path = try arena.dupe(u8, std.fs.path.basename(dif.sub_file_path)); | |
| 2574 | 2578 | |
| 2575 | 2579 | const dir_index: u28 = blk: { |
| 2576 | const actual_dir_path = if (mem.indexOf(u8, dir_path, comp_dir)) |_| inner: { | |
| 2577 | if (comp_dir.len == dir_path.len) break :blk 0; | |
| 2578 | break :inner dir_path[comp_dir.len + 1 ..]; | |
| 2579 | } else dir_path; | |
| 2580 | const dirs_gop = dirs.getOrPutAssumeCapacity(actual_dir_path); | |
| 2580 | const dirs_gop = dirs.getOrPutAssumeCapacity(dir_path); | |
| 2581 | 2581 | break :blk @intCast(u28, dirs_gop.index + 1); |
| 2582 | 2582 | }; |
| 2583 | 2583 |
src/link/Elf.zig+1-1| ... | ... | @@ -1149,7 +1149,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1149 | 1149 | } |
| 1150 | 1150 | |
| 1151 | 1151 | if (self.debug_line_header_dirty) { |
| 1152 | try dw.writeDbgLineHeader(module); | |
| 1152 | try dw.writeDbgLineHeader(); | |
| 1153 | 1153 | self.debug_line_header_dirty = false; |
| 1154 | 1154 | } |
| 1155 | 1155 | } |
src/link/MachO/DebugSymbols.zig+1-1| ... | ... | @@ -281,7 +281,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | 283 | if (self.debug_line_header_dirty) { |
| 284 | try self.dwarf.writeDbgLineHeader(module); | |
| 284 | try self.dwarf.writeDbgLineHeader(); | |
| 285 | 285 | self.debug_line_header_dirty = false; |
| 286 | 286 | } |
| 287 | 287 |
src/link/Wasm.zig+1-1| ... | ... | @@ -2672,7 +2672,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2672 | 2672 | // as locations are always offsets relative to 'code' section. |
| 2673 | 2673 | try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); |
| 2674 | 2674 | try dwarf.writeDbgAranges(0, code_section_size); |
| 2675 | try dwarf.writeDbgLineHeader(mod); | |
| 2675 | try dwarf.writeDbgLineHeader(); | |
| 2676 | 2676 | } |
| 2677 | 2677 | |
| 2678 | 2678 | var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); |