// Check for JMP or JMPW at end of "if" part void Decompiler::get_else_part(Byte *start, Byte *&if_part_end, bool &has_else, Byte *&else_part_end) { Byte *last_instr = NULL; has_else = false; else_part_end = NULL; for (Byte *instr_scan = start; instr_scan < if_part_end; instr_scan += get_instr_len(*instr_scan)) last_instr = instr_scan; if (last_instr != NULL && (*last_instr == JMP || *last_instr == JMPW)) { has_else = true; else_part_end = if_part_end + last_instr[1]; if (*last_instr == JMPW) else_part_end += (last_instr[2] << 8); if_part_end = last_instr; } }
void Decompiler::decompileRange(Byte *start, Byte *end) { // First, scan for IFFUPJMP, which is used for repeat/until, so // we can recognize the start of such loops. We only keep the // last value to match each address, which represents the outermost // repeat/until loop starting at that point. std::map<Byte *, Byte *> rev_iffupjmp_map; for (Byte *scan = start; end == NULL || scan < end; scan += get_instr_len(*scan)) { if (*scan == IFFUPJMP) rev_iffupjmp_map[scan + 2 - scan[1]] = scan; else if (*scan == IFFUPJMPW) rev_iffupjmp_map[scan + 3 - (scan[1] | (scan[2] << 8))] = scan; else if (*scan == ENDCODE) break; } while (end == NULL || start < end) { int locs_here = local_var_defs->count(start); if (locs_here > 0) { // There were local variable slots just pushed onto the stack // Print them out (in the second pass) // First, if there are multiple defined, it must be from // local x, y, z = f() or local a, b. So just ignore the extra // entries. for (int i = 1; i < locs_here; i++) { delete stk->top(); stk->pop(); } Expression *def = stk->top(); stk->pop(); // Print the local variable names, and at the same time push // fake values onto the stack *os << indent_str << "local "; for (int i = 0; i < locs_here; i++) { std::string locname = localname(tf, tf->code[1] + stk->size()); *os << locname; if (i + 1 < locs_here) *os << ", "; stk->push(new VarExpr(start, "<" + locname + " stack slot>")); } // Print the definition, unless it's nil VarExpr *v = dynamic_cast<VarExpr *>(def); if (v == NULL || v->name != "nil") *os << " = " << *def; *os << std::endl; delete def; local_var_defs->erase(start); } if (rev_iffupjmp_map.find(start) != rev_iffupjmp_map.end()) { // aha, do a repeat/until loop *os << indent_str << "repeat\n"; Decompiler indented_dc = *this; indented_dc.indent_str += std::string(4, ' '); indented_dc.break_pos = rev_iffupjmp_map[start]; indented_dc.break_pos += get_instr_len(*indented_dc.break_pos); indented_dc.decompileRange(start, rev_iffupjmp_map[start]); Expression *e = stk->top(); stk->pop(); *os << indent_str << "until " << *e << std::endl; delete e; start = indented_dc.break_pos; continue; } Byte opc = *start++; int aux; switch (opc) { case ENDCODE: return; case PUSHNIL: aux = *start++; goto pushnil; case PUSHNIL0: aux = 0; pushnil: for (int i = 0; i <= aux; i++) stk->push(new VarExpr(start, "nil")); // Cheat a little :) break; case PUSHNUMBER: aux = *start++; goto pushnumber; case PUSHNUMBER0: case PUSHNUMBER1: case PUSHNUMBER2: aux = opc - PUSHNUMBER0; goto pushnumber; case PUSHNUMBERW: aux = start[0] | (start[1] << 8); start += 2; pushnumber: stk->push(new NumberExpr(start, aux)); break; case PUSHCONSTANT: aux = *start++; goto pushconst; case PUSHCONSTANT0: case PUSHCONSTANT1: case PUSHCONSTANT2: case PUSHCONSTANT3: case PUSHCONSTANT4: case PUSHCONSTANT5: case PUSHCONSTANT6: case PUSHCONSTANT7: aux = opc - PUSHCONSTANT0; goto pushconst; case PUSHCONSTANTW: aux = start[0] | (start[1] << 8); start += 2; pushconst: switch (ttype(tf->consts + aux)) { case LUA_T_STRING: stk->push(new StringExpr(start, tsvalue(tf->consts + aux))); break; case LUA_T_NUMBER: stk->push(new NumberExpr(start, nvalue(tf->consts + aux))); break; case LUA_T_PROTO: stk->push(new FuncExpr(start, tfvalue(tf->consts + aux), indent_str)); break; default: *os << indent_str << "error: invalid constant type " << int(ttype(tf->consts + aux)) << std::endl; } break; case PUSHUPVALUE: aux = *start++; goto pushupvalue; case PUSHUPVALUE0: case PUSHUPVALUE1: aux = opc - PUSHUPVALUE0; pushupvalue: { if (aux >= num_upvals) { *os << indent_str << "error: invalid upvalue #" << aux << std::endl; } std::ostringstream s; s << "%" << *upvals[aux]; stk->push(new VarExpr(start, s.str())); } break; case PUSHLOCAL: aux = *start++; goto pushlocal; case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2: case PUSHLOCAL3: case PUSHLOCAL4: case PUSHLOCAL5: case PUSHLOCAL6: case PUSHLOCAL7: aux = opc - PUSHLOCAL0; pushlocal: stk->push(new VarExpr(start, localname(tf, aux))); break; case GETGLOBAL: aux = *start++; goto getglobal; case GETGLOBAL0: case GETGLOBAL1: case GETGLOBAL2: case GETGLOBAL3: case GETGLOBAL4: case GETGLOBAL5: case GETGLOBAL6: case GETGLOBAL7: aux = opc - GETGLOBAL0; goto getglobal; case GETGLOBALW: aux = start[0] | (start[1] << 8); start += 2; getglobal: stk->push(new VarExpr(start, svalue(tf->consts + aux))); break; case GETTABLE: { Expression *index = stk->top(); stk->pop(); Expression *table = stk->top(); stk->pop(); stk->push(new BracketsIndexExpr(start, table, index)); } break; case GETDOTTED: aux = *start++; goto getdotted; case GETDOTTED0: case GETDOTTED1: case GETDOTTED2: case GETDOTTED3: case GETDOTTED4: case GETDOTTED5: case GETDOTTED6: case GETDOTTED7: aux = opc - GETDOTTED0; goto getdotted; case GETDOTTEDW: aux = start[0] | (start[1] << 8); start += 2; getdotted: { Expression *tbl = stk->top(); stk->pop(); stk->push(new DotIndexExpr(start, tbl, new StringExpr (start, tsvalue(tf->consts + aux)))); } break; case PUSHSELF: aux = *start++; goto pushself; case PUSHSELF0: case PUSHSELF1: case PUSHSELF2: case PUSHSELF3: case PUSHSELF4: case PUSHSELF5: case PUSHSELF6: case PUSHSELF7: aux = opc - PUSHSELF0; goto pushself; case PUSHSELFW: aux = start[0] | (start[1] << 8); start += 2; pushself: { Expression *tbl = stk->top(); stk->pop(); stk->push(new SelfExpr(start, tbl, new StringExpr (start, tsvalue(tf->consts + aux)))); stk->push(new VarExpr(start, "<self>")); // Fake value, FuncCallExpr will handle it } break; case CREATEARRAY: start++; goto createarray; case CREATEARRAY0: case CREATEARRAY1: goto createarray; case CREATEARRAYW: start += 2; createarray: stk->push(new ArrayExpr(start)); break; case SETLOCAL: case SETLOCAL0: case SETLOCAL1: case SETLOCAL2: case SETLOCAL3: case SETLOCAL4: case SETLOCAL5: case SETLOCAL6: case SETLOCAL7: case SETGLOBAL: case SETGLOBAL0: case SETGLOBAL1: case SETGLOBAL2: case SETGLOBAL3: case SETGLOBAL4: case SETGLOBAL5: case SETGLOBAL6: case SETGLOBAL7: case SETGLOBALW: case SETTABLE0: case SETTABLE: start--; do_multi_assign(start); break; case SETLIST: start++; // assume offset is correct goto setlist; case SETLISTW: start += 2; case SETLIST0: setlist: aux = *start++; { ArrayExpr::mapping_list new_mappings; for (int i = 0; i < aux; i++) { Expression *val = stk->top(); stk->pop(); new_mappings.push_front(std::make_pair((Expression *) NULL, val)); } ArrayExpr *a = dynamic_cast<ArrayExpr *>(stk->top()); if (a == NULL) { *os << indent_str << "error: attempt to setlist a non-array object\n"; } // Append the new list a->mappings.splice(a->mappings.end(), new_mappings); a->pos = start; } break; case SETMAP: aux = *start++; goto setmap; case SETMAP0: aux = 0; setmap: { ArrayExpr::mapping_list new_mappings; for (int i = 0; i <= aux; i++) { Expression *val = stk->top(); stk->pop(); Expression *key = stk->top(); stk->pop(); new_mappings.push_front(std::make_pair(key, val)); } ArrayExpr *a = dynamic_cast<ArrayExpr *>(stk->top()); if (a == NULL) { *os << indent_str << "error: attempt to setmap a non-array object\n"; } // Append the new list a->mappings.splice(a->mappings.end(), new_mappings); a->pos = start; } break; case EQOP: do_binary_op(start, 1, false, " == "); break; case NEQOP: do_binary_op(start, 1, false, " ~= "); break; case LTOP: do_binary_op(start, 1, false, " < "); break; case LEOP: do_binary_op(start, 1, false, " <= "); break; case GTOP: do_binary_op(start, 1, false, " > "); break; case GEOP: do_binary_op(start, 1, false, " >= "); break; case ADDOP: do_binary_op(start, 3, false, " + "); break; case SUBOP: do_binary_op(start, 3, false, " - "); break; case MULTOP: do_binary_op(start, 4, false, " * "); break; case DIVOP: do_binary_op(start, 4, false, " / "); break; case POWOP: do_binary_op(start, 6, true, " ^ "); break; case CONCOP: do_binary_op(start, 2, false, " .. "); break; case MINUSOP: do_unary_op(start, 5, "-"); break; case NOTOP: do_unary_op(start, 5, "not "); break; case ONTJMP: aux = *start++; goto ontjmp; case ONTJMPW: aux = start[0] | (start[1] << 8); start += 2; ontjmp: // push_expr_1 ontjmp(label) push_expr_2 label: -> expr_1 || expr_2 decompileRange(start, start + aux); do_binary_op(start + aux, 0, false, " or "); start = start + aux; break; case ONFJMP: aux = *start++; goto onfjmp; case ONFJMPW: aux = start[0] | (start[1] << 8); start += 2; onfjmp: // push_expr_1 onfjmp(label) push_expr_2 label: -> expr_2 && expr_2 decompileRange(start, start + aux); do_binary_op(start + aux, 0, false, " and "); start = start + aux; break; case JMP: aux = *start++; goto jmp; case JMPW: aux = start[0] | (start[1] << 8); start += 2; jmp: { Byte *dest = start + aux; if (dest == break_pos) { *os << indent_str << "break\n"; break; } // otherwise, must be the start of a while statement Byte *while_cond_end; for (while_cond_end = dest; end == NULL || while_cond_end < end; while_cond_end += get_instr_len(*while_cond_end)) if (*while_cond_end == IFTUPJMP || *while_cond_end == IFTUPJMPW) break; if (end != NULL && while_cond_end >= end) { *os << indent_str << "error: JMP not in break, while, if/else\n"; } // push the while condition onto the stack decompileRange(dest, while_cond_end); *os << indent_str << "while " << *stk->top() << " do\n"; delete stk->top(); stk->pop(); // decompile the while body Decompiler indented_dc = *this; indented_dc.indent_str += std::string(4, ' '); indented_dc.break_pos = while_cond_end + get_instr_len(*while_cond_end); indented_dc.decompileRange(start, dest); *os << indent_str << "end\n"; start = indented_dc.break_pos; } break; case IFFJMP: aux = *start++; goto iffjmp; case IFFJMPW: aux = start[0] | (start[1] << 8); start += 2; iffjmp: { // Output an if/end, if/else/end, if/elseif/else/end, ... statement Byte *if_part_end = start + aux; Decompiler indented_dc = *this; indented_dc.indent_str += std::string(4, ' '); *os << indent_str << "if " << *stk->top(); delete stk->top(); stk->pop(); *os << " then\n"; bool has_else; Byte *else_part_end; get_else_part(start, if_part_end, has_else, else_part_end); // Output the if part output_if: indented_dc.decompileRange(start, if_part_end); start = start + aux; if (has_else) { // Check whether the entire else part is a single // if or if/else statement Byte *instr_scan = start; while (is_expr_opc(*instr_scan) && (end == NULL || instr_scan < else_part_end)) instr_scan += get_instr_len(*instr_scan); if ((end == NULL || instr_scan < else_part_end) && (*instr_scan == IFFJMP || *instr_scan == IFFJMPW)) { // OK, first line will be if, check if it will go all // the way through Byte *new_start, *new_if_part_end, *new_else_part_end; bool new_has_else; if (*instr_scan == IFFJMP) { aux = instr_scan[1]; new_start = instr_scan + 2; } else { aux = instr_scan[1] | (instr_scan[2] << 8); new_start = instr_scan + 3; } new_if_part_end = new_start + aux; get_else_part(new_start, new_if_part_end, new_has_else, new_else_part_end); if (new_if_part_end == else_part_end || (new_has_else && new_else_part_end == else_part_end)) { // Yes, output an elseif decompileRange(start, instr_scan); // push condition *os << indent_str << "elseif " << *stk->top() << " then\n"; delete stk->top(); stk->pop(); start = new_start; if_part_end = new_if_part_end; has_else = new_has_else; else_part_end = new_else_part_end; goto output_if; } } *os << indent_str << "else\n"; indented_dc.decompileRange(start, else_part_end); start = else_part_end; } *os << indent_str << "end\n"; } break; case CLOSURE: aux = *start++; goto closure; case CLOSURE0: case CLOSURE1: aux = opc - CLOSURE0; closure: { FuncExpr *f = dynamic_cast<FuncExpr *>(stk->top()); if (f == NULL) { *os << indent_str << "error: closure requires a function\n"; } stk->pop(); f->num_upvals = aux; f->upvals = new Expression*[aux]; for (int i = aux - 1; i >= 0; i--) { f->upvals[i] = stk->top(); stk->pop(); } stk->push(f); } break; case CALLFUNC: aux = *start++; goto callfunc; case CALLFUNC0: case CALLFUNC1: aux = opc - CALLFUNC0; callfunc: { int num_args = *start++; FuncCallExpr *e = new FuncCallExpr(start); e->num_args = num_args; e->args = new Expression*[num_args]; for (int i = num_args - 1; i >= 0; i--) { e->args[i] = stk->top(); stk->pop(); } e->func = stk->top(); stk->pop(); if (aux == 0) { *os << indent_str << *e << std::endl; delete e; } else if (aux == 1 || aux == 255) // 255 for return f() stk->push(e); else { stk->push(e); for (int i = 1; i < aux; i++) stk->push(new VarExpr(start, "<extra result>")); } } break; case RETCODE: { int num_rets = stk->size() + tf->code[1] - *start++; ExprStack rets; for (int i = 0; i < num_rets; i++) { rets.push(stk->top()); stk->pop(); } *os << indent_str << "return"; for (int i = 0; i < num_rets; i++) { *os << " " << *rets.top(); delete rets.top(); rets.pop(); if (i + 1 < num_rets) *os << ","; } *os << std::endl; } break; case SETLINE: aux = *start++; goto setline; case SETLINEW: aux = start[0] | (start[1] << 8); start += 2; setline: break; // ignore line info case POP: aux = *start++; goto pop; case POP0: case POP1: aux = opc - POP0; pop: for (int i = 0; i <= aux; i++) { local_var_defs->insert(stk->top()->pos); delete stk->top(); stk->pop(); } break; //Nop default: break; } } }
void cpu_exec(volatile uint32_t n) { if(nemu_state == END) { printf("Program execution has ended. To restart the program, exit NEMU and run again.\n"); return; } nemu_state = RUNNING; #ifdef DEBUG volatile uint32_t n_temp = n; #endif setjmp(jbuf); for(; n > 0; n --) { #ifdef DEBUG swaddr_t eip_temp = cpu.eip; if((n & 0xffff) == 0) { /* Output some dots while executing the program. */ fputc('.', stderr); } #endif /* Execute one instruction, including instruction fetch, * instruction decode, and the actual execution. */ int instr_len = exec(cpu.eip); //printf("instr_len is %x in cpu-exec.c\n",instr_len); cpu.eip += instr_len; si_count++; #ifdef DEBUG #endif print_bin_instr(eip_temp, instr_len); strcat(asm_buf, assembly); Log_write("%s\n", asm_buf); if(n_temp < MAX_INSTR_TO_PRINT) { printf("%s\n", asm_buf); } /* TODO: check watchpoints here. */ if(check_watchpoints()){ n=1; nemu_state = STOP; } if(nemu_state != RUNNING) { printf("Cache cost = %llu, L1 miss rate = %f, L2 miss rate = %f\n",(unsigned long long)(L2_get_cache_cost()+L1_get_cache_cost()), (double)L1_cache_miss/(double)L1_cache_access,(double)L2_cache_miss/(double)L2_cache_access); printf("Instruction executed: %llu\n",(long long unsigned)si_count); return; } //printf("cpu.eip is %x in rear cpu-exec.c\n",cpu.eip); //PA 4.4 if(cpu.INTR & eflags.eflags.IF) { //printf("cpu.INTR triggered\n"); uint32_t intr_no = i8259_query_intr(); i8259_ack_intr(); int len = get_instr_len(); cpu.eip --; //TODO: ?! raise_intr(intr_no, len); } } if(nemu_state == RUNNING) { nemu_state = STOP; } }