| 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,9 +1035,6 @@ pub const InitOptions = struct { |
| 1035 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols | 1035 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols |
| 1036 | dead_strip_dylibs: bool = false, | 1036 | dead_strip_dylibs: bool = false, |
| 1037 | libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, | 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 | fn addPackageTableToCacheHash( | 1040 | fn addPackageTableToCacheHash( |
| ... | @@ -1740,27 +1737,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -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 | const implib_emit: ?link.Emit = blk: { | 1740 | const implib_emit: ?link.Emit = blk: { |
| 1765 | const emit_implib = options.emit_implib orelse break :blk null; | 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,7 +1882,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1906 | .headerpad_max_install_names = options.headerpad_max_install_names, | 1882 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 1907 | .dead_strip_dylibs = options.dead_strip_dylibs, | 1883 | .dead_strip_dylibs = options.dead_strip_dylibs, |
| 1908 | .force_undefined_symbols = .{}, | 1884 | .force_undefined_symbols = .{}, |
| 1909 | .pdb_source_path = pdb_source_path, | ||
| 1910 | }); | 1885 | }); |
| 1911 | errdefer bin_file.destroy(); | 1886 | errdefer bin_file.destroy(); |
| 1912 | comp.* = .{ | 1887 | comp.* = .{ |
src/codegen/llvm.zig+25-19| ... | @@ -443,20 +443,19 @@ pub const Object = struct { | ... | @@ -443,20 +443,19 @@ pub const Object = struct { |
| 443 | }); | 443 | }); |
| 444 | defer gpa.free(producer); | 444 | defer gpa.free(producer); |
| 445 | 445 | ||
| 446 | // For macOS stack traces, we want to avoid having to parse the compilation unit debug | 446 | // We fully resolve all paths at this point to avoid lack of source line info in stack |
| 447 | // info. As long as each debug info file has a path independent of the compilation unit | 447 | // traces or lack of debugging information which, if relative paths were used, would |
| 448 | // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug | 448 | // be very location dependent. |
| 449 | // info. If we provide an absolute path to LLVM here for the compilation unit debug | 449 | // TODO: the only concern I have with this is WASI as either host or target, should |
| 450 | // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we | 450 | // we leave the paths as relative then? |
| 451 | // pass "." for the compilation unit directory. This forces each debug file to have a | 451 | var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 452 | // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug | 452 | const compile_unit_dir = blk: { |
| 453 | // files will no longer reference DW_AT_comp_dir, for the purpose of being able to | 453 | const path = d: { |
| 454 | // support the common practice of stripping all but the line number sections from an | 454 | const mod = options.module orelse break :d "."; |
| 455 | // executable. | 455 | break :d mod.root_pkg.root_src_directory.path orelse "."; |
| 456 | const compile_unit_dir = d: { | 456 | }; |
| 457 | if (options.target.isDarwin()) break :d "."; | 457 | if (std.fs.path.isAbsolute(path)) break :blk path; |
| 458 | const mod = options.module orelse break :d "."; | 458 | break :blk std.os.realpath(path, &buf) catch path; // If realpath fails, fallback to whatever path was |
| 459 | break :d mod.root_pkg.root_src_directory.path orelse "."; | ||
| 460 | }; | 459 | }; |
| 461 | const compile_unit_dir_z = try gpa.dupeZ(u8, compile_unit_dir); | 460 | const compile_unit_dir_z = try gpa.dupeZ(u8, compile_unit_dir); |
| 462 | defer gpa.free(compile_unit_dir_z); | 461 | defer gpa.free(compile_unit_dir_z); |
| ... | @@ -1389,13 +1388,20 @@ pub const Object = struct { | ... | @@ -1389,13 +1388,20 @@ pub const Object = struct { |
| 1389 | if (gop.found_existing) { | 1388 | if (gop.found_existing) { |
| 1390 | return @ptrCast(*llvm.DIFile, gop.value_ptr.*); | 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 | const sub_file_path_z = try gpa.dupeZ(u8, std.fs.path.basename(file.sub_file_path)); | 1403 | const sub_file_path_z = try gpa.dupeZ(u8, std.fs.path.basename(file.sub_file_path)); |
| 1394 | defer gpa.free(sub_file_path_z); | 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 | const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z); | 1405 | const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z); |
| 1400 | gop.value_ptr.* = di_file.toNode(); | 1406 | gop.value_ptr.* = di_file.toNode(); |
| 1401 | return di_file; | 1407 | return di_file; |
src/link.zig-4| ... | @@ -217,10 +217,6 @@ pub const Options = struct { | ... | @@ -217,10 +217,6 @@ pub const Options = struct { |
| 217 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols | 217 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols |
| 218 | dead_strip_dylibs: bool = false, | 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 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { | 220 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { |
| 225 | return if (options.use_lld) .Obj else options.output_mode; | 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,10 +171,6 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 171 | }); | 171 | }); |
| 172 | try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); | 172 | try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); |
| 173 | try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb})); | 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 | if (self.base.options.lto) { | 175 | if (self.base.options.lto) { |
| 180 | switch (self.base.options.optimize_mode) { | 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,7 +1771,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1771 | } | 1771 | } |
| 1772 | // Write the form for the compile unit, which must match the abbrev table above. | 1772 | // Write the form for the compile unit, which must match the abbrev table above. |
| 1773 | const name_strp = try self.makeString(module.root_pkg.root_src_path); | 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 | const comp_dir_strp = try self.makeString(compile_unit_dir); | 1776 | const comp_dir_strp = try self.makeString(compile_unit_dir); |
| 1776 | const producer_strp = try self.makeString(link.producer_string); | 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,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 | fn resolveCompilationDir(module: *Module, buffer: *[std.fs.MAX_PATH_BYTES]u8) []const u8 { |
| 1827 | // For macOS stack traces, we want to avoid having to parse the compilation unit debug | 1828 | // We fully resolve all paths at this point to avoid lack of source line info in stack |
| 1828 | // info. As long as each debug info file has a path independent of the compilation unit | 1829 | // traces or lack of debugging information which, if relative paths were used, would |
| 1829 | // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug | 1830 | // be very location dependent. |
| 1830 | // info. If we provide an absolute path to LLVM here for the compilation unit debug | 1831 | // TODO: the only concern I have with this is WASI as either host or target, should |
| 1831 | // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we | 1832 | // we leave the paths as relative then? |
| 1832 | // pass "." for the compilation unit directory. This forces each debug file to have a | 1833 | const comp_dir_path = module.root_pkg.root_src_directory.path orelse "."; |
| 1833 | // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug | 1834 | if (std.fs.path.isAbsolute(comp_dir_path)) return comp_dir_path; |
| 1834 | // files will no longer reference DW_AT_comp_dir, for the purpose of being able to | 1835 | return std.os.realpath(comp_dir_path, buffer) catch comp_dir_path; // If realpath fails, fallback to whatever comp_dir_path was |
| 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 "."; | ||
| 1839 | } | 1836 | } |
| 1840 | 1837 | ||
| 1841 | fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { | 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,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 | const gpa = self.allocator; | 2150 | const gpa = self.allocator; |
| 2154 | 2151 | ||
| 2155 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); | 2152 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); |
| ... | @@ -2167,7 +2164,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { | ... | @@ -2167,7 +2164,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { |
| 2167 | // Convert all input DI files into a set of include dirs and file names. | 2164 | // Convert all input DI files into a set of include dirs and file names. |
| 2168 | var arena = std.heap.ArenaAllocator.init(gpa); | 2165 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 2169 | defer arena.deinit(); | 2166 | defer arena.deinit(); |
| 2170 | const paths = try self.genIncludeDirsAndFileNames(arena.allocator(), module); | 2167 | const paths = try self.genIncludeDirsAndFileNames(arena.allocator()); |
| 2171 | 2168 | ||
| 2172 | // The size of this header is variable, depending on the number of directories, | 2169 | // The size of this header is variable, depending on the number of directories, |
| 2173 | // files, and padding. We have a function to compute the upper bound size, however, | 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,7 +2548,7 @@ fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 { |
| 2551 | return @intCast(u28, gop.index + 1); | 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 | dirs: []const []const u8, | 2552 | dirs: []const []const u8, |
| 2556 | files: []const []const u8, | 2553 | files: []const []const u8, |
| 2557 | files_dirs_indexes: []u28, | 2554 | files_dirs_indexes: []u28, |
| ... | @@ -2565,19 +2562,22 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator, module: *Module) ! | ... | @@ -2565,19 +2562,22 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator, module: *Module) ! |
| 2565 | var files_dir_indexes = std.ArrayList(u28).init(arena); | 2562 | var files_dir_indexes = std.ArrayList(u28).init(arena); |
| 2566 | try files_dir_indexes.ensureTotalCapacity(self.di_files.count()); | 2563 | try files_dir_indexes.ensureTotalCapacity(self.di_files.count()); |
| 2567 | 2564 | ||
| 2568 | const comp_dir = self.getCompDir(module); | ||
| 2569 | |||
| 2570 | for (self.di_files.keys()) |dif| { | 2565 | for (self.di_files.keys()) |dif| { |
| 2571 | const full_path = try dif.fullPath(arena); | 2566 | const dir_path = d: { |
| 2572 | const dir_path = std.fs.path.dirname(full_path) orelse "."; | 2567 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 2573 | const sub_file_path = std.fs.path.basename(full_path); | 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 | const dir_index: u28 = blk: { | 2579 | const dir_index: u28 = blk: { |
| 2576 | const actual_dir_path = if (mem.indexOf(u8, dir_path, comp_dir)) |_| inner: { | 2580 | const dirs_gop = dirs.getOrPutAssumeCapacity(dir_path); |
| 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); | ||
| 2581 | break :blk @intCast(u28, dirs_gop.index + 1); | 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,7 +1149,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1149 | } | 1149 | } |
| 1150 | 1150 | ||
| 1151 | if (self.debug_line_header_dirty) { | 1151 | if (self.debug_line_header_dirty) { |
| 1152 | try dw.writeDbgLineHeader(module); | 1152 | try dw.writeDbgLineHeader(); |
| 1153 | self.debug_line_header_dirty = false; | 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,7 +281,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | if (self.debug_line_header_dirty) { | 283 | if (self.debug_line_header_dirty) { |
| 284 | try self.dwarf.writeDbgLineHeader(module); | 284 | try self.dwarf.writeDbgLineHeader(); |
| 285 | self.debug_line_header_dirty = false; | 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,7 +2672,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2672 | // as locations are always offsets relative to 'code' section. | 2672 | // as locations are always offsets relative to 'code' section. |
| 2673 | try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); | 2673 | try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); |
| 2674 | try dwarf.writeDbgAranges(0, code_section_size); | 2674 | try dwarf.writeDbgAranges(0, code_section_size); |
| 2675 | try dwarf.writeDbgLineHeader(mod); | 2675 | try dwarf.writeDbgLineHeader(); |
| 2676 | } | 2676 | } |
| 2677 | 2677 | ||
| 2678 | var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); | 2678 | var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); |