authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-25 10:39:53+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-25 18:04:40+02:00
log589bf67635a9fe9c3e6df4b63c09e0cc4954d29a
treec8e26db7d4f8d83facb6b4a91514bbbff496ed78
parenta6fbdfabb9b60262594da012b8d877a43d8d8e99

macho: implement -headerpad_max_install_names


13 files changed, 180 insertions(+), 61 deletions(-)

lib/std/build.zig+2-2
...@@ -1595,7 +1595,7 @@ pub const LibExeObjStep = struct {...@@ -1595,7 +1595,7 @@ pub const LibExeObjStep = struct {
15951595
1596 /// (Darwin) Set size of the padding between the end of load commands1596 /// (Darwin) Set size of the padding between the end of load commands
1597 /// and start of `__TEXT,__text` section.1597 /// and start of `__TEXT,__text` section.
1598 headerpad_size: ?u64 = null,1598 headerpad_size: ?u32 = null,
15991599
1600 /// (Darwin) Automatically Set size of the padding between the end of load commands1600 /// (Darwin) Automatically Set size of the padding between the end of load commands
1601 /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.1601 /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
...@@ -2671,7 +2671,7 @@ pub const LibExeObjStep = struct {...@@ -2671,7 +2671,7 @@ pub const LibExeObjStep = struct {
2671 };2671 };
2672 if (self.headerpad_size) |headerpad_size| {2672 if (self.headerpad_size) |headerpad_size| {
2673 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});2673 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
2674 try zig_args.appendSlice(&[_][]const u8{ "-headerpad_size", size });2674 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
2675 }2675 }
2676 if (self.headerpad_max_install_names) {2676 if (self.headerpad_max_install_names) {
2677 try zig_args.append("-headerpad_max_install_names");2677 try zig_args.append("-headerpad_max_install_names");
src/Compilation.zig+5-3
...@@ -908,7 +908,7 @@ pub const InitOptions = struct {...@@ -908,7 +908,7 @@ pub const InitOptions = struct {
908 /// (Darwin) search strategy for system libraries908 /// (Darwin) search strategy for system libraries
909 search_strategy: ?link.File.MachO.SearchStrategy = null,909 search_strategy: ?link.File.MachO.SearchStrategy = null,
910 /// (Darwin) set minimum space for future expansion of the load commands910 /// (Darwin) set minimum space for future expansion of the load commands
911 headerpad_size: ?u64 = null,911 headerpad_size: ?u32 = null,
912 /// (Darwin) set enough space as if all paths were MATPATHLEN912 /// (Darwin) set enough space as if all paths were MATPATHLEN
913 headerpad_max_install_names: bool = false,913 headerpad_max_install_names: bool = false,
914};914};
...@@ -2369,7 +2369,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo...@@ -2369,7 +2369,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
2369/// to remind the programmer to update multiple related pieces of code that2369/// to remind the programmer to update multiple related pieces of code that
2370/// are in different locations. Bump this number when adding or deleting2370/// are in different locations. Bump this number when adding or deleting
2371/// anything from the link cache manifest.2371/// anything from the link cache manifest.
2372pub const link_hash_implementation_version = 5;2372pub const link_hash_implementation_version = 6;
23732373
2374fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {2374fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
2375 const gpa = comp.gpa;2375 const gpa = comp.gpa;
...@@ -2379,7 +2379,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2379,7 +2379,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2379 defer arena_allocator.deinit();2379 defer arena_allocator.deinit();
2380 const arena = arena_allocator.allocator();2380 const arena = arena_allocator.allocator();
23812381
2382 comptime assert(link_hash_implementation_version == 5);2382 comptime assert(link_hash_implementation_version == 6);
23832383
2384 if (comp.bin_file.options.module) |mod| {2384 if (comp.bin_file.options.module) |mod| {
2385 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{2385 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
...@@ -2486,6 +2486,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2486,6 +2486,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2486 try man.addOptionalFile(comp.bin_file.options.entitlements);2486 try man.addOptionalFile(comp.bin_file.options.entitlements);
2487 man.hash.addOptional(comp.bin_file.options.pagezero_size);2487 man.hash.addOptional(comp.bin_file.options.pagezero_size);
2488 man.hash.addOptional(comp.bin_file.options.search_strategy);2488 man.hash.addOptional(comp.bin_file.options.search_strategy);
2489 man.hash.addOptional(comp.bin_file.options.headerpad_size);
2490 man.hash.add(comp.bin_file.options.headerpad_max_install_names);
24892491
2490 // COFF specific stuff2492 // COFF specific stuff
2491 man.hash.addOptional(comp.bin_file.options.subsystem);2493 man.hash.addOptional(comp.bin_file.options.subsystem);
src/link.zig+1-1
...@@ -194,7 +194,7 @@ pub const Options = struct {...@@ -194,7 +194,7 @@ pub const Options = struct {
194 search_strategy: ?File.MachO.SearchStrategy = null,194 search_strategy: ?File.MachO.SearchStrategy = null,
195195
196 /// (Darwin) set minimum space for future expansion of the load commands196 /// (Darwin) set minimum space for future expansion of the load commands
197 headerpad_size: ?u64 = null,197 headerpad_size: ?u32 = null,
198198
199 /// (Darwin) set enough space as if all paths were MATPATHLEN199 /// (Darwin) set enough space as if all paths were MATPATHLEN
200 headerpad_max_install_names: bool = false,200 headerpad_max_install_names: bool = false,
src/link/Coff.zig+1-1
...@@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !
969 man = comp.cache_parent.obtain();969 man = comp.cache_parent.obtain();
970 self.base.releaseLock();970 self.base.releaseLock();
971971
972 comptime assert(Compilation.link_hash_implementation_version == 5);972 comptime assert(Compilation.link_hash_implementation_version == 6);
973973
974 for (self.base.options.objects) |obj| {974 for (self.base.options.objects) |obj| {
975 _ = try man.addFile(obj.path, null);975 _ = try man.addFile(obj.path, null);
src/link/Elf.zig+1-1
...@@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1298 // We are about to obtain this lock, so here we give other processes a chance first.1298 // We are about to obtain this lock, so here we give other processes a chance first.
1299 self.base.releaseLock();1299 self.base.releaseLock();
13001300
1301 comptime assert(Compilation.link_hash_implementation_version == 5);1301 comptime assert(Compilation.link_hash_implementation_version == 6);
13021302
1303 try man.addOptionalFile(self.base.options.linker_script);1303 try man.addOptionalFile(self.base.options.linker_script);
1304 try man.addOptionalFile(self.base.options.version_script);1304 try man.addOptionalFile(self.base.options.version_script);
src/link/MachO.zig+35-18
...@@ -69,9 +69,6 @@ page_size: u16,...@@ -69,9 +69,6 @@ page_size: u16,
69/// and potentially stage2 release builds in the future.69/// and potentially stage2 release builds in the future.
70needs_prealloc: bool = true,70needs_prealloc: bool = true,
7171
72/// Size of the padding between the end of load commands and start of the '__TEXT,__text' section.
73headerpad_size: u64,
74
75/// The absolute address of the entry point.72/// The absolute address of the entry point.
76entry_addr: ?u64 = null,73entry_addr: ?u64 = null,
7774
...@@ -296,7 +293,7 @@ const default_pagezero_vmsize: u64 = 0x100000000;...@@ -296,7 +293,7 @@ const default_pagezero_vmsize: u64 = 0x100000000;
296/// We commit 0x1000 = 4096 bytes of space to the header and293/// We commit 0x1000 = 4096 bytes of space to the header and
297/// the table of load commands. This should be plenty for any294/// the table of load commands. This should be plenty for any
298/// potential future extensions.295/// potential future extensions.
299const default_headerpad_size: u64 = 0x1000;296const default_headerpad_size: u32 = 0x1000;
300297
301pub const Export = struct {298pub const Export = struct {
302 sym_index: ?u32 = null,299 sym_index: ?u32 = null,
...@@ -403,12 +400,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -403,12 +400,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
403 const use_llvm = build_options.have_llvm and options.use_llvm;400 const use_llvm = build_options.have_llvm and options.use_llvm;
404 const use_stage1 = build_options.is_stage1 and options.use_stage1;401 const use_stage1 = build_options.is_stage1 and options.use_stage1;
405 const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole);402 const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole);
406 // TODO handle `headerpad_max_install_names` in incremental context
407 const explicit_headerpad_size = options.headerpad_size orelse 0;
408 const headerpad_size = if (needs_prealloc)
409 @maximum(explicit_headerpad_size, default_headerpad_size)
410 else
411 explicit_headerpad_size;
412403
413 const self = try gpa.create(MachO);404 const self = try gpa.create(MachO);
414 errdefer gpa.destroy(self);405 errdefer gpa.destroy(self);
...@@ -421,7 +412,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -421,7 +412,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
421 .file = null,412 .file = null,
422 },413 },
423 .page_size = page_size,414 .page_size = page_size,
424 .headerpad_size = headerpad_size,
425 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,415 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,
426 .needs_prealloc = needs_prealloc,416 .needs_prealloc = needs_prealloc,
427 };417 };
...@@ -551,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -551,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
551 // We are about to obtain this lock, so here we give other processes a chance first.541 // We are about to obtain this lock, so here we give other processes a chance first.
552 self.base.releaseLock();542 self.base.releaseLock();
553543
554 comptime assert(Compilation.link_hash_implementation_version == 5);544 comptime assert(Compilation.link_hash_implementation_version == 6);
555545
556 for (self.base.options.objects) |obj| {546 for (self.base.options.objects) |obj| {
557 _ = try man.addFile(obj.path, null);547 _ = try man.addFile(obj.path, null);
...@@ -566,6 +556,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -566,6 +556,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
566 man.hash.add(stack_size);556 man.hash.add(stack_size);
567 man.hash.addOptional(self.base.options.pagezero_size);557 man.hash.addOptional(self.base.options.pagezero_size);
568 man.hash.addOptional(self.base.options.search_strategy);558 man.hash.addOptional(self.base.options.search_strategy);
559 man.hash.addOptional(self.base.options.headerpad_size);
560 man.hash.add(self.base.options.headerpad_max_install_names);
569 man.hash.addListOfBytes(self.base.options.lib_dirs);561 man.hash.addListOfBytes(self.base.options.lib_dirs);
570 man.hash.addListOfBytes(self.base.options.framework_dirs);562 man.hash.addListOfBytes(self.base.options.framework_dirs);
571 man.hash.addListOfBytes(self.base.options.frameworks);563 man.hash.addListOfBytes(self.base.options.frameworks);
...@@ -4470,9 +4462,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4470,9 +4462,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4470 if (self.text_segment_cmd_index == null) {4462 if (self.text_segment_cmd_index == null) {
4471 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);4463 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4472 const needed_size = if (self.needs_prealloc) blk: {4464 const needed_size = if (self.needs_prealloc) blk: {
4465 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
4473 const program_code_size_hint = self.base.options.program_code_size_hint;4466 const program_code_size_hint = self.base.options.program_code_size_hint;
4474 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;4467 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
4475 const ideal_size = self.headerpad_size + program_code_size_hint + got_size_hint;4468 const ideal_size = headerpad_size + program_code_size_hint + got_size_hint;
4476 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);4469 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4477 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });4470 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
4478 break :blk needed_size;4471 break :blk needed_size;
...@@ -4975,13 +4968,34 @@ fn allocateTextSegment(self: *MachO) !void {...@@ -4975,13 +4968,34 @@ fn allocateTextSegment(self: *MachO) !void {
4975 seg.inner.fileoff = 0;4968 seg.inner.fileoff = 0;
4976 seg.inner.vmaddr = base_vmaddr;4969 seg.inner.vmaddr = base_vmaddr;
49774970
4978 var sizeofcmds: u64 = 0;4971 var sizeofcmds: u32 = 0;
4979 for (self.load_commands.items) |lc| {4972 for (self.load_commands.items) |lc| {
4980 sizeofcmds += lc.cmdsize();4973 sizeofcmds += lc.cmdsize();
4981 }4974 }
49824975
4983 // TODO verify if `headerpad_max_install_names` leads to larger padding size4976 var padding: u32 = sizeofcmds + (self.base.options.headerpad_size orelse 0);
4984 const offset = @sizeOf(macho.mach_header_64) + sizeofcmds + self.headerpad_size;4977 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
4978
4979 if (self.base.options.headerpad_max_install_names) {
4980 var min_headerpad_size: u32 = 0;
4981 for (self.load_commands.items) |lc| switch (lc.cmd()) {
4982 .ID_DYLIB,
4983 .LOAD_WEAK_DYLIB,
4984 .LOAD_DYLIB,
4985 .REEXPORT_DYLIB,
4986 => {
4987 min_headerpad_size += @sizeOf(macho.dylib_command) + std.os.PATH_MAX + 1;
4988 },
4989
4990 else => {},
4991 };
4992 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
4993 min_headerpad_size + @sizeOf(macho.mach_header_64),
4994 });
4995 padding = @maximum(padding, min_headerpad_size);
4996 }
4997 const offset = @sizeOf(macho.mach_header_64) + padding;
4998 log.debug("actual headerpad size 0x{x}", .{offset});
4985 try self.allocateSegment(self.text_segment_cmd_index.?, offset);4999 try self.allocateSegment(self.text_segment_cmd_index.?, offset);
49865000
4987 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.5001 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
...@@ -5109,7 +5123,10 @@ fn initSection(...@@ -5109,7 +5123,10 @@ fn initSection(
51095123
5110 if (self.needs_prealloc) {5124 if (self.needs_prealloc) {
5111 const alignment_pow_2 = try math.powi(u32, 2, alignment);5125 const alignment_pow_2 = try math.powi(u32, 2, alignment);
5112 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.headerpad_size else null;5126 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)
5127 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)
5128 else
5129 null;
5113 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);5130 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
5114 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{5131 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
5115 sect.segName(),5132 sect.segName(),
...@@ -5148,7 +5165,7 @@ fn initSection(...@@ -5148,7 +5165,7 @@ fn initSection(
5148 return index;5165 return index;
5149}5166}
51505167
5151fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {5168fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 {
5152 const seg = self.load_commands.items[segment_id].segment;5169 const seg = self.load_commands.items[segment_id].segment;
5153 if (seg.sections.items.len == 0) {5170 if (seg.sections.items.len == 0) {
5154 return if (start) |v| v else seg.inner.fileoff;5171 return if (start) |v| v else seg.inner.fileoff;
src/link/Wasm.zig+1-1
...@@ -2481,7 +2481,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -2481,7 +2481,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
2481 // We are about to obtain this lock, so here we give other processes a chance first.2481 // We are about to obtain this lock, so here we give other processes a chance first.
2482 self.base.releaseLock();2482 self.base.releaseLock();
24832483
2484 comptime assert(Compilation.link_hash_implementation_version == 5);2484 comptime assert(Compilation.link_hash_implementation_version == 6);
24852485
2486 for (self.base.options.objects) |obj| {2486 for (self.base.options.objects) |obj| {
2487 _ = try man.addFile(obj.path, null);2487 _ = try man.addFile(obj.path, null);
src/main.zig+6-6
...@@ -450,7 +450,7 @@ const usage_build_generic =...@@ -450,7 +450,7 @@ const usage_build_generic =
450 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation450 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
451 \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`451 \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
452 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`452 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
453 \\ -headerpad_size [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation453 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
454 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN454 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
455 \\ --import-memory (WebAssembly) import memory from the environment455 \\ --import-memory (WebAssembly) import memory from the environment
456 \\ --import-table (WebAssembly) import function table from the host environment456 \\ --import-table (WebAssembly) import function table from the host environment
...@@ -701,7 +701,7 @@ fn buildOutputType(...@@ -701,7 +701,7 @@ fn buildOutputType(
701 var entitlements: ?[]const u8 = null;701 var entitlements: ?[]const u8 = null;
702 var pagezero_size: ?u64 = null;702 var pagezero_size: ?u64 = null;
703 var search_strategy: ?link.File.MachO.SearchStrategy = null;703 var search_strategy: ?link.File.MachO.SearchStrategy = null;
704 var headerpad_size: ?u64 = null;704 var headerpad_size: ?u32 = null;
705 var headerpad_max_install_names: bool = false;705 var headerpad_max_install_names: bool = false;
706706
707 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.707 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
...@@ -928,11 +928,11 @@ fn buildOutputType(...@@ -928,11 +928,11 @@ fn buildOutputType(
928 search_strategy = .paths_first;928 search_strategy = .paths_first;
929 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {929 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
930 search_strategy = .dylibs_first;930 search_strategy = .dylibs_first;
931 } else if (mem.eql(u8, arg, "-headerpad_size")) {931 } else if (mem.eql(u8, arg, "-headerpad")) {
932 const next_arg = args_iter.next() orelse {932 const next_arg = args_iter.next() orelse {
933 fatal("expected parameter after {s}", .{arg});933 fatal("expected parameter after {s}", .{arg});
934 };934 };
935 headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {935 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
936 fatal("unable to parser '{s}': {s}", .{ arg, @errorName(err) });936 fatal("unable to parser '{s}': {s}", .{ arg, @errorName(err) });
937 };937 };
938 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {938 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
...@@ -1689,13 +1689,13 @@ fn buildOutputType(...@@ -1689,13 +1689,13 @@ fn buildOutputType(
1689 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {1689 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
1690 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });1690 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1691 };1691 };
1692 } else if (mem.eql(u8, arg, "-headerpad_size")) {1692 } else if (mem.eql(u8, arg, "-headerpad")) {
1693 i += 1;1693 i += 1;
1694 if (i >= linker_args.items.len) {1694 if (i >= linker_args.items.len) {
1695 fatal("expected linker arg after '{s}'", .{arg});1695 fatal("expected linker arg after '{s}'", .{arg});
1696 }1696 }
1697 const next_arg = linker_args.items[i];1697 const next_arg = linker_args.items[i];
1698 headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {1698 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
1699 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });1699 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1700 };1700 };
1701 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {1701 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
test/link.zig+5
...@@ -64,5 +64,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -64,5 +64,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
64 cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{64 cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{
65 .build_modes = true,65 .build_modes = true,
66 });66 });
67
68 cases.addBuildFile("test/link/macho/headerpad/build.zig", .{
69 .build_modes = true,
70 .requires_macos_sdk = true,
71 });
67 }72 }
68}73}
test/link/macho/headerpad/build.zig created+120
...@@ -0,0 +1,120 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
5
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
8
9 const test_step = b.step("test", "Test");
10 test_step.dependOn(b.getInstallStep());
11
12 {
13 // Test -headerpad_max_install_names
14 const exe = simpleExe(b, mode);
15 exe.headerpad_max_install_names = true;
16
17 const check = exe.checkObject(.macho);
18 check.checkStart("sectname __text");
19 check.checkNext("offset {offset}");
20
21 switch (builtin.cpu.arch) {
22 .aarch64 => {
23 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } });
24 },
25 .x86_64 => {
26 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } });
27 },
28 else => unreachable,
29 }
30
31 test_step.dependOn(&check.step);
32
33 const run = exe.run();
34 test_step.dependOn(&run.step);
35 }
36
37 {
38 // Test -headerpad
39 const exe = simpleExe(b, mode);
40 exe.headerpad_size = 0x10000;
41
42 const check = exe.checkObject(.macho);
43 check.checkStart("sectname __text");
44 check.checkNext("offset {offset}");
45 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
46
47 test_step.dependOn(&check.step);
48
49 const run = exe.run();
50 test_step.dependOn(&run.step);
51 }
52
53 {
54 // Test both flags with -headerpad overriding -headerpad_max_install_names
55 const exe = simpleExe(b, mode);
56 exe.headerpad_max_install_names = true;
57 exe.headerpad_size = 0x10000;
58
59 const check = exe.checkObject(.macho);
60 check.checkStart("sectname __text");
61 check.checkNext("offset {offset}");
62 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
63
64 test_step.dependOn(&check.step);
65
66 const run = exe.run();
67 test_step.dependOn(&run.step);
68 }
69
70 {
71 // Test both flags with -headerpad_max_install_names overriding -headerpad
72 const exe = simpleExe(b, mode);
73 exe.headerpad_size = 0x1000;
74 exe.headerpad_max_install_names = true;
75
76 const check = exe.checkObject(.macho);
77 check.checkStart("sectname __text");
78 check.checkNext("offset {offset}");
79
80 switch (builtin.cpu.arch) {
81 .aarch64 => {
82 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } });
83 },
84 .x86_64 => {
85 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } });
86 },
87 else => unreachable,
88 }
89
90 test_step.dependOn(&check.step);
91
92 const run = exe.run();
93 test_step.dependOn(&run.step);
94 }
95}
96
97fn simpleExe(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
98 const exe = b.addExecutable("main", null);
99 exe.setBuildMode(mode);
100 exe.addCSourceFile("main.c", &.{});
101 exe.linkLibC();
102 exe.linkFramework("CoreFoundation");
103 exe.linkFramework("Foundation");
104 exe.linkFramework("Cocoa");
105 exe.linkFramework("CoreGraphics");
106 exe.linkFramework("CoreHaptics");
107 exe.linkFramework("CoreAudio");
108 exe.linkFramework("AVFoundation");
109 exe.linkFramework("CoreImage");
110 exe.linkFramework("CoreLocation");
111 exe.linkFramework("CoreML");
112 exe.linkFramework("CoreVideo");
113 exe.linkFramework("CoreText");
114 exe.linkFramework("CryptoKit");
115 exe.linkFramework("GameKit");
116 exe.linkFramework("SwiftUI");
117 exe.linkFramework("StoreKit");
118 exe.linkFramework("SpriteKit");
119 return exe;
120}
test/link/macho/headerpad/main.c created+3
...@@ -0,0 +1,3 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/headerpad_size/build.zig deleted-25
...@@ -1,25 +0,0 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
9
10 const exe = b.addExecutable("main", null);
11 exe.setBuildMode(mode);
12 exe.addCSourceFile("main.c", &.{});
13 exe.linkLibC();
14 exe.headerpad_size = 0x10000;
15
16 const check = exe.checkObject(.macho);
17 check.checkStart("sectname __text");
18 check.checkNext("offset {offset}");
19 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
20
21 test_step.dependOn(&check.step);
22
23 const run = exe.run();
24 test_step.dependOn(&run.step);
25}
test/link/macho/headerpad_size/main.c deleted-3
...@@ -1,3 +0,0 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}