diff --git a/.editorconfig b/.editorconfig index f4ee83c9..cf6b463b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,7 @@ root = true end_of_line = lf insert_final_newline = true -[{**/*.cc,**/*.hpp,**/*.ah,**/*.ah.in,**/*.proto}] +[{**/*.cc,**/*.cpp,**/*.hpp,**/*.ah,**/*.ah.in,**/*.proto}] indent_style = tab indent_size = 4 diff --git a/cmake/bochs.cmake b/cmake/bochs.cmake index c22a2378..6b590a03 100644 --- a/cmake/bochs.cmake +++ b/cmake/bochs.cmake @@ -79,7 +79,7 @@ if(BUILD_BOCHS) set(bochs_configure_params --enable-a20-pin --enable-x86-64 --enable-cpu-level=6 --enable-ne2000 --enable-acpi --enable-pci --enable-usb --enable-trace-cache --enable-fast-function-calls --enable-host-specific-asms --enable-disasm --enable-readline --enable-clgd54xx --enable-fpu --enable-vmx=2 --enable-monitor-mwait --enable-cdrom --enable-sb16=linux --enable-gdb-stub --disable-docbook --with-nogui --with-x11 --with-wx --with-sdl CACHE STRING "Bochs configure parameters") ## Bochs CXX args for calling make - set(bochs_build_CXX CXX=${AGXX}\ -p\ ${PROJECT_SOURCE_DIR}/src\ -p\ ${PROJECT_SOURCE_DIR}/simulators\ -p\ ${PROJECT_SOURCE_DIR}/debuggers\ -p\ ${PROJECT_SOURCE_DIR}/tools\ -p\ ${PROJECT_BINARY_DIR}/src\ -I${PROJECT_SOURCE_DIR}/src/core\ -I${CMAKE_BINARY_DIR}/src/core\ ${CMAKE_AGPP_FLAGS}\ --Xcompiler\ -std=gnu++11\ -Wno-narrowing) + set(bochs_build_CXX CXX=${AGXX}\ -p\ ${PROJECT_SOURCE_DIR}/src\ -p\ ${PROJECT_SOURCE_DIR}/simulators\ -p\ ${PROJECT_SOURCE_DIR}/debuggers\ -p\ ${PROJECT_SOURCE_DIR}/tools\ -p\ ${PROJECT_BINARY_DIR}/src\ -I${PROJECT_SOURCE_DIR}/src/core\ -I${CMAKE_BINARY_DIR}/src/core\ ${CMAKE_AGPP_FLAGS}\ --Xcompiler\ -std=gnu++14\ -Wno-narrowing) ## Bochs libtool command. set(bochs_build_LIBTOOL LIBTOOL=/bin/sh\ ./libtool\ --tag=CXX) diff --git a/cmake/compilerconfig.cmake b/cmake/compilerconfig.cmake index 30fc2084..ec1527ae 100644 --- a/cmake/compilerconfig.cmake +++ b/cmake/compilerconfig.cmake @@ -1,7 +1,7 @@ #### C++14 #### # We need at least C++11, as some library headers begin to require it. C++14 # has already aged sufficiently to mandate it here. -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/src/core/comm/DatabaseCampaignMessage.proto.in b/src/core/comm/DatabaseCampaignMessage.proto.in index 474f56b9..dec542c4 100644 --- a/src/core/comm/DatabaseCampaignMessage.proto.in +++ b/src/core/comm/DatabaseCampaignMessage.proto.in @@ -15,21 +15,19 @@ message DatabaseCampaignMessage { // using generic InjectionPointMessage required uint32 injection_instr = 4 [(sql_ignore) = true]; optional uint32 injection_instr_absolute = 5 [(sql_ignore) = true]; - required uint32 data_address = 6 [(sql_ignore) = true]; - required uint32 data_width = 7 [(sql_ignore) = true]; + required uint64 data_address = 6 [(sql_ignore) = true]; + required uint64 data_mask = 7 [(sql_ignore) = true]; required string variant = 8 [(sql_ignore) = true]; required string benchmark = 9 [(sql_ignore) = true]; required InjectionPointMessage injection_point = 10 [(sql_ignore) = true]; - required bool inject_bursts = 11 [default = false]; - enum RegisterInjectionMode { - OFF = 0; - AUTO = 1; - FORCE = 2; - RANDOMJUMP = 3; + enum InjectionMode { + BITFLIP = 0; + BURST = 1; + RANDOMJUMP = 2; } - optional RegisterInjectionMode register_injection_mode = 12 [default = OFF]; + optional InjectionMode injection_mode = 11 [default = BITFLIP]; } message DatabaseExperimentMessage { diff --git a/src/core/cpn/DatabaseCampaign.cc b/src/core/cpn/DatabaseCampaign.cc index 679f2a69..8694afd6 100644 --- a/src/core/cpn/DatabaseCampaign.cc +++ b/src/core/cpn/DatabaseCampaign.cc @@ -49,13 +49,7 @@ bool DatabaseCampaign::run() { CommandLine::option_handle BURST = cmd.addOption("","inject-bursts", Arg::None, "--inject-bursts \tinject burst faults (default: single bitflips)"); - CommandLine::option_handle REGISTERS = - cmd.addOption("","inject-registers", Arg::None, - "--inject-registers \tinject into ISA registers (default: memory)"); - CommandLine::option_handle REGISTERS_FORCE = - cmd.addOption("","force-inject-registers", Arg::None, - "--force-inject-registers \tinject into ISA registers only, ignore high addresses"); - CommandLine::option_handle REGISTERS_RANDOMJUMP = + CommandLine::option_handle RANDOMJUMP = cmd.addOption("","inject-randomjumps", Arg::None, "--inject-randomjumps \tinject random jumps (interpret data_address as jump target, as prepared by RandomJumpImporter)"); @@ -105,27 +99,16 @@ bool DatabaseCampaign::run() { } if (cmd[BURST]) { - m_inject_bursts = true; + m_injection_mode = DatabaseCampaignMessage::BURST; log_send << "fault model: burst" << std::endl; + } else if (cmd[RANDOMJUMP]) { + m_injection_mode = DatabaseCampaignMessage::RANDOMJUMP; + log_send << "fault model: randomjump" << std::endl; } else { - m_inject_bursts = false; + m_injection_mode = DatabaseCampaignMessage::BITFLIP; log_send << "fault model: single-bit flip" << std::endl; } - if (cmd[REGISTERS] && !cmd[REGISTERS_FORCE]) { - m_register_injection_mode = DatabaseCampaignMessage::AUTO; - log_send << "register injection: auto" << std::endl; - } else if (cmd[REGISTERS_FORCE]) { - m_register_injection_mode = DatabaseCampaignMessage::FORCE; - log_send << "register injection: on" << std::endl; - } else if (cmd[REGISTERS_RANDOMJUMP]) { - m_register_injection_mode = DatabaseCampaignMessage::RANDOMJUMP; - log_send << "register injection: randomjump" << std::endl; - } else { - m_register_injection_mode = DatabaseCampaignMessage::OFF; - log_send << "register injection: off" << std::endl; - } - if (cmd[PRUNER]) { m_fspmethod = std::string(cmd[PRUNER].first()->arg); } else { @@ -202,24 +185,23 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { /* Gather jobs */ unsigned long experiment_count; std::stringstream ss; - std::string sql_select = "SELECT p.id, p.injection_instr, p.injection_instr_absolute, p.data_address, p.data_width, t.instr1, t.instr2 "; + std::string sql_select = "SELECT p.id, p.injection_instr, p.injection_instr_absolute, p.data_address, (t.data_mask & p.data_mask), t.instr1, t.instr2 "; ss << " FROM fsppilot p " << " JOIN trace t" - << " ON t.variant_id = p.variant_id AND t.data_address = p.data_address AND t.instr2 = p.instr2" + << " ON t.variant_id = p.variant_id AND t.data_address = p.data_address AND t.instr2 = p.instr2 AND (t.data_mask & p.data_mask)" << " WHERE p.fspmethod_id IN (SELECT id FROM fspmethod WHERE method LIKE '" << m_fspmethod << "')" << " AND p.variant_id = " << variant.id << " ORDER BY t.instr1"; // Smart-Hopping needs this ordering std::string sql_body = ss.str(); /* Get the number of unfinished experiments */ - MYSQL_RES *count = db->query(("SELECT COUNT(*) " + sql_body).c_str(), true); + MYSQL_RES *count = db->query(("SELECT COUNT(*) as injections " + sql_body).c_str(), true); if (!count) { exit(1); } MYSQL_ROW row = mysql_fetch_row(count); experiment_count = strtoul(row[0], NULL, 10); - MYSQL_RES *pilots = db->query_stream ((sql_select + sql_body).c_str()); if (!pilots) { exit(1); @@ -235,22 +217,28 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { // calculating at trace instruction zero ConcreteInjectionPoint ip; - unsigned expected_results = expected_number_of_results(variant.variant, variant.benchmark); - unsigned sent_pilots = 0, skipped_pilots = 0; + unsigned long long expected_injections = 0; while ((row = mysql_fetch_row(pilots)) != 0) { unsigned pilot_id = strtoul(row[0], NULL, 10); + unsigned injection_instr = strtoul(row[1], NULL, 10); + uint64_t data_address = strtoul(row[3], NULL, 10); + unsigned data_mask = strtoul(row[4], NULL, 10); + unsigned instr1 = strtoul(row[5], NULL, 10); + unsigned instr2 = strtoul(row[6], NULL, 10); + + unsigned expected_results = __builtin_popcount(data_mask); + if (m_injection_mode == DatabaseCampaignMessage::BURST + || m_injection_mode == DatabaseCampaignMessage::RANDOMJUMP) + expected_results = 1; + if (existing_results_for_pilot(pilot_id) == expected_results) { skipped_pilots++; campaignmanager.skipJobs(1); continue; } + expected_injections += expected_results - existing_results_for_pilot(pilot_id); - unsigned injection_instr = strtoul(row[1], NULL, 10); - unsigned data_address = strtoul(row[3], NULL, 10); - unsigned data_width = strtoul(row[4], NULL, 10); - unsigned instr1 = strtoul(row[5], NULL, 10); - unsigned instr2 = strtoul(row[6], NULL, 10); DatabaseCampaignMessage pilot; pilot.set_pilot_id(pilot_id); @@ -267,9 +255,8 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { pilot.set_injection_instr_absolute(injection_instr_absolute); } pilot.set_data_address(data_address); - pilot.set_data_width(data_width); - pilot.set_inject_bursts(m_inject_bursts); - pilot.set_register_injection_mode(m_register_injection_mode); + pilot.set_data_mask(data_mask); + pilot.set_injection_mode(m_injection_mode); this->cb_send_pilot(pilot); @@ -277,6 +264,7 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { log_send << "pushed " << sent_pilots << " pilots into the queue" << std::endl; } } + log_send << "expecting " << expected_injections << " injections to take place." << std::endl; if (*mysql_error(db->getHandle())) { log_send << "MYSQL ERROR: " << mysql_error(db->getHandle()) << std::endl; @@ -312,7 +300,7 @@ void DatabaseCampaign::load_completed_pilots(std::vector &var log_send << "loading completed pilot IDs ..." << std::endl; std::stringstream sql; - sql << "SELECT pilot_id, COUNT(*) FROM fsppilot p" + sql << "SELECT pilot_id, COUNT(*), bit_count(data_mask) FROM fsppilot p" << " JOIN " << db_connect.result_table() << " r ON r.pilot_id = p.id" << " WHERE variant_id in (" << variant_str.str() << ")" << " AND fspmethod_id IN (SELECT id FROM fspmethod WHERE method LIKE '" << m_fspmethod << "')" @@ -326,6 +314,7 @@ void DatabaseCampaign::load_completed_pilots(std::vector &var while ((row = mysql_fetch_row(ids)) != 0) { unsigned pilot_id = strtoul(row[0], NULL, 10); unsigned result_count = strtoul(row[1], NULL, 10); + unsigned inj_count = strtoul(row[2], NULL, 10); #ifndef __puma completed_pilots.add( make_pair( diff --git a/src/core/cpn/DatabaseCampaign.hpp b/src/core/cpn/DatabaseCampaign.hpp index 066623ef..d786213c 100644 --- a/src/core/cpn/DatabaseCampaign.hpp +++ b/src/core/cpn/DatabaseCampaign.hpp @@ -39,8 +39,7 @@ class DatabaseCampaign : public Campaign { id_map completed_pilots; // !< map: Pilot IDs -> result count #endif - bool m_inject_bursts; // !< inject burst faults? - DatabaseCampaignMessage::RegisterInjectionMode m_register_injection_mode; // !< inject into registers? OFF, ON, AUTO (= use registers if address is small) + DatabaseCampaignMessage::InjectionMode m_injection_mode; // !< How should we inject? public: DatabaseCampaign() {}; @@ -59,15 +58,6 @@ class DatabaseCampaign : public Campaign { */ virtual bool run_variant(fail::Database::Variant); - /** - * How many results have to are expected from each fsppilot. If - * there are less result rows, the pilot will be again sent to the clients - * @return \c exptected number of results - */ - virtual int expected_number_of_results(std::string variant, std::string benchmark) { - return (m_inject_bursts ? 1 : 8); - } - /** * Callback function that can be used to add command line options * to the campaign diff --git a/src/core/efw/DatabaseExperiment.cc b/src/core/efw/DatabaseExperiment.cc index 284330fb..ecc9d567 100644 --- a/src/core/efw/DatabaseExperiment.cc +++ b/src/core/efw/DatabaseExperiment.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -12,14 +13,13 @@ #include "sal/SALConfig.hpp" #include "sal/Memory.hpp" #include "sal/Listener.hpp" +#include "sal/faultspace/FaultSpace.hpp" +#include "sal/faultspace/RegisterArea.hpp" +#include "sal/faultspace/MemoryArea.hpp" + #include "efw/DatabaseExperiment.hpp" #include "comm/DatabaseCampaignMessage.pb.h" -#if defined(BUILD_CAPSTONE_DISASSEMBLER) -# include "util/capstonedisassembler/CapstoneToFailTranslator.hpp" -#elif defined(BUILD_LLVM_DISASSEMBLER) -# include "util/llvmdisassembler/LLVMtoFailTranslator.hpp" -#endif //#define LOCAL @@ -29,125 +29,66 @@ using namespace google::protobuf; // Check if configuration dependencies are satisfied: #if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) - #error This experiment needs: breakpoints, restore. Enable these in the configuration. + #error This experiment needs: breakpoints, restore. Enable these in the configuration. #endif DatabaseExperiment::~DatabaseExperiment() { delete this->m_jc; } -void DatabaseExperiment::redecodeCurrentInstruction() { - /* Flush Instruction Caches and Prefetch queue */ - BX_CPU_C *cpu_context = simulator.getCPUContext(); - cpu_context->invalidate_prefetch_q(); - cpu_context->iCache.flushICacheEntries(); - - guest_address_t pc = simulator.getCPU(0).getInstructionPointer(); - bxInstruction_c *currInstr = simulator.getCurrentInstruction(); +unsigned DatabaseExperiment::injectFault(DatabaseCampaignMessage * fsppilot, unsigned bitpos) { + fsp_address_t data_address = fsppilot->data_address(); - m_log << "REDECODE INSTRUCTION @ IP 0x" << std::hex << pc << endl; - - guest_address_t eipBiased = pc + cpu_context->eipPageBias; - Bit8u instr_plain[15]; + // Random Jump Injection + if (fsppilot->injection_mode() == fsppilot->RANDOMJUMP) { + // interpret data_address as new value for the IP, i.e. jump there + ConcreteCPU &cpu = simulator.getCPU(0); + address_t current_PC = cpu.getInstructionPointer(); + address_t new_PC = data_address; + m_log << "INJECT: jump from 0x" << hex << current_PC << " to 0x" << new_PC << std::endl; - MemoryManager& mm = simulator.getMemoryManager(); - for (unsigned i = 0; i < sizeof(instr_plain); ++i) { - if (!mm.isMapped(pc + i)) { - m_log << "REDECODE: 0x" << std::hex << pc+i << "UNMAPPED" << endl; - // TODO: error? - return; - } + cpu.setRegisterContent(cpu.getRegister(RID_PC), new_PC); + simulator.redecodeCurrentInstruction(&cpu); + return current_PC; } - mm.getBytes(pc, sizeof(instr_plain), instr_plain); - - guest_address_t remainingInPage = cpu_context->eipPageWindowSize - eipBiased; - int ret; -#if BX_SUPPORT_X86_64 - if (cpu_context->cpu_mode == BX_MODE_LONG_64) - ret = cpu_context->fetchDecode64(instr_plain, currInstr, remainingInPage); - else -#endif - ret = cpu_context->fetchDecode32(instr_plain, currInstr, remainingInPage); - if (ret < 0) { - // handle instrumentation callback inside boundaryFetch - cpu_context->boundaryFetch(instr_plain, remainingInPage, currInstr); + // Regular Injections + std::unique_ptr target = m_fsp.decode(data_address); + FaultSpaceElement::injector_result result; + if(fsppilot->injection_mode() == fsppilot->BURST) { + m_log << "INJECTING BURST " << endl; + uint8_t mask = static_cast(fsppilot->data_mask()); + result = target->inject([&] (unsigned val) -> unsigned { + return val ^ (mask & 0xFF); + }); + } else { + m_log << "INJECTING BITFLIP (bitpos = " << bitpos << ") " << endl; + result = target->inject([&] (unsigned val) -> unsigned { + return val ^ (1 << bitpos); + }); } -} - -unsigned DatabaseExperiment::injectFault( - address_t data_address, unsigned bitpos, bool inject_burst, - bool inject_registers, bool force_registers, bool randomjump) { - unsigned value, injected_value; - - if (randomjump) { - // interpret data_address as new value for the IP, i.e. jump there - address_t current_PC = simulator.getCPU(0).getInstructionPointer(); - address_t new_PC = data_address; - m_log << "jump from 0x" << hex << current_PC << " to 0x" << new_PC << std::endl; - simulator.getCPU(0).setRegisterContent(simulator.getCPU(0).getRegister(RID_PC), new_PC); - redecodeCurrentInstruction(); - - // set program counter - value = current_PC; - injected_value = new_PC; - - /* First 128 registers, TODO use LLVMtoFailTranslator::getMaxDataAddress() */ - } else if (data_address < (128 << 4) && inject_registers) { -#if defined(BUILD_LLVM_DISASSEMBLER) || defined(BUILD_CAPSTONE_DISASSEMBLER) -#if defined(BUILD_LLVM_DISASSEMBLER) - typedef LLVMtoFailTranslator XtoFailTranslator; -#elif defined(BUILD_CAPSTONE_DISASSEMBLER) - typedef CapstoneToFailTranslator XtoFailTranslator; -#endif - // register FI - XtoFailTranslator::reginfo_t reginfo = - XtoFailTranslator::reginfo_t::fromDataAddress(data_address, 1); - - value = XtoFailTranslator::getRegisterContent(simulator.getCPU(0), reginfo); - if (inject_burst) { - injected_value = value ^ 0xff; - m_log << "INJECTING BURST at: REGISTER " << dec << reginfo.id - << " bitpos " << (reginfo.offset + bitpos) << endl; - } else { - injected_value = value ^ (1 << bitpos); - m_log << "INJECTING BIT-FLIP at: REGISTER " << dec << reginfo.id - << " bitpos " << (reginfo.offset + bitpos) << endl; - } - XtoFailTranslator::setRegisterContent(simulator.getCPU(0), reginfo, injected_value); - if (reginfo.id == RID_PC) { - // FIXME move this into the Bochs backend - m_log << "Redecode current instruction" << endl; - redecodeCurrentInstruction(); - } -#else - m_log << "ERROR: Not compiled with LLVM or Capstone. Enable BUILD_LLVM_DISASSEMBLER OR BUILD_CAPSTONE_DISASSEMBLER at buildtime." << endl; - simulator.terminate(1); -#endif - } else if (!force_registers) { - // memory FI - value = m_mm.getByte(data_address); - - if (inject_burst) { - injected_value = value ^ 0xff; - m_log << "INJECTING BURST at: MEM 0x" - << hex << setw(2) << setfill('0') << data_address << endl; - } else { - injected_value = value ^ (1 << bitpos); - m_log << "INJECTING BIT-FLIP (" << dec << bitpos << ") at: MEM 0x" - << hex << setw(2) << setfill('0') << data_address << endl; + // Redecode Instruction on RID_PC Change + // FIXME: After AspectC++ is removed, we can push a simulator + // instance down into the fault-space hierarchy and redecode in + // RegisterElement::inject(). At the moment, this is impossible as + // AspectC++ introduced linking dependencies on Bochs symbols. + if (auto reg = dynamic_cast(target.get())) { + ConcreteCPU & cpu = simulator.getCPU(0); + if (reg->get_base()->getId() == RID_PC) { + simulator.redecodeCurrentInstruction(&cpu); } - m_mm.setByte(data_address, injected_value); - - } else { - m_log << "WARNING: Skipping injection, data address 0x" - << hex << data_address << " out of range." << endl; - return 0; } + + m_log << hex << setw(2) << setfill('0') - << " value: 0x" << value - << " -> 0x" << injected_value << endl; - return value; + << std::showbase + << "\tIP: " << fsppilot->injection_instr_absolute() << endl + << "\tFAULT SITE: FSP " << data_address + << " -> AREA " << target->get_area()->get_name() << " @ " << target->get_offset() << endl + << "\telement: " << *target << endl + << "\tvalue: 0x" << (int)result.original + << " -> 0x" << (int)result.injected << endl; + return result.original; } template @@ -176,16 +117,28 @@ T * protobufFindSubmessageByTypename(Message *msg, const std::string &name) { bool DatabaseExperiment::run() { - m_log << "STARTING EXPERIMENT" << endl; + m_log << "STARTING EXPERIMENT" << endl; + + // Initialize the faultspace abstraction for injection mode + RegisterArea ®_area = (RegisterArea&) m_fsp.get_area("register"); + reg_area.set_state(&simulator.getCPU(0)); + MemoryArea &mem_area = (MemoryArea&) m_fsp.get_area("ram"); + mem_area.set_manager(&simulator.getMemoryManager()); + + if (!this->cb_start_experiment()) { m_log << "Initialization failed. Exiting." << endl; simulator.terminate(1); } +#if defined(BUILD_BOCHS) + #define MAX_EXECUTED_JOBS 25 +#else + #define MAX_EXECUTED_JOBS UINT_MAX +#endif unsigned executed_jobs = 0; - - while (executed_jobs < 25 || m_jc->getNumberOfUndoneJobs() > 0) { + while (executed_jobs < MAX_EXECUTED_JOBS || m_jc->getNumberOfUndoneJobs() > 0) { m_log << "asking jobserver for parameters" << endl; ExperimentData * param = this->cb_allocate_experiment_data(); #ifndef LOCAL @@ -204,17 +157,31 @@ bool DatabaseExperiment::run() fsppilot->set_injection_instr(0); fsppilot->set_injection_instr_absolute(1048677); fsppilot->set_data_address(2101240); - fsppilot->set_data_width(1); + fsppilot->set_data_mask(0xff); fsppilot->set_inject_bursts(true); #endif unsigned injection_instr = fsppilot->injection_instr(); - address_t data_address = fsppilot->data_address(); - unsigned width = fsppilot->data_width(); - unsigned injection_width = - (fsppilot->inject_bursts() || fsppilot->register_injection_mode() == fsppilot->RANDOMJUMP) ? 8 : 1; - for (unsigned bit_offset = 0; bit_offset < width * 8; bit_offset += injection_width) { + assert(fsppilot->data_mask() <=255 && "mask covers more than 8 bit, this is unsupported!"); + uint8_t mask = static_cast(fsppilot->data_mask()); + + bool single_injection = false; + if ( fsppilot->injection_mode() == fsppilot->BURST + || fsppilot->injection_mode() == fsppilot->RANDOMJUMP) { + single_injection = true; + } + + for (unsigned bit_offset = 0; bit_offset < 8; bit_offset += 1) { + // if the mask is zero at this bit offset, this bit shall not be injected. + bool allowed_mask = mask & (1 << bit_offset); + // additionally, always inject once for bursts. + // this first bit might be unset otherwise and thus, + // this address will never be injected otherwise. + if(!allowed_mask && !single_injection) { + continue; + } + // 8 results in one job Message *outer_result = cb_new_result(param); m_current_result = outer_result; @@ -235,7 +202,7 @@ bool DatabaseExperiment::run() } // Do we need to fast-forward at all? - fail::BaseListener *listener = 0; + BaseListener *listener = 0; if (injection_instr > 0) { // Create a listener that matches any IP event. It is used to // forward to the injection point. @@ -279,13 +246,8 @@ bool DatabaseExperiment::run() simulator.clearListeners(this); // inject fault (single-bit flip or burst) - result->set_original_value( - injectFault(data_address + bit_offset / 8, bit_offset % 8, - fsppilot->inject_bursts(), - fsppilot->register_injection_mode() != fsppilot->OFF, - fsppilot->register_injection_mode() == fsppilot->FORCE, - fsppilot->register_injection_mode() == fsppilot->RANDOMJUMP)); - result->set_injection_width(injection_width); + result->set_original_value(injectFault(fsppilot, bit_offset)); + result->set_injection_width(single_injection ? 8 : 1); if (!this->cb_before_resume()) { continue; // Continue to next experiment @@ -303,6 +265,10 @@ bool DatabaseExperiment::run() this->cb_after_resume(listener); simulator.clearListeners(this); + + // Break bit_offset loop, if we only perform a single injection + if (single_injection) + break; } #ifndef LOCAL m_jc->sendResult(*param); diff --git a/src/core/efw/DatabaseExperiment.hpp b/src/core/efw/DatabaseExperiment.hpp index 5a4de7d3..9d09e3ce 100644 --- a/src/core/efw/DatabaseExperiment.hpp +++ b/src/core/efw/DatabaseExperiment.hpp @@ -7,6 +7,10 @@ #include "util/Logger.hpp" #include #include +#include +#include "sal/faultspace/FaultSpace.hpp" +#include "comm/DatabaseCampaignMessage.pb.h" + namespace fail { class ExperimentData; @@ -14,9 +18,7 @@ class ExperimentData; class DatabaseExperiment : public fail::ExperimentFlow { fail::JobClient *m_jc; - unsigned injectFault(address_t data_address, unsigned bitpos, bool inject_burst, - bool inject_registers, bool force_registers, bool randomjump); - + unsigned injectFault(DatabaseCampaignMessage * fsppilot, unsigned bitpos); /** The current experiment data as returned by the job client. This allocated by cb_allocate_experiment_data() @@ -26,7 +28,7 @@ class DatabaseExperiment : public fail::ExperimentFlow { public: DatabaseExperiment(const std::string &name) - : m_log(name, false), m_mm(fail::simulator.getMemoryManager()) { + : m_log(name, false), m_mm(fail::simulator.getMemoryManager()), m_fsp(fail::simulator.getFaultSpace()) { /* The fail server can be set with an environent variable, otherwise the JOBSERVER configured by cmake ist used */ @@ -46,6 +48,7 @@ class DatabaseExperiment : public fail::ExperimentFlow { protected: fail::Logger m_log; fail::MemoryManager& m_mm; + fail::FaultSpace& m_fsp; /** Returns the currently running experiment message as returned * by the job client @@ -144,9 +147,6 @@ class DatabaseExperiment : public fail::ExperimentFlow { * */ virtual void cb_after_resume(fail::BaseListener *) = 0; - -private: - void redecodeCurrentInstruction(); }; } diff --git a/src/core/efw/JobClient.cc b/src/core/efw/JobClient.cc index b9ab0bf1..7f7320bd 100644 --- a/src/core/efw/JobClient.cc +++ b/src/core/efw/JobClient.cc @@ -124,8 +124,8 @@ bool JobClient::getParam(ExperimentData& exp) template bool sendMsg(Socket &s, google::protobuf::Message &msg) { - int size = htonl(msg.ByteSize()); - const auto msg_size = msg.ByteSize() + sizeof(size); + int size = htonl((uint32_t)msg.ByteSizeLong()); + const auto msg_size = (uint32_t)msg.ByteSizeLong() + sizeof(size); std::string buf; if (!msg.SerializeToString(&buf)) diff --git a/src/core/sal/Architecture.hpp b/src/core/sal/Architecture.hpp index 9afc701a..6492c606 100644 --- a/src/core/sal/Architecture.hpp +++ b/src/core/sal/Architecture.hpp @@ -3,6 +3,8 @@ #define __ARCHITECTURE_HPP__ #include "config/FailConfig.hpp" +#include "config/VariantConfig.hpp" + #ifdef BUILD_X86 #include "x86/X86Architecture.hpp" diff --git a/src/core/sal/CMakeLists.txt b/src/core/sal/CMakeLists.txt index cc5f6cb7..b086e9d6 100644 --- a/src/core/sal/CMakeLists.txt +++ b/src/core/sal/CMakeLists.txt @@ -1,3 +1,4 @@ + if(BUILD_BOCHS) set(SRCS CPU.cc @@ -119,9 +120,11 @@ if(CONFIG_FAST_BREAKPOINTS) ) endif(CONFIG_FAST_BREAKPOINTS) +add_subdirectory(faultspace) + add_library(fail-sal ${SRCS}) -target_link_libraries(fail-sal fail-efw fail-util) +target_link_libraries(fail-sal fail-efw fail-fsp fail-util) foreach(exp ${EXPERIMENTS_ACTIVATED}) target_link_libraries(fail-sal fail-${exp}) diff --git a/src/core/sal/CPU.cc b/src/core/sal/CPU.cc index 675853ab..d618a665 100644 --- a/src/core/sal/CPU.cc +++ b/src/core/sal/CPU.cc @@ -7,6 +7,8 @@ void CPUArchitecture::m_addRegister(Register* reg, RegisterType type) { // We may be called multiple times with the same register, if it needs to // reside in multiple subsets. + // std::cout << "adding " << reg->getName() << " (id=" << reg->getId() << ") to subset: " << type << std::endl; + if ((m_Registers.size()) < reg->getId()+1) { m_Registers.resize(reg->getId()+1); } @@ -20,6 +22,12 @@ void CPUArchitecture::m_addRegister(Register* reg, RegisterType type) urs->m_add(reg); } +void CPUArchitecture::m_addRegister(Register* reg, std::vector types) { + for(RegisterType t: types) { + this->m_addRegister(reg, t); + } +} + Register* CPUArchitecture::getRegister(size_t i) const { assert(i < m_Registers.size() && m_Registers[i] != NULL && "FATAL ERROR: Invalid index provided!"); diff --git a/src/core/sal/CPU.hpp b/src/core/sal/CPU.hpp index 17217257..eb720dfb 100644 --- a/src/core/sal/CPU.hpp +++ b/src/core/sal/CPU.hpp @@ -63,6 +63,8 @@ class CPUArchitecture { * @return a pointer to the retrieved register set (if found), \c NULL otherwise */ UniformRegisterSet* getRegisterSetOfType(RegisterType t) const; + + virtual ~CPUArchitecture() { } protected: std::vector m_Registers; //!< the total (!) register set /// a set of register subsets (each set has its own type) @@ -74,6 +76,13 @@ class CPUArchitecture { * @see getType() */ void m_addRegister(Register* reg, RegisterType type = RT_NONE); + /** + * Adds a new register to multiple sets + * @param reg a pointer to the register object to be added + * @param types the subset in which the register should be added + * @see getType() + */ + void m_addRegister(Register* reg, std::vector types); }; } // end-of-namespace: fail diff --git a/src/core/sal/Listener.hpp b/src/core/sal/Listener.hpp index d11b5fae..5366b0ca 100644 --- a/src/core/sal/Listener.hpp +++ b/src/core/sal/Listener.hpp @@ -328,12 +328,12 @@ class MemAccessListener : public BaseListener { WP_CTOR_SCOPE: MemAccessListener(MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE, ConcreteCPU* cpu = NULL) - : BaseListener(cpu), m_WatchAddr(ANY_ADDR), m_WatchMemType(ANY_MEMORY), m_WatchWidth(1), m_WatchType(type), m_Data() { } - MemAccessListener(address_t addr, memory_type_t memtype = ANY_MEMORY, + : BaseListener(cpu), m_WatchAddr(ANY_ADDR), m_WatchMemType(MEMTYPE_RAM), m_WatchWidth(1), m_WatchType(type), m_Data() { } + MemAccessListener(address_t addr, memory_type_t memtype = MEMTYPE_RAM, MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE, ConcreteCPU* cpu = NULL) : BaseListener(cpu), m_WatchAddr(addr), m_WatchMemType(memtype), m_WatchWidth(1), m_WatchType(type), m_Data() { } - MemAccessListener(const ElfSymbol &symbol, memory_type_t memtype = ANY_MEMORY, + MemAccessListener(const ElfSymbol &symbol, memory_type_t memtype = MEMTYPE_RAM, MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE, ConcreteCPU* cpu = NULL) : BaseListener(cpu), m_WatchAddr(symbol.getAddress()), m_WatchMemType(memtype), m_WatchWidth(symbol.getSize()), m_WatchType(type) , m_Data() { } @@ -462,7 +462,7 @@ class MemReadListener : public MemAccessListener { WPREAD_CTOR_SCOPE: MemReadListener(ConcreteCPU* cpu = NULL) : MemAccessListener(MemAccessEvent::MEM_READ, cpu) { } - MemReadListener(address_t addr, memory_type_t type = ANY_MEMORY, ConcreteCPU* cpu = NULL) + MemReadListener(address_t addr, memory_type_t type = MEMTYPE_RAM, ConcreteCPU* cpu = NULL) : MemAccessListener(addr, type, MemAccessEvent::MEM_READ, cpu) { } }; @@ -479,7 +479,7 @@ class MemWriteListener : public MemAccessListener { WPWRITE_CTOR_SCOPE: MemWriteListener(ConcreteCPU* cpu = NULL) : MemAccessListener(MemAccessEvent::MEM_WRITE, cpu) { } - MemWriteListener(address_t addr, memory_type_t type = ANY_MEMORY, ConcreteCPU* cpu = NULL) + MemWriteListener(address_t addr, memory_type_t type = MEMTYPE_RAM, ConcreteCPU* cpu = NULL) : MemAccessListener(addr, type, MemAccessEvent::MEM_WRITE, cpu) { } }; diff --git a/src/core/sal/Register.cc b/src/core/sal/Register.cc index 3f04a29c..7480b7ed 100644 --- a/src/core/sal/Register.cc +++ b/src/core/sal/Register.cc @@ -3,10 +3,10 @@ namespace fail { -Register* UniformRegisterSet::getRegister(size_t i) const +Register* UniformRegisterSet::getRegister(Register::id_t id) const { - assert(i < m_Regs.size() && "FATAL ERROR: Invalid index provided!"); - return m_Regs[i]; + assert(id < m_Regs.size() && "FATAL ERROR: Invalid index provided!"); + return m_Regs[id]; } void UniformRegisterSet::m_add(Register* preg) diff --git a/src/core/sal/Register.hpp b/src/core/sal/Register.hpp index daf96082..b8804ddc 100644 --- a/src/core/sal/Register.hpp +++ b/src/core/sal/Register.hpp @@ -38,9 +38,11 @@ enum RegisterType { * of classes which had been derived from this class. */ class Register { +public: + typedef unsigned int id_t; //!< Every Register has a unique id protected: regwidth_t m_Width; //!< the register width - unsigned int m_Id; //!< the unique id of this register + id_t m_Id; //!< the unique id of this register std::string m_Name; //!< The (optional) name, maybe empty friend class UniformRegisterSet; public: @@ -50,7 +52,7 @@ class Register { * @param t the type of the register to be constructed * @param w the width of the register in bits */ - Register(unsigned int id, regwidth_t w) + Register(id_t id, regwidth_t w) : m_Width(w), m_Id(id) { } /** * Returns the (fixed) width of this register. @@ -71,7 +73,7 @@ class Register { * Returns the unique id of this register. * @return the unique id */ - unsigned int getId() const { return m_Id; } + id_t getId() const { return m_Id; } }; /** @@ -134,7 +136,7 @@ class UniformRegisterSet { * @return a pointer to the \a i-th register; if \a i is invalid, an * assertion is thrown */ - Register* getRegister(size_t i) const; + Register* getRegister(Register::id_t i) const; /** * Retrieves the first register within this set (syntactical sugar). * @return a pointer to the first register (if existing -- otherwise an @@ -143,6 +145,22 @@ class UniformRegisterSet { virtual Register* first() const { return getRegister(0); } }; +/** + * \class RegisterView + * + * Describes a (partial) view onto a register with the given id. The + * view is width bits wide and starts with a bit offset. This class is + * used by the dissassemblers. + */ +struct RegisterView { + Register::id_t id; + regwidth_t width; + byte_t offset; + + RegisterView(int id=-1, regwidth_t width=-1, byte_t offs = 0) + : id(id), width(width), offset(offs) { } +}; + } // end-of-namespace: fail #endif // __REGISTER_HPP__ diff --git a/src/core/sal/SALConfig.cc b/src/core/sal/SALConfig.cc index 371d4a47..6a250103 100644 --- a/src/core/sal/SALConfig.cc +++ b/src/core/sal/SALConfig.cc @@ -2,6 +2,14 @@ namespace fail { +const char* memtype_descriptions[MEMTYPE_LAST] = { + "unknown", + "ram", + "flash", + "tags", + "eeprom", +}; + // Flag initialization depends on the current selected simulator // (For now, the initialization values are all the same): #if defined BUILD_BOCHS || defined BUILD_GEM5 || \ diff --git a/src/core/sal/SALConfig.hpp b/src/core/sal/SALConfig.hpp index 7a6ead5d..561b57f9 100644 --- a/src/core/sal/SALConfig.hpp +++ b/src/core/sal/SALConfig.hpp @@ -2,6 +2,8 @@ #define __SAL_CONFIG_HPP__ #include +#include +#include #include "config/VariantConfig.hpp" @@ -38,9 +40,13 @@ typedef enum { MEMTYPE_RAM = 0x1, //!< Access to volatile memory MEMTYPE_FLASH = 0x2, //!< Access to flash memory MEMTYPE_TAGS = 0x3, //!< Access to tag-bits (see SAIL) - MEMTYPE_EEPROM = 0x4 //!< Access to EEPROM (see AVR) + MEMTYPE_EEPROM = 0x4, //!< Access to EEPROM (see AVR) + MEMTYPE_LAST, } memory_type_t; //! memory type (RAM, FLASH, etc...) +// Defined in faultspace/FaultSpace.cc +extern const char* memtype_descriptions[]; + // Note: The following flags are defined in SALConfig.cc. //! invalid address flag (e.g. for memory address ptrs) @@ -58,6 +64,7 @@ extern const unsigned ANY_INTERRUPT; //! invalid timer id (e.g. for timer listeners) extern const timer_id_t INVALID_TIMER; + } // end-of-namespace: fail #endif // __SAL_CONFIG_HPP__ diff --git a/src/core/sal/SimulatorController.cc b/src/core/sal/SimulatorController.cc index 5f193ecc..ed11e609 100644 --- a/src/core/sal/SimulatorController.cc +++ b/src/core/sal/SimulatorController.cc @@ -3,6 +3,8 @@ #include "Event.hpp" #include "Listener.hpp" #include "util/CommandLine.hpp" +#include "sal/faultspace/RegisterArea.hpp" +#include "sal/faultspace/MemoryArea.hpp" #include "Memory.hpp" namespace fail { @@ -252,6 +254,22 @@ void SimulatorController::addCPU(ConcreteCPU* cpu) { assert(cpu != NULL && "FATAL ERROR: Argument (cpu) cannot be NULL!"); m_CPUs.push_back(cpu); + + // Connect FaultSpace with simulator via Memory Managers + if (m_CPUs.size() == 1) { + RegisterArea ®_area = (RegisterArea&) m_fsp.get_area("register"); + reg_area.set_state(cpu); + } +} + +void SimulatorController::setMemoryManager(MemoryManager* pMem, memory_type_t type) { + m_Mems[type] = pMem; + + // Connect FaultSpace with simulator via Memory Managers + std::string area_id = memtype_descriptions[type]; + MemoryArea* area = dynamic_cast(&m_fsp.get_area(area_id)); + assert(area != nullptr && "Faultspace has no entry for given memory area"); + area->set_manager(pMem); } ConcreteCPU& SimulatorController::getCPU(size_t i) const diff --git a/src/core/sal/SimulatorController.hpp b/src/core/sal/SimulatorController.hpp index 8ed8273a..f932079a 100644 --- a/src/core/sal/SimulatorController.hpp +++ b/src/core/sal/SimulatorController.hpp @@ -11,6 +11,7 @@ #include "SALConfig.hpp" #include "ConcreteCPU.hpp" #include "util/Logger.hpp" +#include "sal/faultspace/FaultSpace.hpp" @@ -45,6 +46,7 @@ class SimulatorController { std::vector m_CPUs; //!< list of CPUs in the target system friend class ListenerManager; //!< "outsources" the listener management std::string m_argv0; //!< Invocation name of simulator process + FaultSpace m_fsp; public: SimulatorController() : m_log("SimulatorController", false), @@ -53,8 +55,8 @@ class SimulatorController { SimulatorController(MemoryManager* mem) : m_log("SimulatorController", false), m_isInitialized(false) { - m_Mems[MEMTYPE_RAM] = mem; // The RAM memory manager is the default - } + setMemoryManager(mem); + } virtual ~SimulatorController() { } /** * @brief Initialization function each implementation needs to call on @@ -172,6 +174,11 @@ class SimulatorController { * @return the CPU count */ size_t getCPUCount() const { return m_CPUs.size(); } + /** + * Redecode instruction after we have changed RID_PC. Has to be + * called after setRegisterContent(RID_PC). + */ + virtual void redecodeCurrentInstruction(ConcreteCPU* cpu) { /* empty */} /** * Returns the (constant) initialized memory manager. * @return a reference to the memory manager @@ -186,9 +193,11 @@ class SimulatorController { * Sets the memory manager. * @param pMem a new concrete memory manager */ - void setMemoryManager(MemoryManager* pMem, memory_type_t type=MEMTYPE_RAM) { - m_Mems[type] = pMem; - } + void setMemoryManager(MemoryManager* pMem, memory_type_t type=MEMTYPE_RAM); + /** + * Get the connected fault space abstraction + */ + FaultSpace &getFaultSpace() { return m_fsp; } /* ******************************************************************** * Experiment-Flow & Listener Management API: * ********************************************************************/ diff --git a/src/core/sal/arm/ArmFaultSpace.cc b/src/core/sal/arm/ArmFaultSpace.cc new file mode 100644 index 00000000..d1ca9661 --- /dev/null +++ b/src/core/sal/arm/ArmFaultSpace.cc @@ -0,0 +1,19 @@ +#include +#include "ArmFaultSpace.hpp" +#include "sal/faultspace/MemoryArea.hpp" +#include "sal/faultspace/RegisterArea.hpp" + +fail::ArmFaultSpace::ArmFaultSpace() { + // Order Matters! + add_area(std::make_unique()); + + // This magic register break was used in pre-faultspace version. + // We stick to this value to remain as compatible as possible with + // the old data_addresses + unsigned register_break = (128 << 4); + set_point(register_break); + + // 512-4GiB of RAM + add_area(std::make_unique("ram", register_break, 0xFFFFFFFF-register_break)); +} + diff --git a/src/core/sal/arm/ArmFaultSpace.hpp b/src/core/sal/arm/ArmFaultSpace.hpp new file mode 100644 index 00000000..04490023 --- /dev/null +++ b/src/core/sal/arm/ArmFaultSpace.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "sal/faultspace/BaseFaultSpace.hpp" + +namespace fail { +class ArmFaultSpace: public BaseFaultSpace { +public: + ArmFaultSpace(); +}; + +} // end-of-namespace: fail + diff --git a/src/core/sal/bochs/BochsController.cc b/src/core/sal/bochs/BochsController.cc index c7c39303..e5945a1f 100644 --- a/src/core/sal/bochs/BochsController.cc +++ b/src/core/sal/bochs/BochsController.cc @@ -174,4 +174,44 @@ ConcreteCPU& BochsController::detectCPU(BX_CPU_C* pCPU) const return getCPU(i); } +void BochsController::redecodeCurrentInstruction(ConcreteCPU* cpu) { + /* Flush Instruction Caches and Prefetch queue */ + BX_CPU_C *cpu_context = BX_CPU(cpu->getId()); + cpu_context->invalidate_prefetch_q(); + cpu_context->iCache.flushICacheEntries(); + + guest_address_t pc = cpu->getInstructionPointer(); + bxInstruction_c *currInstr = this->getCurrentInstruction(); + + m_log << "REDECODE INSTRUCTION @ IP 0x" << std::hex << pc << std::endl; + + guest_address_t eipBiased = pc + cpu_context->eipPageBias; + Bit8u instr_plain[15]; + + MemoryManager& mm = this->getMemoryManager(); + for (unsigned i = 0; i < sizeof(instr_plain); ++i) { + if (!mm.isMapped(pc + i)) { + m_log << "REDECODE: 0x" << std::hex << pc+i << "UNMAPPED" << std::endl; + // TODO: error? + return; + } + } + + mm.getBytes(pc, sizeof(instr_plain), instr_plain); + + guest_address_t remainingInPage = cpu_context->eipPageWindowSize - eipBiased; + int ret; +#if BX_SUPPORT_X86_64 + if (cpu_context->cpu_mode == BX_MODE_LONG_64) + ret = cpu_context->fetchDecode64(instr_plain, currInstr, remainingInPage); + else +#endif + ret = cpu_context->fetchDecode32(instr_plain, currInstr, remainingInPage); + if (ret < 0) { + // handle instrumentation callback inside boundaryFetch + cpu_context->boundaryFetch(instr_plain, remainingInPage, currInstr); + } +} + + } // end-of-namespace: fail diff --git a/src/core/sal/bochs/BochsController.hpp b/src/core/sal/bochs/BochsController.hpp index 7d5184e2..52746ada 100644 --- a/src/core/sal/bochs/BochsController.hpp +++ b/src/core/sal/bochs/BochsController.hpp @@ -121,6 +121,11 @@ class BochsController : public SimulatorController { * calling simulator.restore(). */ virtual simtime_t getTimerTicksPerSecond() { return bx_pc_system.time_ticks() / bx_pc_system.time_usec() * 1000000; /* imprecise hack */ } + /** + * Redecode instruction after we have changed RID_PC. Has to be + * called after setRegisterContent(RID_PC). + */ + virtual void redecodeCurrentInstruction(ConcreteCPU* cpu); /* ******************************************************************** * BochsController-specific (not implemented in SimulatorController!): * ********************************************************************/ diff --git a/src/core/sal/faultspace/BaseFaultSpace.cc b/src/core/sal/faultspace/BaseFaultSpace.cc new file mode 100644 index 00000000..0233f5d3 --- /dev/null +++ b/src/core/sal/faultspace/BaseFaultSpace.cc @@ -0,0 +1,56 @@ +#include "BaseFaultSpace.hpp" +#include "util/Logger.hpp" + +namespace fail { + +using namespace std; + +static Logger LOG("FaultSpace", false); + +void BaseFaultSpace::set_point(fsp_address_t point) { + if (point <= bumping_ptr) { + throw new std::runtime_error("cannot decrease fault-space bumping ptr"); + } + bumping_ptr = point; +} + +void +BaseFaultSpace::add_area(unique_ptr area) { + Logger LOG("FaultSpace", false); + LOG << "mapping " << area->get_name() << " @ 0x" << std::hex << bumping_ptr << std::endl; + area->set_offset(this->bumping_ptr); + size_t size = area->get_size(); + m_areas.emplace(bumping_ptr, std::move(area)); + bumping_ptr += size; +} + + +FaultSpaceArea& +BaseFaultSpace::get_area(const std::string& name) { + for(auto& kv: m_areas) { + if(kv.second->get_name() == name) { + return *kv.second; + } + } + throw std::invalid_argument("couldn't find area with name: " + name); +} + +// recreate an element from a previously generated fault space address +unique_ptr BaseFaultSpace::decode(fsp_address_t addr) { + auto upper_bound = m_areas.upper_bound(addr); + if(upper_bound == m_areas.begin()) { + throw std::invalid_argument("invalid address in fault space: too low"); + } + auto& area = std::prev(upper_bound)->second; + LOG << "decoding 0x" << std::hex << addr << " with area: " << area->get_name() << std::endl; + fsp_offset_t relative = addr - area->get_offset(); + return area->decode(relative); +} + +FaultSpaceElement::injector_result +FaultSpaceElement::inject(FaultSpaceElement::injector_fn injector) { + throw new std::runtime_error("can't inject a non-subclassed fault space element"); + }; + +} + diff --git a/src/core/sal/faultspace/BaseFaultSpace.hpp b/src/core/sal/faultspace/BaseFaultSpace.hpp new file mode 100644 index 00000000..8182b903 --- /dev/null +++ b/src/core/sal/faultspace/BaseFaultSpace.hpp @@ -0,0 +1,201 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace fail { + +typedef uint64_t fsp_address_t; +typedef uint64_t fsp_offset_t; + + +class FaultSpaceElement; + +/** + * Abstraction for a specific area inside the fault space. The given + * implementation can translate existing fault space elements which + * belong to this area into absolute fault space addresses. When + * subclassed, the specific subclass needs to provide a way to + * generate fault space elements, which then can be mapped through the + * base class. Additionally the subclass must provide function for + * decoding an absolute fault space address to a injectable fault + * space element and a method for getting its size. + */ + +class FaultSpaceArea { +private: +protected: + const std::string m_name; + fsp_offset_t m_offset; +public: + FaultSpaceArea(const std::string name): m_name(std::move(name)) { } + FaultSpaceArea(FaultSpaceArea&) = delete; + FaultSpaceArea(const FaultSpaceArea&) = delete; + virtual ~FaultSpaceArea() { } + + void set_offset(fsp_offset_t offset) { m_offset = offset; } + fsp_offset_t get_offset() const { return m_offset; } + + const std::string& get_name() const { return m_name; } + + // Returns the size of this area. + virtual const size_t get_size() const = 0; + + // recreate a previsouly encoded element from the fault space address + // the address passed to this function will be relative to the fault areas + // mapping. + virtual std::unique_ptr decode(fsp_offset_t addr) = 0; +}; + +/** + * Abstraction for a single element inside the fault space. + * It belongs to an area, and saves additional information to the + * point in the fault space such as. + * NOTE: This is copy constructible, but this functionality should only + * be used, if the element is not used for injecting since the inject + * method is virtual and overriden in specific subclasses. + * every fault space element is 1 byte or 8 bit in length, since fault + * space memory is byte-addressed. + * It may however only inject a subset of these 8 bits, more + * specifically, all bits that are set in m_mask + */ + +class FaultSpaceElement { +protected: + FaultSpaceArea* m_area; + const fsp_offset_t m_offset; + +public: + FaultSpaceElement(FaultSpaceArea* area, fsp_offset_t offset) + : m_area(area), m_offset(offset) + { } + + // copy + move constructor + FaultSpaceElement(const FaultSpaceElement& e) = default; + FaultSpaceElement(FaultSpaceElement&& e) = default; + FaultSpaceElement() = delete; + + virtual ~FaultSpaceElement() { } + + FaultSpaceArea* get_area() const { return m_area; }; + void set_area(FaultSpaceArea* a) { m_area = a; } + + fsp_offset_t get_offset() const { return m_offset; } + + // !< Calculate the absolute fault space address + fsp_address_t get_address() const { + return this->get_area()->get_offset() + this->get_offset(); + } + + inline bool operator==(const FaultSpaceElement& rhs) const { + return m_area == rhs.get_area() && m_offset == rhs.get_offset(); + } + + /** + * Compare two fault space elements for their total ordering + */ + bool operator<(const FaultSpaceElement& other) const { + return this->get_address() < other.get_address(); + } + + // each fault space element is at most 8 bit in length, thus its + // value and/orinjected may also only be at most 8 bit long. + typedef uint8_t injector_value; + struct injector_result { + uint8_t original; + uint8_t injected; + }; + typedef std::function injector_fn; + + + /** Inject the given element using the injector function. + * The injector function takes the original value and returns + * the injected value. + * Implementations of this function should then wrap both values inside the + * injector_result_t struct and return them. + * NOTE: this should be pure virtual, but if it is, element is not copy constructible. + * instead we provide a default implementation, here which throws an + * exception. + */ + virtual injector_result inject(injector_fn injector); + + /** + * Describe this element for output in an ostream. + * This function is called by the element ostream<< + * operator to describe this element instance. + * May be override in subclasses to provide a more + * in-depth description. + * */ + virtual std::ostream& dump(std::ostream & os) const { + return (os << "{ generic element " + << " @ " + << std::hex << std::showbase + << get_offset() + << " in area '" + << get_area()->get_name() + << "' }"); + } +}; + + +/** + * Output a faultspace element to an ostream It uses the virtual + * get_description() function so that element subclasses may provde + * their own description + */ +inline std::ostream& operator<<(std::ostream& os, const FaultSpaceElement& e) { + return e.dump(os); +} + +/** + * This class is a fault space abstraction. It contains one or more + * memory areas, who themselves can encode or decode elements into or + * from this fault space. + * + * To generate an element, i.e. when importing a trace, subclass + * FaultSpaceArea for your use case and provide a generation method and + * a size for your fault space area. Finally pass the generated element + * to this classes encode function to get an absolute fault space address. + * + * To decode a fault space address an element, i.e. when executing an + * experiment, use the provided decode method, which automatically returns + * an element which can be injected. + * + * It is possible to have multiple fault space instances, but within + * one instance of FAIL*, they are all consistent with each other. + * + * NOTE: make sure all your areas mapped with this abstraction fit in + * sizeof(address_t) (currently 64-Bit). Otherwise the generated map might + * not work correctly and this abstraction will yield weird results. + */ +class BaseFaultSpace { +private: + std::map> m_areas; + fsp_address_t bumping_ptr = 0; + +protected: + void set_point(fsp_address_t point); + + void add_area(std::unique_ptr area); +public: + BaseFaultSpace() {}; + virtual ~BaseFaultSpace() { } + + + + // return an area matching the provided name + // used to create elements in this area + FaultSpaceArea& get_area(const std::string& name); + + // recreate an element from a previously generated fault space address + std::unique_ptr decode(fsp_address_t addr); +}; + +} /* namespace fail */ diff --git a/src/core/sal/faultspace/CMakeLists.txt b/src/core/sal/faultspace/CMakeLists.txt new file mode 100644 index 00000000..13e2fa12 --- /dev/null +++ b/src/core/sal/faultspace/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(fail-fsp + BaseFaultSpace.cc + MemoryArea.cc + RegisterArea.cc + ../x86/X86FaultSpace.cc +) diff --git a/src/core/sal/faultspace/FaultSpace.hpp b/src/core/sal/faultspace/FaultSpace.hpp new file mode 100644 index 00000000..898c5e73 --- /dev/null +++ b/src/core/sal/faultspace/FaultSpace.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "config/FailConfig.hpp" +#include "config/VariantConfig.hpp" + + +#if defined(BUILD_X86) +#include "sal/x86/X86FaultSpace.hpp" +namespace fail { +typedef X86FaultSpace FaultSpace; +} +#elif defined(BUILD_ARM) +#include "sal/arm/ArmFaultSpace.hpp" +namespace fail { +typedef ArmFaultSpace FaultSpace; +} +#else +#error Architecture does not yet support the Virtual Fault Space abstraction +#endif + diff --git a/src/core/sal/faultspace/MemoryArea.cc b/src/core/sal/faultspace/MemoryArea.cc new file mode 100644 index 00000000..64aa4093 --- /dev/null +++ b/src/core/sal/faultspace/MemoryArea.cc @@ -0,0 +1,62 @@ +#include "MemoryArea.hpp" +#include "util/Logger.hpp" +#include + +namespace fail { + +static Logger LOG("FaultSpace", false); + + +guest_address_t +MemoryElement::get_guest_address() const { + return static_cast(get_area())->get_base() + + static_cast(get_offset()); +} + +FaultSpaceElement::injector_result +MemoryElement::inject(injector_fn injector) { + const MemoryArea* ma = dynamic_cast(get_area()); + assert(ma != nullptr); + auto mm = ma->get_manager(); + assert(mm != nullptr); + + guest_address_t addr = get_guest_address(); + byte_t byte = mm->getByte(addr); + injector_value injected = injector(byte); + + mm->setByte(addr, injected); + return { .original = byte, .injected = injected }; +} + +std::ostream& +MemoryElement::dump(std::ostream &os) const { + return (os << "{ MemoryElement for addr " + << std::hex << std::showbase + << get_guest_address() + << " (mapped at=" + << (get_area()->get_offset() + get_offset()) + << ") } "); +} + +std::vector +MemoryArea::translate(guest_address_t from, guest_address_t to, + std::function filter) { + std::vector ret; + for(guest_address_t it = from; it < to; ++it) { + if(filter(it)) { + ret.push_back(MemoryElement(this, it - get_base())); + } + } + return ret; +} + + + +std::unique_ptr +MemoryArea::decode(fsp_offset_t addr) { + auto e = std::make_unique(this, addr); + return std::move(e); +} + +} /* namespace fsp */ + diff --git a/src/core/sal/faultspace/MemoryArea.hpp b/src/core/sal/faultspace/MemoryArea.hpp new file mode 100644 index 00000000..28306a74 --- /dev/null +++ b/src/core/sal/faultspace/MemoryArea.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "BaseFaultSpace.hpp" + + +namespace fail { + +class MemoryArea; + +class MemoryElement : public FaultSpaceElement { +public: + + MemoryElement(FaultSpaceArea* area, fsp_offset_t addr) + : FaultSpaceElement(area, addr) { } + + virtual std::ostream& dump(std::ostream & os) const override; + virtual injector_result inject(injector_fn injector) override; + guest_address_t get_guest_address() const; +}; + + + +class MemoryArea: public FaultSpaceArea { +private: + MemoryManager* m_mm; + guest_address_t m_base; + size_t m_size; +public: + MemoryArea(std::string name, guest_address_t base, size_t size) + : FaultSpaceArea(name), m_mm(nullptr), m_base(base), m_size(size) { } + + fail::MemoryManager * get_manager() const { return m_mm; } + void set_manager(MemoryManager *mm) { m_mm = mm; } + + virtual std::unique_ptr decode(fsp_address_t addr) override; + + guest_address_t get_base() const { return m_base; } + virtual const size_t get_size() const override { return m_size; } + + + // For the Importers, we translate memory ranges to memory elements + std::vector translate(guest_address_t from, guest_address_t to, + std::function filter); + + +}; + +} /* end-of-namespace fail */ + + diff --git a/src/core/sal/faultspace/RegisterArea.cc b/src/core/sal/faultspace/RegisterArea.cc new file mode 100644 index 00000000..b4b3186e --- /dev/null +++ b/src/core/sal/faultspace/RegisterArea.cc @@ -0,0 +1,110 @@ +#include "sal/faultspace/RegisterArea.hpp" +#include "sal/Register.hpp" +#include "sal/SALInst.hpp" +#include // std::ceil +#include + +namespace fail { + +static Logger LOG("RegisterArea", false); + +RegisterArea::RegisterArea() + : FaultSpaceArea("register"), m_state(nullptr) { + fsp_offset_t offset = 0; + for (Register *r : m_arch) { + if (r == nullptr) + continue; // Register IDs can have wholes + m_addr_to_reg[offset] = r; + m_reg_to_addr[r] = offset; + offset += std::ceil(r->getWidth()/8.0); + } + m_size = offset; +} + +std::vector> +RegisterArea::translate(const RegisterView &view) { + std::vector> ret; + + // Generate access mask. There is surely a more efficient way to + // do this. But this implementation can be understood more easily. + unsigned char masks[sizeof(regdata_t)] = {0}; + for (unsigned i = view.offset; i < (view.offset + view.width); i++) { + masks[i / 8] |= 1 << (i% 8); + } + + // Get the FAIL* base register and its location in the faultspace + Register* base = m_arch.getRegister(view.id); + fsp_offset_t base_offset = m_reg_to_addr[base]; + for (int byte = 0; byte < sizeof(regdata_t); byte++) { + if (masks[byte] == 0) continue; + ret.push_back( + std::make_pair( + RegisterElement(this, base_offset + byte, base, byte), + masks[byte]) + ); + } + + return ret; +} + +std::unique_ptr +RegisterArea::decode(fsp_address_t addr) { + // find next Register that is mapped below this address. + auto upper_bound = m_addr_to_reg.upper_bound(addr); + if(upper_bound == m_addr_to_reg.begin()) { + throw std::invalid_argument("invalid address in fault area: too low"); + } + Register* reg = std::prev(upper_bound)->second; + // if we subtract this registers base address, we get the byte offset + unsigned byte = addr - m_reg_to_addr[reg]; + + auto e = std::make_unique(this,addr,reg,byte); + return std::move(e); +} + + +FaultSpaceElement::injector_result +RegisterElement::inject(injector_fn injector) +{ + RegisterArea *area = dynamic_cast(get_area()); + assert(area != nullptr); + auto cpu = area->get_state(); + Register *base = get_base(); + + + regdata_t value = cpu->getRegisterContent(base); + + assert(sizeof(injector_value) == 1); + + injector_value original_byte = static_cast((value >> m_byte*8) & 0xFF); + injector_value injected_byte = injector(original_byte); + // mask out original byte + regdata_t mask = ~(static_cast(0xFF) << m_byte*8); + regdata_t injected_value = value & mask; + // and update with injected version + injected_value |= static_cast(injected_byte) << m_byte*8; + + cpu->setRegisterContent(base, injected_value); + + LOG << "injecting register " << base->getName() << "(id=" << base->getId() << ") at offset " << std::dec << m_byte << std::endl + << std::hex << std::showbase + << '\t' << "previous value: " << value << std::endl + << '\t' << "injected value: " << injected_value << std::endl + << std::dec << std::noshowbase; + + return { .original = original_byte, .injected = injected_byte }; +} + +std::ostream& RegisterElement::dump(std::ostream & os) const { + return ( + os << "{ RegisterElement '" + << get_base()->getName() + << "' at byte " + << std::hex << std::showbase + << m_byte + << " (mapped @ " << get_area()->get_offset() << "+"<< get_offset() + << " ->" << (get_offset() + get_area()->get_offset()) + << ") } "); +} + +} // end-of-namespace: fail diff --git a/src/core/sal/faultspace/RegisterArea.hpp b/src/core/sal/faultspace/RegisterArea.hpp new file mode 100644 index 00000000..6425af62 --- /dev/null +++ b/src/core/sal/faultspace/RegisterArea.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include "sal/Architecture.hpp" +#include "sal/Register.hpp" +#include "sal/CPUState.hpp" +#include "BaseFaultSpace.hpp" + +namespace fail { + +class RegisterArea; + +class RegisterElement: public FaultSpaceElement { +private: + Register* m_register; + unsigned m_byte; + +public: + RegisterElement(FaultSpaceArea *area, fsp_offset_t offset, + Register* reg, unsigned byte_offset) + : FaultSpaceElement(area, offset), + m_register(reg), m_byte(byte_offset) { + assert(byte_offset < reg->getWidth()/8); + }; + + virtual injector_result inject(injector_fn injector) override; + virtual std::ostream& dump(std::ostream & os) const override; + + Register* get_base() const { return m_register; } +}; + +class CPUState; + +class RegisterArea: public FaultSpaceArea { +private: + std::map m_addr_to_reg; + std::map m_reg_to_addr; + size_t m_size; + +protected: + fail::Architecture m_arch; + fail::CPUState* m_state; +public: + RegisterArea(); + + fail::Architecture * get_arch() { return &m_arch; } + + CPUState * get_state() { return m_state; } + void set_state(CPUState *state) { m_state = state; } + + virtual const size_t get_size() const override { return m_size; } + + virtual std::unique_ptr decode(fsp_address_t addr) override; + + // For the RegisterImporter, we translate Register IDs to FaultSpaceElements + virtual std::vector> translate(const RegisterView &); +}; + + + +} // end-of-namespace: fail + + diff --git a/src/core/sal/x86/X86FaultSpace.cc b/src/core/sal/x86/X86FaultSpace.cc new file mode 100644 index 00000000..89da9d93 --- /dev/null +++ b/src/core/sal/x86/X86FaultSpace.cc @@ -0,0 +1,20 @@ +#include +#include "X86FaultSpace.hpp" +#include "sal/faultspace/MemoryArea.hpp" +#include "sal/faultspace/RegisterArea.hpp" + + +fail::X86FaultSpace::X86FaultSpace() { + // Order Matters! + add_area(std::make_unique()); + + // This magic register break was used in pre-faultspace version. + // We stick to this value to remain as compatible as possible with + // the old data_addresses + unsigned register_break = (128 << 4); + set_point(register_break); + + // 512-4GiB of RAM + add_area(std::make_unique("ram", register_break, 0xFFFFFFFF-register_break)); +} + diff --git a/src/core/sal/x86/X86FaultSpace.hpp b/src/core/sal/x86/X86FaultSpace.hpp new file mode 100644 index 00000000..f90abd81 --- /dev/null +++ b/src/core/sal/x86/X86FaultSpace.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "sal/faultspace/BaseFaultSpace.hpp" + +namespace fail { +class X86FaultSpace: public BaseFaultSpace { +public: + X86FaultSpace(); +}; + +} // end-of-namespace: fail + diff --git a/src/core/util/ProtoStream.cc b/src/core/util/ProtoStream.cc index ea6f6c21..99f58661 100644 --- a/src/core/util/ProtoStream.cc +++ b/src/core/util/ProtoStream.cc @@ -11,7 +11,7 @@ ProtoOStream::ProtoOStream(std::ostream *outfile) : m_outfile(outfile) bool ProtoOStream::writeMessage(google::protobuf::Message *m) { - uint32_t m_size = htonl(m->ByteSize()); + uint32_t m_size = htonl((uint32_t)m->ByteSizeLong()); m_outfile->write(reinterpret_cast(&m_size), sizeof(m_size)); if (m_outfile->bad()) { diff --git a/src/core/util/capstonedisassembler/CapstoneDisassembler.cpp b/src/core/util/capstonedisassembler/CapstoneDisassembler.cpp index 424b01be..62fc4bd3 100644 --- a/src/core/util/capstonedisassembler/CapstoneDisassembler.cpp +++ b/src/core/util/capstonedisassembler/CapstoneDisassembler.cpp @@ -13,10 +13,10 @@ CapstoneToFailTranslator *CapstoneDisassembler::getTranslator() { switch (m_elf->m_machine) { case EM_386: case EM_X86_64: - ctofail = new CapstoneToFailBochs(); + ctofail = new CapstoneToFailBochs(this); break; case EM_ARM: - ctofail = new CapstoneToFailGem5(); + ctofail = new CapstoneToFailGem5(this); break; default: std::cerr << "ArchType " @@ -88,7 +88,7 @@ int CapstoneDisassembler::disassemble_section(Elf_Data *data, Elf32_Shdr *shdr32 #if 0 std::cout << std::dec << "bit: " << m_elf->m_elfclass << " 32: "<< ELFCLASS32 << " 64: " << ELFCLASS64 << " arch: " << m_elf->m_machine << " arm:" << EM_ARM << " x86: " << EM_386 << " x86_64: "<< EM_X86_64 << std::endl; #endif - csh handle; + csh handle = 0; cs_insn *insn; size_t count, j; cs_regs regs_read, regs_write; @@ -113,6 +113,8 @@ int CapstoneDisassembler::disassemble_section(Elf_Data *data, Elf32_Shdr *shdr32 if (cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &handle) != CS_ERR_OK) return -1; } + } else { + return -1; } cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON); diff --git a/src/core/util/capstonedisassembler/CapstoneDisassembler.hpp b/src/core/util/capstonedisassembler/CapstoneDisassembler.hpp index 749a089c..ba23fe82 100644 --- a/src/core/util/capstonedisassembler/CapstoneDisassembler.hpp +++ b/src/core/util/capstonedisassembler/CapstoneDisassembler.hpp @@ -61,19 +61,30 @@ class CapstoneDisassembler { return true; } + unsigned m_xlen; + public: CapstoneDisassembler(fail::ElfReader *elf) : ctofail(0) { this->m_elf = elf; this->instrs.reset(new InstrMap()); + m_xlen = elf->m_elfclass == ELFCLASS64 ? 64 : 32; } ~CapstoneDisassembler() { delete ctofail; }; - InstrMap &getInstrMap() { return *instrs; }; + std::string getRegisterName(unsigned id) { + std::stringstream ss; + ss << id; + return ss.str(); + } + + InstrMap *getInstrMap() { return instrs.get(); }; fail::CapstoneToFailTranslator *getTranslator(); void disassemble(); + unsigned getWordWidth() { return m_xlen; } + private: int disassemble_section(Elf_Data *data, Elf32_Shdr *shdr, Elf64_Shdr *shdr64, std::map symtab_map); std::map get_symtab_map(uint64_t sect_addr, uint64_t sect_size); diff --git a/src/core/util/capstonedisassembler/CapstoneToFailBochs.cpp b/src/core/util/capstonedisassembler/CapstoneToFailBochs.cpp index 209fdca0..82175f52 100644 --- a/src/core/util/capstonedisassembler/CapstoneToFailBochs.cpp +++ b/src/core/util/capstonedisassembler/CapstoneToFailBochs.cpp @@ -1,103 +1,104 @@ #include #include "CapstoneToFailBochs.hpp" #include "sal/x86/X86Architecture.hpp" +#include "CapstoneDisassembler.hpp" using namespace fail; -CapstoneToFailBochs::CapstoneToFailBochs() { - capstone_to_fail_map[X86_REG_AH] = reginfo_t(RID_CAX, 8, 8); - capstone_to_fail_map[X86_REG_AL] = reginfo_t(RID_CAX, 8); - capstone_to_fail_map[X86_REG_AX] = reginfo_t(RID_CAX, 16); - capstone_to_fail_map[X86_REG_EAX] = reginfo_t(RID_CAX, 32); - capstone_to_fail_map[X86_REG_RAX] = reginfo_t(RID_CAX, 64); +CapstoneToFailBochs::CapstoneToFailBochs(CapstoneDisassembler *disas) { + capstone_to_fail_map[X86_REG_AH] = RegisterView(RID_CAX, 8, 8); + capstone_to_fail_map[X86_REG_AL] = RegisterView(RID_CAX, 8); + capstone_to_fail_map[X86_REG_AX] = RegisterView(RID_CAX, 16); + capstone_to_fail_map[X86_REG_EAX] = RegisterView(RID_CAX, 32); + capstone_to_fail_map[X86_REG_RAX] = RegisterView(RID_CAX, 64); - capstone_to_fail_map[X86_REG_BH] = reginfo_t(RID_CBX, 8, 8); - capstone_to_fail_map[X86_REG_BL] = reginfo_t(RID_CBX, 8); - capstone_to_fail_map[X86_REG_BX] = reginfo_t(RID_CBX, 16); - capstone_to_fail_map[X86_REG_EBX] = reginfo_t(RID_CBX, 32); - capstone_to_fail_map[X86_REG_RBX] = reginfo_t(RID_CBX, 64); + capstone_to_fail_map[X86_REG_BH] = RegisterView(RID_CBX, 8, 8); + capstone_to_fail_map[X86_REG_BL] = RegisterView(RID_CBX, 8); + capstone_to_fail_map[X86_REG_BX] = RegisterView(RID_CBX, 16); + capstone_to_fail_map[X86_REG_EBX] = RegisterView(RID_CBX, 32); + capstone_to_fail_map[X86_REG_RBX] = RegisterView(RID_CBX, 64); - capstone_to_fail_map[X86_REG_CH] = reginfo_t(RID_CCX, 8, 8); - capstone_to_fail_map[X86_REG_CL] = reginfo_t(RID_CCX, 8); - capstone_to_fail_map[X86_REG_CX] = reginfo_t(RID_CCX, 16); - capstone_to_fail_map[X86_REG_ECX] = reginfo_t(RID_CCX, 32); - capstone_to_fail_map[X86_REG_RCX] = reginfo_t(RID_CCX, 64); + capstone_to_fail_map[X86_REG_CH] = RegisterView(RID_CCX, 8, 8); + capstone_to_fail_map[X86_REG_CL] = RegisterView(RID_CCX, 8); + capstone_to_fail_map[X86_REG_CX] = RegisterView(RID_CCX, 16); + capstone_to_fail_map[X86_REG_ECX] = RegisterView(RID_CCX, 32); + capstone_to_fail_map[X86_REG_RCX] = RegisterView(RID_CCX, 64); - capstone_to_fail_map[X86_REG_DH] = reginfo_t(RID_CDX, 8, 8); - capstone_to_fail_map[X86_REG_DL] = reginfo_t(RID_CDX, 8); - capstone_to_fail_map[X86_REG_DX] = reginfo_t(RID_CDX, 16); - capstone_to_fail_map[X86_REG_EDX] = reginfo_t(RID_CDX, 32); - capstone_to_fail_map[X86_REG_RDX] = reginfo_t(RID_CDX, 64); + capstone_to_fail_map[X86_REG_DH] = RegisterView(RID_CDX, 8, 8); + capstone_to_fail_map[X86_REG_DL] = RegisterView(RID_CDX, 8); + capstone_to_fail_map[X86_REG_DX] = RegisterView(RID_CDX, 16); + capstone_to_fail_map[X86_REG_EDX] = RegisterView(RID_CDX, 32); + capstone_to_fail_map[X86_REG_RDX] = RegisterView(RID_CDX, 64); - capstone_to_fail_map[X86_REG_R8] = reginfo_t(RID_R8, 64); - capstone_to_fail_map[X86_REG_R8D] = reginfo_t(RID_R8, 32); - capstone_to_fail_map[X86_REG_R8W] = reginfo_t(RID_R8, 16); - capstone_to_fail_map[X86_REG_R8B] = reginfo_t(RID_R8, 8); - capstone_to_fail_map[X86_REG_R9] = reginfo_t(RID_R9, 64); - capstone_to_fail_map[X86_REG_R9D] = reginfo_t(RID_R9, 32); - capstone_to_fail_map[X86_REG_R9W] = reginfo_t(RID_R9, 16); - capstone_to_fail_map[X86_REG_R9B] = reginfo_t(RID_R9, 8); - capstone_to_fail_map[X86_REG_R10] = reginfo_t(RID_R10, 64); - capstone_to_fail_map[X86_REG_R10D] = reginfo_t(RID_R10, 32); - capstone_to_fail_map[X86_REG_R10W] = reginfo_t(RID_R10, 16); - capstone_to_fail_map[X86_REG_R10B] = reginfo_t(RID_R10, 8); - capstone_to_fail_map[X86_REG_R11] = reginfo_t(RID_R11, 64); - capstone_to_fail_map[X86_REG_R11D] = reginfo_t(RID_R11, 32); - capstone_to_fail_map[X86_REG_R11W] = reginfo_t(RID_R11, 16); - capstone_to_fail_map[X86_REG_R11B] = reginfo_t(RID_R11, 8); - capstone_to_fail_map[X86_REG_R12] = reginfo_t(RID_R12, 64); - capstone_to_fail_map[X86_REG_R12D] = reginfo_t(RID_R12, 32); - capstone_to_fail_map[X86_REG_R12W] = reginfo_t(RID_R12, 16); - capstone_to_fail_map[X86_REG_R12B] = reginfo_t(RID_R12, 8); - capstone_to_fail_map[X86_REG_R13] = reginfo_t(RID_R13, 64); - capstone_to_fail_map[X86_REG_R13D] = reginfo_t(RID_R13, 32); - capstone_to_fail_map[X86_REG_R13W] = reginfo_t(RID_R13, 16); - capstone_to_fail_map[X86_REG_R13B] = reginfo_t(RID_R13, 8); - capstone_to_fail_map[X86_REG_R14] = reginfo_t(RID_R14, 64); - capstone_to_fail_map[X86_REG_R14D] = reginfo_t(RID_R14, 32); - capstone_to_fail_map[X86_REG_R14W] = reginfo_t(RID_R14, 16); - capstone_to_fail_map[X86_REG_R14B] = reginfo_t(RID_R14, 8); - capstone_to_fail_map[X86_REG_R15] = reginfo_t(RID_R15, 64); - capstone_to_fail_map[X86_REG_R15D] = reginfo_t(RID_R15, 32); - capstone_to_fail_map[X86_REG_R15W] = reginfo_t(RID_R15, 16); - capstone_to_fail_map[X86_REG_R15B] = reginfo_t(RID_R15, 8); + capstone_to_fail_map[X86_REG_R8] = RegisterView(RID_R8, 64); + capstone_to_fail_map[X86_REG_R8D] = RegisterView(RID_R8, 32); + capstone_to_fail_map[X86_REG_R8W] = RegisterView(RID_R8, 16); + capstone_to_fail_map[X86_REG_R8B] = RegisterView(RID_R8, 8); + capstone_to_fail_map[X86_REG_R9] = RegisterView(RID_R9, 64); + capstone_to_fail_map[X86_REG_R9D] = RegisterView(RID_R9, 32); + capstone_to_fail_map[X86_REG_R9W] = RegisterView(RID_R9, 16); + capstone_to_fail_map[X86_REG_R9B] = RegisterView(RID_R9, 8); + capstone_to_fail_map[X86_REG_R10] = RegisterView(RID_R10, 64); + capstone_to_fail_map[X86_REG_R10D] = RegisterView(RID_R10, 32); + capstone_to_fail_map[X86_REG_R10W] = RegisterView(RID_R10, 16); + capstone_to_fail_map[X86_REG_R10B] = RegisterView(RID_R10, 8); + capstone_to_fail_map[X86_REG_R11] = RegisterView(RID_R11, 64); + capstone_to_fail_map[X86_REG_R11D] = RegisterView(RID_R11, 32); + capstone_to_fail_map[X86_REG_R11W] = RegisterView(RID_R11, 16); + capstone_to_fail_map[X86_REG_R11B] = RegisterView(RID_R11, 8); + capstone_to_fail_map[X86_REG_R12] = RegisterView(RID_R12, 64); + capstone_to_fail_map[X86_REG_R12D] = RegisterView(RID_R12, 32); + capstone_to_fail_map[X86_REG_R12W] = RegisterView(RID_R12, 16); + capstone_to_fail_map[X86_REG_R12B] = RegisterView(RID_R12, 8); + capstone_to_fail_map[X86_REG_R13] = RegisterView(RID_R13, 64); + capstone_to_fail_map[X86_REG_R13D] = RegisterView(RID_R13, 32); + capstone_to_fail_map[X86_REG_R13W] = RegisterView(RID_R13, 16); + capstone_to_fail_map[X86_REG_R13B] = RegisterView(RID_R13, 8); + capstone_to_fail_map[X86_REG_R14] = RegisterView(RID_R14, 64); + capstone_to_fail_map[X86_REG_R14D] = RegisterView(RID_R14, 32); + capstone_to_fail_map[X86_REG_R14W] = RegisterView(RID_R14, 16); + capstone_to_fail_map[X86_REG_R14B] = RegisterView(RID_R14, 8); + capstone_to_fail_map[X86_REG_R15] = RegisterView(RID_R15, 64); + capstone_to_fail_map[X86_REG_R15D] = RegisterView(RID_R15, 32); + capstone_to_fail_map[X86_REG_R15W] = RegisterView(RID_R15, 16); + capstone_to_fail_map[X86_REG_R15B] = RegisterView(RID_R15, 8); - capstone_to_fail_map[X86_REG_DIL] = reginfo_t(RID_CDI, 8); - capstone_to_fail_map[X86_REG_DI] = reginfo_t(RID_CDI, 16); - capstone_to_fail_map[X86_REG_EDI] = reginfo_t(RID_CDI, 32); - capstone_to_fail_map[X86_REG_RDI] = reginfo_t(RID_CDI, 64); + capstone_to_fail_map[X86_REG_DIL] = RegisterView(RID_CDI, 8); + capstone_to_fail_map[X86_REG_DI] = RegisterView(RID_CDI, 16); + capstone_to_fail_map[X86_REG_EDI] = RegisterView(RID_CDI, 32); + capstone_to_fail_map[X86_REG_RDI] = RegisterView(RID_CDI, 64); - capstone_to_fail_map[X86_REG_BPL] = reginfo_t(RID_CBP, 8); - capstone_to_fail_map[X86_REG_BP] = reginfo_t(RID_CBP, 16); - capstone_to_fail_map[X86_REG_EBP] = reginfo_t(RID_CBP, 32); - capstone_to_fail_map[X86_REG_RBP] = reginfo_t(RID_CBP, 64); + capstone_to_fail_map[X86_REG_BPL] = RegisterView(RID_CBP, 8); + capstone_to_fail_map[X86_REG_BP] = RegisterView(RID_CBP, 16); + capstone_to_fail_map[X86_REG_EBP] = RegisterView(RID_CBP, 32); + capstone_to_fail_map[X86_REG_RBP] = RegisterView(RID_CBP, 64); - capstone_to_fail_map[X86_REG_EFLAGS] = reginfo_t(RID_FLAGS, 64); + capstone_to_fail_map[X86_REG_EFLAGS] = RegisterView(RID_FLAGS, disas->getWordWidth()); // RFLAGS doesn't exist in the x86.h of capstone, therefore X86_REG_EFLAGS is set to 64bit - // capstone_to_fail_map[RFLAGS] = reginfo_t(RID_FLAGS, 64); + // capstone_to_fail_map[RFLAGS] = RegisterView(RID_FLAGS, 64); - capstone_to_fail_map[X86_REG_EIP] = reginfo_t(RID_PC, 32); - capstone_to_fail_map[X86_REG_RIP] = reginfo_t(RID_PC, 64); + capstone_to_fail_map[X86_REG_EIP] = RegisterView(RID_PC, 32); + capstone_to_fail_map[X86_REG_RIP] = RegisterView(RID_PC, 64); - capstone_to_fail_map[X86_REG_SIL] = reginfo_t(RID_CSI, 8); - capstone_to_fail_map[X86_REG_SI] = reginfo_t(RID_CSI, 16); - capstone_to_fail_map[X86_REG_ESI] = reginfo_t(RID_CSI, 32); - capstone_to_fail_map[X86_REG_RSI] = reginfo_t(RID_CSI, 64); + capstone_to_fail_map[X86_REG_SIL] = RegisterView(RID_CSI, 8); + capstone_to_fail_map[X86_REG_SI] = RegisterView(RID_CSI, 16); + capstone_to_fail_map[X86_REG_ESI] = RegisterView(RID_CSI, 32); + capstone_to_fail_map[X86_REG_RSI] = RegisterView(RID_CSI, 64); - capstone_to_fail_map[X86_REG_SPL] = reginfo_t(RID_CSP, 8); - capstone_to_fail_map[X86_REG_SP] = reginfo_t(RID_CSP, 16); - capstone_to_fail_map[X86_REG_ESP] = reginfo_t(RID_CSP, 32); - capstone_to_fail_map[X86_REG_RSP] = reginfo_t(RID_CSP, 64); + capstone_to_fail_map[X86_REG_SPL] = RegisterView(RID_CSP, 8); + capstone_to_fail_map[X86_REG_SP] = RegisterView(RID_CSP, 16); + capstone_to_fail_map[X86_REG_ESP] = RegisterView(RID_CSP, 32); + capstone_to_fail_map[X86_REG_RSP] = RegisterView(RID_CSP, 64); - capstone_to_fail_map[X86_REG_CR0] = reginfo_t(RID_CR0); - capstone_to_fail_map[X86_REG_CR2] = reginfo_t(RID_CR2); - capstone_to_fail_map[X86_REG_CR3] = reginfo_t(RID_CR3); - capstone_to_fail_map[X86_REG_CR4] = reginfo_t(RID_CR4); + capstone_to_fail_map[X86_REG_CR0] = RegisterView(RID_CR0); + capstone_to_fail_map[X86_REG_CR2] = RegisterView(RID_CR2); + capstone_to_fail_map[X86_REG_CR3] = RegisterView(RID_CR3); + capstone_to_fail_map[X86_REG_CR4] = RegisterView(RID_CR4); - capstone_to_fail_map[X86_REG_CS] = reginfo_t(RID_CS, 16); - capstone_to_fail_map[X86_REG_DS] = reginfo_t(RID_DS, 16); - capstone_to_fail_map[X86_REG_ES] = reginfo_t(RID_ES, 16); - capstone_to_fail_map[X86_REG_FS] = reginfo_t(RID_FS, 16); - capstone_to_fail_map[X86_REG_GS] = reginfo_t(RID_GS, 16); - capstone_to_fail_map[X86_REG_SS] = reginfo_t(RID_SS, 16); + capstone_to_fail_map[X86_REG_CS] = RegisterView(RID_CS, 16); + capstone_to_fail_map[X86_REG_DS] = RegisterView(RID_DS, 16); + capstone_to_fail_map[X86_REG_ES] = RegisterView(RID_ES, 16); + capstone_to_fail_map[X86_REG_FS] = RegisterView(RID_FS, 16); + capstone_to_fail_map[X86_REG_GS] = RegisterView(RID_GS, 16); + capstone_to_fail_map[X86_REG_SS] = RegisterView(RID_SS, 16); } diff --git a/src/core/util/capstonedisassembler/CapstoneToFailBochs.hpp b/src/core/util/capstonedisassembler/CapstoneToFailBochs.hpp index 22763931..aa52e9dc 100644 --- a/src/core/util/capstonedisassembler/CapstoneToFailBochs.hpp +++ b/src/core/util/capstonedisassembler/CapstoneToFailBochs.hpp @@ -7,7 +7,7 @@ namespace fail { class CapstoneToFailBochs : public CapstoneToFailTranslator { public: - CapstoneToFailBochs(); + CapstoneToFailBochs(CapstoneDisassembler *disas); }; } // end of namespace diff --git a/src/core/util/capstonedisassembler/CapstoneToFailGem5.cpp b/src/core/util/capstonedisassembler/CapstoneToFailGem5.cpp index 15d8322c..b069f6ff 100644 --- a/src/core/util/capstonedisassembler/CapstoneToFailGem5.cpp +++ b/src/core/util/capstonedisassembler/CapstoneToFailGem5.cpp @@ -1,24 +1,25 @@ #include #include "CapstoneToFailGem5.hpp" #include "sal/arm/ArmArchitecture.hpp" +#include "CapstoneDisassembler.hpp" using namespace fail; -CapstoneToFailGem5::CapstoneToFailGem5() { - capstone_to_fail_map[ARM_REG_R0] = reginfo_t(RI_R0); - capstone_to_fail_map[ARM_REG_R1] = reginfo_t(RI_R1); - capstone_to_fail_map[ARM_REG_R2] = reginfo_t(RI_R2); - capstone_to_fail_map[ARM_REG_R3] = reginfo_t(RI_R3); - capstone_to_fail_map[ARM_REG_R4] = reginfo_t(RI_R4); - capstone_to_fail_map[ARM_REG_R5] = reginfo_t(RI_R5); - capstone_to_fail_map[ARM_REG_R6] = reginfo_t(RI_R6); - capstone_to_fail_map[ARM_REG_R7] = reginfo_t(RI_R7); - capstone_to_fail_map[ARM_REG_R8] = reginfo_t(RI_R8); - capstone_to_fail_map[ARM_REG_R9] = reginfo_t(RI_R9); - capstone_to_fail_map[ARM_REG_R10] = reginfo_t(RI_R10); - capstone_to_fail_map[ARM_REG_R11] = reginfo_t(RI_R11); - capstone_to_fail_map[ARM_REG_R12] = reginfo_t(RI_R12); - capstone_to_fail_map[ARM_REG_SP] = reginfo_t(RI_SP); - capstone_to_fail_map[ARM_REG_LR] = reginfo_t(RI_LR); - capstone_to_fail_map[ARM_REG_PC] = reginfo_t(RI_IP); +CapstoneToFailGem5::CapstoneToFailGem5(CapstoneDisassembler *disas) { + capstone_to_fail_map[ARM_REG_R0] = RegisterView(RI_R0); + capstone_to_fail_map[ARM_REG_R1] = RegisterView(RI_R1); + capstone_to_fail_map[ARM_REG_R2] = RegisterView(RI_R2); + capstone_to_fail_map[ARM_REG_R3] = RegisterView(RI_R3); + capstone_to_fail_map[ARM_REG_R4] = RegisterView(RI_R4); + capstone_to_fail_map[ARM_REG_R5] = RegisterView(RI_R5); + capstone_to_fail_map[ARM_REG_R6] = RegisterView(RI_R6); + capstone_to_fail_map[ARM_REG_R7] = RegisterView(RI_R7); + capstone_to_fail_map[ARM_REG_R8] = RegisterView(RI_R8); + capstone_to_fail_map[ARM_REG_R9] = RegisterView(RI_R9); + capstone_to_fail_map[ARM_REG_R10] = RegisterView(RI_R10); + capstone_to_fail_map[ARM_REG_R11] = RegisterView(RI_R11); + capstone_to_fail_map[ARM_REG_R12] = RegisterView(RI_R12); + capstone_to_fail_map[ARM_REG_SP] = RegisterView(RI_SP); + capstone_to_fail_map[ARM_REG_LR] = RegisterView(RI_LR); + capstone_to_fail_map[ARM_REG_PC] = RegisterView(RI_IP); } diff --git a/src/core/util/capstonedisassembler/CapstoneToFailGem5.hpp b/src/core/util/capstonedisassembler/CapstoneToFailGem5.hpp index ebc9d973..6bdb4d00 100644 --- a/src/core/util/capstonedisassembler/CapstoneToFailGem5.hpp +++ b/src/core/util/capstonedisassembler/CapstoneToFailGem5.hpp @@ -7,7 +7,7 @@ namespace fail { class CapstoneToFailGem5 : public CapstoneToFailTranslator { public: - CapstoneToFailGem5(); + CapstoneToFailGem5(CapstoneDisassembler *); }; } // end of namespace diff --git a/src/core/util/capstonedisassembler/CapstoneToFailTranslator.cpp b/src/core/util/capstonedisassembler/CapstoneToFailTranslator.cpp index a406c0ea..25b8a274 100644 --- a/src/core/util/capstonedisassembler/CapstoneToFailTranslator.cpp +++ b/src/core/util/capstonedisassembler/CapstoneToFailTranslator.cpp @@ -4,7 +4,7 @@ using namespace fail; -const CapstoneToFailTranslator::reginfo_t & CapstoneToFailTranslator::getFailRegisterInfo(unsigned int regid) { +const RegisterView & CapstoneToFailTranslator::getFailRegisterInfo(unsigned int regid) { ctof_map_t::iterator it = capstone_to_fail_map.find(regid); if ( it != capstone_to_fail_map.end() ) {// found return (*it).second; @@ -14,37 +14,3 @@ const CapstoneToFailTranslator::reginfo_t & CapstoneToFailTranslator::getFailRe return notfound; } } - -regdata_t CapstoneToFailTranslator::getRegisterContent(ConcreteCPU& cpu, const reginfo_t ®info){ - regdata_t result; - - Register* reg = cpu.getRegister(reginfo.id); - result = cpu.getRegisterContent(reg); - - result &= reginfo.mask; - result >>= reginfo.offset; - - return result; -} - -void CapstoneToFailTranslator::setRegisterContent(ConcreteCPU & cpu, const reginfo_t ®info, regdata_t value){ - Register* reg = cpu.getRegister(reginfo.id); - - regdata_t origval = cpu.getRegisterContent(reg); // Get register Value from fail - origval &= ~(reginfo.mask); // clear bits to write - - value <<= reginfo.offset; // shift value to write up to position - value &= reginfo.mask; // mask out trailing and leading bits - value |= origval; // set bits to write - - cpu.setRegisterContent( reg, value ); // write back register content -} - -int CapstoneToFailTranslator::getMaxFailRegisterID() -{ - auto max = std::max_element(capstone_to_fail_map.cbegin(), capstone_to_fail_map.cend(), - [] (const ctof_map_t::value_type& v1, const ctof_map_t::value_type& v2) { - return v1.second.id < v2.second.id; - }); - return max->second.id; -} diff --git a/src/core/util/capstonedisassembler/CapstoneToFailTranslator.hpp b/src/core/util/capstonedisassembler/CapstoneToFailTranslator.hpp index e1e777fb..8d0140bf 100644 --- a/src/core/util/capstonedisassembler/CapstoneToFailTranslator.hpp +++ b/src/core/util/capstonedisassembler/CapstoneToFailTranslator.hpp @@ -6,86 +6,30 @@ #include namespace fail { +class CapstoneDisassembler; // Forward /** * Translates Capstone disassembler ids * to FAIL* SAL representations. */ class CapstoneToFailTranslator { -public: - /** - * Maps registers to/from linear addresses usable for def/use-pruning - * purposes and storage in the database. Takes care that the linear - * addresses of x86 subregisters (e.g., AX represents the lower 16 bits of - * EAX) overlap with their siblings. - */ - struct reginfo_t { - int id; - regwidth_t width; - regdata_t mask; - byte_t offset; - - int toDataAddress() const { - // .. 5 4 | 3 2 1 0 - // | - return (id << 4) | (offset / 8); - } - - static reginfo_t fromDataAddress(int addr, int width) { - int id = addr >> 4; - byte_t offset = (addr & 0xf) * 8; - return reginfo_t(id, width * 8, offset); - } - - reginfo_t(int id=-1, regwidth_t width = 32, byte_t offs = 0) - : id(id), width(width), mask((((regdata_t) 1 << width) - 1) << offs), offset(offs) - { - if (width >= sizeof(regdata_t) * 8) { // all ones, (1 << width) == 0! - mask = -1; - } -#if 0 - std::cerr << "constructing reginfo_t: " << std::dec << id << " " << width << " " << ((int)offs) << std::hex << " 0x" << mask << std::endl; -#endif - } - }; protected: CapstoneToFailTranslator(){}; - typedef std::map ctof_map_t; + typedef std::map ctof_map_t; ctof_map_t capstone_to_fail_map; public: /** - * Translates a backend-specific register ID to a Fail register ID. + * Translates a backend-specific register ID to a FAIl RegisterView * @param regid A backend-specific register ID. * @return A FAIL* register-info struct, or CapstonetoFailTranslator::notfound * if no mapping was found. */ - const reginfo_t & getFailRegisterInfo(unsigned int regid); - - static regdata_t getRegisterContent(ConcreteCPU & cpu, const reginfo_t & reg); - static void setRegisterContent(ConcreteCPU & cpu, const reginfo_t ®, regdata_t value); - regdata_t getRegisterContent(ConcreteCPU & cpu, unsigned int csid) { - return getRegisterContent(cpu, getFailRegisterInfo(csid)); - } - void setRegisterContent(ConcreteCPU & cpu, unsigned int csid, regdata_t value) { - setRegisterContent(cpu, getFailRegisterInfo(csid), value); - } - - /** - * Translates a backend-specific register ID to a Fail register ID. - * @param regid A backend-specific register ID. - * @return A FAIL* register ID. May do funny things if regid does not exist. - */ - int getFailRegisterID(unsigned int regid) { return this->getFailRegisterInfo(regid).id; }; - - int getMaxFailRegisterID(); - fail::address_t getMaxDataAddress() { reginfo_t r(getMaxFailRegisterID() + 1); return r.toDataAddress() - 1; } - - reginfo_t notfound; + const RegisterView & getFailRegisterInfo(unsigned int regid); -// static CapstoneToFailTranslator* createFromBinary(const std::string elf_path); + RegisterView notfound; }; } // end of namespace diff --git a/src/core/util/capstonedisassembler/testing/capstoneDisTest.cc b/src/core/util/capstonedisassembler/testing/capstoneDisTest.cc index ba3d9247..2b5abbcf 100644 --- a/src/core/util/capstonedisassembler/testing/capstoneDisTest.cc +++ b/src/core/util/capstonedisassembler/testing/capstoneDisTest.cc @@ -6,7 +6,7 @@ using namespace fail; bool show_mapping(fail::CapstoneToFailTranslator *ctof, unsigned llvmid) { - const CapstoneToFailTranslator::reginfo_t& failreg = ctof->getFailRegisterInfo(llvmid); + const RegisterView& failreg = ctof->getFailRegisterInfo(llvmid); std::cout /*<< reg_info.getName(llvmid)*/ << "(" << std::dec << llvmid << "->"; if (&failreg != &ctof->notfound) { std::cout << failreg.id; @@ -33,7 +33,7 @@ int main(int argc, char* argv[]) { CapstoneDisassembler disas(m_elf); disas.disassemble(); - CapstoneDisassembler::InstrMap &instr_map = disas.getInstrMap(); + CapstoneDisassembler::InstrMap &instr_map = *disas.getInstrMap(); std::cout << "Map Size: " << instr_map.size() << std::endl; CapstoneDisassembler::InstrMap::const_iterator itr; diff --git a/src/core/util/llvmdisassembler/CMakeLists.txt b/src/core/util/llvmdisassembler/CMakeLists.txt index 3fe2a89e..5e9e7bb4 100644 --- a/src/core/util/llvmdisassembler/CMakeLists.txt +++ b/src/core/util/llvmdisassembler/CMakeLists.txt @@ -19,7 +19,7 @@ target_link_libraries(fail-llvmdisassembler ${LLVM_LIBS} ${LLVM_LDFLAGS} ) ### Tests add_executable(llvmDisTest testing/llvmDisTest.cc) -target_link_libraries(llvmDisTest fail-llvmdisassembler fail-sal) +target_link_libraries(llvmDisTest fail-llvmdisassembler) add_test(NAME llvmDisx86Test COMMAND llvmDisTest ${CMAKE_CURRENT_SOURCE_DIR}/testing/x86 ) add_test(NAME llvmDisx86_64Test COMMAND llvmDisTest ${CMAKE_CURRENT_SOURCE_DIR}/testing/x86_64 ) diff --git a/src/core/util/llvmdisassembler/LLVMDisassembler.cpp b/src/core/util/llvmdisassembler/LLVMDisassembler.cpp index 3648ab91..a4f117f1 100644 --- a/src/core/util/llvmdisassembler/LLVMDisassembler.cpp +++ b/src/core/util/llvmdisassembler/LLVMDisassembler.cpp @@ -1,43 +1,104 @@ #include "LLVMDisassembler.hpp" +#include "util/Logger.hpp" using namespace fail; using namespace llvm; using namespace llvm::object; -// In LLVM 3.9, llvm::Triple::getArchTypeName() returns const char*, since LLVM -// 4.0 it returns StringRef. This overload catches the latter case. -__attribute__((unused)) -static std::ostream& operator<<(std::ostream& stream, const llvm::StringRef& s) -{ - stream << s.str(); - return stream; -} +static Logger LOG("LLVMDisassembler"); LLVMtoFailTranslator *LLVMDisassembler::getTranslator() { - if (ltofail == 0) { - switch ( llvm::Triple::ArchType(object->getArch()) ) { + if (!ltofail) { + switch ( llvm::Triple::ArchType(m_object->getArch()) ) { case llvm::Triple::x86: case llvm::Triple::x86_64: - ltofail = new LLVMtoFailBochs(this); + ltofail.reset(new LLVMtoFailBochs(this)); break; case llvm::Triple::arm: - ltofail = new LLVMtoFailGem5(this); + ltofail.reset(new LLVMtoFailGem5(this)); break; default: - std::cerr << "ArchType " - << llvm::Triple::getArchTypeName(llvm::Triple::ArchType(object->getArch())) + LOG << "ArchType " + << llvm::Triple::getArchTypeName(llvm::Triple::ArchType(m_object->getArch())).str() << " not supported\n"; exit(1); } } - return ltofail; + return ltofail.get(); +} + +LLVMDisassembler::LLVMDisassembler(ElfReader *elf) { + /* Disassemble the binary if necessary */ + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + std::string filename = elf->getFilename(); + + Expected> BinaryOrErr = createBinary(filename); + if (!BinaryOrErr) { + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(BinaryOrErr.takeError()), OS, ""); + OS.flush(); + LOG << "Could not read ELF file:" << filename << "': " << Buf << ".\n"; + exit(1); + } + + m_binary = std::move(BinaryOrErr.get()); + m_object = llvm::dyn_cast(m_binary.getBinary()); + + + this->triple = GetTriple(m_object); + this->target = GetTarget(triple); + + std::unique_ptr MRI(target->createMCRegInfo(triple)); + if (!MRI) { + std::cerr << "DIS error: no register info for target " << triple << "\n"; + return; + } + + std::unique_ptr MAI(target->createMCAsmInfo(*MRI, triple)); + if (!MAI) { + std::cerr << "DIS error: no assembly info for target " << triple << "\n"; + return; + } + + std::unique_ptr STI( + target->createMCSubtargetInfo(triple, MCPU, FeaturesStr)); + if (!STI) { + std::cerr << "DIS error: no subtarget info for target " << triple << "\n"; + return; + } + std::unique_ptr MII(target->createMCInstrInfo()); + if (!MII) { + std::cerr << "DIS error: no instruction info for target " << triple << "\n"; + return; + } + std::unique_ptr MOFI(new llvm::MCObjectFileInfo); + // Set up the MCContext for creating symbols and MCExpr's. + llvm::MCContext Ctx(MAI.get(), MRI.get(), MOFI.get()); + + this->subtargetinfo = std::move(STI); + std::unique_ptr DisAsm( + target->createMCDisassembler(*subtargetinfo, Ctx)); + if (!DisAsm) { + std::cerr << "DIS error: no disassembler for target " << triple << "\n"; + return; + } + this->disas = std::move(DisAsm); + this->instr_info = std::move(MII); + this->register_info = std::move(MRI); + + this->instrs.reset(new InstrMap()); } + void LLVMDisassembler::disassemble() { std::error_code ec; - for (section_iterator i = object->section_begin(), - e = object->section_end(); i != e; ++i) { + for (section_iterator i = m_object->section_begin(), + e = m_object->section_end(); i != e; ++i) { bool text = i->isText(); if (!text) continue; @@ -46,9 +107,7 @@ void LLVMDisassembler::disassemble() // Make a list of all the symbols in this section. std::vector > Symbols; - for (const SymbolRef &symbol : object->symbols()) { - StringRef Name; - + for (const SymbolRef &symbol : m_object->symbols()) { if (!i->containsSymbol(symbol)) { continue; } @@ -159,4 +218,6 @@ void LLVMDisassembler::disassemble() } } } + + LOG << "instructions disassembled: " << std::dec << instrs->size() << std::endl; } diff --git a/src/core/util/llvmdisassembler/LLVMDisassembler.hpp b/src/core/util/llvmdisassembler/LLVMDisassembler.hpp index ee47830b..5109c757 100644 --- a/src/core/util/llvmdisassembler/LLVMDisassembler.hpp +++ b/src/core/util/llvmdisassembler/LLVMDisassembler.hpp @@ -7,6 +7,8 @@ #include #include // unique_ptr +#include "util/ElfReader.hpp" + #include "llvm/Object/ObjectFile.h" #include "llvm/ADT/STLExtras.h" @@ -29,8 +31,7 @@ #include "llvm/Support/Casting.h" #include "LLVMtoFailTranslator.hpp" -#include "LLVMtoFailBochs.hpp" -#include "LLVMtoFailGem5.hpp" + namespace fail { @@ -51,7 +52,9 @@ class LLVMDisassembler { typedef std::map InstrMap; private: - const llvm::object::ObjectFile *object; + llvm::object::OwningBinary m_binary; + llvm::object::ObjectFile *m_object; + const llvm::Target *target; std::string triple; std::string MCPU; @@ -62,7 +65,7 @@ class LLVMDisassembler { std::unique_ptr register_info; std::unique_ptr instrs; - fail::LLVMtoFailTranslator *ltofail; + std::unique_ptr ltofail; static std::string GetTriple(const llvm::object::ObjectFile *Obj) { @@ -92,62 +95,25 @@ class LLVMDisassembler { return true; } -public: - LLVMDisassembler(const llvm::object::ObjectFile *object) : ltofail(0) { - this->object = object; - this->triple = GetTriple(object); - this->target = GetTarget(triple); - - std::unique_ptr MRI(target->createMCRegInfo(triple)); - if (!MRI) { - std::cerr << "DIS error: no register info for target " << triple << "\n"; - return; - } - - std::unique_ptr MAI(target->createMCAsmInfo(*MRI, triple)); - if (!MAI) { - std::cerr << "DIS error: no assembly info for target " << triple << "\n"; - return; - } - - std::unique_ptr STI( - target->createMCSubtargetInfo(triple, MCPU, FeaturesStr)); - if (!STI) { - std::cerr << "DIS error: no subtarget info for target " << triple << "\n"; - return; - } - std::unique_ptr MII(target->createMCInstrInfo()); - if (!MII) { - std::cerr << "DIS error: no instruction info for target " << triple << "\n"; - return; - } - std::unique_ptr MOFI(new llvm::MCObjectFileInfo); - // Set up the MCContext for creating symbols and MCExpr's. - llvm::MCContext Ctx(MAI.get(), MRI.get(), MOFI.get()); - - this->subtargetinfo = std::move(STI); - std::unique_ptr DisAsm( - target->createMCDisassembler(*subtargetinfo, Ctx)); - if (!DisAsm) { - std::cerr << "DIS error: no disassembler for target " << triple << "\n"; - return; - } - this->disas = std::move(DisAsm); - this->instr_info = std::move(MII); - this->register_info = std::move(MRI); - - this->instrs.reset(new InstrMap()); - } + unsigned m_xlen; - ~LLVMDisassembler() { delete ltofail; }; +public: + LLVMDisassembler(fail::ElfReader *elf); - InstrMap &getInstrMap() { return *instrs; }; - const llvm::MCRegisterInfo& getRegisterInfo() { return *register_info; } + InstrMap *getInstrMap() { return instrs.get(); }; fail::LLVMtoFailTranslator *getTranslator() ; const std::string& GetTriple() const { return triple; }; + const llvm::MCRegisterInfo& getRegisterInfo() { return *register_info; } + + std::string getRegisterName(unsigned id) { return getRegisterInfo().getName(id); } + + const std::string GetSubtargetFeatures() const { return m_object->getFeatures().getString(); } + void disassemble(); + + unsigned getWordWidth() { return m_xlen; } }; } diff --git a/src/core/util/llvmdisassembler/LLVMtoFailBochs.cpp b/src/core/util/llvmdisassembler/LLVMtoFailBochs.cpp index 5e0dab2f..0b9a0c4f 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailBochs.cpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailBochs.cpp @@ -5,102 +5,102 @@ using namespace fail; LLVMtoFailBochs::LLVMtoFailBochs(LLVMDisassembler *disas) { - std::map reg_name_map; - - reg_name_map["AH"] = reginfo_t(RID_CAX, 8, 8); - reg_name_map["AL"] = reginfo_t(RID_CAX, 8); - reg_name_map["AX"] = reginfo_t(RID_CAX, 16); - reg_name_map["EAX"] = reginfo_t(RID_CAX, 32); - reg_name_map["RAX"] = reginfo_t(RID_CAX, 64); - - reg_name_map["BH"] = reginfo_t(RID_CBX, 8, 8); - reg_name_map["BL"] = reginfo_t(RID_CBX, 8); - reg_name_map["BX"] = reginfo_t(RID_CBX, 16); - reg_name_map["EBX"] = reginfo_t(RID_CBX, 32); - reg_name_map["RBX"] = reginfo_t(RID_CBX, 64); - - reg_name_map["CH"] = reginfo_t(RID_CCX, 8, 8); - reg_name_map["CL"] = reginfo_t(RID_CCX, 8); - reg_name_map["CX"] = reginfo_t(RID_CCX, 16); - reg_name_map["ECX"] = reginfo_t(RID_CCX, 32); - reg_name_map["RCX"] = reginfo_t(RID_CCX, 64); - - reg_name_map["DH"] = reginfo_t(RID_CDX, 8, 8); - reg_name_map["DL"] = reginfo_t(RID_CDX, 8); - reg_name_map["DX"] = reginfo_t(RID_CDX, 16); - reg_name_map["EDX"] = reginfo_t(RID_CDX, 32); - reg_name_map["RDX"] = reginfo_t(RID_CDX, 64); - - reg_name_map["R8"] = reginfo_t(RID_R8, 64); - reg_name_map["R8D"] = reginfo_t(RID_R8, 32); - reg_name_map["R8W"] = reginfo_t(RID_R8, 16); - reg_name_map["R8B"] = reginfo_t(RID_R8, 8); - reg_name_map["R9"] = reginfo_t(RID_R9, 64); - reg_name_map["R9D"] = reginfo_t(RID_R9, 32); - reg_name_map["R9W"] = reginfo_t(RID_R9, 16); - reg_name_map["R9B"] = reginfo_t(RID_R9, 8); - reg_name_map["R10"] = reginfo_t(RID_R10, 64); - reg_name_map["R10D"] = reginfo_t(RID_R10, 32); - reg_name_map["R10W"] = reginfo_t(RID_R10, 16); - reg_name_map["R10B"] = reginfo_t(RID_R10, 8); - reg_name_map["R11"] = reginfo_t(RID_R11, 64); - reg_name_map["R11D"] = reginfo_t(RID_R11, 32); - reg_name_map["R11W"] = reginfo_t(RID_R11, 16); - reg_name_map["R11B"] = reginfo_t(RID_R11, 8); - reg_name_map["R12"] = reginfo_t(RID_R12, 64); - reg_name_map["R12D"] = reginfo_t(RID_R12, 32); - reg_name_map["R12W"] = reginfo_t(RID_R12, 16); - reg_name_map["R12B"] = reginfo_t(RID_R12, 8); - reg_name_map["R13"] = reginfo_t(RID_R13, 64); - reg_name_map["R13D"] = reginfo_t(RID_R13, 32); - reg_name_map["R13W"] = reginfo_t(RID_R13, 16); - reg_name_map["R13B"] = reginfo_t(RID_R13, 8); - reg_name_map["R14"] = reginfo_t(RID_R14, 64); - reg_name_map["R14D"] = reginfo_t(RID_R14, 32); - reg_name_map["R14W"] = reginfo_t(RID_R14, 16); - reg_name_map["R14B"] = reginfo_t(RID_R14, 8); - reg_name_map["R15"] = reginfo_t(RID_R15, 64); - reg_name_map["R15D"] = reginfo_t(RID_R15, 32); - reg_name_map["R15W"] = reginfo_t(RID_R15, 16); - reg_name_map["R15B"] = reginfo_t(RID_R15, 8); - - reg_name_map["DIL"] = reginfo_t(RID_CDI, 8); - reg_name_map["DI"] = reginfo_t(RID_CDI, 16); - reg_name_map["EDI"] = reginfo_t(RID_CDI, 32); - reg_name_map["RDI"] = reginfo_t(RID_CDI, 64); - - reg_name_map["BPL"] = reginfo_t(RID_CBP, 8); - reg_name_map["BP"] = reginfo_t(RID_CBP, 16); - reg_name_map["EBP"] = reginfo_t(RID_CBP, 32); - reg_name_map["RBP"] = reginfo_t(RID_CBP, 64); - - reg_name_map["EFLAGS"] = reginfo_t(RID_FLAGS, 32); - reg_name_map["RFLAGS"] = reginfo_t(RID_FLAGS, 64); - - reg_name_map["EIP"] = reginfo_t(RID_PC, 32); - reg_name_map["RIP"] = reginfo_t(RID_PC, 64); - - reg_name_map["SIL"] = reginfo_t(RID_CSI, 8); - reg_name_map["SI"] = reginfo_t(RID_CSI, 16); - reg_name_map["ESI"] = reginfo_t(RID_CSI, 32); - reg_name_map["RSI"] = reginfo_t(RID_CSI, 64); - - reg_name_map["SPL"] = reginfo_t(RID_CSP, 8); - reg_name_map["SP"] = reginfo_t(RID_CSP, 16); - reg_name_map["ESP"] = reginfo_t(RID_CSP, 32); - reg_name_map["RSP"] = reginfo_t(RID_CSP, 64); - - reg_name_map["CR0"] = reginfo_t(RID_CR0); - reg_name_map["CR2"] = reginfo_t(RID_CR2); - reg_name_map["CR3"] = reginfo_t(RID_CR3); - reg_name_map["CR4"] = reginfo_t(RID_CR4); - - reg_name_map["CS"] = reginfo_t(RID_CS, 16); - reg_name_map["DS"] = reginfo_t(RID_DS, 16); - reg_name_map["ES"] = reginfo_t(RID_ES, 16); - reg_name_map["FS"] = reginfo_t(RID_FS, 16); - reg_name_map["GS"] = reginfo_t(RID_GS, 16); - reg_name_map["SS"] = reginfo_t(RID_SS, 16); + std::map reg_name_map; + + reg_name_map["AH"] = RegisterView(RID_CAX, 8, 8); + reg_name_map["AL"] = RegisterView(RID_CAX, 8); + reg_name_map["AX"] = RegisterView(RID_CAX, 16); + reg_name_map["EAX"] = RegisterView(RID_CAX, 32); + reg_name_map["RAX"] = RegisterView(RID_CAX, 64); + + reg_name_map["BH"] = RegisterView(RID_CBX, 8, 8); + reg_name_map["BL"] = RegisterView(RID_CBX, 8); + reg_name_map["BX"] = RegisterView(RID_CBX, 16); + reg_name_map["EBX"] = RegisterView(RID_CBX, 32); + reg_name_map["RBX"] = RegisterView(RID_CBX, 64); + + reg_name_map["CH"] = RegisterView(RID_CCX, 8, 8); + reg_name_map["CL"] = RegisterView(RID_CCX, 8); + reg_name_map["CX"] = RegisterView(RID_CCX, 16); + reg_name_map["ECX"] = RegisterView(RID_CCX, 32); + reg_name_map["RCX"] = RegisterView(RID_CCX, 64); + + reg_name_map["DH"] = RegisterView(RID_CDX, 8, 8); + reg_name_map["DL"] = RegisterView(RID_CDX, 8); + reg_name_map["DX"] = RegisterView(RID_CDX, 16); + reg_name_map["EDX"] = RegisterView(RID_CDX, 32); + reg_name_map["RDX"] = RegisterView(RID_CDX, 64); + + reg_name_map["R8"] = RegisterView(RID_R8, 64); + reg_name_map["R8D"] = RegisterView(RID_R8, 32); + reg_name_map["R8W"] = RegisterView(RID_R8, 16); + reg_name_map["R8B"] = RegisterView(RID_R8, 8); + reg_name_map["R9"] = RegisterView(RID_R9, 64); + reg_name_map["R9D"] = RegisterView(RID_R9, 32); + reg_name_map["R9W"] = RegisterView(RID_R9, 16); + reg_name_map["R9B"] = RegisterView(RID_R9, 8); + reg_name_map["R10"] = RegisterView(RID_R10, 64); + reg_name_map["R10D"] = RegisterView(RID_R10, 32); + reg_name_map["R10W"] = RegisterView(RID_R10, 16); + reg_name_map["R10B"] = RegisterView(RID_R10, 8); + reg_name_map["R11"] = RegisterView(RID_R11, 64); + reg_name_map["R11D"] = RegisterView(RID_R11, 32); + reg_name_map["R11W"] = RegisterView(RID_R11, 16); + reg_name_map["R11B"] = RegisterView(RID_R11, 8); + reg_name_map["R12"] = RegisterView(RID_R12, 64); + reg_name_map["R12D"] = RegisterView(RID_R12, 32); + reg_name_map["R12W"] = RegisterView(RID_R12, 16); + reg_name_map["R12B"] = RegisterView(RID_R12, 8); + reg_name_map["R13"] = RegisterView(RID_R13, 64); + reg_name_map["R13D"] = RegisterView(RID_R13, 32); + reg_name_map["R13W"] = RegisterView(RID_R13, 16); + reg_name_map["R13B"] = RegisterView(RID_R13, 8); + reg_name_map["R14"] = RegisterView(RID_R14, 64); + reg_name_map["R14D"] = RegisterView(RID_R14, 32); + reg_name_map["R14W"] = RegisterView(RID_R14, 16); + reg_name_map["R14B"] = RegisterView(RID_R14, 8); + reg_name_map["R15"] = RegisterView(RID_R15, 64); + reg_name_map["R15D"] = RegisterView(RID_R15, 32); + reg_name_map["R15W"] = RegisterView(RID_R15, 16); + reg_name_map["R15B"] = RegisterView(RID_R15, 8); + + reg_name_map["DIL"] = RegisterView(RID_CDI, 8); + reg_name_map["DI"] = RegisterView(RID_CDI, 16); + reg_name_map["EDI"] = RegisterView(RID_CDI, 32); + reg_name_map["RDI"] = RegisterView(RID_CDI, 64); + + reg_name_map["BPL"] = RegisterView(RID_CBP, 8); + reg_name_map["BP"] = RegisterView(RID_CBP, 16); + reg_name_map["EBP"] = RegisterView(RID_CBP, 32); + reg_name_map["RBP"] = RegisterView(RID_CBP, 64); + + reg_name_map["EFLAGS"] = RegisterView(RID_FLAGS, 32); + reg_name_map["RFLAGS"] = RegisterView(RID_FLAGS, 64); + + reg_name_map["EIP"] = RegisterView(RID_PC, 32); + reg_name_map["RIP"] = RegisterView(RID_PC, 64); + + reg_name_map["SIL"] = RegisterView(RID_CSI, 8); + reg_name_map["SI"] = RegisterView(RID_CSI, 16); + reg_name_map["ESI"] = RegisterView(RID_CSI, 32); + reg_name_map["RSI"] = RegisterView(RID_CSI, 64); + + reg_name_map["SPL"] = RegisterView(RID_CSP, 8); + reg_name_map["SP"] = RegisterView(RID_CSP, 16); + reg_name_map["ESP"] = RegisterView(RID_CSP, 32); + reg_name_map["RSP"] = RegisterView(RID_CSP, 64); + + reg_name_map["CR0"] = RegisterView(RID_CR0); + reg_name_map["CR2"] = RegisterView(RID_CR2); + reg_name_map["CR3"] = RegisterView(RID_CR3); + reg_name_map["CR4"] = RegisterView(RID_CR4); + + reg_name_map["CS"] = RegisterView(RID_CS, 16); + reg_name_map["DS"] = RegisterView(RID_DS, 16); + reg_name_map["ES"] = RegisterView(RID_ES, 16); + reg_name_map["FS"] = RegisterView(RID_FS, 16); + reg_name_map["GS"] = RegisterView(RID_GS, 16); + reg_name_map["SS"] = RegisterView(RID_SS, 16); const llvm::MCRegisterInfo ®_info = disas->getRegisterInfo(); for (unsigned int i = 0; i < reg_info.getNumRegs(); ++i){ diff --git a/src/core/util/llvmdisassembler/LLVMtoFailBochs.hpp b/src/core/util/llvmdisassembler/LLVMtoFailBochs.hpp index 86a9e966..e7f47fe1 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailBochs.hpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailBochs.hpp @@ -12,9 +12,7 @@ class LLVMDisassembler; class LLVMtoFailBochs : public LLVMtoFailTranslator { public: - LLVMtoFailBochs(LLVMDisassembler *disas); - }; } // end of namespace diff --git a/src/core/util/llvmdisassembler/LLVMtoFailGem5.cpp b/src/core/util/llvmdisassembler/LLVMtoFailGem5.cpp index c7d7031d..3d59f7fa 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailGem5.cpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailGem5.cpp @@ -5,24 +5,24 @@ using namespace fail; LLVMtoFailGem5::LLVMtoFailGem5(LLVMDisassembler *disas) { - std::map reg_name_map; + std::map reg_name_map; - reg_name_map["R0"] = reginfo_t(RI_R0); - reg_name_map["R1"] = reginfo_t(RI_R1); - reg_name_map["R2"] = reginfo_t(RI_R2); - reg_name_map["R3"] = reginfo_t(RI_R3); - reg_name_map["R4"] = reginfo_t(RI_R4); - reg_name_map["R5"] = reginfo_t(RI_R5); - reg_name_map["R6"] = reginfo_t(RI_R6); - reg_name_map["R7"] = reginfo_t(RI_R7); - reg_name_map["R8"] = reginfo_t(RI_R8); - reg_name_map["R9"] = reginfo_t(RI_R9); - reg_name_map["R10"] = reginfo_t(RI_R10); - reg_name_map["R11"] = reginfo_t(RI_R11); - reg_name_map["R12"] = reginfo_t(RI_R12); - reg_name_map["SP"] = reginfo_t(RI_SP); - reg_name_map["LR"] = reginfo_t(RI_LR); - reg_name_map["PC"] = reginfo_t(RI_IP); + reg_name_map["R0"] = RegisterView(RI_R0); + reg_name_map["R1"] = RegisterView(RI_R1); + reg_name_map["R2"] = RegisterView(RI_R2); + reg_name_map["R3"] = RegisterView(RI_R3); + reg_name_map["R4"] = RegisterView(RI_R4); + reg_name_map["R5"] = RegisterView(RI_R5); + reg_name_map["R6"] = RegisterView(RI_R6); + reg_name_map["R7"] = RegisterView(RI_R7); + reg_name_map["R8"] = RegisterView(RI_R8); + reg_name_map["R9"] = RegisterView(RI_R9); + reg_name_map["R10"] = RegisterView(RI_R10); + reg_name_map["R11"] = RegisterView(RI_R11); + reg_name_map["R12"] = RegisterView(RI_R12); + reg_name_map["SP"] = RegisterView(RI_SP); + reg_name_map["LR"] = RegisterView(RI_LR); + reg_name_map["PC"] = RegisterView(RI_IP); const llvm::MCRegisterInfo ®_info = disas->getRegisterInfo(); for (unsigned int i = 0; i < reg_info.getNumRegs(); ++i){ diff --git a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp index 4a4059fc..31c53d76 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp @@ -7,7 +7,7 @@ using namespace fail; using namespace llvm; using namespace llvm::object; -const LLVMtoFailTranslator::reginfo_t & LLVMtoFailTranslator::getFailRegisterInfo(unsigned int regid) { +const RegisterView& LLVMtoFailTranslator::getFailRegisterInfo(unsigned int regid) { ltof_map_t::iterator it = llvm_to_fail_map.find(regid); if ( it != llvm_to_fail_map.end() ) {// found return (*it).second; @@ -18,54 +18,20 @@ const LLVMtoFailTranslator::reginfo_t & LLVMtoFailTranslator::getFailRegisterIn } } -regdata_t LLVMtoFailTranslator::getRegisterContent(ConcreteCPU& cpu, const reginfo_t ®info){ - regdata_t result; - - Register* reg = cpu.getRegister(reginfo.id); - result = cpu.getRegisterContent(reg); - - result &= reginfo.mask; - result >>= reginfo.offset; - - return result; -} - -void LLVMtoFailTranslator::setRegisterContent(ConcreteCPU & cpu, const reginfo_t ®info, regdata_t value){ - Register* reg = cpu.getRegister(reginfo.id); - - regdata_t origval = cpu.getRegisterContent(reg); // Get register Value from fail - origval &= ~(reginfo.mask); // clear bits to write - - value <<= reginfo.offset; // shift value to write up to position - value &= reginfo.mask; // mask out trailing and leading bits - value |= origval; // set bits to write - - cpu.setRegisterContent( reg, value ); // write back register content -} - -int LLVMtoFailTranslator::getMaxFailRegisterID() -{ - auto max = std::max_element(llvm_to_fail_map.cbegin(), llvm_to_fail_map.cend(), - [] (const ltof_map_t::value_type& v1, const ltof_map_t::value_type& v2) { - return v1.second.id < v2.second.id; - }); - return max->second.id; -} - -LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string elf_path) { - //llvm_shutdown_obj Y; - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllDisassemblers(); - - Expected> BinaryOrErr = createBinary(elf_path); - assert (BinaryOrErr); - Binary *binary = BinaryOrErr.get().getBinary(); - - #ifndef __puma - LLVMDisassembler disas(dyn_cast(binary)); - return disas.getTranslator(); - #else - return 0; - #endif -} +// LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string elf_path) { +// //llvm_shutdown_obj Y; +// llvm::InitializeAllTargetInfos(); +// llvm::InitializeAllTargetMCs(); +// llvm::InitializeAllDisassemblers(); + +// Expected> BinaryOrErr = createBinary(elf_path); +// assert (BinaryOrErr); +// Binary *binary = BinaryOrErr.get().getBinary(); + +// #ifndef __puma +// LLVMDisassembler disas(dyn_cast(binary)); +// return disas.getTranslator(); +// #else +// return 0; +// #endif +// } diff --git a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp index f25dcbb2..fdf1b662 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp @@ -1,9 +1,9 @@ #ifndef __LLVMTOFAILTRANSLATOR_HPP_ #define __LLVMTOFAILTRANSLATOR_HPP_ -#include "sal/SALConfig.hpp" -#include "sal/ConcreteCPU.hpp" #include +#include "sal/SALConfig.hpp" +#include "sal/Register.hpp" namespace fail { @@ -13,79 +13,24 @@ namespace fail { */ class LLVMtoFailTranslator { public: - /** - * Maps registers to/from linear addresses usable for def/use-pruning - * purposes and storage in the database. Takes care that the linear - * addresses of x86 subregisters (e.g., AX represents the lower 16 bits of - * EAX) overlap with their siblings. - */ - struct reginfo_t { - int id; - regwidth_t width; - regdata_t mask; - byte_t offset; - int toDataAddress() const { - // .. 5 4 | 3 2 1 0 - // | - return (id << 4) | (offset / 8); - } - - static reginfo_t fromDataAddress(int addr, int width) { - int id = addr >> 4; - byte_t offset = (addr & 0xf) * 8; - return reginfo_t(id, width * 8, offset); - } - - reginfo_t(int id=-1, regwidth_t width = 32, byte_t offs = 0) - : id(id), width(width), mask((((regdata_t) 1 << width) - 1) << offs), offset(offs) - { - if (width >= sizeof(regdata_t) * 8) { // all ones, (1 << width) == 0! - mask = -1; - } -#if 0 - std::cerr << "constructing reginfo_t: " << std::dec << id << " " << width << " " << ((int)offs) << std::hex << " 0x" << mask << std::endl; -#endif - } - }; protected: LLVMtoFailTranslator(){}; - typedef std::map ltof_map_t; + typedef std::map ltof_map_t; ltof_map_t llvm_to_fail_map; public: /** - * Translates a backend-specific register ID to a Fail register ID. - * @param regid A backend-specific register ID. + * Translates a backend-specific register ID to a FAIL* RegisterView. + * @param regid A backend-specific register ID. * @return A FAIL* register-info struct, or LLVMtoFailTranslator::notfound * if no mapping was found. */ - const reginfo_t & getFailRegisterInfo(unsigned int regid); - - static regdata_t getRegisterContent(ConcreteCPU & cpu, const reginfo_t & reg); - static void setRegisterContent(ConcreteCPU & cpu, const reginfo_t ®, regdata_t value); - regdata_t getRegisterContent(ConcreteCPU & cpu, unsigned int llvmid) { - return getRegisterContent(cpu, getFailRegisterInfo(llvmid)); - } - void setRegisterContent(ConcreteCPU & cpu, unsigned int llvmid, regdata_t value) { - setRegisterContent(cpu, getFailRegisterInfo(llvmid), value); - } - - /** - * Translates a backend-specific register ID to a Fail register ID. - * @param regid A backend-specific register ID. - * @return A FAIL* register ID. May do funny things if regid does not exist. - */ - int getFailRegisterID(unsigned int regid) { return this->getFailRegisterInfo(regid).id; }; - - int getMaxFailRegisterID(); - fail::address_t getMaxDataAddress() { reginfo_t r(getMaxFailRegisterID() + 1); return r.toDataAddress() - 1; } - - reginfo_t notfound; + const RegisterView& getFailRegisterInfo(unsigned int regid); - static LLVMtoFailTranslator* createFromBinary(const std::string elf_path); + RegisterView notfound; }; } // end of namespace diff --git a/src/core/util/llvmdisassembler/testing/llvmDisTest.cc b/src/core/util/llvmdisassembler/testing/llvmDisTest.cc index e307c948..cc11fbce 100644 --- a/src/core/util/llvmdisassembler/testing/llvmDisTest.cc +++ b/src/core/util/llvmdisassembler/testing/llvmDisTest.cc @@ -7,7 +7,7 @@ using namespace fail; bool show_mapping(fail::LLVMtoFailTranslator *ltof, const MCRegisterInfo ®_info, unsigned llvmid) { - const LLVMtoFailTranslator::reginfo_t& failreg = ltof->getFailRegisterInfo(llvmid); + const RegisterView failreg = ltof->getFailRegisterInfo(llvmid); std::cout << reg_info.getName(llvmid) << "(" << std::dec << llvmid << "->"; if (&failreg != <of->notfound) { std::cout << failreg.id; @@ -26,31 +26,19 @@ int main(int argc, char* argv[]) { // llvm::InitializeAllAsmParsers(); llvm::InitializeAllDisassemblers(); - std::string file; - + if(argc > 1){ std::cout << "Trying to disassemble: " << argv[1] << std::endl; - file = argv[1]; - } else { + } else { std::cerr << "No file to disassemble :(" << std::endl; return -1; } - Expected> BinaryOrErr = createBinary(file); - if (!BinaryOrErr) { - std::cerr << "Dis: '" << file << "': "; - raw_os_ostream OS(std::cerr); - logAllUnhandledErrors(BinaryOrErr.takeError(), OS, ""); - return -1; - } - Binary *binary = BinaryOrErr.get().getBinary(); - - ObjectFile *obj = dyn_cast(binary); - - LLVMDisassembler disas(obj); + ElfReader elf(argv[1]); + LLVMDisassembler disas(&elf); disas.disassemble(); - LLVMDisassembler::InstrMap &instr_map = disas.getInstrMap(); + LLVMDisassembler::InstrMap &instr_map = *disas.getInstrMap(); std::cout << "Map Size: " << instr_map.size() << "\nTriple: " << disas.GetTriple() << std::endl; LLVMDisassembler::InstrMap::const_iterator itr; diff --git a/tools/analysis/resultbrowser/app/model.py b/tools/analysis/resultbrowser/app/model.py index 1ee5e1f4..f3d62c69 100755 --- a/tools/analysis/resultbrowser/app/model.py +++ b/tools/analysis/resultbrowser/app/model.py @@ -63,7 +63,7 @@ def closeSession(): '''Populate variant results for overview data''' def getVariants(cur, table): restbl = table.getDetails().getDBName() - cur.execute("""SELECT sum((t.time2 - t.time1 + 1) * width) AS total, resulttype,variant, v.id as variant_id, benchmark, details FROM variant v JOIN trace t ON v.id = t.variant_id JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address JOIN %s r ON r.pilot_id = g.pilot_id JOIN fsppilot p ON r.pilot_id = p.id GROUP BY v.id, resulttype, details""" % (restbl)) # % is used here, as a tablename must not be quoted + cur.execute("""SELECT sum((t.time2 - t.time1 + 1)) AS total, resulttype,variant, v.id as variant_id, benchmark, details FROM variant v JOIN trace t ON v.id = t.variant_id JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address AND (g.data_mask & t.data_mask) JOIN %s r ON r.pilot_id = g.pilot_id JOIN fsppilot p ON r.pilot_id = p.id GROUP BY v.id, resulttype, details""" % (restbl)) # % is used here, as a tablename must not be quoted res = cur.fetchall() rdic = {} # Build dict with variant id as key @@ -143,7 +143,7 @@ def getCode(result_table, variant_id, resultlabel=None): # I especially like this one: select = "SELECT instr_address, opcode, disassemble, comment, sum(t.time2 - t.time1 + 1) as totals, GROUP_CONCAT(DISTINCT resulttype SEPARATOR ', ') as results FROM variant v " - join = " JOIN trace t ON v.id = t.variant_id JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address JOIN %s r ON r.pilot_id = g.pilot_id JOIN fsppilot p ON r.pilot_id = p.id JOIN objdump ON objdump.variant_id = v.id AND objdump.instr_address = injection_instr_absolute " %(scrub(result_table)) + join = " JOIN trace t ON v.id = t.variant_id JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address AND (g.data_mask & t.data_mask) JOIN %s r ON r.pilot_id = g.pilot_id JOIN fsppilot p ON r.pilot_id = p.id JOIN objdump ON objdump.variant_id = v.id AND objdump.instr_address = injection_instr_absolute " %(scrub(result_table)) where = "WHERE v.id = %s " group = "GROUP BY injection_instr_absolute ORDER BY totals DESC " diff --git a/tools/import-trace/AdvancedMemoryImporter.cc b/tools/import-trace/AdvancedMemoryImporter.cc index 61eaead8..1df8f2b5 100644 --- a/tools/import-trace/AdvancedMemoryImporter.cc +++ b/tools/import-trace/AdvancedMemoryImporter.cc @@ -87,6 +87,27 @@ void AdvancedMemoryImporter::insert_delayed_entries(bool finalizing) // memory footprint } +bool AdvancedMemoryImporter::cb_initialize() { + // 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; + } + + if (!m_elf) { + LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; + return false; + } + + m_disassembler.reset(new Disassembler(m_elf)); + m_disassembler->disassemble(); + m_instr_map = m_disassembler->getInstrMap(); + + return true; +} + bool AdvancedMemoryImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, Trace_Event &ev) { @@ -100,82 +121,14 @@ bool AdvancedMemoryImporter::handle_ip_event(fail::simtime_t curtime, instructio // (delayed) trace entries insert_delayed_entries(false); -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - if (!isDisassembled) { - if (!m_elf) { - LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; - return false; - } - - disas.reset(new CapstoneDisassembler(m_elf)); - - disas->disassemble(); - CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - LOG << "instructions disassembled: " << std::dec << instr_map.size() << std::endl; -#if 0 - for (CapstoneDisassembler::InstrMap::const_iterator it = instr_map.begin(); - it != instr_map.end(); ++it) { - LOG << "DIS " << std::hex << it->second.address << " " << (int) it->second.length << std::endl; - } -#endif - } - - const CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - const CapstoneDisassembler::InstrMap::const_iterator it = instr_map.find(ev.ip()); - if (it == instr_map.end()) { - LOG << "WARNING: CapstoneDisassembler hasn't disassembled instruction at 0x" - << ev.ip() << " -- are you using Capstone < 4.0?" << std::endl; - return true; // probably weird things will happen now - } - const CapstoneDisassembler::Instr &opcode = it->second; -#elif defined(BUILD_LLVM_DISASSEMBLER) - if (!binary) { - /* 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> 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(binary); - disas.reset(new LLVMDisassembler(obj)); -#endif - disas->disassemble(); - LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap(); - LOG << "instructions disassembled: " << std::dec << instr_map.size() << " Triple: " << disas->GetTriple() << std::endl; -#if 0 - for (LLVMDisassembler::InstrMap::const_iterator it = instr_map.begin(); - it != instr_map.end(); ++it) { - LOG << "DIS " << std::hex << it->second.address << " " << (int) it->second.length << std::endl; - } -#endif - } - - const LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap(); - const LLVMDisassembler::InstrMap::const_iterator it = instr_map.find(ev.ip()); - if (it == instr_map.end()) { - LOG << "WARNING: LLVMDisassembler hasn't disassembled instruction at 0x" - << ev.ip() << " -- are you using LLVM < 3.3?" << std::endl; + const Disassembler::InstrMap::const_iterator it = m_instr_map->find(ev.ip()); + if (it == m_instr_map->end()) { + LOG << "WARNING: Disassembler hasn't disassembled instruction at 0x" + << ev.ip() << std::endl; return true; // probably weird things will happen now } - const LLVMDisassembler::Instr &opcode = it->second; -#endif + const Disassembler::Instr &opcode = it->second; /* Now we've got the opcode and know whether it's a conditional branch. If * it is, the next IP event will tell us whether it was taken or not. */ @@ -193,13 +146,8 @@ bool AdvancedMemoryImporter::handle_ip_event(fail::simtime_t curtime, instructio bool AdvancedMemoryImporter::handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, Trace_Event &ev) { -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - const CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - const CapstoneDisassembler::Instr &opcode = instr_map.at(ev.ip()); -#elif defined(BUILD_LLVM_DISASSEMBLER) - const LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap(); - const LLVMDisassembler::Instr &opcode = instr_map.at(ev.ip()); -#endif + const Disassembler::Instr &opcode = m_instr_map->at(ev.ip()); + DelayedTraceEntry entry = { curtime, instr, ev, opcode.opcode, (unsigned) branches_taken.size() }; delayed_entries.push_back(entry); diff --git a/tools/import-trace/AdvancedMemoryImporter.hpp b/tools/import-trace/AdvancedMemoryImporter.hpp index 1165d2b0..702dbce2 100644 --- a/tools/import-trace/AdvancedMemoryImporter.hpp +++ b/tools/import-trace/AdvancedMemoryImporter.hpp @@ -28,13 +28,8 @@ * operations with a set of new virtual functions that are called downwards. */ class AdvancedMemoryImporter : public MemoryImporter { -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - bool isDisassembled = false; - std::unique_ptr disas; -#elif defined(BUILD_LLVM_DISASSEMBLER) - llvm::object::Binary *binary = 0; - std::unique_ptr disas; -#endif + + bool m_last_was_conditional_branch; fail::guest_address_t m_ip_jump_not_taken; std::vector branches_taken; @@ -50,12 +45,17 @@ class AdvancedMemoryImporter : public MemoryImporter { unsigned m_cur_branchmask; + std::unique_ptr m_disassembler; + Disassembler::InstrMap* m_instr_map; + void insert_delayed_entries(bool finalizing); public: AdvancedMemoryImporter() : m_last_was_conditional_branch(false), m_ip_jump_not_taken(0), m_cur_branchmask(0) {} + virtual bool cb_initialize(); + protected: virtual std::string database_additional_columns(); virtual void database_insert_columns(std::string& sql, unsigned& num_columns); diff --git a/tools/import-trace/CMakeLists.txt b/tools/import-trace/CMakeLists.txt index dc1f6f13..d2f4b427 100644 --- a/tools/import-trace/CMakeLists.txt +++ b/tools/import-trace/CMakeLists.txt @@ -53,16 +53,16 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MYSQL_CFLAGS}") add_executable(import-trace main.cc ${SRCS}) -target_link_libraries(import-trace - ${PROTOBUF_LIBRARY} +target_link_libraries(import-trace + ${PROTOBUF_LIBRARY} ${MYSQL_LIBRARIES} - fail-util + fail-util fail-comm - fail-sal + fail-fsp ) if (BUILD_LLVM_DISASSEMBLER) - target_link_libraries(import-trace fail-llvmdisassembler fail-sal ${LLVM_LIBS} ${LLVM_LDFLAGS} ${Boost_LIBRARIES}) + target_link_libraries(import-trace fail-llvmdisassembler fail-fsp ${LLVM_LIBS} ${LLVM_LDFLAGS} ${Boost_LIBRARIES}) endif (BUILD_LLVM_DISASSEMBLER) if (BUILD_CAPSTONE_DISASSEMBLER) diff --git a/tools/import-trace/FullTraceImporter.cc b/tools/import-trace/FullTraceImporter.cc index d08bd2d7..03ff5472 100644 --- a/tools/import-trace/FullTraceImporter.cc +++ b/tools/import-trace/FullTraceImporter.cc @@ -12,19 +12,17 @@ Database *db; bool FullTraceImporter::handle_ip_event(simtime_t curtime, instruction_count_t instr, Trace_Event &ev) { + std::stringstream sql; + sql << "(" << m_variant_id << "," << instr << "," << ev.ip() << ")"; - margin_info_t right_margin; - right_margin.time = curtime; - right_margin.dyninstr = instr; // !< The current instruction - right_margin.ip = ev.ip(); - - // pass through potentially available extended trace information - if (!add_trace_event(right_margin, right_margin, ev)) { - LOG << "add_trace_event failed" << std::endl; - return false; + bool ret = + db->insert_multiple("INSERT INTO fulltrace (variant_id, instr, instr_absolute) VALUES ", sql.str().c_str()); + m_row_count++; + if (m_row_count % 10000 == 0 && ret) { + LOG << "Inserted " << std::dec << m_row_count << " trace events into the database" << std::endl; } - return true; + return ret; } bool FullTraceImporter::handle_mem_event(simtime_t curtime, instruction_count_t instr, @@ -53,27 +51,6 @@ bool FullTraceImporter::clear_database() { return ret; } -bool FullTraceImporter::add_trace_event(margin_info_t &begin, margin_info_t &end, - Trace_Event &event, bool is_fake) { - // Is event a fake mem-event? - if (is_fake) { - return true; - } - - // INSERT group entry - std::stringstream sql; - sql << "(" << m_variant_id << "," << end.dyninstr << "," << end.ip << ")"; - - bool ret = - db->insert_multiple("INSERT INTO fulltrace (variant_id, instr, instr_absolute) VALUES ", sql.str().c_str()); - m_row_count++; - if (m_row_count % 10000 == 0 && ret) { - LOG << "Inserted " << std::dec << m_row_count << " trace events into the database" << std::endl; - } - - return ret; -} - bool FullTraceImporter::trace_end_reached() { return db->insert_multiple(); } diff --git a/tools/import-trace/FullTraceImporter.hpp b/tools/import-trace/FullTraceImporter.hpp index e6abefaf..51e3a939 100644 --- a/tools/import-trace/FullTraceImporter.hpp +++ b/tools/import-trace/FullTraceImporter.hpp @@ -5,24 +5,22 @@ #include "util/CommandLine.hpp" /** - The FullTraceImporter imports every dynamic ip-event from the trace into the database. + The FullTraceImporter imports every dynamic ip-event from the trace into the fulltrace database table. */ class FullTraceImporter : public Importer { protected: virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev); + Trace_Event &ev) override; virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev); - virtual bool create_database(); - virtual bool clear_database(); - using Importer::add_trace_event; - virtual bool add_trace_event(margin_info_t &begin, margin_info_t &end, - Trace_Event &event, bool is_fake = false); - virtual bool trace_end_reached(); + Trace_Event &ev) override; + virtual bool create_database() override; + virtual bool clear_database() override; + + virtual bool trace_end_reached() override; public: - void getAliases(std::deque *aliases) { + virtual void getAliases(std::deque *aliases) override { aliases->push_back("FullTraceImporter"); } }; diff --git a/tools/import-trace/Importer.cc b/tools/import-trace/Importer.cc index 305e43fa..e2dd4594 100644 --- a/tools/import-trace/Importer.cc +++ b/tools/import-trace/Importer.cc @@ -3,6 +3,7 @@ #include // std::pair #include "Importer.hpp" #include "util/Logger.hpp" +#include "sal/faultspace/MemoryArea.hpp" using namespace fail; @@ -18,6 +19,10 @@ bool Importer::init(const std::string &variant, const std::string &benchmark, Da m_arch.getRegisterSetOfType(RT_TRACE); LOG << "Importing to variant " << variant << "/" << benchmark << " (ID: " << m_variant_id << ")" << std::endl; + + if (!cb_initialize()) + return false; + return true; } @@ -31,8 +36,8 @@ bool Importer::create_database() { " instr2_absolute int(10) unsigned DEFAULT NULL," " time1 bigint(10) unsigned NOT NULL," " time2 bigint(10) unsigned NOT NULL," - " data_address int(10) unsigned NOT NULL," - " width tinyint(3) unsigned NOT NULL," + " data_address bigint(10) unsigned NOT NULL," + " data_mask tinyint(3) unsigned NOT NULL," " accesstype enum('R','W') NOT NULL,"; if (m_extended_trace) { create_statement << " data_value int(10) unsigned NULL,"; @@ -44,7 +49,7 @@ bool Importer::create_database() { } create_statement << database_additional_columns(); create_statement << - " PRIMARY KEY (variant_id,data_address,instr2)" + " PRIMARY KEY (variant_id,data_address,instr2,data_mask)" ") engine=MyISAM "; return db->query(create_statement.str().c_str()); } @@ -55,25 +60,35 @@ bool Importer::clear_database() { ss << "DELETE FROM trace WHERE variant_id = " << m_variant_id; bool ret = db->query(ss.str().c_str()) == 0 ? false : true; - LOG << "deleted " << db->affected_rows() << " rows from trace table" << std::endl; + LOG << "deleted " << std::dec << db->affected_rows() << " rows from trace table" << std::endl; return ret; } bool Importer::sanitycheck(std::string check_name, std::string fail_msg, std::string sql) { - LOG << "Sanity check: " << check_name << " ..." << std::flush; + LOG << "Sanity check: " << check_name << " ..." << std::flush << std::endl; MYSQL_RES *res = db->query(sql.c_str(), true); if (res && mysql_num_rows(res) == 0) { - std::cout << " OK" << std::endl; + LOG << " OK" << std::endl; return true; } else { - std::cout << " FAILED: " << fail_msg << std::endl; + LOG << " FAILED: " << std::endl << fail_msg << std::endl; + LOG << " ERROR MSG: " << std::endl; + MYSQL_ROW row; + int num_fields = mysql_num_fields(res); + while((row = mysql_fetch_row(res))) { + std::stringstream this_row; + for(int i = 0; i < num_fields; ++i) { + this_row << " " << row[i]; + } + LOG << this_row.str() << std::endl; + } return false; } } -bool Importer::copy_to_database(fail::ProtoIStream &ps) { +bool Importer::copy_to_database(ProtoIStream &ps) { // For now we just use the min/max occuring timestamp; for "sparse" traces // (e.g., only mem accesses, only a subset of the address space) it might // be a good idea to store this explicitly in the trace file, though. @@ -170,12 +185,12 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { // non-overlapping (instr1/2)? ss << - "SELECT t1.variant_id\n" + "SELECT t1.variant_id,HEX(t1.data_address),t1.instr1,t1.instr2\n" "FROM trace t1\n" "JOIN variant v\n" " ON v.id = t1.variant_id\n" "JOIN trace t2\n" - " ON t1.variant_id = t2.variant_id AND t1.data_address = t2.data_address\n" + " ON t1.variant_id = t2.variant_id AND t1.data_address = t2.data_address AND t1.data_mask = t2.data_mask\n" " AND (t1.instr1 != t2.instr1 OR t2.instr2 != t2.instr2)\n" " AND ((t1.instr1 >= t2.instr1 AND t1.instr1 <= t2.instr2)\n" " OR (t1.instr2 >= t2.instr1 AND t1.instr2 <= t2.instr2)\n" @@ -189,12 +204,12 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { // non-overlapping (time1/2)? ss << - "SELECT t1.variant_id\n" + "SELECT t1.variant_id, HEX(t1.data_address), t1.instr1, t1.instr2\n" "FROM trace t1\n" "JOIN variant v\n" " ON v.id = t1.variant_id\n" "JOIN trace t2\n" - " ON t1.variant_id = t2.variant_id AND t1.data_address = t2.data_address\n" + " ON t1.variant_id = t2.variant_id AND t1.data_address = t2.data_address AND t1.data_mask = t2.data_mask\n" " AND (t1.time1 != t2.time1 OR t2.time2 != t2.time2)\n" " AND ((t1.time1 >= t2.time1 AND t1.time1 <= t2.time2)\n" " OR (t1.time2 >= t2.time1 AND t1.time2 <= t2.time2)\n" @@ -208,18 +223,18 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { // covered (instr1/2)? ss << - "SELECT t.variant_id, t.data_address,\n" + "SELECT t.variant_id, HEX(t.data_address),\n" " (SELECT (MAX(t2.instr2) - MIN(t2.instr1) + 1)\n" " FROM trace t2\n" " WHERE t2.variant_id = t.variant_id)\n" " -\n" " (SELECT SUM(t3.instr2 - t3.instr1 + 1)\n" " FROM trace t3\n" - " WHERE t3.variant_id = t.variant_id AND t3.data_address = t.data_address)\n" + " WHERE t3.variant_id = t.variant_id AND t3.data_address = t.data_address AND t3.data_mask = t.data_mask)\n" " AS diff\n" "FROM trace t\n" "WHERE t.variant_id = " << m_variant_id << "\n" - "GROUP BY t.variant_id, t.data_address\n" + "GROUP BY t.variant_id, t.data_address,t.data_mask\n" "HAVING diff != 0\n" "ORDER BY t.data_address\n"; if (!sanitycheck("FS row sum = total width (instr1/2)", @@ -238,11 +253,11 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { " -\n" " (SELECT SUM(t3.time2 - t3.time1 + 1)\n" " FROM trace t3\n" - " WHERE t3.variant_id = t.variant_id AND t3.data_address = t.data_address)\n" + " WHERE t3.variant_id = t.variant_id AND t3.data_address = t.data_address AND t3.data_mask = t.data_mask)\n" " AS diff\n" "FROM trace t\n" "WHERE t.variant_id = " << m_variant_id << "\n" - "GROUP BY t.variant_id, t.data_address\n" + "GROUP BY t.variant_id, t.data_address, t.data_mask\n" "HAVING diff != 0\n" "ORDER BY t.data_address\n"; if (!sanitycheck("FS row sum = total width (time1/2)", @@ -264,7 +279,7 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { " (SELECT data_address, MIN(instr1) AS min_instr, MAX(instr2) AS max_instr\n" " FROM trace t3\n" " WHERE variant_id = " << m_variant_id << "\n" - " GROUP BY t3.variant_id, t3.data_address) AS local\n" + " GROUP BY t3.variant_id, t3.data_address, t3.data_mask) AS local\n" " ON (local.min_instr != global.min_instr\n" " OR local.max_instr != global.max_instr)"; if (!sanitycheck("Global min/max = FS row local min/max (instr1/2)", @@ -286,7 +301,7 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { " (SELECT data_address, MIN(time1) AS min_time, MAX(time2) AS max_time\n" " FROM trace t3\n" " WHERE variant_id = " << m_variant_id << "\n" - " GROUP BY t3.variant_id, t3.data_address) AS local\n" + " GROUP BY t3.variant_id, t3.data_address,t3.data_mask) AS local\n" " ON (local.min_time != global.min_time\n" " OR local.max_time != global.max_time)"; if (!sanitycheck("Global min/max = FS row local min/max (time1/2)", @@ -304,24 +319,117 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { return true; } -bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, - access_info_t &access, bool is_fake) { - Trace_Event e; - e.set_ip(end.ip); - e.set_memaddr(access.data_address); - e.set_width(access.data_width); - e.set_accesstype(access.access_type == 'R' ? e.READ : e.WRITE); - return add_trace_event(begin, end, e, is_fake); +using margin_info_t = Importer::margin_info_t; +std::ostream& operator<<(std::ostream& os, margin_info_t& v) { + os << std::hex << std::showbase; + os << "{ instr=" << v.dyninstr << ", ip=" << v.ip + << ", mask=" << std::hex << (int)v.mask << ", syn=" << v.synthetic << "}"; + return os << std::dec << std::noshowbase; +} + +std::vector +Importer::getLeftMargins(const FaultSpaceElement& fsp_elem, + simtime_t time, instruction_count_t dyninstr, + guest_address_t ip, uint8_t mask) { + std::vector results; + + if(m_open_ecs.count(fsp_elem) == 0) { + /* If this fault space element has never been before we must + * create a synthetic fallback margin, which contains all + * bits and will used, if no event inside the trace has ever touched + * a specific bit. + */ + m_open_ecs[fsp_elem].emplace_back(0, 0, m_time_trace_start, 0xFF, true); + } + + // For a given FSE, we get the stack of all open left margins. + auto& left_margins = m_open_ecs[fsp_elem]; + + /* Within this stack, we go from the newsest to the oldest left + margin and look for all margins that match our mask. + + While searching, we : + (1) delete the matching bits from the left margins + (2) we delete the left margin, if it matched. + */ + + assert(left_margins.size() > 0 + && "For a touched FSE, at least one margin must exist"); + + uint8_t found_bits = 0; + for(auto it = left_margins.rbegin(); it != left_margins.rend(); ++it) { + margin_info_t& margin = *it; + uint8_t overlap = margin.mask & mask; + + // Sanity Check: There is at most one margin_info_t that matches our mask. + assert((overlap & found_bits) == 0 + && "Multiple margin_info_t matched our access mask"); + found_bits |= overlap; + + if (overlap) { + // create left margin which contains the bits of margin + // that are closed by this interval. + results.emplace_back(margin, overlap); + + margin.mask ^= overlap; // delete bits + + if (margin.mask == 0) { + left_margins.erase(std::next(it).base()); + } + } + } + + // Create a new margin for the current instruction, with the + // requested access mask. + left_margins.emplace_back(dyninstr+1, ip, time+1, mask); + + return results; +} + +bool Importer::add_faultspace_element(simtime_t curtime, instruction_count_t instr, + const FaultSpaceElement &element, + uint8_t mask, char access_type, Trace_Event& origin) { + LOG << std::hex << std::showbase + << "[IP=" << origin.ip() << "] " + << " working on element: " << element + << " with access: " << access_type + << " and mask " << (int)mask << std::endl; + + auto left_margins = getLeftMargins(element,curtime,instr,origin.ip(),mask); + + for(auto& left_margin : left_margins) { + margin_info_t right_margin(instr, origin.ip(), curtime, left_margin.mask); + + // skip zero-sized intervals: these can occur when an + // instruction accesses a fault location more than once. This + // for example happens, if memory is read and written (e.g., + // INC, CMPXCHG) or if --ip is given and the instruction also reads EIP + if(left_margin.time > right_margin.time) { + continue; + } + + // pass through potentially available extended trace information + fsp_address_t data_address = element.get_address(); + if(!add_trace_row(left_margin, right_margin, data_address, access_type, origin)) { + LOG << "add_trace_row failed" << std::endl; + return false; + } + } + + return true; } -bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, - Trace_Event &event, bool is_fake) { - if (!m_import_write_ecs && event.accesstype() == event.WRITE) { +bool Importer::add_trace_row(margin_info_t& begin, margin_info_t &end, + const fsp_address_t data_address, + char access_type, + Trace_Event& origin, + bool known_outcome) { + if (!m_import_write_ecs && access_type == 'W') { return true; } // insert extended trace info if configured and available - bool extended = m_extended_trace && event.has_trace_ext(); + bool extended = m_extended_trace && origin.has_trace_ext(); std::string *insert_sql; unsigned *columns; @@ -333,11 +441,12 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, columns = extended ? &columns_extended : &columns_basic; static unsigned num_additional_columns = 0; + LOG << "add interval:" << begin << " -> " << end << std::endl; if (!insert_sql->size()) { std::stringstream sql; sql << "INSERT INTO trace (variant_id, instr1, instr1_absolute, instr2, instr2_absolute, time1, time2, " - "data_address, width, accesstype"; + "data_address, data_mask, accesstype"; *columns = 10; if (extended) { sql << ", data_value"; @@ -365,7 +474,7 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, std::map > register_values; std::map > register_values_deref; unsigned data_value = 0; - const Trace_Event_Extended& ev_ext = event.trace_ext(); + const Trace_Event_Extended& ev_ext = origin.trace_ext(); if (extended) { data_value = ev_ext.data(); for (int i = 0; i < ev_ext.registers_size(); i++) { @@ -375,20 +484,20 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, } } - unsigned data_address = event.memaddr(); - char accesstype = event.accesstype() == event.READ ? 'R' : 'W'; + unsigned mask = begin.mask; + assert(begin.mask == end.mask); std::stringstream value_sql; value_sql << "(" << m_variant_id << "," << begin.dyninstr << ","; - if (begin.ip == 0 || is_fake) { + if (begin.ip == 0 || known_outcome) { value_sql << "NULL,"; } else { value_sql << begin.ip << ","; } value_sql << end.dyninstr << ","; - if (end.ip == 0 || is_fake) { + if (end.ip == 0 || known_outcome) { value_sql << "NULL,"; } else { value_sql << end.ip << ","; @@ -396,8 +505,8 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, value_sql << begin.time << "," << end.time << "," << data_address << "," - << "1," // width - << "'" << accesstype << "',"; + << mask << "," + << "'" << access_type << "',"; if (extended) { value_sql << data_value << ","; @@ -425,7 +534,7 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, // Ask specialized importers what concrete data they want to INSERT. if (num_additional_columns && - !database_insert_data(event, value_sql, num_additional_columns, is_fake)) { + !database_insert_data(origin, value_sql, num_additional_columns, known_outcome)) { return false; } @@ -461,10 +570,15 @@ void Importer::open_unused_ec_intervals() { // we just don't know the extents of our fault space; just guessing by // using the minimum and maximum addresses is not a good idea, we might // have large holes in the fault space. + if (m_mm) { + auto filter = [] (address_t a) -> bool { return true; }; + auto area = dynamic_cast(&m_fsp.get_area("ram")); for (MemoryMap::iterator it = m_mm->begin(); it != m_mm->end(); ++it) { - if (m_open_ecs.count(*it) == 0) { - newOpenEC(*it, m_time_trace_start, 0, 0); + for (auto &element : area->translate(*it, *it, filter)) { // exactly one element + if (m_open_ecs.count(element) == 0) { + m_open_ecs[element].emplace_back(0, 0, m_time_trace_start, 0xFF, true); + } } } } @@ -481,34 +595,27 @@ bool Importer::close_ec_intervals() { // (synthetic read), the latter resulting in more experiments to be done. for (AddrLastaccessMap::iterator lastuse_it = m_open_ecs.begin(); lastuse_it != m_open_ecs.end(); ++lastuse_it) { + // iterate over each left_margin and close with a similarily + // masked right margin unless the left margin is synthetic. + std::vector& margins = lastuse_it->second; + for(auto& left_margin: margins) { + // don't close synthetic intervals + // FIXME: this should probably be a function. + if(left_margin.synthetic) + continue; + margin_info_t right_margin(m_last_instr, m_last_ip, m_last_time, left_margin.mask); + // zero-sized? skip. + // FIXME: look at timing instead? + if (left_margin.dyninstr > right_margin.dyninstr) { + continue; + } - access_info_t access; - access.access_type = m_faultspace_rightmargin; - access.data_address = lastuse_it->first; - access.data_width = 1; // One Byte - - margin_info_t left_margin, right_margin; - left_margin = lastuse_it->second; - - right_margin.dyninstr = m_last_instr; - right_margin.time = m_last_time; // -1? - right_margin.ip = m_last_ip; // The last event in the log. -// #else -// // EcosKernelTestCampaign only variant: fault space ends with the last FI experiment -// FIXME probably implement this with cmdline parameter FAULTSPACE_CUTOFF -// right_margin.dyninstr = instr_rightmost; -// #endif - - // zero-sized? skip. - // FIXME: look at timing instead? - if (left_margin.dyninstr > right_margin.dyninstr) { - continue; - } - - - if (!add_trace_event(left_margin, right_margin, access, true)) { - LOG << "add_trace_event failed" << std::endl; - return false; + Trace_Event t; + fsp_address_t data_address = lastuse_it->first.get_address(); + if (!add_trace_row(left_margin, right_margin, data_address, m_faultspace_rightmargin, t)) { + LOG << "add_trace_row failed" << std::endl; + return false; + } } } diff --git a/tools/import-trace/Importer.hpp b/tools/import-trace/Importer.hpp index 8cb03267..54f8d794 100644 --- a/tools/import-trace/Importer.hpp +++ b/tools/import-trace/Importer.hpp @@ -8,18 +8,45 @@ #include "util/ElfReader.hpp" #include "sal/SALConfig.hpp" #include "sal/Architecture.hpp" +#include "sal/faultspace/FaultSpace.hpp" #include "util/Database.hpp" #include "util/MemoryMap.hpp" #include "comm/TracePlugin.pb.h" #include "util/AliasedRegisterable.hpp" +#if defined(BUILD_CAPSTONE_DISASSEMBLER) +#include "util/capstonedisassembler/CapstoneDisassembler.hpp" +#elif defined(BUILD_LLVM_DISASSEMBLER) +#include "util/llvmdisassembler/LLVMDisassembler.hpp" +#endif + + class Importer : public fail::AliasedRegisterable { + public: typedef unsigned instruction_count_t; //!< not big enough for some benchmarks - struct margin_info_t { instruction_count_t dyninstr; fail::guest_address_t ip; fail::simtime_t time; }; - struct access_info_t { char access_type; fail::guest_address_t data_address; int data_width; }; + struct margin_info_t { + instruction_count_t dyninstr; + fail::guest_address_t ip; + fail::simtime_t time; + uint8_t mask; + bool synthetic; + margin_info_t(instruction_count_t dyninstr, fail::guest_address_t ip, fail::simtime_t time, uint8_t mask, bool synthetic = false): + dyninstr(dyninstr), ip(ip), time(time), mask(mask), synthetic(synthetic) { } + margin_info_t(const margin_info_t& other, uint8_t mask, bool synthetic = false): + dyninstr(other.dyninstr), ip(other.ip), time(other.time), mask(mask), synthetic(other.synthetic) { } + }; protected: +#if defined(BUILD_LLVM_DISASSEMBLER) + typedef fail::LLVMtoFailTranslator RegisterTranslator; + typedef fail::LLVMDisassembler Disassembler; + +#elif defined(BUILD_CAPSTONE_DISASSEMBLER) + typedef fail::CapstoneToFailTranslator RegisterTranslator; + typedef fail::CapstoneDisassembler Disassembler; +#endif + int m_variant_id; fail::ElfReader *m_elf; fail::MemoryMap *m_mm; @@ -32,6 +59,7 @@ class Importer : public fail::AliasedRegisterable { fail::Architecture m_arch; fail::UniformRegisterSet *m_extended_trace_regs; fail::memory_type_t m_memtype; + fail::FaultSpace m_fsp; /* How many rows were inserted into the database */ unsigned m_row_count; @@ -43,26 +71,21 @@ class Importer : public fail::AliasedRegisterable { (maps injection data address => dyn. instruction count / time information for equivalence class left margin) */ - typedef std::map AddrLastaccessMap; + typedef std::map> AddrLastaccessMap; AddrLastaccessMap m_open_ecs; - margin_info_t getOpenEC(fail::address_t data_address) { - margin_info_t ec = m_open_ecs[data_address]; - // defaulting to 0 is not such a good idea, memory reads at the - // beginning of the trace would get an unnaturally high weight: - if (ec.time == 0) { - ec.time = m_time_trace_start; - } - return ec; - } - - void newOpenEC(fail::address_t data_address, fail::simtime_t time, instruction_count_t dyninstr, - fail::guest_address_t ip) { - m_open_ecs[data_address].dyninstr = dyninstr; - m_open_ecs[data_address].time = time; - // FIXME: This should be the next IP, not the current, or? - m_open_ecs[data_address].ip = ip; - } + std::vector getLeftMargins( + const fail::FaultSpaceElement& fsp_elem, + fail::simtime_t time, instruction_count_t dyninstr, + fail::guest_address_t ip, uint8_t mask + ); + + void addLeftMargin( + const fail::FaultSpaceElement& fsp_elem, + fail::simtime_t time, instruction_count_t dyninstr, + fail::guest_address_t ip, + uint8_t mask + ); fail::guest_address_t m_last_ip; @@ -80,28 +103,36 @@ class Importer : public fail::AliasedRegisterable { /** * Similar to database_additional_columns(), this allows specialized * importers to define which additional columns it wants to INSERT - * alongside what add_trace_event() adds by itself. This may be identical + * alongside what add_trace_row() adds by itself. This may be identical * to or a subset of what database_additional_columns() specifies. The SQL * snippet should *begin* with a comma if non-empty. */ virtual void database_insert_columns(std::string& sql, unsigned& num_columns) { num_columns = 0; } /** - * Will be called back from add_trace_event() to fill in data for the + * Will be called back from add_trace_row() to fill in data for the * columns specified by database_insert_columns(). */ virtual bool database_insert_data(Trace_Event &ev, std::stringstream& value_sql, unsigned num_columns, bool is_fake) { return true; } /** - * Use this variant if passing through the IP/MEM event does not make any - * sense for your Importer implementation. + * To be called from your importer implementation to add fault + * space elements to the database. */ - virtual bool add_trace_event(margin_info_t &begin, margin_info_t &end, - access_info_t &event, bool is_fake = false); + virtual bool add_faultspace_element( + fail::simtime_t curtime, instruction_count_t instr, + const fail::FaultSpaceElement &element, + uint8_t mask, char access_type, Trace_Event& origin + ); /** - * Use this variant for passing through the IP/MEM event your Importer - * received. + * Add a single row into the trace database table. This is usually + * called from add_fault_space_elements, but can optionally be + * called directly to bypass the automatic margin generation. */ - virtual bool add_trace_event(margin_info_t &begin, margin_info_t &end, - Trace_Event &event, bool is_fake = false); + virtual bool add_trace_row(margin_info_t &begin, margin_info_t &end, + const fail::fsp_address_t data_address, + char access_type, + Trace_Event& origin, + bool known_outcome = false + ); virtual void open_unused_ec_intervals(); virtual bool close_ec_intervals(); @@ -134,6 +165,13 @@ class Importer : public fail::AliasedRegisterable { */ virtual bool cb_commandline_init() { return true; } + /** + * Callback function that can be used to initialize the importer + * before the trace is consumed. + */ + virtual bool cb_initialize() { return true; } + + virtual bool create_database(); virtual bool copy_to_database(fail::ProtoIStream &ps); virtual bool clear_database(); diff --git a/tools/import-trace/InstructionImporter.cc b/tools/import-trace/InstructionImporter.cc index 60243cf4..2bffcaba 100644 --- a/tools/import-trace/InstructionImporter.cc +++ b/tools/import-trace/InstructionImporter.cc @@ -2,6 +2,7 @@ #include #include "InstructionImporter.hpp" #include "util/Logger.hpp" +#include "sal/faultspace/MemoryArea.hpp" #ifdef BUILD_LLVM_DISASSEMBLER using namespace llvm; @@ -12,104 +13,42 @@ using namespace fail; static Logger LOG("InstructionImporter"); -bool InstructionImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev) { -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - if (!isDisassembled) { - if (!m_elf) { - LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; - return false; - } +bool InstructionImporter::cb_initialize() { + if (!m_elf) { + LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; + return false; + } - disas.reset(new CapstoneDisassembler(m_elf)); + m_disassembler.reset(new Disassembler(m_elf)); + m_disassembler->disassemble(); + m_instr_map = m_disassembler->getInstrMap(); - disas->disassemble(); - CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - LOG << "instructions disassembled: " << instr_map.size() << std::endl; - isDisassembled = true; - } - const CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - if (instr_map.find(ev.ip()) == instr_map.end()) { + return true; +} + +bool InstructionImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { + if (m_instr_map->find(ev.ip()) == m_instr_map->end()) { LOG << "Could not find instruction for IP " << std::hex << ev.ip() << ", skipping" << std::endl; return true; } - const CapstoneDisassembler::Instr &opcode = instr_map.at(ev.ip()); -#elif defined(BUILD_LLVM_DISASSEMBLER) - if (!binary) { - /* 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> 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 = llvm::dyn_cast(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; - } - - const LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap(); - const LLVMDisassembler::Instr &opcode = instr_map.at(ev.ip()); -#endif - - address_t from = ev.ip(), to = ev.ip() + opcode.length; + const Disassembler::Instr &opcode = m_instr_map->at(ev.ip()); - // 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(data_address)) { - 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(); + guest_address_t from = ev.ip(), to = ev.ip() + std::ceil(opcode.length/8.0); - // 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; - } + assert(std::ceil(opcode.length/8.0) == std::floor(opcode.length/8.0) + && "Instruction importer can't handle instruction length which don't have byte aligned length"); + + auto filter = [this] (address_t a) -> bool { return !(this->m_mm && !this->m_mm->isMatching(a)); }; + auto area = dynamic_cast(&m_fsp.get_area("ram")); + assert(area != nullptr && "InstructionImporter failed to get a MemoryArea from the fault space description"); - // 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_accesstype(ev.READ); // instruction fetch is always a read - ev.set_memaddr(data_address); - ev.set_width(1); // exactly one byte - if (!add_trace_event(left_margin, right_margin, ev)) { - LOG << "add_trace_event failed" << std::endl; + char access_type = ev.accesstype() == ev.READ ? 'R' : 'W'; + for (auto element : area->translate(from, to, filter)) { + if (!add_faultspace_element(curtime, instr, element, 0xFF, access_type, ev)) { 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; } diff --git a/tools/import-trace/InstructionImporter.hpp b/tools/import-trace/InstructionImporter.hpp index 9640e230..975edb34 100644 --- a/tools/import-trace/InstructionImporter.hpp +++ b/tools/import-trace/InstructionImporter.hpp @@ -3,22 +3,13 @@ #include "Importer.hpp" -#if defined(BUILD_CAPSTONE_DISASSEMBLER) -#include "util/capstonedisassembler/CapstoneDisassembler.hpp" -#elif defined(BUILD_LLVM_DISASSEMBLER) -#include "util/llvmdisassembler/LLVMDisassembler.hpp" -#endif - class InstructionImporter : public Importer { -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - bool isDisassembled = false; - std::unique_ptr disas; -#elif defined(BUILD_LLVM_DISASSEMBLER) - llvm::object::Binary *binary = 0; - std::unique_ptr disas; -#endif + std::unique_ptr m_disassembler; + Disassembler::InstrMap* m_instr_map; protected: + virtual bool cb_initialize(); + virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, Trace_Event &ev); virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, diff --git a/tools/import-trace/MemoryImporter.cc b/tools/import-trace/MemoryImporter.cc index d54d502c..13126165 100644 --- a/tools/import-trace/MemoryImporter.cc +++ b/tools/import-trace/MemoryImporter.cc @@ -1,5 +1,6 @@ #include "util/Logger.hpp" #include "MemoryImporter.hpp" +#include "sal/faultspace/MemoryArea.hpp" using namespace fail; static fail::Logger LOG("MemoryImporter"); @@ -11,51 +12,27 @@ bool MemoryImporter::handle_ip_event(simtime_t curtime, instruction_count_t inst bool MemoryImporter::handle_mem_event(simtime_t curtime, instruction_count_t instr, Trace_Event &ev) { - // Filter out memory events of wrong memory type memory_type_t mtype = static_cast(ev.memtype()); - if(mtype != m_memtype && m_memtype != ANY_MEMORY) - return true; - - address_t from = ev.memaddr(), to = ev.memaddr() + ev.width(); - // Iterate over all accessed bytes - // FIXME Keep complete trace information (access width)? - // advantages: may be used for pruning strategies, complete value would be visible; less DB entries - // disadvantages: may need splitting when width varies, lots of special case handling - // Probably implement this in a separate importer when necessary. - 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(data_address)) { - 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) - // FIXME: look at timing instead? - if (left_margin.dyninstr > right_margin.dyninstr) { - continue; - } + if (mtype == MEMTYPE_UNKNOWN) // Legacy traces + mtype = MEMTYPE_RAM; - // 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_memaddr(data_address); - ev.set_width(1); // exactly one byte - if (!add_trace_event(left_margin, right_margin, ev)) { - LOG << "add_trace_event failed" << std::endl; - return false; - } + if(mtype == m_memtype || m_memtype == ANY_MEMORY) { + // Get MemoryArea by memory type + std::string area_id = memtype_descriptions[mtype]; + auto area = dynamic_cast(&m_fsp.get_area(area_id)); + assert(area != nullptr && "MemoryImporter failed to get a MemoryArea from the fault space description"); - // next interval must start at next instruction; the aforementioned - // skipping mechanism wouldn't work otherwise - newOpenEC(data_address, curtime + 1, instr + 1, ev.ip()); + char access_type = ev.accesstype() == ev.READ ? 'R' : 'W'; + + guest_address_t from = ev.memaddr(), to = ev.memaddr() + ev.width(); + LOG << std::hex << std::showbase << ev.width() << " bytes -> from=" << from << " to=" << to << std::endl; + + auto filter = [this] (address_t a) -> bool { return !(this->m_mm && !this->m_mm->isMatching(a)); }; + for (auto &element : area->translate(from, to, filter)) { + if(!add_faultspace_element(curtime, instr, element, 0xFF, + access_type, ev)) + return false; + } } return true; } diff --git a/tools/import-trace/RandomJumpImporter.cc b/tools/import-trace/RandomJumpImporter.cc index e799a904..7b23304e 100644 --- a/tools/import-trace/RandomJumpImporter.cc +++ b/tools/import-trace/RandomJumpImporter.cc @@ -26,99 +26,64 @@ bool RandomJumpImporter::cb_commandline_init() { return true; } -bool RandomJumpImporter::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; - } +bool RandomJumpImporter::cb_initialize() { + // 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; + } - // Read FROM memory map(s) - if (cmd[FROM]) { - m_mm_from = new MemoryMap(); - for (option::Option *o = cmd[FROM]; o; o = o->next()) { - if (!m_mm_from->readFromFile(o->arg)) { - LOG << "failed to load memorymap " << o->arg << endl; - return false; - } + // Read FROM memory map(s) + if (cmd[FROM]) { + m_mm_from = new MemoryMap(); + for (option::Option *o = cmd[FROM]; o; o = o->next()) { + if (!m_mm_from->readFromFile(o->arg)) { + LOG << "failed to load memorymap " << o->arg << endl; + return false; } } + } - // Read TO memory map(s) - if (cmd[TO]) { - m_mm_to = new MemoryMap(); - for (option::Option *o = cmd[TO]; o; o = o->next()) { - if (!m_mm_to->readFromFile(o->arg)) { - LOG << "failed to load memorymap " << o->arg << endl; - return false; - } + // Read TO memory map(s) + if (cmd[TO]) { + m_mm_to = new MemoryMap(); + for (option::Option *o = cmd[TO]; o; o = o->next()) { + if (!m_mm_to->readFromFile(o->arg)) { + LOG << "failed to load memorymap " << o->arg << endl; + return false; } - } else { - LOG << "Please give at least one --jump-to memory map" << endl; - return false; } + } else { + LOG << "Please give at least one --jump-to memory map" << endl; + return false; + } - if (!m_elf) { - LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; - return false; - } -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - disas.reset(new CapstoneDisassembler(m_elf)); - - disas->disassemble(); - CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - LOG << "instructions disassembled: " << instr_map.size() << std::endl; - - /* Collect all addresses we want to jump to */ - for (CapstoneDisassembler::InstrMap::const_iterator instr = instr_map.begin(); - instr != instr_map.end(); ++instr) { - if (m_mm_to && m_mm_to->isMatching(instr->first)) { - m_jump_to_addresses.push_back(instr->first); - } - } - binary = true; -#elif defined(BUILD_LLVM_DISASSEMBLER) - /* Disassemble the binary if necessary */ - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllDisassemblers(); - - Expected> 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(); + if (!m_elf) { + LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; + return false; + } -// necessary due to an AspectC++ bug triggered by LLVM 3.3's dyn_cast() -#ifndef __puma - ObjectFile *obj = dyn_cast(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; - - /* Collect all addresses we want to jump to */ - for (LLVMDisassembler::InstrMap::const_iterator instr = instr_map.begin(); - instr != instr_map.end(); ++instr) { - if (m_mm_to && m_mm_to->isMatching(instr->first)) { - m_jump_to_addresses.push_back(instr->first); - } + m_disassembler.reset(new Disassembler(m_elf)); + m_disassembler->disassemble(); + auto instr_map = m_disassembler->getInstrMap(); + + /* Collect all addresses we want to jump to */ + for (Disassembler::InstrMap::const_iterator instr = instr_map->begin(); + instr != instr_map->end(); ++instr) { + if (m_mm_to && m_mm_to->isMatching(instr->first)) { + m_jump_to_addresses.push_back(instr->first); } -#endif - LOG << "we will jump to " << m_jump_to_addresses.size() << " addresses" << endl; } + LOG << "we will jump to " << m_jump_to_addresses.size() << " addresses" << endl; + + return true; +} +bool RandomJumpImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { // skip events that are outside the memory map. -m instruction map if (m_mm && !m_mm->isMatching(ev.ip())) { return true; @@ -129,7 +94,6 @@ bool RandomJumpImporter::handle_ip_event(fail::simtime_t curtime, instruction_co return true; } - for (std::vector::const_iterator it = m_jump_to_addresses.begin(); it != m_jump_to_addresses.end(); ++it) { guest_address_t to_addr = *it; @@ -137,22 +101,22 @@ bool RandomJumpImporter::handle_ip_event(fail::simtime_t curtime, instruction_co if (to_addr == ev.ip()) continue; - margin_info_t margin; - margin.time = curtime; - margin.dyninstr = instr; // !< The current instruction - margin.ip = ev.ip(); + margin_info_t margin(instr, ev.ip(), curtime, 0xFF); // 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_accesstype(ev.READ); // instruction fetch is always a read ev.set_memaddr(to_addr); - ev.set_width(4); // FIXME arbitrary, use Instr.length instead? - if (!add_trace_event(margin, margin, ev)) { - LOG << "add_trace_event failed" << std::endl; + unsigned pc_width = m_elf->m_elfclass == ELFCLASS64 ? 64 : 32; + ev.set_width(pc_width); // FIXME arbitrary, use Instr.length instead? + + if (!add_trace_row(margin, margin, to_addr, 'R', ev)) { + LOG << "add_trace_row failed" << std::endl; return false; } } diff --git a/tools/import-trace/RandomJumpImporter.hpp b/tools/import-trace/RandomJumpImporter.hpp index 48096bd3..622186e7 100644 --- a/tools/import-trace/RandomJumpImporter.hpp +++ b/tools/import-trace/RandomJumpImporter.hpp @@ -5,25 +5,15 @@ #include "util/CommandLine.hpp" #include "Importer.hpp" -#if defined(BUILD_CAPSTONE_DISASSEMBLER) -#include "util/capstonedisassembler/CapstoneDisassembler.hpp" -#elif defined(BUILD_LLVM_DISASSEMBLER) -#include "util/llvmdisassembler/LLVMDisassembler.hpp" -#endif class RandomJumpImporter : public Importer { -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - bool binary = false; - std::unique_ptr disas; -#elif defined(BUILD_LLVM_DISASSEMBLER) - llvm::object::Binary *binary = 0; - std::unique_ptr disas; -#endif - fail::CommandLine::option_handle FROM, TO; fail::MemoryMap *m_mm_from, *m_mm_to; std::vector m_jump_to_addresses; + + std::unique_ptr m_disassembler; + public: RandomJumpImporter() : m_mm_from(0), m_mm_to(0) {} /** @@ -32,6 +22,8 @@ class RandomJumpImporter : public Importer { */ virtual bool cb_commandline_init(); + virtual bool cb_initialize(); + protected: virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, Trace_Event &ev); diff --git a/tools/import-trace/RegisterImporter.cc b/tools/import-trace/RegisterImporter.cc index 05606edc..8dc86fba 100644 --- a/tools/import-trace/RegisterImporter.cc +++ b/tools/import-trace/RegisterImporter.cc @@ -1,16 +1,12 @@ #include #include +#include #include "RegisterImporter.hpp" +#include "sal/Register.hpp" #include "util/Logger.hpp" -#ifdef BUILD_LLVM_DISASSEMBLER -using namespace llvm; -using namespace llvm::object; -#endif - using namespace fail; - static Logger LOG("RegisterImporter"); /** @@ -26,158 +22,91 @@ bool RegisterImporter::cb_commandline_init() { "--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; } -#if defined(BUILD_CAPSTONE_DISASSEMBLER) -bool RegisterImporter::addRegisterTrace(simtime_t curtime, instruction_count_t instr, - Trace_Event &ev, - const CapstoneToFailTranslator::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 */ - CapstoneToFailTranslator::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); +bool RegisterImporter::cb_initialize(void) { + CommandLine &cmd = CommandLine::Inst(); + if (!cmd.parse()) { + std::cerr << "Error parsing arguments." << std::endl; + return false; } - // 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; - } + if (!m_elf) { + LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; + return false; + } - // 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; + // Depending on the command line interface, we fill up + // m_register_ids, which is used to filter all def/used registers + // during trace consumption. + fail::Architecture *arch = m_area->get_arch(); + if (cmd[IP]) { + m_import_ip = true; + Register *pc = *(arch->getRegisterSetOfType(RT_IP)->begin()); + // Depending on the ELF Class of the imported binary, we + // select the width of the PC register to be injected on + // the --ip command-line switch. + unsigned pc_width = m_elf->m_elfclass == ELFCLASS64 ? 64 : 32; + m_ip_register = RegisterView(pc->getId(), pc_width); + m_register_ids.insert(pc->getId()); + } + + if (!cmd[NO_GP]) { + auto regset = arch->getRegisterSetOfType(RT_GP); + assert(regset != nullptr && "No register was marked as general purpose register"); + for (Register * reg : *regset) { + m_register_ids.insert(reg->getId()); } + } - // next interval must start at next instruction; the aforementioned - // skipping mechanism wouldn't work otherwise - newOpenEC(data_address, curtime + 1, instr + 1, ev.ip()); + if (cmd[FLAGS]) { + auto regset = arch->getRegisterSetOfType(RT_ST); + assert(regset != nullptr && "Architecture has no flags registers"); + for (Register * reg : *regset) { + m_register_ids.insert(reg->getId()); + } } + + + m_disassembler.reset(new Disassembler(m_elf)); + m_disassembler->disassemble(); + m_instr_map = m_disassembler->getInstrMap(); + m_translator = m_disassembler->getTranslator(); + return true; } - bool RegisterImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, Trace_Event &ev) { - if (!isDisassembled) { - // 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()); - } - } - + // If the instruction pointer is not included in the memory map, ignore ir. + if(m_mm && m_mm->isMatching(ev.ip())) + return true; - if (!m_elf) { - LOG << "Please give an ELF binary as parameter (-e/--elf)." << std::endl; + // instruction pointer is read + written at each instruction + if (m_import_ip) { + if (!addRegisterTrace(curtime, instr, ev, m_ip_register, 'R') || + !addRegisterTrace(curtime, instr, ev, m_ip_register, 'W')) { return false; } - disas.reset(new CapstoneDisassembler(m_elf)); - LOG << "Start to dissamble" << std::endl; - disas->disassemble(); - LOG << "Get instr map" << std::endl; - CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - LOG << "instructions disassembled: " << std::dec << instr_map.size() << std::endl; - m_ctof = disas->getTranslator(); - isDisassembled = true; } - // instruction pointer is read + written at each instruction - const CapstoneToFailTranslator::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 CapstoneDisassembler::InstrMap &instr_map = disas->getInstrMap(); - if (instr_map.find(ev.ip()) == instr_map.end()) { + if (m_instr_map->find(ev.ip()) == m_instr_map->end()) { LOG << "Could not find instruction for IP " << std::hex << ev.ip() << ", skipping" << std::endl; return true; } - const CapstoneDisassembler::Instr &opcode = instr_map.at(ev.ip()); - //const MCRegisterInfo ®_info = disas->getRegisterInfo(); -// LOG << std::hex << "Address: " << opcode.address << " Opcode: " << opcode.opcode << std::endl; -// std::string log_regs = "Uses: "; - for (std::vector::const_iterator it = opcode.reg_uses.begin(); - it != opcode.reg_uses.end(); ++it) { -// log_regs += std::to_string(*it) + " "; - const CapstoneToFailTranslator::reginfo_t &info = m_ctof->getFailRegisterInfo(*it); - if (&info == &m_ctof->notfound) { - // record failed translation, report later - m_regnotfound[*it].count++; - m_regnotfound[*it].address.insert(ev.ip()); - continue; - } - /* only proceed if we want to inject into this register */ - if (m_register_ids.find(info.id) == m_register_ids.end()) { -// log_regs += "n "; + const Disassembler::Instr &opcode = m_instr_map->at(ev.ip()); + LOG << "working on instruction @ IP " << std::hex << ev.ip() << std::endl; + + for (Disassembler::register_t reg : opcode.reg_uses) { + const RegisterView &info = m_translator->getFailRegisterInfo(reg); + if (&info == &m_translator->notfound) { + // record failed translation, report later + m_regnotfound[reg].count++; + m_regnotfound[reg].address.insert(ev.ip()); continue; } @@ -186,270 +115,65 @@ bool RegisterImporter::handle_ip_event(fail::simtime_t curtime, instruction_coun } } -// log_regs += "Defs: "; - - for (std::vector::const_iterator it = opcode.reg_defs.begin(); - it != opcode.reg_defs.end(); ++it) { -// log_regs += std::to_string(*it) + " "; - const CapstoneToFailTranslator::reginfo_t &info = m_ctof->getFailRegisterInfo(*it); - if (&info == &m_ctof->notfound) { + for (Disassembler::register_t reg : opcode.reg_defs) { + const RegisterView &info = m_translator->getFailRegisterInfo(reg); + if (&info == &m_translator->notfound) { // record failed translation, report later - m_regnotfound[*it].count++; - m_regnotfound[*it].address.insert(ev.ip()); - continue; - } - - /* only proceed if we want to inject into this register */ - if (m_register_ids.find(info.id) == m_register_ids.end()) { -// log_regs += "n "; + m_regnotfound[reg].count++; + m_regnotfound[reg].address.insert(ev.ip()); continue; } if (!addRegisterTrace(curtime, instr, ev, info, 'W')) return false; } -// LOG << log_regs.c_str() << std::endl; return true; } -bool RegisterImporter::trace_end_reached() -{ - // report failed LLVM -> FAIL* register mappings, if any - if (m_regnotfound.empty()) { - return true; - } - - LOG << "WARNING: Some LLVM -> FAIL* register mappings failed during import, these will not be injected into:" << std::endl; - for (auto it = m_regnotfound.cbegin(); it != m_regnotfound.cend(); ++it) { - const CapstoneDisassembler::register_t id = it->first; - const RegNotFound& rnf = it->second; - LOG << "Capstone register " << std::dec << id - /* << " \"" << disas->getRegisterInfo().getName(id) << "\": " */ - << " seen " << rnf.count << " times in the trace" << std::endl; - std::ostream& o = LOG << " corresponding instruction addresses in ELF binary: " << std::hex; - for (auto addr_it = rnf.address.cbegin(); addr_it != rnf.address.cend(); ++addr_it) { - if (addr_it != rnf.address.cbegin()) { - o << ", "; - } - o << "0x" << *addr_it; - } - o << std::endl; - } - - return true; -} -#elif defined(BUILD_LLVM_DISASSEMBLER) bool RegisterImporter::addRegisterTrace(simtime_t curtime, instruction_count_t instr, Trace_Event &ev, - const LLVMtoFailTranslator::reginfo_t &info, + RegisterView view, 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> 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(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; + /* only proceed if we want to inject into this register */ + if (m_register_ids.find(view.id) == m_register_ids.end()) return true; - } - const LLVMDisassembler::Instr &opcode = instr_map.at(ev.ip()); - //const MCRegisterInfo ®_info = disas->getRegisterInfo(); -// LOG << std::hex << "Address: " << opcode.address << " Opcode: " << opcode.opcode << std::endl; -// std::string log_regs = "Uses: "; - - for (std::vector::const_iterator it = opcode.reg_uses.begin(); - it != opcode.reg_uses.end(); ++it) { -// log_regs += std::to_string(*it) + " "; - const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterInfo(*it); - if (&info == &m_ltof->notfound) { - // record failed translation, report later - m_regnotfound[*it].count++; - m_regnotfound[*it].address.insert(ev.ip()); - continue; - } - /* only proceed if we want to inject into this register */ - if (m_register_ids.find(info.id) == m_register_ids.end()) { - continue; - } + Architecture *arch = m_area->get_arch(); + Register * reg = arch->getRegister(view.id); - if (!addRegisterTrace(curtime, instr, ev, info, 'R')) { - return false; - } + if (view.width == -1) { + view.width = reg->getWidth(); } -// log_regs += "Defs: "; - for (std::vector::const_iterator it = opcode.reg_defs.begin(); - it != opcode.reg_defs.end(); ++it) { -// log_regs += std::to_string(*it) + " "; - const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterInfo(*it); - if (&info == &m_ltof->notfound) { - // record failed translation, report later - m_regnotfound[*it].count++; - m_regnotfound[*it].address.insert(ev.ip()); - continue; - } + LOG << std::hex << "[IP=0x" << ev.ip() << "] " << reg->getName() << std::dec + << " access: " << access_type + << " offset: " << (int) view.offset + << " width: " << (int) view.width + << std::endl; - /* only proceed if we want to inject into this register */ - if (m_register_ids.find(info.id) == m_register_ids.end()) { -// log_regs += "n "; - continue; - } - - if (!addRegisterTrace(curtime, instr, ev, info, 'W')) - return false; + for (auto access : m_area->translate(view)) { + bool succ = add_faultspace_element(curtime, instr, + access.first, access.second, + access_type, ev); + if (!succ) return false; } -// LOG << log_regs.c_str() << std::endl; - return true; } bool RegisterImporter::trace_end_reached() { - // report failed LLVM -> FAIL* register mappings, if any + // report failed Dissassembler -> FAIL* register mappings, if any if (m_regnotfound.empty()) { return true; } - LOG << "WARNING: Some LLVM -> FAIL* register mappings failed during import, these will not be injected into:" << std::endl; + LOG << "WARNING: Some Disassembler -> FAIL* register mappings failed during import, these will not be injected into:" << std::endl; for (auto it = m_regnotfound.cbegin(); it != m_regnotfound.cend(); ++it) { - const LLVMDisassembler::register_t id = it->first; + const Disassembler::register_t id = it->first; const RegNotFound& rnf = it->second; - LOG << "LLVM register " << std::dec << id - << " \"" << disas->getRegisterInfo().getName(id) << "\": " + LOG << "Disassembler register " << std::dec << id + << " \"" << m_disassembler->getRegisterName(id) << "\": " << "seen " << rnf.count << " times in the trace" << std::endl; std::ostream& o = LOG << " corresponding instruction addresses in ELF binary: " << std::hex; for (auto addr_it = rnf.address.cbegin(); addr_it != rnf.address.cend(); ++addr_it) { @@ -463,4 +187,5 @@ bool RegisterImporter::trace_end_reached() return true; } -#endif + + diff --git a/tools/import-trace/RegisterImporter.hpp b/tools/import-trace/RegisterImporter.hpp index b0e927c3..df456ba9 100644 --- a/tools/import-trace/RegisterImporter.hpp +++ b/tools/import-trace/RegisterImporter.hpp @@ -4,90 +4,75 @@ #include #include "util/CommandLine.hpp" #include "Importer.hpp" +#include "sal/faultspace/RegisterArea.hpp" -#if defined(BUILD_CAPSTONE_DISASSEMBLER) -#include "util/capstonedisassembler/CapstoneDisassembler.hpp" -#elif defined(BUILD_LLVM_DISASSEMBLER) -#include "util/llvmdisassembler/LLVMDisassembler.hpp" -#endif -class RegisterImporter : public Importer { -#if defined(BUILD_CAPSTONE_DISASSEMBLER) - bool isDisassembled = false; - std::unique_ptr disas; - fail::CapstoneToFailTranslator *m_ctof = 0; - - bool addRegisterTrace(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev, - const fail::CapstoneToFailTranslator::reginfo_t &info, - char access_type); +class RegisterImporter : public Importer { + // Importer Options fail::CommandLine::option_handle NO_GP, FLAGS, IP, NO_SPLIT; - bool do_gp, do_flags, do_ip, do_split_registers; std::set m_register_ids; - unsigned m_ip_register_id; - // Data structures for recording failed LLVM -> FAIL* register mappings, - // including occurrence counts in the trace (to give an estimate on the - // impact) and instruction addresses (for debugging purposes). - struct RegNotFound { - uint64_t count = 0; - std::set address; - }; - std::map m_regnotfound; -#elif defined(BUILD_LLVM_DISASSEMBLER) - llvm::object::Binary *binary = 0; - std::unique_ptr disas; - fail::LLVMtoFailTranslator *m_ltof = 0; + fail::RegisterArea *m_area; - bool addRegisterTrace(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev, - const fail::LLVMtoFailTranslator::reginfo_t &info, - char access_type); - fail::CommandLine::option_handle NO_GP, FLAGS, IP, NO_SPLIT; - bool do_gp, do_flags, do_ip, do_split_registers; + std::unique_ptr m_disassembler; + Disassembler::InstrMap* m_instr_map; + RegisterTranslator* m_translator; - std::set m_register_ids; - unsigned m_ip_register_id; + bool m_import_ip; + fail::RegisterView m_ip_register; - // Data structures for recording failed LLVM -> FAIL* register mappings, - // including occurrence counts in the trace (to give an estimate on the - // impact) and instruction addresses (for debugging purposes). + // Data structures for recording failed {LLVM,Capstone} -> FAIL* + // register mappings, including occurrence counts in the trace (to + // give an estimate on the impact) and instruction addresses (for + // debugging purposes). struct RegNotFound { uint64_t count = 0; std::set address; }; - std::map m_regnotfound; -#endif + std::map m_regnotfound; + + bool addRegisterTrace(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev, + fail::RegisterView info, + char access_type); public: - RegisterImporter() : Importer(), do_gp(true), do_flags(false), do_ip(false), - do_split_registers(true), m_ip_register_id(0) {} + RegisterImporter() : m_instr_map(0), m_translator(0), m_import_ip(false) { + m_area = dynamic_cast(&m_fsp.get_area("register")); + assert(m_area != nullptr && "RegisterImporter failed to get a RegisterArea from the fault space description"); + } /** * Callback function that can be used to add command line options * to the cmd interface */ - virtual bool cb_commandline_init(); + virtual bool cb_commandline_init() override; + + /** + * Callback function that can be used to initialize the importer + * before the trace is consumed. + */ + virtual bool cb_initialize() override; protected: virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev); + Trace_Event &ev) override; virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, - Trace_Event &ev) { + Trace_Event &ev) override { /* ignore on purpose */ return true; } - virtual bool trace_end_reached(); + virtual bool trace_end_reached() override; - virtual void open_unused_ec_intervals() { + virtual void open_unused_ec_intervals() override { /* empty, Memory Map has a different meaning in this importer */ } - void getAliases(std::deque *aliases) { + void getAliases(std::deque *aliases) override{ aliases->push_back("RegisterImporter"); aliases->push_back("regs"); } diff --git a/tools/import-trace/main.cc b/tools/import-trace/main.cc index 5588e9af..f5870bd9 100644 --- a/tools/import-trace/main.cc +++ b/tools/import-trace/main.cc @@ -245,19 +245,21 @@ int main(int argc, char *argv[]) { if (cmd[MEMORY_TYPE]) { std::string arg(cmd[MEMORY_TYPE].first()->arg); memory_type_t type; - if (arg == "ram") - type = MEMTYPE_RAM; - else if (arg == "flash") - type = MEMTYPE_FLASH; - else if (arg == "tags") - type = MEMTYPE_TAGS; - else if (arg == "eeprom") - type = MEMTYPE_EEPROM; - else if (arg == "any") + if (arg == "any") type = ANY_MEMORY; else { - LOG << "ERROR: unknown memory type: " << arg << std::endl; - exit(-1); + bool found = false; + for (int i = 0; i < MEMTYPE_LAST; i++) { + if (arg == memtype_descriptions[i]) { + found = true; + type = (memory_type_t) i; + break; + } + } + if (! found) { + LOG << "ERROR: unknown memory type: " << arg << std::endl; + exit(-1); + } } importer->set_memory_type(type); } diff --git a/tools/prune-trace/BasicPruner.cc b/tools/prune-trace/BasicPruner.cc index 5fbc1d9d..d1294571 100644 --- a/tools/prune-trace/BasicPruner.cc +++ b/tools/prune-trace/BasicPruner.cc @@ -15,9 +15,9 @@ bool BasicPruner::prune_all() { std::string injection_instr = this->use_instr1 ? "instr1" : "instr2"; std::string injection_instr_absolute = this->use_instr1 ? "instr1_absolute" : "instr2_absolute"; - ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, data_width, fspmethod_id) " + ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, data_mask, fspmethod_id) " "SELECT 0, variant_id, instr2, " << injection_instr << ", " << injection_instr_absolute << ", " - " data_address, width, " << m_method_id << " " + " data_address, data_mask, " << m_method_id << " " "FROM trace " "WHERE variant_id IN (" << m_variants_sql << ") AND accesstype = 'R'"; if (!db->query(ss.str().c_str())) return false; @@ -29,9 +29,9 @@ bool BasicPruner::prune_all() { for (std::vector::const_iterator it = m_variants.begin(); it != m_variants.end(); ++it) { // single entry for known outcome (write access) - ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, data_width, fspmethod_id) " + ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, data_mask, fspmethod_id) " "SELECT 1, variant_id, instr2, " << injection_instr << ", " << injection_instr_absolute << ", " - " data_address, width, " << m_method_id << " " + " data_address, data_mask, " << m_method_id << " " "FROM trace " "WHERE variant_id = " << it->id << " AND accesstype = 'W' " "ORDER BY instr2 ASC " @@ -43,11 +43,11 @@ bool BasicPruner::prune_all() { LOG << "created " << rows << " fsppilot entries" << std::endl; - ss << "INSERT INTO fspgroup (variant_id, instr2, data_address, fspmethod_id, pilot_id) " - << "SELECT STRAIGHT_JOIN p.variant_id, p.instr2, p.data_address, p.fspmethod_id, p.id " + ss << "INSERT INTO fspgroup (variant_id, instr2, data_address, data_mask, fspmethod_id, pilot_id) " + << "SELECT STRAIGHT_JOIN p.variant_id, p.instr2, p.data_address, p.data_mask, p.fspmethod_id, p.id " << "FROM fsppilot p " << "JOIN trace t ON t.variant_id = p.variant_id AND t.instr2 = p.instr2" - << " AND t.data_address = p.data_address " + << " AND t.data_address = p.data_address AND t.data_mask = p.data_mask " << "WHERE known_outcome = 0 AND p.fspmethod_id = " << m_method_id << " " << "AND p.variant_id IN (" << m_variants_sql << ")"; @@ -55,8 +55,8 @@ bool BasicPruner::prune_all() { ss.str(""); rows = db->affected_rows(); - ss << "INSERT INTO fspgroup (variant_id, instr2, data_address, fspmethod_id, pilot_id) " - "SELECT STRAIGHT_JOIN t.variant_id, t.instr2, t.data_address, p.fspmethod_id, p.id " + ss << "INSERT INTO fspgroup (variant_id, instr2, data_address, data_mask, fspmethod_id, pilot_id) " + "SELECT STRAIGHT_JOIN t.variant_id, t.instr2, t.data_address, t.data_mask, p.fspmethod_id, p.id " "FROM fsppilot p " "JOIN trace t " "ON t.variant_id = p.variant_id AND p.fspmethod_id = " << m_method_id << " AND p.known_outcome = 1 " diff --git a/tools/prune-trace/FESamplingPruner.cc b/tools/prune-trace/FESamplingPruner.cc index 91a374d3..c66832ad 100644 --- a/tools/prune-trace/FESamplingPruner.cc +++ b/tools/prune-trace/FESamplingPruner.cc @@ -160,7 +160,7 @@ bool FESamplingPruner::sampling_prune(const fail::Database::Variant& variant) if (!m_use_known_results) { // FIXME: change strategy when trace entries have IDs, insert into fspgroup first ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, " - << "injection_instr_absolute, data_address, data_width, fspmethod_id) VALUES "; + << "injection_instr_absolute, data_address, data_mask, fspmethod_id) VALUES "; std::string insert_sql(ss.str()); ss.str(""); @@ -169,7 +169,7 @@ bool FESamplingPruner::sampling_prune(const fail::Database::Variant& variant) Pilot p = pop.remove(pos); ss << "(0," << variant.id << "," << p.instr2 << "," << p.instr2 << "," << p.instr2_absolute << "," << p.data_address - << ",1," << m_method_id << ")"; + << ",255," << m_method_id << ")"; db->insert_multiple(insert_sql.c_str(), ss.str().c_str()); ss.str(""); } @@ -178,9 +178,9 @@ bool FESamplingPruner::sampling_prune(const fail::Database::Variant& variant) uint64_t num_fsppilot_entries = samplerows; // single entry for known outcome (write access) - ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, data_width, fspmethod_id) " + ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, data_mask, fspmethod_id) " "SELECT 1, variant_id, instr2, instr2, instr2_absolute, " - " data_address, width, " << m_method_id << " " + " data_address, data_mask, " << m_method_id << " " "FROM trace " "WHERE variant_id = " << variant.id << " AND accesstype = 'W' " "ORDER BY instr2 ASC " diff --git a/tools/prune-trace/Pruner.cc b/tools/prune-trace/Pruner.cc index a3491b75..6f6da862 100644 --- a/tools/prune-trace/Pruner.cc +++ b/tools/prune-trace/Pruner.cc @@ -85,11 +85,11 @@ bool Pruner::create_database() { " instr2 int(10) unsigned NOT NULL," " injection_instr int(10) unsigned NOT NULL," " injection_instr_absolute int(10) unsigned," - " data_address int(10) unsigned NOT NULL," - " data_width int(10) unsigned NOT NULL," + " data_address bigint(10) unsigned NOT NULL," + " data_mask tinyint(3) unsigned NOT NULL," " fspmethod_id int(11) NOT NULL," " PRIMARY KEY (id)," - " KEY fspmethod_id (fspmethod_id,variant_id,data_address,instr2)" + " KEY fspmethod_id (fspmethod_id,variant_id,data_address,instr2,data_mask)" ") engine=MyISAM "; bool success = (bool) db->query(create_statement.c_str()); if (!success) return false; @@ -97,11 +97,12 @@ bool Pruner::create_database() { create_statement = "CREATE TABLE IF NOT EXISTS fspgroup (" " variant_id int(11) NOT NULL," " instr2 int(11) unsigned NOT NULL," - " data_address int(10) unsigned NOT NULL," + " data_address bigint(10) unsigned NOT NULL," + " data_mask tinyint(3) unsigned NOT NULL," " fspmethod_id int(11) NOT NULL," " pilot_id int(11) NOT NULL," " weight int(11) UNSIGNED," - " PRIMARY KEY (variant_id, data_address, instr2, fspmethod_id)," + " PRIMARY KEY (variant_id, data_address, instr2, data_mask, fspmethod_id)," " KEY joinresults (pilot_id,fspmethod_id)) engine=MyISAM"; return db->query(create_statement.c_str()); diff --git a/tools/prune-trace/SamplingPruner.cc b/tools/prune-trace/SamplingPruner.cc index 41219dba..e54198a6 100644 --- a/tools/prune-trace/SamplingPruner.cc +++ b/tools/prune-trace/SamplingPruner.cc @@ -16,7 +16,7 @@ struct WeightedPilot { uint32_t id; uint32_t instr2; uint32_t instr2_absolute; - uint32_t data_address; + uint64_t data_address; uint32_t weight; typedef uint64_t size_type; @@ -112,7 +112,7 @@ bool SamplingPruner::sampling_prune(const fail::Database::Variant& variant) " IFNULL(g.pilot_id, 0), IFNULL(g.weight, 0)" " FROM trace t" " LEFT JOIN fspgroup g" - " ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND t.instr2 = g.instr2" + " ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND (t.data_mask & g.data_mask) AND t.instr2 = g.instr2" " AND g.fspmethod_id = " << m_method_id << " WHERE t.variant_id = " << variant.id << " AND t.accesstype = 'R'"; @@ -151,7 +151,7 @@ bool SamplingPruner::sampling_prune(const fail::Database::Variant& variant) " JOIN trace t" " ON t.variant_id = p.variant_id AND t.data_address = p.data_address AND t.instr2 = p.instr2" " LEFT JOIN fspgroup g" - " ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND t.instr2 = g.instr2" + " ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND (t.data_mask & g.data_mask) AND t.instr2 = g.instr2" " AND g.fspmethod_id = " << m_method_id << " WHERE p.fspmethod_id = " << db->get_fspmethod_id("basic") << " AND p.variant_id = " << variant.id << @@ -182,7 +182,7 @@ bool SamplingPruner::sampling_prune(const fail::Database::Variant& variant) << m_samplesize << " fault-space coordinates ..." << endl; ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, " - << "injection_instr_absolute, data_address, data_width, fspmethod_id) VALUES "; + << "injection_instr_absolute, data_address, data_mask, fspmethod_id) VALUES "; std::string insert_sql(ss.str()); ss.str(""); @@ -196,9 +196,10 @@ bool SamplingPruner::sampling_prune(const fail::Database::Variant& variant) if (!m_use_known_results && p.weight == 1) { // no need to special-case existing pilots (incremental mode), as // their initial weight is supposed to be at least 1 + // FIXME: this is untested for data_mask ss << "(0," << variant.id << "," << p.instr2 << "," << p.instr2 << "," << p.instr2_absolute << "," << p.data_address - << ",1," << m_method_id << ")"; + << ",255," << m_method_id << ")"; db->insert_multiple(insert_sql.c_str(), ss.str().c_str()); ss.str(""); ++num_fsppilot_entries; diff --git a/tools/tests/export-injection b/tools/tests/export-injection index 3dddf946..802d2885 100755 --- a/tools/tests/export-injection +++ b/tools/tests/export-injection @@ -16,6 +16,7 @@ join fspgroup g on t.variant_id = g.variant_id and t.instr2 = g.instr2 and t.data_address = g.data_address + and (t.data_mask & g.data_mask) join fsppilot p on t.variant_id = p.variant_id and g.fspmethod_id = p.fspmethod_id diff --git a/tools/tests/run b/tools/tests/run index 9703e4e3..2b8b802c 100755 --- a/tools/tests/run +++ b/tools/tests/run @@ -118,16 +118,21 @@ def import_trace(): # Infer the number of fault locations - result = mysql(f"""SELECT DISTINCT data_address, width + result = mysql(f"""SELECT DISTINCT data_address, data_mask FROM trace t JOIN variant v ON v.id = t.variant_id WHERE v.variant = "{args.benchmark}" and v.benchmark = "{location}" """) bits = set() for r in result: - for offset in range(0, int(r['width'])): - for bit in range(0, 8): - bits.add((int(r['data_address']) + offset, bit)) + mask = int(r['data_mask']) + i = 0 + while (1 << i) <= mask: + if not ((1 << i) & mask): continue + offset = i // 8 + bit = i % 8 + bits.add((int(r['data_address']) + offset, bit)) + i += 1 logging.info(f"{len(bits)} fault locations ({location})") @@ -136,7 +141,7 @@ def import_trace(): assert len(bits) == int(trace_pb_stats["#memLocations"]) * 8,\ "Number of fault locations (memory) is not equal to dump-trace" - result = mysql(f"""SELECT sum((t.time2 - t.time1 + 1) * width * 8) as fault_space_size + result = mysql(f"""SELECT sum((t.time2 - t.time1 + 1) * bit_count(data_mask)) as fault_space_size FROM trace t JOIN variant v ON v.id = t.variant_id WHERE v.variant = "{args.benchmark}" and v.benchmark = "{location}" @@ -163,7 +168,7 @@ def basic_pruner(): check_output(cmd) # Is every trace event covered by a fsppilot? - sql = f"""SELECT p.id is null as no_pilot, count(*) as intervals, sum((t.time2 - t.time1 + 1) * t.width * 8) as area, + sql = f"""SELECT p.id is null as no_pilot, count(*) as intervals, sum((t.time2 - t.time1 + 1) * bit_count(t.data_mask)) as area, count(distinct p.id) as pilots FROM trace t LEFT OUTER JOIN fspgroup g @@ -192,6 +197,7 @@ def basic_pruner(): on t.variant_id = g.variant_id and t.instr2 = g.instr2 and t.data_address = g.data_address + and (t.data_address & g.data_address) JOIN fsppilot p on t.variant_id = p.variant_id and g.fspmethod_id = p.fspmethod_id