summaryrefslogtreecommitdiffstats
path: root/src/disassembler_arm.cc
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2012-04-10 18:28:35 -0700
committerElliott Hughes <enh@google.com>2012-04-10 18:28:35 -0700
commit3d71d0799748aac23ce5935d61b909bec6e96461 (patch)
treee9df4425b3f6aeb889c0e30d0a54fb667b8a70ea /src/disassembler_arm.cc
parent836eb8086a58cefb2cd7c31df86ac071f80f0fda (diff)
downloadart-3d71d0799748aac23ce5935d61b909bec6e96461.zip
art-3d71d0799748aac23ce5935d61b909bec6e96461.tar.gz
art-3d71d0799748aac23ce5935d61b909bec6e96461.tar.bz2
Improve ARM disassembler to cope with JNI stubs.
Also decode r9-relative loads/stores, and add the Thread::state_ offset. Also lose the implicit 's' on the test instructions, and don't show the destination register for those instructions, since they don't use it. Examples: 0x60cdd718: e599c060 ldr r12, [r9, #96] ; top_sirt_ 0x60cdd754: e589c06c str r12, [r9, #108] ; state_ 0x60cdd760: e35c0000 cmp r12, #0 0x60cdd7c4: 1a00000b bne 44 (0x60cdd7f8) 0x60cdd814: e1200070 bkpt #0 Change-Id: I4afa9f47267daefded46211d62718fd7fb87cf97
Diffstat (limited to 'src/disassembler_arm.cc')
-rw-r--r--src/disassembler_arm.cc64
1 files changed, 46 insertions, 18 deletions
diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc
index 2492f50..2baf02c 100644
--- a/src/disassembler_arm.cc
+++ b/src/disassembler_arm.cc
@@ -128,13 +128,15 @@ std::ostream& operator<<(std::ostream& os, const Rm& r) {
}
struct Imm12 {
- Imm12(uint32_t instruction) : rotate((instruction >> 8) & 0xf), imm(instruction & 0xff) {}
- uint32_t rotate;
- uint32_t imm;
+ Imm12(uint32_t instruction) {
+ uint32_t rotate = ((instruction >> 8) & 0xf);
+ uint32_t imm = (instruction & 0xff);
+ value = (imm >> (2 * rotate)) | (imm << (32 - (2 * rotate)));
+ }
+ uint32_t value;
};
std::ostream& operator<<(std::ostream& os, const Imm12& rhs) {
- uint32_t imm = (rhs.imm >> (2 * rhs.rotate)) | (rhs.imm << (32 - (2 * rhs.rotate)));
- os << "#" << imm;
+ os << "#" << rhs.value;
return os;
}
@@ -167,23 +169,38 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
uint32_t instruction = ReadU32(instr_ptr);
uint32_t cond = (instruction >> 28) & 0xf;
uint32_t op1 = (instruction >> 25) & 0x7;
- std::ostringstream opcode;
+ std::string opcode;
+ std::string suffixes;
std::ostringstream args;
switch (op1) {
case 0:
case 1: // Data processing instructions.
{
+ if ((instruction & 0x0ff000f0) == 0x01200070) { // BKPT
+ opcode = "bkpt";
+ uint32_t imm12 = (instruction >> 8) & 0xfff;
+ uint32_t imm4 = (instruction & 0xf);
+ args << '#' << ((imm12 << 4) | imm4);
+ break;
+ }
if ((instruction & 0x0fffffd0) == 0x012fff10) { // BX and BLX (register)
- opcode << (((instruction >> 5) & 1) ? "blx" : "bx");
+ opcode = (((instruction >> 5) & 1) ? "blx" : "bx");
args << ArmRegister(instruction & 0xf);
break;
}
bool i = (instruction & (1 << 25)) != 0;
bool s = (instruction & (1 << 20)) != 0;
- opcode << kDataProcessingOperations[(instruction >> 21) & 0xf]
- << kConditionCodeNames[cond]
- << (s ? "s" : "");
- args << ArmRegister(instruction, 12) << ", ";
+ uint32_t op = (instruction >> 21) & 0xf;
+ opcode = kDataProcessingOperations[op];
+ bool implicit_s = ((op & ~3) == 8); // TST, TEQ, CMP, and CMN.
+ if (implicit_s) {
+ // Rd is unused (and not shown), and we don't show the 's' suffix either.
+ } else {
+ if (s) {
+ suffixes += 's';
+ }
+ args << ArmRegister(instruction, 12) << ", ";
+ }
if (i) {
args << ArmRegister(instruction, 16) << ", " << Imm12(instruction);
} else {
@@ -197,7 +214,7 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
bool b = (instruction & (1 << 22)) != 0;
bool w = (instruction & (1 << 21)) != 0;
bool l = (instruction & (1 << 20)) != 0;
- opcode << (l ? "ldr" : "str") << (b ? "b" : "") << kConditionCodeNames[cond];
+ opcode = StringPrintf("%s%s", (l ? "ldr" : "str"), (b ? "b" : ""));
args << ArmRegister(instruction, 12) << ", ";
ArmRegister rn(instruction, 16);
if (rn.r == 0xf) {
@@ -213,6 +230,10 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
} else {
LOG(FATAL) << p << " " << w;
}
+ if (rn.r == 9) {
+ args << " ; ";
+ Thread::DumpThreadOffset(args, Imm12(instruction).value, 4);
+ }
}
}
break;
@@ -222,19 +243,26 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
bool u = (instruction & (1 << 23)) != 0;
bool w = (instruction & (1 << 21)) != 0;
bool l = (instruction & (1 << 20)) != 0;
- opcode << (l ? "ldm" : "stm")
- << (u ? 'i' : 'd')
- << (p ? 'b' : 'a')
- << kConditionCodeNames[cond];
+ opcode = StringPrintf("%s%c%c", (l ? "ldm" : "stm"), (u ? 'i' : 'd'), (p ? 'b' : 'a'));
args << ArmRegister(instruction, 16) << (w ? "!" : "") << ", " << RegisterList(instruction);
}
break;
+ case 5: // Branch/branch with link.
+ {
+ bool bl = (instruction & (1 << 24)) != 0;
+ opcode = (bl ? "bl" : "b");
+ uint32_t imm32 = (instruction & 0xffffff) << 2;
+ DumpBranchTarget(args, instr_ptr + 8, imm32);
+ }
+ break;
default:
- opcode << "???";
+ opcode = "???";
break;
}
+ opcode += kConditionCodeNames[cond];
+ opcode += suffixes;
// TODO: a more complete ARM disassembler could generate wider opcodes.
- os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instruction, opcode.str().c_str()) << args.str() << '\n';
+ os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
}
size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {