ACPI AML decompiler w/ CFG recovery and structured pseudocode
1#include "nm.h"
2
3#include "mem.h"
4#include "str.h"
5
6#include <ctype.h>
7#include <stdlib.h>
8#include <string.h>
9
10int tobi_nm_is_lead(unsigned char c) {
11 return (c >= 'A' && c <= 'Z') || c == '_';
12}
13
14int tobi_nm_is_tail(unsigned char c) {
15 return tobi_nm_is_lead(c) || (c >= '0' && c <= '9');
16}
17
18int tobi_nm_nameseg(tobi_rd *rd, char **out) {
19 size_t start = rd->pos;
20 uint8_t b[4];
21 if (!tobi_rd_bytes(rd, b, sizeof(b))) {
22 rd->pos = start;
23 return 0;
24 }
25 if (!tobi_nm_is_lead(b[0])) {
26 rd->pos = start;
27 rd->failed = 1;
28 return 0;
29 }
30 for (size_t i = 1; i < 4; i++) {
31 if (!tobi_nm_is_tail(b[i])) {
32 rd->pos = start;
33 rd->failed = 1;
34 return 0;
35 }
36 }
37 *out = tobi_xstrndup((const char *)b, 4);
38 return 1;
39}
40
41static void add_seg(tobi_sb *sb, const char *seg, int *first) {
42 if (!*first && sb->len > 0 && sb->buf[sb->len - 1] != '\\' && sb->buf[sb->len - 1] != '^') {
43 tobi_sb_ch(sb, '.');
44 }
45 tobi_sb_add(sb, seg);
46 *first = 0;
47}
48
49int tobi_nm_namestring(tobi_rd *rd, char **out) {
50 size_t start = rd->pos;
51 tobi_sb sb;
52 tobi_sb_init(&sb);
53 uint8_t b = 0;
54 int first = 1;
55 if (!tobi_rd_peek(rd, &b)) {
56 tobi_sb_free(&sb);
57 return 0;
58 }
59 if (b == '\\') {
60 (void)tobi_rd_u8(rd, &b);
61 tobi_sb_ch(&sb, '\\');
62 } else {
63 while (tobi_rd_peek(rd, &b) && b == '^') {
64 (void)tobi_rd_u8(rd, &b);
65 tobi_sb_ch(&sb, '^');
66 }
67 }
68 if (!tobi_rd_peek(rd, &b)) {
69 tobi_sb_free(&sb);
70 rd->pos = start;
71 return 0;
72 }
73 if (b == 0x00) {
74 (void)tobi_rd_u8(rd, &b);
75 if (sb.len == 0) {
76 tobi_sb_add(&sb, "<null>");
77 }
78 *out = tobi_sb_take(&sb);
79 return 1;
80 }
81 if (b == 0x2e) {
82 (void)tobi_rd_u8(rd, &b);
83 for (int i = 0; i < 2; i++) {
84 char *seg = NULL;
85 if (!tobi_nm_nameseg(rd, &seg)) {
86 tobi_sb_free(&sb);
87 rd->pos = start;
88 return 0;
89 }
90 add_seg(&sb, seg, &first);
91 free(seg);
92 }
93 *out = tobi_sb_take(&sb);
94 return 1;
95 }
96 if (b == 0x2f) {
97 (void)tobi_rd_u8(rd, &b);
98 uint8_t count = 0;
99 if (!tobi_rd_u8(rd, &count) || count == 0) {
100 tobi_sb_free(&sb);
101 rd->pos = start;
102 rd->failed = 1;
103 return 0;
104 }
105 for (uint8_t i = 0; i < count; i++) {
106 char *seg = NULL;
107 if (!tobi_nm_nameseg(rd, &seg)) {
108 tobi_sb_free(&sb);
109 rd->pos = start;
110 return 0;
111 }
112 add_seg(&sb, seg, &first);
113 free(seg);
114 }
115 *out = tobi_sb_take(&sb);
116 return 1;
117 }
118 char *seg = NULL;
119 if (!tobi_nm_nameseg(rd, &seg)) {
120 tobi_sb_free(&sb);
121 rd->pos = start;
122 return 0;
123 }
124 add_seg(&sb, seg, &first);
125 free(seg);
126 *out = tobi_sb_take(&sb);
127 return 1;
128}
129
130static char *pop_scope(const char *scope) {
131 if (!scope || strcmp(scope, "\\") == 0 || scope[0] == '\0') {
132 return tobi_xstrdup("\\");
133 }
134 const char *last = strrchr(scope, '.');
135 if (!last) {
136 return tobi_xstrdup("\\");
137 }
138 return tobi_xstrndup(scope, (size_t)(last - scope));
139}
140
141char *tobi_nm_resolve(const char *scope, const char *name) {
142 if (!name || name[0] == '\0') {
143 return tobi_xstrdup(scope && scope[0] ? scope : "\\");
144 }
145 if (name[0] == '\\') {
146 return tobi_xstrdup(name);
147 }
148 char *cur = tobi_xstrdup(scope && scope[0] ? scope : "\\");
149 const char *rest = name;
150 while (*rest == '^') {
151 char *p = pop_scope(cur);
152 free(cur);
153 cur = p;
154 rest++;
155 }
156 if (*rest == '\0' || strcmp(rest, "<null>") == 0) {
157 return cur;
158 }
159 tobi_sb sb;
160 tobi_sb_init(&sb);
161 if (strcmp(cur, "\\") == 0) {
162 tobi_sb_add(&sb, "\\");
163 tobi_sb_add(&sb, rest);
164 } else {
165 tobi_sb_add(&sb, cur);
166 tobi_sb_ch(&sb, '.');
167 tobi_sb_add(&sb, rest);
168 }
169 free(cur);
170 return tobi_sb_take(&sb);
171}
172
173void tobi_ns_init(tobi_ns *ns) {
174 ns->items = NULL;
175 ns->len = 0;
176 ns->cap = 0;
177}
178
179static void ns_ent_free(tobi_ns_ent *e) {
180 free(e->path);
181 free(e->name);
182 free(e->owner);
183 free(e->target);
184}
185
186void tobi_ns_free(tobi_ns *ns) {
187 if (!ns) {
188 return;
189 }
190 for (size_t i = 0; i < ns->len; i++) {
191 ns_ent_free(&ns->items[i]);
192 }
193 free(ns->items);
194 ns->items = NULL;
195 ns->len = 0;
196 ns->cap = 0;
197}
198
199const char *tobi_ns_kind_name(tobi_ns_kind kind) {
200 switch (kind) {
201 case TOBI_NS_UNKNOWN: return "unknown";
202 case TOBI_NS_SCOPE: return "scope";
203 case TOBI_NS_DEVICE: return "device";
204 case TOBI_NS_METHOD: return "method";
205 case TOBI_NS_PROCESSOR: return "processor";
206 case TOBI_NS_NAME: return "name";
207 case TOBI_NS_ALIAS: return "alias";
208 case TOBI_NS_OPREGION: return "opregion";
209 case TOBI_NS_FIELD: return "field";
210 case TOBI_NS_MUTEX: return "mutex";
211 case TOBI_NS_EVENT: return "event";
212 case TOBI_NS_EXTERNAL: return "external";
213 }
214 return "invalid";
215}
216
217const char *tobi_ns_leaf(const char *path) {
218 if (!path || path[0] == '\0') {
219 return "";
220 }
221 const char *dot = strrchr(path, '.');
222 if (dot && dot[1]) {
223 return dot + 1;
224 }
225 if (path[0] == '\\') {
226 return path + 1;
227 }
228 return path;
229}
230
231tobi_ns_ent *tobi_ns_find(tobi_ns *ns, const char *path) {
232 if (!ns || !path) {
233 return NULL;
234 }
235 for (size_t i = 0; i < ns->len; i++) {
236 if (strcmp(ns->items[i].path, path) == 0) {
237 return &ns->items[i];
238 }
239 }
240 return NULL;
241}
242
243const tobi_ns_ent *tobi_ns_find_const(const tobi_ns *ns, const char *path) {
244 if (!ns || !path) {
245 return NULL;
246 }
247 for (size_t i = 0; i < ns->len; i++) {
248 if (strcmp(ns->items[i].path, path) == 0) {
249 return &ns->items[i];
250 }
251 }
252 return NULL;
253}
254
255const tobi_ns_ent *tobi_ns_find_method(const tobi_ns *ns, const char *name) {
256 if (!ns || !name) {
257 return NULL;
258 }
259 for (size_t i = 0; i < ns->len; i++) {
260 const tobi_ns_ent *e = &ns->items[i];
261 if (e->kind == TOBI_NS_METHOD && strcmp(e->path, name) == 0) {
262 return e;
263 }
264 }
265 for (size_t i = 0; i < ns->len; i++) {
266 const tobi_ns_ent *e = &ns->items[i];
267 if (e->kind == TOBI_NS_METHOD && strcmp(e->name, name) == 0) {
268 return e;
269 }
270 }
271 const char *leaf = strrchr(name, '.');
272 leaf = leaf ? leaf + 1 : name;
273 for (size_t i = 0; i < ns->len; i++) {
274 const tobi_ns_ent *e = &ns->items[i];
275 if (e->kind == TOBI_NS_METHOD && strcmp(e->name, leaf) == 0) {
276 return e;
277 }
278 }
279 return NULL;
280}
281
282static char *ns_parent(const char *scope) {
283 if (!scope || strcmp(scope, "\\") == 0 || scope[0] == '\0') {
284 return NULL;
285 }
286 const char *dot = strrchr(scope, '.');
287 if (!dot) {
288 return tobi_xstrdup("\\");
289 }
290 if (dot == scope || (scope[0] == '\\' && dot == scope + 1)) {
291 return tobi_xstrdup("\\");
292 }
293 return tobi_xstrndup(scope, (size_t)(dot - scope));
294}
295
296static char *ns_join(const char *scope, const char *name) {
297 tobi_sb sb;
298 tobi_sb_init(&sb);
299 if (!scope || scope[0] == '\0' || strcmp(scope, "\\") == 0) {
300 tobi_sb_add(&sb, "\\");
301 tobi_sb_add(&sb, name);
302 } else {
303 tobi_sb_add(&sb, scope);
304 tobi_sb_ch(&sb, '.');
305 tobi_sb_add(&sb, name);
306 }
307 return tobi_sb_take(&sb);
308}
309
310static const tobi_ns_ent *ns_follow_alias(const tobi_ns *ns, const tobi_ns_ent *ent) {
311 const tobi_ns_ent *cur = ent;
312 for (unsigned i = 0; cur && cur->kind == TOBI_NS_ALIAS && cur->target && i < 16; i++) {
313 const tobi_ns_ent *next = tobi_ns_find_const(ns, cur->target);
314 if (!next || next == cur) {
315 break;
316 }
317 cur = next;
318 }
319 return cur;
320}
321
322const tobi_ns_ent *tobi_ns_lookup(const tobi_ns *ns, const char *scope, const char *name) {
323 if (!ns || !name || name[0] == '\0') {
324 return NULL;
325 }
326 if (name[0] == '\\' || name[0] == '^' || strcmp(name, "<null>") == 0) {
327 char *path = tobi_nm_resolve(scope, name);
328 const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, path));
329 free(path);
330 return ent;
331 }
332 char *cur = tobi_xstrdup(scope && scope[0] ? scope : "\\");
333 for (;;) {
334 char *candidate = ns_join(cur, name);
335 const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, candidate));
336 free(candidate);
337 if (ent) {
338 free(cur);
339 return ent;
340 }
341 char *parent = ns_parent(cur);
342 free(cur);
343 if (!parent) {
344 break;
345 }
346 cur = parent;
347 }
348 char *root = ns_join("\\", name);
349 const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, root));
350 free(root);
351 return ent;
352}
353
354char *tobi_ns_resolve_path(const tobi_ns *ns, const char *scope, const char *name) {
355 const tobi_ns_ent *ent = tobi_ns_lookup(ns, scope, name);
356 if (ent) {
357 return tobi_xstrdup(ent->path);
358 }
359 return tobi_nm_resolve(scope, name);
360}
361
362tobi_ns_ent *tobi_ns_add(tobi_ns *ns, tobi_ns_kind kind, const char *path, const char *owner,
363 size_t off, unsigned args, unsigned flags, int external,
364 const char *target, int *duplicate) {
365 if (duplicate) {
366 *duplicate = 0;
367 }
368 if (!ns || !path || path[0] == '\0') {
369 return NULL;
370 }
371 tobi_ns_ent *old = tobi_ns_find(ns, path);
372 if (old) {
373 int old_is_external = old->external || old->kind == TOBI_NS_EXTERNAL;
374 int same_scope = old->kind == TOBI_NS_SCOPE && kind == TOBI_NS_SCOPE;
375 int same_decl = old->kind == kind && old->off == off;
376 if (duplicate && !same_decl && !same_scope && !(old_is_external && !external)) {
377 *duplicate = 1;
378 }
379 if (old_is_external && !external) {
380 old->kind = kind;
381 old->external = 0;
382 old->off = off;
383 old->args = args;
384 } else if (kind == TOBI_NS_METHOD && old->kind == TOBI_NS_METHOD) {
385 old->args = args;
386 }
387 old->flags = flags;
388 if (target) {
389 free(old->target);
390 old->target = tobi_xstrdup(target);
391 }
392 return old;
393 }
394 if (ns->len == ns->cap) {
395 ns->cap = ns->cap ? ns->cap * 2 : 16;
396 ns->items = tobi_xrealloc(ns->items, ns->cap * sizeof(ns->items[0]));
397 }
398 tobi_ns_ent *e = &ns->items[ns->len++];
399 memset(e, 0, sizeof(*e));
400 e->path = tobi_xstrdup(path);
401 e->name = tobi_xstrdup(tobi_ns_leaf(path));
402 e->owner = tobi_xstrdup(owner && owner[0] ? owner : "\\");
403 e->target = target ? tobi_xstrdup(target) : NULL;
404 e->kind = kind;
405 e->off = off;
406 e->args = args;
407 e->flags = flags;
408 e->external = external;
409 return e;
410}