authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-23 19:07:36+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-23 19:07:36+01:00
logbb62d5105ca02a8b1c959ccf79b9f2861505a150
treed033a5e45e7d79982e7075d8ade9cecf50c8ad06
parentf211c1559ab8c25a52d45e495ef42d4937e98e97
parent22b39f034b495176784f7ca295cf61c285958abe
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14049 from ziglang/issue-14045

macho+zld: write code signature padding before committing load commands

6 files changed, 262 insertions(+), 50 deletions(-)

lib/std/build/CheckObjectStep.zig+102-9
......@@ -126,22 +126,28 @@ const Action = struct {
126126 /// its reduced, computed value compares using `op` with the expected value, either
127127 /// a literal or another extracted variable.
128128 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
129 var op_stack = std.ArrayList(enum { add }).init(gpa);
129 var op_stack = std.ArrayList(enum { add, sub, mod }).init(gpa);
130130 var values = std.ArrayList(u64).init(gpa);
131131
132132 var it = mem.tokenize(u8, act.phrase, " ");
133133 while (it.next()) |next| {
134134 if (mem.eql(u8, next, "+")) {
135135 try op_stack.append(.add);
136 } else if (mem.eql(u8, next, "-")) {
137 try op_stack.append(.sub);
138 } else if (mem.eql(u8, next, "%")) {
139 try op_stack.append(.mod);
136140 } else {
137 const val = global_vars.get(next) orelse {
138 std.debug.print(
139 \\
140 \\========= Variable was not extracted: ===========
141 \\{s}
142 \\
143 , .{next});
144 return error.UnknownVariable;
141 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142 break :blk global_vars.get(next) orelse {
143 std.debug.print(
144 \\
145 \\========= Variable was not extracted: ===========
146 \\{s}
147 \\
148 , .{next});
149 return error.UnknownVariable;
150 };
145151 };
146152 try values.append(val);
147153 }
......@@ -155,7 +161,14 @@ const Action = struct {
155161 .add => {
156162 reduced += other;
157163 },
164 .sub => {
165 reduced -= other;
166 },
167 .mod => {
168 reduced %= other;
169 },
158170 }
171 op_i += 1;
159172 }
160173
161174 const exp_value = switch (act.expected.?.value) {
......@@ -577,6 +590,86 @@ const MachODumper = struct {
577590 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
578591 },
579592
593 .DATA_IN_CODE,
594 .FUNCTION_STARTS,
595 .CODE_SIGNATURE,
596 => {
597 const llc = lc.cast(macho.linkedit_data_command).?;
598 try writer.writeByte('\n');
599 try writer.print(
600 \\dataoff {x}
601 \\datasize {x}
602 , .{ llc.dataoff, llc.datasize });
603 },
604
605 .DYLD_INFO_ONLY => {
606 const dlc = lc.cast(macho.dyld_info_command).?;
607 try writer.writeByte('\n');
608 try writer.print(
609 \\rebaseoff {x}
610 \\rebasesize {x}
611 \\bindoff {x}
612 \\bindsize {x}
613 \\weakbindoff {x}
614 \\weakbindsize {x}
615 \\lazybindoff {x}
616 \\lazybindsize {x}
617 \\exportoff {x}
618 \\exportsize {x}
619 , .{
620 dlc.rebase_off,
621 dlc.rebase_size,
622 dlc.bind_off,
623 dlc.bind_size,
624 dlc.weak_bind_off,
625 dlc.weak_bind_size,
626 dlc.lazy_bind_off,
627 dlc.lazy_bind_size,
628 dlc.export_off,
629 dlc.export_size,
630 });
631 },
632
633 .SYMTAB => {
634 const slc = lc.cast(macho.symtab_command).?;
635 try writer.writeByte('\n');
636 try writer.print(
637 \\symoff {x}
638 \\nsyms {x}
639 \\stroff {x}
640 \\strsize {x}
641 , .{
642 slc.symoff,
643 slc.nsyms,
644 slc.stroff,
645 slc.strsize,
646 });
647 },
648
649 .DYSYMTAB => {
650 const dlc = lc.cast(macho.dysymtab_command).?;
651 try writer.writeByte('\n');
652 try writer.print(
653 \\ilocalsym {x}
654 \\nlocalsym {x}
655 \\iextdefsym {x}
656 \\nextdefsym {x}
657 \\iundefsym {x}
658 \\nundefsym {x}
659 \\indirectsymoff {x}
660 \\nindirectsyms {x}
661 , .{
662 dlc.ilocalsym,
663 dlc.nlocalsym,
664 dlc.iextdefsym,
665 dlc.nextdefsym,
666 dlc.iundefsym,
667 dlc.nundefsym,
668 dlc.indirectsymoff,
669 dlc.nindirectsyms,
670 });
671 },
672
580673 else => {},
581674 }
582675 }
src/link/MachO.zig+25-21
......@@ -558,6 +558,29 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
558558
559559 try self.writeLinkeditSegmentData();
560560
561 const target = self.base.options.target;
562 const requires_codesig = blk: {
563 if (self.base.options.entitlements) |_| break :blk true;
564 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
565 break :blk true;
566 break :blk false;
567 };
568 var codesig: ?CodeSignature = if (requires_codesig) blk: {
569 // Preallocate space for the code signature.
570 // We need to do this at this stage so that we have the load commands with proper values
571 // written out to the file.
572 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
573 // where the code signature goes into.
574 var codesig = CodeSignature.init(self.page_size);
575 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
576 if (self.base.options.entitlements) |path| {
577 try codesig.addEntitlements(self.base.allocator, path);
578 }
579 try self.writeCodeSignaturePadding(&codesig);
580 break :blk codesig;
581 } else null;
582 defer if (codesig) |*csig| csig.deinit(self.base.allocator);
583
561584 // Write load commands
562585 var lc_buffer = std.ArrayList(u8).init(arena);
563586 const lc_writer = lc_buffer.writer();
......@@ -606,28 +629,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
606629
607630 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
608631
609 const target = self.base.options.target;
610 const requires_codesig = blk: {
611 if (self.base.options.entitlements) |_| break :blk true;
612 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
613 break :blk true;
614 break :blk false;
615 };
616 var codesig: ?CodeSignature = if (requires_codesig) blk: {
617 // Preallocate space for the code signature.
618 // We need to do this at this stage so that we have the load commands with proper values
619 // written out to the file.
620 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621 // where the code signature goes into.
622 var codesig = CodeSignature.init(self.page_size);
623 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
624 if (self.base.options.entitlements) |path| {
625 try codesig.addEntitlements(arena, path);
626 }
627 try self.writeCodeSignaturePadding(&codesig);
632 if (requires_codesig) {
628633 try lc_writer.writeStruct(self.codesig_cmd);
629 break :blk codesig;
630 } else null;
634 }
631635
632636 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
633637
src/link/MachO/zld.zig+24-20
......@@ -4100,6 +4100,28 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
41004100 }
41014101 }
41024102
4103 // Write code signature padding if required
4104 const requires_codesig = blk: {
4105 if (options.entitlements) |_| break :blk true;
4106 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
4107 break :blk false;
4108 };
4109 var codesig: ?CodeSignature = if (requires_codesig) blk: {
4110 // Preallocate space for the code signature.
4111 // We need to do this at this stage so that we have the load commands with proper values
4112 // written out to the file.
4113 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
4114 // where the code signature goes into.
4115 var codesig = CodeSignature.init(page_size);
4116 codesig.code_directory.ident = fs.path.basename(full_out_path);
4117 if (options.entitlements) |path| {
4118 try codesig.addEntitlements(zld.gpa, path);
4119 }
4120 try zld.writeCodeSignaturePadding(&codesig);
4121 break :blk codesig;
4122 } else null;
4123 defer if (codesig) |*csig| csig.deinit(zld.gpa);
4124
41034125 // Write load commands
41044126 var lc_buffer = std.ArrayList(u8).init(arena);
41054127 const lc_writer = lc_buffer.writer();
......@@ -4142,29 +4164,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
41424164
41434165 try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
41444166
4145 const requires_codesig = blk: {
4146 if (options.entitlements) |_| break :blk true;
4147 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
4148 break :blk false;
4149 };
41504167 var codesig_cmd_offset: ?u32 = null;
4151 var codesig: ?CodeSignature = if (requires_codesig) blk: {
4152 // Preallocate space for the code signature.
4153 // We need to do this at this stage so that we have the load commands with proper values
4154 // written out to the file.
4155 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
4156 // where the code signature goes into.
4157 var codesig = CodeSignature.init(page_size);
4158 codesig.code_directory.ident = fs.path.basename(full_out_path);
4159 if (options.entitlements) |path| {
4160 try codesig.addEntitlements(gpa, path);
4161 }
4162 try zld.writeCodeSignaturePadding(&codesig);
4168 if (requires_codesig) {
41634169 codesig_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len);
41644170 try lc_writer.writeStruct(zld.codesig_cmd);
4165 break :blk codesig;
4166 } else null;
4167 defer if (codesig) |*csig| csig.deinit(gpa);
4171 }
41684172
41694173 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
41704174 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
test/link.zig+5
......@@ -165,6 +165,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
165165 .requires_symlinks = true,
166166 });
167167
168 cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{
169 .build_modes = true,
170 .requires_symlinks = true,
171 });
172
168173 cases.addBuildFile("test/link/macho/tls/build.zig", .{
169174 .build_modes = true,
170175 .requires_symlinks = true,
test/link/macho/strict_validation/build.zig created+100
......@@ -0,0 +1,100 @@
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 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
9
10 const test_step = b.step("test", "Test");
11 test_step.dependOn(b.getInstallStep());
12
13 const exe = b.addExecutable("main", "main.zig");
14 exe.setBuildMode(mode);
15 exe.setTarget(target);
16 exe.linkLibC();
17
18 const check_exe = exe.checkObject(.macho);
19
20 check_exe.checkStart("cmd SEGMENT_64");
21 check_exe.checkNext("segname __LINKEDIT");
22 check_exe.checkNext("fileoff {fileoff}");
23 check_exe.checkNext("filesz {filesz}");
24
25 check_exe.checkStart("cmd DYLD_INFO_ONLY");
26 check_exe.checkNext("rebaseoff {rebaseoff}");
27 check_exe.checkNext("rebasesize {rebasesize}");
28 check_exe.checkNext("bindoff {bindoff}");
29 check_exe.checkNext("bindsize {bindsize}");
30 check_exe.checkNext("lazybindoff {lazybindoff}");
31 check_exe.checkNext("lazybindsize {lazybindsize}");
32 check_exe.checkNext("exportoff {exportoff}");
33 check_exe.checkNext("exportsize {exportsize}");
34
35 check_exe.checkStart("cmd SYMTAB");
36 check_exe.checkNext("symoff {symoff}");
37 check_exe.checkNext("stroff {stroff}");
38 check_exe.checkNext("strsize {strsize}");
39
40 check_exe.checkStart("cmd DYSYMTAB");
41 check_exe.checkNext("indirectsymoff {dysymoff}");
42
43 switch (builtin.cpu.arch) {
44 .aarch64 => {
45 check_exe.checkStart("cmd CODE_SIGNATURE");
46 check_exe.checkNext("dataoff {codesigoff}");
47 check_exe.checkNext("datasize {codesigsize}");
48 },
49 .x86_64 => {},
50 else => unreachable,
51 }
52
53 // Next check: DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export
54 check_exe.checkComputeCompare("rebaseoff ", .{ .op = .lt, .value = .{ .variable = "bindoff" } });
55 check_exe.checkComputeCompare("bindoff", .{ .op = .lt, .value = .{ .variable = "lazybindoff" } });
56 check_exe.checkComputeCompare("lazybindoff", .{ .op = .lt, .value = .{ .variable = "exportoff" } });
57
58 // Next check: DYLD_INFO_ONLY subsections do not overlap
59 check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .lte, .value = .{ .variable = "bindoff" } });
60 check_exe.checkComputeCompare("bindoff bindsize +", .{ .op = .lte, .value = .{ .variable = "lazybindoff" } });
61 check_exe.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .lte, .value = .{ .variable = "exportoff" } });
62
63 // Next check: we maintain order: symtab < dysymtab < strtab
64 check_exe.checkComputeCompare("symoff", .{ .op = .lt, .value = .{ .variable = "dysymoff" } });
65 check_exe.checkComputeCompare("dysymoff", .{ .op = .lt, .value = .{ .variable = "stroff" } });
66
67 // Next check: all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned
68 check_exe.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
69 check_exe.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
70 check_exe.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
71 check_exe.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
72 check_exe.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
73 check_exe.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
74 check_exe.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
75
76 switch (builtin.cpu.arch) {
77 .aarch64 => {
78 // Next check: LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data
79 check_exe.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{
80 .op = .eq,
81 .value = .{ .literal = 0 },
82 });
83
84 // Next check: CODE_SIGNATURE data offset is 16-bytes aligned
85 check_exe.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
86 },
87 .x86_64 => {
88 // Next check: LINKEDIT segment does not extend beyond, or does not include, strtab data
89 check_exe.checkComputeCompare("fileoff filesz stroff strsize + - -", .{
90 .op = .eq,
91 .value = .{ .literal = 0 },
92 });
93 },
94 else => unreachable,
95 }
96
97 const run = check_exe.runAndCompare();
98 run.expectStdOutEqual("Hello!\n");
99 test_step.dependOn(&run.step);
100}
test/link/macho/strict_validation/main.zig created+6
......@@ -0,0 +1,6 @@
1const std = @import("std");
2
3pub fn main() !void {
4 const stdout = std.io.getStdOut().writer();
5 try stdout.writeAll("Hello!\n");
6}