| author | |
| committer | |
| log | 0376fd09bc9f29ceeb83760e32532923e4fe7f98 |
| tree | 2458f38c54fa77f642dc41658f3cf89c7af506ea |
| parent | 91fd0f42c88f4bea424b5a5c58435a2a98b57a58 |
With this change, we can now bake in entitlements into the binary.
Additionally, I see this as the first step towards full code signature
support which includes baking in Apple issued certificates for
redistribution, etc.6 files changed, 384 insertions(+), 142 deletions(-)
lib/std/build.zig+7| ... | @@ -1570,6 +1570,9 @@ pub const LibExeObjStep = struct { | ... | @@ -1570,6 +1570,9 @@ pub const LibExeObjStep = struct { |
| 1570 | /// (Darwin) Install name for the dylib | 1570 | /// (Darwin) Install name for the dylib |
| 1571 | install_name: ?[]const u8 = null, | 1571 | install_name: ?[]const u8 = null, |
| 1572 | 1572 | ||
| 1573 | /// (Darwin) Path to entitlements file | ||
| 1574 | entitlements: ?[]const u8 = null, | ||
| 1575 | |||
| 1573 | /// Position Independent Code | 1576 | /// Position Independent Code |
| 1574 | force_pic: ?bool = null, | 1577 | force_pic: ?bool = null, |
| 1575 | 1578 | ||
| ... | @@ -2515,6 +2518,10 @@ pub const LibExeObjStep = struct { | ... | @@ -2515,6 +2518,10 @@ pub const LibExeObjStep = struct { |
| 2515 | } | 2518 | } |
| 2516 | } | 2519 | } |
| 2517 | 2520 | ||
| 2521 | if (self.entitlements) |entitlements| { | ||
| 2522 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); | ||
| 2523 | } | ||
| 2524 | |||
| 2518 | if (self.bundle_compiler_rt) |x| { | 2525 | if (self.bundle_compiler_rt) |x| { |
| 2519 | if (x) { | 2526 | if (x) { |
| 2520 | try zig_args.append("-fcompiler-rt"); | 2527 | try zig_args.append("-fcompiler-rt"); |
src/Compilation.zig+4| ... | @@ -815,6 +815,8 @@ pub const InitOptions = struct { | ... | @@ -815,6 +815,8 @@ pub const InitOptions = struct { |
| 815 | native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null, | 815 | native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null, |
| 816 | /// (Darwin) Install name of the dylib | 816 | /// (Darwin) Install name of the dylib |
| 817 | install_name: ?[]const u8 = null, | 817 | install_name: ?[]const u8 = null, |
| 818 | /// (Darwin) Path to entitlements file | ||
| 819 | entitlements: ?[]const u8 = null, | ||
| 818 | }; | 820 | }; |
| 819 | 821 | ||
| 820 | fn addPackageTableToCacheHash( | 822 | fn addPackageTableToCacheHash( |
| ... | @@ -1624,6 +1626,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1624,6 +1626,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1624 | .enable_link_snapshots = options.enable_link_snapshots, | 1626 | .enable_link_snapshots = options.enable_link_snapshots, |
| 1625 | .native_darwin_sdk = options.native_darwin_sdk, | 1627 | .native_darwin_sdk = options.native_darwin_sdk, |
| 1626 | .install_name = options.install_name, | 1628 | .install_name = options.install_name, |
| 1629 | .entitlements = options.entitlements, | ||
| 1627 | }); | 1630 | }); |
| 1628 | errdefer bin_file.destroy(); | 1631 | errdefer bin_file.destroy(); |
| 1629 | comp.* = .{ | 1632 | comp.* = .{ |
| ... | @@ -2351,6 +2354,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes | ... | @@ -2351,6 +2354,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2351 | // Mach-O specific stuff | 2354 | // Mach-O specific stuff |
| 2352 | man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); | 2355 | man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); |
| 2353 | man.hash.addListOfBytes(comp.bin_file.options.frameworks); | 2356 | man.hash.addListOfBytes(comp.bin_file.options.frameworks); |
| 2357 | try man.addOptionalFile(comp.bin_file.options.entitlements); | ||
| 2354 | 2358 | ||
| 2355 | // COFF specific stuff | 2359 | // COFF specific stuff |
| 2356 | man.hash.addOptional(comp.bin_file.options.subsystem); | 2360 | man.hash.addOptional(comp.bin_file.options.subsystem); |
src/link.zig+3| ... | @@ -183,6 +183,9 @@ pub const Options = struct { | ... | @@ -183,6 +183,9 @@ pub const Options = struct { |
| 183 | /// (Darwin) Install name for the dylib | 183 | /// (Darwin) Install name for the dylib |
| 184 | install_name: ?[]const u8 = null, | 184 | install_name: ?[]const u8 = null, |
| 185 | 185 | ||
| 186 | /// (Darwin) Path to entitlements file | ||
| 187 | entitlements: ?[]const u8 = null, | ||
| 188 | |||
| 186 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { | 189 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { |
| 187 | return if (options.use_lld) .Obj else options.output_mode; | 190 | return if (options.use_lld) .Obj else options.output_mode; |
| 188 | } | 191 | } |
src/link/MachO.zig+48-38| ... | @@ -58,11 +58,6 @@ d_sym: ?DebugSymbols = null, | ... | @@ -58,11 +58,6 @@ d_sym: ?DebugSymbols = null, |
| 58 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. | 58 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| 59 | page_size: u16, | 59 | page_size: u16, |
| 60 | 60 | ||
| 61 | /// TODO Should we figure out embedding code signatures for other Apple platforms as part of the linker? | ||
| 62 | /// Or should this be a separate tool? | ||
| 63 | /// https://github.com/ziglang/zig/issues/9567 | ||
| 64 | requires_adhoc_codesig: bool, | ||
| 65 | |||
| 66 | /// If true, the linker will preallocate several sections and segments before starting the linking | 61 | /// If true, the linker will preallocate several sections and segments before starting the linking |
| 67 | /// process. This is for example true for stage2 debug builds, however, this is false for stage1 | 62 | /// process. This is for example true for stage2 debug builds, however, this is false for stage1 |
| 68 | /// and potentially stage2 release builds in the future. | 63 | /// and potentially stage2 release builds in the future. |
| ... | @@ -76,6 +71,9 @@ header_pad: u16 = 0x1000, | ... | @@ -76,6 +71,9 @@ header_pad: u16 = 0x1000, |
| 76 | /// The absolute address of the entry point. | 71 | /// The absolute address of the entry point. |
| 77 | entry_addr: ?u64 = null, | 72 | entry_addr: ?u64 = null, |
| 78 | 73 | ||
| 74 | /// Code signature (if any) | ||
| 75 | code_signature: ?CodeSignature = null, | ||
| 76 | |||
| 79 | objects: std.ArrayListUnmanaged(Object) = .{}, | 77 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 80 | archives: std.ArrayListUnmanaged(Archive) = .{}, | 78 | archives: std.ArrayListUnmanaged(Archive) = .{}, |
| 81 | 79 | ||
| ... | @@ -402,7 +400,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { | ... | @@ -402,7 +400,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { |
| 402 | .file = null, | 400 | .file = null, |
| 403 | }, | 401 | }, |
| 404 | .page_size = page_size, | 402 | .page_size = page_size, |
| 405 | .requires_adhoc_codesig = requires_adhoc_codesig, | 403 | .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null, |
| 406 | .needs_prealloc = needs_prealloc, | 404 | .needs_prealloc = needs_prealloc, |
| 407 | }; | 405 | }; |
| 408 | 406 | ||
| ... | @@ -534,6 +532,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -534,6 +532,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 534 | } | 532 | } |
| 535 | link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); | 533 | link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); |
| 536 | man.hash.addOptionalBytes(self.base.options.sysroot); | 534 | man.hash.addOptionalBytes(self.base.options.sysroot); |
| 535 | try man.addOptionalFile(self.base.options.entitlements); | ||
| 537 | 536 | ||
| 538 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. | 537 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| 539 | _ = try man.hit(); | 538 | _ = try man.hit(); |
| ... | @@ -859,6 +858,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -859,6 +858,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 859 | self.load_commands_dirty = true; | 858 | self.load_commands_dirty = true; |
| 860 | } | 859 | } |
| 861 | 860 | ||
| 861 | // code signature and entitlements | ||
| 862 | if (self.base.options.entitlements) |path| { | ||
| 863 | if (self.code_signature) |*csig| { | ||
| 864 | try csig.addEntitlements(self.base.allocator, path); | ||
| 865 | csig.code_directory.ident = self.base.options.emit.?.sub_path; | ||
| 866 | } else { | ||
| 867 | var csig = CodeSignature.init(self.page_size); | ||
| 868 | try csig.addEntitlements(self.base.allocator, path); | ||
| 869 | csig.code_directory.ident = self.base.options.emit.?.sub_path; | ||
| 870 | self.code_signature = csig; | ||
| 871 | } | ||
| 872 | } | ||
| 873 | |||
| 862 | if (self.base.options.verbose_link) { | 874 | if (self.base.options.verbose_link) { |
| 863 | var argv = std.ArrayList([]const u8).init(arena); | 875 | var argv = std.ArrayList([]const u8).init(arena); |
| 864 | 876 | ||
| ... | @@ -1033,13 +1045,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -1033,13 +1045,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 1033 | try d_sym.flushModule(self.base.allocator, self.base.options); | 1045 | try d_sym.flushModule(self.base.allocator, self.base.options); |
| 1034 | } | 1046 | } |
| 1035 | 1047 | ||
| 1036 | if (self.requires_adhoc_codesig) { | 1048 | if (self.code_signature) |*csig| { |
| 1049 | csig.clear(self.base.allocator); | ||
| 1050 | csig.code_directory.ident = self.base.options.emit.?.sub_path; | ||
| 1037 | // Preallocate space for the code signature. | 1051 | // Preallocate space for the code signature. |
| 1038 | // We need to do this at this stage so that we have the load commands with proper values | 1052 | // We need to do this at this stage so that we have the load commands with proper values |
| 1039 | // written out to the file. | 1053 | // written out to the file. |
| 1040 | // The most important here is to have the correct vm and filesize of the __LINKEDIT segment | 1054 | // The most important here is to have the correct vm and filesize of the __LINKEDIT segment |
| 1041 | // where the code signature goes into. | 1055 | // where the code signature goes into. |
| 1042 | try self.writeCodeSignaturePadding(); | 1056 | try self.writeCodeSignaturePadding(csig); |
| 1043 | } | 1057 | } |
| 1044 | 1058 | ||
| 1045 | try self.writeLoadCommands(); | 1059 | try self.writeLoadCommands(); |
| ... | @@ -1055,8 +1069,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -1055,8 +1069,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 1055 | 1069 | ||
| 1056 | assert(!self.load_commands_dirty); | 1070 | assert(!self.load_commands_dirty); |
| 1057 | 1071 | ||
| 1058 | if (self.requires_adhoc_codesig) { | 1072 | if (self.code_signature) |*csig| { |
| 1059 | try self.writeCodeSignature(); // code signing always comes last | 1073 | try self.writeCodeSignature(csig); // code signing always comes last |
| 1060 | } | 1074 | } |
| 1061 | 1075 | ||
| 1062 | if (build_options.enable_link_snapshots) { | 1076 | if (build_options.enable_link_snapshots) { |
| ... | @@ -3315,7 +3329,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void { | ... | @@ -3315,7 +3329,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void { |
| 3315 | } | 3329 | } |
| 3316 | 3330 | ||
| 3317 | fn addCodeSignatureLC(self: *MachO) !void { | 3331 | fn addCodeSignatureLC(self: *MachO) !void { |
| 3318 | if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return; | 3332 | if (self.code_signature_cmd_index != null or self.code_signature == null) return; |
| 3319 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | 3333 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 3320 | try self.load_commands.append(self.base.allocator, .{ | 3334 | try self.load_commands.append(self.base.allocator, .{ |
| 3321 | .linkedit_data = .{ | 3335 | .linkedit_data = .{ |
| ... | @@ -3429,6 +3443,10 @@ pub fn deinit(self: *MachO) void { | ... | @@ -3429,6 +3443,10 @@ pub fn deinit(self: *MachO) void { |
| 3429 | } | 3443 | } |
| 3430 | 3444 | ||
| 3431 | self.atom_by_index_table.deinit(self.base.allocator); | 3445 | self.atom_by_index_table.deinit(self.base.allocator); |
| 3446 | |||
| 3447 | if (self.code_signature) |*csig| { | ||
| 3448 | csig.deinit(self.base.allocator); | ||
| 3449 | } | ||
| 3432 | } | 3450 | } |
| 3433 | 3451 | ||
| 3434 | pub fn closeFiles(self: MachO) void { | 3452 | pub fn closeFiles(self: MachO) void { |
| ... | @@ -6143,7 +6161,7 @@ fn writeLinkeditSegment(self: *MachO) !void { | ... | @@ -6143,7 +6161,7 @@ fn writeLinkeditSegment(self: *MachO) !void { |
| 6143 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); | 6161 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); |
| 6144 | } | 6162 | } |
| 6145 | 6163 | ||
| 6146 | fn writeCodeSignaturePadding(self: *MachO) !void { | 6164 | fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 6147 | const tracy = trace(@src()); | 6165 | const tracy = trace(@src()); |
| 6148 | defer tracy.end(); | 6166 | defer tracy.end(); |
| 6149 | 6167 | ||
| ... | @@ -6153,11 +6171,7 @@ fn writeCodeSignaturePadding(self: *MachO) !void { | ... | @@ -6153,11 +6171,7 @@ fn writeCodeSignaturePadding(self: *MachO) !void { |
| 6153 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 | 6171 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 |
| 6154 | const fileoff = mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, 16); | 6172 | const fileoff = mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, 16); |
| 6155 | const padding = fileoff - (linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize); | 6173 | const padding = fileoff - (linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize); |
| 6156 | const needed_size = CodeSignature.calcCodeSignaturePaddingSize( | 6174 | const needed_size = code_sig.estimateSize(fileoff); |
| 6157 | self.base.options.emit.?.sub_path, | ||
| 6158 | fileoff, | ||
| 6159 | self.page_size, | ||
| 6160 | ); | ||
| 6161 | code_sig_cmd.dataoff = @intCast(u32, fileoff); | 6175 | code_sig_cmd.dataoff = @intCast(u32, fileoff); |
| 6162 | code_sig_cmd.datasize = needed_size; | 6176 | code_sig_cmd.datasize = needed_size; |
| 6163 | 6177 | ||
| ... | @@ -6173,34 +6187,30 @@ fn writeCodeSignaturePadding(self: *MachO) !void { | ... | @@ -6173,34 +6187,30 @@ fn writeCodeSignaturePadding(self: *MachO) !void { |
| 6173 | self.load_commands_dirty = true; | 6187 | self.load_commands_dirty = true; |
| 6174 | } | 6188 | } |
| 6175 | 6189 | ||
| 6176 | fn writeCodeSignature(self: *MachO) !void { | 6190 | fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { |
| 6177 | const tracy = trace(@src()); | 6191 | const tracy = trace(@src()); |
| 6178 | defer tracy.end(); | 6192 | defer tracy.end(); |
| 6179 | 6193 | ||
| 6180 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; | 6194 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 6181 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; | 6195 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; |
| 6182 | 6196 | ||
| 6183 | var code_sig: CodeSignature = .{}; | 6197 | var buffer = std.ArrayList(u8).init(self.base.allocator); |
| 6184 | defer code_sig.deinit(self.base.allocator); | 6198 | defer buffer.deinit(); |
| 6185 | 6199 | try buffer.ensureTotalCapacityPrecise(code_sig.size()); | |
| 6186 | try code_sig.calcAdhocSignature( | 6200 | try code_sig.writeAdhocSignature(self.base.allocator, .{ |
| 6187 | self.base.allocator, | 6201 | .file = self.base.file.?, |
| 6188 | self.base.file.?, | 6202 | .text_segment = text_segment.inner, |
| 6189 | self.base.options.emit.?.sub_path, | 6203 | .code_sig_cmd = code_sig_cmd, |
| 6190 | text_segment.inner, | 6204 | .output_mode = self.base.options.output_mode, |
| 6191 | code_sig_cmd, | 6205 | }, buffer.writer()); |
| 6192 | self.base.options.output_mode, | 6206 | assert(buffer.items.len == code_sig.size()); |
| 6193 | self.page_size, | 6207 | |
| 6194 | ); | 6208 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ |
| 6195 | 6209 | code_sig_cmd.dataoff, | |
| 6196 | var buffer = try self.base.allocator.alloc(u8, code_sig.size()); | 6210 | code_sig_cmd.dataoff + buffer.items.len, |
| 6197 | defer self.base.allocator.free(buffer); | 6211 | }); |
| 6198 | var stream = std.io.fixedBufferStream(buffer); | ||
| 6199 | try code_sig.write(stream.writer()); | ||
| 6200 | |||
| 6201 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); | ||
| 6202 | 6212 | ||
| 6203 | try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); | 6213 | try self.base.file.?.pwriteAll(buffer.items, code_sig_cmd.dataoff); |
| 6204 | } | 6214 | } |
| 6205 | 6215 | ||
| 6206 | /// Writes all load commands and section headers. | 6216 | /// Writes all load commands and section headers. |
src/link/MachO/CodeSignature.zig+315-104| ... | @@ -12,12 +12,102 @@ const Sha256 = std.crypto.hash.sha2.Sha256; | ... | @@ -12,12 +12,102 @@ const Sha256 = std.crypto.hash.sha2.Sha256; |
| 12 | 12 | ||
| 13 | const hash_size: u8 = 32; | 13 | const hash_size: u8 = 32; |
| 14 | 14 | ||
| 15 | const Blob = union(enum) { | ||
| 16 | code_directory: *CodeDirectory, | ||
| 17 | requirements: *Requirements, | ||
| 18 | entitlements: *Entitlements, | ||
| 19 | signature: *Signature, | ||
| 20 | |||
| 21 | fn slotType(self: Blob) u32 { | ||
| 22 | return switch (self) { | ||
| 23 | .code_directory => |x| x.slotType(), | ||
| 24 | .requirements => |x| x.slotType(), | ||
| 25 | .entitlements => |x| x.slotType(), | ||
| 26 | .signature => |x| x.slotType(), | ||
| 27 | }; | ||
| 28 | } | ||
| 29 | |||
| 30 | fn size(self: Blob) u32 { | ||
| 31 | return switch (self) { | ||
| 32 | .code_directory => |x| x.size(), | ||
| 33 | .requirements => |x| x.size(), | ||
| 34 | .entitlements => |x| x.size(), | ||
| 35 | .signature => |x| x.size(), | ||
| 36 | }; | ||
| 37 | } | ||
| 38 | |||
| 39 | fn write(self: Blob, writer: anytype) !void { | ||
| 40 | return switch (self) { | ||
| 41 | .code_directory => |x| x.write(writer), | ||
| 42 | .requirements => |x| x.write(writer), | ||
| 43 | .entitlements => |x| x.write(writer), | ||
| 44 | .signature => |x| x.write(writer), | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 15 | const CodeDirectory = struct { | 49 | const CodeDirectory = struct { |
| 16 | inner: macho.CodeDirectory, | 50 | inner: macho.CodeDirectory, |
| 17 | data: std.ArrayListUnmanaged(u8) = .{}, | 51 | ident: []const u8, |
| 52 | special_slots: [n_special_slots][hash_size]u8, | ||
| 53 | code_slots: std.ArrayListUnmanaged([hash_size]u8) = .{}, | ||
| 54 | |||
| 55 | const n_special_slots: usize = 7; | ||
| 56 | |||
| 57 | fn init(page_size: u16) CodeDirectory { | ||
| 58 | var cdir: CodeDirectory = .{ | ||
| 59 | .inner = .{ | ||
| 60 | .magic = macho.CSMAGIC_CODEDIRECTORY, | ||
| 61 | .length = @sizeOf(macho.CodeDirectory), | ||
| 62 | .version = macho.CS_SUPPORTSEXECSEG, | ||
| 63 | .flags = macho.CS_ADHOC, | ||
| 64 | .hashOffset = 0, | ||
| 65 | .identOffset = @sizeOf(macho.CodeDirectory), | ||
| 66 | .nSpecialSlots = 0, | ||
| 67 | .nCodeSlots = 0, | ||
| 68 | .codeLimit = 0, | ||
| 69 | .hashSize = hash_size, | ||
| 70 | .hashType = macho.CS_HASHTYPE_SHA256, | ||
| 71 | .platform = 0, | ||
| 72 | .pageSize = @truncate(u8, std.math.log2(page_size)), | ||
| 73 | .spare2 = 0, | ||
| 74 | .scatterOffset = 0, | ||
| 75 | .teamOffset = 0, | ||
| 76 | .spare3 = 0, | ||
| 77 | .codeLimit64 = 0, | ||
| 78 | .execSegBase = 0, | ||
| 79 | .execSegLimit = 0, | ||
| 80 | .execSegFlags = 0, | ||
| 81 | }, | ||
| 82 | .ident = undefined, | ||
| 83 | .special_slots = undefined, | ||
| 84 | }; | ||
| 85 | comptime var i = 0; | ||
| 86 | inline while (i < n_special_slots) : (i += 1) { | ||
| 87 | cdir.special_slots[i] = [_]u8{0} ** hash_size; | ||
| 88 | } | ||
| 89 | return cdir; | ||
| 90 | } | ||
| 91 | |||
| 92 | fn deinit(self: *CodeDirectory, allocator: Allocator) void { | ||
| 93 | self.code_slots.deinit(allocator); | ||
| 94 | } | ||
| 95 | |||
| 96 | fn addSpecialHash(self: *CodeDirectory, index: u32, hash: [hash_size]u8) void { | ||
| 97 | assert(index > 0); | ||
| 98 | self.inner.nSpecialSlots = std.math.max(self.inner.nSpecialSlots, index); | ||
| 99 | mem.copy(u8, &self.special_slots[index - 1], &hash); | ||
| 100 | } | ||
| 101 | |||
| 102 | fn slotType(self: CodeDirectory) u32 { | ||
| 103 | _ = self; | ||
| 104 | return macho.CSSLOT_CODEDIRECTORY; | ||
| 105 | } | ||
| 18 | 106 | ||
| 19 | fn size(self: CodeDirectory) u32 { | 107 | fn size(self: CodeDirectory) u32 { |
| 20 | return self.inner.length; | 108 | const code_slots = self.inner.nCodeSlots * hash_size; |
| 109 | const special_slots = self.inner.nSpecialSlots * hash_size; | ||
| 110 | return @sizeOf(macho.CodeDirectory) + @intCast(u32, self.ident.len + 1) + special_slots + code_slots; | ||
| 21 | } | 111 | } |
| 22 | 112 | ||
| 23 | fn write(self: CodeDirectory, writer: anytype) !void { | 113 | fn write(self: CodeDirectory, writer: anytype) !void { |
| ... | @@ -42,142 +132,263 @@ const CodeDirectory = struct { | ... | @@ -42,142 +132,263 @@ const CodeDirectory = struct { |
| 42 | try writer.writeIntBig(u64, self.inner.execSegBase); | 132 | try writer.writeIntBig(u64, self.inner.execSegBase); |
| 43 | try writer.writeIntBig(u64, self.inner.execSegLimit); | 133 | try writer.writeIntBig(u64, self.inner.execSegLimit); |
| 44 | try writer.writeIntBig(u64, self.inner.execSegFlags); | 134 | try writer.writeIntBig(u64, self.inner.execSegFlags); |
| 45 | try writer.writeAll(self.data.items); | 135 | |
| 136 | try writer.writeAll(self.ident); | ||
| 137 | try writer.writeByte(0); | ||
| 138 | |||
| 139 | var i: isize = @intCast(isize, self.inner.nSpecialSlots); | ||
| 140 | while (i > 0) : (i -= 1) { | ||
| 141 | try writer.writeAll(&self.special_slots[@intCast(usize, i - 1)]); | ||
| 142 | } | ||
| 143 | |||
| 144 | for (self.code_slots.items) |slot| { | ||
| 145 | try writer.writeAll(&slot); | ||
| 146 | } | ||
| 46 | } | 147 | } |
| 47 | }; | 148 | }; |
| 48 | 149 | ||
| 49 | /// Code signature blob header. | 150 | const Requirements = struct { |
| 50 | inner: macho.SuperBlob = .{ | 151 | fn deinit(self: *Requirements, allocator: Allocator) void { |
| 51 | .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE, | 152 | _ = self; |
| 52 | .length = @sizeOf(macho.SuperBlob), | 153 | _ = allocator; |
| 53 | .count = 0, | 154 | } |
| 54 | }, | ||
| 55 | 155 | ||
| 56 | /// CodeDirectory header which holds the hash of the binary. | 156 | fn slotType(self: Requirements) u32 { |
| 57 | cdir: ?CodeDirectory = null, | 157 | _ = self; |
| 158 | return macho.CSSLOT_REQUIREMENTS; | ||
| 159 | } | ||
| 58 | 160 | ||
| 59 | pub fn calcAdhocSignature( | 161 | fn size(self: Requirements) u32 { |
| 60 | self: *CodeSignature, | 162 | _ = self; |
| 61 | allocator: Allocator, | 163 | return 3 * @sizeOf(u32); |
| 164 | } | ||
| 165 | |||
| 166 | fn write(self: Requirements, writer: anytype) !void { | ||
| 167 | try writer.writeIntBig(u32, macho.CSMAGIC_REQUIREMENTS); | ||
| 168 | try writer.writeIntBig(u32, self.size()); | ||
| 169 | try writer.writeIntBig(u32, 0); | ||
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | const Entitlements = struct { | ||
| 174 | inner: []const u8, | ||
| 175 | |||
| 176 | fn deinit(self: *Entitlements, allocator: Allocator) void { | ||
| 177 | allocator.free(self.inner); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn slotType(self: Entitlements) u32 { | ||
| 181 | _ = self; | ||
| 182 | return macho.CSSLOT_ENTITLEMENTS; | ||
| 183 | } | ||
| 184 | |||
| 185 | fn size(self: Entitlements) u32 { | ||
| 186 | return @intCast(u32, self.inner.len) + 2 * @sizeOf(u32); | ||
| 187 | } | ||
| 188 | |||
| 189 | fn write(self: Entitlements, writer: anytype) !void { | ||
| 190 | try writer.writeIntBig(u32, macho.CSMAGIC_EMBEDDED_ENTITLEMENTS); | ||
| 191 | try writer.writeIntBig(u32, self.size()); | ||
| 192 | try writer.writeAll(self.inner); | ||
| 193 | } | ||
| 194 | }; | ||
| 195 | |||
| 196 | const Signature = struct { | ||
| 197 | fn deinit(self: *Signature, allocator: Allocator) void { | ||
| 198 | _ = self; | ||
| 199 | _ = allocator; | ||
| 200 | } | ||
| 201 | |||
| 202 | fn slotType(self: Signature) u32 { | ||
| 203 | _ = self; | ||
| 204 | return macho.CSSLOT_SIGNATURESLOT; | ||
| 205 | } | ||
| 206 | |||
| 207 | fn size(self: Signature) u32 { | ||
| 208 | _ = self; | ||
| 209 | return 2 * @sizeOf(u32); | ||
| 210 | } | ||
| 211 | |||
| 212 | fn write(self: Signature, writer: anytype) !void { | ||
| 213 | try writer.writeIntBig(u32, macho.CSMAGIC_BLOBWRAPPER); | ||
| 214 | try writer.writeIntBig(u32, self.size()); | ||
| 215 | } | ||
| 216 | }; | ||
| 217 | |||
| 218 | page_size: u16, | ||
| 219 | code_directory: CodeDirectory, | ||
| 220 | requirements: ?Requirements = null, | ||
| 221 | entitlements: ?Entitlements = null, | ||
| 222 | signature: ?Signature = null, | ||
| 223 | |||
| 224 | pub fn init(page_size: u16) CodeSignature { | ||
| 225 | return .{ | ||
| 226 | .page_size = page_size, | ||
| 227 | .code_directory = CodeDirectory.init(page_size), | ||
| 228 | }; | ||
| 229 | } | ||
| 230 | |||
| 231 | pub fn deinit(self: *CodeSignature, allocator: Allocator) void { | ||
| 232 | self.code_directory.deinit(allocator); | ||
| 233 | if (self.requirements) |*req| { | ||
| 234 | req.deinit(allocator); | ||
| 235 | } | ||
| 236 | if (self.entitlements) |*ents| { | ||
| 237 | ents.deinit(allocator); | ||
| 238 | } | ||
| 239 | if (self.signature) |*sig| { | ||
| 240 | sig.deinit(allocator); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { | ||
| 245 | const file = try fs.cwd().openFile(path, .{}); | ||
| 246 | defer file.close(); | ||
| 247 | const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); | ||
| 248 | self.entitlements = .{ .inner = inner }; | ||
| 249 | } | ||
| 250 | |||
| 251 | pub const WriteOpts = struct { | ||
| 62 | file: fs.File, | 252 | file: fs.File, |
| 63 | id: []const u8, | ||
| 64 | text_segment: macho.segment_command_64, | 253 | text_segment: macho.segment_command_64, |
| 65 | code_sig_cmd: macho.linkedit_data_command, | 254 | code_sig_cmd: macho.linkedit_data_command, |
| 66 | output_mode: std.builtin.OutputMode, | 255 | output_mode: std.builtin.OutputMode, |
| 67 | page_size: u16, | 256 | }; |
| 257 | |||
| 258 | pub fn writeAdhocSignature( | ||
| 259 | self: *CodeSignature, | ||
| 260 | allocator: Allocator, | ||
| 261 | opts: WriteOpts, | ||
| 262 | writer: anytype, | ||
| 68 | ) !void { | 263 | ) !void { |
| 69 | const execSegBase: u64 = text_segment.fileoff; | 264 | var header: macho.SuperBlob = .{ |
| 70 | const execSegLimit: u64 = text_segment.filesize; | 265 | .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE, |
| 71 | const execSegFlags: u64 = if (output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; | 266 | .length = @sizeOf(macho.SuperBlob), |
| 72 | const file_size = code_sig_cmd.dataoff; | 267 | .count = 0, |
| 73 | var cdir = CodeDirectory{ | ||
| 74 | .inner = .{ | ||
| 75 | .magic = macho.CSMAGIC_CODEDIRECTORY, | ||
| 76 | .length = @sizeOf(macho.CodeDirectory), | ||
| 77 | .version = macho.CS_SUPPORTSEXECSEG, | ||
| 78 | .flags = macho.CS_ADHOC, | ||
| 79 | .hashOffset = 0, | ||
| 80 | .identOffset = 0, | ||
| 81 | .nSpecialSlots = 0, | ||
| 82 | .nCodeSlots = 0, | ||
| 83 | .codeLimit = file_size, | ||
| 84 | .hashSize = hash_size, | ||
| 85 | .hashType = macho.CS_HASHTYPE_SHA256, | ||
| 86 | .platform = 0, | ||
| 87 | .pageSize = @truncate(u8, std.math.log2(page_size)), | ||
| 88 | .spare2 = 0, | ||
| 89 | .scatterOffset = 0, | ||
| 90 | .teamOffset = 0, | ||
| 91 | .spare3 = 0, | ||
| 92 | .codeLimit64 = 0, | ||
| 93 | .execSegBase = execSegBase, | ||
| 94 | .execSegLimit = execSegLimit, | ||
| 95 | .execSegFlags = execSegFlags, | ||
| 96 | }, | ||
| 97 | }; | 268 | }; |
| 98 | 269 | ||
| 99 | const total_pages = mem.alignForward(file_size, page_size) / page_size; | 270 | var blobs = std.ArrayList(Blob).init(allocator); |
| 271 | defer blobs.deinit(); | ||
| 100 | 272 | ||
| 101 | var hash: [hash_size]u8 = undefined; | 273 | self.code_directory.inner.execSegBase = opts.text_segment.fileoff; |
| 102 | var buffer = try allocator.alloc(u8, page_size); | 274 | self.code_directory.inner.execSegLimit = opts.text_segment.filesize; |
| 103 | defer allocator.free(buffer); | 275 | self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; |
| 276 | const file_size = opts.code_sig_cmd.dataoff; | ||
| 277 | self.code_directory.inner.codeLimit = file_size; | ||
| 104 | 278 | ||
| 105 | try cdir.data.ensureTotalCapacityPrecise(allocator, total_pages * hash_size + id.len + 1); | 279 | const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size; |
| 106 | 280 | ||
| 107 | // 1. Save the identifier and update offsets | 281 | var buffer = try allocator.alloc(u8, self.page_size); |
| 108 | cdir.inner.identOffset = cdir.inner.length; | 282 | defer allocator.free(buffer); |
| 109 | cdir.data.appendSliceAssumeCapacity(id); | ||
| 110 | cdir.data.appendAssumeCapacity(0); | ||
| 111 | 283 | ||
| 112 | // 2. Calculate hash for each page (in file) and write it to the buffer | 284 | try self.code_directory.code_slots.ensureTotalCapacityPrecise(allocator, total_pages); |
| 113 | // TODO figure out how we can cache several hashes since we won't update | 285 | |
| 114 | // every page during incremental linking | 286 | // Calculate hash for each page (in file) and write it to the buffer |
| 115 | cdir.inner.hashOffset = cdir.inner.identOffset + @intCast(u32, id.len) + 1; | 287 | var hash: [hash_size]u8 = undefined; |
| 116 | var i: usize = 0; | 288 | var i: usize = 0; |
| 117 | while (i < total_pages) : (i += 1) { | 289 | while (i < total_pages) : (i += 1) { |
| 118 | const fstart = i * page_size; | 290 | const fstart = i * self.page_size; |
| 119 | const fsize = if (fstart + page_size > file_size) file_size - fstart else page_size; | 291 | const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size; |
| 120 | const len = try file.preadAll(buffer, fstart); | 292 | const len = try opts.file.preadAll(buffer, fstart); |
| 121 | assert(fsize <= len); | 293 | assert(fsize <= len); |
| 122 | 294 | ||
| 123 | Sha256.hash(buffer[0..fsize], &hash, .{}); | 295 | Sha256.hash(buffer[0..fsize], &hash, .{}); |
| 124 | 296 | ||
| 125 | cdir.data.appendSliceAssumeCapacity(&hash); | 297 | self.code_directory.code_slots.appendAssumeCapacity(hash); |
| 126 | cdir.inner.nCodeSlots += 1; | 298 | self.code_directory.inner.nCodeSlots += 1; |
| 127 | } | 299 | } |
| 128 | 300 | ||
| 129 | // 3. Update CodeDirectory length | 301 | try blobs.append(.{ .code_directory = &self.code_directory }); |
| 130 | cdir.inner.length += @intCast(u32, cdir.data.items.len); | 302 | header.length += @sizeOf(macho.BlobIndex); |
| 303 | header.count += 1; | ||
| 131 | 304 | ||
| 132 | self.inner.length += @sizeOf(macho.BlobIndex) + cdir.size(); | 305 | if (self.requirements) |*req| { |
| 133 | self.inner.count = 1; | 306 | var buf = std.ArrayList(u8).init(allocator); |
| 134 | self.cdir = cdir; | 307 | defer buf.deinit(); |
| 135 | } | 308 | try req.write(buf.writer()); |
| 309 | Sha256.hash(buf.items, &hash, .{}); | ||
| 310 | self.code_directory.addSpecialHash(req.slotType(), hash); | ||
| 136 | 311 | ||
| 137 | pub fn size(self: CodeSignature) u32 { | 312 | try blobs.append(.{ .requirements = req }); |
| 138 | return self.inner.length; | 313 | header.count += 1; |
| 139 | } | 314 | header.length += @sizeOf(macho.BlobIndex) + req.size(); |
| 315 | } | ||
| 140 | 316 | ||
| 141 | pub fn write(self: CodeSignature, writer: anytype) !void { | 317 | if (self.entitlements) |*ents| { |
| 142 | try self.writeHeader(writer); | 318 | var buf = std.ArrayList(u8).init(allocator); |
| 143 | const offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex); | 319 | defer buf.deinit(); |
| 144 | try writeBlobIndex(macho.CSSLOT_CODEDIRECTORY, offset, writer); | 320 | try ents.write(buf.writer()); |
| 145 | try self.cdir.?.write(writer); | 321 | Sha256.hash(buf.items, &hash, .{}); |
| 146 | } | 322 | self.code_directory.addSpecialHash(ents.slotType(), hash); |
| 147 | 323 | ||
| 148 | pub fn deinit(self: *CodeSignature, allocator: Allocator) void { | 324 | try blobs.append(.{ .entitlements = ents }); |
| 149 | if (self.cdir) |*cdir| { | 325 | header.count += 1; |
| 150 | cdir.data.deinit(allocator); | 326 | header.length += @sizeOf(macho.BlobIndex) + ents.size(); |
| 151 | } | 327 | } |
| 152 | } | ||
| 153 | 328 | ||
| 154 | fn writeHeader(self: CodeSignature, writer: anytype) !void { | 329 | if (self.signature) |*sig| { |
| 155 | try writer.writeIntBig(u32, self.inner.magic); | 330 | try blobs.append(.{ .signature = sig }); |
| 156 | try writer.writeIntBig(u32, self.inner.length); | 331 | header.count += 1; |
| 157 | try writer.writeIntBig(u32, self.inner.count); | 332 | header.length += @sizeOf(macho.BlobIndex) + sig.size(); |
| 158 | } | 333 | } |
| 159 | 334 | ||
| 160 | fn writeBlobIndex(tt: u32, offset: u32, writer: anytype) !void { | 335 | self.code_directory.inner.hashOffset = |
| 161 | try writer.writeIntBig(u32, tt); | 336 | @sizeOf(macho.CodeDirectory) + @intCast(u32, self.code_directory.ident.len + 1) + self.code_directory.inner.nSpecialSlots * hash_size; |
| 162 | try writer.writeIntBig(u32, offset); | 337 | self.code_directory.inner.length = self.code_directory.size(); |
| 163 | } | 338 | header.length += self.code_directory.size(); |
| 164 | 339 | ||
| 165 | test "CodeSignature header" { | 340 | try writer.writeIntBig(u32, header.magic); |
| 166 | var code_sig: CodeSignature = .{}; | 341 | try writer.writeIntBig(u32, header.length); |
| 167 | defer code_sig.deinit(testing.allocator); | 342 | try writer.writeIntBig(u32, header.count); |
| 168 | 343 | ||
| 169 | var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined; | 344 | var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @intCast(u32, blobs.items.len); |
| 170 | var stream = std.io.fixedBufferStream(&buffer); | 345 | for (blobs.items) |blob| { |
| 171 | try code_sig.writeHeader(stream.writer()); | 346 | try writer.writeIntBig(u32, blob.slotType()); |
| 347 | try writer.writeIntBig(u32, offset); | ||
| 348 | offset += blob.size(); | ||
| 349 | } | ||
| 172 | 350 | ||
| 173 | const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 }; | 351 | for (blobs.items) |blob| { |
| 174 | try testing.expect(mem.eql(u8, expected, &buffer)); | 352 | try blob.write(writer); |
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | pub fn size(self: CodeSignature) u32 { | ||
| 357 | var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); | ||
| 358 | if (self.requirements) |req| { | ||
| 359 | ssize += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 360 | } | ||
| 361 | if (self.entitlements) |ent| { | ||
| 362 | ssize += @sizeOf(macho.BlobIndex) + ent.size(); | ||
| 363 | } | ||
| 364 | if (self.signature) |sig| { | ||
| 365 | ssize += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 366 | } | ||
| 367 | return ssize; | ||
| 368 | } | ||
| 369 | |||
| 370 | pub fn estimateSize(self: CodeSignature, file_size: u64) u32 { | ||
| 371 | var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); | ||
| 372 | // Approx code slots | ||
| 373 | const total_pages = mem.alignForwardGeneric(u64, file_size, self.page_size) / self.page_size; | ||
| 374 | ssize += total_pages * hash_size; | ||
| 375 | var n_special_slots: u32 = 0; | ||
| 376 | if (self.requirements) |req| { | ||
| 377 | ssize += @sizeOf(macho.BlobIndex) + req.size(); | ||
| 378 | n_special_slots = std.math.max(n_special_slots, req.slotType()); | ||
| 379 | } | ||
| 380 | if (self.entitlements) |ent| { | ||
| 381 | ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size; | ||
| 382 | n_special_slots = std.math.max(n_special_slots, ent.slotType()); | ||
| 383 | } | ||
| 384 | if (self.signature) |sig| { | ||
| 385 | ssize += @sizeOf(macho.BlobIndex) + sig.size(); | ||
| 386 | } | ||
| 387 | ssize += n_special_slots * hash_size; | ||
| 388 | return @intCast(u32, mem.alignForwardGeneric(u64, ssize, @sizeOf(u64))); | ||
| 175 | } | 389 | } |
| 176 | 390 | ||
| 177 | pub fn calcCodeSignaturePaddingSize(id: []const u8, file_size: u64, page_size: u16) u32 { | 391 | pub fn clear(self: *CodeSignature, allocator: Allocator) void { |
| 178 | const ident_size = id.len + 1; | 392 | self.code_directory.deinit(allocator); |
| 179 | const total_pages = mem.alignForwardGeneric(u64, file_size, page_size) / page_size; | 393 | self.code_directory = CodeDirectory.init(self.page_size); |
| 180 | const hashed_size = total_pages * hash_size; | ||
| 181 | const codesig_header = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + @sizeOf(macho.CodeDirectory); | ||
| 182 | return @intCast(u32, mem.alignForwardGeneric(u64, codesig_header + ident_size + hashed_size, @sizeOf(u64))); | ||
| 183 | } | 394 | } |
src/main.zig+7| ... | @@ -433,6 +433,7 @@ const usage_build_generic = | ... | @@ -433,6 +433,7 @@ const usage_build_generic = |
| 433 | \\ -framework [name] (Darwin) link against framework | 433 | \\ -framework [name] (Darwin) link against framework |
| 434 | \\ -F[dir] (Darwin) add search path for frameworks | 434 | \\ -F[dir] (Darwin) add search path for frameworks |
| 435 | \\ -install_name=[value] (Darwin) add dylib's install name | 435 | \\ -install_name=[value] (Darwin) add dylib's install name |
| 436 | \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature | ||
| 436 | \\ --import-memory (WebAssembly) import memory from the environment | 437 | \\ --import-memory (WebAssembly) import memory from the environment |
| 437 | \\ --import-table (WebAssembly) import function table from the host environment | 438 | \\ --import-table (WebAssembly) import function table from the host environment |
| 438 | \\ --export-table (WebAssembly) export function table to the host environment | 439 | \\ --export-table (WebAssembly) export function table to the host environment |
| ... | @@ -680,6 +681,7 @@ fn buildOutputType( | ... | @@ -680,6 +681,7 @@ fn buildOutputType( |
| 680 | var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null; | 681 | var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null; |
| 681 | var install_name: ?[]const u8 = null; | 682 | var install_name: ?[]const u8 = null; |
| 682 | var hash_style: link.HashStyle = .both; | 683 | var hash_style: link.HashStyle = .both; |
| 684 | var entitlements: ?[]const u8 = null; | ||
| 683 | 685 | ||
| 684 | // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. | 686 | // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. |
| 685 | // This array is populated by zig cc frontend and then has to be converted to zig-style | 687 | // This array is populated by zig cc frontend and then has to be converted to zig-style |
| ... | @@ -1036,6 +1038,10 @@ fn buildOutputType( | ... | @@ -1036,6 +1038,10 @@ fn buildOutputType( |
| 1036 | } else { | 1038 | } else { |
| 1037 | enable_link_snapshots = true; | 1039 | enable_link_snapshots = true; |
| 1038 | } | 1040 | } |
| 1041 | } else if (mem.eql(u8, arg, "--entitlements")) { | ||
| 1042 | entitlements = args_iter.next() orelse { | ||
| 1043 | fatal("expected parameter after {s}", .{arg}); | ||
| 1044 | }; | ||
| 1039 | } else if (mem.eql(u8, arg, "-fcompiler-rt")) { | 1045 | } else if (mem.eql(u8, arg, "-fcompiler-rt")) { |
| 1040 | want_compiler_rt = true; | 1046 | want_compiler_rt = true; |
| 1041 | } else if (mem.eql(u8, arg, "-fno-compiler-rt")) { | 1047 | } else if (mem.eql(u8, arg, "-fno-compiler-rt")) { |
| ... | @@ -2729,6 +2735,7 @@ fn buildOutputType( | ... | @@ -2729,6 +2735,7 @@ fn buildOutputType( |
| 2729 | .enable_link_snapshots = enable_link_snapshots, | 2735 | .enable_link_snapshots = enable_link_snapshots, |
| 2730 | .native_darwin_sdk = native_darwin_sdk, | 2736 | .native_darwin_sdk = native_darwin_sdk, |
| 2731 | .install_name = install_name, | 2737 | .install_name = install_name, |
| 2738 | .entitlements = entitlements, | ||
| 2732 | }) catch |err| switch (err) { | 2739 | }) catch |err| switch (err) { |
| 2733 | error.LibCUnavailable => { | 2740 | error.LibCUnavailable => { |
| 2734 | const target = target_info.target; | 2741 | const target = target_info.target; |