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 {
224 const char *out_path = "/tmp/tobi_cli_graph.dot";
225 const char *argv_out[] = {TOBI_BIN, "--dot", path, "-o", out_path, NULL};
226 s = run_capture_argv(argv_out, NULL, 0, &st);
227 T_CHECK(st == 0);
228 T_CHECK(s[0] == '\0');
229 free(s);
230 s = read_text(out_path);
231 T_STR(s, "digraph tobi_cfg");
232 free(s);
233 }
234 {
235 unsigned char stdin_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
236 const char *argv_stdin[] = {TOBI_BIN, "--json", "-", NULL};
237 s = run_capture_argv(argv_stdin, stdin_file, sizeof(stdin_file), &st);
238 T_CHECK(st == 0);
239 T_STR(s, "\"source\":\"<stdin>\"");
240 T_STR(s, "MTH0");
241 free(s);
242 }
243 {
244 unsigned char user_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
245 const char *space_path = "/tmp/tobi cli space.aml";
246 T_CHECK(write_blob(space_path, user_file, sizeof(user_file)));
247 const char *argv_space[] = {TOBI_BIN, "--json", space_path, NULL};
248 s = run_capture_argv(argv_space, NULL, 0, &st);
249 T_CHECK(st == 0);
250 T_STR(s, space_path);
251 free(s);
252 }
253 {
254 unsigned char user_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60};
255 char long_path[240] = "/tmp/tobi_cli_";
256 size_t pos = strlen(long_path);
257 memset(long_path + pos, 'a', 150);
258 strcpy(long_path + pos + 150, ".aml");
259 T_CHECK(write_blob(long_path, user_file, sizeof(user_file)));
260 const char *argv_long[] = {TOBI_BIN, "--json", long_path, NULL};
261 s = run_capture_argv(argv_long, NULL, 0, &st);
262 T_CHECK(st == 0);
263 T_STR(s, long_path);
264 free(s);
265 }
266 {
267 unsigned char user_file[] = {
268 0x14,0x24,'L','L','0','0',0x01,
269 0x70,0x00,0x60,
270 0x70,0x0a,0x03,0x61,
271 0xa2,0x14,0x61,
272 0xa0,0x06,0x68,0x72,0x60,0x01,0x60,
273 0xa1,0x06,0x72,0x60,0x0a,0x02,0x60,
274 0x74,0x61,0x01,0x61,
275 0xa4,0x60
276 };
277 const char *user_path = "/tmp/tobi_cli_user_dot.aml";
278 T_CHECK(write_blob(user_path, user_file, sizeof(user_file)));
279 const char *argv_user_dot[] = {TOBI_BIN, "--dot", user_path, NULL};
280 s = run_capture_argv(argv_user_dot, NULL, 0, &st);
281 T_CHECK(st == 0);
282 T_STR(s, "digraph tobi_cfg");
283 T_STR(s, "LL00");
284 T_STR(s, "while");
285 T_STR(s, "back");
286 free(s);
287 }
288 const char *argv_raw[] = {TOBI_BIN, "--raw", path, NULL};
289 s = run_capture_argv(argv_raw, NULL, 0, &st);
290 T_CHECK(st == 0);
291 T_STR(s, "checksum_sum=0x00");
292 T_STR(s, "checksum_valid=false");
293 free(s);
294 {
295 unsigned char use_file[] = {0x14,0x0d,'U','S','E','0',0x00,0xa4,'C','A','L','0',0x0a,0x05};
296 unsigned char cal_file[] = {0x14,0x08,'C','A','L','0',0x01,0xa4,0x68};
297 const char *use_path = "/tmp/tobi_cli_use.aml";
298 const char *cal_path = "/tmp/tobi_cli_cal.aml";
299 T_CHECK(write_blob(use_path, use_file, sizeof(use_file)));
300 T_CHECK(write_blob(cal_path, cal_file, sizeof(cal_file)));
301 const char *argv_multi[] = {TOBI_BIN, use_path, cal_path, NULL};
302 s = run_capture_argv(argv_multi, NULL, 0, &st);
303 T_CHECK(st == 0);
304 T_STR(s, "method USE0");
305 T_STR(s, "CAL0(0x5)");
306 free(s);
307 }
308 const char *argv_help[] = {TOBI_BIN, "--help", NULL};
309 s = run_capture_argv(argv_help, NULL, 0, &st);
310 T_CHECK(st == 0);
311 T_STR(s, "usage: tobi");
312 free(s);
313 const char *argv_version[] = {TOBI_BIN, "--version", NULL};
314 s = run_capture_argv(argv_version, NULL, 0, &st);
315 T_CHECK(st == 0);
316 T_STR(s, "tobi 0.1.0");
317 free(s);
318 const char *argv_conflict[] = {TOBI_BIN, "--json", "--dis", path, NULL};
319 s = run_capture_argv(argv_conflict, NULL, 0, &st);
320 T_CHECK(st == 2);
321 free(s);
322 const char *argv_bad_opt[] = {TOBI_BIN, "--definitely-not-real", NULL};
323 s = run_capture_argv(argv_bad_opt, NULL, 0, &st);
324 T_CHECK(st == 2);
325 free(s);
326 const char *argv_bad_graph[] = {TOBI_BIN, "--graph", "nope", path, NULL};
327 s = run_capture_argv(argv_bad_graph, NULL, 0, &st);
328 T_CHECK(st == 2);
329 free(s);
330 const char *argv_missing_graph[] = {TOBI_BIN, "--graph", NULL};
331 s = run_capture_argv(argv_missing_graph, NULL, 0, &st);
332 T_CHECK(st == 2);
333 free(s);
334 snprintf(path, sizeof(path), "%s/sam/bad0.aml", TOBI_SRC_DIR);
335 const char *argv_strict[] = {TOBI_BIN, "--strict", path, NULL};
336 s = run_capture_argv(argv_strict, NULL, 0, &st);
337 T_CHECK(st != 0);
338 free(s);
339 return 0;
340}