Skip to content

Commit 499218b

Browse files
authored
Merge pull request #1642 from xwings/dev
Add AGENT documentation.
2 parents a42fb28 + e522d7e commit 499218b

15 files changed

Lines changed: 793 additions & 415 deletions

File tree

AGENT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ARCHITECTURE.md

ARCHITECTURE.md

Lines changed: 170 additions & 414 deletions
Large diffs are not rendered by default.

ARCHITECTURE/arch.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Arch — CPU architecture layer
2+
3+
## Goal
4+
5+
Own everything CPU-specific: the Unicorn `Uc` instance, register access, stack
6+
primitives, disassembler/assembler, CPU models, and per-arch calling
7+
conventions. This is the bottom layer — everything else reads `ql.arch`; arch
8+
depends only on Unicorn/Capstone/Keystone. Mature released infrastructure;
9+
maturity-based status.
10+
11+
## Status
12+
13+
`done` — all architectures exercised by the CI suite; CPU model selection
14+
covered by `tests/test_cpu_models.py`.
15+
16+
## Code Structure
17+
18+
| File | Role |
19+
| ---- | ---- |
20+
| `qiling/arch/arch.py` | Abstract base `QlArch`: owns `uc`, `regs`, stack push/pop, save/restore, disassembler |
21+
| `qiling/arch/x86.py` | `QlArchIntel` base + `QlArchA8086`/`QlArchX86`/`QlArchX8664`, GDT/MSR wiring |
22+
| `qiling/arch/arm.py`, `arm64.py` | ARM/AArch64, thumb handling, coprocessor access |
23+
| `qiling/arch/cortex_m.py` | Cortex-M on top of ARM: NVIC-style interrupt entry/exit for MCU mode |
24+
| `qiling/arch/mips.py`, `riscv.py`, `riscv64.py`, `ppc.py` | Remaining architectures |
25+
| `qiling/arch/register.py` | `QlRegisterManager` — attribute-style register read/write |
26+
| `qiling/arch/models.py` | CPU model enums (`X86_CPU_MODEL``RISCV64_CPU_MODEL`) |
27+
| `qiling/arch/msr.py`, `cpr.py`, `cpr64.py` | x86 MSRs, ARM/ARM64 coprocessor registers |
28+
| `qiling/arch/utils.py` | `QlArchUtils`: disassembly output for verbose/trace modes |
29+
| `qiling/cc/__init__.py` + `intel.py`, `arm.py`, `mips.py`, `ppc.py`, `riscv.py` | Calling conventions (arg/retval marshalling) consumed by `os/fcall.py` |
30+
31+
## Key Types and Entry Points
32+
33+
- `qiling/arch/arch.py:22` - `QlArch(ABC)` - cached properties `uc` (`:34`), `regs` (`:42`), `stack_push/stack_pop` (`:52`/`:66`), `save/restore` via UcContext (`:108`/`:112`), `disassembler` (`:117`).
34+
- `qiling/arch/register.py:11` - `QlRegisterManager` - `ql.arch.regs.rax`-style access, backed by per-arch `*_const.py` tables.
35+
- `qiling/arch/x86.py:22,53,79,111` - `QlArchIntel` / `QlArchA8086` / `QlArchX86` / `QlArchX8664`.
36+
- `qiling/arch/cortex_m.py:67` - `QlArchCORTEX_M(QlArchARM)` - plus `QlInterruptContext` (`:25`) for exception entry/exit in MCU mode.
37+
- `qiling/arch/models.py` - CPU model enums selected via the `cputype` kwarg (resolved in `select_arch`, `qiling/utils.py:376`).
38+
- `qiling/cc/__init__.py:9` - `QlCC` - abstract calling convention; `QlCommonBaseCC` (`:104`); e.g. `cc/intel.py` defines `cdecl`/`stdcall`/`ms64`/`macosx64`.
39+
40+
## Interactions
41+
42+
- Instantiated first by [core.md](core.md) (`qiling/core.py:154`); `Qiling.uc` proxies `arch.uc` (`qiling/core.py:479`).
43+
- [loader.md](loader.md) and the OS layers use `arch.regs` and stack primitives to set up entry state.
44+
- `qiling/cc/` is consumed by `QlFunctionCall` in [os-base.md](os-base.md) for API argument marshalling.
45+
- [debugger.md](debugger.md) reads/writes registers through this layer.
46+
47+
## How to Test
48+
49+
```sh
50+
cd tests && python3 test_cpu_models.py # pass = unittest "OK", exit code 0
51+
```
52+
53+
- Broader arch coverage comes for free from `test_shellcode.py` (5 archs) and the per-OS suites.
54+
55+
## Open Gaps / Roadmap
56+
57+
- PowerPC and RISC-V have fewer OS-level tests than x86/ARM/MIPS (no dedicated POSIX suite beyond `tests/test_riscv.py`).
58+
- Thumb state handling has a known fixup in `Qiling.emu_start` (`qiling/core.py:743`) rather than in the arch layer itself.

ARCHITECTURE/cli.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# CLI — qltool and qltui
2+
3+
## Goal
4+
5+
Give users a no-code way to run emulations: `qltool run` executes a binary
6+
against a rootfs, `qltool code` runs shellcode (hex/asm/bin), `qltool
7+
examples` prints usage samples, and `qltool qltui` launches an interactive
8+
TUI that gathers the same options. Mature released infrastructure;
9+
maturity-based status.
10+
11+
## Status
12+
13+
`done` — covered by `tests/test_qltool.py`, which shells out to `qltool` for
14+
run/code subcommands including gdb attach and coverage output.
15+
16+
## Code Structure
17+
18+
| File | Role |
19+
| ---- | ---- |
20+
| `qltool` | Executable argparse CLI; builds kwargs and drives `Qiling` |
21+
| `qltui.py` | questionary/pyfx/termcolor TUI; collects options, returned to qltool |
22+
23+
## Key Types and Entry Points
24+
25+
- `qltool:189` - `run()` - argparse setup with subcommands `run`, `code`, `examples`, `qltui`; enum-mapping actions translate `--arch/--os/--endian/--verbose` strings to `QL_ARCH`/`QL_OS` enums (`qltool:59-75`).
26+
- `qltool:129` - `handle_run(options)` - builds `{'argv': [file]+args, 'rootfs': ...}`.
27+
- `qltool:78` - `handle_code(options)` - reads hex/asm/bin shellcode, assembling asm via `qiling.arch.utils.assembler` (`qltool:104`).
28+
- `qltool:276` - `ql = Qiling(**ql_args)` - the single construction point; then optional Qdb (`:279`), gdbserver (`:285`), coverage-wrapped `ql.run()` (`:306-310`), JSON report (`:312`), exit with `ql.os.exit_code` (`:321`).
29+
30+
## Interactions
31+
32+
- Thin client of [core.md](core.md): constructs `Qiling` and calls `run()`.
33+
- Attaches [debugger.md](debugger.md) via `--gdb host:port` / `--qdb [--rr]`.
34+
- Uses [extensions.md](extensions.md) for `--coverage-file` (drcov) and `--json` report output.
35+
- `qltool examples` mirrors scripts documented in `examples/README.md`.
36+
37+
## How to Test
38+
39+
```sh
40+
cd tests && python3 test_qltool.py # pass = unittest "OK", exit 0
41+
```
42+
43+
- Manual smoke test: `./qltool run -f examples/rootfs/x8664_linux/bin/x8664_hello --rootfs examples/rootfs/x8664_linux` — pass = prints `Hello, World!`.
44+
45+
## Open Gaps / Roadmap
46+
47+
- `qltui.py` (TUI) has no automated tests — interactive only.
48+
- `qltool` predates subcommand-style config files; complex setups (fs mappers, custom hooks) still require the Python API.

ARCHITECTURE/core.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Core — the Qiling facade and plumbing
2+
3+
## Goal
4+
5+
Own the public API and object lifecycle of an emulation: the `Qiling` class
6+
composes arch, loader, memory, OS, and (for bare-metal) hardware components,
7+
and exposes hooks, memory/register access, patching, and save/restore to
8+
users. No roadmap milestone applies — this is mature released infrastructure;
9+
status is maturity-based per the control center.
10+
11+
## Status
12+
13+
`done` — exercised by the whole test suite; the How to Test command below
14+
boots shellcode end-to-end through `Qiling.__init__``run()``emu_start`.
15+
16+
## Code Structure
17+
18+
| File | Role |
19+
| ---- | ---- |
20+
| `qiling/core.py` | `Qiling` class: constructor/composition root, `run`, `emu_start/stop`, `save/restore`, `patch`, properties |
21+
| `qiling/core_hooks.py` | `QlCoreHooks` mixin: wraps Unicorn hooks, dispatches to `Hook` lists |
22+
| `qiling/core_hooks_types.py` | `Hook`, `HookAddr`, `HookIntr`, `HookRet` records |
23+
| `qiling/core_struct.py` | `QlCoreStructs` mixin: endian/bit-width-aware pack/unpack helpers |
24+
| `qiling/utils.py` | Name→class resolution: `select_arch/loader/os/component/debugger`, binary format sniffing, profile loading |
25+
| `qiling/const.py` | Enums: `QL_ARCH`, `QL_OS`, `QL_VERBOSE`, `QL_INTERCEPT`, `QL_STATE`, groupings `QL_OS_POSIX`/`QL_OS_BAREMETAL` |
26+
| `qiling/exception.py` | `QlErrorBase` and ~20 subclasses (`QlErrorArch`, `QlSyscallError`, …) |
27+
| `qiling/host.py` | `QlHost`: describes the *hosting* platform (for pass-through features) |
28+
| `qiling/log.py` | Logger setup, colored/plain formatters, regex filtering behind `Qiling.filter` |
29+
| `qiling/profiles/*.ql` | Default per-OS config (memory layout, kernel uid/gid/pid) merged with user overrides |
30+
31+
## Key Types and Entry Points
32+
33+
- `qiling/core.py:35` - `Qiling(QlCoreHooks, QlCoreStructs)` - the facade; `__init__` composes components in fixed order: arch (`:154`) → mixins (`:157`) → logger → profile (`:178`) → loader (`:183`) → memory (`:188`) → OS (`:189`) → hw if bare-metal (`:191`) → `loader.run()` (`:195`).
34+
- `qiling/core.py:561` - `Qiling.run(begin, end, timeout, count)` - attaches debugger, applies patches, writes exit trap, delegates to `os.run()`.
35+
- `qiling/core.py:743` - `Qiling.emu_start(begin, end, timeout, count)` - thin wrapper over `uc.emu_start`; manages thumb bit, `QL_STATE`, exception re-raise.
36+
- `qiling/core.py:609` / `:658` - `save()` / `restore()` - snapshot regs/mem/fd/os per-component.
37+
- `qiling/core_hooks.py:150` - `QlCoreHooks` - hook registration API: `hook_code` (`:400`), `hook_block` (`:422`), `hook_address` (`:550`), `hook_intno` (`:575`), `hook_mem_read/write` (`:592`/`:610`), `hook_insn` (`:646`), `hook_del` (`:686`).
38+
- `qiling/utils.py:278` - `ql_guess_emu_env(path)` - sniffs arch/OS/endian from pathname, ELF, Mach-O, or PE headers when not given.
39+
- `qiling/utils.py:297,376,409,323,332` - `select_loader/arch/os/component/debugger` - dynamic-import factories; core never imports concrete subclasses.
40+
- `qiling/utils.py:419` - `profile_setup(ostype, user_config)` - YAML for MCU, else ConfigParser over `qiling/profiles/<os>.ql` + user overrides.
41+
42+
## Interactions
43+
44+
- Instantiates every other subsystem: [arch.md](arch.md), [loader.md](loader.md), [os-base.md](os-base.md) (memory + OS), [hw.md](hw.md) (bare-metal only).
45+
- Lazily instantiates [debugger.md](debugger.md) inside `run()` via `select_debugger`.
46+
- [extensions.md](extensions.md) and [cli.md](cli.md) consume only this public API.
47+
- Hook dispatch protocol (`QL_HOOK_BLOCK`, `qiling/const.py:77`) is honored by the OS layers when they intercept syscalls/APIs.
48+
49+
## How to Test
50+
51+
```sh
52+
cd tests && python3 test_shellcode.py # pass = unittest "OK", exit code 0
53+
```
54+
55+
- Exercises `Qiling(code=...)` construction and `run()` across x86/x86-64/ARM/ARM64/MIPS shellcode for Linux and Windows.
56+
57+
## Open Gaps / Roadmap
58+
59+
- `ChangeLog` lags the released version (1.4.6 vs 1.4.8 in `pyproject.toml`).
60+
- `unicorn` is hard-pinned to 2.1.3; upgrading Unicorn is a project-wide event.
61+
- Feature wishlist lives in GitHub issue [#333](https://github.com/qilingframework/qiling/issues/333).

ARCHITECTURE/debugger.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Debugger — GDB server and Qdb
2+
3+
## Goal
4+
5+
Let users debug emulated targets: a GDB remote-serial-protocol server so any
6+
GDB/IDA/lldb front end can attach cross-architecture, and Qdb — a built-in
7+
interactive CLI debugger with stepping, branch prediction, and record/replay
8+
reverse debugging. Mature released infrastructure; maturity-based status.
9+
10+
## Status
11+
12+
`done` — Qdb covered by `tests/test_qdb.py`; the GDB server by
13+
`tests/test_debugger.py` (spawns a real client session).
14+
15+
## Code Structure
16+
17+
| File | Role |
18+
| ---- | ---- |
19+
| `qiling/debugger/debugger.py` | Base `QlDebugger` |
20+
| `qiling/debugger/gdb/gdb.py` | `QlGdb`: GDB remote-serial-protocol server |
21+
| `qiling/debugger/gdb/xmlregs.py`, `gdb/xml/` | Target-description XML per arch for modern GDB clients |
22+
| `qiling/debugger/qdb/qdb.py` | `QlQdb`: interactive Cmd-based debugger |
23+
| `qiling/debugger/qdb/arch/` | Per-arch Qdb support (arm, intel, mips) |
24+
| `qiling/debugger/qdb/branch_predictor/` | Predicts branch targets for step/next |
25+
| `qiling/debugger/qdb/render/` | Register/stack/disasm view rendering |
26+
27+
## Key Types and Entry Points
28+
29+
- `qiling/debugger/debugger.py:13` - `QlDebugger` - base; `run()` starts the session.
30+
- `qiling/debugger/gdb/gdb.py:68` - `QlGdb(QlDebugger)` - listens on ip:port, translates RSP packets to Qiling hook/mem/reg operations.
31+
- `qiling/debugger/qdb/qdb.py:59` - `QlQdb(Cmd, QlDebugger)` - CLI loop; `rr` mode enables record/replay reverse debugging.
32+
- Activation: set `ql.debugger = True | "gdb" | "gdb:0.0.0.0:9999" | "qdb" | "qdb:rr"` (`qiling/core.py:437`); instantiated lazily in `Qiling.run` via `select_debugger` (`qiling/utils.py:332`).
33+
- `qltool` flags: `--gdb` and `--qdb` (see [cli.md](cli.md)).
34+
35+
## Interactions
36+
37+
- Instantiated by [core.md](core.md) at `Qiling.run` time, not construction.
38+
- Reads/writes state exclusively through public APIs: registers via [arch.md](arch.md), memory via [os-base.md](os-base.md), breakpoints via `hook_address` ([core.md](core.md)).
39+
- The IDA plugin in [extensions.md](extensions.md) offers an alternative front end over the same public API.
40+
41+
## How to Test
42+
43+
```sh
44+
cd tests && python3 test_qdb.py # pass = unittest "OK", exit 0
45+
```
46+
47+
- GDB server: `cd tests && python3 test_debugger.py` — starts `QlGdb` and drives a scripted client.
48+
49+
## Open Gaps / Roadmap
50+
51+
- Qdb per-arch support covers arm/cortex-m/mips/intel; RISC-V and PPC lack Qdb arch modules (`qiling/debugger/qdb/arch/`).
52+
- Record/replay (`qdb:rr`) stores full state per step — memory-heavy on long runs.

ARCHITECTURE/extensions.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Extensions — optional tooling on top of the core
2+
3+
## Goal
4+
5+
House everything that builds on the public `Qiling` API without being required
6+
by it: fuzzing (AFL), code-coverage and execution-trace writers, a heap
7+
sanitizer, radare2 and IDA Pro integration, fake stdio pipes, and JSON run
8+
reports. Mature released infrastructure; maturity-based status.
9+
10+
## Status
11+
12+
`done` — history/coverage tracker tested in CI; AFL and r2 integrations need
13+
optional extras (`fuzz`, `RE`); the IDA plugin needs IDA Pro (untested in CI).
14+
15+
## Code Structure
16+
17+
| File | Role |
18+
| ---- | ---- |
19+
| `qiling/extensions/afl/` | `ql_afl_fuzz` (unicornafl bridge) + `QlFuzzer` harness base class |
20+
| `qiling/extensions/coverage/` | `collect_coverage` context manager; writers in `formats/` (drcov, drcov_exact, ezcov, history) |
21+
| `qiling/extensions/tracing/` | Tenet-style execution trace writers (`formats/`) |
22+
| `qiling/extensions/trace.py` | Disassembly tracing: full trace or ring-buffer history |
23+
| `qiling/extensions/sanitizers/heap.py` | Canary-based heap sanitizer (UAF/OOB detection) |
24+
| `qiling/extensions/r2/r2.py` | radare2 (r2libr) integration: sections/symbols/functions/xrefs of the loaded target |
25+
| `qiling/extensions/idaplugin/qilingida.py` | IDA Pro plugin driving Qiling emulation from IDA |
26+
| `qiling/extensions/pipe.py` | Fake stdio streams for hijacking emulated I/O (fuzzing staple) |
27+
| `qiling/extensions/report/report.py` | `generate_report(ql)`: JSON summary of a run |
28+
| `qiling/extensions/multitask.py` | Cooperative-multitask Unicorn wrapper (documented in [os-baremetal.md](os-baremetal.md)) |
29+
| `qiling/extensions/mcu/` | Board/chip definitions (documented in [hw.md](hw.md)) |
30+
| `qiling/extensions/winsdkapi.py` | Windows API signature decorator glue |
31+
32+
## Key Types and Entry Points
33+
34+
- `qiling/extensions/afl/afl.py:21` / `:87` - `ql_afl_fuzz` / `ql_afl_fuzz_custom` - hand control to AFL++ via unicornafl; harness base `QlFuzzer` (`qiling/extensions/afl/qlfuzzer.py:14`).
35+
- `qiling/extensions/coverage/utils.py:48` - `collect_coverage(ql, name, file)` - context manager writing e.g. drcov files (used by `qltool --coverage-file`).
36+
- `qiling/extensions/trace.py:145` / `:180` - `enable_full_trace` / `enable_history_trace` - disassembly tracing via `hook_code`.
37+
- `qiling/extensions/sanitizers/heap.py:16` - `QlSanitizedMemoryHeap` - drop-in replacement for `ql.os.heap` with canaries and free-list checks.
38+
- `qiling/extensions/r2/r2.py:135` - `R2(ql)` - rzpipe-backed analysis of the loaded image.
39+
- `qiling/extensions/pipe.py` - `SimpleInStream`/`SimpleOutStream` (`:62`/`:69`) - assigned to `ql.os.stdin`/`stdout`.
40+
- `qiling/extensions/report/report.py:56` - `generate_report(ql)` - JSON report (used by `qltool --json`).
41+
42+
## Interactions
43+
44+
- Everything here consumes only the public API of [core.md](core.md) (hooks, mem, regs) and [os-base.md](os-base.md) (heap, stdio, syscall/API overrides).
45+
- The heap sanitizer wraps `QlMemoryHeap` from [os-base.md](os-base.md) (demo: `examples/uefi_sanitized_heap.py` with [os-windows.md](os-windows.md) UEFI).
46+
- Fuzzing harnesses live in `examples/fuzzing/` and pair AFL with `pipe.py` and `set_syscall` from [os-posix.md](os-posix.md).
47+
- `qltool` wires in coverage and report generation ([cli.md](cli.md)).
48+
49+
## How to Test
50+
51+
```sh
52+
cd tests && python3 test_history.py # pass = unittest "OK", exit 0
53+
```
54+
55+
- r2 integration (needs `pip install qiling[RE]`): `cd tests && python3 test_r2.py`.
56+
- AFL (needs `pip install qiling[fuzz]` + AFL++): run a harness from `examples/fuzzing/linux_x8664/`.
57+
58+
## Open Gaps / Roadmap
59+
60+
- `afl/` and `r2/` depend on optional extras not installed by default; the IDA plugin cannot be CI-tested (requires an IDA Pro license).
61+
- Two overlapping trace mechanisms exist (`trace.py` and `tracing/`); no unified interface.

ARCHITECTURE/hw.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# HW — peripheral emulation for bare-metal targets
2+
3+
## Goal
4+
5+
Emulate memory-mapped peripherals (GPIO, UART/char, SPI, I2C, timers, DMA,
6+
interrupt controllers, flash, …) so MCU firmware runs against realistic
7+
hardware. Peripherals are instantiated from a YAML profile naming which
8+
peripheral class sits at which MMIO base address. Only active in bare-metal
9+
(MCU) mode. Mature released infrastructure; maturity-based status.
10+
11+
## Status
12+
13+
`done` — exercised by `tests/test_mcu.py` against STM32F1/F4 and GD32VF1
14+
firmware images (UART echo, freertos, blink, crc, dma_clock, i2c/spi/lcd).
15+
16+
## Code Structure
17+
18+
| File | Role |
19+
| ---- | ---- |
20+
| `qiling/hw/hw.py` | `QlHwManager`: creates peripherals from profile, maps MMIO, steps them each tick |
21+
| `qiling/hw/peripheral.py` | Base `QlPeripheral` (+ `QlPeripheralUtils`) |
22+
| `qiling/hw/analog/ char/ dma/ flash/ gpio/ i2c/ intc/ math/ mem/ misc/ net/ power/ sd/ spi/ timer/` | One directory per peripheral class, chip-specific implementations inside |
23+
| `qiling/hw/const/` | Register layout constants per chip family |
24+
| `qiling/hw/connectivity.py`, `external_device/` | External-interface plumbing (e.g. connecting a fake device to a bus) |
25+
| `qiling/extensions/mcu/` | Board/chip definitions (stm32f4xx, gd32vf1, nxp, atmel, bes): memory maps naming peripheral class + base address, passed as `env=` to `Qiling` |
26+
27+
## Key Types and Entry Points
28+
29+
- `qiling/hw/hw.py:14` - `QlHwManager` - available as `ql.hw`; created by core only when `ql.baremetal` (`qiling/core.py:191`).
30+
- `qiling/hw/hw.py:23` - `QlHwManager.create(label, struct, base)` - instantiates a peripheral from the profile entry and maps its MMIO region.
31+
- `qiling/hw/hw.py:82` - `QlHwManager.step()` - advances every peripheral one tick; called from the MCU run loop.
32+
- `qiling/hw/peripheral.py:132` - `QlPeripheral(QlPeripheralUtils)` - base class: a ctypes register struct + read/write handlers on the MMIO region.
33+
- `qiling/extensions/mcu/stm32f4xx/stm32f407.py` (and siblings) - chip `env` dicts consumed at `Qiling(..., env=...)` construction.
34+
35+
## Interactions
36+
37+
- Created by [core.md](core.md) for bare-metal targets; driven by the MCU run loop in [os-baremetal.md](os-baremetal.md) (`QlHwManager.step()` between execution slices).
38+
- Interrupt controller peripherals (`intc/`) raise exceptions delivered through `QlInterruptContext` in [arch.md](arch.md).
39+
- MMIO regions are mapped through `QlMemoryManager` ([os-base.md](os-base.md)).
40+
- Profile parsing (YAML) is in `profile_setup` ([core.md](core.md), `qiling/utils.py:419`).
41+
42+
## How to Test
43+
44+
```sh
45+
cd tests && python3 test_mcu.py # pass = unittest "OK", exit 0
46+
```
47+
48+
- Exercises GPIO, UART, EXTI, I2C, SPI, DMA, CRC, RTC, watchdog peripherals on real firmware from `examples/rootfs/mcu/`.
49+
50+
## Open Gaps / Roadmap
51+
52+
- Peripheral fidelity is demand-driven: registers behave as observed firmware needs, not per full datasheets.
53+
- Chip coverage limited to families under `qiling/extensions/mcu/`; adding a chip means writing its memory map + any missing peripheral classes.

0 commit comments

Comments
 (0)