aboutsummaryrefslogtreecommitdiff
path: root/lib/std/special
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-12-12 18:27:17 -0500
committerAndrew Kelley <andrew@ziglang.org>2019-12-12 18:33:44 -0500
commitfff3c1fff4c3ebfcb2bd4f08a43ae7815b5c446b (patch)
treeea996d6252efe24158a802fb50c1a28c3b3d6717 /lib/std/special
parentb37acc4d6870a090c3501d81d3f647bc30220e4b (diff)
downloadzig-fff3c1fff4c3ebfcb2bd4f08a43ae7815b5c446b.tar.gz
zig-fff3c1fff4c3ebfcb2bd4f08a43ae7815b5c446b.zip
un-special-case startup code in the std lib
Previously, the compiler had special logic to determine whether to include the startup code, which was in `std/special/start.zig`. Now, the file is moved to `std/start.zig`, and there is no special logic in the compiler. Instead, the standard library unconditionally imports the `start.zig` file, which then has a `comptime` block that does the logic of determining what, if any, start symbols to export. Instead of `start.zig` being in its own special package, it is just another normal file that is part of the standard library. `std.builtin.TestFn` is now part of the standard library rather than specially generated by the compiler.
Diffstat (limited to 'lib/std/special')
-rw-r--r--lib/std/special/start.zig283
-rw-r--r--lib/std/special/start_windows_tls.zig48
-rw-r--r--lib/std/special/test_runner.zig6
3 files changed, 4 insertions, 333 deletions
diff --git a/lib/std/special/start.zig b/lib/std/special/start.zig
deleted file mode 100644
index ee53254098..0000000000
--- a/lib/std/special/start.zig
+++ /dev/null
@@ -1,283 +0,0 @@
-// This file is included in the compilation unit when exporting an executable.
-
-const root = @import("root");
-const std = @import("std");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const uefi = std.os.uefi;
-
-var starting_stack_ptr: [*]usize = undefined;
-
-const is_wasm = switch (builtin.arch) {
- .wasm32, .wasm64 => true,
- else => false,
-};
-
-const is_mips = switch (builtin.arch) {
- .mips, .mipsel, .mips64, .mips64el => true,
- else => false,
-};
-const start_sym_name = if (is_mips) "__start" else "_start";
-
-comptime {
- if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {
- if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
- @export("_DllMainCRTStartup", _DllMainCRTStartup, .Strong);
- }
- } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
- if (builtin.link_libc and @hasDecl(root, "main")) {
- if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
- @export("main", main, .Weak);
- }
- } else if (builtin.os == .windows) {
- if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) {
- @export("WinMainCRTStartup", WinMainCRTStartup, .Strong);
- }
- } else if (builtin.os == .uefi) {
- if (!@hasDecl(root, "EfiMain")) @export("EfiMain", EfiMain, .Strong);
- } else if (is_wasm and builtin.os == .freestanding) {
- if (!@hasDecl(root, start_sym_name)) @export(start_sym_name, wasm_freestanding_start, .Strong);
- } else if (builtin.os != .other and builtin.os != .freestanding) {
- if (!@hasDecl(root, start_sym_name)) @export(start_sym_name, _start, .Strong);
- }
- }
-}
-
-stdcallcc fn _DllMainCRTStartup(
- hinstDLL: std.os.windows.HINSTANCE,
- fdwReason: std.os.windows.DWORD,
- lpReserved: std.os.windows.LPVOID,
-) std.os.windows.BOOL {
- if (@hasDecl(root, "DllMain")) {
- return root.DllMain(hinstDLL, fdwReason, lpReserved);
- }
-
- return std.os.windows.TRUE;
-}
-
-extern fn wasm_freestanding_start() void {
- // This is marked inline because for some reason LLVM in release mode fails to inline it,
- // and we want fewer call frames in stack traces.
- _ = @call(.{ .modifier = .always_inline }, callMain, .{});
-}
-
-extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize {
- const bad_efi_main_ret = "expected return type of main to be 'void', 'noreturn', or 'usize'";
- uefi.handle = handle;
- uefi.system_table = system_table;
-
- switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
- .NoReturn => {
- root.main();
- },
- .Void => {
- root.main();
- return 0;
- },
- .Int => |info| {
- if (info.bits != @typeInfo(usize).Int.bits) {
- @compileError(bad_efi_main_ret);
- }
- return root.main();
- },
- else => @compileError(bad_efi_main_ret),
- }
-}
-
-nakedcc fn _start() noreturn {
- if (builtin.os == builtin.Os.wasi) {
- // This is marked inline because for some reason LLVM in release mode fails to inline it,
- // and we want fewer call frames in stack traces.
- std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
- }
-
- switch (builtin.arch) {
- .x86_64 => {
- starting_stack_ptr = asm (""
- : [argc] "={rsp}" (-> [*]usize)
- );
- },
- .i386 => {
- starting_stack_ptr = asm (""
- : [argc] "={esp}" (-> [*]usize)
- );
- },
- .aarch64, .aarch64_be, .arm => {
- starting_stack_ptr = asm ("mov %[argc], sp"
- : [argc] "=r" (-> [*]usize)
- );
- },
- .riscv64 => {
- starting_stack_ptr = asm ("mv %[argc], sp"
- : [argc] "=r" (-> [*]usize)
- );
- },
- .mipsel => {
- // Need noat here because LLVM is free to pick any register
- starting_stack_ptr = asm (
- \\ .set noat
- \\ move %[argc], $sp
- : [argc] "=r" (-> [*]usize)
- );
- },
- else => @compileError("unsupported arch"),
- }
- // If LLVM inlines stack variables into _start, they will overwrite
- // the command line argument data.
- @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
-}
-
-stdcallcc fn WinMainCRTStartup() noreturn {
- @setAlignStack(16);
- if (!builtin.single_threaded) {
- _ = @import("start_windows_tls.zig");
- }
-
- std.debug.maybeEnableSegfaultHandler();
-
- std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain());
-}
-
-// TODO https://github.com/ziglang/zig/issues/265
-fn posixCallMainAndExit() noreturn {
- if (builtin.os == builtin.Os.freebsd) {
- @setAlignStack(16);
- }
- const argc = starting_stack_ptr[0];
- const argv = @ptrCast([*][*:0]u8, starting_stack_ptr + 1);
-
- const envp_optional = @ptrCast([*:null]?[*:0]u8, argv + argc + 1);
- var envp_count: usize = 0;
- while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
- const envp = @ptrCast([*][*:0]u8, envp_optional)[0..envp_count];
-
- if (builtin.os == .linux) {
- // Find the beginning of the auxiliary vector
- const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
- std.os.linux.elf_aux_maybe = auxv;
- // Initialize the TLS area
- const gnu_stack_phdr = std.os.linux.tls.initTLS() orelse @panic("ELF missing stack size");
-
- if (std.os.linux.tls.tls_image) |tls_img| {
- const tls_addr = std.os.linux.tls.allocateTLS(tls_img.alloc_size);
- const tp = std.os.linux.tls.copyTLS(tls_addr);
- std.os.linux.tls.setThreadPointer(tp);
- }
-
- // TODO This is disabled because what should we do when linking libc and this code
- // does not execute? And also it's causing a test failure in stack traces in release modes.
-
- //// Linux ignores the stack size from the ELF file, and instead always does 8 MiB. A further
- //// problem is that it uses PROT_GROWSDOWN which prevents stores to addresses too far down
- //// the stack and requires "probing". So here we allocate our own stack.
- //const wanted_stack_size = gnu_stack_phdr.p_memsz;
- //assert(wanted_stack_size % std.mem.page_size == 0);
- //// Allocate an extra page as the guard page.
- //const total_size = wanted_stack_size + std.mem.page_size;
- //const new_stack = std.os.mmap(
- // null,
- // total_size,
- // std.os.PROT_READ | std.os.PROT_WRITE,
- // std.os.MAP_PRIVATE | std.os.MAP_ANONYMOUS,
- // -1,
- // 0,
- //) catch @panic("out of memory");
- //std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {};
- //std.os.exit(@call(.{.stack = new_stack}, callMainWithArgs, .{argc, argv, envp}));
- }
-
- std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
-}
-
-fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
- std.os.argv = argv[0..argc];
- std.os.environ = envp;
-
- std.debug.maybeEnableSegfaultHandler();
-
- return initEventLoopAndCallMain();
-}
-
-extern fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) i32 {
- var env_count: usize = 0;
- while (c_envp[env_count] != null) : (env_count += 1) {}
- const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];
- return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
-}
-
-// General error message for a malformed return type
-const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
-
-// This is marked inline because for some reason LLVM in release mode fails to inline it,
-// and we want fewer call frames in stack traces.
-inline fn initEventLoopAndCallMain() u8 {
- if (std.event.Loop.instance) |loop| {
- if (!@hasDecl(root, "event_loop")) {
- loop.init() catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- return 1;
- };
- defer loop.deinit();
-
- var result: u8 = undefined;
- var frame: @Frame(callMainAsync) = undefined;
- _ = @asyncCall(&frame, &result, callMainAsync, loop);
- loop.run();
- return result;
- }
- }
-
- // This is marked inline because for some reason LLVM in release mode fails to inline it,
- // and we want fewer call frames in stack traces.
- return @call(.{ .modifier = .always_inline }, callMain, .{});
-}
-
-async fn callMainAsync(loop: *std.event.Loop) u8 {
- // This prevents the event loop from terminating at least until main() has returned.
- loop.beginOneEvent();
- defer loop.finishOneEvent();
- return callMain();
-}
-
-// This is not marked inline because it is called with @asyncCall when
-// there is an event loop.
-pub fn callMain() u8 {
- switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
- .NoReturn => {
- root.main();
- },
- .Void => {
- root.main();
- return 0;
- },
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_main_ret);
- }
- return root.main();
- },
- .ErrorUnion => {
- const result = root.main() catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- return 1;
- };
- switch (@typeInfo(@TypeOf(result))) {
- .Void => return 0,
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_main_ret);
- }
- return result;
- },
- else => @compileError(bad_main_ret),
- }
- },
- else => @compileError(bad_main_ret),
- }
-}
diff --git a/lib/std/special/start_windows_tls.zig b/lib/std/special/start_windows_tls.zig
deleted file mode 100644
index bfd0e44122..0000000000
--- a/lib/std/special/start_windows_tls.zig
+++ /dev/null
@@ -1,48 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-
-export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
-export var _tls_start: u8 linksection(".tls") = 0;
-export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
-export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
-export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
-
-comptime {
- if (builtin.arch == .i386) {
- // The __tls_array is the offset of the ThreadLocalStoragePointer field
- // in the TEB block whose base address held in the %fs segment.
- asm (
- \\ .global __tls_array
- \\ __tls_array = 0x2C
- );
- }
-}
-
-// TODO this is how I would like it to be expressed
-// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
-// why they do that.
-//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
-// .StartAddressOfRawData = @ptrToInt(&_tls_start),
-// .EndAddressOfRawData = @ptrToInt(&_tls_end),
-// .AddressOfIndex = @ptrToInt(&_tls_index),
-// .AddressOfCallBacks = @ptrToInt(__xl_a),
-// .SizeOfZeroFill = 0,
-// .Characteristics = 0,
-//};
-// This is the workaround because we can't do @ptrToInt at comptime like that.
-pub const IMAGE_TLS_DIRECTORY = extern struct {
- StartAddressOfRawData: *c_void,
- EndAddressOfRawData: *c_void,
- AddressOfIndex: *c_void,
- AddressOfCallBacks: *c_void,
- SizeOfZeroFill: u32,
- Characteristics: u32,
-};
-export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{
- .StartAddressOfRawData = &_tls_start,
- .EndAddressOfRawData = &_tls_end,
- .AddressOfIndex = &_tls_index,
- .AddressOfCallBacks = &__xl_a,
- .SizeOfZeroFill = 0,
- .Characteristics = 0,
-};
diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig
index 7bb53774d3..0335c8562d 100644
--- a/lib/std/special/test_runner.zig
+++ b/lib/std/special/test_runner.zig
@@ -1,9 +1,9 @@
const std = @import("std");
const io = std.io;
const builtin = @import("builtin");
-const test_fn_list = builtin.test_functions;
pub fn main() anyerror!void {
+ const test_fn_list = builtin.test_functions;
var ok_count: usize = 0;
var skip_count: usize = 0;
var progress = std.Progress{};
@@ -16,7 +16,9 @@ pub fn main() anyerror!void {
var test_node = root_node.start(test_fn.name, null);
test_node.activate();
progress.refresh();
- if (progress.terminal == null) std.debug.warn("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
+ if (progress.terminal == null) {
+ std.debug.warn("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
+ }
if (test_fn.func()) |_| {
ok_count += 1;
test_node.end();