#include "CodeGenerator.hpp" #include "gen_context_struct.hpp" #include "settings.hpp" #include "../shared/context_struct.h" #include #include #include #include using namespace std; class UnhandledRegister: public std::exception {}; static const char* PRELUDE = "#include \n" "\n" ; struct UnwFlags { UnwFlags(): error(false), rip(false), rsp(false), rbp(false), rbx(false) {} bool error, rip, rsp, rbp, rbx; uint8_t to_uint8() const { uint8_t out = 0; if(rip) out |= (1 << UNWF_RIP); if(rsp) out |= (1 << UNWF_RSP); if(rbp) out |= (1 << UNWF_RBP); if(rbx) out |= (1 << UNWF_RBX); if(error) out |= (1 << UNWF_ERROR); return out; } }; CodeGenerator::CodeGenerator( const SimpleDwarf& dwarf, std::ostream& os, NamingScheme naming_scheme, AbstractSwitchCompiler* sw_compiler) : dwarf(dwarf), os(os), pc_list(nullptr), naming_scheme(naming_scheme), switch_compiler(sw_compiler) { if(!settings::pc_list.empty()) { pc_list = make_unique(settings::pc_list); pc_list->read(); } } void CodeGenerator::generate() { gen_of_dwarf(); } SwitchStatement CodeGenerator::gen_fresh_switch() const { SwitchStatement out; out.switch_var = "pc"; ostringstream default_oss; UnwFlags flags; flags.error = true; default_oss << "out_ctx.flags = " << (int) flags.to_uint8() << "u;\n" << "return out_ctx;\n"; out.default_case = default_oss.str(); return out; } void CodeGenerator::switch_append_fde( SwitchStatement& sw, const SimpleDwarf::Fde& fde) const { for(size_t fde_row_id=0; fde_row_id < fde.rows.size(); ++fde_row_id) { SwitchStatement::SwitchCase sw_case; uintptr_t up_bound = fde.end_ip - 1; if(fde_row_id != fde.rows.size() - 1) up_bound = fde.rows[fde_row_id + 1].ip - 1; sw_case.low_bound = fde.rows[fde_row_id].ip; sw_case.high_bound = up_bound; ostringstream case_oss; gen_of_row_content(fde.rows[fde_row_id], case_oss); sw_case.content.code = case_oss.str(); sw.cases.push_back(sw_case); } } void CodeGenerator::gen_of_dwarf() { os << CONTEXT_STRUCT_STR << '\n' << PRELUDE << '\n' << endl; switch(settings::switch_generation_policy) { case settings::SGP_SwitchPerFunc: { vector lookup_entries; // A function per FDE for(const auto& fde: dwarf.fde_list) { LookupEntry cur_entry; cur_entry.name = naming_scheme(fde); cur_entry.beg = fde.beg_ip; cur_entry.end = fde.end_ip; lookup_entries.push_back(cur_entry); gen_function_of_fde(fde); os << endl; } gen_lookup(lookup_entries); break; } case settings::SGP_GlobalSwitch: { gen_unwind_func_header("_eh_elf"); SwitchStatement sw_stmt = gen_fresh_switch(); for(const auto& fde: dwarf.fde_list) switch_append_fde(sw_stmt, fde); (*switch_compiler)(os, sw_stmt); gen_unwind_func_footer(); break; } } } void CodeGenerator::gen_unwind_func_header(const std::string& name) { string deref_arg; if(settings::enable_deref_arg) deref_arg = ", deref_func_t deref"; os << "unwind_context_t " << name << "(unwind_context_t ctx, uintptr_t pc" << deref_arg << ") {\n" << "\tunwind_context_t out_ctx;" << endl; } void CodeGenerator::gen_unwind_func_footer() { os << "}" << endl; } void CodeGenerator::gen_function_of_fde(const SimpleDwarf::Fde& fde) { gen_unwind_func_header(naming_scheme(fde)); SwitchStatement sw_stmt = gen_fresh_switch(); switch_append_fde(sw_stmt, fde); (*switch_compiler)(os, sw_stmt); gen_unwind_func_footer(); } void CodeGenerator::gen_of_row_content( const SimpleDwarf::DwRow& row, std::ostream& stream) const { UnwFlags flags; try { if(!check_reg_valid(row.ra)) { // RA might be undefined (last frame), but if it is defined and we // don't implement it (eg. EXPR), it is an error flags.error = true; goto write_flags; } if(check_reg_valid(row.cfa)) { flags.rsp = true; stream << "out_ctx.rsp = "; gen_of_reg(row.cfa, stream); stream << ';' << endl; } else { // rsp is required (CFA) flags.error = true; goto write_flags; } if(check_reg_defined(row.rbp)) { flags.rbp = true; stream << "out_ctx.rbp = "; gen_of_reg(row.rbp, stream); stream << ';' << endl; } if(check_reg_defined(row.ra)) { flags.rip = true; stream << "out_ctx.rip = "; gen_of_reg(row.ra, stream); stream << ';' << endl; } if(check_reg_defined(row.rbx)) { flags.rbx = true; stream << "out_ctx.rbx = "; gen_of_reg(row.rbx, stream); stream << ';' << endl; } } catch(const UnhandledRegister& exn) { // This should not happen, since we check_reg_*, but heh. flags.error = true; stream << ";\n"; } write_flags: stream << "out_ctx.flags = " << (int)flags.to_uint8() << "u;" << endl; stream << "return " << "out_ctx" << ";" << endl; } static const char* ctx_of_dw_name(SimpleDwarf::MachineRegister reg) { switch(reg) { case SimpleDwarf::REG_RIP: throw CodeGenerator::NotImplementedCase(); case SimpleDwarf::REG_RSP: return "ctx.rsp"; case SimpleDwarf::REG_RBP: return "ctx.rbp"; case SimpleDwarf::REG_RBX: return "ctx.rbx"; case SimpleDwarf::REG_RA: throw CodeGenerator::NotImplementedCase(); } return ""; } bool CodeGenerator::check_reg_defined( const SimpleDwarf::DwRegister& reg) const { switch(reg.type) { case SimpleDwarf::DwRegister::REG_UNDEFINED: case SimpleDwarf::DwRegister::REG_NOT_IMPLEMENTED: return false; default: return true; } } bool CodeGenerator::check_reg_valid(const SimpleDwarf::DwRegister& reg) const { return reg.type != SimpleDwarf::DwRegister::REG_NOT_IMPLEMENTED; } void CodeGenerator::gen_of_reg(const SimpleDwarf::DwRegister& reg, ostream& stream) const { switch(reg.type) { case SimpleDwarf::DwRegister::REG_UNDEFINED: // This function is not supposed to be called on an undefined // register throw UnhandledRegister(); break; case SimpleDwarf::DwRegister::REG_REGISTER: stream << ctx_of_dw_name(reg.reg) << " + (" << reg.offset << ")"; break; case SimpleDwarf::DwRegister::REG_CFA_OFFSET: { if(settings::enable_deref_arg) { stream << "deref(out_ctx.rsp + (" << reg.offset << "))"; } else { stream << "*((uintptr_t*)(out_ctx.rsp + (" << reg.offset << ")))"; } break; } case SimpleDwarf::DwRegister::REG_PLT_EXPR: { /* if(settings::enable_deref_arg) stream << "(deref("; else stream << "*((uintptr_t*)("; */ stream << "(((ctx.rip & 15) >= 11) ? 8 : 0) + ctx.rsp"; break; } case SimpleDwarf::DwRegister::REG_NOT_IMPLEMENTED: stream << "0"; throw UnhandledRegister(); break; } } void CodeGenerator::gen_lookup(const std::vector& entries) { os << "_fde_func_t _fde_lookup(uintptr_t pc) {\n" << "\tswitch(pc) {" << endl; for(const auto& entry: entries) { os << "\t\tcase " << std::hex << "0x" << entry.beg << " ... 0x" << entry.end - 1 << ":\n" << std::dec << "\t\t\treturn &" << entry.name << ";" << endl; } os << "\t\tdefault: assert(0);\n" << "\t}\n" << "}" << endl; }