C++ Host Library

The C++ host library (zbc/Semihost.h, namespace zbc) is a C++17 alternative to the C host API, intended for emulators and other host-side embedders that prefer object lifetimes, value types, and composition over C vtables and callbacks. It implements the same RIFF wire protocol as the C host – in fact it reuses the C library’s endianness helpers – so a guest cannot tell which host it is talking to.

For the high-level structure (collaborators, Policy-vs-Backend separation, integration sketch, error reporting model), see Library Architecture. This page is the per-class reference.

Value types

The library depends only on the C++17 standard library. zbc::Result<T> carries a value or an error message and zbc::Status a success/failure; zbc::ByteSpan is a minimal std::span-like view; owned data is a std::vector<uint8_t> (zbc::Bytes). Strings cross the API as std::string_view.

Reference

The following classes and structs are extracted from the headers in include/cpp/zbc/ by Doxygen and rendered here through Breathe.

Device

class Device

Public Types

using IrqCallback = std::function<void(bool Assert)>

Asserts (true) or deasserts (false) the CPU interrupt line.

Public Functions

Device(GuestMemory &Mem, PlatformConfig Config, std::unique_ptr<Backend> Backend, std::unique_ptr<Policy> Pol, std::size_t WorkBufSize = DefaultWorkBufferSize)
Parameters:
  • Mem – Guest memory accessor (must outlive the device).

  • Config – Platform defaults (CNFG, if sent, overrides them).

  • Backend – Performs the actual I/O.

  • Pol – Authorizes each operation.

  • WorkBufSize – Work buffer size. Requests whose RIFF size exceeds this are rejected as malformed (the size field is guest-controlled and must not drive allocation).

uint8_t read(uint64_t Offset)

Register-window access (offsets 0x00-0x1F).

inline void setIrqCallback(IrqCallback CB)

Set the IRQ line callback (for the periodic timer).

void timerTick()

Called by the embedder when the periodic timer fires: latches the TIMER status bit and asserts the IRQ line.

inline Backend &backend()

Direct access for embedders that wire their own backend factories.

Public Static Attributes

static constexpr std::size_t DefaultWorkBufferSize = 4096

Default work buffer size; bounds the largest acceptable request.

Backend hierarchy

class Backend

Abstract backend. Every operation fails by default.

Subclassed by zbc::ConsoleBackend

class ConsoleBackend : public zbc::Backend

Backend with console I/O, time, exit, and timer support.

Subclassed by zbc::FileBackend

class FileBackend : public zbc::ConsoleBackend

Backend that adds file operations.

Paths arrive already resolved by the Policy layer; this class performs no security checks of its own.

Public Static Attributes

static constexpr int MaxDirs = 8

Maximum simultaneously open directories.

static constexpr int FirstDirHandle = 256

Directory handles start here, well above any FILE* fd.

struct OpResult

Result of a backend operation: return value, errno, and optional data.

Policy hierarchy

class Policy

Per-operation security decisions. Default: deny everything.

Subclassed by zbc::ConsoleOnlyPolicy, zbc::SandboxedPolicy, zbc::UnrestrictedPolicy

Public Functions

inline virtual Result<std::string> resolvePath(std::string_view Path, bool, bool = true)

Resolve/authorize a path for path-based operations (after the allow* check).

May rewrite the path (e.g. into the sandbox) or reject it.

FollowLeafSymlink defaults to true (POSIX stat / open / mkdir / … all follow symlinks on the final component). The two opcodes whose semantics require operating on the symlink itself &#8212; LSTAT and READLINK &#8212; pass false so the sandbox doesn’t canonicalize through the link and leave the backend pointed at the target.

inline virtual void addAllowedPath(std::string_view, bool)

Add an allowed path prefix (only meaningful for SandboxedPolicy).

class ConsoleOnlyPolicy : public zbc::Policy

Console I/O only: stdin/stdout/stderr, nothing else.

class SandboxedPolicy : public zbc::Policy

Filesystem access sandboxed to a directory, plus console and timer.

Public Functions

virtual Result<std::string> resolvePath(std::string_view Path, bool ForWrite, bool FollowLeafSymlink = true) override

Resolve/authorize a path for path-based operations (after the allow* check).

May rewrite the path (e.g. into the sandbox) or reject it.

FollowLeafSymlink defaults to true (POSIX stat / open / mkdir / … all follow symlinks on the final component). The two opcodes whose semantics require operating on the symlink itself &#8212; LSTAT and READLINK &#8212; pass false so the sandbox doesn’t canonicalize through the link and leave the backend pointed at the target.

virtual void addAllowedPath(std::string_view Prefix, bool AllowWrite) override

Add an allowed path prefix (only meaningful for SandboxedPolicy).

class UnrestrictedPolicy : public zbc::Policy

Everything allowed. DANGEROUS &#8212; trusted guests only.

Guest memory and support types

class GuestMemory

Public Functions

inline virtual void readBlock(uint8_t *Dest, uint64_t Addr, std::size_t Size)

Block helpers default to byte-at-a-time; override for efficiency.

struct PlatformConfig

Guest architecture parameters.

Provided by the embedder at construction (an emulator knows these from the CPU it emulates / the target triple). A CNFG chunk, if present in a request, overrides these for the session &#8212; see the spec’s “Platform-provided defaults”.

Public Functions

inline int endianCode() const

Endianness as the C library’s int code (ZBC_ENDIAN_*).

struct ParsedRequest

A parsed RIFF SEMI request.

Offsets are byte offsets from the start of the RIFF buffer to the payload of the pre-allocated response chunks, so the host can write responses in place without modifying the RIFF structure.

Public Functions

inline std::string_view dataAsString(std::size_t Index) const

Interpret DATA chunk Index as a string view (empty if out of range).

String-typed DATA payloads carry a trailing NUL terminator on the wire (the client sends strlen()+1 bytes). It is dropped here so the view is the logical string; any interior NUL is preserved so callers that security-check the path still reject it.

Public Members

PlatformConfig Config

Effective config (platform default or CNFG)

bool HasCnfg = false

A CNFG chunk was present (overrode the default)

std::vector<intmax_t> Parms

Decoded PARM values, in order.

std::vector<ByteSpan> DataChunks

DATA payloads (views into the buffer)

std::size_t RetnPayloadOffset = 0

0 means “no RETN chunk”

std::size_t ErroPayloadOffset = 0

0 means “no ERRO chunk”

class FileDescTable

Public Functions

int allocate(std::FILE *FP)

Allocate an FD for FP, or -1 if full.

bool release(int FD)

Close and release an FD (no-op for stdio). Returns true if released.

std::FILE *get(int FD) const

FILE* for FD, or nullptr if invalid.

class PathValidator

Public Functions

Result<std::string> validate(std::string_view Path, bool ForWrite, bool FollowLeafSymlink = true) const

Resolve and authorize Path.

Returns the resolved absolute path or an error describing the denial.

FollowLeafSymlink defaults to true. Set false for LSTAT / READLINK where the leaf must NOT be dereferenced: we still canonicalize the parent (so a traversal through the parent is caught) but leave the final component verbatim, so the backend’s lstat / readlink sees the symlink itself.

struct PathValidatorConfig
class Status

Success-or-error with an optional message.