Make it compile on windows

This commit is contained in:
flysand7 2023-07-28 19:53:06 +11:00
parent 5e177fae50
commit 1f811d7646
16 changed files with 292 additions and 64 deletions

View File

@ -7,10 +7,6 @@ import os
import sys
import pyjson5
dependencies = [
'nasm',
'llvm-ar'
]
# Parse command line arguments
arg_parser = argparse.ArgumentParser('build.py')
@ -23,17 +19,25 @@ arg_parser.add_argument('--cflags', nargs='*', default=[], help='Pass additional
args = arg_parser.parse_args()
compiler = args.compiler
print('==> Performing basic checks')
# Perform cleaning if required
def rm(path):
if os.path.exists(path):
os.remove(path)
if args.clean:
shutil.rmtree('bin')
shutil.rmtree('lib')
os.remove(os.path.join('src', 'ciabatta.c'))
os.remove('a')
if os.path.exists('bin'):
shutil.rmtree('bin')
if os.path.exists('lib'):
shutil.rmtree('lib')
rm(os.path.join('src', 'ciabatta.c'))
rm('a')
rm('a.exe')
rm('a.ilk')
rm('a.pdb')
sys.exit(0)
# Check host OS
print('==> Performing basic checks')
target = args.target
if target is None:
host_os = platform.system().lower()
@ -44,8 +48,31 @@ if target is None:
target = host_os
# Add compiler to dependencies
dependencies = [
'nasm',
'llvm-ar'
]
dependencies.append(args.compiler)
# Figure out the flags
includes = ['include']
cc = args.compiler
cc_defines = []
cc_flags = ['-nostdlib']
crt_file = 'crt.lib'
lib_file = 'cia.lib'
if args.mode == 'release':
cc_flags.append('-O2')
cc_defines.append('NDEBUG')
else: # 'debug'
cc_flags.append('-g')
cc_flags.append('-O0')
cc_defines.append('DEBUG')
if target != 'windows':
cc_flags.append('-fpic')
cc_defines.append(f'_CIA_OS_{target.upper()}')
# Check dependencies
print('==> Checking dependencies')
for dependency in dependencies:
@ -54,24 +81,6 @@ for dependency in dependencies:
sys.exit(1)
print(' -> Everything OK')
# Figure out the flags
includes = ['include']
cc = ''
cc_defines = []
cc_flags = ['-nostdlib']
crt_file = 'crt.lib'
lib_file = 'cia.lib'
cc = args.compiler
if args.mode == 'release':
cc_flags.append('-O2')
cc_defines.append('NDEBUG')
else: # 'debug'
cc_flags.append('-g')
cc_flags.append('-O0')
cc_defines.append('DEBUG')
cc_defines.append(f'_CIA_OS_{target.upper()}')
# Generate TinyRT headers for the target platform
print(f"==> Generating TinyRT header for {target}")
tinyrt_config_path = os.path.join('src', target, 'tinyrt.json')
@ -133,8 +142,8 @@ try:
if platform_config is None:
print(f" -> [ERROR] library config doesn't contain configuration for platform {target}")
for include in platform_config['includes']:
include_path = os.path.join(platform_config['path'], include)
ciabatta_header.write(f'#include "{include_path}"\n')
include_path = platform_config['path']
ciabatta_header.write(f'#include "{include_path}/{include}"\n')
ciabatta_header.write(f'#include "{target}/tinyrt-iface.h"\n')
ciabatta_header.write(f'#include <tinyrt.h>\n')
for tinyrt_source in platform_config['tinyrt']:
@ -193,7 +202,7 @@ def assemble(src, out):
def compile(srcs, out, extra_flags = ''):
flags = cc_flags_str + ' ' + extra_flags + ' '.join(args.cflags)
inputs = ' '.join(map(quote, srcs))
cmdline = f'{compiler} {flags} {inputs} -o {quote(out)}'
cmdline = f'{cc} {flags} {inputs} -o {quote(out)}'
print(' >', cmdline)
code = os.system(cmdline)
if code != 0:
@ -207,20 +216,34 @@ def archive(srcs, out):
if code != 0:
sys.exit(code)
# Ciabatta build spec
if not os.path.exists('lib'):
os.mkdir('lib')
if not os.path.exists('bin'):
os.mkdir('bin')
p = os.path.join
def p(path):
l = path.split('/')
return os.path.join(*l)
assemble(p('src', 'linux', 'crt-entry.asm'), p('bin', 'crt-entry.o'))
compile([p('src', 'linux', 'crt-ctors.c')], p('bin', 'crt-ctors.o'), '-fpic -c')
compile([p('src', 'ciabatta.c')], p('bin', 'ciabatta.o'), '-fpic -c')
archive([p('bin', 'crt-ctors.o'), p('bin', 'crt-entry.o')], p('lib', crt_file))
archive([p('bin', 'ciabatta.o'), ], p('lib', lib_file))
cia_lib = p(f'lib/{lib_file}')
crt_lib = p(f'lib/{crt_file}')
ciabatta_c = p('src/ciabatta.c')
ciabatta_o = p('bin/ciabatta.o')
if target == 'linux':
assemble(p('src/linux/crt-entry.asm'), p('bin/crt-entry.o'))
compile([p('src/linux/crt-ctors.c')], p('bin/crt-ctors.o'), '-c')
archive([p('bin/crt-ctors.o'), p('bin/crt-entry.o')], p('lib', crt_file))
elif target == 'windows':
assemble(p('src/windows/chkstk.asm'), p('bin/chkstk.o'))
compile([p('src/windows/crt-entry.c')], p('bin/crt-entry.o'), '-c')
archive([p('bin/crt-entry.o')], crt_lib)
compile([ciabatta_c], ciabatta_o, '-c')
archive([ciabatta_o], cia_lib)
if args.test:
compile([args.test, p('lib', lib_file), p('lib', crt_file)], 'a', '-pie')
if target == 'linux':
compile([args.test, cia_lib, crt_lib], 'a', '-pie')
elif target == 'windows':
compile([args.test, cia_lib, crt_lib], 'a.exe', '-lkernel32')

View File

@ -1,9 +1,16 @@
#pragma once
// Since we're re-defining noreturn below, this would mess
// with __declspec(noreturn) in windows headers, which
// is hidden behind a define. Good thing, because now we
// can override that define over here.
#if defined(_CIA_OS_WINDOWS)
#define DECLSPEC_NORETURN __declspec("noreturn")
#endif
// Pre-C23 keyword macros and stddef
#define static_assert _Static_assert
#define noreturn _Noreturn
#define NULL ((void *)0)
// Assert commonly-accepted platform-invariant sizes

View File

@ -5,7 +5,7 @@
int atexit(void (*func)(void));
int at_quick_exit(void (*func)(void));
noreturn void abort(void);
noreturn void exit(int code);
noreturn void _Exit(int code);
noreturn void quick_exit(int code);
[[noreturn]] void abort(void);
[[noreturn]] void exit(int code);
[[noreturn]] void _Exit(int code);
[[noreturn]] void quick_exit(int code);

View File

@ -41,7 +41,7 @@ static _RT_Status _rt_deinit();
// Program API
#if _RT_API_PROGRAM == 1
static noreturn void _rt_program_exit(int code);
[[noreturn]] static void _rt_program_exit(int code);
#endif
#if _RT_API_ENVIRONMENT == 1

View File

@ -25,12 +25,13 @@ int at_quick_exit(void (*func)(void)) {
return 0;
}
noreturn void abort(void) {
[[noreturn]] void abort(void) {
// TODO: Ideally do a debug trap if the process is being debugged
_Exit(-1);
_rt_program_exit(1);
__builtin_unreachable();
}
noreturn void exit(int code) {
[[noreturn]] void exit(int code) {
for(u64 i = n_atexit_handlers-1; i-- != 0; ) {
void (*handler)(void) = atexit_handlers[i];
handler();
@ -38,17 +39,20 @@ noreturn void exit(int code) {
// TODO(bumbread): flush all the unflushed file streams
// TODO(bumbread): close all file streams and delete temporary files
_rt_program_exit(code);
__builtin_unreachable();
}
noreturn void _Exit(int code) {
[[noreturn]] void _Exit(int code) {
_rt_program_exit(code);
__builtin_unreachable();
}
noreturn void quick_exit(int code) {
[[noreturn]] void quick_exit(int code) {
for(u64 i = n_at_quick_exit_handlers-1; i-- != 0; ) {
void (*handler)(void) = at_quick_exit_handlers[i];
handler();
}
_rt_program_exit(code);
__builtin_unreachable();
}

View File

@ -44,8 +44,13 @@ platforms: [
{
name: "windows",
path: "windows",
includes: [],
tinyrt: [],
includes: [
"windows.c"
],
tinyrt: [
"tinyrt.c",
"cia-init.c"
],
},
],

View File

@ -1,9 +1,6 @@
// See src/tinyrt.h file for the interface this file implements
static _RT_File _rt_file_stdin;
static _RT_File _rt_file_stderr;
static _RT_Status _rt_file_std_handles_init() {
_rt_file_stdin.fd = 0;
_rt_file_stdin.flags = _RT_FILE_READ;
@ -68,6 +65,6 @@ static _RT_Status _rt_file_close(_RT_File *file) {
return _RT_STATUS_OK;
}
static noreturn void _rt_program_exit(int code) {
[[noreturn]] static void _rt_program_exit(int code) {
syscall_exit(code);
}

26
src/windows/chkstk.asm Normal file
View File

@ -0,0 +1,26 @@
bits 64
segment .text
global __chkstk
__chkstk:
sub rsp, 0x10
mov [rsp], r10
mov [rsp+0x8], r11
xor r11, r11
lea r10, [rsp+0x18]
sub r10, rax
cmovb r10, r11
mov r11, gs:[0x10]
cmp r10, r11
jnb .end
and r10w, 0xf000
.loop:
lea r11, [r11-0x1000]
mov byte [r11], 0x0
cmp r10, r11
jnz .loop
.end:
mov r10, [rsp]
mov r11, [rsp+0x8]
add rsp, 0x10
ret

6
src/windows/cia-init.c Normal file
View File

@ -0,0 +1,6 @@
static void _fileapi_init();
void _cia_init() {
_fileapi_init();
}

62
src/windows/crt-entry.c Normal file
View File

@ -0,0 +1,62 @@
#include <cia-def.h>
#include "windows.c"
extern int main(int argc, char **argv, char **envp);
extern void _cia_init();
void mainCRTStartup() {
_cia_init();
__security_init_cookie();
main(0, NULL, NULL);
ExitProcess(0);
}
void wmainCRTStartup() {
ExitProcess(0);
}
void _WinMainCRTStartup() {
ExitProcess(0);
}
void wWinMainCRTStartup() {
ExitProcess(0);
}
BOOL _DllMainCRTStartup(HINSTANCE instance, DWORD reason, void *_reserved) {
switch(reason) {
case DLL_PROCESS_ATTACH: {
// Initialize once for each new process.
// Return FALSE to fail DLL load.
} break;
case DLL_THREAD_ATTACH: {
// Do thread-specific initialization.
} break;
case DLL_THREAD_DETACH: {
// Do thread-specific cleanup.
} break;
case DLL_PROCESS_DETACH: {
// Perform any necessary cleanup.
} break;
}
return TRUE;
}
u64 __security_cookie;
void __security_init_cookie() {
// They say it's a random number so I generated
// one using numbergenerator.org
__security_cookie = 0xb26e04cc62ba48aULL;
}
void __security_check_cookie(u64 retrieved) {
if(__security_cookie != retrieved) {
char buf[] = "Buffer overrun detected!\n";
HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(stdout, buf, sizeof buf, NULL, NULL);
// TODO: abort-like behaviour here
ExitProcess(1);
}
}

76
src/windows/tinyrt.c Normal file
View File

@ -0,0 +1,76 @@
// See src/tinyrt.h file for the interface this file implements
static _RT_Status _rt_file_std_handles_init() {
_rt_file_stdin.handle = GetStdHandle(STD_INPUT_HANDLE);
_rt_file_stdin.flags = _RT_FILE_READ;
_rt_file_stdout.handle = GetStdHandle(STD_OUTPUT_HANDLE);
_rt_file_stdout.flags = _RT_FILE_WRITE;
_rt_file_stderr.handle = GetStdHandle(STD_ERROR_HANDLE);
_rt_file_stdout.flags = _RT_FILE_WRITE;
return _RT_STATUS_OK;
}
static _RT_Status _rt_file_open(_RT_File *file, char const *name, int _rt_flags) {
u32 access = 0;
if(_rt_flags & _RT_FILE_READ) access |= GENERIC_READ;
if(_rt_flags & _RT_FILE_WRITE) access |= GENERIC_WRITE;
u32 share = 0;
if((_rt_flags & _RT_FILE_READ) == 0) share |= FILE_SHARE_READ;
u32 creation = 0;
if(_rt_flags & _RT_FILE_TRUNCATE) {
creation = TRUNCATE_EXISTING;
}
else if(_rt_flags & _RT_FILE_CREATE) {
if(_rt_flags & _RT_FILE_EXCLUSIVE) creation = CREATE_ALWAYS;
else creation = CREATE_NEW;
}
else {
if(_rt_flags & _RT_FILE_EXCLUSIVE) creation = OPEN_ALWAYS;
else creation = OPEN_EXISTING;
}
HANDLE handle = CreateFileA(name, access, share, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
if(file == INVALID_HANDLE_VALUE) {
// ERROR_FILE_NOT_FOUND
// ERROR_PATH_NOT_FOUND
// ERROR_ACCESS_DENIED
// ERROR_NOT_ENOUGH_MEMORY
return _RT_STATUS_FILE_IO_ERROR;
}
file->handle = handle;
file->flags = _rt_flags;
return _RT_STATUS_OK;
}
static _RT_Status _rt_file_read(u64 size, void *buffer, _RT_File *from, u64 *out_bytes_read) {
*out_bytes_read = 0;
BOOL ok = ReadFile(from->handle, buffer, size, (unsigned long *)out_bytes_read, NULL);
if(!ok) {
return _RT_STATUS_FILE_IO_ERROR;
}
return _RT_STATUS_OK;
}
static _RT_Status _rt_file_write(_RT_File *to, u64 size, void *buffer, u64 *out_bytes_written) {
*out_bytes_written = 0;
BOOL ok = WriteFile(to->handle, buffer, size, (unsigned long *)out_bytes_written, NULL);
if(!ok) {
return _RT_STATUS_FILE_IO_ERROR;
}
return _RT_STATUS_OK;
}
static _RT_Status _rt_file_close(_RT_File *file) {
BOOL ok = CloseHandle(file->handle);
if(!ok) {
return _RT_STATUS_FILE_BAD_FILE;
}
return _RT_STATUS_OK;
}
[[noreturn]] static void _rt_program_exit(int code) {
ExitProcess(code);
__builtin_unreachable();
}

12
src/windows/tinyrt.json Normal file
View File

@ -0,0 +1,12 @@
{
// This file contains TinyRT API subsets that are exported
// for this operating system, one name per line. This file
// is used in a build script to generate tinyrt_macro file
// and to resolve higher-level API dependencies
rt_api_file: true,
rt_api_program: true,
rt_api_shell: false,
}

View File

6
src/windows/windows.c Normal file
View File

@ -0,0 +1,6 @@
#define NOGDI
#define NOUSER
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

View File

@ -1,9 +1,4 @@
#include <cia-def.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
char string[] = "Hello, world!\n";
fwrite(string, 1, sizeof string-1, stdout);
return 0;
}
int main(int argc, char **argv) {
return 0;
}

9
tests/hello.c Normal file
View File

@ -0,0 +1,9 @@
#include <cia-def.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
char string[] = "Hello, world!\n";
fwrite(string, 1, sizeof string-1, stdout);
return 0;
}