diff --git a/base/runtime/entry_unix.odin b/base/runtime/entry_unix.odin index e49698e6e..7d7252625 100644 --- a/base/runtime/entry_unix.odin +++ b/base/runtime/entry_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku //+no-instrumentation package runtime diff --git a/base/runtime/heap_allocator_unix.odin b/base/runtime/heap_allocator_unix.odin index 2b6698885..a8a4e9169 100644 --- a/base/runtime/heap_allocator_unix.odin +++ b/base/runtime/heap_allocator_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku //+private package runtime diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin index 9cd065ff6..46ce51166 100644 --- a/base/runtime/os_specific_bsd.odin +++ b/base/runtime/os_specific_bsd.odin @@ -1,4 +1,4 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd //+private package runtime @@ -9,7 +9,11 @@ foreign libc { @(link_name="write") _unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int --- - __error :: proc() -> ^i32 --- + when ODIN_OS == .NetBSD { + @(link_name="__errno") __error :: proc() -> ^i32 --- + } else { + __error :: proc() -> ^i32 --- + } } _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { diff --git a/build_odin.sh b/build_odin.sh index 76c4e1198..7e0d0e585 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -71,6 +71,10 @@ FreeBSD) CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" ;; +NetBSD) + CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" + ;; Linux) CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)" diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin index 7af763706..d28a24f56 100644 --- a/core/c/libc/errno.odin +++ b/core/c/libc/errno.odin @@ -40,7 +40,7 @@ when ODIN_OS == .FreeBSD { ERANGE :: 34 } -when ODIN_OS == .OpenBSD { +when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { @(private="file") @(default_calling_convention="c") foreign libc { diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index b83ddecc8..f17d3bd06 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -83,7 +83,7 @@ when ODIN_OS == .Linux { } } -when ODIN_OS == .OpenBSD { +when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { fpos_t :: distinct i64 _IOFBF :: 0 diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin index 4c4280f30..924cf4aec 100644 --- a/core/c/libc/time.odin +++ b/core/c/libc/time.odin @@ -45,7 +45,7 @@ when ODIN_OS == .Windows { } } -when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku { +when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku { @(default_calling_convention="c") foreign libc { // 7.27.2 Time manipulation functions diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin index cbce220d4..a41fe7fac 100644 --- a/core/c/libc/wctype.odin +++ b/core/c/libc/wctype.odin @@ -22,7 +22,7 @@ when ODIN_OS == .Windows { wctrans_t :: distinct int wctype_t :: distinct u32 -} else when ODIN_OS == .OpenBSD { +} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { wctrans_t :: distinct rawptr wctype_t :: distinct rawptr diff --git a/core/crypto/rand_bsd.odin b/core/crypto/rand_bsd.odin index a31e4f2b2..641b72933 100644 --- a/core/crypto/rand_bsd.odin +++ b/core/crypto/rand_bsd.odin @@ -1,4 +1,4 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd package crypto foreign import libc "system:c" diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin index 4ea61ec91..46fb881b3 100644 --- a/core/crypto/rand_generic.odin +++ b/core/crypto/rand_generic.odin @@ -2,6 +2,7 @@ //+build !windows //+build !openbsd //+build !freebsd +//+build !netbsd //+build !darwin //+build !js package crypto diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin index 97f70b576..fb0270ea3 100644 --- a/core/dynlib/lib_unix.odin +++ b/core/dynlib/lib_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd //+private package dynlib diff --git a/core/mem/virtual/virtual_bsd.odin b/core/mem/virtual/virtual_bsd.odin index 7568084c0..12e7818ef 100644 --- a/core/mem/virtual/virtual_bsd.odin +++ b/core/mem/virtual/virtual_bsd.odin @@ -1,9 +1,7 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd //+private package mem_virtual - - _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { return nil, nil } @@ -11,16 +9,18 @@ _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Err _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { return nil } + _decommit :: proc "contextless" (data: rawptr, size: uint) { } + _release :: proc "contextless" (data: rawptr, size: uint) { } + _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { return false } _platform_memory_init :: proc() { - } _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { diff --git a/core/net/dns.odin b/core/net/dns.odin index 48cd8bf29..408fd201b 100644 --- a/core/net/dns.odin +++ b/core/net/dns.odin @@ -30,7 +30,7 @@ when ODIN_OS == .Windows { resolv_conf = "", hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts", } -} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD { +} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{ resolv_conf = "/etc/resolv.conf", hosts_file = "/etc/hosts", @@ -854,4 +854,4 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator } return _records[:], true -} \ No newline at end of file +} diff --git a/core/os/dir_freebsd.odin b/core/os/dir_bsd.odin similarity index 98% rename from core/os/dir_freebsd.odin rename to core/os/dir_bsd.odin index 2965182cd..c0dc8ad1f 100644 --- a/core/os/dir_freebsd.odin +++ b/core/os/dir_bsd.odin @@ -1,3 +1,4 @@ +//+build freebsd, netbsd package os import "core:mem" diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin new file mode 100644 index 000000000..e8e551340 --- /dev/null +++ b/core/os/os_netbsd.odin @@ -0,0 +1,780 @@ +package os + +foreign import dl "system:dl" +foreign import libc "system:c" + +import "base:runtime" +import "core:strings" +import "core:sys/unix" +import "core:c" + +Handle :: distinct i32 +File_Time :: distinct u64 +Errno :: distinct i32 + +INVALID_HANDLE :: ~Handle(0) + +ERROR_NONE: Errno : 0 /* No error */ +EPERM: Errno : 1 /* Operation not permitted */ +ENOENT: Errno : 2 /* No such file or directory */ +EINTR: Errno : 4 /* Interrupted system call */ +ESRCH: Errno : 3 /* No such process */ +EIO: Errno : 5 /* Input/output error */ +ENXIO: Errno : 6 /* Device not configured */ +E2BIG: Errno : 7 /* Argument list too long */ +ENOEXEC: Errno : 8 /* Exec format error */ +EBADF: Errno : 9 /* Bad file descriptor */ +ECHILD: Errno : 10 /* No child processes */ +EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */ +ENOMEM: Errno : 12 /* Cannot allocate memory */ +EACCES: Errno : 13 /* Permission denied */ +EFAULT: Errno : 14 /* Bad address */ +ENOTBLK: Errno : 15 /* Block device required */ +EBUSY: Errno : 16 /* Device busy */ +EEXIST: Errno : 17 /* File exists */ +EXDEV: Errno : 18 /* Cross-device link */ +ENODEV: Errno : 19 /* Operation not supported by device */ +ENOTDIR: Errno : 20 /* Not a directory */ +EISDIR: Errno : 21 /* Is a directory */ +EINVAL: Errno : 22 /* Invalid argument */ +ENFILE: Errno : 23 /* Too many open files in system */ +EMFILE: Errno : 24 /* Too many open files */ +ENOTTY: Errno : 25 /* Inappropriate ioctl for device */ +ETXTBSY: Errno : 26 /* Text file busy */ +EFBIG: Errno : 27 /* File too large */ +ENOSPC: Errno : 28 /* No space left on device */ +ESPIPE: Errno : 29 /* Illegal seek */ +EROFS: Errno : 30 /* Read-only file system */ +EMLINK: Errno : 31 /* Too many links */ +EPIPE: Errno : 32 /* Broken pipe */ + +/* math software */ +EDOM: Errno : 33 /* Numerical argument out of domain */ +ERANGE: Errno : 34 /* Result too large or too small */ + +/* non-blocking and interrupt i/o */ +EAGAIN: Errno : 35 /* Resource temporarily unavailable */ +EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ +EINPROGRESS: Errno : 36 /* Operation now in progress */ +EALREADY: Errno : 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +ENOTSOCK: Errno : 38 /* Socket operation on non-socket */ +EDESTADDRREQ: Errno : 39 /* Destination address required */ +EMSGSIZE: Errno : 40 /* Message too long */ +EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */ +ENOPROTOOPT: Errno : 42 /* Protocol option not available */ +EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */ +ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */ +EOPNOTSUPP: Errno : 45 /* Operation not supported */ +EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */ +EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */ +EADDRINUSE: Errno : 48 /* Address already in use */ +EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +ENETDOWN: Errno : 50 /* Network is down */ +ENETUNREACH: Errno : 51 /* Network is unreachable */ +ENETRESET: Errno : 52 /* Network dropped connection on reset */ +ECONNABORTED: Errno : 53 /* Software caused connection abort */ +ECONNRESET: Errno : 54 /* Connection reset by peer */ +ENOBUFS: Errno : 55 /* No buffer space available */ +EISCONN: Errno : 56 /* Socket is already connected */ +ENOTCONN: Errno : 57 /* Socket is not connected */ +ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */ +ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */ +ETIMEDOUT: Errno : 60 /* Operation timed out */ +ECONNREFUSED: Errno : 61 /* Connection refused */ + +ELOOP: Errno : 62 /* Too many levels of symbolic links */ +ENAMETOOLONG: Errno : 63 /* File name too long */ + +/* should be rearranged */ +EHOSTDOWN: Errno : 64 /* Host is down */ +EHOSTUNREACH: Errno : 65 /* No route to host */ +ENOTEMPTY: Errno : 66 /* Directory not empty */ + +/* quotas & mush */ +EPROCLIM: Errno : 67 /* Too many processes */ +EUSERS: Errno : 68 /* Too many users */ +EDQUOT: Errno : 69 /* Disc quota exceeded */ + +/* Network File System */ +ESTALE: Errno : 70 /* Stale NFS file handle */ +EREMOTE: Errno : 71 /* Too many levels of remote in path */ +EBADRPC: Errno : 72 /* RPC struct is bad */ +ERPCMISMATCH: Errno : 73 /* RPC version wrong */ +EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */ +EPROGMISMATCH: Errno : 75 /* Program version wrong */ +EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */ + +ENOLCK: Errno : 77 /* No locks available */ +ENOSYS: Errno : 78 /* Function not implemented */ + +EFTYPE: Errno : 79 /* Inappropriate file type or format */ +EAUTH: Errno : 80 /* Authentication error */ +ENEEDAUTH: Errno : 81 /* Need authenticator */ + +/* SystemV IPC */ +EIDRM: Errno : 82 /* Identifier removed */ +ENOMSG: Errno : 83 /* No message of desired type */ +EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */ + +/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ +EILSEQ: Errno : 85 /* Illegal byte sequence */ + +/* From IEEE Std 1003.1-2001 */ +/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ +ENOTSUP: Errno : 86 /* Not supported */ + +/* Realtime option errors */ +ECANCELED: Errno : 87 /* Operation canceled */ + +/* Realtime, XSI STREAMS option errors */ +EBADMSG: Errno : 88 /* Bad or Corrupt message */ + +/* XSI STREAMS option errors */ +ENODATA: Errno : 89 /* No message available */ +ENOSR: Errno : 90 /* No STREAM resources */ +ENOSTR: Errno : 91 /* Not a STREAM */ +ETIME: Errno : 92 /* STREAM ioctl timeout */ + +/* File system extended attribute errors */ +ENOATTR: Errno : 93 /* Attribute not found */ + +/* Realtime, XSI STREAMS option errors */ +EMULTIHOP: Errno : 94 /* Multihop attempted */ +ENOLINK: Errno : 95 /* Link has been severed */ +EPROTO: Errno : 96 /* Protocol error */ + +/* Robust mutexes */ +EOWNERDEAD: Errno : 97 /* Previous owner died */ +ENOTRECOVERABLE: Errno : 98 /* State not recoverable */ + +ELAST: Errno : 98 /* Must equal largest errno */ + +/* end of errno */ + +O_RDONLY :: 0x000000000 +O_WRONLY :: 0x000000001 +O_RDWR :: 0x000000002 +O_CREATE :: 0x000000200 +O_EXCL :: 0x000000800 +O_NOCTTY :: 0x000008000 +O_TRUNC :: 0x000000400 +O_NONBLOCK :: 0x000000004 +O_APPEND :: 0x000000008 +O_SYNC :: 0x000000080 +O_ASYNC :: 0x000000040 +O_CLOEXEC :: 0x000400000 + +RTLD_LAZY :: 0x001 +RTLD_NOW :: 0x002 +RTLD_GLOBAL :: 0x100 +RTLD_LOCAL :: 0x200 +RTLD_TRACE :: 0x200 +RTLD_NODELETE :: 0x01000 +RTLD_NOLOAD :: 0x02000 + +F_GETPATH :: 15 + +MAX_PATH :: 1024 +MAXNAMLEN :: 511 + +args := _alloc_command_line_arguments() + +Unix_File_Time :: struct { + seconds: time_t, + nanoseconds: c.long, +} + +dev_t :: u64 +ino_t :: u64 +nlink_t :: u32 +off_t :: i64 +mode_t :: u16 +pid_t :: u32 +uid_t :: u32 +gid_t :: u32 +blkcnt_t :: i64 +blksize_t :: i32 +fflags_t :: u32 +time_t :: i64 + +OS_Stat :: struct { + device_id: dev_t, + mode: mode_t, + _padding0: i16, + ino: ino_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + _padding1: i32, + rdev: dev_t, + + last_access: Unix_File_Time, + modified: Unix_File_Time, + status_change: Unix_File_Time, + birthtime: Unix_File_Time, + + size: off_t, + blocks: blkcnt_t, + block_size: blksize_t, + + flags: fflags_t, + gen: u32, + lspare: [2]u32, +} + +Dirent :: struct { + ino: ino_t, + reclen: u16, + namlen: u16, + type: u8, + name: [MAXNAMLEN + 1]byte, +} + +Dir :: distinct rawptr // DIR* + +// File type +S_IFMT :: 0o170000 // Type of file mask +S_IFIFO :: 0o010000 // Named pipe (fifo) +S_IFCHR :: 0o020000 // Character special +S_IFDIR :: 0o040000 // Directory +S_IFBLK :: 0o060000 // Block special +S_IFREG :: 0o100000 // Regular +S_IFLNK :: 0o120000 // Symbolic link +S_IFSOCK :: 0o140000 // Socket + +// File mode +// Read, write, execute/search by owner +S_IRWXU :: 0o0700 // RWX mask for owner +S_IRUSR :: 0o0400 // R for owner +S_IWUSR :: 0o0200 // W for owner +S_IXUSR :: 0o0100 // X for owner + +// Read, write, execute/search by group +S_IRWXG :: 0o0070 // RWX mask for group +S_IRGRP :: 0o0040 // R for group +S_IWGRP :: 0o0020 // W for group +S_IXGRP :: 0o0010 // X for group + +// Read, write, execute/search by others +S_IRWXO :: 0o0007 // RWX mask for other +S_IROTH :: 0o0004 // R for other +S_IWOTH :: 0o0002 // W for other +S_IXOTH :: 0o0001 // X for other + +S_ISUID :: 0o4000 // Set user id on execution +S_ISGID :: 0o2000 // Set group id on execution +S_ISVTX :: 0o1000 // Directory restrcted delete + +S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } + +F_OK :: 0 // Test for file existance +X_OK :: 1 // Test for execute permission +W_OK :: 2 // Test for write permission +R_OK :: 4 // Test for read permission + +foreign libc { + @(link_name="__errno") __errno_location :: proc() -> ^c.int --- + + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- + @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- + @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- + @(link_name="__lstat50") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="__fstat50") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- + @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- + @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- + @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- + @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- + @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- + @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int --- + + @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- + @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- + @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- + + @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- + @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- + @(link_name="free") _unix_free :: proc(ptr: rawptr) --- + @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- + + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- + + @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- +} + +foreign dl { + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- + @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- + @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- +} + +// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end. + +is_path_separator :: proc(r: rune) -> bool { + return r == '/' +} + +get_last_error :: proc "contextless" () -> int { + return int(__errno_location()^) +} + +open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + handle := _unix_open(cstr, c.int(flags), c.int(mode)) + if handle == -1 { + return INVALID_HANDLE, Errno(get_last_error()) + } + return handle, ERROR_NONE +} + +close :: proc(fd: Handle) -> Errno { + result := _unix_close(fd) + if result == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +// We set a max of 1GB to keep alignment and to be safe. +@(private) +MAX_RW :: 1 << 30 + +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + to_read := min(c.size_t(len(data)), MAX_RW) + bytes_read := _unix_read(fd, &data[0], to_read) + if bytes_read == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_read), ERROR_NONE +} + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if len(data) == 0 { + return 0, ERROR_NONE + } + + to_write := min(c.size_t(len(data)), MAX_RW) + bytes_written := _unix_write(fd, &data[0], to_write) + if bytes_written == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_written), ERROR_NONE +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + res := _unix_seek(fd, offset, c.int(whence)) + if res == -1 { + return -1, Errno(get_last_error()) + } + return res, ERROR_NONE +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return -1, err + } + return s.size, ERROR_NONE +} + +rename :: proc(old_path, new_path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) + new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) + res := _unix_rename(old_path_cstr, new_path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove :: proc(path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_unlink(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove_directory :: proc(path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_rmdir(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +exists :: proc(path: string) -> bool { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cpath := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_access(cpath, O_RDONLY) + return res == 0 +} + +fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { + result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg)) + if result < 0 { + return 0, Errno(get_last_error()) + } + return int(result), ERROR_NONE +} + +// NOTE(bill): Uses startup to initialize it + +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 + +last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { + s, err := _stat(name) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +@private +_stat :: proc(path: string) -> (OS_Stat, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + s: OS_Stat = --- + result := _unix_lstat(cstr, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_lstat :: proc(path: string) -> (OS_Stat, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_lstat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { + s: OS_Stat = --- + result := _unix_fstat(fd, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { + dirp := _unix_fdopendir(fd) + if dirp == cast(Dir)nil { + return nil, Errno(get_last_error()) + } + return dirp, ERROR_NONE +} + +@private +_closedir :: proc(dirp: Dir) -> Errno { + rc := _unix_closedir(dirp) + if rc != 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +@private +_rewinddir :: proc(dirp: Dir) { + _unix_rewinddir(dirp) +} + +@private +_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { + result: ^Dirent + rc := _unix_readdir_r(dirp, &entry, &result) + + if rc != 0 { + err = Errno(get_last_error()) + return + } + err = ERROR_NONE + + if result == nil { + end_of_stream = true + return + } + + return +} + +@private +_readlink :: proc(path: string) -> (string, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + bufsz : uint = MAX_PATH + buf := make([]byte, MAX_PATH) + for { + rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) + if rc == -1 { + delete(buf) + return "", Errno(get_last_error()) + } else if rc == int(bufsz) { + bufsz += MAX_PATH + delete(buf) + buf = make([]byte, bufsz) + } else { + return strings.string_from_ptr(&buf[0], rc), ERROR_NONE + } + } + + return "", Errno{} +} + +absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { + buf: [MAX_PATH]byte + _, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) + if err != ERROR_NONE { + return "", err + } + + path := strings.clone_from_cstring(cstring(&buf[0])) + return path, err +} + +absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { + rel := rel + if rel == "" { + rel = "." + } + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) + + path_ptr := _unix_realpath(rel_cstr, nil) + if path_ptr == nil { + return "", Errno(get_last_error()) + } + defer _unix_free(path_ptr) + + path_cstr := transmute(cstring)path_ptr + path = strings.clone( string(path_cstr) ) + + return path, ERROR_NONE +} + +access :: proc(path: string, mask: int) -> (bool, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + + cstr := strings.clone_to_cstring(path, context.temp_allocator) + result := _unix_access(cstr, c.int(mask)) + if result == -1 { + return false, Errno(get_last_error()) + } + return true, ERROR_NONE +} + +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + + path_str := strings.clone_to_cstring(key, context.temp_allocator) + cstr := _unix_getenv(path_str) + if cstr == nil { + return "", false + } + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return +} + +get_current_directory :: proc() -> string { + // NOTE(tetra): I would use PATH_MAX here, but I was not able to find + // an authoritative value for it across all systems. + // The largest value I could find was 4096, so might as well use the page size. + page_size := get_page_size() + buf := make([dynamic]u8, page_size) + #no_bounds_check for { + cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))) + if cwd != nil { + return string(cwd) + } + if Errno(get_last_error()) != ERANGE { + delete(buf) + return "" + } + resize(&buf, len(buf)+page_size) + } + unreachable() +} + +set_current_directory :: proc(path: string) -> (err: Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_chdir(cstr) + if res == -1 do return Errno(get_last_error()) + return ERROR_NONE +} + +exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() + _unix_exit(c.int(code)) +} + +current_thread_id :: proc "contextless" () -> int { + return cast(int) unix.pthread_self() +} + +dlopen :: proc(filename: string, flags: int) -> rawptr { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(filename, context.temp_allocator) + handle := _unix_dlopen(cstr, c.int(flags)) + return handle +} + +dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { + assert(handle != nil) + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(symbol, context.temp_allocator) + proc_handle := _unix_dlsym(handle, cstr) + return proc_handle +} + +dlclose :: proc(handle: rawptr) -> bool { + assert(handle != nil) + return _unix_dlclose(handle) == 0 +} + +dlerror :: proc() -> string { + return string(_unix_dlerror()) +} + +get_page_size :: proc() -> int { + // NOTE(tetra): The page size never changes, so why do anything complicated + // if we don't have to. + @static page_size := -1 + if page_size != -1 do return page_size + + page_size = int(_unix_getpagesize()) + return page_size +} + +@(private) +_processor_core_count :: proc() -> int { + count : int = 0 + count_size := size_of(count) + if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 { + if count > 0 { + return count + } + } + + return 1 +} + +_alloc_command_line_arguments :: proc() -> []string { + res := make([]string, len(runtime.args__)) + for arg, i in runtime.args__ { + res[i] = string(arg) + } + return res +} diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 5e83c0e16..3bd62dfc7 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package os import "core:time" diff --git a/core/os/stream.odin b/core/os/stream.odin index 25f31218c..9a168b95c 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -32,7 +32,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, } case .Read_At: - when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) { + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = read_at(fd, p, offset) n = i64(n_int) if n == 0 && os_err == 0 { @@ -46,7 +46,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, err = .EOF } case .Write_At: - when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) { + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = write_at(fd, p, offset) n = i64(n_int) if n == 0 && os_err == 0 { @@ -60,7 +60,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, case .Destroy: err = .Empty case .Query: - when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku { + when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku { return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query}) } else { return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index 9beda5557..a4b27b027 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd package filepath when ODIN_OS == .Darwin { @@ -61,7 +61,7 @@ when ODIN_OS == .Darwin { foreign libc { @(link_name="__error") __error :: proc() -> ^i32 --- } -} else when ODIN_OS == .OpenBSD { +} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { @(private) foreign libc { @(link_name="__errno") __error :: proc() -> ^i32 --- diff --git a/core/prof/spall/spall_unix.odin b/core/prof/spall/spall_unix.odin index 7915f8c32..e6199d86b 100644 --- a/core/prof/spall/spall_unix.odin +++ b/core/prof/spall/spall_unix.odin @@ -1,5 +1,5 @@ //+private -//+build darwin, freebsd, openbsd +//+build darwin, freebsd, openbsd, netbsd package spall // Only for types. diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin new file mode 100644 index 000000000..08f7cd6c9 --- /dev/null +++ b/core/sync/futex_netbsd.odin @@ -0,0 +1,74 @@ +//+private +package sync + +import "base:intrinsics" +import "core:time" +import "core:c" +import "core:sys/unix" + +foreign import libc "system:c" + +FUTEX_PRIVATE_FLAG :: 128 + +FUTEX_WAIT_PRIVATE :: 0 | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_PRIVATE :: 1 | FUTEX_PRIVATE_FLAG + +EINTR :: 4 /* Interrupted system call */ +EAGAIN :: 35 /* Resource temporarily unavailable */ +ETIMEDOUT :: 60 /* Operation timed out */ + +Time_Spec :: struct { + time_sec: uint, + time_nsec: uint, +} + +get_last_error :: proc "contextless" () -> int { + foreign libc { + __errno :: proc() -> ^c.int --- + } + return int(__errno()^) +} + +_futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0, 0, 0) == -1 { + switch get_last_error() { + case EINTR, EAGAIN: + return true + case: + _panic("futex_wait failure") + } + } + return true +} + +_futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, duration: time.Duration) -> bool { + if duration <= 0 { + return false + } + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{ + time_sec = cast(uint)(duration / 1e9), + time_nsec = cast(uint)(duration % 1e9), + }, 0, 0) == -1 { + switch get_last_error() { + case EINTR, EAGAIN: + return true + case ETIMEDOUT: + return false + case: + _panic("futex_wait_with_timeout failure") + } + } + return true +} + +_futex_signal :: proc "contextless" (futex: ^Futex) { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0) == -1 { + _panic("futex_wake_single failure") + } +} + +_futex_broadcast :: proc "contextless" (futex: ^Futex) { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0) == -1 { + _panic("_futex_wake_all failure") + } +} diff --git a/core/sync/primitives_netbsd.odin b/core/sync/primitives_netbsd.odin new file mode 100644 index 000000000..042e744e8 --- /dev/null +++ b/core/sync/primitives_netbsd.odin @@ -0,0 +1,8 @@ +//+private +package sync + +import "core:sys/unix" + +_current_thread_id :: proc "contextless" () -> int { + return cast(int) unix.pthread_self() +} diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index 2b8f9852f..63af853d0 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -67,8 +67,8 @@ init_cpu_features :: proc "c" () { try_set(&set, .os_xsave, 27, ecx1) try_set(&set, .rdrand, 30, ecx1) - when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { - // xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1 + when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { + // xgetbv is an illegal instruction under FreeBSD 13, OpenBSD 7.1 and NetBSD 10 // return before probing further cpu_features = set return @@ -133,4 +133,4 @@ init_cpu_name :: proc "c" () { brand = brand[:len(brand) - 1] } cpu_name = brand -} \ No newline at end of file +} diff --git a/core/sys/info/platform_openbsd.odin b/core/sys/info/platform_bsd.odin similarity index 89% rename from core/sys/info/platform_openbsd.odin rename to core/sys/info/platform_bsd.odin index 3a7c07570..e2273d253 100644 --- a/core/sys/info/platform_openbsd.odin +++ b/core/sys/info/platform_bsd.odin @@ -1,3 +1,4 @@ +//+build openbsd, netbsd package sysinfo import sys "core:sys/unix" @@ -10,12 +11,16 @@ version_string_buf: [1024]u8 @(init, private) init_os_version :: proc () { - os_version.platform = .OpenBSD + when ODIN_OS == .NetBSD { + os_version.platform = .NetBSD + } else { + os_version.platform = .OpenBSD + } kernel_version_buf: [1024]u8 b := strings.builder_from_bytes(version_string_buf[:]) - // Retrieve kernel info using `sysctl`, e.g. OpenBSD + // Retrieve kernel info using `sysctl`, e.g. OpenBSD and NetBSD mib := []i32{sys.CTL_KERN, sys.KERN_OSTYPE} if !sys.sysctl(mib, &kernel_version_buf) { return @@ -68,4 +73,4 @@ init_ram :: proc() { if sys.sysctl(mib, &mem_size) { ram.total_ram = int(mem_size) } -} \ No newline at end of file +} diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin new file mode 100644 index 000000000..afbbc321c --- /dev/null +++ b/core/sys/unix/pthread_netbsd.odin @@ -0,0 +1,102 @@ +package unix + +import "core:c" + +pthread_t :: distinct u64 + +SEM_T_SIZE :: 8 + +PTHREAD_CONDATTR_T_SIZE :: 16 +PTHREAD_MUTEXATTR_T_SIZE :: 16 +PTHREAD_RWLOCKATTR_T_SIZE :: 16 +PTHREAD_BARRIERATTR_T_SIZE :: 16 + +PTHREAD_COND_T_SIZE :: 40 +PTHREAD_MUTEX_T_SIZE :: 48 +PTHREAD_RWLOCK_T_SIZE :: 64 +PTHREAD_BARRIER_T_SIZE :: 48 +PTHREAD_ATTR_T_SIZE :: 16 + +pthread_cond_t :: struct #align(8) { + _: [PTHREAD_COND_T_SIZE] c.char, +} + +pthread_mutex_t :: struct #align(8) { + _: [PTHREAD_MUTEX_T_SIZE] c.char, +} + +pthread_rwlock_t :: struct #align(8) { + _: [PTHREAD_RWLOCK_T_SIZE] c.char, +} + +pthread_barrier_t :: struct #align(8) { + _: [PTHREAD_BARRIER_T_SIZE] c.char, +} + +pthread_attr_t :: struct #align(8) { + _: [PTHREAD_ATTR_T_SIZE] c.char, +} + +pthread_condattr_t :: struct #align(8) { + _: [PTHREAD_CONDATTR_T_SIZE] c.char, +} + +pthread_mutexattr_t :: struct #align(8) { + _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, +} + +pthread_rwlockattr_t :: struct #align(8) { + _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, +} + +pthread_barrierattr_t :: struct #align(8) { + _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, +} + +PTHREAD_MUTEX_NORMAL :: 0 +PTHREAD_MUTEX_ERRORCHECK :: 1 +PTHREAD_MUTEX_RECURSIVE :: 2 + +PTHREAD_CREATE_JOINABLE :: 0 +PTHREAD_CREATE_DETACHED :: 1 +PTHREAD_INHERIT_SCHED :: 0 +PTHREAD_EXPLICIT_SCHED :: 1 +PTHREAD_PROCESS_PRIVATE :: 0 +PTHREAD_PROCESS_SHARED :: 1 + +SCHED_NONE :: -1 +SCHED_OTHER :: 0 +SCHED_FIFO :: 1 +SCHED_RR :: 3 + +sched_param :: struct { + sched_priority: c.int, +} + +sem_t :: struct #align(16) { + _: [SEM_T_SIZE] c.char, +} + +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + +foreign import "system:pthread" + +@(default_calling_convention="c") +foreign pthread { + sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- + + sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- + sem_destroy :: proc(sem: ^sem_t) -> c.int --- + sem_post :: proc(sem: ^sem_t) -> c.int --- + sem_wait :: proc(sem: ^sem_t) -> c.int --- + sem_trywait :: proc(sem: ^sem_t) -> c.int --- + + pthread_yield :: proc() --- + + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- +} diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index 4fe3c8dfa..5760560ee 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix foreign import "system:pthread" diff --git a/core/sys/unix/syscalls_netbsd.odin b/core/sys/unix/syscalls_netbsd.odin new file mode 100644 index 000000000..e92dc6d92 --- /dev/null +++ b/core/sys/unix/syscalls_netbsd.odin @@ -0,0 +1,3 @@ +package unix + +SYS___futex : uintptr : 166 diff --git a/core/sys/unix/sysctl_netbsd.odin b/core/sys/unix/sysctl_netbsd.odin new file mode 100644 index 000000000..ad89b9ad4 --- /dev/null +++ b/core/sys/unix/sysctl_netbsd.odin @@ -0,0 +1,44 @@ +package unix + +import "core:c" +foreign import libc "system:c" + +@(default_calling_convention="c") +foreign libc { + @(link_name="sysctl") _unix_sysctl :: proc(name: [^]i32, namelen: u32, oldp: rawptr, oldlenp: ^c.size_t, newp: rawptr, newlen: c.size_t) -> i32 --- +} + +sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { + mib := mib + result_size := c.size_t(size_of(T)) + res := _unix_sysctl(raw_data(mib), u32(len(mib)), val, &result_size, nil, 0) + return res == 0 +} + +// See /usr/include/sys/sysctl.h for details +CTL_KERN :: 1 + KERN_OSTYPE :: 1 + KERN_OSRELEASE :: 2 + KERN_OSREV :: 3 + KERN_VERSION :: 4 +CTL_VM :: 2 +CTL_FS :: 3 +CTL_NET :: 4 +CTL_DEBUG :: 5 +CTL_HW :: 6 + HW_MACHINE :: 1 + HW_MODEL :: 2 + HW_NCPU :: 3 + HW_BYTEORDER :: 4 + HW_PHYSMEM :: 5 + HW_USERMEM :: 6 + HW_PAGESIZE :: 7 + HW_DISKNAMES :: 8 + HW_IOSTATS :: 9 + HW_MACHINE_ARCH :: 10 + HW_ALIGNBYTES :: 11 + HW_CNMAGIC :: 12 + HW_PHYSMEM64 :: 13 + HW_USERMEM64 :: 14 + HW_IOSTATNAMES :: 15 + HW_NCPUONLINE :: 16 diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin index 088dc378b..442202d36 100644 --- a/core/sys/unix/time_unix.odin +++ b/core/sys/unix/time_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix when ODIN_OS == .Darwin { @@ -9,11 +9,20 @@ when ODIN_OS == .Darwin { import "core:c" -@(default_calling_convention="c") -foreign libc { - clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- - sleep :: proc(seconds: c.uint) -> c.int --- - nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- +when ODIN_OS == .NetBSD { + @(default_calling_convention="c") + foreign libc { + @(link_name="__clock_gettime50") clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- + @(link_name="__nanosleep50") nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- + @(link_name="sleep") sleep :: proc(seconds: c.uint) -> c.int --- + } +} else { + @(default_calling_convention="c") + foreign libc { + clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- + sleep :: proc(seconds: c.uint) -> c.int --- + nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- + } } timespec :: struct { @@ -65,7 +74,6 @@ seconds_since_boot :: proc "c" () -> f64 { return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 } - inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) { s, ns := nanoseconds / 1e9, nanoseconds % 1e9 requested := timespec{tv_sec=s, tv_nsec=ns} diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 7c353fd33..acc0e05cb 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -1,4 +1,4 @@ -// +build linux, darwin, freebsd, openbsd, haiku +// +build linux, darwin, freebsd, openbsd, netbsd, haiku // +private package thread @@ -20,7 +20,7 @@ Thread_Os_Specific :: struct #align(16) { // It then waits for `start` to be called. // _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { - __linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr { + __unix_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr { t := (^Thread)(t) when ODIN_OS != .Darwin { @@ -82,7 +82,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // NOTE(tetra, 2019-11-01): These only fail if their argument is invalid. assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0) - when ODIN_OS != .Haiku { + when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0) } @@ -95,7 +95,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // Set thread priority. policy: i32 res: i32 - when ODIN_OS != .Haiku { + when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { res = unix.pthread_attr_getschedpolicy(&attrs, &policy) assert(res == 0) } @@ -113,7 +113,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { assert(res == 0) thread.procedure = procedure - if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 { + if unix.pthread_create(&thread.unix_thread, &attrs, __unix_thread_entry_proc, thread) != 0 { free(thread, thread.creation_allocator) return nil } diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index 1c46b5994..f3e0d6640 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package time import "core:sys/unix" diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 88ab9492c..c73595e99 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -17,7 +17,7 @@ #include #endif -#if defined(GB_SYSTEM_OPENBSD) +#if defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) #include #include #endif @@ -263,6 +263,14 @@ gb_internal void report_ram_info() { if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } + #elif defined(GB_SYSTEM_NETBSD) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int mibs[] = { CTL_HW, HW_PHYSMEM64 }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1)); + } #elif defined(GB_SYSTEM_OPENBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); @@ -998,13 +1006,17 @@ gb_internal void report_os_info() { gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch); return; } - #elif defined(GB_SYSTEM_OPENBSD) + #elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) struct utsname un; if (uname(&un) != -1) { gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine); } else { - gb_printf("OpenBSD: Unknown\n"); + #if defined(GB_SYSTEM_NETBSD) + gb_printf("NetBSD: Unknown\n"); + #else + gb_printf("OpenBSD: Unknown\n"); + #endif } #elif defined(GB_SYSTEM_FREEBSD) #define freebsd_version_buffer 129 diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ec7d03a84..7eb198185 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -18,6 +18,7 @@ enum TargetOsKind : u16 { TargetOs_essence, TargetOs_freebsd, TargetOs_openbsd, + TargetOs_netbsd, TargetOs_haiku, TargetOs_wasi, @@ -84,6 +85,7 @@ gb_global String target_os_names[TargetOs_COUNT] = { str_lit("essence"), str_lit("freebsd"), str_lit("openbsd"), + str_lit("netbsd"), str_lit("haiku"), str_lit("wasi"), @@ -1020,6 +1022,13 @@ gb_global TargetMetrics target_openbsd_amd64 = { str_lit("x86_64-unknown-openbsd-elf"), }; +gb_global TargetMetrics target_netbsd_amd64 = { + TargetOs_netbsd, + TargetArch_amd64, + 8, 8, AMD64_MAX_ALIGNMENT, 16, + str_lit("x86_64-unknown-netbsd-elf"), +}; + gb_global TargetMetrics target_haiku_amd64 = { TargetOs_haiku, TargetArch_amd64, @@ -1127,6 +1136,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_arm64"), &target_freebsd_arm64 }, { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, + { str_lit("netbsd_amd64"), &target_netbsd_amd64 }, { str_lit("haiku_amd64"), &target_haiku_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, @@ -1886,6 +1896,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta #endif #elif defined(GB_SYSTEM_OPENBSD) metrics = &target_openbsd_amd64; + #elif defined(GB_SYSTEM_NETBSD) + metrics = &target_netbsd_amd64; #elif defined(GB_SYSTEM_HAIKU) metrics = &target_haiku_amd64; #elif defined(GB_CPU_ARM) @@ -2423,6 +2435,7 @@ gb_internal bool init_build_paths(String init_filename) { case TargetOs_essence: case TargetOs_freebsd: case TargetOs_openbsd: + case TargetOs_netbsd: case TargetOs_haiku: gb_printf_err("-no-crt on unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present because the default allocator requires crt\n"); return false; diff --git a/src/checker.cpp b/src/checker.cpp index 70ca4fc47..12c8f5291 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1013,6 +1013,7 @@ gb_internal void init_universal(void) { {"FreeBSD", TargetOs_freebsd}, {"Haiku", TargetOs_haiku}, {"OpenBSD", TargetOs_openbsd}, + {"NetBSD", TargetOs_netbsd}, {"WASI", TargetOs_wasi}, {"JS", TargetOs_js}, {"Freestanding", TargetOs_freestanding}, diff --git a/src/gb/gb.h b/src/gb/gb.h index c55ff8a9a..58fbfa071 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -83,6 +83,10 @@ extern "C" { #ifndef GB_SYSTEM_OPENBSD #define GB_SYSTEM_OPENBSD 1 #endif + #elif defined(__NetBSD__) + #ifndef GB_SYSTEM_NETBSD + #define GB_SYSTEM_NETBSD 1 + #endif #elif defined(__HAIKU__) || defined(__haiku__) #ifndef GB_SYSTEM_HAIKU #define GB_SYSTEM_HAIKU 1 @@ -208,7 +212,7 @@ extern "C" { #endif #include // NOTE(bill): malloc on linux #include - #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__) + #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__HAIKU__) #include #endif #include @@ -250,6 +254,11 @@ extern "C" { #define lseek64 lseek #endif +#if defined(GB_SYSTEM_NETBSD) + #include + #define lseek64 lseek +#endif + #if defined(GB_SYSTEM_HAIKU) #include #include @@ -817,6 +826,13 @@ typedef struct gbAffinity { isize thread_count; isize threads_per_core; } gbAffinity; +#elif defined(GB_SYSTEM_NETBSD) +typedef struct gbAffinity { + b32 is_accurate; + isize core_count; + isize thread_count; + isize threads_per_core; +} gbAffinity; #elif defined(GB_SYSTEM_HAIKU) typedef struct gbAffinity { b32 is_accurate; @@ -3269,6 +3285,31 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { GB_ASSERT(0 <= core && core < a->core_count); return a->threads_per_core; } + +#elif defined(GB_SYSTEM_NETBSD) +#include + +void gb_affinity_init(gbAffinity *a) { + a->core_count = sysconf(_SC_NPROCESSORS_ONLN); + a->threads_per_core = 1; + a->is_accurate = a->core_count > 0; + a->core_count = a->is_accurate ? a->core_count : 1; + a->thread_count = a->core_count; +} + +void gb_affinity_destroy(gbAffinity *a) { + gb_unused(a); +} + +b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { + return true; +} + +isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { + GB_ASSERT(0 <= core && core < a->core_count); + return a->threads_per_core; +} + #elif defined(GB_SYSTEM_HAIKU) #include diff --git a/src/path.cpp b/src/path.cpp index 93f6f5000..26ccb7cbf 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -341,7 +341,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) return ReadDirectory_None; } -#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU) +#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) || defined(GB_SYSTEM_HAIKU) #include diff --git a/src/threading.cpp b/src/threading.cpp index f2e0789f8..48c58e8f4 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -628,15 +628,23 @@ gb_internal void thread_set_name(Thread *t, char const *name) { pthread_setname_np(name); #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) pthread_set_name_np(t->posix_handle, name); +#elif defined(GB_SYSTEM_NETBSD) + pthread_setname_np(t->posix_handle, "%s", (void*)name); #else // TODO(bill): Test if this works pthread_setname_np(t->posix_handle, name); #endif } -#if defined(GB_SYSTEM_LINUX) -#include +#if defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_NETBSD) + #include +#ifdef GB_SYSTEM_LINUX + #include +#else + #include + #define SYS_futex SYS___futex +#endif gb_internal void futex_signal(Futex *addr) { int ret = syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0); diff --git a/tests/core/Makefile b/tests/core/Makefile index 8996e2241..9026ed3d9 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -3,30 +3,32 @@ PYTHON=$(shell which python3) COMMON=-vet -strict-style COLLECTION=-collection:tests=.. -all: c_libc_test \ - compress_test \ - container_test \ - crypto_test \ - download_test_assets \ - encoding_test \ - filepath_test \ - fmt_test \ - hash_test \ - i18n_test \ - image_test \ - linalg_glsl_math_test \ - match_test \ - math_test \ - net_test \ - noise_test \ - os_exit_test \ - reflect_test \ - slice_test \ - strings_test \ - thread_test \ - runtime_test \ - time_test \ - fmt_test +all: all_bsd \ + net_test + +all_bsd: c_libc_test \ + compress_test \ + container_test \ + crypto_test \ + download_test_assets \ + encoding_test \ + filepath_test \ + fmt_test \ + hash_test \ + i18n_test \ + image_test \ + linalg_glsl_math_test \ + match_test \ + math_test \ + noise_test \ + os_exit_test \ + reflect_test \ + slice_test \ + strings_test \ + thread_test \ + runtime_test \ + time_test \ + fmt_test download_test_assets: $(PYTHON) download_assets.py @@ -101,4 +103,4 @@ runtime_test: $(ODIN) run runtime $(COMMON) -out:test_core_runtime time_test: - $(ODIN) run time $(COMMON) -out:test_core_time \ No newline at end of file + $(ODIN) run time $(COMMON) -out:test_core_time diff --git a/tests/internal/Makefile b/tests/internal/Makefile index d17b9d012..09182cd23 100644 --- a/tests/internal/Makefile +++ b/tests/internal/Makefile @@ -1,6 +1,8 @@ ODIN=../../odin -all: rtti_test map_test pow_test 128_test asan_test string_compare_test +all: all_bsd asan_test + +all_bsd: rtti_test map_test pow_test 128_test string_compare_test rtti_test: $(ODIN) run test_rtti.odin -file -vet -strict-style -o:minimal @@ -18,4 +20,4 @@ asan_test: $(ODIN) run test_asan.odin -file -sanitize:address -debug string_compare_test: - $(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal \ No newline at end of file + $(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal diff --git a/vendor/ENet/unix.odin b/vendor/ENet/unix.odin index 05ce41e05..0bbfce20a 100644 --- a/vendor/ENet/unix.odin +++ b/vendor/ENet/unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd package ENet // When we implement the appropriate bindings for Unix, the section separated diff --git a/vendor/directx/dxc/dxcdef_unix.odin b/vendor/directx/dxc/dxcdef_unix.odin index 12a682310..530d03ff0 100644 --- a/vendor/directx/dxc/dxcdef_unix.odin +++ b/vendor/directx/dxc/dxcdef_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd package directx_dxc import "core:c"