Refactor switch generation
This commit is contained in:
parent
cd9ecafb4f
commit
6fef1c5444
8 changed files with 265 additions and 106 deletions
|
@ -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<PcListReader>(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;
|
||||
}
|
||||
|
|
|
@ -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<LookupEntry>& 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<PcListReader> pc_list;
|
||||
|
||||
NamingScheme naming_scheme;
|
||||
|
||||
std::unique_ptr<AbstractSwitchCompilerFactory> switch_factory;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,8 @@ OBJS=\
|
|||
SimpleDwarfFilter.o \
|
||||
PcHoleFiller.o \
|
||||
ConseqEquivFilter.o \
|
||||
SwitchStatement.o \
|
||||
NativeSwitchCompiler.o \
|
||||
settings.o \
|
||||
main.o
|
||||
|
||||
|
|
29
src/NativeSwitchCompiler.cpp
Normal file
29
src/NativeSwitchCompiler.cpp
Normal file
|
@ -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--;
|
||||
}
|
10
src/NativeSwitchCompiler.hpp
Normal file
10
src/NativeSwitchCompiler.hpp
Normal file
|
@ -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);
|
||||
};
|
49
src/SwitchStatement.cpp
Normal file
49
src/SwitchStatement.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#include "SwitchStatement.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
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() {}
|
58
src/SwitchStatement.hpp
Normal file
58
src/SwitchStatement.hpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
/** Contains an abstract switch statement, which can be turned to C code later
|
||||
* on. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
|
||||
struct SwitchStatement {
|
||||
struct SwitchCase {
|
||||
uintptr_t low_bound, high_bound;
|
||||
std::string code;
|
||||
};
|
||||
|
||||
std::string switch_var;
|
||||
std::string default_case;
|
||||
std::vector<SwitchCase> 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<AbstractSwitchCompiler> operator()(
|
||||
const SwitchStatement& sw,
|
||||
int indent=0) = 0;
|
||||
};
|
||||
|
||||
template<class Compiler>
|
||||
class SwitchCompilerFactory : public AbstractSwitchCompilerFactory {
|
||||
public:
|
||||
virtual std::shared_ptr<AbstractSwitchCompiler> operator()(
|
||||
const SwitchStatement& sw,
|
||||
int indent=0)
|
||||
{
|
||||
return std::shared_ptr<AbstractSwitchCompiler>(
|
||||
new Compiler(sw, indent));
|
||||
}
|
||||
};
|
|
@ -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<NativeSwitchCompiler>());
|
||||
code_gen.generate();
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue