mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #14049 from ziglang/issue-14045
macho+zld: write code signature padding before committing load commands
This commit is contained in:
commit
bb62d5105c
@ -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 => {},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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,
|
||||
|
||||
100
test/link/macho/strict_validation/build.zig
Normal file
100
test/link/macho/strict_validation/build.zig
Normal 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);
|
||||
}
|
||||
6
test/link/macho/strict_validation/main.zig
Normal file
6
test/link/macho/strict_validation/main.zig
Normal file
@ -0,0 +1,6 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try stdout.writeAll("Hello!\n");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user