Commit e0ccbff87d

Jakub Konka <kubkon@jakubkonka.com>
2023-01-20 20:57:40
link-tests: test unwind info emitter via c++ exceptions
1 parent 835a60a
test/link/macho/unwind_info/all.h
@@ -0,0 +1,41 @@
+#ifndef ALL
+#define ALL
+
+#include <cstddef>
+#include <string>
+#include <stdexcept>
+
+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
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;
+}
test/link/macho/unwind_info/main.cpp
@@ -0,0 +1,24 @@
+#include "all.h"
+#include <cstdio>
+
+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;
+}
test/link/macho/unwind_info/simple_string.cpp
@@ -0,0 +1,30 @@
+#include "all.h"
+#include <cstdio>
+#include <cstring>
+
+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;
+}
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");
+}
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,