authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-25 00:34:16+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-25 17:59:08+02:00
log8c1feef4cd3af223ed8278bd55e8191936b526f0
tree1d4c7e0b459a47f1af6c6534822330684c8b31d6
parentf91503e5773ddf4ce3989533ed586ace81e41e90

macho: implement -headerpad_size option

Includes both traditiona and incremental codepaths with one caveat that in incremental case, the requested size cannot be smaller than the default padding size due to prealloc required due to incremental nature of linking. Also parse `-headerpad_max_install_names`, however, not actionable just yet - missing implementation.

5 files changed, 81 insertions(+), 7 deletions(-)

lib/std/build.zig+15
...@@ -1593,6 +1593,14 @@ pub const LibExeObjStep = struct {...@@ -1593,6 +1593,14 @@ pub const LibExeObjStep = struct {
1593 /// search strategy.1593 /// search strategy.
1594 search_strategy: ?enum { paths_first, dylibs_first } = null,1594 search_strategy: ?enum { paths_first, dylibs_first } = null,
15951595
1596 /// (Darwin) Set size of the padding between the end of load commands
1597 /// and start of `__TEXT,__text` section.
1598 headerpad_size: ?u64 = null,
1599
1600 /// (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.
1602 headerpad_max_install_names: bool = false,
1603
1596 /// Position Independent Code1604 /// Position Independent Code
1597 force_pic: ?bool = null,1605 force_pic: ?bool = null,
15981606
...@@ -2661,6 +2669,13 @@ pub const LibExeObjStep = struct {...@@ -2661,6 +2669,13 @@ pub const LibExeObjStep = struct {
2661 .paths_first => try zig_args.append("-search_paths_first"),2669 .paths_first => try zig_args.append("-search_paths_first"),
2662 .dylibs_first => try zig_args.append("-search_dylibs_first"),2670 .dylibs_first => try zig_args.append("-search_dylibs_first"),
2663 };2671 };
2672 if (self.headerpad_size) |headerpad_size| {
2673 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
2674 try zig_args.appendSlice(&[_][]const u8{ "-headerpad_size", size });
2675 }
2676 if (self.headerpad_max_install_names) {
2677 try zig_args.append("-headerpad_max_install_names");
2678 }
26642679
2665 if (self.bundle_compiler_rt) |x| {2680 if (self.bundle_compiler_rt) |x| {
2666 if (x) {2681 if (x) {
src/Compilation.zig+6
...@@ -907,6 +907,10 @@ pub const InitOptions = struct {...@@ -907,6 +907,10 @@ pub const InitOptions = struct {
907 pagezero_size: ?u64 = null,907 pagezero_size: ?u64 = null,
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 commands
911 headerpad_size: ?u64 = null,
912 /// (Darwin) set enough space as if all paths were MATPATHLEN
913 headerpad_max_install_names: bool = false,
910};914};
911915
912fn addPackageTableToCacheHash(916fn addPackageTableToCacheHash(
...@@ -1748,6 +1752,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1748,6 +1752,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1748 .entitlements = options.entitlements,1752 .entitlements = options.entitlements,
1749 .pagezero_size = options.pagezero_size,1753 .pagezero_size = options.pagezero_size,
1750 .search_strategy = options.search_strategy,1754 .search_strategy = options.search_strategy,
1755 .headerpad_size = options.headerpad_size,
1756 .headerpad_max_install_names = options.headerpad_max_install_names,
1751 });1757 });
1752 errdefer bin_file.destroy();1758 errdefer bin_file.destroy();
1753 comp.* = .{1759 comp.* = .{
src/link.zig+6
...@@ -193,6 +193,12 @@ pub const Options = struct {...@@ -193,6 +193,12 @@ pub const Options = struct {
193 /// (Darwin) search strategy for system libraries193 /// (Darwin) search strategy for system libraries
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 commands
197 headerpad_size: ?u64 = null,
198
199 /// (Darwin) set enough space as if all paths were MATPATHLEN
200 headerpad_max_install_names: bool = false,
201
196 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {202 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
197 return if (options.use_lld) .Obj else options.output_mode;203 return if (options.use_lld) .Obj else options.output_mode;
198 }204 }
src/link/MachO.zig+28-7
...@@ -69,10 +69,8 @@ page_size: u16,...@@ -69,10 +69,8 @@ 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/// We commit 0x1000 = 4096 bytes of space to the header and72/// Size of the padding between the end of load commands and start of the '__TEXT,__text' section.
73/// the table of load commands. This should be plenty for any73headerpad_size: u64,
74/// potential future extensions.
75header_pad: u16 = 0x1000,
7674
77/// The absolute address of the entry point.75/// The absolute address of the entry point.
78entry_addr: ?u64 = null,76entry_addr: ?u64 = null,
...@@ -295,6 +293,11 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size);...@@ -295,6 +293,11 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size);
295/// start of __TEXT segment.293/// start of __TEXT segment.
296const default_pagezero_vmsize: u64 = 0x100000000;294const default_pagezero_vmsize: u64 = 0x100000000;
297295
296/// We commit 0x1000 = 4096 bytes of space to the header and
297/// the table of load commands. This should be plenty for any
298/// potential future extensions.
299const default_headerpad_size: u64 = 0x1000;
300
298pub const Export = struct {301pub const Export = struct {
299 sym_index: ?u32 = null,302 sym_index: ?u32 = null,
300};303};
...@@ -400,6 +403,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -400,6 +403,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
400 const use_llvm = build_options.have_llvm and options.use_llvm;403 const use_llvm = build_options.have_llvm and options.use_llvm;
401 const use_stage1 = build_options.is_stage1 and options.use_stage1;404 const use_stage1 = build_options.is_stage1 and options.use_stage1;
402 const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole);405 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;
403412
404 const self = try gpa.create(MachO);413 const self = try gpa.create(MachO);
405 errdefer gpa.destroy(self);414 errdefer gpa.destroy(self);
...@@ -412,6 +421,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -412,6 +421,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
412 .file = null,421 .file = null,
413 },422 },
414 .page_size = page_size,423 .page_size = page_size,
424 .headerpad_size = headerpad_size,
415 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,425 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,
416 .needs_prealloc = needs_prealloc,426 .needs_prealloc = needs_prealloc,
417 };427 };
...@@ -976,6 +986,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -976,6 +986,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
976 .dylibs_first => try argv.append("-search_dylibs_first"),986 .dylibs_first => try argv.append("-search_dylibs_first"),
977 };987 };
978988
989 if (self.base.options.headerpad_size) |headerpad_size| {
990 try argv.append("-headerpad_size");
991 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
992 }
993
994 if (self.base.options.headerpad_max_install_names) {
995 try argv.append("-headerpad_max_install_names");
996 }
997
979 if (self.base.options.entry) |entry| {998 if (self.base.options.entry) |entry| {
980 try argv.append("-e");999 try argv.append("-e");
981 try argv.append(entry);1000 try argv.append(entry);
...@@ -4453,7 +4472,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4453,7 +4472,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4453 const needed_size = if (self.needs_prealloc) blk: {4472 const needed_size = if (self.needs_prealloc) blk: {
4454 const program_code_size_hint = self.base.options.program_code_size_hint;4473 const program_code_size_hint = self.base.options.program_code_size_hint;
4455 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;4474 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
4456 const ideal_size = self.header_pad + program_code_size_hint + got_size_hint;4475 const ideal_size = self.headerpad_size + program_code_size_hint + got_size_hint;
4457 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);4476 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4458 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });4477 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
4459 break :blk needed_size;4478 break :blk needed_size;
...@@ -4961,7 +4980,9 @@ fn allocateTextSegment(self: *MachO) !void {...@@ -4961,7 +4980,9 @@ fn allocateTextSegment(self: *MachO) !void {
4961 sizeofcmds += lc.cmdsize();4980 sizeofcmds += lc.cmdsize();
4962 }4981 }
49634982
4964 try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);4983 // TODO verify if `headerpad_max_install_names` leads to larger padding size
4984 const offset = @sizeOf(macho.mach_header_64) + sizeofcmds + self.headerpad_size;
4985 try self.allocateSegment(self.text_segment_cmd_index.?, offset);
49654986
4966 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.4987 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
4967 var min_alignment: u32 = 0;4988 var min_alignment: u32 = 0;
...@@ -5088,7 +5109,7 @@ fn initSection(...@@ -5088,7 +5109,7 @@ fn initSection(
50885109
5089 if (self.needs_prealloc) {5110 if (self.needs_prealloc) {
5090 const alignment_pow_2 = try math.powi(u32, 2, alignment);5111 const alignment_pow_2 = try math.powi(u32, 2, alignment);
5091 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null;5112 const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.headerpad_size else null;
5092 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);5113 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
5093 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{5114 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
5094 sect.segName(),5115 sect.segName(),
src/main.zig+26
...@@ -450,6 +450,8 @@ const usage_build_generic =...@@ -450,6 +450,8 @@ 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 notation
454 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
453 \\ --import-memory (WebAssembly) import memory from the environment455 \\ --import-memory (WebAssembly) import memory from the environment
454 \\ --import-table (WebAssembly) import function table from the host environment456 \\ --import-table (WebAssembly) import function table from the host environment
455 \\ --export-table (WebAssembly) export function table to the host environment457 \\ --export-table (WebAssembly) export function table to the host environment
...@@ -699,6 +701,8 @@ fn buildOutputType(...@@ -699,6 +701,8 @@ fn buildOutputType(
699 var entitlements: ?[]const u8 = null;701 var entitlements: ?[]const u8 = null;
700 var pagezero_size: ?u64 = null;702 var pagezero_size: ?u64 = null;
701 var search_strategy: ?link.File.MachO.SearchStrategy = null;703 var search_strategy: ?link.File.MachO.SearchStrategy = null;
704 var headerpad_size: ?u64 = null;
705 var headerpad_max_install_names: bool = false;
702706
703 // 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.
704 // This array is populated by zig cc frontend and then has to be converted to zig-style708 // This array is populated by zig cc frontend and then has to be converted to zig-style
...@@ -924,6 +928,15 @@ fn buildOutputType(...@@ -924,6 +928,15 @@ fn buildOutputType(
924 search_strategy = .paths_first;928 search_strategy = .paths_first;
925 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {929 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
926 search_strategy = .dylibs_first;930 search_strategy = .dylibs_first;
931 } else if (mem.eql(u8, arg, "-headerpad_size")) {
932 const next_arg = args_iter.next() orelse {
933 fatal("expected parameter after {s}", .{arg});
934 };
935 headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
936 fatal("unable to parser '{s}': {s}", .{ arg, @errorName(err) });
937 };
938 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
939 headerpad_max_install_names = true;
927 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {940 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
928 linker_script = args_iter.next() orelse {941 linker_script = args_iter.next() orelse {
929 fatal("expected parameter after {s}", .{arg});942 fatal("expected parameter after {s}", .{arg});
...@@ -1676,6 +1689,17 @@ fn buildOutputType(...@@ -1676,6 +1689,17 @@ fn buildOutputType(
1676 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| {
1677 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });1690 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1678 };1691 };
1692 } else if (mem.eql(u8, arg, "-headerpad_size")) {
1693 i += 1;
1694 if (i >= linker_args.items.len) {
1695 fatal("expected linker arg after '{s}'", .{arg});
1696 }
1697 const next_arg = linker_args.items[i];
1698 headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
1699 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1700 };
1701 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
1702 headerpad_max_install_names = true;
1679 } else if (mem.eql(u8, arg, "--gc-sections")) {1703 } else if (mem.eql(u8, arg, "--gc-sections")) {
1680 linker_gc_sections = true;1704 linker_gc_sections = true;
1681 } else if (mem.eql(u8, arg, "--no-gc-sections")) {1705 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
...@@ -2795,6 +2819,8 @@ fn buildOutputType(...@@ -2795,6 +2819,8 @@ fn buildOutputType(
2795 .entitlements = entitlements,2819 .entitlements = entitlements,
2796 .pagezero_size = pagezero_size,2820 .pagezero_size = pagezero_size,
2797 .search_strategy = search_strategy,2821 .search_strategy = search_strategy,
2822 .headerpad_size = headerpad_size,
2823 .headerpad_max_install_names = headerpad_max_install_names,
2798 }) catch |err| switch (err) {2824 }) catch |err| switch (err) {
2799 error.LibCUnavailable => {2825 error.LibCUnavailable => {
2800 const target = target_info.target;2826 const target = target_info.target;