Commit 53f74d6a04

Andrew Kelley <andrew@ziglang.org>
2023-11-07 07:21:06
move libssp into libcompiler_rt
closes #7265
1 parent 2a81a0f
Changed files (6)
lib/ssp.zig → lib/compiler_rt/ssp.zig
@@ -13,43 +13,44 @@
 //! - __vsprintf_chk
 
 const std = @import("std");
+const common = @import("./common.zig");
+const builtin = @import("builtin");
 
 extern fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8;
 extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8;
 extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8;
 extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8;
 
-// Avoid dragging in the runtime safety mechanisms into this .o file.
-pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
-    _ = msg;
-    _ = error_return_trace;
-    @setCold(true);
-    std.os.abort();
+comptime {
+    @export(__stack_chk_fail, .{ .name = "__stack_chk_fail", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__chk_fail, .{ .name = "__chk_fail", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__strcpy_chk, .{ .name = "__strcpy_chk", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__strncpy_chk, .{ .name = "__strncpy_chk", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__strcat_chk, .{ .name = "__strcat_chk", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__strncat_chk, .{ .name = "__strncat_chk", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__memcpy_chk, .{ .name = "__memcpy_chk", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__memmove_chk, .{ .name = "__memmove_chk", .linkage = common.linkage, .visibility = common.visibility });
+    @export(__memset_chk, .{ .name = "__memset_chk", .linkage = common.linkage, .visibility = common.visibility });
 }
 
-export fn __stack_chk_fail() callconv(.C) noreturn {
+fn __stack_chk_fail() callconv(.C) noreturn {
     @panic("stack smashing detected");
 }
 
-export fn __chk_fail() callconv(.C) noreturn {
+fn __chk_fail() callconv(.C) noreturn {
     @panic("buffer overflow detected");
 }
 
-// Emitted when targeting some architectures (eg. x86)
-// XXX: This symbol should be hidden
-export fn __stack_chk_fail_local() callconv(.C) noreturn {
-    __stack_chk_fail();
-}
-
-// XXX: Initialize the canary with random data
-export var __stack_chk_guard: usize = blk: {
+// TODO: Initialize the canary with random data
+var __stack_chk_guard: usize = blk: {
     var buf = [1]u8{0} ** @sizeOf(usize);
     buf[@sizeOf(usize) - 1] = 255;
     buf[@sizeOf(usize) - 2] = '\n';
     break :blk @as(usize, @bitCast(buf));
 };
 
-export fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
+fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
     @setRuntimeSafety(false);
 
     var i: usize = 0;
@@ -64,12 +65,12 @@ export fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callcon
     return dest;
 }
 
-export fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
+fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
     if (dest_n < n) __chk_fail();
     return strncpy(dest, src, n);
 }
 
-export fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
+fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
     @setRuntimeSafety(false);
 
     var avail = dest_n;
@@ -94,7 +95,7 @@ export fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callcon
     return dest;
 }
 
-export fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
+fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
     @setRuntimeSafety(false);
 
     var avail = dest_n;
@@ -119,17 +120,17 @@ export fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usi
     return dest;
 }
 
-export fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
+fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
     if (dest_n < n) __chk_fail();
     return memcpy(dest, src, n);
 }
 
-export fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
+fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
     if (dest_n < n) __chk_fail();
     return memmove(dest, src, n);
 }
 
-export fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
+fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
     if (dest_n < n) __chk_fail();
     return memset(dest, c, n);
 }
lib/compiler_rt.zig
@@ -233,5 +233,6 @@ comptime {
         _ = @import("compiler_rt/memmove.zig");
         _ = @import("compiler_rt/memcmp.zig");
         _ = @import("compiler_rt/bcmp.zig");
+        _ = @import("compiler_rt/ssp.zig");
     }
 }
src/link/Coff/lld.zig
@@ -483,12 +483,6 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
                     try argv.append(lib.full_object_path);
                 }
             }
-            // MinGW doesn't provide libssp symbols
-            if (target.abi.isGnu()) {
-                if (comp.libssp_static_lib) |lib| {
-                    try argv.append(lib.full_object_path);
-                }
-            }
             // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
             // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
             if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path);
src/link/Elf.zig
@@ -1040,12 +1040,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         }
     }
 
-    // stack-protector.
-    // Related: https://github.com/ziglang/zig/issues/7265
-    if (comp.libssp_static_lib) |ssp| {
-        try positionals.append(.{ .path = ssp.full_object_path });
-    }
-
     for (positionals.items) |obj| {
         var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
         self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
@@ -1689,12 +1683,6 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
             }
         }
 
-        // stack-protector.
-        // Related: https://github.com/ziglang/zig/issues/7265
-        if (comp.libssp_static_lib) |ssp| {
-            try argv.append(ssp.full_object_path);
-        }
-
         // Shared libraries.
         // Worst-case, we need an --as-needed argument for every lib, as well
         // as one before and one after.
@@ -2729,12 +2717,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             }
         }
 
-        // stack-protector.
-        // Related: https://github.com/ziglang/zig/issues/7265
-        if (comp.libssp_static_lib) |ssp| {
-            try argv.append(ssp.full_object_path);
-        }
-
         // Shared libraries.
         if (is_exe_or_dyn_lib) {
             const system_libs = self.base.options.system_libs.keys();
src/Compilation.zig
@@ -152,9 +152,6 @@ libunwind_static_lib: ?CRTFile = null,
 /// Populated when we build the TSAN static library. A Job to build this is placed in the queue
 /// and resolved before calling linker.flush().
 tsan_static_lib: ?CRTFile = null,
-/// Populated when we build the libssp static library. A Job to build this is placed in the queue
-/// and resolved before calling linker.flush().
-libssp_static_lib: ?CRTFile = null,
 /// Populated when we build the libc static library. A Job to build this is placed in the queue
 /// and resolved before calling linker.flush().
 libc_static_lib: ?CRTFile = null,
@@ -286,7 +283,6 @@ const Job = union(enum) {
     libcxx: void,
     libcxxabi: void,
     libtsan: void,
-    libssp: void,
     /// needed when not linking libc and using LLVM for code generation because it generates
     /// calls to, for example, memcpy and memset.
     zig_libc: void,
@@ -683,7 +679,6 @@ pub const MiscTask = enum {
     libtsan,
     wasi_libc_crt_file,
     compiler_rt,
-    libssp,
     zig_libc,
     analyze_mod,
 
@@ -1353,10 +1348,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         if (stack_check and !target_util.supportsStackProbing(options.target))
             return error.StackCheckUnsupportedByTarget;
 
-        const capable_of_building_ssp = canBuildLibSsp(options.target, use_llvm);
-
-        const stack_protector: u32 = options.want_stack_protector orelse b: {
-            if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0);
+        const stack_protector: u32 = sp: {
+            const zig_backend = zigBackend(options.target, use_llvm);
+            if (!target_util.supportsStackProtector(options.target, zig_backend)) {
+                if (options.want_stack_protector) |x| {
+                    if (x > 0) return error.StackProtectorUnsupportedByTarget;
+                }
+                break :sp 0;
+            }
 
             // This logic is checking for linking libc because otherwise our start code
             // which is trying to set up TLS (i.e. the fs/gs registers) but the stack
@@ -1365,19 +1364,17 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             // as being exempt from stack protection checks, we could change this logic
             // to supporting stack protection even when not linking libc.
             // TODO file issue about this
-            if (!link_libc) break :b 0;
-            if (!capable_of_building_ssp) break :b 0;
-            if (is_safe_mode) break :b default_stack_protector_buffer_size;
-            break :b 0;
+            if (!link_libc) {
+                if (options.want_stack_protector) |x| {
+                    if (x > 0) return error.StackProtectorUnavailableWithoutLibC;
+                }
+                break :sp 0;
+            }
+
+            if (options.want_stack_protector) |x| break :sp x;
+            if (is_safe_mode) break :sp default_stack_protector_buffer_size;
+            break :sp 0;
         };
-        if (stack_protector != 0) {
-            if (!target_util.supportsStackProtector(options.target))
-                return error.StackProtectorUnsupportedByTarget;
-            if (!capable_of_building_ssp)
-                return error.StackProtectorUnsupportedByBackend;
-            if (!link_libc)
-                return error.StackProtectorUnavailableWithoutLibC;
-        }
 
         const include_compiler_rt = options.want_compiler_rt orelse
             (!options.skip_linker_dependencies and is_exe_or_dyn_lib);
@@ -2195,24 +2192,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
                 comp.job_queued_compiler_rt_obj = true;
             }
         }
-        if (needsCSymbols(
-            options.skip_linker_dependencies,
-            options.output_mode,
-            options.link_mode,
-            options.target,
-            comp.bin_file.options.use_llvm,
-        )) {
-            // Related: https://github.com/ziglang/zig/issues/7265.
-            if (comp.bin_file.options.stack_protector != 0 and
-                (!comp.bin_file.options.link_libc or
-                !target_util.libcProvidesStackProtector(target)))
-            {
-                try comp.work_queue.writeItem(.{ .libssp = {} });
-            }
 
-            if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) {
-                try comp.work_queue.writeItem(.{ .zig_libc = {} });
-            }
+        if (!comp.bin_file.options.skip_linker_dependencies and is_exe_or_dyn_lib and
+            !comp.bin_file.options.link_libc and capable_of_building_zig_libc)
+        {
+            try comp.work_queue.writeItem(.{ .zig_libc = {} });
         }
     }
 
@@ -2258,9 +2242,6 @@ pub fn destroy(self: *Compilation) void {
     if (self.compiler_rt_obj) |*crt_file| {
         crt_file.deinit(gpa);
     }
-    if (self.libssp_static_lib) |*crt_file| {
-        crt_file.deinit(gpa);
-    }
     if (self.libc_static_lib) |*crt_file| {
         crt_file.deinit(gpa);
     }
@@ -4027,26 +4008,6 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
                 );
             };
         },
-        .libssp => {
-            const named_frame = tracy.namedFrame("libssp");
-            defer named_frame.end();
-
-            comp.buildOutputFromZig(
-                "ssp.zig",
-                .Lib,
-                &comp.libssp_static_lib,
-                .libssp,
-                prog_node,
-            ) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.SubCompilationFailed => return, // error reported already
-                else => comp.lockAndSetMiscFailure(
-                    .libssp,
-                    "unable to build libssp: {s}",
-                    .{@errorName(err)},
-                ),
-            };
-        },
         .zig_libc => {
             const named_frame = tracy.namedFrame("zig_libc");
             defer named_frame.end();
@@ -6531,21 +6492,6 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool {
     };
 }
 
-fn canBuildLibSsp(target: std.Target, use_llvm: bool) bool {
-    switch (target.os.tag) {
-        .plan9 => return false,
-        else => {},
-    }
-    switch (target.cpu.arch) {
-        .spirv32, .spirv64 => return false,
-        else => {},
-    }
-    return switch (zigBackend(target, use_llvm)) {
-        .stage2_llvm => true,
-        else => build_options.have_llvm,
-    };
-}
-
 /// Not to be confused with canBuildLibC, which builds musl, glibc, and similar.
 /// This one builds lib/c.zig.
 fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
@@ -6585,29 +6531,6 @@ fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend {
     };
 }
 
-fn needsCSymbols(
-    skip_linker_dependencies: bool,
-    output_mode: std.builtin.OutputMode,
-    link_mode: ?std.builtin.LinkMode,
-    target: std.Target,
-    use_llvm: bool,
-) bool {
-    if (skip_linker_dependencies)
-        return false;
-
-    switch (output_mode) {
-        .Obj => return false,
-        .Lib => if (link_mode != .Dynamic) return false,
-        .Exe => {},
-    }
-
-    // LLVM might generate calls to libc symbols.
-    if (zigBackend(target, use_llvm) == .stage2_llvm)
-        return true;
-
-    return false;
-}
-
 pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 {
     const tracy_trace = trace(@src());
     defer tracy_trace.end();
src/target.zig
@@ -328,8 +328,19 @@ pub fn supportsStackProbing(target: std.Target) bool {
         (target.cpu.arch == .x86 or target.cpu.arch == .x86_64);
 }
 
-pub fn supportsStackProtector(target: std.Target) bool {
-    return !target.isSpirV();
+pub fn supportsStackProtector(target: std.Target, backend: std.builtin.CompilerBackend) bool {
+    switch (target.os.tag) {
+        .plan9 => return false,
+        else => {},
+    }
+    switch (target.cpu.arch) {
+        .spirv32, .spirv64 => return false,
+        else => {},
+    }
+    return switch (backend) {
+        .stage2_llvm => true,
+        else => false,
+    };
 }
 
 pub fn libcProvidesStackProtector(target: std.Target) bool {