From a3f68c6fa23f893597b7708e0c00e81f57cb35c1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jan 2024 12:00:51 +0100 Subject: [PATCH] test/link/macho: upgrade unwind info tests --- test/link.zig | 8 - test/link/macho.zig | 273 ++++++++++++++++++ test/link/macho/reexports/a.zig | 7 - test/link/macho/reexports/build.zig | 38 --- test/link/macho/reexports/main.c | 5 - test/link/macho/unwind_info/all.h | 41 --- test/link/macho/unwind_info/build.zig | 88 ------ test/link/macho/unwind_info/main.cpp | 24 -- test/link/macho/unwind_info/simple_string.cpp | 30 -- .../macho/unwind_info/simple_string_owner.cpp | 12 - 10 files changed, 273 insertions(+), 253 deletions(-) delete mode 100644 test/link/macho/reexports/a.zig delete mode 100644 test/link/macho/reexports/build.zig delete mode 100644 test/link/macho/reexports/main.c delete mode 100644 test/link/macho/unwind_info/all.h delete mode 100644 test/link/macho/unwind_info/build.zig delete mode 100644 test/link/macho/unwind_info/main.cpp delete mode 100644 test/link/macho/unwind_info/simple_string.cpp delete mode 100644 test/link/macho/unwind_info/simple_string_owner.cpp diff --git a/test/link.zig b/test/link.zig index 0eb39c2a5c..3f85cd1a20 100644 --- a/test/link.zig +++ b/test/link.zig @@ -115,16 +115,8 @@ pub const cases = [_]Case{ .build_root = "test/link/macho/objcpp", .import = @import("link/macho/objcpp/build.zig"), }, - .{ - .build_root = "test/link/macho/reexports", - .import = @import("link/macho/reexports/build.zig"), - }, .{ .build_root = "test/link/macho/tbdv3", .import = @import("link/macho/tbdv3/build.zig"), }, - .{ - .build_root = "test/link/macho/unwind_info", - .import = @import("link/macho/unwind_info/build.zig"), - }, }; diff --git a/test/link/macho.zig b/test/link/macho.zig index 0cf2de1b85..6692b1888b 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -38,6 +38,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testThunks(b, .{ .target = aarch64_target })); macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target })); + macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target })); + macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target })); + macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target })); macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target })); // Tests requiring symlinks when tested on Windows @@ -1589,6 +1592,276 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step { return test_step; } +fn testUnwindInfo(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "macho-unwind-info", opts); + + const all_h = all_h: { + const wf = WriteFile.create(b); + break :all_h wf.add("all.h", + \\#ifndef ALL + \\#define ALL + \\ + \\#include + \\#include + \\#include + \\ + \\struct SimpleString { + \\ SimpleString(size_t max_size); + \\ ~SimpleString(); + \\ + \\ void print(const char* tag) const; + \\ bool append_line(const char* x); + \\ + \\private: + \\ size_t max_size; + \\ char* buffer; + \\ size_t length; + \\}; + \\ + \\struct SimpleStringOwner { + \\ SimpleStringOwner(const char* x); + \\ ~SimpleStringOwner(); + \\ + \\private: + \\ SimpleString string; + \\}; + \\ + \\class Error: public std::exception { + \\public: + \\ explicit Error(const char* msg) : msg{ msg } {} + \\ virtual ~Error() noexcept {} + \\ virtual const char* what() const noexcept { + \\ return msg.c_str(); + \\ } + \\ + \\protected: + \\ std::string msg; + \\}; + \\ + \\#endif + ); + }; + + const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes = + \\#include "all.h" + \\#include + \\ + \\void fn_c() { + \\ SimpleStringOwner c{ "cccccccccc" }; + \\} + \\ + \\void fn_b() { + \\ SimpleStringOwner b{ "b" }; + \\ fn_c(); + \\} + \\ + \\int main() { + \\ try { + \\ SimpleStringOwner a{ "a" }; + \\ fn_b(); + \\ SimpleStringOwner d{ "d" }; + \\ } catch (const Error& e) { + \\ printf("Error: %s\n", e.what()); + \\ } catch(const std::exception& e) { + \\ printf("Exception: %s\n", e.what()); + \\ } + \\ return 0; + \\} + }); + main_o.root_module.addIncludePath(all_h.dirname()); + main_o.linkLibCpp(); + + const simple_string_o = addObject(b, opts, .{ .name = "simple_string", .cpp_source_bytes = + \\#include "all.h" + \\#include + \\#include + \\ + \\SimpleString::SimpleString(size_t max_size) + \\: max_size{ max_size }, length{} { + \\ if (max_size == 0) { + \\ throw Error{ "Max size must be at least 1." }; + \\ } + \\ buffer = new char[max_size]; + \\ buffer[0] = 0; + \\} + \\ + \\SimpleString::~SimpleString() { + \\ delete[] buffer; + \\} + \\ + \\void SimpleString::print(const char* tag) const { + \\ printf("%s: %s", tag, buffer); + \\} + \\ + \\bool SimpleString::append_line(const char* x) { + \\ const auto x_len = strlen(x); + \\ if (x_len + length + 2 > max_size) return false; + \\ std::strncpy(buffer + length, x, max_size - length); + \\ length += x_len; + \\ buffer[length++] = '\n'; + \\ buffer[length] = 0; + \\ return true; + \\} + }); + simple_string_o.root_module.addIncludePath(all_h.dirname()); + simple_string_o.linkLibCpp(); + + const simple_string_owner_o = addObject(b, opts, .{ .name = "simple_string_owner", .cpp_source_bytes = + \\#include "all.h" + \\ + \\SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } { + \\ if (!string.append_line(x)) { + \\ throw Error{ "Not enough memory!" }; + \\ } + \\ string.print("Constructed"); + \\} + \\ + \\SimpleStringOwner::~SimpleStringOwner() { + \\ string.print("About to destroy"); + \\} + }); + simple_string_owner_o.root_module.addIncludePath(all_h.dirname()); + simple_string_owner_o.linkLibCpp(); + + const exp_stdout = + \\Constructed: a + \\Constructed: b + \\About to destroy: b + \\About to destroy: a + \\Error: Not enough memory! + \\ + ; + + const exe = addExecutable(b, opts, .{ .name = "main" }); + exe.addObject(main_o); + exe.addObject(simple_string_o); + exe.addObject(simple_string_owner_o); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual(exp_stdout); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkContains("(was private external) ___gxx_personality_v0"); + test_step.dependOn(&check.step); + + return test_step; +} + +fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "macho-unwind-info-no-subsections-arm64", opts); + + const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = + \\.globl _foo + \\.align 4 + \\_foo: + \\ .cfi_startproc + \\ stp x29, x30, [sp, #-32]! + \\ .cfi_def_cfa_offset 32 + \\ .cfi_offset w30, -24 + \\ .cfi_offset w29, -32 + \\ mov x29, sp + \\ .cfi_def_cfa w29, 32 + \\ bl _bar + \\ ldp x29, x30, [sp], #32 + \\ .cfi_restore w29 + \\ .cfi_restore w30 + \\ .cfi_def_cfa_offset 0 + \\ ret + \\ .cfi_endproc + \\ + \\.globl _bar + \\.align 4 + \\_bar: + \\ .cfi_startproc + \\ sub sp, sp, #32 + \\ .cfi_def_cfa_offset -32 + \\ stp x29, x30, [sp, #16] + \\ .cfi_offset w30, -24 + \\ .cfi_offset w29, -32 + \\ mov x29, sp + \\ .cfi_def_cfa w29, 32 + \\ mov w0, #4 + \\ ldp x29, x30, [sp, #16] + \\ .cfi_restore w29 + \\ .cfi_restore w30 + \\ add sp, sp, #32 + \\ .cfi_def_cfa_offset 0 + \\ ret + \\ .cfi_endproc + }); + + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = + \\#include + \\int foo(); + \\int main() { + \\ printf("%d\n", foo()); + \\ return 0; + \\} + }); + exe.addObject(a_o); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("4\n"); + test_step.dependOn(&run.step); + + return test_step; +} + +fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "macho-unwind-info-no-subsections-x64", opts); + + const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = + \\.globl _foo + \\_foo: + \\ .cfi_startproc + \\ push %rbp + \\ .cfi_def_cfa_offset 8 + \\ .cfi_offset %rbp, -8 + \\ mov %rsp, %rbp + \\ .cfi_def_cfa_register %rbp + \\ call _bar + \\ pop %rbp + \\ .cfi_restore %rbp + \\ .cfi_def_cfa_offset 0 + \\ ret + \\ .cfi_endproc + \\ + \\.globl _bar + \\_bar: + \\ .cfi_startproc + \\ push %rbp + \\ .cfi_def_cfa_offset 8 + \\ .cfi_offset %rbp, -8 + \\ mov %rsp, %rbp + \\ .cfi_def_cfa_register %rbp + \\ mov $4, %rax + \\ pop %rbp + \\ .cfi_restore %rbp + \\ .cfi_def_cfa_offset 0 + \\ ret + \\ .cfi_endproc + }); + + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = + \\#include + \\int foo(); + \\int main() { + \\ printf("%d\n", foo()); + \\ return 0; + \\} + }); + exe.addObject(a_o); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("4\n"); + test_step.dependOn(&run.step); + + return test_step; +} + // Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s fn testWeakBind(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "macho-weak-bind", opts); diff --git a/test/link/macho/reexports/a.zig b/test/link/macho/reexports/a.zig deleted file mode 100644 index cfa7e8b3ac..0000000000 --- a/test/link/macho/reexports/a.zig +++ /dev/null @@ -1,7 +0,0 @@ -const x: i32 = 42; -export fn foo() i32 { - return x; -} -comptime { - @export(foo, .{ .name = "bar", .linkage = .Strong }); -} diff --git a/test/link/macho/reexports/build.zig b/test/link/macho/reexports/build.zig deleted file mode 100644 index 44c96ecf7e..0000000000 --- a/test/link/macho/reexports/build.zig +++ /dev/null @@ -1,38 +0,0 @@ -const std = @import("std"); - -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 lib = b.addStaticLibrary(.{ - .name = "a", - .root_source_file = .{ .path = "a.zig" }, - .optimize = optimize, - .target = target, - }); - - const exe = b.addExecutable(.{ - .name = "test", - .optimize = optimize, - .target = target, - }); - exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); - exe.linkLibrary(lib); - exe.linkLibC(); - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectExitCode(0); - test_step.dependOn(&run.step); -} diff --git a/test/link/macho/reexports/main.c b/test/link/macho/reexports/main.c deleted file mode 100644 index 2beb701f1f..0000000000 --- a/test/link/macho/reexports/main.c +++ /dev/null @@ -1,5 +0,0 @@ -extern int foo(); -extern int bar(); -int main() { - return bar() - foo(); -} diff --git a/test/link/macho/unwind_info/all.h b/test/link/macho/unwind_info/all.h deleted file mode 100644 index 15efba90d3..0000000000 --- a/test/link/macho/unwind_info/all.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef ALL -#define ALL - -#include -#include -#include - -struct SimpleString { - SimpleString(size_t max_size); - ~SimpleString(); - - void print(const char* tag) const; - bool append_line(const char* x); - -private: - size_t max_size; - char* buffer; - size_t length; -}; - -struct SimpleStringOwner { - SimpleStringOwner(const char* x); - ~SimpleStringOwner(); - -private: - SimpleString string; -}; - -class Error: public std::exception { -public: - explicit Error(const char* msg) : msg{ msg } {} - virtual ~Error() noexcept {} - virtual const char* what() const noexcept { - return msg.c_str(); - } - -protected: - std::string msg; -}; - -#endif diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig deleted file mode 100644 index 33af6016f9..0000000000 --- a/test/link/macho/unwind_info/build.zig +++ /dev/null @@ -1,88 +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 }); - - testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip"); - testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip"); -} - -fn testUnwindInfo( - b: *std.Build, - test_step: *std.Build.Step, - optimize: std.builtin.OptimizeMode, - target: std.Build.ResolvedTarget, - dead_strip: bool, - name: []const u8, -) void { - const exe = createScenario(b, optimize, target, name); - exe.link_gc_sections = dead_strip; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("segname __TEXT"); - check.checkExact("sectname __gcc_except_tab"); - check.checkExact("sectname __unwind_info"); - - switch (builtin.cpu.arch) { - .aarch64 => { - check.checkExact("sectname __eh_frame"); - }, - .x86_64 => {}, // We do not expect `__eh_frame` section on x86_64 in this case - else => unreachable, - } - - check.checkInSymtab(); - check.checkContains("(was private external) ___gxx_personality_v0"); - test_step.dependOn(&check.step); - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectStdOutEqual( - \\Constructed: a - \\Constructed: b - \\About to destroy: b - \\About to destroy: a - \\Error: Not enough memory! - \\ - ); - - test_step.dependOn(&run.step); -} - -fn createScenario( - b: *std.Build, - optimize: std.builtin.OptimizeMode, - target: std.Build.ResolvedTarget, - name: []const u8, -) *std.Build.Step.Compile { - const exe = b.addExecutable(.{ - .name = name, - .optimize = optimize, - .target = target, - }); - b.default_step.dependOn(&exe.step); - exe.addIncludePath(.{ .path = "." }); - exe.addCSourceFiles(.{ - .files = &[_][]const u8{ - "main.cpp", - "simple_string.cpp", - "simple_string_owner.cpp", - }, - }); - exe.linkLibCpp(); - return exe; -} diff --git a/test/link/macho/unwind_info/main.cpp b/test/link/macho/unwind_info/main.cpp deleted file mode 100644 index 8195f80b3c..0000000000 --- a/test/link/macho/unwind_info/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "all.h" -#include - -void fn_c() { - SimpleStringOwner c{ "cccccccccc" }; -} - -void fn_b() { - SimpleStringOwner b{ "b" }; - fn_c(); -} - -int main() { - try { - SimpleStringOwner a{ "a" }; - fn_b(); - SimpleStringOwner d{ "d" }; - } catch (const Error& e) { - printf("Error: %s\n", e.what()); - } catch(const std::exception& e) { - printf("Exception: %s\n", e.what()); - } - return 0; -} diff --git a/test/link/macho/unwind_info/simple_string.cpp b/test/link/macho/unwind_info/simple_string.cpp deleted file mode 100644 index 15699cd1e4..0000000000 --- a/test/link/macho/unwind_info/simple_string.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "all.h" -#include -#include - -SimpleString::SimpleString(size_t max_size) -: max_size{ max_size }, length{} { - if (max_size == 0) { - throw Error{ "Max size must be at least 1." }; - } - buffer = new char[max_size]; - buffer[0] = 0; -} - -SimpleString::~SimpleString() { - delete[] buffer; -} - -void SimpleString::print(const char* tag) const { - printf("%s: %s", tag, buffer); -} - -bool SimpleString::append_line(const char* x) { - const auto x_len = strlen(x); - if (x_len + length + 2 > max_size) return false; - std::strncpy(buffer + length, x, max_size - length); - length += x_len; - buffer[length++] = '\n'; - buffer[length] = 0; - return true; -} diff --git a/test/link/macho/unwind_info/simple_string_owner.cpp b/test/link/macho/unwind_info/simple_string_owner.cpp deleted file mode 100644 index c242af6ecc..0000000000 --- a/test/link/macho/unwind_info/simple_string_owner.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "all.h" - -SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } { - if (!string.append_line(x)) { - throw Error{ "Not enough memory!" }; - } - string.print("Constructed"); -} - -SimpleStringOwner::~SimpleStringOwner() { - string.print("About to destroy"); -}