The language used to write hooks is superficially similar to C; however, it has the following differences from it:
- the only types supported are integer types and pointers to them,
- there is no way to define functions; each hook is written like a function body,
- variables may be automatically declared at assignment if the type can be fully derived from the right-hand side,
- variables can be declared anywhere before use, including inside
for(;;)loop initial statements,
- variables are globally scoped,
- character string literals are not of pointer type. Instead, they are a special
strlittype(because pointers access VM memory instead, literal strings can't be pointers).
The supported control structures are:
Built-in integer types can be referred to with standard C names (
stdint-style names (
uint64_t), or short names (
& (reference) operator is not supported.
Variables declared with static keep their values between invocations of the hook, just like static variables inside a C function. There is a limit of 8 such variables in a given hook, imposed by the execution environment.
Skipping the existing instruction after a hook
A hook can perform an operation and skip/replace the existing instruction at the hooked address with
mon patch 0xfffffff0061b8ca4 cpu.x = 0; return 1;
This example causes the hypervisor to write
x0 and skip the existing instruction at
Accessing the device state
Accessing the processor state is done by using a pseudo-struct CPU. The supported fields in that structure are:
cpu.xfor 64-bit GPRs (note that register index must be a constant),
cpu.wfor 32-bit GPRs,
cpu.cpsrfor the processor state,
cpu.tpidrro_el0for EL1-visible system registers.
Writing these fields will modify the corresponding processor state upon return to VM.
Accessing VM memory (kernel virtual address view) happens by dereferencing pointers. For instance,
print_int(“foo”, *(u64 *)0xfffffff001234568);
will print the value of a 64-bit word at
0xFFFFFFF001234568 in the VM.
The following functions are available:
void print(strlit s);
print a literal string.
void print_int(strlit s, u64 a);
print an integer with optional string prefix
0 to not print a string).
void print_str(strlit s, void *p, u64 z);
print a zero-terminated string from VM memory at
z bytes, optional prefix
void print_buf(strlit s, void *p, u64 z);
print a buffer from VM memory at
p, maximum size
z bytes, with optional prefix
void print_thread(strlit s, u64 t);
print info on thread
0 for current) with optional string prefix
void print_backtrace(strlit s, u64 d);
print backtrace, maximum depth
d, with optional string prefix
void usleep(u32 usec);
cause the device to take a debug trap immediately after the hook returns.
u64 mapped(type *p);
check if the device memory location is valid. The type determines the size of the location.
u64 mapped(void *p, u64 z);
check if the device memory range is valid; the size is defined by
u64 min(u64 a, u64 b); u64 max(u64 a, u64 b);
s64 min(s64 a, s64 b); s64 max(s64 a, s64 b);
minimum/maximum for unsigned and signed values.
Additionally, the constant
NULL is defined as
global are 64-bit numbers shared between all hooks installed on the VM.