diff --git a/src/MachO.zig b/src/MachO.zig index 187752ca..e19e63dc 100644 --- a/src/MachO.zig +++ b/src/MachO.zig @@ -1470,14 +1470,14 @@ fn initSyntheticSections(self: *MachO) !void { } const needs_unwind_info = for (self.objects.items) |index| { - if (self.getFile(index).?.object.has_unwind) break true; + if (self.getFile(index).?.object.compact_unwind_sect_index != null) break true; } else false; if (needs_unwind_info) { self.unwind_info_sect_index = try self.addSection("__TEXT", "__unwind_info", .{}); } const needs_eh_frame = for (self.objects.items) |index| { - if (self.getFile(index).?.object.has_eh_frame) break true; + if (self.getFile(index).?.object.eh_frame_sect_index != null) break true; } else false; if (needs_eh_frame) { assert(needs_unwind_info); diff --git a/src/MachO/Atom.zig b/src/MachO/Atom.zig index 29060d30..41552726 100644 --- a/src/MachO/Atom.zig +++ b/src/MachO/Atom.zig @@ -690,11 +690,12 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra const cpu_arch = macho_file.options.cpu_arch.?; const relocs = self.getRelocs(macho_file); + const sect = macho_file.sections.items(.header)[self.out_n_sect]; var stream = std.io.fixedBufferStream(code); for (relocs) |rel| { const rel_offset = rel.offset - self.off; - const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; + const r_address: i32 = math.cast(i32, self.value + rel_offset - sect.addr) orelse return error.Overflow; const r_symbolnum = r_symbolnum: { const r_symbolnum: u32 = switch (rel.tag) { .local => rel.getTargetAtom(macho_file).out_n_sect + 1, diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index fc54c208..306cfac7 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -22,8 +22,6 @@ fdes: std.ArrayListUnmanaged(Fde) = .{}, eh_frame_data: std.ArrayListUnmanaged(u8) = .{}, unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{}, -has_unwind: bool = false, -has_eh_frame: bool = false, alive: bool = true, hidden: bool = false, num_rebase_relocs: u32 = 0, @@ -681,6 +679,35 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { } } + if (!macho_file.options.relocatable) try self.synthesiseNullUnwindRecords(macho_file); + + const sortFn = struct { + fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool { + const lhs = ctx.getUnwindRecord(lhs_index); + const rhs = ctx.getUnwindRecord(rhs_index); + const lhsa = lhs.getAtom(ctx); + const rhsa = rhs.getAtom(ctx); + return lhsa.getInputAddress(ctx) + lhs.atom_offset < rhsa.getInputAddress(ctx) + rhs.atom_offset; + } + }.sortFn; + mem.sort(UnwindInfo.Record.Index, self.unwind_records.items, macho_file, sortFn); + + // Associate unwind records to atoms + var next_cu: u32 = 0; + while (next_cu < self.unwind_records.items.len) { + const start = next_cu; + const rec_index = self.unwind_records.items[start]; + const rec = macho_file.getUnwindRecord(rec_index); + while (next_cu < self.unwind_records.items.len and + macho_file.getUnwindRecord(self.unwind_records.items[next_cu]).atom == rec.atom) : (next_cu += 1) + {} + + const atom = rec.getAtom(macho_file); + atom.unwind_records = .{ .pos = start, .len = next_cu - start }; + } +} + +fn synthesiseNullUnwindRecords(self: *Object, macho_file: *MachO) !void { // Synthesise missing unwind records. // The logic here is as follows: // 1. if an atom has unwind info record that is not DWARF, FDE is marked dead @@ -690,6 +717,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null }; + const gpa = macho_file.base.allocator; var superposition = std.AutoArrayHashMap(u64, Superposition).init(gpa); defer superposition.deinit(); @@ -721,8 +749,6 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { } for (superposition.keys(), superposition.values()) |addr, meta| { - self.has_unwind = true; - if (meta.fde) |fde_index| { const fde = &self.fdes.items[fde_index]; @@ -734,7 +760,6 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { } else { // Tie FDE to unwind record rec.fde = fde_index; - self.has_eh_frame = true; } } else { // Synthesise new unwind info record @@ -753,7 +778,6 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { .aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF), else => unreachable, } - self.has_eh_frame = true; } } else if (meta.cu == null and meta.fde == null) { // Create a null record @@ -767,31 +791,6 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { rec.file = self.index; } } - - const sortFn = struct { - fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool { - const lhs = ctx.getUnwindRecord(lhs_index); - const rhs = ctx.getUnwindRecord(rhs_index); - const lhsa = lhs.getAtom(ctx); - const rhsa = rhs.getAtom(ctx); - return lhsa.getInputAddress(ctx) + lhs.atom_offset < rhsa.getInputAddress(ctx) + rhs.atom_offset; - } - }.sortFn; - mem.sort(UnwindInfo.Record.Index, self.unwind_records.items, macho_file, sortFn); - - // Associate unwind records to atoms - var next_cu: u32 = 0; - while (next_cu < self.unwind_records.items.len) { - const start = next_cu; - const rec_index = self.unwind_records.items[start]; - const rec = macho_file.getUnwindRecord(rec_index); - while (next_cu < self.unwind_records.items.len and - macho_file.getUnwindRecord(self.unwind_records.items[next_cu]).atom == rec.atom) : (next_cu += 1) - {} - - const atom = rec.getAtom(macho_file); - atom.unwind_records = .{ .pos = start, .len = next_cu - start }; - } } fn initPlatform(self: *Object) void { diff --git a/src/MachO/eh_frame.zig b/src/MachO/eh_frame.zig index 004743f0..a197d1e0 100644 --- a/src/MachO/eh_frame.zig +++ b/src/MachO/eh_frame.zig @@ -319,7 +319,6 @@ pub fn calcSize(macho_file: *MachO) !u32 { for (macho_file.objects.items) |index| { const object = macho_file.getFile(index).?.object; - if (!object.has_eh_frame) continue; outer: for (object.cies.items) |*cie| { for (cies.items) |other| { @@ -341,7 +340,6 @@ pub fn calcSize(macho_file: *MachO) !u32 { for (macho_file.objects.items) |index| { const object = macho_file.getFile(index).?.object; - if (!object.has_eh_frame) continue; for (object.fdes.items) |*fde| { if (!fde.alive) continue; fde.out_offset = offset; diff --git a/src/MachO/relocatable.zig b/src/MachO/relocatable.zig index d3032c24..c2856c89 100644 --- a/src/MachO/relocatable.zig +++ b/src/MachO/relocatable.zig @@ -110,7 +110,7 @@ fn initOutputSections(macho_file: *MachO) !void { } const needs_unwind_info = for (macho_file.objects.items) |index| { - if (macho_file.getFile(index).?.object.has_unwind) break true; + if (macho_file.getFile(index).?.object.compact_unwind_sect_index != null) break true; } else false; if (needs_unwind_info) { macho_file.unwind_info_sect_index = try macho_file.addSection("__LD", "__compact_unwind", .{ @@ -119,7 +119,7 @@ fn initOutputSections(macho_file: *MachO) !void { } const needs_eh_frame = for (macho_file.objects.items) |index| { - if (macho_file.getFile(index).?.object.has_eh_frame) break true; + if (macho_file.getFile(index).?.object.eh_frame_sect_index != null) break true; } else false; if (needs_eh_frame) { assert(needs_unwind_info); diff --git a/test/macho.zig b/test/macho.zig index 6103f6f4..ac25267b 100644 --- a/test/macho.zig +++ b/test/macho.zig @@ -54,6 +54,7 @@ pub fn addMachOTests(b: *Build, options: common.Options) *Step { macho_step.dependOn(testObjcStubs2(b, opts)); macho_step.dependOn(testPagezeroSize(b, opts)); macho_step.dependOn(testReexportsZig(b, opts)); + macho_step.dependOn(testRelocatable(b, opts)); macho_step.dependOn(testSearchStrategy(b, opts)); macho_step.dependOn(testSectionBoundarySymbols(b, opts)); macho_step.dependOn(testSegmentBoundarySymbols(b, opts)); @@ -2002,6 +2003,91 @@ fn testReexportsZig(b: *Build, opts: Options) *Step { return test_step; } +fn testRelocatable(b: *Build, opts: Options) *Step { + const test_step = b.step("test-macho-relocatable", ""); + + const a_c = + \\#include + \\int try_me() { + \\ throw std::runtime_error("Oh no!"); + \\} + ; + const b_c = + \\extern int try_me(); + \\int try_again() { + \\ return try_me(); + \\} + ; + const main_c = + \\#include + \\#include + \\extern int try_again(); + \\int main() { + \\ try { + \\ try_again(); + \\ } catch (const std::exception &e) { + \\ std::cout << "exception=" << e.what(); + \\ } + \\ return 0; + \\} + ; + const exp_stdout = "exception=Oh no!"; + + { + const a_o = cc(b, opts); + a_o.addCppSource(a_c); + a_o.addArg("-c"); + + const b_o = cc(b, opts); + b_o.addCppSource(b_c); + b_o.addArg("-c"); + + const c_o = ld(b, opts); + c_o.addFileSource(a_o.out); + c_o.addFileSource(b_o.out); + c_o.addArg("-r"); + + const exe = cc(b, opts); + exe.addCppSource(main_c); + exe.addFileSource(c_o.out); + exe.addArg("-lc++"); + + const run = exe.run(); + run.expectStdOutEqual(exp_stdout); + test_step.dependOn(run.step()); + } + + { + const a_o = cc(b, opts); + a_o.addCppSource(a_c); + a_o.addArg("-c"); + + const b_o = cc(b, opts); + b_o.addCppSource(b_c); + b_o.addArg("-c"); + + const main_o = cc(b, opts); + main_o.addCppSource(main_c); + main_o.addArg("-c"); + + const c_o = ld(b, opts); + c_o.addFileSource(a_o.out); + c_o.addFileSource(b_o.out); + c_o.addFileSource(main_o.out); + c_o.addArg("-r"); + + const exe = cc(b, opts); + exe.addFileSource(c_o.out); + exe.addArg("-lc++"); + + const run = exe.run(); + run.expectStdOutEqual(exp_stdout); + test_step.dependOn(run.step()); + } + + return test_step; +} + fn testSearchStrategy(b: *Build, opts: Options) *Step { const test_step = b.step("test-macho-search-strategy", "");