From a81fb5fb76131d0df3042f20e2d20a3b9216b254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 4 Jun 2025 03:44:55 +0200 Subject: [PATCH 1/4] compiler: Rework PIE option logic. To my knowledge, the only platforms that actually *require* PIE are Fuchsia and Android, and the latter *only* when building a dynamically-linked executable. OpenBSD and macOS both strongly encourage using PIE by default, but it isn't technically required. So for the latter platforms, we enable it by default but don't enforce it. Also, importantly, if we're building an object file or a static library, and the user hasn't explicitly told us whether to build PIE or non-PIE code (and the target doesn't require PIE), we should *not* default to PIE. Doing so produces code that cannot be linked into non-PIE output. In other words, building an object file or a static library as PIE is an optimization only to be done when the user knows that it'll end up in a PIE executable in the end. Closes #21837. --- src/Compilation/Config.zig | 19 +++++++++++++------ src/target.zig | 4 ---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 12515520865171f75c701bb13c5386e2ae4e6d6e..3415acf33ed546d76a4779bcb0c964e232b5484a 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -416,22 +416,29 @@ pub fn resolve(options: Options) ResolveError!Config { const pie: bool = b: { switch (options.output_mode) { - .Obj, .Exe => {}, + .Exe => if (target.os.tag == .fuchsia or + (target.abi.isAndroid() and link_mode == .dynamic)) + { + if (options.pie == false) return error.TargetRequiresPie; + break :b true; + }, .Lib => if (link_mode == .dynamic) { if (options.pie == true) return error.DynamicLibraryPrecludesPie; break :b false; }, - } - if (target_util.requiresPIE(target)) { - if (options.pie == false) return error.TargetRequiresPie; - break :b true; + .Obj => {}, } if (options.any_sanitize_thread) { if (options.pie == false) return error.SanitizeThreadRequiresPie; break :b true; } if (options.pie) |pie| break :b pie; - break :b false; + break :b if (options.output_mode == .Exe) switch (target.os.tag) { + .fuchsia, + .openbsd, + => true, + else => target.os.tag.isDarwin(), + } else false; }; const root_strip = b: { diff --git a/src/target.zig b/src/target.zig index 4b0cc20bdad086c6e2243a8edae109356ba60392..81a1a4901f1b03ce4ec98aea5fc7aeaa8d8279b1 100644 --- a/src/target.zig +++ b/src/target.zig @@ -43,10 +43,6 @@ pub fn libCxxNeedsLibUnwind(target: std.Target) bool { }; } -pub fn requiresPIE(target: std.Target) bool { - return target.abi.isAndroid() or target.os.tag.isDarwin() or target.os.tag == .openbsd; -} - /// This function returns whether non-pic code is completely invalid on the given target. pub fn requiresPIC(target: std.Target, linking_libc: bool) bool { return target.abi.isAndroid() or -- 2.54.0 From 0ccd2b0c5cdf4bee706b4a9341b97f786b19a978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 4 Jun 2025 06:53:14 +0200 Subject: [PATCH 2/4] compiler: Always dynamically link executables for Fuchsia. Fuchsia only supports PIE executables, specifically ET_DYN. https://fuchsia.dev/fuchsia-src/concepts/process/program_loading --- src/Compilation/Config.zig | 5 +++++ src/main.zig | 1 + 2 files changed, 6 insertions(+) diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 3415acf33ed546d76a4779bcb0c964e232b5484a..e2dea2b93fd56536acbbf244497a8260a0a0248d 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -135,6 +135,7 @@ pub const ResolveError = error{ LibCppRequiresLibC, LibUnwindRequiresLibC, TargetCannotDynamicLink, + TargetCannotStaticLinkExecutables, LibCRequiresDynamicLinking, SharedLibrariesRequireDynamicLinking, ExportMemoryAndDynamicIncompatible, @@ -360,6 +361,10 @@ pub fn resolve(options: Options) ResolveError!Config { if (options.link_mode == .dynamic) return error.TargetCannotDynamicLink; break :b .static; } + if (target.os.tag == .fuchsia and options.output_mode == .Exe) { + if (options.link_mode == .static) return error.TargetCannotStaticLinkExecutables; + break :b .dynamic; + } if (explicitly_exe_or_dyn_lib and link_libc and (target_util.osRequiresLibC(target) or // For these libcs, Zig can only provide dynamic libc when cross-compiling. diff --git a/src/main.zig b/src/main.zig index 73ba082e657080785841071689809f3ca5abb22d..ade23312b7cc162b8af9b61de6c3cc0e0dc69ec4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4118,6 +4118,7 @@ fn createModule( error.LibCppRequiresLibC => fatal("libc++ requires linking libc", .{}), error.LibUnwindRequiresLibC => fatal("libunwind requires linking libc", .{}), error.TargetCannotDynamicLink => fatal("dynamic linking unavailable on the specified target", .{}), + error.TargetCannotStaticLinkExecutables => fatal("static linking of executables unavailable on the specified target", .{}), error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}), error.SharedLibrariesRequireDynamicLinking => fatal("using shared libraries requires dynamic linking", .{}), error.ExportMemoryAndDynamicIncompatible => fatal("exporting memory is incompatible with dynamic linking", .{}), -- 2.54.0 From c6208369458208f097b8ce1ad1bb6c2bf40f211d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 4 Jun 2025 10:29:13 +0200 Subject: [PATCH 3/4] zig cc: Pass -f(no-)PIE to clang. Otherwise we rely on Clang's default which is known to not always match ours. --- src/Compilation.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 8df018f224508c19cb5b5496157cc13af5b3f8ce..790053ed4ce829ebe0cfce12839c564a25ee6f69 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6169,6 +6169,9 @@ pub fn addCCArgs( } if (target_util.supports_fpic(target)) { + // PIE needs to go before PIC because Clang interprets `-fno-PIE` to imply `-fno-PIC`, which + // we don't necessarily want. + try argv.append(if (comp.config.pie) "-fPIE" else "-fno-PIE"); try argv.append(if (mod.pic) "-fPIC" else "-fno-PIC"); } -- 2.54.0 From c8b92f3a8ed740148c225608b6504fb8461249bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 4 Jun 2025 10:35:04 +0200 Subject: [PATCH 4/4] zig cc: Pass -f(no-)(PIC,PIE) to Clang for *-(windows,uefi)-(gnu,cygnus). The previous supports_fpic() check was too broad. --- src/target.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/target.zig b/src/target.zig index 81a1a4901f1b03ce4ec98aea5fc7aeaa8d8279b1..f66fa1679a0a099180b26e1f570750608e17b450 100644 --- a/src/target.zig +++ b/src/target.zig @@ -60,7 +60,12 @@ pub fn picLevel(target: std.Target) u32 { /// This is not whether the target supports Position Independent Code, but whether the -fPIC /// C compiler argument is valid to Clang. pub fn supports_fpic(target: std.Target) bool { - return target.os.tag != .windows and target.os.tag != .uefi; + return switch (target.os.tag) { + .windows, + .uefi, + => target.abi == .gnu or target.abi == .cygnus, + else => true, + }; } pub fn alwaysSingleThreaded(target: std.Target) bool { -- 2.54.0