authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-10-10 11:20:19+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-10-10 20:33:30+01:00
log45143c6f04c84e8a519aa24e4d79e9385b5d5e2f
treeb36a125db7a5861a14f61154b6200535548eee2a
parentd629a146f5d3a487fa666c0a51f9e0683dde76b8

MachO: emit absolute path in N_OSO stabs

This path being relative is unconventional and causes issues for us if the output artifact is ever used from a different cwd than the one it was built from. The behavior implemented by this commit of always emitting these paths as absolute was actually the behavior in 0.14.x, but it regressed in 0.15.1 due to internal reworks to path handling which led to relative paths being more common in the compiler internals. Resolves: #25433

4 files changed, 58 insertions(+), 65 deletions(-)

src/link/MachO.zig+11-5
......@@ -919,7 +919,16 @@ fn addObject(self: *MachO, path: Path, handle: File.HandleIndex, offset: u64) !v
919919 const tracy = trace(@src());
920920 defer tracy.end();
921921
922 const gpa = self.base.comp.gpa;
922 const comp = self.base.comp;
923 const gpa = comp.gpa;
924
925 const abs_path = try std.fs.path.resolvePosix(gpa, &.{
926 comp.dirs.cwd,
927 path.root_dir.path orelse ".",
928 path.sub_path,
929 });
930 errdefer gpa.free(abs_path);
931
923932 const mtime: u64 = mtime: {
924933 const file = self.getFileHandle(handle);
925934 const stat = file.stat() catch break :mtime 0;
......@@ -928,10 +937,7 @@ fn addObject(self: *MachO, path: Path, handle: File.HandleIndex, offset: u64) !v
928937 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
929938 self.files.set(index, .{ .object = .{
930939 .offset = offset,
931 .path = .{
932 .root_dir = path.root_dir,
933 .sub_path = try gpa.dupe(u8, path.sub_path),
934 },
940 .path = abs_path,
935941 .file_handle = handle,
936942 .mtime = mtime,
937943 .index = index,
src/link/MachO/Archive.zig+16-8
......@@ -5,8 +5,9 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
55}
66
77pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
8 const gpa = macho_file.base.comp.gpa;
9 const diags = &macho_file.base.comp.link_diags;
8 const comp = macho_file.base.comp;
9 const gpa = comp.gpa;
10 const diags = &comp.link_diags;
1011
1112 var arena = std.heap.ArenaAllocator.init(gpa);
1213 defer arena.deinit();
......@@ -55,23 +56,30 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
5556 mem.eql(u8, name, SYMDEF_SORTED) or
5657 mem.eql(u8, name, SYMDEF64_SORTED)) continue;
5758
59 const abs_path = try std.fs.path.resolvePosix(gpa, &.{
60 comp.dirs.cwd,
61 path.root_dir.path orelse ".",
62 path.sub_path,
63 });
64 errdefer gpa.free(abs_path);
65
66 const o_basename = try gpa.dupe(u8, name);
67 errdefer gpa.free(o_basename);
68
5869 const object: Object = .{
5970 .offset = pos,
6071 .in_archive = .{
61 .path = .{
62 .root_dir = path.root_dir,
63 .sub_path = try gpa.dupe(u8, path.sub_path),
64 },
72 .path = abs_path,
6573 .size = hdr_size,
6674 },
67 .path = Path.initCwd(try gpa.dupe(u8, name)),
75 .path = o_basename,
6876 .file_handle = handle_index,
6977 .index = undefined,
7078 .alive = false,
7179 .mtime = hdr.date() catch 0,
7280 };
7381
74 log.debug("extracting object '{f}' from archive '{f}'", .{ object.path, path });
82 log.debug("extracting object '{s}' from archive '{f}'", .{ o_basename, path });
7583
7684 try self.objects.append(gpa, object);
7785 }
src/link/MachO/Object.zig+30-51
......@@ -1,8 +1,9 @@
11/// Non-zero for fat object files or archives
22offset: u64,
3/// Archive files cannot contain subdirectories, so only the basename is needed
4/// for output. However, the full path is kept for error reporting.
5path: Path,
3/// If `in_archive` is not `null`, this is the basename of the object in the archive. Otherwise,
4/// this is a fully-resolved absolute path, because that is the path we need to embed in stabs to
5/// ensure the output does not depend on its cwd.
6path: []u8,
67file_handle: File.HandleIndex,
78mtime: u64,
89index: File.Index,
......@@ -41,8 +42,8 @@ output_symtab_ctx: MachO.SymtabCtx = .{},
4142output_ar_state: Archive.ArState = .{},
4243
4344pub fn deinit(self: *Object, allocator: Allocator) void {
44 if (self.in_archive) |*ar| allocator.free(ar.path.sub_path);
45 allocator.free(self.path.sub_path);
45 if (self.in_archive) |*ar| allocator.free(ar.path);
46 allocator.free(self.path);
4647 for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
4748 relocs.deinit(allocator);
4849 sub.deinit(allocator);
......@@ -1703,7 +1704,7 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
17031704pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
17041705 // Header
17051706 const size = try macho_file.cast(usize, self.output_ar_state.size);
1706 const basename = std.fs.path.basename(self.path.sub_path);
1707 const basename = std.fs.path.basename(self.path);
17071708 try Archive.writeHeader(basename, size, ar_format, writer);
17081709 // Data
17091710 const file = macho_file.getFileHandle(self.file_handle);
......@@ -1756,12 +1757,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
17561757 self.calcStabsSize(macho_file);
17571758}
17581759
1759fn pathLen(path: Path) usize {
1760 // +1 for the path separator
1761 return (if (path.root_dir.path) |p| p.len + @intFromBool(path.sub_path.len != 0) else 0) + path.sub_path.len;
1762}
1763
1764pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
1760fn calcStabsSize(self: *Object, macho_file: *MachO) void {
17651761 if (self.compile_unit) |cu| {
17661762 const comp_dir = cu.getCompDir(self.*);
17671763 const tu_name = cu.getTuName(self.*);
......@@ -1771,9 +1767,11 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
17711767 self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
17721768
17731769 if (self.in_archive) |ar| {
1774 self.output_symtab_ctx.strsize += @intCast(pathLen(ar.path) + 1 + self.path.basename().len + 1 + 1);
1770 // "/path/to/archive.a(object.o)\x00"
1771 self.output_symtab_ctx.strsize += @intCast(ar.path.len + self.path.len + 3);
17751772 } else {
1776 self.output_symtab_ctx.strsize += @intCast(pathLen(self.path) + 1);
1773 // "/path/to/object.o\x00"
1774 self.output_symtab_ctx.strsize += @intCast(self.path.len + 1);
17771775 }
17781776
17791777 for (self.symbols.items, 0..) |sym, i| {
......@@ -2018,7 +2016,7 @@ pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) void {
20182016 self.writeStabs(n_strx, macho_file, ctx);
20192017}
20202018
2021pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) void {
2019fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) void {
20222020 const writeFuncStab = struct {
20232021 inline fn writeFuncStab(
20242022 n_strx: u32,
......@@ -2103,38 +2101,20 @@ pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) v
21032101 };
21042102 index += 1;
21052103 if (self.in_archive) |ar| {
2106 if (ar.path.root_dir.path) |p| {
2107 @memcpy(ctx.strtab.items[n_strx..][0..p.len], p);
2108 n_strx += @intCast(p.len);
2109 if (ar.path.sub_path.len != 0) {
2110 ctx.strtab.items[n_strx] = '/';
2111 n_strx += 1;
2112 }
2113 }
2114 @memcpy(ctx.strtab.items[n_strx..][0..ar.path.sub_path.len], ar.path.sub_path);
2115 n_strx += @intCast(ar.path.sub_path.len);
2116 ctx.strtab.items[n_strx] = '(';
2117 n_strx += 1;
2118 const basename = self.path.basename();
2119 @memcpy(ctx.strtab.items[n_strx..][0..basename.len], basename);
2120 n_strx += @intCast(basename.len);
2121 ctx.strtab.items[n_strx] = ')';
2122 n_strx += 1;
2123 ctx.strtab.items[n_strx] = 0;
2104 // "/path/to/archive.a(object.o)\x00"
2105 @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path);
2106 n_strx += @intCast(ar.path.len);
2107 ctx.strtab.items[n_strx..][0] = '(';
21242108 n_strx += 1;
2109 @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
2110 n_strx += @intCast(self.path.len);
2111 ctx.strtab.items[n_strx..][0..2].* = ")\x00".*;
2112 n_strx += 2;
21252113 } else {
2126 if (self.path.root_dir.path) |p| {
2127 @memcpy(ctx.strtab.items[n_strx..][0..p.len], p);
2128 n_strx += @intCast(p.len);
2129 if (self.path.sub_path.len != 0) {
2130 ctx.strtab.items[n_strx] = '/';
2131 n_strx += 1;
2132 }
2133 }
2134 @memcpy(ctx.strtab.items[n_strx..][0..self.path.sub_path.len], self.path.sub_path);
2135 n_strx += @intCast(self.path.sub_path.len);
2136 ctx.strtab.items[n_strx] = 0;
2137 n_strx += 1;
2114 // "/path/to/object.o\x00"
2115 @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
2116 ctx.strtab.items[n_strx..][self.path.len] = 0;
2117 n_strx += @intCast(self.path.len + 1);
21382118 }
21392119
21402120 for (self.symbols.items, 0..) |sym, i| {
......@@ -2621,11 +2601,9 @@ pub fn fmtPath(self: Object) std.fmt.Alt(Object, formatPath) {
26212601
26222602fn formatPath(object: Object, w: *Writer) Writer.Error!void {
26232603 if (object.in_archive) |ar| {
2624 try w.print("{f}({s})", .{
2625 ar.path, object.path.basename(),
2626 });
2604 try w.print("{s}({s})", .{ ar.path, object.path });
26272605 } else {
2628 try w.print("{f}", .{object.path});
2606 try w.writeAll(object.path);
26292607 }
26302608}
26312609
......@@ -2716,7 +2694,9 @@ const CompileUnit = struct {
27162694};
27172695
27182696const InArchive = struct {
2719 path: Path,
2697 /// This is a fully-resolved absolute path, because that is the path we need to embed in stabs
2698 /// to ensure the output does not depend on its cwd.
2699 path: []u8,
27202700 size: u32,
27212701};
27222702
......@@ -3094,7 +3074,6 @@ const log = std.log.scoped(.link);
30943074const macho = std.macho;
30953075const math = std.math;
30963076const mem = std.mem;
3097const Path = std.Build.Cache.Path;
30983077const Allocator = std.mem.Allocator;
30993078const Writer = std.Io.Writer;
31003079
src/link/MachO/relocatable.zig+1-1
......@@ -191,7 +191,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
191191 pos = mem.alignForward(usize, pos, 2);
192192 state.file_off = pos;
193193 pos += @sizeOf(Archive.ar_hdr);
194 pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width);
194 pos += mem.alignForward(usize, std.fs.path.basename(o.path).len + 1, ptr_width);
195195 pos += try macho_file.cast(usize, state.size);
196196 },
197197 else => unreachable,