authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-15 12:00:51+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:40+01:00
loga3f68c6fa23f893597b7708e0c00e81f57cb35c1
treeefa9cc4c4f75ab76e976f131c5c1aaade866f8bd
parentd500caaa62aa666916f44dc07f2e618a2260f640

test/link/macho: upgrade unwind info tests


10 files changed, 273 insertions(+), 253 deletions(-)

test/link.zig-8
...@@ -115,16 +115,8 @@ pub const cases = [_]Case{...@@ -115,16 +115,8 @@ pub const cases = [_]Case{
115 .build_root = "test/link/macho/objcpp",115 .build_root = "test/link/macho/objcpp",
116 .import = @import("link/macho/objcpp/build.zig"),116 .import = @import("link/macho/objcpp/build.zig"),
117 },117 },
118 .{
119 .build_root = "test/link/macho/reexports",
120 .import = @import("link/macho/reexports/build.zig"),
121 },
122 .{118 .{
123 .build_root = "test/link/macho/tbdv3",119 .build_root = "test/link/macho/tbdv3",
124 .import = @import("link/macho/tbdv3/build.zig"),120 .import = @import("link/macho/tbdv3/build.zig"),
125 },121 },
126 .{
127 .build_root = "test/link/macho/unwind_info",
128 .import = @import("link/macho/unwind_info/build.zig"),
129 },
130};122};
test/link/macho.zig+273
...@@ -38,6 +38,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -38,6 +38,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
38 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));38 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));
39 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));39 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
40 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));40 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
41 macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
42 macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));
43 macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));
41 macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target }));44 macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target }));
4245
43 // Tests requiring symlinks when tested on Windows46 // Tests requiring symlinks when tested on Windows
...@@ -1589,6 +1592,276 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {...@@ -1589,6 +1592,276 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
1589 return test_step;1592 return test_step;
1590}1593}
15911594
1595fn testUnwindInfo(b: *Build, opts: Options) *Step {
1596 const test_step = addTestStep(b, "macho-unwind-info", opts);
1597
1598 const all_h = all_h: {
1599 const wf = WriteFile.create(b);
1600 break :all_h wf.add("all.h",
1601 \\#ifndef ALL
1602 \\#define ALL
1603 \\
1604 \\#include <cstddef>
1605 \\#include <string>
1606 \\#include <stdexcept>
1607 \\
1608 \\struct SimpleString {
1609 \\ SimpleString(size_t max_size);
1610 \\ ~SimpleString();
1611 \\
1612 \\ void print(const char* tag) const;
1613 \\ bool append_line(const char* x);
1614 \\
1615 \\private:
1616 \\ size_t max_size;
1617 \\ char* buffer;
1618 \\ size_t length;
1619 \\};
1620 \\
1621 \\struct SimpleStringOwner {
1622 \\ SimpleStringOwner(const char* x);
1623 \\ ~SimpleStringOwner();
1624 \\
1625 \\private:
1626 \\ SimpleString string;
1627 \\};
1628 \\
1629 \\class Error: public std::exception {
1630 \\public:
1631 \\ explicit Error(const char* msg) : msg{ msg } {}
1632 \\ virtual ~Error() noexcept {}
1633 \\ virtual const char* what() const noexcept {
1634 \\ return msg.c_str();
1635 \\ }
1636 \\
1637 \\protected:
1638 \\ std::string msg;
1639 \\};
1640 \\
1641 \\#endif
1642 );
1643 };
1644
1645 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
1646 \\#include "all.h"
1647 \\#include <cstdio>
1648 \\
1649 \\void fn_c() {
1650 \\ SimpleStringOwner c{ "cccccccccc" };
1651 \\}
1652 \\
1653 \\void fn_b() {
1654 \\ SimpleStringOwner b{ "b" };
1655 \\ fn_c();
1656 \\}
1657 \\
1658 \\int main() {
1659 \\ try {
1660 \\ SimpleStringOwner a{ "a" };
1661 \\ fn_b();
1662 \\ SimpleStringOwner d{ "d" };
1663 \\ } catch (const Error& e) {
1664 \\ printf("Error: %s\n", e.what());
1665 \\ } catch(const std::exception& e) {
1666 \\ printf("Exception: %s\n", e.what());
1667 \\ }
1668 \\ return 0;
1669 \\}
1670 });
1671 main_o.root_module.addIncludePath(all_h.dirname());
1672 main_o.linkLibCpp();
1673
1674 const simple_string_o = addObject(b, opts, .{ .name = "simple_string", .cpp_source_bytes =
1675 \\#include "all.h"
1676 \\#include <cstdio>
1677 \\#include <cstring>
1678 \\
1679 \\SimpleString::SimpleString(size_t max_size)
1680 \\: max_size{ max_size }, length{} {
1681 \\ if (max_size == 0) {
1682 \\ throw Error{ "Max size must be at least 1." };
1683 \\ }
1684 \\ buffer = new char[max_size];
1685 \\ buffer[0] = 0;
1686 \\}
1687 \\
1688 \\SimpleString::~SimpleString() {
1689 \\ delete[] buffer;
1690 \\}
1691 \\
1692 \\void SimpleString::print(const char* tag) const {
1693 \\ printf("%s: %s", tag, buffer);
1694 \\}
1695 \\
1696 \\bool SimpleString::append_line(const char* x) {
1697 \\ const auto x_len = strlen(x);
1698 \\ if (x_len + length + 2 > max_size) return false;
1699 \\ std::strncpy(buffer + length, x, max_size - length);
1700 \\ length += x_len;
1701 \\ buffer[length++] = '\n';
1702 \\ buffer[length] = 0;
1703 \\ return true;
1704 \\}
1705 });
1706 simple_string_o.root_module.addIncludePath(all_h.dirname());
1707 simple_string_o.linkLibCpp();
1708
1709 const simple_string_owner_o = addObject(b, opts, .{ .name = "simple_string_owner", .cpp_source_bytes =
1710 \\#include "all.h"
1711 \\
1712 \\SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } {
1713 \\ if (!string.append_line(x)) {
1714 \\ throw Error{ "Not enough memory!" };
1715 \\ }
1716 \\ string.print("Constructed");
1717 \\}
1718 \\
1719 \\SimpleStringOwner::~SimpleStringOwner() {
1720 \\ string.print("About to destroy");
1721 \\}
1722 });
1723 simple_string_owner_o.root_module.addIncludePath(all_h.dirname());
1724 simple_string_owner_o.linkLibCpp();
1725
1726 const exp_stdout =
1727 \\Constructed: a
1728 \\Constructed: b
1729 \\About to destroy: b
1730 \\About to destroy: a
1731 \\Error: Not enough memory!
1732 \\
1733 ;
1734
1735 const exe = addExecutable(b, opts, .{ .name = "main" });
1736 exe.addObject(main_o);
1737 exe.addObject(simple_string_o);
1738 exe.addObject(simple_string_owner_o);
1739 exe.linkLibCpp();
1740
1741 const run = addRunArtifact(exe);
1742 run.expectStdOutEqual(exp_stdout);
1743 test_step.dependOn(&run.step);
1744
1745 const check = exe.checkObject();
1746 check.checkInSymtab();
1747 check.checkContains("(was private external) ___gxx_personality_v0");
1748 test_step.dependOn(&check.step);
1749
1750 return test_step;
1751}
1752
1753fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step {
1754 const test_step = addTestStep(b, "macho-unwind-info-no-subsections-arm64", opts);
1755
1756 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1757 \\.globl _foo
1758 \\.align 4
1759 \\_foo:
1760 \\ .cfi_startproc
1761 \\ stp x29, x30, [sp, #-32]!
1762 \\ .cfi_def_cfa_offset 32
1763 \\ .cfi_offset w30, -24
1764 \\ .cfi_offset w29, -32
1765 \\ mov x29, sp
1766 \\ .cfi_def_cfa w29, 32
1767 \\ bl _bar
1768 \\ ldp x29, x30, [sp], #32
1769 \\ .cfi_restore w29
1770 \\ .cfi_restore w30
1771 \\ .cfi_def_cfa_offset 0
1772 \\ ret
1773 \\ .cfi_endproc
1774 \\
1775 \\.globl _bar
1776 \\.align 4
1777 \\_bar:
1778 \\ .cfi_startproc
1779 \\ sub sp, sp, #32
1780 \\ .cfi_def_cfa_offset -32
1781 \\ stp x29, x30, [sp, #16]
1782 \\ .cfi_offset w30, -24
1783 \\ .cfi_offset w29, -32
1784 \\ mov x29, sp
1785 \\ .cfi_def_cfa w29, 32
1786 \\ mov w0, #4
1787 \\ ldp x29, x30, [sp, #16]
1788 \\ .cfi_restore w29
1789 \\ .cfi_restore w30
1790 \\ add sp, sp, #32
1791 \\ .cfi_def_cfa_offset 0
1792 \\ ret
1793 \\ .cfi_endproc
1794 });
1795
1796 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
1797 \\#include <stdio.h>
1798 \\int foo();
1799 \\int main() {
1800 \\ printf("%d\n", foo());
1801 \\ return 0;
1802 \\}
1803 });
1804 exe.addObject(a_o);
1805
1806 const run = addRunArtifact(exe);
1807 run.expectStdOutEqual("4\n");
1808 test_step.dependOn(&run.step);
1809
1810 return test_step;
1811}
1812
1813fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step {
1814 const test_step = addTestStep(b, "macho-unwind-info-no-subsections-x64", opts);
1815
1816 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1817 \\.globl _foo
1818 \\_foo:
1819 \\ .cfi_startproc
1820 \\ push %rbp
1821 \\ .cfi_def_cfa_offset 8
1822 \\ .cfi_offset %rbp, -8
1823 \\ mov %rsp, %rbp
1824 \\ .cfi_def_cfa_register %rbp
1825 \\ call _bar
1826 \\ pop %rbp
1827 \\ .cfi_restore %rbp
1828 \\ .cfi_def_cfa_offset 0
1829 \\ ret
1830 \\ .cfi_endproc
1831 \\
1832 \\.globl _bar
1833 \\_bar:
1834 \\ .cfi_startproc
1835 \\ push %rbp
1836 \\ .cfi_def_cfa_offset 8
1837 \\ .cfi_offset %rbp, -8
1838 \\ mov %rsp, %rbp
1839 \\ .cfi_def_cfa_register %rbp
1840 \\ mov $4, %rax
1841 \\ pop %rbp
1842 \\ .cfi_restore %rbp
1843 \\ .cfi_def_cfa_offset 0
1844 \\ ret
1845 \\ .cfi_endproc
1846 });
1847
1848 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
1849 \\#include <stdio.h>
1850 \\int foo();
1851 \\int main() {
1852 \\ printf("%d\n", foo());
1853 \\ return 0;
1854 \\}
1855 });
1856 exe.addObject(a_o);
1857
1858 const run = addRunArtifact(exe);
1859 run.expectStdOutEqual("4\n");
1860 test_step.dependOn(&run.step);
1861
1862 return test_step;
1863}
1864
1592// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s1865// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s
1593fn testWeakBind(b: *Build, opts: Options) *Step {1866fn testWeakBind(b: *Build, opts: Options) *Step {
1594 const test_step = addTestStep(b, "macho-weak-bind", opts);1867 const test_step = addTestStep(b, "macho-weak-bind", opts);
test/link/macho/reexports/a.zig deleted-7
...@@ -1,7 +0,0 @@
1const x: i32 = 42;
2export fn foo() i32 {
3 return x;
4}
5comptime {
6 @export(foo, .{ .name = "bar", .linkage = .Strong });
7}
test/link/macho/reexports/build.zig deleted-38
...@@ -1,38 +0,0 @@
1const std = @import("std");
2
3pub const requires_symlinks = true;
4
5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target = b.resolveTargetQuery(.{ .os_tag = .macos });
17
18 const lib = b.addStaticLibrary(.{
19 .name = "a",
20 .root_source_file = .{ .path = "a.zig" },
21 .optimize = optimize,
22 .target = target,
23 });
24
25 const exe = b.addExecutable(.{
26 .name = "test",
27 .optimize = optimize,
28 .target = target,
29 });
30 exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
31 exe.linkLibrary(lib);
32 exe.linkLibC();
33
34 const run = b.addRunArtifact(exe);
35 run.skip_foreign_checks = true;
36 run.expectExitCode(0);
37 test_step.dependOn(&run.step);
38}
test/link/macho/reexports/main.c deleted-5
...@@ -1,5 +0,0 @@
1extern int foo();
2extern int bar();
3int main() {
4 return bar() - foo();
5}
test/link/macho/unwind_info/all.h deleted-41
...@@ -1,41 +0,0 @@
1#ifndef ALL
2#define ALL
3
4#include <cstddef>
5#include <string>
6#include <stdexcept>
7
8struct SimpleString {
9 SimpleString(size_t max_size);
10 ~SimpleString();
11
12 void print(const char* tag) const;
13 bool append_line(const char* x);
14
15private:
16 size_t max_size;
17 char* buffer;
18 size_t length;
19};
20
21struct SimpleStringOwner {
22 SimpleStringOwner(const char* x);
23 ~SimpleStringOwner();
24
25private:
26 SimpleString string;
27};
28
29class Error: public std::exception {
30public:
31 explicit Error(const char* msg) : msg{ msg } {}
32 virtual ~Error() noexcept {}
33 virtual const char* what() const noexcept {
34 return msg.c_str();
35 }
36
37protected:
38 std::string msg;
39};
40
41#endif
test/link/macho/unwind_info/build.zig deleted-88
...@@ -1,88 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4pub const requires_symlinks = true;
5
6pub fn build(b: *std.Build) void {
7 const test_step = b.step("test", "Test it");
8 b.default_step = test_step;
9
10 add(b, test_step, .Debug);
11 add(b, test_step, .ReleaseFast);
12 add(b, test_step, .ReleaseSmall);
13 add(b, test_step, .ReleaseSafe);
14}
15
16fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
17 const target = b.resolveTargetQuery(.{ .os_tag = .macos });
18
19 testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip");
20 testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip");
21}
22
23fn testUnwindInfo(
24 b: *std.Build,
25 test_step: *std.Build.Step,
26 optimize: std.builtin.OptimizeMode,
27 target: std.Build.ResolvedTarget,
28 dead_strip: bool,
29 name: []const u8,
30) void {
31 const exe = createScenario(b, optimize, target, name);
32 exe.link_gc_sections = dead_strip;
33
34 const check = exe.checkObject();
35 check.checkInHeaders();
36 check.checkExact("segname __TEXT");
37 check.checkExact("sectname __gcc_except_tab");
38 check.checkExact("sectname __unwind_info");
39
40 switch (builtin.cpu.arch) {
41 .aarch64 => {
42 check.checkExact("sectname __eh_frame");
43 },
44 .x86_64 => {}, // We do not expect `__eh_frame` section on x86_64 in this case
45 else => unreachable,
46 }
47
48 check.checkInSymtab();
49 check.checkContains("(was private external) ___gxx_personality_v0");
50 test_step.dependOn(&check.step);
51
52 const run = b.addRunArtifact(exe);
53 run.skip_foreign_checks = true;
54 run.expectStdOutEqual(
55 \\Constructed: a
56 \\Constructed: b
57 \\About to destroy: b
58 \\About to destroy: a
59 \\Error: Not enough memory!
60 \\
61 );
62
63 test_step.dependOn(&run.step);
64}
65
66fn createScenario(
67 b: *std.Build,
68 optimize: std.builtin.OptimizeMode,
69 target: std.Build.ResolvedTarget,
70 name: []const u8,
71) *std.Build.Step.Compile {
72 const exe = b.addExecutable(.{
73 .name = name,
74 .optimize = optimize,
75 .target = target,
76 });
77 b.default_step.dependOn(&exe.step);
78 exe.addIncludePath(.{ .path = "." });
79 exe.addCSourceFiles(.{
80 .files = &[_][]const u8{
81 "main.cpp",
82 "simple_string.cpp",
83 "simple_string_owner.cpp",
84 },
85 });
86 exe.linkLibCpp();
87 return exe;
88}
test/link/macho/unwind_info/main.cpp deleted-24
...@@ -1,24 +0,0 @@
1#include "all.h"
2#include <cstdio>
3
4void fn_c() {
5 SimpleStringOwner c{ "cccccccccc" };
6}
7
8void fn_b() {
9 SimpleStringOwner b{ "b" };
10 fn_c();
11}
12
13int main() {
14 try {
15 SimpleStringOwner a{ "a" };
16 fn_b();
17 SimpleStringOwner d{ "d" };
18 } catch (const Error& e) {
19 printf("Error: %s\n", e.what());
20 } catch(const std::exception& e) {
21 printf("Exception: %s\n", e.what());
22 }
23 return 0;
24}
test/link/macho/unwind_info/simple_string.cpp deleted-30
...@@ -1,30 +0,0 @@
1#include "all.h"
2#include <cstdio>
3#include <cstring>
4
5SimpleString::SimpleString(size_t max_size)
6: max_size{ max_size }, length{} {
7 if (max_size == 0) {
8 throw Error{ "Max size must be at least 1." };
9 }
10 buffer = new char[max_size];
11 buffer[0] = 0;
12}
13
14SimpleString::~SimpleString() {
15 delete[] buffer;
16}
17
18void SimpleString::print(const char* tag) const {
19 printf("%s: %s", tag, buffer);
20}
21
22bool SimpleString::append_line(const char* x) {
23 const auto x_len = strlen(x);
24 if (x_len + length + 2 > max_size) return false;
25 std::strncpy(buffer + length, x, max_size - length);
26 length += x_len;
27 buffer[length++] = '\n';
28 buffer[length] = 0;
29 return true;
30}
test/link/macho/unwind_info/simple_string_owner.cpp deleted-12
...@@ -1,12 +0,0 @@
1#include "all.h"
2
3SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } {
4 if (!string.append_line(x)) {
5 throw Error{ "Not enough memory!" };
6 }
7 string.print("Constructed");
8}
9
10SimpleStringOwner::~SimpleStringOwner() {
11 string.print("About to destroy");
12}