@@ -173,6 +173,7 @@ astgen_wait_group: WaitGroup = .{},
173
173
/// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information.
174
174
export_symbol_names : std .ArrayListUnmanaged ([]const u8 ) = .{},
175
175
176
+ pub const default_stack_protector_buffer_size = 4 ;
176
177
pub const SemaError = Module .SemaError ;
177
178
178
179
pub const CRTFile = struct {
@@ -837,6 +838,10 @@ pub const InitOptions = struct {
837
838
want_pie : ? bool = null ,
838
839
want_sanitize_c : ? bool = null ,
839
840
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 ,
840
845
want_red_zone : ? bool = null ,
841
846
omit_frame_pointer : ? bool = null ,
842
847
want_valgrind : ? bool = null ,
@@ -1014,6 +1019,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1014
1019
return error .ExportTableAndImportTableConflict ;
1015
1020
}
1016
1021
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
+
1017
1031
const comp : * Compilation = comp : {
1018
1032
// For allocations that have the same lifetime as Compilation. This arena is used only during this
1019
1033
// initialization and then is freed in deinit().
@@ -1289,11 +1303,36 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1289
1303
1290
1304
const sanitize_c = options .want_sanitize_c orelse is_safe_mode ;
1291
1305
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 ;
1296
1309
};
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
+ }
1297
1336
1298
1337
const valgrind : bool = b : {
1299
1338
if (! target_util .hasValgrindSupport (options .target ))
@@ -1378,6 +1417,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1378
1417
cache .hash .add (unwind_tables );
1379
1418
cache .hash .add (tsan );
1380
1419
cache .hash .add (stack_check );
1420
+ cache .hash .add (stack_protector );
1381
1421
cache .hash .add (red_zone );
1382
1422
cache .hash .add (omit_frame_pointer );
1383
1423
cache .hash .add (link_mode );
@@ -1741,6 +1781,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1741
1781
.valgrind = valgrind ,
1742
1782
.tsan = tsan ,
1743
1783
.stack_check = stack_check ,
1784
+ .stack_protector = stack_protector ,
1744
1785
.red_zone = red_zone ,
1745
1786
.omit_frame_pointer = omit_frame_pointer ,
1746
1787
.single_threaded = single_threaded ,
@@ -1822,6 +1863,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1822
1863
};
1823
1864
errdefer comp .destroy ();
1824
1865
1866
+ const target = comp .getTarget ();
1867
+
1825
1868
// Add a `CObject` for each `c_source_files`.
1826
1869
try comp .c_object_table .ensureTotalCapacity (gpa , options .c_source_files .len );
1827
1870
for (options .c_source_files ) | c_source_file | {
@@ -1837,11 +1880,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1837
1880
1838
1881
const have_bin_emit = comp .bin_file .options .emit != null or comp .whole_bin_sub_path != null ;
1839
1882
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 ) {
1845
1886
.none ,
1846
1887
.simulator ,
1847
1888
.macabi ,
@@ -1852,9 +1893,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1852
1893
// If we need to build glibc for the target, add work items for it.
1853
1894
// We go through the work queue so that building can be done in parallel.
1854
1895
if (comp .wantBuildGLibCFromSource ()) {
1855
- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1896
+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
1856
1897
1857
- if (glibc .needsCrtiCrtn (comp . getTarget () )) {
1898
+ if (glibc .needsCrtiCrtn (target )) {
1858
1899
try comp .work_queue .write (&[_ ]Job {
1859
1900
.{ .glibc_crt_file = .crti_o },
1860
1901
.{ .glibc_crt_file = .crtn_o },
@@ -1867,10 +1908,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1867
1908
});
1868
1909
}
1869
1910
if (comp .wantBuildMuslFromSource ()) {
1870
- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1911
+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
1871
1912
1872
1913
try comp .work_queue .ensureUnusedCapacity (6 );
1873
- if (musl .needsCrtiCrtn (comp . getTarget () )) {
1914
+ if (musl .needsCrtiCrtn (target )) {
1874
1915
comp .work_queue .writeAssumeCapacity (&[_ ]Job {
1875
1916
.{ .musl_crt_file = .crti_o },
1876
1917
.{ .musl_crt_file = .crtn_o },
@@ -1887,7 +1928,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1887
1928
});
1888
1929
}
1889
1930
if (comp .wantBuildWasiLibcFromSource ()) {
1890
- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1931
+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
1891
1932
1892
1933
const wasi_emulated_libs = comp .bin_file .options .wasi_emulated_libs ;
1893
1934
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 {
1902
1943
});
1903
1944
}
1904
1945
if (comp .wantBuildMinGWFromSource ()) {
1905
- if (! target_util .canBuildLibC (comp . getTarget () )) return error .LibCUnavailable ;
1946
+ if (! target_util .canBuildLibC (target )) return error .LibCUnavailable ;
1906
1947
1907
1948
const static_lib_jobs = [_ ]Job {
1908
1949
.{ .mingw_crt_file = .mingw32_lib },
@@ -1921,7 +1962,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1921
1962
}
1922
1963
}
1923
1964
// Generate Windows import libs.
1924
- if (comp . getTarget () .os .tag == .windows ) {
1965
+ if (target .os .tag == .windows ) {
1925
1966
const count = comp .bin_file .options .system_libs .count ();
1926
1967
try comp .work_queue .ensureUnusedCapacity (count );
1927
1968
var i : usize = 0 ;
@@ -1940,15 +1981,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1940
1981
try comp .work_queue .writeItem (.libtsan );
1941
1982
}
1942
1983
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
-
1952
1984
if (comp .bin_file .options .include_compiler_rt and capable_of_building_compiler_rt ) {
1953
1985
if (is_exe_or_dyn_lib ) {
1954
1986
log .debug ("queuing a job to build compiler_rt_lib" , .{});
@@ -1962,8 +1994,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1962
1994
}
1963
1995
}
1964
1996
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
+ {
1967
2002
try comp .work_queue .writeItem (.{ .libssp = {} });
1968
2003
}
1969
2004
@@ -4123,6 +4158,17 @@ pub fn addCCArgs(
4123
4158
try argv .append ("-fno-omit-frame-pointer" );
4124
4159
}
4125
4160
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
+
4126
4172
switch (comp .bin_file .options .optimize_mode ) {
4127
4173
.Debug = > {
4128
4174
// windows c runtime requires -D_DEBUG if using debug libraries
@@ -4131,27 +4177,12 @@ pub fn addCCArgs(
4131
4177
// to -O1. Besides potentially impairing debugging, -O1/-Og significantly
4132
4178
// increases compile times.
4133
4179
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
- }
4142
4180
},
4143
4181
.ReleaseSafe = > {
4144
4182
// See the comment in the BuildModeFastRelease case for why we pass -O2 rather
4145
4183
// than -O3 here.
4146
4184
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" );
4155
4186
},
4156
4187
.ReleaseFast = > {
4157
4188
try argv .append ("-DNDEBUG" );
@@ -4161,12 +4192,10 @@ pub fn addCCArgs(
4161
4192
// Zig code than it is for C code. Also, C programmers are used to their code
4162
4193
// running in -O2 and thus the -O3 path has been tested less.
4163
4194
try argv .append ("-O2" );
4164
- try argv .append ("-fno-stack-protector" );
4165
4195
},
4166
4196
.ReleaseSmall = > {
4167
4197
try argv .append ("-DNDEBUG" );
4168
4198
try argv .append ("-Os" );
4169
- try argv .append ("-fno-stack-protector" );
4170
4199
},
4171
4200
}
4172
4201
@@ -5031,6 +5060,7 @@ fn buildOutputFromZig(
5031
5060
.use_stage1 = build_options .is_stage1 and comp .bin_file .options .use_stage1 ,
5032
5061
.want_sanitize_c = false ,
5033
5062
.want_stack_check = false ,
5063
+ .want_stack_protector = 0 ,
5034
5064
.want_red_zone = comp .bin_file .options .red_zone ,
5035
5065
.omit_frame_pointer = comp .bin_file .options .omit_frame_pointer ,
5036
5066
.want_valgrind = false ,
@@ -5311,6 +5341,7 @@ pub fn build_crt_file(
5311
5341
.optimize_mode = comp .compilerRtOptMode (),
5312
5342
.want_sanitize_c = false ,
5313
5343
.want_stack_check = false ,
5344
+ .want_stack_protector = 0 ,
5314
5345
.want_red_zone = comp .bin_file .options .red_zone ,
5315
5346
.omit_frame_pointer = comp .bin_file .options .omit_frame_pointer ,
5316
5347
.want_valgrind = false ,
0 commit comments