diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f200efe..4f2fe9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,3 +30,12 @@ jobs: # - name: Build and test # run: | # ./project.sh test all + windows_build_and_test: + runs-on: windows-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build and test + run: | + ./project.bat test all diff --git a/bootstrap/build.c b/bootstrap/build.c index 15ecee7..50155cb 100644 --- a/bootstrap/build.c +++ b/bootstrap/build.c @@ -50,19 +50,21 @@ fn u8 is_debug(OptimizationMode optimization_mode, u8 debug_info) return (optimization_mode == O0) & (debug_info != 0); } -fn void compile_c(const CompileOptions *const options, char** envp) +fn void compile_c(Arena* arena, CompileOptions options, char** envp) { VirtualBufferP(char) argument_stack = {}; auto* args = &argument_stack; char* compiler; - switch (options->compiler) + switch (options.compiler) { case gcc: compiler = "/usr/bin/gcc"; break; case clang: -#ifdef __APPLE__ +#if _WIN32 + compiler = "clang"; +#elif defined( __APPLE__) compiler = "/opt/homebrew/opt/llvm/bin/clang"; #else compiler = "/usr/bin/clang"; @@ -74,16 +76,16 @@ fn void compile_c(const CompileOptions *const options, char** envp) *vb_add(args, 1) = compiler; // *vb_add(args, 1) = "-E"; - *vb_add(args, 1) = options->in_path; + *vb_add(args, 1) = options.in_path; *vb_add(args, 1) = "-o"; - *vb_add(args, 1) = options->out_path; + *vb_add(args, 1) = options.out_path; - if (options->debug_info) + if (options.debug_info) { *vb_add(args, 1) = "-g3"; } - switch (options->optimization_mode) + switch (options.optimization_mode) { case O0: *vb_add(args, 1) = "-O0"; @@ -109,7 +111,7 @@ fn void compile_c(const CompileOptions *const options, char** envp) *vb_add(args, 1) = "-march=native"; - if (options->error_on_warning) + if (options.error_on_warning) { *vb_add(args, 1) = "-Werror"; } @@ -134,12 +136,12 @@ fn void compile_c(const CompileOptions *const options, char** envp) }; memcpy(vb_add(args, array_length(general_options)), general_options, sizeof(general_options)); - if (!is_debug(options->optimization_mode, options->debug_info)) + if (!is_debug(options.optimization_mode, options.debug_info)) { *vb_add(args, 1) = "-DNDEBUG=1"; } - if (options->linkage == LINKAGE_STATIC) + if (options.linkage == LINKAGE_STATIC) { char* static_options[] = { "-ffreestanding", "-nostdlib", "-static", "-DSTATIC", "-lgcc" }; memcpy(vb_add(args, array_length(static_options)), static_options, sizeof(static_options)); @@ -147,7 +149,7 @@ fn void compile_c(const CompileOptions *const options, char** envp) // *vb_add(args, 1) = "-DSILENT"; - if (options->compiler == clang) + if (options.compiler == clang) { *vb_add(args, 1) = "-MJ"; *vb_add(args, 1) = build_dir "/" "compile_commands.json"; @@ -155,7 +157,7 @@ fn void compile_c(const CompileOptions *const options, char** envp) *vb_add(args, 1) = 0; - run_command((CStringSlice) { .pointer = args->pointer, .length = args->length }, envp); + run_command(arena, (CStringSlice) { .pointer = args->pointer, .length = args->length }, envp); } typedef enum CompilerBackend @@ -167,9 +169,9 @@ typedef enum CompilerBackend } CompilerBackend; declare_slice(CompilerBackend); -fn void compile_and_run(const CompileOptions* const options, char** envp, CompilerBackend compiler_backend, u8 debug, char* nest_source_path) +fn void compile_and_run(Arena* arena, CompileOptions options, char** envp, CompilerBackend compiler_backend, u8 debug, char* nest_source_path) { - compile_c(options, envp); + compile_c(arena, options, envp); CStringSlice args = {}; char* compiler_backend_string; switch (compiler_backend) @@ -188,14 +190,20 @@ fn void compile_and_run(const CompileOptions* const options, char** envp, Compil } #define common_compile_and_run_args \ - options->out_path, \ + options.out_path, \ nest_source_path, \ compiler_backend_string, \ 0, if (debug) { -#ifdef __linux__ +#if _WIN32 + args = (CStringSlice) array_to_slice(((char*[]){ + "C:\\Users\\David\\Downloads\\remedybg_0_4_0_7\\remedybg.exe", + "-g", + common_compile_and_run_args + })); +#elif defined(__linux__) args = (CStringSlice) array_to_slice(((char*[]){ "/home/david/source/gf/gf2", "-ex", @@ -222,7 +230,7 @@ fn void compile_and_run(const CompileOptions* const options, char** envp, Compil })); } - run_command(args, envp); + run_command(arena, args, envp); } typedef enum Command : u8 @@ -298,16 +306,20 @@ fn void run_tests(Arena* arena, TestOptions const * const test_options, char** e print("TESTS (linkage={s}, optimization={s})\n", linkage_string, optimization_string); print("===========================\n\n"); - String compiler_path = arena_join_string(arena, ((Slice(String)) array_to_slice(((String[]){ - strlit(build_dir "/" "nest"), + String compiler_path_split[] = { + strlit("./" build_dir "/" "nest"), strlit("_"), optimization_string, strlit("_"), linkage_string, - })))); +#if _WIN32 + strlit(".exe"), +#endif + }; + String compiler_path = arena_join_string(arena, ((Slice(String)) array_to_slice(compiler_path_split))); compile_options.out_path = string_to_c(compiler_path); - compile_c(&compile_options, envp); + compile_c(arena, compile_options, envp); print("\n===========================\n"); print("COMPILER BUILD [OK]\n"); @@ -346,19 +358,23 @@ fn void run_tests(Arena* arena, TestOptions const * const test_options, char** e 0, }; - run_command((CStringSlice) array_to_slice(arguments), envp); + run_command(arena, (CStringSlice) array_to_slice(arguments), envp); if (compiler_backend != COMPILER_BACKEND_INTERPRETER) { - String out_program = arena_join_string(arena, ((Slice(String)) array_to_slice(((String[]){ - strlit(nest_dir "/"), - test_name, - })))); + String path_split[] = { + strlit("./" nest_dir "/"), + test_name, +#if _WIN32 + strlit(".exe"), +#endif + }; + String out_program = arena_join_string(arena, ((Slice(String)) array_to_slice(path_split))); char* run_arguments[] = { string_to_c(out_program), 0, }; - run_command((CStringSlice) array_to_slice(run_arguments), envp); + run_command(arena, (CStringSlice) array_to_slice(run_arguments), envp); } } } @@ -374,6 +390,8 @@ fn void entry_point(int argc, char* argv[], char* envp[]) fail(); } + Arena* arena = arena_init_default(KB(64)); + CompilerBackend preferred_compiler_backend = COMPILER_BACKEND_COUNT; Command command = COMMAND_COUNT; u8 test_every_config = 0; @@ -418,7 +436,16 @@ fn void entry_point(int argc, char* argv[], char* envp[]) { auto* c_argument = argv[index]; auto argument = cstr(c_argument); - String expected_starts[] = { strlit("tests/"), strlit("src/") }; + String expected_starts[] = { + strlit("tests/"), + strlit("tests\\"), + strlit("./tests/"), + strlit(".\\tests\\"), + strlit("src/"), + strlit("src\\"), + strlit("./src/"), + strlit(".\\src\\"), + }; for (u32 i = 0; i < array_length(expected_starts); i += 1) { @@ -450,7 +477,7 @@ fn void entry_point(int argc, char* argv[], char* envp[]) { if (preferred_compiler_backend == COMPILER_BACKEND_COUNT) { - preferred_compiler_backend = COMPILER_BACKEND_INTERPRETER; + preferred_compiler_backend = COMPILER_BACKEND_MACHINE; } } @@ -465,9 +492,19 @@ fn void entry_point(int argc, char* argv[], char* envp[]) // Test always with dynamic linkage because it's more trustworthy Linkage linkage = LINKAGE_DYNAMIC; - compile_and_run(&(CompileOptions) { + compile_and_run(arena, (CompileOptions) { .in_path = compiler_source_path, - .out_path = linkage == LINKAGE_DYNAMIC ? (build_dir "/" "nest_O0_dynamic") : (build_dir "/" "nest_O0_static"), + .out_path = linkage == LINKAGE_DYNAMIC ? ( + build_dir "/" "nest_O0_dynamic" +#if _WIN32 + ".exe" +#endif + ) : ( + build_dir "/" "nest_O0_static" +#if _WIN32 + ".exe" +#endif + ), .compiler = default_compiler, .debug_info = 1, .error_on_warning = 0, @@ -477,7 +514,6 @@ fn void entry_point(int argc, char* argv[], char* envp[]) break; case COMMAND_RUN_TESTS: { - Arena* arena = arena_init_default(KB(64)); Linkage all_linkages[] = { LINKAGE_DYNAMIC, #ifdef __linux__ @@ -513,9 +549,7 @@ fn void entry_point(int argc, char* argv[], char* envp[]) CompilerBackend all_compiler_backends[] = { // COMPILER_BACKEND_INTERPRETER, // COMPILER_BACKEND_C, -#ifdef __linux__ COMPILER_BACKEND_MACHINE, -#endif }; Slice(Linkage) linkage_selection; @@ -557,7 +591,7 @@ fn void entry_point(int argc, char* argv[], char* envp[]) }, envp); } break; case COMMAND_COMPILE: - compile_c(&(CompileOptions) { + compile_c(arena, (CompileOptions) { .in_path = compiler_source_path, .out_path = build_dir "/" "nest_O0_static", .compiler = default_compiler, diff --git a/bootstrap/lib.h b/bootstrap/lib.h index d27f4ef..689f166 100644 --- a/bootstrap/lib.h +++ b/bootstrap/lib.h @@ -23,13 +23,6 @@ #define _DEBUG 1 #endif -#if _WIN32 -#define stdout_handle (GetStdHandle(STD_OUTPUT_HANDLE)) -#else -#define stdout_handle (1) -#endif - - #ifdef STATIC #define LINK_LIBC 0 #else @@ -146,6 +139,7 @@ timestamp() #endif } + #if LINK_LIBC global struct timespec cpu_resolution; #else @@ -494,6 +488,16 @@ may_be_unused fn s32 cast_s64_to_s32(s64 source, const char* name, int line) #define todo() do { print("TODO at {cstr}:{u32}\n", __FILE__, __LINE__); __builtin_trap(); } while(0) +fn FileDescriptor stdout_get() +{ +#if _WIN32 + auto handle = GetStdHandle(STD_OUTPUT_HANDLE); + assert(handle != INVALID_HANDLE_VALUE); + return handle; +#else + return 1; +#endif +} #if __APPLE__ const global u64 page_size = KB(16); @@ -509,14 +513,24 @@ typedef enum TimeUnit TIME_UNIT_SECONDS, } TimeUnit; -fn f64 resolve_timestamp( +may_be_unused fn f64 resolve_timestamp( +#if _WIN32 + u64 start, u64 end, +#else #if LINK_LIBC struct timespec start, struct timespec end, #else u64 start, u64 end, +#endif #endif TimeUnit time_unit) { +#if _WIN32 + unused(start); + unused(end); + unused(time_unit); + todo(); +#else #if LINK_LIBC assert(end.tv_sec >= start.tv_sec); struct timespec result = { @@ -569,6 +583,7 @@ fn f64 resolve_timestamp( return s; } #endif +#endif } const may_be_unused global u8 brace_open = '{'; @@ -1462,30 +1477,33 @@ may_be_unused fn ssize_t syscall_write(int fd, const void *buffer, size_t bytes) #endif } -may_be_unused fn int syscall_mkdir(const char* path, u32 mode) +may_be_unused fn int syscall_mkdir(String path, u32 mode) { + assert(path.pointer[path.length] == 0); #if LINK_LIBC - return mkdir(path, mode); + return mkdir((char*)path.pointer, mode); #else - return cast(s32, s64, syscall2(syscall_x86_64_mkdir, (s64)path, (s64)mode)); + return cast(s32, s64, syscall2(syscall_x86_64_mkdir, (s64)path.pointer, (s64)mode)); #endif } -may_be_unused fn int syscall_rmdir(const char* path) +may_be_unused fn int syscall_rmdir(String path) { + assert(path.pointer[path.length] == 0); #if LINK_LIBC - return rmdir(path); + return rmdir((char*)path.pointer); #else - return cast(s32, s64, syscall1(syscall_x86_64_rmdir, (s64)path)); + return cast(s32, s64, syscall1(syscall_x86_64_rmdir, (s64)path.pointer)); #endif } -may_be_unused fn int syscall_unlink(const char* path) +may_be_unused fn int syscall_unlink(String path) { + assert(path.pointer[path.length] == 0); #if LINK_LIBC - return unlink(path); + return unlink((char*)path.pointer); #else - return cast(s32, s64, syscall1(syscall_x86_64_unlink, (s64)path)); + return cast(s32, s64, syscall1(syscall_x86_64_unlink, (s64)path.pointer)); #endif } @@ -1507,6 +1525,7 @@ may_be_unused fn signed long syscall_execve(const char* path, char *const argv[] return syscall3(syscall_x86_64_execve, (s64)path, (s64)argv, (s64)envp); #endif } + may_be_unused fn pid_t syscall_waitpid(pid_t pid, int* status, int options) { #if LINK_LIBC @@ -1540,22 +1559,6 @@ may_be_unused [[noreturn]] [[gnu::cold]] fn void syscall_exit(int status) } #endif -may_be_unused fn u64 file_get_size(FileDescriptor fd) -{ -#if _WIN32 - LARGE_INTEGER file_size; - GetFileSizeEx(fd, &file_size); - return (u64)file_size.QuadPart; -#else - struct stat stat_buffer; - int stat_result = syscall_fstat(fd, &stat_buffer); - assert(stat_result == 0); - auto size = cast(u64, s64, stat_buffer.st_size); - return size; -#endif -} - - may_be_unused fn u64 os_timer_freq() { return 1000 * 1000; @@ -1575,15 +1578,111 @@ may_be_unused fn u64 os_timer_get() #endif } +STRUCT(OSFileOpenFlags) +{ + u32 truncate:1; + u32 executable:1; + u32 write:1; + u32 read:1; + u32 create:1; +}; + +may_be_unused fn u8 os_file_descriptor_is_valid(FileDescriptor fd) +{ +#if _WIN32 + return fd != INVALID_HANDLE_VALUE; +#else + return fd >= 0; +#endif +} + +may_be_unused fn FileDescriptor os_file_open(String path, OSFileOpenFlags flags) +{ + assert(path.pointer[path.length] == 0); +#if _WIN32 + + DWORD dwDesiredAccess = 0; + dwDesiredAccess |= flags.read * GENERIC_READ; + dwDesiredAccess |= flags.write * GENERIC_WRITE; + dwDesiredAccess |= flags.write * GENERIC_EXECUTE; + DWORD dwShareMode = 0; + LPSECURITY_ATTRIBUTES lpSecurityAttributes = 0; + DWORD dwCreationDisposition = 0; + dwCreationDisposition |= (!flags.create) * OPEN_EXISTING; + dwCreationDisposition |= flags.create * CREATE_ALWAYS; + DWORD dwFlagsAndAttributes = 0; + dwFlagsAndAttributes |= FILE_ATTRIBUTE_NORMAL; + HANDLE hTemplateFile = 0; + auto handle = CreateFileA((char*)path.pointer, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + return handle; +#else + int posix_flags = 0; + posix_flags |= O_WRONLY * (flags.write & !flags.read); + posix_flags |= O_RDONLY * ((!flags.write) & flags.read); + posix_flags |= O_RDWR * (flags.write & flags.read); + posix_flags |= O_CREAT * flags.create; + posix_flags |= O_TRUNC * flags.truncate; + + int permissions; + if (flags.executable && flags.write) + { + permissions = 0755; + } + else + { + permissions = 0644; + } + auto result = syscall_open((char*)path.pointer, posix_flags, permissions); + return result; +#endif +} + +may_be_unused fn u64 os_file_get_size(FileDescriptor fd) +{ +#if _WIN32 + LARGE_INTEGER file_size; + GetFileSizeEx(fd, &file_size); + return (u64)file_size.QuadPart; +#else + struct stat stat_buffer; + int stat_result = syscall_fstat(fd, &stat_buffer); + assert(stat_result == 0); + auto size = cast(u64, s64, stat_buffer.st_size); + return size; +#endif +} + may_be_unused fn void os_file_write(FileDescriptor fd, String content) { #if _WIN32 - WriteFileEx(fd, content.pointer, content.length, 0, 0); + WriteFile(fd, content.pointer, cast(u32, u64, content.length), 0, 0); #else syscall_write(fd, content.pointer, content.length); #endif } +may_be_unused fn void os_file_read(FileDescriptor fd, String buffer, u64 byte_count) +{ + assert(byte_count <= buffer.length); + if (byte_count <= buffer.length) + { +#if _WIN32 + ReadFile(fd, buffer.pointer, cast(u32, u64, byte_count), 0, 0); +#else + syscall_read(fd, buffer.pointer, byte_count); +#endif + } +} + +may_be_unused fn void os_file_close(FileDescriptor fd) +{ +#if _WIN32 + CloseHandle(fd); +#else + syscall_close(fd); +#endif +} + may_be_unused fn void calibrate_cpu_timer() { #ifndef SILENT @@ -1629,7 +1728,13 @@ STRUCT(OSReserveMapFlags) fn u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map) { #if _WIN32 - return (u8*)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + DWORD map_flags = 0; + map_flags |= (MEM_RESERVE * map.noreserve); + DWORD protection_flags = 0; + protection_flags |= PAGE_READWRITE * (!protection.write && !protection.read); + protection_flags |= PAGE_READWRITE * (protection.write && protection.read); + protection_flags |= PAGE_READONLY * (protection.write && !protection.read); + return (u8*)VirtualAlloc((void*)base, size, map_flags, protection_flags); #else int protection_flags = (protection.read * PROT_READ) | (protection.write * PROT_WRITE) | (protection.execute * PROT_EXEC); int map_flags = (map.anon * MAP_ANONYMOUS) | (map.priv * MAP_PRIVATE) | (map.noreserve * MAP_NORESERVE); @@ -1649,6 +1754,16 @@ fn void commit(void* address, u64 size) #endif } +may_be_unused fn void os_directory_make(String path) +{ + assert(path.pointer[path.length] == 0); +#if _WIN32 + CreateDirectoryA((char*)path.pointer, 0); +#else + syscall_mkdir(path, 0755); +#endif +} + may_be_unused fn u64 align_forward(u64 value, u64 alignment) { u64 mask = alignment - 1; @@ -2792,7 +2907,7 @@ may_be_unused fn void print(const char* format, ...) } String final_string = s_get_slice(u8, buffer, 0, buffer_i); - os_file_write(stdout_handle, final_string); + os_file_write(stdout_get(), final_string); #endif } @@ -2814,7 +2929,10 @@ static_assert(sizeof(Arena) == 64); fn Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size) { Arena* arena = (Arena*)os_reserve(0, reserved_size, - (OSReserveProtectionFlags) {}, + (OSReserveProtectionFlags) { + .read = 1, + .write = 1, + }, (OSReserveMapFlags) { .priv = 1, .anon = 1, @@ -2889,9 +3007,10 @@ may_be_unused fn void arena_reset(Arena* arena) #define transmute(D, source) *(D*)&source -may_be_unused fn void run_command(CStringSlice arguments, char* envp[]) +may_be_unused fn void run_command(Arena* arena, CStringSlice arguments, char* envp[]) { print("Running command:\n"); + assert(arguments.length > 0); assert(arguments.pointer[arguments.length - 1] == 0); for (u32 i = 0; i < arguments.length - 1; i += 1) { @@ -2899,8 +3018,75 @@ may_be_unused fn void run_command(CStringSlice arguments, char* envp[]) print("{cstr} ", argument); } print("\n"); - pid_t pid = syscall_fork(); +#if _WIN32 + auto start_timestamp = timestamp(); + + u32 length = 0; + for (u32 i = 0; i < arguments.length; i += 1) + { + auto argument = arguments.pointer[i]; + if (argument) + { + auto string_len = strlen(argument); + length += cast(u32, u64, string_len + 1); + } + } + + char* bytes = (char*)arena_allocate_bytes(arena, length, 1); + u32 byte_i = 0; + for (u32 i = 0; i < arguments.length; i += 1) + { + auto argument = arguments.pointer[i]; + if (argument) + { + auto len = strlen(argument); + memcpy(&bytes[byte_i], argument, len); + byte_i += len; + bytes[byte_i] = ' '; + byte_i += 1; + } + } + bytes[byte_i - 1] = 0; + auto end_timestamp = timestamp(); + + PROCESS_INFORMATION process_information = {}; + STARTUPINFOA startup_info = {}; + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + auto handle_inheritance = 1; + if (CreateProcessA(0, bytes, 0, 0, handle_inheritance, 0, 0, 0, &startup_info, &process_information)) + { + WaitForSingleObject(process_information.hProcess, INFINITE); + CloseHandle(process_information.hProcess); + CloseHandle(process_information.hThread); + } + else + { + print("Failure\n"); + auto err = GetLastError(); + LPSTR lpMsgBuf; + DWORD bufSize = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + LANG_NEUTRAL, // Use default language + (LPSTR)&lpMsgBuf, + 0, + NULL + ); + unused(bufSize); + todo(); + } + unused(start_timestamp); + unused(end_timestamp); + unused(envp); +#else + unused(arena); + pid_t pid = syscall_fork(); if (pid == -1) { @@ -2950,7 +3136,6 @@ may_be_unused fn void run_command(CStringSlice arguments, char* envp[]) print("Program failed to run!\n"); fail(); } - auto ms = resolve_timestamp(start_timestamp, end_timestamp, TIME_UNIT_MILLISECONDS); auto ticks = #if LINK_LIBC @@ -2961,6 +3146,7 @@ may_be_unused fn void run_command(CStringSlice arguments, char* envp[]) ; print("Command run successfully in {f64} {cstr}\n", ms, ticks ? "ticks" : "ms"); } +#endif } #define VirtualBuffer(T) VirtualBuffer_ ## T @@ -3045,6 +3231,8 @@ may_be_unused fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes) #define vb_append_one(a, item) (typeof((a)->pointer)) vb_generic_append((VirtualBuffer(u8)*)(a), &(item), sizeof(*((a)->pointer)), 1) #define vb_to_bytes(vb) (Slice(u8)) { .pointer = (u8*)((vb).pointer), .length = sizeof(*((vb).pointer)) * (vb).length, } #define vb_ensure_capacity(a, count) vb_generic_ensure_capacity((VirtualBuffer(u8)*)(a), sizeof(*((a)->pointer)), count) +#define vb_add_array(vb, arr) memcpy(vb_add(vb, sizeof(arr)), arr, sizeof(arr)) + may_be_unused fn Hash32 hash32_fib_end(Hash32 hash) { diff --git a/bootstrap/main.c b/bootstrap/main.c index 8126f58..4a89b57 100644 --- a/bootstrap/main.c +++ b/bootstrap/main.c @@ -28,22 +28,19 @@ STRUCT(GetOrPut(T)) \ auto compiler_name = strlit("nest"); -fn int dir_make(const char* path) -{ - return syscall_mkdir(path, 0755); -} - fn String file_read(Arena* arena, String path) { String result = {}; - int file_descriptor = syscall_open(string_to_c(path), 0, 0); - if (file_descriptor >= 0) + auto file_descriptor = os_file_open(path, (OSFileOpenFlags) { + .read = 1, + .write = 0, + .create = 0, + .truncate = 0, + .executable = 0, + }); + if (os_file_descriptor_is_valid(file_descriptor)) { - struct stat stat_buffer; - int stat_result = syscall_fstat(file_descriptor, &stat_buffer); - assert(stat_result == 0); - - auto file_size = cast(u64, s64, stat_buffer.st_size); + auto file_size = os_file_get_size(file_descriptor); if (file_size > 0) { result = (String){ @@ -52,17 +49,16 @@ fn String file_read(Arena* arena, String path) }; // TODO: big files - ssize_t read_result = syscall_read(file_descriptor, result.pointer, result.length); - assert(read_result >= 0); - assert((u64)read_result == file_size); + // TODO: result codes + os_file_read(file_descriptor, result, file_size); } else { result.pointer = (u8*)&result; } - auto close_result = syscall_close(file_descriptor); - assert(close_result == 0); + // TODO: check result + os_file_close(file_descriptor); } @@ -72,9 +68,10 @@ fn String file_read(Arena* arena, String path) fn void print_string(String message) { #ifndef SILENT - ssize_t result = syscall_write(1, message.pointer, message.length); - assert(result >= 0); - assert((u64)result == message.length); + // TODO: check writes + os_file_write(stdout_get(), message); + // assert(result >= 0); + // assert((u64)result == message.length); #else unused(message); #endif @@ -433,9 +430,9 @@ typedef enum ELFMachine : u16 typedef enum ELFSectionIndex : u16 { - UNDEFINED = 0, - ABSOLUTE = 0xfff1, - COMMON = 0xfff2, + ELF_SECTION_UNDEFINED = 0, + ELF_SECTION_ABSOLUTE = 0xfff1, + ELF_SECTION_COMMON = 0xfff2, } ELFSectionIndex; STRUCT(ELFVersionRequirement) @@ -1676,7 +1673,7 @@ STRUCT(DebugType) decl_vb(DebugType); declare_ip(DebugType); -typedef enum BackendTypeId +typedef enum BackendTypeId : u8 { BACKEND_TYPE_VOID = 0x00, BACKEND_TYPE_INTEGER_8 = 0x01, @@ -5325,7 +5322,7 @@ fn TypePair analyze_type(Thread* thread, Parser* parser, String src) static_assert(array_length(thread->types.debug.integer.array) == 8); auto index = signedness * 4 + bit_index; auto debug_type_index = thread->types.debug.integer.array[index]; - BackendTypeId backend_type = bit_index + 1; + BackendTypeId backend_type = cast(u8, u32, bit_index + 1); auto type_pair = type_pair_make(debug_type_index, backend_type); return type_pair; } @@ -7170,8 +7167,8 @@ typedef enum CompilerBackend : u8 STRUCT(ObjectOptions) { - char* object_path; - char* exe_path; + String object_path; + String exe_path; Slice(u8) code; u64 dynamic:1; u64 reserved:63; @@ -10329,9 +10326,6 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) auto name = elf_get_section_name(builder, strlit(".symtab")); - u16 section_index_undefined = 0; - u16 section_index_absolute = 0xfff1; - auto main_c_string = st_get_string(&builder->static_st, strlit("first.nat")); auto _dynamic_string = st_get_string(&builder->static_st, strlit("_DYNAMIC")); auto eh_frame_hdr_string = st_get_string(&builder->static_st, strlit("__GNU_EH_FRAME_HDR")); @@ -10349,7 +10343,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_FILE, .binding = ELF_SYMBOL_BINDING_LOCAL, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_absolute, + .section_index = ELF_SECTION_ABSOLUTE, .value = 0, .size = 0, }, @@ -10358,7 +10352,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_FILE, .binding = ELF_SYMBOL_BINDING_LOCAL, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_absolute, + .section_index = ELF_SECTION_ABSOLUTE, .value = 0, .size = 0 }, @@ -10394,7 +10388,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_FUNCTION, .binding = ELF_SYMBOL_BINDING_GLOBAL, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_undefined, + .section_index = ELF_SECTION_UNDEFINED, .value = 0, .size = 0, }, @@ -10403,7 +10397,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_NONE, .binding = ELF_SYMBOL_BINDING_WEAK, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_undefined, + .section_index = ELF_SECTION_UNDEFINED, .value = 0, .size = 0, }, @@ -10448,7 +10442,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_NONE, .binding = ELF_SYMBOL_BINDING_WEAK, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_undefined, + .section_index = ELF_SECTION_UNDEFINED, .value = 0, .size = 0, }, @@ -10520,7 +10514,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_NONE, .binding = ELF_SYMBOL_BINDING_WEAK, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_undefined, + .section_index = ELF_SECTION_UNDEFINED, .value = 0, .size = 0, }, @@ -10529,7 +10523,7 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) .type = ELF_SYMBOL_TYPE_FUNCTION, .binding = ELF_SYMBOL_BINDING_WEAK, .visibility = ELF_SYMBOL_VISIBILITY_DEFAULT, - .section_index = section_index_undefined, + .section_index = ELF_SECTION_UNDEFINED, .value = 0, .size = 0, }, @@ -10678,17 +10672,505 @@ may_be_unused fn void write_elf(Thread* thread, ObjectOptions options) assert(dynamic_relocation_count == expected_dynamic_relocation_count); - auto exe_path_z = options.exe_path; { - int fd = syscall_open(exe_path_z, O_WRONLY | O_CREAT | O_TRUNC, 0755); - assert(fd != -1); - syscall_write(fd, builder->file.pointer, builder->file.length); - - syscall_close(fd); + auto fd = os_file_open(options.exe_path, (OSFileOpenFlags) { + .write = 1, + .truncate = 1, + .create = 1, + .executable = 1, + }); + assert(os_file_descriptor_is_valid(fd)); + os_file_write(fd, (String) { builder->file.pointer, builder->file.length }); + os_file_close(fd); } } -void subsume_node_without_killing(Thread* thread, NodeIndex old_node_index, NodeIndex new_node_index) +STRUCT(DOSHeader) +{ + u16 signature; + u16 extra_page_size; + u16 page_count; + u16 relocations; + u16 header_size_in_paragraphs; + u16 minimum_allocated_paragraphs; + u16 maximum_allocated_paragraphs; + u16 initial_ss_value; + u16 initial_relative_sp_value; + u16 checksum; + u16 initial_relative_ip_value; + u16 initial_cs_value; + u16 relocation_table_pointer; + u16 overlay_number; + u16 reserved_words[4]; + u16 oem_identifier; + u16 oem_information; + u16 other_reserved_words[10]; + u32 coff_header_pointer; +}; + +static_assert(sizeof(DOSHeader) == 0x40); + +typedef enum COFFArchitecture : u16 +{ + COFF_ARCH_UNKNOWN = 0x0000, + COFF_ARCH_AMD64 = 0x8664, + COFF_ARCH_ARM64 = 0xAA64, +} COFFArchitecture; + +STRUCT(COFFCharacteristics) +{ + u16 base_relocations_stripped:1; + u16 executable_image:1; + u16 line_numbers_stripped:1; + u16 symbols_stripped:1; + u16 aggressively_trim_working_set:1; + u16 large_address_aware:1; + u16 padding:1; + u16 bytes_reversed_low:1; + u16 machine_32_bit:1; + u16 debug_info_stripped:1; + u16 removable_run_from_swap:1; + u16 net_run_from_swap:1; + u16 system_file:1; + u16 dll:1; + u16 uniprocessor_machine_only:1; + u16 bytes_reversed_high:1; +}; + +static_assert(sizeof(COFFCharacteristics) == sizeof(u16)); + +typedef enum COFFOptionalHeaderFormat : u16 +{ + COFF_FORMAT_ROM = 0x107, + COFF_FORMAT_PE32 = 0x10b, + COFF_FORMAT_PE32_PLUS = 0x20b, +} COFFOptionalHeaderFormat; + +typedef enum COFFSubsystem : u16 +{ + COFF_SUBSYSTEM_UNKNOWN = 0x0000, + COFF_SUBSYSTEM_NATIVE = 0x0001, + COFF_SUBSYSTEM_WINDOWS_GUI = 0x0002, + COFF_SUBSYSTEM_WINDOWS_CUI = 0x0003, + COFF_SUBSYSTEM_OS_2_CUI = 0x0005, + COFF_SUBSYSTEM_POSIX_CUI = 0x0007, + COFF_SUBSYSTEM_WINDOWS_9X_NATIVE = 0x0008, + COFF_SUBSYSTEM_WINDOWS_CE_GUI = 0x0009, + COFF_SUBSYSTEM_EFI_APPLICATION = 0x000a, + COFF_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 0x000b, + COFF_SUBSYSTEM_EFI_RUNTIME_DRIVER = 0x000c, + COFF_SUBSYSTEM_EFI_ROM = 0x000d, + COFF_SUBSYSTEM_XBOX = 0x000e, + COFF_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 0x0010, +} COFFSubsystem; + +STRUCT(COFFDllCharacteristics) +{ + u16 call_when_loaded:1; + u16 call_when_thread_terminates:1; + u16 call_when_thread_starts:1; + u16 call_when_exiting:1; + u16 padding:1; + u16 high_entropy_va:1; + u16 dynamic_base:1; + u16 force_integrity:1; + u16 nx_compatible:1; + u16 no_isolation:1; + u16 no_seh:1; + u16 do_not_bind:1; + u16 app_container:1; + u16 is_wdm_driver:1; + u16 supports_control_flow_guard:1; + u16 terminal_server_aware:1; +}; + +static_assert(sizeof(COFFDllCharacteristics) == sizeof(u16)); + +STRUCT(COFFLoaderFlags) +{ + u32 prestart_breakpoint:1; + u32 postloading_debugger:1; + u32 padding:30; +}; + +static_assert(sizeof(COFFLoaderFlags) == sizeof(u32)); + +STRUCT(COFFHeader) +{ + u32 signature; + COFFArchitecture architecture; + u16 section_count; + u32 time_date_stamp; + u32 symbol_table_pointer; + u32 symbol_count; + u16 optional_header_size; + COFFCharacteristics characteristics; +}; + +STRUCT(COFFOptionalHeader) +{ + COFFOptionalHeaderFormat format; + u8 major_linker_version; + u8 minor_linker_version; + u32 code_size; + u32 initialized_data_size; + u32 uninitialized_data_size; + u32 entry_point_address; + u32 code_offset; + u64 image_offset; + u32 virtual_section_alignment; + u32 file_section_alignment; + u16 major_operating_system_version; + u16 minor_operating_system_version; + u16 major_image_version; + u16 minor_image_version; + u16 major_subsystem_version; + u16 minor_subsystem_version; + u32 win32_version_value; + u32 image_size; + u32 headers_size; + u32 checksum; + COFFSubsystem subsystem; + COFFDllCharacteristics dll_characteristics; + u64 stack_reserve_size; + u64 stack_commit_size; + u64 heap_reserve_size; + u64 heap_commit_size; + COFFLoaderFlags loader_flags; + u32 directory_count; +}; + +STRUCT(COFFDataDirectory) +{ + u32 rva; + u32 size; +}; + +STRUCT(COFFSectionFlags) +{ + u32 padding:3; + u32 do_not_pad:1; + u32 padding1:1; + u32 contains_code:1; + u32 contains_initialized_data:1; + u32 contains_uninitialized_data:1; + u32 link_other:1; + u32 link_has_information:1; + u32 padding2:1; + u32 link_remove:1; + u32 link_has_comdat:1; + u32 padding3:1; + u32 reset_speculative_exceptions:1; + u32 global_pointer_relocations:1; + u32 purgeable:1; + u32 is_16_bit:1; + u32 locked:1; + u32 preloaded:1; + u32 data_alignment:4; + u32 link_extended_relocations:1; + u32 discardable:1; + u32 not_cached:1; + u32 not_pageable:1; + u32 shared:1; + u32 execute:1; + u32 read:1; + u32 writte:1; +}; + +static_assert(sizeof(COFFSectionFlags) == sizeof(u32)); + +STRUCT(COFFSectionName) +{ + u8 name[8]; +}; + +STRUCT(COFFSectionHeader) +{ + COFFSectionName name; + u32 virtual_size; + u32 rva; + u32 file_size; + u32 file_offset; + u32 relocation_offset; + u32 line_number_offset; + u16 relocation_count; + u16 line_number_count; + COFFSectionFlags flags; +}; + +static_assert(sizeof(COFFSectionHeader) == 40); + +fn COFFSectionName coff_section_name(String name) +{ + COFFSectionName result = {}; + assert(name.length <= array_length(result.name)); + memcpy(result.name, name.pointer, name.length); + + return result; +} + +may_be_unused fn void write_pe(Thread* thread, ObjectOptions options) +{ + VirtualBuffer(u8) file = {}; + auto* mz = "MZ"; + auto signature = *(u16*)mz; + *vb_add_struct(&file, DOSHeader) = (DOSHeader) + { + .signature = signature, + .extra_page_size = 144, + .page_count = 3, + .relocations = 0, + .header_size_in_paragraphs = 4, + .minimum_allocated_paragraphs = 0, + .maximum_allocated_paragraphs = (u16)~((u16)(0u)), + .initial_ss_value = 0, + .initial_relative_sp_value = 184, + .checksum = 0, + .initial_relative_ip_value = 0, + .initial_cs_value = 0, + .relocation_table_pointer = sizeof(DOSHeader), + .overlay_number = 0, + .coff_header_pointer = 208, + }; + u8 code[] = { 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, }; + vb_add_array(&file, code); + + auto str = strlit("This program cannot be run in DOS mode.\r\r\n"); + memcpy(vb_add(&file, cast(u32, u64, str.length)), str.pointer, str.length); + *vb_add(&file, 1) = '$'; + + vb_align(&file, 8); + + u8 rich_header[] = { + 0xDD, 0x6A, 0x05, 0xC7, 0x99, 0x0B, 0x6B, 0x94, 0x99, 0x0B, 0x6B, 0x94, 0x99, 0x0B, 0x6B, 0x94, + 0xD2, 0x73, 0x6A, 0x95, 0x9A, 0x0B, 0x6B, 0x94, 0x99, 0x0B, 0x6A, 0x94, 0x98, 0x0B, 0x6B, 0x94, + 0xD1, 0x8E, 0x6F, 0x95, 0x98, 0x0B, 0x6B, 0x94, 0xD1, 0x8E, 0x69, 0x95, 0x98, 0x0B, 0x6B, 0x94, + 0x52, 0x69, 0x63, 0x68, 0x99, 0x0B, 0x6B, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + vb_add_array(&file, rich_header); + + COFFDataDirectory directories[] = { + { .rva = 0, .size = 0, }, + { .rva = 8632, .size = 40, }, + { .rva = 0, .size = 0, }, + { .rva = 12288, .size = 12, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + { .rva = 8208, .size = 84, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + { .rva = 8192, .size = 16, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + { .rva = 0, .size = 0, }, + }; + + auto coff_optional_header = (COFFOptionalHeader) { + .format = COFF_FORMAT_PE32_PLUS, + .major_linker_version = 14, + .minor_linker_version = 41, + .code_size = 0x200, + .initialized_data_size = 0x600, + .uninitialized_data_size = 0, + .entry_point_address = 0x1000, + .code_offset = 0x1000, + .image_offset = 0x140000000, + .virtual_section_alignment = 0x1000, + .file_section_alignment = 0x200, + .major_operating_system_version = 6, + .minor_operating_system_version = 0, + .major_image_version = 0, + .minor_image_version = 0, + .major_subsystem_version = 6, + .minor_subsystem_version = 0, + .win32_version_value = 0, + .image_size = 0x4000, + .headers_size = 0x400, + .checksum = 0, + .subsystem = COFF_SUBSYSTEM_WINDOWS_CUI, + .dll_characteristics = { + .high_entropy_va = 1, + .dynamic_base = 1, + .nx_compatible = 1, + .terminal_server_aware = 1, + }, + .stack_reserve_size = MB(1), + .stack_commit_size = 0x1000, + .heap_reserve_size = MB(1), + .heap_commit_size = 0x1000, + .loader_flags = {}, + .directory_count = array_length(directories), + }; + + u8 coff_signature[] = { 'P', 'E', 0, 0 }; + auto coff_header = (COFFHeader) { + .signature = *(u32*)coff_signature, + .architecture = COFF_ARCH_AMD64, + .section_count = 3, + .time_date_stamp = 1727882096, + .symbol_table_pointer = 0, + .symbol_count = 0, + .optional_header_size = 240, + .characteristics = { + .executable_image = 1, + .large_address_aware = 1, + }, + }; + + assert(file.length == 0xd0); + *vb_add_struct(&file, COFFHeader) = coff_header; + *vb_add_struct(&file, COFFOptionalHeader) = coff_optional_header; + assert(file.length == 0x158); + vb_add_array(&file, directories); + + COFFSectionHeader section_headers[] = { + { + .name = coff_section_name(strlit(".text")), + .virtual_size = 18, + .rva = 0x1000, + .file_size = 0x200, + .file_offset = 1024, + .flags = { + .contains_code = 1, + .execute = 1, + .read = 1, + }, + }, + { + .name = coff_section_name(strlit(".rdata")), + .virtual_size = 524, + .rva = 0x2000, + .file_size = 0x400, + .file_offset = 0x600, + .flags = { + .contains_initialized_data = 1, + .read = 1, + }, + }, + { + .name = coff_section_name(strlit(".pdata")), + .virtual_size = 12, + .rva = 0x3000, + .file_size = 0x200, + .file_offset = 0xa00, + .flags = { + .contains_initialized_data = 1, + .read = 1, + }, + }, + }; + vb_add_array(&file, section_headers); + + u8 text_content[] = { 0x48, 0x83, 0xEC, 0x28, 0x33, 0xC9, 0xFF, 0x15, 0xF4, 0x0F, 0x00, 0x00, 0x90, 0x48, 0x83, 0xC4, 0x28, 0xC3, }; + + u8 rdata_content[] = { + 0xF0, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x63, 0xFD, 0x66, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x84, 0x20, 0x00, 0x00, 0x84, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x63, 0xFD, 0x66, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0xC0, 0x20, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x63, 0xFD, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xD4, 0x20, 0x00, 0x00, + 0xD4, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x20, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x52, 0x53, 0x44, 0x53, 0x3D, 0x15, 0x84, 0x0A, 0xBC, 0x9F, 0xA1, 0x4B, + 0x82, 0xB4, 0x94, 0xF1, 0x5B, 0x91, 0x63, 0x3A, 0x03, 0x00, 0x00, 0x00, 0x43, 0x3A, 0x5C, 0x55, + 0x73, 0x65, 0x72, 0x73, 0x5C, 0x44, 0x61, 0x76, 0x69, 0x64, 0x5C, 0x64, 0x65, 0x76, 0x5C, 0x6D, + 0x69, 0x6E, 0x69, 0x6D, 0x61, 0x6C, 0x5C, 0x6D, 0x61, 0x69, 0x6E, 0x2E, 0x70, 0x64, 0x62, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x2E, 0x74, 0x65, 0x78, 0x74, 0x24, 0x6D, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x2E, 0x69, 0x64, 0x61, 0x74, 0x61, 0x24, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x2E, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, + 0x64, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2E, 0x72, 0x64, 0x61, 0x74, 0x61, 0x24, 0x76, + 0x6F, 0x6C, 0x74, 0x6D, 0x64, 0x00, 0x00, 0x00, 0x84, 0x20, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, + 0x2E, 0x72, 0x64, 0x61, 0x74, 0x61, 0x24, 0x7A, 0x7A, 0x7A, 0x64, 0x62, 0x67, 0x00, 0x00, 0x00, + 0xB0, 0x21, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2E, 0x78, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, + 0xB8, 0x21, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2E, 0x69, 0x64, 0x61, 0x74, 0x61, 0x24, 0x32, + 0x00, 0x00, 0x00, 0x00, 0xCC, 0x21, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2E, 0x69, 0x64, 0x61, + 0x74, 0x61, 0x24, 0x33, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x21, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2E, 0x69, 0x64, 0x61, 0x74, 0x61, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x21, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x2E, 0x69, 0x64, 0x61, 0x74, 0x61, 0x24, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2E, 0x70, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, + 0x01, 0x04, 0x01, 0x00, 0x04, 0x42, 0x00, 0x00, 0xE0, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF0, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x01, 0x45, 0x78, 0x69, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x00, 0x4B, 0x45, + 0x52, 0x4E, 0x45, 0x4C, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x00, + }; + + u8 pdata_content[] = { 0x00, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, 0xB0, 0x21, 0x00, 0x00, }; + + String section_contents[] = { + { text_content, sizeof(text_content) }, + { rdata_content, sizeof(rdata_content) }, + { pdata_content, sizeof(pdata_content) }, + }; + + vb_align(&file, 0x200); + + for (u32 i = 0; i < array_length(section_contents); i += 1) + { + auto section_content = section_contents[i]; + + memcpy(vb_add(&file, cast(u32, u64, section_content.length)), section_content.pointer, section_content.length); + + vb_align(&file, 0x200); + } + + // Check if file matches +#define CHECK_PE_MATCH 0 +#if CHECK_PE_MATCH + auto minimal = file_read(thread->arena, strlit("C:/Users/David/dev/minimal/main.exe")); + assert(file.length == minimal.length); + + for (u32 i = 0; i < minimal.length; i += 1) + { + auto mine = file.pointer[i]; + auto original = minimal.pointer[i]; + assert(mine == original); + } +#else + unused(thread); +#endif + + { + auto fd = os_file_open(options.exe_path, (OSFileOpenFlags) { + .write = 1, + .truncate = 1, + .create = 1, + .executable = 1, + }); +#if _WIN32 + if (!os_file_descriptor_is_valid(fd)) + { + auto err = GetLastError(); + LPSTR lpMsgBuf; + DWORD bufSize = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + LANG_NEUTRAL, // Use default language + (LPSTR)&lpMsgBuf, + 0, + NULL + ); + unused(bufSize); + print("Error opening file \"{s}\": {cstr}\n", options.exe_path, lpMsgBuf); + fail(); + } +#endif + assert(os_file_descriptor_is_valid(fd)); + os_file_write(fd, (String) { file.pointer, file.length }); + os_file_close(fd); + } +} + +fn void subsume_node_without_killing(Thread* thread, NodeIndex old_node_index, NodeIndex new_node_index) { assert(!index_equal(old_node_index, new_node_index)); auto* old = thread_node_get(thread, old_node_index); @@ -13647,9 +14129,20 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options) }))); auto exe_path_view = s_get_slice(u8, object_path, 0, object_path.length - 2); - auto exe_path = (char*)arena_allocate_bytes(thread->arena, exe_path_view.length + 1, 1); - memcpy(exe_path, exe_path_view.pointer, exe_path_view.length); - exe_path[exe_path_view.length] = 0; + u32 extra_bytes = 0; +#if _WIN32 + extra_bytes = strlen(".exe"); +#endif + String exe_path = { + .pointer = arena_allocate_bytes(thread->arena, exe_path_view.length + extra_bytes + 1, 1), + .length = exe_path_view.length + extra_bytes, + }; + + memcpy(exe_path.pointer, exe_path_view.pointer, exe_path_view.length); +#if _WIN32 + memcpy(exe_path.pointer + exe_path_view.length, ".exe", extra_bytes); +#endif + exe_path.pointer[exe_path_view.length + extra_bytes] = 0; switch (options.backend) { @@ -13687,13 +14180,15 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options) { auto code_slice = (Slice(u8)) { .pointer = code.pointer, .length = code.length, }; auto object_options = (ObjectOptions) { - .object_path = string_to_c(object_path), + .object_path = object_path, .exe_path = exe_path, .code = code_slice, .dynamic = 1, }; -#if defined(__APPLE__) - write_macho(thread, &object_options, envp); +#if _WIN32 + write_pe(thread, object_options); +#elif defined(__APPLE__) + write_macho(thread, object_options, envp); #elif defined(__linux__) write_elf(thread, object_options); #else @@ -14122,7 +14617,7 @@ fn void entry_point(int argc, char* argv[], char* envp[]) CompilerBackend compiler_backend = arguments.pointer[2].pointer[0]; u8 emit_ir = arguments.length >= 4 && arguments.pointer[3].pointer[0] == 'y'; - dir_make("nest"); + os_directory_make(strlit("nest")); File file = { .path = source_file_path, diff --git a/project.bat b/project.bat index 59c2198..22fb7a5 100644 --- a/project.bat +++ b/project.bat @@ -1 +1,9 @@ -clang -o build/build bootstrap/build.c -g -march=native -std=gnu2x -Wall -Wextra -Wpedantic -Wno-nested-anon-types -Wno-keyword-macro -Wno-gnu-auto-type -Wno-auto-decl-extensions -Wno-gnu-empty-initializer -Wno-fixed-enum-extension -pedantic -fno-exceptions -fno-stack-protector +mkdir -p build +clang -o build/build.exe bootstrap/build.c -g -march=native -std=gnu2x -Wall -Wextra -Wpedantic -Wno-nested-anon-types -Wno-keyword-macro -Wno-gnu-auto-type -Wno-auto-decl-extensions -Wno-gnu-empty-initializer -Wno-fixed-enum-extension -pedantic -fno-exceptions -fno-stack-protector -Wl,/INCREMENTAL:no +SET clang_exit_code=%errorlevel% +echo Clang exit code: %clang_exit_code% +if %clang_exit_code% neq 0 exit /b %clang_exit_code% +.\build\build.exe %* +SET builder_exit_code=%errorlevel% +echo Builder exit code: %builder_exit_code% +if %builder_exit_code% neq 0 exit /b %builder_exit_code%