From 148b09be2ec033bb4304adf64e6a89dc31a809ea Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Thu, 26 Sep 2013 16:37:43 +0200 Subject: [PATCH] tools/import-trace: added ElfImporter The ElfImporter is not a real trace importer, but we locate it into the import-trace utility, since here the infrastructure is already in place to import things related to an elf binary into the database. The ElfImporter calls objdump and dissassembles an elf binary and imports the results into the database. Change-Id: I6e35673c8dbee3b7e8dfc7549d10e5dca9b55935 --- tools/import-trace/CMakeLists.txt | 7 +- tools/import-trace/ElfImporter.cc | 209 +++++++++++++++++++++++++++++ tools/import-trace/ElfImporter.hpp | 53 ++++++++ tools/import-trace/main.cc | 3 + 4 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 tools/import-trace/ElfImporter.cc create mode 100644 tools/import-trace/ElfImporter.hpp diff --git a/tools/import-trace/CMakeLists.txt b/tools/import-trace/CMakeLists.txt index 56afe370..d0e5f659 100644 --- a/tools/import-trace/CMakeLists.txt +++ b/tools/import-trace/CMakeLists.txt @@ -9,6 +9,7 @@ if (BUILD_LLVM_DISASSEMBLER) RegisterImporter.cc RandomJumpImporter.cc AdvancedMemoryImporter.cc + ElfImporter.cc ) include(FindLLVM) @@ -16,6 +17,10 @@ if (BUILD_LLVM_DISASSEMBLER) # llvm-config does add -fno-exception to the command line. But this # breaks some boost libraries. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS} -fexceptions") + + find_package(Boost 1.42 COMPONENTS regex REQUIRED) + include_directories(${Boost_INCLUDE_DIRS}) + link_directories(${Boost_LIBRARY_DIRS}) endif(BUILD_LLVM_DISASSEMBLER) find_package(MySQL REQUIRED) @@ -31,7 +36,7 @@ target_link_libraries(import-trace fail-sal) if (BUILD_LLVM_DISASSEMBLER) - target_link_libraries(import-trace fail-llvmdisassembler fail-sal ${LLVM_LIBS} ${LLVM_LDFLAGS}) + target_link_libraries(import-trace fail-llvmdisassembler fail-sal ${LLVM_LIBS} ${LLVM_LDFLAGS} ${Boost_LIBRARIES}) endif (BUILD_LLVM_DISASSEMBLER) install(TARGETS import-trace RUNTIME DESTINATION bin) diff --git a/tools/import-trace/ElfImporter.cc b/tools/import-trace/ElfImporter.cc new file mode 100644 index 00000000..aaa28286 --- /dev/null +++ b/tools/import-trace/ElfImporter.cc @@ -0,0 +1,209 @@ +#include +#include +#include "util/Logger.hpp" +#include "ElfImporter.hpp" +#include "util/pstream.h" +#ifndef __puma +#include +#include +#endif + + +using namespace llvm; +using namespace llvm::object; +using namespace fail; +using namespace std; + +static Logger LOG("ElfImporter"); + +/** + * Callback function that can be used to add command line options + * to the campaign + */ +bool ElfImporter::cb_commandline_init() { + CommandLine &cmd = CommandLine::Inst(); + + OBJDUMP = cmd.addOption("", "objdump", Arg::Required, + "--objdump \tObjdump: location of objdump binary, otherwise LLVM Disassembler is used"); + return true; +} + +bool ElfImporter::create_database() { + std::stringstream create_statement; + create_statement << "CREATE TABLE IF NOT EXISTS objdump (" + " variant_id int(11) NOT NULL," + " instr_address int(11) NOT NULL," + " opcode varchar(32) NOT NULL," + " disassemble VARCHAR(64)," + " comment VARCHAR(128)," + " PRIMARY KEY (variant_id,instr_address)" + ") engine=MyISAM "; + return db->query(create_statement.str().c_str()); +} + +bool ElfImporter::copy_to_database(ProtoIStream &ps) { + if (!m_elf) { + LOG << "please give an elf binary as parameter (-e/--elf)" << std::endl; + return false; + } + + CommandLine &cmd = CommandLine::Inst(); + if (!cmd.parse()) { + std::cerr << "Error parsing arguments." << std::endl; + return false; + } + + if (cmd[OBJDUMP]) { // using objdump + return import_with_objdump(std::string(cmd[OBJDUMP].first()->arg)); + } else { + LOG << "importing an objdump with internal llvm dissassembler is not yet implemented" << std::endl; + return false; + } + + + return true; +} + +bool ElfImporter::import_with_objdump(const std::string &binary) { +#ifndef __puma + + LOG << "importing with " << binary << std::endl; + + // -d: disassemble + // -C: demangle C++ + std::string command = binary + " -C -d " + m_elf->getFilename(); + LOG << "Executing: " << command << std::endl; + redi::ipstream objdump( command ); + std::string str; + while(std::getline(objdump, str)){ + if (!evaluate_objdump_line(str)) { + objdump.close(); + return false; + } + } + objdump.close(); + if(objdump.rdbuf()->exited()){ + int ex = objdump.rdbuf()->status(); + if(ex != 0){ + clear_database(); + LOG << "Could not disassemble!" << std::endl; + return false; + } + } + +#endif + return true; +} + +bool ElfImporter::evaluate_objdump_line(const std::string& line){ +#ifndef __puma + // Only read in real code lines: + // Code lines start with a leading whitespace! (hopefully in each objdump implementation!) + if(line.size() > 0 && isspace(line[0])){ + // a line looks like: 800156c:\tdd14 \tble.n 8001598 <_ZN2hw3hal7T32Term8PutBlockEPci+0x30> + static boost::regex expr("\\s+([A-Fa-f0-9]+):((?:\\s+[A-Fa-f0-9]{2})+)\\s+(.+?)(;.*)?$"); + boost::smatch res; + if(boost::regex_search(line, res, expr)){ + std::string address = res[1]; + std::stringstream ss; + ss << std::hex << address; + address_t addr = 0; + ss >> addr; + std::string opcode = res[2]; + boost::trim(opcode); + std::string instruction = res[3]; + boost::trim(instruction); + std::string comment = res[4]; + boost::trim(comment); + + // transform hex opcode to char array + char opcode_read[33]; + std::stringstream opcode_stream(opcode); + unsigned long opcode_length = 0; + char c; + while (opcode_length < 16 && opcode_stream) { + opcode_stream << std::hex; + opcode_stream >> c; + if (!opcode_stream) break; + opcode_read[opcode_length++] = c; + } + opcode_read[opcode_length] = 0; + + /* import instruction into database */ + if (!import_instruction(addr, opcode_read, opcode_length, + instruction, comment)) + return false; + } + } +#endif + return true; +} + + + +bool ElfImporter::import_instruction(fail::address_t addr, char *opcode, int opcode_length, + const std::string &instruction, const std::string &comment) { + /* Prepare a mysql statement if it was not done before */ + static MYSQL_STMT *stmt = 0; + if (!stmt) { + std::string sql("INSERT INTO objdump (variant_id, instr_address, opcode, disassemble, comment) VALUES (?,?,?,?,?)"); + stmt = mysql_stmt_init(db->getHandle()); + if (mysql_stmt_prepare(stmt, sql.c_str(), sql.length())) { + LOG << "query '" << sql << "' failed: " << mysql_error(db->getHandle()) << std::endl; + return false; + } + } + MYSQL_BIND bind[5]; + memset(bind, 0, sizeof(bind)); + for (unsigned i = 0; i < 5; ++i) { + bind[i].buffer_type = MYSQL_TYPE_STRING; + bind[i].is_unsigned = 1; + switch (i) { + case 0: + bind[i].buffer = &m_variant_id; + bind[i].buffer_type = MYSQL_TYPE_LONG; + break; + case 1: + bind[i].buffer = &addr; + bind[i].buffer_type = MYSQL_TYPE_LONG; + break; + case 2: + bind[i].buffer_type = MYSQL_TYPE_BLOB; + bind[i].buffer = opcode; + bind[i].buffer_length = opcode_length; + break; + case 3: + bind[i].buffer = const_cast(instruction.c_str()); + bind[i].buffer_length = instruction.length(); + break; + case 4: + bind[i].buffer = const_cast(comment.c_str()); + bind[i].buffer_length = comment.length(); + break; + } + } + + + if (mysql_stmt_bind_param(stmt, bind)) { + LOG << "mysql_stmt_bind_param() failed: " << mysql_stmt_error(stmt) << std::endl; + return false; + } + if (mysql_stmt_execute(stmt)) { + LOG << "mysql_stmt_execute() failed: " << mysql_stmt_error(stmt) << std::endl; + return false; + } + + return true; +} + + + +bool ElfImporter::clear_database() { + std::stringstream ss; + ss << "DELETE FROM objdump WHERE variant_id = " << m_variant_id; + + bool ret = db->query(ss.str().c_str()) == 0 ? false : true; + LOG << "deleted " << db->affected_rows() << " rows from objdump table" << std::endl; + return ret; +} + diff --git a/tools/import-trace/ElfImporter.hpp b/tools/import-trace/ElfImporter.hpp new file mode 100644 index 00000000..20f4a656 --- /dev/null +++ b/tools/import-trace/ElfImporter.hpp @@ -0,0 +1,53 @@ +#ifndef __OBJDUMP_IMPORTER_H__ +#define __OBJDUMP_IMPORTER_H__ + +#include "Importer.hpp" +#include "util/llvmdisassembler/LLVMDisassembler.hpp" +#include "util/CommandLine.hpp" + +/** + The ElfImporter is not a real trace importer, but we locate it + into the import-trace utility, since here the infrastructure is + already in place to import things related to an elf binary into + the database. + + The ElfImporter calls objdump and dissassembles an elf binary + and imports the results into the database +*/ +class ElfImporter : public Importer { + llvm::OwningPtr binary; + llvm::OwningPtr disas; + + fail::CommandLine::option_handle OBJDUMP; + + bool import_with_objdump(const std::string &objdump_binary); + bool evaluate_objdump_line(const std::string &line); + + /* Imports a single instruction into the objdump table */ + bool import_instruction(fail::address_t addr, char opcode[16], int opcode_length, + const std::string &instruction, const std::string &comment); + +protected: + virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { return true; } + virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { return true; } + + virtual void open_unused_ec_intervals() { + /* empty, Memory Map has a no meaning in this importer */ + } + +public: + ElfImporter() : Importer() {}; + + /** + * Callback function that can be used to add command line options + * to the cmd interface + */ + virtual bool cb_commandline_init(); + virtual bool create_database(); + virtual bool copy_to_database(fail::ProtoIStream &ps); + virtual bool clear_database(); +}; + +#endif diff --git a/tools/import-trace/main.cc b/tools/import-trace/main.cc index 7290636e..2f727d39 100644 --- a/tools/import-trace/main.cc +++ b/tools/import-trace/main.cc @@ -13,6 +13,7 @@ #include "RegisterImporter.hpp" #include "RandomJumpImporter.hpp" #include "AdvancedMemoryImporter.hpp" +#include "ElfImporter.hpp" #endif @@ -140,6 +141,8 @@ int main(int argc, char *argv[]) { importer = new RandomJumpImporter(); } else if (imp == "AdvancedMemoryImporter") { importer = new AdvancedMemoryImporter(); + } else if (imp == "ObjdumpImporter" || imp == "objdump" || imp == "ElfImporter") { + importer = new ElfImporter(); #endif } else { LOG << "Unkown import method: " << imp << endl;