Merge pull request #14049 from ziglang/issue-14045

macho+zld: write code signature padding before committing load commands
This commit is contained in:
Jakub Konka 2022-12-23 19:07:36 +01:00 committed by GitHub
commit bb62d5105c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 262 additions and 50 deletions

View File

@ -126,22 +126,28 @@ const Action = struct {
/// its reduced, computed value compares using `op` with the expected value, either
/// a literal or another extracted variable.
fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
var op_stack = std.ArrayList(enum { add }).init(gpa);
var op_stack = std.ArrayList(enum { add, sub, mod }).init(gpa);
var values = std.ArrayList(u64).init(gpa);
var it = mem.tokenize(u8, act.phrase, " ");
while (it.next()) |next| {
if (mem.eql(u8, next, "+")) {
try op_stack.append(.add);
} else if (mem.eql(u8, next, "-")) {
try op_stack.append(.sub);
} else if (mem.eql(u8, next, "%")) {
try op_stack.append(.mod);
} else {
const val = global_vars.get(next) orelse {
std.debug.print(
\\
\\========= Variable was not extracted: ===========
\\{s}
\\
, .{next});
return error.UnknownVariable;
const val = std.fmt.parseInt(u64, next, 0) catch blk: {
break :blk global_vars.get(next) orelse {
std.debug.print(
\\
\\========= Variable was not extracted: ===========
\\{s}
\\
, .{next});
return error.UnknownVariable;
};
};
try values.append(val);
}
@ -155,7 +161,14 @@ const Action = struct {
.add => {
reduced += other;
},
.sub => {
reduced -= other;
},
.mod => {
reduced %= other;
},
}
op_i += 1;
}
const exp_value = switch (act.expected.?.value) {
@ -577,6 +590,86 @@ const MachODumper = struct {
try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
},
.DATA_IN_CODE,
.FUNCTION_STARTS,
.CODE_SIGNATURE,
=> {
const llc = lc.cast(macho.linkedit_data_command).?;
try writer.writeByte('\n');
try writer.print(
\\dataoff {x}
\\datasize {x}
, .{ llc.dataoff, llc.datasize });
},
.DYLD_INFO_ONLY => {
const dlc = lc.cast(macho.dyld_info_command).?;
try writer.writeByte('\n');
try writer.print(
\\rebaseoff {x}
\\rebasesize {x}
\\bindoff {x}
\\bindsize {x}
\\weakbindoff {x}
\\weakbindsize {x}
\\lazybindoff {x}
\\lazybindsize {x}
\\exportoff {x}
\\exportsize {x}
, .{
dlc.rebase_off,
dlc.rebase_size,
dlc.bind_off,
dlc.bind_size,
dlc.weak_bind_off,
dlc.weak_bind_size,
dlc.lazy_bind_off,
dlc.lazy_bind_size,
dlc.export_off,
dlc.export_size,
});
},
.SYMTAB => {
const slc = lc.cast(macho.symtab_command).?;
try writer.writeByte('\n');
try writer.print(
\\symoff {x}
\\nsyms {x}
\\stroff {x}
\\strsize {x}
, .{
slc.symoff,
slc.nsyms,
slc.stroff,
slc.strsize,
});
},
.DYSYMTAB => {
const dlc = lc.cast(macho.dysymtab_command).?;
try writer.writeByte('\n');
try writer.print(
\\ilocalsym {x}
\\nlocalsym {x}
\\iextdefsym {x}
\\nextdefsym {x}
\\iundefsym {x}
\\nundefsym {x}
\\indirectsymoff {x}
\\nindirectsyms {x}
, .{
dlc.ilocalsym,
dlc.nlocalsym,
dlc.iextdefsym,
dlc.nextdefsym,
dlc.iundefsym,
dlc.nundefsym,
dlc.indirectsymoff,
dlc.nindirectsyms,
});
},
else => {},
}
}

View File

@ -558,6 +558,29 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
try self.writeLinkeditSegmentData();
const target = self.base.options.target;
const requires_codesig = blk: {
if (self.base.options.entitlements) |_| break :blk true;
if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
break :blk true;
break :blk false;
};
var codesig: ?CodeSignature = if (requires_codesig) blk: {
// Preallocate space for the code signature.
// We need to do this at this stage so that we have the load commands with proper values
// written out to the file.
// The most important here is to have the correct vm and filesize of the __LINKEDIT segment
// where the code signature goes into.
var codesig = CodeSignature.init(self.page_size);
codesig.code_directory.ident = self.base.options.emit.?.sub_path;
if (self.base.options.entitlements) |path| {
try codesig.addEntitlements(self.base.allocator, path);
}
try self.writeCodeSignaturePadding(&codesig);
break :blk codesig;
} else null;
defer if (codesig) |*csig| csig.deinit(self.base.allocator);
// Write load commands
var lc_buffer = std.ArrayList(u8).init(arena);
const lc_writer = lc_buffer.writer();
@ -606,28 +629,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
const target = self.base.options.target;
const requires_codesig = blk: {
if (self.base.options.entitlements) |_| break :blk true;
if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
break :blk true;
break :blk false;
};
var codesig: ?CodeSignature = if (requires_codesig) blk: {
// Preallocate space for the code signature.
// We need to do this at this stage so that we have the load commands with proper values
// written out to the file.
// The most important here is to have the correct vm and filesize of the __LINKEDIT segment
// where the code signature goes into.
var codesig = CodeSignature.init(self.page_size);
codesig.code_directory.ident = self.base.options.emit.?.sub_path;
if (self.base.options.entitlements) |path| {
try codesig.addEntitlements(arena, path);
}
try self.writeCodeSignaturePadding(&codesig);
if (requires_codesig) {
try lc_writer.writeStruct(self.codesig_cmd);
break :blk codesig;
} else null;
}
try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));

View File

@ -4100,6 +4100,28 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
}
}
// Write code signature padding if required
const requires_codesig = blk: {
if (options.entitlements) |_| break :blk true;
if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
break :blk false;
};
var codesig: ?CodeSignature = if (requires_codesig) blk: {
// Preallocate space for the code signature.
// We need to do this at this stage so that we have the load commands with proper values
// written out to the file.
// The most important here is to have the correct vm and filesize of the __LINKEDIT segment
// where the code signature goes into.
var codesig = CodeSignature.init(page_size);
codesig.code_directory.ident = fs.path.basename(full_out_path);
if (options.entitlements) |path| {
try codesig.addEntitlements(zld.gpa, path);
}
try zld.writeCodeSignaturePadding(&codesig);
break :blk codesig;
} else null;
defer if (codesig) |*csig| csig.deinit(zld.gpa);
// Write load commands
var lc_buffer = std.ArrayList(u8).init(arena);
const lc_writer = lc_buffer.writer();
@ -4142,29 +4164,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
const requires_codesig = blk: {
if (options.entitlements) |_| break :blk true;
if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
break :blk false;
};
var codesig_cmd_offset: ?u32 = null;
var codesig: ?CodeSignature = if (requires_codesig) blk: {
// Preallocate space for the code signature.
// We need to do this at this stage so that we have the load commands with proper values
// written out to the file.
// The most important here is to have the correct vm and filesize of the __LINKEDIT segment
// where the code signature goes into.
var codesig = CodeSignature.init(page_size);
codesig.code_directory.ident = fs.path.basename(full_out_path);
if (options.entitlements) |path| {
try codesig.addEntitlements(gpa, path);
}
try zld.writeCodeSignaturePadding(&codesig);
if (requires_codesig) {
codesig_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len);
try lc_writer.writeStruct(zld.codesig_cmd);
break :blk codesig;
} else null;
defer if (codesig) |*csig| csig.deinit(gpa);
}
const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));

View File

@ -165,6 +165,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
.requires_symlinks = true,
});
cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{
.build_modes = true,
.requires_symlinks = true,
});
cases.addBuildFile("test/link/macho/tls/build.zig", .{
.build_modes = true,
.requires_symlinks = true,

View File

@ -0,0 +1,100 @@
const std = @import("std");
const builtin = @import("builtin");
const Builder = std.build.Builder;
const LibExeObjectStep = std.build.LibExeObjStep;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
const exe = b.addExecutable("main", "main.zig");
exe.setBuildMode(mode);
exe.setTarget(target);
exe.linkLibC();
const check_exe = exe.checkObject(.macho);
check_exe.checkStart("cmd SEGMENT_64");
check_exe.checkNext("segname __LINKEDIT");
check_exe.checkNext("fileoff {fileoff}");
check_exe.checkNext("filesz {filesz}");
check_exe.checkStart("cmd DYLD_INFO_ONLY");
check_exe.checkNext("rebaseoff {rebaseoff}");
check_exe.checkNext("rebasesize {rebasesize}");
check_exe.checkNext("bindoff {bindoff}");
check_exe.checkNext("bindsize {bindsize}");
check_exe.checkNext("lazybindoff {lazybindoff}");
check_exe.checkNext("lazybindsize {lazybindsize}");
check_exe.checkNext("exportoff {exportoff}");
check_exe.checkNext("exportsize {exportsize}");
check_exe.checkStart("cmd SYMTAB");
check_exe.checkNext("symoff {symoff}");
check_exe.checkNext("stroff {stroff}");
check_exe.checkNext("strsize {strsize}");
check_exe.checkStart("cmd DYSYMTAB");
check_exe.checkNext("indirectsymoff {dysymoff}");
switch (builtin.cpu.arch) {
.aarch64 => {
check_exe.checkStart("cmd CODE_SIGNATURE");
check_exe.checkNext("dataoff {codesigoff}");
check_exe.checkNext("datasize {codesigsize}");
},
.x86_64 => {},
else => unreachable,
}
// Next check: DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export
check_exe.checkComputeCompare("rebaseoff ", .{ .op = .lt, .value = .{ .variable = "bindoff" } });
check_exe.checkComputeCompare("bindoff", .{ .op = .lt, .value = .{ .variable = "lazybindoff" } });
check_exe.checkComputeCompare("lazybindoff", .{ .op = .lt, .value = .{ .variable = "exportoff" } });
// Next check: DYLD_INFO_ONLY subsections do not overlap
check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .lte, .value = .{ .variable = "bindoff" } });
check_exe.checkComputeCompare("bindoff bindsize +", .{ .op = .lte, .value = .{ .variable = "lazybindoff" } });
check_exe.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .lte, .value = .{ .variable = "exportoff" } });
// Next check: we maintain order: symtab < dysymtab < strtab
check_exe.checkComputeCompare("symoff", .{ .op = .lt, .value = .{ .variable = "dysymoff" } });
check_exe.checkComputeCompare("dysymoff", .{ .op = .lt, .value = .{ .variable = "stroff" } });
// Next check: all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned
check_exe.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
check_exe.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
check_exe.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
check_exe.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
check_exe.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
check_exe.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
check_exe.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
switch (builtin.cpu.arch) {
.aarch64 => {
// Next check: LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data
check_exe.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{
.op = .eq,
.value = .{ .literal = 0 },
});
// Next check: CODE_SIGNATURE data offset is 16-bytes aligned
check_exe.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
},
.x86_64 => {
// Next check: LINKEDIT segment does not extend beyond, or does not include, strtab data
check_exe.checkComputeCompare("fileoff filesz stroff strsize + - -", .{
.op = .eq,
.value = .{ .literal = 0 },
});
},
else => unreachable,
}
const run = check_exe.runAndCompare();
run.expectStdOutEqual("Hello!\n");
test_step.dependOn(&run.step);
}

View File

@ -0,0 +1,6 @@
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("Hello!\n");
}