diff --git a/src/MachO/Atom.zig b/src/MachO/Atom.zig index 473e20f4..458fffdc 100644 --- a/src/MachO/Atom.zig +++ b/src/MachO/Atom.zig @@ -669,6 +669,89 @@ fn encode(insts: []const Instruction, code: []u8) !void { } } +pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { + switch (macho_file.options.cpu_arch.?) { + .aarch64 => { + var nreloc: u32 = 0; + for (self.getRelocs(macho_file)) |rel| { + nreloc += 1; + if (rel.addend > 0) nreloc += 1; + } + return nreloc; + }, + .x86_64 => return @intCast(self.getRelocs(macho_file).len), + else => unreachable, + } +} + +pub fn writeRelocs(self: Atom, macho_file: *MachO, buffer: *std.ArrayList(macho.relocation_info)) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const cpu_arch = macho_file.options.cpu_arch.?; + const relocs = self.getRelocs(macho_file); + + 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_symbolnum = r_symbolnum: { + const r_symbolnum: u32 = switch (rel.tag) { + .local => rel.getTargetAtom(macho_file).out_n_sect + 1, + .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?, + }; + break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow; + }; + const r_extern = rel.tag == .@"extern"; + const addend = rel.addend + rel.getRelocAddend(cpu_arch); + + switch (cpu_arch) { + .aarch64 => { + if (addend > 0 and rel.type != .unsigned) { + buffer.appendAssumeCapacity(.{ + .r_address = r_address, + .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow), + .r_pcrel = 0, + .r_length = 2, + .r_extern = 0, + .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND), + }); + } + + const r_type: macho.reloc_type_arm64 = switch (rel.type) { + .page => .ARM64_RELOC_PAGE21, + .pageoff => .ARM64_RELOC_PAGEOFF12, + .got_load_page => .ARM64_RELOC_GOT_LOAD_PAGE21, + .got_load_pageoff => .ARM64_RELOC_GOT_LOAD_PAGEOFF12, + .tlvp_page => .ARM64_RELOC_TLVP_LOAD_PAGE21, + .tlvp_pageoff => .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, + .branch => .ARM64_RELOC_BRANCH26, + .got => .ARM64_RELOC_POINTER_TO_GOT, + .subtractor => .ARM64_RELOC_SUBTRACTOR, + .unsigned => .ARM64_RELOC_UNSIGNED, + + .signed, + .signed1, + .signed2, + .signed4, + .got_load, + .tlv, + => unreachable, + }; + buffer.appendAssumeCapacity(.{ + .r_address = r_address, + .r_symbolnum = r_symbolnum, + .r_pcrel = @intFromBool(rel.meta.pcrel), + .r_extern = @intFromBool(r_extern), + .r_length = rel.meta.length, + .r_type = @intFromEnum(r_type), + }); + }, + .x86_64 => @panic("TODO"), + else => unreachable, + } + } +} + pub fn format( atom: Atom, comptime unused_fmt_string: []const u8, diff --git a/src/MachO/relocatable.zig b/src/MachO/relocatable.zig index bbf1f0d5..c4a04075 100644 --- a/src/MachO/relocatable.zig +++ b/src/MachO/relocatable.zig @@ -46,13 +46,13 @@ pub fn flush(macho_file: *MachO) !void { state_log.debug("{}", .{macho_file.dumpState()}); + 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 writeDataInCode(macho_file, off); - try macho_file.calcSymtabSize(); off = mem.alignForward(u32, off, @alignOf(u64)); off = try macho_file.writeSymtab(off); off = mem.alignForward(u32, off, @alignOf(u64)); @@ -141,7 +141,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { atom.value = offset; header.size += padding + atom.size; header.@"align" = @max(header.@"align", atom.alignment); - header.nreloc += @intCast(atom.relocs.len); + header.nreloc += atom.calcNumRelocs(macho_file); } } @@ -224,20 +224,26 @@ fn writeAtoms(macho_file: *MachO) !void { if (atoms.items.len == 0) continue; if (header.isZerofill()) continue; - const buffer = try gpa.alloc(u8, header.size); - defer gpa.free(buffer); + const code = try gpa.alloc(u8, header.size); + defer gpa.free(code); const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; - @memset(buffer, padding_byte); + @memset(code, padding_byte); + + var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc); + defer relocs.deinit(); for (atoms.items) |atom_index| { const atom = macho_file.getAtom(atom_index).?; assert(atom.flags.alive); const off = atom.value - header.addr; - @memcpy(buffer[off..][0..atom.size], atom.getCode(macho_file)); - // TODO write relocs + @memcpy(code[off..][0..atom.size], atom.getCode(macho_file)); + + try atom.writeRelocs(macho_file, &relocs); } - try macho_file.base.file.pwriteAll(buffer, header.offset); + // TODO scattered writes? + try macho_file.base.file.pwriteAll(code, header.offset); + try macho_file.base.file.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); } }