Implementing your sysdeps (part 1)
As we need libc headers to build the toolchain, we will first implement enough of the sysdeps to install the headers.
Creating a Meson cross file
mlibc uses a build system called Meson. When cross-compiling, you must tell meson about your compiler and target via a cross file.
For mlibc-demo-os, our cross file looks like this:
[host_machine]
system = 'demo'
cpu_family = 'riscv64'
cpu = 'riscv64'
endian = 'little'
[binaries]
c = 'riscv64-demo-gcc'
cpp = 'riscv64-demo-g++'
ar = 'riscv64-demo-ar'
strip = 'riscv64-demo-strip'
ld = 'riscv64-demo-ld'
[built-in options]
c_args = ['-march=rv64gc', '-mabi=lp64d']
cpp_args = ['-march=rv64gc', '-mabi=lp64d']
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:
elif host_machine.system() == 'demo'
subdir('sysdeps/demo')
# ...
Now, create the sysdeps/demo folder. At minimum it should contain a meson.build file. Here, all the source and include files are declared for the sysdep. For now, we will only add this:
sysdep_supported_options = {
'posix': true,
'linux': false,
'glibc': false,
'bsd': false,
}
The sysdep_supported_options tells mlibc which 'options' your sysdeps support. For example, the unistd.h header is only available when the POSIX option is enabled.
While sysdep_supported_options only states which options are supported to be used by the sysdeps, you are free to disable options with meson options at meson setup time. The default behavior is to enable supported options.
Selecting ABI headers
Your userland needs an ABI to communicate with your kernel. For example, we must define the layout of struct stat.
Re-using an existing ABI is strongly recommended. Many programs assume a particular ABI and will fail to compile or subtly break if you choose a different one (This is the program's fault and not mlibc's or your sysdeps').
Note that using an existing ABI does not imply you have to implement OS specific features. For example, even if you use the Linux ABI you do not have to implement Linux specific features.
For the demo OS, we'll symlink each ABI header to Linux's definition in abis/linux. This is so we don't have to define a whole new ABI for a brief demo. Then, we'll add the following to our meson.build:
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',
'include/abi-bits/riscv-hwprobe.h',
'include/abi-bits/sigevent.h',
'include/abi-bits/sigval.h',
'include/abi-bits/sa_family_t.h',
'include/abi-bits/sockaddr_storage.h',
'include/abi-bits/sig-limits.h',
'include/abi-bits/suseconds_t.h',
'include/abi-bits/access.h',
'include/abi-bits/socklen_t.h',
'include/abi-bits/socket.h',
'include/abi-bits/poll.h',
'include/abi-bits/resource.h',
'include/abi-bits/in.h',
'include/abi-bits/rlim_t.h',
'include/abi-bits/utsname.h',
'include/abi-bits/fd_set.h',
'include/abi-bits/sem.h',
'include/abi-bits/time.h',
'include/abi-bits/ipc.h',
'include/abi-bits/statvfs.h',
'include/abi-bits/fsblkcnt_t.h',
'include/abi-bits/fsfilcnt_t.h',
'include/abi-bits/shm.h',
'include/abi-bits/termios.h',
'include/abi-bits/msg.h',
'include/abi-bits/mqueue.h',
'include/abi-bits/utmp-defines.h',
'include/abi-bits/utmpx.h',
subdir: 'abi-bits',
follow_symlinks: true
)
endif
This is not an exhaustive list of ABI headers but should be enough to get started.
Note that the Linux ABI will be silently changed if it turns out that it is wrong - you end up subscribing to what amounts to ABI breaks if that (hopefully only rarely) happens.
For this reason, it is also strongly recommended to pin the commit or version of mlibc which will be used, as otherwise there could be changes which silently break your compiled software.
If subscribing to the Linux ABI is not desired, you can still fork it by creating a copy under abis/$os/ and symlinking to it instead of the Linux ABI.
Configuring mlibc for headers only
Now, you can run:
$ meson \
setup \
--cross-file=path/to/your.cross-file \
--prefix=/usr \
-Dheaders_only=true \
headers-build
The -Dheaders_only=true option tells meson that we only want the headers and not a full libc.
To install the headers into the sysroot, run:
DESTDIR=${SYSROOT_DIR} ninja -C headers-build install
We are now ready to start building the toolchain!