From 63f345a75afdf4f956b136c06776f109f5c567af Mon Sep 17 00:00:00 2001 From: Vladislav Shabanov Date: Thu, 15 Jan 2026 19:48:16 +0100 Subject: [PATCH] link.MachO: support sdata4 pointer encoding (#30846) Fixes https://codeberg.org/ziglang/zig/issues/30669 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30846 Reviewed-by: Andrew Kelley Co-authored-by: Vladislav Shabanov Co-committed-by: Vladislav Shabanov --- src/link/MachO/eh_frame.zig | 92 +++++++++++++++++++++++++------------ test/link/macho.zig | 66 ++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 29 deletions(-) diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 7c4f5a51d5..d03aaa63b1 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -3,6 +3,7 @@ pub const Cie = struct { offset: u32, out_offset: u32 = 0, size: u32, + address_ptr_size: enum { p32, p64 } = .p64, lsda_size: ?enum { p32, p64 } = null, personality: ?Personality = null, file: File.Index = 0, @@ -27,9 +28,15 @@ pub const Cie = struct { for (aug[1..]) |ch| switch (ch) { 'R' => { const enc: DW.EH.PE = @bitCast(try reader.takeByte()); - if (enc != @as(DW.EH.PE, .{ .type = .absptr, .rel = .pcrel })) { + if (enc.rel != .pcrel) { @panic("unexpected pointer encoding"); // TODO error } + + switch (enc.type) { + .sdata4 => cie.address_ptr_size = .p32, + .absptr => cie.address_ptr_size = .p64, + else => @panic("unexpected pointer encoding"), // TODO error + } }, 'P' => { const enc: DW.EH.PE = @bitCast(try reader.takeByte()); @@ -131,21 +138,6 @@ pub const Fde = struct { const object = fde.getObject(macho_file); const sect = object.sections.items(.header)[object.eh_frame_sect_index.?]; - // Parse target atom index - const pc_begin = std.mem.readInt(i64, data[8..][0..8], .little); - const taddr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + 8)) + pc_begin); - fde.atom = object.findAtom(taddr) orelse { - try macho_file.reportParseError2(object.index, "{s},{s}: 0x{x}: invalid function reference in FDE", .{ - sect.segName(), sect.sectName(), fde.offset + 8, - }); - return error.MalformedObject; - }; - 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; @@ -163,10 +155,34 @@ pub const Fde = struct { const cie = fde.getCie(macho_file); + // Parse target atom index + const pc_begin = switch (cie.address_ptr_size) { + .p32 => std.mem.readInt(i32, data[8..][0..4], .little), + .p64 => std.mem.readInt(i64, data[8..][0..8], .little), + }; + const taddr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + 8)) + pc_begin); + fde.atom = object.findAtom(taddr) orelse { + try macho_file.reportParseError2(object.index, "{s},{s}: 0x{x}: invalid function reference in FDE", .{ + sect.segName(), sect.sectName(), fde.offset + 8, + }); + return error.MalformedObject; + }; + const atom = fde.getAtom(macho_file); + fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file)); + + // Parse pc_range (function size) + fde.pc_range = switch (cie.address_ptr_size) { + .p32 => std.mem.readInt(u32, data[12..][0..4], .little), + .p64 => std.mem.readInt(u64, data[16..][0..8], .little), + }; + // Parse LSDA atom index if any if (cie.lsda_size) |lsda_size| { var reader: std.Io.Reader = .fixed(data); - reader.seek = 24; + reader.seek = switch (cie.address_ptr_size) { + .p32 => 16, + .p64 => 24, + }; _ = try reader.takeLeb128(u64); // augmentation length fde.lsda_ptr_offset = @intCast(reader.seek); const lsda_ptr = switch (lsda_size) { @@ -378,12 +394,21 @@ 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) + fde.atom_offset; - std.mem.writeInt( - i64, - buffer[offset..][0..8], - @as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr)), - .little, - ); + + switch (fde.getCie(macho_file).address_ptr_size) { + .p32 => std.mem.writeInt( + i32, + buffer[offset..][0..4], + @intCast(@as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr))), + .little, + ), + .p64 => std.mem.writeInt( + i64, + buffer[offset..][0..8], + @as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr)), + .little, + ), + } } if (fde.getLsdaAtom(macho_file)) |atom| { @@ -465,12 +490,21 @@ 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) + fde.atom_offset; - std.mem.writeInt( - i64, - code[offset..][0..8], - @as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr)), - .little, - ); + + switch (fde.getCie(macho_file).address_ptr_size) { + .p32 => std.mem.writeInt( + i32, + code[offset..][0..4], + @intCast(@as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr))), + .little, + ), + .p64 => std.mem.writeInt( + i64, + code[offset..][0..8], + @as(i64, @intCast(taddr)) - @as(i64, @intCast(saddr)), + .little, + ), + } } if (fde.getLsdaAtom(macho_file)) |atom| { diff --git a/test/link/macho.zig b/test/link/macho.zig index ccfecefa44..d947a8fbab 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -74,6 +74,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target })); macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target })); macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target })); + macho_step.dependOn(testEhFramePointerEncodingSdata4(b, .{ .target = aarch64_target })); macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target })); macho_step.dependOn(testWeakRef(b, .{ .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, @@ -2852,6 +2853,71 @@ fn testUnwindInfo(b: *Build, opts: Options) *Step { return test_step; } +fn testEhFramePointerEncodingSdata4(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "eh_frame-pointer-encoding-sdata4", opts); + + const a_o = addObject(b, opts, .{ .name = "foo", .asm_source_bytes = + \\.global _foo + \\.align 2 + \\_foo: + \\ mov w0, #100 + \\ ret + \\LEND_foo: + \\ + \\.section __TEXT,__gcc_except_tab + \\LLSDA_foo: + \\ .byte 0xff + \\ .byte 0xff + \\ .byte 0x01 + \\ .uleb128 0 + \\ + \\.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support + \\LCIE: + \\ .long LCIE_end - LCIE_start + \\LCIE_start: + \\ .long 0 ; CIE ID + \\ .byte 1 ; Version + \\ .asciz "zLR" ; Augmentation string + \\ .uleb128 1 ; Code alignment factor + \\ .sleb128 -8 ; Data alignment factor + \\ .byte 30 ; Return address register + \\ .uleb128 2 ; Augmentation data length + \\ .byte 0x1b ; LSDA pointer encoding (DW_EH_PE_pcrel | DW_EH_PE_sdata4) + \\ .byte 0x1b ; FDE pointer encoding (DW_EH_PE_pcrel | DW_EH_PE_sdata4) + \\ .byte 0x0c ; DW_CFA_def_cfa + \\ .uleb128 31 ; Reg 31 + \\ .uleb128 0 ; Offset 0 + \\ .align 3 + \\LCIE_end: + \\LFDE: + \\ .long LFDE_end - LFDE_start + \\LFDE_start: + \\ .long LFDE_start - LCIE ; CIE pointer + \\ .long _foo - . ; PC begin + \\ .long LEND_foo - _foo ; PC range + \\ .uleb128 4 ; Augmentation data length + \\ .long LLSDA_foo - . ; LSDA pointer + \\ .align 3 + \\LFDE_end: + }); + + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = + \\#include + \\int foo(); + \\int main() { + \\ printf("%d\n", foo()); + \\ return 0; + \\} + }); + exe.root_module.addObject(a_o); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("100\n"); + test_step.dependOn(&run.step); + + return test_step; +} + fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "unwind-info-no-subsections-arm64", opts);