| author | |
| committer | |
| log | 76b28ed45238fff843e04e94657e2790ee95954b |
| tree | c8e26db7d4f8d83facb6b4a91514bbbff496ed78 |
| parent | 0078d36ff39f2ef35e32f2d58e5d447b62f3a37e |
| parent | 589bf67635a9fe9c3e6df4b63c09e0cc4954d29a |
| signature |
macho: handle `-headerpad` and `-headerpad_max_install_names`13 files changed, 375 insertions(+), 99 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, |
| 1595 | 1595 | ||
| 1596 | /// (Darwin) Set size of the padding between the end of load commands | ||
| 1597 | /// and start of `__TEXT,__text` section. | ||
| 1598 | headerpad_size: ?u32 = 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 Code | 1604 | /// Position Independent Code |
| 1597 | force_pic: ?bool = null, | 1605 | force_pic: ?bool = null, |
| 1598 | 1606 | ||
| ... | @@ -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 }); | ||
| 2675 | } | ||
| 2676 | if (self.headerpad_max_install_names) { | ||
| 2677 | try zig_args.append("-headerpad_max_install_names"); | ||
| 2678 | } | ||
| 2664 | 2679 | ||
| 2665 | if (self.bundle_compiler_rt) |x| { | 2680 | if (self.bundle_compiler_rt) |x| { |
| 2666 | if (x) { | 2681 | if (x) { |
lib/std/build/CheckObjectStep.zig+137-82| ... | @@ -3,6 +3,7 @@ const assert = std.debug.assert; | ... | @@ -3,6 +3,7 @@ const assert = std.debug.assert; |
| 3 | const build = std.build; | 3 | const build = std.build; |
| 4 | const fs = std.fs; | 4 | const fs = std.fs; |
| 5 | const macho = std.macho; | 5 | const macho = std.macho; |
| 6 | const math = std.math; | ||
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| 7 | const testing = std.testing; | 8 | const testing = std.testing; |
| 8 | 9 | ||
| ... | @@ -36,20 +37,24 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe | ... | @@ -36,20 +37,24 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe |
| 36 | return self; | 37 | return self; |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | const Action = union(enum) { | 40 | /// There two types of actions currently suported: |
| 40 | match: MatchAction, | 41 | /// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}` |
| 41 | compute_eq: ComputeEqAction, | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` | ||
| 45 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature | 42 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature |
| 46 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use | 43 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use |
| 47 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. | 44 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. |
| 48 | /// it should be plenty useful in its current form. | 45 | /// it should be plenty useful in its current form. |
| 49 | const MatchAction = struct { | 46 | /// * `.compute_cmp` - can be used to perform an operation on the extracted global variables |
| 50 | needle: []const u8, | 47 | /// using the MatchAction. It currently only supports an addition. The operation is required |
| 48 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, | ||
| 49 | /// to avoid any parsing really). | ||
| 50 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively | ||
| 51 | /// they could then be added with this simple program `vmaddr entryoff +`. | ||
| 52 | const Action = struct { | ||
| 53 | tag: enum { match, compute_cmp }, | ||
| 54 | phrase: []const u8, | ||
| 55 | expected: ?ComputeCompareExpected = null, | ||
| 51 | 56 | ||
| 52 | /// Will return true if the `needle` was found in the `haystack`. | 57 | /// Will return true if the `phrase` was found in the `haystack`. |
| 53 | /// Some examples include: | 58 | /// Some examples include: |
| 54 | /// | 59 | /// |
| 55 | /// LC 0 => will match in its entirety | 60 | /// LC 0 => will match in its entirety |
| ... | @@ -57,9 +62,11 @@ const MatchAction = struct { | ... | @@ -57,9 +62,11 @@ const MatchAction = struct { |
| 57 | /// and save under `vmaddr` global name (see `global_vars` param) | 62 | /// and save under `vmaddr` global name (see `global_vars` param) |
| 58 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` | 63 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` |
| 59 | /// in that order with other letters in between | 64 | /// in that order with other letters in between |
| 60 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { | 65 | fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool { |
| 66 | assert(act.tag == .match); | ||
| 67 | |||
| 61 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); | 68 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); |
| 62 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); | 69 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " "); |
| 63 | 70 | ||
| 64 | while (needle_it.next()) |needle_tok| { | 71 | while (needle_it.next()) |needle_tok| { |
| 65 | const hay_tok = hay_it.next() orelse return false; | 72 | const hay_tok = hay_it.next() orelse return false; |
| ... | @@ -93,22 +100,80 @@ const MatchAction = struct { | ... | @@ -93,22 +100,80 @@ const MatchAction = struct { |
| 93 | 100 | ||
| 94 | return true; | 101 | return true; |
| 95 | } | 102 | } |
| 96 | }; | ||
| 97 | 103 | ||
| 98 | /// ComputeEqAction can be used to perform an operation on the extracted global variables | 104 | /// Will return true if the `phrase` is correctly parsed into an RPN program and |
| 99 | /// using the MatchAction. It currently only supports an addition. The operation is required | 105 | /// its reduced, computed value compares using `op` with the expected value, either |
| 100 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, | 106 | /// a literal or another extracted variable. |
| 101 | /// to avoid any parsing really). | 107 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { |
| 102 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively | 108 | var op_stack = std.ArrayList(enum { add }).init(gpa); |
| 103 | /// they could then be added with this simple program `vmaddr entryoff +`. | 109 | var values = std.ArrayList(u64).init(gpa); |
| 104 | const ComputeEqAction = struct { | 110 | |
| 105 | expected: []const u8, | 111 | var it = mem.tokenize(u8, act.phrase, " "); |
| 106 | var_stack: std.ArrayList([]const u8), | 112 | while (it.next()) |next| { |
| 107 | op_stack: std.ArrayList(Op), | 113 | if (mem.eql(u8, next, "+")) { |
| 114 | try op_stack.append(.add); | ||
| 115 | } else { | ||
| 116 | const val = global_vars.get(next) orelse { | ||
| 117 | std.debug.print( | ||
| 118 | \\ | ||
| 119 | \\========= Variable was not extracted: =========== | ||
| 120 | \\{s} | ||
| 121 | \\ | ||
| 122 | , .{next}); | ||
| 123 | return error.UnknownVariable; | ||
| 124 | }; | ||
| 125 | try values.append(val); | ||
| 126 | } | ||
| 127 | } | ||
| 108 | 128 | ||
| 109 | const Op = enum { | 129 | var op_i: usize = 1; |
| 110 | add, | 130 | var reduced: u64 = values.items[0]; |
| 111 | }; | 131 | for (op_stack.items) |op| { |
| 132 | const other = values.items[op_i]; | ||
| 133 | switch (op) { | ||
| 134 | .add => { | ||
| 135 | reduced += other; | ||
| 136 | }, | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | const exp_value = switch (act.expected.?.value) { | ||
| 141 | .variable => |name| global_vars.get(name) orelse { | ||
| 142 | std.debug.print( | ||
| 143 | \\ | ||
| 144 | \\========= Variable was not extracted: =========== | ||
| 145 | \\{s} | ||
| 146 | \\ | ||
| 147 | , .{name}); | ||
| 148 | return error.UnknownVariable; | ||
| 149 | }, | ||
| 150 | .literal => |x| x, | ||
| 151 | }; | ||
| 152 | return math.compare(reduced, act.expected.?.op, exp_value); | ||
| 153 | } | ||
| 154 | }; | ||
| 155 | |||
| 156 | const ComputeCompareExpected = struct { | ||
| 157 | op: math.CompareOperator, | ||
| 158 | value: union(enum) { | ||
| 159 | variable: []const u8, | ||
| 160 | literal: u64, | ||
| 161 | }, | ||
| 162 | |||
| 163 | pub fn format( | ||
| 164 | value: @This(), | ||
| 165 | comptime fmt: []const u8, | ||
| 166 | options: std.fmt.FormatOptions, | ||
| 167 | writer: anytype, | ||
| 168 | ) !void { | ||
| 169 | _ = fmt; | ||
| 170 | _ = options; | ||
| 171 | try writer.print("{s} ", .{@tagName(value.op)}); | ||
| 172 | switch (value.value) { | ||
| 173 | .variable => |name| try writer.writeAll(name), | ||
| 174 | .literal => |x| try writer.print("{x}", .{x}), | ||
| 175 | } | ||
| 176 | } | ||
| 112 | }; | 177 | }; |
| 113 | 178 | ||
| 114 | const Check = struct { | 179 | const Check = struct { |
| ... | @@ -122,15 +187,18 @@ const Check = struct { | ... | @@ -122,15 +187,18 @@ const Check = struct { |
| 122 | }; | 187 | }; |
| 123 | } | 188 | } |
| 124 | 189 | ||
| 125 | fn match(self: *Check, needle: []const u8) void { | 190 | fn match(self: *Check, phrase: []const u8) void { |
| 126 | self.actions.append(.{ | 191 | self.actions.append(.{ |
| 127 | .match = .{ .needle = self.builder.dupe(needle) }, | 192 | .tag = .match, |
| 193 | .phrase = self.builder.dupe(phrase), | ||
| 128 | }) catch unreachable; | 194 | }) catch unreachable; |
| 129 | } | 195 | } |
| 130 | 196 | ||
| 131 | fn computeEq(self: *Check, act: ComputeEqAction) void { | 197 | fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { |
| 132 | self.actions.append(.{ | 198 | self.actions.append(.{ |
| 133 | .compute_eq = act, | 199 | .tag = .compute_cmp, |
| 200 | .phrase = self.builder.dupe(phrase), | ||
| 201 | .expected = expected, | ||
| 134 | }) catch unreachable; | 202 | }) catch unreachable; |
| 135 | } | 203 | } |
| 136 | }; | 204 | }; |
| ... | @@ -165,25 +233,13 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { | ... | @@ -165,25 +233,13 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { |
| 165 | /// Creates a new standalone, singular check which allows running simple binary operations | 233 | /// Creates a new standalone, singular check which allows running simple binary operations |
| 166 | /// on the extracted variables. It will then compare the reduced program with the value of | 234 | /// on the extracted variables. It will then compare the reduced program with the value of |
| 167 | /// the expected variable. | 235 | /// the expected variable. |
| 168 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { | 236 | pub fn checkComputeCompare( |
| 169 | const gpa = self.builder.allocator; | 237 | self: *CheckObjectStep, |
| 170 | var ca = ComputeEqAction{ | 238 | program: []const u8, |
| 171 | .expected = expected, | 239 | expected: ComputeCompareExpected, |
| 172 | .var_stack = std.ArrayList([]const u8).init(gpa), | 240 | ) void { |
| 173 | .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), | ||
| 174 | }; | ||
| 175 | |||
| 176 | var it = mem.tokenize(u8, program, " "); | ||
| 177 | while (it.next()) |next| { | ||
| 178 | if (mem.eql(u8, next, "+")) { | ||
| 179 | ca.op_stack.append(.add) catch unreachable; | ||
| 180 | } else { | ||
| 181 | ca.var_stack.append(self.builder.dupe(next)) catch unreachable; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | var new_check = Check.create(self.builder); | 241 | var new_check = Check.create(self.builder); |
| 186 | new_check.computeEq(ca); | 242 | new_check.computeCmp(program, expected); |
| 187 | self.checks.append(new_check) catch unreachable; | 243 | self.checks.append(new_check) catch unreachable; |
| 188 | } | 244 | } |
| 189 | 245 | ||
| ... | @@ -210,10 +266,10 @@ fn make(step: *Step) !void { | ... | @@ -210,10 +266,10 @@ fn make(step: *Step) !void { |
| 210 | for (self.checks.items) |chk| { | 266 | for (self.checks.items) |chk| { |
| 211 | var it = mem.tokenize(u8, output, "\r\n"); | 267 | var it = mem.tokenize(u8, output, "\r\n"); |
| 212 | for (chk.actions.items) |act| { | 268 | for (chk.actions.items) |act| { |
| 213 | switch (act) { | 269 | switch (act.tag) { |
| 214 | .match => |match_act| { | 270 | .match => { |
| 215 | while (it.next()) |line| { | 271 | while (it.next()) |line| { |
| 216 | if (try match_act.match(line, &vars)) break; | 272 | if (try act.match(line, &vars)) break; |
| 217 | } else { | 273 | } else { |
| 218 | std.debug.print( | 274 | std.debug.print( |
| 219 | \\ | 275 | \\ |
| ... | @@ -222,51 +278,33 @@ fn make(step: *Step) !void { | ... | @@ -222,51 +278,33 @@ fn make(step: *Step) !void { |
| 222 | \\========= But parsed file does not contain it: ======= | 278 | \\========= But parsed file does not contain it: ======= |
| 223 | \\{s} | 279 | \\{s} |
| 224 | \\ | 280 | \\ |
| 225 | , .{ match_act.needle, output }); | 281 | , .{ act.phrase, output }); |
| 226 | return error.TestFailed; | 282 | return error.TestFailed; |
| 227 | } | 283 | } |
| 228 | }, | 284 | }, |
| 229 | .compute_eq => |c_eq| { | 285 | .compute_cmp => { |
| 230 | var values = std.ArrayList(u64).init(gpa); | 286 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { |
| 231 | try values.ensureTotalCapacity(c_eq.var_stack.items.len); | 287 | error.UnknownVariable => { |
| 232 | for (c_eq.var_stack.items) |vv| { | ||
| 233 | const val = vars.get(vv) orelse { | ||
| 234 | std.debug.print( | 288 | std.debug.print( |
| 235 | \\ | ||
| 236 | \\========= Variable was not extracted: =========== | ||
| 237 | \\{s} | ||
| 238 | \\========= From parsed file: ===================== | 289 | \\========= From parsed file: ===================== |
| 239 | \\{s} | 290 | \\{s} |
| 240 | \\ | 291 | \\ |
| 241 | , .{ vv, output }); | 292 | , .{output}); |
| 242 | return error.TestFailed; | 293 | return error.TestFailed; |
| 243 | }; | 294 | }, |
| 244 | values.appendAssumeCapacity(val); | 295 | else => |e| return e, |
| 245 | } | 296 | }; |
| 246 | 297 | if (!res) { | |
| 247 | var op_i: usize = 1; | ||
| 248 | var reduced: u64 = values.items[0]; | ||
| 249 | for (c_eq.op_stack.items) |op| { | ||
| 250 | const other = values.items[op_i]; | ||
| 251 | switch (op) { | ||
| 252 | .add => { | ||
| 253 | reduced += other; | ||
| 254 | }, | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | const expected = vars.get(c_eq.expected) orelse { | ||
| 259 | std.debug.print( | 298 | std.debug.print( |
| 260 | \\ | 299 | \\ |
| 261 | \\========= Variable was not extracted: =========== | 300 | \\========= Comparison failed for action: =========== |
| 262 | \\{s} | 301 | \\{s} {s} |
| 263 | \\========= From parsed file: ===================== | 302 | \\========= From parsed file: ======================= |
| 264 | \\{s} | 303 | \\{s} |
| 265 | \\ | 304 | \\ |
| 266 | , .{ c_eq.expected, output }); | 305 | , .{ act.phrase, act.expected.?, output }); |
| 267 | return error.TestFailed; | 306 | return error.TestFailed; |
| 268 | }; | 307 | } |
| 269 | try testing.expectEqual(reduced, expected); | ||
| 270 | }, | 308 | }, |
| 271 | } | 309 | } |
| 272 | } | 310 | } |
| ... | @@ -349,6 +387,23 @@ const MachODumper = struct { | ... | @@ -349,6 +387,23 @@ const MachODumper = struct { |
| 349 | seg.fileoff, | 387 | seg.fileoff, |
| 350 | seg.filesize, | 388 | seg.filesize, |
| 351 | }); | 389 | }); |
| 390 | |||
| 391 | for (lc.segment.sections.items) |sect| { | ||
| 392 | try writer.writeByte('\n'); | ||
| 393 | try writer.print( | ||
| 394 | \\sectname {s} | ||
| 395 | \\addr {x} | ||
| 396 | \\size {x} | ||
| 397 | \\offset {x} | ||
| 398 | \\align {x} | ||
| 399 | , .{ | ||
| 400 | sect.sectName(), | ||
| 401 | sect.addr, | ||
| 402 | sect.size, | ||
| 403 | sect.offset, | ||
| 404 | sect.@"align", | ||
| 405 | }); | ||
| 406 | } | ||
| 352 | }, | 407 | }, |
| 353 | 408 | ||
| 354 | .ID_DYLIB, | 409 | .ID_DYLIB, |
src/Compilation.zig+10-2| ... | @@ -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 libraries | 908 | /// (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: ?u32 = null, | ||
| 912 | /// (Darwin) set enough space as if all paths were MATPATHLEN | ||
| 913 | headerpad_max_install_names: bool = false, | ||
| 910 | }; | 914 | }; |
| 911 | 915 | ||
| 912 | fn addPackageTableToCacheHash( | 916 | fn 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.* = .{ |
| ... | @@ -2363,7 +2369,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo | ... | @@ -2363,7 +2369,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo |
| 2363 | /// to remind the programmer to update multiple related pieces of code that | 2369 | /// to remind the programmer to update multiple related pieces of code that |
| 2364 | /// are in different locations. Bump this number when adding or deleting | 2370 | /// are in different locations. Bump this number when adding or deleting |
| 2365 | /// anything from the link cache manifest. | 2371 | /// anything from the link cache manifest. |
| 2366 | pub const link_hash_implementation_version = 5; | 2372 | pub const link_hash_implementation_version = 6; |
| 2367 | 2373 | ||
| 2368 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { | 2374 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { |
| 2369 | const gpa = comp.gpa; | 2375 | const gpa = comp.gpa; |
| ... | @@ -2373,7 +2379,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes | ... | @@ -2373,7 +2379,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2373 | defer arena_allocator.deinit(); | 2379 | defer arena_allocator.deinit(); |
| 2374 | const arena = arena_allocator.allocator(); | 2380 | const arena = arena_allocator.allocator(); |
| 2375 | 2381 | ||
| 2376 | comptime assert(link_hash_implementation_version == 5); | 2382 | comptime assert(link_hash_implementation_version == 6); |
| 2377 | 2383 | ||
| 2378 | if (comp.bin_file.options.module) |mod| { | 2384 | if (comp.bin_file.options.module) |mod| { |
| 2379 | 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{ |
| ... | @@ -2480,6 +2486,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes | ... | @@ -2480,6 +2486,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2480 | try man.addOptionalFile(comp.bin_file.options.entitlements); | 2486 | try man.addOptionalFile(comp.bin_file.options.entitlements); |
| 2481 | man.hash.addOptional(comp.bin_file.options.pagezero_size); | 2487 | man.hash.addOptional(comp.bin_file.options.pagezero_size); |
| 2482 | 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); | ||
| 2483 | 2491 | ||
| 2484 | // COFF specific stuff | 2492 | // COFF specific stuff |
| 2485 | man.hash.addOptional(comp.bin_file.options.subsystem); | 2493 | man.hash.addOptional(comp.bin_file.options.subsystem); |
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 libraries | 193 | /// (Darwin) search strategy for system libraries |
| 194 | search_strategy: ?File.MachO.SearchStrategy = null, | 194 | search_strategy: ?File.MachO.SearchStrategy = null, |
| 195 | 195 | ||
| 196 | /// (Darwin) set minimum space for future expansion of the load commands | ||
| 197 | headerpad_size: ?u32 = 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/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(); |
| 971 | 971 | ||
| 972 | comptime assert(Compilation.link_hash_implementation_version == 5); | 972 | comptime assert(Compilation.link_hash_implementation_version == 6); |
| 973 | 973 | ||
| 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(); |
| 1300 | 1300 | ||
| 1301 | comptime assert(Compilation.link_hash_implementation_version == 5); | 1301 | comptime assert(Compilation.link_hash_implementation_version == 6); |
| 1302 | 1302 | ||
| 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+49-11| ... | @@ -69,11 +69,6 @@ page_size: u16, | ... | @@ -69,11 +69,6 @@ page_size: u16, |
| 69 | /// and potentially stage2 release builds in the future. | 69 | /// and potentially stage2 release builds in the future. |
| 70 | needs_prealloc: bool = true, | 70 | needs_prealloc: bool = true, |
| 71 | 71 | ||
| 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. | ||
| 75 | header_pad: u16 = 0x1000, | ||
| 76 | |||
| 77 | /// The absolute address of the entry point. | 72 | /// The absolute address of the entry point. |
| 78 | entry_addr: ?u64 = null, | 73 | entry_addr: ?u64 = null, |
| 79 | 74 | ||
| ... | @@ -295,6 +290,11 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); | ... | @@ -295,6 +290,11 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); |
| 295 | /// start of __TEXT segment. | 290 | /// start of __TEXT segment. |
| 296 | const default_pagezero_vmsize: u64 = 0x100000000; | 291 | const default_pagezero_vmsize: u64 = 0x100000000; |
| 297 | 292 | ||
| 293 | /// We commit 0x1000 = 4096 bytes of space to the header and | ||
| 294 | /// the table of load commands. This should be plenty for any | ||
| 295 | /// potential future extensions. | ||
| 296 | const default_headerpad_size: u32 = 0x1000; | ||
| 297 | |||
| 298 | pub const Export = struct { | 298 | pub const Export = struct { |
| 299 | sym_index: ?u32 = null, | 299 | sym_index: ?u32 = null, |
| 300 | }; | 300 | }; |
| ... | @@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 541 | // 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. |
| 542 | self.base.releaseLock(); | 542 | self.base.releaseLock(); |
| 543 | 543 | ||
| 544 | comptime assert(Compilation.link_hash_implementation_version == 5); | 544 | comptime assert(Compilation.link_hash_implementation_version == 6); |
| 545 | 545 | ||
| 546 | for (self.base.options.objects) |obj| { | 546 | for (self.base.options.objects) |obj| { |
| 547 | _ = try man.addFile(obj.path, null); | 547 | _ = try man.addFile(obj.path, null); |
| ... | @@ -556,6 +556,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -556,6 +556,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 556 | man.hash.add(stack_size); | 556 | man.hash.add(stack_size); |
| 557 | man.hash.addOptional(self.base.options.pagezero_size); | 557 | man.hash.addOptional(self.base.options.pagezero_size); |
| 558 | 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); | ||
| 559 | man.hash.addListOfBytes(self.base.options.lib_dirs); | 561 | man.hash.addListOfBytes(self.base.options.lib_dirs); |
| 560 | man.hash.addListOfBytes(self.base.options.framework_dirs); | 562 | man.hash.addListOfBytes(self.base.options.framework_dirs); |
| 561 | man.hash.addListOfBytes(self.base.options.frameworks); | 563 | man.hash.addListOfBytes(self.base.options.frameworks); |
| ... | @@ -976,6 +978,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -976,6 +978,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 976 | .dylibs_first => try argv.append("-search_dylibs_first"), | 978 | .dylibs_first => try argv.append("-search_dylibs_first"), |
| 977 | }; | 979 | }; |
| 978 | 980 | ||
| 981 | if (self.base.options.headerpad_size) |headerpad_size| { | ||
| 982 | try argv.append("-headerpad_size"); | ||
| 983 | try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size})); | ||
| 984 | } | ||
| 985 | |||
| 986 | if (self.base.options.headerpad_max_install_names) { | ||
| 987 | try argv.append("-headerpad_max_install_names"); | ||
| 988 | } | ||
| 989 | |||
| 979 | if (self.base.options.entry) |entry| { | 990 | if (self.base.options.entry) |entry| { |
| 980 | try argv.append("-e"); | 991 | try argv.append("-e"); |
| 981 | try argv.append(entry); | 992 | try argv.append(entry); |
| ... | @@ -4451,9 +4462,10 @@ fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -4451,9 +4462,10 @@ fn populateMissingMetadata(self: *MachO) !void { |
| 4451 | if (self.text_segment_cmd_index == null) { | 4462 | if (self.text_segment_cmd_index == null) { |
| 4452 | 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); |
| 4453 | 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); | ||
| 4454 | 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; |
| 4455 | 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; |
| 4456 | const ideal_size = self.header_pad + program_code_size_hint + got_size_hint; | 4468 | const ideal_size = headerpad_size + program_code_size_hint + got_size_hint; |
| 4457 | 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); |
| 4458 | 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 }); |
| 4459 | break :blk needed_size; | 4471 | break :blk needed_size; |
| ... | @@ -4956,12 +4968,35 @@ fn allocateTextSegment(self: *MachO) !void { | ... | @@ -4956,12 +4968,35 @@ fn allocateTextSegment(self: *MachO) !void { |
| 4956 | seg.inner.fileoff = 0; | 4968 | seg.inner.fileoff = 0; |
| 4957 | seg.inner.vmaddr = base_vmaddr; | 4969 | seg.inner.vmaddr = base_vmaddr; |
| 4958 | 4970 | ||
| 4959 | var sizeofcmds: u64 = 0; | 4971 | var sizeofcmds: u32 = 0; |
| 4960 | for (self.load_commands.items) |lc| { | 4972 | for (self.load_commands.items) |lc| { |
| 4961 | sizeofcmds += lc.cmdsize(); | 4973 | sizeofcmds += lc.cmdsize(); |
| 4962 | } | 4974 | } |
| 4963 | 4975 | ||
| 4964 | try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); | 4976 | var padding: u32 = sizeofcmds + (self.base.options.headerpad_size orelse 0); |
| 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}); | ||
| 4999 | try self.allocateSegment(self.text_segment_cmd_index.?, offset); | ||
| 4965 | 5000 | ||
| 4966 | // 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. |
| 4967 | var min_alignment: u32 = 0; | 5002 | var min_alignment: u32 = 0; |
| ... | @@ -5088,7 +5123,10 @@ fn initSection( | ... | @@ -5088,7 +5123,10 @@ fn initSection( |
| 5088 | 5123 | ||
| 5089 | if (self.needs_prealloc) { | 5124 | if (self.needs_prealloc) { |
| 5090 | const alignment_pow_2 = try math.powi(u32, 2, alignment); | 5125 | 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; | 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; | ||
| 5092 | const off = self.findFreeSpace(segment_id, alignment_pow_2, padding); | 5130 | const off = self.findFreeSpace(segment_id, alignment_pow_2, padding); |
| 5093 | 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}", .{ |
| 5094 | sect.segName(), | 5132 | sect.segName(), |
| ... | @@ -5127,7 +5165,7 @@ fn initSection( | ... | @@ -5127,7 +5165,7 @@ fn initSection( |
| 5127 | return index; | 5165 | return index; |
| 5128 | } | 5166 | } |
| 5129 | 5167 | ||
| 5130 | fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 { | 5168 | fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 { |
| 5131 | const seg = self.load_commands.items[segment_id].segment; | 5169 | const seg = self.load_commands.items[segment_id].segment; |
| 5132 | if (seg.sections.items.len == 0) { | 5170 | if (seg.sections.items.len == 0) { |
| 5133 | 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(); |
| 2483 | 2483 | ||
| 2484 | comptime assert(Compilation.link_hash_implementation_version == 5); | 2484 | comptime assert(Compilation.link_hash_implementation_version == 6); |
| 2485 | 2485 | ||
| 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+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 notation | 450 | \\ -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 [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 environment | 455 | \\ --import-memory (WebAssembly) import memory from the environment |
| 454 | \\ --import-table (WebAssembly) import function table from the host environment | 456 | \\ --import-table (WebAssembly) import function table from the host environment |
| 455 | \\ --export-table (WebAssembly) export function table to the host environment | 457 | \\ --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: ?u32 = null; | ||
| 705 | var headerpad_max_install_names: bool = false; | ||
| 702 | 706 | ||
| 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-style | 708 | // 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")) { | ||
| 932 | const next_arg = args_iter.next() orelse { | ||
| 933 | fatal("expected parameter after {s}", .{arg}); | ||
| 934 | }; | ||
| 935 | headerpad_size = std.fmt.parseUnsigned(u32, 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")) { | ||
| 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(u32, 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; |
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/entry/build.zig+1-1| ... | @@ -24,7 +24,7 @@ pub fn build(b: *Builder) void { | ... | @@ -24,7 +24,7 @@ pub fn build(b: *Builder) void { |
| 24 | check_exe.checkInSymtab(); | 24 | check_exe.checkInSymtab(); |
| 25 | check_exe.checkNext("_non_main {n_value}"); | 25 | check_exe.checkNext("_non_main {n_value}"); |
| 26 | 26 | ||
| 27 | check_exe.checkComputeEq("vmaddr entryoff +", "n_value"); | 27 | check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } }); |
| 28 | 28 | ||
| 29 | test_step.dependOn(&check_exe.step); | 29 | test_step.dependOn(&check_exe.step); |
| 30 | 30 |
test/link/macho/headerpad/build.zig created+120| ... | @@ -0,0 +1,120 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Builder = std.build.Builder; | ||
| 4 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 5 | |||
| 6 | pub 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 | |||
| 97 | fn 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 @@ | ||
| 1 | int main(int argc, char* argv[]) { | ||
| 2 | return 0; | ||
| 3 | } | ||