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 free(e->source);
185}
186
187void tobi_ns_free(tobi_ns *ns) {
188 if (!ns) {
189 return;
190 }
191 for (size_t i = 0; i < ns->len; i++) {
192 ns_ent_free(&ns->items[i]);
193 }
194 free(ns->items);
195 ns->items = NULL;
196 ns->len = 0;
197 ns->cap = 0;
198}
199
200const char *tobi_ns_kind_name(tobi_ns_kind kind) {
201 switch (kind) {
202 case TOBI_NS_UNKNOWN: return "unknown";
203 case TOBI_NS_SCOPE: return "scope";
204 case TOBI_NS_DEVICE: return "device";
205 case TOBI_NS_METHOD: return "method";
206 case TOBI_NS_PROCESSOR: return "processor";
207 case TOBI_NS_NAME: return "name";
208 case TOBI_NS_ALIAS: return "alias";
209 case TOBI_NS_OPREGION: return "opregion";
210 case TOBI_NS_FIELD: return "field";
211 case TOBI_NS_MUTEX: return "mutex";
212 case TOBI_NS_EVENT: return "event";
213 case TOBI_NS_EXTERNAL: return "external";
214 }
215 return "invalid";
216}
217
218const char *tobi_ns_leaf(const char *path) {
219 if (!path || path[0] == '\0') {
220 return "";
221 }
222 const char *dot = strrchr(path, '.');
223 if (dot && dot[1]) {
224 return dot + 1;
225 }
226 if (path[0] == '\\') {
227 return path + 1;
228 }
229 return path;
230}
231
232tobi_ns_ent *tobi_ns_find(tobi_ns *ns, const char *path) {
233 if (!ns || !path) {
234 return NULL;
235 }
236 for (size_t i = 0; i < ns->len; i++) {
237 if (strcmp(ns->items[i].path, path) == 0) {
238 return &ns->items[i];
239 }
240 }
241 return NULL;
242}
243
244const tobi_ns_ent *tobi_ns_find_const(const tobi_ns *ns, const char *path) {
245 if (!ns || !path) {
246 return NULL;
247 }
248 for (size_t i = 0; i < ns->len; i++) {
249 if (strcmp(ns->items[i].path, path) == 0) {
250 return &ns->items[i];
251 }
252 }
253 return NULL;
254}
255
256const tobi_ns_ent *tobi_ns_find_method(const tobi_ns *ns, const char *name) {
257 if (!ns || !name) {
258 return NULL;
259 }
260 for (size_t i = 0; i < ns->len; i++) {
261 const tobi_ns_ent *e = &ns->items[i];
262 if (e->kind == TOBI_NS_METHOD && strcmp(e->path, name) == 0) {
263 return e;
264 }
265 }
266 for (size_t i = 0; i < ns->len; i++) {
267 const tobi_ns_ent *e = &ns->items[i];
268 if (e->kind == TOBI_NS_METHOD && strcmp(e->name, name) == 0) {
269 return e;
270 }
271 }
272 const char *leaf = strrchr(name, '.');
273 leaf = leaf ? leaf + 1 : name;
274 for (size_t i = 0; i < ns->len; i++) {
275 const tobi_ns_ent *e = &ns->items[i];
276 if (e->kind == TOBI_NS_METHOD && strcmp(e->name, leaf) == 0) {
277 return e;
278 }
279 }
280 return NULL;
281}
282
283static char *ns_parent(const char *scope) {
284 if (!scope || strcmp(scope, "\\") == 0 || scope[0] == '\0') {
285 return NULL;
286 }
287 const char *dot = strrchr(scope, '.');
288 if (!dot) {
289 return tobi_xstrdup("\\");
290 }
291 if (dot == scope || (scope[0] == '\\' && dot == scope + 1)) {
292 return tobi_xstrdup("\\");
293 }
294 return tobi_xstrndup(scope, (size_t)(dot - scope));
295}
296
297static char *ns_join(const char *scope, const char *name) {
298 tobi_sb sb;
299 tobi_sb_init(&sb);
300 if (!scope || scope[0] == '\0' || strcmp(scope, "\\") == 0) {
301 tobi_sb_add(&sb, "\\");
302 tobi_sb_add(&sb, name);
303 } else {
304 tobi_sb_add(&sb, scope);
305 tobi_sb_ch(&sb, '.');
306 tobi_sb_add(&sb, name);
307 }
308 return tobi_sb_take(&sb);
309}
310
311static const tobi_ns_ent *ns_follow_alias(const tobi_ns *ns, const tobi_ns_ent *ent) {
312 const tobi_ns_ent *cur = ent;
313 for (unsigned i = 0; cur && cur->kind == TOBI_NS_ALIAS && cur->target && i < 16; i++) {
314 const tobi_ns_ent *next = tobi_ns_find_const(ns, cur->target);
315 if (!next || next == cur) {
316 break;
317 }
318 cur = next;
319 }
320 return cur;
321}
322
323const tobi_ns_ent *tobi_ns_lookup(const tobi_ns *ns, const char *scope, const char *name) {
324 if (!ns || !name || name[0] == '\0') {
325 return NULL;
326 }
327 if (name[0] == '\\' || name[0] == '^' || strcmp(name, "<null>") == 0) {
328 char *path = tobi_nm_resolve(scope, name);
329 const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, path));
330 free(path);
331 return ent;
332 }
333 char *cur = tobi_xstrdup(scope && scope[0] ? scope : "\\");
334 for (;;) {
335 char *candidate = ns_join(cur, name);
336 const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, candidate));
337 free(candidate);
338 if (ent) {
339 free(cur);
340 return ent;
341 }
342 char *parent = ns_parent(cur);
343 free(cur);
344 if (!parent) {
345 break;
346 }
347 cur = parent;
348 }
349 char *root = ns_join("\\", name);
350 const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, root));
351 free(root);
352 return ent;
353}
354
355char *tobi_ns_resolve_path(const tobi_ns *ns, const char *scope, const char *name) {
356 const tobi_ns_ent *ent = tobi_ns_lookup(ns, scope, name);
357 if (ent) {
358 return tobi_xstrdup(ent->path);
359 }
360 return tobi_nm_resolve(scope, name);
361}
362
363tobi_ns_ent *tobi_ns_add(tobi_ns *ns, tobi_ns_kind kind, const char *path, const char *owner,
364 size_t off, unsigned args, unsigned flags, int external,
365 const char *target, int *duplicate) {
366 if (duplicate) {
367 *duplicate = 0;
368 }
369 if (!ns || !path || path[0] == '\0') {
370 return NULL;
371 }
372 tobi_ns_ent *old = tobi_ns_find(ns, path);
373 if (old) {
374 int old_is_external = old->external || old->kind == TOBI_NS_EXTERNAL;
375 int same_scope = old->kind == TOBI_NS_SCOPE && kind == TOBI_NS_SCOPE;
376 int same_decl = old->kind == kind && old->off == off;
377 if (duplicate && !same_decl && !same_scope && !(old_is_external && !external)) {
378 *duplicate = 1;
379 }
380 if (old_is_external && !external) {
381 old->kind = kind;
382 old->external = 0;
383 old->off = off;
384 old->args = args;
385 } else if (kind == TOBI_NS_METHOD && old->kind == TOBI_NS_METHOD) {
386 old->args = args;
387 }
388 old->flags = flags;
389 if (target) {
390 free(old->target);
391 old->target = tobi_xstrdup(target);
392 }
393 return old;
394 }
395 if (ns->len == ns->cap) {
396 ns->cap = tobi_xgrow_cap(ns->cap, ns->len + 1u, 16u);
397 ns->items = tobi_xrealloc(ns->items, tobi_xmul_size(ns->cap, sizeof(ns->items[0])));
398 }
399 tobi_ns_ent *e = &ns->items[ns->len++];
400 memset(e, 0, sizeof(*e));
401 e->path = tobi_xstrdup(path);
402 e->name = tobi_xstrdup(tobi_ns_leaf(path));
403 e->owner = tobi_xstrdup(owner && owner[0] ? owner : "\\");
404 e->target = target ? tobi_xstrdup(target) : NULL;
405 e->kind = kind;
406 e->off = off;
407 e->args = args;
408 e->flags = flags;
409 e->external = external;
410 return e;
411}
412
413void tobi_ns_ent_set_source(tobi_ns_ent *e, const char *source, size_t input_index) {
414 if (!e || !source) {
415 return;
416 }
417 free(e->source);
418 e->source = tobi_xstrdup(source);
419 e->input_index = input_index;
420 e->has_source = 1;
421}
422
423void tobi_ns_set_source(tobi_ns *ns, const char *source, size_t input_index) {
424 if (!ns || !source) {
425 return;
426 }
427 for (size_t i = 0; i < ns->len; i++) {
428 tobi_ns_ent_set_source(&ns->items[i], source, input_index);
429 }
430}