From e18be8e8cb807ba551e1a13137e5cc7f622b379c Mon Sep 17 00:00:00 2001 From: Malte Bargholz Date: Mon, 4 Jan 2021 13:07:22 +0100 Subject: [PATCH 1/3] Use bit masks instead of access widths With this *breaking* change, FAIL* does no longer use widths of data accesses in its database schema, but bit masks. Thereby it is possible to prevent or focus on the injection of individual bits. For this change, the following database tables use masks with the following meanings: - trace.data_mask: given bits were accessed - fspgroup.data_mask: complete or partial match of a trace-table entries data-mask - fsppilot.data_mask: given bits are to be injected with this pilot In order to reflect these database changes, you should add (fspgroup.data_mask & trace.data_mask) to your join between trace and fspgroup table to record for a cardinalty mismatch between both tables. However, for the basic pruner (def-use pruner), this is not necessary, as the data mask is in both cases 255. Co-authored: Christian Dietrich --- .../comm/DatabaseCampaignMessage.proto.in | 4 +- src/core/cpn/DatabaseCampaign.cc | 29 ++++++---- src/core/cpn/DatabaseCampaign.hpp | 9 --- src/core/efw/DatabaseExperiment.cc | 44 +++++++++++--- tools/analysis/resultbrowser/app/model.py | 4 +- tools/import-trace/Importer.cc | 57 +++++++++++-------- tools/prune-trace/BasicPruner.cc | 18 +++--- tools/prune-trace/FESamplingPruner.cc | 8 +-- tools/prune-trace/Pruner.cc | 11 ++-- tools/prune-trace/SamplingPruner.cc | 9 +-- tools/tests/export-injection | 1 + tools/tests/run | 18 ++++-- 12 files changed, 127 insertions(+), 85 deletions(-) diff --git a/src/core/comm/DatabaseCampaignMessage.proto.in b/src/core/comm/DatabaseCampaignMessage.proto.in index 474f56b9..7e2bd597 100644 --- a/src/core/comm/DatabaseCampaignMessage.proto.in +++ b/src/core/comm/DatabaseCampaignMessage.proto.in @@ -15,8 +15,8 @@ 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]; diff --git a/src/core/cpn/DatabaseCampaign.cc b/src/core/cpn/DatabaseCampaign.cc index 679f2a69..df37aa61 100644 --- a/src/core/cpn/DatabaseCampaign.cc +++ b/src/core/cpn/DatabaseCampaign.cc @@ -202,17 +202,17 @@ 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); } @@ -220,6 +220,7 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { 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 +236,24 @@ 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 = m_inject_bursts ? 1 : __builtin_popcount(data_mask); 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,7 +270,7 @@ 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_data_mask(data_mask); pilot.set_inject_bursts(m_inject_bursts); pilot.set_register_injection_mode(m_register_injection_mode); @@ -277,6 +280,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 +316,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 +330,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..161f982b 100644 --- a/src/core/cpn/DatabaseCampaign.hpp +++ b/src/core/cpn/DatabaseCampaign.hpp @@ -59,15 +59,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..8b9111c6 100644 --- a/src/core/efw/DatabaseExperiment.cc +++ b/src/core/efw/DatabaseExperiment.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -183,9 +184,13 @@ bool DatabaseExperiment::run() 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 +209,38 @@ 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; + guest_address_t data_address = fsppilot->data_address(); + + unsigned unchecked_mask = fsppilot->data_mask(); + assert(unchecked_mask <=255 && "mask covers more than 8 bit, this currently not unsupported!"); + uint8_t mask = static_cast(unchecked_mask); + unsigned injection_width = 1; + // FIXME: Injection for Randomjump + if (fsppilot->inject_bursts() || fsppilot->register_injection_mode() == fsppilot->RANDOMJUMP) { + injection_width = 8; + } + + m_log << std::hex << std::showbase + << " fsp: " << data_address + << " mask: " << std::bitset<8>(mask) + << std::dec + << " injection_width: " << injection_width << std::endl; + + for (unsigned bit_offset = 0; bit_offset < 8; bit_offset += injection_width) { + // 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 || fsppilot->inject_bursts())) { + continue; + } - for (unsigned bit_offset = 0; bit_offset < width * 8; bit_offset += injection_width) { // 8 results in one job Message *outer_result = cb_new_result(param); m_current_result = outer_result; @@ -280,7 +306,7 @@ bool DatabaseExperiment::run() // inject fault (single-bit flip or burst) result->set_original_value( - injectFault(data_address + bit_offset / 8, bit_offset % 8, + injectFault(data_address, bit_offset, fsppilot->inject_bursts(), fsppilot->register_injection_mode() != fsppilot->OFF, fsppilot->register_injection_mode() == fsppilot->FORCE, 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/Importer.cc b/tools/import-trace/Importer.cc index 305e43fa..71849dc7 100644 --- a/tools/import-trace/Importer.cc +++ b/tools/import-trace/Importer.cc @@ -31,8 +31,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 +44,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()); } @@ -61,14 +61,24 @@ bool Importer::clear_database() { 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; } } @@ -170,12 +180,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 +199,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 +218,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 +248,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 +274,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 +296,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)", @@ -315,7 +325,7 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, } bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, - Trace_Event &event, bool is_fake) { + Trace_Event &event, bool known_outcome) { if (!m_import_write_ecs && event.accesstype() == event.WRITE) { return true; } @@ -337,7 +347,7 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, 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"; @@ -377,18 +387,19 @@ 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 data_mask = 255; 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,7 +407,7 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, value_sql << begin.time << "," << end.time << "," << data_address << "," - << "1," // width + << data_mask << "," << "'" << accesstype << "',"; if (extended) { @@ -425,7 +436,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(event, value_sql, num_additional_columns, known_outcome)) { return false; } 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..c9bd7146 100644 --- a/tools/prune-trace/SamplingPruner.cc +++ b/tools/prune-trace/SamplingPruner.cc @@ -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 From e2bf7e37a62808cbfa440e41802e3ef2022224f1 Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Tue, 5 Jan 2021 13:49:34 +0100 Subject: [PATCH 2/3] Revert "cmake: use -std=c++11 instead of c++14" This reverts commit 8d1657a409adc5ba79b1e24ac86ba05cd3b77695. --- cmake/bochs.cmake | 2 +- cmake/compilerconfig.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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) From 7fffccf979c4d9fe073c584ef5c329f1e2abc636 Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Tue, 5 Jan 2021 14:52:40 +0100 Subject: [PATCH 3/3] Virtual Fault Space Abstraction This is a large change that will break your traces and your database layouts. However, it repairs a lot of ad-hoc hacks that came into FAIL* over the years. What has changed? ================= The Virtual Fault Space Abstraction ----------------------------------- Files: sal/faultspace/* sal/x86/X86Faultspace.* sal/arm/ArmFaultSpace.* Instead of mapping registers ad-hoc into the virtual address space in order to smuggle the bit-packed register-id from import-trace through prune-trace and DatabaseCampaign to the DatabaseExperiment, we make this mapping now official and adaptable to more complex architectures. From now on the data_address field in the database is a fsp_address_t. These addresses are translated by the FS abstraction within the importer, which uses the architecture specific description of the virtual fault space. Within our whole tool chain, we now only work with fault-space addresses, which are the Y-axis of our fault space. When the fault-space address is about to be injected, we decode the numerical address to a FaultSpaceElement*, which knows how to inject a fault into itself. Thereby, we can unify the injection over different types of machine state and support multiple memory types. Furthermore, this simplifies import-trace and the DatabaseExperiment significantly. A detailed description of can be found in Malte Bargholz' Master thesis[1] Bit-Wise Pruning ---------------- Files: tools/import-trace/Importer.cc Until now, every state (register/memory) access had to be at least 8 bits wide. While this is good for memory reads, which always access at least a whole byte, this is not properly working for register accesses. Especially, for our integration of more complex CPU architectures (i.e., CHERI), we have to have bit-wise pruning to inject only single tag bits instead of injecting always 8 tag bits. Therefore, this change introduces bit-wise pruning for our importer. Instead of tracking only a single left margin for every fault-space address, we now track up to 8 different margins that are associated with an access mask. Thereby, the importer (which is effectively a def-use pruner) has to handle up to 8 left margins when accessing a byte with an access mask of 0xff. A detailed description of can be found in Malte Bargholz' Master thesis[1] Unification of Disassembler Interface ------------------------------------- The integration of the capstone disassembler into the RegisterImporter was more or less a clone-and-own integration. However, as both disassemblers basically perform the same task, a common interface is possible. Furthermore, the *FailTo*Translator* classes are no longer used for injecting registers as the dissassembler has nothing to do with the injector. This task is no performed by the virtual fault space. This change was tested with: - The integrated test-suite - The fail-target repo and injection of register,memory,EIP,randomjump - Both disassemblers Currently untested: - Gem5 [1] Quantifying Soft-Error Resilience of Embedded RISC-V Systems with Capability-based Memory Protection https://www.sra.uni-hannover.de/Theses/2020/bargholz_20_ma.pdf Co-authored-by: Christian Dietrich Co-authored-by: Malte Bargholz --- .editorconfig | 2 +- .../comm/DatabaseCampaignMessage.proto.in | 12 +- src/core/cpn/DatabaseCampaign.cc | 46 +- src/core/cpn/DatabaseCampaign.hpp | 3 +- src/core/efw/DatabaseExperiment.cc | 228 ++++----- src/core/efw/DatabaseExperiment.hpp | 14 +- src/core/efw/JobClient.cc | 4 +- src/core/sal/Architecture.hpp | 2 + src/core/sal/CMakeLists.txt | 5 +- src/core/sal/CPU.cc | 8 + src/core/sal/CPU.hpp | 9 + src/core/sal/Listener.hpp | 10 +- src/core/sal/Register.cc | 6 +- src/core/sal/Register.hpp | 26 +- src/core/sal/SALConfig.cc | 8 + src/core/sal/SALConfig.hpp | 9 +- src/core/sal/SimulatorController.cc | 18 + src/core/sal/SimulatorController.hpp | 19 +- src/core/sal/arm/ArmFaultSpace.cc | 19 + src/core/sal/arm/ArmFaultSpace.hpp | 13 + src/core/sal/bochs/BochsController.cc | 40 ++ src/core/sal/bochs/BochsController.hpp | 5 + src/core/sal/faultspace/BaseFaultSpace.cc | 56 +++ src/core/sal/faultspace/BaseFaultSpace.hpp | 201 ++++++++ src/core/sal/faultspace/CMakeLists.txt | 6 + src/core/sal/faultspace/FaultSpace.hpp | 20 + src/core/sal/faultspace/MemoryArea.cc | 62 +++ src/core/sal/faultspace/MemoryArea.hpp | 55 +++ src/core/sal/faultspace/RegisterArea.cc | 110 +++++ src/core/sal/faultspace/RegisterArea.hpp | 63 +++ src/core/sal/x86/X86FaultSpace.cc | 20 + src/core/sal/x86/X86FaultSpace.hpp | 13 + src/core/util/ProtoStream.cc | 2 +- .../CapstoneDisassembler.cpp | 8 +- .../CapstoneDisassembler.hpp | 13 +- .../CapstoneToFailBochs.cpp | 167 +++---- .../CapstoneToFailBochs.hpp | 2 +- .../CapstoneToFailGem5.cpp | 35 +- .../CapstoneToFailGem5.hpp | 2 +- .../CapstoneToFailTranslator.cpp | 36 +- .../CapstoneToFailTranslator.hpp | 66 +-- .../testing/capstoneDisTest.cc | 4 +- src/core/util/llvmdisassembler/CMakeLists.txt | 2 +- .../llvmdisassembler/LLVMDisassembler.cpp | 101 +++- .../llvmdisassembler/LLVMDisassembler.hpp | 72 +-- .../util/llvmdisassembler/LLVMtoFailBochs.cpp | 192 ++++---- .../util/llvmdisassembler/LLVMtoFailBochs.hpp | 2 - .../util/llvmdisassembler/LLVMtoFailGem5.cpp | 34 +- .../llvmdisassembler/LLVMtoFailTranslator.cpp | 70 +-- .../llvmdisassembler/LLVMtoFailTranslator.hpp | 69 +-- .../llvmdisassembler/testing/llvmDisTest.cc | 24 +- tools/import-trace/AdvancedMemoryImporter.cc | 108 ++--- tools/import-trace/AdvancedMemoryImporter.hpp | 14 +- tools/import-trace/CMakeLists.txt | 10 +- tools/import-trace/FullTraceImporter.cc | 39 +- tools/import-trace/FullTraceImporter.hpp | 18 +- tools/import-trace/Importer.cc | 196 ++++++-- tools/import-trace/Importer.hpp | 98 ++-- tools/import-trace/InstructionImporter.cc | 113 +---- tools/import-trace/InstructionImporter.hpp | 17 +- tools/import-trace/MemoryImporter.cc | 61 +-- tools/import-trace/RandomJumpImporter.cc | 142 ++---- tools/import-trace/RandomJumpImporter.hpp | 18 +- tools/import-trace/RegisterImporter.cc | 457 ++++-------------- tools/import-trace/RegisterImporter.hpp | 85 ++-- tools/import-trace/main.cc | 24 +- tools/prune-trace/SamplingPruner.cc | 2 +- 67 files changed, 1791 insertions(+), 1624 deletions(-) create mode 100644 src/core/sal/arm/ArmFaultSpace.cc create mode 100644 src/core/sal/arm/ArmFaultSpace.hpp create mode 100644 src/core/sal/faultspace/BaseFaultSpace.cc create mode 100644 src/core/sal/faultspace/BaseFaultSpace.hpp create mode 100644 src/core/sal/faultspace/CMakeLists.txt create mode 100644 src/core/sal/faultspace/FaultSpace.hpp create mode 100644 src/core/sal/faultspace/MemoryArea.cc create mode 100644 src/core/sal/faultspace/MemoryArea.hpp create mode 100644 src/core/sal/faultspace/RegisterArea.cc create mode 100644 src/core/sal/faultspace/RegisterArea.hpp create mode 100644 src/core/sal/x86/X86FaultSpace.cc create mode 100644 src/core/sal/x86/X86FaultSpace.hpp 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/src/core/comm/DatabaseCampaignMessage.proto.in b/src/core/comm/DatabaseCampaignMessage.proto.in index 7e2bd597..dec542c4 100644 --- a/src/core/comm/DatabaseCampaignMessage.proto.in +++ b/src/core/comm/DatabaseCampaignMessage.proto.in @@ -22,14 +22,12 @@ message DatabaseCampaignMessage { 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 df37aa61..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 { @@ -219,8 +202,6 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { 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); @@ -237,7 +218,7 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { ConcreteInjectionPoint ip; unsigned sent_pilots = 0, skipped_pilots = 0; - unsigned long long expected_injections = 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); @@ -246,13 +227,17 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { unsigned instr1 = strtoul(row[5], NULL, 10); unsigned instr2 = strtoul(row[6], NULL, 10); - unsigned expected_results = m_inject_bursts ? 1 : __builtin_popcount(data_mask); + 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); + expected_injections += expected_results - existing_results_for_pilot(pilot_id); DatabaseCampaignMessage pilot; @@ -271,8 +256,7 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { } pilot.set_data_address(data_address); pilot.set_data_mask(data_mask); - pilot.set_inject_bursts(m_inject_bursts); - pilot.set_register_injection_mode(m_register_injection_mode); + pilot.set_injection_mode(m_injection_mode); this->cb_send_pilot(pilot); @@ -280,7 +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; + 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; diff --git a/src/core/cpn/DatabaseCampaign.hpp b/src/core/cpn/DatabaseCampaign.hpp index 161f982b..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() {}; diff --git a/src/core/efw/DatabaseExperiment.cc b/src/core/efw/DatabaseExperiment.cc index 8b9111c6..ecc9d567 100644 --- a/src/core/efw/DatabaseExperiment.cc +++ b/src/core/efw/DatabaseExperiment.cc @@ -13,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 @@ -30,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(); + // 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); } -#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; - } - 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 @@ -177,7 +117,15 @@ 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; @@ -187,7 +135,7 @@ bool DatabaseExperiment::run() #if defined(BUILD_BOCHS) #define MAX_EXECUTED_JOBS 25 #else - #define MAX_EXECUTED_JOBS UINT_MAX + #define MAX_EXECUTED_JOBS UINT_MAX #endif unsigned executed_jobs = 0; while (executed_jobs < MAX_EXECUTED_JOBS || m_jc->getNumberOfUndoneJobs() > 0) { @@ -214,32 +162,25 @@ bool DatabaseExperiment::run() #endif unsigned injection_instr = fsppilot->injection_instr(); - guest_address_t data_address = fsppilot->data_address(); - - unsigned unchecked_mask = fsppilot->data_mask(); - assert(unchecked_mask <=255 && "mask covers more than 8 bit, this currently not unsupported!"); - uint8_t mask = static_cast(unchecked_mask); - unsigned injection_width = 1; - // FIXME: Injection for Randomjump - if (fsppilot->inject_bursts() || fsppilot->register_injection_mode() == fsppilot->RANDOMJUMP) { - injection_width = 8; + + 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; } - m_log << std::hex << std::showbase - << " fsp: " << data_address - << " mask: " << std::bitset<8>(mask) - << std::dec - << " injection_width: " << injection_width << std::endl; - - for (unsigned bit_offset = 0; bit_offset < 8; bit_offset += injection_width) { - // 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 || fsppilot->inject_bursts())) { - continue; - } + 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); @@ -261,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. @@ -305,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, - 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 @@ -329,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/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 71849dc7..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; } @@ -55,7 +60,7 @@ 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; } @@ -83,7 +88,7 @@ bool Importer::sanitycheck(std::string check_name, std::string fail_msg, std::st } } -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. @@ -314,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_trace_event(margin_info_t &begin, margin_info_t &end, - Trace_Event &event, bool known_outcome) { - if (!m_import_write_ecs && event.accesstype() == event.WRITE) { +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_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; @@ -343,6 +441,7 @@ 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; @@ -375,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++) { @@ -385,9 +484,8 @@ 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 data_mask = 255; + unsigned mask = begin.mask; + assert(begin.mask == end.mask); std::stringstream value_sql; value_sql << "(" @@ -407,8 +505,8 @@ bool Importer::add_trace_event(margin_info_t &begin, margin_info_t &end, value_sql << begin.time << "," << end.time << "," << data_address << "," - << data_mask << "," - << "'" << accesstype << "',"; + << mask << "," + << "'" << access_type << "',"; if (extended) { value_sql << data_value << ","; @@ -436,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, known_outcome)) { + !database_insert_data(origin, value_sql, num_additional_columns, known_outcome)) { return false; } @@ -472,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); + } } } } @@ -492,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/SamplingPruner.cc b/tools/prune-trace/SamplingPruner.cc index c9bd7146..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;