Landin library reference source

platform/libc

Target scope is enforced by the module assertion below.

The C library's declarations, as ordinary extern(c) imports, raw.

On Darwin and FreeBSD, whose C library is their system interface, platform/posix reaches the operating system only through this module; on Linux it makes platform/linux's system calls, and this module serves a program linked with --link=libc. Its scope is the systems with a C library, Linux, Darwin and FreeBSD. Every declaration is bodyless, so a program that names none of them needs no C library, and a static Linux program that names one is refused before it links. open and openat are declared variadic, as C declares them, and take the mode as the one unnamed argument. Facts that differ between systems are chosen by compiler.os: the errno accessor errno_cell, __errno_location on Linux and __error on Darwin and FreeBSD, the open and mmap flags and poll's count type; stat's record follows the system and the C ABI. Read the errno cell immediately after a failing call. A malloc result is the allocation union, so the null pointer is no_allocation rather than a pointer, and mmap's failure is map_failed. The directory calls are fdopendir, readdir, closedir, fstatat, mkdirat and unlinkat, with each system's struct dirent as directory_record, its at_fdcwd, o_directory, at_symlink_nofollow and at_removedir, and the d_type and mode bits; readdir's and fdopendir's null pointer is no_directory. mkdirat's mode is 16 bits on Darwin and FreeBSD and 32 on Linux, as each system's mode_t is. A program that declares one of these C functions itself must declare it with the same types, or the two declarations of one C symbol are refused with L0347.

compiler.assert(compiler.os == linux or compiler.os == darwin
                or compiler.os == freebsd)

Items

Executable example

This complete program is maintained in the repository runtime tests. View source.

-- C-owned startup uses the library's startup-independent services: the C
-- library through platform/libc and the hosted heap, with no argument root.
import core/mem
import hosted/heap
import platform/libc

-- Private Landin names cannot replace the library's calls into libc.
strlen: () -> (result: usize) = result = 99 end strlen
malloc: () -> (result: usize) = result = 99 end malloc
free: () -> (result: usize) = result = 99 end free

public extern(c) l_length: (data: ptr u8) -> (length: usize) =
    _ = strlen()
    length = libc.strlen(data)
end l_length

public extern(c) l_copy: (input: i32, output: i32, buffer: ptr mut u8, length: usize) -> (count: usize) =
    received := libc.read(input, buffer, length)
    count = 0
    if received == isize(length) then
        count = usize(libc.write(output, buffer, length))
    end if
end l_copy

public extern(c) l_heap: () -> (ok: bool) =
    _ = malloc()
    _ = free()
    ok = false
    provider: heap.system = heap.host()
    block: ptr mut u8 = mem.allocate(provider, usize(64), usize(24)) else (problem)
        _ = problem
        return
    end
    block.val = 42
    ok = block.val == 42 and usize(block) % 24 == 0
    mem.free(provider, block, usize(64))
end l_heap

allocation type

public allocation: type = no_allocation | ptr mut u8

platform/libc/libc.ldn:57

A malloc result: a block, or no_allocation.

directory_entry type

public directory_entry: type = no_directory | ptr directory_record

platform/libc/libc.ldn:426

A readdir result: the next record, or no_directory.

directory_record type

When compiler.os == linux

public directory_record: type = struct
    inode: u64
    offset: i64
    record_length: u16
    kind: u8
    name: [256]u8
end directory_record

platform/libc/libc.ldn:337

C's struct dirent as readdir returns it. glibc gives no name length: the name ends at its NUL.

When not (compiler.os == linux) and (compiler.os == darwin)

public directory_record: type = struct
    inode: u64
    seek_offset: u64
    record_length: u16
    name_length: u16
    kind: u8
    name: [1024]u8
end directory_record

platform/libc/libc.ldn:365

C's struct dirent as readdir returns it, with the 64-bit inode every arm64 Darwin has.

When not (compiler.os == linux or compiler.os == darwin)

public directory_record: type = struct
    inode: u64
    offset: i64
    record_length: u16
    kind: u8
    padding0: u8
    name_length: u16
    padding1: u16
    name: [256]u8
end directory_record

platform/libc/libc.ldn:394

C's struct dirent as readdir returns it, in the layout FreeBSD 12 gave it with 64-bit inodes.

directory_stream type

public directory_stream: type = no_directory | ptr mut u8

platform/libc/libc.ldn:423

An fdopendir result: a directory stream, DIR *, or no_directory.

file_status type

When compiler.os == freebsd

public file_status: type = struct
    device: u64
    inode: u64
    links: u64
    mode: u16
    bsdflags: i16
    uid: u32
    gid: u32
    padding: i32
    special_device: u64
    accessed: timespec
    modified: timespec
    changed: timespec
    born: timespec
    bytes: i64
    blocks: i64
    block_size: i32
    flags: u32
    generation: u64
    revision: u64
    spare: [9]u64
end file_status

platform/libc/libc.ldn:192

C's struct stat, as stat fills it on this system.

When not (compiler.os == freebsd) and (compiler.c_sysv_lp64)

public file_status: type = struct
    device: u64
    inode: u64
    links: u64
    mode: u32
    uid: u32
    gid: u32
    padding: i32
    special_device: u64
    bytes: i64
    block_size: i64
    blocks: i64
    accessed: timespec
    modified: timespec
    changed: timespec
    reserved: [3]i64
end file_status

platform/libc/libc.ldn:218

C's struct stat, as stat fills it on this system.

When not (compiler.os == freebsd or compiler.c_sysv_lp64) and (compiler.c_aapcs64_lp64 or compiler.c_riscv_lp64d)

public file_status: type = struct
    device: u64
    inode: u64
    mode: u32
    links: u32
    uid: u32
    gid: u32
    special_device: u64
    padding1: u64
    bytes: i64
    block_size: i32
    padding2: i32
    blocks: i64
    accessed: timespec
    modified: timespec
    changed: timespec
    reserved: [2]u32
end file_status

platform/libc/libc.ldn:239

C's struct stat, as stat fills it on this system.

When not (compiler.os == freebsd or compiler.c_sysv_lp64 or compiler.c_aapcs64_lp64 or compiler.c_riscv_lp64d)

public file_status: type = struct
    device: u32
    mode: u16
    links: u16
    inode: u64
    uid: u32
    gid: u32
    special_device: u32
    accessed: timespec
    modified: timespec
    changed: timespec
    created: timespec
    bytes: i64
    blocks: i64
    block_size: i32
    flags: u32
    generation: u32
    spare: i32
    reserved: [2]i64
end file_status

platform/libc/libc.ldn:264

C's struct stat, as stat fills it on this system. C declares dev_t as int32_t here, but a device number is a bit pattern, not a quantity, and devfs's may have the sign bit set, so both device fields are unsigned and widening them never traps.

poll_count type

When compiler.os == linux

public poll_count: type = u64

platform/libc/libc.ldn:115

poll's count of records, C's nfds_t.

When not (compiler.os == linux)

public poll_count: type = u32

platform/libc/libc.ldn:129

poll's count of records, C's nfds_t.

poll_record type

public poll_record: type = struct
    descriptor: i32
    events: i16
    returned: i16
end poll_record

platform/libc/libc.ldn:165

One record poll reads and fills, C's struct pollfd: the descriptor to watch, the events asked about and the events that happened. A negative descriptor is ignored. The same on every hosted system.

timespec type

public timespec: type = struct
    seconds: i64
    nanoseconds: i64
end timespec

platform/libc/libc.ldn:179

C's struct timespec: whole seconds and the nanoseconds after them.

no_allocation atom

public no_allocation: atom

platform/libc/libc.ldn:54

What malloc returns when it cannot allocate: C's null pointer.

no_directory atom

public no_directory: atom

platform/libc/libc.ldn:420

What fdopendir gives when it cannot open a directory stream, and readdir at the end or on a failure: C's null pointer.

at_fdcwd value

When compiler.os == linux

public at_fdcwd: i32 = -100

platform/libc/libc.ldn:347

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

When not (compiler.os == linux) and (compiler.os == darwin)

public at_fdcwd: i32 = -2

platform/libc/libc.ldn:376

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

When not (compiler.os == linux or compiler.os == darwin)

public at_fdcwd: i32 = -100

platform/libc/libc.ldn:407

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

at_removedir value

When compiler.os == linux

public at_removedir: i32 = 512

platform/libc/libc.ldn:349

unlinkat flag: remove an empty directory rather than a file.

When not (compiler.os == linux) and (compiler.os == darwin)

public at_removedir: i32 = 128

platform/libc/libc.ldn:378

unlinkat flag: remove an empty directory rather than a file.

When not (compiler.os == linux or compiler.os == darwin)

public at_removedir: i32 = 2048

platform/libc/libc.ldn:409

unlinkat flag: remove an empty directory rather than a file.

dt_dir value

public dt_dir: u8 = 4

platform/libc/libc.ldn:306

d_type: a directory.

dt_lnk value

public dt_lnk: u8 = 10

platform/libc/libc.ldn:310

d_type: a symbolic link.

dt_reg value

public dt_reg: u8 = 8

platform/libc/libc.ldn:308

d_type: a regular file.

dt_unknown value

public dt_unknown: u8 = 0

platform/libc/libc.ldn:304

A struct dirent's d_type: the entry's type is not known, and a status call relative to the directory tells it. The values are the same on every hosted system.

eacces value

public eacces: i32 = 13

platform/libc/libc.ldn:89

errno: permission denied.

eexist value

public eexist: i32 = 17

platform/libc/libc.ldn:299

errno: the name already exists. The same on every hosted system.

eintr value

public eintr: i32 = 4

platform/libc/libc.ldn:87

errno: interrupted by a signal before anything was transferred.

enoent value

public enoent: i32 = 2

platform/libc/libc.ldn:85

errno: no such file or directory.

enotdir value

public enotdir: i32 = 20

platform/libc/libc.ldn:91

errno: a path component is not a directory.

eperm value

public eperm: i32 = 1

platform/libc/libc.ldn:83

errno: the operation is not permitted. The same on every hosted system.

erofs value

public erofs: i32 = 30

platform/libc/libc.ldn:93

errno: the file system is read-only.

file_status_size value

When compiler.os == freebsd

public file_status_size: usize = 224

platform/libc/libc.ldn:190

sizeof (struct stat) in the native C headers.

When not (compiler.os == freebsd) and (compiler.c_sysv_lp64)

public file_status_size: usize = 144

platform/libc/libc.ldn:216

sizeof (struct stat) in the native C headers.

When not (compiler.os == freebsd or compiler.c_sysv_lp64) and (compiler.c_aapcs64_lp64 or compiler.c_riscv_lp64d)

public file_status_size: usize = 128

platform/libc/libc.ldn:237

sizeof (struct stat) in the native C headers.

When not (compiler.os == freebsd or compiler.c_sysv_lp64 or compiler.c_aapcs64_lp64 or compiler.c_riscv_lp64d)

public file_status_size: usize = 144

platform/libc/libc.ldn:259

sizeof (struct stat) in the native C headers.

map_anonymous value

When compiler.os == linux

public map_anonymous: i32 = 32

platform/libc/libc.ldn:113

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

When not (compiler.os == linux)

public map_anonymous: i32 = 4096

platform/libc/libc.ldn:127

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

map_failed value

public map_failed: usize = 0 -% 1

platform/libc/libc.ldn:148

What mmap returns when it fails: C's MAP_FAILED, all bits set.

map_private value

public map_private: i32 = 2

platform/libc/libc.ldn:138

mmap flag: the mapping is private to this process.

o_append value

When compiler.os == linux

public o_append: i32 = 1024

platform/libc/libc.ldn:111

open flag: every write goes to the end of the file.

When not (compiler.os == linux)

public o_append: i32 = 8

platform/libc/libc.ldn:125

open flag: every write goes to the end of the file.

o_creat value

When compiler.os == linux

public o_creat: i32 = 64

platform/libc/libc.ldn:105

open flag: create the file if it does not exist. Linux keeps one value on every architecture; Darwin and FreeBSD share the BSD one.

When not (compiler.os == linux)

public o_creat: i32 = 512

platform/libc/libc.ldn:119

open flag: create the file if it does not exist. Linux keeps one value on every architecture; Darwin and FreeBSD share the BSD one.

o_directory value

When compiler.os == linux; compiler.arch == arm64

public o_directory: i32 = 16384

platform/libc/libc.ldn:326

open flag: fail with ENOTDIR unless the path names a directory. Linux's arm64 keeps a value of its own.

When compiler.os == linux; not (compiler.arch == arm64)

public o_directory: i32 = 65536

platform/libc/libc.ldn:330

open flag: fail with ENOTDIR unless the path names a directory. Linux's arm64 keeps a value of its own.

When not (compiler.os == linux) and (compiler.os == darwin)

public o_directory: i32 = 1048576

platform/libc/libc.ldn:359

open flag: fail with ENOTDIR unless the path names a directory. Linux's arm64 keeps a value of its own.

When not (compiler.os == linux or compiler.os == darwin)

public o_directory: i32 = 131072

platform/libc/libc.ldn:388

open flag: fail with ENOTDIR unless the path names a directory. Linux's arm64 keeps a value of its own.

o_excl value

When compiler.os == linux

public o_excl: i32 = 128

platform/libc/libc.ldn:109

open flag: with o_creat, fail with EEXIST if the name is taken.

When not (compiler.os == linux)

public o_excl: i32 = 2048

platform/libc/libc.ldn:123

open flag: with o_creat, fail with EEXIST if the name is taken.

o_rdonly value

public o_rdonly: i32 = 0

platform/libc/libc.ldn:96

open flag: read only. The same on every hosted system.

o_rdwr value

public o_rdwr: i32 = 2

platform/libc/libc.ldn:100

open flag: read and write. The same on every hosted system.

o_trunc value

When compiler.os == linux

public o_trunc: i32 = 512

platform/libc/libc.ldn:107

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

When not (compiler.os == linux)

public o_trunc: i32 = 1024

platform/libc/libc.ldn:121

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

o_wronly value

public o_wronly: i32 = 1

platform/libc/libc.ldn:98

open flag: write only. The same on every hosted system.

prot_read value

public prot_read: i32 = 1

platform/libc/libc.ldn:134

mmap protection: the pages may be read. The same on every hosted system.

prot_write value

public prot_write: i32 = 2

platform/libc/libc.ldn:136

mmap protection: the pages may be written.

s_ifdir value

public s_ifdir: u32 = 0o040000

platform/libc/libc.ldn:316

stat mode: a directory.

s_iflnk value

public s_iflnk: u32 = 0o120000

platform/libc/libc.ldn:320

stat mode: a symbolic link.

s_ifmt value

public s_ifmt: u32 = 0o170000

platform/libc/libc.ldn:314

A stat mode's file-type bits, and the types among them. The same on every hosted system.

s_ifreg value

public s_ifreg: u32 = 0o100000

platform/libc/libc.ldn:318

stat mode: a regular file.

_exit function

public extern(c) _exit: (status: i32) -> noreturn

platform/libc/libc.ldn:160

End the process with status at once: no atexit handler runs and no C stream is flushed.

close function

public extern(c) close: (descriptor: i32) -> (status: i32)

platform/libc/libc.ldn:48

Close descriptor: 0, or -1 with the reason in errno. The descriptor may be released even when this fails, so a failed close is never retried.

closedir function

public extern(c) closedir: (stream: ptr mut u8) -> (status: i32)

platform/libc/libc.ldn:440

Close a directory stream and its descriptor: 0, or -1 with the reason in errno. The stream is released either way.

errno_cell function

When compiler.os == linux

public extern(c) link(symbol: "__errno_location") errno_cell:
    () -> (cell: ptr mut i32)

platform/libc/libc.ldn:71

The calling thread's errno cell, __errno_location on Linux and __error on Darwin and FreeBSD. Read it immediately after the failing call, before any other call that may set it.

When not (compiler.os == linux)

public extern(c) link(symbol: "__error") errno_cell:
    () -> (cell: ptr mut i32)

platform/libc/libc.ldn:77

The calling thread's errno cell, __errno_location on Linux and __error on Darwin and FreeBSD. Read it immediately after the failing call, before any other call that may set it.

fdopendir function

public extern(c) fdopendir: (descriptor: i32) -> (stream: directory_stream)

platform/libc/libc.ldn:431

A directory stream over the directory open as descriptor, which the stream then owns and closedir closes, or no_directory with the reason in errno.

free function

public extern(c) free: (block: ptr mut u8) -> none

platform/libc/libc.ldn:65

Give a block from malloc back to the C library.

fstatat function

public extern(c) fstatat: (directory: i32, path: ptr u8,
                           result: ptr mut file_status, flags: i32)
                          -> (status: i32)

platform/libc/libc.ldn:294

Fill result with the status of path, relative to the directory open as directory: 0, or -1 with the reason in errno. With at_symlink_nofollow in flags a symbolic link is reported itself.

getpagesize function

public extern(c) getpagesize: () -> (bytes: i32)

platform/libc/libc.ldn:156

The size of a page in bytes, 16 KiB on Darwin arm64 and 4 KiB on the others.

malloc function

public extern(c) malloc: (size: usize) -> (block: allocation)

platform/libc/libc.ldn:62

Allocate size bytes aligned for any C object, or no_allocation. malloc(0) may return either; a caller that needs a distinct block for an empty request asks for a nonzero size.

mkdirat function

When compiler.os == linux

public extern(c) mkdirat: (directory: i32, path: ptr u8, mode: u32)
                          -> (status: i32)

platform/libc/libc.ldn:354

Make the directory path, relative to directory (or at_fdcwd), with the permission bits mode, which the process umask filters: 0, or -1 with the reason in errno.

When not (compiler.os == linux) and (compiler.os == darwin)

public extern(c) mkdirat: (directory: i32, path: ptr u8, mode: u16)
                          -> (status: i32)

platform/libc/libc.ldn:383

Make the directory path, relative to directory (or at_fdcwd), with the permission bits mode, which the process umask filters: 0, or -1 with the reason in errno.

When not (compiler.os == linux or compiler.os == darwin)

public extern(c) mkdirat: (directory: i32, path: ptr u8, mode: u16)
                          -> (status: i32)

platform/libc/libc.ldn:414

Make the directory path, relative to directory (or at_fdcwd), with the permission bits mode, which the process umask filters: 0, or -1 with the reason in errno.

mmap function

public extern(c) mmap: (address: usize, length: usize, protection: i32,
                        flags: i32, descriptor: i32, offset: i64)
                       -> (result: usize)

platform/libc/libc.ldn:143

Map length bytes, at a place of the system's choosing when address is zero: the mapping's address, or map_failed with the reason in errno. With map_anonymous the descriptor is -1 and the offset zero.

munmap function

public extern(c) munmap: (address: usize, length: usize) -> (status: i32)

platform/libc/libc.ldn:152

Unmap the length bytes at address: 0, or -1 with the reason in errno.

open function

public extern(c) open: (path: ptr u8, flags: i32, ...) -> (descriptor: i32)

platform/libc/libc.ldn:24

Open the file at the NUL-terminated path and return its descriptor, or -1 with the reason in errno. flags is o_rdonly or o_wronly, or'd with o_creat and o_trunc; with o_creat the one variadic argument is the new file's permission bits as an i32, which the process umask filters.

openat function

public extern(c) openat: (directory: i32, path: ptr u8, flags: i32, ...)
                         -> (descriptor: i32)

platform/libc/libc.ldn:42

Open path, relative to the directory open as directory (or at_fdcwd), as open does: the new descriptor, or -1 with the reason in errno. With o_creat the one variadic argument is the permission bits as an i32.

poll function

public extern(c) poll: (records: ptr mut poll_record, count: poll_count,
                        timeout: i32) -> (result: i32)

platform/libc/libc.ldn:175

Wait until one of the count records at records has an event, or timeout milliseconds pass, waiting indefinitely when it is negative: the number of records with an event, zero when the time passed first, or -1 with the reason in errno.

read function

public extern(c) read: (descriptor: i32, data: ptr mut u8, length: usize)
                       -> (count: isize)

platform/libc/libc.ldn:29

Read at most length bytes from descriptor into data. The count read, zero at end of file, or -1 with the reason in errno. EINTR means nothing was transferred.

readdir function

public extern(c) readdir: (stream: ptr mut u8) -> (record: directory_entry)

platform/libc/libc.ldn:436

The stream's next record, good until the next readdir or closedir on it, or no_directory at the end and on a failure, which errno tells apart when the caller zeroed it first. Records include . and ...

stat function

public extern(c) stat: (path: ptr u8, result: ptr mut file_status)
                       -> (status: i32)

platform/libc/libc.ldn:288

Fill result with the status of the file path names, following symbolic links: 0, or -1 with the reason in errno.

strlen function

public extern(c) strlen: (text: ptr u8) -> (length: usize)

platform/libc/libc.ldn:51

The number of bytes before the first NUL at text.

unlinkat function

public extern(c) unlinkat: (directory: i32, path: ptr u8, flags: i32)
                           -> (status: i32)

platform/libc/libc.ldn:445

Remove the name path, relative to directory (or at_fdcwd): a file with flags zero, an empty directory with at_removedir. 0, or -1 with the reason in errno.

write function

public extern(c) write: (descriptor: i32, data: ptr u8, length: usize)
                        -> (count: isize)

platform/libc/libc.ldn:35

Write at most length bytes from data to descriptor. The count accepted, which may be short, or -1 with the reason in errno. EINTR means nothing was transferred.