You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
non-null, alignment, expr, etc.) into LLVM IR `icmp`/`and`/`or` sequences.
121
+
-**Function-level contract collection**: walking a module to associate
122
+
contracts with their enclosing functions.
123
+
124
+
Each verification backend then only needs a thin driver layer: SeaHorn emits
125
+
`__VERIFIER_assert`/`__VERIFIER_assume`, while KLEE emits
126
+
`klee_assume`/`abort()`.
127
+
128
+
### 4.3 Contract Source
102
129
103
-
Contracts are always read from the `!static_contract` metadata strings embedded on call instructions by `patchir-cir2llvm`, using a local `parseStaticContractString()` helper that attributes each contract to its enclosing LLVM function.
130
+
Contracts are always read from the `!static_contract` metadata strings embedded on call instructions by `patchir-cir2llvm`, using the shared metadata parsing helpers described above.
104
131
105
132
If `-spec <yaml>` is provided, the `contracts:` section of the spec is also parsed (reusing `ContractSpec` and `Predicate` types from `include/patchestry/YAML/ContractSpec.hpp`) and merged with the metadata-derived contracts. Spec entries take precedence over metadata for the same function, allowing per-deployment overrides without re-running the full transform pipeline.
106
133
@@ -127,10 +154,19 @@ For a patched function `int f(int arg0, char* arg1)` with:
127
154
-**Precondition:**`arg0 >= 0`
128
155
-**Postcondition:**`return_value in [0, 32]`
129
156
157
+
**Note on pointer width:** The harness must derive integer widths from the
158
+
module's `DataLayout` rather than hardcoding `i64`. For 32-bit firmware targets
159
+
(e.g. `ARM:LE:32:Cortex`), the size parameter to `klee_make_symbolic` and the
160
+
argument to `klee_assume` should use the target's native pointer-sized integer
161
+
(e.g. `i32`). The examples below use `i64` for readability; the implementation
162
+
must use `DataLayout::getIntPtrType()` to select the correct width.
163
+
130
164
The tool generates the following LLVM IR appended to the patched module:
131
165
132
166
```llvm
133
167
; ─── KLEE runtime declarations (added once per module) ───────────────────────
168
+
; NOTE: size_t and assume argument widths are derived from DataLayout;
169
+
; i64 is shown here for a 64-bit target. For 32-bit targets these become i32.
134
170
declare void @klee_make_symbolic(ptr, i64, ptr)
135
171
declare void @klee_assume(i64)
136
172
declare void @abort() noreturn
@@ -184,27 +220,93 @@ done:
184
220
185
221
## 6. Predicate-to-IR Translation
186
222
187
-
The following table defines how each predicate kind in the contract spec is translated into harness IR:
223
+
This section defines how each predicate kind from `Contract.td` is lowered to KLEE harness IR. Preconditions emit `klee_assume` (constraining symbolic inputs); postconditions emit `if (!cond) abort()` (KLEE reports the violation).
224
+
225
+
### 6.1 Targets
226
+
227
+
Targets identify what a predicate applies to. The YAML spec and serialized metadata use different spellings; the parser normalizes both:
228
+
229
+
| YAML | Serialized (in `!static_contract`) | Operand in IR |
230
+
|---|---|---|
231
+
|`arg0` .. `argN`|`Arg(0)` .. `Arg(N)`| Loaded value of the Nth symbolic argument |
232
+
|`return_value`|`ReturnValue`|`%result` from `call @f(...)`|
233
+
|`symbol` + `symbol: "name"`|`Symbol(@name)`|`load @name` from the module |
234
+
235
+
### 6.2 Values and Constants
236
+
237
+
String values in `relation.value` and `range.min`/`max` resolve as follows:
238
+
239
+
- Numeric literals (`"0"`, `"-1"`, `"0x1000"`) — parsed to `ConstantInt` matching the target type.
240
+
-`"NULL"` / `"null"` — `ConstantPointerNull` for pointers; zero for integers.
241
+
- Named constants (`"USB_MAX_PACKET_SIZE"`) — looked up as a module global; diagnostic + skip if absent. C `#define` values are not available as LLVM symbols and must be pre-resolved in the YAML spec.
242
+
243
+
### 6.3 Translation by Predicate Kind
244
+
245
+
Each subsection shows the IR pattern per target. `V` denotes the loaded target value; `PRE` = `klee_assume(cond)`, `POST` = `br i1 cond, %done, %fail` where `%fail` calls `abort()`.
Maps `relation` field to `icmp` (signed by default; unsigned for pointer-width / unsigned types):
258
+
259
+
| Relation |`icmp`|| Relation |`icmp`|
260
+
|---|---|---|---|---|
261
+
|`eq`|`eq`||`gt`|`sgt` / `ugt`|
262
+
|`neq`|`ne`||`gte`|`sge` / `uge`|
263
+
|`lt`|`slt` / `ult`||`none`| no IR (existence check) |
264
+
|`lte`|`sle` / `ule`||||
265
+
266
+
For `Arg(N)`: `PRE = klee_assume(icmp <rel> V, const)`. For `ReturnValue` / `Symbol(@s)`: `POST = if (!(icmp <rel> V, const)) abort()`. `relation: none` emits no IR — it is an existence assertion only.
267
+
268
+
#### `range`
269
+
270
+
Emits two `icmp` instructions ANDed together:
271
+
272
+
```
273
+
%lo = icmp sge V, resolved_min
274
+
%hi = icmp sle V, resolved_max
275
+
%ok = and i1 %lo, %hi
276
+
```
277
+
278
+
`Arg(N)` → PRE (`klee_assume`). `ReturnValue` / `Symbol(@s)` → POST (`abort` on `!%ok`).
`parseStaticContractString()` in `MetadataParser.hpp` parses this with a hand-written lexer: splits on `preconditions=[` / `postconditions=[`, extracts `{...}` blocks, then parses `key=value` pairs within each block. Results map to `ParsedPredicate` structs that mirror the existing `::contracts::PredicateAttr` field names.
309
+
Fields per `{...}` block: `id` (always), `kind` (always), `target` (all except `expr`), plus kind-specific fields — `relation`+`value`, `range=[min=..., max=...]`, `align`, `expr`, or `symbol` (when target is `Symbol`). `parseStaticContractString()` in `MetadataParser.hpp` lexes this into `ParsedPredicate` structs.
0 commit comments