authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-13 04:27:12+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-13 04:27:12+01:00
log7e4c386f6ab5546898b94d80f54e88d45dc6c7b9
treea856f1921d6353ad0e0034a731d3b39c817fdea9
parentf1267e02c49f5d571420d687a6337a85ab76767d
parente835b864e8e5d2ff33b9f59f9f65c74f86093dfc

Merge pull request 'link/MachO: fix eh_frame handling on macOS' (#30768) from vprodan/zig:fix-macos-eh-frame into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30768 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

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

src/link/MachO.zig+3-1
...@@ -1707,7 +1707,9 @@ fn initSyntheticSections(self: *MachO) !void {...@@ -1707,7 +1707,9 @@ fn initSyntheticSections(self: *MachO) !void {
1707 } else false;1707 } else false;
1708 if (needs_eh_frame) {1708 if (needs_eh_frame) {
1709 assert(needs_unwind_info);1709 assert(needs_unwind_info);
1710 self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{});1710 self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{
1711 .flags = macho.S_COALESCED | macho.S_ATTR_NO_TOC | macho.S_ATTR_STRIP_STATIC_SYMS | macho.S_ATTR_LIVE_SUPPORT,
1712 });
1711 }1713 }
17121714
1713 if (self.getInternalObject()) |obj| {1715 if (self.getInternalObject()) |obj| {
src/link/MachO/Object.zig+45-9
...@@ -1298,10 +1298,18 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target....@@ -1298,10 +1298,18 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.
1298 superposition.getPtr(addr).?.cu = rec_index;1298 superposition.getPtr(addr).?.cu = rec_index;
1299 }1299 }
13001300
1301 const FdeRange = struct { start: u64, end: u64 };
1302 var fde_ranges = try std.ArrayList(FdeRange).initCapacity(allocator, self.fdes.items.len);
1303 defer fde_ranges.deinit(allocator);
1304
1301 for (self.fdes.items, 0..) |fde, fde_index| {1305 for (self.fdes.items, 0..) |fde, fde_index| {
1302 const atom = fde.getAtom(macho_file);1306 const atom = fde.getAtom(macho_file);
1303 const addr = atom.getInputAddress(macho_file) + fde.atom_offset;1307 const addr = atom.getInputAddress(macho_file) + fde.atom_offset;
1304 superposition.getPtr(addr).?.fde = @intCast(fde_index);1308 superposition.getPtr(addr).?.fde = @intCast(fde_index);
1309
1310 // Build FDE range for coverage check
1311 const pc_range = fde.pc_range;
1312 fde_ranges.appendAssumeCapacity(.{ .start = addr, .end = addr + pc_range });
1305 }1313 }
13061314
1307 for (superposition.keys(), superposition.values()) |addr, meta| {1315 for (superposition.keys(), superposition.values()) |addr, meta| {
...@@ -1333,15 +1341,43 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target....@@ -1333,15 +1341,43 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.
1333 }1341 }
1334 }1342 }
1335 } else if (meta.cu == null and meta.fde == null) {1343 } else if (meta.cu == null and meta.fde == null) {
1336 // Create a null record1344 // Check if this address is covered by an existing FDE.
1337 const rec_index = try self.addUnwindRecord(allocator);1345 // If so, don't create a null record - let the unwinder fall back to DWARF.
1338 const rec = self.getUnwindRecord(rec_index);1346 // This is important for local labels within a function that has DWARF unwind info.
1339 const atom = self.getAtom(meta.atom).?;1347 const is_covered_by_fde = blk: {
1340 try self.unwind_records_indexes.append(allocator, rec_index);1348 if (fde_ranges.items.len == 0) break :blk false;
1341 rec.length = @intCast(meta.size);1349
1342 rec.atom = meta.atom;1350 // Binary search: find the last FDE where start <= addr
1343 rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file));1351 var left: usize = 0;
1344 rec.file = self.index;1352 var right: usize = fde_ranges.items.len;
1353 while (left < right) {
1354 const mid = left + (right - left) / 2;
1355 if (fde_ranges.items[mid].start <= addr) {
1356 left = mid + 1;
1357 } else {
1358 right = mid;
1359 }
1360 }
1361
1362 // Check if the FDE before insertion point covers this address
1363 if (left > 0) {
1364 const range = fde_ranges.items[left - 1];
1365 break :blk addr < range.end;
1366 }
1367 break :blk false;
1368 };
1369
1370 if (!is_covered_by_fde) {
1371 // Create a null record only if not covered by DWARF
1372 const rec_index = try self.addUnwindRecord(allocator);
1373 const rec = self.getUnwindRecord(rec_index);
1374 const atom = self.getAtom(meta.atom).?;
1375 try self.unwind_records_indexes.append(allocator, rec_index);
1376 rec.length = @intCast(meta.size);
1377 rec.atom = meta.atom;
1378 rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file));
1379 rec.file = self.index;
1380 }
1345 }1381 }
1346 }1382 }
13471383
src/link/MachO/eh_frame.zig+7-3
...@@ -116,6 +116,7 @@ pub const Fde = struct {...@@ -116,6 +116,7 @@ pub const Fde = struct {
116 cie: Cie.Index,116 cie: Cie.Index,
117 atom: Atom.Index = 0,117 atom: Atom.Index = 0,
118 atom_offset: u32 = 0,118 atom_offset: u32 = 0,
119 pc_range: u64 = 0,
119 lsda: Atom.Index = 0,120 lsda: Atom.Index = 0,
120 lsda_offset: u32 = 0,121 lsda_offset: u32 = 0,
121 lsda_ptr_offset: u32 = 0,122 lsda_ptr_offset: u32 = 0,
...@@ -142,6 +143,9 @@ pub const Fde = struct {...@@ -142,6 +143,9 @@ pub const Fde = struct {
142 const atom = fde.getAtom(macho_file);143 const atom = fde.getAtom(macho_file);
143 fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file));144 fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file));
144145
146 // Parse pc_range (function size)
147 fde.pc_range = std.mem.readInt(u64, data[16..][0..8], .little);
148
145 // Associate with a CIE149 // Associate with a CIE
146 const cie_ptr = std.mem.readInt(u32, data[4..8], .little);150 const cie_ptr = std.mem.readInt(u32, data[4..8], .little);
147 const cie_offset = fde.offset + 4 - cie_ptr;151 const cie_offset = fde.offset + 4 - cie_ptr;
...@@ -350,7 +354,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {...@@ -350,7 +354,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
350 std.mem.writeInt(354 std.mem.writeInt(
351 i32,355 i32,
352 buffer[offset..][0..4],356 buffer[offset..][0..4],
353 @intCast(@as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr)) + addend),357 @intCast(@as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr))),
354 .little,358 .little,
355 );359 );
356 }360 }
...@@ -373,7 +377,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {...@@ -373,7 +377,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
373 {377 {
374 const offset = fde.out_offset + 8;378 const offset = fde.out_offset + 8;
375 const saddr = sect.addr + offset;379 const saddr = sect.addr + offset;
376 const taddr = fde.getAtom(macho_file).getAddress(macho_file);380 const taddr = fde.getAtom(macho_file).getAddress(macho_file) + fde.atom_offset;
377 std.mem.writeInt(381 std.mem.writeInt(
378 i64,382 i64,
379 buffer[offset..][0..8],383 buffer[offset..][0..8],
...@@ -460,7 +464,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_in...@@ -460,7 +464,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_in
460 {464 {
461 const offset = fde.out_offset + 8;465 const offset = fde.out_offset + 8;
462 const saddr = sect.addr + offset;466 const saddr = sect.addr + offset;
463 const taddr = fde.getAtom(macho_file).getAddress(macho_file);467 const taddr = fde.getAtom(macho_file).getAddress(macho_file) + fde.atom_offset;
464 std.mem.writeInt(468 std.mem.writeInt(
465 i64,469 i64,
466 code[offset..][0..8],470 code[offset..][0..8],
src/link/MachO/relocatable.zig+3-1
...@@ -289,7 +289,9 @@ fn initOutputSections(macho_file: *MachO) !void {...@@ -289,7 +289,9 @@ fn initOutputSections(macho_file: *MachO) !void {
289 } else false;289 } else false;
290 if (needs_eh_frame) {290 if (needs_eh_frame) {
291 assert(needs_unwind_info);291 assert(needs_unwind_info);
292 macho_file.eh_frame_sect_index = try macho_file.addSection("__TEXT", "__eh_frame", .{});292 macho_file.eh_frame_sect_index = try macho_file.addSection("__TEXT", "__eh_frame", .{
293 .flags = std.macho.S_COALESCED | std.macho.S_ATTR_NO_TOC | std.macho.S_ATTR_STRIP_STATIC_SYMS | std.macho.S_ATTR_LIVE_SUPPORT,
294 });
293 }295 }
294}296}
295297