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 {
15931593 /// search strategy.
15941594 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
15961604 /// Position Independent Code
15971605 force_pic: ?bool = null,
15981606
......@@ -2661,6 +2669,13 @@ pub const LibExeObjStep = struct {
26612669 .paths_first => try zig_args.append("-search_paths_first"),
26622670 .dylibs_first => try zig_args.append("-search_dylibs_first"),
26632671 };
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
26652680 if (self.bundle_compiler_rt) |x| {
26662681 if (x) {
src/Compilation.zig+6
......@@ -907,6 +907,10 @@ pub const InitOptions = struct {
907907 pagezero_size: ?u64 = null,
908908 /// (Darwin) search strategy for system libraries
909909 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,
910914};
911915
912916fn addPackageTableToCacheHash(
......@@ -1748,6 +1752,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17481752 .entitlements = options.entitlements,
17491753 .pagezero_size = options.pagezero_size,
17501754 .search_strategy = options.search_strategy,
1755 .headerpad_size = options.headerpad_size,
1756 .headerpad_max_install_names = options.headerpad_max_install_names,
17511757 });
17521758 errdefer bin_file.destroy();
17531759 comp.* = .{
src/link.zig+6
......@@ -193,6 +193,12 @@ pub const Options = struct {
193193 /// (Darwin) search strategy for system libraries
194194 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
196202 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
197203 return if (options.use_lld) .Obj else options.output_mode;
198204 }
src/link/MachO.zig+28-7
......@@ -69,10 +69,8 @@ page_size: u16,
6969/// and potentially stage2 release builds in the future.
7070needs_prealloc: bool = true,
7171
72/// We commit 0x1000 = 4096 bytes of space to the header and
73/// the table of load commands. This should be plenty for any
74/// potential future extensions.
75header_pad: u16 = 0x1000,
72/// Size of the padding between the end of load commands and start of the '__TEXT,__text' section.
73headerpad_size: u64,
7674
7775/// The absolute address of the entry point.
7876entry_addr: ?u64 = null,
......@@ -295,6 +293,11 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size);
295293/// start of __TEXT segment.
296294const 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
298301pub const Export = struct {
299302 sym_index: ?u32 = null,
300303};
......@@ -400,6 +403,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
400403 const use_llvm = build_options.have_llvm and options.use_llvm;
401404 const use_stage1 = build_options.is_stage1 and options.use_stage1;
402405 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
404413 const self = try gpa.create(MachO);
405414 errdefer gpa.destroy(self);
......@@ -412,6 +421,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
412421 .file = null,
413422 },
414423 .page_size = page_size,
424 .headerpad_size = headerpad_size,
415425 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,
416426 .needs_prealloc = needs_prealloc,
417427 };
......@@ -976,6 +986,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
976986 .dylibs_first => try argv.append("-search_dylibs_first"),
977987 };
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
979998 if (self.base.options.entry) |entry| {
980999 try argv.append("-e");
9811000 try argv.append(entry);
......@@ -4453,7 +4472,7 @@ fn populateMissingMetadata(self: *MachO) !void {
44534472 const needed_size = if (self.needs_prealloc) blk: {
44544473 const program_code_size_hint = self.base.options.program_code_size_hint;
44554474 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;
44574476 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
44584477 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
44594478 break :blk needed_size;
......@@ -4961,7 +4980,9 @@ fn allocateTextSegment(self: *MachO) !void {
49614980 sizeofcmds += lc.cmdsize();
49624981 }
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
49664987 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
49674988 var min_alignment: u32 = 0;
......@@ -5088,7 +5109,7 @@ fn initSection(
50885109
50895110 if (self.needs_prealloc) {
50905111 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;
50925113 const off = self.findFreeSpace(segment_id, alignment_pow_2, padding);
50935114 log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{
50945115 sect.segName(),
src/main.zig+26
......@@ -450,6 +450,8 @@ const usage_build_generic =
450450 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
451451 \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
452452 \\ -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
453455 \\ --import-memory (WebAssembly) import memory from the environment
454456 \\ --import-table (WebAssembly) import function table from the host environment
455457 \\ --export-table (WebAssembly) export function table to the host environment
......@@ -699,6 +701,8 @@ fn buildOutputType(
699701 var entitlements: ?[]const u8 = null;
700702 var pagezero_size: ?u64 = null;
701703 var search_strategy: ?link.File.MachO.SearchStrategy = null;
704 var headerpad_size: ?u64 = null;
705 var headerpad_max_install_names: bool = false;
702706
703707 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
704708 // This array is populated by zig cc frontend and then has to be converted to zig-style
......@@ -924,6 +928,15 @@ fn buildOutputType(
924928 search_strategy = .paths_first;
925929 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
926930 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;
927940 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
928941 linker_script = args_iter.next() orelse {
929942 fatal("expected parameter after {s}", .{arg});
......@@ -1676,6 +1689,17 @@ fn buildOutputType(
16761689 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
16771690 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
16781691 };
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;
16791703 } else if (mem.eql(u8, arg, "--gc-sections")) {
16801704 linker_gc_sections = true;
16811705 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
......@@ -2795,6 +2819,8 @@ fn buildOutputType(
27952819 .entitlements = entitlements,
27962820 .pagezero_size = pagezero_size,
27972821 .search_strategy = search_strategy,
2822 .headerpad_size = headerpad_size,
2823 .headerpad_max_install_names = headerpad_max_install_names,
27982824 }) catch |err| switch (err) {
27992825 error.LibCUnavailable => {
28002826 const target = target_info.target;