···11+MIT License
22+33+Copyright (c) 2026 tobi contributors
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
···11+# tobi
22+33+ACPI AML decompiler that reads raw AML blobs and DSDT/SSDT tables, skips validated ACPI headers, and builds its own IR while doing conservative CFG recovery over package bounded control operations, then emits pseudocode, opcode disassembly, JSON IR, raw table metadata/Graphviz CFG view
44+55+Unsupported bytes stay in the tree as explicit IR nodes with offsets anyway and malformed input goes through diagnostics rather than blowing up whereas the parser keeps bounded reader state as well so hostile or truncated input just fails as data instead of turning into out of bounds reads
66+77+## Build
88+99+```sh
1010+cmake -S . -B build -DTOBI_WERROR=ON -DTOBI_FZ=ON
1111+cmake --build build
1212+ctest --test-dir build --output-on-failure
1313+```
1414+1515+The recommended validation path before landing parser changes is the sanitised configuration. ASan/UBSan are enabled to surface memory safety violations and UB during parser execution. LSan is intentionally disabled in the default environment because the test harness executes under ptrace and causes LeakSanitizer to abort before it can report meaningful allocation diagnostics:
1616+1717+```sh
1818+cmake -S . -B build-san -DCMAKE_C_COMPILER=clang -DTOBI_SAN=ON -DTOBI_WERROR=ON -DTOBI_FZ=ON
1919+cmake --build build-san
2020+ctest --test-dir build-san --output-on-failure
2121+./build-san/fz
2222+```
2323+2424+## Usage
2525+2626+```sh
2727+tobi [opts] <file> [file...]
2828+```
2929+3030+```text
3131+--dis print opcode disam
3232+--json print parsed IR as json
3333+--dot print recovered CF as Graphviz DOT
3434+--raw print input/table metadata and AML offsets
3535+--strict treat malformed/unsupported constructs as hard errors
3636+--plain disable coloured diagnostics
3737+--help print usage
3838+--version print version
3939+```
4040+4141+Default mode prints pseudocode:
4242+4343+```sh
4444+./build/tobi sam/m0.aml
4545+```
4646+4747+```text
4848+method MTH0(args=1, serialized=false, sync=0) {
4949+ Local0 = 0x2a;
5050+ return Local0;
5151+}
5252+```
5353+5454+Opcode view:
5555+5656+```sh
5757+./build/tobi --dis sam/m0.aml
5858+```
5959+6060+JSON IR:
6161+6262+```sh
6363+./build/tobi --json sam/m0.aml
6464+```
6565+6666+CFG view:
6767+6868+```sh
6969+./build/tobi --dot sam/m1.aml
7070+```
7171+7272+Multiple AML inputs share one namespace pre pass before parsing:
7373+7474+```sh
7575+./build/tobi dsdt.aml ssdt1.aml ssdt2.aml
7676+```
7777+7878+`--dis` and `--raw` still report each input independently because those modes are byte stream oriented and dont need the aggregate IR
7979+8080+## Input
8181+8282+Accepted inputs:
8383+8484+```text
8585+raw AML bytecode
8686+DSDT tables with a 36 byte ACPI header
8787+SSDT tables with a 36 byte ACPI header
8888+```
8989+9090+For tables it checks the signature and then validates the length at offset 4 before touching the AML body and verifies the ACPI checksum across the declared table length. Bad lengths are errors (obviously) and checksum mismatches are warnings by default and hard errors under `--strict`
9191+9292+Every IR node stores:
9393+9494+```text
9595+kind
9696+source byte offset
9797+source byte length where known
9898+owned name/path/string payloads
9999+raw opcode for unknown or opcode expression nodes
100100+deterministic child array
101101+```
102102+103103+## Control flow
104104+105105+AML IfOp, ElseOp, and WhileOp carry package bounded bodies so tobi reconstructs those directly while preserving the original lexical nesting. It doesnt invent higher level source constructs from control bytes whose semantics cant be established with confidence
106106+107107+Any ambiguous branch residue is kept as explicit fallback labels or annotated comments tied to the originating byte offset instead. Break and Continue are only emitted when the surrounding lexical context makes them unambiguously valid loop control operations. Outside loops they remain explicit IR nodes with diagnostics rather than being rewritten into incorrect high level control flow
108108+109109+## Samples
110110+111111+```text
112112+m0 14 0c 4d 54 48 30 01 70 0a 2a 60 a4 60
113113+m1 14 15 49 46 45 30 01 a0 06 68 70 0a 01 60 a1 05 70 0a 02 60 a4 60
114114+m2 14 20 57 4c 4f 30 01 70 0a 03 60 a2 13 60 a0 06 68 70 0a 01 61 a1 05 70 0a 02 61 74 60 01 60 a4 61
115115+dev0 10 31 5c 5f 53 42 5f 5b 82 29 44 45 56 30 5b 80 52 45 47 30 01 0a 10 0a 04 5b 81 0b 52 45 47 30 00 46 4c 44 30 08 14 0b 52 44 4d 30 00 a4 46 4c 44
116116+```
117117+118118+## Limits
119119+120120+This isnt a full ACPICA replacement. Namespace resolution is enough for method-call arity and readable paths but unresolved names can remain refs. Resource descriptors are recognised before theyre fully decoded. Less-structured AML stays as labelled IR/commentary instead of getting rewritten into invented source constructs. Unknown opcode coverage should shrink by adding metadata and operand parsers, not by special casing sample bytes
···11+#ifndef TOBI_MEM_H
22+#define TOBI_MEM_H
33+44+#include <stddef.h>
55+66+/** Allocate n bytes or terminate the process on allocation failure. */
77+void *tobi_xmalloc(size_t n);
88+99+/** Allocate zeroed storage for count elements of size bytes or terminate. */
1010+void *tobi_xcalloc(size_t count, size_t size);
1111+1212+/** Resize an allocation or terminate the process on allocation failure. */
1313+void *tobi_xrealloc(void *ptr, size_t n);
1414+1515+/** Duplicate a NUL-terminated string or terminate on allocation failure. */
1616+char *tobi_xstrdup(const char *s);
1717+1818+/** Duplicate exactly n bytes from a string and add a NUL terminator. */
1919+char *tobi_xstrndup(const char *s, size_t n);
2020+2121+#endif
···11+#ifndef TOBI_NM_H
22+#define TOBI_NM_H
33+44+#include "rd.h"
55+66+/** Return non-zero when c may start an AML NameSeg. */
77+int tobi_nm_is_lead(unsigned char c);
88+99+/** Return non-zero when c may appear after the first NameSeg byte. */
1010+int tobi_nm_is_tail(unsigned char c);
1111+1212+/** Parse a four-byte AML NameSeg into a newly allocated string. */
1313+int tobi_nm_nameseg(tobi_rd *rd, char **out);
1414+1515+/** Parse an AML NameString, including root, parent, dual, and multi prefixes. */
1616+int tobi_nm_namestring(tobi_rd *rd, char **out);
1717+1818+/** Join a parsed AML path to a lexical scope path. */
1919+char *tobi_nm_resolve(const char *scope, const char *name);
2020+2121+#endif