From 99df8ee74b2285dbb0266621c1d13d18dc7c90bf Mon Sep 17 00:00:00 2001 From: Matt Staveley-Taylor Date: Sun, 29 Dec 2024 13:41:10 +0000 Subject: [PATCH 1/3] Remove fast checks --- options/internal/include/bits/types.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/options/internal/include/bits/types.h b/options/internal/include/bits/types.h index e18d0c92a3..f4bb4efdea 100644 --- a/options/internal/include/bits/types.h +++ b/options/internal/include/bits/types.h @@ -347,18 +347,4 @@ __MLIBC_CHECK_TYPE(__mlibc_uint16, __UINT_LEAST16_TYPE__); __MLIBC_CHECK_TYPE(__mlibc_uint32, __UINT_LEAST32_TYPE__); __MLIBC_CHECK_TYPE(__mlibc_uint64, __UINT_LEAST64_TYPE__); -/* Fast-width. */ -/* Unfortunately, GCC and Clang disagree about fast types. */ -#ifndef __clang__ - __MLIBC_CHECK_TYPE(__mlibc_int_fast8, __INT_FAST8_TYPE__); - __MLIBC_CHECK_TYPE(__mlibc_int_fast16, __INT_FAST16_TYPE__); - __MLIBC_CHECK_TYPE(__mlibc_int_fast32, __INT_FAST32_TYPE__); - __MLIBC_CHECK_TYPE(__mlibc_int_fast64, __INT_FAST64_TYPE__); - - __MLIBC_CHECK_TYPE(__mlibc_uint_fast8, __UINT_FAST8_TYPE__); - __MLIBC_CHECK_TYPE(__mlibc_uint_fast16, __UINT_FAST16_TYPE__); - __MLIBC_CHECK_TYPE(__mlibc_uint_fast32, __UINT_FAST32_TYPE__); - __MLIBC_CHECK_TYPE(__mlibc_uint_fast64, __UINT_FAST64_TYPE__); -#endif - #endif /* _MLIBC_INTERNAL_TYPES_H */ From a876dd4292b1f88df6afd11516ef471292928604 Mon Sep 17 00:00:00 2001 From: Matt Staveley-Taylor Date: Sat, 28 Dec 2024 21:20:01 +0000 Subject: [PATCH 2/3] Initial sysdeps --- ci/demo.cross-file | 11 ++ meson.build | 4 + notes.txt | 9 ++ options/rtld/generic/main.cpp | 3 +- sysdeps/demo/crt1.S | 31 +++++ sysdeps/demo/entry.cpp | 37 ++++++ sysdeps/demo/include/abi-bits/auxv.h | 1 + sysdeps/demo/include/abi-bits/blkcnt_t.h | 1 + sysdeps/demo/include/abi-bits/blksize_t.h | 1 + sysdeps/demo/include/abi-bits/clockid_t.h | 1 + sysdeps/demo/include/abi-bits/dev_t.h | 1 + sysdeps/demo/include/abi-bits/errno.h | 1 + sysdeps/demo/include/abi-bits/fcntl.h | 1 + sysdeps/demo/include/abi-bits/gid_t.h | 1 + sysdeps/demo/include/abi-bits/ino_t.h | 1 + sysdeps/demo/include/abi-bits/limits.h | 1 + sysdeps/demo/include/abi-bits/mode_t.h | 1 + sysdeps/demo/include/abi-bits/nlink_t.h | 1 + sysdeps/demo/include/abi-bits/pid_t.h | 1 + sysdeps/demo/include/abi-bits/seek-whence.h | 1 + sysdeps/demo/include/abi-bits/signal.h | 1 + sysdeps/demo/include/abi-bits/stat.h | 1 + sysdeps/demo/include/abi-bits/uid_t.h | 1 + sysdeps/demo/include/abi-bits/vm-flags.h | 1 + sysdeps/demo/include/abi-bits/wait.h | 1 + sysdeps/demo/include/bits/syscall.h | 100 ++++++++++++++++ sysdeps/demo/meson.build | 54 +++++++++ sysdeps/demo/syscall.cpp | 125 ++++++++++++++++++++ sysdeps/demo/sysdeps.cpp | 74 ++++++++++++ 29 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 ci/demo.cross-file create mode 100644 notes.txt create mode 100644 sysdeps/demo/crt1.S create mode 100644 sysdeps/demo/entry.cpp create mode 120000 sysdeps/demo/include/abi-bits/auxv.h create mode 120000 sysdeps/demo/include/abi-bits/blkcnt_t.h create mode 120000 sysdeps/demo/include/abi-bits/blksize_t.h create mode 120000 sysdeps/demo/include/abi-bits/clockid_t.h create mode 120000 sysdeps/demo/include/abi-bits/dev_t.h create mode 120000 sysdeps/demo/include/abi-bits/errno.h create mode 120000 sysdeps/demo/include/abi-bits/fcntl.h create mode 120000 sysdeps/demo/include/abi-bits/gid_t.h create mode 120000 sysdeps/demo/include/abi-bits/ino_t.h create mode 120000 sysdeps/demo/include/abi-bits/limits.h create mode 120000 sysdeps/demo/include/abi-bits/mode_t.h create mode 120000 sysdeps/demo/include/abi-bits/nlink_t.h create mode 120000 sysdeps/demo/include/abi-bits/pid_t.h create mode 120000 sysdeps/demo/include/abi-bits/seek-whence.h create mode 120000 sysdeps/demo/include/abi-bits/signal.h create mode 120000 sysdeps/demo/include/abi-bits/stat.h create mode 120000 sysdeps/demo/include/abi-bits/uid_t.h create mode 120000 sysdeps/demo/include/abi-bits/vm-flags.h create mode 120000 sysdeps/demo/include/abi-bits/wait.h create mode 100644 sysdeps/demo/include/bits/syscall.h create mode 100644 sysdeps/demo/meson.build create mode 100644 sysdeps/demo/syscall.cpp create mode 100644 sysdeps/demo/sysdeps.cpp diff --git a/ci/demo.cross-file b/ci/demo.cross-file new file mode 100644 index 0000000000..565a31b23a --- /dev/null +++ b/ci/demo.cross-file @@ -0,0 +1,11 @@ +[binaries] +c = ['riscv64-elf-gcc'] +cpp = ['riscv64-elf-g++'] +ar = ['riscv64-elf-ar'] +ranlib = ['riscv64-elf-ranlib'] + +[host_machine] +system = 'demo' +cpu_family = 'riscv64' +cpu = 'unknown' +endian = 'little' diff --git a/meson.build b/meson.build index 251176e1cb..e7540ecf51 100644 --- a/meson.build +++ b/meson.build @@ -259,6 +259,10 @@ elif host_machine.system() == 'astral' internal_conf.set10('MLIBC_MAP_DSO_SEGMENTS', true) internal_conf.set10('MLIBC_MAP_FILE_WINDOWS', true) subdir('sysdeps/astral') +elif host_machine.system() == 'demo' + rtld_include_dirs += include_directories('sysdeps/demo/include') + libc_include_dirs += include_directories('sysdeps/demo/include') + subdir('sysdeps/demo') else error('No sysdeps defined for OS: ' + host_machine.system()) endif diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000000..384fce4a7e --- /dev/null +++ b/notes.txt @@ -0,0 +1,9 @@ +setup: make cross file pointing to compiler + +meson setup --cross-file=ci/demo.cross-file build + +-Ddefault_library=static + +add sysdeps directory + + diff --git a/options/rtld/generic/main.cpp b/options/rtld/generic/main.cpp index de7995b924..63c3a9ab3b 100644 --- a/options/rtld/generic/main.cpp +++ b/options/rtld/generic/main.cpp @@ -16,6 +16,7 @@ #include "elf.hpp" #include "linker.hpp" +#include "mlibc/ansi-sysdeps.hpp" #if __MLIBC_POSIX_OPTION #include @@ -24,7 +25,7 @@ #define HIDDEN __attribute__((__visibility__("hidden"))) #define EXPORT __attribute__((__visibility__("default"))) -static constexpr bool logEntryExit = false; +static constexpr bool logEntryExit = true; static constexpr bool logStartup = false; static constexpr bool logDlCalls = false; diff --git a/sysdeps/demo/crt1.S b/sysdeps/demo/crt1.S new file mode 100644 index 0000000000..c0e9289e51 --- /dev/null +++ b/sysdeps/demo/crt1.S @@ -0,0 +1,31 @@ +.weak __global_pointer$ +.hidden __global_pointer$ + +.section .text +.global _start +_start: + # Load gp. +.option push +.option norelax + lla gp, __global_pointer$ +.option pop + + mv a0, sp + la a1, main + call __mlibc_entry + unimp + +# Load gp from .preinit_array since it may be used by the executable's .init_array. +# We still load it in _start to account for static binaries. This matches glibc's behavior. +load_gp: +.option push +.option norelax + lla gp, __global_pointer$ +.option pop + ret + +.section .preinit_array,"aw" + .dword load_gp + +.section .note.GNU-stack,"",%progbits + diff --git a/sysdeps/demo/entry.cpp b/sysdeps/demo/entry.cpp new file mode 100644 index 0000000000..18e919c3dc --- /dev/null +++ b/sysdeps/demo/entry.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +// defined by the POSIX library +void __mlibc_initLocale(); + +extern "C" uintptr_t *__dlapi_entrystack(); +extern "C" void __dlapi_enter(uintptr_t *); + +extern char **environ; +static mlibc::exec_stack_data __mlibc_stack_data; + +struct LibraryGuard { + LibraryGuard(); +}; + +static LibraryGuard guard; + +LibraryGuard::LibraryGuard() { + __mlibc_initLocale(); + + // Parse the exec() stack. + mlibc::parse_exec_stack(__dlapi_entrystack(), &__mlibc_stack_data); + mlibc::set_startup_data(__mlibc_stack_data.argc, __mlibc_stack_data.argv, + __mlibc_stack_data.envp); +} + +extern "C" void __mlibc_entry(uintptr_t *entry_stack, + int (*main_fn)(int argc, char *argv[], + char *env[])) { + __dlapi_enter(entry_stack); + auto result = + main_fn(__mlibc_stack_data.argc, __mlibc_stack_data.argv, environ); + exit(result); +} diff --git a/sysdeps/demo/include/abi-bits/auxv.h b/sysdeps/demo/include/abi-bits/auxv.h new file mode 120000 index 0000000000..ae6decde37 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/auxv.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/auxv.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/blkcnt_t.h b/sysdeps/demo/include/abi-bits/blkcnt_t.h new file mode 120000 index 0000000000..c0c6c3deb8 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/blkcnt_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/blkcnt_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/blksize_t.h b/sysdeps/demo/include/abi-bits/blksize_t.h new file mode 120000 index 0000000000..1ce9912d3e --- /dev/null +++ b/sysdeps/demo/include/abi-bits/blksize_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/blksize_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/clockid_t.h b/sysdeps/demo/include/abi-bits/clockid_t.h new file mode 120000 index 0000000000..5155462171 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/clockid_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/clockid_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/dev_t.h b/sysdeps/demo/include/abi-bits/dev_t.h new file mode 120000 index 0000000000..947e7d8b8d --- /dev/null +++ b/sysdeps/demo/include/abi-bits/dev_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/dev_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/errno.h b/sysdeps/demo/include/abi-bits/errno.h new file mode 120000 index 0000000000..3bc678eed5 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/errno.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/errno.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/fcntl.h b/sysdeps/demo/include/abi-bits/fcntl.h new file mode 120000 index 0000000000..5fc52ea8ad --- /dev/null +++ b/sysdeps/demo/include/abi-bits/fcntl.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/fcntl.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/gid_t.h b/sysdeps/demo/include/abi-bits/gid_t.h new file mode 120000 index 0000000000..b04f739ac0 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/gid_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/gid_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/ino_t.h b/sysdeps/demo/include/abi-bits/ino_t.h new file mode 120000 index 0000000000..efe2462324 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/ino_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/ino_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/limits.h b/sysdeps/demo/include/abi-bits/limits.h new file mode 120000 index 0000000000..96970aec57 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/limits.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/limits.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/mode_t.h b/sysdeps/demo/include/abi-bits/mode_t.h new file mode 120000 index 0000000000..1aa6c6fa9f --- /dev/null +++ b/sysdeps/demo/include/abi-bits/mode_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/mode_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/nlink_t.h b/sysdeps/demo/include/abi-bits/nlink_t.h new file mode 120000 index 0000000000..efc197993d --- /dev/null +++ b/sysdeps/demo/include/abi-bits/nlink_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/nlink_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/pid_t.h b/sysdeps/demo/include/abi-bits/pid_t.h new file mode 120000 index 0000000000..12416cc242 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/pid_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/pid_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/seek-whence.h b/sysdeps/demo/include/abi-bits/seek-whence.h new file mode 120000 index 0000000000..9e81608d0c --- /dev/null +++ b/sysdeps/demo/include/abi-bits/seek-whence.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/seek-whence.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/signal.h b/sysdeps/demo/include/abi-bits/signal.h new file mode 120000 index 0000000000..1ffbc3bd66 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/signal.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/signal.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/stat.h b/sysdeps/demo/include/abi-bits/stat.h new file mode 120000 index 0000000000..456c469980 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/stat.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/stat.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/uid_t.h b/sysdeps/demo/include/abi-bits/uid_t.h new file mode 120000 index 0000000000..2793900730 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/uid_t.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/uid_t.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/vm-flags.h b/sysdeps/demo/include/abi-bits/vm-flags.h new file mode 120000 index 0000000000..b949271f61 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/vm-flags.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/vm-flags.h \ No newline at end of file diff --git a/sysdeps/demo/include/abi-bits/wait.h b/sysdeps/demo/include/abi-bits/wait.h new file mode 120000 index 0000000000..58ba88fa81 --- /dev/null +++ b/sysdeps/demo/include/abi-bits/wait.h @@ -0,0 +1 @@ +../../../linux/include/abi-bits/wait.h \ No newline at end of file diff --git a/sysdeps/demo/include/bits/syscall.h b/sysdeps/demo/include/bits/syscall.h new file mode 100644 index 0000000000..680bb8c299 --- /dev/null +++ b/sysdeps/demo/include/bits/syscall.h @@ -0,0 +1,100 @@ +#ifndef _MLIBC_SYSCALL_H +#define _MLIBC_SYSCALL_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long __sc_word_t; + +/* These functions are implemented in arch-syscall.cpp. */ +__sc_word_t __do_syscall0(long); +__sc_word_t __do_syscall1(long, __sc_word_t); +__sc_word_t __do_syscall2(long, __sc_word_t, __sc_word_t); +__sc_word_t __do_syscall3(long, __sc_word_t, __sc_word_t, __sc_word_t); +__sc_word_t __do_syscall4(long, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t); +__sc_word_t __do_syscall5(long, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t, + __sc_word_t); +__sc_word_t __do_syscall6(long, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t, + __sc_word_t, __sc_word_t); +__sc_word_t __do_syscall7(long, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t, + __sc_word_t, __sc_word_t, __sc_word_t); +long __do_syscall_ret(unsigned long); + +#ifdef __cplusplus +extern "C++" { + +/* Defining a syscall as a macro is more problematic in C++, since there's a high chance of + * a name collision e.g foo.syscall() or foo::syscall. + */ +inline long syscall(long n) { + return __do_syscall_ret(__do_syscall0(n)); +} +template +long syscall(long n, Arg0 a0) { + return __do_syscall_ret(__do_syscall1(n, (long)a0)); +} +template +long syscall(long n, Arg0 a0, Arg1 a1) { + return __do_syscall_ret(__do_syscall2(n, (long)a0, (long)a1)); +} +template +long syscall(long n, Arg0 a0, Arg1 a1, Arg2 a2) { + return __do_syscall_ret(__do_syscall3(n, (long)a0, (long)a1, (long)a2)); +} +template +long syscall(long n, Arg0 a0, Arg1 a1, Arg2 a2, Arg3 a3) { + return __do_syscall_ret(__do_syscall4(n, (long)a0, (long)a1, (long)a2, (long)a3)); +} +template +long syscall(long n, Arg0 a0, Arg1 a1, Arg2 a2, Arg3 a3, Arg4 a4) { + return __do_syscall_ret(__do_syscall5(n, (long)a0, (long)a1, (long)a2, (long)a3, (long)a4)); +} +template +long syscall(long n, Arg0 a0, Arg1 a1, Arg2 a2, Arg3 a3, Arg4 a4, Arg5 a5) { + return __do_syscall_ret(__do_syscall6(n, (long)a0, (long)a1, (long)a2, (long)a3, (long)a4, (long)a5)); +} +template +long syscall(long n, Arg0 a0, Arg1 a1, Arg2 a2, Arg3 a3, Arg4 a4, Arg5 a5, Arg6 a6) { + return __do_syscall_ret(__do_syscall7(n, (long)a0, (long)a1, (long)a2, (long)a3, (long)a4, (long)a5, (long)a6)); +} + +} /* extern C++ */ +#else + +/* + * Variadic macros are not supported in C89. + * glibc implements syscall() as a variadic function, which we've ruled out. + * musl uses them without checking the C standard in use. So suppressing + * the check here seems reasonable. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvariadic-macros" + +/* These syscall macros were copied from musl. */ +#define __scc(x) ((__sc_word_t)(x)) +#define __syscall0(n) __do_syscall0(n) +#define __syscall1(n,a) __do_syscall1(n,__scc(a)) +#define __syscall2(n,a,b) __do_syscall2(n,__scc(a),__scc(b)) +#define __syscall3(n,a,b,c) __do_syscall3(n,__scc(a),__scc(b),__scc(c)) +#define __syscall4(n,a,b,c,d) __do_syscall4(n,__scc(a),__scc(b),__scc(c),__scc(d)) +#define __syscall5(n,a,b,c,d,e) __do_syscall5(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e)) +#define __syscall6(n,a,b,c,d,e,f) __do_syscall6(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f)) +#define __syscall7(n,a,b,c,d,e,f,g) __do_syscall7(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f),__scc(g)) +#define __SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n +#define __SYSCALL_NARGS(...) __SYSCALL_NARGS_X(__VA_ARGS__,7,6,5,4,3,2,1,0,) +#define __SYSCALL_CONCAT_X(a,b) a##b +#define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X(a,b) +#define __SYSCALL_DISP(b,...) __SYSCALL_CONCAT(b,__SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__) +#define __syscall(...) __SYSCALL_DISP(__syscall,__VA_ARGS__) +#define syscall(...) __do_syscall_ret(__syscall(__VA_ARGS__)) + +#pragma GCC diagnostic pop + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _MLIBC_SYSCALL_H */ diff --git a/sysdeps/demo/meson.build b/sysdeps/demo/meson.build new file mode 100644 index 0000000000..55a4de3f11 --- /dev/null +++ b/sysdeps/demo/meson.build @@ -0,0 +1,54 @@ +sysdep_supported_options = { + 'posix': false, + 'linux': false, + 'glibc': false, + 'bsd': false, +} + +rtld_sources += files( + 'sysdeps.cpp', +) + +libc_sources += files( + 'entry.cpp', + 'sysdeps.cpp', + 'syscall.cpp', +) + +if not no_headers + install_headers( + 'include/abi-bits/auxv.h', + 'include/abi-bits/auxv.h', + 'include/abi-bits/blkcnt_t.h', + 'include/abi-bits/blksize_t.h', + 'include/abi-bits/clockid_t.h', + 'include/abi-bits/dev_t.h', + 'include/abi-bits/errno.h', + 'include/abi-bits/fcntl.h', + 'include/abi-bits/gid_t.h', + 'include/abi-bits/ino_t.h', + 'include/abi-bits/limits.h', + 'include/abi-bits/mode_t.h', + 'include/abi-bits/nlink_t.h', + 'include/abi-bits/pid_t.h', + 'include/abi-bits/seek-whence.h', + 'include/abi-bits/signal.h', + 'include/abi-bits/stat.h', + 'include/abi-bits/uid_t.h', + 'include/abi-bits/vm-flags.h', + 'include/abi-bits/wait.h', + subdir: 'abi-bits', + follow_symlinks: true + ) +endif + +if not headers_only + crt = custom_target('crt1', + build_by_default: true, + command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'], + input: 'crt1.S', + output: 'crt1.o', + install: true, + install_dir: get_option('libdir') + ) +endif diff --git a/sysdeps/demo/syscall.cpp b/sysdeps/demo/syscall.cpp new file mode 100644 index 0000000000..63664943f5 --- /dev/null +++ b/sysdeps/demo/syscall.cpp @@ -0,0 +1,125 @@ +#include +#include + +using sc_word_t = long; + +extern "C" long __do_syscall_ret(unsigned long ret) { + if(ret > -4096UL) { + errno = -ret; + return -1; + } + return ret; +} + +sc_word_t __do_syscall0(long sc) { + register int sc_reg asm("a7") = sc; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : "r"(sc_reg) : "memory", "a1"); + return ret; +} + +sc_word_t __do_syscall1(long sc, + sc_word_t arg1) { + register int sc_reg asm("a7") = sc; + register sc_word_t arg1_reg asm("a0") = arg1; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : + "r"(sc_reg), + "r"(arg1_reg) + : "memory", "a1"); + return ret; +} + +sc_word_t __do_syscall2(long sc, + sc_word_t arg1, sc_word_t arg2) { + register int sc_reg asm("a7") = sc; + register sc_word_t arg1_reg asm("a0") = arg1; + register sc_word_t arg2_reg asm("a1") = arg2; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : + "r"(sc_reg), + "r"(arg1_reg), + "r"(arg2_reg) + : "memory"); + return ret; +} + +sc_word_t __do_syscall3(long sc, + sc_word_t arg1, sc_word_t arg2, sc_word_t arg3) { + register int sc_reg asm("a7") = sc; + register sc_word_t arg1_reg asm("a0") = arg1; + register sc_word_t arg2_reg asm("a1") = arg2; + register sc_word_t arg3_reg asm("a2") = arg3; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : + "r"(sc_reg), + "r"(arg1_reg), + "r"(arg2_reg), + "r"(arg3_reg) + : "memory"); + return ret; +} + +sc_word_t __do_syscall4(long sc, + sc_word_t arg1, sc_word_t arg2, sc_word_t arg3, + sc_word_t arg4) { + register int sc_reg asm("a7") = sc; + register sc_word_t arg1_reg asm("a0") = arg1; + register sc_word_t arg2_reg asm("a1") = arg2; + register sc_word_t arg3_reg asm("a2") = arg3; + register sc_word_t arg4_reg asm("a3") = arg4; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : + "r"(sc_reg), + "r"(arg1_reg), + "r"(arg2_reg), + "r"(arg3_reg), + "r"(arg4_reg) + : "memory"); + return ret; +} + +sc_word_t __do_syscall5(long sc, + sc_word_t arg1, sc_word_t arg2, sc_word_t arg3, + sc_word_t arg4, sc_word_t arg5) { + register int sc_reg asm("a7") = sc; + register sc_word_t arg1_reg asm("a0") = arg1; + register sc_word_t arg2_reg asm("a1") = arg2; + register sc_word_t arg3_reg asm("a2") = arg3; + register sc_word_t arg4_reg asm("a3") = arg4; + register sc_word_t arg5_reg asm("a4") = arg5; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : + "r"(sc_reg), + "r"(arg1_reg), + "r"(arg2_reg), + "r"(arg3_reg), + "r"(arg4_reg), + "r"(arg5_reg) + : "memory"); + return ret; +} + +sc_word_t __do_syscall6(long sc, + sc_word_t arg1, sc_word_t arg2, sc_word_t arg3, + sc_word_t arg4, sc_word_t arg5, sc_word_t arg6) { + register int sc_reg asm("a7") = sc; + register sc_word_t arg1_reg asm("a0") = arg1; + register sc_word_t arg2_reg asm("a1") = arg2; + register sc_word_t arg3_reg asm("a2") = arg3; + register sc_word_t arg4_reg asm("a3") = arg4; + register sc_word_t arg5_reg asm("a4") = arg5; + register sc_word_t arg6_reg asm("a5") = arg6; + register sc_word_t ret asm("a0"); + asm volatile ("ecall" : "=r"(ret) : + "r"(sc_reg), + "r"(arg1_reg), + "r"(arg2_reg), + "r"(arg3_reg), + "r"(arg4_reg), + "r"(arg5_reg), + "r"(arg6_reg) + : "memory" + ); + return ret; +} diff --git a/sysdeps/demo/sysdeps.cpp b/sysdeps/demo/sysdeps.cpp new file mode 100644 index 0000000000..988c8445a1 --- /dev/null +++ b/sysdeps/demo/sysdeps.cpp @@ -0,0 +1,74 @@ +#include "mlibc/tcb.hpp" +#include +#include +#include +#include +#include + +#define SYS_EXIT 0 +#define SYS_WRITE 1 +#define SYS_MMAP 2 + +#define STUB() \ + ({ \ + __ensure(!"STUB_ONLY function was called"); \ + __builtin_unreachable(); \ + }) + +namespace mlibc { + +void sys_libc_panic() { + sys_libc_log("!!! mlibc panic !!!"); + sys_exit(-1); + __builtin_trap(); +} + +void sys_libc_log(const char *msg) { + ssize_t unused; + sys_write(2, msg, strlen(msg), &unused); +} + +int sys_isatty(int fd) { + (void)fd; + return 0; +} + +int sys_write(int fd, void const *buf, size_t size, ssize_t *ret) { + *ret = syscall(SYS_WRITE, fd, buf, size); + return ret == 0 ? 0 : -1; +} + +int sys_tcb_set(void *pointer) { + uintptr_t thread_data = reinterpret_cast(pointer) + sizeof(Tcb); + asm volatile("mv tp, %0" ::"r"(thread_data)); + return 0; +} + +int sys_anon_allocate(size_t size, void **pointer) { + auto out = syscall(SYS_MMAP, nullptr, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + *pointer = (void *)out; + if (*pointer == MAP_FAILED) + return -1; + return 0; +} + +int sys_anon_free(void *, unsigned long) { return 0; } + +int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) { return 0; } + +void sys_exit(int status) { + syscall(SYS_EXIT, status); + __builtin_unreachable(); +} + +int sys_close(int fd) { STUB(); } +int sys_futex_wake(int *pointer) { STUB(); } +int sys_futex_wait(int *, int, timespec const *) { STUB(); } +int sys_read(int, void *, unsigned long, long *) { STUB(); } +int sys_open(const char *, int, unsigned int, int *) { STUB(); } +int sys_vm_map(void *addr, size_t len, int prot, int flags, int fd, + off_t offset, void **ret) { + STUB(); +} +} // namespace mlibc From d36425b49d637df5d0ace34e887e14427b902832 Mon Sep 17 00:00:00 2001 From: Matt Staveley-Taylor Date: Sat, 28 Dec 2024 21:19:25 +0000 Subject: [PATCH 3/3] Add book --- book/.gitignore | 1 + book/book.toml | 6 + book/src/README.md | 5 + book/src/SUMMARY.md | 11 ++ book/src/porting/choosing_a_compiler.md | 26 ++++ book/src/porting/implementing_sysdeps.md | 148 +++++++++++++++++++++++ book/src/porting/intro.md | 1 + book/src/porting/kernel_prerequisites.md | 11 ++ book/src/porting/next_steps.md | 3 + book/src/porting/upstreaming.md | 9 ++ meson.build | 4 +- sysdeps/demo/meson.build | 8 ++ sysdeps/demo/sysdeps.cpp | 10 +- 13 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 book/.gitignore create mode 100644 book/book.toml create mode 100644 book/src/README.md create mode 100644 book/src/SUMMARY.md create mode 100644 book/src/porting/choosing_a_compiler.md create mode 100644 book/src/porting/implementing_sysdeps.md create mode 100644 book/src/porting/intro.md create mode 100644 book/src/porting/kernel_prerequisites.md create mode 100644 book/src/porting/next_steps.md create mode 100644 book/src/porting/upstreaming.md diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 0000000000..7585238efe --- /dev/null +++ b/book/.gitignore @@ -0,0 +1 @@ +book diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 0000000000..dd63fc53cc --- /dev/null +++ b/book/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["Matt Staveley-Taylor"] +language = "en" +multilingual = false +src = "src" +title = "mlibc User Guide" diff --git a/book/src/README.md b/book/src/README.md new file mode 100644 index 0000000000..2bd6104a07 --- /dev/null +++ b/book/src/README.md @@ -0,0 +1,5 @@ +# Introduction + +Welcome to the [`mlibc`](https://github.com/managarm/mlibc) User Guide. + +[`mlibc`](https://github.com/managarm/mlibc) is a fully featured C standard library designed with portability in mind. It provides a clean syscall abstraction layer for new operating system ports to plug into. This guide will explain how to integrate a new OS / syscall backend and get your first program up and running. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 0000000000..43fa6b28b2 --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,11 @@ +# Summary + +[Introduction](README.md) + +- [Adding a new OS port](porting/intro.md) + - [Kernel prerequisites](porting/kernel_prerequisites.md) + - [Choosing a compiler](porting/choosing_a_compiler.md) + - [Implementing sysdeps](porting/implementing_sysdeps.md) + - [Upstreaming your port](porting/upstreaming.md) + - [Next steps](porting/next_steps.md) +- [Adding a new ISA port]() diff --git a/book/src/porting/choosing_a_compiler.md b/book/src/porting/choosing_a_compiler.md new file mode 100644 index 0000000000..9dcbc6567e --- /dev/null +++ b/book/src/porting/choosing_a_compiler.md @@ -0,0 +1,26 @@ +# Choosing a compiler + +To compile [`mlibc`](https://github.com/managarm/mlibc) and any userspace programs which link against it, you'll need a suitable compiler. Roughly speaking you have two choices: + +1. **Build a full [OS Specific Toolchain](https://wiki.osdev.org/OS_Specific_Toolchain).** + + With this, you'll have a compiler that implicitly links against (m)libc, so we recommend doing this _after_ you have a basic port working. + +1. **Use a generic ELF toolchain.** + + If compiling your kernel with a generic toolchain like `x86_64-unknown-elf-gcc` or `riscv64-elf-gcc`, you may re-use the same compiler for this. + + Note that when compiling userspace programs, you must manually provide a number of command line arguments (such as the location of your compiled libc and any headers). + + +For [`mlibc-demo-os`](https://github.com/64/mlibc-demo-os), we'll rely on a generic `riscv64-elf-gcc` toolchain provided by our distro. + +## Creating a Meson cross file + +[`mlibc`](https://github.com/managarm/mlibc) uses a build system called [Meson](https://mesonbuild.com/). When cross-compiling, you must tell meson about your compiler and target via a [_cross file_](https://mesonbuild.com/Cross-compilation.html). + +For [`mlibc-demo-os`](https://github.com/64/mlibc-demo-os), our cross file looks like this: + +```toml +{{#include ../../../ci/demo.cross-file}} +``` diff --git a/book/src/porting/implementing_sysdeps.md b/book/src/porting/implementing_sysdeps.md new file mode 100644 index 0000000000..9b425079c4 --- /dev/null +++ b/book/src/porting/implementing_sysdeps.md @@ -0,0 +1,148 @@ +# Implementing your sysdeps + +## Telling mlibc about your sysdeps + +In mlibc's top level `meson.build` file, you'll see a large `if`/`else` chain that selects the right sysdeps subdirectory based on the system name. We'll add ourselves here: +```meson +{{#include ../../../meson.build:demo-sysdeps}} + # ... +``` + +Now, create the `sysdeps/demo` folder. At minimum it should contain a `meson.build` file where we'll declare all the `.cpp` files and select header files to install: + +```meson +{{#include ../../../sysdeps/demo/meson.build:sources-and-includes}} +``` + +The `sysdep_supported_options` tells mlibc which 'options' your sysdeps supports. For example, the `unistd.h` header is only available when the POSIX option is enabled. + +## Preparing to build mlibc + +Now, from a fresh clone of `mlibc` you can run: + +``` +$ meson setup --cross-file=path/to/your.cross-file -Ddefault_library=static -Dbuildtype=debug build +``` + +The `-Ddefault_library=static` tells meson to only produce a statically linked library (`libc.a`). We *strongly* recommend getting statically linked binaries to work before dynamically linked ones. + +Now run `ninja -C build` to start the build. You'll likely get a large number of compilation errors at this point, and we'll work on fixing these in the next sections. + +## Selecting ABI headers + +Many of the compile errors are because we need to tell mlibc about the ABI between our sysdeps and kernel. For example, we must define the layout of `struct stat`. + +We _strongly_ recommend re-using an existing ABI where possible rather than creating your own, because many programs assume a particular ABI and will fail to compile or subtly break if you choose a different one. + +For the demo OS, we'll symlink each ABI header to Linux's definition in `sysdeps/linux/include/abi-bits`. Then, we'll add the following to our `meson.build`: + +```meson +{{#include ../../../sysdeps/demo/meson.build:abi-includes}} +``` + +This is not an exhaustive list of ABI headers but should be enough to get started. + +## Implementing (S)crt1 + +Next, we must provide a definition of the ELF entrypoint (traditionally named `_start`). Each ISA tends to require its own definition written in assembly; for example RISC-V targets must initialise the `gp` register before jumping to C++. + +Traditionally the file that defines `_start` is called `crt1.S` (or `Scrt1.S` for position independent executables). This produces an object file which has to be linked into each application. + +Part of configuring an [OS Specific Toolchain](https://wiki.osdev.org/OS_Specific_Toolchain) is [specifying the location of `(S)crt1.o`](https://wiki.osdev.org/OS_Specific_Toolchain#Start_Files_Directory) so that it linked automatically, but generic ELF targets must link it explicitly. + +We recommend copying `(S)crt1.S` from an existing target like Linux. Then, we'll compile it by adding the following to our `meson.build`: + +``` +{{#include ../../../sysdeps/demo/meson.build:crt1}} +``` + +## Implementing a C++ entry point + +Now, we'll implement the C++ entry point that we call from `crt1.S`. + +```cpp +{{#include ../../../sysdeps/demo/entry.cpp}} +``` + +The call to `__dlapi_enter` is used to perform initialisation in statically linked executables (but is a no-op in dynamically linked ones). For example, any global constructors in the program will be called from here. + +The `LibraryGuard` is used to perform initialisation of `libc` itself like parsing the environment and arguments from the kernel. Putting this code in a global constructor ensures that it's called at the right time relative to other pieces of initialisation. + +## Performing system calls + +The final piece of infrastructure we require is the ability to invoke system calls. + +For example, on RISC-V a system call is invoked via the `ecall` instruction and requires putting arguments in specific registers, which requires a bit of (inline) assembly. We recommend copying this glue from an existing target. + +For the demo OS, this is provided by `syscall.cpp` and `include/bits/syscall.h`. + +## Implementing sysdeps + +Finally we're ready to implement the actual sysdep functions. For a basic statically-linked hello world program, you'll need to provide definitions of the following sysdep functions: + +- `mlibc::sys_libc_panic` +- `mlibc::sys_libc_log` +- `mlibc::sys_isatty` +- `mlibc::sys_write` +- `mlibc::sys_tcb_set` +- `mlibc::sys_anon_allocate` +- `mlibc::sys_anon_free` +- `mlibc::sys_seek` +- `mlibc::sys_exit` +- `mlibc::sys_close` +- `mlibc::sys_futex_wake` +- `mlibc::sys_futex_wait` +- `mlibc::sys_read` +- `mlibc::sys_open` +- `mlibc::sys_vm_map` + +Note that many of these functions are declared as weak symbols. You _must_ include the relevant headers (e.g ``) before providing definitions otherwise these won't be emitted as weak symbols and you'll see strange errors. + +Most sysdep functions return an integer error code (0 for success, -E for errors) and return data via out parameters. Note that sysdeps shouldn't set errno directly - mlibc will set it from the error code you return. + +As a general strategy, it's a good idea to stub whatever's required to make things compile, and then add proper implementations later. For example: + +```cpp +{{#include ../../../sysdeps/demo/sysdeps.cpp:stub}} + +namespace mlibc { +int sys_close(int fd) { STUB(); } +} +``` + +## Compiling a test program + +At this point, you should be able to compile and link mlibc itself. Congratulations! + +Now we'll compile a simple hello world program that we can run on our kernel: + +```bash +# Install mlibc to a temporary directory +DESTDIR=/tmp/mlibc-install ninja -C build install + +# Some targets require an implementation of libgcc (if you see linker errors below). +# We recommend using cc-runtime: https://github.com/osdev0/cc-runtime + +riscv64-elf-gcc \ + -static -nostdinc -nostdlib -g \ + -I /tmp/mlibc-install/include \ + /tmp/mlibc-install/lib/crt1.o \ + hello_world_program.c \ + /tmp/mlibc-install/lib/libc.a \ + /path/to/cc-runtime.a \ + -o hello_world_program +``` + +## Troubleshooting + +Something not working? Here are some common issues to look out for: + +- Your kernel isn't loading the ELF file correctly. Double check that you're loading the contents from file to the right addresses and zeroing the uninitialised portions. +- Your `sys_anon_allocate` function is broken. Try commenting out `sys_anon_free` and see if that helps. Try printing the addresses to see if you're getting unique allocations. +- You're pushing the arguments/environment/auxiliary vectors to the stack wrong. Double check you're pushing in the right order and everything is properly aligned. +- You're not saving and restoring the userspace state properly when a syscall happens. +- Your `syscall` glue is passing arguments in the wrong registers. Double check that the kernel and userspace agree on the order of arguments. + +When stuck, we recommend using GDB to step through the program. + +If all else fails, feel free to hop in [the Managarm Discord server](https://discord.gg/7WB6Ur3) and ask for help in `#mlibc-dev`. diff --git a/book/src/porting/intro.md b/book/src/porting/intro.md new file mode 100644 index 0000000000..84dcd2aeae --- /dev/null +++ b/book/src/porting/intro.md @@ -0,0 +1 @@ +# Adding a new OS port diff --git a/book/src/porting/kernel_prerequisites.md b/book/src/porting/kernel_prerequisites.md new file mode 100644 index 0000000000..ebdb13ddef --- /dev/null +++ b/book/src/porting/kernel_prerequisites.md @@ -0,0 +1,11 @@ +# Kernel prerequisites + +Before you attempt to port [`mlibc`](https://github.com/managarm/mlibc), ensure your kernel supports these things: +- Writing text to the screen or a serial port +- Basic paging and virtual memory operations +- Loading an ELF program, mapping a stack and jumping to the entrypoint +- Handling syscalls from userspace + +For this port, we'll use [`mlibc-demo-os`](https://github.com/64/mlibc-demo-os), a small RISC-V kernel which supports the minimum functionality required for a mlibc 'hello world' program. Refer to this if you get stuck! + +Note that a filesystem and storage drivers are not strictly necessary. In [`mlibc-demo-os`](https://github.com/64/mlibc-demo-os), we `include_bytes!` the contents of the user-space program into the kernel's executable. To support multiple files, you could include a trivial read-only filesystem like `tar` instead. diff --git a/book/src/porting/next_steps.md b/book/src/porting/next_steps.md new file mode 100644 index 0000000000..a5f9841c40 --- /dev/null +++ b/book/src/porting/next_steps.md @@ -0,0 +1,3 @@ +# Next steps + +- implement more sysdeps, enable more options diff --git a/book/src/porting/upstreaming.md b/book/src/porting/upstreaming.md new file mode 100644 index 0000000000..f8612bc412 --- /dev/null +++ b/book/src/porting/upstreaming.md @@ -0,0 +1,9 @@ +# Upstreaming your port + +Once your port is reasonably stable, feel free to submit it to us upstream. This ensures that your sysdeps won't be broken by any internal refactorings. + +Though we won't be able to test your kernel on our CI, we require you add your port to the 'Compile sysdeps' GitHub action which checks that compilation succeeds. + +It's a good idea to include a `.clang-format` file so that any changes we make to your code will be formatted to your liking. + +See the [pull request adding Astral sysdeps](https://github.com/managarm/mlibc/pull/1136) for an example to follow. diff --git a/meson.build b/meson.build index e7540ecf51..e8957834eb 100644 --- a/meson.build +++ b/meson.build @@ -259,11 +259,11 @@ elif host_machine.system() == 'astral' internal_conf.set10('MLIBC_MAP_DSO_SEGMENTS', true) internal_conf.set10('MLIBC_MAP_FILE_WINDOWS', true) subdir('sysdeps/astral') +# ANCHOR: demo-sysdeps elif host_machine.system() == 'demo' - rtld_include_dirs += include_directories('sysdeps/demo/include') - libc_include_dirs += include_directories('sysdeps/demo/include') subdir('sysdeps/demo') else +# ANCHOR_END: demo-sysdeps error('No sysdeps defined for OS: ' + host_machine.system()) endif diff --git a/sysdeps/demo/meson.build b/sysdeps/demo/meson.build index 55a4de3f11..153feed1c6 100644 --- a/sysdeps/demo/meson.build +++ b/sysdeps/demo/meson.build @@ -1,3 +1,4 @@ +# ANCHOR: sources-and-includes sysdep_supported_options = { 'posix': false, 'linux': false, @@ -8,13 +9,17 @@ sysdep_supported_options = { rtld_sources += files( 'sysdeps.cpp', ) +rtld_include_dirs += include_directories('sysdeps/demo/include') libc_sources += files( 'entry.cpp', 'sysdeps.cpp', 'syscall.cpp', ) +libc_include_dirs += include_directories('sysdeps/demo/include') +# ANCHOR_END: sources-and-includes +# ANCHOR: abi-includes if not no_headers install_headers( 'include/abi-bits/auxv.h', @@ -41,7 +46,9 @@ if not no_headers follow_symlinks: true ) endif +# ANCHOR_END: abi-includes +# ANCHOR: crt1 if not headers_only crt = custom_target('crt1', build_by_default: true, @@ -52,3 +59,4 @@ if not headers_only install_dir: get_option('libdir') ) endif +# ANCHOR_END: crt1 diff --git a/sysdeps/demo/sysdeps.cpp b/sysdeps/demo/sysdeps.cpp index 988c8445a1..03ba55d807 100644 --- a/sysdeps/demo/sysdeps.cpp +++ b/sysdeps/demo/sysdeps.cpp @@ -9,11 +9,13 @@ #define SYS_WRITE 1 #define SYS_MMAP 2 +// ANCHOR: stub #define STUB() \ ({ \ - __ensure(!"STUB_ONLY function was called"); \ + __ensure(!"STUB function was called"); \ __builtin_unreachable(); \ }) +// ANCHOR_END: stub namespace mlibc { @@ -35,7 +37,7 @@ int sys_isatty(int fd) { int sys_write(int fd, void const *buf, size_t size, ssize_t *ret) { *ret = syscall(SYS_WRITE, fd, buf, size); - return ret == 0 ? 0 : -1; + return *ret >= 0 ? 0 : -1; } int sys_tcb_set(void *pointer) { @@ -55,7 +57,9 @@ int sys_anon_allocate(size_t size, void **pointer) { int sys_anon_free(void *, unsigned long) { return 0; } -int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) { return 0; } +int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) { + return ESPIPE; +} void sys_exit(int status) { syscall(SYS_EXIT, status);