ACPI AML decompiler w/ CFG recovery and structured pseudocode
1#include "da.h"
2
3#include "nm.h"
4#include "op.h"
5#include "rd.h"
6#include "str.h"
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12static void raw_bytes(tobi_sb *sb, const tobi_rd *rd, size_t start, size_t end) {
13 tobi_sb_add(sb, " raw=");
14 size_t n = end > start ? end - start : 0;
15 size_t max = n > 8 ? 8 : n;
16 for (size_t i = 0; i < max && start + i < rd->len; i++) {
17 tobi_sb_printf(sb, "%02x", rd->data[start + i]);
18 if (i + 1 < max) {
19 tobi_sb_ch(sb, ' ');
20 }
21 }
22 if (n > max) {
23 tobi_sb_add(sb, " ...");
24 }
25}
26
27static void skip_simple_arg(tobi_rd *rd) {
28 uint8_t op = 0;
29 if (!tobi_rd_u8(rd, &op)) {
30 return;
31 }
32 if (op == 0x0a) {
33 (void)tobi_rd_skip(rd, 1);
34 } else if (op == 0x0b) {
35 (void)tobi_rd_skip(rd, 2);
36 } else if (op == 0x0c) {
37 (void)tobi_rd_skip(rd, 4);
38 } else if (op == 0x0e) {
39 (void)tobi_rd_skip(rd, 8);
40 } else if (op == 0x0d) {
41 while (tobi_rd_left(rd) > 0) {
42 uint8_t c = 0;
43 (void)tobi_rd_u8(rd, &c);
44 if (c == 0) {
45 break;
46 }
47 }
48 }
49}
50
51static void walk(tobi_sb *sb, tobi_rd *rd, unsigned depth) {
52 if (depth > 64) {
53 tobi_sb_printf(sb, "0x%04zx: <nesting limit>\n", tobi_rd_off(rd));
54 rd->pos = rd->len;
55 return;
56 }
57 while (tobi_rd_left(rd) > 0) {
58 size_t start = rd->pos;
59 size_t off = tobi_rd_off(rd);
60 uint8_t op = 0;
61 if (!tobi_rd_u8(rd, &op)) {
62 break;
63 }
64 uint16_t code = op;
65 const tobi_op *meta = NULL;
66 if (op == 0x5b) {
67 uint8_t ext = 0;
68 if (!tobi_rd_u8(rd, &ext)) {
69 tobi_sb_printf(sb, "0x%04zx: ExtOpPrefix truncated raw=5b\n", off);
70 return;
71 }
72 code = (uint16_t)(0x5b00u | ext);
73 meta = tobi_op_find_ext(ext);
74 } else {
75 meta = tobi_op_find(op);
76 }
77 if (!meta && (op == '\\' || op == '^' || op == 0x2e || op == 0x2f || tobi_nm_is_lead(op))) {
78 rd->pos = start;
79 char *nm = NULL;
80 if (tobi_nm_namestring(rd, &nm)) {
81 tobi_sb_printf(sb, "0x%04zx: NameString name=%s", off, nm);
82 raw_bytes(sb, rd, start, rd->pos);
83 tobi_sb_ch(sb, '\n');
84 free(nm);
85 continue;
86 }
87 rd->pos = start + 1;
88 }
89 const char *name = meta ? meta->name : "UnknownOp";
90 const char *operands = tobi_op_operands(code);
91 int decoded_pseudo = 0;
92 char extra[128];
93 extra[0] = '\0';
94 if (op == 0x0a) {
95 uint8_t v = 0;
96 if (tobi_rd_u8(rd, &v)) {
97 (void)snprintf(extra, sizeof(extra), " value=0x%x", (unsigned)v);
98 }
99 } else if (op == 0x0b) {
100 uint16_t v = 0;
101 if (tobi_rd_u16(rd, &v)) {
102 (void)snprintf(extra, sizeof(extra), " value=0x%x", (unsigned)v);
103 }
104 } else if (op == 0x0c) {
105 uint32_t v = 0;
106 if (tobi_rd_u32(rd, &v)) {
107 (void)snprintf(extra, sizeof(extra), " value=0x%x", (unsigned)v);
108 }
109 } else if (op == 0x0e) {
110 uint64_t v = 0;
111 if (tobi_rd_u64(rd, &v)) {
112 (void)snprintf(extra, sizeof(extra), " value=0x%llx", (unsigned long long)v);
113 }
114 } else if (op == 0x0d) {
115 size_t s = rd->pos;
116 while (rd->pos < rd->len && rd->data[rd->pos] != 0) {
117 rd->pos++;
118 }
119 size_t n = rd->pos - s;
120 if (rd->pos < rd->len) {
121 rd->pos++;
122 }
123 (void)snprintf(extra, sizeof(extra), " strlen=%zu", n);
124 } else if (op >= 0x60 && op <= 0x67) {
125 (void)snprintf(extra, sizeof(extra), " name=Local%u", (unsigned)(op - 0x60));
126 name = "LocalObj";
127 decoded_pseudo = 1;
128 } else if (op >= 0x68 && op <= 0x6e) {
129 (void)snprintf(extra, sizeof(extra), " name=Arg%u", (unsigned)(op - 0x68));
130 name = "ArgObj";
131 decoded_pseudo = 1;
132 } else if (op == 0x08 || op == 0x06 || code == 0x5b80 || code == 0x5b01 || code == 0x5b02) {
133 char *nm = NULL;
134 if (tobi_nm_namestring(rd, &nm)) {
135 (void)snprintf(extra, sizeof(extra), " name=%s", nm);
136 free(nm);
137 }
138 if (op == 0x06) {
139 char *dst = NULL;
140 if (tobi_nm_namestring(rd, &dst)) {
141 size_t used = strlen(extra);
142 (void)snprintf(extra + used, sizeof(extra) - used, " target=%s", dst);
143 free(dst);
144 }
145 }
146 }
147 tobi_sb_printf(sb, "0x%04zx: %s", off, name);
148 if (operands && operands[0]) {
149 tobi_sb_printf(sb, " operands=%s", operands);
150 }
151 if (meta && meta->has_pkg_len) {
152 size_t pkg_start = rd->pos;
153 size_t pkg_len = 0;
154 size_t enc = 0;
155 if (!tobi_rd_pkg_len(rd, &pkg_len, &enc)) {
156 raw_bytes(sb, rd, start, rd->pos);
157 tobi_sb_add(sb, " malformed-pkglen\n");
158 return;
159 }
160 size_t body_start = rd->pos;
161 size_t end = pkg_start + pkg_len;
162 if (end > rd->len || end < body_start) {
163 end = rd->len;
164 tobi_sb_add(sb, " truncated");
165 }
166 tobi_sb_printf(sb, " pkglen=%zu", pkg_len);
167 if (code == 0x10 || code == 0x14 || code == 0x5b82 || code == 0x5b83 || code == 0x5b81) {
168 tobi_rd copy = *rd;
169 char *nm = NULL;
170 if (tobi_nm_namestring(©, &nm)) {
171 tobi_sb_add(sb, " name=");
172 tobi_sb_add(sb, nm);
173 free(nm);
174 }
175 }
176 raw_bytes(sb, rd, start, body_start);
177 tobi_sb_ch(sb, '\n');
178 tobi_rd sub;
179 tobi_rd_init(&sub, rd->data + body_start, end - body_start, rd->base + body_start);
180 if (code == 0x11) {
181 skip_simple_arg(&sub);
182 tobi_sb_printf(sb, "0x%04zx: BufferData len=%zu ", tobi_rd_off(&sub), tobi_rd_left(&sub));
183 raw_bytes(sb, &sub, sub.pos, sub.len);
184 tobi_sb_ch(sb, '\n');
185 } else if (code == 0x14) {
186 char *nm = NULL;
187 if (tobi_nm_namestring(&sub, &nm)) {
188 free(nm);
189 (void)tobi_rd_skip(&sub, 1);
190 }
191 } else if (code == 0x10 || code == 0x5b82) {
192 char *nm = NULL;
193 if (tobi_nm_namestring(&sub, &nm)) {
194 free(nm);
195 }
196 } else if (code == 0x5b81) {
197 char *nm = NULL;
198 if (tobi_nm_namestring(&sub, &nm)) {
199 free(nm);
200 (void)tobi_rd_skip(&sub, 1);
201 }
202 } else if (code == 0xa0 || code == 0xa2) {
203 (void)tobi_rd_skip(&sub, 1);
204 }
205 if (code != 0x11) {
206 walk(sb, &sub, depth + 1);
207 }
208 rd->pos = end;
209 } else {
210 tobi_sb_add(sb, extra);
211 raw_bytes(sb, rd, start, rd->pos);
212 if (!meta && !decoded_pseudo) {
213 tobi_sb_printf(sb, " opcode=0x%x", (unsigned)code);
214 }
215 tobi_sb_ch(sb, '\n');
216 }
217 }
218}
219
220char *tobi_da_emit(const uint8_t *data, size_t len, const tobi_input_meta *meta) {
221 tobi_sb sb;
222 tobi_sb_init(&sb);
223 size_t off = meta ? meta->aml_off : 0;
224 size_t alen = meta ? meta->aml_len : len;
225 if (off > len || alen > len - off) {
226 off = 0;
227 alen = len;
228 }
229 tobi_rd rd;
230 tobi_rd_init(&rd, data + off, alen, off);
231 walk(&sb, &rd, 0);
232 return tobi_sb_take(&sb);
233}