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 / cf.c
5.5 kB 163 lines
1#include "cf.h" 2 3#include "str.h" 4 5#include <stdio.h> 6#include <string.h> 7 8static int has_cfg_note(const tobi_ir *n, size_t off) { 9 for (size_t i = 0; i < n->child_len; i++) { 10 const tobi_ir *c = n->child[i]; 11 if (c->kind == TOBI_IR_DIAG && c->off == off && c->str && strstr(c->str, "cfg fallback") != NULL) { 12 return 1; 13 } 14 } 15 return 0; 16} 17 18static int unclear_control(const tobi_ir *n) { 19 if (n->kind != TOBI_IR_UNKNOWN) { 20 return 0; 21 } 22 return n->raw_op == 0xa1u || n->raw_op == 0xccu || n->raw_op == 0x5b31u || n->raw_op == 0x5b32u || n->raw_op == 0u; 23} 24 25static void add_cfg_note(tobi_ir *parent, const tobi_ir *unknown) { 26 if (!parent || !unknown || has_cfg_note(parent, unknown->off)) { 27 return; 28 } 29 char msg[128]; 30 (void)snprintf(msg, sizeof(msg), "cfg fallback label L_%04zx for unclear opcode 0x%x", unknown->off, unknown->raw_op); 31 tobi_ir *note = tobi_ir_new(TOBI_IR_DIAG, unknown->off, unknown->len); 32 note->raw_op = unknown->raw_op; 33 tobi_ir_set_name(note, "cfg"); 34 tobi_ir_set_str(note, msg); 35 tobi_ir_add(parent, note); 36} 37 38static void walk(tobi_ir *n, tobi_diag_list *diag, unsigned loop_depth) { 39 if (!n) { 40 return; 41 } 42 if (n->kind == TOBI_IR_IF && n->child_len < 2) { 43 tobi_diag_add(diag, TOBI_DIAG_ERROR, n->off, "if node lacks predicate or body"); 44 } 45 if (n->kind == TOBI_IR_WHILE && n->child_len < 2) { 46 tobi_diag_add(diag, TOBI_DIAG_ERROR, n->off, "while node lacks predicate or body"); 47 } 48 if ((n->kind == TOBI_IR_BREAK || n->kind == TOBI_IR_CONTINUE) && loop_depth == 0) { 49 tobi_diag_add(diag, TOBI_DIAG_WARN, n->off, "%s outside loop kept as explicit statement", 50 n->kind == TOBI_IR_BREAK ? "break" : "continue"); 51 } 52 size_t original_children = n->child_len; 53 unsigned child_loop_depth = loop_depth + (n->kind == TOBI_IR_WHILE ? 1u : 0u); 54 for (size_t i = 0; i < original_children; i++) { 55 if ((n->kind == TOBI_IR_BLOCK || n->kind == TOBI_IR_ROOT) && unclear_control(n->child[i])) { 56 add_cfg_note(n, n->child[i]); 57 } 58 walk(n->child[i], diag, child_loop_depth); 59 } 60} 61 62int tobi_cf_recover(tobi_ir *root, tobi_diag_list *diag) { 63 walk(root, diag, 0); 64 tobi_ir_assign_ids(root); 65 return !tobi_diag_has_error(diag); 66} 67 68static void dot_escape(tobi_sb *sb, const char *s) { 69 if (!s) { 70 return; 71 } 72 for (const char *p = s; *p; p++) { 73 if (*p == '"' || *p == '\\') { 74 tobi_sb_ch(sb, '\\'); 75 } 76 if (*p == '\n' || *p == '\r') { 77 tobi_sb_ch(sb, ' '); 78 } else { 79 tobi_sb_ch(sb, *p); 80 } 81 } 82} 83 84static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next); 85 86static void dot_edge(tobi_sb *sb, size_t from, size_t to, const char *label) { 87 tobi_sb_printf(sb, " n%zu -> n%zu", from, to); 88 if (label && label[0]) { 89 tobi_sb_add(sb, " [label=\""); 90 dot_escape(sb, label); 91 tobi_sb_add(sb, "\"]"); 92 } 93 tobi_sb_add(sb, ";\n"); 94} 95 96static void dot_block(tobi_sb *sb, const tobi_ir *b, size_t owner, const char *edge_label, size_t *next) { 97 if (!b) { 98 return; 99 } 100 size_t prev = owner; 101 for (size_t i = 0; i < b->child_len; i++) { 102 size_t id = dot_stmt(sb, b->child[i], next); 103 dot_edge(sb, prev, id, i == 0 ? edge_label : "next"); 104 prev = id; 105 } 106} 107 108static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next) { 109 size_t id = (*next)++; 110 tobi_sb_printf(sb, " n%zu [label=\"%s", id, tobi_ir_kind_name(n->kind)); 111 if (n->name && n->name[0]) { 112 tobi_sb_add(sb, "\\n"); 113 dot_escape(sb, n->name); 114 } else if (n->str && n->str[0]) { 115 tobi_sb_add(sb, "\\n"); 116 dot_escape(sb, n->str); 117 } 118 tobi_sb_printf(sb, "\\noff=0x%zx", n->off); 119 if (n->kind == TOBI_IR_UNKNOWN) { 120 tobi_sb_printf(sb, "\\nop=0x%x", n->raw_op); 121 } 122 tobi_sb_add(sb, "\"];\n"); 123 if (n->kind == TOBI_IR_METHOD && n->child_len > 0) { 124 dot_block(sb, n->child[0], id, "entry", next); 125 } else if (n->kind == TOBI_IR_SCOPE || n->kind == TOBI_IR_DEVICE || n->kind == TOBI_IR_PROCESSOR) { 126 if (n->child_len > 0) { 127 dot_block(sb, n->child[0], id, "body", next); 128 } 129 } else if (n->kind == TOBI_IR_IF) { 130 if (n->child_len > 1) { 131 dot_block(sb, n->child[1], id, "then", next); 132 } 133 if (n->child_len > 2) { 134 dot_block(sb, n->child[2], id, "else", next); 135 } 136 } else if (n->kind == TOBI_IR_WHILE) { 137 if (n->child_len > 1) { 138 dot_block(sb, n->child[1], id, "while-body", next); 139 tobi_sb_printf(sb, " n%zu -> n%zu [label=\"back\"];\n", (*next) - 1u, id); 140 } 141 } 142 return id; 143} 144 145char *tobi_cf_dot(const tobi_ir *root) { 146 tobi_sb sb; 147 tobi_sb_init(&sb); 148 tobi_sb_add(&sb, "digraph tobi_cfg {\n"); 149 tobi_sb_add(&sb, " graph [rankdir=TB];\n"); 150 tobi_sb_add(&sb, " node [shape=box,fontname=\"monospace\"];\n"); 151 if (root) { 152 size_t next = 0; 153 size_t root_id = dot_stmt(&sb, root, &next); 154 size_t prev = root_id; 155 for (size_t i = 0; i < root->child_len; i++) { 156 size_t id = dot_stmt(&sb, root->child[i], &next); 157 dot_edge(&sb, prev, id, i == 0 ? "entry" : "next"); 158 prev = id; 159 } 160 } 161 tobi_sb_add(&sb, "}\n"); 162 return tobi_sb_take(&sb); 163}