authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 19:44:32+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-17 19:44:32+01:00
log0f7b036eb75f52c69e8458bd12563a7ecf4c9237
tree1cf9707303ddbbe728551cfc16ad9678887bd47f
parent119fc318a753f57b55809e9256e823accba6b56a
parente5234c0e9ee1d60b7a87df8de0350fee2d4e6c55
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8281 from kubkon/macho-got-refactor

stage2+macho: refactor global offset table for incremental linker

6 files changed, 703 insertions(+), 546 deletions(-)

lib/std/macho.zig+8
......@@ -1422,6 +1422,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
14221422pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
14231423pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;
14241424
1425// An indirect symbol table entry is simply a 32bit index into the symbol table
1426// to the symbol that the pointer or stub is refering to. Unless it is for a
1427// non-lazy symbol pointer section for a defined symbol which strip(1) as
1428// removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
1429// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
1430pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000;
1431pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000;
1432
14251433// Codesign consts and structs taken from:
14261434// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html
14271435
src/codegen.zig+61-134
......@@ -2132,9 +2132,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21322132 if (inst.func.value()) |func_value| {
21332133 if (func_value.castTag(.function)) |func_payload| {
21342134 const func = func_payload.data;
2135 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
2136 const got = &text_segment.sections.items[macho_file.got_section_index.?];
2137 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2135 const got_addr = blk: {
2136 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
2137 const got = seg.sections.items[macho_file.got_section_index.?];
2138 break :blk got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2139 };
2140 log.debug("got_addr = 0x{x}", .{got_addr});
21382141 switch (arch) {
21392142 .x86_64 => {
21402143 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });
......@@ -2152,8 +2155,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21522155 const decl = func_payload.data;
21532156 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
21542157 defer self.bin_file.allocator.free(decl_name);
2155 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
2156 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|
2158 const already_defined = macho_file.lazy_imports.contains(decl_name);
2159 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
21572160 @intCast(u32, index)
21582161 else
21592162 try macho_file.addExternSymbol(decl_name);
......@@ -3111,7 +3114,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31113114 4, 8 => {
31123115 const offset = if (math.cast(i9, adj_off)) |imm|
31133116 Instruction.LoadStoreOffset.imm_post_index(-imm)
3114 else |_| Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
3117 else |_|
3118 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
31153119 const rn: Register = switch (arch) {
31163120 .aarch64, .aarch64_be => .x29,
31173121 .aarch64_32 => .w29,
......@@ -3302,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
33023306 },
33033307 .memory => |addr| {
33043308 if (self.bin_file.options.pie) {
3305 // For MachO, the binary, with the exception of object files, has to be a PIE.
3306 // Therefore we cannot load an absolute address.
3307 // Instead, we need to make use of PC-relative addressing.
3308 if (reg.id() == 0) { // x0 is special-cased
3309 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack
3310 // like described here:
3311 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)
3312 // str x28, [sp, #-16]
3313 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
3314 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),
3315 }).toU32());
3316 // adr x28, #8
3317 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3318 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3319 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3320 .address = addr,
3321 .start = self.code.items.len,
3322 .len = 4,
3323 });
3324 } else {
3325 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3326 }
3327 // b [label]
3328 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3329 // mov r, x0
3330 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3331 reg,
3332 .xzr,
3333 .x0,
3334 Instruction.Shift.none,
3335 ).toU32());
3336 // ldr x28, [sp], #16
3337 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
3338 .register = .{
3339 .rn = Register.sp,
3340 .offset = Instruction.LoadStoreOffset.imm_post_index(16),
3341 },
3342 }).toU32());
3309 // PC-relative displacement to the entry in the GOT table.
3310 // TODO we should come up with our own, backend independent relocation types
3311 // which each backend (Elf, MachO, etc.) would then translate into an actual
3312 // fixup when linking.
3313 // adrp reg, pages
3314 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3315 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3316 .target_addr = addr,
3317 .offset = self.code.items.len,
3318 .size = 4,
3319 });
33433320 } else {
3344 // stp x0, x28, [sp, #-16]
3345 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(
3346 .x0,
3347 .x28,
3348 Register.sp,
3349 Instruction.LoadStorePairOffset.pre_index(-16),
3350 ).toU32());
3351 // adr x28, #8
3352 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3353 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3354 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3355 .address = addr,
3356 .start = self.code.items.len,
3357 .len = 4,
3358 });
3359 } else {
3360 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3361 }
3362 // b [label]
3363 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3364 // mov r, x0
3365 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3366 reg,
3367 .xzr,
3368 .x0,
3369 Instruction.Shift.none,
3370 ).toU32());
3371 // ldp x0, x28, [sp, #16]
3372 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
3373 .x0,
3374 .x28,
3375 Register.sp,
3376 Instruction.LoadStorePairOffset.post_index(16),
3377 ).toU32());
3321 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
33783322 }
3323 mem.writeIntLittle(
3324 u32,
3325 try self.code.addManyAsArray(4),
3326 Instruction.adrp(reg, 0).toU32(),
3327 );
3328 // ldr reg, reg, offset
3329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
3330 .register = .{
3331 .rn = reg,
3332 .offset = Instruction.LoadStoreOffset.imm(0),
3333 },
3334 }).toU32());
33793335 } else {
33803336 // The value is in memory at a hard-coded address.
33813337 // If the type is a pointer, it means the pointer address is at this memory location.
......@@ -3559,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35593515 },
35603516 .memory => |x| {
35613517 if (self.bin_file.options.pie) {
3562 // For MachO, the binary, with the exception of object files, has to be a PIE.
3563 // Therefore, we cannot load an absolute address.
3564 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.
3565 // The plan here is to use unconditional relative jump to GOT entry, where we store
3566 // pre-calculated and stored effective address to load into the target register.
3567 // We leave the actual displacement information empty (0-padded) and fixing it up
3568 // later in the linker.
3569 if (reg.id() == 0) { // %rax is special-cased
3570 try self.code.ensureCapacity(self.code.items.len + 5);
3571 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3572 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3573 .address = x,
3574 .start = self.code.items.len,
3575 .len = 5,
3576 });
3577 } else {
3578 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3579 }
3580 // call [label]
3581 self.code.appendSliceAssumeCapacity(&[_]u8{
3582 0xE8,
3583 0x0,
3584 0x0,
3585 0x0,
3586 0x0,
3518 // RIP-relative displacement to the entry in the GOT table.
3519 // TODO we should come up with our own, backend independent relocation types
3520 // which each backend (Elf, MachO, etc.) would then translate into an actual
3521 // fixup when linking.
3522 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3523 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3524 .target_addr = x,
3525 .offset = self.code.items.len + 3,
3526 .size = 4,
35873527 });
35883528 } else {
3589 try self.code.ensureCapacity(self.code.items.len + 10);
3590 // push %rax
3591 self.code.appendSliceAssumeCapacity(&[_]u8{0x50});
3592 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3593 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3594 .address = x,
3595 .start = self.code.items.len,
3596 .len = 5,
3597 });
3598 } else {
3599 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3600 }
3601 // call [label]
3602 self.code.appendSliceAssumeCapacity(&[_]u8{
3603 0xE8,
3604 0x0,
3605 0x0,
3606 0x0,
3607 0x0,
3608 });
3609 // mov %r, %rax
3610 self.code.appendSliceAssumeCapacity(&[_]u8{
3611 0x48,
3612 0x89,
3613 0xC0 | @as(u8, reg.id()),
3614 });
3615 // pop %rax
3616 self.code.appendSliceAssumeCapacity(&[_]u8{0x58});
3529 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
36173530 }
3531 try self.code.ensureCapacity(self.code.items.len + 7);
3532 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
3533 self.code.appendSliceAssumeCapacity(&[_]u8{
3534 0x8D,
3535 0x05 | (@as(u8, reg.id() & 0b111) << 3),
3536 });
3537 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), 0);
3538
3539 try self.code.ensureCapacity(self.code.items.len + 3);
3540 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
3541 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
3542 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
36183543 } else if (x <= math.maxInt(u32)) {
36193544 // Moving from memory to a register is a variant of `8B /r`.
36203545 // Since we're using 64-bit moves, we require a REX.
......@@ -3777,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37773702 return MCValue{ .memory = got_addr };
37783703 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
37793704 const decl = payload.data;
3780 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
3781 const got = &text_segment.sections.items[macho_file.got_section_index.?];
3782 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3705 const got_addr = blk: {
3706 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
3707 const got = seg.sections.items[macho_file.got_section_index.?];
3708 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3709 };
37833710 return MCValue{ .memory = got_addr };
37843711 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
37853712 const decl = payload.data;
src/link/MachO.zig+485-256
......@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");
1111const aarch64 = @import("../codegen/aarch64.zig");
1212const math = std.math;
1313const mem = std.mem;
14const meta = std.meta;
1415
16const bind = @import("MachO/bind.zig");
1517const trace = @import("../tracy.zig").trace;
1618const build_options = @import("build_options");
1719const Module = @import("../Module.zig");
......@@ -26,7 +28,6 @@ const Trie = @import("MachO/Trie.zig");
2628const CodeSignature = @import("MachO/CodeSignature.zig");
2729
2830usingnamespace @import("MachO/commands.zig");
29usingnamespace @import("MachO/imports.zig");
3031
3132pub const base_tag: File.Tag = File.Tag.macho;
3233
......@@ -87,14 +88,12 @@ code_signature_cmd_index: ?u16 = null,
8788
8889/// Index into __TEXT,__text section.
8990text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
9291/// Index into __TEXT,__stubs section.
9392stubs_section_index: ?u16 = null,
9493/// Index into __TEXT,__stub_helper section.
9594stub_helper_section_index: ?u16 = null,
9695/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,
96got_section_index: ?u16 = null,
9897/// Index into __DATA,__la_symbol_ptr section.
9998la_symbol_ptr_section_index: ?u16 = null,
10099/// Index into __DATA,__data section.
......@@ -104,16 +103,16 @@ entry_addr: ?u64 = null,
104103
105104/// Table of all local symbols
106105/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
106locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108107/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110109/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
110nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
112111/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
114113
115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
114locals_free_list: std.ArrayListUnmanaged(u32) = .{},
115globals_free_list: std.ArrayListUnmanaged(u32) = .{},
117116offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
118117
119118stub_helper_stubs_start_off: ?u64 = null,
......@@ -122,8 +121,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122121string_table: std.ArrayListUnmanaged(u8) = .{},
123122string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124123
125/// Table of trampolines to the actual symbols in __text section.
126offset_table: std.ArrayListUnmanaged(u64) = .{},
124/// Table of GOT entries.
125offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127126
128127error_flags: File.ErrorFlags = File.ErrorFlags{},
129128
......@@ -154,14 +153,19 @@ string_table_needs_relocation: bool = false,
154153/// allocate a fresh text block, which will have ideal capacity, and then grow it
155154/// by 1 byte. It will then have -1 overcapacity.
156155text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
156
157157/// Pointer to the last allocated text block
158158last_text_block: ?*TextBlock = null,
159
159160/// A list of all PIE fixups required for this run of the linker.
160161/// Warning, this is currently NOT thread-safe. See the TODO below.
161162/// TODO Move this list inside `updateDecl` where it should be allocated
162163/// prior to calling `generateSymbol`, and then immediately deallocated
163164/// rather than sitting in the global scope.
164pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
165/// TODO We should also rewrite this using generic relocations common to all
166/// backends.
167pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
168
165169/// A list of all stub (extern decls) fixups required for this run of the linker.
166170/// Warning, this is currently NOT thread-safe. See the TODO below.
167171/// TODO Move this list inside `updateDecl` where it should be allocated
......@@ -169,14 +173,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169173/// rather than sitting in the global scope.
170174stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171175
172pub const PieFixup = struct {
173 /// Target address we wanted to address in absolute terms.
174 address: u64,
175 /// Where in the byte stream we should perform the fixup.
176 start: usize,
177 /// The length of the byte stream. For x86_64, this will be
178 /// variable. For aarch64, it will be fixed at 4 bytes.
179 len: usize,
176pub const GOTEntry = struct {
177 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
178 kind: enum {
179 Local,
180 Extern,
181 },
182
183 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
184 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
185 /// rather than 4 as we currently do, but I'll follow up in the future PR.
186 symbol: u32,
187
188 /// Index of this entry in the GOT.
189 index: u32,
190};
191
192pub const Import = struct {
193 /// MachO symbol table entry.
194 symbol: macho.nlist_64,
195
196 /// Id of the dynamic library where the specified entries can be found.
197 dylib_ordinal: i64,
198
199 /// Index of this import within the import list.
200 index: u32,
201};
202
203pub const PIEFixup = struct {
204 /// Target VM address of this relocation.
205 target_addr: u64,
206
207 /// Offset within the byte stream.
208 offset: usize,
209
210 /// Size of the relocation.
211 size: usize,
180212};
181213
182214pub const StubFixup = struct {
......@@ -260,9 +292,9 @@ pub const TextBlock = struct {
260292 /// File offset relocation happens transparently, so it is not included in
261293 /// this calculation.
262294 fn capacity(self: TextBlock, macho_file: MachO) u64 {
263 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
295 const self_sym = macho_file.locals.items[self.local_sym_index];
264296 if (self.next) |next| {
265 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
297 const next_sym = macho_file.locals.items[next.local_sym_index];
266298 return next_sym.n_value - self_sym.n_value;
267299 } else {
268300 // We are the last block.
......@@ -274,8 +306,8 @@ pub const TextBlock = struct {
274306 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
275307 // No need to keep a free list node for the last block.
276308 const next = self.next orelse return false;
277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
309 const self_sym = macho_file.locals.items[self.local_sym_index];
310 const next_sym = macho_file.locals.items[next.local_sym_index];
279311 const cap = next_sym.n_value - self_sym.n_value;
280312 const ideal_cap = padToIdeal(self.size);
281313 if (cap <= ideal_cap) return false;
......@@ -344,7 +376,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
344376 };
345377
346378 // Index 0 is always a null symbol.
347 try self.local_symbols.append(allocator, .{
379 try self.locals.append(allocator, .{
348380 .n_strx = 0,
349381 .n_type = 0,
350382 .n_sect = 0,
......@@ -834,7 +866,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
834866 }
835867 },
836868 else => {
837 log.err("{s} terminated", .{ argv.items[0] });
869 log.err("{s} terminated", .{argv.items[0]});
838870 return error.LLDCrashed;
839871 },
840872 }
......@@ -1019,14 +1051,14 @@ pub fn deinit(self: *MachO) void {
10191051 if (self.d_sym) |*ds| {
10201052 ds.deinit(self.base.allocator);
10211053 }
1022 for (self.extern_lazy_symbols.items()) |*entry| {
1054 for (self.lazy_imports.items()) |*entry| {
10231055 self.base.allocator.free(entry.key);
10241056 }
1025 self.extern_lazy_symbols.deinit(self.base.allocator);
1026 for (self.extern_nonlazy_symbols.items()) |*entry| {
1057 self.lazy_imports.deinit(self.base.allocator);
1058 for (self.nonlazy_imports.items()) |*entry| {
10271059 self.base.allocator.free(entry.key);
10281060 }
1029 self.extern_nonlazy_symbols.deinit(self.base.allocator);
1061 self.nonlazy_imports.deinit(self.base.allocator);
10301062 self.pie_fixups.deinit(self.base.allocator);
10311063 self.stub_fixups.deinit(self.base.allocator);
10321064 self.text_block_free_list.deinit(self.base.allocator);
......@@ -1040,10 +1072,10 @@ pub fn deinit(self: *MachO) void {
10401072 }
10411073 self.string_table_directory.deinit(self.base.allocator);
10421074 self.string_table.deinit(self.base.allocator);
1043 self.global_symbols.deinit(self.base.allocator);
1044 self.global_symbol_free_list.deinit(self.base.allocator);
1045 self.local_symbols.deinit(self.base.allocator);
1046 self.local_symbol_free_list.deinit(self.base.allocator);
1075 self.globals.deinit(self.base.allocator);
1076 self.globals_free_list.deinit(self.base.allocator);
1077 self.locals.deinit(self.base.allocator);
1078 self.locals_free_list.deinit(self.base.allocator);
10471079 for (self.load_commands.items) |*lc| {
10481080 lc.deinit(self.base.allocator);
10491081 }
......@@ -1098,7 +1130,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo
10981130}
10991131
11001132fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1101 const sym = self.local_symbols.items[text_block.local_sym_index];
1133 const sym = self.locals.items[text_block.local_sym_index];
11021134 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
11031135 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
11041136 if (!need_realloc) return sym.n_value;
......@@ -1108,34 +1140,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
11081140pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11091141 if (decl.link.macho.local_sym_index != 0) return;
11101142
1111 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
1143 try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1);
11121144 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
11131145
1114 if (self.local_symbol_free_list.popOrNull()) |i| {
1146 if (self.locals_free_list.popOrNull()) |i| {
11151147 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
11161148 decl.link.macho.local_sym_index = i;
11171149 } else {
1118 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });
1119 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1120 _ = self.local_symbols.addOneAssumeCapacity();
1150 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
1151 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
1152 _ = self.locals.addOneAssumeCapacity();
11211153 }
11221154
11231155 if (self.offset_table_free_list.popOrNull()) |i| {
1156 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
11241157 decl.link.macho.offset_table_index = i;
11251158 } else {
1159 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
11261160 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
11271161 _ = self.offset_table.addOneAssumeCapacity();
11281162 self.offset_table_count_dirty = true;
1163 self.rebase_info_dirty = true;
11291164 }
11301165
1131 self.local_symbols.items[decl.link.macho.local_sym_index] = .{
1166 self.locals.items[decl.link.macho.local_sym_index] = .{
11321167 .n_strx = 0,
11331168 .n_type = 0,
11341169 .n_sect = 0,
11351170 .n_desc = 0,
11361171 .n_value = 0,
11371172 };
1138 self.offset_table.items[decl.link.macho.offset_table_index] = 0;
1173 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1174 .kind = .Local,
1175 .symbol = decl.link.macho.local_sym_index,
1176 .index = decl.link.macho.offset_table_index,
1177 };
11391178}
11401179
11411180pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
......@@ -1178,8 +1217,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11781217 .externally_managed => |x| x,
11791218 .appended => code_buffer.items,
11801219 .fail => |em| {
1181 // Clear any PIE fixups and stub fixups for this decl.
1220 // Clear any PIE fixups for this decl.
11821221 self.pie_fixups.shrinkRetainingCapacity(0);
1222 // Clear any stub fixups for this decl.
11831223 self.stub_fixups.shrinkRetainingCapacity(0);
11841224 decl.analysis = .codegen_failure;
11851225 try module.failed_decls.put(module.gpa, decl, em);
......@@ -1189,7 +1229,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11891229
11901230 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
11911231 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
1192 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
1232 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
11931233
11941234 if (decl.link.macho.size != 0) {
11951235 const capacity = decl.link.macho.capacity(self.*);
......@@ -1198,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11981238 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
11991239 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
12001240 if (vaddr != symbol.n_value) {
1201 symbol.n_value = vaddr;
12021241 log.debug(" (writing new offset table entry)", .{});
1203 self.offset_table.items[decl.link.macho.offset_table_index] = vaddr;
1242 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1243 .kind = .Local,
1244 .symbol = decl.link.macho.local_sym_index,
1245 .index = decl.link.macho.offset_table_index,
1246 };
12041247 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12051248 }
12061249 } else if (code.len < decl.link.macho.size) {
......@@ -1229,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12291272 .n_desc = 0,
12301273 .n_value = addr,
12311274 };
1232 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
1275 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1276 .kind = .Local,
1277 .symbol = decl.link.macho.local_sym_index,
1278 .index = decl.link.macho.offset_table_index,
1279 };
12331280
12341281 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
12351282 if (self.d_sym) |*ds|
......@@ -1237,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12371284 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12381285 }
12391286
1240 // Perform PIE fixups (if any)
1241 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1242 const got_section = text_segment.sections.items[self.got_section_index.?];
1287 // Calculate displacements to target addr (if any).
12431288 while (self.pie_fixups.popOrNull()) |fixup| {
1244 const target_addr = fixup.address;
1245 const this_addr = symbol.n_value + fixup.start;
1289 assert(fixup.size == 4);
1290 const this_addr = symbol.n_value + fixup.offset;
1291 const target_addr = fixup.target_addr;
1292
12461293 switch (self.base.options.target.cpu.arch) {
12471294 .x86_64 => {
1248 assert(target_addr >= this_addr + fixup.len);
1249 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);
1250 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1251 mem.writeIntSliceLittle(u32, placeholder, displacement);
1295 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1296 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
12521297 },
12531298 .aarch64 => {
1254 assert(target_addr >= this_addr);
1255 const displacement = try math.cast(u27, target_addr - this_addr);
1256 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1257 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());
1299 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1300 {
1301 const inst = code_buffer.items[fixup.offset..][0..4];
1302 var parsed = mem.bytesAsValue(meta.TagPayload(
1303 aarch64.Instruction,
1304 aarch64.Instruction.PCRelativeAddress,
1305 ), inst);
1306 const this_page = @intCast(i32, this_addr >> 12);
1307 const target_page = @intCast(i32, target_addr >> 12);
1308 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1309 parsed.immhi = @truncate(u19, pages >> 2);
1310 parsed.immlo = @truncate(u2, pages);
1311 }
1312 {
1313 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
1314 var parsed = mem.bytesAsValue(meta.TagPayload(
1315 aarch64.Instruction,
1316 aarch64.Instruction.LoadStoreRegister,
1317 ), inst);
1318 const narrowed = @truncate(u12, target_addr);
1319 const offset = try math.divExact(u12, narrowed, 8);
1320 parsed.offset = offset;
1321 }
12581322 },
12591323 else => unreachable, // unsupported target architecture
12601324 }
12611325 }
12621326
12631327 // Resolve stubs (if any)
1328 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
12641329 const stubs = text_segment.sections.items[self.stubs_section_index.?];
12651330 for (self.stub_fixups.items) |fixup| {
12661331 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
......@@ -1285,9 +1350,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12851350 try self.writeStubInStubHelper(fixup.symbol);
12861351 try self.writeLazySymbolPointer(fixup.symbol);
12871352
1288 const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
1289 extern_sym.segment = self.data_segment_cmd_index.?;
1290 extern_sym.offset = fixup.symbol * @sizeOf(u64);
12911353 self.rebase_info_dirty = true;
12921354 self.lazy_binding_info_dirty = true;
12931355 }
......@@ -1329,9 +1391,9 @@ pub fn updateDeclExports(
13291391 const tracy = trace(@src());
13301392 defer tracy.end();
13311393
1332 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
1394 try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len);
13331395 if (decl.link.macho.local_sym_index == 0) return;
1334 const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index];
1396 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
13351397
13361398 for (exports) |exp| {
13371399 if (exp.options.section) |section_name| {
......@@ -1364,7 +1426,7 @@ pub fn updateDeclExports(
13641426 };
13651427 const n_type = decl_sym.n_type | macho.N_EXT;
13661428 if (exp.link.macho.sym_index) |i| {
1367 const sym = &self.global_symbols.items[i];
1429 const sym = &self.globals.items[i];
13681430 sym.* = .{
13691431 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
13701432 .n_type = n_type,
......@@ -1374,12 +1436,12 @@ pub fn updateDeclExports(
13741436 };
13751437 } else {
13761438 const name_str_index = try self.makeString(exp.options.name);
1377 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1378 _ = self.global_symbols.addOneAssumeCapacity();
1439 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1440 _ = self.globals.addOneAssumeCapacity();
13791441 self.export_info_dirty = true;
1380 break :blk self.global_symbols.items.len - 1;
1442 break :blk self.globals.items.len - 1;
13811443 };
1382 self.global_symbols.items[i] = .{
1444 self.globals.items[i] = .{
13831445 .n_strx = name_str_index,
13841446 .n_type = n_type,
13851447 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
......@@ -1394,18 +1456,18 @@ pub fn updateDeclExports(
13941456
13951457pub fn deleteExport(self: *MachO, exp: Export) void {
13961458 const sym_index = exp.sym_index orelse return;
1397 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
1398 self.global_symbols.items[sym_index].n_type = 0;
1459 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
1460 self.globals.items[sym_index].n_type = 0;
13991461}
14001462
14011463pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14021464 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
14031465 self.freeTextBlock(&decl.link.macho);
14041466 if (decl.link.macho.local_sym_index != 0) {
1405 self.local_symbol_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
1467 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
14061468 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};
14071469
1408 self.local_symbols.items[decl.link.macho.local_sym_index].n_type = 0;
1470 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
14091471
14101472 decl.link.macho.local_sym_index = 0;
14111473 }
......@@ -1413,7 +1475,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14131475
14141476pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
14151477 assert(decl.link.macho.local_sym_index != 0);
1416 return self.local_symbols.items[decl.link.macho.local_sym_index].n_value;
1478 return self.locals.items[decl.link.macho.local_sym_index].n_value;
14171479}
14181480
14191481pub fn populateMissingMetadata(self: *MachO) !void {
......@@ -1553,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15531615 self.header_dirty = true;
15541616 self.load_commands_dirty = true;
15551617 }
1556 if (self.got_section_index == null) {
1557 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1558 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
1559
1560 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1561 .x86_64 => 0,
1562 .aarch64 => 2,
1563 else => unreachable, // unhandled architecture type
1564 };
1565 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1566 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1567 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
1568 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
1569
1570 log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1571
1572 try text_segment.addSection(self.base.allocator, .{
1573 .sectname = makeStaticString("__ziggot"),
1574 .segname = makeStaticString("__TEXT"),
1575 .addr = text_segment.inner.vmaddr + off,
1576 .size = needed_size,
1577 .offset = @intCast(u32, off),
1578 .@"align" = alignment,
1579 .reloff = 0,
1580 .nreloc = 0,
1581 .flags = flags,
1582 .reserved1 = 0,
1583 .reserved2 = 0,
1584 .reserved3 = 0,
1585 });
1586 self.header_dirty = true;
1587 self.load_commands_dirty = true;
1588 }
15891618 if (self.stubs_section_index == null) {
15901619 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
15911620 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
......@@ -1597,7 +1626,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15971626 };
15981627 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
15991628 .x86_64 => 6,
1600 .aarch64 => 2 * @sizeOf(u32),
1629 .aarch64 => 3 * @sizeOf(u32),
16011630 else => unreachable, // unhandled architecture type
16021631 };
16031632 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
......@@ -1686,9 +1715,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16861715 self.header_dirty = true;
16871716 self.load_commands_dirty = true;
16881717 }
1689 if (self.data_got_section_index == null) {
1718 if (self.got_section_index == null) {
16901719 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1691 self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len);
1720 self.got_section_index = @intCast(u16, dc_segment.sections.items.len);
16921721
16931722 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
16941723 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
......@@ -2060,12 +2089,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20602089 self.header_dirty = true;
20612090 self.load_commands_dirty = true;
20622091 }
2063 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
2064 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
2092 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
2093 const index = @intCast(u32, self.nonlazy_imports.items().len);
20652094 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
20662095 const offset = try self.makeString("dyld_stub_binder");
2067 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2068 .inner = .{
2096 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
2097 .symbol = .{
20692098 .n_strx = offset,
20702099 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
20712100 .n_sect = 0,
......@@ -2073,68 +2102,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20732102 .n_value = 0,
20742103 },
20752104 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2076 .segment = self.data_const_segment_cmd_index.?,
2077 .offset = index * @sizeOf(u64),
2105 .index = index,
20782106 });
2107 const off_index = @intCast(u32, self.offset_table.items.len);
2108 try self.offset_table.append(self.base.allocator, .{
2109 .kind = .Extern,
2110 .symbol = index,
2111 .index = off_index,
2112 });
2113 try self.writeOffsetTableEntry(off_index);
20792114 self.binding_info_dirty = true;
20802115 }
20812116 if (self.stub_helper_stubs_start_off == null) {
2082 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2083 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2084 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2085 const data = &data_segment.sections.items[self.data_section_index.?];
2086 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2087 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2088 switch (self.base.options.target.cpu.arch) {
2089 .x86_64 => {
2090 const code_size = 15;
2091 var code: [code_size]u8 = undefined;
2092 // lea %r11, [rip + disp]
2093 code[0] = 0x4c;
2094 code[1] = 0x8d;
2095 code[2] = 0x1d;
2096 {
2097 const displacement = try math.cast(u32, data.addr - stub_helper.addr - 7);
2098 mem.writeIntLittle(u32, code[3..7], displacement);
2099 }
2100 // push %r11
2101 code[7] = 0x41;
2102 code[8] = 0x53;
2103 // jmp [rip + disp]
2104 code[9] = 0xff;
2105 code[10] = 0x25;
2106 {
2107 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2108 mem.writeIntLittle(u32, code[11..], displacement);
2109 }
2110 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2111 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2112 },
2113 .aarch64 => {
2114 var code: [4 * @sizeOf(u32)]u8 = undefined;
2115 {
2116 const displacement = try math.cast(i21, data.addr - stub_helper.addr);
2117 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2118 }
2119 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2120 .x16,
2121 .x17,
2122 aarch64.Register.sp,
2123 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2124 ).toU32());
2125 {
2126 const displacement = try math.divExact(u64, got.addr - stub_helper.addr - 2 * @sizeOf(u32), 4);
2127 const literal = try math.cast(u19, displacement);
2128 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2129 .literal = literal,
2130 }).toU32());
2131 }
2132 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
2133 self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32);
2134 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2135 },
2136 else => unreachable,
2137 }
2117 try self.writeStubHelperPreamble();
21382118 }
21392119}
21402120
......@@ -2159,7 +2139,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
21592139 const big_block = self.text_block_free_list.items[i];
21602140 // We now have a pointer to a live text block that has too much capacity.
21612141 // Is it enough that we could fit this new text block?
2162 const sym = self.local_symbols.items[big_block.local_sym_index];
2142 const sym = self.locals.items[big_block.local_sym_index];
21632143 const capacity = big_block.capacity(self.*);
21642144 const ideal_capacity = padToIdeal(capacity);
21652145 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
......@@ -2190,7 +2170,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
21902170 }
21912171 break :blk new_start_vaddr;
21922172 } else if (self.last_text_block) |last| {
2193 const last_symbol = self.local_symbols.items[last.local_sym_index];
2173 const last_symbol = self.locals.items[last.local_sym_index];
21942174 // TODO We should pad out the excess capacity with NOPs. For executables,
21952175 // no padding seems to be OK, but it will probably not be for objects.
21962176 const ideal_capacity = padToIdeal(last.size);
......@@ -2288,12 +2268,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
22882268}
22892269
22902270pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2291 const index = @intCast(u32, self.extern_lazy_symbols.items().len);
2271 const index = @intCast(u32, self.lazy_imports.items().len);
22922272 const offset = try self.makeString(name);
22932273 const sym_name = try self.base.allocator.dupe(u8, name);
22942274 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.
2295 try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{
2296 .inner = .{
2275 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2276 .symbol = .{
22972277 .n_strx = offset,
22982278 .n_type = macho.N_UNDF | macho.N_EXT,
22992279 .n_sect = 0,
......@@ -2301,6 +2281,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
23012281 .n_value = 0,
23022282 },
23032283 .dylib_ordinal = dylib_ordinal,
2284 .index = index,
23042285 });
23052286 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
23062287 return index;
......@@ -2459,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
24592440}
24602441
24612442fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2462 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2463 const sect = &text_segment.sections.items[self.got_section_index.?];
2443 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2444 const sect = &seg.sections.items[self.got_section_index.?];
24642445 const off = sect.offset + @sizeOf(u64) * index;
2465 const vmaddr = sect.addr + @sizeOf(u64) * index;
24662446
24672447 if (self.offset_table_count_dirty) {
24682448 // TODO relocate.
24692449 self.offset_table_count_dirty = false;
24702450 }
24712451
2472 var code: [8]u8 = undefined;
2473 switch (self.base.options.target.cpu.arch) {
2474 .x86_64 => {
2475 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);
2476 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);
2477 // lea %rax, [rip - disp]
2478 code[0] = 0x48;
2479 code[1] = 0x8D;
2480 code[2] = 0x5;
2481 mem.writeIntLittle(u32, code[3..7], symbol_off);
2482 // ret
2483 code[7] = 0xC3;
2484 },
2485 .aarch64 => {
2486 const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]);
2487 const symbol_off = @as(i21, pos_symbol_off) * -1;
2488 // adr x0, #-disp
2489 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
2490 // ret x28
2491 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
2492 },
2493 else => unreachable, // unsupported target architecture
2494 }
2495 log.debug("writing offset table entry 0x{x} at 0x{x}", .{ self.offset_table.items[index], off });
2496 try self.base.file.?.pwriteAll(&code, off);
2452 const got_entry = self.offset_table.items[index];
2453 const sym = blk: {
2454 switch (got_entry.kind) {
2455 .Local => {
2456 break :blk self.locals.items[got_entry.symbol];
2457 },
2458 .Extern => {
2459 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2460 },
2461 }
2462 };
2463 const sym_name = self.getString(sym.n_strx);
2464 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2465 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
24972466}
24982467
24992468fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
......@@ -2516,6 +2485,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
25162485 try self.base.file.?.pwriteAll(&buf, off);
25172486}
25182487
2488fn writeStubHelperPreamble(self: *MachO) !void {
2489 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2490 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2491 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2492 const got = &data_const_segment.sections.items[self.got_section_index.?];
2493 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2494 const data = &data_segment.sections.items[self.data_section_index.?];
2495
2496 switch (self.base.options.target.cpu.arch) {
2497 .x86_64 => {
2498 const code_size = 15;
2499 var code: [code_size]u8 = undefined;
2500 // lea %r11, [rip + disp]
2501 code[0] = 0x4c;
2502 code[1] = 0x8d;
2503 code[2] = 0x1d;
2504 {
2505 const target_addr = data.addr;
2506 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
2507 mem.writeIntLittle(u32, code[3..7], displacement);
2508 }
2509 // push %r11
2510 code[7] = 0x41;
2511 code[8] = 0x53;
2512 // jmp [rip + disp]
2513 code[9] = 0xff;
2514 code[10] = 0x25;
2515 {
2516 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2517 mem.writeIntLittle(u32, code[11..], displacement);
2518 }
2519 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2520 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2521 },
2522 .aarch64 => {
2523 var code: [6 * @sizeOf(u32)]u8 = undefined;
2524
2525 data_blk_outer: {
2526 const this_addr = stub_helper.addr;
2527 const target_addr = data.addr;
2528 data_blk: {
2529 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
2530 // adr x17, disp
2531 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2532 // nop
2533 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2534 break :data_blk_outer;
2535 }
2536 data_blk: {
2537 const new_this_addr = this_addr + @sizeOf(u32);
2538 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
2539 // nop
2540 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2541 // adr x17, disp
2542 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
2543 break :data_blk_outer;
2544 }
2545 // Jump is too big, replace adr with adrp and add.
2546 const this_page = @intCast(i32, this_addr >> 12);
2547 const target_page = @intCast(i32, target_addr >> 12);
2548 const pages = @intCast(i21, target_page - this_page);
2549 // adrp x17, pages
2550 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
2551 const narrowed = @truncate(u12, target_addr);
2552 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
2553 }
2554
2555 // stp x16, x17, [sp, #-16]!
2556 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.stp(
2557 .x16,
2558 .x17,
2559 aarch64.Register.sp,
2560 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2561 ).toU32());
2562
2563 binder_blk_outer: {
2564 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
2565 const target_addr = got.addr;
2566 binder_blk: {
2567 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
2568 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
2569 // ldr x16, label
2570 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
2571 .literal = literal,
2572 }).toU32());
2573 // nop
2574 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
2575 break :binder_blk_outer;
2576 }
2577 binder_blk: {
2578 const new_this_addr = this_addr + @sizeOf(u32);
2579 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
2580 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
2581 // nop
2582 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
2583 // ldr x16, label
2584 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2585 .literal = literal,
2586 }).toU32());
2587 break :binder_blk_outer;
2588 }
2589 // Jump is too big, replace ldr with adrp and ldr(register).
2590 const this_page = @intCast(i32, this_addr >> 12);
2591 const target_page = @intCast(i32, target_addr >> 12);
2592 const pages = @intCast(i21, target_page - this_page);
2593 // adrp x16, pages
2594 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
2595 const narrowed = @truncate(u12, target_addr);
2596 const offset = try math.divExact(u12, narrowed, 8);
2597 // ldr x16, x16, offset
2598 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2599 .register = .{
2600 .rn = .x16,
2601 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2602 },
2603 }).toU32());
2604 }
2605
2606 // br x16
2607 mem.writeIntLittle(u32, code[20..24], aarch64.Instruction.br(.x16).toU32());
2608 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2609 self.stub_helper_stubs_start_off = stub_helper.offset + code.len;
2610 },
2611 else => unreachable,
2612 }
2613}
2614
25192615fn writeStub(self: *MachO, index: u32) !void {
25202616 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
25212617 const stubs = text_segment.sections.items[self.stubs_section_index.?];
......@@ -2525,9 +2621,12 @@ fn writeStub(self: *MachO, index: u32) !void {
25252621 const stub_off = stubs.offset + index * stubs.reserved2;
25262622 const stub_addr = stubs.addr + index * stubs.reserved2;
25272623 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2624
25282625 log.debug("writing stub at 0x{x}", .{stub_off});
2626
25292627 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
25302628 defer self.base.allocator.free(code);
2629
25312630 switch (self.base.options.target.cpu.arch) {
25322631 .x86_64 => {
25332632 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
......@@ -2539,12 +2638,50 @@ fn writeStub(self: *MachO, index: u32) !void {
25392638 },
25402639 .aarch64 => {
25412640 assert(la_ptr_addr >= stub_addr);
2542 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);
2543 const literal = try math.cast(u19, displacement);
2544 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2545 .literal = literal,
2546 }).toU32());
2547 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());
2641 outer: {
2642 const this_addr = stub_addr;
2643 const target_addr = la_ptr_addr;
2644 inner: {
2645 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
2646 const literal = math.cast(u18, displacement) catch |_| break :inner;
2647 // ldr x16, literal
2648 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2649 .literal = literal,
2650 }).toU32());
2651 // nop
2652 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2653 break :outer;
2654 }
2655 inner: {
2656 const new_this_addr = this_addr + @sizeOf(u32);
2657 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
2658 const literal = math.cast(u18, displacement) catch |_| break :inner;
2659 // nop
2660 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2661 // ldr x16, literal
2662 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
2663 .literal = literal,
2664 }).toU32());
2665 break :outer;
2666 }
2667 // Use adrp followed by ldr(register).
2668 const this_page = @intCast(i32, this_addr >> 12);
2669 const target_page = @intCast(i32, target_addr >> 12);
2670 const pages = @intCast(i21, target_page - this_page);
2671 // adrp x16, pages
2672 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
2673 const narrowed = @truncate(u12, target_addr);
2674 const offset = try math.divExact(u12, narrowed, 8);
2675 // ldr x16, x16, offset
2676 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
2677 .register = .{
2678 .rn = .x16,
2679 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2680 },
2681 }).toU32());
2682 }
2683 // br x16
2684 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
25482685 },
25492686 else => unreachable,
25502687 }
......@@ -2561,8 +2698,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25612698 else => unreachable,
25622699 };
25632700 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2701
25642702 var code = try self.base.allocator.alloc(u8, stub_size);
25652703 defer self.base.allocator.free(code);
2704
25662705 switch (self.base.options.target.cpu.arch) {
25672706 .x86_64 => {
25682707 const displacement = try math.cast(
......@@ -2577,12 +2716,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25772716 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
25782717 },
25792718 .aarch64 => {
2580 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2719 const literal = blk: {
2720 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
2721 break :blk try math.cast(u18, div_res);
2722 };
2723 // ldr w16, literal
25812724 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2582 .literal = @divExact(stub_size - @sizeOf(u32), 4),
2725 .literal = literal,
25832726 }).toU32());
2727 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2728 // b disp
25842729 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
2585 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2730 // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2731 mem.writeIntLittle(u32, code[8..12], 0x0);
25862732 },
25872733 else => unreachable,
25882734 }
......@@ -2591,9 +2737,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25912737
25922738fn relocateSymbolTable(self: *MachO) !void {
25932739 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2594 const nlocals = self.local_symbols.items.len;
2595 const nglobals = self.global_symbols.items.len;
2596 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
2740 const nlocals = self.locals.items.len;
2741 const nglobals = self.globals.items.len;
2742 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
25972743 const nsyms = nlocals + nglobals + nundefs;
25982744
25992745 if (symtab.nsyms < nsyms) {
......@@ -2628,7 +2774,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {
26282774 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
26292775 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
26302776 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
2631 try self.base.file.?.pwriteAll(mem.asBytes(&self.local_symbols.items[index]), off);
2777 try self.base.file.?.pwriteAll(mem.asBytes(&self.locals.items[index]), off);
26322778}
26332779
26342780fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
......@@ -2637,18 +2783,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26372783
26382784 try self.relocateSymbolTable();
26392785 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2640 const nlocals = self.local_symbols.items.len;
2641 const nglobals = self.global_symbols.items.len;
2786 const nlocals = self.locals.items.len;
2787 const nglobals = self.globals.items.len;
26422788
2643 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
2789 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
26442790 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
26452791 defer undefs.deinit();
26462792 try undefs.ensureCapacity(nundefs);
2647 for (self.extern_lazy_symbols.items()) |entry| {
2648 undefs.appendAssumeCapacity(entry.value.inner);
2793 for (self.lazy_imports.items()) |entry| {
2794 undefs.appendAssumeCapacity(entry.value.symbol);
26492795 }
2650 for (self.extern_nonlazy_symbols.items()) |entry| {
2651 undefs.appendAssumeCapacity(entry.value.inner);
2796 for (self.nonlazy_imports.items()) |entry| {
2797 undefs.appendAssumeCapacity(entry.value.symbol);
26522798 }
26532799
26542800 const locals_off = symtab.symoff;
......@@ -2657,7 +2803,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26572803 const globals_off = locals_off + locals_size;
26582804 const globals_size = nglobals * @sizeOf(macho.nlist_64);
26592805 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });
2660 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off);
2806 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), globals_off);
26612807
26622808 const undefs_off = globals_off + globals_size;
26632809 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
......@@ -2683,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
26832829 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
26842830 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
26852831 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2686 const got = &data_const_seg.sections.items[self.data_got_section_index.?];
2832 const got = &data_const_seg.sections.items[self.got_section_index.?];
26872833 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
26882834 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
26892835 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26902836
2691 const lazy = self.extern_lazy_symbols.items();
2692 const nonlazy = self.extern_nonlazy_symbols.items();
2837 const lazy = self.lazy_imports.items();
2838 const got_entries = self.offset_table.items;
26932839 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
2694 const nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len);
2840 const nindirectsyms = @intCast(u32, lazy.len * 2 + got_entries.len);
26952841 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26962842
26972843 if (needed_size > allocated_size) {
......@@ -2710,20 +2856,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
27102856 var writer = stream.writer();
27112857
27122858 stubs.reserved1 = 0;
2713 for (self.extern_lazy_symbols.items()) |_, i| {
2859 for (lazy) |_, i| {
27142860 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
27152861 try writer.writeIntLittle(u32, symtab_idx);
27162862 }
27172863
27182864 const base_id = @intCast(u32, lazy.len);
27192865 got.reserved1 = base_id;
2720 for (self.extern_nonlazy_symbols.items()) |_, i| {
2721 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2722 try writer.writeIntLittle(u32, symtab_idx);
2866 for (got_entries) |entry| {
2867 switch (entry.kind) {
2868 .Local => {
2869 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2870 },
2871 .Extern => {
2872 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id);
2873 try writer.writeIntLittle(u32, symtab_idx);
2874 },
2875 }
27232876 }
27242877
2725 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);
2726 for (self.extern_lazy_symbols.items()) |_, i| {
2878 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2879 for (lazy) |_, i| {
27272880 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
27282881 try writer.writeIntLittle(u32, symtab_idx);
27292882 }
......@@ -2789,7 +2942,7 @@ fn writeCodeSignature(self: *MachO) !void {
27892942
27902943fn writeExportTrie(self: *MachO) !void {
27912944 if (!self.export_info_dirty) return;
2792 if (self.global_symbols.items.len == 0) return;
2945 if (self.globals.items.len == 0) return;
27932946
27942947 const tracy = trace(@src());
27952948 defer tracy.end();
......@@ -2798,7 +2951,7 @@ fn writeExportTrie(self: *MachO) !void {
27982951 defer trie.deinit();
27992952
28002953 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2801 for (self.global_symbols.items) |symbol| {
2954 for (self.globals.items) |symbol| {
28022955 // TODO figure out if we should put all global symbols into the export trie
28032956 const name = self.getString(symbol.n_strx);
28042957 assert(symbol.n_value >= text_segment.inner.vmaddr);
......@@ -2840,14 +2993,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {
28402993 const tracy = trace(@src());
28412994 defer tracy.end();
28422995
2843 const size = try rebaseInfoSize(self.extern_lazy_symbols.items());
2996 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
2997 defer pointers.deinit();
2998
2999 if (self.got_section_index) |idx| {
3000 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3001 const sect = seg.sections.items[idx];
3002 const base_offset = sect.addr - seg.inner.vmaddr;
3003 const segment_id = self.data_const_segment_cmd_index.?;
3004
3005 for (self.offset_table.items) |entry| {
3006 if (entry.kind == .Extern) continue;
3007 try pointers.append(.{
3008 .offset = base_offset + entry.index * @sizeOf(u64),
3009 .segment_id = segment_id,
3010 });
3011 }
3012 }
3013
3014 if (self.la_symbol_ptr_section_index) |idx| {
3015 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
3016 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3017 const sect = seg.sections.items[idx];
3018 const base_offset = sect.addr - seg.inner.vmaddr;
3019 const segment_id = self.data_segment_cmd_index.?;
3020
3021 for (self.lazy_imports.items()) |entry| {
3022 pointers.appendAssumeCapacity(.{
3023 .offset = base_offset + entry.value.index * @sizeOf(u64),
3024 .segment_id = segment_id,
3025 });
3026 }
3027 }
3028
3029 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
3030
3031 const size = try bind.rebaseInfoSize(pointers.items);
28443032 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28453033 defer self.base.allocator.free(buffer);
28463034
28473035 var stream = std.io.fixedBufferStream(buffer);
2848 try writeRebaseInfo(self.extern_lazy_symbols.items(), stream.writer());
3036 try bind.writeRebaseInfo(pointers.items, stream.writer());
28493037
2850 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
28513038 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
28523039 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
28533040 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2872,14 +3059,34 @@ fn writeBindingInfoTable(self: *MachO) !void {
28723059 const tracy = trace(@src());
28733060 defer tracy.end();
28743061
2875 const size = try bindInfoSize(self.extern_nonlazy_symbols.items());
3062 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3063 defer pointers.deinit();
3064
3065 if (self.got_section_index) |idx| {
3066 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3067 const sect = seg.sections.items[idx];
3068 const base_offset = sect.addr - seg.inner.vmaddr;
3069 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
3070
3071 for (self.offset_table.items) |entry| {
3072 if (entry.kind == .Local) continue;
3073 const import = self.nonlazy_imports.items()[entry.symbol];
3074 try pointers.append(.{
3075 .offset = base_offset + entry.index * @sizeOf(u64),
3076 .segment_id = segment_id,
3077 .dylib_ordinal = import.value.dylib_ordinal,
3078 .name = import.key,
3079 });
3080 }
3081 }
3082
3083 const size = try bind.bindInfoSize(pointers.items);
28763084 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28773085 defer self.base.allocator.free(buffer);
28783086
28793087 var stream = std.io.fixedBufferStream(buffer);
2880 try writeBindInfo(self.extern_nonlazy_symbols.items(), stream.writer());
3088 try bind.writeBindInfo(pointers.items, stream.writer());
28813089
2882 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
28833090 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
28843091 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
28853092 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2901,14 +3108,36 @@ fn writeBindingInfoTable(self: *MachO) !void {
29013108fn writeLazyBindingInfoTable(self: *MachO) !void {
29023109 if (!self.lazy_binding_info_dirty) return;
29033110
2904 const size = try lazyBindInfoSize(self.extern_lazy_symbols.items());
3111 const tracy = trace(@src());
3112 defer tracy.end();
3113
3114 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3115 defer pointers.deinit();
3116
3117 if (self.la_symbol_ptr_section_index) |idx| {
3118 try pointers.ensureCapacity(self.lazy_imports.items().len);
3119 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3120 const sect = seg.sections.items[idx];
3121 const base_offset = sect.addr - seg.inner.vmaddr;
3122 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
3123
3124 for (self.lazy_imports.items()) |entry| {
3125 pointers.appendAssumeCapacity(.{
3126 .offset = base_offset + entry.value.index * @sizeOf(u64),
3127 .segment_id = segment_id,
3128 .dylib_ordinal = entry.value.dylib_ordinal,
3129 .name = entry.key,
3130 });
3131 }
3132 }
3133
3134 const size = try bind.lazyBindInfoSize(pointers.items);
29053135 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
29063136 defer self.base.allocator.free(buffer);
29073137
29083138 var stream = std.io.fixedBufferStream(buffer);
2909 try writeLazyBindInfo(self.extern_lazy_symbols.items(), stream.writer());
3139 try bind.writeLazyBindInfo(pointers.items, stream.writer());
29103140
2911 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
29123141 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
29133142 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
29143143 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2929,7 +3158,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
29293158}
29303159
29313160fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2932 if (self.extern_lazy_symbols.items().len == 0) return;
3161 if (self.lazy_imports.items().len == 0) return;
29333162
29343163 var stream = std.io.fixedBufferStream(buffer);
29353164 var reader = stream.reader();
......@@ -2975,7 +3204,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
29753204 else => {},
29763205 }
29773206 }
2978 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);
3207 assert(self.lazy_imports.items().len <= offsets.items.len);
29793208
29803209 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
29813210 .x86_64 => 10,
......@@ -2988,7 +3217,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
29883217 else => unreachable,
29893218 };
29903219 var buf: [@sizeOf(u32)]u8 = undefined;
2991 for (self.extern_lazy_symbols.items()) |_, i| {
3220 for (self.lazy_imports.items()) |_, i| {
29923221 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
29933222 mem.writeIntLittle(u32, &buf, offsets.items[i]);
29943223 try self.base.file.?.pwriteAll(&buf, placeholder_off);
......@@ -3193,12 +3422,12 @@ fn parseSymbolTable(self: *MachO) !void {
31933422 const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);
31943423 assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);
31953424
3196 try self.local_symbols.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
3197 try self.global_symbols.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
3425 try self.locals.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
3426 try self.globals.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
31983427 try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);
31993428
3200 self.local_symbols.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
3201 self.global_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
3429 self.locals.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
3430 self.globals.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
32023431 self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);
32033432}
32043433
src/link/MachO/DebugSymbols.zig+4-4
......@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
839839
840840fn relocateSymbolTable(self: *DebugSymbols) !void {
841841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
842 const nlocals = self.base.local_symbols.items.len;
843 const nglobals = self.base.global_symbols.items.len;
842 const nlocals = self.base.locals.items.len;
843 const nglobals = self.base.globals.items.len;
844844 const nsyms = nlocals + nglobals;
845845
846846 if (symtab.nsyms < nsyms) {
......@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
875875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
876876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
877877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });
878 try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off);
878 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
879879}
880880
881881fn writeStringTable(self: *DebugSymbols) !void {
......@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(
10571057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
10581058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
10591059
1060 const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];
1060 const symbol = self.base.locals.items[decl.link.macho.local_sym_index];
10611061 const text_block = &decl.link.macho;
10621062 // If the Decl is a function, we need to update the __debug_line program.
10631063 const typed_value = decl.typed_value.most_recent.typed_value;
src/link/MachO/bind.zig created+145
......@@ -0,0 +1,145 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4
5pub const Pointer = struct {
6 offset: u64,
7 segment_id: u16,
8 dylib_ordinal: ?i64 = null,
9 name: ?[]const u8 = null,
10};
11
12pub fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
13 if (a.segment_id < b.segment_id) return true;
14 if (a.segment_id == b.segment_id) {
15 return a.offset < b.offset;
16 }
17 return false;
18}
19
20pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
21 var stream = std.io.countingWriter(std.io.null_writer);
22 var writer = stream.writer();
23 var size: u64 = 0;
24
25 for (pointers) |pointer| {
26 size += 2;
27 try leb.writeILEB128(writer, pointer.offset);
28 size += 1;
29 }
30
31 size += 1 + stream.bytes_written;
32 return size;
33}
34
35pub fn writeRebaseInfo(pointers: []const Pointer, writer: anytype) !void {
36 for (pointers) |pointer| {
37 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
38 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
39
40 try leb.writeILEB128(writer, pointer.offset);
41 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
42 }
43 try writer.writeByte(macho.REBASE_OPCODE_DONE);
44}
45
46pub fn bindInfoSize(pointers: []const Pointer) !u64 {
47 var stream = std.io.countingWriter(std.io.null_writer);
48 var writer = stream.writer();
49 var size: u64 = 0;
50
51 for (pointers) |pointer| {
52 size += 1;
53 if (pointer.dylib_ordinal.? > 15) {
54 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
55 }
56 size += 1;
57
58 size += 1;
59 size += pointer.name.?.len;
60 size += 1;
61
62 size += 1;
63
64 try leb.writeILEB128(writer, pointer.offset);
65 size += 1;
66 }
67
68 size += stream.bytes_written + 1;
69 return size;
70}
71
72pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
73 for (pointers) |pointer| {
74 if (pointer.dylib_ordinal.? > 15) {
75 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
76 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
77 } else if (pointer.dylib_ordinal.? > 0) {
78 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
79 } else {
80 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
81 }
82 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
83
84 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
85 try writer.writeAll(pointer.name.?);
86 try writer.writeByte(0);
87
88 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
89
90 try leb.writeILEB128(writer, pointer.offset);
91 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
92 }
93
94 try writer.writeByte(macho.BIND_OPCODE_DONE);
95}
96
97pub fn lazyBindInfoSize(pointers: []const Pointer) !u64 {
98 var stream = std.io.countingWriter(std.io.null_writer);
99 var writer = stream.writer();
100 var size: u64 = 0;
101
102 for (pointers) |pointer| {
103 size += 1;
104
105 try leb.writeILEB128(writer, pointer.offset);
106
107 size += 1;
108 if (pointer.dylib_ordinal.? > 15) {
109 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
110 }
111
112 size += 1;
113 size += pointer.name.?.len;
114 size += 1;
115
116 size += 2;
117 }
118
119 size += stream.bytes_written;
120 return size;
121}
122
123pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
124 for (pointers) |pointer| {
125 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
126
127 try leb.writeILEB128(writer, pointer.offset);
128
129 if (pointer.dylib_ordinal.? > 15) {
130 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
131 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
132 } else if (pointer.dylib_ordinal.? > 0) {
133 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
134 } else {
135 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
136 }
137
138 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
139 try writer.writeAll(pointer.name.?);
140 try writer.writeByte(0);
141
142 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
143 try writer.writeByte(macho.BIND_OPCODE_DONE);
144 }
145}
src/link/MachO/imports.zig deleted-152
......@@ -1,152 +0,0 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4const mem = std.mem;
5
6const assert = std.debug.assert;
7const Allocator = mem.Allocator;
8
9pub const ExternSymbol = struct {
10 /// MachO symbol table entry.
11 inner: macho.nlist_64,
12
13 /// Id of the dynamic library where the specified entries can be found.
14 /// Id of 0 means self.
15 /// TODO this should really be an id into the table of all defined
16 /// dylibs.
17 dylib_ordinal: i64 = 0,
18
19 /// Id of the segment where this symbol is defined (will have its address
20 /// resolved).
21 segment: u16 = 0,
22
23 /// Offset relative to the start address of the `segment`.
24 offset: u32 = 0,
25};
26
27pub fn rebaseInfoSize(symbols: anytype) !u64 {
28 var stream = std.io.countingWriter(std.io.null_writer);
29 var writer = stream.writer();
30 var size: u64 = 0;
31
32 for (symbols) |entry| {
33 size += 2;
34 try leb.writeILEB128(writer, entry.value.offset);
35 size += 1;
36 }
37
38 size += 1 + stream.bytes_written;
39 return size;
40}
41
42pub fn writeRebaseInfo(symbols: anytype, writer: anytype) !void {
43 for (symbols) |entry| {
44 const symbol = entry.value;
45 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
46 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
47 try leb.writeILEB128(writer, symbol.offset);
48 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
49 }
50 try writer.writeByte(macho.REBASE_OPCODE_DONE);
51}
52
53pub fn bindInfoSize(symbols: anytype) !u64 {
54 var stream = std.io.countingWriter(std.io.null_writer);
55 var writer = stream.writer();
56 var size: u64 = 0;
57
58 for (symbols) |entry| {
59 const symbol = entry.value;
60
61 size += 1;
62 if (symbol.dylib_ordinal > 15) {
63 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
64 }
65 size += 1;
66
67 size += 1;
68 size += entry.key.len;
69 size += 1;
70
71 size += 1;
72 try leb.writeILEB128(writer, symbol.offset);
73 size += 2;
74 }
75
76 size += stream.bytes_written;
77 return size;
78}
79
80pub fn writeBindInfo(symbols: anytype, writer: anytype) !void {
81 for (symbols) |entry| {
82 const symbol = entry.value;
83
84 if (symbol.dylib_ordinal > 15) {
85 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
86 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
87 } else if (symbol.dylib_ordinal > 0) {
88 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
89 } else {
90 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
91 }
92 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
93
94 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
95 try writer.writeAll(entry.key);
96 try writer.writeByte(0);
97
98 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
99 try leb.writeILEB128(writer, symbol.offset);
100 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
101 try writer.writeByte(macho.BIND_OPCODE_DONE);
102 }
103}
104
105pub fn lazyBindInfoSize(symbols: anytype) !u64 {
106 var stream = std.io.countingWriter(std.io.null_writer);
107 var writer = stream.writer();
108 var size: u64 = 0;
109
110 for (symbols) |entry| {
111 const symbol = entry.value;
112 size += 1;
113 try leb.writeILEB128(writer, symbol.offset);
114 size += 1;
115 if (symbol.dylib_ordinal > 15) {
116 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
117 }
118
119 size += 1;
120 size += entry.key.len;
121 size += 1;
122
123 size += 2;
124 }
125
126 size += stream.bytes_written;
127 return size;
128}
129
130pub fn writeLazyBindInfo(symbols: anytype, writer: anytype) !void {
131 for (symbols) |entry| {
132 const symbol = entry.value;
133 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
134 try leb.writeILEB128(writer, symbol.offset);
135
136 if (symbol.dylib_ordinal > 15) {
137 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
138 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
139 } else if (symbol.dylib_ordinal > 0) {
140 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
141 } else {
142 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
143 }
144
145 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
146 try writer.writeAll(entry.key);
147 try writer.writeByte(0);
148
149 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
150 try writer.writeByte(macho.BIND_OPCODE_DONE);
151 }
152}