diff --git a/test/link.zig b/test/link.zig index 5e26ae728d..c787e8b1ae 100644 --- a/test/link.zig +++ b/test/link.zig @@ -190,6 +190,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void { .requires_symlinks = true, }); + cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{ + .build_modes = true, + .requires_symlinks = true, + }); + cases.addBuildFile("test/link/macho/uuid/build.zig", .{ .build_modes = false, .requires_symlinks = true, diff --git a/test/link/macho/unwind_info/all.h b/test/link/macho/unwind_info/all.h new file mode 100644 index 0000000000..15efba90d3 --- /dev/null +++ b/test/link/macho/unwind_info/all.h @@ -0,0 +1,41 @@ +#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 new file mode 100644 index 0000000000..b6405ebc09 --- /dev/null +++ b/test/link/macho/unwind_info/build.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +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 the program"); + + testUnwindInfo(b, test_step, mode, target, false); + testUnwindInfo(b, test_step, mode, target, true); +} + +fn testUnwindInfo( + b: *Builder, + test_step: *std.build.Step, + mode: std.builtin.Mode, + target: std.zig.CrossTarget, + dead_strip: bool, +) void { + const exe = createScenario(b, mode, target); + exe.link_gc_sections = dead_strip; + + const check = exe.checkObject(.macho); + check.checkStart("segname __TEXT"); + check.checkNext("sectname __gcc_except_tab"); + check.checkNext("sectname __unwind_info"); + check.checkNext("sectname __eh_frame"); + + check.checkInSymtab(); + check.checkNext("{*} (__TEXT,__text) external ___gxx_personality_v0"); + + const run_cmd = check.runAndCompare(); + run_cmd.expectStdOutEqual( + \\Constructed: a + \\Constructed: b + \\About to destroy: b + \\About to destroy: a + \\Error: Not enough memory! + \\ + ); + + test_step.dependOn(&run_cmd.step); +} + +fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep { + const exe = b.addExecutable("test", null); + b.default_step.dependOn(&exe.step); + exe.addIncludePath("."); + exe.addCSourceFiles(&[_][]const u8{ + "main.cpp", + "simple_string.cpp", + "simple_string_owner.cpp", + }, &[0][]const u8{}); + exe.setBuildMode(mode); + exe.setTarget(target); + exe.linkLibCpp(); + return exe; +} diff --git a/test/link/macho/unwind_info/main.cpp b/test/link/macho/unwind_info/main.cpp new file mode 100644 index 0000000000..8195f80b3c --- /dev/null +++ b/test/link/macho/unwind_info/main.cpp @@ -0,0 +1,24 @@ +#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 new file mode 100644 index 0000000000..15699cd1e4 --- /dev/null +++ b/test/link/macho/unwind_info/simple_string.cpp @@ -0,0 +1,30 @@ +#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 new file mode 100644 index 0000000000..c242af6ecc --- /dev/null +++ b/test/link/macho/unwind_info/simple_string_owner.cpp @@ -0,0 +1,12 @@ +#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"); +}