authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-02 16:48:18+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-16 21:22:14-04:00
logd44486b274a916823fcf6045a6400ef51e07d544
treec12a3a3923b644d219f815630941073d99d31889
parent2aff27d92247eed3b64ffc68da8fae9e5994ea0b

std: Add libssp implementation for GNU/Windows targets

Unlike glibc and musl, MinGW provides no libssp symbols leading to countless compile errors if FORTIFY_SOURCE is defined. Add a (incomplete) implementation of libssp written in Zig so that linking succeeds. Closes #6492

5 files changed, 276 insertions(+), 53 deletions(-)

lib/std/special/c.zig+113-33
......@@ -35,6 +35,10 @@ comptime {
3535 @export(strncmp, .{ .name = "strncmp", .linkage = .Strong });
3636 @export(strerror, .{ .name = "strerror", .linkage = .Strong });
3737 @export(strlen, .{ .name = "strlen", .linkage = .Strong });
38 @export(strcpy, .{ .name = "strcpy", .linkage = .Strong });
39 @export(strncpy, .{ .name = "strncpy", .linkage = .Strong });
40 @export(strcat, .{ .name = "strcat", .linkage = .Strong });
41 @export(strncat, .{ .name = "strncat", .linkage = .Strong });
3842 } else if (is_msvc) {
3943 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });
4044 }
......@@ -47,6 +51,90 @@ fn wasm_start() callconv(.C) void {
4751 _ = main(0, undefined);
4852}
4953
54fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
55 var i: usize = 0;
56 while (src[i] != 0) : (i += 1) {
57 dest[i] = src[i];
58 }
59 dest[i] = 0;
60
61 return dest;
62}
63
64test "strcpy" {
65 var s1: [9:0]u8 = undefined;
66
67 s1[0] = 0;
68 _ = strcpy(&s1, "foobarbaz");
69 std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1));
70}
71
72fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8 {
73 var i: usize = 0;
74 while (i < n and src[i] != 0) : (i += 1) {
75 dest[i] = src[i];
76 }
77 while (i < n) : (i += 1) {
78 dest[i] = 0;
79 }
80
81 return dest;
82}
83
84test "strncpy" {
85 var s1: [9:0]u8 = undefined;
86
87 s1[0] = 0;
88 _ = strncpy(&s1, "foobarbaz", 9);
89 std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1));
90}
91
92fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
93 var dest_end: usize = 0;
94 while (dest[dest_end] != 0) : (dest_end += 1) {}
95
96 var i: usize = 0;
97 while (src[i] != 0) : (i += 1) {
98 dest[dest_end + i] = src[i];
99 }
100 dest[dest_end + i] = 0;
101
102 return dest;
103}
104
105test "strcat" {
106 var s1: [9:0]u8 = undefined;
107
108 s1[0] = 0;
109 _ = strcat(&s1, "foo");
110 _ = strcat(&s1, "bar");
111 _ = strcat(&s1, "baz");
112 std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1));
113}
114
115fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.C) [*:0]u8 {
116 var dest_end: usize = 0;
117 while (dest[dest_end] != 0) : (dest_end += 1) {}
118
119 var i: usize = 0;
120 while (i < avail and src[i] != 0) : (i += 1) {
121 dest[dest_end + i] = src[i];
122 }
123 dest[dest_end + i] = 0;
124
125 return dest;
126}
127
128test "strncat" {
129 var s1: [9:0]u8 = undefined;
130
131 s1[0] = 0;
132 _ = strncat(&s1, "foo1111", 3);
133 _ = strncat(&s1, "bar1111", 3);
134 _ = strncat(&s1, "baz1111", 3);
135 std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1));
136}
137
50138fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int {
51139 return std.cstr.cmp(s1, s2);
52140}
......@@ -92,7 +180,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
92180 while (true) {}
93181}
94182
95export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {
183export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 {
96184 @setRuntimeSafety(false);
97185
98186 var index: usize = 0;
......@@ -102,7 +190,13 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {
102190 return dest;
103191}
104192
105export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8 {
193export fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
194 if (dest_n < n)
195 @panic("buffer overflow");
196 return memset(dest, c, n);
197}
198
199export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 {
106200 @setRuntimeSafety(false);
107201
108202 var index: usize = 0;
......@@ -112,7 +206,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]
112206 return dest;
113207}
114208
115export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {
209export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 {
116210 @setRuntimeSafety(false);
117211
118212 if (@ptrToInt(dest) < @ptrToInt(src)) {
......@@ -131,7 +225,7 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {
131225 return dest;
132226}
133227
134export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize {
228export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) isize {
135229 @setRuntimeSafety(false);
136230
137231 var index: usize = 0;
......@@ -146,17 +240,17 @@ export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize {
146240}
147241
148242test "test_memcmp" {
149 const base_arr = []u8{ 1, 1, 1 };
150 const arr1 = []u8{ 1, 1, 1 };
151 const arr2 = []u8{ 1, 0, 1 };
152 const arr3 = []u8{ 1, 2, 1 };
243 const base_arr = &[_]u8{ 1, 1, 1 };
244 const arr1 = &[_]u8{ 1, 1, 1 };
245 const arr2 = &[_]u8{ 1, 0, 1 };
246 const arr3 = &[_]u8{ 1, 2, 1 };
153247
154 std.testing.expect(memcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0);
155 std.testing.expect(memcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) > 0);
156 std.testing.expect(memcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) < 0);
248 std.testing.expect(memcmp(base_arr[0..], arr1[0..], base_arr.len) == 0);
249 std.testing.expect(memcmp(base_arr[0..], arr2[0..], base_arr.len) > 0);
250 std.testing.expect(memcmp(base_arr[0..], arr3[0..], base_arr.len) < 0);
157251}
158252
159export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) isize {
253export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.C) isize {
160254 @setRuntimeSafety(false);
161255
162256 var index: usize = 0;
......@@ -170,30 +264,21 @@ export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) i
170264}
171265
172266test "test_bcmp" {
173 const base_arr = []u8{ 1, 1, 1 };
174 const arr1 = []u8{ 1, 1, 1 };
175 const arr2 = []u8{ 1, 0, 1 };
176 const arr3 = []u8{ 1, 2, 1 };
267 const base_arr = &[_]u8{ 1, 1, 1 };
268 const arr1 = &[_]u8{ 1, 1, 1 };
269 const arr2 = &[_]u8{ 1, 0, 1 };
270 const arr3 = &[_]u8{ 1, 2, 1 };
177271
178 std.testing.expect(bcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0);
179 std.testing.expect(bcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) != 0);
180 std.testing.expect(bcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) != 0);
272 std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0);
273 std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0);
274 std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0);
181275}
182276
183277comptime {
184 if (builtin.mode != builtin.Mode.ReleaseFast and
185 builtin.mode != builtin.Mode.ReleaseSmall and
186 builtin.os.tag != .windows)
187 {
188 @export(__stack_chk_fail, .{ .name = "__stack_chk_fail" });
189 }
190278 if (builtin.os.tag == .linux) {
191279 @export(clone, .{ .name = "clone" });
192280 }
193281}
194fn __stack_chk_fail() callconv(.C) noreturn {
195 @panic("stack smashing detected");
196}
197282
198283// TODO we should be able to put this directly in std/linux/x86_64.zig but
199284// it causes a segfault in release mode. this is a workaround of calling it
......@@ -416,7 +501,6 @@ fn clone() callconv(.Naked) void {
416501 \\ # move syscall number into r0
417502 \\ li 0, 120
418503 \\ sc
419
420504 \\ # check for syscall error
421505 \\ bns+ 1f # jump to label 1 if no summary overflow.
422506 \\ #else
......@@ -424,10 +508,8 @@ fn clone() callconv(.Naked) void {
424508 \\1:
425509 \\ # compare sc result with 0
426510 \\ cmpwi cr7, 3, 0
427
428511 \\ # if not 0, jump to end
429512 \\ bne cr7, 2f
430
431513 \\ #else: we're the child
432514 \\ #call funcptr: move arg (d) into r3
433515 \\ mr 3, 31
......@@ -438,13 +520,11 @@ fn clone() callconv(.Naked) void {
438520 \\ # mov SYS_exit into r0 (the exit param is already in r3)
439521 \\ li 0, 1
440522 \\ sc
441
442523 \\2:
443524 \\ # restore stack
444525 \\ lwz 30, 0(1)
445526 \\ lwz 31, 4(1)
446527 \\ addi 1, 1, 16
447
448528 \\ blr
449529 );
450530 },
lib/std/special/compiler_rt.zig-19
......@@ -284,11 +284,6 @@ comptime {
284284 @export(@import("compiler_rt/stack_probe.zig").__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage });
285285 }
286286
287 if (is_mingw) {
288 @export(__stack_chk_fail, .{ .name = "__stack_chk_fail", .linkage = strong_linkage });
289 @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = strong_linkage });
290 }
291
292287 switch (builtin.arch) {
293288 .i386 => {
294289 @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage });
......@@ -311,9 +306,6 @@ comptime {
311306 else => {},
312307 }
313308 } else {
314 if (std.Target.current.isGnuLibC() and builtin.link_libc) {
315 @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = linkage });
316 }
317309 @export(@import("compiler_rt/divti3.zig").__divti3, .{ .name = "__divti3", .linkage = linkage });
318310 @export(@import("compiler_rt/modti3.zig").__modti3, .{ .name = "__modti3", .linkage = linkage });
319311 @export(@import("compiler_rt/multi3.zig").__multi3, .{ .name = "__multi3", .linkage = linkage });
......@@ -337,14 +329,3 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
337329 unreachable;
338330 }
339331}
340
341fn __stack_chk_fail() callconv(.C) noreturn {
342 @panic("stack smashing detected");
343}
344
345var __stack_chk_guard: usize = blk: {
346 var buf = [1]u8{0} ** @sizeOf(usize);
347 buf[@sizeOf(usize) - 1] = 255;
348 buf[@sizeOf(usize) - 2] = '\n';
349 break :blk @bitCast(usize, buf);
350};
lib/std/special/ssp.zig created+141
......@@ -0,0 +1,141 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6//
7// Small Zig reimplementation of gcc's libssp.
8//
9// This library implements most of the builtins required by the stack smashing
10// protection as implemented by gcc&clang.
11const std = @import("std");
12const builtin = std.builtin;
13
14// Missing exports:
15// - __gets_chk
16// - __mempcpy_chk
17// - __snprintf_chk
18// - __sprintf_chk
19// - __stpcpy_chk
20// - __vsnprintf_chk
21// - __vsprintf_chk
22
23extern fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8;
24extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8;
25extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8;
26extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8;
27
28// Avoid dragging in the runtime safety mechanisms into this .o file.
29pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
30 @setCold(true);
31 if (@hasDecl(std.os, "abort"))
32 std.os.abort();
33 while (true) {}
34}
35
36export fn __stack_chk_fail() callconv(.C) noreturn {
37 @panic("stack smashing detected");
38}
39
40export fn __chk_fail() callconv(.C) noreturn {
41 @panic("buffer overflow detected");
42}
43
44// Emitted when targeting some architectures (eg. i386)
45// XXX: This symbol should be hidden
46export fn __stack_chk_fail_local() callconv(.C) noreturn {
47 __stack_chk_fail();
48}
49
50// XXX: Initialize the canary with random data
51export var __stack_chk_guard: usize = blk: {
52 var buf = [1]u8{0} ** @sizeOf(usize);
53 buf[@sizeOf(usize) - 1] = 255;
54 buf[@sizeOf(usize) - 2] = '\n';
55 break :blk @bitCast(usize, buf);
56};
57
58export fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
59 @setRuntimeSafety(false);
60
61 var i: usize = 0;
62 while (i < dest_n and src[i] != 0) : (i += 1) {
63 dest[i] = src[i];
64 }
65
66 if (i == dest_n) __chk_fail();
67
68 dest[i] = 0;
69
70 return dest;
71}
72
73export fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
74 if (dest_n < n) __chk_fail();
75 return strncpy(dest, src, n);
76}
77
78export fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 {
79 @setRuntimeSafety(false);
80
81 var avail = dest_n;
82
83 var dest_end: usize = 0;
84 while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) {
85 avail -= 1;
86 }
87
88 if (avail < 1) __chk_fail();
89
90 var i: usize = 0;
91 while (avail > 0 and src[i] != 0) : (i += 1) {
92 dest[dest_end + i] = src[i];
93 avail -= 1;
94 }
95
96 if (avail < 1) __chk_fail();
97
98 dest[dest_end + i] = 0;
99
100 return dest;
101}
102
103export fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 {
104 @setRuntimeSafety(false);
105
106 var avail = dest_n;
107
108 var dest_end: usize = 0;
109 while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) {
110 avail -= 1;
111 }
112
113 if (avail < 1) __chk_fail();
114
115 var i: usize = 0;
116 while (avail > 0 and i < n and src[i] != 0) : (i += 1) {
117 dest[dest_end + i] = src[i];
118 avail -= 1;
119 }
120
121 if (avail < 1) __chk_fail();
122
123 dest[dest_end + i] = 0;
124
125 return dest;
126}
127
128export fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
129 if (dest_n < n) __chk_fail();
130 return memcpy(dest, src, n);
131}
132
133export fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
134 if (dest_n < n) __chk_fail();
135 return memmove(dest, src, n);
136}
137
138export fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 {
139 if (dest_n < n) __chk_fail();
140 return memset(dest, c, n);
141}
src/Compilation.zig+17
......@@ -84,6 +84,9 @@ libcxxabi_static_lib: ?CRTFile = null,
8484/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
8585/// and resolved before calling linker.flush().
8686libunwind_static_lib: ?CRTFile = null,
87/// Populated when we build the libssp static library. A Job to build this is placed in the queue
88/// and resolved before calling linker.flush().
89libssp_static_lib: ?CRTFile = null,
8790/// Populated when we build the libc static library. A Job to build this is placed in the queue
8891/// and resolved before calling linker.flush().
8992libc_static_lib: ?CRTFile = null,
......@@ -160,6 +163,7 @@ const Job = union(enum) {
160163 libunwind: void,
161164 libcxx: void,
162165 libcxxabi: void,
166 libssp: void,
163167 /// needed when producing a dynamic library or executable
164168 libcompiler_rt: void,
165169 /// needed when not linking libc and using LLVM for code generation because it generates
......@@ -927,6 +931,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
927931 (comp.getTarget().isWasm() and comp.bin_file.options.output_mode != .Obj);
928932 if (needs_compiler_rt_and_c and build_options.is_stage1) {
929933 try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
934 // MinGW provides no libssp, use our own implementation.
935 if (comp.getTarget().isMinGW()) {
936 try comp.work_queue.writeItem(.{ .libssp = {} });
937 }
930938 if (!comp.bin_file.options.link_libc) {
931939 try comp.work_queue.writeItem(.{ .zig_libc = {} });
932940 }
......@@ -978,6 +986,9 @@ pub fn destroy(self: *Compilation) void {
978986 if (self.compiler_rt_static_lib) |*crt_file| {
979987 crt_file.deinit(gpa);
980988 }
989 if (self.libssp_static_lib) |*crt_file| {
990 crt_file.deinit(gpa);
991 }
981992 if (self.libc_static_lib) |*crt_file| {
982993 crt_file.deinit(gpa);
983994 }
......@@ -1329,6 +1340,12 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
13291340 fatal("unable to build compiler_rt: {}", .{@errorName(err)});
13301341 };
13311342 },
1343 .libssp => {
1344 self.buildStaticLibFromZig("ssp.zig", &self.libssp_static_lib) catch |err| {
1345 // TODO Expose this as a normal compile error rather than crashing here.
1346 fatal("unable to build libssp: {}", .{@errorName(err)});
1347 };
1348 },
13321349 .zig_libc => {
13331350 self.buildStaticLibFromZig("c.zig", &self.libc_static_lib) catch |err| {
13341351 // TODO Expose this as a normal compile error rather than crashing here.
src/link/Coff.zig+5-1
......@@ -1117,11 +1117,15 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
11171117 try argv.append(comp.libunwind_static_lib.?.full_object_path);
11181118 }
11191119
1120 // compiler-rt and libc
1120 // compiler-rt, libc and libssp
11211121 if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
11221122 if (!self.base.options.link_libc) {
11231123 try argv.append(comp.libc_static_lib.?.full_object_path);
11241124 }
1125 // MinGW doesn't provide libssp symbols
1126 if (target.abi.isGnu()) {
1127 try argv.append(comp.libssp_static_lib.?.full_object_path);
1128 }
11251129 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
11261130 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
11271131 try argv.append(comp.compiler_rt_static_lib.?.full_object_path);