From dcbb811d7219e2b4da2aaf06c8304a156c256497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 13 Nov 2024 11:15:56 +0100 Subject: [PATCH 1/6] riscv64: Support the fp alias for register s0 in inline assembly. --- src/arch/riscv64/CodeGen.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 24497defa2e7..26e7620ece07 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -8441,6 +8441,10 @@ fn failSymbol(func: *Func, comptime format: []const u8, args: anytype) InnerErro } fn parseRegName(name: []const u8) ?Register { + // The `fp` alias for `s0` is awkward to fit into the current `Register` scheme, so for now we + // special-case it here. + if (std.mem.eql(u8, name, "fp")) return .s0; + return std.meta.stringToEnum(Register, name); } From 9986e6b623d605f6dd057fd6741ec50c262f17fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 13 Nov 2024 06:04:04 +0100 Subject: [PATCH 2/6] compiler: Improve the handling of unwind table levels. The goal here is to support both levels of unwind tables (sync and async) in zig cc and zig build. Previously, the LLVM backend always used async tables while zig cc was partially influenced by whatever was Clang's default. --- lib/std/Build.zig | 10 ++--- lib/std/Build/Module.zig | 13 ++++-- lib/std/builtin.zig | 8 ++++ src/Builtin.zig | 3 ++ src/Compilation.zig | 27 +++++++++--- src/Compilation/Config.zig | 24 ++++++----- src/Package/Module.zig | 7 +-- src/clang_options_data.zig | 18 +++++++- src/codegen/llvm.zig | 7 ++- src/libcxx.zig | 5 +-- src/libunwind.zig | 2 +- src/main.zig | 54 ++++++++++++++++++++---- src/mingw.zig | 17 ++++++-- src/target.zig | 13 +++++- test/standalone/stack_iterator/build.zig | 6 +-- tools/update_clang_options.zig | 8 ++++ 16 files changed, 169 insertions(+), 53 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 041de3e06f1b..65e2db3d1d2b 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -706,7 +706,7 @@ pub const ExecutableOptions = struct { single_threaded: ?bool = null, pic: ?bool = null, strip: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, omit_frame_pointer: ?bool = null, sanitize_thread: ?bool = null, error_tracing: ?bool = null, @@ -762,7 +762,7 @@ pub const ObjectOptions = struct { single_threaded: ?bool = null, pic: ?bool = null, strip: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, omit_frame_pointer: ?bool = null, sanitize_thread: ?bool = null, error_tracing: ?bool = null, @@ -810,7 +810,7 @@ pub const SharedLibraryOptions = struct { single_threaded: ?bool = null, pic: ?bool = null, strip: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, omit_frame_pointer: ?bool = null, sanitize_thread: ?bool = null, error_tracing: ?bool = null, @@ -867,7 +867,7 @@ pub const StaticLibraryOptions = struct { single_threaded: ?bool = null, pic: ?bool = null, strip: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, omit_frame_pointer: ?bool = null, sanitize_thread: ?bool = null, error_tracing: ?bool = null, @@ -919,7 +919,7 @@ pub const TestOptions = struct { single_threaded: ?bool = null, pic: ?bool = null, strip: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, omit_frame_pointer: ?bool = null, sanitize_thread: ?bool = null, error_tracing: ?bool = null, diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 347695a4c038..0e421280f095 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -22,7 +22,7 @@ frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions), link_objects: std.ArrayListUnmanaged(LinkObject), strip: ?bool, -unwind_tables: ?bool, +unwind_tables: ?std.builtin.UnwindTables, single_threaded: ?bool, stack_protector: ?bool, stack_check: ?bool, @@ -218,7 +218,7 @@ pub const CreateOptions = struct { link_libcpp: ?bool = null, single_threaded: ?bool = null, strip: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, dwarf_format: ?std.dwarf.Format = null, code_model: std.builtin.CodeModel = .default, stack_protector: ?bool = null, @@ -675,7 +675,6 @@ pub fn appendZigProcessFlags( const b = m.owner; try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip"); - try addFlag(zig_args, m.unwind_tables, "-funwind-tables", "-fno-unwind-tables"); try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded"); try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check"); try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector"); @@ -695,6 +694,14 @@ pub fn appendZigProcessFlags( }); } + if (m.unwind_tables) |unwind_tables| { + try zig_args.append(switch (unwind_tables) { + .none => "-fno-unwind-tables", + .sync => "-funwind-tables", + .@"async" => "-fasync-unwind-tables", + }); + } + try zig_args.ensureUnusedCapacity(1); if (m.optimize) |optimize| switch (optimize) { .Debug => zig_args.appendAssumeCapacity("-ODebug"), diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index ed18965e50ef..1f132372c743 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -803,6 +803,14 @@ pub const LinkMode = enum { dynamic, }; +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const UnwindTables = enum { + none, + sync, + @"async", +}; + /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const WasiExecModel = enum { diff --git a/src/Builtin.zig b/src/Builtin.zig index f3018d30235f..64a09cc07b40 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -2,6 +2,7 @@ target: std.Target, zig_backend: std.builtin.CompilerBackend, output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, +unwind_tables: std.builtin.UnwindTables, is_test: bool, single_threaded: bool, link_libc: bool, @@ -40,6 +41,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { \\ \\pub const output_mode = std.builtin.OutputMode.{p_}; \\pub const link_mode = std.builtin.LinkMode.{p_}; + \\pub const unwind_tables = std.builtin.UnwindTables.{p_}; \\pub const is_test = {}; \\pub const single_threaded = {}; \\pub const abi = std.Target.Abi.{p_}; @@ -53,6 +55,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { std.zig.fmtId(@tagName(zig_backend)), std.zig.fmtId(@tagName(opts.output_mode)), std.zig.fmtId(@tagName(opts.link_mode)), + std.zig.fmtId(@tagName(opts.unwind_tables)), opts.is_test, opts.single_threaded, std.zig.fmtId(@tagName(target.abi)), diff --git a/src/Compilation.zig b/src/Compilation.zig index a228d612573f..5d9c7c828bab 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1261,12 +1261,15 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // The "any" values provided by resolved config only account for // explicitly-provided settings. We now make them additionally account // for default setting resolution. - const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables; + const any_unwind_tables = switch (options.config.any_unwind_tables) { + .none => options.root_mod.unwind_tables, + .sync, .@"async" => |uwt| uwt, + }; const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded; const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread; const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz; - const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables; + const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables != .none; const build_id = options.build_id orelse .none; const link_libc = options.config.link_libc; @@ -1354,6 +1357,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil cache.hash.add(options.config.pie); cache.hash.add(options.config.lto); cache.hash.add(options.config.link_mode); + cache.hash.add(options.config.any_unwind_tables); cache.hash.add(options.function_sections); cache.hash.add(options.data_sections); cache.hash.add(link_libc); @@ -5539,10 +5543,17 @@ pub fn addCCArgs( try argv.append("-Werror=date-time"); } - if (mod.unwind_tables) { - try argv.append("-funwind-tables"); - } else { - try argv.append("-fno-unwind-tables"); + switch (mod.unwind_tables) { + .none => { + try argv.append("-fno-unwind-tables"); + try argv.append("-fno-asynchronous-unwind-tables"); + }, + .sync => { + // Need to override Clang's convoluted default logic. + try argv.append("-fno-asynchronous-unwind-tables"); + try argv.append("-funwind-tables"); + }, + .@"async" => try argv.append("-fasynchronous-unwind-tables"), } }, .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig, .res, .manifest => {}, @@ -6269,6 +6280,7 @@ pub const CrtFileOptions = struct { function_sections: ?bool = null, data_sections: ?bool = null, omit_frame_pointer: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, pic: ?bool = null, no_builtin: ?bool = null, }; @@ -6330,7 +6342,8 @@ pub fn build_crt_file( // Some libcs (e.g. musl) are opinionated about -fomit-frame-pointer. .omit_frame_pointer = options.omit_frame_pointer orelse comp.root_mod.omit_frame_pointer, .valgrind = false, - .unwind_tables = false, + // Some libcs (e.g. MinGW) are opinionated about -funwind-tables. + .unwind_tables = options.unwind_tables orelse .none, // Some CRT objects (e.g. musl's rcrt1.o and Scrt1.o) are opinionated about PIC. .pic = options.pic orelse comp.root_mod.pic, .optimize_mode = comp.compilerRtOptMode(), diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 4458d474bbf0..baaa6c9f4741 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -12,13 +12,14 @@ link_libunwind: bool, /// True if and only if the c_source_files field will have nonzero length when /// calling Compilation.create. any_c_source_files: bool, -/// This is true if any Module has unwind_tables set explicitly to true. Until -/// Compilation.create is called, it is possible for this to be false while in -/// fact all Module instances have unwind_tables=true due to the default -/// being unwind_tables=true. After Compilation.create is called this will -/// also take into account the default setting, making this value true if and -/// only if any Module has unwind_tables set to true. -any_unwind_tables: bool, +/// This is not `.none` if any `Module` has `unwind_tables` set explicitly to a +/// value other than `.none`. Until `Compilation.create()` is called, it is +/// possible for this to be `.none` while in fact all `Module` instances have +/// `unwind_tables != .none` due to the default. After `Compilation.create()` is +/// called, this will also take into account the default setting, making this +/// value `.sync` or `.@"async"` if and only if any `Module` has +/// `unwind_tables != .none`. +any_unwind_tables: std.builtin.UnwindTables, /// This is true if any Module has single_threaded set explicitly to false. Until /// Compilation.create is called, it is possible for this to be false while in /// fact all Module instances have single_threaded=false due to the default @@ -85,7 +86,7 @@ pub const Options = struct { any_non_single_threaded: bool = false, any_sanitize_thread: bool = false, any_fuzz: bool = false, - any_unwind_tables: bool = false, + any_unwind_tables: std.builtin.UnwindTables = .none, any_dyn_libs: bool = false, any_c_source_files: bool = false, any_non_stripped: bool = false, @@ -356,8 +357,11 @@ pub fn resolve(options: Options) ResolveError!Config { break :b false; }; - const any_unwind_tables = options.any_unwind_tables or - link_libunwind or target_util.needUnwindTables(target); + const any_unwind_tables = b: { + if (options.any_unwind_tables != .none) break :b options.any_unwind_tables; + + break :b target_util.needUnwindTables(target, link_libunwind, options.any_sanitize_thread); + }; const link_mode = b: { const explicitly_exe_or_dyn_lib = switch (options.output_mode) { diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 996e58ddfb6c..c2de11f4798f 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -27,7 +27,7 @@ red_zone: bool, sanitize_c: bool, sanitize_thread: bool, fuzz: bool, -unwind_tables: bool, +unwind_tables: std.builtin.UnwindTables, cc_argv: []const []const u8, /// (SPIR-V) whether to generate a structured control flow graph or not structured_cfg: bool, @@ -91,7 +91,7 @@ pub const CreateOptions = struct { /// other number means stack protection with that buffer size. stack_protector: ?u32 = null, red_zone: ?bool = null, - unwind_tables: ?bool = null, + unwind_tables: ?std.builtin.UnwindTables = null, sanitize_c: ?bool = null, sanitize_thread: ?bool = null, fuzz: ?bool = null, @@ -112,7 +112,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread); if (options.inherited.fuzz == true) assert(options.global.any_fuzz); if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded); - if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables); + if (options.inherited.unwind_tables) |uwt| if (uwt != .none) assert(options.global.any_unwind_tables != .none); if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing); const resolved_target = options.inherited.resolved_target orelse options.parent.?.resolved_target; @@ -382,6 +382,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { .zig_backend = zig_backend, .output_mode = options.global.output_mode, .link_mode = options.global.link_mode, + .unwind_tables = options.global.any_unwind_tables, .is_test = options.global.is_test, .single_threaded = single_threaded, .link_libc = options.global.link_libc, diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index 9ced7e9ebe21..88cf41d0d4ff 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -2757,7 +2757,14 @@ flagpd1("fast"), flagpd1("fastcp"), flagpd1("fastf"), flagpd1("fasync-exceptions"), -flagpd1("fasynchronous-unwind-tables"), +.{ + .name = "fasynchronous-unwind-tables", + .syntax = .flag, + .zig_equivalent = .asynchronous_unwind_tables, + .pd1 = true, + .pd2 = false, + .psl = false, +}, flagpd1("fauto-import"), flagpd1("fauto-profile"), flagpd1("fauto-profile-accurate"), @@ -3247,7 +3254,14 @@ flagpd1("fno-assume-sane-operator-new"), flagpd1("fno-assume-unique-vtables"), flagpd1("fno-assumptions"), flagpd1("fno-async-exceptions"), -flagpd1("fno-asynchronous-unwind-tables"), +.{ + .name = "fno-asynchronous-unwind-tables", + .syntax = .flag, + .zig_equivalent = .no_asynchronous_unwind_tables, + .pd1 = true, + .pd2 = false, + .psl = false, +}, flagpd1("fno-auto-import"), flagpd1("fno-auto-profile"), flagpd1("fno-auto-profile-accurate"), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fb20d4d622df..fb78ffe2d736 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3237,8 +3237,11 @@ pub const Object = struct { } }, &o.builder); } try attributes.addFnAttr(.nounwind, &o.builder); - if (owner_mod.unwind_tables) { - try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder); + if (owner_mod.unwind_tables != .none) { + try attributes.addFnAttr( + .{ .uwtable = if (owner_mod.unwind_tables == .@"async") .@"async" else .sync }, + &o.builder, + ); } if (owner_mod.no_builtin) { // The intent here is for compiler-rt and libc functions to not generate diff --git a/src/libcxx.zig b/src/libcxx.zig index ec89ac1cfc07..7bea0c90ad93 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -397,7 +397,6 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr const optimize_mode = comp.compilerRtOptMode(); const strip = comp.compilerRtStrip(); - const unwind_tables = true; const config = Compilation.Config.resolve(.{ .output_mode = output_mode, @@ -409,7 +408,6 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .root_optimize_mode = optimize_mode, .root_strip = strip, .link_libc = true, - .any_unwind_tables = unwind_tables, .lto = comp.config.lto, .any_sanitize_thread = comp.config.any_sanitize_thread, }) catch |err| { @@ -440,7 +438,8 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .valgrind = false, .optimize_mode = optimize_mode, .structured_cfg = comp.root_mod.structured_cfg, - .unwind_tables = unwind_tables, + // See the `-fno-exceptions` logic for WASI. + .unwind_tables = if (target.os.tag == .wasi) .none else .@"async", .pic = comp.root_mod.pic, }, .global = config, diff --git a/src/libunwind.zig b/src/libunwind.zig index 1e101da229f0..cf66d8ec2dc8 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -65,7 +65,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .sanitize_c = false, .sanitize_thread = false, // necessary so that libunwind can unwind through its own stack frames - .unwind_tables = true, + .unwind_tables = .@"async", .pic = if (target_util.supports_fpic(target)) true else null, .optimize_mode = comp.compilerRtOptMode(), }, diff --git a/src/main.zig b/src/main.zig index 291820cb7523..b725afc52dda 100644 --- a/src/main.zig +++ b/src/main.zig @@ -509,6 +509,7 @@ const usage_build_generic = \\ -ffuzz Enable fuzz testing instrumentation \\ -fno-fuzz Disable fuzz testing instrumentation \\ -funwind-tables Always produce unwind table entries for all functions + \\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions \\ -fno-unwind-tables Never produce unwind table entries \\ -ferror-tracing Enable error tracing in ReleaseFast mode \\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode @@ -1384,9 +1385,11 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-fno-lto")) { create_module.opts.lto = false; } else if (mem.eql(u8, arg, "-funwind-tables")) { - mod_opts.unwind_tables = true; + mod_opts.unwind_tables = .sync; + } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) { + mod_opts.unwind_tables = .@"async"; } else if (mem.eql(u8, arg, "-fno-unwind-tables")) { - mod_opts.unwind_tables = false; + mod_opts.unwind_tables = .none; } else if (mem.eql(u8, arg, "-fstack-check")) { mod_opts.stack_check = true; } else if (mem.eql(u8, arg, "-fno-stack-check")) { @@ -1972,8 +1975,27 @@ fn buildOutputType( } }, .no_stack_protector => mod_opts.stack_protector = 0, - .unwind_tables => mod_opts.unwind_tables = true, - .no_unwind_tables => mod_opts.unwind_tables = false, + // The way these unwind table options are processed in GCC and Clang is crazy + // convoluted, and we also don't know the target triple here, so this is all + // best-effort. + .unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) { + .none => { + mod_opts.unwind_tables = .sync; + }, + .sync, .@"async" => {}, + } else { + mod_opts.unwind_tables = .sync; + }, + .no_unwind_tables => mod_opts.unwind_tables = .none, + .asynchronous_unwind_tables => mod_opts.unwind_tables = .@"async", + .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) { + .none, .sync => {}, + .@"async" => { + mod_opts.unwind_tables = .sync; + }, + } else { + mod_opts.unwind_tables = .sync; + }, .nostdlib => { create_module.opts.ensure_libc_on_non_freestanding = false; create_module.opts.ensure_libcpp_on_non_freestanding = false; @@ -2787,8 +2809,15 @@ fn buildOutputType( create_module.opts.any_sanitize_thread = true; if (mod_opts.fuzz == true) create_module.opts.any_fuzz = true; - if (mod_opts.unwind_tables == true) - create_module.opts.any_unwind_tables = true; + if (mod_opts.unwind_tables) |uwt| switch (uwt) { + .none => {}, + .sync => if (create_module.opts.any_unwind_tables == .none) { + create_module.opts.any_unwind_tables = .sync; + }, + .@"async" => { + create_module.opts.any_unwind_tables = .@"async"; + }, + }; if (mod_opts.strip == false) create_module.opts.any_non_stripped = true; if (mod_opts.error_tracing == true) @@ -5711,6 +5740,8 @@ pub const ClangArgIterator = struct { no_lto, unwind_tables, no_unwind_tables, + asynchronous_unwind_tables, + no_asynchronous_unwind_tables, nostdlib, nostdlib_cpp, shared, @@ -7420,8 +7451,15 @@ fn handleModArg( create_module.opts.any_sanitize_thread = true; if (mod_opts.fuzz == true) create_module.opts.any_fuzz = true; - if (mod_opts.unwind_tables == true) - create_module.opts.any_unwind_tables = true; + if (mod_opts.unwind_tables) |uwt| switch (uwt) { + .none => {}, + .sync => if (create_module.opts.any_unwind_tables == .none) { + create_module.opts.any_unwind_tables = .sync; + }, + .@"async" => { + create_module.opts.any_unwind_tables = .@"async"; + }, + }; if (mod_opts.strip == false) create_module.opts.any_non_stripped = true; if (mod_opts.error_tracing == true) diff --git a/src/mingw.zig b/src/mingw.zig index cdee9acb070b..8473a7a93b23 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -24,6 +24,10 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + const target = comp.getTarget(); + + // The old 32-bit x86 variant of SEH doesn't use tables. + const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch != .x86) .@"async" else .none; switch (crt_file) { .crt2_o => { @@ -41,7 +45,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre .owner = undefined, }, }; - return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &files, .{}); + return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &files, .{ + .unwind_tables = unwind_tables, + }); }, .dllcrt2_o => { @@ -56,7 +62,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre .owner = undefined, }, }; - return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &files, .{}); + return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &files, .{ + .unwind_tables = unwind_tables, + }); }, .mingw32_lib => { @@ -73,7 +81,6 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre .owner = undefined, }); } - const target = comp.getTarget(); if (target.cpu.arch == .x86 or target.cpu.arch == .x86_64) { for (mingw32_x86_src) |dep| { try c_source_files.append(.{ @@ -118,7 +125,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre } else { @panic("unsupported arch"); } - return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, c_source_files.items, .{}); + return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, c_source_files.items, .{ + .unwind_tables = unwind_tables, + }); }, } } diff --git a/src/target.zig b/src/target.zig index 6297d21bfc80..290d68c4fcac 100644 --- a/src/target.zig +++ b/src/target.zig @@ -4,6 +4,7 @@ const assert = std.debug.assert; const Type = @import("Type.zig"); const AddressSpace = std.builtin.AddressSpace; const Alignment = @import("InternPool.zig").Alignment; +const Compilation = @import("Compilation.zig"); const Feature = @import("Zcu.zig").Feature; pub const default_stack_protector_buffer_size = 4; @@ -396,8 +397,16 @@ pub fn clangSupportsNoImplicitFloatArg(target: std.Target) bool { }; } -pub fn needUnwindTables(target: std.Target) bool { - return target.os.tag == .windows or target.isDarwin() or std.debug.Dwarf.abi.supportsUnwinding(target); +pub fn needUnwindTables(target: std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables { + if (target.os.tag == .windows) { + // The old 32-bit x86 variant of SEH doesn't use tables. + return if (target.cpu.arch != .x86) .@"async" else .none; + } + if (target.os.tag.isDarwin()) return .@"async"; + if (libunwind) return .@"async"; + if (libtsan) return .@"async"; + if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .@"async"; + return .none; } pub fn defaultAddressSpace( diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index d69d59091238..e573a0396ba5 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -23,7 +23,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("unwind.zig"), .target = target, .optimize = optimize, - .unwind_tables = if (target.result.isDarwin()) true else null, + .unwind_tables = if (target.result.isDarwin()) .@"async" else null, .omit_frame_pointer = false, }); @@ -46,7 +46,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("unwind.zig"), .target = target, .optimize = optimize, - .unwind_tables = true, + .unwind_tables = .@"async", .omit_frame_pointer = true, }); @@ -85,7 +85,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("shared_lib_unwind.zig"), .target = target, .optimize = optimize, - .unwind_tables = if (target.result.isDarwin()) true else null, + .unwind_tables = if (target.result.isDarwin()) .@"async" else null, .omit_frame_pointer = true, }); diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 637c9a5eaff6..9cc0ad90cd71 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -110,6 +110,14 @@ const known_options = [_]KnownOpt{ .name = "fno-unwind-tables", .ident = "no_unwind_tables", }, + .{ + .name = "fasynchronous-unwind-tables", + .ident = "asynchronous_unwind_tables", + }, + .{ + .name = "fno-asynchronous-unwind-tables", + .ident = "no_asynchronous_unwind_tables", + }, .{ .name = "nolibc", .ident = "nostdlib", From 438801a90e19a53662257afa3cf32da6ed654482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 12 Nov 2024 18:06:42 +0100 Subject: [PATCH 3/6] start: Zero the link and frame pointer registers on all architectures. --- lib/std/start.zig | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 0ded8ea62543..39bea440394d 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -279,8 +279,10 @@ fn _start() callconv(.naked) noreturn { , .arm, .armeb, .thumb, .thumbeb => // Note that this code must work for Thumb-1. + // r7 = FP (local), r11 = FP (unwind) \\ movs v1, #0 - \\ mov fp, v1 + \\ mov r7, v1 + \\ mov r11, v1 \\ mov lr, v1 \\ mov a1, sp \\ subs v1, #16 @@ -290,20 +292,23 @@ fn _start() callconv(.naked) noreturn { , .csky => // The CSKY ABI assumes that `gb` is set to the address of the GOT in order for - // position-independent code to work. We depend on this in `std.os.linux.start_pie` - // to locate `_DYNAMIC` as well. + // position-independent code to work. We depend on this in `std.os.linux.pie` to locate + // `_DYNAMIC` as well. + // r8 = FP \\ grs t0, 1f \\ 1: \\ lrw gb, 1b@GOTPC \\ addu gb, t0 + \\ movi r8, 0 \\ movi lr, 0 \\ mov a0, sp \\ andi sp, sp, -8 \\ jmpi %[posixCallMainAndExit] , .hexagon => - // r29 = SP, r30 = FP + // r29 = SP, r30 = FP, r31 = LR \\ r30 = #0 + \\ r31 = #0 \\ r0 = r29 \\ r29 = and(r29, #-16) \\ memw(r29 + #-8) = r29 @@ -312,12 +317,13 @@ fn _start() callconv(.naked) noreturn { , .loongarch32, .loongarch64 => \\ move $fp, $zero + \\ move $ra, $zero \\ move $a0, $sp \\ bstrins.d $sp, $zero, 3, 0 \\ b %[posixCallMainAndExit] , .riscv32, .riscv64 => - \\ li s0, 0 + \\ li fp, 0 \\ li ra, 0 \\ mv a0, sp \\ andi sp, sp, -16 @@ -371,28 +377,35 @@ fn _start() callconv(.naked) noreturn { , .powerpc, .powerpcle => // Set up the initial stack frame, and clear the back chain pointer. + // r1 = SP, r31 = FP \\ mr 3, 1 \\ clrrwi 1, 1, 4 \\ li 0, 0 \\ stwu 1, -16(1) \\ stw 0, 0(1) + \\ li 31, 0 \\ mtlr 0 \\ b %[posixCallMainAndExit] , .powerpc64, .powerpc64le => // Set up the ToC and initial stack frame, and clear the back chain pointer. + // r1 = SP, r2 = ToC, r31 = FP \\ addis 2, 12, .TOC. - %[_start]@ha \\ addi 2, 2, .TOC. - %[_start]@l \\ mr 3, 1 \\ clrrdi 1, 1, 4 \\ li 0, 0 \\ stdu 0, -32(1) + \\ li 31, 0 \\ mtlr 0 \\ b %[posixCallMainAndExit] \\ nop , .s390x => // Set up the stack frame (register save area and cleared back-chain slot). + // r11 = FP, r14 = LR, r15 = SP + \\ lghi %%r11, 0 + \\ lghi %%r14, 0 \\ lgr %%r2, %%r15 \\ lghi %%r0, -16 \\ ngr %%r15, %%r0 @@ -403,7 +416,9 @@ fn _start() callconv(.naked) noreturn { , .sparc => // argc is stored after a register window (16 registers * 4 bytes). + // i7 = LR \\ mov %%g0, %%fp + \\ mov %%g0, %%i7 \\ add %%sp, 64, %%o0 \\ and %%sp, -8, %%sp \\ ba,a %[posixCallMainAndExit] @@ -411,7 +426,9 @@ fn _start() callconv(.naked) noreturn { .sparc64 => // argc is stored after a register window (16 registers * 8 bytes) plus the stack bias // (2047 bytes). + // i7 = LR \\ mov %%g0, %%fp + \\ mov %%g0, %%i7 \\ add %%sp, 2175, %%o0 \\ add %%sp, 2047, %%sp \\ and %%sp, -16, %%sp From 354919883827f7573ff8bb292150058ae6bdb807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 12 Nov 2024 17:39:59 +0100 Subject: [PATCH 4/6] start: Extend DWARF unwinding protection to more architectures. The function is not marked .cantunwind for Arm/Thumb because of an LLVM assembler bug: https://github.com/llvm/llvm-project/issues/115891 --- lib/std/start.zig | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 39bea440394d..9da0cb2ec61b 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -230,6 +230,29 @@ fn _start() callconv(.naked) noreturn { ); } + // This is the first userspace frame. Prevent DWARF-based unwinders from unwinding further. We + // prevent FP-based unwinders from unwinding further by zeroing the register further below. + asm volatile (switch (native_arch) { + .arc => ".cfi_undefined blink", + .arm, .armeb, .thumb, .thumbeb => "", // https://github.com/llvm/llvm-project/issues/115891 + .aarch64, .aarch64_be => ".cfi_undefined lr", + .csky => ".cfi_undefined lr", + .hexagon => ".cfi_undefined r31", + .loongarch32, .loongarch64 => ".cfi_undefined 1", + .m68k => ".cfi_undefined pc", + .mips, .mipsel, .mips64, .mips64el => ".cfi_undefined $ra", + .powerpc, .powerpcle, .powerpc64, .powerpc64le => ".cfi_undefined lr", + .riscv32, .riscv64 => if (builtin.zig_backend == .stage2_riscv64) + "" + else + ".cfi_undefined ra", + .s390x => ".cfi_undefined %%r14", + .sparc, .sparc64 => ".cfi_undefined %%i7", + .x86 => ".cfi_undefined %%eip", + .x86_64 => ".cfi_undefined %%rip", + else => @compileError("unsupported arch"), + }); + // Move this to the riscv prong below when this is resolved: https://github.com/ziglang/zig/issues/20918 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend != .stage2_riscv64) asm volatile ( \\ .weak __global_pointer$ @@ -247,7 +270,6 @@ fn _start() callconv(.naked) noreturn { // linker explicitly. asm volatile (switch (native_arch) { .x86_64 => - \\ .cfi_undefined %%rip \\ xorl %%ebp, %%ebp \\ movq %%rsp, %%rdi \\ andq $-16, %%rsp From 996f3a0fa1debc8242f22043b2834068f25d505f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 4 Nov 2024 18:30:36 +0100 Subject: [PATCH 5/6] std.os.linux: Add unwinding protection in clone() implementations. Whatever was in the frame pointer register prior to clone() will no longer be valid in the child process, so zero it to protect FP-based unwinders. Similarly, mark the link register as undefined to protect DWARF-based unwinders. This is only zeroing the frame pointer(s) on Arm/Thumb because of an LLVM assembler bug: https://github.com/llvm/llvm-project/issues/115891 --- lib/std/os/linux/aarch64.zig | 7 ++++++- lib/std/os/linux/arm.zig | 11 ++++++++--- lib/std/os/linux/hexagon.zig | 4 ++++ lib/std/os/linux/loongarch64.zig | 4 ++++ lib/std/os/linux/mips.zig | 4 ++++ lib/std/os/linux/mips64.zig | 4 ++++ lib/std/os/linux/powerpc.zig | 26 +++++++++++++++----------- lib/std/os/linux/powerpc64.zig | 7 ++++++- lib/std/os/linux/riscv32.zig | 6 +++++- lib/std/os/linux/riscv64.zig | 6 +++++- lib/std/os/linux/s390x.zig | 7 ++++++- lib/std/os/linux/sparc64.zig | 7 ++++++- lib/std/os/linux/x86.zig | 5 ++++- lib/std/os/linux/x86_64.zig | 2 ++ 14 files changed, 79 insertions(+), 21 deletions(-) diff --git a/lib/std/os/linux/aarch64.zig b/lib/std/os/linux/aarch64.zig index 9248360ae97d..db304a3a33b4 100644 --- a/lib/std/os/linux/aarch64.zig +++ b/lib/std/os/linux/aarch64.zig @@ -120,8 +120,13 @@ pub fn clone() callconv(.Naked) usize { \\ cbz x0,1f \\ // parent \\ ret + \\ \\ // child - \\1: ldp x1,x0,[sp],#16 + \\1: .cfi_undefined lr + \\ mov fp, 0 + \\ mov lr, 0 + \\ + \\ ldp x1,x0,[sp],#16 \\ blr x1 \\ mov x8,#93 // SYS_exit \\ svc #0 diff --git a/lib/std/os/linux/arm.zig b/lib/std/os/linux/arm.zig index 1d22b8c3dba7..e9263b5cea85 100644 --- a/lib/std/os/linux/arm.zig +++ b/lib/std/os/linux/arm.zig @@ -120,11 +120,16 @@ pub fn clone() callconv(.Naked) usize { \\ ldmfd sp!,{r4,r5,r6,r7} \\ bx lr \\ - \\1: mov r0,r6 + \\ // https://github.com/llvm/llvm-project/issues/115891 + \\1: mov r7, #0 + \\ mov r11, #0 + \\ mov lr, #0 + \\ + \\ mov r0,r6 \\ bl 3f - \\2: mov r7,#1 // SYS_exit + \\ mov r7,#1 // SYS_exit \\ svc 0 - \\ b 2b + \\ \\3: bx r5 ); } diff --git a/lib/std/os/linux/hexagon.zig b/lib/std/os/linux/hexagon.zig index e9bf9ab57c47..0839ea8297a0 100644 --- a/lib/std/os/linux/hexagon.zig +++ b/lib/std/os/linux/hexagon.zig @@ -118,6 +118,10 @@ pub fn clone() callconv(.Naked) usize { \\ p0 = cmp.eq(r0, #0) \\ if (!p0) dealloc_return \\ + \\ .cfi_undefined r31 + \\ r30 = #0 + \\ r31 = #0 + \\ \\ r0 = r10 \\ callr r11 \\ diff --git a/lib/std/os/linux/loongarch64.zig b/lib/std/os/linux/loongarch64.zig index def391549462..b52f1d10b137 100644 --- a/lib/std/os/linux/loongarch64.zig +++ b/lib/std/os/linux/loongarch64.zig @@ -121,6 +121,10 @@ pub fn clone() callconv(.Naked) usize { \\ beqz $a0, 1f # whether child process \\ jirl $zero, $ra, 0 # parent process return \\1: + \\ .cfi_undefined 1 + \\ move $fp, $zero + \\ move $ra, $zero + \\ \\ ld.d $t8, $sp, 0 # function pointer \\ ld.d $a0, $sp, 8 # argument pointer \\ jirl $ra, $t8, 0 # call the user's function diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index e6cb5f790025..5d786ccde7f6 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -231,6 +231,10 @@ pub fn clone() callconv(.Naked) usize { \\ jr $ra \\ nop \\1: + \\ .cfi_undefined $ra + \\ move $fp, $zero + \\ move $ra, $zero + \\ \\ lw $25, 0($sp) \\ lw $4, 4($sp) \\ jalr $25 diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig index 5e6661eae5cf..256c911bbe58 100644 --- a/lib/std/os/linux/mips64.zig +++ b/lib/std/os/linux/mips64.zig @@ -210,6 +210,10 @@ pub fn clone() callconv(.Naked) usize { \\ jr $ra \\ nop \\1: + \\ .cfi_undefined $ra + \\ move $fp, $zero + \\ move $ra, $zero + \\ \\ ld $25, 0($sp) \\ ld $4, 8($sp) \\ jalr $25 diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index 7b56b94823cd..db22789335c1 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -133,14 +133,14 @@ pub fn clone() callconv(.Naked) usize { // syscall(SYS_clone, flags, stack, ptid, tls, ctid) // 0 3, 4, 5, 6, 7 asm volatile ( - \\ # store non-volatile regs r30, r31 on stack in order to put our + \\ # store non-volatile regs r29, r30 on stack in order to put our \\ # start func and its arg there - \\ stwu 30, -16(1) - \\ stw 31, 4(1) + \\ stwu 29, -16(1) + \\ stw 30, 4(1) \\ - \\ # save r3 (func) into r30, and r6(arg) into r31 - \\ mr 30, 3 - \\ mr 31, 6 + \\ # save r3 (func) into r29, and r6(arg) into r30 + \\ mr 29, 3 + \\ mr 30, 6 \\ \\ # create initial stack frame for new thread \\ clrrwi 4, 4, 4 @@ -171,10 +171,14 @@ pub fn clone() callconv(.Naked) usize { \\ bne cr7, 2f \\ \\ #else: we're the child + \\ .cfi_undefined lr + \\ li 31, 0 + \\ mtlr 0 + \\ \\ #call funcptr: move arg (d) into r3 - \\ mr 3, 31 - \\ #move r30 (funcptr) into CTR reg - \\ mtctr 30 + \\ mr 3, 30 + \\ #move r29 (funcptr) into CTR reg + \\ mtctr 29 \\ # call CTR reg \\ bctrl \\ # mov SYS_exit into r0 (the exit param is already in r3) @@ -184,8 +188,8 @@ pub fn clone() callconv(.Naked) usize { \\ 2: \\ \\ # restore stack - \\ lwz 30, 0(1) - \\ lwz 31, 4(1) + \\ lwz 29, 0(1) + \\ lwz 30, 4(1) \\ addi 1, 1, 16 \\ \\ blr diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 06b67c17f163..6a1f98a65fba 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -160,7 +160,12 @@ pub fn clone() callconv(.Naked) usize { \\ cmpwi cr7, 3, 0 \\ bnelr cr7 \\ - \\ # we're the child. call fn(arg) + \\ # we're the child + \\ .cfi_undefined lr + \\ li 31, 0 + \\ mtlr 0 + \\ + \\ # call fn(arg) \\ ld 3, 16(1) \\ ld 12, 8(1) \\ mtctr 12 diff --git a/lib/std/os/linux/riscv32.zig b/lib/std/os/linux/riscv32.zig index 457d7e50b4b4..dc444a19560b 100644 --- a/lib/std/os/linux/riscv32.zig +++ b/lib/std/os/linux/riscv32.zig @@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize { \\ ret \\ \\ # Child - \\1: lw a1, 0(sp) + \\1: .cfi_undefined ra + \\ mv fp, zero + \\ mv ra, zero + \\ + \\ lw a1, 0(sp) \\ lw a0, 4(sp) \\ jalr a1 \\ diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index e33169c5b115..45ba568c1bf9 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize { \\ ret \\ \\ # Child - \\1: ld a1, 0(sp) + \\1: .cfi_undefined ra + \\ mv fp, zero + \\ mv ra, zero + \\ + \\ ld a1, 0(sp) \\ ld a0, 8(sp) \\ jalr a1 \\ diff --git a/lib/std/os/linux/s390x.zig b/lib/std/os/linux/s390x.zig index efc48af4ff39..8fbe1c930b3b 100644 --- a/lib/std/os/linux/s390x.zig +++ b/lib/std/os/linux/s390x.zig @@ -133,7 +133,12 @@ pub fn clone() callconv(.Naked) usize { \\ltgr %%r2, %%r2 \\bnzr %%r14 \\ - \\# we're the child. call fn(arg) + \\# we're the child + \\.cfi_undefined %%r14 + \\lghi %%r11, 0 + \\lghi %%r14, 0 + \\ + \\# call fn(arg) \\lg %%r1, 8(%%r15) \\lg %%r2, 16(%%r15) \\basr %%r14, %%r1 diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index c146ed17cf2a..efe272a16c0f 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -209,7 +209,12 @@ pub fn clone() callconv(.Naked) usize { \\ ret \\ restore \\1: - \\ # Child process, call func(arg) + \\ # Child process + \\ .cfi_undefined %%i7 + \\ mov %%g0, %%fp + \\ mov %%g0, %%i7 + \\ + \\ # call func(arg) \\ mov %%g0, %%fp \\ call %%g2 \\ mov %%g3, %%o0 diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig index abcfb99b3777..de1373408bfe 100644 --- a/lib/std/os/linux/x86.zig +++ b/lib/std/os/linux/x86.zig @@ -149,8 +149,11 @@ pub fn clone() callconv(.Naked) usize { \\ int $128 \\ testl %%eax,%%eax \\ jnz 1f - \\ popl %%eax + \\ + \\ .cfi_undefined %%eip \\ xorl %%ebp,%%ebp + \\ + \\ popl %%eax \\ calll *%%eax \\ movl %%eax,%%ebx \\ movl $1,%%eax // SYS_exit diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index cef15105eee7..44a37345f0f4 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -116,8 +116,10 @@ pub fn clone() callconv(.Naked) usize { \\ testq %%rax,%%rax \\ jz 1f \\ retq + \\ \\1: .cfi_undefined %%rip \\ xorl %%ebp,%%ebp + \\ \\ popq %%rdi \\ callq *%%r9 \\ movl %%eax,%%edi From 02f6f0e83bfa018eee86909e3a2961e7a2648884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 13 Nov 2024 09:18:37 +0100 Subject: [PATCH 6/6] musl: Add unwinding protection in clone() implementations. Whatever was in the frame pointer register prior to clone() will no longer be valid in the child process, so zero it to protect FP-based unwinders. This is just an extension of what was already done for i386 and x86_64. Only applied to architectures where the _start() code also zeroes the frame pointer. --- lib/libc/musl/src/thread/aarch64/clone.s | 3 ++- lib/libc/musl/src/thread/arm/clone.s | 3 ++- lib/libc/musl/src/thread/loongarch64/clone.s | 1 + lib/libc/musl/src/thread/m68k/clone.s | 3 ++- lib/libc/musl/src/thread/microblaze/clone.s | 3 ++- lib/libc/musl/src/thread/mips/clone.s | 3 ++- lib/libc/musl/src/thread/mips64/clone.s | 3 ++- lib/libc/musl/src/thread/mipsn32/clone.s | 3 ++- lib/libc/musl/src/thread/or1k/clone.s | 3 ++- 9 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/libc/musl/src/thread/aarch64/clone.s b/lib/libc/musl/src/thread/aarch64/clone.s index e3c83395cad2..9ac272bde3e6 100644 --- a/lib/libc/musl/src/thread/aarch64/clone.s +++ b/lib/libc/musl/src/thread/aarch64/clone.s @@ -24,7 +24,8 @@ __clone: // parent ret // child -1: ldp x1,x0,[sp],#16 +1: mov fp, 0 + ldp x1,x0,[sp],#16 blr x1 mov x8,#93 // SYS_exit svc #0 diff --git a/lib/libc/musl/src/thread/arm/clone.s b/lib/libc/musl/src/thread/arm/clone.s index bb0965dafe82..4ff0c0e88b1d 100644 --- a/lib/libc/musl/src/thread/arm/clone.s +++ b/lib/libc/musl/src/thread/arm/clone.s @@ -19,7 +19,8 @@ __clone: ldmfd sp!,{r4,r5,r6,r7} bx lr -1: mov r0,r6 +1: mov fp,#0 + mov r0,r6 bl 3f 2: mov r7,#1 svc 0 diff --git a/lib/libc/musl/src/thread/loongarch64/clone.s b/lib/libc/musl/src/thread/loongarch64/clone.s index a165b365a38e..cb4aacfc6d89 100644 --- a/lib/libc/musl/src/thread/loongarch64/clone.s +++ b/lib/libc/musl/src/thread/loongarch64/clone.s @@ -22,6 +22,7 @@ __clone: beqz $a0, 1f # whether child process jirl $zero, $ra, 0 # parent process return 1: + move $fp, $zero ld.d $t8, $sp, 0 # function pointer ld.d $a0, $sp, 8 # argument pointer jirl $ra, $t8, 0 # call the user's function diff --git a/lib/libc/musl/src/thread/m68k/clone.s b/lib/libc/musl/src/thread/m68k/clone.s index f6dfa06f49da..42ec19f7242a 100644 --- a/lib/libc/musl/src/thread/m68k/clone.s +++ b/lib/libc/musl/src/thread/m68k/clone.s @@ -18,7 +18,8 @@ __clone: beq 1f movem.l (%sp)+,%d2-%d5 rts -1: move.l %a1,-(%sp) +1: suba.l %%fp,%%fp + move.l %a1,-(%sp) jsr (%a0) move.l #1,%d0 trap #0 diff --git a/lib/libc/musl/src/thread/microblaze/clone.s b/lib/libc/musl/src/thread/microblaze/clone.s index b68cc5fc2213..64e3f0743f6c 100644 --- a/lib/libc/musl/src/thread/microblaze/clone.s +++ b/lib/libc/musl/src/thread/microblaze/clone.s @@ -22,7 +22,8 @@ __clone: rtsd r15, 8 nop -1: lwi r3, r1, 0 +1: add r19, r0, r0 + lwi r3, r1, 0 lwi r5, r1, 4 brald r15, r3 nop diff --git a/lib/libc/musl/src/thread/mips/clone.s b/lib/libc/musl/src/thread/mips/clone.s index 0446338568f0..229b987e9691 100644 --- a/lib/libc/musl/src/thread/mips/clone.s +++ b/lib/libc/musl/src/thread/mips/clone.s @@ -27,7 +27,8 @@ __clone: addu $sp, $sp, 16 jr $ra nop -1: lw $25, 0($sp) +1: move $fp, $0 + lw $25, 0($sp) lw $4, 4($sp) jalr $25 nop diff --git a/lib/libc/musl/src/thread/mips64/clone.s b/lib/libc/musl/src/thread/mips64/clone.s index 2d86899a1daa..8de3db6c4e36 100644 --- a/lib/libc/musl/src/thread/mips64/clone.s +++ b/lib/libc/musl/src/thread/mips64/clone.s @@ -25,7 +25,8 @@ __clone: nop jr $ra nop -1: ld $25, 0($sp) # function pointer +1: move $fp, $0 + ld $25, 0($sp) # function pointer ld $4, 8($sp) # argument pointer jalr $25 # call the user's function nop diff --git a/lib/libc/musl/src/thread/mipsn32/clone.s b/lib/libc/musl/src/thread/mipsn32/clone.s index 4d3c8c7a2502..9571231a62cf 100644 --- a/lib/libc/musl/src/thread/mipsn32/clone.s +++ b/lib/libc/musl/src/thread/mipsn32/clone.s @@ -25,7 +25,8 @@ __clone: nop jr $ra nop -1: lw $25, 0($sp) # function pointer +1: move $fp, $0 + lw $25, 0($sp) # function pointer lw $4, 4($sp) # argument pointer jalr $25 # call the user's function nop diff --git a/lib/libc/musl/src/thread/or1k/clone.s b/lib/libc/musl/src/thread/or1k/clone.s index 2473ac204036..05c55c6982dc 100644 --- a/lib/libc/musl/src/thread/or1k/clone.s +++ b/lib/libc/musl/src/thread/or1k/clone.s @@ -23,7 +23,8 @@ __clone: l.jr r9 l.nop -1: l.lwz r11, 0(r1) +1: l.ori r2, r0, 0 + l.lwz r11, 0(r1) l.jalr r11 l.lwz r3, 4(r1)