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
- allocation type
- directory_entry type
- directory_record type
- directory_stream type
- file_status type
- poll_count type
- poll_record type
- timespec type
- no_allocation atom
- no_directory atom
- at_fdcwd value
- at_removedir value
- at_symlink_nofollow value
- dt_dir value
- dt_lnk value
- dt_reg value
- dt_unknown value
- eacces value
- eexist value
- eintr value
- enoent value
- enotdir value
- eperm value
- erofs value
- file_status_size value
- map_anonymous value
- map_failed value
- map_private value
- o_append value
- o_creat value
- o_directory value
- o_excl value
- o_rdonly value
- o_rdwr value
- o_trunc value
- o_wronly value
- prot_read value
- prot_write value
- s_ifdir value
- s_iflnk value
- s_ifmt value
- s_ifreg value
- _exit function
- close function
- closedir function
- errno_cell function
- fdopendir function
- free function
- fstatat function
- getpagesize function
- malloc function
- mkdirat function
- mmap function
- munmap function
- open function
- openat function
- poll function
- read function
- readdir function
- stat function
- strlen function
- unlinkat function
- write function
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 u8A malloc result: a block, or no_allocation.
directory_entry type
public directory_entry: type = no_directory | ptr directory_recordA 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_recordC'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_recordC'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_recordC'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 u8An 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_statusC'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_statusC'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_statusC'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_statusC'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 = u64poll's count of records, C's nfds_t.
When not (compiler.os == linux)
public poll_count: type = u32poll's count of records, C's nfds_t.
poll_record type
public poll_record: type = struct
descriptor: i32
events: i16
returned: i16
end poll_recordOne 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 timespecC's struct timespec: whole seconds and the nanoseconds after them.
no_allocation atom
public no_allocation: atomWhat malloc returns when it cannot allocate: C's null pointer.
no_directory atom
public no_directory: atomWhat 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 = -100The 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 = -2The 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 = -100The 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 = 512unlinkat flag: remove an empty directory rather than a file.
When not (compiler.os == linux) and (compiler.os == darwin)
public at_removedir: i32 = 128unlinkat flag: remove an empty directory rather than a file.
When not (compiler.os == linux or compiler.os == darwin)
public at_removedir: i32 = 2048unlinkat flag: remove an empty directory rather than a file.
at_symlink_nofollow value
When compiler.os == linux
public at_symlink_nofollow: i32 = 256fstatat flag: report a symbolic link itself.
When not (compiler.os == linux) and (compiler.os == darwin)
public at_symlink_nofollow: i32 = 32fstatat flag: report a symbolic link itself.
When not (compiler.os == linux or compiler.os == darwin)
public at_symlink_nofollow: i32 = 512fstatat flag: report a symbolic link itself.
dt_dir value
public dt_dir: u8 = 4d_type: a directory.
dt_lnk value
public dt_lnk: u8 = 10d_type: a symbolic link.
dt_reg value
public dt_reg: u8 = 8d_type: a regular file.
dt_unknown value
public dt_unknown: u8 = 0A 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 = 13errno: permission denied.
eexist value
public eexist: i32 = 17errno: the name already exists. The same on every hosted system.
eintr value
public eintr: i32 = 4errno: interrupted by a signal before anything was transferred.
enoent value
public enoent: i32 = 2errno: no such file or directory.
enotdir value
public enotdir: i32 = 20errno: a path component is not a directory.
eperm value
public eperm: i32 = 1errno: the operation is not permitted. The same on every hosted system.
erofs value
public erofs: i32 = 30errno: the file system is read-only.
file_status_size value
When compiler.os == freebsd
public file_status_size: usize = 224sizeof (struct stat) in the native C headers.
When not (compiler.os == freebsd) and (compiler.c_sysv_lp64)
public file_status_size: usize = 144sizeof (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 = 128sizeof (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 = 144sizeof (struct stat) in the native C headers.
map_anonymous value
When compiler.os == linux
public map_anonymous: i32 = 32mmap flag: the mapping is backed by no file and starts zeroed.
When not (compiler.os == linux)
public map_anonymous: i32 = 4096mmap flag: the mapping is backed by no file and starts zeroed.
map_failed value
public map_failed: usize = 0 -% 1What mmap returns when it fails: C's MAP_FAILED, all bits set.
map_private value
public map_private: i32 = 2mmap flag: the mapping is private to this process.
o_append value
When compiler.os == linux
public o_append: i32 = 1024open flag: every write goes to the end of the file.
When not (compiler.os == linux)
public o_append: i32 = 8open flag: every write goes to the end of the file.
o_creat value
When compiler.os == linux
public o_creat: i32 = 64open 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 = 512open 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 = 16384open 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 = 65536open 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 = 1048576open 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 = 131072open 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 = 128open flag: with o_creat, fail with EEXIST if the name is taken.
When not (compiler.os == linux)
public o_excl: i32 = 2048open flag: with o_creat, fail with EEXIST if the name is taken.
o_rdonly value
public o_rdonly: i32 = 0open flag: read only. The same on every hosted system.
o_rdwr value
public o_rdwr: i32 = 2open flag: read and write. The same on every hosted system.
o_trunc value
When compiler.os == linux
public o_trunc: i32 = 512open flag: truncate an existing regular file to length zero.
When not (compiler.os == linux)
public o_trunc: i32 = 1024open flag: truncate an existing regular file to length zero.
o_wronly value
public o_wronly: i32 = 1open flag: write only. The same on every hosted system.
prot_read value
public prot_read: i32 = 1mmap protection: the pages may be read. The same on every hosted system.
prot_write value
public prot_write: i32 = 2mmap protection: the pages may be written.
s_ifdir value
public s_ifdir: u32 = 0o040000stat mode: a directory.
s_iflnk value
public s_iflnk: u32 = 0o120000stat mode: a symbolic link.
s_ifmt value
public s_ifmt: u32 = 0o170000A stat mode's file-type bits, and the types among them. The same on every hosted system.
s_ifreg value
public s_ifreg: u32 = 0o100000stat mode: a regular file.
_exit function
public extern(c) _exit: (status: i32) -> noreturnEnd 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)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)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)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)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)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) -> noneGive 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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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.