| ... | ... | @@ -38,6 +38,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { |
| 38 | 38 | macho_step.dependOn(testThunks(b, .{ .target = aarch64_target })); |
| 39 | 39 | macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); |
| 40 | 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 | 44 | macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target })); |
| 42 | 45 | |
| 43 | 46 | // Tests requiring symlinks when tested on Windows |
| ... | ... | @@ -1589,6 +1592,276 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step { |
| 1589 | 1592 | return test_step; |
| 1590 | 1593 | } |
| 1591 | 1594 | |
| 1595 | fn 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 | |
| 1753 | fn 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 | |
| 1813 | fn 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 | 1865 | // Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s |
| 1593 | 1866 | fn testWeakBind(b: *Build, opts: Options) *Step { |
| 1594 | 1867 | const test_step = addTestStep(b, "macho-weak-bind", opts); |