Skip to content

Commit 76b28ed

Browse files
authored
Merge pull request #11929 from ziglang/headerpad_max_install_names
macho: handle `-headerpad` and `-headerpad_max_install_names`
2 parents 0078d36 + 589bf67 commit 76b28ed

File tree

13 files changed

+375
-99
lines changed

13 files changed

+375
-99
lines changed

lib/std/build.zig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: ?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+
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 });
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) {

lib/std/build/CheckObjectStep.zig

Lines changed: 137 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const assert = std.debug.assert;
33
const build = std.build;
44
const fs = std.fs;
55
const macho = std.macho;
6+
const math = std.math;
67
const mem = std.mem;
78
const testing = std.testing;
89

@@ -36,30 +37,36 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe
3637
return self;
3738
}
3839

39-
const Action = union(enum) {
40-
match: MatchAction,
41-
compute_eq: ComputeEqAction,
42-
};
43-
44-
/// MatchAction is the main building block of standard matchers with optional eat-all token `{*}`
40+
/// There two types of actions currently suported:
41+
/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
4542
/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
4643
/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
4744
/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
4845
/// it should be plenty useful in its current form.
49-
const MatchAction = struct {
50-
needle: []const u8,
46+
/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
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,
5156

52-
/// Will return true if the `needle` was found in the `haystack`.
57+
/// Will return true if the `phrase` was found in the `haystack`.
5358
/// Some examples include:
5459
///
5560
/// LC 0 => will match in its entirety
5661
/// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
5762
/// and save under `vmaddr` global name (see `global_vars` param)
5863
/// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
5964
/// 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+
6168
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, " "), " ");
6370

6471
while (needle_it.next()) |needle_tok| {
6572
const hay_tok = hay_it.next() orelse return false;
@@ -93,22 +100,80 @@ const MatchAction = struct {
93100

94101
return true;
95102
}
96-
};
97103

98-
/// ComputeEqAction can be used to perform an operation on the extracted global variables
99-
/// using the MatchAction. It currently only supports an addition. The operation is required
100-
/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
101-
/// to avoid any parsing really).
102-
/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
103-
/// they could then be added with this simple program `vmaddr entryoff +`.
104-
const ComputeEqAction = struct {
105-
expected: []const u8,
106-
var_stack: std.ArrayList([]const u8),
107-
op_stack: std.ArrayList(Op),
104+
/// Will return true if the `phrase` is correctly parsed into an RPN program and
105+
/// its reduced, computed value compares using `op` with the expected value, either
106+
/// a literal or another extracted variable.
107+
fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
108+
var op_stack = std.ArrayList(enum { add }).init(gpa);
109+
var values = std.ArrayList(u64).init(gpa);
110+
111+
var it = mem.tokenize(u8, act.phrase, " ");
112+
while (it.next()) |next| {
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+
}
108128

109-
const Op = enum {
110-
add,
111-
};
129+
var op_i: usize = 1;
130+
var reduced: u64 = values.items[0];
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+
}
112177
};
113178

114179
const Check = struct {
@@ -122,15 +187,18 @@ const Check = struct {
122187
};
123188
}
124189

125-
fn match(self: *Check, needle: []const u8) void {
190+
fn match(self: *Check, phrase: []const u8) void {
126191
self.actions.append(.{
127-
.match = .{ .needle = self.builder.dupe(needle) },
192+
.tag = .match,
193+
.phrase = self.builder.dupe(phrase),
128194
}) catch unreachable;
129195
}
130196

131-
fn computeEq(self: *Check, act: ComputeEqAction) void {
197+
fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
132198
self.actions.append(.{
133-
.compute_eq = act,
199+
.tag = .compute_cmp,
200+
.phrase = self.builder.dupe(phrase),
201+
.expected = expected,
134202
}) catch unreachable;
135203
}
136204
};
@@ -165,25 +233,13 @@ pub fn checkInSymtab(self: *CheckObjectStep) void {
165233
/// Creates a new standalone, singular check which allows running simple binary operations
166234
/// on the extracted variables. It will then compare the reduced program with the value of
167235
/// the expected variable.
168-
pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void {
169-
const gpa = self.builder.allocator;
170-
var ca = ComputeEqAction{
171-
.expected = expected,
172-
.var_stack = std.ArrayList([]const u8).init(gpa),
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-
236+
pub fn checkComputeCompare(
237+
self: *CheckObjectStep,
238+
program: []const u8,
239+
expected: ComputeCompareExpected,
240+
) void {
185241
var new_check = Check.create(self.builder);
186-
new_check.computeEq(ca);
242+
new_check.computeCmp(program, expected);
187243
self.checks.append(new_check) catch unreachable;
188244
}
189245

@@ -210,10 +266,10 @@ fn make(step: *Step) !void {
210266
for (self.checks.items) |chk| {
211267
var it = mem.tokenize(u8, output, "\r\n");
212268
for (chk.actions.items) |act| {
213-
switch (act) {
214-
.match => |match_act| {
269+
switch (act.tag) {
270+
.match => {
215271
while (it.next()) |line| {
216-
if (try match_act.match(line, &vars)) break;
272+
if (try act.match(line, &vars)) break;
217273
} else {
218274
std.debug.print(
219275
\\
@@ -222,51 +278,33 @@ fn make(step: *Step) !void {
222278
\\========= But parsed file does not contain it: =======
223279
\\{s}
224280
\\
225-
, .{ match_act.needle, output });
281+
, .{ act.phrase, output });
226282
return error.TestFailed;
227283
}
228284
},
229-
.compute_eq => |c_eq| {
230-
var values = std.ArrayList(u64).init(gpa);
231-
try values.ensureTotalCapacity(c_eq.var_stack.items.len);
232-
for (c_eq.var_stack.items) |vv| {
233-
const val = vars.get(vv) orelse {
285+
.compute_cmp => {
286+
const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
287+
error.UnknownVariable => {
234288
std.debug.print(
235-
\\
236-
\\========= Variable was not extracted: ===========
237-
\\{s}
238289
\\========= From parsed file: =====================
239290
\\{s}
240291
\\
241-
, .{ vv, output });
292+
, .{output});
242293
return error.TestFailed;
243-
};
244-
values.appendAssumeCapacity(val);
245-
}
246-
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 {
294+
},
295+
else => |e| return e,
296+
};
297+
if (!res) {
259298
std.debug.print(
260299
\\
261-
\\========= Variable was not extracted: ===========
262-
\\{s}
263-
\\========= From parsed file: =====================
300+
\\========= Comparison failed for action: ===========
301+
\\{s} {s}
302+
\\========= From parsed file: =======================
264303
\\{s}
265304
\\
266-
, .{ c_eq.expected, output });
305+
, .{ act.phrase, act.expected.?, output });
267306
return error.TestFailed;
268-
};
269-
try testing.expectEqual(reduced, expected);
307+
}
270308
},
271309
}
272310
}
@@ -349,6 +387,23 @@ const MachODumper = struct {
349387
seg.fileoff,
350388
seg.filesize,
351389
});
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+
}
352407
},
353408

354409
.ID_DYLIB,

src/Compilation.zig

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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: ?u32 = null,
912+
/// (Darwin) set enough space as if all paths were MATPATHLEN
913+
headerpad_max_install_names: bool = false,
910914
};
911915

912916
fn 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.* = .{
@@ -2363,7 +2369,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
23632369
/// to remind the programmer to update multiple related pieces of code that
23642370
/// are in different locations. Bump this number when adding or deleting
23652371
/// anything from the link cache manifest.
2366-
pub const link_hash_implementation_version = 5;
2372+
pub const link_hash_implementation_version = 6;
23672373

23682374
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
23692375
const gpa = comp.gpa;
@@ -2373,7 +2379,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23732379
defer arena_allocator.deinit();
23742380
const arena = arena_allocator.allocator();
23752381

2376-
comptime assert(link_hash_implementation_version == 5);
2382+
comptime assert(link_hash_implementation_version == 6);
23772383

23782384
if (comp.bin_file.options.module) |mod| {
23792385
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
24802486
try man.addOptionalFile(comp.bin_file.options.entitlements);
24812487
man.hash.addOptional(comp.bin_file.options.pagezero_size);
24822488
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);
24832491

24842492
// COFF specific stuff
24852493
man.hash.addOptional(comp.bin_file.options.subsystem);

src/link.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: ?u32 = 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
}

0 commit comments

Comments
 (0)