authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-26 12:00:41+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:56+01:00
log8950831d3c4af4dd169e0a404e25e8aa9b045caa
tree50d8df70cffdd5d42b5bd73737356a44b1936501
parent156cd8f678ebdcccc48382d093a3ef7e45c85a45
signaturelock-open Commit is signed but in an unrecognized format.

Dwarf.Unwind: handle macOS deviation from standard

Apparently the `__eh_frame` in Mach-O binaries doesn't include the terminator entry, but in all other respects it acts like `.eh_frame` rather than `.debug_frame`. I have no idea.

3 files changed, 12 insertions(+), 6 deletions(-)

lib/std/debug/Dwarf/Unwind.zig+10-4
......@@ -475,10 +475,15 @@ pub fn prepare(
475475 addr_size_bytes: u8,
476476 endian: Endian,
477477 need_lookup: bool,
478 /// The `__eh_frame` section in Mach-O binaries deviates from the standard `.eh_frame` section
479 /// in one way which this function needs to be aware of.
480 is_macho: bool,
478481) !void {
479482 if (unwind.cie_list.len > 0 and (!need_lookup or unwind.lookup != null)) return;
480483 unwind.cie_list.clearRetainingCapacity();
481484
485 if (is_macho) assert(unwind.lookup == null or unwind.lookup.? != .eh_frame_hdr);
486
482487 const section = unwind.frame_section;
483488
484489 var r: Reader = .fixed(section.bytes);
......@@ -519,10 +524,11 @@ pub fn prepare(
519524 .terminator => break true,
520525 }
521526 } else false;
522 switch (section.id) {
523 .eh_frame => if (!saw_terminator) return bad(), // `.eh_frame` indicates the end of the CIE/FDE list with a sentinel entry
524 .debug_frame => if (saw_terminator) return bad(), // `.debug_frame` uses the section bounds and does not specify a sentinel entry
525 }
527 const expect_terminator = switch (section.id) {
528 .eh_frame => !is_macho, // `.eh_frame` indicates the end of the CIE/FDE list with a sentinel entry, though macOS omits this
529 .debug_frame => false, // `.debug_frame` uses the section bounds and does not specify a sentinel entry
530 };
531 if (saw_terminator != expect_terminator) return bad();
526532
527533 std.mem.sortUnstable(SortedFdeEntry, fde_list.items, {}, struct {
528534 fn lessThan(ctx: void, a: SortedFdeEntry, b: SortedFdeEntry) bool {
lib/std/debug/SelfInfo/DarwinModule.zig+1-1
......@@ -59,7 +59,7 @@ fn loadUnwindInfo(module: *const DarwinModule, gpa: Allocator, out: *DebugInfo)
5959 var dwarf: Dwarf.Unwind = .initSection(.eh_frame, @intFromPtr(eh_frame.ptr) - vmaddr_slide, eh_frame);
6060 errdefer dwarf.deinit(gpa);
6161 // We don't need lookups, so this call is just for scanning CIEs.
62 dwarf.prepare(gpa, @sizeOf(usize), native_endian, false) catch |err| switch (err) {
62 dwarf.prepare(gpa, @sizeOf(usize), native_endian, false, true) catch |err| switch (err) {
6363 error.ReadFailed => unreachable, // it's all fixed buffers
6464 error.InvalidDebugInfo,
6565 error.MissingDebugInfo,
lib/std/debug/SelfInfo/ElfModule.zig+1-1
......@@ -228,7 +228,7 @@ pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugIn
228228 };
229229}
230230fn prepareUnwindLookup(unwind: *Dwarf.Unwind, gpa: Allocator) Error!void {
231 unwind.prepare(gpa, @sizeOf(usize), native_endian, true) catch |err| switch (err) {
231 unwind.prepare(gpa, @sizeOf(usize), native_endian, true, false) catch |err| switch (err) {
232232 error.ReadFailed => unreachable, // it's all fixed buffers
233233 error.InvalidDebugInfo,
234234 error.MissingDebugInfo,