Commit a3f68c6fa2

Jakub Konka <kubkon@jakubkonka.com>
2024-01-15 12:00:51
test/link/macho: upgrade unwind info tests
1 parent d500caa
test/link/macho/reexports/a.zig
@@ -1,7 +0,0 @@
-const x: i32 = 42;
-export fn foo() i32 {
-    return x;
-}
-comptime {
-    @export(foo, .{ .name = "bar", .linkage = .Strong });
-}
test/link/macho/reexports/build.zig
@@ -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);
-}
test/link/macho/reexports/main.c
@@ -1,5 +0,0 @@
-extern int foo();
-extern int bar();
-int main() {
-  return bar() - foo();
-}
test/link/macho/unwind_info/all.h
@@ -1,41 +0,0 @@
-#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
@@ -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;
-}
test/link/macho/unwind_info/main.cpp
@@ -1,24 +0,0 @@
-#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
@@ -1,30 +0,0 @@
-#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
@@ -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");
-}
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 <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
+        );
+    };
+
+    const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes = 
+    \\#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;
+    \\}
+    });
+    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 <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;
+    \\}
+    });
+    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 <stdio.h>
+    \\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 <stdio.h>
+    \\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);
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"),
-    },
 };