aboutsummaryrefslogtreecommitdiff
path: root/src/link
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2024-08-28 18:25:14 +0100
committermlugg <mlugg@mlugg.co.uk>2024-08-29 23:43:52 +0100
commitc62487da76b08a0dfb69fbf76501250ca065c140 (patch)
tree1ef46d45f3ff9edaade3805d9ab5a9910cb8571b /src/link
parentba8d3f69ca65738f27deea43e795f5e787a061f2 (diff)
downloadzig-c62487da76b08a0dfb69fbf76501250ca065c140.tar.gz
zig-c62487da76b08a0dfb69fbf76501250ca065c140.zip
compiler: avoid field/decl name conflicts
Most of the required renames here are net wins for readaibility, I'd say. The ones in `arch` are a little more verbose, but I think better. I didn't bother renaming the non-conflicting functions in `arch/arm/bits.zig` and `arch/aarch64/bits.zig`, since these backends are pretty bit-rotted anyway AIUI.
Diffstat (limited to 'src/link')
-rw-r--r--src/link/Elf.zig87
-rw-r--r--src/link/Elf/Atom.zig2
-rw-r--r--src/link/Elf/Thunk.zig144
-rw-r--r--src/link/Elf/thunks.zig234
-rw-r--r--src/link/MachO.zig130
-rw-r--r--src/link/MachO/Atom.zig2
-rw-r--r--src/link/MachO/Thunk.zig125
-rw-r--r--src/link/MachO/synthetic.zig2
-rw-r--r--src/link/MachO/thunks.zig218
9 files changed, 467 insertions, 477 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 3dca7eace3..e577b8d45a 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -3467,7 +3467,7 @@ fn updateSectionSizes(self: *Elf) !void {
if (atom_list.items.len == 0) continue;
// Create jump/branch range extenders if needed.
- try thunks.createThunks(shdr, @intCast(shndx), self);
+ try self.createThunks(shdr, @intCast(shndx));
}
}
@@ -5576,6 +5576,88 @@ fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
};
}
+fn createThunks(elf_file: *Elf, shdr: *elf.Elf64_Shdr, shndx: u32) !void {
+ const gpa = elf_file.base.comp.gpa;
+ const cpu_arch = elf_file.getTarget().cpu.arch;
+ // A branch will need an extender if its target is larger than
+ // `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
+ const max_distance = switch (cpu_arch) {
+ .aarch64 => 0x500_000,
+ .x86_64, .riscv64 => unreachable,
+ else => @panic("unhandled arch"),
+ };
+ const atoms = elf_file.sections.items(.atom_list)[shndx].items;
+ assert(atoms.len > 0);
+
+ for (atoms) |ref| {
+ elf_file.atom(ref).?.value = -1;
+ }
+
+ var i: usize = 0;
+ while (i < atoms.len) {
+ const start = i;
+ const start_atom = elf_file.atom(atoms[start]).?;
+ assert(start_atom.alive);
+ start_atom.value = try advanceSection(shdr, start_atom.size, start_atom.alignment);
+ i += 1;
+
+ while (i < atoms.len) : (i += 1) {
+ const atom_ptr = elf_file.atom(atoms[i]).?;
+ assert(atom_ptr.alive);
+ if (@as(i64, @intCast(atom_ptr.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance)
+ break;
+ atom_ptr.value = try advanceSection(shdr, atom_ptr.size, atom_ptr.alignment);
+ }
+
+ // Insert a thunk at the group end
+ const thunk_index = try elf_file.addThunk();
+ const thunk_ptr = elf_file.thunk(thunk_index);
+ thunk_ptr.output_section_index = shndx;
+
+ // Scan relocs in the group and create trampolines for any unreachable callsite
+ for (atoms[start..i]) |ref| {
+ const atom_ptr = elf_file.atom(ref).?;
+ const file_ptr = atom_ptr.file(elf_file).?;
+ log.debug("atom({}) {s}", .{ ref, atom_ptr.name(elf_file) });
+ for (atom_ptr.relocs(elf_file)) |rel| {
+ const is_reachable = switch (cpu_arch) {
+ .aarch64 => r: {
+ const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
+ if (r_type != .CALL26 and r_type != .JUMP26) break :r true;
+ const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
+ const target = elf_file.symbol(target_ref).?;
+ if (target.flags.has_plt) break :r false;
+ if (atom_ptr.output_section_index != target.output_section_index) break :r false;
+ const target_atom = target.atom(elf_file).?;
+ if (target_atom.value == -1) break :r false;
+ const saddr = atom_ptr.address(elf_file) + @as(i64, @intCast(rel.r_offset));
+ const taddr = target.address(.{}, elf_file);
+ _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse break :r false;
+ break :r true;
+ },
+ .x86_64, .riscv64 => unreachable,
+ else => @panic("unsupported arch"),
+ };
+ if (is_reachable) continue;
+ const target = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
+ try thunk_ptr.symbols.put(gpa, target, {});
+ }
+ atom_ptr.addExtra(.{ .thunk = thunk_index }, elf_file);
+ }
+
+ thunk_ptr.value = try advanceSection(shdr, thunk_ptr.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));
+
+ log.debug("thunk({d}) : {}", .{ thunk_index, thunk_ptr.fmt(elf_file) });
+ }
+}
+fn advanceSection(shdr: *elf.Elf64_Shdr, adv_size: u64, alignment: Atom.Alignment) !i64 {
+ const offset = alignment.forward(shdr.sh_size);
+ const padding = offset - shdr.sh_size;
+ shdr.sh_size += padding + adv_size;
+ shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1);
+ return @intCast(offset);
+}
+
const std = @import("std");
const build_options = @import("build_options");
const builtin = @import("builtin");
@@ -5598,7 +5680,6 @@ const musl = @import("../musl.zig");
const relocatable = @import("Elf/relocatable.zig");
const relocation = @import("Elf/relocation.zig");
const target_util = @import("../target.zig");
-const thunks = @import("Elf/thunks.zig");
const trace = @import("../tracy.zig").trace;
const synthetic_sections = @import("Elf/synthetic_sections.zig");
@@ -5636,7 +5717,7 @@ const PltGotSection = synthetic_sections.PltGotSection;
const SharedObject = @import("Elf/SharedObject.zig");
const Symbol = @import("Elf/Symbol.zig");
const StringTable = @import("StringTable.zig");
-const Thunk = thunks.Thunk;
+const Thunk = @import("Elf/Thunk.zig");
const Value = @import("../Value.zig");
const VerneedSection = synthetic_sections.VerneedSection;
const ZigObject = @import("Elf/ZigObject.zig");
diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig
index 10a7aa2b79..ef2301f1cd 100644
--- a/src/link/Elf/Atom.zig
+++ b/src/link/Elf/Atom.zig
@@ -2251,6 +2251,6 @@ const Fde = eh_frame.Fde;
const File = @import("file.zig").File;
const Object = @import("Object.zig");
const Symbol = @import("Symbol.zig");
-const Thunk = @import("thunks.zig").Thunk;
+const Thunk = @import("Thunk.zig");
const ZigObject = @import("ZigObject.zig");
const dev = @import("../../dev.zig");
diff --git a/src/link/Elf/Thunk.zig b/src/link/Elf/Thunk.zig
new file mode 100644
index 0000000000..389ba7ffed
--- /dev/null
+++ b/src/link/Elf/Thunk.zig
@@ -0,0 +1,144 @@
+value: i64 = 0,
+output_section_index: u32 = 0,
+symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{},
+output_symtab_ctx: Elf.SymtabCtx = .{},
+
+pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
+ thunk.symbols.deinit(allocator);
+}
+
+pub fn size(thunk: Thunk, elf_file: *Elf) usize {
+ const cpu_arch = elf_file.getTarget().cpu.arch;
+ return thunk.symbols.keys().len * trampolineSize(cpu_arch);
+}
+
+pub fn address(thunk: Thunk, elf_file: *Elf) i64 {
+ const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index];
+ return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
+}
+
+pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
+ const cpu_arch = elf_file.getTarget().cpu.arch;
+ return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
+}
+
+pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
+ switch (elf_file.getTarget().cpu.arch) {
+ .aarch64 => try aarch64.write(thunk, elf_file, writer),
+ .x86_64, .riscv64 => unreachable,
+ else => @panic("unhandled arch"),
+ }
+}
+
+pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
+ thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
+ for (thunk.symbols.keys()) |ref| {
+ const sym = elf_file.symbol(ref).?;
+ thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
+ }
+}
+
+pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
+ const cpu_arch = elf_file.getTarget().cpu.arch;
+ for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
+ const sym = elf_file.symbol(ref).?;
+ const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
+ elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
+ elf_file.strtab.appendSliceAssumeCapacity("$thunk");
+ elf_file.strtab.appendAssumeCapacity(0);
+ elf_file.symtab.items[ilocal] = .{
+ .st_name = st_name,
+ .st_info = elf.STT_FUNC,
+ .st_other = 0,
+ .st_shndx = @intCast(thunk.output_section_index),
+ .st_value = @intCast(thunk.targetAddress(ref, elf_file)),
+ .st_size = trampolineSize(cpu_arch),
+ };
+ }
+}
+
+fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
+ return switch (cpu_arch) {
+ .aarch64 => aarch64.trampoline_size,
+ .x86_64, .riscv64 => unreachable,
+ else => @panic("unhandled arch"),
+ };
+}
+
+pub fn format(
+ thunk: Thunk,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ _ = thunk;
+ _ = unused_fmt_string;
+ _ = options;
+ _ = writer;
+ @compileError("do not format Thunk directly");
+}
+
+pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) {
+ return .{ .data = .{
+ .thunk = thunk,
+ .elf_file = elf_file,
+ } };
+}
+
+const FormatContext = struct {
+ thunk: Thunk,
+ elf_file: *Elf,
+};
+
+fn format2(
+ ctx: FormatContext,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ _ = options;
+ _ = unused_fmt_string;
+ const thunk = ctx.thunk;
+ const elf_file = ctx.elf_file;
+ try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
+ for (thunk.symbols.keys()) |ref| {
+ const sym = elf_file.symbol(ref).?;
+ try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
+ }
+}
+
+pub const Index = u32;
+
+const aarch64 = struct {
+ fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
+ for (thunk.symbols.keys(), 0..) |ref, i| {
+ const sym = elf_file.symbol(ref).?;
+ const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size));
+ const taddr = sym.address(.{}, elf_file);
+ const pages = try util.calcNumberOfPages(saddr, taddr);
+ try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little);
+ const off: u12 = @truncate(@as(u64, @bitCast(taddr)));
+ try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little);
+ try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little);
+ }
+ }
+
+ const trampoline_size = 3 * @sizeOf(u32);
+
+ const util = @import("../aarch64.zig");
+ const Instruction = util.Instruction;
+};
+
+const assert = std.debug.assert;
+const elf = std.elf;
+const log = std.log.scoped(.link);
+const math = std.math;
+const mem = std.mem;
+const std = @import("std");
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const Elf = @import("../Elf.zig");
+const Symbol = @import("Symbol.zig");
+
+const Thunk = @This();
diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig
deleted file mode 100644
index bc534639da..0000000000
--- a/src/link/Elf/thunks.zig
+++ /dev/null
@@ -1,234 +0,0 @@
-pub fn createThunks(shdr: *elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void {
- const gpa = elf_file.base.comp.gpa;
- const cpu_arch = elf_file.getTarget().cpu.arch;
- const max_distance = maxAllowedDistance(cpu_arch);
- const atoms = elf_file.sections.items(.atom_list)[shndx].items;
- assert(atoms.len > 0);
-
- for (atoms) |ref| {
- elf_file.atom(ref).?.value = -1;
- }
-
- var i: usize = 0;
- while (i < atoms.len) {
- const start = i;
- const start_atom = elf_file.atom(atoms[start]).?;
- assert(start_atom.alive);
- start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment);
- i += 1;
-
- while (i < atoms.len) : (i += 1) {
- const atom = elf_file.atom(atoms[i]).?;
- assert(atom.alive);
- if (@as(i64, @intCast(atom.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance)
- break;
- atom.value = try advance(shdr, atom.size, atom.alignment);
- }
-
- // Insert a thunk at the group end
- const thunk_index = try elf_file.addThunk();
- const thunk = elf_file.thunk(thunk_index);
- thunk.output_section_index = shndx;
-
- // Scan relocs in the group and create trampolines for any unreachable callsite
- for (atoms[start..i]) |ref| {
- const atom = elf_file.atom(ref).?;
- const file = atom.file(elf_file).?;
- log.debug("atom({}) {s}", .{ ref, atom.name(elf_file) });
- for (atom.relocs(elf_file)) |rel| {
- const is_reachable = switch (cpu_arch) {
- .aarch64 => aarch64.isReachable(atom, rel, elf_file),
- .x86_64, .riscv64 => unreachable,
- else => @panic("unsupported arch"),
- };
- if (is_reachable) continue;
- const target = file.resolveSymbol(rel.r_sym(), elf_file);
- try thunk.symbols.put(gpa, target, {});
- }
- atom.addExtra(.{ .thunk = thunk_index }, elf_file);
- }
-
- thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));
-
- log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(elf_file) });
- }
-}
-
-fn advance(shdr: *elf.Elf64_Shdr, size: u64, alignment: Atom.Alignment) !i64 {
- const offset = alignment.forward(shdr.sh_size);
- const padding = offset - shdr.sh_size;
- shdr.sh_size += padding + size;
- shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1);
- return @intCast(offset);
-}
-
-/// A branch will need an extender if its target is larger than
-/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
-fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 {
- return switch (cpu_arch) {
- .aarch64 => 0x500_000,
- .x86_64, .riscv64 => unreachable,
- else => @panic("unhandled arch"),
- };
-}
-
-pub const Thunk = struct {
- value: i64 = 0,
- output_section_index: u32 = 0,
- symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{},
- output_symtab_ctx: Elf.SymtabCtx = .{},
-
- pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
- thunk.symbols.deinit(allocator);
- }
-
- pub fn size(thunk: Thunk, elf_file: *Elf) usize {
- const cpu_arch = elf_file.getTarget().cpu.arch;
- return thunk.symbols.keys().len * trampolineSize(cpu_arch);
- }
-
- pub fn address(thunk: Thunk, elf_file: *Elf) i64 {
- const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index];
- return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
- }
-
- pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
- const cpu_arch = elf_file.getTarget().cpu.arch;
- return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
- }
-
- pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
- switch (elf_file.getTarget().cpu.arch) {
- .aarch64 => try aarch64.write(thunk, elf_file, writer),
- .x86_64, .riscv64 => unreachable,
- else => @panic("unhandled arch"),
- }
- }
-
- pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
- thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
- for (thunk.symbols.keys()) |ref| {
- const sym = elf_file.symbol(ref).?;
- thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
- }
- }
-
- pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
- const cpu_arch = elf_file.getTarget().cpu.arch;
- for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
- const sym = elf_file.symbol(ref).?;
- const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
- elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
- elf_file.strtab.appendSliceAssumeCapacity("$thunk");
- elf_file.strtab.appendAssumeCapacity(0);
- elf_file.symtab.items[ilocal] = .{
- .st_name = st_name,
- .st_info = elf.STT_FUNC,
- .st_other = 0,
- .st_shndx = @intCast(thunk.output_section_index),
- .st_value = @intCast(thunk.targetAddress(ref, elf_file)),
- .st_size = trampolineSize(cpu_arch),
- };
- }
- }
-
- fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
- return switch (cpu_arch) {
- .aarch64 => aarch64.trampoline_size,
- .x86_64, .riscv64 => unreachable,
- else => @panic("unhandled arch"),
- };
- }
-
- pub fn format(
- thunk: Thunk,
- comptime unused_fmt_string: []const u8,
- options: std.fmt.FormatOptions,
- writer: anytype,
- ) !void {
- _ = thunk;
- _ = unused_fmt_string;
- _ = options;
- _ = writer;
- @compileError("do not format Thunk directly");
- }
-
- pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) {
- return .{ .data = .{
- .thunk = thunk,
- .elf_file = elf_file,
- } };
- }
-
- const FormatContext = struct {
- thunk: Thunk,
- elf_file: *Elf,
- };
-
- fn format2(
- ctx: FormatContext,
- comptime unused_fmt_string: []const u8,
- options: std.fmt.FormatOptions,
- writer: anytype,
- ) !void {
- _ = options;
- _ = unused_fmt_string;
- const thunk = ctx.thunk;
- const elf_file = ctx.elf_file;
- try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
- for (thunk.symbols.keys()) |ref| {
- const sym = elf_file.symbol(ref).?;
- try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
- }
- }
-
- pub const Index = u32;
-};
-
-const aarch64 = struct {
- fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool {
- const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
- if (r_type != .CALL26 and r_type != .JUMP26) return true;
- const file = atom.file(elf_file).?;
- const target_ref = file.resolveSymbol(rel.r_sym(), elf_file);
- const target = elf_file.symbol(target_ref).?;
- if (target.flags.has_plt) return false;
- if (atom.output_section_index != target.output_section_index) return false;
- const target_atom = target.atom(elf_file).?;
- if (target_atom.value == -1) return false;
- const saddr = atom.address(elf_file) + @as(i64, @intCast(rel.r_offset));
- const taddr = target.address(.{}, elf_file);
- _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse return false;
- return true;
- }
-
- fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
- for (thunk.symbols.keys(), 0..) |ref, i| {
- const sym = elf_file.symbol(ref).?;
- const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size));
- const taddr = sym.address(.{}, elf_file);
- const pages = try util.calcNumberOfPages(saddr, taddr);
- try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little);
- const off: u12 = @truncate(@as(u64, @bitCast(taddr)));
- try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little);
- try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little);
- }
- }
-
- const trampoline_size = 3 * @sizeOf(u32);
-
- const util = @import("../aarch64.zig");
- const Instruction = util.Instruction;
-};
-
-const assert = std.debug.assert;
-const elf = std.elf;
-const log = std.log.scoped(.link);
-const math = std.math;
-const mem = std.mem;
-const std = @import("std");
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const Elf = @import("../Elf.zig");
-const Symbol = @import("Symbol.zig");
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index fd77201739..27bfc9392e 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -64,10 +64,10 @@ stubs_helper: StubsHelperSection = .{},
objc_stubs: ObjcStubsSection = .{},
la_symbol_ptr: LaSymbolPtrSection = .{},
tlv_ptr: TlvPtrSection = .{},
-rebase: Rebase = .{},
-bind: Bind = .{},
-weak_bind: WeakBind = .{},
-lazy_bind: LazyBind = .{},
+rebase_section: Rebase = .{},
+bind_section: Bind = .{},
+weak_bind_section: WeakBind = .{},
+lazy_bind_section: LazyBind = .{},
export_trie: ExportTrie = .{},
unwind_info: UnwindInfo = .{},
data_in_code: DataInCode = .{},
@@ -324,10 +324,10 @@ pub fn deinit(self: *MachO) void {
self.stubs.deinit(gpa);
self.objc_stubs.deinit(gpa);
self.tlv_ptr.deinit(gpa);
- self.rebase.deinit(gpa);
- self.bind.deinit(gpa);
- self.weak_bind.deinit(gpa);
- self.lazy_bind.deinit(gpa);
+ self.rebase_section.deinit(gpa);
+ self.bind_section.deinit(gpa);
+ self.weak_bind_section.deinit(gpa);
+ self.lazy_bind_section.deinit(gpa);
self.export_trie.deinit(gpa);
self.unwind_info.deinit(gpa);
self.data_in_code.deinit(gpa);
@@ -2005,7 +2005,7 @@ fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void {
fn createThunksWorker(self: *MachO, sect_id: u8) void {
const tracy = trace(@src());
defer tracy.end();
- thunks.createThunks(sect_id, self) catch |err| {
+ self.createThunks(sect_id) catch |err| {
const header = self.sections.items(.header)[sect_id];
self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{
header.segName(),
@@ -2562,7 +2562,7 @@ fn updateLazyBindSizeWorker(self: *MachO) void {
defer tracy.end();
const doWork = struct {
fn doWork(macho_file: *MachO) !void {
- try macho_file.lazy_bind.updateSize(macho_file);
+ try macho_file.lazy_bind_section.updateSize(macho_file);
const sect_id = macho_file.stubs_helper_sect_index.?;
const out = &macho_file.sections.items(.out)[sect_id];
var stream = std.io.fixedBufferStream(out.items);
@@ -2585,9 +2585,9 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
data_in_code,
}) void {
const res = switch (tag) {
- .rebase => self.rebase.updateSize(self),
- .bind => self.bind.updateSize(self),
- .weak_bind => self.weak_bind.updateSize(self),
+ .rebase => self.rebase_section.updateSize(self),
+ .bind => self.bind_section.updateSize(self),
+ .weak_bind => self.weak_bind_section.updateSize(self),
.export_trie => self.export_trie.updateSize(self),
.data_in_code => self.data_in_code.updateSize(self),
};
@@ -2640,13 +2640,13 @@ fn writeDyldInfo(self: *MachO) !void {
var stream = std.io.fixedBufferStream(buffer);
const writer = stream.writer();
- try self.rebase.write(writer);
+ try self.rebase_section.write(writer);
try stream.seekTo(cmd.bind_off - base_off);
- try self.bind.write(writer);
+ try self.bind_section.write(writer);
try stream.seekTo(cmd.weak_bind_off - base_off);
- try self.weak_bind.write(writer);
+ try self.weak_bind_section.write(writer);
try stream.seekTo(cmd.lazy_bind_off - base_off);
- try self.lazy_bind.write(writer);
+ try self.lazy_bind_section.write(writer);
try stream.seekTo(cmd.export_off - base_off);
try self.export_trie.write(writer);
try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);
@@ -4602,7 +4602,6 @@ const load_commands = @import("MachO/load_commands.zig");
const relocatable = @import("MachO/relocatable.zig");
const tapi = @import("tapi.zig");
const target_util = @import("../target.zig");
-const thunks = @import("MachO/thunks.zig");
const trace = @import("../tracy.zig").trace;
const synthetic = @import("MachO/synthetic.zig");
@@ -4641,7 +4640,7 @@ const StringTable = @import("StringTable.zig");
const StubsSection = synthetic.StubsSection;
const StubsHelperSection = synthetic.StubsHelperSection;
const Symbol = @import("MachO/Symbol.zig");
-const Thunk = thunks.Thunk;
+const Thunk = @import("MachO/Thunk.zig");
const TlvPtrSection = synthetic.TlvPtrSection;
const Value = @import("../Value.zig");
const UnwindInfo = @import("MachO/UnwindInfo.zig");
@@ -5292,3 +5291,96 @@ pub const KernE = enum(u32) {
NOT_FOUND = 56,
_,
};
+
+fn createThunks(macho_file: *MachO, sect_id: u8) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+ const slice = macho_file.sections.slice();
+ const header = &slice.items(.header)[sect_id];
+ const thnks = &slice.items(.thunks)[sect_id];
+ const atoms = slice.items(.atoms)[sect_id].items;
+ assert(atoms.len > 0);
+
+ for (atoms) |ref| {
+ ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
+ }
+
+ var i: usize = 0;
+ while (i < atoms.len) {
+ const start = i;
+ const start_atom = atoms[start].getAtom(macho_file).?;
+ assert(start_atom.isAlive());
+ start_atom.value = advanceSection(header, start_atom.size, start_atom.alignment);
+ i += 1;
+
+ while (i < atoms.len and
+ header.size - start_atom.value < max_allowed_distance) : (i += 1)
+ {
+ const atom = atoms[i].getAtom(macho_file).?;
+ assert(atom.isAlive());
+ atom.value = advanceSection(header, atom.size, atom.alignment);
+ }
+
+ // Insert a thunk at the group end
+ const thunk_index = try macho_file.addThunk();
+ const thunk = macho_file.getThunk(thunk_index);
+ thunk.out_n_sect = sect_id;
+ try thnks.append(gpa, thunk_index);
+
+ // Scan relocs in the group and create trampolines for any unreachable callsite
+ try scanThunkRelocs(thunk_index, gpa, atoms[start..i], macho_file);
+ thunk.value = advanceSection(header, thunk.size(), .@"4");
+
+ log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
+ }
+}
+
+fn advanceSection(sect: *macho.section_64, adv_size: u64, alignment: Atom.Alignment) u64 {
+ const offset = alignment.forward(sect.size);
+ const padding = offset - sect.size;
+ sect.size += padding + adv_size;
+ sect.@"align" = @max(sect.@"align", alignment.toLog2Units());
+ return offset;
+}
+
+fn scanThunkRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const thunk = macho_file.getThunk(thunk_index);
+
+ for (atoms) |ref| {
+ const atom = ref.getAtom(macho_file).?;
+ log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
+ for (atom.getRelocs(macho_file)) |rel| {
+ if (rel.type != .branch) continue;
+ if (isReachable(atom, rel, macho_file)) continue;
+ try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
+ }
+ atom.addExtra(.{ .thunk = thunk_index }, macho_file);
+ }
+}
+
+fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
+ const target = rel.getTargetSymbol(atom.*, macho_file);
+ if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false;
+ if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
+ const target_atom = target.getAtom(macho_file).?;
+ if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
+ const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
+ const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
+ _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
+ return true;
+}
+
+/// Branch instruction has 26 bits immediate but is 4 byte aligned.
+const jump_bits = @bitSizeOf(i28);
+const max_distance = (1 << (jump_bits - 1));
+
+/// A branch will need an extender if its target is larger than
+/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
+/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
+/// and assume margin to be 5MiB.
+const max_allowed_distance = max_distance - 0x500_000;
diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig
index a0193c20be..d2a6a13497 100644
--- a/src/link/MachO/Atom.zig
+++ b/src/link/MachO/Atom.zig
@@ -1220,6 +1220,6 @@ const MachO = @import("../MachO.zig");
const Object = @import("Object.zig");
const Relocation = @import("Relocation.zig");
const Symbol = @import("Symbol.zig");
-const Thunk = @import("thunks.zig").Thunk;
+const Thunk = @import("Thunk.zig");
const UnwindInfo = @import("UnwindInfo.zig");
const dev = @import("../../dev.zig");
diff --git a/src/link/MachO/Thunk.zig b/src/link/MachO/Thunk.zig
new file mode 100644
index 0000000000..4a76a408ed
--- /dev/null
+++ b/src/link/MachO/Thunk.zig
@@ -0,0 +1,125 @@
+value: u64 = 0,
+out_n_sect: u8 = 0,
+symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
+output_symtab_ctx: MachO.SymtabCtx = .{},
+
+pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
+ thunk.symbols.deinit(allocator);
+}
+
+pub fn size(thunk: Thunk) usize {
+ return thunk.symbols.keys().len * trampoline_size;
+}
+
+pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 {
+ const header = macho_file.sections.items(.header)[thunk.out_n_sect];
+ return header.addr + thunk.value;
+}
+
+pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
+ return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
+}
+
+pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
+ for (thunk.symbols.keys(), 0..) |ref, i| {
+ const sym = ref.getSymbol(macho_file).?;
+ const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
+ const taddr = sym.getAddress(.{}, macho_file);
+ const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
+ try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
+ const off: u12 = @truncate(taddr);
+ try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
+ try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
+ }
+}
+
+pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void {
+ thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
+ for (thunk.symbols.keys()) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
+ thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1));
+ }
+}
+
+pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
+ var n_strx = thunk.output_symtab_ctx.stroff;
+ for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
+ const sym = ref.getSymbol(macho_file).?;
+ const name = sym.getName(macho_file);
+ const out_sym = &ctx.symtab.items[ilocal];
+ out_sym.n_strx = n_strx;
+ @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
+ n_strx += @intCast(name.len);
+ @memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk");
+ n_strx += @intCast("__thunk".len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
+ out_sym.n_type = macho.N_SECT;
+ out_sym.n_sect = @intCast(thunk.out_n_sect + 1);
+ out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file));
+ out_sym.n_desc = 0;
+ }
+}
+
+pub fn format(
+ thunk: Thunk,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ _ = thunk;
+ _ = unused_fmt_string;
+ _ = options;
+ _ = writer;
+ @compileError("do not format Thunk directly");
+}
+
+pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) {
+ return .{ .data = .{
+ .thunk = thunk,
+ .macho_file = macho_file,
+ } };
+}
+
+const FormatContext = struct {
+ thunk: Thunk,
+ macho_file: *MachO,
+};
+
+fn format2(
+ ctx: FormatContext,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ _ = options;
+ _ = unused_fmt_string;
+ const thunk = ctx.thunk;
+ const macho_file = ctx.macho_file;
+ try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
+ for (thunk.symbols.keys()) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
+ try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
+ }
+}
+
+const trampoline_size = 3 * @sizeOf(u32);
+
+pub const Index = u32;
+
+const aarch64 = @import("../aarch64.zig");
+const assert = std.debug.assert;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const std = @import("std");
+const trace = @import("../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const MachO = @import("../MachO.zig");
+const Relocation = @import("Relocation.zig");
+const Symbol = @import("Symbol.zig");
+
+const Thunk = @This();
diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig
index 35f9d45536..5c7ede387d 100644
--- a/src/link/MachO/synthetic.zig
+++ b/src/link/MachO/synthetic.zig
@@ -204,7 +204,7 @@ pub const StubsHelperSection = struct {
for (macho_file.stubs.symbols.items) |ref| {
const sym = ref.getSymbol(macho_file).?;
if (sym.flags.weak) continue;
- const offset = macho_file.lazy_bind.offsets.items[idx];
+ const offset = macho_file.lazy_bind_section.offsets.items[idx];
const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx);
const target: i64 = @intCast(sect.addr);
switch (cpu_arch) {
diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig
deleted file mode 100644
index 4248785c54..0000000000
--- a/src/link/MachO/thunks.zig
+++ /dev/null
@@ -1,218 +0,0 @@
-pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = macho_file.base.comp.gpa;
- const slice = macho_file.sections.slice();
- const header = &slice.items(.header)[sect_id];
- const thnks = &slice.items(.thunks)[sect_id];
- const atoms = slice.items(.atoms)[sect_id].items;
- assert(atoms.len > 0);
-
- for (atoms) |ref| {
- ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
- }
-
- var i: usize = 0;
- while (i < atoms.len) {
- const start = i;
- const start_atom = atoms[start].getAtom(macho_file).?;
- assert(start_atom.isAlive());
- start_atom.value = advance(header, start_atom.size, start_atom.alignment);
- i += 1;
-
- while (i < atoms.len and
- header.size - start_atom.value < max_allowed_distance) : (i += 1)
- {
- const atom = atoms[i].getAtom(macho_file).?;
- assert(atom.isAlive());
- atom.value = advance(header, atom.size, atom.alignment);
- }
-
- // Insert a thunk at the group end
- const thunk_index = try macho_file.addThunk();
- const thunk = macho_file.getThunk(thunk_index);
- thunk.out_n_sect = sect_id;
- try thnks.append(gpa, thunk_index);
-
- // Scan relocs in the group and create trampolines for any unreachable callsite
- try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file);
- thunk.value = advance(header, thunk.size(), .@"4");
-
- log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
- }
-}
-
-fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 {
- const offset = alignment.forward(sect.size);
- const padding = offset - sect.size;
- sect.size += padding + size;
- sect.@"align" = @max(sect.@"align", alignment.toLog2Units());
- return offset;
-}
-
-fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const thunk = macho_file.getThunk(thunk_index);
-
- for (atoms) |ref| {
- const atom = ref.getAtom(macho_file).?;
- log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
- for (atom.getRelocs(macho_file)) |rel| {
- if (rel.type != .branch) continue;
- if (isReachable(atom, rel, macho_file)) continue;
- try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
- }
- atom.addExtra(.{ .thunk = thunk_index }, macho_file);
- }
-}
-
-fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
- const target = rel.getTargetSymbol(atom.*, macho_file);
- if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false;
- if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
- const target_atom = target.getAtom(macho_file).?;
- if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
- const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
- const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
- _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
- return true;
-}
-
-pub const Thunk = struct {
- value: u64 = 0,
- out_n_sect: u8 = 0,
- symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
- output_symtab_ctx: MachO.SymtabCtx = .{},
-
- pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
- thunk.symbols.deinit(allocator);
- }
-
- pub fn size(thunk: Thunk) usize {
- return thunk.symbols.keys().len * trampoline_size;
- }
-
- pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 {
- const header = macho_file.sections.items(.header)[thunk.out_n_sect];
- return header.addr + thunk.value;
- }
-
- pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
- return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
- }
-
- pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
- for (thunk.symbols.keys(), 0..) |ref, i| {
- const sym = ref.getSymbol(macho_file).?;
- const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
- const taddr = sym.getAddress(.{}, macho_file);
- const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
- try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
- const off: u12 = @truncate(taddr);
- try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
- try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
- }
- }
-
- pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void {
- thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
- for (thunk.symbols.keys()) |ref| {
- const sym = ref.getSymbol(macho_file).?;
- thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1));
- }
- }
-
- pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
- var n_strx = thunk.output_symtab_ctx.stroff;
- for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
- const sym = ref.getSymbol(macho_file).?;
- const name = sym.getName(macho_file);
- const out_sym = &ctx.symtab.items[ilocal];
- out_sym.n_strx = n_strx;
- @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
- n_strx += @intCast(name.len);
- @memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk");
- n_strx += @intCast("__thunk".len);
- ctx.strtab.items[n_strx] = 0;
- n_strx += 1;
- out_sym.n_type = macho.N_SECT;
- out_sym.n_sect = @intCast(thunk.out_n_sect + 1);
- out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file));
- out_sym.n_desc = 0;
- }
- }
-
- pub fn format(
- thunk: Thunk,
- comptime unused_fmt_string: []const u8,
- options: std.fmt.FormatOptions,
- writer: anytype,
- ) !void {
- _ = thunk;
- _ = unused_fmt_string;
- _ = options;
- _ = writer;
- @compileError("do not format Thunk directly");
- }
-
- pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) {
- return .{ .data = .{
- .thunk = thunk,
- .macho_file = macho_file,
- } };
- }
-
- const FormatContext = struct {
- thunk: Thunk,
- macho_file: *MachO,
- };
-
- fn format2(
- ctx: FormatContext,
- comptime unused_fmt_string: []const u8,
- options: std.fmt.FormatOptions,
- writer: anytype,
- ) !void {
- _ = options;
- _ = unused_fmt_string;
- const thunk = ctx.thunk;
- const macho_file = ctx.macho_file;
- try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
- for (thunk.symbols.keys()) |ref| {
- const sym = ref.getSymbol(macho_file).?;
- try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
- }
- }
-
- const trampoline_size = 3 * @sizeOf(u32);
-
- pub const Index = u32;
-};
-
-/// Branch instruction has 26 bits immediate but is 4 byte aligned.
-const jump_bits = @bitSizeOf(i28);
-const max_distance = (1 << (jump_bits - 1));
-
-/// A branch will need an extender if its target is larger than
-/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
-/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
-/// and assume margin to be 5MiB.
-const max_allowed_distance = max_distance - 0x500_000;
-
-const aarch64 = @import("../aarch64.zig");
-const assert = std.debug.assert;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const std = @import("std");
-const trace = @import("../../tracy.zig").trace;
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const MachO = @import("../MachO.zig");
-const Relocation = @import("Relocation.zig");
-const Symbol = @import("Symbol.zig");