hosted/linux
Hosted targets only.
Linux's system calls with no C library beneath them, and the process entry of a static Linux executable.
Every declaration is Linux-only; on Darwin and FreeBSD the module declares nothing, which lets hosted/io and hosted/heap import it on every hosted system. Each call is one assembly block on x86-64, arm64 and RV64, and a failure comes back in its result as minus the errno; there is no errno cell. The *at calls take at_fdcwd for a path relative to the working directory, and statx's file_status is the kernel's one record on every architecture. Every public routine has a link name of its own, _landin_linux_ and the routine's name, so none of them replaces the C library's function of the same name under --link=libc. _landin_start is where a static executable starts: it reads argc, argv, the environment and the auxiliary vector from the initial stack and calls main as C's runtime would. Before main it holds the processor to the executable's CPU feature level, as a loader holds an x86-64 executable to its ISA note: cpuid and the operating system's enabled state on x86-64, AT_HWCAP on arm64 and riscv_hwprobe on RV64. A processor without a feature of the level ends the process with status 127 and one line on standard error naming the level and the missing feature; the baseline checks nothing. level, read_cpu_words and missing_feature expose the check. No C library initialisation runs and there is no thread-local storage. environment and auxiliary report what that entry found, and nothing when the C runtime started the program instead.
Items
- cpu_words type
- environment_table type
- file_status type
- statx_time type
- no_environment atom
- at_fdcwd value
- eacces value
- eintr value
- enoent value
- enotdir value
- eperm value
- erofs value
- map_anonymous value
- map_private value
- o_creat value
- o_rdonly value
- o_trunc value
- o_wronly value
- prot_read value
- prot_write value
- statx_ino value
- _landin_start function
- _landin_start_main function
- auxiliary function
- close function
- environment function
- exit_group function
- level function
- missing_feature function
- mmap function
- munmap function
- openat function
- page_size function
- read function
- read_cpu_words function
- statx function
- write function
Executable example
This complete program is maintained in the repository runtime tests. View source.
import hosted/linux -- hosted/linux's system calls, made directly with no C library -- beneath them, on every Linux architecture. Each failure comes back as -- minus its errno. The paths are NUL-terminated: /dev/zero and /dev/null -- exist everywhere; a missing parent gives ENOENT, a path through a -- character device ENOTDIR, and a read-only sysctl, which refuses a writer -- whoever it is, EACCES. zero_name: [10]u8 = [47, 100, 101, 118, 47, 122, 101, 114, 111, 0] null_name: [10]u8 = [47, 100, 101, 118, 47, 110, 117, 108, 108, 0] null_again: [17]u8 = [47, 100, 101, 118, 47, 46, 46, 47, 100, 101, 118, 47, 110, 117, 108, 108, 0] missing: [30]u8 = [47, 108, 97, 110, 100, 105, 110, 45, 97, 98, 115, 101, 110, 116, 45, 100, 105, 114, 101, 99, 116, 111, 114, 121, 47, 102, 105, 108, 101, 0] not_directory: [15]u8 = [47, 100, 101, 118, 47, 110, 117, 108, 108, 47, 102, 105, 108, 101, 0] sysctl: [27]u8 = [47, 112, 114, 111, 99, 47, 115, 121, 115, 47, 107, 101, 114, 110, 101, 108, 47, 111, 115, 114, 101, 108, 101, 97, 115, 101, 0] -- A character device's file type in a statx mode. type_mask: u16 = 0o170000 character_device: u16 = 0o020000 public main: () -> (code: i32) = code = 1 message: []u8 = "system calls\n" sent: isize = linux.write(1, addr message[0], lenof message) return when sent <> isize(lenof message) code = 2 zero: i32 = linux.openat(linux.at_fdcwd, addr zero_name[0], linux.o_rdonly, 0) return when zero < 0 mut buffer: [8]u8 = [8 of 255] count: isize = linux.read(zero, addr buffer[0], 8) return when count <> 8 for at in usize(0)..<usize(8) do return when buffer[at] <> 0 end for code = 3 return when linux.close(zero) <> 0 -- EBADF: the descriptor was released. return when linux.close(zero) <> -9 code = 4 return when linux.openat(linux.at_fdcwd, addr missing[0], linux.o_rdonly, 0) <> -linux.enoent code = 5 return when linux.openat(linux.at_fdcwd, addr not_directory[0], linux.o_rdonly, 0) <> -linux.enotdir code = 6 return when linux.openat(linux.at_fdcwd, addr sysctl[0], linux.o_wronly, 0) <> -linux.eacces code = 7 mut first: linux.file_status = zeroed return when linux.statx(linux.at_fdcwd, addr null_name[0], 0, linux.statx_ino, addr first) <> 0 return when (first.mask & linux.statx_ino) == 0 return when (first.mode & type_mask) <> character_device code = 8 mut second: linux.file_status = zeroed return when linux.statx(linux.at_fdcwd, addr null_again[0], 0, linux.statx_ino, addr second) <> 0 return when second.inode <> first.inode or second.device_major <> first.device_major or second.device_minor <> first.device_minor code = 9 mut absent: linux.file_status = zeroed return when linux.statx(linux.at_fdcwd, addr missing[0], 0, linux.statx_ino, addr absent) <> -linux.enoent code = 42 end main
cpu_words type
When compiler.os == linux; compiler.arch == x86_64
public cpu_words: type = struct
standard_ecx: u32
structured_ebx: u32
extended_ecx: u32
enabled_state: u32
end cpu_wordsWhat the processor says of itself: cpuid leaf 1's ecx, leaf 7's ebx, leaf 0x80000001's ecx, and the state the operating system enables, XCR0's low half, which is zero unless both the level needs AVX state and cpuid reports OSXSAVE. A leaf the processor does not have reads as zero.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64)
public cpu_words: type = struct
hwcap: u64
end cpu_wordsWhat the kernel says of the processor: the auxiliary vector's AT_HWCAP, as _landin_start found it, or zero.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64)
public cpu_words: type = struct
extensions: u64
end cpu_wordsWhat the kernel says of the processor: riscv_hwprobe's RISCV_HWPROBE_KEY_IMA_EXT_0, or zero when the kernel cannot answer. Neither it nor AT_HWCAP reports a vendor extension, so XTheadBa is not among what an executable is held to.
environment_table type
When compiler.os == linux
public environment_table: type = no_environment | ptr ptr u8The environment table: NUL-terminated NAME=value strings ending at a null entry, or no_environment.
file_status type
When compiler.os == linux
public file_status: type = struct
mask: u32
block_size: u32
attributes: u64
links: u32
uid: u32
gid: u32
mode: u16
spare0: u16
inode: u64
size: u64
blocks: u64
attributes_mask: u64
accessed: statx_time
born: statx_time
changed: statx_time
modified: statx_time
special_device_major: u32
special_device_minor: u32
device_major: u32
device_minor: u32
mount_id: u64
spare: [13]u64
end file_statusThe kernel's struct statx, one layout on every architecture. The device is device_major and device_minor; mask says which other fields the call filled.
statx_time type
When compiler.os == linux
public statx_time: type = struct
seconds: i64
nanoseconds: u32
reserved: i32
end statx_timeA statx time: whole seconds and the nanoseconds after them.
no_environment atom
When compiler.os == linux
public no_environment: atomWhat environment returns when the C runtime, not _landin_start, started the program.
at_fdcwd value
When compiler.os == linux
public at_fdcwd: i32 = -100The directory descriptor that makes an *at call's relative path relative to the working directory.
eacces value
When compiler.os == linux
public eacces: i32 = 13errno: permission denied.
eintr value
When compiler.os == linux
public eintr: i32 = 4errno: interrupted by a signal before anything was transferred.
enoent value
When compiler.os == linux
public enoent: i32 = 2errno: no such file or directory.
enotdir value
When compiler.os == linux
public enotdir: i32 = 20errno: a path component is not a directory.
eperm value
When compiler.os == linux
public eperm: i32 = 1errno: the operation is not permitted.
erofs value
When compiler.os == linux
public erofs: i32 = 30errno: the file system is read-only.
map_anonymous value
When compiler.os == linux
public map_anonymous: i32 = 32mmap flag: the mapping is backed by no file and starts zeroed.
map_private value
When compiler.os == linux
public map_private: i32 = 2mmap flag: the mapping is private to this process.
o_creat value
When compiler.os == linux
public o_creat: i32 = 64openat flag: create the file if it does not exist.
o_rdonly value
When compiler.os == linux
public o_rdonly: i32 = 0openat flag: read only.
o_trunc value
When compiler.os == linux
public o_trunc: i32 = 512openat flag: truncate an existing regular file to length zero.
o_wronly value
When compiler.os == linux
public o_wronly: i32 = 1openat flag: write only.
prot_read value
When compiler.os == linux
public prot_read: i32 = 1mmap protection: the pages may be read.
prot_write value
When compiler.os == linux
public prot_write: i32 = 2mmap protection: the pages may be written.
statx_ino value
When compiler.os == linux
public statx_ino: u32 = 256statx mask: the inode number, beside the basic fields every call fills.
_landin_start function
When compiler.os == linux; compiler.arch == x86_64
public extern(naked) _landin_start: () -> noneThe process entry: the kernel starts a static Linux executable here, with argc on top of the stack and no return address. It clears the frame pointer so a debugger stops at this frame, aligns the stack to sixteen bytes and calls _landin_start_main with the initial stack's address.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64)
public extern(naked) _landin_start: () -> noneThe process entry: the kernel starts a static Linux executable here, with argc at the sixteen-byte-aligned stack pointer and no return address. It clears the frame and link registers so a debugger stops at this frame and calls _landin_start_main with the initial stack's address.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64)
public extern(naked) _landin_start: () -> noneThe process entry: the kernel starts a static Linux executable here, with argc on top of the stack and no return address. It clears the frame pointer and return address so a debugger stops at this frame, aligns the stack to sixteen bytes and calls _landin_start_main with the initial stack's address.
_landin_start_main function
When compiler.os == linux
public extern(c) _landin_start_main: (stack: ptr usize) -> noneWhat _landin_start runs: it reads argc, argv, the environment and the auxiliary vector from the initial stack at stack, keeps the environment and the vector, holds the processor to the executable's CPU feature level, calls the program's main with them as C's runtime would, and ends the process with main's result. A processor without a feature of the level ends the process with status 127 and one line on standard error before main runs. No C library initialisation runs and no thread-local storage exists.
auxiliary function
When compiler.os == linux
public link(symbol: "_landin_linux_auxiliary") auxiliary:
(tag: u64) -> (found: bool, value: u64)The auxiliary-vector entry tagged tag (6 is the page size), as _landin_start found it: whether there is one, and its value.
close function
When compiler.os == linux
public link(symbol: "_landin_linux_close") close:
(descriptor: i32) -> (result: i32)Close descriptor: zero, or minus the errno. The descriptor is released even when this fails, so a failed close is never retried.
environment function
When compiler.os == linux
public link(symbol: "_landin_linux_environment") environment:
() -> (table: environment_table)The environment _landin_start found on the initial stack.
exit_group function
When compiler.os == linux
public link(symbol: "_landin_linux_exit_group") exit_group:
(status: i32) -> noreturnEnd every thread of the process with status. It does not return.
level function
When compiler.os == linux; compiler.arch == x86_64; compiler.feature.avx512f
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; compiler.arch == x86_64; not (compiler.feature.avx512f) and (compiler.feature.avx)
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; compiler.arch == x86_64; not (compiler.feature.avx512f or compiler.feature.avx) and (compiler.feature.popcnt)
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; compiler.arch == x86_64; not (compiler.feature.avx512f or compiler.feature.avx or compiler.feature.popcnt)
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64); compiler.feature.lse
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64); not (compiler.feature.lse)
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64); compiler.feature.zba; compiler.feature.xtheadba
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64); compiler.feature.zba; not (compiler.feature.xtheadba)
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64); not (compiler.feature.zba); compiler.feature.xtheadba
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64); not (compiler.feature.zba); not (compiler.feature.xtheadba)
public link(symbol: "_landin_linux_level") level:
() -> (name: []u8)The level this executable was built for.
missing_feature function
When compiler.os == linux; compiler.arch == x86_64; compiler.feature.avx512f
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there.
When compiler.os == linux; compiler.arch == x86_64; not (compiler.feature.avx512f) and (compiler.feature.avx)
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there.
When compiler.os == linux; compiler.arch == x86_64; not (compiler.feature.avx512f or compiler.feature.avx) and (compiler.feature.popcnt)
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there.
When compiler.os == linux; compiler.arch == x86_64; not (compiler.feature.avx512f or compiler.feature.avx or compiler.feature.popcnt)
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64); compiler.feature.lse
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there: HWCAP_ATOMICS, HWCAP_CRC32 and HWCAP_ASIMDRDM.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64); not (compiler.feature.lse)
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64); compiler.feature.zba
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there: RISCV_HWPROBE_EXT_ZBA.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64); not (compiler.feature.zba)
public link(symbol: "_landin_linux_missing_feature") missing_feature:
(words: cpu_words) -> (name: []u8)The first feature of this executable's level that words lacks, by the name compiler.feature gives it, or "" when the level is all there.
mmap function
When compiler.os == linux
public link(symbol: "_landin_linux_mmap") mmap:
(address: usize, length: usize, protection: i32, flags: i32,
descriptor: i32, offset: i64) -> (result: i64)Map length bytes: the mapping's address, or minus the errno. With map_anonymous the descriptor is -1 and the offset zero.
munmap function
When compiler.os == linux
public link(symbol: "_landin_linux_munmap") munmap:
(address: usize, length: usize) -> (result: i32)Unmap the length bytes at address: zero, or minus the errno.
openat function
When compiler.os == linux
public link(symbol: "_landin_linux_openat") openat:
(directory: i32, path: ptr u8, flags: i32, mode: u32)
-> (result: i32)Open path, relative to directory (or at_fdcwd), with flags and, for o_creat, the permission bits mode: the new descriptor, or minus the errno.
page_size function
When compiler.os == linux
public link(symbol: "_landin_linux_page_size") page_size:
() -> (bytes: usize)The page size the kernel reported, or 4096 when it reported none.
read function
When compiler.os == linux
public link(symbol: "_landin_linux_read") read:
(descriptor: i32, data: ptr mut u8, length: usize) -> (result: isize)Read at most length bytes from descriptor into data: the count read, zero at end of file, or minus the errno.
read_cpu_words function
When compiler.os == linux; compiler.arch == x86_64
public link(symbol: "_landin_linux_read_cpu_words") read_cpu_words:
() -> (words: cpu_words)The words this processor gives.
When compiler.os == linux; not (compiler.arch == x86_64) and (compiler.arch == arm64)
public link(symbol: "_landin_linux_read_cpu_words") read_cpu_words:
() -> (words: cpu_words)The words this processor gives.
When compiler.os == linux; not (compiler.arch == x86_64 or compiler.arch == arm64)
public link(symbol: "_landin_linux_read_cpu_words") read_cpu_words:
() -> (words: cpu_words)The words this processor gives.
statx function
When compiler.os == linux
public link(symbol: "_landin_linux_statx") statx:
(directory: i32, path: ptr u8, flags: i32, mask: u32,
into: ptr mut file_status) -> (result: i32)Fill into with the status of path, relative to directory: zero, or minus the errno. flags zero follows symbolic links; mask asks for fields beyond the basic ones.
write function
When compiler.os == linux
public link(symbol: "_landin_linux_write") write:
(descriptor: i32, data: ptr u8, length: usize) -> (result: isize)Write at most length bytes from data to descriptor: the count accepted, which may be short, or minus the errno.