authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-27 21:49:06+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-11-27 21:49:06+01:00
log3bc1c719bd632e6baccb574bf75cb93f0bdef08f
tree288fea5f073ec13c4b435a853e40f7c1d1e90ab4
parentdaf6c0da5b2d96530dea2d2a907f2b2ba55c8ba6
parent5ed76268c9f0ecca8c5d62cb7e56da05aa8f1f7c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7231 from kubkon/stage2-arm-macos

stage2: Hello, Silicon!\n

8 files changed, 749 insertions(+), 209 deletions(-)

lib/std/macho.zig+21-11
......@@ -1384,10 +1384,10 @@ pub const CSTYPE_INDEX_REQUIREMENTS: u32 = 0x00000002;
13841384/// Compat with amfi
13851385pub const CSTYPE_INDEX_ENTITLEMENTS: u32 = 0x00000005;
13861386
1387pub const CS_HASHTYPE_SHA1: u32 = 1;
1388pub const CS_HASHTYPE_SHA256: u32 = 2;
1389pub const CS_HASHTYPE_SHA256_TRUNCATED: u32 = 3;
1390pub const CS_HASHTYPE_SHA384: u32 = 4;
1387pub const CS_HASHTYPE_SHA1: u8 = 1;
1388pub const CS_HASHTYPE_SHA256: u8 = 2;
1389pub const CS_HASHTYPE_SHA256_TRUNCATED: u8 = 3;
1390pub const CS_HASHTYPE_SHA384: u8 = 4;
13911391
13921392pub const CS_SHA1_LEN: u32 = 20;
13931393pub const CS_SHA256_LEN: u32 = 32;
......@@ -1402,6 +1402,10 @@ pub const CS_SIGNER_TYPE_UNKNOWN: u32 = 0;
14021402pub const CS_SIGNER_TYPE_LEGACYVPN: u32 = 5;
14031403pub const CS_SIGNER_TYPE_MAC_APP_STORE: u32 = 6;
14041404
1405pub const CS_ADHOC: u32 = 0x2;
1406
1407pub const CS_EXECSEG_MAIN_BINARY: u32 = 0x1;
1408
14051409/// This CodeDirectory is tailored specfically at version 0x20400.
14061410pub const CodeDirectory = extern struct {
14071411 /// Magic number (CSMAGIC_CODEDIRECTORY)
......@@ -1446,6 +1450,18 @@ pub const CodeDirectory = extern struct {
14461450 /// Unused (must be zero)
14471451 spare2: u32,
14481452
1453 ///
1454 scatterOffset: u32,
1455
1456 ///
1457 teamOffset: u32,
1458
1459 ///
1460 spare3: u32,
1461
1462 ///
1463 codeLimit64: u64,
1464
14491465 /// Offset of executable segment
14501466 execSegBase: u64,
14511467
......@@ -1453,9 +1469,7 @@ pub const CodeDirectory = extern struct {
14531469 execSegLimit: u64,
14541470
14551471 /// Executable segment flags
1456 execSegFlags,
1457
1458 // end_withExecSeg: [*]u8,
1472 execSegFlags: u64,
14591473};
14601474
14611475/// Structure of an embedded-signature SuperBlob
......@@ -1478,8 +1492,6 @@ pub const SuperBlob = extern struct {
14781492
14791493 /// Number of index BlobIndex entries following this struct
14801494 count: u32,
1481
1482 // index: []const BlobIndex,
14831495};
14841496
14851497pub const GenericBlob = extern struct {
......@@ -1488,6 +1500,4 @@ pub const GenericBlob = extern struct {
14881500
14891501 /// Total length of blob
14901502 length: u32,
1491
1492 // data: []const u8,
14931503};
src/codegen.zig+135-8
......@@ -1683,15 +1683,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16831683 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
16841684 switch (arch) {
16851685 .x86_64 => {
1686 // Here, we store the got address in %rax, and then call %rax
1687 // movabsq [addr], %rax
16881686 try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr });
16891687 // callq *%rax
1690 try self.code.ensureCapacity(self.code.items.len + 2);
16911688 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
16921689 },
16931690 .aarch64 => {
16941691 try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr });
1692 // blr x30
16951693 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
16961694 },
16971695 else => unreachable, // unsupported architecture on MachO
......@@ -2586,10 +2584,82 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25862584 },
25872585 .register => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}),
25882586 .memory => |addr| {
2589 // The value is in memory at a hard-coded address.
2590 // If the type is a pointer, it means the pointer address is at this memory location.
2591 try self.genSetReg(src, reg, .{ .immediate = addr });
2592 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .rn = reg }).toU32());
2587 if (self.bin_file.options.pie) {
2588 // For MachO, the binary, with the exception of object files, has to be a PIE.
2589 // Therefore we cannot load an absolute address.
2590 // Instead, we need to make use of PC-relative addressing.
2591 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack
2592 // like described here:
2593 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)
2594 // TODO As far as branching is concerned, instead of saving the return address
2595 // in a register, I'm thinking here of immitating x86_64, and having the address
2596 // passed on the stack.
2597 if (reg.id() == 0) { // x0 is special-cased
2598 // str x28, [sp, #-16]
2599 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
2600 .offset = Instruction.Offset.imm_pre_index(-16),
2601 }).toU32());
2602 // adr x28, #8
2603 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
2604 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2605 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
2606 .address = addr,
2607 .start = self.code.items.len,
2608 .len = 4,
2609 });
2610 } else {
2611 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
2612 }
2613 // b [label]
2614 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
2615 // mov r, x0
2616 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32());
2617 // ldr x28, [sp], #16
2618 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
2619 .rn = Register.sp,
2620 .offset = Instruction.Offset.imm_post_index(16),
2621 }).toU32());
2622 } else {
2623 // str x28, [sp, #-16]
2624 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
2625 .offset = Instruction.Offset.imm_pre_index(-16),
2626 }).toU32());
2627 // str x0, [sp, #-16]
2628 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x0, Register.sp, .{
2629 .offset = Instruction.Offset.imm_pre_index(-16),
2630 }).toU32());
2631 // adr x28, #8
2632 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
2633 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2634 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
2635 .address = addr,
2636 .start = self.code.items.len,
2637 .len = 4,
2638 });
2639 } else {
2640 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
2641 }
2642 // b [label]
2643 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
2644 // mov r, x0
2645 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32());
2646 // ldr x0, [sp], #16
2647 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x0, .{
2648 .rn = Register.sp,
2649 .offset = Instruction.Offset.imm_post_index(16),
2650 }).toU32());
2651 // ldr x28, [sp], #16
2652 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
2653 .rn = Register.sp,
2654 .offset = Instruction.Offset.imm_post_index(16),
2655 }).toU32());
2656 }
2657 } else {
2658 // The value is in memory at a hard-coded address.
2659 // If the type is a pointer, it means the pointer address is at this memory location.
2660 try self.genSetReg(src, reg, .{ .immediate = addr });
2661 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .rn = reg }).toU32());
2662 }
25932663 },
25942664 else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}),
25952665 },
......@@ -2766,7 +2836,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27662836 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
27672837 },
27682838 .memory => |x| {
2769 if (x <= math.maxInt(u32)) {
2839 if (self.bin_file.options.pie) {
2840 // For MachO, the binary, with the exception of object files, has to be a PIE.
2841 // Therefore, we cannot load an absolute address.
2842 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.
2843 // The plan here is to use unconditional relative jump to GOT entry, where we store
2844 // pre-calculated and stored effective address to load into the target register.
2845 // We leave the actual displacement information empty (0-padded) and fixing it up
2846 // later in the linker.
2847 if (reg.id() == 0) { // %rax is special-cased
2848 try self.code.ensureCapacity(self.code.items.len + 5);
2849 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2850 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
2851 .address = x,
2852 .start = self.code.items.len,
2853 .len = 5,
2854 });
2855 } else {
2856 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
2857 }
2858 // call [label]
2859 self.code.appendSliceAssumeCapacity(&[_]u8{
2860 0xE8,
2861 0x0,
2862 0x0,
2863 0x0,
2864 0x0,
2865 });
2866 } else {
2867 try self.code.ensureCapacity(self.code.items.len + 10);
2868 // push %rax
2869 self.code.appendSliceAssumeCapacity(&[_]u8{0x50});
2870 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2871 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
2872 .address = x,
2873 .start = self.code.items.len,
2874 .len = 5,
2875 });
2876 } else {
2877 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
2878 }
2879 // call [label]
2880 self.code.appendSliceAssumeCapacity(&[_]u8{
2881 0xE8,
2882 0x0,
2883 0x0,
2884 0x0,
2885 0x0,
2886 });
2887 // mov %r, %rax
2888 self.code.appendSliceAssumeCapacity(&[_]u8{
2889 0x48,
2890 0x89,
2891 0xC0 | @as(u8, reg.id()),
2892 });
2893 // pop %rax
2894 self.code.appendSliceAssumeCapacity(&[_]u8{0x58});
2895 }
2896 } else if (x <= math.maxInt(u32)) {
27702897 // Moving from memory to a register is a variant of `8B /r`.
27712898 // Since we're using 64-bit moves, we require a REX.
27722899 // This variant also requires a SIB, as it would otherwise be RIP-relative.
src/codegen/aarch64.zig+89
......@@ -19,6 +19,8 @@ pub const Register = enum(u6) {
1919 w16, w17, w18, w19, w20, w21, w22, w23,
2020 w24, w25, w26, w27, w28, w29, w30, wzr,
2121
22 pub const sp = .xzr;
23
2224 pub fn id(self: Register) u5 {
2325 return @truncate(u5, @enumToInt(self));
2426 }
......@@ -195,6 +197,17 @@ test "FloatingPointRegister.toX" {
195197
196198/// Represents an instruction in the AArch64 instruction set
197199pub const Instruction = union(enum) {
200 OrShiftedRegister: packed struct {
201 rd: u5,
202 rn: u5,
203 imm6: u6,
204 rm: u5,
205 n: u1,
206 shift: u2,
207 fixed: u5 = 0b01010,
208 opc: u2 = 0b01,
209 sf: u1,
210 },
198211 MoveWideImmediate: packed struct {
199212 rd: u5,
200213 imm16: u16,
......@@ -251,6 +264,7 @@ pub const Instruction = union(enum) {
251264
252265 pub fn toU32(self: Instruction) u32 {
253266 return switch (self) {
267 .OrShiftedRegister => |v| @bitCast(u32, v),
254268 .MoveWideImmediate => |v| @bitCast(u32, v),
255269 .PCRelativeAddress => |v| @bitCast(u32, v),
256270 .LoadStoreRegister => |v| @bitCast(u32, v),
......@@ -379,8 +393,65 @@ pub const Instruction = union(enum) {
379393 }
380394 };
381395
396 pub const RegisterShift = struct {
397 rn: u5,
398 imm6: u6,
399 shift: enum(u2) {
400 Lsl = 0,
401 Lsr = 1,
402 Asr = 2,
403 Ror = 3,
404 },
405
406 pub fn none() RegisterShift {
407 return .{
408 .rn = 0b11111,
409 .imm6 = 0,
410 .shift = .Lsl,
411 };
412 }
413 };
414
382415 // Helper functions for assembly syntax functions
383416
417 fn orShiftedRegister(
418 rd: Register,
419 rm: Register,
420 shift: RegisterShift,
421 invert: bool,
422 ) Instruction {
423 const n: u1 = if (invert) 1 else 0;
424 switch (rd.size()) {
425 32 => {
426 return Instruction{
427 .OrShiftedRegister = .{
428 .rd = rd.id(),
429 .rn = shift.rn,
430 .imm6 = shift.imm6,
431 .rm = rm.id(),
432 .n = n,
433 .shift = @enumToInt(shift.shift),
434 .sf = 0,
435 },
436 };
437 },
438 64 => {
439 return Instruction{
440 .OrShiftedRegister = .{
441 .rd = rd.id(),
442 .rn = shift.rn,
443 .imm6 = shift.imm6,
444 .rm = rm.id(),
445 .n = n,
446 .shift = @enumToInt(shift.shift),
447 .sf = 1,
448 },
449 };
450 },
451 else => unreachable, // unexpected register size
452 }
453 }
454
384455 fn moveWideImmediate(
385456 opc: u2,
386457 rd: Register,
......@@ -543,6 +614,16 @@ pub const Instruction = union(enum) {
543614 };
544615 }
545616
617 // Bitwise (inclusive) OR of a register value
618
619 pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction {
620 return orShiftedRegister(rd, rm, shift, false);
621 }
622
623 pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction {
624 return orShiftedRegister(rd, rm, shift, true);
625 }
626
546627 // Move wide (immediate)
547628
548629 pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
......@@ -653,6 +734,14 @@ test "serialize instructions" {
653734 };
654735
655736 const testcases = [_]Testcase{
737 .{ // orr x0 x1
738 .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()),
739 .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
740 },
741 .{ // orn x0 x1
742 .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()),
743 .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
744 },
656745 .{ // movz x1 #4
657746 .inst = Instruction.movz(.x1, 4, 0),
658747 .expected = 0b1_10_100101_00_0000000000000100_00001,
src/link.zig+23-1
......@@ -238,7 +238,29 @@ pub const File = struct {
238238
239239 pub fn makeExecutable(base: *File) !void {
240240 switch (base.tag) {
241 .coff, .elf, .macho => if (base.file) |f| {
241 .macho => if (base.file) |f| {
242 if (base.intermediary_basename != null) {
243 // The file we have open is not the final file that we want to
244 // make executable, so we don't have to close it.
245 return;
246 }
247 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
248 if (base.options.target.cpu.arch != .aarch64) return; // If we're not targeting aarch64, nothing to do.
249 // XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
250 // Any change to the binary will effectively invalidate the kernel's cache
251 // resulting in a SIGKILL on each subsequent run. Since when doing incremental
252 // linking we're modifying a binary in-place, this will end up with the kernel
253 // killing it on every subsequent run. To circumvent it, we will copy the file
254 // into a new inode, remove the original file, and rename the copy to match
255 // the original file. This is super messy, but there doesn't seem any other
256 // way to please the XNU.
257 const emit = base.options.emit orelse return;
258 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{});
259 }
260 f.close();
261 base.file = null;
262 },
263 .coff, .elf => if (base.file) |f| {
242264 if (base.intermediary_basename != null) {
243265 // The file we have open is not the final file that we want to
244266 // make executable, so we don't have to close it.
src/link/MachO.zig+295-186
......@@ -7,6 +7,7 @@ const fs = std.fs;
77const log = std.log.scoped(.link);
88const macho = std.macho;
99const codegen = @import("../codegen.zig");
10const aarch64 = @import("../codegen/aarch64.zig");
1011const math = std.math;
1112const mem = std.mem;
1213
......@@ -21,6 +22,7 @@ const Cache = @import("../Cache.zig");
2122const target_util = @import("../target.zig");
2223
2324const Trie = @import("MachO/Trie.zig");
25const CodeSignature = @import("MachO/CodeSignature.zig");
2426
2527pub const base_tag: File.Tag = File.Tag.macho;
2628
......@@ -33,6 +35,8 @@ const LoadCommand = union(enum) {
3335 Dylinker: macho.dylinker_command,
3436 Dylib: macho.dylib_command,
3537 EntryPoint: macho.entry_point_command,
38 MinVersion: macho.version_min_command,
39 SourceVersion: macho.source_version_command,
3640
3741 pub fn cmdsize(self: LoadCommand) u32 {
3842 return switch (self) {
......@@ -44,6 +48,8 @@ const LoadCommand = union(enum) {
4448 .Dylinker => |x| x.cmdsize,
4549 .Dylib => |x| x.cmdsize,
4650 .EntryPoint => |x| x.cmdsize,
51 .MinVersion => |x| x.cmdsize,
52 .SourceVersion => |x| x.cmdsize,
4753 };
4854 }
4955
......@@ -57,6 +63,8 @@ const LoadCommand = union(enum) {
5763 .Dylinker => |cmd| writeGeneric(cmd, file, offset),
5864 .Dylib => |cmd| writeGeneric(cmd, file, offset),
5965 .EntryPoint => |cmd| writeGeneric(cmd, file, offset),
66 .MinVersion => |cmd| writeGeneric(cmd, file, offset),
67 .SourceVersion => |cmd| writeGeneric(cmd, file, offset),
6068 };
6169 }
6270
......@@ -68,6 +76,10 @@ const LoadCommand = union(enum) {
6876
6977base: File,
7078
79/// Page size is dependent on the target cpu architecture.
80/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
81page_size: u16,
82
7183/// Table of all load commands
7284load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
7385/// __PAGEZERO segment
......@@ -96,6 +108,12 @@ function_starts_cmd_index: ?u16 = null,
96108/// Specifies offset wrt __TEXT segment start address to the main entry point
97109/// of the binary.
98110main_cmd_index: ?u16 = null,
111/// Minimum OS version
112version_min_cmd_index: ?u16 = null,
113/// Source version
114source_version_cmd_index: ?u16 = null,
115/// Code signature
116code_signature_cmd_index: ?u16 = null,
99117
100118/// Table of all sections
101119sections: std.ArrayListUnmanaged(macho.section_64) = .{},
......@@ -108,6 +126,9 @@ got_section_index: ?u16 = null,
108126
109127entry_addr: ?u64 = null,
110128
129// TODO move this into each Segment aggregator
130linkedit_segment_next_offset: ?u32 = null,
131
111132/// Table of all local symbols
112133/// Internally references string table for names (which are optional).
113134local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
......@@ -154,6 +175,22 @@ libsystem_cmd_dirty: bool = false,
154175text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
155176/// Pointer to the last allocated text block
156177last_text_block: ?*TextBlock = null,
178/// A list of all PIE fixups required for this run of the linker.
179/// Warning, this is currently NOT thread-safe. See the TODO below.
180/// TODO Move this list inside `updateDecl` where it should be allocated
181/// prior to calling `generateSymbol`, and then immediately deallocated
182/// rather than sitting in the global scope.
183pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
184
185pub const PieFixup = struct {
186 /// Target address we wanted to address in absolute terms.
187 address: u64,
188 /// Where in the byte stream we should perform the fixup.
189 start: usize,
190 /// The length of the byte stream. For x86_64, this will be
191 /// variable. For aarch64, it will be fixed at 4 bytes.
192 len: usize,
193};
157194
158195/// `alloc_num / alloc_den` is the factor of padding when allocating.
159196const alloc_num = 4;
......@@ -292,6 +329,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
292329 .allocator = gpa,
293330 .file = null,
294331 },
332 .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000,
295333 };
296334 return self;
297335}
......@@ -312,35 +350,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
312350 const tracy = trace(@src());
313351 defer tracy.end();
314352
315 // Unfortunately these have to be buffered and done at the end because MachO does not allow
316 // mixing local, global and undefined symbols within a symbol table.
317 try self.writeAllGlobalSymbols();
318 try self.writeAllUndefSymbols();
319
320 try self.writeStringTable();
321
322353 switch (self.base.options.output_mode) {
323354 .Exe => {
324 // Write export trie.
325 try self.writeExportTrie();
326355 if (self.entry_addr) |addr| {
327356 // Update LC_MAIN with entry offset.
328357 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
329358 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
330359 main_cmd.entryoff = addr - text_segment.vmaddr;
331360 }
332 {
333 // Update dynamic symbol table.
334 const nlocals = @intCast(u32, self.local_symbols.items.len);
335 const nglobals = @intCast(u32, self.global_symbols.items.len);
336 const nundefs = @intCast(u32, self.undef_symbols.items.len);
337 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
338 dysymtab.nlocalsym = nlocals;
339 dysymtab.iextdefsym = nlocals;
340 dysymtab.nextdefsym = nglobals;
341 dysymtab.iundefsym = nlocals + nglobals;
342 dysymtab.nundefsym = nundefs;
343 }
344361 if (self.dylinker_cmd_dirty) {
345362 // Write path to dyld loader.
346363 var off: usize = @sizeOf(macho.mach_header_64);
......@@ -367,20 +384,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
367384 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
368385 self.libsystem_cmd_dirty = false;
369386 }
387
388 try self.writeExportTrie();
389 try self.writeSymbolTable();
390 try self.writeStringTable();
391
392 // Preallocate space for the code signature.
393 // We need to do this at this stage so that we have the load commands with proper values
394 // written out to the file.
395 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
396 // where the code signature goes into.
397 try self.writeCodeSignaturePadding();
370398 },
371399 .Obj => {},
372400 .Lib => return error.TODOImplementWritingLibFiles,
373401 }
374402
375 {
376 // Update symbol table.
377 const nlocals = @intCast(u32, self.local_symbols.items.len);
378 const nglobals = @intCast(u32, self.global_symbols.items.len);
379 const nundefs = @intCast(u32, self.undef_symbols.items.len);
380 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
381 symtab.nsyms = nlocals + nglobals + nundefs;
382 }
383
384403 if (self.cmd_table_dirty) {
385404 try self.writeCmdHeaders();
386405 try self.writeMachOHeader();
......@@ -398,6 +417,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
398417 assert(!self.cmd_table_dirty);
399418 assert(!self.dylinker_cmd_dirty);
400419 assert(!self.libsystem_cmd_dirty);
420
421 switch (self.base.options.output_mode) {
422 .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last
423 else => {},
424 }
401425}
402426
403427fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
......@@ -823,6 +847,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
823847}
824848
825849pub fn deinit(self: *MachO) void {
850 self.pie_fixups.deinit(self.base.allocator);
826851 self.text_block_free_list.deinit(self.base.allocator);
827852 self.offset_table.deinit(self.base.allocator);
828853 self.offset_table_free_list.deinit(self.base.allocator);
......@@ -955,7 +980,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
955980 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr });
956981 if (vaddr != symbol.n_value) {
957982 symbol.n_value = vaddr;
958
959983 log.debug(" (writing new offset table entry)\n", .{});
960984 self.offset_table.items[decl.link.macho.offset_table_index] = vaddr;
961985 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
......@@ -968,8 +992,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
968992 symbol.n_type = macho.N_SECT;
969993 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
970994 symbol.n_desc = 0;
971 // TODO this write could be avoided if no fields of the symbol were changed.
972 try self.writeSymbol(decl.link.macho.local_sym_index);
973995 } else {
974996 const decl_name = mem.spanZ(decl.name);
975997 const name_str_index = try self.makeString(decl_name);
......@@ -985,15 +1007,32 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
9851007 .n_value = addr,
9861008 };
9871009 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
988
989 try self.writeSymbol(decl.link.macho.local_sym_index);
9901010 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
9911011 }
9921012
1013 // Perform PIE fixups (if any)
1014 const got_section = self.sections.items[self.got_section_index.?];
1015 while (self.pie_fixups.popOrNull()) |fixup| {
1016 const target_addr = fixup.address;
1017 const this_addr = symbol.n_value + fixup.start;
1018 switch (self.base.options.target.cpu.arch) {
1019 .x86_64 => {
1020 const displacement = @intCast(u32, target_addr - this_addr - fixup.len);
1021 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1022 mem.writeIntSliceLittle(u32, placeholder, displacement);
1023 },
1024 .aarch64 => {
1025 const displacement = @intCast(u27, target_addr - this_addr);
1026 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1027 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32());
1028 },
1029 else => unreachable, // unsupported target architecture
1030 }
1031 }
1032
9931033 const text_section = self.sections.items[self.text_section_index.?];
9941034 const section_offset = symbol.n_value - text_section.addr;
9951035 const file_offset = text_section.offset + section_offset;
996
9971036 try self.base.file.?.pwriteAll(code, file_offset);
9981037
9991038 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
......@@ -1127,7 +1166,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11271166 }
11281167 if (self.text_segment_cmd_index == null) {
11291168 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1130 const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
1169 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1170 const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
11311171 try self.load_commands.append(self.base.allocator, .{
11321172 .Segment = .{
11331173 .cmd = macho.LC_SEGMENT_64,
......@@ -1137,8 +1177,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11371177 .vmsize = 0,
11381178 .fileoff = 0,
11391179 .filesize = 0,
1140 .maxprot = prot,
1141 .initprot = prot,
1180 .maxprot = maxprot,
1181 .initprot = initprot,
11421182 .nsects = 0,
11431183 .flags = 0,
11441184 },
......@@ -1148,11 +1188,10 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11481188 if (self.text_section_index == null) {
11491189 self.text_section_index = @intCast(u16, self.sections.items.len);
11501190 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1151 text_segment.cmdsize += @sizeOf(macho.section_64);
1152 text_segment.nsects += 1;
11531191
1154 const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000);
1155 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?
1192 const program_code_size_hint = self.base.options.program_code_size_hint;
1193 const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size);
1194 const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly?
11561195 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
11571196
11581197 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
......@@ -1163,7 +1202,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11631202 .addr = text_segment.vmaddr + off,
11641203 .size = file_size,
11651204 .offset = off,
1166 .@"align" = 12, // 2^12 = 4096
1205 .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64
11671206 .reloff = 0,
11681207 .nreloc = 0,
11691208 .flags = flags,
......@@ -1174,47 +1213,27 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11741213
11751214 text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
11761215 text_segment.filesize = file_size + off;
1177 self.cmd_table_dirty = true;
1178 }
1179 if (self.data_segment_cmd_index == null) {
1180 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1181 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1182 const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
1183 try self.load_commands.append(self.base.allocator, .{
1184 .Segment = .{
1185 .cmd = macho.LC_SEGMENT_64,
1186 .cmdsize = @sizeOf(macho.segment_command_64),
1187 .segname = makeStaticString("__DATA"),
1188 .vmaddr = text_segment.vmaddr + text_segment.vmsize,
1189 .vmsize = 0,
1190 .fileoff = 0,
1191 .filesize = 0,
1192 .maxprot = prot,
1193 .initprot = prot,
1194 .nsects = 0,
1195 .flags = 0,
1196 },
1197 });
1216 text_segment.cmdsize += @sizeOf(macho.section_64);
1217 text_segment.nsects += 1;
11981218 self.cmd_table_dirty = true;
11991219 }
12001220 if (self.got_section_index == null) {
12011221 self.got_section_index = @intCast(u16, self.sections.items.len);
1202 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1203 data_segment.cmdsize += @sizeOf(macho.section_64);
1204 data_segment.nsects += 1;
1222 const text_section = &self.sections.items[self.text_section_index.?];
12051223
12061224 const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1207 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
1225 // TODO looking for free space should be done *within* a segment it belongs to
1226 const off = @intCast(u32, text_section.offset + text_section.size);
12081227
12091228 log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
12101229
12111230 try self.sections.append(self.base.allocator, .{
12121231 .sectname = makeStaticString("__got"),
1213 .segname = makeStaticString("__DATA"),
1214 .addr = data_segment.vmaddr,
1232 .segname = makeStaticString("__TEXT"),
1233 .addr = text_section.addr + text_section.size,
12151234 .size = file_size,
12161235 .offset = off,
1217 .@"align" = 3, // 2^3 = 8
1236 .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0,
12181237 .reloff = 0,
12191238 .nreloc = 0,
12201239 .flags = macho.S_REGULAR,
......@@ -1223,31 +1242,36 @@ pub fn populateMissingMetadata(self: *MachO) !void {
12231242 .reserved3 = 0,
12241243 });
12251244
1226 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1227 data_segment.vmsize = segment_size;
1228 data_segment.filesize = segment_size;
1229 data_segment.fileoff = off;
1245 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1246 const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size);
1247 text_segment.vmsize += added_size;
1248 text_segment.filesize += added_size;
1249 text_segment.cmdsize += @sizeOf(macho.section_64);
1250 text_segment.nsects += 1;
12301251 self.cmd_table_dirty = true;
12311252 }
12321253 if (self.linkedit_segment_cmd_index == null) {
12331254 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1234 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1235 const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1255 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1256 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1257 const initprot = macho.VM_PROT_READ;
1258 const off = text_segment.fileoff + text_segment.filesize;
12361259 try self.load_commands.append(self.base.allocator, .{
12371260 .Segment = .{
12381261 .cmd = macho.LC_SEGMENT_64,
12391262 .cmdsize = @sizeOf(macho.segment_command_64),
12401263 .segname = makeStaticString("__LINKEDIT"),
1241 .vmaddr = data_segment.vmaddr + data_segment.vmsize,
1264 .vmaddr = text_segment.vmaddr + text_segment.vmsize,
12421265 .vmsize = 0,
1243 .fileoff = 0,
1266 .fileoff = off,
12441267 .filesize = 0,
1245 .maxprot = prot,
1246 .initprot = prot,
1268 .maxprot = maxprot,
1269 .initprot = initprot,
12471270 .nsects = 0,
12481271 .flags = 0,
12491272 },
12501273 });
1274 self.linkedit_segment_next_offset = @intCast(u32, off);
12511275 self.cmd_table_dirty = true;
12521276 }
12531277 if (self.dyld_info_cmd_index == null) {
......@@ -1329,8 +1353,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13291353 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
13301354 const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64));
13311355 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
1332 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
1333 const min_version = 0x10000;
1356 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
1357 const min_version = 0x0;
13341358 const dylib = .{
13351359 .name = @sizeOf(macho.dylib_command),
13361360 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
......@@ -1359,47 +1383,46 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13591383 });
13601384 self.cmd_table_dirty = true;
13611385 }
1362 {
1363 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1364 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
1365 if (dyld_info.export_off == 0) {
1366 const nsyms = self.base.options.symbol_count_hint;
1367 const file_size = @sizeOf(u64) * nsyms;
1368 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
1369 log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
1370 dyld_info.export_off = off;
1371 dyld_info.export_size = @intCast(u32, file_size);
1372
1373 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1374 linkedit.vmsize += segment_size;
1375 linkedit.fileoff = off;
1376 }
1386 if (self.version_min_cmd_index == null) {
1387 self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len);
1388 const cmd: u32 = switch (self.base.options.target.os.tag) {
1389 .macos => macho.LC_VERSION_MIN_MACOSX,
1390 .ios => macho.LC_VERSION_MIN_IPHONEOS,
1391 .tvos => macho.LC_VERSION_MIN_TVOS,
1392 .watchos => macho.LC_VERSION_MIN_WATCHOS,
1393 else => unreachable, // wrong OS
1394 };
1395 const ver = self.base.options.target.os.version_range.semver.min;
1396 const version = ver.major << 16 | ver.minor << 8 | ver.patch;
1397 try self.load_commands.append(self.base.allocator, .{
1398 .MinVersion = .{
1399 .cmd = cmd,
1400 .cmdsize = @sizeOf(macho.version_min_command),
1401 .version = version,
1402 .sdk = version,
1403 },
1404 });
13771405 }
1378 {
1379 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1380 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1381 if (symtab.symoff == 0) {
1382 const nsyms = self.base.options.symbol_count_hint;
1383 const file_size = @sizeOf(macho.nlist_64) * nsyms;
1384 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
1385 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
1386 symtab.symoff = off;
1387 symtab.nsyms = @intCast(u32, nsyms);
1388
1389 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1390 linkedit.vmsize += segment_size;
1391 }
1392 if (symtab.stroff == 0) {
1393 try self.string_table.append(self.base.allocator, 0);
1394 const file_size = @intCast(u32, self.string_table.items.len);
1395 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
1396 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
1397 symtab.stroff = off;
1398 symtab.strsize = file_size;
1399
1400 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1401 linkedit.vmsize += segment_size;
1402 }
1406 if (self.source_version_cmd_index == null) {
1407 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
1408 try self.load_commands.append(self.base.allocator, .{
1409 .SourceVersion = .{
1410 .cmd = macho.LC_SOURCE_VERSION,
1411 .cmdsize = @sizeOf(macho.source_version_command),
1412 .version = 0x0,
1413 },
1414 });
1415 }
1416 if (self.code_signature_cmd_index == null) {
1417 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
1418 try self.load_commands.append(self.base.allocator, .{
1419 .LinkeditData = .{
1420 .cmd = macho.LC_CODE_SIGNATURE,
1421 .cmdsize = @sizeOf(macho.linkedit_data_command),
1422 .dataoff = 0,
1423 .datasize = 0,
1424 },
1425 });
14031426 }
14041427 if (self.dyld_stub_binder_index == null) {
14051428 self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);
......@@ -1626,47 +1649,121 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
16261649 return start;
16271650}
16281651
1629fn writeSymbol(self: *MachO, index: usize) !void {
1630 const tracy = trace(@src());
1631 defer tracy.end();
1632
1633 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1634 const sym = [1]macho.nlist_64{self.local_symbols.items[index]};
1635 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
1636 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
1637 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1638}
1639
16401652fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
16411653 const sect = &self.sections.items[self.got_section_index.?];
1642 const endian = self.base.options.target.cpu.arch.endian();
1643 var buf: [@sizeOf(u64)]u8 = undefined;
1644 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
16451654 const off = sect.offset + @sizeOf(u64) * index;
1655 const vmaddr = sect.addr + @sizeOf(u64) * index;
1656
1657 var code: [8]u8 = undefined;
1658 switch (self.base.options.target.cpu.arch) {
1659 .x86_64 => {
1660 const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7);
1661 const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1);
1662 // lea %rax, [rip - disp]
1663 code[0] = 0x48;
1664 code[1] = 0x8D;
1665 code[2] = 0x5;
1666 mem.writeIntLittle(u32, code[3..7], symbol_off);
1667 // ret
1668 code[7] = 0xC3;
1669 },
1670 .aarch64 => {
1671 const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]);
1672 const symbol_off = @intCast(i21, pos_symbol_off) * -1;
1673 // adr x0, #-disp
1674 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
1675 // ret x28
1676 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
1677 },
1678 else => unreachable, // unsupported target architecture
1679 }
16461680 log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
1647 try self.base.file.?.pwriteAll(&buf, off);
1681 try self.base.file.?.pwriteAll(&code, off);
16481682}
16491683
1650fn writeAllGlobalSymbols(self: *MachO) !void {
1684fn writeSymbolTable(self: *MachO) !void {
1685 // TODO workout how we can cache these so that we only overwrite symbols that were updated
16511686 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1652 const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64);
1653 const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64);
1654 log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off });
1655 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off);
1687
1688 const locals_off = self.linkedit_segment_next_offset.?;
1689 const locals_size = self.local_symbols.items.len * @sizeOf(macho.nlist_64);
1690 log.debug("writing local symbols from 0x{x} to 0x{x}\n", .{ locals_off, locals_size + locals_off });
1691 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.local_symbols.items), locals_off);
1692
1693 const globals_off = locals_off + locals_size;
1694 const globals_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64);
1695 log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ globals_off, globals_size + globals_off });
1696 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off);
1697
1698 const undefs_off = globals_off + globals_size;
1699 const undefs_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64);
1700 log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ undefs_off, undefs_size + undefs_off });
1701 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off);
1702
1703 // Update symbol table.
1704 const nlocals = @intCast(u32, self.local_symbols.items.len);
1705 const nglobals = @intCast(u32, self.global_symbols.items.len);
1706 const nundefs = @intCast(u32, self.undef_symbols.items.len);
1707 symtab.symoff = self.linkedit_segment_next_offset.?;
1708 symtab.nsyms = nlocals + nglobals + nundefs;
1709 self.linkedit_segment_next_offset = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
1710
1711 // Update dynamic symbol table.
1712 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
1713 dysymtab.nlocalsym = nlocals;
1714 dysymtab.iextdefsym = nlocals;
1715 dysymtab.nextdefsym = nglobals;
1716 dysymtab.iundefsym = nlocals + nglobals;
1717 dysymtab.nundefsym = nundefs;
1718
1719 // Advance size of __LINKEDIT segment
1720 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1721 linkedit.filesize += symtab.nsyms * @sizeOf(macho.nlist_64);
1722 if (linkedit.vmsize < linkedit.filesize) {
1723 linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size);
1724 }
1725 self.cmd_table_dirty = true;
16561726}
16571727
1658fn writeAllUndefSymbols(self: *MachO) !void {
1659 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1660 const nlocals = self.local_symbols.items.len;
1661 const nglobals = self.global_symbols.items.len;
1662 const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64);
1663 const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64);
1664 log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off });
1665 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off);
1728fn writeCodeSignaturePadding(self: *MachO) !void {
1729 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
1730 const fileoff = self.linkedit_segment_next_offset.?;
1731 const datasize: u32 = 0x1000; // TODO Calculate the expected size of the signature.
1732 code_sig_cmd.dataoff = fileoff;
1733 code_sig_cmd.datasize = datasize;
1734
1735 self.linkedit_segment_next_offset = fileoff + datasize;
1736 // Advance size of __LINKEDIT segment
1737 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1738 linkedit.filesize += datasize;
1739 if (linkedit.vmsize < linkedit.filesize) {
1740 linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size);
1741 }
1742 log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize });
1743 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
1744 // except for code signature data.
1745 try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + datasize - 1);
1746}
1747
1748fn writeCodeSignature(self: *MachO) !void {
1749 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
1750 var code_sig = CodeSignature.init(self.base.allocator);
1751 defer code_sig.deinit();
1752
1753 try code_sig.calcAdhocSignature(self);
1754
1755 var buffer = try self.base.allocator.alloc(u8, code_sig.size());
1756 defer self.base.allocator.free(buffer);
1757
1758 code_sig.write(buffer);
1759
1760 log.debug("writing code signature from 0x{x} to 0x{x}\n", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
1761
1762 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
16661763}
16671764
16681765fn writeExportTrie(self: *MachO) !void {
1669 if (self.global_symbols.items.len == 0) return; // No exports, nothing to do.
1766 if (self.global_symbols.items.len == 0) return;
16701767
16711768 var trie: Trie = .{};
16721769 defer trie.deinit(self.base.allocator);
......@@ -1689,28 +1786,51 @@ fn writeExportTrie(self: *MachO) !void {
16891786 try trie.writeULEB128Mem(self.base.allocator, &buffer);
16901787
16911788 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
1789 const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64)));
1790 dyld_info.export_off = self.linkedit_segment_next_offset.?;
1791 dyld_info.export_size = export_size;
1792
1793 log.debug("writing export trie from 0x{x} to 0x{x}\n", .{ dyld_info.export_off, dyld_info.export_off + export_size });
1794
1795 if (export_size > buffer.items.len) {
1796 // Pad out to align(8).
1797 try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.export_off + export_size);
1798 }
16921799 try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off);
1800
1801 self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size;
1802 // Advance size of __LINKEDIT segment
1803 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1804 linkedit.filesize += dyld_info.export_size;
1805 if (linkedit.vmsize < linkedit.filesize) {
1806 linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size);
1807 }
1808 self.cmd_table_dirty = true;
16931809}
16941810
16951811fn writeStringTable(self: *MachO) !void {
16961812 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1697 const allocated_size = self.allocatedSize(symtab.stroff);
16981813 const needed_size = self.string_table.items.len;
16991814
1700 if (needed_size > allocated_size) {
1701 symtab.strsize = 0;
1702 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
1703 }
1704 symtab.strsize = @intCast(u32, needed_size);
1815 symtab.stroff = self.linkedit_segment_next_offset.?;
1816 symtab.strsize = @intCast(u32, mem.alignForward(needed_size, @sizeOf(u64)));
17051817
17061818 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
17071819
1820 if (symtab.strsize > needed_size) {
1821 // Pad out to align(8);
1822 try self.base.file.?.pwriteAll(&[_]u8{0}, symtab.stroff + symtab.strsize);
1823 }
17081824 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
17091825
1710 // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of
1711 // doing dynamic updates like this.
1826 self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize;
1827 // Advance size of __LINKEDIT segment
17121828 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1713 linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff;
1829 linkedit.filesize += symtab.strsize;
1830 if (linkedit.vmsize < linkedit.filesize) {
1831 linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size);
1832 }
1833 self.cmd_table_dirty = true;
17141834}
17151835
17161836fn writeCmdHeaders(self: *MachO) !void {
......@@ -1726,7 +1846,6 @@ fn writeCmdHeaders(self: *MachO) !void {
17261846 last_cmd_offset += cmd.cmdsize();
17271847 }
17281848 {
1729 // write __text section header
17301849 const off = if (self.text_segment_cmd_index) |text_segment_index| blk: {
17311850 var i: usize = 0;
17321851 var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
......@@ -1739,27 +1858,11 @@ fn writeCmdHeaders(self: *MachO) !void {
17391858 // only one, noname segment to append this section header to.
17401859 return error.TODOImplementWritingObjFiles;
17411860 };
1742 const idx = self.text_section_index.?;
1743 log.debug("writing text section header at 0x{x}\n", .{off});
1744 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
1745 }
1746 {
1747 // write __got section header
1748 const off = if (self.data_segment_cmd_index) |data_segment_index| blk: {
1749 var i: usize = 0;
1750 var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
1751 while (i < data_segment_index) : (i += 1) {
1752 cmdsize += self.load_commands.items[i].cmdsize();
1753 }
1754 break :blk cmdsize;
1755 } else {
1756 // If we've landed in here, we are building a MachO object file, so we have
1757 // only one, noname segment to append this section header to.
1758 return error.TODOImplementWritingObjFiles;
1759 };
1760 const idx = self.got_section_index.?;
1761 log.debug("writing got section header at 0x{x}\n", .{off});
1762 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
1861 // write sections belonging to __TEXT segment
1862 // TODO section indices should belong to each Segment, and we should iterate dynamically.
1863 const id = self.text_section_index.?;
1864 log.debug("writing __TEXT section headers at 0x{x}\n", .{off});
1865 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off);
17631866 }
17641867}
17651868
......@@ -1807,8 +1910,14 @@ fn writeMachOHeader(self: *MachO) !void {
18071910
18081911 hdr.sizeofcmds = sizeofcmds;
18091912
1810 // TODO should these be set to something else?
1811 hdr.flags = 0;
1913 switch (self.base.options.output_mode) {
1914 .Exe => {
1915 hdr.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE;
1916 },
1917 else => {
1918 hdr.flags = 0;
1919 },
1920 }
18121921 hdr.reserved = 0;
18131922
18141923 log.debug("writing Mach-O header {}\n", .{hdr});
src/link/MachO/CodeSignature.zig created+182
......@@ -0,0 +1,182 @@
1const CodeSignature = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const log = std.log.scoped(.link);
6const macho = std.macho;
7const mem = std.mem;
8const testing = std.testing;
9const Allocator = mem.Allocator;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11
12const MachO = @import("../MachO.zig");
13
14const hash_size: u8 = 32;
15const page_size: u16 = 0x1000;
16
17const CodeDirectory = struct {
18 inner: macho.CodeDirectory,
19 data: std.ArrayListUnmanaged(u8) = .{},
20
21 fn size(self: CodeDirectory) u32 {
22 return self.inner.length;
23 }
24
25 fn write(self: CodeDirectory, buffer: []u8) void {
26 assert(buffer.len >= self.inner.length);
27
28 mem.writeIntBig(u32, buffer[0..4], self.inner.magic);
29 mem.writeIntBig(u32, buffer[4..8], self.inner.length);
30 mem.writeIntBig(u32, buffer[8..12], self.inner.version);
31 mem.writeIntBig(u32, buffer[12..16], self.inner.flags);
32 mem.writeIntBig(u32, buffer[16..20], self.inner.hashOffset);
33 mem.writeIntBig(u32, buffer[20..24], self.inner.identOffset);
34 mem.writeIntBig(u32, buffer[24..28], self.inner.nSpecialSlots);
35 mem.writeIntBig(u32, buffer[28..32], self.inner.nCodeSlots);
36 mem.writeIntBig(u32, buffer[32..36], self.inner.codeLimit);
37 buffer[36] = self.inner.hashSize;
38 buffer[37] = self.inner.hashType;
39 buffer[38] = self.inner.platform;
40 buffer[39] = self.inner.pageSize;
41 mem.writeIntBig(u32, buffer[40..44], self.inner.spare2);
42 mem.writeIntBig(u32, buffer[44..48], self.inner.scatterOffset);
43 mem.writeIntBig(u32, buffer[48..52], self.inner.teamOffset);
44 mem.writeIntBig(u32, buffer[52..56], self.inner.spare3);
45 mem.writeIntBig(u64, buffer[56..64], self.inner.codeLimit64);
46 mem.writeIntBig(u64, buffer[64..72], self.inner.execSegBase);
47 mem.writeIntBig(u64, buffer[72..80], self.inner.execSegLimit);
48 mem.writeIntBig(u64, buffer[80..88], self.inner.execSegFlags);
49
50 mem.copy(u8, buffer[88..], self.data.items);
51 }
52};
53
54alloc: *Allocator,
55inner: macho.SuperBlob = .{
56 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
57 .length = @sizeOf(macho.SuperBlob),
58 .count = 0,
59},
60cdir: ?CodeDirectory = null,
61
62pub fn init(alloc: *Allocator) CodeSignature {
63 return .{
64 .alloc = alloc,
65 };
66}
67
68pub fn calcAdhocSignature(self: *CodeSignature, bin_file: *const MachO) !void {
69 const text_segment = bin_file.load_commands.items[bin_file.text_segment_cmd_index.?].Segment;
70 const code_sig_cmd = bin_file.load_commands.items[bin_file.code_signature_cmd_index.?].LinkeditData;
71
72 const execSegBase: u64 = text_segment.fileoff;
73 const execSegLimit: u64 = text_segment.filesize;
74 const execSegFlags: u64 = if (bin_file.base.options.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
75 const file_size = code_sig_cmd.dataoff;
76 var cdir = CodeDirectory{
77 .inner = .{
78 .magic = macho.CSMAGIC_CODEDIRECTORY,
79 .length = @sizeOf(macho.CodeDirectory),
80 .version = macho.CS_SUPPORTSEXECSEG,
81 .flags = macho.CS_ADHOC,
82 .hashOffset = 0,
83 .identOffset = 0,
84 .nSpecialSlots = 0,
85 .nCodeSlots = 0,
86 .codeLimit = @intCast(u32, file_size),
87 .hashSize = hash_size,
88 .hashType = macho.CS_HASHTYPE_SHA256,
89 .platform = 0,
90 .pageSize = @truncate(u8, std.math.log2(page_size)),
91 .spare2 = 0,
92 .scatterOffset = 0,
93 .teamOffset = 0,
94 .spare3 = 0,
95 .codeLimit64 = 0,
96 .execSegBase = execSegBase,
97 .execSegLimit = execSegLimit,
98 .execSegFlags = execSegFlags,
99 },
100 };
101
102 const total_pages = mem.alignForward(file_size, page_size) / page_size;
103
104 var hash: [hash_size]u8 = undefined;
105 var buffer = try bin_file.base.allocator.alloc(u8, page_size);
106 defer bin_file.base.allocator.free(buffer);
107 const macho_file = bin_file.base.file.?;
108
109 const id = bin_file.base.options.emit.?.sub_path;
110 try cdir.data.ensureCapacity(self.alloc, total_pages * hash_size + id.len + 1);
111
112 // 1. Save the identifier and update offsets
113 cdir.inner.identOffset = cdir.inner.length;
114 cdir.data.appendSliceAssumeCapacity(id);
115 cdir.data.appendAssumeCapacity(0);
116
117 // 2. Calculate hash for each page (in file) and write it to the buffer
118 // TODO figure out how we can cache several hashes since we won't update
119 // every page during incremental linking
120 cdir.inner.hashOffset = cdir.inner.identOffset + @intCast(u32, id.len) + 1;
121 var i: usize = 0;
122 while (i < total_pages) : (i += 1) {
123 const fstart = i * page_size;
124 const fsize = if (fstart + page_size > file_size) file_size - fstart else page_size;
125 const len = try macho_file.preadAll(buffer, fstart);
126 assert(fsize <= len);
127
128 Sha256.hash(buffer[0..fsize], &hash, .{});
129
130 cdir.data.appendSliceAssumeCapacity(hash[0..]);
131 cdir.inner.nCodeSlots += 1;
132 }
133
134 // 3. Update CodeDirectory length
135 cdir.inner.length += @intCast(u32, cdir.data.items.len);
136
137 self.inner.length += @sizeOf(macho.BlobIndex) + cdir.size();
138 self.inner.count = 1;
139 self.cdir = cdir;
140}
141
142pub fn size(self: CodeSignature) u32 {
143 return self.inner.length;
144}
145
146pub fn write(self: CodeSignature, buffer: []u8) void {
147 assert(buffer.len >= self.inner.length);
148 self.writeHeader(buffer);
149 const offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex);
150 writeBlobIndex(macho.CSSLOT_CODEDIRECTORY, offset, buffer[@sizeOf(macho.SuperBlob)..]);
151 self.cdir.?.write(buffer[offset..]);
152}
153
154pub fn deinit(self: *CodeSignature) void {
155 if (self.cdir) |*cdir| {
156 cdir.data.deinit(self.alloc);
157 }
158}
159
160fn writeHeader(self: CodeSignature, buffer: []u8) void {
161 assert(buffer.len >= @sizeOf(macho.SuperBlob));
162 mem.writeIntBig(u32, buffer[0..4], self.inner.magic);
163 mem.writeIntBig(u32, buffer[4..8], self.inner.length);
164 mem.writeIntBig(u32, buffer[8..12], self.inner.count);
165}
166
167fn writeBlobIndex(tt: u32, offset: u32, buffer: []u8) void {
168 assert(buffer.len >= @sizeOf(macho.BlobIndex));
169 mem.writeIntBig(u32, buffer[0..4], tt);
170 mem.writeIntBig(u32, buffer[4..8], offset);
171}
172
173test "CodeSignature header" {
174 var code_sig = CodeSignature.init(testing.allocator);
175 defer code_sig.deinit();
176
177 var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined;
178 code_sig.writeHeader(buffer[0..]);
179
180 const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 };
181 testing.expect(mem.eql(u8, expected[0..], buffer[0..]));
182}
src/main.zig+3
......@@ -1727,6 +1727,9 @@ fn buildOutputType(
17271727 error.SemanticAnalyzeFail => process.exit(1),
17281728 else => |e| return e,
17291729 };
1730 if (output_mode == .Exe) {
1731 try comp.makeBinFileExecutable();
1732 }
17301733
17311734 if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
17321735 warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
test/stage2/aarch64.zig+1-3
......@@ -12,9 +12,7 @@ const linux_aarch64 = std.zig.CrossTarget{
1212};
1313
1414pub fn addCases(ctx: *TestContext) !void {
15 // TODO enable when we add codesigning to the self-hosted linker
16 // related to #6971
17 if (false) {
15 {
1816 var case = ctx.exe("hello world with updates", macos_aarch64);
1917
2018 // Regular old hello world