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.
Public Static Attributes
-
static constexpr std::size_t DefaultWorkBufferSize = 4096
Default work buffer size; bounds the largest acceptable request.
-
using IrqCallback = std::function<void(bool Assert)>
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.
-
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 — LSTAT and READLINK — 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).
-
inline virtual Result<std::string> resolvePath(std::string_view Path, bool, bool = true)
-
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 — LSTAT and READLINK — 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).
-
virtual Result<std::string> resolvePath(std::string_view Path, bool ForWrite, bool FollowLeafSymlink = true) override
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.
-
inline virtual void readBlock(uint8_t *Dest, uint64_t Addr, std::size_t Size)
-
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 — see the spec’s “Platform-provided defaults”.
Public Functions
-
inline int endianCode() const
Endianness as the C library’s int code (ZBC_ENDIAN_*).
-
inline int endianCode() const
-
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”
-
inline std::string_view dataAsString(std::size_t Index) const
-
class FileDescTable
-
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.
-
Result<std::string> validate(std::string_view Path, bool ForWrite, bool FollowLeafSymlink = true) const
-
struct PathValidatorConfig
-
class Status
Success-or-error with an optional message.