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;...@@ -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+57-131
...@@ -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 });
...@@ -3303,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3303,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3303 },3306 },
3304 .memory => |addr| {3307 .memory => |addr| {
3305 if (self.bin_file.options.pie) {3308 if (self.bin_file.options.pie) {
3306 // 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.
3307 // Therefore we cannot load an absolute address.3310 // TODO we should come up with our own, backend independent relocation types
3308 // Instead, we need to make use of PC-relative addressing.3311 // which each backend (Elf, MachO, etc.) would then translate into an actual
3309 if (reg.id() == 0) { // x0 is special-cased3312 // fixup when linking.
3310 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack3313 // adrp reg, pages
3311 // like described here:3314 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3312 // 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, .{
3313 // str x28, [sp, #-16]3316 .target_addr = addr,
3314 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{3317 .offset = self.code.items.len,
3315 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),3318 .size = 4,
3316 }).toU32());3319 });
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());
3344 } else {3320 } else {
3345 // stp x0, x28, [sp, #-16]3321 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
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());
3379 }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());
3380 } else {3335 } else {
3381 // The value is in memory at a hard-coded address.3336 // The value is in memory at a hard-coded address.
3382 // 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.
...@@ -3560,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3560,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3560 },3515 },
3561 .memory => |x| {3516 .memory => |x| {
3562 if (self.bin_file.options.pie) {3517 if (self.bin_file.options.pie) {
3563 // 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.
3564 // Therefore, we cannot load an absolute address.3519 // TODO we should come up with our own, backend independent relocation types
3565 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
3566 // The plan here is to use unconditional relative jump to GOT entry, where we store3521 // fixup when linking.
3567 // pre-calculated and stored effective address to load into the target register.3522 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3568 // We leave the actual displacement information empty (0-padded) and fixing it up3523 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3569 // later in the linker.3524 .target_addr = x,
3570 if (reg.id() == 0) { // %rax is special-cased3525 .offset = self.code.items.len + 3,
3571 try self.code.ensureCapacity(self.code.items.len + 5);3526 .size = 4,
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,
3588 });3527 });
3589 } else {3528 } else {
3590 try self.code.ensureCapacity(self.code.items.len + 10);3529 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
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});
3618 }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 });
3619 } else if (x <= math.maxInt(u32)) {3543 } else if (x <= math.maxInt(u32)) {
3620 // 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`.
3621 // Since we're using 64-bit moves, we require a REX.3545 // Since we're using 64-bit moves, we require a REX.
...@@ -3778,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3778,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3778 return MCValue{ .memory = got_addr };3702 return MCValue{ .memory = got_addr };
3779 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {3703 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3780 const decl = payload.data;3704 const decl = payload.data;
3781 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;3705 const got_addr = blk: {
3782 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;
3783 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 };
3784 return MCValue{ .memory = got_addr };3710 return MCValue{ .memory = got_addr };
3785 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {3711 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
3786 const decl = payload.data;3712 const decl = payload.data;
src/link/MachO.zig+150-111
...@@ -11,6 +11,7 @@ const codegen = @import("../codegen.zig");...@@ -11,6 +11,7 @@ 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
15const bind = @import("MachO/bind.zig");16const bind = @import("MachO/bind.zig");
16const trace = @import("../tracy.zig").trace;17const trace = @import("../tracy.zig").trace;
...@@ -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.
...@@ -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,6 +173,22 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},...@@ -169,6 +173,22 @@ 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
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
172pub const Import = struct {192pub const Import = struct {
173 /// MachO symbol table entry.193 /// MachO symbol table entry.
174 symbol: macho.nlist_64,194 symbol: macho.nlist_64,
...@@ -180,14 +200,15 @@ pub const Import = struct {...@@ -180,14 +200,15 @@ pub const Import = struct {
180 index: u32,200 index: u32,
181};201};
182202
183pub const PieFixup = struct {203pub const PIEFixup = struct {
184 /// Target address we wanted to address in absolute terms.204 /// Target VM address of this relocation.
185 address: u64,205 target_addr: u64,
186 /// Where in the byte stream we should perform the fixup.206
187 start: usize,207 /// Offset within the byte stream.
188 /// The length of the byte stream. For x86_64, this will be208 offset: usize,
189 /// variable. For aarch64, it will be fixed at 4 bytes.209
190 len: usize,210 /// Size of the relocation.
211 size: usize,
191};212};
192213
193pub const StubFixup = struct {214pub const StubFixup = struct {
...@@ -1132,11 +1153,14 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {...@@ -1132,11 +1153,14 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
1132 }1153 }
11331154
1134 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 });
1135 decl.link.macho.offset_table_index = i;1157 decl.link.macho.offset_table_index = i;
1136 } else {1158 } else {
1159 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
1137 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);
1138 _ = self.offset_table.addOneAssumeCapacity();1161 _ = self.offset_table.addOneAssumeCapacity();
1139 self.offset_table_count_dirty = true;1162 self.offset_table_count_dirty = true;
1163 self.rebase_info_dirty = true;
1140 }1164 }
11411165
1142 self.locals.items[decl.link.macho.local_sym_index] = .{1166 self.locals.items[decl.link.macho.local_sym_index] = .{
...@@ -1146,7 +1170,11 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {...@@ -1146,7 +1170,11 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
1146 .n_desc = 0,1170 .n_desc = 0,
1147 .n_value = 0,1171 .n_value = 0,
1148 };1172 };
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 };
1150}1178}
11511179
1152pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {1180pub 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 {...@@ -1189,8 +1217,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1189 .externally_managed => |x| x,1217 .externally_managed => |x| x,
1190 .appended => code_buffer.items,1218 .appended => code_buffer.items,
1191 .fail => |em| {1219 .fail => |em| {
1192 // Clear any PIE fixups and stub fixups for this decl.1220 // Clear any PIE fixups for this decl.
1193 self.pie_fixups.shrinkRetainingCapacity(0);1221 self.pie_fixups.shrinkRetainingCapacity(0);
1222 // Clear any stub fixups for this decl.
1194 self.stub_fixups.shrinkRetainingCapacity(0);1223 self.stub_fixups.shrinkRetainingCapacity(0);
1195 decl.analysis = .codegen_failure;1224 decl.analysis = .codegen_failure;
1196 try module.failed_decls.put(module.gpa, decl, em);1225 try module.failed_decls.put(module.gpa, decl, em);
...@@ -1209,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1209,9 +1238,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1209 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);
1210 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 });
1211 if (vaddr != symbol.n_value) {1240 if (vaddr != symbol.n_value) {
1212 symbol.n_value = vaddr;
1213 log.debug(" (writing new offset table entry)", .{});1241 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 };
1215 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1247 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1216 }1248 }
1217 } else if (code.len < decl.link.macho.size) {1249 } else if (code.len < decl.link.macho.size) {
...@@ -1240,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1240,7 +1272,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1240 .n_desc = 0,1272 .n_desc = 0,
1241 .n_value = addr,1273 .n_value = addr,
1242 };1274 };
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
1245 try self.writeLocalSymbol(decl.link.macho.local_sym_index);1281 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1246 if (self.d_sym) |*ds|1282 if (self.d_sym) |*ds|
...@@ -1248,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1248,30 +1284,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1248 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1284 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1249 }1285 }
12501286
1251 // Perform PIE fixups (if any)1287 // Calculate displacements to target addr (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.?];
1254 while (self.pie_fixups.popOrNull()) |fixup| {1288 while (self.pie_fixups.popOrNull()) |fixup| {
1255 const target_addr = fixup.address;1289 assert(fixup.size == 4);
1256 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
1257 switch (self.base.options.target.cpu.arch) {1293 switch (self.base.options.target.cpu.arch) {
1258 .x86_64 => {1294 .x86_64 => {
1259 assert(target_addr >= this_addr + fixup.len);1295 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1260 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);1296 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
1261 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1262 mem.writeIntSliceLittle(u32, placeholder, displacement);
1263 },1297 },
1264 .aarch64 => {1298 .aarch64 => {
1265 assert(target_addr >= this_addr);1299 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1266 const displacement = try math.cast(u27, target_addr - this_addr);1300 {
1267 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1301 const inst = code_buffer.items[fixup.offset..][0..4];
1268 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 }
1269 },1322 },
1270 else => unreachable, // unsupported target architecture1323 else => unreachable, // unsupported target architecture
1271 }1324 }
1272 }1325 }
12731326
1274 // Resolve stubs (if any)1327 // Resolve stubs (if any)
1328 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1275 const stubs = text_segment.sections.items[self.stubs_section_index.?];1329 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1276 for (self.stub_fixups.items) |fixup| {1330 for (self.stub_fixups.items) |fixup| {
1277 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;1331 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
...@@ -1561,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1561,39 +1615,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1561 self.header_dirty = true;1615 self.header_dirty = true;
1562 self.load_commands_dirty = true;1616 self.load_commands_dirty = true;
1563 }1617 }
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 }
1597 if (self.stubs_section_index == null) {1618 if (self.stubs_section_index == null) {
1598 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;
1599 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);1620 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
...@@ -1694,9 +1715,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1694,9 +1715,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1694 self.header_dirty = true;1715 self.header_dirty = true;
1695 self.load_commands_dirty = true;1716 self.load_commands_dirty = true;
1696 }1717 }
1697 if (self.data_got_section_index == null) {1718 if (self.got_section_index == null) {
1698 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;
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
1701 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;1722 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
1702 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1723 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
...@@ -2083,6 +2104,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2083,6 +2104,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2083 .dylib_ordinal = 1, // TODO this is currently hardcoded.2104 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2084 .index = index,2105 .index = index,
2085 });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);
2086 self.binding_info_dirty = true;2114 self.binding_info_dirty = true;
2087 }2115 }
2088 if (self.stub_helper_stubs_start_off == null) {2116 if (self.stub_helper_stubs_start_off == null) {
...@@ -2412,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta...@@ -2412,41 +2440,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
2412}2440}
24132441
2414fn writeOffsetTableEntry(self: *MachO, index: usize) !void {2442fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2415 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;
2416 const sect = &text_segment.sections.items[self.got_section_index.?];2444 const sect = &seg.sections.items[self.got_section_index.?];
2417 const off = sect.offset + @sizeOf(u64) * index;2445 const off = sect.offset + @sizeOf(u64) * index;
2418 const vmaddr = sect.addr + @sizeOf(u64) * index;
24192446
2420 if (self.offset_table_count_dirty) {2447 if (self.offset_table_count_dirty) {
2421 // TODO relocate.2448 // TODO relocate.
2422 self.offset_table_count_dirty = false;2449 self.offset_table_count_dirty = false;
2423 }2450 }
24242451
2425 var code: [8]u8 = undefined;2452 const got_entry = self.offset_table.items[index];
2426 switch (self.base.options.target.cpu.arch) {2453 const sym = blk: {
2427 .x86_64 => {2454 switch (got_entry.kind) {
2428 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);2455 .Local => {
2429 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);2456 break :blk self.locals.items[got_entry.symbol];
2430 // lea %rax, [rip - disp]2457 },
2431 code[0] = 0x48;2458 .Extern => {
2432 code[1] = 0x8D;2459 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2433 code[2] = 0x5;2460 },
2434 mem.writeIntLittle(u32, code[3..7], symbol_off);2461 }
2435 // ret2462 };
2436 code[7] = 0xC3;2463 const sym_name = self.getString(sym.n_strx);
2437 },2464 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2438 .aarch64 => {2465 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
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);
2450}2466}
24512467
2452fn writeLazySymbolPointer(self: *MachO, index: u32) !void {2468fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
...@@ -2473,7 +2489,7 @@ fn writeStubHelperPreamble(self: *MachO) !void {...@@ -2473,7 +2489,7 @@ fn writeStubHelperPreamble(self: *MachO) !void {
2473 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2489 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2474 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];2490 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2475 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2491 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.?];
2477 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2493 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2478 const data = &data_segment.sections.items[self.data_section_index.?];2494 const data = &data_segment.sections.items[self.data_section_index.?];
24792495
...@@ -2813,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2813,15 +2829,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2813 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;
2814 const stubs = &text_segment.sections.items[self.stubs_section_index.?];2830 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2815 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;
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.?];
2817 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;
2818 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.?];
2819 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2835 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
28202836
2821 const lazy = self.lazy_imports.items();2837 const lazy = self.lazy_imports.items();
2822 const nonlazy = self.nonlazy_imports.items();2838 const got_entries = self.offset_table.items;
2823 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);2839 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);
2825 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));2841 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
28262842
2827 if (needed_size > allocated_size) {2843 if (needed_size > allocated_size) {
...@@ -2847,12 +2863,19 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2847,12 +2863,19 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
28472863
2848 const base_id = @intCast(u32, lazy.len);2864 const base_id = @intCast(u32, lazy.len);
2849 got.reserved1 = base_id;2865 got.reserved1 = base_id;
2850 for (nonlazy) |_, i| {2866 for (got_entries) |entry| {
2851 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);2867 switch (entry.kind) {
2852 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 }
2853 }2876 }
28542877
2855 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);2878 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2856 for (lazy) |_, i| {2879 for (lazy) |_, i| {
2857 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2880 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2858 try writer.writeIntLittle(u32, symtab_idx);2881 try writer.writeIntLittle(u32, symtab_idx);
...@@ -2973,12 +2996,27 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2973,12 +2996,27 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2973 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);2996 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
2974 defer pointers.deinit();2997 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
2976 if (self.la_symbol_ptr_section_index) |idx| {3014 if (self.la_symbol_ptr_section_index) |idx| {
2977 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);3015 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2978 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;3016 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2979 const sect = seg.sections.items[idx];3017 const sect = seg.sections.items[idx];
2980 const base_offset = sect.addr - seg.inner.vmaddr;3018 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
2983 for (self.lazy_imports.items()) |entry| {3021 for (self.lazy_imports.items()) |entry| {
2984 pointers.appendAssumeCapacity(.{3022 pointers.appendAssumeCapacity(.{
...@@ -3024,19 +3062,20 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -3024,19 +3062,20 @@ fn writeBindingInfoTable(self: *MachO) !void {
3024 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);3062 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3025 defer pointers.deinit();3063 defer pointers.deinit();
30263064
3027 if (self.data_got_section_index) |idx| {3065 if (self.got_section_index) |idx| {
3028 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len);
3029 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;3066 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3030 const sect = seg.sections.items[idx];3067 const sect = seg.sections.items[idx];
3031 const base_offset = sect.addr - seg.inner.vmaddr;3068 const base_offset = sect.addr - seg.inner.vmaddr;
3032 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);3069 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
30333070
3034 for (self.nonlazy_imports.items()) |entry| {3071 for (self.offset_table.items) |entry| {
3035 pointers.appendAssumeCapacity(.{3072 if (entry.kind == .Local) continue;
3036 .offset = base_offset + entry.value.index * @sizeOf(u64),3073 const import = self.nonlazy_imports.items()[entry.symbol];
3074 try pointers.append(.{
3075 .offset = base_offset + entry.index * @sizeOf(u64),
3037 .segment_id = segment_id,3076 .segment_id = segment_id,
3038 .dylib_ordinal = entry.value.dylib_ordinal,3077 .dylib_ordinal = import.value.dylib_ordinal,
3039 .name = entry.key,3078 .name = import.key,
3040 });3079 });
3041 }3080 }
3042 }3081 }