Semantics.lean
Declarations on this page (9)
ARM64 Small-Step Operational Semantics (Arm64.)
-
Layer / Role:
Arm64Trusted Computing Base (Role::Spec, definitions only, 0 theorems). -
Purpose & Key Exports: Read-only relocated code image (
Program), instruction fetch and decode from executable memory (Program.) and from the static image (fetchMem Program.), code-in-memory invariant (fetch Program.), single-step transition relation (CodeLoaded Step), and multi-step closure (Steps). The executable fuel-bounded evaluators (ProofLib.,stepState execN,execUntilPC) and the test-memory builderProgram.are untrusted and live inloadCode ProofLib/.Exec.lean -
When to read: Read when auditing how
Stepfetches froms.(mem fetchMem), dispatchesexterns/overrideshooks (Step.), or enforcesextern Memory.. For proof stepping lemmas, seeKeepsFrozen ProofLib/.Semantics.lean
Instructions are fetched from memory (Program.) at an aligned, executable pc. Because executable memory is never writable (canWrite requires ¬x) and every Step preserves frozen bytes (Memory.), Program. is invariant across execution and Program. agrees with Program. on loaded states.
@[expose] public sectionnamespace Arm641 declaration not shown (lines 43–44): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
def evalSvc
Apply supervisor-call handler svc while preserving frozen memory (Perm.), tpidrEl0, and symtab, matching architectural user-space supervisor-call invariants.
structure Program
A read-only AArch64 code image:
-
code: instruction bytes, with relocations applied, -
symbols: defined symbols (demangled names) and their offsets frombase, -
stubs: extern functions and the offsets of their stubs, -
externs: behaviour of extern functions, consulted at stub offsets, -
overrides: replacement behaviour for defined functions, consulted at symbol offsets, -
svc: supervisor-call (SVC #0) handler bundle, defaulting toSyscall.. Seestandard Step.andextern Step..exec
structure Program where
base : BitVec 64 := 0#64
code : List (BitVec 8) := []
symbols : List (String × Nat) := []
stubs : List (String × Nat) := []
externs : String → Option (State → State) := fun _ => none
overrides : String → Option (State → State) := fun _ => none
svc : State → State := Syscall.standardnamespace Program2 declarations not shown (lines 75–86): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64.
def evalInst
6 declarations not shown (lines 92–122): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64., Arm64., Arm64., Arm64., Arm64.
def hookAtOff?
The hook at offset off: an extern at a stub, or an override at a defined symbol.
def externAt?
The hook, if any, whose entry point is pc.
def externAt? (prog : Program) (pc : BitVec 64) : Option (State → State) :=
prog.hookAtOff? (pc - prog.base).toNat2 declarations not shown (lines 133–149): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64.
def fetchMem
Fetch and decode the instruction at pc from memory m: the 32-bit little-endian word at pc, which must be 4-byte aligned and executable (Memory.); otherwise none (the real machine faults). At a hooked entry point fetchMem returns none, as fetch does.
6 declarations not shown (lines 161–228): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64., Arm64., Arm64., Arm64., Arm64.
end Programinductive Step
Trusted small-step operational semantics relation Step prog s s'.
At an extern entry point (prog.) exactly one of extern / extern_fault applies (fetchMem returns none there); elsewhere exactly one of exec / halt_unmapped applies, so Step is deterministic.
inductive Step (prog : Program) : State → State → Prop where
/-- Execute one instruction, fetched from memory and decoded, when `s.status = .Running`. -/
| exec {s : State} {inst : Instr}
(h_run : s.status = .Running)
(h_fetch : prog.fetchMem s.mem s.pc = some inst) :
Step prog s (prog.evalInst inst s)
/-- Halt when no valid instruction can be fetched at `s.pc` (unmapped, not executable,
misaligned, or not decodable) and `s.pc` is not a hooked entry point. This includes an
extern stub with no `externs` entry. -/
| halt_unmapped {s : State}
(h_run : s.status = .Running)
(h_fetch : prog.fetchMem s.mem s.pc = none)
(h_ext : prog.externAt? s.pc = none) :
Step prog s { s with status := .Halted }
/-- A call to a hooked function at `s.pc` takes effect in one step: the architectural
state `s` is mapped to `f s`, provided `f` keeps every frozen (mapped, non-writable)
byte (`Memory.KeepsFrozen`) and preserves `symtab`, as any real function must. Otherwise
`extern_fault` applies. Beyond that the semantics places no constraint on `f`; whoever
supplies it is responsible for `f` representing a sensible return. -/
| extern {s : State} {f : State → State}
(h_run : s.status = .Running)
(h_ext : prog.externAt? s.pc = some f)
(h_keep : s.mem.KeepsFrozen (f s).mem)
(h_symtab : (f s).symtab = s.symtab := by rfl) :
Step prog s (f s)
/-- A hook that would change a frozen byte (e.g. code or read-only data) or the symbol
table faults instead, leaving the state unchanged except for the status. -/
| extern_fault {s : State} {f : State → State}
(h_run : s.status = .Running)
(h_ext : prog.externAt? s.pc = some f)
(h_nkeep : ¬ (s.mem.KeepsFrozen (f s).mem ∧ (f s).symtab = s.symtab)) :
Step prog s { s with status := .Exception "extern hook modified read-only memory or symbol table" }inductive Steps
Reflexive-transitive closure of Step.
inductive StepsUntil
Reflexive-transitive execution of prog that stops as soon as stop s holds: no intermediate state prior to the final state satisfies stop.
inductive StepsUntil (prog : Program) (stop : State → Prop) : State → State → Prop where
| refl (s : State) : StepsUntil prog stop s s
| step {s1 s2 s3 : State}
(h_cont : ¬ stop s1)
(h_step : Step prog s1 s2)
(h_rest : StepsUntil prog stop s2 s3) :
StepsUntil prog stop s1 s3end Arm64