Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macho: implement range extension thunks #93

Merged
merged 5 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 57 additions & 28 deletions src/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export_trie: ExportTrieSection = .{},
unwind_info: UnwindInfo = .{},

atoms: std.ArrayListUnmanaged(Atom) = .{},
thunks: std.ArrayListUnmanaged(Thunk) = .{},
unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},

has_tlv: bool = false,
Expand Down Expand Up @@ -132,6 +133,7 @@ pub fn deinit(self: *MachO) void {
self.segments.deinit(gpa);
self.sections.deinit(gpa);
self.atoms.deinit(gpa);
self.thunks.deinit(gpa);

self.symtab.deinit(gpa);
self.strtab.deinit(gpa);
Expand Down Expand Up @@ -349,7 +351,6 @@ pub fn flush(self: *MachO) !void {
try self.allocateSections();
self.allocateSegments();
self.allocateAtoms();
self.allocateSymbols();
self.allocateSyntheticSymbols();

state_log.debug("{}", .{self.dumpState()});
Expand Down Expand Up @@ -1577,12 +1578,20 @@ fn sortSections(self: *MachO) !void {

fn addAtomsToSections(self: *MachO) !void {
for (self.objects.items) |index| {
for (self.getFile(index).?.object.atoms.items) |atom_index| {
const object = self.getFile(index).?.object;
for (object.atoms.items) |atom_index| {
const atom = self.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
try atoms.append(self.base.allocator, atom_index);
}
for (object.symbols.items) |sym_index| {
const sym = self.getSymbol(sym_index);
const atom = sym.getAtom(self) orelse continue;
if (!atom.flags.alive) continue;
if (sym.getFile(self).?.getIndex() != index) continue;
sym.out_n_sect = atom.out_n_sect;
}
}
if (self.getInternalObject()) |object| {
for (object.atoms.items) |atom_index| {
Expand All @@ -1591,6 +1600,13 @@ fn addAtomsToSections(self: *MachO) !void {
const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
try atoms.append(self.base.allocator, atom_index);
}
for (object.symbols.items) |sym_index| {
const sym = self.getSymbol(sym_index);
const atom = sym.getAtom(self) orelse continue;
if (!atom.flags.alive) continue;
if (sym.getFile(self).?.getIndex() != object.index) continue;
sym.out_n_sect = atom.out_n_sect;
}
}
}

Expand Down Expand Up @@ -1623,11 +1639,7 @@ fn calcSectionSizes(self: *MachO) !void {
const slice = self.sections.slice();
for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
if (atoms.items.len == 0) continue;

// TODO
// if (self.requiresThunks()) {
// if (header.isCode()) continue;
// }
if (self.requiresThunks() and header.isCode()) continue;

for (atoms.items) |atom_index| {
const atom = self.getAtom(atom_index).?;
Expand All @@ -1640,16 +1652,15 @@ fn calcSectionSizes(self: *MachO) !void {
}
}

// TODO
// if (self.requiresThunks()) {
// for (slice.items(.header), slice.items(.atoms)) |header,atoms| {
// if (!header.isCode()) continue;
// if (atoms.items.len == 0) continue;
if (self.requiresThunks()) {
for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
if (!header.isCode()) continue;
if (atoms.items.len == 0) continue;

// // Create jump/branch range extenders if needed.
// try thunks.createThunks(self, @as(u8, @intCast(sect_id)));
// }
// }
// Create jump/branch range extenders if needed.
try thunks.createThunks(@intCast(i), self);
}
}

if (self.got_sect_index) |idx| {
const header = &self.sections.items(.header)[idx];
Expand Down Expand Up @@ -1874,19 +1885,10 @@ fn allocateAtoms(self: *MachO) void {
atom.value += header.addr;
}
}
}

fn allocateSymbols(self: *MachO) void {
for (self.objects.items) |index| {
for (self.getFile(index).?.getSymbols()) |sym_index| {
const sym = self.getSymbol(sym_index);
const atom = sym.getAtom(self) orelse continue;
if (!atom.flags.alive) continue;
if (sym.getFile(self).?.getIndex() != index) continue;

sym.value += atom.value;
sym.out_n_sect = atom.out_n_sect;
}
for (self.thunks.items) |*thunk| {
const header = self.sections.items(.header)[thunk.out_n_sect];
thunk.value += header.addr;
}
}

Expand Down Expand Up @@ -2059,6 +2061,16 @@ fn writeAtoms(self: *MachO) !void {
try self.base.file.pwriteAll(buffer, header.offset);
}

for (self.thunks.items) |thunk| {
const header = slice.items(.header)[thunk.out_n_sect];
const offset = thunk.value - header.addr + header.offset;
const buffer = try gpa.alloc(u8, thunk.size());
defer gpa.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
try thunk.write(self, stream.writer());
try self.base.file.pwriteAll(buffer, offset);
}

if (has_resolve_error) return error.ResolveFailed;
}

Expand Down Expand Up @@ -2817,6 +2829,18 @@ pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo
return &self.unwind_records.items[index];
}

pub fn addThunk(self: *MachO) !Thunk.Index {
const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
const thunk = try self.thunks.addOne(self.base.allocator);
thunk.* = .{};
return index;
}

pub fn getThunk(self: *MachO, index: Thunk.Index) *Thunk {
assert(index < self.thunks.items.len);
return &self.thunks.items[index];
}

pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
if (mem.startsWith(u8, path, prefix)) return path[prefix.len..];
return null;
Expand Down Expand Up @@ -2867,6 +2891,10 @@ fn fmtDumpState(
try writer.print("internal({d}) : internal\n", .{internal.index});
try writer.print("{}{}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });
}
try writer.writeAll("thunks\n");
for (self.thunks.items, 0..) |thunk, index| {
try writer.print("thunk({d}) : {}\n", .{ index, thunk.fmt(self) });
}
try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)});
try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)});
try writer.print("got\n{}\n", .{self.got.fmt(self)});
Expand Down Expand Up @@ -3082,6 +3110,7 @@ const Symbol = @import("MachO/Symbol.zig");
const StringTable = @import("strtab.zig").StringTable;
const StubsSection = synthetic.StubsSection;
const StubsHelperSection = synthetic.StubsHelperSection;
const Thunk = thunks.Thunk;
const ThreadPool = std.Thread.Pool;
const TlvPtrSection = synthetic.TlvPtrSection;
const UnwindInfo = @import("MachO/UnwindInfo.zig");
Expand Down
25 changes: 19 additions & 6 deletions src/MachO/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ relocs: Loc = .{},
/// Index of this atom in the linker's atoms table.
atom_index: Index = 0,

/// Index of the thunk for this atom.
thunk_index: Thunk.Index = 0,

/// Unwind records associated with this atom.
unwind_records: Loc = .{},

Expand Down Expand Up @@ -93,6 +96,10 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
}
}

pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
return macho_file.getThunk(self.thunk_index);
}

pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
const segname, const sectname, const flags = blk: {
if (sect.isCode()) break :blk .{
Expand Down Expand Up @@ -410,12 +417,16 @@ fn resolveRelocInner(
.branch => {
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
assert(rel.tag == .@"extern");

switch (cpu_arch) {
.x86_64 => try writer.writeInt(i32, @intCast(S + A - P), .little),
.aarch64 => {
// TODO thunk indirection
const disp = math.cast(i28, S + A - P) orelse return error.Overflow;
const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
const thunk = self.getThunk(macho_file);
const S_: i64 = @intCast(thunk.getAddress(rel.target));
break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
};
var inst = aarch64.Instruction{
.unconditional_branch_immediate = mem.bytesToValue(std.meta.TagPayload(
aarch64.Instruction,
Expand Down Expand Up @@ -666,7 +677,7 @@ pub fn format(
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format symbols directly");
@compileError("do not format Atom directly");
}

pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(format2) {
Expand All @@ -691,9 +702,10 @@ fn format2(
_ = unused_fmt_string;
const atom = ctx.atom;
const macho_file = ctx.macho_file;
try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{
atom.atom_index, atom.getName(macho_file), atom.value,
atom.out_n_sect, atom.alignment, atom.size,
try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : thunk({d})", .{
atom.atom_index, atom.getName(macho_file), atom.value,
atom.out_n_sect, atom.alignment, atom.size,
atom.thunk_index,
});
if (!atom.flags.alive) try writer.writeAll(" : [*]");
if (atom.unwind_records.len > 0) {
Expand Down Expand Up @@ -744,4 +756,5 @@ const MachO = @import("../MachO.zig");
const Object = @import("Object.zig");
const Relocation = @import("Relocation.zig");
const Symbol = @import("Symbol.zig");
const Thunk = @import("thunks.zig").Thunk;
const UnwindInfo = @import("UnwindInfo.zig");
7 changes: 6 additions & 1 deletion src/MachO/Symbol.zig
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub fn getAddress(symbol: Symbol, opts: struct {
return symbol.getObjcStubsAddress(macho_file);
}
}
if (symbol.getAtom(macho_file)) |atom| return atom.value + symbol.value;
return symbol.value;
}

Expand Down Expand Up @@ -272,7 +273,11 @@ fn format2(
_ = options;
_ = unused_fmt_string;
const symbol = ctx.symbol;
try writer.print("%{d} : {s} : @{x}", .{ symbol.nlist_idx, symbol.getName(ctx.macho_file), symbol.value });
try writer.print("%{d} : {s} : @{x}", .{
symbol.nlist_idx,
symbol.getName(ctx.macho_file),
symbol.getAddress(.{}, ctx.macho_file),
});
if (symbol.getFile(ctx.macho_file)) |file| {
if (symbol.out_n_sect != 0) {
try writer.print(" : sect({d})", .{symbol.out_n_sect});
Expand Down
Loading