mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
test/link/macho: upgrade unwind info tests
This commit is contained in:
parent
d500caaa62
commit
a3f68c6fa2
@ -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"),
|
||||
},
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
const x: i32 = 42;
|
||||
export fn foo() i32 {
|
||||
return x;
|
||||
}
|
||||
comptime {
|
||||
@export(foo, .{ .name = "bar", .linkage = .Strong });
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
extern int foo();
|
||||
extern int bar();
|
||||
int main() {
|
||||
return bar() - foo();
|
||||
}
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user