ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

Select the types of activity you want to include in your feed.

tobi / src / main.c
8.7 kB 261 lines
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#include <unistd.h> 13 14#define TOBI_VERSION "0.1.0" 15 16static void usage(FILE *f) { 17 fputs("usage: tobi [opts] <file> [file...]\n" 18 "\n" 19 "options:\n" 20 " --dis print opcode-level disassembly\n" 21 " --json print parsed ir as json\n" 22 " --dot print recovered control flow as Graphviz DOT\n" 23 " --raw print input/table metadata and aml offsets\n" 24 " --strict treat malformed or unsupported constructs as hard errors\n" 25 " --plain disable coloured diagnostics\n" 26 " --help print usage\n" 27 " --version print version\n", f); 28} 29 30static int read_file(const char *path, uint8_t **data, size_t *len) { 31 FILE *fp = fopen(path, "rb"); 32 if (!fp) { 33 fprintf(stderr, "tobi: %s: %s\n", path, strerror(errno)); 34 return 0; 35 } 36 if (fseek(fp, 0, SEEK_END) != 0) { 37 fprintf(stderr, "tobi: %s: seek failed: %s\n", path, strerror(errno)); 38 fclose(fp); 39 return 0; 40 } 41 long n = ftell(fp); 42 if (n < 0) { 43 fprintf(stderr, "tobi: %s: size failed: %s\n", path, strerror(errno)); 44 fclose(fp); 45 return 0; 46 } 47 rewind(fp); 48 *data = tobi_xmalloc((size_t)n ? (size_t)n : 1); 49 *len = (size_t)n; 50 if (*len && fread(*data, 1, *len, fp) != *len) { 51 fprintf(stderr, "tobi: %s: read failed%s%s\n", path, ferror(fp) ? ": " : "", 52 ferror(fp) ? strerror(errno) : ""); 53 fclose(fp); 54 free(*data); 55 *data = NULL; 56 *len = 0; 57 return 0; 58 } 59 if (fclose(fp) != 0) { 60 fprintf(stderr, "tobi: %s: close failed: %s\n", path, strerror(errno)); 61 free(*data); 62 *data = NULL; 63 *len = 0; 64 return 0; 65 } 66 return 1; 67} 68 69static void print_diags(const tobi_diag_list *dl, int plain) { 70 int colour = !plain && isatty(fileno(stderr)); 71 for (size_t i = 0; i < dl->len; i++) { 72 const char *lvl = tobi_diag_level_name(dl->items[i].level); 73 const char *c = dl->items[i].level == TOBI_DIAG_ERROR ? "\033[31m" : "\033[33m"; 74 if (dl->items[i].has_source && dl->items[i].source) { 75 fprintf(stderr, "%s:", dl->items[i].source); 76 } 77 if (colour) { 78 fprintf(stderr, "0x%zx: %s%s\033[0m: %s\n", dl->items[i].off, c, lvl, dl->items[i].msg); 79 } else { 80 fprintf(stderr, "0x%zx: %s: %s\n", dl->items[i].off, lvl, dl->items[i].msg); 81 } 82 } 83} 84 85static void set_source(tobi_input_meta *meta, const char *path) { 86 if (!path) { 87 return; 88 } 89 (void)snprintf(meta->source, sizeof(meta->source), "%s", path); 90} 91 92static void apply_source(tobi_parse_result *res, const char *path, size_t input_index) { 93 set_source(&res->meta, path); 94 if (input_index < res->input_len) { 95 set_source(&res->inputs[input_index], path); 96 } 97 tobi_diag_set_source(&res->diag, path, input_index); 98 tobi_ns_set_source(&res->ns, path, input_index); 99 if (res->root) { 100 tobi_ir_set_source_recursive(res->root, path, input_index); 101 } 102} 103 104static void print_raw_meta(const char *input, const tobi_parse_result *res) { 105 printf("input=%s\nsource=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\nrevision=%u\nchecksum_byte=0x%02x\nchecksum_sum=0x%02x\nchecksum_valid=%s\noem_id=%s\noem_table_id=%s\noem_revision=%u\ncreator_id=%s\ncreator_revision=%u\nheader_hash=0x%08x\naml_hash=0x%08x\nnamespace_objects=%zu\n", 106 input, res->meta.source, res->meta.signature, res->meta.is_table ? "true" : "false", 107 res->meta.table_len, res->meta.aml_off, res->meta.aml_len, 108 (unsigned)res->meta.revision, (unsigned)res->meta.checksum_byte, 109 (unsigned)res->meta.checksum_sum, res->meta.checksum_valid ? "true" : "false", 110 res->meta.oem_id, res->meta.oem_table_id, res->meta.oem_revision, 111 res->meta.creator_id, res->meta.creator_revision, res->meta.header_hash, 112 res->meta.aml_hash, res->ns.len); 113} 114 115int main(int argc, char **argv) { 116 int mode_dis = 0; 117 int mode_json = 0; 118 int mode_dot = 0; 119 int mode_raw = 0; 120 int strict = 0; 121 int plain = 0; 122 const char **files = NULL; 123 size_t file_len = 0; 124 size_t file_cap = 0; 125 for (int i = 1; i < argc; i++) { 126 if (strcmp(argv[i], "--help") == 0) { 127 usage(stdout); 128 return 0; 129 } 130 if (strcmp(argv[i], "--version") == 0) { 131 puts("tobi " TOBI_VERSION); 132 return 0; 133 } 134 if (strcmp(argv[i], "--dis") == 0) { 135 mode_dis = 1; 136 } else if (strcmp(argv[i], "--json") == 0) { 137 mode_json = 1; 138 } else if (strcmp(argv[i], "--dot") == 0) { 139 mode_dot = 1; 140 } else if (strcmp(argv[i], "--raw") == 0) { 141 mode_raw = 1; 142 } else if (strcmp(argv[i], "--strict") == 0) { 143 strict = 1; 144 } else if (strcmp(argv[i], "--plain") == 0) { 145 plain = 1; 146 } else if (argv[i][0] == '-') { 147 fprintf(stderr, "tobi: unknown option %s\n", argv[i]); 148 usage(stderr); 149 return 2; 150 } else { 151 if (file_len == file_cap) { 152 file_cap = tobi_xgrow_cap(file_cap, file_len + 1u, 4u); 153 files = tobi_xrealloc(files, tobi_xmul_size(file_cap, sizeof(files[0]))); 154 } 155 files[file_len++] = argv[i]; 156 } 157 } 158 if (file_len == 0) { 159 usage(stderr); 160 free(files); 161 return 2; 162 } 163 if ((mode_dis + mode_json + mode_dot + mode_raw) > 1) { 164 fprintf(stderr, "tobi: choose only one output mode\n"); 165 free(files); 166 return 2; 167 } 168 uint8_t **data = tobi_xcalloc(file_len, sizeof(data[0])); 169 size_t *lens = tobi_xcalloc(file_len, sizeof(lens[0])); 170 for (size_t i = 0; i < file_len; i++) { 171 if (!read_file(files[i], &data[i], &lens[i])) { 172 for (size_t j = 0; j < file_len; j++) { 173 free(data[j]); 174 } 175 free(lens); 176 free(data); 177 free(files); 178 return 1; 179 } 180 } 181 if (mode_dis || mode_raw) { 182 int fail_any = 0; 183 for (size_t i = 0; i < file_len; i++) { 184 tobi_parse_result one; 185 int ok = tobi_parse(data[i], lens[i], strict, &one); 186 apply_source(&one, files[i], 0); 187 if (mode_raw) { 188 print_raw_meta(files[i], &one); 189 } else { 190 if (file_len > 1) { 191 printf("== %s ==\n", files[i]); 192 } 193 char *s = tobi_da_emit(data[i], lens[i], &one.meta); 194 fputs(s, stdout); 195 free(s); 196 } 197 print_diags(&one.diag, plain); 198 if ((strict && tobi_diag_has_error(&one.diag)) || !ok) { 199 fail_any = 1; 200 } 201 tobi_parse_result_free(&one); 202 } 203 for (size_t i = 0; i < file_len; i++) { 204 free(data[i]); 205 } 206 free(lens); 207 free(data); 208 free(files); 209 return fail_any ? 1 : 0; 210 } 211 tobi_parse_result res; 212 int ok = 0; 213 if (file_len == 1) { 214 ok = tobi_parse(data[0], lens[0], strict, &res); 215 apply_source(&res, files[0], 0); 216 } else { 217 tobi_parse_input *inputs = tobi_xcalloc(file_len, sizeof(inputs[0])); 218 for (size_t i = 0; i < file_len; i++) { 219 inputs[i].data = data[i]; 220 inputs[i].len = lens[i]; 221 inputs[i].name = files[i]; 222 } 223 ok = tobi_parse_multi(inputs, file_len, strict, &res); 224 free(inputs); 225 } 226 if (!res.root) { 227 print_diags(&res.diag, plain); 228 tobi_parse_result_free(&res); 229 for (size_t i = 0; i < file_len; i++) { 230 free(data[i]); 231 } 232 free(lens); 233 free(data); 234 free(files); 235 return 1; 236 } 237 (void)tobi_cf_recover(res.root, &res.diag); 238 if (mode_json) { 239 char *s = tobi_js_emit(&res); 240 fputs(s, stdout); 241 free(s); 242 } else if (mode_dot) { 243 char *s = tobi_cf_dot(res.root); 244 fputs(s, stdout); 245 free(s); 246 } else { 247 char *s = tobi_dc_emit(res.root, &res.diag); 248 fputs(s, stdout); 249 free(s); 250 } 251 print_diags(&res.diag, plain); 252 int fail = (strict && tobi_diag_has_error(&res.diag)) || !ok; 253 tobi_parse_result_free(&res); 254 for (size_t i = 0; i < file_len; i++) { 255 free(data[i]); 256 } 257 free(lens); 258 free(data); 259 free(files); 260 return fail ? 1 : 0; 261}