T32: Dissassembler to evaluate memory instructions.

For the T32 variant we have to evaluate the memory
access instruction to find out, which memory address
was accessed.

Dissassmbly by OpenOCDs arm_disassembler.hpp/.cc:
- fine for ARM / Thumb1
- needs fixes for Thumb2 :( (currently doing that..)
This commit is contained in:
Martin Hoffmann
2013-03-06 18:34:20 +01:00
parent 1fe1dbb3ed
commit f586351e79
13 changed files with 5016 additions and 23 deletions

View File

@ -1,6 +1,7 @@
#include "T32Connector.hpp" #include "T32Connector.hpp"
#include <iostream> #include <iostream>
#include <t32.h> #include <t32.h>
#include "sal/MemoryInstruction.hpp"
using namespace fail; using namespace fail;
@ -93,7 +94,6 @@ void T32Connector::brk() {
#include "sal/t32/T32Constants.hpp" #include "sal/t32/T32Constants.hpp"
void T32Connector::test() { void T32Connector::test() {
} }
/* Default T32 error handler */ /* Default T32 error handler */

View File

@ -23,6 +23,8 @@
#include "T32Connector.hpp" #include "T32Connector.hpp"
#include "t32config.hpp" #include "t32config.hpp"
#include "sal/MemoryInstruction.hpp"
#include "util/Disassembler.hpp"
using namespace std; using namespace std;
using namespace fail; using namespace fail;
@ -96,6 +98,8 @@ int main(int argc, char** argv){
// The experiments/traces hopefully set some Breakpoints, we can react on. // The experiments/traces hopefully set some Breakpoints, we can react on.
// We may also provide a timeout, if a TimerListener was set wanted. // We may also provide a timeout, if a TimerListener was set wanted.
MemoryInstruction mem;
address_t ip;
while(1) { while(1) {
// Start execution (with next timeout, if any) // Start execution (with next timeout, if any)
@ -105,8 +109,11 @@ int main(int argc, char** argv){
// Evaluate state. // Evaluate state.
t32.test();// TODO t32.test();// TODO
// Call appropriate callback of the SimulatorController. // Call appropriate callback of the SimulatorController.
fail::simulator.onBreakpoint(&fail::simulator.getCPU(0), fail::simulator.getCPU(0).getInstructionPointer(), fail::ANY_ADDR); ip = fail::simulator.getCPU(0).getInstructionPointer();
// TODO fail::simulator.onMemoryAccess(&fail::simulator.getCPU(0), 0x20002074, 1, true, fail::simulator.getCPU(0).getInstructionPointer()); fail::simulator.onBreakpoint(&fail::simulator.getCPU(0), ip , fail::ANY_ADDR);
if( meminstruction.eval(ip, mem) ) {
fail::simulator.onMemoryAccess(&fail::simulator.getCPU(0), mem.getAddress(), mem.getWidth(), mem.isWriteAccess(), ip );
}
} }
cout << "[T32 Backend] After startup" << endl; cout << "[T32 Backend] After startup" << endl;

View File

@ -64,6 +64,8 @@ elseif(BUILD_T32)
if(BUILD_ARM) if(BUILD_ARM)
set(SRCS ${SRCS} set(SRCS ${SRCS}
t32/T32ArmCPU.cc t32/T32ArmCPU.cc
arm/ArmMemoryInstruction.cc
arm/arm_disassembler.cc
) )
endif(BUILD_ARM) endif(BUILD_ARM)
endif(BUILD_BOCHS) endif(BUILD_BOCHS)

View File

@ -0,0 +1,40 @@
#ifndef __MEMORYINSTRUCTION_HPP__
#define __MEMORYINSTRUCTION_HPP__
#include <string>
#include "SALConfig.hpp"
namespace fail {
class MemoryInstruction {
regdata_t address;
regdata_t value;
uint8_t width;
bool writeAccess;
public:
MemoryInstruction(regdata_t address = ADDR_INV, regdata_t value = 0, uint8_t width = 0, bool writeAccess = false) :
address(address), value(value), width(width), writeAccess(writeAccess) { };
bool isWriteAccess(void) const { return writeAccess; }
regdata_t getAddress() const { return address; }
regdata_t getValue() const { return value; }
uint8_t getWidth() const { return width; }
bool isValid(void) const { return address != ADDR_INV; }
void setAddress(regdata_t addr) { address = addr; }
void setValue(regdata_t val) { value = val; }
void setWidth(uint8_t w) { width = w; }
void setWriteAccess(bool iswrite) { writeAccess = iswrite; }
};
class MemoryInstructionAnalyzer {
public:
virtual bool eval(address_t opcode, MemoryInstruction & result) = 0;
};
extern MemoryInstructionAnalyzer & meminstruction;
} // end of namespace fail
#endif // __MEMORYINSTRUCTION_HPP__

View File

@ -0,0 +1,97 @@
#include "ArmMemoryInstruction.hpp"
#include <iostream>
using namespace std;
namespace fail {
static ArmMemoryInstructionAnalyzer anal;
MemoryInstructionAnalyzer & meminstruction = anal;
address_t ArmMemoryInstructionAnalyzer::findPrevious(address_t address){
if(m_dis.hasInstructionAt(address-2)) {
return address - 2;
} else if (m_dis.hasInstructionAt(address - 4)) {
return address - 4;
} else {
return ADDR_INV;
}
}
void ArmMemoryInstructionAnalyzer::evaluate(arm_instruction & inst, MemoryInstruction& result){
cout << "Memory Access: " << inst.text << " - Size: " << inst.instruction_size << " Type " << inst.type << endl;
inst.info.load_store.evaluate();
result.setValue(inst.info.load_store.value);
result.setAddress(inst.info.load_store.address);
result.setWidth(4); // TODO;
result.setWriteAccess(inst.isStoreInstruction());
}
// The Cortex M3 Lauterbach is a pain in the ass, as a Memory Watchpoint does not stop
// at the accessing instruction, but 1 or 2 instructions later.
bool ArmMemoryInstructionAnalyzer::eval_cm3(address_t address, MemoryInstruction& result){
arm_instruction inst;
uint32_t opcode =0;
address = findPrevious(address); // Cortex M3: memory access is at the previous instruction
opcode = m_dis.disassemble(address).opcode;
// OpenOCDs thumb2_opcode evaluation is not complete yet. :(
thumb2_opcode(address, opcode, &inst);
if(inst.isMemoryAccess()){
evaluate(inst, result);
return true;
} else if(inst.isBranchInstruction()){
// The memory access took place within the function previously branched
int regop = inst.info.b_bl_bx_blx.reg_operand;
uint32_t addr = inst.info.b_bl_bx_blx.target_address;
//cout << " Reg:" << hex << regop << " address " << hex << addr << endl;
// Lets look into this function
if( regop == -1 ){
// address should be set..
const ElfSymbol & sym = m_elf.getSymbol(addr|1); // | 1 to set first bit -> thumbmode
addr += sym.getSize(); // Go to end of function.
// THIS IS DANGEROUS: The memory access can be anywhere within this function, one instruction before a ret.
// OR, the memory access itself can result in leaving the function :e.g., ldr pc, [r3]
// We cannot be sure :( Here we assume the first memory access from the back.
do { // go backwards until there is a memory instruction.
addr = findPrevious(addr); // find previous
thumb2_opcode(addr, m_dis.disassemble(addr).opcode, &inst);
} while( !inst.isMemoryAccess() );
evaluate(inst, result);
return true;
}
} else {
// There was a memory access before, but the previous instruction
// is neither an access nor a branch.
// This can happen if we came here from anywhere, e.g. by ldr pc, [r4]
}
return false;
}
bool ArmMemoryInstructionAnalyzer::eval_ca9(address_t address, MemoryInstruction& result){
arm_instruction inst;
uint32_t opcode = m_dis.disassemble(address).opcode;
arm_evaluate_opcode(address, opcode, &inst);
if( inst.isMemoryAccess() ){
evaluate(inst, result);
return true;
}
return false;
}
#define CORTEXM3
bool ArmMemoryInstructionAnalyzer::eval(address_t address, MemoryInstruction & result){
#ifdef CORTEXM3
#warning "Memory Accesses cannot be evaluated completedly!"
return eval_cm3(address, result);
#elif defined CORTEXA9
return eval_ca9(address, result);
#else
#warning "Memory Accesses are not evaluated!"
return false;
#endif
}
};

View File

@ -0,0 +1,34 @@
#ifndef __ARMMEMORYINSTRUCITON_HPP__
#define __ARMMEMORYINSTRUCITON_HPP__
#include "../MemoryInstruction.hpp"
#include "util/Disassembler.hpp"
#include "util/ElfReader.hpp"
#include "arm_disassembler.hpp"
namespace fail {
class ArmMemoryInstructionAnalyzer : public MemoryInstructionAnalyzer {
fail::ElfReader m_elf;
fail::Disassembler m_dis;
address_t findPrevious(address_t addr);
void evaluate(arm_instruction & inst, MemoryInstruction& result);
bool eval_ca9(address_t address, MemoryInstruction& result);
bool eval_cm3(address_t address, MemoryInstruction& result);
public:
ArmMemoryInstructionAnalyzer() {
m_dis.init();
};
bool eval(address_t opcode, MemoryInstruction & result);
};
} //end of namespace fail
#endif // __ARMMEMORYINSTRUCITON_HPP__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
/***************************************************************************
* Copyright (C) 2006 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ARM_DISASSEMBLER_H
#define ARM_DISASSEMBLER_H
#include <stdint.h>
enum arm_instruction_type {
ARM_UNKNOWN_INSTUCTION,
/* Branch instructions */
ARM_B,
ARM_BL,
ARM_BX,
ARM_BLX,
/* Data processing instructions */
ARM_AND,
ARM_EOR,
ARM_SUB,
ARM_RSB,
ARM_ADD,
ARM_ADC,
ARM_SBC,
ARM_RSC,
ARM_TST,
ARM_TEQ,
ARM_CMP,
ARM_CMN,
ARM_ORR,
ARM_MOV,
ARM_BIC,
ARM_MVN,
/* Load/store instructions */
ARM_LDR,
ARM_LDRB,
ARM_LDRT,
ARM_LDRBT,
ARM_LDRH,
ARM_LDRSB,
ARM_LDRSH,
ARM_LDREX,
ARM_LDREXB,
ARM_LDREXH,
ARM_LDM,
ARM_STR,
ARM_STRB,
ARM_STRT,
ARM_STRBT,
ARM_STRH,
ARM_STREX,
ARM_STREXB,
ARM_STREXH,
ARM_STM,
/* Status register access instructions */
ARM_MRS,
ARM_MSR,
/* Multiply instructions */
ARM_MUL,
ARM_MLA,
ARM_SMULL,
ARM_SMLAL,
ARM_UMULL,
ARM_UMLAL,
/* Miscellaneous instructions */
ARM_CLZ,
/* Exception generating instructions */
ARM_BKPT,
ARM_SWI,
/* Coprocessor instructions */
ARM_CDP,
ARM_LDC,
ARM_STC,
ARM_MCR,
ARM_MRC,
/* Semaphore instructions */
ARM_SWP,
ARM_SWPB,
/* Enhanced DSP extensions */
ARM_MCRR,
ARM_MRRC,
ARM_PLD,
ARM_QADD,
ARM_QDADD,
ARM_QSUB,
ARM_QDSUB,
ARM_SMLAxy,
ARM_SMLALxy,
ARM_SMLAWy,
ARM_SMULxy,
ARM_SMULWy,
ARM_LDRD,
ARM_STRD,
ARM_UNDEFINED_INSTRUCTION = 0xffffffff,
};
struct arm_b_bl_bx_blx_instr {
int reg_operand;
uint32_t target_address;
};
union arm_shifter_operand {
struct {
uint32_t immediate;
} immediate;
struct {
uint8_t Rm;
uint8_t shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
uint8_t shift_imm;
} immediate_shift;
struct {
uint8_t Rm;
uint8_t shift;
uint8_t Rs;
} register_shift;
};
struct arm_data_proc_instr {
int variant; /* 0: immediate, 1: immediate_shift, 2: register_shift */
uint8_t S;
uint8_t Rn;
uint8_t Rd;
union arm_shifter_operand shifter_operand;
};
struct arm_load_store_instr {
uint8_t Rd;
uint8_t Rn;
uint8_t U;
int index_mode; /* 0: offset, 1: pre-indexed, 2: post-indexed */
int offset_mode; /* 0: immediate, 1: (scaled) register */
union {
uint32_t offset;
struct {
uint8_t Rm;
uint8_t shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
uint8_t shift_imm;
} reg;
} offset;
uint32_t address;
uint32_t value;
uint8_t width;
void evaluate(void);
};
struct arm_load_store_multiple_instr {
uint8_t Rn;
uint32_t register_list;
uint8_t addressing_mode; /* 0: IA, 1: IB, 2: DA, 3: DB */
uint8_t S;
uint8_t W;
};
struct arm_instruction {
enum arm_instruction_type type;
char text[128];
uint32_t opcode;
/* return value ... Thumb-2 sizes vary */
unsigned instruction_size;
union {
struct arm_b_bl_bx_blx_instr b_bl_bx_blx;
struct arm_data_proc_instr data_proc;
struct arm_load_store_instr load_store;
struct arm_load_store_multiple_instr load_store_multiple;
} info;
bool isBranchInstruction(void) const {
return (type == ARM_B) || (type == ARM_BL) || (type == ARM_BX) || (type == ARM_BLX);
};
bool isLoadInstruction(void) const {
return (type >= ARM_LDR) && (type <= ARM_LDM);
}
bool isStoreInstruction(void) const {
return (type >= ARM_STR) && (type <= ARM_STM);
}
bool isMemoryAccess(void) const {
return isLoadInstruction() || isStoreInstruction();
};
};
int arm_evaluate_opcode(uint32_t opcode, uint32_t address,
struct arm_instruction *instruction);
int thumb_evaluate_opcode(uint16_t opcode, uint32_t address,
struct arm_instruction *instruction);
int thumb2_opcode(uint32_t address, uint32_t opcode,
struct arm_instruction *instruction);
int arm_access_size(struct arm_instruction *instruction);
#define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000) >> 28])
#endif /* ARM_DISASSEMBLER_H */

View File

@ -53,7 +53,9 @@ namespace fail {
} }
std::ostream& operator <<(std::ostream & os, const fail::Instruction & i) { std::ostream& operator <<(std::ostream & os, const fail::Instruction & i) {
os << i.opcode << "\t" << i.instruction << "\t" << i.comment; #ifndef __puma
os << std::hex << ((int)(i.address)) << "\t" << i.opcode << "\t" << i.instruction << "\t" << i.comment;
#endif
return os; return os;
} }
@ -63,7 +65,7 @@ namespace fail {
// Code lines start with a leading whitespace! (hopefully in each objdump implementation!) // Code lines start with a leading whitespace! (hopefully in each objdump implementation!)
if(line.size() > 0 && isspace(line[0])){ if(line.size() > 0 && isspace(line[0])){
// a line looks like: 800156c:\tdd14 \tble.n 8001598 <_ZN2hw3hal7T32Term8PutBlockEPci+0x30> // a line looks like: 800156c:\tdd14 \tble.n 8001598 <_ZN2hw3hal7T32Term8PutBlockEPci+0x30>
boost::regex expr("\\s+([A-Fa-f0-9]+):((?:\\s+[A-Fa-f0-9]+)+)\\s+(.+?)(;.*)?$"); boost::regex expr("\\s+([A-Fa-f0-9]+):\\t(.*?)\\t(.+?)(;.*)?$");
boost::smatch res; boost::smatch res;
if(boost::regex_search(line, res, expr)){ if(boost::regex_search(line, res, expr)){
std::string address = res[1]; std::string address = res[1];
@ -71,22 +73,31 @@ namespace fail {
ss << std::hex << address; ss << std::hex << address;
address_t addr = 0; address_t addr = 0;
ss >> addr; ss >> addr;
ss.clear();
ss.str("");
std::string opcode = res[2]; std::string opcode = res[2];
// delete trailing/leading whitespaces
boost::trim(opcode); boost::trim(opcode);
// delete inner whitespaces and merge nibbles
opcode.erase(std::remove(opcode.begin(), opcode.end(), ' '), opcode.end());
ss << std::hex << opcode;
unsigned opc = 0;
ss >> opc;
std::string instruction = res[3]; std::string instruction = res[3];
boost::trim(instruction); boost::trim(instruction);
std::string comment = res[4]; std::string comment = res[4];
boost::trim(comment); boost::trim(comment);
m_code.insert(std::make_pair(addr, Instruction(opcode, instruction, comment))); m_code.insert(std::make_pair(addr, Instruction(addr, opc, instruction, comment)));
} }
} }
#endif #endif
} }
static Instruction g_InstructionNotFound; static Instruction g_InstructionNotFound;
const Instruction & Disassembler::disassemble(address_t address) const {
const Instruction& Disassembler::disassemble(address_t address){
InstructionMap_t::const_iterator it = m_code.find(address); InstructionMap_t::const_iterator it = m_code.find(address);
if(it == m_code.end()){ if(it == m_code.end()){
return g_InstructionNotFound; return g_InstructionNotFound;

View File

@ -19,11 +19,12 @@ namespace fail {
* @brief An Instruction represents an disassembled opcode * @brief An Instruction represents an disassembled opcode
*/ */
struct Instruction { struct Instruction {
std::string opcode; // TODO convert to integer, size? address_t address; //!< The instruction address
regdata_t opcode; //!< The opcode itself
std::string instruction; //!< The disassembled instruction std::string instruction; //!< The disassembled instruction
std::string comment; //!< Comment (rest of line after ; ) std::string comment; //!< Comment (rest of line after ; )
Instruction(std::string opcode = "", const std::string& instr = DISASSEMBLER::FAILED, const std::string& comment = "") Instruction(address_t address = ADDR_INV, regdata_t opcode = 0, const std::string& instr = DISASSEMBLER::FAILED, const std::string& comment = "")
: opcode(opcode), instruction(instr), comment(comment) { }; : address(address), opcode(opcode), instruction(instr), comment(comment) { };
}; };
//<! This allows to print an Instruction via Logger or cout //<! This allows to print an Instruction via Logger or cout
std::ostream& operator <<(std::ostream & os, const fail::Instruction & i); std::ostream& operator <<(std::ostream & os, const fail::Instruction & i);
@ -41,7 +42,16 @@ namespace fail {
* @param address The instruction address * @param address The instruction address
* @return The according disassembled instruction if found, else DISASSEMBLER:FAILED * @return The according disassembled instruction if found, else DISASSEMBLER:FAILED
*/ */
const Instruction& disassemble(address_t address); const Instruction & disassemble(address_t address) const;
/**
* Test if there is an instruction at a given address
* @param address The address to test
* @return true if found, else false
*/
bool hasInstructionAt(address_t address) const {
return m_code.find(address) != m_code.end();;
};
/** /**
* Evaluate new ELF file * Evaluate new ELF file

View File

@ -216,7 +216,7 @@ const ElfSymbol& ElfReader::getSection( const std::string& name ){
return g_SymbolNotFound; return g_SymbolNotFound;
} }
// "Pretty" Print // "Pretty" Print
void ElfReader::printDemangled(){ void ElfReader::printDemangled(){
m_log << "Demangled: " << std::endl; m_log << "Demangled: " << std::endl;
for(container_t::const_iterator it = m_symboltable.begin(); it !=m_symboltable.end(); ++it){ for(container_t::const_iterator it = m_symboltable.begin(); it !=m_symboltable.end(); ++it){
@ -230,7 +230,7 @@ void ElfReader::printDemangled(){
void ElfReader::printMangled(){ void ElfReader::printMangled(){
for(container_t::const_iterator it = m_symboltable.begin(); it !=m_symboltable.end(); ++it){ for(container_t::const_iterator it = m_symboltable.begin(); it !=m_symboltable.end(); ++it){
m_log << "0x" << it->getAddress() << "\t" << it->getName().c_str() << "\t" << it->getSize() << std::endl; m_log << "0x" << std::hex << it->getAddress() << "\t" << it->getName().c_str() << "\t" << it->getSize() << std::endl;
} }
} }

View File

@ -43,7 +43,7 @@ unsigned KESOrefs::injectBitFlip(address_t data_address, unsigned bitpos){
unsigned value, injectedval; unsigned value, injectedval;
mm.getBytes(data_address, 4, (void*)&value); mm.getBytes(data_address, 4, (void*)&value);
injectedval = value ^ bitpos; injectedval = value ^ (1<<bitpos);
mm.setBytes(data_address, 4, (void*)&injectedval); mm.setBytes(data_address, 4, (void*)&injectedval);
m_log << "INJECTION at: 0x" << hex << setw(8) << setfill('0') << data_address; m_log << "INJECTION at: 0x" << hex << setw(8) << setfill('0') << data_address;

View File

@ -20,7 +20,7 @@ using namespace fail;
bool VEZSExperiment::run() bool VEZSExperiment::run()
{ {
MemoryManager& mm = simulator.getMemoryManager(); //MemoryManager& mm = simulator.getMemoryManager();
//m_elf.printDemangled(); //m_elf.printDemangled();
m_log << "STARTING EXPERIMENT" << endl; m_log << "STARTING EXPERIMENT" << endl;
@ -40,17 +40,18 @@ bool VEZSExperiment::run()
address_t pfoo = m_elf.getSymbol("foo").getAddress(); address_t pfoo = m_elf.getSymbol("foo").getAddress();
//BPSingleListener bp(address); //BPSingleListener bp(address);
BPRangeListener bp(address-32, address + 32); //BPRangeListener bp(address-32, address + 32);
MemWriteListener l_foo( pfoo ); //MemWriteListener l_foo( pfoo );
simulator.addListener(&l_foo); MemAccessListener l_foo( 0x20002018 ); l_foo.setWatchWidth(0x20);
reg = simulator.getCPU(0).getRegister(RI_R4); reg = simulator.getCPU(0).getRegister(RI_R4);
unsigned foo = 23; unsigned foo = 23;
for(int i = 0; i < 15; i++){ for(int i = 0; i < 15; i++){
simulator.addListenerAndResume(&bp); simulator.addListenerAndResume(&l_foo);
if(i == 0) mm.setBytes(pfoo, 4, (void*)&foo); //if(i == 0) mm.setBytes(pfoo, 4, (void*)&foo);
m_log << " Breakpoint hit! @ 0x" << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl; m_log << " Breakpoint hit! @ 0x" << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl;
m_log << " Register R3: 0x" << hex << simulator.getCPU(0).getRegisterContent(reg) << endl; //m_log << " Register R3: 0x" << hex << simulator.getCPU(0).getRegisterContent(reg) << endl;
mm.getBytes(pfoo, 4, (void*)&foo); //mm.getBytes(pfoo, 4, (void*)&foo);
m_log << " foo @ 0x"<< std::hex << pfoo << " = " << foo << std::endl; m_log << " foo @ 0x"<< std::hex << pfoo << " = " << foo << std::endl;
} }