Landin library reference source

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

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_words

hosted/linux/linux.ldn:323

What 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_words

hosted/linux/linux.ldn:534

What 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_words

hosted/linux/linux.ldn:595

What 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 u8

hosted/linux/linux.ldn:268

The 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_status

hosted/linux/linux.ldn:164

The 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_time

hosted/linux/linux.ldn:155

A statx time: whole seconds and the nanoseconds after them.

no_environment atom

When compiler.os == linux

public no_environment: atom

hosted/linux/linux.ldn:264

What environment returns when the C runtime, not _landin_start, started the program.

at_fdcwd value

When compiler.os == linux

public at_fdcwd: i32 = -100

hosted/linux/linux.ldn:117

The directory descriptor that makes an *at call's relative path relative to the working directory.

eacces value

When compiler.os == linux

public eacces: i32 = 13

hosted/linux/linux.ldn:135

errno: permission denied.

eintr value

When compiler.os == linux

public eintr: i32 = 4

hosted/linux/linux.ldn:133

errno: interrupted by a signal before anything was transferred.

enoent value

When compiler.os == linux

public enoent: i32 = 2

hosted/linux/linux.ldn:131

errno: no such file or directory.

enotdir value

When compiler.os == linux

public enotdir: i32 = 20

hosted/linux/linux.ldn:137

errno: a path component is not a directory.

eperm value

When compiler.os == linux

public eperm: i32 = 1

hosted/linux/linux.ldn:129

errno: the operation is not permitted.

erofs value

When compiler.os == linux

public erofs: i32 = 30

hosted/linux/linux.ldn:139

errno: the file system is read-only.

map_anonymous value

When compiler.os == linux

public map_anonymous: i32 = 32

hosted/linux/linux.ldn:148

mmap flag: the mapping is backed by no file and starts zeroed.

map_private value

When compiler.os == linux

public map_private: i32 = 2

hosted/linux/linux.ldn:146

mmap flag: the mapping is private to this process.

o_creat value

When compiler.os == linux

public o_creat: i32 = 64

hosted/linux/linux.ldn:124

openat flag: create the file if it does not exist.

o_rdonly value

When compiler.os == linux

public o_rdonly: i32 = 0

hosted/linux/linux.ldn:120

openat flag: read only.

o_trunc value

When compiler.os == linux

public o_trunc: i32 = 512

hosted/linux/linux.ldn:126

openat flag: truncate an existing regular file to length zero.

o_wronly value

When compiler.os == linux

public o_wronly: i32 = 1

hosted/linux/linux.ldn:122

openat flag: write only.

prot_read value

When compiler.os == linux

public prot_read: i32 = 1

hosted/linux/linux.ldn:142

mmap protection: the pages may be read.

prot_write value

When compiler.os == linux

public prot_write: i32 = 2

hosted/linux/linux.ldn:144

mmap protection: the pages may be written.

statx_ino value

When compiler.os == linux

public statx_ino: u32 = 256

hosted/linux/linux.ldn:152

statx 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: () -> none

hosted/linux/linux.ldn:48

The 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: () -> none

hosted/linux/linux.ldn:79

The 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: () -> none

hosted/linux/linux.ldn:110

The 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) -> none

hosted/linux/linux.ldn:718

What _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)

hosted/linux/linux.ldn:283

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)

hosted/linux/linux.ldn:217

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)

hosted/linux/linux.ldn:271

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) -> noreturn

hosted/linux/linux.ldn:249

End 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)

hosted/linux/linux.ldn:449

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)

hosted/linux/linux.ldn:472

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)

hosted/linux/linux.ldn:493

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)

hosted/linux/linux.ldn:512

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)

hosted/linux/linux.ldn:547

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)

hosted/linux/linux.ldn:571

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)

hosted/linux/linux.ldn:619

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)

hosted/linux/linux.ldn:625

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)

hosted/linux/linux.ldn:647

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)

hosted/linux/linux.ldn:653

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)

hosted/linux/linux.ldn:462

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)

hosted/linux/linux.ldn:485

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)

hosted/linux/linux.ldn:506

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)

hosted/linux/linux.ldn:524

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)

hosted/linux/linux.ldn:561

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)

hosted/linux/linux.ldn:583

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)

hosted/linux/linux.ldn:639

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)

hosted/linux/linux.ldn:666

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)

hosted/linux/linux.ldn:234

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)

hosted/linux/linux.ldn:242

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)

hosted/linux/linux.ldn:208

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)

hosted/linux/linux.ldn:303

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)

hosted/linux/linux.ldn:191

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)

hosted/linux/linux.ldn:387

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)

hosted/linux/linux.ldn:539

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)

hosted/linux/linux.ldn:607

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)

hosted/linux/linux.ldn:225

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)

hosted/linux/linux.ldn:199

Write at most length bytes from data to descriptor: the count accepted, which may be short, or minus the errno.