From ae2b4deab0cf3c1a1de9304da684f65557e51276 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 1 Jan 2024 20:11:42 +0100 Subject: [PATCH] macho: write well-formed relocatable object file --- src/MachO.zig | 6 +-- src/MachO/Object.zig | 6 ++- src/MachO/relocatable.zig | 109 +++++++++++++++++++++++++++++++++++--- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/src/MachO.zig b/src/MachO.zig index 0bdb9557..64990562 100644 --- a/src/MachO.zig +++ b/src/MachO.zig @@ -2355,7 +2355,7 @@ fn writeDataInCode(self: *MachO, off: u64) !u64 { return off + needed_size; } -fn calcSymtabSize(self: *MachO) !void { +pub fn calcSymtabSize(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.allocator; @@ -2417,7 +2417,7 @@ fn calcSymtabSize(self: *MachO) !void { } } -fn writeSymtab(self: *MachO, off: u64) !u64 { +pub fn writeSymtab(self: *MachO, off: u64) !u64 { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.allocator; @@ -2461,7 +2461,7 @@ fn writeIndsymtab(self: *MachO, off: u64) !u64 { return off + needed_size; } -fn writeStrtab(self: *MachO, off: u64) !u64 { +pub fn writeStrtab(self: *MachO, off: u64) !u64 { const cmd = &self.symtab_cmd; cmd.stroff = @intCast(off); try self.base.file.pwriteAll(self.strtab.items, cmd.stroff); diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index b99e81f4..5dbacb2f 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -120,7 +120,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { } mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan); - if (self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) { + if (self.hasSubsections()) { try self.initSubsections(nlists.items, macho_file); } else { try self.initSections(nlists.items, macho_file); @@ -1291,6 +1291,10 @@ pub fn getDataInCode(self: Object) []align(1) const macho.data_in_code_entry { return dice; } +pub inline fn hasSubsections(self: Object) bool { + return self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; +} + pub fn asFile(self: *Object) File { return .{ .object = self }; } diff --git a/src/MachO/relocatable.zig b/src/MachO/relocatable.zig index f1708ef4..7fe1e2b2 100644 --- a/src/MachO/relocatable.zig +++ b/src/MachO/relocatable.zig @@ -14,6 +14,9 @@ pub fn flush(macho_file: *MachO) !void { .maxprot = prot, .initprot = prot, }); + const seg = &macho_file.segments.items[0]; + seg.nsects = @intCast(macho_file.sections.items(.header).len); + seg.cmdsize += seg.nsects * @sizeOf(macho.section_64); } try allocateSections(macho_file); @@ -23,7 +26,7 @@ pub fn flush(macho_file: *MachO) !void { assert(macho_file.segments.items.len == 1); const seg = &macho_file.segments.items[0]; var vmaddr: u64 = 0; - var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file); + var fileoff: u64 = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64); seg.vmaddr = vmaddr; seg.fileoff = fileoff; @@ -45,12 +48,18 @@ pub fn flush(macho_file: *MachO) !void { try writeAtoms(macho_file); try writeCompactUnwind(macho_file); try writeEhFrame(macho_file); - // try macho_file.calcSymtabSize(); - // try macho_file.writeSymtab(); + + var off = off: { + const seg = macho_file.segments.items[0]; + break :off mem.alignForward(u64, seg.fileoff + seg.filesize, @alignOf(u64)); + }; + try macho_file.calcSymtabSize(); + off = try macho_file.writeSymtab(off); + off = try macho_file.writeStrtab(off); // TODO write data-in-code - macho_file.base.fatal("-r mode unimplemented", .{}); - return error.Unimplemented; + const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); + try writeHeader(macho_file, ncmds, sizeofcmds); } fn claimUnresolved(macho_file: *MachO) void { @@ -72,6 +81,7 @@ fn claimUnresolved(macho_file: *MachO) void { sym.file = index; sym.flags.weak_ref = nlist.weakRef(); sym.flags.import = true; + sym.visibility = .global; } } } @@ -156,7 +166,7 @@ fn calcCompactUnwindSize(macho_file: *MachO) usize { } fn allocateSections(macho_file: *MachO) !void { - var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file); + var fileoff = load_commands.calcLoadCommandsSizeObject(macho_file) + @sizeOf(macho.mach_header_64); var vmaddr: u64 = 0; const slice = macho_file.sections.slice(); @@ -237,6 +247,93 @@ fn writeEhFrame(macho_file: *MachO) !void { // TODO write relocs } +fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } { + const gpa = macho_file.base.allocator; + const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); + const buffer = try gpa.alloc(u8, needed_size); + defer gpa.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + var cwriter = std.io.countingWriter(stream.writer()); + const writer = cwriter.writer(); + + var ncmds: usize = 0; + + // Segment and section load commands + { + assert(macho_file.segments.items.len == 1); + const seg = macho_file.segments.items[0]; + try writer.writeStruct(seg); + for (macho_file.sections.items(.header)) |header| { + try writer.writeStruct(header); + } + ncmds += 1; + } + + try writer.writeStruct(macho_file.data_in_code_cmd); + ncmds += 1; + try writer.writeStruct(macho_file.symtab_cmd); + ncmds += 1; + try writer.writeStruct(macho_file.dysymtab_cmd); + ncmds += 1; + + if (macho_file.options.platform) |platform| { + if (platform.isBuildVersionCompatible()) { + try load_commands.writeBuildVersionLC(platform, macho_file.options.sdk_version, writer); + ncmds += 1; + } else { + try load_commands.writeVersionMinLC(platform, macho_file.options.sdk_version, writer); + ncmds += 1; + } + } + + assert(cwriter.bytes_written == needed_size); + + try macho_file.base.file.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); + + return .{ ncmds, buffer.len }; +} + +fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { + var header: macho.mach_header_64 = .{}; + header.filetype = macho.MH_OBJECT; + + const subsections_via_symbols = for (macho_file.objects.items) |index| { + const object = macho_file.getFile(index).?.object; + if (object.hasSubsections()) break true; + } else false; + if (subsections_via_symbols) { + header.flags |= macho.MH_SUBSECTIONS_VIA_SYMBOLS; + } + + switch (macho_file.options.cpu_arch.?) { + .aarch64 => { + header.cputype = macho.CPU_TYPE_ARM64; + header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; + }, + .x86_64 => { + header.cputype = macho.CPU_TYPE_X86_64; + header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; + }, + else => {}, + } + + if (macho_file.has_tlv) { + header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; + } + if (macho_file.binds_to_weak) { + header.flags |= macho.MH_BINDS_TO_WEAK; + } + if (macho_file.weak_defines) { + header.flags |= macho.MH_WEAK_DEFINES; + } + + header.ncmds = @intCast(ncmds); + header.sizeofcmds = @intCast(sizeofcmds); + + try macho_file.base.file.pwriteAll(mem.asBytes(&header), 0); +} + const assert = std.debug.assert; const eh_frame = @import("eh_frame.zig"); const load_commands = @import("load_commands.zig");