diff options
| author | Alex Rønne Petersen <alex@alexrp.com> | 2024-12-13 03:09:24 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-13 03:09:24 +0100 |
| commit | 130f7c2ed8e3358e24bb2fc7cca57f7a6f1f85c3 (patch) | |
| tree | ebb88312b13fb8b1e67b852bcb0e42903ad11193 /src | |
| parent | d48611ba67c7871cb348f28a01b89d8771170dd8 (diff) | |
| parent | c666ebb1f8aac440a4d0d554e9f8aa836bf58060 (diff) | |
| download | zig-130f7c2ed8e3358e24bb2fc7cca57f7a6f1f85c3.tar.gz zig-130f7c2ed8e3358e24bb2fc7cca57f7a6f1f85c3.zip | |
Merge pull request #22035 from alexrp/unwind-fixes
Better unwind table support + unwind protection in `_start()` and `clone()`
Diffstat (limited to 'src')
| -rw-r--r-- | src/Builtin.zig | 3 | ||||
| -rw-r--r-- | src/Compilation.zig | 27 | ||||
| -rw-r--r-- | src/Compilation/Config.zig | 24 | ||||
| -rw-r--r-- | src/Package/Module.zig | 7 | ||||
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 4 | ||||
| -rw-r--r-- | src/clang_options_data.zig | 18 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 7 | ||||
| -rw-r--r-- | src/libcxx.zig | 9 | ||||
| -rw-r--r-- | src/libunwind.zig | 3 | ||||
| -rw-r--r-- | src/main.zig | 54 | ||||
| -rw-r--r-- | src/mingw.zig | 17 | ||||
| -rw-r--r-- | src/target.zig | 13 |
12 files changed, 144 insertions, 42 deletions
diff --git a/src/Builtin.zig b/src/Builtin.zig index 8dc5b31c16..1782527f69 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 5f4404ff99..947012e716 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); @@ -5553,10 +5557,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 => {}, @@ -6288,6 +6299,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, }; @@ -6349,7 +6361,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 4458d474bb..baaa6c9f47 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 781a8e50c2..bfcef038ed 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/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 29a0a8b8b5..a836d02d71 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -8442,6 +8442,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); } diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index 9ced7e9ebe..88cf41d0d4 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 ff1e679898..b94ea07995 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3141,8 +3141,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 39fd999112..6386d493a6 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,12 @@ 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. + // The old 32-bit x86 variant of SEH doesn't use tables. + .unwind_tables = if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) + .none + else + .@"async", .pic = comp.root_mod.pic, }, .global = config, diff --git a/src/libunwind.zig b/src/libunwind.zig index 1e101da229..4b3583e1cd 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -65,7 +65,8 @@ 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, + // The old 32-bit x86 variant of SEH doesn't use tables. + .unwind_tables = if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"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 23c4d7507e..1f8bd8d065 100644 --- a/src/main.zig +++ b/src/main.zig @@ -510,6 +510,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 @@ -1385,9 +1386,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")) { @@ -1973,8 +1976,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; @@ -2788,8 +2810,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) @@ -5713,6 +5742,8 @@ pub const ClangArgIterator = struct { no_lto, unwind_tables, no_unwind_tables, + asynchronous_unwind_tables, + no_asynchronous_unwind_tables, nostdlib, nostdlib_cpp, shared, @@ -7435,8 +7466,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 cdee9acb07..8473a7a93b 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 a1c9a19724..37f2cd50da 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; @@ -408,8 +409,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( |
