Files
fail/tools/import-trace/RegisterImporter.cc
Horst Schirmeier 5d5927a88a DatabaseExperiment: add register FI
Calling the DatabaseCampaign with --inject-registers or
--force-inject-registers now injects into CPU registers.  This is achieved
by reinterpreting data addresses in the DB as addresses within the register
file.  (The mapping between registers and data addresses is implemented in
core/util/llvmdisassembler/LLVMtoFailTranslator.hpp.)  The difference
between --inject-registers and --force-inject-registers is what the
experiment does when a data address is not interpretable as a register: the
former option then injects into memory (DatabaseCampaignMessage,
RegisterInjectionMode AUTO), the latter skips the injection altogether
(FORCE).

Currently only compiles together with the Bochs backend; the
DatabaseExperiment's redecodeCurrentInstruction() function must be
moved into the Bochs EEA to remedy this.

Change-Id: I23f152ac0adf4cb6fbe82377ac871e654263fe57
2018-07-24 09:45:00 +02:00

222 lines
7.1 KiB
C++

#include <sstream>
#include <iostream>
#include "RegisterImporter.hpp"
#include "util/Logger.hpp"
using namespace llvm;
using namespace llvm::object;
using namespace fail;
static Logger LOG("RegisterImporter");
/**
* Callback function that can be used to add command line options
* to the campaign
*/
bool RegisterImporter::cb_commandline_init() {
CommandLine &cmd = CommandLine::Inst();
NO_GP = cmd.addOption("", "no-gp", Arg::None,
"--no-gp \tRegisterImporter: do not inject general purpose registers");
FLAGS = cmd.addOption("", "flags", Arg::None,
"--flags \tRegisterImporter: inject flags register");
IP = cmd.addOption("", "ip", Arg::None,
"--ip \tRegisterImporter: inject instruction pointer");
NO_SPLIT = cmd.addOption("", "do-not-split", Arg::None,
"--do-not-split \tRegisterImporter: Do not split the registers into one byte chunks");
return true;
}
bool RegisterImporter::addRegisterTrace(simtime_t curtime, instruction_count_t instr,
Trace_Event &ev,
const LLVMtoFailTranslator::reginfo_t &info,
char access_type) {
address_t from, to;
int chunk_width;
if (do_split_registers) {
/* If we want to split the registers into one byte chunks (to
enable proper pruning, we use a one byte window register,
to determine beginning and end address */
LLVMtoFailTranslator::reginfo_t one_byte_window = info;
one_byte_window.width = 8;
from = one_byte_window.toDataAddress();
to = one_byte_window.toDataAddress() + info.width / 8;
chunk_width = 1; // One byte chunks
} else {
/* We trace whole registers */
from = info.toDataAddress();
to = from + 1; /* exactly one trace event per register access*/
chunk_width = (info.width / 8);
}
// Iterate over all accessed bytes
for (address_t data_address = from; data_address < to; ++data_address) {
// skip events outside a possibly supplied memory map
if (m_mm && !m_mm->isMatching(ev.ip())) {
continue;
}
margin_info_t left_margin = getOpenEC(data_address);
margin_info_t right_margin;
right_margin.time = curtime;
right_margin.dyninstr = instr; // !< The current instruction
right_margin.ip = ev.ip();
// skip zero-sized intervals: these can occur when an instruction
// accesses a memory location more than once (e.g., INC, CMPXCHG)
if (left_margin.dyninstr > right_margin.dyninstr) {
continue;
}
// we now have an interval-terminating R/W event to the memaddr
// we're currently looking at; the EC is defined by
// data_address, dynamic instruction start/end, the absolute PC at
// the end, and time start/end
// pass through potentially available extended trace information
ev.set_width(chunk_width);
ev.set_memaddr(data_address);
ev.set_accesstype(access_type == 'R' ? ev.READ : ev.WRITE);
if (!add_trace_event(left_margin, right_margin, ev)) {
LOG << "add_trace_event failed" << std::endl;
return false;
}
// next interval must start at next instruction; the aforementioned
// skipping mechanism wouldn't work otherwise
newOpenEC(data_address, curtime + 1, instr + 1, ev.ip());
}
return true;
}
bool RegisterImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr,
Trace_Event &ev) {
if (!binary) {
// Parse command line again, for jump-from and jump-to
// operations
CommandLine &cmd = CommandLine::Inst();
if (!cmd.parse()) {
std::cerr << "Error parsing arguments." << std::endl;
return false;
}
do_gp = !cmd[NO_GP];
do_flags = cmd[FLAGS];
do_ip = cmd[IP];
do_split_registers = !cmd[NO_SPLIT];
// retrieve register IDs for general-purpose and flags register(s) for
// the configured architecture
fail::Architecture arch;
m_ip_register_id =
(*arch.getRegisterSetOfType(RT_IP)->begin())->getId();
fail::UniformRegisterSet *regset;
if (do_gp) {
regset = arch.getRegisterSetOfType(RT_GP);
for (fail::UniformRegisterSet::iterator it = regset->begin();
it != regset->end(); ++it) {
m_register_ids.insert((*it)->getId());
}
}
if (do_flags) {
regset = arch.getRegisterSetOfType(RT_ST);
for (fail::UniformRegisterSet::iterator it = regset->begin();
it != regset->end(); ++it) {
m_register_ids.insert((*it)->getId());
}
}
/* Disassemble the binary if necessary */
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
if (!m_elf) {
LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl;
return false;
}
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(m_elf->getFilename());
if (!BinaryOrErr) {
std::string Buf;
raw_string_ostream OS(Buf);
logAllUnhandledErrors(std::move(BinaryOrErr.takeError()), OS, "");
OS.flush();
LOG << m_elf->getFilename() << "': " << Buf << ".\n";
return false;
}
binary = BinaryOrErr.get().getBinary();
// necessary due to an AspectC++ bug triggered by LLVM 3.3's dyn_cast()
#ifndef __puma
ObjectFile *obj = dyn_cast<ObjectFile>(binary);
disas.reset(new LLVMDisassembler(obj));
#endif
disas->disassemble();
LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap();
LOG << "instructions disassembled: " << instr_map.size() << " Triple: " << disas->GetTriple() << std::endl;
m_ltof = disas->getTranslator() ;
}
// instruction pointer is read + written at each instruction
const LLVMtoFailTranslator::reginfo_t info_pc(m_ip_register_id);
if (do_ip &&
(!addRegisterTrace(curtime, instr, ev, info_pc, 'R') ||
!addRegisterTrace(curtime, instr, ev, info_pc, 'W'))) {
return false;
}
const LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap();
if (instr_map.find(ev.ip()) == instr_map.end()) {
LOG << "Could not find instruction for IP " << std::hex << ev.ip()
<< ", skipping" << std::endl;
return true;
}
const LLVMDisassembler::Instr &opcode = instr_map.at(ev.ip());
//const MCRegisterInfo &reg_info = disas->getRegisterInfo();
for (std::vector<LLVMDisassembler::register_t>::const_iterator it = opcode.reg_uses.begin();
it != opcode.reg_uses.end(); ++it) {
const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterInfo(*it);
if (&info == &m_ltof->notfound) {
LOG << "Could not find a mapping for LLVM input register #" << std::dec << *it
<< " at IP " << std::hex << ev.ip()
<< ", skipping" << std::endl;
continue;
}
/* only proceed if we want to inject into this register */
if (m_register_ids.find(info.id) == m_register_ids.end()) {
continue;
}
if (!addRegisterTrace(curtime, instr, ev, info, 'R')) {
return false;
}
}
for (std::vector<LLVMDisassembler::register_t>::const_iterator it = opcode.reg_defs.begin();
it != opcode.reg_defs.end(); ++it) {
const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterInfo(*it);
if (&info == &m_ltof->notfound) {
LOG << "Could not find a mapping for LLVM output register #" << std::dec << *it
<< " at IP " << std::hex << ev.ip()
<< ", skipping" << std::endl;
continue;
}
/* only proceed if we want to inject into this register */
if (m_register_ids.find(info.id) == m_register_ids.end()) {
continue;
}
if (!addRegisterTrace(curtime, instr, ev, info, 'W'))
return false;
}
return true;
}