diff --git a/src/CodeGenerator.cpp b/src/CodeGenerator.cpp index a55a4c0..4c07022 100644 --- a/src/CodeGenerator.cpp +++ b/src/CodeGenerator.cpp @@ -17,11 +17,35 @@ static const char* PRELUDE = "\n" ; +struct UnwFlags { + UnwFlags(): + error(false), rip(false), rsp(false), rbp(false) {} + + bool error, rip, rsp, rbp; + + 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(error) + out |= (1 << UNWF_ERROR); + + return out; + } +}; + + CodeGenerator::CodeGenerator( const SimpleDwarf& dwarf, std::ostream& os, - NamingScheme naming_scheme): - dwarf(dwarf), os(os), pc_list(nullptr), naming_scheme(naming_scheme) + NamingScheme naming_scheme, + AbstractSwitchCompilerFactory* factory) : + dwarf(dwarf), os(os), pc_list(nullptr), + naming_scheme(naming_scheme), switch_factory(factory) { if(!settings::pc_list.empty()) { pc_list = make_unique(settings::pc_list); @@ -33,6 +57,41 @@ 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.code = case_oss.str(); + + sw.cases.push_back(sw_case); + } +} + void CodeGenerator::gen_of_dwarf() { os << CONTEXT_STRUCT_STR << '\n' << PRELUDE << '\n' << endl; @@ -60,9 +119,11 @@ void CodeGenerator::gen_of_dwarf() { case settings::SGP_GlobalSwitch: { gen_unwind_func_header("_eh_elf"); - for(const auto& fde: dwarf.fde_list) { - gen_switchpart_of_fde(fde); - } + SwitchStatement sw_stmt = gen_fresh_switch(); + for(const auto& fde: dwarf.fde_list) + switch_append_fde(sw_stmt, fde); + auto sw_compiler = (*switch_factory)(sw_stmt); + (*sw_compiler)(os); gen_unwind_func_footer(); break; } @@ -77,70 +138,30 @@ void CodeGenerator::gen_unwind_func_header(const std::string& name) { os << "unwind_context_t " << name << "(unwind_context_t ctx, uintptr_t pc" << deref_arg << ") {\n" - << "\tunwind_context_t out_ctx;\n" - << "\tswitch(pc) {" << endl; + << "\tunwind_context_t out_ctx;" << endl; } -struct UnwFlags { - UnwFlags(): - error(false), rip(false), rsp(false), rbp(false) {} - - bool error, rip, rsp, rbp; - - 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(error) - out |= (1 << UNWF_ERROR); - - return out; - } -}; - void CodeGenerator::gen_unwind_func_footer() { - UnwFlags flags; - flags.error = true; - os << "\t\tdefault:\n" - << "\t\t\tout_ctx.flags = " << (int) flags.to_uint8() << "u;\n" - << "\t\t\treturn out_ctx;\n" - << "\t}\n" - << "}" << endl; + os << "}" << endl; } void CodeGenerator::gen_function_of_fde(const SimpleDwarf::Fde& fde) { gen_unwind_func_header(naming_scheme(fde)); - gen_switchpart_of_fde(fde); + SwitchStatement sw_stmt = gen_fresh_switch(); + switch_append_fde(sw_stmt, fde); + auto sw_compiler = (*switch_factory)(sw_stmt); + (*sw_compiler)(os); gen_unwind_func_footer(); } -void CodeGenerator::gen_switchpart_of_fde(const SimpleDwarf::Fde& fde) { - os << "\t\t/********** FDE: 0x" << std::hex << fde.fde_offset - << ", PC = 0x" << fde.beg_ip << std::dec << " */" << std::endl; - for(size_t fde_row_id=0; fde_row_id < fde.rows.size(); ++fde_row_id) - { - 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; - - gen_of_row(fde.rows[fde_row_id], up_bound); - } -} - -void CodeGenerator::gen_of_row( +void CodeGenerator::gen_of_row_content( const SimpleDwarf::DwRow& row, - uintptr_t row_end) + std::ostream& stream) const { - gen_case(row.ip, row_end); UnwFlags flags; - try { if(!check_reg_valid(row.ra)) { // RA might be undefined (last frame), but if it is defined and we @@ -151,9 +172,9 @@ void CodeGenerator::gen_of_row( if(check_reg_valid(row.cfa)) { flags.rsp = true; - os << "\t\t\t" << "out_ctx.rsp = "; - gen_of_reg(row.cfa); - os << ';' << endl; + stream << "out_ctx.rsp = "; + gen_of_reg(row.cfa, stream); + stream << ';' << endl; } else { // rsp is required (CFA) flags.error = true; @@ -162,53 +183,28 @@ void CodeGenerator::gen_of_row( if(check_reg_defined(row.rbp)) { flags.rbp = true; - os << "\t\t\t" << "out_ctx.rbp = "; - gen_of_reg(row.rbp); - os << ';' << endl; + stream << "out_ctx.rbp = "; + gen_of_reg(row.rbp, stream); + stream << ';' << endl; } if(check_reg_defined(row.ra)) { flags.rip = true; - os << "\t\t\t" << "out_ctx.rip = "; - gen_of_reg(row.ra); - os << ';' << endl; + stream << "out_ctx.rip = "; + gen_of_reg(row.ra, stream); + stream << ';' << endl; } } catch(const UnhandledRegister& exn) { // This should not happen, since we check_reg_*, but heh. flags.error = true; - os << ";\n"; + stream << ";\n"; } write_flags: - os << "\t\t\tout_ctx.flags = " << (int)flags.to_uint8() << "u;" << endl; + stream << "out_ctx.flags = " << (int)flags.to_uint8() << "u;" << endl; - os << "\t\t\treturn " << "out_ctx" << ";" << endl; -} - -void CodeGenerator::gen_case(uintptr_t low_bound, uintptr_t high_bound) { - if(pc_list == nullptr) { - os << "\t\tcase " << std::hex << "0x" << low_bound - << " ... 0x" << high_bound << ":" << std::dec << endl; - } - else { - const auto& first_it = lower_bound( - pc_list->get_list().begin(), - pc_list->get_list().end(), - low_bound); - const auto& last_it = upper_bound( - pc_list->get_list().begin(), - pc_list->get_list().end(), - high_bound); - - if(first_it == pc_list->get_list().end()) - throw CodeGenerator::InvalidPcList(); - - os << std::hex; - for(auto it = first_it; it != last_it; ++it) - os << "\t\tcase 0x" << *it << ":\n"; - os << std::dec; - } + stream << "return " << "out_ctx" << ";" << endl; } static const char* ctx_of_dw_name(SimpleDwarf::MachineRegister reg) { @@ -225,7 +221,9 @@ static const char* ctx_of_dw_name(SimpleDwarf::MachineRegister reg) { return ""; } -bool CodeGenerator::check_reg_defined(const SimpleDwarf::DwRegister& reg) { +bool CodeGenerator::check_reg_defined( + const SimpleDwarf::DwRegister& reg) const +{ switch(reg.type) { case SimpleDwarf::DwRegister::REG_UNDEFINED: case SimpleDwarf::DwRegister::REG_NOT_IMPLEMENTED: @@ -234,11 +232,13 @@ bool CodeGenerator::check_reg_defined(const SimpleDwarf::DwRegister& reg) { return true; } } -bool CodeGenerator::check_reg_valid(const SimpleDwarf::DwRegister& reg) { +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) { +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 @@ -246,24 +246,24 @@ void CodeGenerator::gen_of_reg(const SimpleDwarf::DwRegister& reg) { throw UnhandledRegister(); break; case SimpleDwarf::DwRegister::REG_REGISTER: - os << ctx_of_dw_name(reg.reg) + stream << ctx_of_dw_name(reg.reg) << " + (" << reg.offset << ")"; break; case SimpleDwarf::DwRegister::REG_CFA_OFFSET: { if(settings::enable_deref_arg) { - os << "deref(out_ctx.rsp + (" + stream << "deref(out_ctx.rsp + (" << reg.offset << "))"; } else { - os << "*((uintptr_t*)(out_ctx.rsp + (" + stream << "*((uintptr_t*)(out_ctx.rsp + (" << reg.offset << ")))"; } break; } case SimpleDwarf::DwRegister::REG_NOT_IMPLEMENTED: - os << "0"; + stream << "0"; throw UnhandledRegister(); break; } diff --git a/src/CodeGenerator.hpp b/src/CodeGenerator.hpp index a559f9c..9434825 100644 --- a/src/CodeGenerator.hpp +++ b/src/CodeGenerator.hpp @@ -8,6 +8,7 @@ #include "SimpleDwarf.hpp" #include "PcListReader.hpp" +#include "SwitchStatement.hpp" class CodeGenerator { public: @@ -23,7 +24,8 @@ class CodeGenerator { /** Create a CodeGenerator to generate code for the given dwarf, on the * given std::ostream object (eg. cout). */ CodeGenerator(const SimpleDwarf& dwarf, std::ostream& os, - NamingScheme naming_scheme); + NamingScheme naming_scheme, + AbstractSwitchCompilerFactory* factory); /// Actually generate the code on the given stream void generate(); @@ -34,22 +36,26 @@ class CodeGenerator { uintptr_t beg, end; }; + + SwitchStatement gen_fresh_switch() const; + void switch_append_fde( + SwitchStatement& sw, + const SimpleDwarf::Fde& fde) const; void gen_of_dwarf(); void gen_unwind_func_header(const std::string& name); void gen_unwind_func_footer(); void gen_function_of_fde(const SimpleDwarf::Fde& fde); - void gen_switchpart_of_fde(const SimpleDwarf::Fde& fde); - void gen_of_row( + void gen_of_row_content( const SimpleDwarf::DwRow& row, - uintptr_t row_end); - void gen_case(uintptr_t low_bound, uintptr_t high_bound); + std::ostream& stream) const; void gen_of_reg( - const SimpleDwarf::DwRegister& reg); + const SimpleDwarf::DwRegister& reg, + std::ostream& stream) const; void gen_lookup(const std::vector& entries); - bool check_reg_defined(const SimpleDwarf::DwRegister& reg); - bool check_reg_valid(const SimpleDwarf::DwRegister& reg); + bool check_reg_defined(const SimpleDwarf::DwRegister& reg) const; + bool check_reg_valid(const SimpleDwarf::DwRegister& reg) const; private: SimpleDwarf dwarf; @@ -57,4 +63,6 @@ class CodeGenerator { std::unique_ptr pc_list; NamingScheme naming_scheme; + + std::unique_ptr switch_factory; }; diff --git a/src/Makefile b/src/Makefile index 4e52eb8..24e0fb1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,6 +12,8 @@ OBJS=\ SimpleDwarfFilter.o \ PcHoleFiller.o \ ConseqEquivFilter.o \ + SwitchStatement.o \ + NativeSwitchCompiler.o \ settings.o \ main.o diff --git a/src/NativeSwitchCompiler.cpp b/src/NativeSwitchCompiler.cpp new file mode 100644 index 0000000..b27ef3d --- /dev/null +++ b/src/NativeSwitchCompiler.cpp @@ -0,0 +1,29 @@ +#include "NativeSwitchCompiler.hpp" + +using namespace std; + +NativeSwitchCompiler::NativeSwitchCompiler( + const SwitchStatement& sw, int indent): + AbstractSwitchCompiler(sw, indent) +{} + +void NativeSwitchCompiler::to_stream(ostream& os) { + os << indent() << "switch(" << sw.switch_var << ") {\n"; + indent_count++; + + for(const auto& cur_case: sw.cases) { + os << indent() << "case 0x" + << hex << cur_case.low_bound << " ... 0x" << cur_case.high_bound + << dec << ":\n"; + indent_count++; + os << indent_str(cur_case.code); + indent_count--; + } + + os << indent() << "default:\n"; + indent_count++; + os << indent_str(sw.default_case); + indent_count--; + os << indent() << "}\n"; + indent_count--; +} diff --git a/src/NativeSwitchCompiler.hpp b/src/NativeSwitchCompiler.hpp new file mode 100644 index 0000000..bc36544 --- /dev/null +++ b/src/NativeSwitchCompiler.hpp @@ -0,0 +1,10 @@ +/** Compiles a SwitchStatement to a native C switch */ + +#include "SwitchStatement.hpp" + +class NativeSwitchCompiler: public AbstractSwitchCompiler { + public: + NativeSwitchCompiler(const SwitchStatement& sw, int indent=0); + private: + virtual void to_stream(std::ostream& os); +}; diff --git a/src/SwitchStatement.cpp b/src/SwitchStatement.cpp new file mode 100644 index 0000000..97b7a45 --- /dev/null +++ b/src/SwitchStatement.cpp @@ -0,0 +1,49 @@ +#include "SwitchStatement.hpp" + +#include + +using namespace std; + +AbstractSwitchCompiler::AbstractSwitchCompiler( + const SwitchStatement& sw, + int indent) + : sw(sw), indent_count(indent) +{ +} + +void AbstractSwitchCompiler::operator()(ostream& os) { + to_stream(os); +} + +string AbstractSwitchCompiler::operator()() { + ostringstream os; + (*this)(os); + return os.str(); +} + +std::string AbstractSwitchCompiler::indent_str(const std::string& str) { + ostringstream out; + + int last_find = -1; + size_t find_pos; + while((find_pos = str.find('\n', last_find + 1)) != string::npos) { + out << indent() + << str.substr(last_find + 1, find_pos - last_find); // includes \n + last_find = find_pos; + } + if(last_find + 1 < (int)str.size()) { + out << indent() + << str.substr(last_find + 1) + << '\n'; + } + return out.str(); +} + +std::string AbstractSwitchCompiler::indent() const { + return string(indent_count, '\t'); +} +std::string AbstractSwitchCompiler::endcl() const { + return string("\n") + indent(); +} + +AbstractSwitchCompilerFactory::AbstractSwitchCompilerFactory() {} diff --git a/src/SwitchStatement.hpp b/src/SwitchStatement.hpp new file mode 100644 index 0000000..565008f --- /dev/null +++ b/src/SwitchStatement.hpp @@ -0,0 +1,58 @@ +/** Contains an abstract switch statement, which can be turned to C code later + * on. */ + +#pragma once + +#include +#include +#include +#include + +struct SwitchStatement { + struct SwitchCase { + uintptr_t low_bound, high_bound; + std::string code; + }; + + std::string switch_var; + std::string default_case; + std::vector cases; +}; + +class AbstractSwitchCompiler { + public: + AbstractSwitchCompiler(const SwitchStatement& sw, + int indent=0); + void operator()(std::ostream& os); + std::string operator()(); + + protected: + virtual void to_stream(std::ostream& os) = 0; + std::string indent_str(const std::string& str) ; + std::string indent() const; + std::string endcl() const; + + + SwitchStatement sw; + int indent_count; +}; + +class AbstractSwitchCompilerFactory { + public: + AbstractSwitchCompilerFactory(); + virtual std::shared_ptr operator()( + const SwitchStatement& sw, + int indent=0) = 0; +}; + +template +class SwitchCompilerFactory : public AbstractSwitchCompilerFactory { + public: + virtual std::shared_ptr operator()( + const SwitchStatement& sw, + int indent=0) + { + return std::shared_ptr( + new Compiler(sw, indent)); + } +}; diff --git a/src/main.cpp b/src/main.cpp index 4fa824f..7ac3230 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,8 @@ #include "SimpleDwarf.hpp" #include "DwarfReader.hpp" #include "CodeGenerator.hpp" +#include "SwitchStatement.hpp" +#include "NativeSwitchCompiler.hpp" #include "PcHoleFiller.hpp" #include "ConseqEquivFilter.hpp" @@ -106,7 +108,8 @@ int main(int argc, char** argv) { std::ostringstream ss; ss << "_fde_" << fde.beg_ip; return ss.str(); - }); + }, + new SwitchCompilerFactory()); code_gen.generate(); return 0;