macho: re-enable relocatable mode

This commit is contained in:
Jakub Konka 2024-07-11 22:35:52 +02:00
parent 90c54f1eb6
commit b62281a9c8
5 changed files with 340 additions and 363 deletions

View File

@ -290,10 +290,16 @@ pub fn deinit(self: *MachO) void {
self.dylibs.deinit(gpa);
self.segments.deinit(gpa);
for (self.sections.items(.atoms), self.sections.items(.out), self.sections.items(.thunks)) |*atoms, *out, *thnks| {
for (
self.sections.items(.atoms),
self.sections.items(.out),
self.sections.items(.thunks),
self.sections.items(.relocs),
) |*atoms, *out, *thnks, *relocs| {
atoms.deinit(gpa);
out.deinit(gpa);
thnks.deinit(gpa);
relocs.deinit(gpa);
}
self.sections.deinit(gpa);
@ -1457,10 +1463,10 @@ pub fn dedupLiterals(self: *MachO) !void {
fn claimUnresolved(self: *MachO) void {
if (self.getZigObject()) |zo| {
zo.claimUnresolved(self);
zo.asFile().claimUnresolved(self);
}
for (self.objects.items) |index| {
self.getFile(index).?.object.claimUnresolved(self);
self.getFile(index).?.claimUnresolved(self);
}
}
@ -3987,6 +3993,7 @@ const Section = struct {
last_atom_index: Atom.Index = 0,
thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},
out: std.ArrayListUnmanaged(u8) = .{},
relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{},
};
pub const LiteralPool = struct {

View File

@ -1008,6 +1008,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
.r_extern = 0,
.r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
};
i += 1;
}
const r_type: macho.reloc_type_arm64 = switch (rel.type) {

View File

@ -1636,56 +1636,6 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
}
}
pub fn claimUnresolved(self: *Object, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
for (self.symbols.items, 0..) |*sym, i| {
const nlist = self.symtab.items(.nlist)[i];
if (!nlist.ext()) continue;
if (!nlist.undf()) continue;
if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
const is_import = switch (macho_file.undefined_treatment) {
.@"error" => false,
.warn, .suppress => nlist.weakRef(),
.dynamic_lookup => true,
};
if (is_import) {
sym.value = 0;
sym.atom_ref = .{ .index = 0, .file = 0 };
sym.flags.weak = false;
sym.flags.weak_ref = nlist.weakRef();
sym.flags.import = is_import;
sym.visibility = .global;
const idx = self.globals.items[i];
macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
}
}
}
pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
for (self.symbols.items, self.symtab.items(.nlist), 0..) |*sym, nlist, i| {
if (!nlist.ext()) continue;
if (!nlist.undf()) continue;
if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
sym.value = 0;
sym.atom_ref = .{ .index = 0, .file = 0 };
sym.flags.weak_ref = nlist.weakRef();
sym.flags.import = true;
sym.visibility = .global;
const idx = self.globals.items[i];
macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
}
}
fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {
const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));
self.sections.set(n_sect, .{
@ -1936,7 +1886,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = macho_file.base.allocator;
const gpa = macho_file.base.comp.gpa;
const headers = self.sections.items(.header);
const sections_data = try gpa.alloc([]const u8, headers.len);
defer {
@ -1995,15 +1945,17 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const cpu_arch = macho_file.getTarget().cpu.arch;
const addReloc = struct {
fn addReloc(offset: u32, cpu_arch: std.Target.Cpu.Arch) !macho.relocation_info {
fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
return .{
.r_address = math.cast(i32, offset) orelse return error.Overflow,
.r_symbolnum = 0,
.r_pcrel = 0,
.r_length = 3,
.r_extern = 0,
.r_type = switch (cpu_arch) {
.r_type = switch (arch) {
.aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
.x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
else => unreachable,
@ -2039,7 +1991,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
const atom = rec.getAtom(macho_file);
const addr = rec.getAtomAddress(macho_file);
out.rangeStart = addr;
var reloc = try addReloc(offset, macho_file.options.cpu_arch.?);
var reloc = try addReloc(offset, cpu_arch);
reloc.r_symbolnum = atom.out_n_sect + 1;
relocs[reloc_index] = reloc;
reloc_index += 1;
@ -2048,7 +2000,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
// Personality function
if (rec.getPersonality(macho_file)) |sym| {
const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
var reloc = try addReloc(offset + 16, macho_file.options.cpu_arch.?);
var reloc = try addReloc(offset + 16, cpu_arch);
reloc.r_symbolnum = r_symbolnum;
reloc.r_extern = 1;
relocs[reloc_index] = reloc;
@ -2059,7 +2011,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
if (rec.getLsdaAtom(macho_file)) |atom| {
const addr = rec.getLsdaAddress(macho_file);
out.lsda = addr;
var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?);
var reloc = try addReloc(offset + 24, cpu_arch);
reloc.r_symbolnum = atom.out_n_sect + 1;
relocs[reloc_index] = reloc;
reloc_index += 1;

View File

@ -118,7 +118,24 @@ pub const File = union(enum) {
};
}
pub fn getNlists(file: File) []macho.nlist_64 {
return switch (file) {
.dylib => unreachable,
.internal => |x| x.symtab.items,
inline else => |x| x.symtab.items(.nlist),
};
}
pub fn getGlobals(file: File) []MachO.SymbolResolver.Index {
return switch (file) {
inline else => |x| x.globals.items,
};
}
pub fn markImportsExports(file: File, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
const nsyms = switch (file) {
.dylib => unreachable,
inline else => |x| x.symbols.items.len,
@ -138,7 +155,25 @@ pub const File = union(enum) {
}
}
pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
assert(file == .object or file == .zig_object);
for (file.getSymbols(), 0..) |*sym, i| {
const ref = file.getSymbolRef(@intCast(i), macho_file);
const other_file = ref.getFile(macho_file) orelse continue;
if (other_file.getIndex() != file.getIndex()) continue;
if (sym.visibility != .global) continue;
sym.flags.@"export" = true;
}
}
pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const nsyms = switch (file) {
inline else => |x| x.symbols.items.len,
};
@ -166,6 +201,59 @@ pub const File = union(enum) {
}
}
pub fn claimUnresolved(file: File, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
assert(file == .object or file == .zig_object);
for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
if (!nlist.ext()) continue;
if (!nlist.undf()) continue;
if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
const is_import = switch (macho_file.undefined_treatment) {
.@"error" => false,
.warn, .suppress => nlist.weakRef(),
.dynamic_lookup => true,
};
if (is_import) {
sym.value = 0;
sym.atom_ref = .{ .index = 0, .file = 0 };
sym.flags.weak = false;
sym.flags.weak_ref = nlist.weakRef();
sym.flags.import = is_import;
sym.visibility = .global;
const idx = file.getGlobals()[i];
macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
}
}
}
pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
assert(file == .object or file == .zig_object);
for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
if (!nlist.ext()) continue;
if (!nlist.undf()) continue;
if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
sym.value = 0;
sym.atom_ref = .{ .index = 0, .file = 0 };
sym.flags.weak_ref = nlist.weakRef();
sym.flags.import = true;
sym.visibility = .global;
const idx = file.getGlobals()[i];
macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
}
}
pub fn initOutputSections(file: File, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();

View File

@ -26,70 +26,51 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
return;
}
@panic("TODO -r mode");
for (positionals.items) |obj| {
macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
error.MalformedObject,
error.MalformedArchive,
error.InvalidCpuArch,
error.InvalidTarget,
=> continue, // already reported
error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
else => |e| try macho_file.reportParseError(
obj.path,
"unexpected error: parsing input file failed with error {s}",
.{@errorName(e)},
),
};
}
// for (positionals.items) |obj| {
// macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
// error.MalformedObject,
// error.MalformedArchive,
// error.InvalidCpuArch,
// error.InvalidTarget,
// => continue, // already reported
// error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
// else => |e| try macho_file.reportParseError(
// obj.path,
// "unexpected error: parsing input file failed with error {s}",
// .{@errorName(e)},
// ),
// };
// }
if (comp.link_errors.items.len > 0) return error.FlushFailure;
// if (comp.link_errors.items.len > 0) return error.FlushFailure;
try macho_file.resolveSymbols();
try macho_file.dedupLiterals();
markExports(macho_file);
claimUnresolved(macho_file);
try initOutputSections(macho_file);
try macho_file.sortSections();
try macho_file.addAtomsToSections();
try calcSectionSizes(macho_file);
// try macho_file.addUndefinedGlobals();
// try macho_file.resolveSymbols();
// try macho_file.parseDebugInfo();
// try macho_file.dedupLiterals();
// markExports(macho_file);
// claimUnresolved(macho_file);
// try initOutputSections(macho_file);
// try macho_file.sortSections();
// try macho_file.addAtomsToSections();
// try calcSectionSizes(macho_file);
try createSegment(macho_file);
try allocateSections(macho_file);
allocateSegment(macho_file);
// try createSegment(macho_file);
// try allocateSections(macho_file);
// allocateSegment(macho_file);
if (build_options.enable_logging) {
state_log.debug("{}", .{macho_file.dumpState()});
}
// var off = off: {
// const seg = macho_file.segments.items[0];
// const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
// break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
// };
// off = allocateSectionsRelocs(macho_file, off);
try writeSections(macho_file);
sortRelocs(macho_file);
try writeSectionsToFile(macho_file);
// if (build_options.enable_logging) {
// state_log.debug("{}", .{macho_file.dumpState()});
// }
// In order to please Apple ld (and possibly other MachO linkers in the wild),
// we will now sanitize segment names of Zig-specific segments.
sanitizeZigSections(macho_file);
// try macho_file.calcSymtabSize();
// try writeAtoms(macho_file);
// try writeCompactUnwind(macho_file);
// try writeEhFrame(macho_file);
// off = mem.alignForward(u32, off, @alignOf(u64));
// off = try macho_file.writeDataInCode(0, off);
// off = mem.alignForward(u32, off, @alignOf(u64));
// off = try macho_file.writeSymtab(off);
// off = mem.alignForward(u32, off, @alignOf(u64));
// off = try macho_file.writeStrtab(off);
// // In order to please Apple ld (and possibly other MachO linkers in the wild),
// // we will now sanitize segment names of Zig-specific segments.
// sanitizeZigSections(macho_file);
// const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
// try writeHeader(macho_file, ncmds, sizeofcmds);
const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
try writeHeader(macho_file, ncmds, sizeofcmds);
}
pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
@ -353,9 +334,9 @@ pub fn claimUnresolved(macho_file: *MachO) void {
fn initOutputSections(macho_file: *MachO) !void {
for (macho_file.objects.items) |index| {
const object = macho_file.getFile(index).?.object;
for (object.atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index) orelse continue;
const file = macho_file.getFile(index).?;
for (file.getAtoms()) |atom_index| {
const atom = file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
}
@ -383,69 +364,141 @@ fn calcSectionSizes(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const slice = macho_file.sections.slice();
for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
if (atoms.items.len == 0) continue;
for (atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index).?;
const atom_alignment = atom.alignment.toByteUnits() orelse 1;
const offset = mem.alignForward(u64, header.size, atom_alignment);
const padding = offset - header.size;
atom.value = offset;
header.size += padding + atom.size;
header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
header.nreloc += atom.calcNumRelocs(macho_file);
calcSectionSize(macho_file, @intCast(i));
}
if (macho_file.eh_frame_sect_index) |_| {
try calcEhFrameSize(macho_file);
}
for (macho_file.objects.items) |index| {
if (macho_file.unwind_info_sect_index) |_| {
macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
}
macho_file.getFile(index).?.calcSymtabSize(macho_file);
}
if (macho_file.unwind_info_sect_index) |index| {
calcCompactUnwindSize(macho_file, index);
}
try macho_file.data_in_code.updateSize(macho_file);
if (macho_file.eh_frame_sect_index) |index| {
const sect = &macho_file.sections.items(.header)[index];
sect.size = try eh_frame.calcSize(macho_file);
sect.@"align" = 3;
sect.nreloc = eh_frame.calcNumRelocs(macho_file);
}
calcCompactUnwindSize(macho_file);
calcSymtabSize(macho_file);
if (macho_file.getZigObject()) |zo| {
for (zo.atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const header = &macho_file.sections.items(.header)[atom.out_n_sect];
if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
header.nreloc += atom.calcNumRelocs(macho_file);
}
// TODO
// if (macho_file.getZigObject()) |zo| {
// for (zo.atoms.items) |atom_index| {
// const atom = macho_file.getAtom(atom_index) orelse continue;
// if (!atom.flags.alive) continue;
// const header = &macho_file.sections.items(.header)[atom.out_n_sect];
// if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
// header.nreloc += atom.calcNumRelocs(macho_file);
// }
// }
}
fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
const tracy = trace(@src());
defer tracy.end();
const slice = macho_file.sections.slice();
const header = &slice.items(.header)[sect_id];
const atoms = slice.items(.atoms)[sect_id].items;
for (atoms) |ref| {
const atom = ref.getAtom(macho_file).?;
const atom_alignment = atom.alignment.toByteUnits() orelse 1;
const offset = mem.alignForward(u64, header.size, atom_alignment);
const padding = offset - header.size;
atom.value = offset;
header.size += padding + atom.size;
header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
const nreloc = atom.calcNumRelocs(macho_file);
atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
header.nreloc += nreloc;
}
}
fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
var size: u32 = 0;
fn calcEhFrameSize(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
header.size = try eh_frame.calcSize(macho_file);
header.@"align" = 3;
header.nreloc = eh_frame.calcNumRelocs(macho_file);
}
fn calcCompactUnwindSize(macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
var nrec: u32 = 0;
var nreloc: u32 = 0;
for (macho_file.objects.items) |index| {
const object = macho_file.getFile(index).?.object;
for (object.unwind_records_indexes.items) |irec| {
const rec = object.getUnwindRecord(irec);
if (!rec.alive) continue;
size += @sizeOf(macho.compact_unwind_entry);
nreloc += 1;
if (rec.getPersonality(macho_file)) |_| {
nreloc += 1;
}
if (rec.getLsdaAtom(macho_file)) |_| {
nreloc += 1;
}
}
const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx;
ctx.rec_index = nrec;
ctx.reloc_index = nreloc;
nrec += ctx.rec_count;
nreloc += ctx.reloc_count;
}
const sect = &macho_file.sections.items(.header)[sect_index];
sect.size = size;
const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
sect.size = nrec * @sizeOf(macho.compact_unwind_entry);
sect.nreloc = nreloc;
sect.@"align" = 3;
}
fn calcSymtabSize(macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
var nlocals: u32 = 0;
var nstabs: u32 = 0;
var nexports: u32 = 0;
var nimports: u32 = 0;
var strsize: u32 = 1;
for (macho_file.objects.items) |index| {
const object = macho_file.getFile(index).?.object;
const ctx = &object.output_symtab_ctx;
ctx.ilocal = nlocals;
ctx.istab = nstabs;
ctx.iexport = nexports;
ctx.iimport = nimports;
ctx.stroff = strsize;
nlocals += ctx.nlocals;
nstabs += ctx.nstabs;
nexports += ctx.nexports;
nimports += ctx.nimports;
strsize += ctx.strsize;
}
for (macho_file.objects.items) |index| {
const object = macho_file.getFile(index).?.object;
const ctx = &object.output_symtab_ctx;
ctx.istab += nlocals;
ctx.iexport += nlocals + nstabs;
ctx.iimport += nlocals + nstabs + nexports;
}
{
const cmd = &macho_file.symtab_cmd;
cmd.nsyms = nlocals + nstabs + nexports + nimports;
cmd.strsize = strsize;
}
{
const cmd = &macho_file.dysymtab_cmd;
cmd.ilocalsym = 0;
cmd.nlocalsym = nlocals + nstabs;
cmd.iextdefsym = nlocals + nstabs;
cmd.nextdefsym = nexports;
cmd.iundefsym = nlocals + nstabs + nexports;
cmd.nundefsym = nimports;
}
}
fn allocateSections(macho_file: *MachO) !void {
const slice = macho_file.sections.slice();
for (slice.items(.header)) |*header| {
@ -463,6 +516,37 @@ fn allocateSections(macho_file: *MachO) !void {
}
header.size = needed_size;
}
var fileoff: u32 = 0;
for (slice.items(.header)) |header| {
fileoff = @max(fileoff, header.offset + @as(u32, @intCast(header.size)));
}
for (slice.items(.header)) |*header| {
if (header.nreloc == 0) continue;
header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
}
// In -r mode, there is no LINKEDIT segment and so we allocate required LINKEDIT commands
// as if they were detached or part of the single segment.
// DATA_IN_CODE
{
const cmd = &macho_file.data_in_code_cmd;
cmd.dataoff = fileoff;
fileoff += cmd.datasize;
fileoff = mem.alignForward(u32, fileoff, @alignOf(u64));
}
// SYMTAB
{
const cmd = &macho_file.symtab_cmd;
cmd.symoff = fileoff;
fileoff += cmd.nsyms * @sizeOf(macho.nlist_64);
fileoff = mem.alignForward(u32, fileoff, @alignOf(u32));
cmd.stroff = fileoff;
}
}
/// Renames segment names in Zig sections to standard MachO segment names such as
@ -525,232 +609,77 @@ fn allocateSegment(macho_file: *MachO) void {
seg.filesize = fileoff - seg.fileoff;
}
fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
var fileoff = off;
const slice = macho_file.sections.slice();
for (slice.items(.header)) |*header| {
if (header.nreloc == 0) continue;
header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
}
return fileoff;
}
// We need to sort relocations in descending order to be compatible with Apple's linker.
fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
_ = ctx;
return lhs.r_address > rhs.r_address;
}
fn writeAtoms(macho_file: *MachO) !void {
fn sortRelocs(macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
for (macho_file.sections.items(.relocs)) |*relocs| {
mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
}
}
fn writeSections(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = macho_file.base.comp.gpa;
const cpu_arch = macho_file.getTarget().cpu.arch;
const slice = macho_file.sections.slice();
var relocs = std.ArrayList(macho.relocation_info).init(gpa);
defer relocs.deinit();
for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
if (atoms.items.len == 0) continue;
for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, *out, *relocs| {
if (header.isZerofill()) continue;
if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue;
const size = math.cast(usize, header.size) orelse return error.Overflow;
const code = try gpa.alloc(u8, size);
defer gpa.free(code);
try out.resize(gpa, header.size);
const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
@memset(code, padding_byte);
try relocs.ensureTotalCapacity(header.nreloc);
for (atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index).?;
assert(atom.flags.alive);
const off = math.cast(usize, atom.value) orelse return error.Overflow;
const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
try atom.getData(macho_file, code[off..][0..atom_size]);
try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
}
assert(relocs.items.len == header.nreloc);
mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
// TODO scattered writes?
try macho_file.base.file.?.pwriteAll(code, header.offset);
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
relocs.clearRetainingCapacity();
@memset(out.items, padding_byte);
try relocs.resize(gpa, header.nreloc);
}
if (macho_file.getZigObject()) |zo| {
// TODO: this is ugly; perhaps we should aggregrate before?
var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);
defer {
for (zo_relocs.values()) |*list| {
list.deinit();
}
zo_relocs.deinit();
}
const cmd = macho_file.symtab_cmd;
try macho_file.symtab.resize(gpa, cmd.nsyms);
try macho_file.strtab.resize(gpa, cmd.strsize);
macho_file.strtab.items[0] = 0;
for (macho_file.sections.items(.header), 0..) |header, n_sect| {
if (header.isZerofill()) continue;
if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue;
const gop = try zo_relocs.getOrPut(@intCast(n_sect));
if (gop.found_existing) continue;
gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
}
for (zo.atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const header = macho_file.sections.items(.header)[atom.out_n_sect];
if (header.isZerofill()) continue;
if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
if (atom.getRelocs(macho_file).len == 0) continue;
const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
const code = try gpa.alloc(u8, atom_size);
defer gpa.free(code);
atom.getData(macho_file, code) catch |err| switch (err) {
error.InputOutput => {
try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
atom.getName(macho_file),
});
return error.FlushFailure;
},
else => |e| {
try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
atom.getName(macho_file),
@errorName(e),
});
return error.FlushFailure;
},
};
const file_offset = header.offset + atom.value;
const rels = zo_relocs.getPtr(atom.out_n_sect).?;
try atom.writeRelocs(macho_file, code, rels);
try macho_file.base.file.?.pwriteAll(code, file_offset);
}
for (zo_relocs.keys(), zo_relocs.values()) |sect_id, rels| {
const header = macho_file.sections.items(.header)[sect_id];
assert(rels.items.len == header.nreloc);
mem.sort(macho.relocation_info, rels.items, {}, sortReloc);
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff);
}
}
}
fn writeCompactUnwind(macho_file: *MachO) !void {
const sect_index = macho_file.unwind_info_sect_index orelse return;
const gpa = macho_file.base.comp.gpa;
const header = macho_file.sections.items(.header)[sect_index];
const nrecs = math.cast(usize, @divExact(header.size, @sizeOf(macho.compact_unwind_entry))) orelse return error.Overflow;
var entries = try std.ArrayList(macho.compact_unwind_entry).initCapacity(gpa, nrecs);
defer entries.deinit();
var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
defer relocs.deinit();
const addReloc = struct {
fn addReloc(offset: i32, cpu_arch: std.Target.Cpu.Arch) macho.relocation_info {
return .{
.r_address = offset,
.r_symbolnum = 0,
.r_pcrel = 0,
.r_length = 3,
.r_extern = 0,
.r_type = switch (cpu_arch) {
.aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
.x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
else => unreachable,
},
};
}
}.addReloc;
var offset: i32 = 0;
for (macho_file.objects.items) |index| {
const object = macho_file.getFile(index).?.object;
for (object.unwind_records_indexes.items) |irec| {
const rec = object.getUnwindRecord(irec);
if (!rec.alive) continue;
var out: macho.compact_unwind_entry = .{
.rangeStart = 0,
.rangeLength = rec.length,
.compactUnwindEncoding = rec.enc.enc,
.personalityFunction = 0,
.lsda = 0,
};
{
// Function address
const atom = rec.getAtom(macho_file);
const addr = rec.getAtomAddress(macho_file);
out.rangeStart = addr;
var reloc = addReloc(offset, macho_file.getTarget().cpu.arch);
reloc.r_symbolnum = atom.out_n_sect + 1;
relocs.appendAssumeCapacity(reloc);
}
// Personality function
if (rec.getPersonality(macho_file)) |sym| {
const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
var reloc = addReloc(offset + 16, macho_file.getTarget().cpu.arch);
reloc.r_symbolnum = r_symbolnum;
reloc.r_extern = 1;
relocs.appendAssumeCapacity(reloc);
}
// LSDA address
if (rec.getLsdaAtom(macho_file)) |atom| {
const addr = rec.getLsdaAddress(macho_file);
out.lsda = addr;
var reloc = addReloc(offset + 24, macho_file.getTarget().cpu.arch);
reloc.r_symbolnum = atom.out_n_sect + 1;
relocs.appendAssumeCapacity(reloc);
}
entries.appendAssumeCapacity(out);
offset += @sizeOf(macho.compact_unwind_entry);
}
try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
}
assert(entries.items.len == nrecs);
assert(relocs.items.len == header.nreloc);
if (macho_file.eh_frame_sect_index) |_| {
try writeEhFrame(macho_file);
}
mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
// TODO scattered writes?
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(entries.items), header.offset);
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
if (macho_file.unwind_info_sect_index) |_| {
for (macho_file.objects.items) |index| {
try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
}
}
}
fn writeEhFrame(macho_file: *MachO) !void {
const sect_index = macho_file.eh_frame_sect_index orelse return;
const gpa = macho_file.base.comp.gpa;
const header = macho_file.sections.items(.header)[sect_index];
const size = math.cast(usize, header.size) orelse return error.Overflow;
const sect_index = macho_file.eh_frame_sect_index.?;
const buffer = macho_file.sections.items(.out)[sect_index];
const relocs = macho_file.sections.items(.relocs)[sect_index];
try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
}
const code = try gpa.alloc(u8, size);
defer gpa.free(code);
fn writeSectionsToFile(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
defer relocs.deinit();
const slice = macho_file.sections.slice();
for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
try macho_file.base.file.?.pwriteAll(out.items, header.offset);
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
}
try eh_frame.writeRelocs(macho_file, code, &relocs);
assert(relocs.items.len == header.nreloc);
mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
// TODO scattered writes?
try macho_file.base.file.?.pwriteAll(code, header.offset);
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
try macho_file.writeDataInCode();
try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
}
fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {