diff --git a/test/link.zig b/test/link.zig index 7fc18a141c..905d6cc35d 100644 --- a/test/link.zig +++ b/test/link.zig @@ -135,10 +135,6 @@ pub const cases = [_]Case{ .build_root = "test/link/macho/stack_size", .import = @import("link/macho/stack_size/build.zig"), }, - .{ - .build_root = "test/link/macho/strict_validation", - .import = @import("link/macho/strict_validation/build.zig"), - }, .{ .build_root = "test/link/macho/tbdv3", .import = @import("link/macho/tbdv3/build.zig"), diff --git a/test/link/macho.zig b/test/link/macho.zig index 66c4b7eb38..03c3b71c59 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -23,6 +23,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testHelloC(b, .{ .target = default_target })); macho_step.dependOn(testHelloZig(b, .{ .target = default_target })); macho_step.dependOn(testLargeBss(b, .{ .target = default_target })); + macho_step.dependOn(testLayout(b, .{ .target = default_target })); macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target })); macho_step.dependOn(testRelocatable(b, .{ .target = default_target })); macho_step.dependOn(testRelocatableZig(b, .{ .target = default_target })); @@ -612,6 +613,125 @@ fn testLargeBss(b: *Build, opts: Options) *Step { return test_step; } +fn testLayout(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "macho-layout", opts); + + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = + \\#include + \\int main() { + \\ printf("Hello world!"); + \\ return 0; + \\} + }); + + const check = exe.checkObject(); + check.checkInHeaders(); + check.checkExact("cmd SEGMENT_64"); + check.checkExact("segname __LINKEDIT"); + check.checkExtract("fileoff {fileoff}"); + check.checkExtract("filesz {filesz}"); + check.checkInHeaders(); + check.checkExact("cmd DYLD_INFO_ONLY"); + check.checkExtract("rebaseoff {rebaseoff}"); + check.checkExtract("rebasesize {rebasesize}"); + check.checkExtract("bindoff {bindoff}"); + check.checkExtract("bindsize {bindsize}"); + check.checkExtract("lazybindoff {lazybindoff}"); + check.checkExtract("lazybindsize {lazybindsize}"); + check.checkExtract("exportoff {exportoff}"); + check.checkExtract("exportsize {exportsize}"); + check.checkInHeaders(); + check.checkExact("cmd FUNCTION_STARTS"); + check.checkExtract("dataoff {fstartoff}"); + check.checkExtract("datasize {fstartsize}"); + check.checkInHeaders(); + check.checkExact("cmd DATA_IN_CODE"); + check.checkExtract("dataoff {diceoff}"); + check.checkExtract("datasize {dicesize}"); + check.checkInHeaders(); + check.checkExact("cmd SYMTAB"); + check.checkExtract("symoff {symoff}"); + check.checkExtract("nsyms {symnsyms}"); + check.checkExtract("stroff {stroff}"); + check.checkExtract("strsize {strsize}"); + check.checkInHeaders(); + check.checkExact("cmd DYSYMTAB"); + check.checkExtract("indirectsymoff {dysymoff}"); + check.checkExtract("nindirectsyms {dysymnsyms}"); + + switch (opts.target.result.cpu.arch) { + .aarch64 => { + check.checkInHeaders(); + check.checkExact("cmd CODE_SIGNATURE"); + check.checkExtract("dataoff {codesigoff}"); + check.checkExtract("datasize {codesigsize}"); + }, + .x86_64 => {}, + else => unreachable, + } + + // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export, + // and there are no gaps between them + check.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } }); + check.checkComputeCompare("bindoff bindsize +", .{ .op = .eq, .value = .{ .variable = "lazybindoff" } }); + check.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .eq, .value = .{ .variable = "exportoff" } }); + + // FUNCTION_STARTS directly follows DYLD_INFO_ONLY (no gap) + check.checkComputeCompare("exportoff exportsize +", .{ .op = .eq, .value = .{ .variable = "fstartoff" } }); + + // DATA_IN_CODE directly follows FUNCTION_STARTS (no gap) + check.checkComputeCompare("fstartoff fstartsize +", .{ .op = .eq, .value = .{ .variable = "diceoff" } }); + + // SYMTAB directly follows DATA_IN_CODE (no gap) + check.checkComputeCompare("diceoff dicesize +", .{ .op = .eq, .value = .{ .variable = "symoff" } }); + + // DYSYMTAB directly follows SYMTAB (no gap) + check.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } }); + + // STRTAB follows DYSYMTAB with possible gap + check.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } }); + + // all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned + check.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("fstartoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("diceoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + check.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + + switch (opts.target.result.cpu.arch) { + .aarch64 => { + // LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data + check.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{ + .op = .eq, + .value = .{ .literal = 0 }, + }); + + // CODE_SIGNATURE data offset is 16-bytes aligned + check.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); + }, + .x86_64 => { + // LINKEDIT segment does not extend beyond, or does not include, strtab data + check.checkComputeCompare("fileoff filesz stroff strsize + - -", .{ + .op = .eq, + .value = .{ .literal = 0 }, + }); + }, + else => unreachable, + } + + test_step.dependOn(&check.step); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("Hello world!"); + test_step.dependOn(&run.step); + + return test_step; +} + fn testMhExecuteHeader(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "macho-mh-execute-header", opts); diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig deleted file mode 100644 index f35438369c..0000000000 --- a/test/link/macho/strict_validation/build.zig +++ /dev/null @@ -1,137 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -pub const requires_symlinks = true; - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target = b.resolveTargetQuery(.{ .os_tag = .macos }); - - const exe = b.addExecutable(.{ - .name = "main", - .root_source_file = .{ .path = "main.zig" }, - .optimize = optimize, - .target = target, - }); - exe.linkLibC(); - - const check_exe = exe.checkObject(); - - check_exe.checkInHeaders(); - check_exe.checkExact("cmd SEGMENT_64"); - check_exe.checkExact("segname __LINKEDIT"); - check_exe.checkExtract("fileoff {fileoff}"); - check_exe.checkExtract("filesz {filesz}"); - - check_exe.checkInHeaders(); - check_exe.checkExact("cmd DYLD_INFO_ONLY"); - check_exe.checkExtract("rebaseoff {rebaseoff}"); - check_exe.checkExtract("rebasesize {rebasesize}"); - check_exe.checkExtract("bindoff {bindoff}"); - check_exe.checkExtract("bindsize {bindsize}"); - check_exe.checkExtract("lazybindoff {lazybindoff}"); - check_exe.checkExtract("lazybindsize {lazybindsize}"); - check_exe.checkExtract("exportoff {exportoff}"); - check_exe.checkExtract("exportsize {exportsize}"); - - check_exe.checkInHeaders(); - check_exe.checkExact("cmd FUNCTION_STARTS"); - check_exe.checkExtract("dataoff {fstartoff}"); - check_exe.checkExtract("datasize {fstartsize}"); - - check_exe.checkInHeaders(); - check_exe.checkExact("cmd DATA_IN_CODE"); - check_exe.checkExtract("dataoff {diceoff}"); - check_exe.checkExtract("datasize {dicesize}"); - - check_exe.checkInHeaders(); - check_exe.checkExact("cmd SYMTAB"); - check_exe.checkExtract("symoff {symoff}"); - check_exe.checkExtract("nsyms {symnsyms}"); - check_exe.checkExtract("stroff {stroff}"); - check_exe.checkExtract("strsize {strsize}"); - - check_exe.checkInHeaders(); - check_exe.checkExact("cmd DYSYMTAB"); - check_exe.checkExtract("indirectsymoff {dysymoff}"); - check_exe.checkExtract("nindirectsyms {dysymnsyms}"); - - switch (builtin.cpu.arch) { - .aarch64 => { - check_exe.checkInHeaders(); - check_exe.checkExact("cmd CODE_SIGNATURE"); - check_exe.checkExtract("dataoff {codesigoff}"); - check_exe.checkExtract("datasize {codesigsize}"); - }, - .x86_64 => {}, - else => unreachable, - } - - // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export, - // and there are no gaps between them - check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } }); - check_exe.checkComputeCompare("bindoff bindsize +", .{ .op = .eq, .value = .{ .variable = "lazybindoff" } }); - check_exe.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .eq, .value = .{ .variable = "exportoff" } }); - - // FUNCTION_STARTS directly follows DYLD_INFO_ONLY (no gap) - check_exe.checkComputeCompare("exportoff exportsize +", .{ .op = .eq, .value = .{ .variable = "fstartoff" } }); - - // DATA_IN_CODE directly follows FUNCTION_STARTS (no gap) - check_exe.checkComputeCompare("fstartoff fstartsize +", .{ .op = .eq, .value = .{ .variable = "diceoff" } }); - - // SYMTAB directly follows DATA_IN_CODE (no gap) - check_exe.checkComputeCompare("diceoff dicesize +", .{ .op = .eq, .value = .{ .variable = "symoff" } }); - - // DYSYMTAB directly follows SYMTAB (no gap) - check_exe.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } }); - - // STRTAB follows DYSYMTAB with possible gap - check_exe.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } }); - - // 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("fstartoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check_exe.checkComputeCompare("diceoff 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 => { - // 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 }, - }); - - // CODE_SIGNATURE data offset is 16-bytes aligned - check_exe.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - }, - .x86_64 => { - // 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, - } - test_step.dependOn(&check_exe.step); - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectStdOutEqual("Hello!\n"); - test_step.dependOn(&run.step); -} diff --git a/test/link/macho/strict_validation/main.zig b/test/link/macho/strict_validation/main.zig deleted file mode 100644 index 6510e13fd7..0000000000 --- a/test/link/macho/strict_validation/main.zig +++ /dev/null @@ -1,6 +0,0 @@ -const std = @import("std"); - -pub fn main() !void { - const stdout = std.io.getStdOut().writer(); - try stdout.writeAll("Hello!\n"); -}