diff --git a/.cirrus.yml b/.cirrus.yml index 06d058d3..5cc1d1a6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,9 +6,7 @@ freebsd_task: OPAMYES: 1 env: matrix: - - OCAML_VERSION: 4.12.1 - - OCAML_VERSION: 4.13.0 - - OCAML_VERSION: 4.13.1 + - OCAML_VERSION: 5.0.0 pkg_install_script: pkg install -y ocaml-opam gmp gmake pkgconf bash opam_ocaml_cache: folder: $HOME/.opam diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ba1bf581..d00d5563 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - ocaml-version: [4.13.1] + ocaml-version: [5.0.0] mode: - name: hvt exec: false @@ -17,17 +17,6 @@ jobs: exec: false - name: xen exec: false - include: - - operating-system: ubuntu-latest - ocaml-version: 4.13.0 - mode: - name: spt - exec: true - - operating-system: ubuntu-latest - ocaml-version: 4.12.1 - mode: - name: spt - exec: true runs-on: ${{ matrix.operating-system }} steps: - uses: actions/checkout@v2 diff --git a/Makefile b/Makefile index e819259a..dbe1e869 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ LOCAL_CFLAGS=$(MAKECONF_CFLAGS) -I$(TOP)/nolibc/include -include _solo5/override # CFLAGS used by the OCaml compiler to build C stubs GLOBAL_CFLAGS=$(MAKECONF_CFLAGS) -I$(MAKECONF_PREFIX)/solo5-sysroot/include/nolibc/ -include _solo5/overrides.h # LIBS used by the OCaml compiler to link executables -GLOBAL_LIBS=-L$(MAKECONF_PREFIX)/solo5-sysroot/lib/nolibc/ -lnolibc -lopenlibm $(MAKECONF_EXTRA_LIBS) +GLOBAL_LIBS=-L$(MAKECONF_PREFIX)/solo5-sysroot/lib/nolibc/ -Wl,--start-group -lnolibc -lopenlibm $(MAKECONF_EXTRA_LIBS) -Wl,--end-group # NOLIBC NOLIBC_CFLAGS=$(LOCAL_CFLAGS) -I$(TOP)/openlibm/src -I$(TOP)/openlibm/include @@ -45,28 +45,43 @@ ocaml/Makefile: # configure link test # - We override OCAML_OS_TYPE since configure just hardcodes it to "Unix". OC_CFLAGS=$(LOCAL_CFLAGS) -I$(TOP)/openlibm/include -I$(TOP)/openlibm/src -nostdlib -OC_LIBS=-L$(TOP)/nolibc -lnolibc -L$(TOP)/openlibm -lopenlibm -nostdlib $(MAKECONF_EXTRA_LIBS) +OC_LIBS=-L$(TOP)/nolibc -lnolibc -L$(TOP)/openlibm -Wl,--start-group -lopenlibm -nostdlib $(MAKECONF_EXTRA_LIBS) -Wl,--end-group ocaml/Makefile.config: ocaml/Makefile openlibm/libopenlibm.a nolibc/libnolibc.a # configure: Do not build dynlink - sed -i -e 's/otherlibraries="dynlink"/otherlibraries=""/g' ocaml/configure + sed -e 's/otherlibraries="dynlink"/otherlibraries=""/g' ocaml/configure > ocaml/configure.sed && \ + mv ocaml/configure.sed ocaml/configure # configure: Allow precise input of flags and libs - sed -i -e 's/oc_cflags="/oc_cflags="$$OC_CFLAGS /g' ocaml/configure - sed -i -e 's/ocamlc_cflags="/ocamlc_cflags="$$OCAMLC_CFLAGS /g' ocaml/configure - sed -i -e 's/nativecclibs="$$cclibs $$DLLIBS"/nativecclibs="$$GLOBAL_LIBS"/g' ocaml/configure -# runtime/Makefile: Runtime rules: don't build libcamlrun.a and import ocamlrun from the system - sed -i -e 's/^all: $$(BYTECODE_STATIC_LIBRARIES) $$(BYTECODE_SHARED_LIBRARIES)/all: primitives ld.conf/' ocaml/runtime/Makefile - sed -i -e 's/^ocamlrun$$(EXE):.*/dummy:/g' ocaml/runtime/Makefile - sed -i -e 's/^ocamlruni$$(EXE):.*/dummyi:/g' ocaml/runtime/Makefile - sed -i -e 's/^ocamlrund$$(EXE):.*/dummyd:/g' ocaml/runtime/Makefile - echo -e "ocamlrun:\n\tcp $(shell which ocamlrun) .\n" >> ocaml/runtime/Makefile - echo -e "ocamlrund:\n\tcp $(shell which ocamlrund) .\n" >> ocaml/runtime/Makefile - echo -e "ocamlruni:\n\tcp $(shell which ocamlruni) .\n" >> ocaml/runtime/Makefile - touch ocaml/runtime/libcamlrun.a ocaml/runtime/libcamlrund.a ocaml/runtime/libcamlruni.a + sed -e 's/oc_cflags="/oc_cflags="$$OC_CFLAGS /g' ocaml/configure > ocaml/configure.sed && \ + mv ocaml/configure.sed ocaml/configure + sed -e 's/ocamlc_cflags="/ocamlc_cflags="$$OCAMLC_CFLAGS /g' ocaml/configure > ocaml/configure.sed && \ + mv ocaml/configure.sed ocaml/configure + sed -e 's/nativecclibs="$$cclibs $$DLLIBS $$PTHREAD_LIBS"/nativecclibs="$$GLOBAL_LIBS"/g' ocaml/configure > ocaml/configure.sed && \ + mv ocaml/configure.sed ocaml/configure + sed -e 's/^arch=none$$/arch=$(MAKECONF_OCAML_BUILD_ARCH)/' ocaml/configure > ocaml/configure.sed && \ + mv ocaml/configure.sed ocaml/configure + chmod +x ocaml/configure +# Makefile: Runtime rules: don't build libcamlrun.a and import ocamlrun from the system + sed -e 's/^ocamlrun$$(EXE):.*/dummy:/g' ocaml/Makefile > ocaml/Makefile.sed && \ + mv ocaml/Makefile.sed ocaml/Makefile + sed -e 's/^ocamlruni$$(EXE):.*/dummyi:/g' ocaml/Makefile > ocaml/Makefile.sed && \ + mv ocaml/Makefile.sed ocaml/Makefile + sed -e 's/^ocamlrund$$(EXE):.*/dummyd:/g' ocaml/Makefile > ocaml/Makefile.sed && \ + mv ocaml/Makefile.sed ocaml/Makefile + sed -e 's,^coldstart: $(COLDSTART_DEPS)$$,coldstart: runtime/primitives $$(COLDSTART_DEPS),' ocaml/Makefile > ocaml/Makefile.sed && \ + mv ocaml/Makefile.sed ocaml/Makefile + echo -e "ocamlrun:\n\tcp $(shell which ocamlrun) .\n" >> ocaml/Makefile + echo -e "ocamlrund:\n\tcp $(shell which ocamlrund) .\n" >> ocaml/Makefile + echo -e "ocamlruni:\n\tcp $(shell which ocamlruni) .\n" >> ocaml/Makefile + echo -e "runtime/ocamlrun\$$(EXE):\n\tcp $(shell which ocamlrun) runtime/\n" >> ocaml/Makefile + echo -e "runtime/ocamlrund\$$(EXE):\n\tcp $(shell which ocamlrund) runtime/\n" >> ocaml/Makefile + echo -e "runtime/ocamlruni\$$(EXE):\n\tcp $(shell which ocamlruni) runtime/\n" >> ocaml/Makefile # yacc/Makefile: import ocamlyacc from the system - sed -i -e 's/^ocamlyacc$$(EXE):.*/dummy:/g' ocaml/yacc/Makefile - echo -e "ocamlyacc:\n\tcp $(shell which ocamlyacc) .\n" >> ocaml/yacc/Makefile -# tools/Makefile: stub out objinfo_helper - echo -e "objinfo_helper:\n\ttouch objinfo_helper\n" >> ocaml/tools/Makefile + sed -e 's,^$$(ocamlyacc_PROGRAM)$$(EXE):.*,dummy_yacc:,g' ocaml/Makefile > ocaml/Makefile.sed && \ + mv ocaml/Makefile.sed ocaml/Makefile + echo -e "\$$(ocamlyacc_PROGRAM)\$$(EXE):\n\tcp $(shell which ocamlyacc) yacc/\n" >> ocaml/Makefile +# patch ocaml 5.0.0 runtime for single domain/thread solo5 + sed -e 's/#define Max_domains 128/#define Max_domains 1/' ocaml/runtime/caml/domain.h > ocaml/runtime/caml/domain.h.sed && \ + mv ocaml/runtime/caml/domain.h.sed ocaml/runtime/caml/domain.h # av_cv_libm_cos=no is passed to configure to prevent -lm being used (which # would use the host system libm instead of the freestanding openlibm, see # https://github.com/mirage/ocaml-solo5/issues/101 @@ -89,10 +104,11 @@ ocaml/Makefile.config: ocaml/Makefile openlibm/libopenlibm.a nolibc/libnolibc.a -disable-systhreads\ -disable-unix-lib\ -disable-instrumented-runtime\ + -disable-debug-runtime\ -disable-ocamltest\ -disable-ocamldoc\ $(MAKECONF_OCAML_CONFIGURE_OPTIONS) - echo "ARCH=$(MAKECONF_OCAML_BUILD_ARCH)" >> ocaml/Makefile.config + echo 'NATIVE_COMPILER=true' >> ocaml/Makefile.config echo 'SAK_CC=cc' >> ocaml/Makefile.config echo 'SAK_CFLAGS=' >> ocaml/Makefile.config echo 'SAK_LINK=cc $(SAK_CFLAGS) $$(OUTPUTEXE)$$(1) $$(2)' >> ocaml/Makefile.config diff --git a/README.md b/README.md index 04e2536b..ef5ee075 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,7 @@ Run: `solo5-hvt _build/default.solo5/main.exe` ## Supported compiler versions -Tested against OCaml 4.12.1 through 4.13.0. Other versions may require -changing `configure.sh`. +Tested against OCaml 5.0. Other versions may require changing `configure.sh`. ## Porting to a different (uni)kernel base layer diff --git a/example/startup.c b/example/startup.c index cde67ab7..263eddb3 100644 --- a/example/startup.c +++ b/example/startup.c @@ -10,4 +10,5 @@ void _nolibc_init(uintptr_t heap_start, size_t heap_size); // defined in nolibc/ int solo5_app_main(const struct solo5_start_info *si) { _nolibc_init(si->heap_start, si->heap_size); caml_startup(unused_argv); + return 0; } diff --git a/nolibc/Makefile b/nolibc/Makefile index 4294f2cf..6f8f4c3e 100644 --- a/nolibc/Makefile +++ b/nolibc/Makefile @@ -21,9 +21,11 @@ OBJS=assert.o \ dtoa.o \ memchr.o memcmp.o memcpy.o memmove.o memset.o \ strcmp.o strlen.o strtol.o strchr.o strchrnul.o strncpy.o stpncpy.o \ - strstr.o strncmp.o \ + strstr.o strncmp.o puts.o \ stubs.o \ - vfprintf.o vsnprintf.o snprintf.o fprintf.o printf.o + vfprintf.o vsnprintf.o snprintf.o fprintf.o printf.o \ + sysconf.o \ + mmap.o dtoa.o: CFLAGS+=-fno-strict-aliasing @@ -42,15 +44,15 @@ TEST_H_OBJS=$(patsubst %.h,test-%.o,$(HEADERS)) # For each HEADER we want to test, generate a C source file including only # that HEADER. As above, HEADER may include subdirectories. -test-include/%.c: include/%.h | test-include/sys +test-include/%.c: include/%.h | test-include/sys/ echo "#include \"../$<\"" >$@ .PRECIOUS: test-include/%.c -test-include: - mkdir $@ +test-include/: + mkdir -p $@ -test-include/sys: test-include - mkdir $@ +test-include/sys/: test-include/ + mkdir -p $@ -test-headers: $(TEST_H_OBJS) +test-headers: test-include/sys/ $(TEST_H_OBJS) diff --git a/nolibc/assert.c b/nolibc/assert.c index dcdcd1fc..ae89be31 100644 --- a/nolibc/assert.c +++ b/nolibc/assert.c @@ -1,17 +1,11 @@ #include -#include -#include +#include /* * These functions deliberately do not call printf() or malloc() in order to * abort as quickly as possible without triggering further errors. */ -static void puts(const char *s) -{ - (void)write(2, s, strlen(s)); -} - void _assert_fail(const char *file, const char *line, const char *e) { puts(file); diff --git a/nolibc/include/ctype.h b/nolibc/include/ctype.h index 354a9a70..71aab003 100644 --- a/nolibc/include/ctype.h +++ b/nolibc/include/ctype.h @@ -6,5 +6,7 @@ int isdigit(int); int isprint(int); int isspace(int); int isupper(int); +int isalnum(int); +int tolower(int); #endif diff --git a/nolibc/include/errno.h b/nolibc/include/errno.h index 67c3b760..fedbe481 100644 --- a/nolibc/include/errno.h +++ b/nolibc/include/errno.h @@ -10,5 +10,9 @@ extern int errno; #define EINVAL 6 #define ENOMEM 7 #define EMFILE 8 +#define EBUSY 9 +/* TODO(dinosaure): we probably should follow the Cosmopolitan + * project about these constants and use values where we have + * an {unix,bsd} consensus. */ #endif diff --git a/nolibc/include/fcntl.h b/nolibc/include/fcntl.h index a291d292..fd45d996 100644 --- a/nolibc/include/fcntl.h +++ b/nolibc/include/fcntl.h @@ -9,5 +9,9 @@ int open(const char *, int, ...); #define O_CREAT (1<<3) #define O_TRUNC (1<<4) #define O_EXCL (1<<5) +#define O_RDWR (1<<6) +/* TODO(dinosaure): same as errno values, we should take a + * look on the Cosmopolitan project and set values when + * we can find an {unix,bsd} consensus. */ #endif diff --git a/nolibc/include/pthread.h b/nolibc/include/pthread.h new file mode 100644 index 00000000..ea7b5651 --- /dev/null +++ b/nolibc/include/pthread.h @@ -0,0 +1,57 @@ +#ifndef _PTHREAD_H +#define _PTHREAD_H + +#include +#include + +typedef unsigned long int pthread_t; +typedef int cpu_set_t; + +int pthread_getaffinity_np(pthread_t, size_t, cpu_set_t *); + +pthread_t pthread_self(void); + +typedef int pthread_attr_t; + +int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); +int pthread_join(pthread_t, void **); +int pthread_attr_init(pthread_attr_t *); +void pthread_cleanup_push(void (*)(void *), void *); +void pthread_cleanup_pop(int); + +typedef int pthread_mutex_t; +typedef int pthread_cond_t; + +int pthread_mutex_lock(pthread_mutex_t *); +int pthread_mutex_trylock(pthread_mutex_t *); +int pthread_mutex_unlock(pthread_mutex_t *); + +#define PTHREAD_MUTEX_INITIALIZER 0 +#define PTHREAD_COND_INITIALIZER 0 + +int pthread_sigmask(int, const sigset_t *, sigset_t *); +int pthread_detach(pthread_t); +int pthread_equal(pthread_t, pthread_t); + +typedef int pthread_mutexattr_t; + +int pthread_mutexattr_init(pthread_mutexattr_t *); +int pthread_mutexattr_settype(pthread_mutexattr_t *, int); + +#define PTHREAD_MUTEX_ERRORCHECK 0 + +int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); +int pthread_mutexattr_destroy(pthread_mutexattr_t *); +int pthread_mutex_destroy(pthread_mutex_t *); + +typedef int pthread_condattr_t; + +int pthread_condattr_init(pthread_condattr_t *); +int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); + +int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int pthread_cond_broadcast(pthread_cond_t *); +int pthread_cond_signal(pthread_cond_t *); +int pthread_cond_destroy(pthread_cond_t *); + +#endif diff --git a/nolibc/include/sched.h b/nolibc/include/sched.h new file mode 100644 index 00000000..e5fa9302 --- /dev/null +++ b/nolibc/include/sched.h @@ -0,0 +1,8 @@ +#ifndef _SCHED_H +#define _SCHED_H + +typedef int cpu_set_t; + +#define CPU_ZERO (x) 0 + +#endif diff --git a/nolibc/include/setjmp.h b/nolibc/include/setjmp.h index 2e602dad..4d079b5d 100644 --- a/nolibc/include/setjmp.h +++ b/nolibc/include/setjmp.h @@ -1 +1,5 @@ #include + +void longjmp(int, int) __attribute__ ((__noreturn__)); + +#define setjmp(buf) 0 diff --git a/nolibc/include/signal.h b/nolibc/include/signal.h index 53fa89f3..5d0a9d55 100644 --- a/nolibc/include/signal.h +++ b/nolibc/include/signal.h @@ -4,14 +4,24 @@ typedef int jmp_buf; int setjmp(jmp_buf); void (*signal(int sig, void (*func)(int)))(int); -#define SIG_DFL NULL -#define SIG_IGN NULL -#define SIG_ERR NULL +#define SIG_DFL 0 +#define SIG_IGN 0 +#define SIG_ERR 0 +#define SIG_BLOCK 0 +#define SIG_SETMASK 0 /* * The following definitions are not required by the OCaml runtime, but are * needed to build the freestanding version of GMP used by Mirage. + * For OCaml 5.0.0, it's not totally true. SIG_{BLOCK,SETMASK,IGN,DFL) are + * needed by the OCaml runtime. + * + * NOTE: Solo5 does not implement signals, but we should not trigger + * a situation where these values are really used. */ #define SIGFPE 1 int raise(int); +typedef int sigset_t; +int sigfillset(sigset_t *); + #endif diff --git a/nolibc/include/stdatomic.h b/nolibc/include/stdatomic.h new file mode 100644 index 00000000..6ab87ddd --- /dev/null +++ b/nolibc/include/stdatomic.h @@ -0,0 +1,69 @@ +#ifndef _STDATOMIC_H +#define _STDATOMIC_H + +// Compatibility with non-clang compilers +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#define atomic_load_explicit(x, mode) ( *x ) +#define atomic_load(x) ( *x ) + +extern int memory_order_release; +extern int memory_order_acquire; +extern int memory_order_relaxed; +extern int memory_order_seq_cst; + +#if __has_builtin(__c11_atomic_fetch_add) +#define atomic_fetch_add(X, Y) __c11_atomic_fetch_add(X, Y, __ATOMIC_SEQ_CST) +#else +#define atomic_fetch_add(X, Y) ({ __auto_type tmp = *X; *X = tmp + Y; tmp; }) +#endif + +#define atomic_fetch_add_explicit(X, Y, MOD) atomic_fetch_add(X, Y) + +#define atomic_thread_fence(MO) do {} while (0) + +typedef unsigned long long atomic_uint_fast64_t; + +#if __has_builtin(__c11_atomic_compare_exchange_strong) +#define atomic_compare_exchange_strong(OBJ, EXPECTED, DESIRED) \ + __c11_atomic_compare_exchange_strong(OBJ, EXPECTED, DESIRED, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#else +#define atomic_compare_exchange_strong(OBJ, EXPECTED, DESIRED) \ + ({ int ret = 0; \ + if (*OBJ == *EXPECTED) { \ + *OBJ = DESIRED; \ + ret = 1; \ + } \ + ret; \ + }) +#endif + +#if __has_builtin(__c11_atomic_exchange) +#define atomic_exchange(OBJ, DESIRED) \ + __c11_atomic_exchange(OBJ, DESIRED, __ATOMIC_SEQ_CST) +#else +#define atomic_exchange(OBJ, DESIRED) \ + ({ __auto_type tmp = *OBJ; \ + *OBJ = DESIRED; \ + tmp; \ + }) +#endif + +#define atomic_store(OBJ, DESIRED) do { *OBJ = DESIRED; } while(0) +#define atomic_store_explicit(OBJ, DESIRED, ORDER) atomic_store(OBJ, DESIRED) + +#if __has_builtin(__c11_atomic_fetch_or) +#define atomic_fetch_or(OBJ, ARG) \ + __c11_atomic_fetch_or(OBJ, ARG, __ATOMIC_SEQ_CST) +#else +#define atomic_fetch_or(OBJ, ARG) \ + ({ __auto_type tmp = *OBJ; \ + *OBJ = *OBJ | ARG; \ + tmp; \ + }) +#endif + +#endif diff --git a/nolibc/include/stdio.h b/nolibc/include/stdio.h index 43b959e0..0754a18e 100644 --- a/nolibc/include/stdio.h +++ b/nolibc/include/stdio.h @@ -33,5 +33,10 @@ size_t fwrite(const void *, size_t, size_t, FILE *); int fputc(int, FILE *); int putc(int, FILE *); int ferror(FILE *); +int fputs(const char *, FILE *); +FILE *fopen(const char *, const char *); +int fclose(FILE *); +int puts(const char *); +int putchar(int); #endif diff --git a/nolibc/include/stdlib.h b/nolibc/include/stdlib.h index 9f890036..e7949749 100644 --- a/nolibc/include/stdlib.h +++ b/nolibc/include/stdlib.h @@ -31,4 +31,9 @@ int system(const char *); double strtod(const char *, char **); long strtol(const char *, char **, int); +void qsort(void *base, size_t nmemb, size_t size, + int (*compare)(const void *, const void *)); + +char *mktemp(char *); + #endif diff --git a/nolibc/include/string.h b/nolibc/include/string.h index 10fb3619..226325e5 100644 --- a/nolibc/include/string.h +++ b/nolibc/include/string.h @@ -16,6 +16,7 @@ char *strerror(int); * needed to build the freestanding version of GMP used by Mirage. */ char *strncpy(char *, const char *, size_t); +char *strcpy(char *, const char *); char *strchr(const char *, int); char *strstr(const char *, const char *); /* @@ -23,4 +24,9 @@ char *strstr(const char *, const char *); */ int strncmp(const char*, const char*, size_t); +/* TODO(dinosaure): we must track where they are used to know if we need + * to implement them or not. */ +int strerror_r(int errnum, char *buf, size_t buflen); +char *strdup(const char *); + #endif diff --git a/nolibc/include/sys/mman.h b/nolibc/include/sys/mman.h new file mode 100644 index 00000000..59bd8a2e --- /dev/null +++ b/nolibc/include/sys/mman.h @@ -0,0 +1,25 @@ +#ifndef _MMAP_H +#define _MMAP_H + +#include +#include + +void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED NULL + +#define OCAML_SOLO5_PAGESIZE (1 << 12) + +int munmap(void *addr, size_t len); + +#endif diff --git a/nolibc/include/sys/stat.h b/nolibc/include/sys/stat.h index 3f0ddffb..cf4d4a59 100644 --- a/nolibc/include/sys/stat.h +++ b/nolibc/include/sys/stat.h @@ -11,7 +11,10 @@ struct stat { #define S_IFMT 0 #define S_IFREG 0 #define S_ISREG(x) (0) +#define S_IRUSR 0 +#define S_IWUSR 0 int stat(const char *, struct stat *); int mkdir(const char *, mode_t); +int fstat(int, struct stat *); #endif diff --git a/nolibc/include/sys/types.h b/nolibc/include/sys/types.h index a916f597..af8bcacb 100644 --- a/nolibc/include/sys/types.h +++ b/nolibc/include/sys/types.h @@ -8,5 +8,6 @@ typedef int pid_t; typedef int off_t; typedef int ssize_t; typedef int mode_t; +typedef int useconds_t; #endif diff --git a/nolibc/include/unistd.h b/nolibc/include/unistd.h index 479e6fcf..cbedb7a3 100644 --- a/nolibc/include/unistd.h +++ b/nolibc/include/unistd.h @@ -15,5 +15,11 @@ ssize_t write(int, const void *, size_t); ssize_t readlink(const char *, char *, size_t); int unlink(const char *); int rmdir(const char *); +int usleep(useconds_t); +int ftruncate(int, off_t); +long sysconf(int); + +#define _SC_PAGESIZE 1 +#define _SC_PAGE_SIZE _SC_PAGESIZE #endif diff --git a/nolibc/mmap.c b/nolibc/mmap.c new file mode 100644 index 00000000..03ca1f96 --- /dev/null +++ b/nolibc/mmap.c @@ -0,0 +1,35 @@ +#include +#include + +#include + +void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) { + + /* man page for mmap says: + * If addr is not NULL, then the kernel takes it as a hint about where to place + * the mapping; [...] If another apping already exists there, the kernel picks + * a new address that may or *may not* depend on the hint. + * + * XXX(dinosaure): for our purpose (Solo5 & OCaml), OCaml does not require a + * specific (aligned) address from [mmap]. We can use [malloc()] instead of. + * The OCaml usage of [mmap()] is only to allocate some spaces, only [fildes + * == -1] is handled so. + */ + (void)addr; + (void)prot; + if (fildes != -1) { + printf("mmap: file descriptor is unsupported.\n"); + abort(); + } + if (!(flags & MAP_ANONYMOUS) || off != 0) { + printf("mmap: only MAP_ANONYMOUS (and offset is 0) is supported.\n"); + abort(); + } + + void *ptr = NULL; + posix_memalign(&ptr, OCAML_SOLO5_PAGESIZE, len); + /* XXX(palainp): Solo5 returns -1 and set errno on error, and it does not + * modify ptr, however both are not standardized. We can return NULL to the + * caller on error and do not check the returned value here. */ + return ptr; +} diff --git a/nolibc/puts.c b/nolibc/puts.c new file mode 100644 index 00000000..a8c366a0 --- /dev/null +++ b/nolibc/puts.c @@ -0,0 +1,17 @@ +#include +#include + +extern void solo5_console_write(const char *, size_t); + +int puts(const char *s) +{ + size_t len = strlen(s); + solo5_console_write(s, len); + return (len); +} + +int putchar(int chr) +{ + solo5_console_write((char *) &chr, 1); + return (1); +} diff --git a/nolibc/stubs.c b/nolibc/stubs.c index 5f951b0a..155c0e6e 100644 --- a/nolibc/stubs.c +++ b/nolibc/stubs.c @@ -55,8 +55,11 @@ STUB_WARN_ONCE(int, getc, EOF); STUB_WARN_ONCE(int, ungetc, EOF); STUB_WARN_ONCE(int, fwrite, 0); STUB_WARN_ONCE(int, fputc, EOF); +STUB_WARN_ONCE(int, fputs, EOF); STUB_WARN_ONCE(int, putc, EOF); STUB_WARN_ONCE(int, ferror, 1); +STUB_WARN_ONCE(int, fopen, 1); +STUB_WARN_ONCE(int, fclose, 1); /* stdlib.h */ STUB_WARN_ONCE(char *, getenv, NULL); @@ -75,6 +78,7 @@ STUB_ABORT(read); STUB_IGNORE(int, readlink, -1); STUB_ABORT(unlink); STUB_ABORT(rmdir); +STUB_ABORT(ftruncate); /* dirent.h */ STUB_WARN_ONCE(int, closedir, -1); @@ -100,3 +104,49 @@ STUB_ABORT(strerror); /* sys/stat.h */ STUB_WARN_ONCE(int, stat, -1); STUB_ABORT(mkdir); + +/* pthread.h */ +STUB_IGNORE(int, pthread_join, 0); +STUB_IGNORE(int, pthread_create, 0); +STUB_IGNORE(int, pthread_attr_init, 0); +STUB_ABORT(pthread_cleanup_push); +STUB_ABORT(pthread_cleanup_pop); + +STUB_IGNORE(int, munmap, 0); /* TODO: Implement munmap correctly and avoid memory leaks */ + +int memory_order_release; +int memory_order_acquire; +int memory_order_relaxed; + +STUB_ABORT(atomic_store_explicit); +STUB_ABORT(atomic_exchange); + +/* above that line, for OCaml 5, those are only required (i guess) for the configure step */ +STUB_IGNORE(int, pthread_mutex_lock, 0); +STUB_IGNORE(int, pthread_mutex_trylock, 0); +STUB_IGNORE(int, pthread_mutex_unlock, 0); +STUB_IGNORE(int, pthread_mutex_destroy, 0); +STUB_IGNORE(int, pthread_mutex_init, 0); + +STUB_IGNORE(int, pthread_mutexattr_init, 0); +STUB_IGNORE(int, pthread_mutexattr_destroy, 0); +STUB_IGNORE(int, pthread_mutexattr_settype, 0); + +STUB_IGNORE(int, pthread_sigmask, 0); + +STUB_IGNORE(int, pthread_equal, 1); + +STUB_IGNORE(int, pthread_condattr_init, 0); +/* TODO: Is there a memory leak in OCaml? Shouldn't there be a call to pthread_condattr_destroy? */ + +STUB_IGNORE(int, pthread_cond_init, 0); +STUB_ABORT(pthread_cond_destroy); +STUB_ABORT(pthread_cond_wait); +STUB_ABORT(pthread_cond_signal); +STUB_IGNORE(int, pthread_cond_broadcast, 0); +STUB_ABORT(pthread_self); +STUB_ABORT(pthread_detach); + +STUB_ABORT(sigfillset); +STUB_ABORT(usleep); +STUB_ABORT(strerror_r); diff --git a/nolibc/sysconf.c b/nolibc/sysconf.c new file mode 100644 index 00000000..81297ec9 --- /dev/null +++ b/nolibc/sysconf.c @@ -0,0 +1,11 @@ +#include +#include + +long sysconf(int x) { + switch (x) { + case _SC_PAGESIZE: /* _SC_PAGE_SIZE */ + return OCAML_SOLO5_PAGESIZE; + default: + return -1; + } +} diff --git a/nolibc/sysdeps_solo5.c b/nolibc/sysdeps_solo5.c index dacd0215..b98c5875 100644 --- a/nolibc/sysdeps_solo5.c +++ b/nolibc/sysdeps_solo5.c @@ -76,6 +76,7 @@ static uintptr_t sbrk_start; static uintptr_t sbrk_end; static uintptr_t sbrk_cur; static uintptr_t sbrk_guard_size; +static uintptr_t tls_main; /* * To be called by Mirage/Solo5 before calling caml_startup(). @@ -96,6 +97,20 @@ void _nolibc_init(uintptr_t heap_start, size_t heap_size) sbrk_start = sbrk_cur = heap_start; sbrk_end = heap_start + heap_size; + + tls_main = (uintptr_t)calloc(solo5_tls_size(), sizeof(uint8_t)); + if (tls_main == (uintptr_t)NULL) { + solo5_console_write("TLS alloc failed\n", 17); + abort(); + } + if (solo5_tls_init(tls_main) != SOLO5_R_OK) { + solo5_console_write("TLS init failed\n", 16); + abort(); + } + if (solo5_set_tls_base(solo5_tls_tp_offset(tls_main)) != SOLO5_R_OK) { + solo5_console_write("TLS set failed\n", 15); + abort(); + } } /* diff --git a/ocaml-solo5-cross-aarch64.opam b/ocaml-solo5-cross-aarch64.opam index f58f85d2..c2b79aca 100644 --- a/ocaml-solo5-cross-aarch64.opam +++ b/ocaml-solo5-cross-aarch64.opam @@ -21,7 +21,7 @@ depends: [ "conf-which" {build} "ocamlfind" {build} # needed by dune context (for tests) "ocaml-src" {build} - "ocaml" {>= "4.12.1" & < "4.15.0"} + "ocaml" {>= "5.0" & < "5.1"} "solo5" {>= "0.7.0"} "solo5-cross-aarch64" {>= "0.7.0" } ] diff --git a/ocaml-solo5.opam b/ocaml-solo5.opam index 3df9e566..c0b3d7ff 100644 --- a/ocaml-solo5.opam +++ b/ocaml-solo5.opam @@ -22,7 +22,7 @@ depends: [ "conf-which" {build} "ocamlfind" {build} # needed by dune context (for tests) "ocaml-src" {build} - "ocaml" {>= "4.12.1" & < "4.15.0"} + "ocaml" {>= "5.0" & < "5.1"} "solo5" {>= "0.7.0"} ] conflicts: [ diff --git a/test/startup.c b/test/startup.c index 5544c44d..8d9d5b04 100644 --- a/test/startup.c +++ b/test/startup.c @@ -13,4 +13,5 @@ void _nolibc_init(uintptr_t heap_start, size_t heap_size); // defined in nolibc/ int solo5_app_main(const struct solo5_start_info *si) { _nolibc_init(si->heap_start, si->heap_size); caml_startup(unused_argv); + return 0; }