ACPI AML decompiler w/ CFG recovery and structured pseudocode
1#include "cf.h"
2#include "da.h"
3#include "dc.h"
4#include "js.h"
5#include "mem.h"
6#include "p.h"
7
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#define TOBI_VERSION "0.1.0"
14
15static void usage(FILE *f) {
16 fputs("usage: tobi [opts] <file> [file...]\n"
17 "\n"
18 "options:\n"
19 " --dis print opcode-level disassembly\n"
20 " --json print parsed ir as json\n"
21 " --dot print recovered control flow as Graphviz DOT\n"
22 " --raw print input/table metadata and aml offsets\n"
23 " --strict treat malformed or unsupported constructs as hard errors\n"
24 " --plain disable coloured diagnostics\n"
25 " --help print usage\n"
26 " --version print version\n", f);
27}
28
29static int read_file(const char *path, uint8_t **data, size_t *len) {
30 FILE *fp = fopen(path, "rb");
31 if (!fp) {
32 fprintf(stderr, "tobi: %s: %s\n", path, strerror(errno));
33 return 0;
34 }
35 if (fseek(fp, 0, SEEK_END) != 0) {
36 fclose(fp);
37 return 0;
38 }
39 long n = ftell(fp);
40 if (n < 0) {
41 fclose(fp);
42 return 0;
43 }
44 rewind(fp);
45 *data = tobi_xmalloc((size_t)n ? (size_t)n : 1);
46 *len = (size_t)n;
47 if (*len && fread(*data, 1, *len, fp) != *len) {
48 fclose(fp);
49 free(*data);
50 *data = NULL;
51 *len = 0;
52 return 0;
53 }
54 fclose(fp);
55 return 1;
56}
57
58static void print_diags(const tobi_diag_list *dl, int plain) {
59 (void)plain;
60 for (size_t i = 0; i < dl->len; i++) {
61 fprintf(stderr, "0x%zx: %s: %s\n", dl->items[i].off,
62 tobi_diag_level_name(dl->items[i].level), dl->items[i].msg);
63 }
64}
65
66int main(int argc, char **argv) {
67 int mode_dis = 0;
68 int mode_json = 0;
69 int mode_dot = 0;
70 int mode_raw = 0;
71 int strict = 0;
72 int plain = 0;
73 const char **files = NULL;
74 size_t file_len = 0;
75 size_t file_cap = 0;
76 for (int i = 1; i < argc; i++) {
77 if (strcmp(argv[i], "--help") == 0) {
78 usage(stdout);
79 return 0;
80 }
81 if (strcmp(argv[i], "--version") == 0) {
82 puts("tobi " TOBI_VERSION);
83 return 0;
84 }
85 if (strcmp(argv[i], "--dis") == 0) {
86 mode_dis = 1;
87 } else if (strcmp(argv[i], "--json") == 0) {
88 mode_json = 1;
89 } else if (strcmp(argv[i], "--dot") == 0) {
90 mode_dot = 1;
91 } else if (strcmp(argv[i], "--raw") == 0) {
92 mode_raw = 1;
93 } else if (strcmp(argv[i], "--strict") == 0) {
94 strict = 1;
95 } else if (strcmp(argv[i], "--plain") == 0) {
96 plain = 1;
97 } else if (argv[i][0] == '-') {
98 fprintf(stderr, "tobi: unknown option %s\n", argv[i]);
99 usage(stderr);
100 return 2;
101 } else {
102 if (file_len == file_cap) {
103 file_cap = file_cap ? file_cap * 2u : 4u;
104 files = tobi_xrealloc(files, file_cap * sizeof(files[0]));
105 }
106 files[file_len++] = argv[i];
107 }
108 }
109 if (file_len == 0) {
110 usage(stderr);
111 free(files);
112 return 2;
113 }
114 if ((mode_dis + mode_json + mode_dot + mode_raw) > 1) {
115 fprintf(stderr, "tobi: choose only one output mode\n");
116 free(files);
117 return 2;
118 }
119 uint8_t **data = tobi_xcalloc(file_len, sizeof(data[0]));
120 size_t *lens = tobi_xcalloc(file_len, sizeof(lens[0]));
121 for (size_t i = 0; i < file_len; i++) {
122 if (!read_file(files[i], &data[i], &lens[i])) {
123 for (size_t j = 0; j < file_len; j++) {
124 free(data[j]);
125 }
126 free(lens);
127 free(data);
128 free(files);
129 return 1;
130 }
131 }
132 if (mode_dis || mode_raw) {
133 int fail_any = 0;
134 for (size_t i = 0; i < file_len; i++) {
135 tobi_parse_result one;
136 int ok = tobi_parse(data[i], lens[i], strict, &one);
137 if (mode_raw) {
138 printf("input=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\nchecksum_sum=0x%02x\nchecksum_valid=%s\nnamespace_objects=%zu\n",
139 files[i], one.meta.signature, one.meta.is_table ? "true" : "false",
140 one.meta.table_len, one.meta.aml_off, one.meta.aml_len,
141 (unsigned)one.meta.checksum_sum, one.meta.checksum_valid ? "true" : "false",
142 one.ns.len);
143 } else {
144 if (file_len > 1) {
145 printf("== %s ==\n", files[i]);
146 }
147 char *s = tobi_da_emit(data[i], lens[i], &one.meta);
148 fputs(s, stdout);
149 free(s);
150 }
151 print_diags(&one.diag, plain);
152 if ((strict && tobi_diag_has_error(&one.diag)) || !ok) {
153 fail_any = 1;
154 }
155 tobi_parse_result_free(&one);
156 }
157 for (size_t i = 0; i < file_len; i++) {
158 free(data[i]);
159 }
160 free(lens);
161 free(data);
162 free(files);
163 return fail_any ? 1 : 0;
164 }
165 tobi_parse_result res;
166 int ok = 0;
167 if (file_len == 1) {
168 ok = tobi_parse(data[0], lens[0], strict, &res);
169 } else {
170 tobi_parse_input *inputs = tobi_xcalloc(file_len, sizeof(inputs[0]));
171 for (size_t i = 0; i < file_len; i++) {
172 inputs[i].data = data[i];
173 inputs[i].len = lens[i];
174 inputs[i].name = files[i];
175 }
176 ok = tobi_parse_multi(inputs, file_len, strict, &res);
177 free(inputs);
178 }
179 if (!res.root) {
180 print_diags(&res.diag, plain);
181 tobi_parse_result_free(&res);
182 for (size_t i = 0; i < file_len; i++) {
183 free(data[i]);
184 }
185 free(lens);
186 free(data);
187 free(files);
188 return 1;
189 }
190 (void)tobi_cf_recover(res.root, &res.diag);
191 if (mode_raw) {
192 printf("input=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\nchecksum_sum=0x%02x\nchecksum_valid=%s\nnamespace_objects=%zu\n",
193 files[0], res.meta.signature, res.meta.is_table ? "true" : "false",
194 res.meta.table_len, res.meta.aml_off, res.meta.aml_len,
195 (unsigned)res.meta.checksum_sum, res.meta.checksum_valid ? "true" : "false",
196 res.ns.len);
197 } else if (mode_json) {
198 char *s = tobi_js_emit(&res);
199 fputs(s, stdout);
200 free(s);
201 } else if (mode_dot) {
202 char *s = tobi_cf_dot(res.root);
203 fputs(s, stdout);
204 free(s);
205 } else {
206 char *s = tobi_dc_emit(res.root, &res.diag);
207 fputs(s, stdout);
208 free(s);
209 }
210 print_diags(&res.diag, plain);
211 int fail = (strict && tobi_diag_has_error(&res.diag)) || !ok;
212 tobi_parse_result_free(&res);
213 for (size_t i = 0; i < file_len; i++) {
214 free(data[i]);
215 }
216 free(lens);
217 free(data);
218 free(files);
219 return fail ? 1 : 0;
220}