authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 00:21:56+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 12:16:36+01:00
loge5234c0e9ee1d60b7a87df8de0350fee2d4e6c55
tree42a31d139b5388c886da523d29543815b2699363
parentb9fa80e5880ed04f0cfd29f753297cbfebf77f2d

macho: offset table part of GOT


3 files changed, 215 insertions(+), 242 deletions(-)

lib/std/macho.zig+8
......@@ -1422,6 +1422,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
14221422pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
14231423pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;
14241424
1425// An indirect symbol table entry is simply a 32bit index into the symbol table
1426// to the symbol that the pointer or stub is refering to. Unless it is for a
1427// non-lazy symbol pointer section for a defined symbol which strip(1) as
1428// removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
1429// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
1430pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000;
1431pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000;
1432
14251433// Codesign consts and structs taken from:
14261434// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html
14271435
src/codegen.zig+57-131
......@@ -2132,9 +2132,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21322132 if (inst.func.value()) |func_value| {
21332133 if (func_value.castTag(.function)) |func_payload| {
21342134 const func = func_payload.data;
2135 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
2136 const got = &text_segment.sections.items[macho_file.got_section_index.?];
2137 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2135 const got_addr = blk: {
2136 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
2137 const got = seg.sections.items[macho_file.got_section_index.?];
2138 break :blk got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2139 };
2140 log.debug("got_addr = 0x{x}", .{got_addr});
21382141 switch (arch) {
21392142 .x86_64 => {
21402143 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });
......@@ -3303,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
33033306 },
33043307 .memory => |addr| {
33053308 if (self.bin_file.options.pie) {
3306 // For MachO, the binary, with the exception of object files, has to be a PIE.
3307 // Therefore we cannot load an absolute address.
3308 // Instead, we need to make use of PC-relative addressing.
3309 if (reg.id() == 0) { // x0 is special-cased
3310 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack
3311 // like described here:
3312 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)
3313 // str x28, [sp, #-16]
3314 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
3315 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),
3316 }).toU32());
3317 // adr x28, #8
3318 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3319 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3320 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3321 .address = addr,
3322 .start = self.code.items.len,
3323 .len = 4,
3324 });
3325 } else {
3326 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3327 }
3328 // b [label]
3329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3330 // mov r, x0
3331 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3332 reg,
3333 .xzr,
3334 .x0,
3335 Instruction.Shift.none,
3336 ).toU32());
3337 // ldr x28, [sp], #16
3338 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
3339 .register = .{
3340 .rn = Register.sp,
3341 .offset = Instruction.LoadStoreOffset.imm_post_index(16),
3342 },
3343 }).toU32());
3309 // PC-relative displacement to the entry in the GOT table.
3310 // TODO we should come up with our own, backend independent relocation types
3311 // which each backend (Elf, MachO, etc.) would then translate into an actual
3312 // fixup when linking.
3313 // adrp reg, pages
3314 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3315 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3316 .target_addr = addr,
3317 .offset = self.code.items.len,
3318 .size = 4,
3319 });
33443320 } else {
3345 // stp x0, x28, [sp, #-16]
3346 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(
3347 .x0,
3348 .x28,
3349 Register.sp,
3350 Instruction.LoadStorePairOffset.pre_index(-16),
3351 ).toU32());
3352 // adr x28, #8
3353 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3354 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3355 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3356 .address = addr,
3357 .start = self.code.items.len,
3358 .len = 4,
3359 });
3360 } else {
3361 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3362 }
3363 // b [label]
3364 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3365 // mov r, x0
3366 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3367 reg,
3368 .xzr,
3369 .x0,
3370 Instruction.Shift.none,
3371 ).toU32());
3372 // ldp x0, x28, [sp, #16]
3373 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
3374 .x0,
3375 .x28,
3376 Register.sp,
3377 Instruction.LoadStorePairOffset.post_index(16),
3378 ).toU32());
3321 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
33793322 }
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());
33803335 } else {
33813336 // The value is in memory at a hard-coded address.
33823337 // If the type is a pointer, it means the pointer address is at this memory location.
......@@ -3560,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35603515 },
35613516 .memory => |x| {
35623517 if (self.bin_file.options.pie) {
3563 // For MachO, the binary, with the exception of object files, has to be a PIE.
3564 // Therefore, we cannot load an absolute address.
3565 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.
3566 // The plan here is to use unconditional relative jump to GOT entry, where we store
3567 // pre-calculated and stored effective address to load into the target register.
3568 // We leave the actual displacement information empty (0-padded) and fixing it up
3569 // later in the linker.
3570 if (reg.id() == 0) { // %rax is special-cased
3571 try self.code.ensureCapacity(self.code.items.len + 5);
3572 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3573 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3574 .address = x,
3575 .start = self.code.items.len,
3576 .len = 5,
3577 });
3578 } else {
3579 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3580 }
3581 // call [label]
3582 self.code.appendSliceAssumeCapacity(&[_]u8{
3583 0xE8,
3584 0x0,
3585 0x0,
3586 0x0,
3587 0x0,
3518 // RIP-relative displacement to the entry in the GOT table.
3519 // TODO we should come up with our own, backend independent relocation types
3520 // which each backend (Elf, MachO, etc.) would then translate into an actual
3521 // fixup when linking.
3522 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3523 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3524 .target_addr = x,
3525 .offset = self.code.items.len + 3,
3526 .size = 4,
35883527 });
35893528 } else {
3590 try self.code.ensureCapacity(self.code.items.len + 10);
3591 // push %rax
3592 self.code.appendSliceAssumeCapacity(&[_]u8{0x50});
3593 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3594 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3595 .address = x,
3596 .start = self.code.items.len,
3597 .len = 5,
3598 });
3599 } else {
3600 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3601 }
3602 // call [label]
3603 self.code.appendSliceAssumeCapacity(&[_]u8{
3604 0xE8,
3605 0x0,
3606 0x0,
3607 0x0,
3608 0x0,
3609 });
3610 // mov %r, %rax
3611 self.code.appendSliceAssumeCapacity(&[_]u8{
3612 0x48,
3613 0x89,
3614 0xC0 | @as(u8, reg.id()),
3615 });
3616 // pop %rax
3617 self.code.appendSliceAssumeCapacity(&[_]u8{0x58});
3529 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
36183530 }
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 });
36193543 } else if (x <= math.maxInt(u32)) {
36203544 // Moving from memory to a register is a variant of `8B /r`.
36213545 // Since we're using 64-bit moves, we require a REX.
......@@ -3778,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37783702 return MCValue{ .memory = got_addr };
37793703 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
37803704 const decl = payload.data;
3781 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
3782 const got = &text_segment.sections.items[macho_file.got_section_index.?];
3783 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3705 const got_addr = blk: {
3706 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
3707 const got = seg.sections.items[macho_file.got_section_index.?];
3708 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3709 };
37843710 return MCValue{ .memory = got_addr };
37853711 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
37863712 const decl = payload.data;
src/link/MachO.zig+150-111
......@@ -11,6 +11,7 @@ const codegen = @import("../codegen.zig");
1111const aarch64 = @import("../codegen/aarch64.zig");
1212const math = std.math;
1313const mem = std.mem;
14const meta = std.meta;
1415
1516const bind = @import("MachO/bind.zig");
1617const trace = @import("../tracy.zig").trace;
......@@ -87,14 +88,12 @@ code_signature_cmd_index: ?u16 = null,
8788
8889/// Index into __TEXT,__text section.
8990text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
9291/// Index into __TEXT,__stubs section.
9392stubs_section_index: ?u16 = null,
9493/// Index into __TEXT,__stub_helper section.
9594stub_helper_section_index: ?u16 = null,
9695/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,
96got_section_index: ?u16 = null,
9897/// Index into __DATA,__la_symbol_ptr section.
9998la_symbol_ptr_section_index: ?u16 = null,
10099/// Index into __DATA,__data section.
......@@ -122,8 +121,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122121string_table: std.ArrayListUnmanaged(u8) = .{},
123122string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124123
125/// Table of trampolines to the actual symbols in __text section.
126offset_table: std.ArrayListUnmanaged(u64) = .{},
124/// Table of GOT entries.
125offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127126
128127error_flags: File.ErrorFlags = File.ErrorFlags{},
129128
......@@ -154,14 +153,19 @@ string_table_needs_relocation: bool = false,
154153/// allocate a fresh text block, which will have ideal capacity, and then grow it
155154/// by 1 byte. It will then have -1 overcapacity.
156155text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
156
157157/// Pointer to the last allocated text block
158158last_text_block: ?*TextBlock = null,
159
159160/// A list of all PIE fixups required for this run of the linker.
160161/// Warning, this is currently NOT thread-safe. See the TODO below.
161162/// TODO Move this list inside `updateDecl` where it should be allocated
162163/// prior to calling `generateSymbol`, and then immediately deallocated
163164/// rather than sitting in the global scope.
164pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
165/// TODO We should also rewrite this using generic relocations common to all
166/// backends.
167pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
168
165169/// A list of all stub (extern decls) fixups required for this run of the linker.
166170/// Warning, this is currently NOT thread-safe. See the TODO below.
167171/// TODO Move this list inside `updateDecl` where it should be allocated
......@@ -169,6 +173,22 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169173/// rather than sitting in the global scope.
170174stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171175
176pub const GOTEntry = struct {
177 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
178 kind: enum {
179 Local,
180 Extern,
181 },
182
183 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
184 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
185 /// rather than 4 as we currently do, but I'll follow up in the future PR.
186 symbol: u32,
187
188 /// Index of this entry in the GOT.
189 index: u32,
190};
191
172192pub const Import = struct {
173193 /// MachO symbol table entry.
174194 symbol: macho.nlist_64,
......@@ -180,14 +200,15 @@ pub const Import = struct {
180200 index: u32,
181201};
182202
183pub const PieFixup = struct {
184 /// Target address we wanted to address in absolute terms.
185 address: u64,
186 /// Where in the byte stream we should perform the fixup.
187 start: usize,
188 /// The length of the byte stream. For x86_64, this will be
189 /// variable. For aarch64, it will be fixed at 4 bytes.
190 len: usize,
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,
191212};
192213
193214pub const StubFixup = struct {
......@@ -1132,11 +1153,14 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11321153 }
11331154
11341155 if (self.offset_table_free_list.popOrNull()) |i| {
1156 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
11351157 decl.link.macho.offset_table_index = i;
11361158 } else {
1159 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
11371160 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
11381161 _ = self.offset_table.addOneAssumeCapacity();
11391162 self.offset_table_count_dirty = true;
1163 self.rebase_info_dirty = true;
11401164 }
11411165
11421166 self.locals.items[decl.link.macho.local_sym_index] = .{
......@@ -1146,7 +1170,11 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11461170 .n_desc = 0,
11471171 .n_value = 0,
11481172 };
1149 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 };
11501178}
11511179
11521180pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
......@@ -1189,8 +1217,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11891217 .externally_managed => |x| x,
11901218 .appended => code_buffer.items,
11911219 .fail => |em| {
1192 // Clear any PIE fixups and stub fixups for this decl.
1220 // Clear any PIE fixups for this decl.
11931221 self.pie_fixups.shrinkRetainingCapacity(0);
1222 // Clear any stub fixups for this decl.
11941223 self.stub_fixups.shrinkRetainingCapacity(0);
11951224 decl.analysis = .codegen_failure;
11961225 try module.failed_decls.put(module.gpa, decl, em);
......@@ -1209,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12091238 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
12101239 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
12111240 if (vaddr != symbol.n_value) {
1212 symbol.n_value = vaddr;
12131241 log.debug(" (writing new offset table entry)", .{});
1214 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 };
12151247 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12161248 }
12171249 } else if (code.len < decl.link.macho.size) {
......@@ -1240,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12401272 .n_desc = 0,
12411273 .n_value = addr,
12421274 };
1243 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 };
12441280
12451281 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
12461282 if (self.d_sym) |*ds|
......@@ -1248,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12481284 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12491285 }
12501286
1251 // Perform PIE fixups (if any)
1252 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1253 const got_section = text_segment.sections.items[self.got_section_index.?];
1287 // Calculate displacements to target addr (if any).
12541288 while (self.pie_fixups.popOrNull()) |fixup| {
1255 const target_addr = fixup.address;
1256 const this_addr = symbol.n_value + fixup.start;
1289 assert(fixup.size == 4);
1290 const this_addr = symbol.n_value + fixup.offset;
1291 const target_addr = fixup.target_addr;
1292
12571293 switch (self.base.options.target.cpu.arch) {
12581294 .x86_64 => {
1259 assert(target_addr >= this_addr + fixup.len);
1260 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);
1261 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1262 mem.writeIntSliceLittle(u32, placeholder, displacement);
1295 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1296 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
12631297 },
12641298 .aarch64 => {
1265 assert(target_addr >= this_addr);
1266 const displacement = try math.cast(u27, target_addr - this_addr);
1267 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1268 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());
1299 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1300 {
1301 const inst = code_buffer.items[fixup.offset..][0..4];
1302 var parsed = mem.bytesAsValue(meta.TagPayload(
1303 aarch64.Instruction,
1304 aarch64.Instruction.PCRelativeAddress,
1305 ), inst);
1306 const this_page = @intCast(i32, this_addr >> 12);
1307 const target_page = @intCast(i32, target_addr >> 12);
1308 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1309 parsed.immhi = @truncate(u19, pages >> 2);
1310 parsed.immlo = @truncate(u2, pages);
1311 }
1312 {
1313 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
1314 var parsed = mem.bytesAsValue(meta.TagPayload(
1315 aarch64.Instruction,
1316 aarch64.Instruction.LoadStoreRegister,
1317 ), inst);
1318 const narrowed = @truncate(u12, target_addr);
1319 const offset = try math.divExact(u12, narrowed, 8);
1320 parsed.offset = offset;
1321 }
12691322 },
12701323 else => unreachable, // unsupported target architecture
12711324 }
12721325 }
12731326
12741327 // Resolve stubs (if any)
1328 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
12751329 const stubs = text_segment.sections.items[self.stubs_section_index.?];
12761330 for (self.stub_fixups.items) |fixup| {
12771331 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
......@@ -1561,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15611615 self.header_dirty = true;
15621616 self.load_commands_dirty = true;
15631617 }
1564 if (self.got_section_index == null) {
1565 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1566 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
1567
1568 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1569 .x86_64 => 0,
1570 .aarch64 => 2,
1571 else => unreachable, // unhandled architecture type
1572 };
1573 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1574 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1575 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
1576 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
1577
1578 log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1579
1580 try text_segment.addSection(self.base.allocator, .{
1581 .sectname = makeStaticString("__ziggot"),
1582 .segname = makeStaticString("__TEXT"),
1583 .addr = text_segment.inner.vmaddr + off,
1584 .size = needed_size,
1585 .offset = @intCast(u32, off),
1586 .@"align" = alignment,
1587 .reloff = 0,
1588 .nreloc = 0,
1589 .flags = flags,
1590 .reserved1 = 0,
1591 .reserved2 = 0,
1592 .reserved3 = 0,
1593 });
1594 self.header_dirty = true;
1595 self.load_commands_dirty = true;
1596 }
15971618 if (self.stubs_section_index == null) {
15981619 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
15991620 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
......@@ -1694,9 +1715,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16941715 self.header_dirty = true;
16951716 self.load_commands_dirty = true;
16961717 }
1697 if (self.data_got_section_index == null) {
1718 if (self.got_section_index == null) {
16981719 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1699 self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len);
1720 self.got_section_index = @intCast(u16, dc_segment.sections.items.len);
17001721
17011722 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
17021723 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
......@@ -2083,6 +2104,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20832104 .dylib_ordinal = 1, // TODO this is currently hardcoded.
20842105 .index = index,
20852106 });
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);
20862114 self.binding_info_dirty = true;
20872115 }
20882116 if (self.stub_helper_stubs_start_off == null) {
......@@ -2412,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
24122440}
24132441
24142442fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2415 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2416 const sect = &text_segment.sections.items[self.got_section_index.?];
2443 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2444 const sect = &seg.sections.items[self.got_section_index.?];
24172445 const off = sect.offset + @sizeOf(u64) * index;
2418 const vmaddr = sect.addr + @sizeOf(u64) * index;
24192446
24202447 if (self.offset_table_count_dirty) {
24212448 // TODO relocate.
24222449 self.offset_table_count_dirty = false;
24232450 }
24242451
2425 var code: [8]u8 = undefined;
2426 switch (self.base.options.target.cpu.arch) {
2427 .x86_64 => {
2428 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);
2429 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);
2430 // lea %rax, [rip - disp]
2431 code[0] = 0x48;
2432 code[1] = 0x8D;
2433 code[2] = 0x5;
2434 mem.writeIntLittle(u32, code[3..7], symbol_off);
2435 // ret
2436 code[7] = 0xC3;
2437 },
2438 .aarch64 => {
2439 const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]);
2440 const symbol_off = @as(i21, pos_symbol_off) * -1;
2441 // adr x0, #-disp
2442 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
2443 // ret x28
2444 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
2445 },
2446 else => unreachable, // unsupported target architecture
2447 }
2448 log.debug("writing offset table entry 0x{x} at 0x{x}", .{ self.offset_table.items[index], off });
2449 try self.base.file.?.pwriteAll(&code, off);
2452 const got_entry = self.offset_table.items[index];
2453 const sym = blk: {
2454 switch (got_entry.kind) {
2455 .Local => {
2456 break :blk self.locals.items[got_entry.symbol];
2457 },
2458 .Extern => {
2459 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2460 },
2461 }
2462 };
2463 const sym_name = self.getString(sym.n_strx);
2464 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2465 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
24502466}
24512467
24522468fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
......@@ -2473,7 +2489,7 @@ fn writeStubHelperPreamble(self: *MachO) !void {
24732489 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
24742490 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
24752491 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2476 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2492 const got = &data_const_segment.sections.items[self.got_section_index.?];
24772493 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
24782494 const data = &data_segment.sections.items[self.data_section_index.?];
24792495
......@@ -2813,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
28132829 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
28142830 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
28152831 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2816 const got = &data_const_seg.sections.items[self.data_got_section_index.?];
2832 const got = &data_const_seg.sections.items[self.got_section_index.?];
28172833 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
28182834 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
28192835 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
28202836
28212837 const lazy = self.lazy_imports.items();
2822 const nonlazy = self.nonlazy_imports.items();
2838 const got_entries = self.offset_table.items;
28232839 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
2824 const nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len);
2840 const nindirectsyms = @intCast(u32, lazy.len * 2 + got_entries.len);
28252841 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
28262842
28272843 if (needed_size > allocated_size) {
......@@ -2847,12 +2863,19 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
28472863
28482864 const base_id = @intCast(u32, lazy.len);
28492865 got.reserved1 = base_id;
2850 for (nonlazy) |_, i| {
2851 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2852 try writer.writeIntLittle(u32, symtab_idx);
2866 for (got_entries) |entry| {
2867 switch (entry.kind) {
2868 .Local => {
2869 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2870 },
2871 .Extern => {
2872 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id);
2873 try writer.writeIntLittle(u32, symtab_idx);
2874 },
2875 }
28532876 }
28542877
2855 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);
2878 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
28562879 for (lazy) |_, i| {
28572880 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
28582881 try writer.writeIntLittle(u32, symtab_idx);
......@@ -2973,12 +2996,27 @@ fn writeRebaseInfoTable(self: *MachO) !void {
29732996 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
29742997 defer pointers.deinit();
29752998
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
29763014 if (self.la_symbol_ptr_section_index) |idx| {
29773015 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
29783016 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
29793017 const sect = seg.sections.items[idx];
29803018 const base_offset = sect.addr - seg.inner.vmaddr;
2981 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
3019 const segment_id = self.data_segment_cmd_index.?;
29823020
29833021 for (self.lazy_imports.items()) |entry| {
29843022 pointers.appendAssumeCapacity(.{
......@@ -3024,19 +3062,20 @@ fn writeBindingInfoTable(self: *MachO) !void {
30243062 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
30253063 defer pointers.deinit();
30263064
3027 if (self.data_got_section_index) |idx| {
3028 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len);
3065 if (self.got_section_index) |idx| {
30293066 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
30303067 const sect = seg.sections.items[idx];
30313068 const base_offset = sect.addr - seg.inner.vmaddr;
30323069 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
30333070
3034 for (self.nonlazy_imports.items()) |entry| {
3035 pointers.appendAssumeCapacity(.{
3036 .offset = base_offset + entry.value.index * @sizeOf(u64),
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),
30373076 .segment_id = segment_id,
3038 .dylib_ordinal = entry.value.dylib_ordinal,
3039 .name = entry.key,
3077 .dylib_ordinal = import.value.dylib_ordinal,
3078 .name = import.key,
30403079 });
30413080 }
30423081 }