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
signature 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;...@@ -1422,6 +1422,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
1422pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;1422pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
1423pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;1423pub 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
1425// Codesign consts and structs taken from:1433// Codesign consts and structs taken from:
1426// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html1434// 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 {...@@ -2132,9 +2132,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2132 if (inst.func.value()) |func_value| {2132 if (inst.func.value()) |func_value| {
2133 if (func_value.castTag(.function)) |func_payload| {2133 if (func_value.castTag(.function)) |func_payload| {
2134 const func = func_payload.data;2134 const func = func_payload.data;
2135 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;2135 const got_addr = blk: {
2136 const got = &text_segment.sections.items[macho_file.got_section_index.?];2136 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
2137 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);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});
2138 switch (arch) {2141 switch (arch) {
2139 .x86_64 => {2142 .x86_64 => {
2140 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });2143 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 {...@@ -2152,8 +2155,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2152 const decl = func_payload.data;2155 const decl = func_payload.data;
2153 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});2156 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
2154 defer self.bin_file.allocator.free(decl_name);2157 defer self.bin_file.allocator.free(decl_name);
2155 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);2158 const already_defined = macho_file.lazy_imports.contains(decl_name);
2156 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|2159 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
2157 @intCast(u32, index)2160 @intCast(u32, index)
2158 else2161 else
2159 try macho_file.addExternSymbol(decl_name);2162 try macho_file.addExternSymbol(decl_name);
...@@ -3111,7 +3114,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3111,7 +3114,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3111 4, 8 => {3114 4, 8 => {
3112 const offset = if (math.cast(i9, adj_off)) |imm|3115 const offset = if (math.cast(i9, adj_off)) |imm|
3113 Instruction.LoadStoreOffset.imm_post_index(-imm)3116 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 }));
3115 const rn: Register = switch (arch) {3119 const rn: Register = switch (arch) {
3116 .aarch64, .aarch64_be => .x29,3120 .aarch64, .aarch64_be => .x29,
3117 .aarch64_32 => .w29,3121 .aarch64_32 => .w29,
...@@ -3302,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3302,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3302 },3306 },
3303 .memory => |addr| {3307 .memory => |addr| {
3304 if (self.bin_file.options.pie) {3308 if (self.bin_file.options.pie) {
3305 // For MachO, the binary, with the exception of object files, has to be a PIE.3309 // PC-relative displacement to the entry in the GOT table.
3306 // Therefore we cannot load an absolute address.3310 // TODO we should come up with our own, backend independent relocation types
3307 // Instead, we need to make use of PC-relative addressing.3311 // which each backend (Elf, MachO, etc.) would then translate into an actual
3308 if (reg.id() == 0) { // x0 is special-cased3312 // fixup when linking.
3309 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack3313 // adrp reg, pages
3310 // like described here:3314 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3311 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)3315 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3312 // str x28, [sp, #-16]3316 .target_addr = addr,
3313 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{3317 .offset = self.code.items.len,
3314 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),3318 .size = 4,
3315 }).toU32());3319 });
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());
3343 } else {3320 } else {
3344 // stp x0, x28, [sp, #-16]3321 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
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());
3378 }3322 }
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());
3379 } else {3335 } else {
3380 // The value is in memory at a hard-coded address.3336 // The value is in memory at a hard-coded address.
3381 // If the type is a pointer, it means the pointer address is at this memory location.3337 // 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 {...@@ -3559,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3559 },3515 },
3560 .memory => |x| {3516 .memory => |x| {
3561 if (self.bin_file.options.pie) {3517 if (self.bin_file.options.pie) {
3562 // For MachO, the binary, with the exception of object files, has to be a PIE.3518 // RIP-relative displacement to the entry in the GOT table.
3563 // Therefore, we cannot load an absolute address.3519 // TODO we should come up with our own, backend independent relocation types
3564 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.3520 // which each backend (Elf, MachO, etc.) would then translate into an actual
3565 // The plan here is to use unconditional relative jump to GOT entry, where we store3521 // fixup when linking.
3566 // pre-calculated and stored effective address to load into the target register.3522 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3567 // We leave the actual displacement information empty (0-padded) and fixing it up3523 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3568 // later in the linker.3524 .target_addr = x,
3569 if (reg.id() == 0) { // %rax is special-cased3525 .offset = self.code.items.len + 3,
3570 try self.code.ensureCapacity(self.code.items.len + 5);3526 .size = 4,
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,
3587 });3527 });
3588 } else {3528 } else {
3589 try self.code.ensureCapacity(self.code.items.len + 10);3529 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
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});
3617 }3530 }
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 });
3618 } else if (x <= math.maxInt(u32)) {3543 } else if (x <= math.maxInt(u32)) {
3619 // Moving from memory to a register is a variant of `8B /r`.3544 // Moving from memory to a register is a variant of `8B /r`.
3620 // Since we're using 64-bit moves, we require a REX.3545 // Since we're using 64-bit moves, we require a REX.
...@@ -3777,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3777,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3777 return MCValue{ .memory = got_addr };3702 return MCValue{ .memory = got_addr };
3778 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {3703 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3779 const decl = payload.data;3704 const decl = payload.data;
3780 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;3705 const got_addr = blk: {
3781 const got = &text_segment.sections.items[macho_file.got_section_index.?];3706 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
3782 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;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 };
3783 return MCValue{ .memory = got_addr };3710 return MCValue{ .memory = got_addr };
3784 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {3711 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
3785 const decl = payload.data;3712 const decl = payload.data;
src/link/MachO.zig+485-256
...@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");...@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");
11const aarch64 = @import("../codegen/aarch64.zig");11const aarch64 = @import("../codegen/aarch64.zig");
12const math = std.math;12const math = std.math;
13const mem = std.mem;13const mem = std.mem;
14const meta = std.meta;
1415
16const bind = @import("MachO/bind.zig");
15const trace = @import("../tracy.zig").trace;17const trace = @import("../tracy.zig").trace;
16const build_options = @import("build_options");18const build_options = @import("build_options");
17const Module = @import("../Module.zig");19const Module = @import("../Module.zig");
...@@ -26,7 +28,6 @@ const Trie = @import("MachO/Trie.zig");...@@ -26,7 +28,6 @@ const Trie = @import("MachO/Trie.zig");
26const CodeSignature = @import("MachO/CodeSignature.zig");28const CodeSignature = @import("MachO/CodeSignature.zig");
2729
28usingnamespace @import("MachO/commands.zig");30usingnamespace @import("MachO/commands.zig");
29usingnamespace @import("MachO/imports.zig");
3031
31pub const base_tag: File.Tag = File.Tag.macho;32pub const base_tag: File.Tag = File.Tag.macho;
3233
...@@ -87,14 +88,12 @@ code_signature_cmd_index: ?u16 = null,...@@ -87,14 +88,12 @@ code_signature_cmd_index: ?u16 = null,
8788
88/// Index into __TEXT,__text section.89/// Index into __TEXT,__text section.
89text_section_index: ?u16 = null,90text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
92/// Index into __TEXT,__stubs section.91/// Index into __TEXT,__stubs section.
93stubs_section_index: ?u16 = null,92stubs_section_index: ?u16 = null,
94/// Index into __TEXT,__stub_helper section.93/// Index into __TEXT,__stub_helper section.
95stub_helper_section_index: ?u16 = null,94stub_helper_section_index: ?u16 = null,
96/// Index into __DATA_CONST,__got section.95/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,96got_section_index: ?u16 = null,
98/// Index into __DATA,__la_symbol_ptr section.97/// Index into __DATA,__la_symbol_ptr section.
99la_symbol_ptr_section_index: ?u16 = null,98la_symbol_ptr_section_index: ?u16 = null,
100/// Index into __DATA,__data section.99/// Index into __DATA,__data section.
...@@ -104,16 +103,16 @@ entry_addr: ?u64 = null,...@@ -104,16 +103,16 @@ entry_addr: ?u64 = null,
104103
105/// Table of all local symbols104/// Table of all local symbols
106/// Internally references string table for names (which are optional).105/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},106locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108/// Table of all global symbols107/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},108globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all extern nonlazy symbols, indexed by name.109/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},110nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
112/// Table of all extern lazy symbols, indexed by name.111/// 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) = .{},114locals_free_list: std.ArrayListUnmanaged(u32) = .{},
116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},115globals_free_list: std.ArrayListUnmanaged(u32) = .{},
117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},116offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
118117
119stub_helper_stubs_start_off: ?u64 = null,118stub_helper_stubs_start_off: ?u64 = null,
...@@ -122,8 +121,8 @@ stub_helper_stubs_start_off: ?u64 = null,...@@ -122,8 +121,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122string_table: std.ArrayListUnmanaged(u8) = .{},121string_table: std.ArrayListUnmanaged(u8) = .{},
123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},122string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124123
125/// Table of trampolines to the actual symbols in __text section.124/// Table of GOT entries.
126offset_table: std.ArrayListUnmanaged(u64) = .{},125offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127126
128error_flags: File.ErrorFlags = File.ErrorFlags{},127error_flags: File.ErrorFlags = File.ErrorFlags{},
129128
...@@ -154,14 +153,19 @@ string_table_needs_relocation: bool = false,...@@ -154,14 +153,19 @@ string_table_needs_relocation: bool = false,
154/// allocate a fresh text block, which will have ideal capacity, and then grow it153/// allocate a fresh text block, which will have ideal capacity, and then grow it
155/// by 1 byte. It will then have -1 overcapacity.154/// by 1 byte. It will then have -1 overcapacity.
156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},155text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
156
157/// Pointer to the last allocated text block157/// Pointer to the last allocated text block
158last_text_block: ?*TextBlock = null,158last_text_block: ?*TextBlock = null,
159
159/// A list of all PIE fixups required for this run of the linker.160/// A list of all PIE fixups required for this run of the linker.
160/// Warning, this is currently NOT thread-safe. See the TODO below.161/// Warning, this is currently NOT thread-safe. See the TODO below.
161/// TODO Move this list inside `updateDecl` where it should be allocated162/// TODO Move this list inside `updateDecl` where it should be allocated
162/// prior to calling `generateSymbol`, and then immediately deallocated163/// prior to calling `generateSymbol`, and then immediately deallocated
163/// rather than sitting in the global scope.164/// 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
165/// A list of all stub (extern decls) fixups required for this run of the linker.169/// A list of all stub (extern decls) fixups required for this run of the linker.
166/// Warning, this is currently NOT thread-safe. See the TODO below.170/// Warning, this is currently NOT thread-safe. See the TODO below.
167/// TODO Move this list inside `updateDecl` where it should be allocated171/// TODO Move this list inside `updateDecl` where it should be allocated
...@@ -169,14 +173,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},...@@ -169,14 +173,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169/// rather than sitting in the global scope.173/// rather than sitting in the global scope.
170stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},174stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171175
172pub const PieFixup = struct {176pub const GOTEntry = struct {
173 /// Target address we wanted to address in absolute terms.177 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
174 address: u64,178 kind: enum {
175 /// Where in the byte stream we should perform the fixup.179 Local,
176 start: usize,180 Extern,
177 /// The length of the byte stream. For x86_64, this will be181 },
178 /// variable. For aarch64, it will be fixed at 4 bytes.182
179 len: usize,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,
180};212};
181213
182pub const StubFixup = struct {214pub const StubFixup = struct {
...@@ -260,9 +292,9 @@ pub const TextBlock = struct {...@@ -260,9 +292,9 @@ pub const TextBlock = struct {
260 /// File offset relocation happens transparently, so it is not included in292 /// File offset relocation happens transparently, so it is not included in
261 /// this calculation.293 /// this calculation.
262 fn capacity(self: TextBlock, macho_file: MachO) u64 {294 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];
264 if (self.next) |next| {296 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];
266 return next_sym.n_value - self_sym.n_value;298 return next_sym.n_value - self_sym.n_value;
267 } else {299 } else {
268 // We are the last block.300 // We are the last block.
...@@ -274,8 +306,8 @@ pub const TextBlock = struct {...@@ -274,8 +306,8 @@ pub const TextBlock = struct {
274 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {306 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
275 // No need to keep a free list node for the last block.307 // No need to keep a free list node for the last block.
276 const next = self.next orelse return false;308 const next = self.next orelse return false;
277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];309 const self_sym = macho_file.locals.items[self.local_sym_index];
278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];310 const next_sym = macho_file.locals.items[next.local_sym_index];
279 const cap = next_sym.n_value - self_sym.n_value;311 const cap = next_sym.n_value - self_sym.n_value;
280 const ideal_cap = padToIdeal(self.size);312 const ideal_cap = padToIdeal(self.size);
281 if (cap <= ideal_cap) return false;313 if (cap <= ideal_cap) return false;
...@@ -344,7 +376,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -344,7 +376,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
344 };376 };
345377
346 // Index 0 is always a null symbol.378 // Index 0 is always a null symbol.
347 try self.local_symbols.append(allocator, .{379 try self.locals.append(allocator, .{
348 .n_strx = 0,380 .n_strx = 0,
349 .n_type = 0,381 .n_type = 0,
350 .n_sect = 0,382 .n_sect = 0,
...@@ -834,7 +866,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -834,7 +866,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
834 }866 }
835 },867 },
836 else => {868 else => {
837 log.err("{s} terminated", .{ argv.items[0] });869 log.err("{s} terminated", .{argv.items[0]});
838 return error.LLDCrashed;870 return error.LLDCrashed;
839 },871 },
840 }872 }
...@@ -1019,14 +1051,14 @@ pub fn deinit(self: *MachO) void {...@@ -1019,14 +1051,14 @@ pub fn deinit(self: *MachO) void {
1019 if (self.d_sym) |*ds| {1051 if (self.d_sym) |*ds| {
1020 ds.deinit(self.base.allocator);1052 ds.deinit(self.base.allocator);
1021 }1053 }
1022 for (self.extern_lazy_symbols.items()) |*entry| {1054 for (self.lazy_imports.items()) |*entry| {
1023 self.base.allocator.free(entry.key);1055 self.base.allocator.free(entry.key);
1024 }1056 }
1025 self.extern_lazy_symbols.deinit(self.base.allocator);1057 self.lazy_imports.deinit(self.base.allocator);
1026 for (self.extern_nonlazy_symbols.items()) |*entry| {1058 for (self.nonlazy_imports.items()) |*entry| {
1027 self.base.allocator.free(entry.key);1059 self.base.allocator.free(entry.key);
1028 }1060 }
1029 self.extern_nonlazy_symbols.deinit(self.base.allocator);1061 self.nonlazy_imports.deinit(self.base.allocator);
1030 self.pie_fixups.deinit(self.base.allocator);1062 self.pie_fixups.deinit(self.base.allocator);
1031 self.stub_fixups.deinit(self.base.allocator);1063 self.stub_fixups.deinit(self.base.allocator);
1032 self.text_block_free_list.deinit(self.base.allocator);1064 self.text_block_free_list.deinit(self.base.allocator);
...@@ -1040,10 +1072,10 @@ pub fn deinit(self: *MachO) void {...@@ -1040,10 +1072,10 @@ pub fn deinit(self: *MachO) void {
1040 }1072 }
1041 self.string_table_directory.deinit(self.base.allocator);1073 self.string_table_directory.deinit(self.base.allocator);
1042 self.string_table.deinit(self.base.allocator);1074 self.string_table.deinit(self.base.allocator);
1043 self.global_symbols.deinit(self.base.allocator);1075 self.globals.deinit(self.base.allocator);
1044 self.global_symbol_free_list.deinit(self.base.allocator);1076 self.globals_free_list.deinit(self.base.allocator);
1045 self.local_symbols.deinit(self.base.allocator);1077 self.locals.deinit(self.base.allocator);
1046 self.local_symbol_free_list.deinit(self.base.allocator);1078 self.locals_free_list.deinit(self.base.allocator);
1047 for (self.load_commands.items) |*lc| {1079 for (self.load_commands.items) |*lc| {
1048 lc.deinit(self.base.allocator);1080 lc.deinit(self.base.allocator);
1049 }1081 }
...@@ -1098,7 +1130,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo...@@ -1098,7 +1130,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo
1098}1130}
10991131
1100fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {1132fn 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];
1102 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;1134 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
1103 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);1135 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1104 if (!need_realloc) return sym.n_value;1136 if (!need_realloc) return sym.n_value;
...@@ -1108,34 +1140,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig...@@ -1108,34 +1140,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
1108pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {1140pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
1109 if (decl.link.macho.local_sym_index != 0) return;1141 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);
1112 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);1144 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| {
1115 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });1147 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
1116 decl.link.macho.local_sym_index = i;1148 decl.link.macho.local_sym_index = i;
1117 } else {1149 } else {
1118 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });1150 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
1119 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);1151 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
1120 _ = self.local_symbols.addOneAssumeCapacity();1152 _ = self.locals.addOneAssumeCapacity();
1121 }1153 }
11221154
1123 if (self.offset_table_free_list.popOrNull()) |i| {1155 if (self.offset_table_free_list.popOrNull()) |i| {
1156 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
1124 decl.link.macho.offset_table_index = i;1157 decl.link.macho.offset_table_index = i;
1125 } else {1158 } else {
1159 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
1126 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);1160 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
1127 _ = self.offset_table.addOneAssumeCapacity();1161 _ = self.offset_table.addOneAssumeCapacity();
1128 self.offset_table_count_dirty = true;1162 self.offset_table_count_dirty = true;
1163 self.rebase_info_dirty = true;
1129 }1164 }
11301165
1131 self.local_symbols.items[decl.link.macho.local_sym_index] = .{1166 self.locals.items[decl.link.macho.local_sym_index] = .{
1132 .n_strx = 0,1167 .n_strx = 0,
1133 .n_type = 0,1168 .n_type = 0,
1134 .n_sect = 0,1169 .n_sect = 0,
1135 .n_desc = 0,1170 .n_desc = 0,
1136 .n_value = 0,1171 .n_value = 0,
1137 };1172 };
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 };
1139}1178}
11401179
1141pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {1180pub 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 {...@@ -1178,8 +1217,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1178 .externally_managed => |x| x,1217 .externally_managed => |x| x,
1179 .appended => code_buffer.items,1218 .appended => code_buffer.items,
1180 .fail => |em| {1219 .fail => |em| {
1181 // Clear any PIE fixups and stub fixups for this decl.1220 // Clear any PIE fixups for this decl.
1182 self.pie_fixups.shrinkRetainingCapacity(0);1221 self.pie_fixups.shrinkRetainingCapacity(0);
1222 // Clear any stub fixups for this decl.
1183 self.stub_fixups.shrinkRetainingCapacity(0);1223 self.stub_fixups.shrinkRetainingCapacity(0);
1184 decl.analysis = .codegen_failure;1224 decl.analysis = .codegen_failure;
1185 try module.failed_decls.put(module.gpa, decl, em);1225 try module.failed_decls.put(module.gpa, decl, em);
...@@ -1189,7 +1229,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1189,7 +1229,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11891229
1190 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);1230 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
1191 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()1231 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
1194 if (decl.link.macho.size != 0) {1234 if (decl.link.macho.size != 0) {
1195 const capacity = decl.link.macho.capacity(self.*);1235 const capacity = decl.link.macho.capacity(self.*);
...@@ -1198,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1198,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1198 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);1238 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
1199 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });1239 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
1200 if (vaddr != symbol.n_value) {1240 if (vaddr != symbol.n_value) {
1201 symbol.n_value = vaddr;
1202 log.debug(" (writing new offset table entry)", .{});1241 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 };
1204 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1247 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1205 }1248 }
1206 } else if (code.len < decl.link.macho.size) {1249 } else if (code.len < decl.link.macho.size) {
...@@ -1229,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1229,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1229 .n_desc = 0,1272 .n_desc = 0,
1230 .n_value = addr,1273 .n_value = addr,
1231 };1274 };
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
1234 try self.writeLocalSymbol(decl.link.macho.local_sym_index);1281 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1235 if (self.d_sym) |*ds|1282 if (self.d_sym) |*ds|
...@@ -1237,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1237,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1237 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1284 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1238 }1285 }
12391286
1240 // Perform PIE fixups (if any)1287 // Calculate displacements to target addr (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.?];
1243 while (self.pie_fixups.popOrNull()) |fixup| {1288 while (self.pie_fixups.popOrNull()) |fixup| {
1244 const target_addr = fixup.address;1289 assert(fixup.size == 4);
1245 const this_addr = symbol.n_value + fixup.start;1290 const this_addr = symbol.n_value + fixup.offset;
1291 const target_addr = fixup.target_addr;
1292
1246 switch (self.base.options.target.cpu.arch) {1293 switch (self.base.options.target.cpu.arch) {
1247 .x86_64 => {1294 .x86_64 => {
1248 assert(target_addr >= this_addr + fixup.len);1295 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1249 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);1296 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
1250 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1251 mem.writeIntSliceLittle(u32, placeholder, displacement);
1252 },1297 },
1253 .aarch64 => {1298 .aarch64 => {
1254 assert(target_addr >= this_addr);1299 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1255 const displacement = try math.cast(u27, target_addr - this_addr);1300 {
1256 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1301 const inst = code_buffer.items[fixup.offset..][0..4];
1257 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());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 }
1258 },1322 },
1259 else => unreachable, // unsupported target architecture1323 else => unreachable, // unsupported target architecture
1260 }1324 }
1261 }1325 }
12621326
1263 // Resolve stubs (if any)1327 // Resolve stubs (if any)
1328 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1264 const stubs = text_segment.sections.items[self.stubs_section_index.?];1329 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1265 for (self.stub_fixups.items) |fixup| {1330 for (self.stub_fixups.items) |fixup| {
1266 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;1331 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
...@@ -1285,9 +1350,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1285,9 +1350,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1285 try self.writeStubInStubHelper(fixup.symbol);1350 try self.writeStubInStubHelper(fixup.symbol);
1286 try self.writeLazySymbolPointer(fixup.symbol);1351 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);
1291 self.rebase_info_dirty = true;1353 self.rebase_info_dirty = true;
1292 self.lazy_binding_info_dirty = true;1354 self.lazy_binding_info_dirty = true;
1293 }1355 }
...@@ -1329,9 +1391,9 @@ pub fn updateDeclExports(...@@ -1329,9 +1391,9 @@ pub fn updateDeclExports(
1329 const tracy = trace(@src());1391 const tracy = trace(@src());
1330 defer tracy.end();1392 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);
1333 if (decl.link.macho.local_sym_index == 0) return;1395 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
1336 for (exports) |exp| {1398 for (exports) |exp| {
1337 if (exp.options.section) |section_name| {1399 if (exp.options.section) |section_name| {
...@@ -1364,7 +1426,7 @@ pub fn updateDeclExports(...@@ -1364,7 +1426,7 @@ pub fn updateDeclExports(
1364 };1426 };
1365 const n_type = decl_sym.n_type | macho.N_EXT;1427 const n_type = decl_sym.n_type | macho.N_EXT;
1366 if (exp.link.macho.sym_index) |i| {1428 if (exp.link.macho.sym_index) |i| {
1367 const sym = &self.global_symbols.items[i];1429 const sym = &self.globals.items[i];
1368 sym.* = .{1430 sym.* = .{
1369 .n_strx = try self.updateString(sym.n_strx, exp.options.name),1431 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
1370 .n_type = n_type,1432 .n_type = n_type,
...@@ -1374,12 +1436,12 @@ pub fn updateDeclExports(...@@ -1374,12 +1436,12 @@ pub fn updateDeclExports(
1374 };1436 };
1375 } else {1437 } else {
1376 const name_str_index = try self.makeString(exp.options.name);1438 const name_str_index = try self.makeString(exp.options.name);
1377 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {1439 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1378 _ = self.global_symbols.addOneAssumeCapacity();1440 _ = self.globals.addOneAssumeCapacity();
1379 self.export_info_dirty = true;1441 self.export_info_dirty = true;
1380 break :blk self.global_symbols.items.len - 1;1442 break :blk self.globals.items.len - 1;
1381 };1443 };
1382 self.global_symbols.items[i] = .{1444 self.globals.items[i] = .{
1383 .n_strx = name_str_index,1445 .n_strx = name_str_index,
1384 .n_type = n_type,1446 .n_type = n_type,
1385 .n_sect = @intCast(u8, self.text_section_index.?) + 1,1447 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
...@@ -1394,18 +1456,18 @@ pub fn updateDeclExports(...@@ -1394,18 +1456,18 @@ pub fn updateDeclExports(
13941456
1395pub fn deleteExport(self: *MachO, exp: Export) void {1457pub fn deleteExport(self: *MachO, exp: Export) void {
1396 const sym_index = exp.sym_index orelse return;1458 const sym_index = exp.sym_index orelse return;
1397 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};1459 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
1398 self.global_symbols.items[sym_index].n_type = 0;1460 self.globals.items[sym_index].n_type = 0;
1399}1461}
14001462
1401pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {1463pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
1402 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.1464 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1403 self.freeTextBlock(&decl.link.macho);1465 self.freeTextBlock(&decl.link.macho);
1404 if (decl.link.macho.local_sym_index != 0) {1466 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 {};
1406 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};1468 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
1410 decl.link.macho.local_sym_index = 0;1472 decl.link.macho.local_sym_index = 0;
1411 }1473 }
...@@ -1413,7 +1475,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {...@@ -1413,7 +1475,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14131475
1414pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {1476pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
1415 assert(decl.link.macho.local_sym_index != 0);1477 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;
1417}1479}
14181480
1419pub fn populateMissingMetadata(self: *MachO) !void {1481pub fn populateMissingMetadata(self: *MachO) !void {
...@@ -1553,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1553,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1553 self.header_dirty = true;1615 self.header_dirty = true;
1554 self.load_commands_dirty = true;1616 self.load_commands_dirty = true;
1555 }1617 }
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 }
1589 if (self.stubs_section_index == null) {1618 if (self.stubs_section_index == null) {
1590 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1619 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1591 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);1620 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
...@@ -1597,7 +1626,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1597,7 +1626,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1597 };1626 };
1598 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {1627 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
1599 .x86_64 => 6,1628 .x86_64 => 6,
1600 .aarch64 => 2 * @sizeOf(u32),1629 .aarch64 => 3 * @sizeOf(u32),
1601 else => unreachable, // unhandled architecture type1630 else => unreachable, // unhandled architecture type
1602 };1631 };
1603 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;1632 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 {...@@ -1686,9 +1715,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1686 self.header_dirty = true;1715 self.header_dirty = true;
1687 self.load_commands_dirty = true;1716 self.load_commands_dirty = true;
1688 }1717 }
1689 if (self.data_got_section_index == null) {1718 if (self.got_section_index == null) {
1690 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1719 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
1693 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;1722 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
1694 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1723 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
...@@ -2060,12 +2089,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2060,12 +2089,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2060 self.header_dirty = true;2089 self.header_dirty = true;
2061 self.load_commands_dirty = true;2090 self.load_commands_dirty = true;
2062 }2091 }
2063 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {2092 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
2064 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);2093 const index = @intCast(u32, self.nonlazy_imports.items().len);
2065 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");2094 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
2066 const offset = try self.makeString("dyld_stub_binder");2095 const offset = try self.makeString("dyld_stub_binder");
2067 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{2096 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
2068 .inner = .{2097 .symbol = .{
2069 .n_strx = offset,2098 .n_strx = offset,
2070 .n_type = std.macho.N_UNDF | std.macho.N_EXT,2099 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2071 .n_sect = 0,2100 .n_sect = 0,
...@@ -2073,68 +2102,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2073,68 +2102,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2073 .n_value = 0,2102 .n_value = 0,
2074 },2103 },
2075 .dylib_ordinal = 1, // TODO this is currently hardcoded.2104 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2076 .segment = self.data_const_segment_cmd_index.?,2105 .index = index,
2077 .offset = index * @sizeOf(u64),
2078 });2106 });
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);
2079 self.binding_info_dirty = true;2114 self.binding_info_dirty = true;
2080 }2115 }
2081 if (self.stub_helper_stubs_start_off == null) {2116 if (self.stub_helper_stubs_start_off == null) {
2082 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2117 try self.writeStubHelperPreamble();
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 }
2138 }2118 }
2139}2119}
21402120
...@@ -2159,7 +2139,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2159,7 +2139,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2159 const big_block = self.text_block_free_list.items[i];2139 const big_block = self.text_block_free_list.items[i];
2160 // We now have a pointer to a live text block that has too much capacity.2140 // We now have a pointer to a live text block that has too much capacity.
2161 // Is it enough that we could fit this new text block?2141 // 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];
2163 const capacity = big_block.capacity(self.*);2143 const capacity = big_block.capacity(self.*);
2164 const ideal_capacity = padToIdeal(capacity);2144 const ideal_capacity = padToIdeal(capacity);
2165 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;2145 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,...@@ -2190,7 +2170,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2190 }2170 }
2191 break :blk new_start_vaddr;2171 break :blk new_start_vaddr;
2192 } else if (self.last_text_block) |last| {2172 } 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];
2194 // TODO We should pad out the excess capacity with NOPs. For executables,2174 // TODO We should pad out the excess capacity with NOPs. For executables,
2195 // no padding seems to be OK, but it will probably not be for objects.2175 // no padding seems to be OK, but it will probably not be for objects.
2196 const ideal_capacity = padToIdeal(last.size);2176 const ideal_capacity = padToIdeal(last.size);
...@@ -2288,12 +2268,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {...@@ -2288,12 +2268,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
2288}2268}
22892269
2290pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {2270pub 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);
2292 const offset = try self.makeString(name);2272 const offset = try self.makeString(name);
2293 const sym_name = try self.base.allocator.dupe(u8, name);2273 const sym_name = try self.base.allocator.dupe(u8, name);
2294 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.2274 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, .{2275 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2296 .inner = .{2276 .symbol = .{
2297 .n_strx = offset,2277 .n_strx = offset,
2298 .n_type = macho.N_UNDF | macho.N_EXT,2278 .n_type = macho.N_UNDF | macho.N_EXT,
2299 .n_sect = 0,2279 .n_sect = 0,
...@@ -2301,6 +2281,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {...@@ -2301,6 +2281,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2301 .n_value = 0,2281 .n_value = 0,
2302 },2282 },
2303 .dylib_ordinal = dylib_ordinal,2283 .dylib_ordinal = dylib_ordinal,
2284 .index = index,
2304 });2285 });
2305 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });2286 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
2306 return index;2287 return index;
...@@ -2459,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta...@@ -2459,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
2459}2440}
24602441
2461fn writeOffsetTableEntry(self: *MachO, index: usize) !void {2442fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2462 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2443 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2463 const sect = &text_segment.sections.items[self.got_section_index.?];2444 const sect = &seg.sections.items[self.got_section_index.?];
2464 const off = sect.offset + @sizeOf(u64) * index;2445 const off = sect.offset + @sizeOf(u64) * index;
2465 const vmaddr = sect.addr + @sizeOf(u64) * index;
24662446
2467 if (self.offset_table_count_dirty) {2447 if (self.offset_table_count_dirty) {
2468 // TODO relocate.2448 // TODO relocate.
2469 self.offset_table_count_dirty = false;2449 self.offset_table_count_dirty = false;
2470 }2450 }
24712451
2472 var code: [8]u8 = undefined;2452 const got_entry = self.offset_table.items[index];
2473 switch (self.base.options.target.cpu.arch) {2453 const sym = blk: {
2474 .x86_64 => {2454 switch (got_entry.kind) {
2475 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);2455 .Local => {
2476 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);2456 break :blk self.locals.items[got_entry.symbol];
2477 // lea %rax, [rip - disp]2457 },
2478 code[0] = 0x48;2458 .Extern => {
2479 code[1] = 0x8D;2459 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2480 code[2] = 0x5;2460 },
2481 mem.writeIntLittle(u32, code[3..7], symbol_off);2461 }
2482 // ret2462 };
2483 code[7] = 0xC3;2463 const sym_name = self.getString(sym.n_strx);
2484 },2464 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2485 .aarch64 => {2465 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
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);
2497}2466}
24982467
2499fn writeLazySymbolPointer(self: *MachO, index: u32) !void {2468fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
...@@ -2516,6 +2485,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {...@@ -2516,6 +2485,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
2516 try self.base.file.?.pwriteAll(&buf, off);2485 try self.base.file.?.pwriteAll(&buf, off);
2517}2486}
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
2519fn writeStub(self: *MachO, index: u32) !void {2615fn writeStub(self: *MachO, index: u32) !void {
2520 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2616 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2521 const stubs = text_segment.sections.items[self.stubs_section_index.?];2617 const stubs = text_segment.sections.items[self.stubs_section_index.?];
...@@ -2525,9 +2621,12 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2525,9 +2621,12 @@ fn writeStub(self: *MachO, index: u32) !void {
2525 const stub_off = stubs.offset + index * stubs.reserved2;2621 const stub_off = stubs.offset + index * stubs.reserved2;
2526 const stub_addr = stubs.addr + index * stubs.reserved2;2622 const stub_addr = stubs.addr + index * stubs.reserved2;
2527 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);2623 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2624
2528 log.debug("writing stub at 0x{x}", .{stub_off});2625 log.debug("writing stub at 0x{x}", .{stub_off});
2626
2529 var code = try self.base.allocator.alloc(u8, stubs.reserved2);2627 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
2530 defer self.base.allocator.free(code);2628 defer self.base.allocator.free(code);
2629
2531 switch (self.base.options.target.cpu.arch) {2630 switch (self.base.options.target.cpu.arch) {
2532 .x86_64 => {2631 .x86_64 => {
2533 assert(la_ptr_addr >= stub_addr + stubs.reserved2);2632 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
...@@ -2539,12 +2638,50 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2539,12 +2638,50 @@ fn writeStub(self: *MachO, index: u32) !void {
2539 },2638 },
2540 .aarch64 => {2639 .aarch64 => {
2541 assert(la_ptr_addr >= stub_addr);2640 assert(la_ptr_addr >= stub_addr);
2542 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);2641 outer: {
2543 const literal = try math.cast(u19, displacement);2642 const this_addr = stub_addr;
2544 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{2643 const target_addr = la_ptr_addr;
2545 .literal = literal,2644 inner: {
2546 }).toU32());2645 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
2547 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());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());
2548 },2685 },
2549 else => unreachable,2686 else => unreachable,
2550 }2687 }
...@@ -2561,8 +2698,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2561,8 +2698,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2561 else => unreachable,2698 else => unreachable,
2562 };2699 };
2563 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;2700 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2701
2564 var code = try self.base.allocator.alloc(u8, stub_size);2702 var code = try self.base.allocator.alloc(u8, stub_size);
2565 defer self.base.allocator.free(code);2703 defer self.base.allocator.free(code);
2704
2566 switch (self.base.options.target.cpu.arch) {2705 switch (self.base.options.target.cpu.arch) {
2567 .x86_64 => {2706 .x86_64 => {
2568 const displacement = try math.cast(2707 const displacement = try math.cast(
...@@ -2577,12 +2716,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2577,12 +2716,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2577 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));2716 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
2578 },2717 },
2579 .aarch64 => {2718 .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
2581 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{2724 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2582 .literal = @divExact(stub_size - @sizeOf(u32), 4),2725 .literal = literal,
2583 }).toU32());2726 }).toU32());
2727 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2728 // b disp
2584 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());2729 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);
2586 },2732 },
2587 else => unreachable,2733 else => unreachable,
2588 }2734 }
...@@ -2591,9 +2737,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2591,9 +2737,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25912737
2592fn relocateSymbolTable(self: *MachO) !void {2738fn relocateSymbolTable(self: *MachO) !void {
2593 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2739 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2594 const nlocals = self.local_symbols.items.len;2740 const nlocals = self.locals.items.len;
2595 const nglobals = self.global_symbols.items.len;2741 const nglobals = self.globals.items.len;
2596 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;2742 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2597 const nsyms = nlocals + nglobals + nundefs;2743 const nsyms = nlocals + nglobals + nundefs;
25982744
2599 if (symtab.nsyms < nsyms) {2745 if (symtab.nsyms < nsyms) {
...@@ -2628,7 +2774,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {...@@ -2628,7 +2774,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {
2628 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2774 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2629 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;2775 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
2630 log.debug("writing local symbol {} at 0x{x}", .{ index, off });2776 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);
2632}2778}
26332779
2634fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {2780fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
...@@ -2637,18 +2783,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2637,18 +2783,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26372783
2638 try self.relocateSymbolTable();2784 try self.relocateSymbolTable();
2639 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2785 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2640 const nlocals = self.local_symbols.items.len;2786 const nlocals = self.locals.items.len;
2641 const nglobals = self.global_symbols.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;
2644 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);2790 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
2645 defer undefs.deinit();2791 defer undefs.deinit();
2646 try undefs.ensureCapacity(nundefs);2792 try undefs.ensureCapacity(nundefs);
2647 for (self.extern_lazy_symbols.items()) |entry| {2793 for (self.lazy_imports.items()) |entry| {
2648 undefs.appendAssumeCapacity(entry.value.inner);2794 undefs.appendAssumeCapacity(entry.value.symbol);
2649 }2795 }
2650 for (self.extern_nonlazy_symbols.items()) |entry| {2796 for (self.nonlazy_imports.items()) |entry| {
2651 undefs.appendAssumeCapacity(entry.value.inner);2797 undefs.appendAssumeCapacity(entry.value.symbol);
2652 }2798 }
26532799
2654 const locals_off = symtab.symoff;2800 const locals_off = symtab.symoff;
...@@ -2657,7 +2803,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2657,7 +2803,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2657 const globals_off = locals_off + locals_size;2803 const globals_off = locals_off + locals_size;
2658 const globals_size = nglobals * @sizeOf(macho.nlist_64);2804 const globals_size = nglobals * @sizeOf(macho.nlist_64);
2659 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });2805 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
2662 const undefs_off = globals_off + globals_size;2808 const undefs_off = globals_off + globals_size;
2663 const undefs_size = nundefs * @sizeOf(macho.nlist_64);2809 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
...@@ -2683,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2683,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2683 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2829 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2684 const stubs = &text_segment.sections.items[self.stubs_section_index.?];2830 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2685 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2831 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.?];
2687 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2833 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2688 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];2834 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2689 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2835 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26902836
2691 const lazy = self.extern_lazy_symbols.items();2837 const lazy = self.lazy_imports.items();
2692 const nonlazy = self.extern_nonlazy_symbols.items();2838 const got_entries = self.offset_table.items;
2693 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);2839 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);
2695 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));2841 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26962842
2697 if (needed_size > allocated_size) {2843 if (needed_size > allocated_size) {
...@@ -2710,20 +2856,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2710,20 +2856,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2710 var writer = stream.writer();2856 var writer = stream.writer();
27112857
2712 stubs.reserved1 = 0;2858 stubs.reserved1 = 0;
2713 for (self.extern_lazy_symbols.items()) |_, i| {2859 for (lazy) |_, i| {
2714 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2860 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2715 try writer.writeIntLittle(u32, symtab_idx);2861 try writer.writeIntLittle(u32, symtab_idx);
2716 }2862 }
27172863
2718 const base_id = @intCast(u32, lazy.len);2864 const base_id = @intCast(u32, lazy.len);
2719 got.reserved1 = base_id;2865 got.reserved1 = base_id;
2720 for (self.extern_nonlazy_symbols.items()) |_, i| {2866 for (got_entries) |entry| {
2721 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);2867 switch (entry.kind) {
2722 try writer.writeIntLittle(u32, symtab_idx);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 }
2723 }2876 }
27242877
2725 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);2878 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2726 for (self.extern_lazy_symbols.items()) |_, i| {2879 for (lazy) |_, i| {
2727 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2880 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2728 try writer.writeIntLittle(u32, symtab_idx);2881 try writer.writeIntLittle(u32, symtab_idx);
2729 }2882 }
...@@ -2789,7 +2942,7 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -2789,7 +2942,7 @@ fn writeCodeSignature(self: *MachO) !void {
27892942
2790fn writeExportTrie(self: *MachO) !void {2943fn writeExportTrie(self: *MachO) !void {
2791 if (!self.export_info_dirty) return;2944 if (!self.export_info_dirty) return;
2792 if (self.global_symbols.items.len == 0) return;2945 if (self.globals.items.len == 0) return;
27932946
2794 const tracy = trace(@src());2947 const tracy = trace(@src());
2795 defer tracy.end();2948 defer tracy.end();
...@@ -2798,7 +2951,7 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2798,7 +2951,7 @@ fn writeExportTrie(self: *MachO) !void {
2798 defer trie.deinit();2951 defer trie.deinit();
27992952
2800 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2953 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| {
2802 // TODO figure out if we should put all global symbols into the export trie2955 // TODO figure out if we should put all global symbols into the export trie
2803 const name = self.getString(symbol.n_strx);2956 const name = self.getString(symbol.n_strx);
2804 assert(symbol.n_value >= text_segment.inner.vmaddr);2957 assert(symbol.n_value >= text_segment.inner.vmaddr);
...@@ -2840,14 +2993,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2840,14 +2993,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2840 const tracy = trace(@src());2993 const tracy = trace(@src());
2841 defer tracy.end();2994 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);
2844 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3032 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2845 defer self.base.allocator.free(buffer);3033 defer self.base.allocator.free(buffer);
28463034
2847 var stream = std.io.fixedBufferStream(buffer);3035 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;
2851 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3038 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2852 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);3039 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
2853 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3040 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2872,14 +3059,34 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2872,14 +3059,34 @@ fn writeBindingInfoTable(self: *MachO) !void {
2872 const tracy = trace(@src());3059 const tracy = trace(@src());
2873 defer tracy.end();3060 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);
2876 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3084 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2877 defer self.base.allocator.free(buffer);3085 defer self.base.allocator.free(buffer);
28783086
2879 var stream = std.io.fixedBufferStream(buffer);3087 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;
2883 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3090 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2884 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);3091 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
2885 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3092 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2901,14 +3108,36 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2901,14 +3108,36 @@ fn writeBindingInfoTable(self: *MachO) !void {
2901fn writeLazyBindingInfoTable(self: *MachO) !void {3108fn writeLazyBindingInfoTable(self: *MachO) !void {
2902 if (!self.lazy_binding_info_dirty) return;3109 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);
2905 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3135 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2906 defer self.base.allocator.free(buffer);3136 defer self.base.allocator.free(buffer);
29073137
2908 var stream = std.io.fixedBufferStream(buffer);3138 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;
2912 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3141 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2913 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);3142 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
2914 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3143 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2929,7 +3158,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {...@@ -2929,7 +3158,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
2929}3158}
29303159
2931fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {3160fn 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
2934 var stream = std.io.fixedBufferStream(buffer);3163 var stream = std.io.fixedBufferStream(buffer);
2935 var reader = stream.reader();3164 var reader = stream.reader();
...@@ -2975,7 +3204,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -2975,7 +3204,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2975 else => {},3204 else => {},
2976 }3205 }
2977 }3206 }
2978 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);3207 assert(self.lazy_imports.items().len <= offsets.items.len);
29793208
2980 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {3209 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2981 .x86_64 => 10,3210 .x86_64 => 10,
...@@ -2988,7 +3217,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -2988,7 +3217,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2988 else => unreachable,3217 else => unreachable,
2989 };3218 };
2990 var buf: [@sizeOf(u32)]u8 = undefined;3219 var buf: [@sizeOf(u32)]u8 = undefined;
2991 for (self.extern_lazy_symbols.items()) |_, i| {3220 for (self.lazy_imports.items()) |_, i| {
2992 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;3221 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
2993 mem.writeIntLittle(u32, &buf, offsets.items[i]);3222 mem.writeIntLittle(u32, &buf, offsets.items[i]);
2994 try self.base.file.?.pwriteAll(&buf, placeholder_off);3223 try self.base.file.?.pwriteAll(&buf, placeholder_off);
...@@ -3193,12 +3422,12 @@ fn parseSymbolTable(self: *MachO) !void {...@@ -3193,12 +3422,12 @@ fn parseSymbolTable(self: *MachO) !void {
3193 const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);3422 const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);
3194 assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);3423 assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);
31953424
3196 try self.local_symbols.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);3425 try self.locals.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
3197 try self.global_symbols.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);3426 try self.globals.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
3198 try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);3427 try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);
31993428
3200 self.local_symbols.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);3429 self.locals.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
3201 self.global_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);3430 self.globals.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
3202 self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);3431 self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);
3203}3432}
32043433
src/link/MachO/DebugSymbols.zig+4-4
...@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u...@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
839839
840fn relocateSymbolTable(self: *DebugSymbols) !void {840fn relocateSymbolTable(self: *DebugSymbols) !void {
841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
842 const nlocals = self.base.local_symbols.items.len;842 const nlocals = self.base.locals.items.len;
843 const nglobals = self.base.global_symbols.items.len;843 const nglobals = self.base.globals.items.len;
844 const nsyms = nlocals + nglobals;844 const nsyms = nlocals + nglobals;
845845
846 if (symtab.nsyms < nsyms) {846 if (symtab.nsyms < nsyms) {
...@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {...@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });877 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);
879}879}
880880
881fn writeStringTable(self: *DebugSymbols) !void {881fn writeStringTable(self: *DebugSymbols) !void {
...@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(...@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(
1057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;1057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
1058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;1058 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];
1061 const text_block = &decl.link.macho;1061 const text_block = &decl.link.macho;
1062 // If the Decl is a function, we need to update the __debug_line program.1062 // If the Decl is a function, we need to update the __debug_line program.
1063 const typed_value = decl.typed_value.most_recent.typed_value;1063 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}