Refactor switch generation

This commit is contained in:
Théophile Bastian 2018-06-25 11:33:36 +02:00
parent cd9ecafb4f
commit 6fef1c5444
8 changed files with 265 additions and 106 deletions

View File

@ -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;
}

View File

@ -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;
};

View File

@ -12,6 +12,8 @@ OBJS=\
SimpleDwarfFilter.o \
PcHoleFiller.o \
ConseqEquivFilter.o \
SwitchStatement.o \
NativeSwitchCompiler.o \
settings.o \
main.o

View 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--;
}

View 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
View 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
View 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));
}
};

View File

@ -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;