Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 102 additions & 9 deletions lib/std/build/CheckObjectStep.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -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);
}
Expand All@@ -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) {
Expand DownExpand Up@@ -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 => {},
}
}
Expand Down
46 changes: 25 additions & 21 deletions src/link/MachO.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -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();
Expand DownExpand Up@@ -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));

Expand Down
44 changes: 24 additions & 20 deletions src/link/MachO/zld.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -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();
Expand DownExpand Up@@ -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));
Expand Down
5 changes: 5 additions & 0 deletions test/link.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -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,
Expand Down
100 changes: 100 additions & 0 deletions test/link/macho/strict_validation/build.zig
Original file line numberDiff line numberDiff line change
@@ -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 changes: 6 additions & 0 deletions test/link/macho/strict_validation/main.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
const std = @import("std");

pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("Hello!\n");
}