Following code is part of software - written by me - that is used to analyze memory data.
I think this can be good sample to help understand the way of "unwinding stack by pseudo execution". For more details see this post.
static void _invalidate_variable_registers(UnwRT* r) { for(int i=0; i<13; i++) { r[i].o = UNWR_INVALID; } } int CRawunw::_Unw_thumb(UnwRT* r, unsigned int inst_cnt, unsigned int stack_base) const { #define __IS_IN_DUMPED_STACK(aDDR) ( (stack_base>=(aDDR)) && ((aDDR)<=osp) ) #ifdef UNW_ESCAPE_INFINITE_LOOP CArr<_CBInfo> cbi; #endif // UNW_ESCAPE_INFINITE_LOOP unsigned short instr; unsigned int osp = r[13].v; // original sp. for(;;) { if(inst_cnt==0) { return UNW_INST_CNT_EXPIRED; } // PC is still on thumb. if(!(r[15].v&0x1)) { return UNW_FAIL; } //ASSERT(r[15].v&0x1); // Check PC & SP is OK. if((UNWR_INVALID==r[15].o)||(UNWR_INVALID==r[13].o)) { //ASSERT(FALSE); return UNW_FAIL; } if(stack_base <= r[13].v) { return UNW_END_OF_FRAME; } if(0 > _read2(_user, r[15].v&(~0x1), &instr)) { /*ASSERT(FALSE);*/ return UNW_FAIL; } // Undefined instruction if( ((instr & 0xf801) == 0xe801) #if 0 // followings can be 'defined instrucion' in future! || ((instr & 0xff00) == 0xbf00) || ((instr & 0xff00) == 0xb100) || ((instr & 0xfa00) == 0xb200) || ((instr & 0xfc00) == 0xb800) #endif ) { // undefined instruction. Unwinding SHOULD BE Stopped! return UNW_FAIL; } //Hi register operations/branch exchange else if((instr & 0xfc00) == 0x4400) { unsigned char op = (instr & 0x0300) >> 8; char h1 = (instr & 0x0080) ? TRUE: FALSE; char h2 = (instr & 0x0040) ? TRUE: FALSE; unsigned char rs = (instr & 0x0038) >> 3; unsigned char rd = (instr & 0x0007); // Adjust the register numbers if(h2) { rs += 8; } if(h1) { rd += 8; } if(op != 3 && !h1 && !h2) { ODPRINTF(("Invalid Instruction:0x%x", instr)); return UNW_FAIL; } switch(op) { case 0: // ADD if((15==rd)&&(13==rd)) { ASSERT(FALSE); return UNW_FAIL; } else { r[rd].v += r[rs].v; r[rd].o = ((UNWR_VALID == r[rd].o) && (UNWR_VALID == r[rs].o) )? UNWR_VALID: UNWR_INVALID; ASSERT(UNWR_VALID == r[13].o); } break; case 1: // CMP ; // nothing to do! break; case 2: // MOV if((15==rd)&&(14==rs)&&(UNWR_VALID==r[rs].o)) { r[15]=r[14]; return UNW_OK; } else { r[rd] = r[rs]; ASSERT(UNWR_VALID == r[13].o); } break; case 3: // BX // if "14==rs" and it is valid than this is return! if(14==rs) { if(UNWR_VALID==r[rs].o) { ODPRINTF(("!!!Return PC=0x%x\n", r[rs].v)); r[15]=r[rs]; return UNW_OK; } else { ASSERT(FALSE); ODPRINTF(("!!!BX Fail\n")); return UNW_FAIL; } } // BX to somewhere. Just ignore it! - do nothing!! ODPRINTF(("!!!BX is ignored! [%s] 0x%x\n", (UNWR_VALID==r[rs].o)? "Valid": "Invalid", r[rs].v)); break; } } // ADD sp,#+imm // ADD sp,#-imm else if((instr & 0xff00) == 0xb000) { unsigned short value = (instr & 0x7f) * 4; /* Check the negative bit */ if(instr & 0x80) { r[13].v -= value; ODPRINTF(("*** SP(-) : 0x%x\n", r[13].v)); } else { r[13].v += value; ODPRINTF(("*** SP(+) : 0x%x\n", r[13].v)); } } // PUSH {Rlist} // PUSH {Rlist, LR} // POP {Rlist} // POP {Rlist, PC} else if((instr & 0xf600) == 0xb400) { char L = (instr & 0x0800) ? TRUE : FALSE; char R = (instr & 0x0100) ? TRUE : FALSE; unsigned char r_list = (instr & 0x00ff); if(L) // stack grows in negative direction in ARM. { for(int i=0; i<8; i++) { if(r_list & (0x1 << i)) { // value only in dumped stack is valid! if( __IS_IN_DUMPED_STACK(r[13].v) ) { // Read the word if(0 > _read4(_user, r[13].v, &(r[i].v)) ) { ASSERT(FALSE); return UNW_FAIL; } r[i].o = UNWR_VALID; ODPRINTF((" r%d = 0x%08x\n", i, r[i].v)); } else { r[i].o = UNWR_INVALID; } r[13].v += 4; } } // Check if the PC is to be popped if(R) { // Get the return address if(0 > _read4(_user, r[13].v, &(r[15].v)) ) { ASSERT(FALSE); return UNW_FAIL; } r[15].o = UNWR_VALID; ODPRINTF((" Return PC=%x\n", r[15].v)); r[13].v += 4; return UNW_OK; // success. } // if(R) ODPRINTF(("*** SP(L) : 0x%x\n", r[13].v)); } // if(L) else { // Stack grows. But we can't believe the values. So, just increase stack pointer!! // Check if the LR is to be pushed if(R) { r[13].v -= 4; } for(int i=7; i>=0; i--) { if(r_list & (0x1 << i)) { r[13].v -= 4; } } ODPRINTF(("*** SP(!L) : 0x%x\n", r[13].v)); } } // B label else if((instr&0xf800) == 0xe000) { short brv = (instr&0x07ff); if(brv & 0x400) { brv |= 0xf800; } // Branch distance is twice that specified in the instruction. brv *= 2; #ifdef UNW_ESCAPE_INFINITE_LOOP int idx = -1; for(int i=0; i<cbi.Size(); i++) { if(cbi[i].pc==r[15].v) { idx=i; break; } } if(idx>=0) { // found if(cbi[idx].b_jump) { cbi[idx].b_jump = false; } else { cbi[idx].b_jump = true; // branch r[15].v += brv; r[15].v += 2; // prefetch advance. } } else { // default is "Jump" _CBInfo info(r[15].v, true); cbi.Append(info); // branch r[15].v += brv; r[15].v += 2; // prefetch advance. } #else // UNW_ESCAPE_INFINITE_LOOP r[15].v += brv; // Need to advance by a word to account for pre-fetch. // Advance by a half word here, allowing the normal address // advance to account for the other half word. r[15].v += 2; #endif // UNW_ESCAPE_INFINITE_LOOP } #ifdef UNW_ESCAPE_INFINITE_LOOP // B<cond> label else if((instr&0xf000) == 0xd000) { short immed8 = instr&0x00ff; int idx = -1; for(int i=0; i<cbi.Size(); i++) { if(cbi[i].pc==r[15].v) { idx=i; break; } } if(idx>=0) { // found if(cbi[idx].b_jump) { cbi[idx].b_jump = false; } else { cbi[idx].b_jump = true; // try to jump r[15].v += immed8*2; r[15].v += 2; // prefetch advance. } } else { // default is "Pass" _CBInfo info(r[15].v, false); cbi.Append(info); } } #endif // UNW_ESCAPE_INFINITE_LOOP // ADD <Rd>, PC/SP, #<immed_8>*4 // It is not essential But, because sp and pc are always 'valid', result is valid. // So, I put this. BUT IT'S NOT ESSENTIAL! else if((instr&0xf000) == 0xa000) { unsigned char rd = (instr&0x0700)>>8; unsigned short imm8 = (instr&0x00ff)<<2; if(instr&0x0800) { r[rd].v = r[13].v + imm8; } // SP else { r[rd].v = (r[15].v & 0xfffffffc) + imm8; } // PC } // LDR <Rd> [SP, #<immd_8>*4] // It is not essential. But, because original stack is 'valid', in most case, result is valid. // So, I put this. BUT IT'S NOT ESSENTIAL! else if((instr&0xf800) == 0x9800) { char rd = instr&0x0700 >> 8; short imm8d = instr&0x00ff; unsigned int addr = r[13].v+imm8d*4; if(addr%4) { ASSERT(FALSE); return UNW_FAIL; } if(osp<=addr) { // valid stack. if(0 > _read4(_user, addr, &(r[rd].v))) { ODPRINTF(("Cannot read from stack\n")); ASSERT(FALSE); return UNW_FAIL; } r[rd].o = UNWR_VALID; // assume that value from stack or constant!. } else { r[rd].o = UNWR_INVALID; } } else // invalidate registers { _invalidate_variable_registers(r); } r[15].v += 2; inst_cnt--; } #undef __IS_IN_DUMPED_STACK }
'Domain > ARM' 카테고리의 다른 글
[ARM] Multi-core optimization test... (0) | 2011.05.03 |
---|---|
[ARM] Sample code for unwinding stack with DWARF2 information. (0) | 2010.04.07 |
[ARM] Unwinding Stack. (0) | 2009.09.15 |
[ARM] Long jump. (0) | 2007.06.05 |
[ARM] .init_array section (0) | 2007.03.18 |