@@ -173,6 +173,7 @@ astgen_wait_group: WaitGroup = .{},
173173/// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information.
174174export_symbol_names : std .ArrayListUnmanaged ([]const u8 ) = .{},
175175
176+ pub const default_stack_protector_buffer_size = 4 ;
176177pub const SemaError = Module .SemaError ;
177178
178179pub const CRTFile = struct {
@@ -837,6 +838,10 @@ pub const InitOptions = struct {
837838 want_pie : ? bool = null ,
838839 want_sanitize_c : ? bool = null ,
839840 want_stack_check : ? bool = null ,
841+ /// null means default.
842+ /// 0 means no stack protector.
843+ /// other number means stack protection with that buffer size.
844+ want_stack_protector : ? u32 = null ,
840845 want_red_zone : ? bool = null ,
841846 omit_frame_pointer : ? bool = null ,
842847 want_valgrind : ? bool = null ,
@@ -1014,6 +1019,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
10141019 return error .ExportTableAndImportTableConflict ;
10151020 }
10161021
1022+ // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt.
1023+ // Once they are capable this condition could be removed. When removing this condition,
1024+ // also test the use case of `build-obj -fcompiler-rt` with the native backends
1025+ // and make sure the compiler-rt symbols are emitted.
1026+ const capable_of_building_compiler_rt = build_options .have_llvm ;
1027+
1028+ const capable_of_building_zig_libc = build_options .have_llvm ;
1029+ const capable_of_building_ssp = build_options .have_llvm ;
1030+
10171031 const comp : * Compilation = comp : {
10181032 // For allocations that have the same lifetime as Compilation. This arena is used only during this
10191033 // initialization and then is freed in deinit().
@@ -1289,11 +1303,36 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12891303
12901304 const sanitize_c = options .want_sanitize_c orelse is_safe_mode ;
12911305
1292- const stack_check : bool = b : {
1293- if (! target_util .supportsStackProbing (options .target ))
1294- break :b false ;
1295- break :b options .want_stack_check orelse is_safe_mode ;
1306+ const stack_check : bool = options .want_stack_check orelse b : {
1307+ if (! target_util .supportsStackProbing (options .target )) break :b false ;
1308+ break :b is_safe_mode ;
12961309 };
1310+ if (stack_check and ! target_util .supportsStackProbing (options .target ))
1311+ return error .StackCheckUnsupportedByTarget ;
1312+
1313+ const stack_protector : u32 = options .want_stack_protector orelse b : {
1314+ if (! target_util .supportsStackProtector (options .target )) break :b @as (u32 , 0 );
1315+
1316+ // This logic is checking for linking libc because otherwise our start code
1317+ // which is trying to set up TLS (i.e. the fs/gs registers) but the stack
1318+ // protection code depends on fs/gs registers being already set up.
1319+ // If we were able to annotate start code, or perhaps the entire std lib,
1320+ // as being exempt from stack protection checks, we could change this logic
1321+ // to supporting stack protection even when not linking libc.
1322+ // TODO file issue about this
1323+ if (! link_libc ) break :b 0 ;
1324+ if (! capable_of_building_ssp ) break :b 0 ;
1325+ if (is_safe_mode ) break :b default_stack_protector_buffer_size ;
1326+ break :b 0 ;
1327+ };
1328+ if (stack_protector != 0 ) {
1329+ if (! target_util .supportsStackProtector (options .target ))
1330+ return error .StackProtectorUnsupportedByTarget ;
1331+ if (! capable_of_building_ssp )
1332+ return error .StackProtectorUnsupportedByBackend ;
1333+ if (! link_libc )
1334+ return error .StackProtectorUnavailableWithoutLibC ;
1335+ }
12971336
12981337 const valgrind : bool = b : {
12991338 if (! target_util .hasValgrindSupport (options .target ))
@@ -1378,6 +1417,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
13781417 cache .hash .add (unwind_tables );
13791418 cache .hash .add (tsan );
13801419 cache .hash .add (stack_check );
1420+ cache .hash .add (stack_protector );
13811421 cache .hash .add (red_zone );
13821422 cache .hash .add (omit_frame_pointer );
13831423 cache .hash .add (link_mode );
@@ -1741,6 +1781,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17411781 .valgrind = valgrind ,
17421782 .tsan = tsan ,
17431783 .stack_check = stack_check ,
1784+ .stack_protector = stack_protector ,
17441785 .red_zone = red_zone ,
17451786 .omit_frame_pointer = omit_frame_pointer ,
17461787 .single_threaded = single_threaded ,
@@ -1822,6 +1863,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18221863 };
18231864 errdefer comp .destroy ();
18241865
1866+ const target = comp .getTarget ();
1867+
18251868 // Add a `CObject` for each `c_source_files`.
18261869 try comp .c_object_table .ensureTotalCapacity (gpa , options .c_source_files .len );
18271870 for (options .c_source_files ) | c_source_file | {
@@ -1837,11 +1880,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18371880
18381881 const have_bin_emit = comp .bin_file .options .emit != null or comp .whole_bin_sub_path != null ;
18391882
1840- if (have_bin_emit and ! comp .bin_file .options .skip_linker_dependencies and
1841- options .target .ofmt != .c )
1842- {
1843- if (comp .getTarget ().isDarwin ()) {
1844- switch (comp .getTarget ().abi ) {
1883+ if (have_bin_emit and ! comp .bin_file .options .skip_linker_dependencies and target .ofmt != .c ) {
1884+ if (target .isDarwin ()) {
1885+ switch (target .abi ) {
18451886 .none ,
18461887 .simulator ,
18471888 .macabi ,
@@ -1852,9 +1893,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18521893 // If we need to build glibc for the target, add work items for it.
18531894 // We go through the work queue so that building can be done in parallel.
18541895 if (comp .wantBuildGLibCFromSource ()) {
1855- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1896+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
18561897
1857- if (glibc .needsCrtiCrtn (comp . getTarget () )) {
1898+ if (glibc .needsCrtiCrtn (target )) {
18581899 try comp .work_queue .write (&[_ ]Job {
18591900 .{ .glibc_crt_file = .crti_o },
18601901 .{ .glibc_crt_file = .crtn_o },
@@ -1867,10 +1908,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18671908 });
18681909 }
18691910 if (comp .wantBuildMuslFromSource ()) {
1870- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1911+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
18711912
18721913 try comp .work_queue .ensureUnusedCapacity (6 );
1873- if (musl .needsCrtiCrtn (comp . getTarget () )) {
1914+ if (musl .needsCrtiCrtn (target )) {
18741915 comp .work_queue .writeAssumeCapacity (&[_ ]Job {
18751916 .{ .musl_crt_file = .crti_o },
18761917 .{ .musl_crt_file = .crtn_o },
@@ -1887,7 +1928,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18871928 });
18881929 }
18891930 if (comp .wantBuildWasiLibcFromSource ()) {
1890- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1931+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
18911932
18921933 const wasi_emulated_libs = comp .bin_file .options .wasi_emulated_libs ;
18931934 try comp .work_queue .ensureUnusedCapacity (wasi_emulated_libs .len + 2 ); // worst-case we need all components
@@ -1902,7 +1943,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
19021943 });
19031944 }
19041945 if (comp .wantBuildMinGWFromSource ()) {
1905- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1946+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
19061947
19071948 const static_lib_jobs = [_ ]Job {
19081949 .{ .mingw_crt_file = .mingw32_lib },
@@ -1921,7 +1962,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
19211962 }
19221963 }
19231964 // Generate Windows import libs.
1924- if (comp . getTarget () .os .tag == .windows ) {
1965+ if (target .os .tag == .windows ) {
19251966 const count = comp .bin_file .options .system_libs .count ();
19261967 try comp .work_queue .ensureUnusedCapacity (count );
19271968 var i : usize = 0 ;
@@ -1940,15 +1981,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
19401981 try comp .work_queue .writeItem (.libtsan );
19411982 }
19421983
1943- // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt.
1944- // Once they are capable this condition could be removed. When removing this condition,
1945- // also test the use case of `build-obj -fcompiler-rt` with the native backends
1946- // and make sure the compiler-rt symbols are emitted.
1947- const capable_of_building_compiler_rt = build_options .have_llvm ;
1948-
1949- const capable_of_building_zig_libc = build_options .have_llvm ;
1950- const capable_of_building_ssp = comp .bin_file .options .use_stage1 ;
1951-
19521984 if (comp .bin_file .options .include_compiler_rt and capable_of_building_compiler_rt ) {
19531985 if (is_exe_or_dyn_lib ) {
19541986 log .debug ("queuing a job to build compiler_rt_lib" , .{});
@@ -1962,8 +1994,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
19621994 }
19631995 }
19641996 if (needs_c_symbols ) {
1965- // MinGW provides no libssp, use our own implementation.
1966- if (comp .getTarget ().isMinGW () and capable_of_building_ssp ) {
1997+ // Related: https://github.com/ziglang/zig/issues/7265.
1998+ if (comp .bin_file .options .stack_protector != 0 and
1999+ (! comp .bin_file .options .link_libc or
2000+ ! target_util .libcProvidesStackProtector (target )))
2001+ {
19672002 try comp .work_queue .writeItem (.{ .libssp = {} });
19682003 }
19692004
@@ -4123,6 +4158,17 @@ pub fn addCCArgs(
41234158 try argv .append ("-fno-omit-frame-pointer" );
41244159 }
41254160
4161+ const ssp_buf_size = comp .bin_file .options .stack_protector ;
4162+ if (ssp_buf_size != 0 ) {
4163+ try argv .appendSlice (&[_ ][]const u8 {
4164+ "-fstack-protector-strong" ,
4165+ "--param" ,
4166+ try std .fmt .allocPrint (arena , "ssp-buffer-size={d}" , .{ssp_buf_size }),
4167+ });
4168+ } else {
4169+ try argv .append ("-fno-stack-protector" );
4170+ }
4171+
41264172 switch (comp .bin_file .options .optimize_mode ) {
41274173 .Debug = > {
41284174 // windows c runtime requires -D_DEBUG if using debug libraries
@@ -4131,27 +4177,12 @@ pub fn addCCArgs(
41314177 // to -O1. Besides potentially impairing debugging, -O1/-Og significantly
41324178 // increases compile times.
41334179 try argv .append ("-O0" );
4134-
4135- if (comp .bin_file .options .link_libc and target .os .tag != .wasi ) {
4136- try argv .append ("-fstack-protector-strong" );
4137- try argv .append ("--param" );
4138- try argv .append ("ssp-buffer-size=4" );
4139- } else {
4140- try argv .append ("-fno-stack-protector" );
4141- }
41424180 },
41434181 .ReleaseSafe = > {
41444182 // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
41454183 // than -O3 here.
41464184 try argv .append ("-O2" );
4147- if (comp .bin_file .options .link_libc and target .os .tag != .wasi ) {
4148- try argv .append ("-D_FORTIFY_SOURCE=2" );
4149- try argv .append ("-fstack-protector-strong" );
4150- try argv .append ("--param" );
4151- try argv .append ("ssp-buffer-size=4" );
4152- } else {
4153- try argv .append ("-fno-stack-protector" );
4154- }
4185+ try argv .append ("-D_FORTIFY_SOURCE=2" );
41554186 },
41564187 .ReleaseFast = > {
41574188 try argv .append ("-DNDEBUG" );
@@ -4161,12 +4192,10 @@ pub fn addCCArgs(
41614192 // Zig code than it is for C code. Also, C programmers are used to their code
41624193 // running in -O2 and thus the -O3 path has been tested less.
41634194 try argv .append ("-O2" );
4164- try argv .append ("-fno-stack-protector" );
41654195 },
41664196 .ReleaseSmall = > {
41674197 try argv .append ("-DNDEBUG" );
41684198 try argv .append ("-Os" );
4169- try argv .append ("-fno-stack-protector" );
41704199 },
41714200 }
41724201
@@ -5031,6 +5060,7 @@ fn buildOutputFromZig(
50315060 .use_stage1 = build_options .is_stage1 and comp .bin_file .options .use_stage1 ,
50325061 .want_sanitize_c = false ,
50335062 .want_stack_check = false ,
5063+ .want_stack_protector = 0 ,
50345064 .want_red_zone = comp .bin_file .options .red_zone ,
50355065 .omit_frame_pointer = comp .bin_file .options .omit_frame_pointer ,
50365066 .want_valgrind = false ,
@@ -5311,6 +5341,7 @@ pub fn build_crt_file(
53115341 .optimize_mode = comp .compilerRtOptMode (),
53125342 .want_sanitize_c = false ,
53135343 .want_stack_check = false ,
5344+ .want_stack_protector = 0 ,
53145345 .want_red_zone = comp .bin_file .options .red_zone ,
53155346 .omit_frame_pointer = comp .bin_file .options .omit_frame_pointer ,
53165347 .want_valgrind = false ,
0 commit comments