ACPI AML decompiler w/ CFG recovery and structured pseudocode
1#include "t.h"
2
3#include <errno.h>
4#include <fcntl.h>
5#include <sys/wait.h>
6#include <unistd.h>
7
8#ifndef TOBI_BIN
9#define TOBI_BIN "./tobi"
10#endif
11#ifndef TOBI_SRC_DIR
12#define TOBI_SRC_DIR "."
13#endif
14
15static char *empty_text(void) {
16 char *buf = malloc(1);
17 if (!buf) {
18 abort();
19 }
20 buf[0] = '\0';
21 return buf;
22}
23
24static int write_blob(const char *path, const unsigned char *data, size_t len) {
25 FILE *fp = fopen(path, "wb");
26 if (!fp) {
27 return 0;
28 }
29 int ok = fwrite(data, 1, len, fp) == len;
30 return fclose(fp) == 0 && ok;
31}
32
33static char *read_text(const char *path) {
34 FILE *fp = fopen(path, "rb");
35 if (!fp) {
36 return empty_text();
37 }
38 if (fseek(fp, 0, SEEK_END) != 0) {
39 fclose(fp);
40 return empty_text();
41 }
42 long n = ftell(fp);
43 if (n < 0) {
44 fclose(fp);
45 return empty_text();
46 }
47 rewind(fp);
48 char *buf = malloc((size_t)n + 1u);
49 if (!buf) {
50 fclose(fp);
51 abort();
52 }
53 size_t got = fread(buf, 1, (size_t)n, fp);
54 buf[got] = '\0';
55 fclose(fp);
56 return buf;
57}
58
59static int write_all_fd(int fd, const unsigned char *data, size_t len) {
60 size_t off = 0;
61 while (off < len) {
62 ssize_t n = write(fd, data + off, len - off);
63 if (n < 0) {
64 if (errno == EINTR) {
65 continue;
66 }
67 return 0;
68 }
69 off += (size_t)n;
70 }
71 return 1;
72}
73
74static char *read_all_fd(int fd) {
75 size_t cap = 4096;
76 size_t len = 0;
77 char *buf = malloc(cap);
78 if (!buf) {
79 abort();
80 }
81 for (;;) {
82 if (len + 1 == cap) {
83 cap *= 2u;
84 char *nb = realloc(buf, cap);
85 if (!nb) {
86 free(buf);
87 abort();
88 }
89 buf = nb;
90 }
91 ssize_t n = read(fd, buf + len, cap - len - 1u);
92 if (n < 0) {
93 if (errno == EINTR) {
94 continue;
95 }
96 free(buf);
97 return empty_text();
98 }
99 if (n == 0) {
100 break;
101 }
102 len += (size_t)n;
103 }
104 buf[len] = '\0';
105 return buf;
106}
107
108static char *run_capture_argv(const char *const argv[], const unsigned char *input, size_t input_len, int *status) {
109 int out_pipe[2];
110 int in_pipe[2] = {-1, -1};
111 if (pipe(out_pipe) != 0) {
112 *status = 99;
113 return empty_text();
114 }
115 if (input && pipe(in_pipe) != 0) {
116 close(out_pipe[0]);
117 close(out_pipe[1]);
118 *status = 99;
119 return empty_text();
120 }
121 pid_t pid = fork();
122 if (pid < 0) {
123 close(out_pipe[0]);
124 close(out_pipe[1]);
125 if (input) {
126 close(in_pipe[0]);
127 close(in_pipe[1]);
128 }
129 *status = 99;
130 return empty_text();
131 }
132 if (pid == 0) {
133 if (input) {
134 dup2(in_pipe[0], STDIN_FILENO);
135 } else {
136 int devnull = open("/dev/null", O_RDONLY);
137 if (devnull >= 0) {
138 dup2(devnull, STDIN_FILENO);
139 close(devnull);
140 }
141 }
142 dup2(out_pipe[1], STDOUT_FILENO);
143 int devnull = open("/dev/null", O_WRONLY);
144 if (devnull >= 0) {
145 dup2(devnull, STDERR_FILENO);
146 close(devnull);
147 }
148 close(out_pipe[0]);
149 close(out_pipe[1]);
150 if (input) {
151 close(in_pipe[0]);
152 close(in_pipe[1]);
153 }
154 execv(argv[0], (char *const *)argv);
155 _exit(127);
156 }
157 close(out_pipe[1]);
158 if (input) {
159 close(in_pipe[0]);
160 if (!write_all_fd(in_pipe[1], input, input_len)) {
161 close(in_pipe[1]);
162 close(out_pipe[0]);
163 (void)waitpid(pid, NULL, 0);
164 *status = 99;
165 return empty_text();
166 }
167 close(in_pipe[1]);
168 }
169 char *buf = read_all_fd(out_pipe[0]);
170 close(out_pipe[0]);
171 int rc = 0;
172 if (waitpid(pid, &rc, 0) < 0) {
173 free(buf);
174 *status = 99;
175 return empty_text();
176 }
177 *status = WIFEXITED(rc) ? WEXITSTATUS(rc) : 99;
178 return buf;
179}
180
181int t_cli(void) {
182 int st = 0;
183 char path[512];
184 snprintf(path, sizeof(path), "%s/sam/m0.aml", TOBI_SRC_DIR);
185 const char *argv_default[] = {TOBI_BIN, path, NULL};
186 char *s = run_capture_argv(argv_default, NULL, 0, &st);
187 T_CHECK(st == 0);
188 T_STR(s, "method MTH0");
189 T_STR(s, "return Local0");
190 free(s);
191 const char *argv_dis[] = {TOBI_BIN, "--dis", path, NULL};
192 s = run_capture_argv(argv_dis, NULL, 0, &st);
193 T_CHECK(st == 0);
194 T_STR(s, "0x0000");
195 T_STR(s, "MethodOp");
196 free(s);
197 const char *argv_json[] = {TOBI_BIN, "--json", path, NULL};
198 s = run_capture_argv(argv_json, NULL, 0, &st);
199 T_CHECK(st == 0);
200 T_STR(s, "\"ir\"");
201 T_STR(s, "\"kind\":\"method\"");
202 T_STR(s, path);
203 T_CHECK(strstr(s, "\"source\":\"raw\"") == NULL);
204 free(s);
205 const char *argv_dot[] = {TOBI_BIN, "--dot", path, NULL};
206 s = run_capture_argv(argv_dot, NULL, 0, &st);
207 T_CHECK(st == 0);
208 T_STR(s, "digraph tobi_cfg");
209 T_STR(s, "method");
210 free(s);
211 const char *argv_ast[] = {TOBI_BIN, "--graph", "ast", path, NULL};
212 s = run_capture_argv(argv_ast, NULL, 0, &st);
213 T_CHECK(st == 0);
214 T_STR(s, "digraph tobi_ast");
215 T_STR(s, "MTH0");
216 free(s);
217 const char *argv_ns[] = {TOBI_BIN, "--graph", "namespace", path, NULL};
218 s = run_capture_argv(argv_ns, NULL, 0, &st);
219 T_CHECK(st == 0);
220 T_STR(s, "digraph tobi_namespace");
221 T_STR(s, "MTH0");
222 free(s);
223 const char *argv_full_labels[] = {TOBI_BIN, "--graph", "ast", "--graph-labels", "full", path, NULL};
224 s = run_capture_argv(argv_full_labels, NULL, 0, &st);
225 T_CHECK(st == 0);
226 T_STR(s, "source=");
227 T_STR(s, "len=");
228 free(s);
229 const char *argv_no_labels[] = {TOBI_BIN, "--dot", "--graph-labels", "none", path, NULL};
230 s = run_capture_argv(argv_no_labels, NULL, 0, &st);
231 T_CHECK(st == 0);
232 T_STR(s, "digraph tobi_cfg");
233 T_CHECK(strstr(s, "MTH0") == NULL);
234 T_CHECK(strstr(s, "off=0x") == NULL);
235 free(s);
236 {
237 const char *out_path = "/tmp/tobi_cli_graph.dot";
238 const char *argv_out[] = {TOBI_BIN, "--dot", path, "-o", out_path, NULL};
239 s = run_capture_argv(argv_out, NULL, 0, &st);
240 T_CHECK(st == 0);
241 T_CHECK(s[0] == '\0');
242 free(s);
243 s = read_text(out_path);
244 T_STR(s, "digraph tobi_cfg");
245 free(s);
246 }
247 {
248 unsigned char stdin_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
249 const char *argv_stdin[] = {TOBI_BIN, "--json", "-", NULL};
250 s = run_capture_argv(argv_stdin, stdin_file, sizeof(stdin_file), &st);
251 T_CHECK(st == 0);
252 T_STR(s, "\"source\":\"<stdin>\"");
253 T_STR(s, "MTH0");
254 free(s);
255 }
256 {
257 unsigned char user_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
258 const char *space_path = "/tmp/tobi cli space.aml";
259 T_CHECK(write_blob(space_path, user_file, sizeof(user_file)));
260 const char *argv_space[] = {TOBI_BIN, "--json", space_path, NULL};
261 s = run_capture_argv(argv_space, NULL, 0, &st);
262 T_CHECK(st == 0);
263 T_STR(s, space_path);
264 free(s);
265 }
266 {
267 unsigned char user_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
268 char long_path[240] = "/tmp/tobi_cli_";
269 size_t pos = strlen(long_path);
270 memset(long_path + pos, 'a', 150);
271 strcpy(long_path + pos + 150, ".aml");
272 T_CHECK(write_blob(long_path, user_file, sizeof(user_file)));
273 const char *argv_long[] = {TOBI_BIN, "--json", long_path, NULL};
274 s = run_capture_argv(argv_long, NULL, 0, &st);
275 T_CHECK(st == 0);
276 T_STR(s, long_path);
277 free(s);
278 }
279 {
280 unsigned char user_file[] = {
281 0x14,0x24,'L','L','0','0',0x01,
282 0x70,0x00,0x60,
283 0x70,0x0a,0x03,0x61,
284 0xa2,0x14,0x61,
285 0xa0,0x06,0x68,0x72,0x60,0x01,0x60,
286 0xa1,0x06,0x72,0x60,0x0a,0x02,0x60,
287 0x74,0x61,0x01,0x61,
288 0xa4,0x60
289 };
290 const char *user_path = "/tmp/tobi_cli_user_dot.aml";
291 T_CHECK(write_blob(user_path, user_file, sizeof(user_file)));
292 const char *argv_user_dot[] = {TOBI_BIN, "--dot", user_path, NULL};
293 s = run_capture_argv(argv_user_dot, NULL, 0, &st);
294 T_CHECK(st == 0);
295 T_STR(s, "digraph tobi_cfg");
296 T_STR(s, "LL00");
297 T_STR(s, "while");
298 T_STR(s, "back");
299 free(s);
300 }
301 const char *argv_raw[] = {TOBI_BIN, "--raw", path, NULL};
302 s = run_capture_argv(argv_raw, NULL, 0, &st);
303 T_CHECK(st == 0);
304 T_STR(s, "checksum_sum=0x00");
305 T_STR(s, "checksum_valid=false");
306 free(s);
307 {
308 unsigned char use_file[] = {0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05};
309 unsigned char cal_file[] = {0x14,0x08,'C','A','L','0',0x01,0xa4,0x68};
310 const char *use_path = "/tmp/tobi_cli_use.aml";
311 const char *cal_path = "/tmp/tobi_cli_cal.aml";
312 T_CHECK(write_blob(use_path, use_file, sizeof(use_file)));
313 T_CHECK(write_blob(cal_path, cal_file, sizeof(cal_file)));
314 const char *argv_multi[] = {TOBI_BIN, use_path, cal_path, NULL};
315 s = run_capture_argv(argv_multi, NULL, 0, &st);
316 T_CHECK(st == 0);
317 T_STR(s, "method USE0");
318 T_STR(s, "CAL0(0x5)");
319 free(s);
320 }
321 const char *argv_help[] = {TOBI_BIN, "--help", NULL};
322 s = run_capture_argv(argv_help, NULL, 0, &st);
323 T_CHECK(st == 0);
324 T_STR(s, "usage: tobi");
325 free(s);
326 const char *argv_version[] = {TOBI_BIN, "--version", NULL};
327 s = run_capture_argv(argv_version, NULL, 0, &st);
328 T_CHECK(st == 0);
329 T_STR(s, "tobi 0.1.0");
330 free(s);
331 const char *argv_conflict[] = {TOBI_BIN, "--json", "--dis", path, NULL};
332 s = run_capture_argv(argv_conflict, NULL, 0, &st);
333 T_CHECK(st == 2);
334 free(s);
335 const char *argv_bad_opt[] = {TOBI_BIN, "--definitely-not-real", NULL};
336 s = run_capture_argv(argv_bad_opt, NULL, 0, &st);
337 T_CHECK(st == 2);
338 free(s);
339 const char *argv_bad_graph[] = {TOBI_BIN, "--graph", "nope", path, NULL};
340 s = run_capture_argv(argv_bad_graph, NULL, 0, &st);
341 T_CHECK(st == 2);
342 free(s);
343 const char *argv_bad_graph_labels[] = {TOBI_BIN, "--graph-labels", "verbose", path, NULL};
344 s = run_capture_argv(argv_bad_graph_labels, NULL, 0, &st);
345 T_CHECK(st == 2);
346 free(s);
347 const char *argv_missing_graph[] = {TOBI_BIN, "--graph", NULL};
348 s = run_capture_argv(argv_missing_graph, NULL, 0, &st);
349 T_CHECK(st == 2);
350 free(s);
351 snprintf(path, sizeof(path), "%s/sam/bad0.aml", TOBI_SRC_DIR);
352 const char *argv_strict[] = {TOBI_BIN, "--strict", path, NULL};
353 s = run_capture_argv(argv_strict, NULL, 0, &st);
354 T_CHECK(st != 0);
355 free(s);
356 return 0;
357}