aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2023-08-22 14:03:45 +0200
committerJakub Konka <kubkon@jakubkonka.com>2023-08-29 11:39:34 +0200
commitda9e7e498af411de477a7b59e72ef763b22f4f5a (patch)
tree4dee3daca757de84f1be423d44096ec287e4d789 /src
parent85f2df5050fd6cec25bd251159795bb0fa0f8731 (diff)
downloadzig-da9e7e498af411de477a7b59e72ef763b22f4f5a.tar.gz
zig-da9e7e498af411de477a7b59e72ef763b22f4f5a.zip
macho: unify Atom concept between drivers
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO.zig3
-rw-r--r--src/link/MachO/Atom.zig967
-rw-r--r--src/link/MachO/Object.zig2
-rw-r--r--src/link/MachO/UnwindInfo.zig2
-rw-r--r--src/link/MachO/ZldAtom.zig1012
-rw-r--r--src/link/MachO/dead_strip.zig2
-rw-r--r--src/link/MachO/eh_frame.zig2
-rw-r--r--src/link/MachO/thunks.zig2
-rw-r--r--src/link/MachO/zld.zig21
9 files changed, 980 insertions, 1033 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index e6f7bd4828..f0db232e4b 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -1597,8 +1597,11 @@ pub fn createAtom(self: *MachO) !Atom.Index {
try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
atom.* = .{
.sym_index = sym_index,
+ .inner_sym_index = 0,
+ .inner_nsyms_trailing = 0,
.file = 0,
.size = 0,
+ .alignment = 0,
.prev_index = null,
.next_index = null,
};
diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig
index 7dc97003aa..73d90eb532 100644
--- a/src/link/MachO/Atom.zig
+++ b/src/link/MachO/Atom.zig
@@ -16,12 +16,13 @@ const Arch = std.Target.Cpu.Arch;
const MachO = @import("../MachO.zig");
pub const Relocation = @import("Relocation.zig");
const SymbolWithLoc = MachO.SymbolWithLoc;
+const Zld = @import("zld.zig").Zld;
/// Each Atom always gets a symbol with the fully qualified name.
/// The symbol can reside in any object file context structure in `symtab` array
/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
/// a stub trampoline, it can be found in the linkers `locals` arraylist.
-/// If this field is 0, it means the codegen size = 0 and there is no symbol or
+/// If this field is 0 and file is 0, it means the codegen size = 0 and there is no symbol or
/// offset table entry.
sym_index: u32,
@@ -31,11 +32,24 @@ sym_index: u32,
/// the field directly.
file: u32,
+/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
+/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
+/// this Atom contains any additional symbol references that fall within this Atom's
+/// address range. These could for example be an alias symbol which can be used
+/// internally by the relocation records, or if the Object file couldn't be split
+/// into subsections, this Atom may encompass an entire input section.
+inner_sym_index: u32,
+inner_nsyms_trailing: u32,
+
/// Size and alignment of this atom
/// Unlike in Elf, we need to store the size of this symbol as part of
/// the atom since macho.nlist_64 lacks this information.
size: u64,
+/// Alignment of this atom as a power of 2.
+/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
+alignment: u32,
+
/// Points to the previous and next neighbours
/// TODO use the same trick as with symbols: reserve index 0 as null atom
next_index: ?Index,
@@ -48,13 +62,15 @@ pub const Binding = struct {
offset: u64,
};
-pub const SymbolAtOffset = struct {
- sym_index: u32,
- offset: u64,
-};
+/// Returns `null` if the Atom is a synthetic Atom.
+/// Otherwise, returns an index into an array of Objects.
+pub fn getFile(self: Atom) ?u32 {
+ if (self.file == 0) return null;
+ return self.file - 1;
+}
pub fn getSymbolIndex(self: Atom) ?u32 {
- if (self.sym_index == 0) return null;
+ if (self.getFile() == null and self.sym_index == 0) return null;
return self.sym_index;
}
@@ -66,10 +82,7 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
/// Returns pointer-to-symbol referencing this atom.
pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
const sym_index = self.getSymbolIndex().?;
- return macho_file.getSymbolPtr(.{
- .sym_index = sym_index,
- .file = self.file,
- });
+ return macho_file.getSymbolPtr(.{ .sym_index = sym_index, .file = self.file });
}
pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
@@ -80,10 +93,7 @@ pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
/// Returns the name of this atom.
pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
const sym_index = self.getSymbolIndex().?;
- return macho_file.getSymbolName(.{
- .sym_index = sym_index,
- .file = self.file,
- });
+ return macho_file.getSymbolName(.{ .sym_index = sym_index, .file = self.file });
}
/// Returns how much room there is to grow in virtual address space.
@@ -182,3 +192,932 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
}
+
+const InnerSymIterator = struct {
+ sym_index: u32,
+ nsyms: u32,
+ file: u32,
+ pos: u32 = 0,
+
+ pub fn next(it: *@This()) ?SymbolWithLoc {
+ if (it.pos == it.nsyms) return null;
+ const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
+ it.pos += 1;
+ return res;
+ }
+};
+
+/// Returns an iterator over potentially contained symbols.
+/// Panics when called on a synthetic Atom.
+pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null);
+ return .{
+ .sym_index = atom.inner_sym_index,
+ .nsyms = atom.inner_nsyms_trailing,
+ .file = atom.file,
+ };
+}
+
+/// Returns a section alias symbol if one is defined.
+/// An alias symbol is used to represent the start of an input section
+/// if there were no symbols defined within that range.
+/// Alias symbols are only used on x86_64.
+pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null);
+
+ const object = zld.objects.items[atom.getFile().?];
+ const nbase = @as(u32, @intCast(object.in_symtab.?.len));
+ const ntotal = @as(u32, @intCast(object.symtab.len));
+ var sym_index: u32 = nbase;
+ while (sym_index < ntotal) : (sym_index += 1) {
+ if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
+ if (other_atom_index == atom_index) return SymbolWithLoc{
+ .sym_index = sym_index,
+ .file = atom.file,
+ };
+ }
+ }
+ return null;
+}
+
+/// Given an index into a contained symbol within, calculates an offset wrt
+/// the start of this Atom.
+pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null);
+
+ if (atom.sym_index == sym_index) return 0;
+
+ const object = zld.objects.items[atom.getFile().?];
+ const source_sym = object.getSourceSymbol(sym_index).?;
+ const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
+ sym.n_value
+ else blk: {
+ const nbase = @as(u32, @intCast(object.in_symtab.?.len));
+ const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
+ const source_sect = object.getSourceSection(sect_id);
+ break :blk source_sect.addr;
+ };
+ return source_sym.n_value - base_addr;
+}
+
+pub fn scanAtomRelocs(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
+ const arch = zld.options.target.cpu.arch;
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null); // synthetic atoms do not have relocs
+
+ return switch (arch) {
+ .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
+ .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
+ else => unreachable,
+ };
+}
+
+const RelocContext = struct {
+ base_addr: i64 = 0,
+ base_offset: i32 = 0,
+};
+
+pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null); // synthetic atoms do not have relocs
+
+ const object = zld.objects.items[atom.getFile().?];
+ if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
+ const source_sect = object.getSourceSection(source_sym.n_sect - 1);
+ return .{
+ .base_addr = @as(i64, @intCast(source_sect.addr)),
+ .base_offset = @as(i32, @intCast(source_sym.n_value - source_sect.addr)),
+ };
+ }
+ const nbase = @as(u32, @intCast(object.in_symtab.?.len));
+ const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
+ const source_sect = object.getSourceSection(sect_id);
+ return .{
+ .base_addr = @as(i64, @intCast(source_sect.addr)),
+ .base_offset = 0,
+ };
+}
+
+pub fn parseRelocTarget(zld: *Zld, ctx: struct {
+ object_id: u32,
+ rel: macho.relocation_info,
+ code: []const u8,
+ base_addr: i64 = 0,
+ base_offset: i32 = 0,
+}) SymbolWithLoc {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const object = &zld.objects.items[ctx.object_id];
+ log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
+
+ const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
+ const sect_id = @as(u8, @intCast(ctx.rel.r_symbolnum - 1));
+ const rel_offset = @as(u32, @intCast(ctx.rel.r_address - ctx.base_offset));
+
+ const address_in_section = if (ctx.rel.r_pcrel == 0) blk: {
+ break :blk if (ctx.rel.r_length == 3)
+ mem.readIntLittle(u64, ctx.code[rel_offset..][0..8])
+ else
+ mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
+ } else blk: {
+ assert(zld.options.target.cpu.arch == .x86_64);
+ const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
+ .X86_64_RELOC_SIGNED => 0,
+ .X86_64_RELOC_SIGNED_1 => 1,
+ .X86_64_RELOC_SIGNED_2 => 2,
+ .X86_64_RELOC_SIGNED_4 => 4,
+ else => unreachable,
+ };
+ const addend = mem.readIntLittle(i32, ctx.code[rel_offset..][0..4]);
+ const target_address = @as(i64, @intCast(ctx.base_addr)) + ctx.rel.r_address + 4 + correction + addend;
+ break :blk @as(u64, @intCast(target_address));
+ };
+
+ // Find containing atom
+ log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
+ break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
+ } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
+
+ const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
+ const sym = zld.getSymbol(sym_loc);
+ const target = if (sym.sect() and !sym.ext())
+ sym_loc
+ else if (object.getGlobal(sym_index)) |global_index|
+ zld.globals.items[global_index]
+ else
+ sym_loc;
+ log.debug(" | target %{d} ('{s}') in object({?d})", .{
+ target.sym_index,
+ zld.getSymbolName(target),
+ target.getFile(),
+ });
+ return target;
+}
+
+pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?Index {
+ if (is_via_got) {
+ return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
+ }
+ if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
+ if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
+
+ if (target.getFile() == null) {
+ const target_sym_name = zld.getSymbolName(target);
+ if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
+ if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
+
+ unreachable; // referenced symbol not found
+ }
+
+ const object = zld.objects.items[target.getFile().?];
+ return object.getAtomIndexForSymbol(target.sym_index);
+}
+
+fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
+ for (relocs) |rel| {
+ const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
+
+ switch (rel_type) {
+ .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
+ else => {},
+ }
+
+ if (rel.r_extern == 0) continue;
+
+ const atom = zld.getAtom(atom_index);
+ const object = &zld.objects.items[atom.getFile().?];
+ const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
+ const sym_loc = SymbolWithLoc{
+ .sym_index = sym_index,
+ .file = atom.file,
+ };
+
+ const target = if (object.getGlobal(sym_index)) |global_index|
+ zld.globals.items[global_index]
+ else
+ sym_loc;
+
+ switch (rel_type) {
+ .ARM64_RELOC_BRANCH26 => {
+ // TODO rewrite relocation
+ try addStub(zld, target);
+ },
+ .ARM64_RELOC_GOT_LOAD_PAGE21,
+ .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+ .ARM64_RELOC_POINTER_TO_GOT,
+ => {
+ // TODO rewrite relocation
+ try addGotEntry(zld, target);
+ },
+ .ARM64_RELOC_TLVP_LOAD_PAGE21,
+ .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
+ => {
+ try addTlvPtrEntry(zld, target);
+ },
+ else => {},
+ }
+ }
+}
+
+fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
+ for (relocs) |rel| {
+ const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
+
+ switch (rel_type) {
+ .X86_64_RELOC_SUBTRACTOR => continue,
+ else => {},
+ }
+
+ if (rel.r_extern == 0) continue;
+
+ const atom = zld.getAtom(atom_index);
+ const object = &zld.objects.items[atom.getFile().?];
+ const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
+ const sym_loc = SymbolWithLoc{
+ .sym_index = sym_index,
+ .file = atom.file,
+ };
+
+ const target = if (object.getGlobal(sym_index)) |global_index|
+ zld.globals.items[global_index]
+ else
+ sym_loc;
+
+ switch (rel_type) {
+ .X86_64_RELOC_BRANCH => {
+ // TODO rewrite relocation
+ try addStub(zld, target);
+ },
+ .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
+ // TODO rewrite relocation
+ try addGotEntry(zld, target);
+ },
+ .X86_64_RELOC_TLV => {
+ try addTlvPtrEntry(zld, target);
+ },
+ else => {},
+ }
+ }
+}
+
+fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
+ const target_sym = zld.getSymbol(target);
+ if (!target_sym.undf()) return;
+ if (zld.tlv_ptr_table.contains(target)) return;
+
+ const gpa = zld.gpa;
+ const atom_index = try zld.createTlvPtrAtom();
+ const tlv_ptr_index = @as(u32, @intCast(zld.tlv_ptr_entries.items.len));
+ try zld.tlv_ptr_entries.append(gpa, .{
+ .target = target,
+ .atom_index = atom_index,
+ });
+ try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
+}
+
+pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
+ if (zld.got_table.contains(target)) return;
+ const gpa = zld.gpa;
+ const atom_index = try zld.createGotAtom();
+ const got_index = @as(u32, @intCast(zld.got_entries.items.len));
+ try zld.got_entries.append(gpa, .{
+ .target = target,
+ .atom_index = atom_index,
+ });
+ try zld.got_table.putNoClobber(gpa, target, got_index);
+}
+
+pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
+ const target_sym = zld.getSymbol(target);
+ if (!target_sym.undf()) return;
+ if (zld.stubs_table.contains(target)) return;
+
+ const gpa = zld.gpa;
+ _ = try zld.createStubHelperAtom();
+ _ = try zld.createLazyPointerAtom();
+ const atom_index = try zld.createStubAtom();
+ const stubs_index = @as(u32, @intCast(zld.stubs.items.len));
+ try zld.stubs.append(gpa, .{
+ .target = target,
+ .atom_index = atom_index,
+ });
+ try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
+}
+
+pub fn resolveRelocs(
+ zld: *Zld,
+ atom_index: Index,
+ atom_code: []u8,
+ atom_relocs: []align(1) const macho.relocation_info,
+) !void {
+ const arch = zld.options.target.cpu.arch;
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null); // synthetic atoms do not have relocs
+
+ log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
+ atom.sym_index,
+ zld.getSymbolName(atom.getSymbolWithLoc()),
+ });
+
+ const ctx = getRelocContext(zld, atom_index);
+
+ return switch (arch) {
+ .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
+ .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
+ else => unreachable,
+ };
+}
+
+pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
+ const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
+ // If there is no atom for target, we still need to check for special, atom-less
+ // symbols such as `___dso_handle`.
+ const target_name = zld.getSymbolName(target);
+ const atomless_sym = zld.getSymbol(target);
+ log.debug(" | atomless target '{s}'", .{target_name});
+ return atomless_sym.n_value;
+ };
+ const target_atom = zld.getAtom(target_atom_index);
+ log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
+ target_atom.sym_index,
+ zld.getSymbolName(target_atom.getSymbolWithLoc()),
+ target_atom.getFile(),
+ });
+
+ const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
+ assert(target_sym.n_desc != @import("zld.zig").N_DEAD);
+
+ // If `target` is contained within the target atom, pull its address value.
+ const offset = if (target_atom.getFile() != null) blk: {
+ const object = zld.objects.items[target_atom.getFile().?];
+ break :blk if (object.getSourceSymbol(target.sym_index)) |_|
+ Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
+ else
+ 0; // section alias
+ } else 0;
+ const base_address: u64 = if (is_tlv) base_address: {
+ // For TLV relocations, the value specified as a relocation is the displacement from the
+ // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
+ // defined TLV template init section in the following order:
+ // * wrt to __thread_data if defined, then
+ // * wrt to __thread_bss
+ const sect_id: u16 = sect_id: {
+ if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
+ break :sect_id i;
+ } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
+ break :sect_id i;
+ } else {
+ log.err("threadlocal variables present but no initializer sections found", .{});
+ log.err(" __thread_data not found", .{});
+ log.err(" __thread_bss not found", .{});
+ return error.FailedToResolveRelocationTarget;
+ }
+ };
+ break :base_address zld.sections.items(.header)[sect_id].addr;
+ } else 0;
+ return target_sym.n_value + offset - base_address;
+}
+
+fn resolveRelocsArm64(
+ zld: *Zld,
+ atom_index: Index,
+ atom_code: []u8,
+ atom_relocs: []align(1) const macho.relocation_info,
+ context: RelocContext,
+) !void {
+ const atom = zld.getAtom(atom_index);
+ const object = zld.objects.items[atom.getFile().?];
+
+ var addend: ?i64 = null;
+ var subtractor: ?SymbolWithLoc = null;
+
+ for (atom_relocs) |rel| {
+ const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
+
+ switch (rel_type) {
+ .ARM64_RELOC_ADDEND => {
+ assert(addend == null);
+
+ log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
+
+ addend = rel.r_symbolnum;
+ continue;
+ },
+ .ARM64_RELOC_SUBTRACTOR => {
+ assert(subtractor == null);
+
+ log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
+ @tagName(rel_type),
+ rel.r_address,
+ rel.r_symbolnum,
+ atom.getFile(),
+ });
+
+ subtractor = parseRelocTarget(zld, .{
+ .object_id = atom.getFile().?,
+ .rel = rel,
+ .code = atom_code,
+ .base_addr = context.base_addr,
+ .base_offset = context.base_offset,
+ });
+ continue;
+ },
+ else => {},
+ }
+
+ const target = parseRelocTarget(zld, .{
+ .object_id = atom.getFile().?,
+ .rel = rel,
+ .code = atom_code,
+ .base_addr = context.base_addr,
+ .base_offset = context.base_offset,
+ });
+ const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
+
+ log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
+ @tagName(rel_type),
+ rel.r_address,
+ target.sym_index,
+ zld.getSymbolName(target),
+ target.getFile(),
+ });
+
+ const source_addr = blk: {
+ const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
+ break :blk source_sym.n_value + rel_offset;
+ };
+ const is_via_got = relocRequiresGot(zld, rel);
+ const is_tlv = is_tlv: {
+ const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
+ const header = zld.sections.items(.header)[source_sym.n_sect - 1];
+ break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
+ };
+ const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
+
+ log.debug(" | source_addr = 0x{x}", .{source_addr});
+
+ switch (rel_type) {
+ .ARM64_RELOC_BRANCH26 => {
+ const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
+ const stub_atom = zld.getAtom(stub_atom_index);
+ break :inner stub_atom.getSymbolWithLoc();
+ } else target;
+ log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
+ zld.getSymbolName(atom.getSymbolWithLoc()),
+ atom.getFile(),
+ zld.getSymbolName(target),
+ zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
+ });
+
+ const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
+ source_addr,
+ zld.getSymbol(actual_target).n_value,
+ )) |disp| blk: {
+ log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
+ break :blk disp;
+ } else |_| blk: {
+ const thunk_index = zld.thunk_table.get(atom_index).?;
+ const thunk = zld.thunks.items[thunk_index];
+ const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
+ zld,
+ actual_target,
+ ).?);
+ log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
+ break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
+ };
+
+ const code = atom_code[rel_offset..][0..4];
+ var inst = aarch64.Instruction{
+ .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.unconditional_branch_immediate,
+ ), code),
+ };
+ inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(displacement >> 2))));
+ mem.writeIntLittle(u32, code, inst.toU32());
+ },
+
+ .ARM64_RELOC_PAGE21,
+ .ARM64_RELOC_GOT_LOAD_PAGE21,
+ .ARM64_RELOC_TLVP_LOAD_PAGE21,
+ => {
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
+
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+
+ const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)));
+ const code = atom_code[rel_offset..][0..4];
+ var inst = aarch64.Instruction{
+ .pc_relative_address = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.pc_relative_address,
+ ), code),
+ };
+ inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
+ inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
+ mem.writeIntLittle(u32, code, inst.toU32());
+ addend = null;
+ },
+
+ .ARM64_RELOC_PAGEOFF12 => {
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
+
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+
+ const code = atom_code[rel_offset..][0..4];
+ if (Relocation.isArithmeticOp(code)) {
+ const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
+ var inst = aarch64.Instruction{
+ .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.add_subtract_immediate,
+ ), code),
+ };
+ inst.add_subtract_immediate.imm12 = off;
+ mem.writeIntLittle(u32, code, inst.toU32());
+ } else {
+ var inst = aarch64.Instruction{
+ .load_store_register = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.load_store_register,
+ ), code),
+ };
+ const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
+ 0 => if (inst.load_store_register.v == 1)
+ Relocation.PageOffsetInstKind.load_store_128
+ else
+ Relocation.PageOffsetInstKind.load_store_8,
+ 1 => .load_store_16,
+ 2 => .load_store_32,
+ 3 => .load_store_64,
+ });
+ inst.load_store_register.offset = off;
+ mem.writeIntLittle(u32, code, inst.toU32());
+ }
+ addend = null;
+ },
+
+ .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
+ const code = atom_code[rel_offset..][0..4];
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
+
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+
+ const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
+ var inst: aarch64.Instruction = .{
+ .load_store_register = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.load_store_register,
+ ), code),
+ };
+ inst.load_store_register.offset = off;
+ mem.writeIntLittle(u32, code, inst.toU32());
+ addend = null;
+ },
+
+ .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
+ const code = atom_code[rel_offset..][0..4];
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
+
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+
+ const RegInfo = struct {
+ rd: u5,
+ rn: u5,
+ size: u2,
+ };
+ const reg_info: RegInfo = blk: {
+ if (Relocation.isArithmeticOp(code)) {
+ const inst = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.add_subtract_immediate,
+ ), code);
+ break :blk .{
+ .rd = inst.rd,
+ .rn = inst.rn,
+ .size = inst.sf,
+ };
+ } else {
+ const inst = mem.bytesToValue(meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.load_store_register,
+ ), code);
+ break :blk .{
+ .rd = inst.rt,
+ .rn = inst.rn,
+ .size = inst.size,
+ };
+ }
+ };
+
+ var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
+ .load_store_register = .{
+ .rt = reg_info.rd,
+ .rn = reg_info.rn,
+ .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
+ .opc = 0b01,
+ .op1 = 0b01,
+ .v = 0,
+ .size = reg_info.size,
+ },
+ } else aarch64.Instruction{
+ .add_subtract_immediate = .{
+ .rd = reg_info.rd,
+ .rn = reg_info.rn,
+ .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
+ .sh = 0,
+ .s = 0,
+ .op = 0,
+ .sf = @as(u1, @truncate(reg_info.size)),
+ },
+ };
+ mem.writeIntLittle(u32, code, inst.toU32());
+ addend = null;
+ },
+
+ .ARM64_RELOC_POINTER_TO_GOT => {
+ log.debug(" | target_addr = 0x{x}", .{target_addr});
+ const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
+ return error.Overflow;
+ mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result)));
+ },
+
+ .ARM64_RELOC_UNSIGNED => {
+ var ptr_addend = if (rel.r_length == 3)
+ mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
+ else
+ mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
+
+ if (rel.r_extern == 0) {
+ const base_addr = if (target.sym_index >= object.source_address_lookup.len)
+ @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
+ else
+ object.source_address_lookup[target.sym_index];
+ ptr_addend -= base_addr;
+ }
+
+ const result = blk: {
+ if (subtractor) |sub| {
+ const sym = zld.getSymbol(sub);
+ break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
+ } else {
+ break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
+ }
+ };
+ log.debug(" | target_addr = 0x{x}", .{result});
+
+ if (rel.r_length == 3) {
+ mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
+ } else {
+ mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
+ }
+
+ subtractor = null;
+ },
+
+ .ARM64_RELOC_ADDEND => unreachable,
+ .ARM64_RELOC_SUBTRACTOR => unreachable,
+ }
+ }
+}
+
+fn resolveRelocsX86(
+ zld: *Zld,
+ atom_index: Index,
+ atom_code: []u8,
+ atom_relocs: []align(1) const macho.relocation_info,
+ context: RelocContext,
+) !void {
+ const atom = zld.getAtom(atom_index);
+ const object = zld.objects.items[atom.getFile().?];
+
+ var subtractor: ?SymbolWithLoc = null;
+
+ for (atom_relocs) |rel| {
+ const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
+
+ switch (rel_type) {
+ .X86_64_RELOC_SUBTRACTOR => {
+ assert(subtractor == null);
+
+ log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
+ @tagName(rel_type),
+ rel.r_address,
+ rel.r_symbolnum,
+ atom.getFile(),
+ });
+
+ subtractor = parseRelocTarget(zld, .{
+ .object_id = atom.getFile().?,
+ .rel = rel,
+ .code = atom_code,
+ .base_addr = context.base_addr,
+ .base_offset = context.base_offset,
+ });
+ continue;
+ },
+ else => {},
+ }
+
+ const target = parseRelocTarget(zld, .{
+ .object_id = atom.getFile().?,
+ .rel = rel,
+ .code = atom_code,
+ .base_addr = context.base_addr,
+ .base_offset = context.base_offset,
+ });
+ const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
+
+ log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
+ @tagName(rel_type),
+ rel.r_address,
+ target.sym_index,
+ zld.getSymbolName(target),
+ target.getFile(),
+ });
+
+ const source_addr = blk: {
+ const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
+ break :blk source_sym.n_value + rel_offset;
+ };
+ const is_via_got = relocRequiresGot(zld, rel);
+ const is_tlv = is_tlv: {
+ const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
+ const header = zld.sections.items(.header)[source_sym.n_sect - 1];
+ break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
+ };
+
+ log.debug(" | source_addr = 0x{x}", .{source_addr});
+
+ const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
+
+ switch (rel_type) {
+ .X86_64_RELOC_BRANCH => {
+ const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+ const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
+ mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
+ },
+
+ .X86_64_RELOC_GOT,
+ .X86_64_RELOC_GOT_LOAD,
+ => {
+ const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+ const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
+ mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
+ },
+
+ .X86_64_RELOC_TLV => {
+ const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+ const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
+
+ if (zld.tlv_ptr_table.get(target) == null) {
+ // We need to rewrite the opcode from movq to leaq.
+ atom_code[rel_offset - 2] = 0x8d;
+ }
+
+ mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
+ },
+
+ .X86_64_RELOC_SIGNED,
+ .X86_64_RELOC_SIGNED_1,
+ .X86_64_RELOC_SIGNED_2,
+ .X86_64_RELOC_SIGNED_4,
+ => {
+ const correction: u3 = switch (rel_type) {
+ .X86_64_RELOC_SIGNED => 0,
+ .X86_64_RELOC_SIGNED_1 => 1,
+ .X86_64_RELOC_SIGNED_2 => 2,
+ .X86_64_RELOC_SIGNED_4 => 4,
+ else => unreachable,
+ };
+ var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
+
+ if (rel.r_extern == 0) {
+ const base_addr = if (target.sym_index >= object.source_address_lookup.len)
+ @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
+ else
+ object.source_address_lookup[target.sym_index];
+ addend += @as(i32, @intCast(@as(i64, @intCast(context.base_addr)) + rel.r_address + 4 -
+ @as(i64, @intCast(base_addr))));
+ }
+
+ const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
+
+ log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
+
+ const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
+ mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
+ },
+
+ .X86_64_RELOC_UNSIGNED => {
+ var addend = if (rel.r_length == 3)
+ mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
+ else
+ mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
+
+ if (rel.r_extern == 0) {
+ const base_addr = if (target.sym_index >= object.source_address_lookup.len)
+ @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
+ else
+ object.source_address_lookup[target.sym_index];
+ addend -= base_addr;
+ }
+
+ const result = blk: {
+ if (subtractor) |sub| {
+ const sym = zld.getSymbol(sub);
+ break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
+ } else {
+ break :blk @as(i64, @intCast(target_addr)) + addend;
+ }
+ };
+ log.debug(" | target_addr = 0x{x}", .{result});
+
+ if (rel.r_length == 3) {
+ mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
+ } else {
+ mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
+ }
+
+ subtractor = null;
+ },
+
+ .X86_64_RELOC_SUBTRACTOR => unreachable,
+ }
+ }
+}
+
+pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
+ const object = zld.objects.items[atom.getFile().?];
+ const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
+ // If there was no matching symbol present in the source symtab, this means
+ // we are dealing with either an entire section, or part of it, but also
+ // starting at the beginning.
+ const nbase = @as(u32, @intCast(object.in_symtab.?.len));
+ const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
+ const source_sect = object.getSourceSection(sect_id);
+ assert(!source_sect.isZerofill());
+ const code = object.getSectionContents(source_sect);
+ const code_len = @as(usize, @intCast(atom.size));
+ return code[0..code_len];
+ };
+ const source_sect = object.getSourceSection(source_sym.n_sect - 1);
+ assert(!source_sect.isZerofill());
+ const code = object.getSectionContents(source_sect);
+ const offset = @as(usize, @intCast(source_sym.n_value - source_sect.addr));
+ const code_len = @as(usize, @intCast(atom.size));
+ return code[offset..][0..code_len];
+}
+
+pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info {
+ const atom = zld.getAtom(atom_index);
+ assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
+ const object = zld.objects.items[atom.getFile().?];
+ const cache = object.relocs_lookup[atom.sym_index];
+
+ const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
+ break :blk source_sym.n_sect - 1;
+ } else blk: {
+ // If there was no matching symbol present in the source symtab, this means
+ // we are dealing with either an entire section, or part of it, but also
+ // starting at the beginning.
+ const nbase = @as(u32, @intCast(object.in_symtab.?.len));
+ const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
+ break :blk sect_id;
+ };
+ const source_sect = object.getSourceSection(source_sect_id);
+ assert(!source_sect.isZerofill());
+ const relocs = object.getRelocs(source_sect_id);
+ return relocs[cache.start..][0..cache.len];
+}
+
+pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
+ switch (zld.options.target.cpu.arch) {
+ .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
+ .ARM64_RELOC_GOT_LOAD_PAGE21,
+ .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+ .ARM64_RELOC_POINTER_TO_GOT,
+ => return true,
+ else => return false,
+ },
+ .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
+ .X86_64_RELOC_GOT,
+ .X86_64_RELOC_GOT_LOAD,
+ => return true,
+ else => return false,
+ },
+ else => unreachable,
+ }
+}
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index e407b83017..03628e0e35 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -19,7 +19,7 @@ const sort = std.sort;
const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
-const Atom = @import("ZldAtom.zig");
+const Atom = @import("Atom.zig");
const AtomIndex = @import("zld.zig").AtomIndex;
const DwarfInfo = @import("DwarfInfo.zig");
const LoadCommandIterator = macho.LoadCommandIterator;
diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig
index 49a3ab9d01..4f2ca33638 100644
--- a/src/link/MachO/UnwindInfo.zig
+++ b/src/link/MachO/UnwindInfo.zig
@@ -12,7 +12,7 @@ const mem = std.mem;
const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
-const Atom = @import("ZldAtom.zig");
+const Atom = @import("Atom.zig");
const AtomIndex = @import("zld.zig").AtomIndex;
const EhFrameRecord = eh_frame.EhFrameRecord;
const MachO = @import("../MachO.zig");
diff --git a/src/link/MachO/ZldAtom.zig b/src/link/MachO/ZldAtom.zig
deleted file mode 100644
index 6bcd74ff0c..0000000000
--- a/src/link/MachO/ZldAtom.zig
+++ /dev/null
@@ -1,1012 +0,0 @@
-//! An atom is a single smallest unit of measure that will get an
-//! allocated virtual memory address in the final linked image.
-//! For example, we parse each input section within an input relocatable
-//! object file into a set of atoms which are then laid out contiguously
-//! as they were defined in the input file.
-
-const Atom = @This();
-
-const std = @import("std");
-const build_options = @import("build_options");
-const aarch64 = @import("../../arch/aarch64/bits.zig");
-const assert = std.debug.assert;
-const log = std.log.scoped(.atom);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const meta = std.meta;
-const trace = @import("../../tracy.zig").trace;
-
-const Allocator = mem.Allocator;
-const Arch = std.Target.Cpu.Arch;
-const AtomIndex = @import("zld.zig").AtomIndex;
-const Object = @import("Object.zig");
-const Relocation = @import("Relocation.zig");
-const SymbolWithLoc = @import("../MachO.zig").SymbolWithLoc;
-const Zld = @import("zld.zig").Zld;
-
-/// Each Atom always gets a symbol with the fully qualified name.
-/// The symbol can reside in any object file context structure in `symtab` array
-/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
-/// a stub trampoline, it can be found in the linkers `locals` arraylist.
-sym_index: u32,
-
-/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
-/// Otherwise, it is the index into appropriate object file (indexing from 1).
-/// Prefer using `getFile()` helper to get the file index out rather than using
-/// the field directly.
-file: u32,
-
-/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
-/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
-/// this Atom contains any additional symbol references that fall within this Atom's
-/// address range. These could for example be an alias symbol which can be used
-/// internally by the relocation records, or if the Object file couldn't be split
-/// into subsections, this Atom may encompass an entire input section.
-inner_sym_index: u32,
-inner_nsyms_trailing: u32,
-
-/// Size of this atom.
-size: u64,
-
-/// Alignment of this atom as a power of 2.
-/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
-alignment: u32,
-
-/// Points to the previous and next neighbours
-next_index: ?AtomIndex,
-prev_index: ?AtomIndex,
-
-pub const empty = Atom{
- .sym_index = 0,
- .inner_sym_index = 0,
- .inner_nsyms_trailing = 0,
- .file = 0,
- .size = 0,
- .alignment = 0,
- .prev_index = null,
- .next_index = null,
-};
-
-/// Returns `null` if the Atom is a synthetic Atom.
-/// Otherwise, returns an index into an array of Objects.
-pub fn getFile(self: Atom) ?u32 {
- if (self.file == 0) return null;
- return self.file - 1;
-}
-
-pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
- return .{
- .sym_index = self.sym_index,
- .file = self.file,
- };
-}
-
-const InnerSymIterator = struct {
- sym_index: u32,
- nsyms: u32,
- file: u32,
- pos: u32 = 0,
-
- pub fn next(it: *@This()) ?SymbolWithLoc {
- if (it.pos == it.nsyms) return null;
- const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
- it.pos += 1;
- return res;
- }
-};
-
-/// Returns an iterator over potentially contained symbols.
-/// Panics when called on a synthetic Atom.
-pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterator {
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null);
- return .{
- .sym_index = atom.inner_sym_index,
- .nsyms = atom.inner_nsyms_trailing,
- .file = atom.file,
- };
-}
-
-/// Returns a section alias symbol if one is defined.
-/// An alias symbol is used to represent the start of an input section
-/// if there were no symbols defined within that range.
-/// Alias symbols are only used on x86_64.
-pub fn getSectionAlias(zld: *Zld, atom_index: AtomIndex) ?SymbolWithLoc {
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null);
-
- const object = zld.objects.items[atom.getFile().?];
- const nbase = @as(u32, @intCast(object.in_symtab.?.len));
- const ntotal = @as(u32, @intCast(object.symtab.len));
- var sym_index: u32 = nbase;
- while (sym_index < ntotal) : (sym_index += 1) {
- if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
- if (other_atom_index == atom_index) return SymbolWithLoc{
- .sym_index = sym_index,
- .file = atom.file,
- };
- }
- }
- return null;
-}
-
-/// Given an index into a contained symbol within, calculates an offset wrt
-/// the start of this Atom.
-pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u64 {
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null);
-
- if (atom.sym_index == sym_index) return 0;
-
- const object = zld.objects.items[atom.getFile().?];
- const source_sym = object.getSourceSymbol(sym_index).?;
- const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
- sym.n_value
- else blk: {
- const nbase = @as(u32, @intCast(object.in_symtab.?.len));
- const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
- const source_sect = object.getSourceSection(sect_id);
- break :blk source_sect.addr;
- };
- return source_sym.n_value - base_addr;
-}
-
-pub fn scanAtomRelocs(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
- const arch = zld.options.target.cpu.arch;
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null); // synthetic atoms do not have relocs
-
- return switch (arch) {
- .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
- .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
- else => unreachable,
- };
-}
-
-const RelocContext = struct {
- base_addr: i64 = 0,
- base_offset: i32 = 0,
-};
-
-pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null); // synthetic atoms do not have relocs
-
- const object = zld.objects.items[atom.getFile().?];
- if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
- const source_sect = object.getSourceSection(source_sym.n_sect - 1);
- return .{
- .base_addr = @as(i64, @intCast(source_sect.addr)),
- .base_offset = @as(i32, @intCast(source_sym.n_value - source_sect.addr)),
- };
- }
- const nbase = @as(u32, @intCast(object.in_symtab.?.len));
- const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
- const source_sect = object.getSourceSection(sect_id);
- return .{
- .base_addr = @as(i64, @intCast(source_sect.addr)),
- .base_offset = 0,
- };
-}
-
-pub fn parseRelocTarget(zld: *Zld, ctx: struct {
- object_id: u32,
- rel: macho.relocation_info,
- code: []const u8,
- base_addr: i64 = 0,
- base_offset: i32 = 0,
-}) SymbolWithLoc {
- const tracy = trace(@src());
- defer tracy.end();
-
- const object = &zld.objects.items[ctx.object_id];
- log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
-
- const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
- const sect_id = @as(u8, @intCast(ctx.rel.r_symbolnum - 1));
- const rel_offset = @as(u32, @intCast(ctx.rel.r_address - ctx.base_offset));
-
- const address_in_section = if (ctx.rel.r_pcrel == 0) blk: {
- break :blk if (ctx.rel.r_length == 3)
- mem.readIntLittle(u64, ctx.code[rel_offset..][0..8])
- else
- mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
- } else blk: {
- assert(zld.options.target.cpu.arch == .x86_64);
- const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
- .X86_64_RELOC_SIGNED => 0,
- .X86_64_RELOC_SIGNED_1 => 1,
- .X86_64_RELOC_SIGNED_2 => 2,
- .X86_64_RELOC_SIGNED_4 => 4,
- else => unreachable,
- };
- const addend = mem.readIntLittle(i32, ctx.code[rel_offset..][0..4]);
- const target_address = @as(i64, @intCast(ctx.base_addr)) + ctx.rel.r_address + 4 + correction + addend;
- break :blk @as(u64, @intCast(target_address));
- };
-
- // Find containing atom
- log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
- break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
- } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
-
- const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
- const sym = zld.getSymbol(sym_loc);
- const target = if (sym.sect() and !sym.ext())
- sym_loc
- else if (object.getGlobal(sym_index)) |global_index|
- zld.globals.items[global_index]
- else
- sym_loc;
- log.debug(" | target %{d} ('{s}') in object({?d})", .{
- target.sym_index,
- zld.getSymbolName(target),
- target.getFile(),
- });
- return target;
-}
-
-pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?AtomIndex {
- if (is_via_got) {
- return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
- }
- if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
- if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
-
- if (target.getFile() == null) {
- const target_sym_name = zld.getSymbolName(target);
- if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
- if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
-
- unreachable; // referenced symbol not found
- }
-
- const object = zld.objects.items[target.getFile().?];
- return object.getAtomIndexForSymbol(target.sym_index);
-}
-
-fn scanAtomRelocsArm64(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
- for (relocs) |rel| {
- const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
-
- switch (rel_type) {
- .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
- else => {},
- }
-
- if (rel.r_extern == 0) continue;
-
- const atom = zld.getAtom(atom_index);
- const object = &zld.objects.items[atom.getFile().?];
- const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
- const sym_loc = SymbolWithLoc{
- .sym_index = sym_index,
- .file = atom.file,
- };
-
- const target = if (object.getGlobal(sym_index)) |global_index|
- zld.globals.items[global_index]
- else
- sym_loc;
-
- switch (rel_type) {
- .ARM64_RELOC_BRANCH26 => {
- // TODO rewrite relocation
- try addStub(zld, target);
- },
- .ARM64_RELOC_GOT_LOAD_PAGE21,
- .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
- .ARM64_RELOC_POINTER_TO_GOT,
- => {
- // TODO rewrite relocation
- try addGotEntry(zld, target);
- },
- .ARM64_RELOC_TLVP_LOAD_PAGE21,
- .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
- => {
- try addTlvPtrEntry(zld, target);
- },
- else => {},
- }
- }
-}
-
-fn scanAtomRelocsX86(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
- for (relocs) |rel| {
- const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
-
- switch (rel_type) {
- .X86_64_RELOC_SUBTRACTOR => continue,
- else => {},
- }
-
- if (rel.r_extern == 0) continue;
-
- const atom = zld.getAtom(atom_index);
- const object = &zld.objects.items[atom.getFile().?];
- const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
- const sym_loc = SymbolWithLoc{
- .sym_index = sym_index,
- .file = atom.file,
- };
-
- const target = if (object.getGlobal(sym_index)) |global_index|
- zld.globals.items[global_index]
- else
- sym_loc;
-
- switch (rel_type) {
- .X86_64_RELOC_BRANCH => {
- // TODO rewrite relocation
- try addStub(zld, target);
- },
- .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
- // TODO rewrite relocation
- try addGotEntry(zld, target);
- },
- .X86_64_RELOC_TLV => {
- try addTlvPtrEntry(zld, target);
- },
- else => {},
- }
- }
-}
-
-fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
- const target_sym = zld.getSymbol(target);
- if (!target_sym.undf()) return;
- if (zld.tlv_ptr_table.contains(target)) return;
-
- const gpa = zld.gpa;
- const atom_index = try zld.createTlvPtrAtom();
- const tlv_ptr_index = @as(u32, @intCast(zld.tlv_ptr_entries.items.len));
- try zld.tlv_ptr_entries.append(gpa, .{
- .target = target,
- .atom_index = atom_index,
- });
- try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
-}
-
-pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
- if (zld.got_table.contains(target)) return;
- const gpa = zld.gpa;
- const atom_index = try zld.createGotAtom();
- const got_index = @as(u32, @intCast(zld.got_entries.items.len));
- try zld.got_entries.append(gpa, .{
- .target = target,
- .atom_index = atom_index,
- });
- try zld.got_table.putNoClobber(gpa, target, got_index);
-}
-
-pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
- const target_sym = zld.getSymbol(target);
- if (!target_sym.undf()) return;
- if (zld.stubs_table.contains(target)) return;
-
- const gpa = zld.gpa;
- _ = try zld.createStubHelperAtom();
- _ = try zld.createLazyPointerAtom();
- const atom_index = try zld.createStubAtom();
- const stubs_index = @as(u32, @intCast(zld.stubs.items.len));
- try zld.stubs.append(gpa, .{
- .target = target,
- .atom_index = atom_index,
- });
- try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
-}
-
-pub fn resolveRelocs(
- zld: *Zld,
- atom_index: AtomIndex,
- atom_code: []u8,
- atom_relocs: []align(1) const macho.relocation_info,
-) !void {
- const arch = zld.options.target.cpu.arch;
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null); // synthetic atoms do not have relocs
-
- log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
- atom.sym_index,
- zld.getSymbolName(atom.getSymbolWithLoc()),
- });
-
- const ctx = getRelocContext(zld, atom_index);
-
- return switch (arch) {
- .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
- .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
- else => unreachable,
- };
-}
-
-pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
- const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
- // If there is no atom for target, we still need to check for special, atom-less
- // symbols such as `___dso_handle`.
- const target_name = zld.getSymbolName(target);
- const atomless_sym = zld.getSymbol(target);
- log.debug(" | atomless target '{s}'", .{target_name});
- return atomless_sym.n_value;
- };
- const target_atom = zld.getAtom(target_atom_index);
- log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
- target_atom.sym_index,
- zld.getSymbolName(target_atom.getSymbolWithLoc()),
- target_atom.getFile(),
- });
-
- const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
- assert(target_sym.n_desc != @import("zld.zig").N_DEAD);
-
- // If `target` is contained within the target atom, pull its address value.
- const offset = if (target_atom.getFile() != null) blk: {
- const object = zld.objects.items[target_atom.getFile().?];
- break :blk if (object.getSourceSymbol(target.sym_index)) |_|
- Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
- else
- 0; // section alias
- } else 0;
- const base_address: u64 = if (is_tlv) base_address: {
- // For TLV relocations, the value specified as a relocation is the displacement from the
- // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
- // defined TLV template init section in the following order:
- // * wrt to __thread_data if defined, then
- // * wrt to __thread_bss
- const sect_id: u16 = sect_id: {
- if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
- break :sect_id i;
- } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
- break :sect_id i;
- } else {
- log.err("threadlocal variables present but no initializer sections found", .{});
- log.err(" __thread_data not found", .{});
- log.err(" __thread_bss not found", .{});
- return error.FailedToResolveRelocationTarget;
- }
- };
- break :base_address zld.sections.items(.header)[sect_id].addr;
- } else 0;
- return target_sym.n_value + offset - base_address;
-}
-
-fn resolveRelocsArm64(
- zld: *Zld,
- atom_index: AtomIndex,
- atom_code: []u8,
- atom_relocs: []align(1) const macho.relocation_info,
- context: RelocContext,
-) !void {
- const atom = zld.getAtom(atom_index);
- const object = zld.objects.items[atom.getFile().?];
-
- var addend: ?i64 = null;
- var subtractor: ?SymbolWithLoc = null;
-
- for (atom_relocs) |rel| {
- const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
-
- switch (rel_type) {
- .ARM64_RELOC_ADDEND => {
- assert(addend == null);
-
- log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
-
- addend = rel.r_symbolnum;
- continue;
- },
- .ARM64_RELOC_SUBTRACTOR => {
- assert(subtractor == null);
-
- log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
- @tagName(rel_type),
- rel.r_address,
- rel.r_symbolnum,
- atom.getFile(),
- });
-
- subtractor = parseRelocTarget(zld, .{
- .object_id = atom.getFile().?,
- .rel = rel,
- .code = atom_code,
- .base_addr = context.base_addr,
- .base_offset = context.base_offset,
- });
- continue;
- },
- else => {},
- }
-
- const target = parseRelocTarget(zld, .{
- .object_id = atom.getFile().?,
- .rel = rel,
- .code = atom_code,
- .base_addr = context.base_addr,
- .base_offset = context.base_offset,
- });
- const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
-
- log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
- @tagName(rel_type),
- rel.r_address,
- target.sym_index,
- zld.getSymbolName(target),
- target.getFile(),
- });
-
- const source_addr = blk: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
- break :blk source_sym.n_value + rel_offset;
- };
- const is_via_got = relocRequiresGot(zld, rel);
- const is_tlv = is_tlv: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
- const header = zld.sections.items(.header)[source_sym.n_sect - 1];
- break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
- };
- const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
-
- log.debug(" | source_addr = 0x{x}", .{source_addr});
-
- switch (rel_type) {
- .ARM64_RELOC_BRANCH26 => {
- const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
- const stub_atom = zld.getAtom(stub_atom_index);
- break :inner stub_atom.getSymbolWithLoc();
- } else target;
- log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
- zld.getSymbolName(atom.getSymbolWithLoc()),
- atom.getFile(),
- zld.getSymbolName(target),
- zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
- });
-
- const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
- source_addr,
- zld.getSymbol(actual_target).n_value,
- )) |disp| blk: {
- log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
- break :blk disp;
- } else |_| blk: {
- const thunk_index = zld.thunk_table.get(atom_index).?;
- const thunk = zld.thunks.items[thunk_index];
- const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
- zld,
- actual_target,
- ).?);
- log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
- break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
- };
-
- const code = atom_code[rel_offset..][0..4];
- var inst = aarch64.Instruction{
- .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.unconditional_branch_immediate,
- ), code),
- };
- inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(displacement >> 2))));
- mem.writeIntLittle(u32, code, inst.toU32());
- },
-
- .ARM64_RELOC_PAGE21,
- .ARM64_RELOC_GOT_LOAD_PAGE21,
- .ARM64_RELOC_TLVP_LOAD_PAGE21,
- => {
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
-
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
-
- const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)));
- const code = atom_code[rel_offset..][0..4];
- var inst = aarch64.Instruction{
- .pc_relative_address = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.pc_relative_address,
- ), code),
- };
- inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
- inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
- mem.writeIntLittle(u32, code, inst.toU32());
- addend = null;
- },
-
- .ARM64_RELOC_PAGEOFF12 => {
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
-
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
-
- const code = atom_code[rel_offset..][0..4];
- if (Relocation.isArithmeticOp(code)) {
- const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
- var inst = aarch64.Instruction{
- .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.add_subtract_immediate,
- ), code),
- };
- inst.add_subtract_immediate.imm12 = off;
- mem.writeIntLittle(u32, code, inst.toU32());
- } else {
- var inst = aarch64.Instruction{
- .load_store_register = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.load_store_register,
- ), code),
- };
- const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
- 0 => if (inst.load_store_register.v == 1)
- Relocation.PageOffsetInstKind.load_store_128
- else
- Relocation.PageOffsetInstKind.load_store_8,
- 1 => .load_store_16,
- 2 => .load_store_32,
- 3 => .load_store_64,
- });
- inst.load_store_register.offset = off;
- mem.writeIntLittle(u32, code, inst.toU32());
- }
- addend = null;
- },
-
- .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
- const code = atom_code[rel_offset..][0..4];
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
-
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
-
- const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
- var inst: aarch64.Instruction = .{
- .load_store_register = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.load_store_register,
- ), code),
- };
- inst.load_store_register.offset = off;
- mem.writeIntLittle(u32, code, inst.toU32());
- addend = null;
- },
-
- .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
- const code = atom_code[rel_offset..][0..4];
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
-
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
-
- const RegInfo = struct {
- rd: u5,
- rn: u5,
- size: u2,
- };
- const reg_info: RegInfo = blk: {
- if (Relocation.isArithmeticOp(code)) {
- const inst = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.add_subtract_immediate,
- ), code);
- break :blk .{
- .rd = inst.rd,
- .rn = inst.rn,
- .size = inst.sf,
- };
- } else {
- const inst = mem.bytesToValue(meta.TagPayload(
- aarch64.Instruction,
- aarch64.Instruction.load_store_register,
- ), code);
- break :blk .{
- .rd = inst.rt,
- .rn = inst.rn,
- .size = inst.size,
- };
- }
- };
-
- var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
- .load_store_register = .{
- .rt = reg_info.rd,
- .rn = reg_info.rn,
- .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
- .opc = 0b01,
- .op1 = 0b01,
- .v = 0,
- .size = reg_info.size,
- },
- } else aarch64.Instruction{
- .add_subtract_immediate = .{
- .rd = reg_info.rd,
- .rn = reg_info.rn,
- .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
- .sh = 0,
- .s = 0,
- .op = 0,
- .sf = @as(u1, @truncate(reg_info.size)),
- },
- };
- mem.writeIntLittle(u32, code, inst.toU32());
- addend = null;
- },
-
- .ARM64_RELOC_POINTER_TO_GOT => {
- log.debug(" | target_addr = 0x{x}", .{target_addr});
- const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
- return error.Overflow;
- mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result)));
- },
-
- .ARM64_RELOC_UNSIGNED => {
- var ptr_addend = if (rel.r_length == 3)
- mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
- else
- mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
-
- if (rel.r_extern == 0) {
- const base_addr = if (target.sym_index >= object.source_address_lookup.len)
- @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
- else
- object.source_address_lookup[target.sym_index];
- ptr_addend -= base_addr;
- }
-
- const result = blk: {
- if (subtractor) |sub| {
- const sym = zld.getSymbol(sub);
- break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
- } else {
- break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
- }
- };
- log.debug(" | target_addr = 0x{x}", .{result});
-
- if (rel.r_length == 3) {
- mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
- } else {
- mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
- }
-
- subtractor = null;
- },
-
- .ARM64_RELOC_ADDEND => unreachable,
- .ARM64_RELOC_SUBTRACTOR => unreachable,
- }
- }
-}
-
-fn resolveRelocsX86(
- zld: *Zld,
- atom_index: AtomIndex,
- atom_code: []u8,
- atom_relocs: []align(1) const macho.relocation_info,
- context: RelocContext,
-) !void {
- const atom = zld.getAtom(atom_index);
- const object = zld.objects.items[atom.getFile().?];
-
- var subtractor: ?SymbolWithLoc = null;
-
- for (atom_relocs) |rel| {
- const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
-
- switch (rel_type) {
- .X86_64_RELOC_SUBTRACTOR => {
- assert(subtractor == null);
-
- log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
- @tagName(rel_type),
- rel.r_address,
- rel.r_symbolnum,
- atom.getFile(),
- });
-
- subtractor = parseRelocTarget(zld, .{
- .object_id = atom.getFile().?,
- .rel = rel,
- .code = atom_code,
- .base_addr = context.base_addr,
- .base_offset = context.base_offset,
- });
- continue;
- },
- else => {},
- }
-
- const target = parseRelocTarget(zld, .{
- .object_id = atom.getFile().?,
- .rel = rel,
- .code = atom_code,
- .base_addr = context.base_addr,
- .base_offset = context.base_offset,
- });
- const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
-
- log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
- @tagName(rel_type),
- rel.r_address,
- target.sym_index,
- zld.getSymbolName(target),
- target.getFile(),
- });
-
- const source_addr = blk: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
- break :blk source_sym.n_value + rel_offset;
- };
- const is_via_got = relocRequiresGot(zld, rel);
- const is_tlv = is_tlv: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
- const header = zld.sections.items(.header)[source_sym.n_sect - 1];
- break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
- };
-
- log.debug(" | source_addr = 0x{x}", .{source_addr});
-
- const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
-
- switch (rel_type) {
- .X86_64_RELOC_BRANCH => {
- const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
- const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
- mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
- },
-
- .X86_64_RELOC_GOT,
- .X86_64_RELOC_GOT_LOAD,
- => {
- const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
- const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
- mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
- },
-
- .X86_64_RELOC_TLV => {
- const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
- const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
-
- if (zld.tlv_ptr_table.get(target) == null) {
- // We need to rewrite the opcode from movq to leaq.
- atom_code[rel_offset - 2] = 0x8d;
- }
-
- mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
- },
-
- .X86_64_RELOC_SIGNED,
- .X86_64_RELOC_SIGNED_1,
- .X86_64_RELOC_SIGNED_2,
- .X86_64_RELOC_SIGNED_4,
- => {
- const correction: u3 = switch (rel_type) {
- .X86_64_RELOC_SIGNED => 0,
- .X86_64_RELOC_SIGNED_1 => 1,
- .X86_64_RELOC_SIGNED_2 => 2,
- .X86_64_RELOC_SIGNED_4 => 4,
- else => unreachable,
- };
- var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
-
- if (rel.r_extern == 0) {
- const base_addr = if (target.sym_index >= object.source_address_lookup.len)
- @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
- else
- object.source_address_lookup[target.sym_index];
- addend += @as(i32, @intCast(@as(i64, @intCast(context.base_addr)) + rel.r_address + 4 -
- @as(i64, @intCast(base_addr))));
- }
-
- const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
-
- log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
-
- const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
- mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
- },
-
- .X86_64_RELOC_UNSIGNED => {
- var addend = if (rel.r_length == 3)
- mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
- else
- mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
-
- if (rel.r_extern == 0) {
- const base_addr = if (target.sym_index >= object.source_address_lookup.len)
- @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
- else
- object.source_address_lookup[target.sym_index];
- addend -= base_addr;
- }
-
- const result = blk: {
- if (subtractor) |sub| {
- const sym = zld.getSymbol(sub);
- break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
- } else {
- break :blk @as(i64, @intCast(target_addr)) + addend;
- }
- };
- log.debug(" | target_addr = 0x{x}", .{result});
-
- if (rel.r_length == 3) {
- mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
- } else {
- mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
- }
-
- subtractor = null;
- },
-
- .X86_64_RELOC_SUBTRACTOR => unreachable,
- }
- }
-}
-
-pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
- const object = zld.objects.items[atom.getFile().?];
- const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
- // If there was no matching symbol present in the source symtab, this means
- // we are dealing with either an entire section, or part of it, but also
- // starting at the beginning.
- const nbase = @as(u32, @intCast(object.in_symtab.?.len));
- const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
- const source_sect = object.getSourceSection(sect_id);
- assert(!source_sect.isZerofill());
- const code = object.getSectionContents(source_sect);
- const code_len = @as(usize, @intCast(atom.size));
- return code[0..code_len];
- };
- const source_sect = object.getSourceSection(source_sym.n_sect - 1);
- assert(!source_sect.isZerofill());
- const code = object.getSectionContents(source_sect);
- const offset = @as(usize, @intCast(source_sym.n_value - source_sect.addr));
- const code_len = @as(usize, @intCast(atom.size));
- return code[offset..][0..code_len];
-}
-
-pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info {
- const atom = zld.getAtom(atom_index);
- assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
- const object = zld.objects.items[atom.getFile().?];
- const cache = object.relocs_lookup[atom.sym_index];
-
- const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
- break :blk source_sym.n_sect - 1;
- } else blk: {
- // If there was no matching symbol present in the source symtab, this means
- // we are dealing with either an entire section, or part of it, but also
- // starting at the beginning.
- const nbase = @as(u32, @intCast(object.in_symtab.?.len));
- const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
- break :blk sect_id;
- };
- const source_sect = object.getSourceSection(source_sect_id);
- assert(!source_sect.isZerofill());
- const relocs = object.getRelocs(source_sect_id);
- return relocs[cache.start..][0..cache.len];
-}
-
-pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
- switch (zld.options.target.cpu.arch) {
- .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
- .ARM64_RELOC_GOT_LOAD_PAGE21,
- .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
- .ARM64_RELOC_POINTER_TO_GOT,
- => return true,
- else => return false,
- },
- .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
- .X86_64_RELOC_GOT,
- .X86_64_RELOC_GOT_LOAD,
- => return true,
- else => return false,
- },
- else => unreachable,
- }
-}
diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig
index b2031590ed..21be34a214 100644
--- a/src/link/MachO/dead_strip.zig
+++ b/src/link/MachO/dead_strip.zig
@@ -10,7 +10,7 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const AtomIndex = @import("zld.zig").AtomIndex;
-const Atom = @import("ZldAtom.zig");
+const Atom = @import("Atom.zig");
const MachO = @import("../MachO.zig");
const SymbolWithLoc = MachO.SymbolWithLoc;
const SymbolResolver = MachO.SymbolResolver;
diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig
index 6ef7a79977..efbb07ac05 100644
--- a/src/link/MachO/eh_frame.zig
+++ b/src/link/MachO/eh_frame.zig
@@ -8,7 +8,7 @@ const log = std.log.scoped(.eh_frame);
const Allocator = mem.Allocator;
const AtomIndex = @import("zld.zig").AtomIndex;
-const Atom = @import("ZldAtom.zig");
+const Atom = @import("Atom.zig");
const MachO = @import("../MachO.zig");
const Relocation = @import("Relocation.zig");
const SymbolWithLoc = MachO.SymbolWithLoc;
diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig
index e5ef305077..b478fb4131 100644
--- a/src/link/MachO/thunks.zig
+++ b/src/link/MachO/thunks.zig
@@ -15,7 +15,7 @@ const mem = std.mem;
const aarch64 = @import("../../arch/aarch64/bits.zig");
const Allocator = mem.Allocator;
-const Atom = @import("ZldAtom.zig");
+const Atom = @import("Atom.zig");
const AtomIndex = @import("zld.zig").AtomIndex;
const MachO = @import("../MachO.zig");
const Relocation = @import("Relocation.zig");
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index d82e80a20a..67620711d3 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -21,7 +21,7 @@ const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");
-const Atom = @import("ZldAtom.zig");
+const Atom = @import("Atom.zig");
const Cache = std.Build.Cache;
const CodeSignature = @import("CodeSignature.zig");
const Compilation = @import("../../Compilation.zig");
@@ -247,7 +247,16 @@ pub const Zld = struct {
const gpa = self.gpa;
const index = @as(AtomIndex, @intCast(self.atoms.items.len));
const atom = try self.atoms.addOne(gpa);
- atom.* = Atom.empty;
+ atom.* = .{
+ .sym_index = 0,
+ .inner_sym_index = 0,
+ .inner_nsyms_trailing = 0,
+ .file = 0,
+ .size = 0,
+ .alignment = 0,
+ .prev_index = null,
+ .next_index = null,
+ };
atom.sym_index = sym_index;
atom.size = size;
atom.alignment = alignment;
@@ -3169,6 +3178,14 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
};
defer zld.deinit();
+ // Index 0 is always a null symbol.
+ try zld.locals.append(gpa, .{
+ .n_strx = 0,
+ .n_type = 0,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
try zld.strtab.buffer.append(gpa, 0);
// Positional arguments to the linker such as object files and static archives.