diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 4c7278da614a3be501524ec399d9422cb664b97c..2c420fcde4ab736f6587be6ca6559dc6a6117e98 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1707,7 +1707,9 @@ fn initSyntheticSections(self: *MachO) !void { } else false; if (needs_eh_frame) { assert(needs_unwind_info); - self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{}); + self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{ + .flags = macho.S_COALESCED | macho.S_ATTR_NO_TOC | macho.S_ATTR_STRIP_STATIC_SYMS | macho.S_ATTR_LIVE_SUPPORT, + }); } if (self.getInternalObject()) |obj| { diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index b9def4568df91bd1891a9110c1912a67be3c5aea..24d44b4e3a46262ce59d21b321baa5b9656bf867 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1298,10 +1298,18 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. superposition.getPtr(addr).?.cu = rec_index; } + const FdeRange = struct { start: u64, end: u64 }; + var fde_ranges = try std.ArrayList(FdeRange).initCapacity(allocator, self.fdes.items.len); + defer fde_ranges.deinit(allocator); + for (self.fdes.items, 0..) |fde, fde_index| { const atom = fde.getAtom(macho_file); const addr = atom.getInputAddress(macho_file) + fde.atom_offset; superposition.getPtr(addr).?.fde = @intCast(fde_index); + + // Build FDE range for coverage check + const pc_range = fde.pc_range; + fde_ranges.appendAssumeCapacity(.{ .start = addr, .end = addr + pc_range }); } for (superposition.keys(), superposition.values()) |addr, meta| { @@ -1333,15 +1341,43 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. } } } else if (meta.cu == null and meta.fde == null) { - // Create a null record - const rec_index = try self.addUnwindRecord(allocator); - const rec = self.getUnwindRecord(rec_index); - const atom = self.getAtom(meta.atom).?; - try self.unwind_records_indexes.append(allocator, rec_index); - rec.length = @intCast(meta.size); - rec.atom = meta.atom; - rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file)); - rec.file = self.index; + // Check if this address is covered by an existing FDE. + // If so, don't create a null record - let the unwinder fall back to DWARF. + // This is important for local labels within a function that has DWARF unwind info. + const is_covered_by_fde = blk: { + if (fde_ranges.items.len == 0) break :blk false; + + // Binary search: find the last FDE where start <= addr + var left: usize = 0; + var right: usize = fde_ranges.items.len; + while (left < right) { + const mid = left + (right - left) / 2; + if (fde_ranges.items[mid].start <= addr) { + left = mid + 1; + } else { + right = mid; + } + } + + // Check if the FDE before insertion point covers this address + if (left > 0) { + const range = fde_ranges.items[left - 1]; + break :blk addr < range.end; + } + break :blk false; + }; + + if (!is_covered_by_fde) { + // Create a null record only if not covered by DWARF + const rec_index = try self.addUnwindRecord(allocator); + const rec = self.getUnwindRecord(rec_index); + const atom = self.getAtom(meta.atom).?; + try self.unwind_records_indexes.append(allocator, rec_index); + rec.length = @intCast(meta.size); + rec.atom = meta.atom; + rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file)); + rec.file = self.index; + } } } diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 1d3afaa3ac87c6c66f6fbfd6fdcc512380aa85f9..7c4f5a51d5ce27bdad8b3143015440085fc13f34 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -116,6 +116,7 @@ pub const Fde = struct { cie: Cie.Index, atom: Atom.Index = 0, atom_offset: u32 = 0, + pc_range: u64 = 0, lsda: Atom.Index = 0, lsda_offset: u32 = 0, lsda_ptr_offset: u32 = 0, @@ -142,6 +143,9 @@ pub const Fde = struct { const atom = fde.getAtom(macho_file); fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file)); + // Parse pc_range (function size) + fde.pc_range = std.mem.readInt(u64, data[16..][0..8], .little); + // Associate with a CIE const cie_ptr = std.mem.readInt(u32, data[4..8], .little); const cie_offset = fde.offset + 4 - cie_ptr; @@ -350,7 +354,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void { std.mem.writeInt( i32, buffer[offset..][0..4], - @intCast(@as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr)) + addend), + @intCast(@as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr))), .little, ); } @@ -373,7 +377,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void { { const offset = fde.out_offset + 8; const saddr = sect.addr + offset; - const taddr = fde.getAtom(macho_file).getAddress(macho_file); + const taddr = fde.getAtom(macho_file).getAddress(macho_file) + fde.atom_offset; std.mem.writeInt( i64, buffer[offset..][0..8], @@ -460,7 +464,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_in { const offset = fde.out_offset + 8; const saddr = sect.addr + offset; - const taddr = fde.getAtom(macho_file).getAddress(macho_file); + const taddr = fde.getAtom(macho_file).getAddress(macho_file) + fde.atom_offset; std.mem.writeInt( i64, code[offset..][0..8], diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 5e96a4d88e54ffdd5dc81826e710eb9c26d9256d..954cdfd5d525c7b59a9746647d7435b636a558aa 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -289,7 +289,9 @@ fn initOutputSections(macho_file: *MachO) !void { } else false; if (needs_eh_frame) { assert(needs_unwind_info); - macho_file.eh_frame_sect_index = try macho_file.addSection("__TEXT", "__eh_frame", .{}); + macho_file.eh_frame_sect_index = try macho_file.addSection("__TEXT", "__eh_frame", .{ + .flags = std.macho.S_COALESCED | std.macho.S_ATTR_NO_TOC | std.macho.S_ATTR_STRIP_STATIC_SYMS | std.macho.S_ATTR_LIVE_SUPPORT, + }); } }