ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

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

Merge pull request #4 from 23384/dev

Expand AML semantic provenance field layout and corpus regression coverage

+561 -26
+2 -2
CMakeLists.txt
··· 41 41 add_executable(tobi_test 42 42 test/main.c test/t_rd.c test/t_nm.c test/t_p.c test/t_ir.c 43 43 test/t_cf.c test/t_da.c test/t_dc.c test/t_js.c test/t_bad.c test/t_cli.c 44 - test/t_prod.c) 44 + test/t_prod.c test/t_corpus.c) 45 45 target_link_libraries(tobi_test PRIVATE tobilib) 46 46 target_compile_options(tobi_test PRIVATE ${TOBI_WARN}) 47 47 target_compile_definitions(tobi_test PRIVATE ··· 52 52 target_link_options(tobi_test PRIVATE ${SAN_FLAGS}) 53 53 endif() 54 54 55 - foreach(t rd nm p ir cf da dc js bad cli prod) 55 + foreach(t rd nm p ir cf da dc js bad cli prod corpus) 56 56 add_test(NAME ${t} COMMAND tobi_test ${t}) 57 57 endforeach() 58 58
+2 -1
src/dc.c
··· 258 258 break; 259 259 case TOBI_IR_OPREGION: 260 260 indent(sb, ind); 261 - tobi_sb_printf(sb, "opregion %s space=%llu offset=", n->path ? n->path : n->name, (unsigned long long)n->value); 261 + tobi_sb_printf(sb, "opregion %s space=%s(%llu) offset=", n->path ? n->path : n->name, 262 + n->str ? n->str : "unknown", (unsigned long long)n->value); 262 263 expr(sb, n->child_len > 0 ? n->child[0] : NULL); 263 264 tobi_sb_add(sb, " length="); 264 265 expr(sb, n->child_len > 1 ? n->child[1] : NULL);
+16 -2
src/js.c
··· 112 112 tobi_sb_init(&sb); 113 113 tobi_sb_add(&sb, "{\"meta\":{"); 114 114 tobi_sb_printf(&sb, "\"is_table\":%s,", res->meta.is_table ? "true" : "false"); 115 + tobi_sb_add(&sb, "\"source\":"); 116 + tobi_json_string(&sb, res->meta.source); 117 + tobi_sb_add(&sb, ","); 115 118 tobi_sb_add(&sb, "\"signature\":"); 116 119 tobi_json_string(&sb, res->meta.signature); 117 120 tobi_sb_printf(&sb, ",\"table_length\":%zu,\"aml_offset\":%zu,\"aml_length\":%zu," 118 - "\"checksum_sum\":%u,\"checksum_valid\":%s},", 121 + "\"revision\":%u,\"checksum_byte\":%u,\"checksum_sum\":%u," 122 + "\"checksum_valid\":%s,\"header_hash\":%u,\"aml_hash\":%u,", 119 123 res->meta.table_len, res->meta.aml_off, res->meta.aml_len, 120 - (unsigned)res->meta.checksum_sum, res->meta.checksum_valid ? "true" : "false"); 124 + (unsigned)res->meta.revision, (unsigned)res->meta.checksum_byte, 125 + (unsigned)res->meta.checksum_sum, res->meta.checksum_valid ? "true" : "false", 126 + res->meta.header_hash, res->meta.aml_hash); 127 + tobi_sb_add(&sb, "\"oem_id\":"); 128 + tobi_json_string(&sb, res->meta.oem_id); 129 + tobi_sb_add(&sb, ",\"oem_table_id\":"); 130 + tobi_json_string(&sb, res->meta.oem_table_id); 131 + tobi_sb_printf(&sb, ",\"oem_revision\":%u,", res->meta.oem_revision); 132 + tobi_sb_add(&sb, "\"creator_id\":"); 133 + tobi_json_string(&sb, res->meta.creator_id); 134 + tobi_sb_printf(&sb, ",\"creator_revision\":%u},", res->meta.creator_revision); 121 135 tobi_sb_add(&sb, "\"diagnostics\":["); 122 136 for (size_t i = 0; i < res->diag.len; i++) { 123 137 if (i) {
+22 -10
src/main.c
··· 63 63 } 64 64 } 65 65 66 + static void set_source(tobi_input_meta *meta, const char *path) { 67 + if (!path) { 68 + return; 69 + } 70 + (void)snprintf(meta->source, sizeof(meta->source), "%s", path); 71 + } 72 + 73 + static void print_raw_meta(const char *input, const tobi_parse_result *res) { 74 + 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", 75 + input, res->meta.source, res->meta.signature, res->meta.is_table ? "true" : "false", 76 + res->meta.table_len, res->meta.aml_off, res->meta.aml_len, 77 + (unsigned)res->meta.revision, (unsigned)res->meta.checksum_byte, 78 + (unsigned)res->meta.checksum_sum, res->meta.checksum_valid ? "true" : "false", 79 + res->meta.oem_id, res->meta.oem_table_id, res->meta.oem_revision, 80 + res->meta.creator_id, res->meta.creator_revision, res->meta.header_hash, 81 + res->meta.aml_hash, res->ns.len); 82 + } 83 + 66 84 int main(int argc, char **argv) { 67 85 int mode_dis = 0; 68 86 int mode_json = 0; ··· 134 152 for (size_t i = 0; i < file_len; i++) { 135 153 tobi_parse_result one; 136 154 int ok = tobi_parse(data[i], lens[i], strict, &one); 155 + set_source(&one.meta, files[i]); 137 156 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); 157 + print_raw_meta(files[i], &one); 143 158 } else { 144 159 if (file_len > 1) { 145 160 printf("== %s ==\n", files[i]); ··· 166 181 int ok = 0; 167 182 if (file_len == 1) { 168 183 ok = tobi_parse(data[0], lens[0], strict, &res); 184 + set_source(&res.meta, files[0]); 169 185 } else { 170 186 tobi_parse_input *inputs = tobi_xcalloc(file_len, sizeof(inputs[0])); 171 187 for (size_t i = 0; i < file_len; i++) { ··· 189 205 } 190 206 (void)tobi_cf_recover(res.root, &res.diag); 191 207 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); 208 + print_raw_meta(files[0], &res); 197 209 } else if (mode_json) { 198 210 char *s = tobi_js_emit(&res); 199 211 fputs(s, stdout);
+3
src/nm.h
··· 28 28 char *target; 29 29 tobi_ns_kind kind; 30 30 size_t off; 31 + uint64_t region_offset; 32 + uint64_t region_len; 31 33 unsigned args; 32 34 unsigned flags; 35 + int has_region_range; 33 36 int external; 34 37 } tobi_ns_ent; 35 38
+309 -8
src/p.c
··· 43 43 return (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24); 44 44 } 45 45 46 + static void copy_trim(char *dst, size_t dst_len, const uint8_t *src, size_t src_len) { 47 + size_t n = src_len; 48 + while (n > 0 && src[n - 1u] == ' ') { 49 + n--; 50 + } 51 + if (n >= dst_len) { 52 + n = dst_len - 1u; 53 + } 54 + memcpy(dst, src, n); 55 + dst[n] = '\0'; 56 + } 57 + 58 + static uint32_t hash32(const uint8_t *data, size_t len) { 59 + uint32_t h = 2166136261u; 60 + for (size_t i = 0; i < len; i++) { 61 + h ^= data[i]; 62 + h *= 16777619u; 63 + } 64 + return h; 65 + } 66 + 46 67 static int is_table_sig(const uint8_t *d) { 47 68 return memcmp(d, "DSDT", 4) == 0 || memcmp(d, "SSDT", 4) == 0; 48 69 } ··· 68 89 } 69 90 } 70 91 92 + static const char *region_space_name(unsigned space) { 93 + switch (space) { 94 + case 0x00: return "SystemMemory"; 95 + case 0x01: return "SystemIO"; 96 + case 0x02: return "PCI_Config"; 97 + case 0x03: return "EmbeddedControl"; 98 + case 0x04: return "SMBus"; 99 + case 0x05: return "SystemCMOS"; 100 + case 0x06: return "PCIBarTarget"; 101 + case 0x07: return "IPMI"; 102 + case 0x08: return "GPIO"; 103 + case 0x09: return "GenericSerialBus"; 104 + case 0x0a: return "PCC"; 105 + default: return "OEM"; 106 + } 107 + } 108 + 71 109 static void ns_declare_path(parser *p, tobi_ns_kind kind, const char *path, size_t off, 72 110 unsigned args, unsigned flags, int external, const char *target) { 73 111 if (!p->ns || !path || path[0] == '\0') { ··· 81 119 } 82 120 } 83 121 122 + static void ns_set_region_range(parser *p, const char *path, uint64_t off, uint64_t len) { 123 + tobi_ns_ent *e = tobi_ns_find(p->ns, path); 124 + if (!e) { 125 + return; 126 + } 127 + e->region_offset = off; 128 + e->region_len = len; 129 + e->has_region_range = 1; 130 + } 131 + 84 132 static void ns_declare_name(parser *p, tobi_ns_kind kind, const char *name, size_t off, 85 133 unsigned args, unsigned flags, int external, const char *target) { 86 134 char *path = tobi_nm_resolve(p->scope, name); ··· 102 150 meta->aml_len = len; 103 151 memcpy(meta->signature, "RAW", 4); 104 152 meta->signature[4] = '\0'; 153 + memcpy(meta->source, "raw", 4); 105 154 if (len >= 4 && is_table_sig(data)) { 106 155 memcpy(meta->signature, data, 4); 107 156 meta->signature[4] = '\0'; ··· 116 165 tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 4, "invalid ACPI table length %u for file size %zu", table_len, len); 117 166 return !strict; 118 167 } 168 + meta->revision = data[8]; 169 + meta->checksum_byte = data[9]; 170 + copy_trim(meta->oem_id, sizeof(meta->oem_id), data + 10, 6); 171 + copy_trim(meta->oem_table_id, sizeof(meta->oem_table_id), data + 16, 8); 172 + meta->oem_revision = le32_at(data + 24); 173 + copy_trim(meta->creator_id, sizeof(meta->creator_id), data + 28, 4); 174 + meta->creator_revision = le32_at(data + 32); 175 + meta->header_hash = hash32(data, 36); 119 176 uint8_t sum = table_sum8(data, table_len); 120 177 meta->checksum_sum = sum; 121 178 meta->checksum_valid = sum == 0; ··· 128 185 } 129 186 meta->aml_off = 36; 130 187 meta->aml_len = (size_t)table_len - 36; 188 + } 189 + if (meta->aml_off <= len && meta->aml_len <= len - meta->aml_off) { 190 + meta->aml_hash = hash32(data + meta->aml_off, meta->aml_len); 131 191 } 132 192 return 1; 133 193 } ··· 332 392 e->update_rule = (flags >> 5) & 0x03u; 333 393 } 334 394 335 - static void parse_field_list(parser *p, tobi_rd *body, tobi_ir *n, uint8_t flags) { 395 + static char *field_target_meta(parser *p, const char *prefix, const char *path) { 396 + const tobi_ns_ent *e = tobi_ns_find_const(p->ns, path); 397 + tobi_sb sb; 398 + tobi_sb_init(&sb); 399 + tobi_sb_add(&sb, prefix); 400 + tobi_sb_add(&sb, path); 401 + if (e && e->kind == TOBI_NS_OPREGION) { 402 + tobi_sb_printf(&sb, " space=%s", region_space_name(e->flags)); 403 + if (e->has_region_range) { 404 + tobi_sb_printf(&sb, " byte_offset=0x%llx byte_length=0x%llx", 405 + (unsigned long long)e->region_offset, 406 + (unsigned long long)e->region_len); 407 + } 408 + } 409 + return tobi_sb_take(&sb); 410 + } 411 + 412 + static void parse_field_list(parser *p, tobi_rd *body, tobi_ir *n, uint8_t flags, 413 + const tobi_ns_ent *region) { 336 414 uint64_t bit_pos = 0; 337 415 while (tobi_rd_left(body) > 0) { 338 416 size_t foff = tobi_rd_off(body); ··· 407 485 tobi_ir_set_name(e, fname); 408 486 e->value = bits; 409 487 apply_field_meta(e, bit_pos, bits, flags); 488 + if (n->target) { 489 + tobi_sb target; 490 + tobi_sb_init(&target); 491 + tobi_sb_add(&target, n->target); 492 + tobi_sb_printf(&target, " field_bit=%llu field_bytes=%llu", 493 + (unsigned long long)bit_pos, 494 + (unsigned long long)((bits + 7u) / 8u)); 495 + if (region && region->has_region_range && 496 + region->region_offset <= UINT64_MAX / 8u && 497 + bit_pos <= UINT64_MAX - (region->region_offset * 8u)) { 498 + uint64_t abs_bit = (region->region_offset * 8u) + bit_pos; 499 + tobi_sb_printf(&target, " absolute_bit=0x%llx absolute_byte=0x%llx absolute_bytes=0x%llx", 500 + (unsigned long long)abs_bit, 501 + (unsigned long long)(abs_bit / 8u), 502 + (unsigned long long)((bits + 7u) / 8u)); 503 + } 504 + tobi_ir_set_target(e, target.buf); 505 + tobi_sb_free(&target); 506 + } 410 507 ns_declare_name(p, TOBI_NS_FIELD, fname, foff, 0, 0, 0, NULL); 411 508 tobi_ir_add(n, e); 412 509 bit_pos += bits; ··· 430 527 } 431 528 tobi_ir *n = tobi_ir_new(TOBI_IR_FIELD, op_start, whole_len); 432 529 tobi_ir_set_str(n, label); 530 + const tobi_ns_ent *backing_region = NULL; 433 531 if (raw == 0x5b81u) { 434 532 tobi_ir_set_name(n, first); 435 533 char *path = tobi_ns_resolve_path(p->ns, p->scope, first); 436 - tobi_ir_set_target(n, path); 534 + char *target = field_target_meta(p, "region=", path); 535 + tobi_ir_set_target(n, target); 536 + backing_region = tobi_ns_find_const(p->ns, path); 537 + free(target); 437 538 free(path); 438 539 } else if (raw == 0x5b86u) { 439 540 char *data = NULL; ··· 452 553 tobi_sb_init(&target); 453 554 char *idx_path = tobi_ns_resolve_path(p->ns, p->scope, first); 454 555 char *data_path = tobi_ns_resolve_path(p->ns, p->scope, data); 455 - tobi_sb_add(&target, "index="); 456 - tobi_sb_add(&target, idx_path); 556 + char *idx_meta = field_target_meta(p, "index=", idx_path); 557 + tobi_sb_add(&target, idx_meta); 457 558 tobi_sb_add(&target, " data="); 458 559 tobi_sb_add(&target, data_path); 459 560 tobi_ir_set_target(n, target.buf); 561 + free(idx_meta); 460 562 free(idx_path); 461 563 free(data_path); 462 564 tobi_sb_free(&target); ··· 479 581 tobi_sb_init(&target); 480 582 char *region_path = tobi_ns_resolve_path(p->ns, p->scope, first); 481 583 char *bank_path = tobi_ns_resolve_path(p->ns, p->scope, bank); 482 - tobi_sb_add(&target, "region="); 483 - tobi_sb_add(&target, region_path); 584 + char *region_meta = field_target_meta(p, "region=", region_path); 585 + backing_region = tobi_ns_find_const(p->ns, region_path); 586 + tobi_sb_add(&target, region_meta); 484 587 tobi_sb_add(&target, " bank="); 485 588 tobi_sb_add(&target, bank_path); 486 589 tobi_ir_set_target(n, target.buf); 590 + free(region_meta); 487 591 free(region_path); 488 592 free(bank_path); 489 593 tobi_sb_free(&target); ··· 502 606 n->access_type = flags & 0x0fu; 503 607 n->lock_rule = (flags >> 4) & 0x01u; 504 608 n->update_rule = (flags >> 5) & 0x03u; 505 - parse_field_list(p, &body, n, flags); 609 + parse_field_list(p, &body, n, flags, backing_region); 506 610 rd->pos = body_start + body_len; 507 611 free(first); 508 612 return n; ··· 671 775 tobi_ir_set_path(n, path); 672 776 ns_declare_path(p, TOBI_NS_OPREGION, path, op_start, 0, space, 0, NULL); 673 777 n->value = space; 778 + tobi_ir_set_str(n, region_space_name(space)); 674 779 tobi_ir *off = parse_expr(p, rd, depth + 1); 675 780 tobi_ir *len = parse_expr(p, rd, depth + 1); 676 781 if (off) { ··· 678 783 } 679 784 if (len) { 680 785 tobi_ir_add(n, len); 786 + } 787 + if (n->child_len >= 2 && n->child[0]->kind == TOBI_IR_INTEGER && n->child[1]->kind == TOBI_IR_INTEGER) { 788 + ns_set_region_range(p, path, n->child[0]->value, n->child[1]->value); 681 789 } 682 790 n->len = tobi_rd_off(rd) - op_start; 683 791 free(path); ··· 1619 1727 } 1620 1728 } 1621 1729 1622 - static void semantic_validate(parser *p, const tobi_ir *root) { 1730 + typedef struct method_state { 1731 + parser *p; 1732 + tobi_ir *method; 1733 + uint16_t locals; 1734 + uint16_t local_reads; 1735 + uint16_t local_writes; 1736 + uint16_t arg_reads; 1737 + uint16_t arg_writes; 1738 + unsigned warnings; 1739 + } method_state; 1740 + 1741 + static int ref_index(const tobi_ir *n, const char *prefix, unsigned max, unsigned *idx) { 1742 + size_t plen = strlen(prefix); 1743 + if (!n || n->kind != TOBI_IR_REF || !n->name || strncmp(n->name, prefix, plen) != 0) { 1744 + return 0; 1745 + } 1746 + char c = n->name[plen]; 1747 + if (n->name[plen + 1u] != '\0' || c < '0' || c > (char)('0' + max)) { 1748 + return 0; 1749 + } 1750 + *idx = (unsigned)(c - '0'); 1751 + return 1; 1752 + } 1753 + 1754 + static void method_mark_ref(method_state *st, tobi_ir *n, int write_target) { 1755 + unsigned idx = 0; 1756 + if (ref_index(n, "Local", 7, &idx)) { 1757 + if (write_target) { 1758 + st->locals |= (uint16_t)(1u << idx); 1759 + st->local_writes |= (uint16_t)(1u << idx); 1760 + tobi_ir_set_str(n, "method_symbol=local write=initialised"); 1761 + } else if ((st->locals & (uint16_t)(1u << idx)) == 0) { 1762 + st->local_reads |= (uint16_t)(1u << idx); 1763 + st->warnings++; 1764 + sem_diag(st->p, n->off, "Local%u may be read before being initialised in method %s", 1765 + idx, st->method->path ? st->method->path : st->method->name); 1766 + tobi_ir_set_str(n, "method_symbol=local read=uninitialised"); 1767 + } else { 1768 + st->local_reads |= (uint16_t)(1u << idx); 1769 + tobi_ir_set_str(n, "method_symbol=local read=initialised"); 1770 + } 1771 + } else if (ref_index(n, "Arg", 6, &idx)) { 1772 + if (write_target) { 1773 + st->arg_writes |= (uint16_t)(1u << idx); 1774 + } else { 1775 + st->arg_reads |= (uint16_t)(1u << idx); 1776 + } 1777 + if (idx >= st->method->method_args) { 1778 + st->warnings++; 1779 + sem_diag(st->p, n->off, "Arg%u %s exceeds declared argument count %u in method %s", 1780 + idx, write_target ? "write" : "read", 1781 + st->method->method_args, st->method->path ? st->method->path : st->method->name); 1782 + tobi_ir_set_str(n, write_target ? "method_symbol=arg write=out_of_range" : 1783 + "method_symbol=arg read=out_of_range"); 1784 + } else if (write_target) { 1785 + tobi_ir_set_str(n, "method_symbol=arg write=declared"); 1786 + } else { 1787 + tobi_ir_set_str(n, "method_symbol=arg read=declared"); 1788 + } 1789 + } 1790 + } 1791 + 1792 + static void method_symbols_node(method_state *st, tobi_ir *n); 1793 + 1794 + static void method_symbols_block(method_state *st, tobi_ir *b) { 1795 + if (!b) { 1796 + return; 1797 + } 1798 + for (size_t i = 0; i < b->child_len; i++) { 1799 + method_symbols_node(st, b->child[i]); 1800 + } 1801 + } 1802 + 1803 + static void method_symbols_node(method_state *st, tobi_ir *n) { 1804 + if (!n) { 1805 + return; 1806 + } 1807 + if (n->kind == TOBI_IR_STORE) { 1808 + if (n->child_len > 0) { 1809 + method_symbols_node(st, n->child[0]); 1810 + } 1811 + if (n->child_len > 1) { 1812 + unsigned idx = 0; 1813 + if (ref_index(n->child[1], "Local", 7, &idx) || ref_index(n->child[1], "Arg", 6, &idx)) { 1814 + method_mark_ref(st, n->child[1], 1); 1815 + } else { 1816 + method_symbols_node(st, n->child[1]); 1817 + } 1818 + } 1819 + return; 1820 + } 1821 + if (n->kind == TOBI_IR_REF) { 1822 + method_mark_ref(st, n, 0); 1823 + return; 1824 + } 1825 + if (n->kind == TOBI_IR_EXPR && n->name) { 1826 + size_t target_start = n->child_len; 1827 + if (strcmp(n->name, "div") == 0 && n->child_len >= 4) { 1828 + target_start = 2; 1829 + } else if ((strcmp(n->name, "add") == 0 || strcmp(n->name, "concat") == 0 || 1830 + strcmp(n->name, "sub") == 0 || strcmp(n->name, "mul") == 0 || 1831 + strcmp(n->name, "shl") == 0 || strcmp(n->name, "shr") == 0 || 1832 + strcmp(n->name, "and") == 0 || strcmp(n->name, "nand") == 0 || 1833 + strcmp(n->name, "or") == 0 || strcmp(n->name, "nor") == 0 || 1834 + strcmp(n->name, "xor") == 0 || strcmp(n->name, "not") == 0 || 1835 + strcmp(n->name, "find_set_left_bit") == 0 || 1836 + strcmp(n->name, "find_set_right_bit") == 0 || 1837 + strcmp(n->name, "concat_res") == 0 || strcmp(n->name, "mod") == 0 || 1838 + strcmp(n->name, "index") == 0 || strcmp(n->name, "to_buffer") == 0 || 1839 + strcmp(n->name, "to_decimal_string") == 0 || 1840 + strcmp(n->name, "to_hex_string") == 0 || strcmp(n->name, "to_integer") == 0 || 1841 + strcmp(n->name, "to_string") == 0 || strcmp(n->name, "copy_object") == 0 || 1842 + strcmp(n->name, "mid") == 0) && n->child_len >= 3) { 1843 + target_start = n->child_len - 1u; 1844 + } 1845 + for (size_t i = 0; i < target_start; i++) { 1846 + method_symbols_node(st, n->child[i]); 1847 + } 1848 + for (size_t i = target_start; i < n->child_len; i++) { 1849 + unsigned idx = 0; 1850 + if (ref_index(n->child[i], "Local", 7, &idx) || ref_index(n->child[i], "Arg", 6, &idx)) { 1851 + method_mark_ref(st, n->child[i], 1); 1852 + } else { 1853 + method_symbols_node(st, n->child[i]); 1854 + } 1855 + } 1856 + return; 1857 + } 1858 + if (n->kind == TOBI_IR_IF) { 1859 + if (n->child_len > 0) { 1860 + method_symbols_node(st, n->child[0]); 1861 + } 1862 + uint16_t base = st->locals; 1863 + uint16_t then_locals = base; 1864 + uint16_t else_locals = base; 1865 + if (n->child_len > 1) { 1866 + st->locals = base; 1867 + method_symbols_block(st, n->child[1]); 1868 + then_locals = st->locals; 1869 + } 1870 + if (n->child_len > 2) { 1871 + st->locals = base; 1872 + method_symbols_block(st, n->child[2]); 1873 + else_locals = st->locals; 1874 + st->locals = then_locals & else_locals; 1875 + } else { 1876 + st->locals = base; 1877 + } 1878 + return; 1879 + } 1880 + if (n->kind == TOBI_IR_WHILE) { 1881 + if (n->child_len > 0) { 1882 + method_symbols_node(st, n->child[0]); 1883 + } 1884 + uint16_t base = st->locals; 1885 + if (n->child_len > 1) { 1886 + st->locals = base; 1887 + method_symbols_block(st, n->child[1]); 1888 + } 1889 + st->locals = base; 1890 + return; 1891 + } 1892 + for (size_t i = 0; i < n->child_len; i++) { 1893 + method_symbols_node(st, n->child[i]); 1894 + } 1895 + } 1896 + 1897 + static void semantic_methods(parser *p, tobi_ir *n) { 1898 + if (!n) { 1899 + return; 1900 + } 1901 + if (n->kind == TOBI_IR_METHOD) { 1902 + method_state st; 1903 + memset(&st, 0, sizeof(st)); 1904 + st.p = p; 1905 + st.method = n; 1906 + if (n->child_len > 0) { 1907 + method_symbols_block(&st, n->child[0]); 1908 + } 1909 + char summary[160]; 1910 + (void)snprintf(summary, sizeof(summary), 1911 + "method_symbols args=%u local_reads=0x%x local_writes=0x%x final_locals=0x%x arg_reads=0x%x arg_writes=0x%x warnings=%u", 1912 + n->method_args, st.local_reads, st.local_writes, st.locals, 1913 + st.arg_reads, st.arg_writes, st.warnings); 1914 + tobi_ir_set_target(n, summary); 1915 + } 1916 + for (size_t i = 0; i < n->child_len; i++) { 1917 + semantic_methods(p, n->child[i]); 1918 + } 1919 + } 1920 + 1921 + static void semantic_validate(parser *p, tobi_ir *root) { 1623 1922 for (size_t i = 0; i < p->ns->len; i++) { 1624 1923 const tobi_ns_ent *e = &p->ns->items[i]; 1625 1924 if (e->external && e->kind == TOBI_NS_METHOD) { ··· 1634 1933 } 1635 1934 } 1636 1935 semantic_walk(p, root, NULL); 1936 + semantic_methods(p, root); 1637 1937 } 1638 1938 1639 1939 int tobi_parse(const uint8_t *data, size_t len, int strict, tobi_parse_result *out) { ··· 1669 1969 tobi_ns_init(&out->ns); 1670 1970 memcpy(out->meta.signature, "MULT", 4); 1671 1971 out->meta.signature[4] = '\0'; 1972 + memcpy(out->meta.source, "multi", 6); 1672 1973 out->root = tobi_ir_new(TOBI_IR_ROOT, 0, 0); 1673 1974 if (!inputs || count == 0) { 1674 1975 tobi_diag_add(&out->diag, TOBI_DIAG_ERROR, 0, "no AML inputs");
+10
src/p.h
··· 11 11 typedef struct tobi_input_meta { 12 12 int is_table; 13 13 char signature[5]; 14 + char source[128]; 15 + char oem_id[7]; 16 + char oem_table_id[9]; 17 + char creator_id[5]; 14 18 size_t table_len; 15 19 size_t aml_off; 16 20 size_t aml_len; 21 + uint32_t oem_revision; 22 + uint32_t creator_revision; 23 + uint32_t header_hash; 24 + uint32_t aml_hash; 25 + uint8_t revision; 26 + uint8_t checksum_byte; 17 27 uint8_t checksum_sum; 18 28 int checksum_valid; 19 29 } tobi_input_meta;
+1 -1
test/main.c
··· 4 4 struct ent { const char *name; int (*fn)(void); } tests[] = { 5 5 {"rd", t_rd}, {"nm", t_nm}, {"p", t_p}, {"ir", t_ir}, {"cf", t_cf}, 6 6 {"da", t_da}, {"dc", t_dc}, {"js", t_js}, {"bad", t_bad}, {"cli", t_cli}, 7 - {"prod", t_prod}, 7 + {"prod", t_prod}, {"corpus", t_corpus}, 8 8 }; 9 9 if (argc == 2) { 10 10 for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+1
test/t.h
··· 19 19 int t_bad(void); 20 20 int t_cli(void); 21 21 int t_prod(void); 22 + int t_corpus(void); 22 23 23 24 #endif
+150
test/t_corpus.c
··· 1 + #include "t.h" 2 + 3 + #include "cf.h" 4 + #include "da.h" 5 + #include "dc.h" 6 + #include "js.h" 7 + #include "p.h" 8 + 9 + #include <dirent.h> 10 + #include <errno.h> 11 + #include <sys/stat.h> 12 + 13 + #ifndef TOBI_SRC_DIR 14 + #define TOBI_SRC_DIR "." 15 + #endif 16 + 17 + static int read_blob(const char *path, uint8_t **data, size_t *len) { 18 + FILE *fp = fopen(path, "rb"); 19 + if (!fp) { 20 + fprintf(stderr, "open %s: %s\n", path, strerror(errno)); 21 + return 0; 22 + } 23 + if (fseek(fp, 0, SEEK_END) != 0) { 24 + fclose(fp); 25 + return 0; 26 + } 27 + long n = ftell(fp); 28 + if (n < 0) { 29 + fclose(fp); 30 + return 0; 31 + } 32 + rewind(fp); 33 + *len = (size_t)n; 34 + *data = malloc(*len ? *len : 1u); 35 + if (!*data) { 36 + fclose(fp); 37 + return 0; 38 + } 39 + if (*len && fread(*data, 1, *len, fp) != *len) { 40 + free(*data); 41 + fclose(fp); 42 + return 0; 43 + } 44 + fclose(fp); 45 + return 1; 46 + } 47 + 48 + static int stable_emit_blob(const char *label, const uint8_t *data, size_t len, int strict_should_pass) { 49 + int ok = 0; 50 + tobi_parse_result a; 51 + tobi_parse_result b; 52 + tobi_parse_result strict; 53 + (void)tobi_parse(data, len, 0, &a); 54 + (void)tobi_parse(data, len, 0, &b); 55 + int strict_ok = tobi_parse(data, len, 1, &strict); 56 + int expected_strict_ok = strict_should_pass && a.diag.len == 0; 57 + (void)tobi_cf_recover(a.root, &a.diag); 58 + (void)tobi_cf_recover(b.root, &b.diag); 59 + char *aj = tobi_js_emit(&a); 60 + char *bj = tobi_js_emit(&b); 61 + char *ad = tobi_dc_emit(a.root, &a.diag); 62 + char *bd = tobi_dc_emit(b.root, &b.diag); 63 + char *aa = tobi_da_emit(data, len, &a.meta); 64 + char *ba = tobi_da_emit(data, len, &b.meta); 65 + ok = aj != NULL && bj != NULL && ad != NULL && bd != NULL && aa != NULL && ba != NULL && 66 + strcmp(aj, bj) == 0 && aj[0] == '{' && strstr(aj, "\"meta\"") != NULL && 67 + strstr(aj, "\"ir\"") != NULL && strstr(aj, ",}") == NULL && strstr(aj, ",]") == NULL && 68 + ad[0] != '\0' && aa[0] != '\0' && strcmp(ad, bd) == 0 && strcmp(aa, ba) == 0; 69 + if (expected_strict_ok) { 70 + ok = ok && strict_ok && !tobi_diag_has_error(&strict.diag); 71 + } else { 72 + ok = ok && (!strict_ok || tobi_diag_has_error(&strict.diag)); 73 + } 74 + if (!ok) { 75 + fprintf(stderr, "corpus regression failed for %s\n", label); 76 + } 77 + free(aa); 78 + free(ba); 79 + free(ad); 80 + free(bd); 81 + free(aj); 82 + free(bj); 83 + tobi_parse_result_free(&a); 84 + tobi_parse_result_free(&b); 85 + tobi_parse_result_free(&strict); 86 + return ok ? 0 : 1; 87 + } 88 + 89 + static int stable_emit(const char *path, int strict_should_pass) { 90 + uint8_t *data = NULL; 91 + size_t len = 0; 92 + int rc = 1; 93 + if (!read_blob(path, &data, &len)) { 94 + return 1; 95 + } 96 + rc = stable_emit_blob(path, data, len, strict_should_pass); 97 + if (rc == 0 && len > 1) { 98 + size_t cuts[] = {1u, len / 2u, len - 1u}; 99 + for (size_t i = 0; i < sizeof(cuts) / sizeof(cuts[0]); i++) { 100 + if (cuts[i] == 0 || cuts[i] >= len) { 101 + continue; 102 + } 103 + char label[900]; 104 + snprintf(label, sizeof(label), "%s truncated:%zu", path, cuts[i]); 105 + if (stable_emit_blob(label, data, cuts[i], 0) != 0) { 106 + rc = 1; 107 + break; 108 + } 109 + } 110 + } 111 + free(data); 112 + return rc; 113 + } 114 + 115 + static int walk_dir(const char *rel, size_t *count, size_t *bad_count) { 116 + char dir_path[512]; 117 + snprintf(dir_path, sizeof(dir_path), "%s/%s", TOBI_SRC_DIR, rel); 118 + DIR *dir = opendir(dir_path); 119 + T_CHECK(dir != NULL); 120 + struct dirent *de = NULL; 121 + while ((de = readdir(dir)) != NULL) { 122 + if (de->d_name[0] == '.') { 123 + continue; 124 + } 125 + char path[768]; 126 + snprintf(path, sizeof(path), "%s/%s", dir_path, de->d_name); 127 + struct stat st; 128 + T_CHECK(stat(path, &st) == 0); 129 + if (S_ISREG(st.st_mode)) { 130 + int bad = strstr(de->d_name, "bad") != NULL; 131 + T_CHECK(stable_emit(path, !bad) == 0); 132 + (*count)++; 133 + if (bad) { 134 + (*bad_count)++; 135 + } 136 + } 137 + } 138 + T_CHECK(closedir(dir) == 0); 139 + return 0; 140 + } 141 + 142 + int t_corpus(void) { 143 + size_t count = 0; 144 + size_t bad_count = 0; 145 + T_CHECK(walk_dir("sam", &count, &bad_count) == 0); 146 + T_CHECK(walk_dir("fuzz/seed", &count, &bad_count) == 0); 147 + T_CHECK(count >= 9); 148 + T_CHECK(bad_count >= 2); 149 + return 0; 150 + }
+6 -1
test/t_js.c
··· 9 9 T_CHECK(tobi_parse(data, sizeof(data), 0, &r)); 10 10 char *s = tobi_js_emit(&r); 11 11 T_STR(s, "\"kind\":\"root\""); 12 + T_STR(s, "\"source\":\"raw\""); 12 13 T_STR(s, "\"checksum_sum\":0"); 13 14 T_STR(s, "\"checksum_valid\":false"); 15 + T_STR(s, "\"header_hash\":0"); 16 + T_STR(s, "\"aml_hash\":"); 17 + T_STR(s, "\"oem_id\":\"\""); 14 18 T_STR(s, "\"namespace\":["); 15 19 T_STR(s, "\"path\":\"\\\\STR0\""); 16 20 T_STR(s, "\"kind\":\"name\""); ··· 35 39 uint8_t fld[] = {0x5b,0x80,'R','E','G','0',0x01,0x0a,0x10,0x0a,0x04,0x5b,0x81,0x0b,'R','E','G','0',0x00,'F','L','D','0',0x08}; 36 40 T_CHECK(tobi_parse(fld, sizeof(fld), 0, &r)); 37 41 s = tobi_js_emit(&r); 38 - T_STR(s, "\"target\":\"\\\\REG0\""); 42 + T_STR(s, "\"target\":\"region=\\\\REG0 space=SystemIO"); 39 43 T_STR(s, "\"bit_offset\":0"); 40 44 T_STR(s, "\"bit_length\":8"); 41 45 T_STR(s, "\"access_type\":0"); 46 + T_STR(s, "absolute_byte=0x10"); 42 47 T_CHECK(strstr(s, ",}") == NULL); 43 48 T_CHECK(strstr(s, ",]") == NULL); 44 49 free(s);
+39 -1
test/t_p.c
··· 35 35 table[5] = (uint8_t)((table_len >> 8) & 0xffu); 36 36 table[6] = (uint8_t)((table_len >> 16) & 0xffu); 37 37 table[7] = (uint8_t)((table_len >> 24) & 0xffu); 38 + table[8] = 2; 39 + memcpy(table + 10, "TOBI ", 6); 40 + memcpy(table + 16, "TBLTEST ", 8); 41 + table[24] = 7; 42 + memcpy(table + 28, "CODX", 4); 43 + table[32] = 9; 38 44 memcpy(table + 36, m0, sizeof(m0)); 39 45 fix_checksum(table, sizeof(table)); 40 46 T_CHECK(parse_ok(table, sizeof(table), &r)); 41 47 T_CHECK(r.meta.is_table && r.meta.aml_off == 36 && r.meta.aml_len == sizeof(m0)); 42 48 T_CHECK(r.meta.checksum_valid && r.meta.checksum_sum == 0); 49 + T_CHECK(r.meta.revision == 2); 50 + T_CHECK(strcmp(r.meta.oem_id, "TOBI") == 0); 51 + T_CHECK(strcmp(r.meta.oem_table_id, "TBLTEST") == 0); 52 + T_CHECK(r.meta.oem_revision == 7); 53 + T_CHECK(strcmp(r.meta.creator_id, "CODX") == 0); 54 + T_CHECK(r.meta.creator_revision == 9); 55 + T_CHECK(r.meta.header_hash != 0); 56 + T_CHECK(r.meta.aml_hash != 0); 43 57 T_CHECK(r.root->child_len == 1 && r.root->child[0]->kind == TOBI_IR_METHOD); 44 58 T_CHECK(r.root->child[0]->off == 36); 45 59 T_CHECK(r.diag.len == 0); ··· 82 96 uint8_t fld[] = {0x5b,0x80,'R','E','G','0',0x01,0x0a,0x10,0x0a,0x04,0x5b,0x81,0x0b,'R','E','G','0',0x00,'F','L','D','0',0x08}; 83 97 T_CHECK(parse_ok(fld, sizeof(fld), &r)); 84 98 T_CHECK(r.root->child_len == 2); 99 + T_STR(r.root->child[0]->str, "SystemIO"); 85 100 T_CHECK(r.root->child[1]->kind == TOBI_IR_FIELD); 86 101 T_CHECK(r.root->child[1]->child_len == 1); 87 102 T_STR(r.root->child[1]->target, "\\REG0"); 103 + T_STR(r.root->child[1]->target, "space=SystemIO"); 104 + T_STR(r.root->child[1]->target, "byte_offset=0x10"); 88 105 T_CHECK(r.root->child[1]->access_type == 0); 89 106 T_CHECK(tobi_ns_find_const(&r.ns, "\\REG0") != NULL); 90 107 const tobi_ns_ent *fe = tobi_ns_find_const(&r.ns, "\\FLD0"); 91 108 T_CHECK(fe != NULL && fe->kind == TOBI_NS_FIELD); 92 109 T_CHECK(r.root->child[1]->child[0]->bit_off == 0); 93 110 T_CHECK(r.root->child[1]->child[0]->bit_len == 8); 111 + T_STR(r.root->child[1]->child[0]->target, "field_bit=0"); 112 + T_STR(r.root->child[1]->child[0]->target, "absolute_byte=0x10"); 94 113 T_CHECK(r.diag.len == 0); 95 114 tobi_parse_result_free(&r); 96 115 ··· 110 129 T_CHECK(r.root->child_len == 1); 111 130 T_CHECK(r.root->child[0]->kind == TOBI_IR_FIELD); 112 131 T_CHECK(strcmp(r.root->child[0]->str, "BankField") == 0); 113 - T_STR(r.root->child[0]->target, "region=\\REG0 bank=\\BNK0"); 132 + T_STR(r.root->child[0]->target, "region=\\REG0"); 133 + T_STR(r.root->child[0]->target, "bank=\\BNK0"); 114 134 T_CHECK(r.root->child[0]->child_len == 2); 115 135 T_CHECK(r.root->child[0]->child[0]->kind == TOBI_IR_INTEGER); 116 136 T_CHECK(r.root->child[0]->child[1]->kind == TOBI_IR_FIELD_ELEM); ··· 285 305 T_CHECK(tobi_parse(alias_cycle, sizeof(alias_cycle), 0, &r)); 286 306 T_CHECK(r.diag.len >= 1); 287 307 T_STR(r.diag.items[0].msg, "alias cycle"); 308 + tobi_parse_result_free(&r); 309 + 310 + uint8_t symbols[] = { 311 + 0x14,0x0a,'S','Y','M','0',0x01, 312 + 0xa4,0x60,0xa4,0x69 313 + }; 314 + T_CHECK(tobi_parse(symbols, sizeof(symbols), 0, &r)); 315 + T_CHECK(r.diag.len >= 2); 316 + T_STR(r.diag.items[0].msg, "Local0 may be read before"); 317 + T_STR(r.diag.items[1].msg, "Arg1 read exceeds declared argument count"); 318 + T_STR(r.root->child[0]->target, "method_symbols"); 319 + T_STR(r.root->child[0]->target, "local_reads=0x1"); 320 + T_STR(r.root->child[0]->target, "arg_reads=0x2"); 321 + T_STR(r.root->child[0]->target, "warnings=2"); 322 + tobi_ir *sym_ret = r.root->child[0]->child[0]->child[0]; 323 + T_CHECK(sym_ret->kind == TOBI_IR_RETURN); 324 + T_STR(sym_ret->child[0]->str, "uninitialised"); 325 + T_STR(r.root->child[0]->child[0]->child[1]->child[0]->str, "out_of_range"); 288 326 tobi_parse_result_free(&r); 289 327 return 0; 290 328 }