Skip to content

Commit

Permalink
Merge pull request #93 from kubkon/macho-thunks
Browse files Browse the repository at this point in the history
macho: implement range extension thunks
  • Loading branch information
kubkon authored Dec 20, 2023
2 parents 1836619 + 41b3f11 commit 53eedb6
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 369 deletions.
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

0 comments on commit 53eedb6

Please sign in to comment.