From 85166bc706212b97125e972f824e3daf653c9f6a Mon Sep 17 00:00:00 2001 From: limit-ordinal Date: Fri, 19 Dec 2025 13:23:06 -0500 Subject: [PATCH] Add NetBSD Support (#2661) * Add NetBSD Support Includes: - Hints to find non-compatibility libc functions - Struct and constant definitions for sockets, polling, etc. - Changes to the linker code to work around some quirks in the NetBSD dynamic linker - A target triple for netbsd aarch64 so llvm builds/links the compiler properly on this platform * Updated releasenotes and some compacting --------- Co-authored-by: Christoffer Lerno --- .github/workflows/main.yml | 111 ++++++++++++++++++++++++++++++++++ CMakeLists.txt | 1 + lib/std/core/env.c3 | 2 +- lib/std/libc/libc.c3 | 96 +++++++++++++++++++++++++---- lib/std/libc/os/errno.c3 | 8 +-- lib/std/libc/os/netbsd.c3 | 38 ++++++++++++ lib/std/libc/os/posix.c3 | 3 +- lib/std/net/os/common.c3 | 8 +-- lib/std/net/os/netbsd.c3 | 108 +++++++++++++++++++++++++++++++++ lib/std/os/posix/clock.c3 | 2 +- lib/std/os/posix/files.c3 | 15 +++-- lib/std/os/posix/process.c3 | 4 +- releasenotes.md | 1 + src/build/build.h | 1 + src/build/build_options.c | 1 + src/build/builder.c | 3 + src/build/project_creation.c | 1 + src/compiler/compiler.c | 1 + src/compiler/linker.c | 27 ++++++--- src/compiler/target.c | 1 + test/unit/stdlib/libc/libc.c3 | 4 +- 21 files changed, 398 insertions(+), 38 deletions(-) create mode 100644 lib/std/libc/os/netbsd.c3 create mode 100644 lib/std/net/os/netbsd.c3 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3cc1a737e..a03f687ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1059,6 +1059,117 @@ jobs: name: c3-openbsd-${{matrix.build_type}} path: c3-openbsd-${{matrix.build_type}}.tar.gz + build-netbsd: + runs-on: ubuntu-latest + + strategy: + # Don't abort runners if a single one fails + fail-fast: false + matrix: + build_type: [Release, Debug] + + steps: + - uses: actions/checkout@v4 + - name: NetBSD VM + uses: vmactions/netbsd-vm@v1 + with: + prepare: | + /usr/sbin/pkg_add cmake llvm-19.1.7 lld-19.1.7nb1 ninja + + run: | + echo "CMake" + cmake -B build -S . + cmake --build build + echo "Compile and run some examples" + cd resources + ../build/c3c compile examples/base64.c3 + ../build/c3c compile examples/binarydigits.c3 + ../build/c3c compile examples/brainfk.c3 + ../build/c3c compile examples/factorial_macro.c3 + ../build/c3c compile examples/fasta.c3 + ../build/c3c compile examples/gameoflife.c3 + ../build/c3c compile examples/hash.c3 + ../build/c3c compile-only examples/levenshtein.c3 + ../build/c3c compile examples/load_world.c3 + ../build/c3c compile-only examples/map.c3 + ../build/c3c compile examples/mandelbrot.c3 + ../build/c3c compile examples/plus_minus.c3 + ../build/c3c compile examples/nbodies.c3 + ../build/c3c compile examples/spectralnorm.c3 + ../build/c3c compile examples/swap.c3 + ../build/c3c compile examples/contextfree/boolerr.c3 + ../build/c3c compile examples/contextfree/dynscope.c3 + ../build/c3c compile examples/contextfree/guess_number.c3 + ../build/c3c compile examples/contextfree/multi.c3 + ../build/c3c compile examples/contextfree/cleanup.c3 + ../build/c3c compile-run examples/hello_world_many.c3 + ../build/c3c compile-run examples/time.c3 + ../build/c3c compile-run examples/fannkuch-redux.c3 + ../build/c3c compile-run examples/contextfree/boolerr.c3 + ../build/c3c compile-run examples/load_world.c3 + ../build/c3c compile-run examples/process.c3 + ../build/c3c compile-run examples/ls.c3 + ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" + cd .. + echo "Compile and run dynlib-test" + cd resources/examples/dynlib-test + ../../../build/c3c -vv dynamic-lib add.c3 + mv add.so libadd.so + cc test.c -L. -ladd -Wl,-rpath=. + ./a.out + ../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=. + cd ../../../ + echo "Compile and run staticlib-test" + cd resources/examples/staticlib-test + ../../../build/c3c -vv static-lib add.c3 + mv add.a libadd.a + ranlib libadd.a + cc test.c -L. -ladd + ./a.out + ../../../build/c3c compile-run test.c3 -L . -l add + cd ../../../ + echo "Compile run unit tests" + cd test + ../build/c3c compile-test unit -D SLOW_TESTS + cd .. + echo "Build testproject" + cd resources/testproject + ../../build/c3c run -vvv --trust=full + cd ../../ + echo "Test WASM" + cd resources/testfragments + ../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3 + cd ../../ + echo "Build testproject direct linker" + cd resources/testproject + ../../build/c3c run -vvv --linker=builtin --trust=full + cd ../../ + echo "Init a library & a project" + ./build/c3c init-lib mylib + ls mylib.c3l + ./build/c3c init myproject + ls myproject + echo "run compiler tests" + cd test + ../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ + cd .. + + - name: bundle_output + run: | + mkdir c3 + cp -r lib c3 + cp msvc_build_libraries.py c3 + cp build/c3c c3 + cp README.md c3 + cp releasenotes.md c3 + tar czf c3-netbsd-${{matrix.build_type}}.tar.gz c3 + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: c3-netbsd-${{matrix.build_type}} + path: c3-netbsd-${{matrix.build_type}}.tar.gz + release: runs-on: ubuntu-22.04 needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22] diff --git a/CMakeLists.txt b/CMakeLists.txt index 24fff428a..922676abe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -572,6 +572,7 @@ else() -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter + -Wno-char-subscripts ) target_link_options(c3c PRIVATE -pthread) endif() diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index 1b609bfda..6d25075f9 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -166,7 +166,7 @@ const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER; const bool THREAD_SANITIZER = $$THREAD_SANITIZER; const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER; const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION; -const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32; +const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32 || env::NETBSD; macro bool os_is_darwin() @const { diff --git a/lib/std/libc/libc.c3 b/lib/std/libc/libc.c3 index eb73d570a..5b5f175e5 100644 --- a/lib/std/libc/libc.c3 +++ b/lib/std/libc/libc.c3 @@ -80,7 +80,7 @@ const CInt SIGCHLD = BSD_FLAVOR_SIG ? 20 : 17; const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY; alias Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid); -alias Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid); +alias Off_t = $typefrom(env::WIN32 ? int.typeid : isz.typeid); module libc @if(env::LIBC); @@ -108,7 +108,7 @@ extern fn CInt ferror(CFile stream); extern fn CInt fflush(CFile stream); extern fn CInt fgetc(CFile stream); extern fn ZString fgets(char* string, CInt n, CFile stream); -extern fn CInt fgetpos(CFile stream, Fpos_t* pos); +extern fn CInt fgetpos(CFile stream, Fpos_t* pos) @if(!env::NETBSD); extern fn Fd fileno(CFile stream) @if(!env::WIN32); extern fn CFile fopen(ZString filename, ZString mode); extern fn CInt fprintf(CFile stream, ZString format, ...); @@ -119,7 +119,7 @@ extern fn void* free(void*); extern fn CFile freopen(ZString filename, ZString mode, CFile stream); extern fn CInt fscanf(CFile stream, ZString format, ...); extern fn CInt fseek(CFile stream, SeekIndex offset, CInt whence) @if(!env::WIN32); -extern fn CInt fsetpos(CFile stream, Fpos_t* pos); +extern fn CInt fsetpos(CFile stream, Fpos_t* pos) @if(!env::NETBSD); extern fn SeekIndex ftell(CFile stream) @if(!env::WIN32); extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream); extern fn CInt getc(CFile stream); @@ -127,13 +127,13 @@ extern fn CInt getchar(); extern fn ZString getenv(ZString name); extern fn ZString gets(char* buffer); extern fn Tm* gmtime(Time_t* timer); -extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @if(!env::WIN32); +extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @if(!env::WIN32 && !env::NETBSD); extern fn CInt ioctl(CInt fd, ulong request, ...); extern fn CInt isatty(Fd fd) @if(!env::WIN32); extern fn CLong labs(CLong x); extern fn LongDivResult ldiv(CLong number, CLong denom); extern fn Tm* localtime(Time_t* timer); -extern fn Tm* localtime_r(Time_t* timer, Tm* result) @if(!env::WIN32); +extern fn Tm* localtime_r(Time_t* timer, Tm* result) @if(!env::WIN32 && !env::NETBSD); extern fn void longjmp(JmpBuf* buffer, CInt value) @if(!env::NETBSD && !env::OPENBSD); extern fn void* malloc(usz size); extern fn void* memchr(void* str, CInt c, usz n); @@ -192,17 +192,17 @@ extern fn CLong strtol(char* str, char** endptr, CInt base); extern fn CULong strtoul(char* str, char** endptr, CInt base); extern fn usz strxfrm(char* dest, ZString src, usz n); extern fn CInt system(ZString str); -extern fn Time_t timegm(Tm* timeptr) @if(!env::WIN32); +extern fn Time_t timegm(Tm* timeptr) @if(!env::WIN32 && !env::NETBSD); extern fn ZString tmpnam(ZString str); extern fn CFile tmpfile(); extern fn CInt ungetc(CInt c, CFile stream); -extern fn CInt unsetenv(ZString name); +extern fn CInt unsetenv(ZString name) @if(!env::NETBSD); extern fn isz write(Fd fd, void* buffer, usz count) @if(!env::WIN32); extern fn CFile fmemopen(void* ptr, usz size, ZString mode); extern fn isz getline(char** linep, usz* linecapp, CFile stream); extern fn CInt timespec_get(TimeSpec* ts, CInt base); -extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining); +extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining) @if(!env::NETBSD); extern fn ZString ctime(Time_t* timer); extern fn Time_t time(Time_t* timer); @@ -229,6 +229,13 @@ extern fn int _longjmp(void*, int); macro usz longjmp(void* ptr, CInt i) => _longjmp(ptr, i); extern fn usz malloc_size(void* ptr); extern fn void* aligned_alloc(usz align, usz size); +extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @cname("__gmtime_r50") @if(env::NETBSD); +extern fn Tm* localtime_r(Time_t* timer, Tm* result) @cname("__localtime_r50") @if(env::NETBSD); +extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining) @cname("__nanosleep50") @if(env::NETBSD); +extern fn CInt fgetpos(CFile stream, Fpos_t* pos) @cname("__fgetpos50") @if(env::NETBSD); +extern fn CInt fsetpos(CFile stream, Fpos_t* pos) @cname("__fsetpos50") @if(env::NETBSD); +extern fn Time_t timegm(Tm* timeptr) @cname("__timegm50") @if(env::NETBSD); +extern fn CInt unsetenv(ZString name) @cname("__unsetenv13") @if(env::NETBSD); macro CFile stdin() { return fdopen(0, "r"); } macro CFile stdout() { return fdopen(1, "w"); } macro CFile stderr() { return fdopen(2, "w"); } @@ -469,8 +476,8 @@ const Errno EBADF = 9; // Bad file number const Errno ECHILD = 10; // No child processes -const Errno EAGAIN @if(env::DARWIN) = 35; // Try again Macos -const Errno EAGAIN @if(!env::DARWIN) = 11; // Try again +const Errno EAGAIN @if(env::DARWIN || env::NETBSD) = 35; // Try again Macos +const Errno EAGAIN @if(!env::DARWIN && !env::NETBSD) = 11; // Try again const Errno ENOMEM = 12; // Out of memory const Errno EACCES = 13; // Permission denied @@ -566,6 +573,73 @@ const Errno EPROTO = 100; // Protocol error const Errno ETIME = 101; // STREAM ioctl() timeout const Errno EOPNOTSUPP = 102; // Operation not supported on socket +module libc::errno @if(env::NETBSD); +const Errno EWOULDBLOCK = EAGAIN; // Operation would block +const Errno EDEADLK = 11; // Resource deadlock avoided +const Errno EINPROGRESS = 36; // Operation now in progress +const Errno EALREADY = 37; // Operation already in progress +const Errno ENOTSOCK = 38; // Socket operation on non-socket +const Errno EDESTADDRREQ = 39; // Destination address required +const Errno EMSGSIZE = 40; // Message too long +const Errno EPROTOTYPE = 41; // Protocol wrong type for socket +const Errno ENOPROTOOPT = 42; // Protocol option not available +const Errno EPROTONOSUPPORT = 43; // Protocol not supported +const Errno ESOCKTNOSUPPORT = 44; // Socket type not supported +const Errno EOPNOTSUPP = 45; // Operation not supported +const Errno EPFNOSUPPORT = 46; // Protocol family not supported +const Errno EAFNOSUPPORT = 47; // Address family not supported by protocol family +const Errno EADDRINUSE = 48; // Address already in use +const Errno EADDRNOTAVAIL = 49; // Can't assign requested address +const Errno ENETDOWN = 50; // Network is down +const Errno ENETUNREACH = 51; // Network is unreachable +const Errno ENETRESET = 52; // Network dropped connection on reset +const Errno ECONNABORTED = 53; // Software caused connection abort +const Errno ECONNRESET = 54; // Connection reset by peer +const Errno ENOBUFS = 55; // No buffer space available +const Errno EISCONN = 56; // Socket is already connected +const Errno ENOTCONN = 57; // Socket is not connected +const Errno ESHUTDOWN = 58; // Can't send after socket shutdown +const Errno ETOOMANYREFS = 59; // Too many references: can't splice +const Errno ETIMEDOUT = 60; // Operation timed out +const Errno ECONNREFUSED = 61; // Connection refused +const Errno ELOOP = 62; // Too many levels of symbolic links +const Errno ENAMETOOLONG = 63; // File name too long +const Errno EHOSTDOWN = 64; // Host is down +const Errno EHOSTUNREACH = 65; // No route to host +const Errno ENOTEMPTY = 66; // Directory not empty +const Errno EPROCLIM = 67; // Too many processes +const Errno EUSERS = 68; // Too many users +const Errno EDQUOT = 69; // Disc quota exceeded +const Errno ESTALE = 70; // Stale NFS file handle +const Errno EREMOTE = 71; // Too many levels of remote in path +const Errno EBADRPC = 72; // RPC struct is bad +const Errno ERPCMISMATCH = 73; // RPC version wrong +const Errno EPROGUNAVAIL = 74; // RPC prog. not avail +const Errno EPROGMISMATCH = 75; // Program version wrong +const Errno EPROCUNAVAIL = 76; // Bad procedure for program +const Errno ENOLCK = 77; // No locks available +const Errno ENOSYS = 78; // Function not implemented +const Errno EFTYPE = 79; // Inappropriate file type or format +const Errno EAUTH = 80; // Authentication error +const Errno ENEEDAUTH = 81; // Need authenticator +const Errno EIDRM = 82; // Identifier removed +const Errno ENOMSG = 83; // No message of desired type +const Errno EOVERFLOW = 84; // Value too large to be stored in data type +const Errno EILSEQ = 85; // Illegal byte sequence +const Errno ENOTSUP = 86; // Not supported +const Errno ECANCELED = 87; // Operation canceled +const Errno EBADMSG = 88; // Bad or Corrupt message +const Errno ENODATA = 89; // No message available +const Errno ENOSR = 90; // No STREAM resources +const Errno ENOSTR = 91; // Not a STREAM +const Errno ETIME = 92; // STREAM ioctl timeout +const Errno ENOATTR = 93; // Attribute not found +const Errno EMULTIHOP = 94; // Multihop attempted +const Errno ENOLINK = 95; // Link has been severed +const Errno EPROTO = 96; // Protocol error +const Errno EOWNERDEAD = 97; // Previous owner died +const Errno ENOTRECOVERABLE = 98; // State not recoverable + module libc::errno @if(env::WIN32); const Errno EDEADLK = 36; // Resource deadlock would occur Win32 const Errno ENAMETOOLONG = 38; // File name too long Win32 @@ -583,7 +657,7 @@ const Errno EINPROGRESS = 112; // Operation now in progress Win32 const Errno EDQUOT = -122; // Quota exceeded, not in Win32 const Errno EWOULDBLOCK = 140; // Operation would block -module libc::errno @if(!env::WIN32 && !env::DARWIN); +module libc::errno @if(!env::WIN32 && !env::DARWIN && !env::NETBSD); const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?) const Errno ENAMETOOLONG = 36; // File name too long Linux (others?) const Errno ENOTEMPTY = 39; // Directory not empty diff --git a/lib/std/libc/os/errno.c3 b/lib/std/libc/os/errno.c3 index 025b6835b..a18ea8126 100644 --- a/lib/std/libc/os/errno.c3 +++ b/lib/std/libc/os/errno.c3 @@ -11,10 +11,10 @@ extern fn int* __errno() @if(env::ANDROID); macro int errno() @if(env::ANDROID) => *__errno(); macro void errno_set(int err) @if(env::ANDROID) => *(__errno()) = err; -// OpenBSD -extern fn int* __errno() @if(env::OPENBSD); -macro int errno() @if(env::OPENBSD) => *__errno(); -macro void errno_set(int err) @if(env::OPENBSD) => *(__errno()) = err; +// OpenBSD and NetBSD +extern fn int* __errno() @if(env::OPENBSD || env::NETBSD); +macro int errno() @if(env::OPENBSD || env::NETBSD) => *__errno(); +macro void errno_set(int err) @if(env::OPENBSD || env::NETBSD) => *(__errno()) = err; // Darwin extern fn int* __error() @if(env::DARWIN); diff --git a/lib/std/libc/os/netbsd.c3 b/lib/std/libc/os/netbsd.c3 new file mode 100644 index 000000000..8f4a5cd98 --- /dev/null +++ b/lib/std/libc/os/netbsd.c3 @@ -0,0 +1,38 @@ +module libc @if(env::NETBSD); + +alias Blksize_t = int; +alias Nlink_t = uint; +alias Dev_t = long; +alias Ino_t = ulong; +alias Mode_t = uint; +alias Blkcnt_t = long; + +struct Stat +{ + Dev_t st_dev; + Mode_t st_mode; + Ino_t st_ino; + Nlink_t st_nlink; + Uid_t st_uid; + Gid_t st_gid; + Dev_t st_rdev; + Time_t st_atime; + long st_atimensec; + Time_t st_mtime; + long st_mtimensec; + Time_t st_ctime; + long st_ctimensec; + Time_t st_birthtime; + long st_birthtimensec; + Off_t st_size; + Blkcnt_t st_blocks; + Blksize_t st_blksize; + uint st_flags; + uint st_gen; + uint[2] st_spare; +} + +extern fn CInt stat(ZString path, Stat* stat) @cname("__stat50"); + +extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen); + diff --git a/lib/std/libc/os/posix.c3 b/lib/std/libc/os/posix.c3 index ef911768e..09f67d8df 100644 --- a/lib/std/libc/os/posix.c3 +++ b/lib/std/libc/os/posix.c3 @@ -60,7 +60,8 @@ struct Stack_t } extern fn CInt sigaltstack(Stack_t* ss, Stack_t* old_ss); -extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction); +extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction) @if(!env::NETBSD); +extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction) @cname("__sigaction_siginfo") @if(env::NETBSD); module libc::termios @if(env::LIBC &&& env::POSIX); diff --git a/lib/std/net/os/common.c3 b/lib/std/net/os/common.c3 index e85dbb44c..f5557e80d 100644 --- a/lib/std/net/os/common.c3 +++ b/lib/std/net/os/common.c3 @@ -1,5 +1,5 @@ module std::net::os; -const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD); +const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD || env::NETBSD); typedef AIFamily = CInt; typedef AIProtocol = CInt; @@ -18,7 +18,7 @@ struct AddrInfo AISockType ai_socktype; AIProtocol ai_protocol; Socklen_t ai_addrlen; - struct @if(env::WIN32 || env::DARWIN || env::ANDROID) + struct @if(env::WIN32 || env::DARWIN || env::ANDROID || env::NETBSD) { ZString ai_canonname; SockAddrPtr ai_addr; @@ -87,7 +87,7 @@ extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* o *> extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t* optlen) @if(SUPPORTS_INET); -module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD)); +module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD || env::NETBSD)); const AIFamily PLATFORM_AF_INET6 = 0; const AIFamily PLATFORM_AF_IPX = 0; @@ -194,4 +194,4 @@ const IPPROTO_IPCOMP = 108; const IPPROTO_PGM = 113; const IPPROTO_SCTP = 132; const IPPROTO_DIVERT = 254; -const IPPROTO_RAW = 255; \ No newline at end of file +const IPPROTO_RAW = 255; diff --git a/lib/std/net/os/netbsd.c3 b/lib/std/net/os/netbsd.c3 new file mode 100644 index 000000000..3932a0c10 --- /dev/null +++ b/lib/std/net/os/netbsd.c3 @@ -0,0 +1,108 @@ +module std::net::os @if(env::NETBSD); +import libc; + +const AIFamily PLATFORM_AF_UNSPEC = 0; // unspecified +const AIFamily PLATFORM_AF_LOCAL = 1; // local to host +const AIFamily PLATFORM_AF_UNIX = PLATFORM_AF_LOCAL; // backward compatibility +const AIFamily PLATFORM_AF_INET = 2; // internetwork: UDP, TCP, etc. +const AIFamily PLATFORM_AF_IMPLINK = 3; // arpanet imp addresses +const AIFamily PLATFORM_AF_PUP = 4; // pup protocols: e.g. BSP +const AIFamily PLATFORM_AF_CHAOS = 5; // mit CHAOS protocols +const AIFamily PLATFORM_AF_NS = 6; // XEROX NS protocols +const AIFamily PLATFORM_AF_ISO = 7; // ISO protocols +const AIFamily PLATFORM_AF_OSI = PLATFORM_AF_ISO; +const AIFamily PLATFORM_AF_ECMA = 8; // european computer manufacturers +const AIFamily PLATFORM_AF_DATAKIT = 9; // datakit protocols +const AIFamily PLATFORM_AF_CCITT = 10; // CCITT protocols, X.25 etc +const AIFamily PLATFORM_AF_SNA = 11; // IBM SNA +const AIFamily PLATFORM_AF_DECNET = 12; // DECnet +const AIFamily PLATFORM_AF_DLI = 13; // DEC Direct data link interface +const AIFamily PLATFORM_AF_LAT = 14; // LAT +const AIFamily PLATFORM_AF_HYLINK = 15; // NSC Hyperchannel +const AIFamily PLATFORM_AF_APPLETALK = 16; // Apple Talk +const AIFamily PLATFORM_AF_OROUTE = 17; // Internal Routing Protocol +const AIFamily PLATFORM_AF_LINK = 18; // Link layer interface +const AIFamily PLATFORM_PSEUDO_AF_XTP = 19; // eXpress Transfer Protocol (no AF) +const AIFamily PLATFORM_AF_COIP = 20; // connection-oriented IP, aka ST II +const AIFamily PLATFORM_AF_CNT = 21; // Computer Network Technology +const AIFamily PLATFORM_PSEUDO_AF_RTIP = 22; // Help Identify RTIP packets +const AIFamily PLATFORM_AF_IPX = 23; // Novell Internet Protocol +const AIFamily PLATFORM_AF_INET6 = 24; // IP version 6 +const AIFamily PLATFORM_PSEUDO_AF_PIP = 25; // Help Identify PIP packets +const AIFamily PLATFORM_AF_ISDN = 26; // Integrated Services Digital Networ +const AIFamily PLATFORM_AF_E164 = PLATFORM_AF_ISDN; // CCITT E.164 recommendation +const AIFamily PLATFORM_AF_NATM = 27; // native ATM access +const AIFamily PLATFORM_AF_ARP = 28; // (rev.) addr. res. prot. (RFC 826) +const AIFamily PLATFORM_PSEUDO_AF_KEY = 29; // Internal key management protocol +const AIFamily PLATFORM_PSEUDO_AF_HDRCMPLT = 30; /* Used by BPF to not rewrite hdrs + * in interface output routine */ +const AIFamily PLATFORM_AF_BLUETOOTH = 31; // Bluetooth: HCI, SCO, L2CAP, RFCOMM +const AIFamily PLATFORM_AF_IEEE80211 = 32; // IEEE80211 +const AIFamily PLATFORM_AF_MPLS = 33; // MultiProtocol Label Switching +const AIFamily PLATFORM_AF_ROUTE = 34; // Internal Routing Protocol +const AIFamily PLATFORM_AF_CAN = 35; +const AIFamily PLATFORM_AF_ETHER = 36; +const AIFamily PLATFORM_AF_MAX = 37; + +const int SOL_SOCKET = 0xFFFF; + +const int SO_DEBUG = 0x0001; // turn on debugging info recording +const int SO_ACCEPTCONN = 0x0002; // socket has had listen() +const int SO_REUSEADDR = 0x0004; // allow local address reuse +const int SO_KEEPALIVE = 0x0008; // keep connections alive +const int SO_DONTROUTE = 0x0010; // just use interface addresses +const int SO_BROADCAST = 0x0020; // permit sending of broadcast msgs +const int SO_USELOOPBACK = 0x0040; // bypass hardware when possible +const int SO_LINGER = 0x0080; // linger on close if data present +const int SO_OOBINLINE = 0x0100; // leave received OOB data in line +const int SO_REUSEPORT = 0x0200; // allow local address & port reuse +const int SO_NOSIGPIPE = 0x0800; // no SIGPIPE from EPIPE +const int SO_ACCEPTFILTER = 0x1000; // there is an accept filter +const int SO_TIMESTAMP = 0x2000; // timestamp received dgram traffic +const int SO_RERROR = 0x4000; // Keep track of receive errors + +// additional +const int SO_SNDBUF = 0x1001; // send buffer size +const int SO_RCVBUF = 0x1002; // receive buffer size +const int SO_SNDLOWAT = 0x1003; // send low-water mark +const int SO_RCVLOWAT = 0x1004; // receive low-water mark +const int SO_ERROR = 0x1007; // get error status and clear +const int SO_TYPE = 0x1008; // get socket type +const int SO_OVERFLOWED = 0x1009; // datagrams: return packets dropped + +const int SO_NOHEADER = 0x100a; /* user supplies no header to kernel; + * kernel removes header and supplies + * payload + */ +const int SO_SNDTIMEO = 0x100b; // send timeout +const int SO_RCVTIMEO = 0x100c; // receive timeout + +// POLLIN through POLLNVAL are predefined by lib/std/net/os/posix.c3 +const CUShort POLLRDNORM = 0x0040; +const CUShort POLLWRNORM = POLLOUT; +const CUShort POLLRDBAND = 0x0080; +const CUShort POLLWRBAND = 0x0100; + +const CInt MSG_OOB = 0x0001; // process out-of-band data +const CInt MSG_PEEK = 0x0002; // peek at incoming message +const CInt MSG_DONTROUTE = 0x0004; // send without using routing tables +const CInt MSG_EOR = 0x0008; // data completes record +const CInt MSG_TRUNC = 0x0010; // data discarded before delivery +const CInt MSG_CTRUNC = 0x0020; // control data lost before delivery +const CInt MSG_WAITALL = 0x0040; // wait for full request or error +const CInt MSG_DONTWAIT = 0x0080; // this message should be nonblocking +const CInt MSG_BCAST = 0x0100; // this message was rcvd using link-level brdcst +const CInt MSG_MCAST = 0x0200; // this message was rcvd using link-level mcast +const CInt MSG_NOSIGNAL = 0x0400; // do not generate SIGPIPE on EOF +const CInt MSG_CMSG_CLOEXEC = 0x0800; // close on exec receiving fd +const CInt MSG_NBIO = 0x1000; // use non-blocking I/O +const CInt MSG_WAITFORONE = 0x2000; // recvmmsg() wait for one message +const CInt MSG_NOTIFICATION = 0x4000; // SCTP notification + +// socket creation options +const SOCK_CLOEXEC = 0x10000000; // set close on exec on socket +const SOCK_NONBLOCK = 0x20000000; // set non blocking i/o socket +const SOCK_NOSIGPIPE = 0x40000000; // don't send sigpipe +const SOCK_FLAGS_MASK = 0xf0000000; // flags mask + +const PLATFORM_O_NONBLOCK = SOCK_NONBLOCK; diff --git a/lib/std/os/posix/clock.c3 b/lib/std/os/posix/clock.c3 index 07e736dca..8e838e6ef 100644 --- a/lib/std/os/posix/clock.c3 +++ b/lib/std/os/posix/clock.c3 @@ -1,7 +1,7 @@ module std::os::posix @if(env::POSIX); import libc; -extern fn CInt clock_gettime(int type, TimeSpec *time); +extern fn CInt clock_gettime(int type, TimeSpec *time) @cname(env::NETBSD ??? "__clock_gettime50" : "clock_gettime"); module std::os::posix @if(env::OPENBSD); const CLOCK_REALTIME = 0; diff --git a/lib/std/os/posix/files.c3 b/lib/std/os/posix/files.c3 index 052e09270..cdb519c44 100644 --- a/lib/std/os/posix/files.c3 +++ b/lib/std/os/posix/files.c3 @@ -32,9 +32,9 @@ extern fn ZString getcwd(char* pwd, usz len); extern fn CInt pipe(CInt[2]* pipes); extern fn CFile fdopen(CInt fd, ZString mode); extern fn CInt access(ZString path, CInt mode); -extern fn Posix_dirent* readdir(DIRPtr) @cname("readdir") @if(!USE_DARWIN_INODE64) ; -extern fn DIRPtr opendir(ZString); extern fn void closedir(DIRPtr); +extern fn Posix_dirent* readdir(DIRPtr) @cname(readdir_cname()); +extern fn DIRPtr opendir(ZString) @cname(env::NETBSD ??? "__opendir30" : "opendir"); const DT_UNKNOWN = 0; const DT_FIFO = 1; @@ -46,5 +46,12 @@ const DT_LNK = 10; const DT_SOCK = 12; const DT_WHT = 14; -const USE_DARWIN_INODE64 = env::DARWIN && env::X86_64; -extern fn Posix_dirent* readdir(DIRPtr) @cname("readdir$INODE64") @if(USE_DARWIN_INODE64); + +macro String readdir_cname() @local @const +{ + $switch: + $case env::DARWIN && env::X86_64: return "readdir$INODE64"; + $case env::NETBSD: return "__readdir30"; + $default: return "readdir"; + $endswitch +} \ No newline at end of file diff --git a/lib/std/os/posix/process.c3 b/lib/std/os/posix/process.c3 index 72a0ea238..44646bfac 100644 --- a/lib/std/os/posix/process.c3 +++ b/lib/std/os/posix/process.c3 @@ -58,7 +58,7 @@ const CInt WUNTRACES = 2; JmpBuf backtrace_jmpbuf @local; alias BacktraceFn = fn CInt(void** buffer, CInt size); -extern fn CInt backtrace(void** buffer, CInt size) @if(env::OPENBSD); +extern fn CInt backtrace(void** buffer, CInt size) @if(env::OPENBSD || env::NETBSD); fn void install_signal_handler(CInt signal, SigActionFunction func) { @@ -69,7 +69,7 @@ fn void install_signal_handler(CInt signal, SigActionFunction func) libc::sigaction(signal, &action, &old); } -fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD) +fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD && !env::NETBSD) { if (size < 1) return 0; void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY|libc::RTLD_NODELETE); diff --git a/releasenotes.md b/releasenotes.md index 8be2f5e08..7fc8a399a 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -7,6 +7,7 @@ - Add `--custom-libc` option for custom libc implementations. - Remove use of LLVMGetGlobalContext for single module compilation. - Fixed bug where constants would get modified when slicing them. #2660 +- Support for NetBSD. ### Fixes - Regression with npot vector in struct triggering an assert #2219. diff --git a/src/build/build.h b/src/build/build.h index 95dd59648..af8d2499f 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -401,6 +401,7 @@ typedef enum MACOS_X64, MCU_X86, MINGW_X64, + NETBSD_AARCH64, NETBSD_X86, NETBSD_X64, OPENBSD_X86, diff --git a/src/build/build_options.c b/src/build/build_options.c index 2b84887f6..76e0dc486 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -1779,6 +1779,7 @@ const char *arch_os_target[ARCH_OS_TARGET_LAST + 1] = { [MACOS_X64] = "macos-x64", [MCU_X86] = "mcu-x86", [MINGW_X64] = "mingw-x64", + [NETBSD_AARCH64] = "netbsd-aarch64", [NETBSD_X86] = "netbsd-x86", [NETBSD_X64] = "netbsd-x64", [OPENBSD_X86] = "openbsd-x86", diff --git a/src/build/builder.c b/src/build/builder.c index c629aa401..dcd26f957 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -34,6 +34,8 @@ ArchOsTarget default_target = ELF_X64; #elif defined(__aarch64__) || defined(_M_ARM64) #if defined(__MACH__) ArchOsTarget default_target = MACOS_AARCH64; + #elif defined(__NetBSD__) +ArchOsTarget default_target = NETBSD_AARCH64; #elif defined(__ANDROID__) ArchOsTarget default_target = ANDROID_AARCH64; #elif defined(__linux__) && __linux__ @@ -286,6 +288,7 @@ static LinkLibc libc_from_arch_os(ArchOsTarget target) case MACOS_AARCH64: case MACOS_X64: case MINGW_X64: + case NETBSD_AARCH64: case NETBSD_X86: case NETBSD_X64: case OPENBSD_X86: diff --git a/src/build/project_creation.c b/src/build/project_creation.c index 6eba5c6dd..846aef5d1 100644 --- a/src/build/project_creation.c +++ b/src/build/project_creation.c @@ -186,6 +186,7 @@ const char* DEFAULT_TARGETS[] = { "linux-x64", "macos-aarch64", "macos-x64", + "netbsd-aarch64", "netbsd-x64", "openbsd-x64", "wasm32", diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index d8991ce58..3b4ad3c88 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1208,6 +1208,7 @@ static int jump_buffer_size() // Godbolt test return 25; case FREEBSD_X64: + case NETBSD_AARCH64: case NETBSD_X64: case OPENBSD_X64: REMINDER("Guessing setjmp for platform."); diff --git a/src/compiler/linker.c b/src/compiler/linker.c index 5525405e2..b58577d24 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -295,9 +295,9 @@ static void linker_setup_macos(const char ***args_ref, Linker linker_type) } -static const char *find_freebsd_crt(void) +static const char *find_bsd_crt(void) { - if (file_exists("/usr/lib/crt1.o")) + if (file_exists("/usr/lib/crt1.o") || file_exists("/usr/lib/crt0.o")) { return "/usr/lib/"; } @@ -583,7 +583,7 @@ static void linker_setup_android(const char ***args_ref, Linker linker_type, boo add_plain_arg("-lc"); } -static void linker_setup_freebsd(const char ***args_ref, Linker linker_type, bool is_dylib) +static void linker_setup_bsd(const char ***args_ref, Linker linker_type, bool is_dylib) { if (linker_type == LINKER_CC) { @@ -602,7 +602,7 @@ static void linker_setup_freebsd(const char ***args_ref, Linker linker_type, boo if (!link_libc()) return; - const char *crt_dir = find_freebsd_crt(); + const char *crt_dir = find_bsd_crt(); if (!crt_dir) { error_exit("Failed to find the C runtime at link time."); @@ -613,22 +613,33 @@ static void linker_setup_freebsd(const char ***args_ref, Linker linker_type, boo } if (is_pie_pic(compiler.platform.reloc_model)) { - add_plain_arg("-pie"); + if (!is_dylib) add_plain_arg("-pie"); add_concat_file_arg(crt_dir, "crti.o"); - if (!is_dylib) add_concat_file_arg(crt_dir, "Scrt1.o"); + if (!is_dylib && compiler.platform.os != OS_TYPE_NETBSD) + { + add_concat_file_arg(crt_dir, "Scrt1.o"); + } add_concat_file_arg(crt_dir, "crtbeginS.o"); add_concat_file_arg(crt_dir, "crtendS.o"); } else { + const char *crt_o = compiler.platform.os == OS_TYPE_NETBSD ? "crt0.o" : "crt1.o"; add_concat_file_arg(crt_dir, "crti.o"); - if (!is_dylib) add_concat_file_arg(crt_dir, "crt1.o"); + if (!is_dylib) add_concat_file_arg(crt_dir, crt_o); add_concat_file_arg(crt_dir, "crtbegin.o"); add_concat_file_arg(crt_dir, "crtend.o"); } add_concat_file_arg(crt_dir, "crtn.o"); add_concat_quote_arg("-L", crt_dir); add_plain_arg("--dynamic-linker=/libexec/ld-elf.so.1"); + if (compiler.platform.os == OS_TYPE_NETBSD) + { + /* The following two flags are needed to work around ld-elf.so not being able + * to handle more than two PT_LOAD segments. */ + add_plain_arg("--no-rosegment"); + add_plain_arg("-znorelro"); + } linking_add_link(&compiler.linking, "c"); if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m"); linking_add_link(&compiler.linking, "gcc"); @@ -735,7 +746,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns case OS_TYPE_FREEBSD: case OS_TYPE_OPENBSD: case OS_TYPE_NETBSD: - linker_setup_freebsd(args_ref, linker_type, is_dylib); + linker_setup_bsd(args_ref, linker_type, is_dylib); break; case OS_TYPE_LINUX: linker_setup_linux(args_ref, linker_type, is_dylib); diff --git a/src/compiler/target.c b/src/compiler/target.c index 087d9d4ef..2d05e0f17 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -1197,6 +1197,7 @@ static char *arch_to_target_triple(ArchOsTarget target, LinuxLibc linux_libc) case MACOS_AARCH64: return "aarch64-apple-macosx"; case ELF_AARCH64: return "aarch64-unknown-elf"; case WINDOWS_AARCH64: return "aarch64-pc-windows-msvc"; + case NETBSD_AARCH64: return "aarch64-unknown-netbsd"; case LINUX_RISCV32: return linux_libc == LINUX_LIBC_MUSL ? "riscv32-unknown-linux-musl" : "riscv32-unknown-linux"; case ELF_RISCV32: return "riscv32-unknown-elf"; case LINUX_RISCV64: return linux_libc == LINUX_LIBC_MUSL ? "riscv64-unknown-linux-musl" : "riscv64-unknown-linux"; diff --git a/test/unit/stdlib/libc/libc.c3 b/test/unit/stdlib/libc/libc.c3 index c33ba0355..093dd97b5 100644 --- a/test/unit/stdlib/libc/libc.c3 +++ b/test/unit/stdlib/libc/libc.c3 @@ -255,8 +255,8 @@ fn void fdopen() @test @if(!env::WIN32) fn void fflush() @test { - CFile stdin = libc::stdin(); - assert(libc::fflush(stdin) == 0); + CFile stdout = libc::stdout(); + assert(libc::fflush(stdout) == 0); } fn void fgets_fget() @test