Skip to content

ELF Programs

Frank edited this page Oct 6, 2026 · 4 revisions

ELF programs

Cupid OS runs static ELF32 i386 programs built with the checked CupidC and CupidLD tools. They run as kernel threads in ring 0. The kernel passes a function-pointer service table to _start; cupid_init makes the wrappers in user/cupid.h use that table.

Build the examples

The supported examples are hello.cc, ls.cc and cat.cc in user/examples/. A small program follows this pattern:

#include "../cupid.h"

void _start(cupid_syscall_table_t *sys) {
    cupid_init(sys);
    print("Hello from an ELF program!\n");
    print_int(getpid());
    print("\n");
    exit();
}

From the repository root, run:

make -C user all

CupidBuild validates the selected seed and release, captures the declared source and header, invokes CupidC and CupidLD, validates the resulting objects and executable, and publishes through its retained transaction. The normal recipe also calls the read-only native verify-user-abi command. The three programs appear in user/build/.

The normal build requires Make and the checked Cupid tools. GCC, Clang, NASM, a host linker and a hosted SDK cache are not required for these programs. The production profile declares its accepted source roots and dependencies; adding another program also requires extending that profile. See the user build and native user compilation.

Stage and run

make sync-user
make run

sync-user builds the image and validated examples, then stages /hello, /ls and /cat into FAT16. The raw FAT filesystem is mounted at /disk. In the guest terminal, run:

exec /disk/hello
exec /disk/ls
exec /disk/cat

The cat example reads /home/readme.txt; its file must exist. On a fresh first boot, the filesystem imports staged files into HomeFS at /home. On an existing image, newly staged files remain accessible under /disk and can be copied into /home with the guest's cp command. HomeFS persists its container as HOMEFS.SYS on FAT16. No external mcopy utility is required.

Image and process contract

The external executable arena is the permanently reserved two MiB range [0x01C00000, 0x01E00000). The normal link uses text base 0x01C00000. The ELF entry point is the resolved _start address and can differ from that text base. One external program holds the arena lease at a time; another load fails while that lease is occupied. Process cleanup releases the lease while the arena stays reserved.

The loader accepts static i386 ET_EXEC images with at most sixteen program headers. It validates load ranges, overlap, alignment, file bounds and an entry in executable file-backed data before copying the image. Dynamic linking and load-time relocation are not supported. The complete image must fit the selected arena. The separate 256 KiB CUPD input bound does not define the ELF arena size.

The kernel creates the program with its syscall-table argument, image ownership and default 32 KiB process stack. Calling exit() ends the process. Returning from _start also reaches the process exit trampoline and cleanup. See the loader and process implementation.

Kernel services

Call cupid_init(sys) before using these wrappers. The current service table has version 5, 103 fields, a 412-byte i386 layout and 101 function providers. The native ABI gate compares the complete user declaration with the kernel declaration and initializer.

Area Wrappers
Console print, putchar, print_int, print_hex, clear_screen
Allocation malloc, free
Strings and memory strlen, strcmp, strncmp, memset, memcpy
Files open, close, read, write, seek, stat, readdir, mkdir, unlink
Process exit, yield, getpid, kill, sleep_ms
Shell and time shell_execute, shell_get_cwd, uptime_ms
Execution and diagnostics exec_program, memstats
Networking Address queries, DNS and socket wrappers

cupid_dirent_t has a 128-byte name field. Use the declarations and constants in user/cupid.h for signatures, file flags, error values and the complete service table.

For source programs compiled inside the running OS, see User programs and CupidC compiler.

Clone this wiki locally