compilation-bundle/dwarf-compilation.base/contrib/libdwarfpp/tests/fde-print/fde-print.cpp

331 lines
11 KiB
C++

#include <fstream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <fileno.hpp>
#include <dwarfpp/lib.hpp>
#include <dwarfpp/expr.hpp>
#include <dwarfpp/frame.hpp>
#include <dwarfpp/regs.hpp>
#include <gelf.h>
using std::cout;
using std::endl;
using std::ostringstream;
using std::setw;
using std::setfill;
using std::map;
using namespace dwarf;
using dwarf::lib::Dwarf_Addr;
using dwarf::lib::dwarf_regnames_for_elf_machine;
using dwarf::core::Fde;
void print_in_readelf_style(std::ostream& s, const core::FrameSection& fs, core::root_die& r);
void print_in_readelf_style(std::ostream& s, const encap::loc_expr& expr);
void print_in_readelf_style(std::ostream& s, const encap::frame_instrlist& instrs, Dwarf_Addr initial_loc);
int elf_machine;
lib::Dwarf_Debug dbg;
int main(int argc, char **argv)
{
cout << "Opening " << argv[1] << "..." << endl;
std::ifstream in(argv[1]);
core::root_die root(fileno(in));
dbg = root.get_dbg().raw_handle();
GElf_Ehdr ehdr;
GElf_Ehdr *ret = gelf_getehdr(root.get_elf(), &ehdr);
assert(ret != 0);
elf_machine = ehdr.e_machine;
core::FrameSection fs(root.get_dbg(), true);
ostringstream s;
print_in_readelf_style(s, fs, root);
string str = s.str();
if (getenv("PRINT_FDE")) cerr << str;
const char *data_start = str.c_str();
const char *data_end = data_start + strlen(data_start);
const char *data_pos = data_start;
// now diff this string against what readelf gives us
FILE *pipein = popen((string("diff -u /dev/stdin /dev/fd/3 3<<END\n$( readelf -wf ") + argv[1] + ")\nEND").c_str(), "w");
assert(pipein);
while (data_pos < data_end)
{
size_t nmemb = data_end - data_pos;
int ret = fwrite(data_pos, 1, nmemb, pipein);
if (ret < nmemb)
{
cerr << "Error! " << strerror(errno);
break;
}
data_pos += ret;
}
// check the status of diff
int status = pclose(pipein);
assert(status == 0);
cout << "Output compares identical to readelf's -- success!" << endl;
return 0;
}
void print_in_readelf_style(std::ostream& s, const core::Cie& cie)
{
// first line is section-offset, length-not-including-length-field, zeroes, "CIE"
s.width(8);
s.fill('0');
s << setw(8) << setfill('0') << std::hex << cie.get_offset()
<< ' '
<< setw(16) << setfill('0') << std::hex << cie.get_bytes_in_cie()
<< ' '
<< setw(8) << setfill('0') << 0 << std::dec
<< " CIE"
<< endl;
// cie fields come next
s << " Version: " << (int) cie.get_version() << endl
<< " Augmentation: \"" << cie.get_augmenter() << "\"" << endl
<< " Code alignment factor: " << cie.get_code_alignment_factor() << endl
<< " Data alignment factor: " << cie.get_data_alignment_factor() << endl
<< " Return address column: " << cie.get_return_address_register_rule() << endl
<< " Augmentation data: ";
auto augbytes = cie.get_augmentation_bytes();
for (auto i_byte = augbytes.begin(); i_byte != augbytes.end(); ++i_byte)
{
if (i_byte != augbytes.begin()) s << ' ';
s << std::hex << setw(2) << setfill('0') << (unsigned) *i_byte;
}
s << std::dec << endl;
s << endl;
/* Now we need to print the "initial instructions". */
encap::frame_instrlist initial_instrs(cie, /* FIXME */ 8, cie.initial_instructions_seq());
print_in_readelf_style(s, initial_instrs, -1);
}
void print_in_readelf_style(std::ostream& s, const encap::frame_instrlist& instrs, Dwarf_Addr initial_loc)
{
Dwarf_Addr loc = initial_loc;
auto reg = [](int regnum) {
std::ostringstream s;
s << "r" << regnum << " (" << dwarf_regnames_for_elf_machine(elf_machine)[regnum]
<< ")";
return s.str();
};
for (auto i_instr = instrs.begin(); i_instr != instrs.end(); ++i_instr)
{
const char *opcode_name;
int encoded_opcode = i_instr->fp_base_op << 6 | i_instr->fp_extended_op;
int ret = lib::dwarf_get_CFA_name(encoded_opcode, &opcode_name);
assert(ret == DW_DLV_OK);
if (string(opcode_name) == "DW_CFA_extended") opcode_name = "DW_CFA_nop";
s << " " << opcode_name;
switch (encoded_opcode)
{
// "packed" two-bit opcodes
case DW_CFA_advance_loc:
case DW_CFA_advance_loc1:
case DW_CFA_advance_loc2:
case DW_CFA_advance_loc4:
loc += i_instr->fp_offset_or_block_len;
s << ": " << i_instr->fp_offset_or_block_len << " to " << setw(16) << setfill('0') << std::hex << loc << std::dec;
break;
case DW_CFA_offset:
s << ": " << reg(i_instr->fp_register)
<< " at cfa" << std::showpos << (lib::Dwarf_Signed)(i_instr->fp_offset_or_block_len) << std::noshowpos;
break;
case DW_CFA_restore_extended: goto register_only;
case DW_CFA_undefined: goto register_only;
case DW_CFA_same_value: goto register_only;
case DW_CFA_def_cfa_register: goto register_only;
case DW_CFA_restore: goto register_only;
register_only:
s << ": " << reg(i_instr->fp_register);
break;
// DW_CFA_extended and DW_CFA_nop are the same value, BUT
case DW_CFA_nop: goto no_args; // this is a full zero byte
// extended opcodes follow
case DW_CFA_remember_state: goto no_args;
case DW_CFA_restore_state: goto no_args;
no_args:
break;
case DW_CFA_set_loc:
goto unsupported_for_now; // FIXME
break;
case DW_CFA_offset_extended_sf: goto register_and_offset;
case DW_CFA_def_cfa_sf: goto register_and_offset;
case DW_CFA_register: goto register_and_offset;
case DW_CFA_offset_extended: goto register_and_offset;
case DW_CFA_def_cfa: goto register_and_offset;
case DW_CFA_val_offset: goto register_and_offset;
case DW_CFA_val_offset_sf: goto register_and_offset;
register_and_offset: // FIXME: second register goes where? I've put it in fp_offset_or_block_len
s << ": " << reg(i_instr->fp_register) << " ofs " << i_instr->fp_offset_or_block_len;
break;
case DW_CFA_def_cfa_offset_sf: goto offset_only;
case DW_CFA_def_cfa_offset: goto offset_only;
offset_only:
s << ": " << i_instr->fp_offset_or_block_len;
break;
case DW_CFA_expression:
goto unsupported_for_now; // FIXME
case DW_CFA_def_cfa_expression: goto expression;
case DW_CFA_val_expression: goto expression;
expression:
s << " (";
print_in_readelf_style(s, encap::loc_expr(dbg, i_instr->fp_expr_block, i_instr->fp_offset_or_block_len));
s << ")";
break;
default: goto unsupported_for_now;
unsupported_for_now:
s << "FIXME";
break;
}
s << endl;
}
}
void print_in_readelf_style(std::ostream& s, const encap::loc_expr& expr)
{
for (auto i_instr = expr.begin(); i_instr != expr.end(); ++i_instr)
{
if (i_instr != expr.begin()) s << "; ";
s << spec::DEFAULT_DWARF_SPEC.op_lookup(i_instr->lr_atom);
if (i_instr->lr_atom >= DW_OP_breg0 && i_instr->lr_atom <= DW_OP_breg31)
{
s << " ("
<< dwarf_regnames_for_elf_machine(elf_machine)[i_instr->lr_atom - DW_OP_breg0]
<< ")";
}
if (i_instr->lr_atom >= DW_OP_reg0 && i_instr->lr_atom <= DW_OP_reg31)
{
s << " ("
<< dwarf_regnames_for_elf_machine(elf_machine)[i_instr->lr_atom - DW_OP_reg0]
<< ")";
}
for (int i = 0; i < spec::DEFAULT_DWARF_SPEC.op_operand_count(i_instr->lr_atom); ++i)
{
assert(i < 2); // no opcode has >2 arguments
if (i == 0) s << ": ";
s << ((i == 0) ? i_instr->lr_number : i_instr->lr_number2);
}
}
}
void print_in_readelf_style(std::ostream& s, const core::Fde& fde)
{
// auto decoded = decode_fde(dbg, fde);
// don't decode -- that's for -wF
// first line is section-offset, length-not-including-length-field, id, "FDE"
s.width(8);
s.fill('0');
s << setw(8) << setfill('0') << std::hex << fde.get_fde_offset()
<< ' '
<< setw(16) << setfill('0') << std::hex << fde.get_fde_byte_length()
<< ' '
<< setw(8) << setfill('0') << fde.get_id() << std::dec
<< " FDE cie="
<< setw(8) << setfill('0') << std::hex << fde.find_cie()->get_cie_offset()
<< " pc="
<< setw(16) << setfill('0') << std::hex << fde.get_low_pc()
<< ".."
<< setw(16) << setfill('0') << std::hex << (fde.get_low_pc() + fde.get_func_length())
<< endl;
auto augbytes = fde.get_augmentation_bytes();
if (augbytes.size() > 0)
{
s << " Augmentation data: ";
for (auto i_byte = augbytes.begin(); i_byte != augbytes.end(); ++i_byte)
{
if (i_byte != augbytes.begin()) s << ' ';
s << std::hex << setw(2) << setfill('0') << (unsigned) *i_byte;
}
s << endl << endl;
}
s << std::dec;
/* Now we need to print the instructions. */
// TODO: eliminate this once the assertion below assures me that it's the same as
// instrbytes etc..
auto instr_seq = fde.instr_bytes_seq();
encap::frame_instrlist instrs(*fde.find_cie(), /* FIXME */ 8, instr_seq);
print_in_readelf_style(s, instrs, fde.get_low_pc());
}
void print_in_readelf_style(std::ostream& s, const core::FrameSection& fs, core::root_die& r)
{
typedef core::FrameSection::cie_iterator cie_iterator;
typedef core::FrameSection::fde_iterator fde_iterator;
/* In what order does readelf print the CIEs and FDEs?
* Answer: strictly offset order. I seem to be doing it in
* a crazy order. This is because our FDEs aren't coming out
* in offset order. Sort them first! */
std::vector< cie_iterator > cies;
for (auto i_cie = fs.cie_begin(); i_cie != fs.cie_end(); ++i_cie) cies.push_back(i_cie);
std::vector< fde_iterator > fdes;
for (auto i_fde = fs.fde_begin(); i_fde != fs.fde_end(); ++i_fde) fdes.push_back(i_fde);
std::sort(cies.begin(), cies.end(), [&fs](const cie_iterator& arg1, const cie_iterator& arg2) {
assert(arg1 != fs.cie_end());
assert(arg2 != fs.cie_end());
return arg1->get_offset() < arg2->get_offset();
});
std::sort(fdes.begin(), fdes.end(), [&fs](const fde_iterator& arg1, const fde_iterator& arg2) {
assert(arg1 != fs.fde_end());
assert(arg2 != fs.fde_end());
return arg1->get_offset() < arg2->get_offset();
});
s << "Contents of the .eh_frame section:\n\n";
auto i_i_cie = cies.begin();
auto i_i_fde = fdes.begin();
lib::Dwarf_Off cur_off = 0;
unsigned size_of_length_field = fs.is_64bit ? 12 : 4;
while (!(i_i_cie == cies.end() && i_i_fde == fdes.end()))
{
auto i_cie = *i_i_cie;
auto i_fde = *i_i_fde;
// print whichever has the lower offset
lib::Dwarf_Off fde_offset = (i_i_fde == fdes.end())
? std::numeric_limits<lib::Dwarf_Off>::max() : i_fde->get_offset();
lib::Dwarf_Off cie_offset = (i_i_cie == cies.end())
? std::numeric_limits<lib::Dwarf_Off>::max() : i_cie->get_offset();
if (cie_offset < fde_offset)
{
cur_off = cie_offset + size_of_length_field;
print_in_readelf_style(s, *i_cie);
s << endl;
++i_i_cie;
cur_off += i_cie->get_bytes_in_cie();
}
else
{
cur_off = fde_offset + size_of_length_field;
print_in_readelf_style(s, *i_fde);
s << endl;
++i_i_fde;
cur_off += i_fde->get_fde_byte_length();
}
}
s << setw(8) << setfill('0') << std::hex << cur_off << std::dec << " ZERO terminator" << endl;
}