Backend API
Header: zbc_backend.h
Backends provide the actual implementation of semihosting operations (file I/O, console, time). The host library dispatches requests to a backend vtable.
Backend Vtable
-
struct zbc_backend_t
Backend vtable defining semihosting operations.
Implement the functions you need, set unused operations to NULL. The host library returns an error to the guest for any NULL operation.
Return value conventions:
open: file descriptor (>=0) on success, -1 on error
close, seek, remove, rename: 0 on success, -1 on error
read, write: bytes NOT transferred (0 = complete), -1 on error
flen: file length on success, -1 on error
clock: centiseconds since start, -1 on error
time: seconds since epoch, -1 on error
tmpnam: 0 on success (fills buf), -1 on error
Public Members
-
int (*open)(void *ctx, const char *path, size_t path_len, int mode)
Open a file.
Path is NOT null-terminated; use path_len. Mode is SH_OPEN_*.
-
int (*close)(void *ctx, int fd)
Close a file descriptor.
-
int (*read)(void *ctx, int fd, void *buf, size_t count)
Read up to count bytes.
Returns bytes NOT read (0 = all read).
-
int (*write)(void *ctx, int fd, const void *buf, size_t count)
Write count bytes.
Returns bytes NOT written (0 = all written).
-
int (*seek)(void *ctx, int fd, int pos)
Seek to absolute position.
-
intmax_t (*flen)(void *ctx, int fd)
Return file length.
-
int (*remove)(void *ctx, const char *path, size_t path_len)
Delete a file.
Path is NOT null-terminated.
-
int (*rename)(void *ctx, const char *old_path, size_t old_len, const char *new_path, size_t new_len)
Rename a file.
Paths are NOT null-terminated.
-
int (*tmpnam)(void *ctx, char *buf, size_t buf_size, int id)
Generate temporary filename.
Write to buf, return 0 on success.
-
void (*writec)(void *ctx, char c)
Write a single character to console.
-
void (*write0)(void *ctx, const char *str)
Write a null-terminated string to console.
-
int (*readc)(void *ctx)
Read a character from console (blocking).
Return char or -1.
-
int (*iserror)(void *ctx, int status)
Check if status indicates error.
Return 1 if error, 0 otherwise.
-
int (*istty)(void *ctx, int fd)
Check if fd is a TTY.
Return 1 if TTY, 0 otherwise.
-
int (*clock)(void *ctx)
Return centiseconds since program start.
-
int (*time)(void *ctx)
Return seconds since Unix epoch.
-
int (*elapsed)(void *ctx, unsigned int *lo, unsigned int *hi)
Return 64-bit tick count in *lo and *hi.
-
int (*tickfreq)(void *ctx)
Return ticks per second.
-
int (*do_system)(void *ctx, const char *cmd, size_t cmd_len)
Execute a shell command.
Return exit code.
-
int (*get_cmdline)(void *ctx, char *buf, size_t buf_size)
Get command line.
Write to buf, return 0 on success.
-
int (*heapinfo)(void *ctx, uintptr_t *heap_base, uintptr_t *heap_limit, uintptr_t *stack_base, uintptr_t *stack_limit)
Get heap/stack info.
Fill all four values, return 0 on success.
-
void (*do_exit)(void *ctx, unsigned int reason, unsigned int subcode)
Guest is exiting.
Handle as appropriate (stop emulation, etc.).
-
int (*get_errno)(void *ctx)
Return last errno value.
-
int (*timer_config)(void *ctx, unsigned int rate_hz)
Configure periodic timer.
rate_hz=0 disables. Returns 0 on success.
-
int (*stat)(void *ctx, const char *path, size_t path_len, void *stat_buf)
Stat a path.
Writes the SH_STAT_BUF_SIZE-byte response struct (ino[8] mode[4] nlink[4] size[8] mtime[8] atime[8] ctime[8], all little-endian) into stat_buf. Returns 0 on success, -1 on error.
-
int (*opendir)(void *ctx, const char *path, size_t path_len)
Open a directory for enumeration.
Returns a non-negative dir handle on success, -1 on error. The handle is host-defined; the guest treats it as opaque and only passes it to readdir/closedir.
-
int (*readdir)(void *ctx, int dir_handle, void *buf, size_t buf_size)
Read one directory entry.
Writes a single entry in SYS_READDIR wire layout (d_ino[8] d_type[1] d_namlen[1] d_name[d_namlen+1]) into buf. Returns the number of bytes written (> 0), 0 at end of directory, or -1 on error.
-
int (*closedir)(void *ctx, int dir_handle)
Release a dir handle from opendir.
Returns 0 on success, -1 on error (e.g. handle was never opened or already closed).
-
int (*readc_poll)(void *ctx)
Non-blocking console char read.
Returns 0-255 if a character is available, -1 if none. -1 is not an error — it just means the caller should poll again later. Hosts implement this with select()/poll() on stdin with a zero timeout.
-
int (*fstat)(void *ctx, int fd, void *stat_buf)
Stat an open file by descriptor.
Writes the same 48-byte little-endian struct as stat(). Returns 0 on success, -1 on error.
-
int (*mkdir)(void *ctx, const char *path, size_t path_len, int mode)
Create a directory at path.
mode is passed through (may be filtered by host umask or ignored on platforms without POSIX permission bits). Returns 0 on success, -1 on error.
-
int (*rmdir)(void *ctx, const char *path, size_t path_len)
Remove an empty directory at path.
Returns 0 on success, -1 on error (including ENOTEMPTY).
-
int (*ftruncate)(void *ctx, int fd, uint64_t length)
Truncate an open file to length bytes.
Length is unsigned 64-bit so a 16-bit guest can still describe sizes beyond its address space. Returns 0 on success, -1 on error.
-
int (*fsync)(void *ctx, int fd)
Flush dirty buffers for an open file to storage.
Returns 0 on success, -1 on error.
-
int (*link)(void *ctx, const char *old_path, size_t old_len, const char *new_path, size_t new_len)
Create a hard link new_path -> old_path.
Returns 0 on success, -1 on error.
-
int (*symlink)(void *ctx, const char *target, size_t target_len, const char *linkpath, size_t linkpath_len)
Create a symbolic link at linkpath that points to target.
Returns 0 on success, -1 on error.
-
int (*readlink)(void *ctx, const char *path, size_t path_len, void *buf, size_t buf_size)
Read the target of a symbolic link at path.
Writes up to buf_size bytes (NOT NUL-terminated). Returns the number of bytes written on success, -1 on error (matches POSIX readlink(2)).
Return Value Conventions
Operation |
Success |
Error |
|---|---|---|
open |
file descriptor (>=0) |
-1 |
close, seek, remove, rename |
0 |
-1 |
read, write |
bytes NOT transferred (0 = complete) |
-1 |
flen |
file length |
-1 |
clock |
centiseconds since start |
-1 |
time |
seconds since epoch |
-1 |
tmpnam |
0 (fills buf) |
-1 |
Set unused operations to NULL. The host library returns an error to the guest for any NULL operation.
Built-in Backends
Secure ANSI Backend
-
const zbc_backend_t *zbc_backend_ansi(void)
Get the secure (sandboxed) ANSI backend.
The secure backend restricts file access to a sandbox directory. Guest code cannot escape the sandbox or access arbitrary host files. Use with zbc_ansi_state_t from zbc_backend_ansi.h.
See also
- Returns:
Pointer to backend vtable
State initialization (from zbc_backend_ansi.h):
#include "zbc_backend_ansi.h"
static zbc_ansi_state_t state;
zbc_ansi_init(&state, "/path/to/sandbox/");
Configuration:
/* Add additional allowed paths */
zbc_ansi_add_path(&state, "/usr/share/data/", 0); /* read-only */
zbc_ansi_add_path(&state, "/tmp/output/", 1); /* read-write */
/* Set flags */
state.flags |= ZBC_ANSI_FLAG_ALLOW_SYSTEM; /* enable system() */
state.flags |= ZBC_ANSI_FLAG_READ_ONLY; /* block all writes */
/* Set callbacks for violations, exit, and timer config */
zbc_ansi_set_callbacks(&state, violation_handler, exit_handler,
timer_handler, ctx);
Cleanup:
zbc_ansi_cleanup(&state);
Insecure ANSI Backend
-
const zbc_backend_t *zbc_backend_ansi_insecure(void)
Get the insecure (unrestricted) ANSI backend.
The insecure backend provides unrestricted access to the host filesystem. Guest code can read, write, and delete any file the host process can access. Use only for trusted code. Use with zbc_ansi_insecure_state_t from zbc_backend_ansi.h.
See also
- Returns:
Pointer to backend vtable
Use only for trusted code (e.g., your own test programs).
State initialization (from zbc_backend_ansi.h):
#include "zbc_backend_ansi.h"
static zbc_ansi_insecure_state_t state;
zbc_ansi_insecure_init(&state);
Cleanup:
zbc_ansi_insecure_cleanup(&state);
Dummy Backend
-
const zbc_backend_t *zbc_backend_dummy(void)
Get the dummy (no-op) backend.
All operations succeed with no side effects. Useful for testing the host processing logic without actual I/O. No state required — pass NULL as backend_ctx.
- Returns:
Pointer to backend vtable
Implementing Custom Backends
To implement a custom backend:
Define your context structure (if needed)
Implement the vtable functions you need
Create a static
zbc_backend_twith your function pointersUse NULL for operations you don’t support
Example:
typedef struct {
int console_fd;
/* ... */
} my_backend_ctx_t;
static int my_open(void *ctx, const char *path, size_t len, int mode)
{
my_backend_ctx_t *my = ctx;
/* Implementation */
return fd;
}
static void my_writec(void *ctx, char c)
{
my_backend_ctx_t *my = ctx;
write(my->console_fd, &c, 1);
}
static const zbc_backend_t my_backend = {
.open = my_open,
.writec = my_writec,
/* Other operations NULL - returns error to guest */
};
/* Usage */
my_backend_ctx_t my_ctx = { .console_fd = STDOUT_FILENO };
zbc_host_init(&host, &mem_ops, NULL, &my_backend, &my_ctx,
work_buf, sizeof(work_buf));
ANSI Backend Types
Header: zbc_backend_ansi.h
Secure Backend State
-
struct zbc_ansi_state_t
Secure ANSI backend state (caller-allocated).
Initialize with zbc_ansi_init() before use.
Public Members
-
char sandbox_dir[ZBC_ANSI_SANDBOX_DIR_MAX]
Primary sandbox directory.
-
size_t sandbox_dir_len
Length of sandbox_dir.
-
unsigned int flags
ZBC_ANSI_FLAG_*.
-
zbc_ansi_path_rule_t path_rules[ZBC_ANSI_MAX_PATH_RULES]
Path rules.
-
int path_rule_count
Number of path rules.
-
const zbc_ansi_policy_t *policy
NULL = use built-in policy.
-
void *policy_ctx
Context for policy callbacks.
-
void (*on_violation)(void *ctx, int type, const char *detail)
Violation callback.
-
void (*on_exit)(void *ctx, unsigned int reason, unsigned int subcode)
Exit callback.
-
int (*on_timer_config)(void *ctx, unsigned int rate_hz)
Timer config callback.
Return 0 on success, non-zero if the rate is not achievable; a non-zero return is reported to the guest as -1 with errno EINVAL (per spec SYS_TIMER_CONFIG).
-
void *callback_ctx
Context for callbacks.
-
uint64_t start_clock
Stored as uint64_t to avoid time.h.
-
char sandbox_dir[ZBC_ANSI_SANDBOX_DIR_MAX]
-
void zbc_ansi_init(zbc_ansi_state_t *state, const char *sandbox_dir)
Initialize secure ANSI backend.
The sandbox_dir is copied into state, so the original can be freed.
- Parameters:
state – Caller-allocated state structure
sandbox_dir – Directory to sandbox file operations to (required). All file paths must start with this prefix. Should end with ‘/’ (will be added if missing).
-
int zbc_ansi_add_path(zbc_ansi_state_t *state, const char *prefix, int allow_write)
Add an additional allowed path.
The prefix string must remain valid for the lifetime of state.
- Parameters:
state – Initialized state
prefix – Path prefix to allow (e.g., “/usr/lib/”)
allow_write – 0 for read-only, 1 for read-write
- Returns:
0 on success, -1 if path_rules array is full
-
void zbc_ansi_set_policy(zbc_ansi_state_t *state, const zbc_ansi_policy_t *policy, void *ctx)
Set custom security policy.
- Parameters:
state – Initialized state
policy – Policy vtable (NULL to use built-in)
ctx – Context passed to policy callbacks
-
void zbc_ansi_set_callbacks(zbc_ansi_state_t *state, void (*on_violation)(void *ctx, int type, const char *detail), void (*on_exit)(void *ctx, unsigned int reason, unsigned int subcode), int (*on_timer_config)(void *ctx, unsigned int rate_hz), void *ctx)
Set callbacks for security events.
- Parameters:
state – Initialized state
on_violation – Called when operation is blocked (may be NULL)
on_exit – Called when exit() is intercepted (may be NULL)
on_timer_config – Called when timer is configured (may be NULL). Return 0 on success, non-zero if the rate is not achievable (guest receives -1 with errno EINVAL).
ctx – Context passed to callbacks
-
void zbc_ansi_cleanup(zbc_ansi_state_t *state)
Clean up secure ANSI backend state.
Closes all open files. State can be reused after calling zbc_ansi_init().
- Parameters:
state – Initialized state
Insecure Backend State
-
struct zbc_ansi_insecure_state_t
Insecure ANSI backend state (caller-allocated).
Initialize with zbc_ansi_insecure_init() before use.
-
void zbc_ansi_insecure_init(zbc_ansi_insecure_state_t *state)
Initialize insecure ANSI backend.
Warning
This provides unrestricted filesystem access! Guest code can read/write/delete any file the host process can access. Only use for trusted guest code or debugging.
- Parameters:
state – Caller-allocated state structure
-
void zbc_ansi_insecure_cleanup(zbc_ansi_insecure_state_t *state)
Clean up insecure ANSI backend state.
Closes all open files. State can be reused after calling zbc_ansi_insecure_init().
- Parameters:
state – Initialized state
Policy Vtable
-
struct zbc_ansi_policy_t
Custom policy vtable (optional, for OS-specific sandboxing).
Public Members
-
int (*validate_path)(void *ctx, const char *path, size_t path_len, int for_write, char *resolved, size_t resolved_size, size_t *resolved_len)
Validate and resolve a path.
Called before any file operation.
- Param ctx:
Policy context (from zbc_ansi_set_policy)
- Param path:
Original path from guest
- Param path_len:
Length of path
- Param for_write:
1 if operation will modify file, 0 for read-only
- Param resolved:
Buffer to write resolved path
- Param resolved_size:
Size of resolved buffer
- Param resolved_len:
[out] Length of resolved path
- Return:
0 on success (path allowed), non-zero to deny (EACCES)
-
int (*validate_system)(void *ctx, const char *cmd, size_t cmd_len)
Validate a system() command.
Only called if ZBC_ANSI_FLAG_ALLOW_SYSTEM is set.
- Param ctx:
Policy context
- Param cmd:
Command string
- Param cmd_len:
Length of command
- Return:
0 to allow execution, non-zero to deny
-
int (*handle_exit)(void *ctx, unsigned int reason, unsigned int subcode)
Handle exit() request.
Only called if ZBC_ANSI_FLAG_ALLOW_EXIT is NOT set.
- Param ctx:
Policy context
- Param reason:
Exit reason code
- Param subcode:
Exit subcode
- Return:
0 to allow exit, non-zero to block
-
int (*validate_path)(void *ctx, const char *path, size_t path_len, int for_write, char *resolved, size_t resolved_size, size_t *resolved_len)