Skip to content

Commit

Permalink
macho: skip null unwind recs synthesis in -r; fix relocs in -r; test
Browse files Browse the repository at this point in the history
  • Loading branch information
kubkon committed Jan 3, 2024
1 parent 72915f0 commit 3aa44e3
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 38 deletions.
4 changes: 2 additions & 2 deletions src/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/MachO/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
61 changes: 30 additions & 31 deletions src/MachO/Object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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();

Expand Down Expand Up @@ -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];

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand Down
2 changes: 0 additions & 2 deletions src/MachO/eh_frame.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/MachO/relocatable.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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", .{
Expand All @@ -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);
Expand Down
86 changes: 86 additions & 0 deletions test/macho.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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 <stdexcept>
\\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 <iostream>
\\#include <stdexcept>
\\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", "");

Expand Down

0 comments on commit 3aa44e3

Please sign in to comment.