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.4 kB 162 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 return !tobi_diag_has_error(diag); 65} 66 67static void dot_escape(tobi_sb *sb, const char *s) { 68 if (!s) { 69 return; 70 } 71 for (const char *p = s; *p; p++) { 72 if (*p == '"' || *p == '\\') { 73 tobi_sb_ch(sb, '\\'); 74 } 75 if (*p == '\n' || *p == '\r') { 76 tobi_sb_ch(sb, ' '); 77 } else { 78 tobi_sb_ch(sb, *p); 79 } 80 } 81} 82 83static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next); 84 85static void dot_edge(tobi_sb *sb, size_t from, size_t to, const char *label) { 86 tobi_sb_printf(sb, " n%zu -> n%zu", from, to); 87 if (label && label[0]) { 88 tobi_sb_add(sb, " [label=\""); 89 dot_escape(sb, label); 90 tobi_sb_add(sb, "\"]"); 91 } 92 tobi_sb_add(sb, ";\n"); 93} 94 95static void dot_block(tobi_sb *sb, const tobi_ir *b, size_t owner, const char *edge_label, size_t *next) { 96 if (!b) { 97 return; 98 } 99 size_t prev = owner; 100 for (size_t i = 0; i < b->child_len; i++) { 101 size_t id = dot_stmt(sb, b->child[i], next); 102 dot_edge(sb, prev, id, i == 0 ? edge_label : "next"); 103 prev = id; 104 } 105} 106 107static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next) { 108 size_t id = (*next)++; 109 tobi_sb_printf(sb, " n%zu [label=\"%s", id, tobi_ir_kind_name(n->kind)); 110 if (n->name && n->name[0]) { 111 tobi_sb_add(sb, "\\n"); 112 dot_escape(sb, n->name); 113 } else if (n->str && n->str[0]) { 114 tobi_sb_add(sb, "\\n"); 115 dot_escape(sb, n->str); 116 } 117 tobi_sb_printf(sb, "\\noff=0x%zx", n->off); 118 if (n->kind == TOBI_IR_UNKNOWN) { 119 tobi_sb_printf(sb, "\\nop=0x%x", n->raw_op); 120 } 121 tobi_sb_add(sb, "\"];\n"); 122 if (n->kind == TOBI_IR_METHOD && n->child_len > 0) { 123 dot_block(sb, n->child[0], id, "entry", next); 124 } else if (n->kind == TOBI_IR_SCOPE || n->kind == TOBI_IR_DEVICE || n->kind == TOBI_IR_PROCESSOR) { 125 if (n->child_len > 0) { 126 dot_block(sb, n->child[0], id, "body", next); 127 } 128 } else if (n->kind == TOBI_IR_IF) { 129 if (n->child_len > 1) { 130 dot_block(sb, n->child[1], id, "then", next); 131 } 132 if (n->child_len > 2) { 133 dot_block(sb, n->child[2], id, "else", next); 134 } 135 } else if (n->kind == TOBI_IR_WHILE) { 136 if (n->child_len > 1) { 137 dot_block(sb, n->child[1], id, "while-body", next); 138 tobi_sb_printf(sb, " n%zu -> n%zu [label=\"back\"];\n", (*next) - 1u, id); 139 } 140 } 141 return id; 142} 143 144char *tobi_cf_dot(const tobi_ir *root) { 145 tobi_sb sb; 146 tobi_sb_init(&sb); 147 tobi_sb_add(&sb, "digraph tobi_cfg {\n"); 148 tobi_sb_add(&sb, " graph [rankdir=TB];\n"); 149 tobi_sb_add(&sb, " node [shape=box,fontname=\"monospace\"];\n"); 150 if (root) { 151 size_t next = 0; 152 size_t root_id = dot_stmt(&sb, root, &next); 153 size_t prev = root_id; 154 for (size_t i = 0; i < root->child_len; i++) { 155 size_t id = dot_stmt(&sb, root->child[i], &next); 156 dot_edge(&sb, prev, id, i == 0 ? "entry" : "next"); 157 prev = id; 158 } 159 } 160 tobi_sb_add(&sb, "}\n"); 161 return tobi_sb_take(&sb); 162}