static int FindCall( address *ip_value, address *return_addr_location ) { address prev_ins; address return_addr; mad_disasm_data dd; return_addr = *ip_value; return_addr.mach.offset = GetAnOffset( return_addr_location ); prev_ins = return_addr; if( GetDisasmPrev( &prev_ins ) != MS_OK ) return( 0 ); DbgAddr = prev_ins; DisAsm( &dd ); if( dd.ins.type == DI_X86_call ) { DbgAddr = prev_ins; if( GetDisasmPrev( &DbgAddr ) != MS_OK ) return( 0 ); DisAsm( &dd ); if( dd.ins.type == DI_X86_push3 && IsCSReg( dd.ins.op[ OP_1 ] ) ) { *ip_value = GetFarAddr( return_addr_location ); } else { ip_value->mach.offset = GetAnOffset( return_addr_location ); } return( 1 ); } return_addr = GetFarAddr( return_addr_location ); MCAddrOvlReturn( &return_addr ); DbgAddr = return_addr; if( GetDisasmPrev( &DbgAddr ) != MS_OK ) return( 0 ); DisAsm( &dd ); if( dd.ins.type == DI_X86_call3 ) { *ip_value = GetFarAddr( return_addr_location ); return( 1 ); } else if( dd.ins.type == DI_X86_call ) { DbgAddr = prev_ins; if( GetDisasmPrev( &DbgAddr ) != MS_OK ) return( 0 ); DisAsm( &dd ); if( dd.ins.type == DI_X86_push3 && IsCSReg( dd.ins.op[ OP_1 ] ) ) { *ip_value = GetFarAddr( return_addr_location ); return( 0 ); } } return( 1 ); }
static int GetBPFromStack( address *where, address *value ) { long offset; *value = *where; offset = GetAnOffset( where ); value->mach.offset = offset & ~1; return( ( offset & 1 ) != 0 ); }
static address GetFarAddr( address *return_location ) { address addr; addr.sect_id = 0; addr.indirect = 0; addr.mach.offset = GetAnOffset( return_location ); addr.mach.segment = GetDataWord(); if( Is32BitSegment ) { GetDataWord(); } return( addr ); }
static void SymbolicTraceBack( address *start, unsigned characteristics, long bp_disp, address *execution, address *frame, address *stack ) { address where; if( execution->mach.offset == start->mach.offset ) { /* return address is top item on the stack */ where = *stack; } else { where = *frame; GetBPFromStack( &where, frame ); where.mach.offset += bp_disp; } execution->mach.offset = GetAnOffset( &where ); if( characteristics ) { execution->mach.segment = (unsigned short) GetDataWord(); } *stack = DbgAddr; }
mad_status DIGENTRY MICallUpStackLevel( mad_call_up_data *cud, const address *start, unsigned rtn_characteristics, long return_disp, const mad_registers *in, address *execution, address *frame, address *stack, mad_registers **out ) { // mad_disasm_data dd; // mad_status ms; address curr; addr_off prev_ra_off; addr_off prev_fp_off; addr_off frame_size; addr_off frame_start; // addr_off proc_end; // dis_register ra_save_gpr; return_disp = return_disp; rtn_characteristics = rtn_characteristics; in = in; *out = NULL; if( cud->ra == 0 ) return( MS_FAIL ); if( cud->sp == 0 ) return( MS_FAIL ); frame_size = 0; frame_start = cud->sp; prev_ra_off = NO_OFF; prev_fp_off = NO_OFF; curr = *execution; curr.mach.offset = start->mach.offset; /* Assume prolog no larger than 16 instructions; this might not be enough */ // proc_end = start->mach.offset + 64; if( curr.mach.offset == 0 ) return( MS_FAIL ); // ra_save_gpr = -1; #if 0 for( ;; ) { if( curr.mach.offset >= execution->mach.offset ) break; if( curr.mach.offset >= proc_end ) break; ms = DisasmOne( &dd, &curr, 0 ); if( ms != MS_OK ) return( ms ); if( curr.mach.offset == start->mach.offset + sizeof( unsigned_32 ) ) { /* first instruction is usually 'stwu sp, -framesize(sp)' */ /* NYI: it could be stwux, and it needn't be the first instruction */ if( dd.ins.type != DI_PPC_stwu ) return( MS_FAIL ); frame_size = -dd.ins.op[1].value; } switch( dd.ins.type ) { /* track fp saves */ case DI_PPC_stw: case DI_PPC_stwu: if( dd.ins.op[0].base == DR_PPC_r31 ) { prev_fp_off = dd.ins.op[1].value; } if( dd.ins.op[0].base == ra_save_gpr ) { prev_ra_off = dd.ins.op[1].value; ra_save_gpr = -1; } break; /* track ra saves (those have to go through a scratch GPR) */ case DI_PPC_mfspr: if( dd.ins.op[1].value == 8 ) { ra_save_gpr = dd.ins.op[0].base; } break; /* track moves from sp to fp */ case DI_PPC_or: /* look for 'mr r31, sp' */ if( (dd.ins.op[0].base == DR_PPC_r31) && (dd.ins.op[1].base == DR_PPC_r1) && (dd.ins.op[2].base == DR_PPC_r1) ) { frame_start = cud->fp; } break; } } #endif if( frame_start == 0 ) return( MS_FAIL ); if( cud->first_frame ) { cud->first_frame = false; cud->sp = frame_start + frame_size; cud->fp = frame_start + frame_size; if( prev_ra_off != NO_OFF ) { if( !GetAnOffset( frame_start + prev_ra_off, &cud->ra ) ) { return( MS_FAIL ); } } /* else return address in ra is still valid */ } else { if( !GetAnOffset( frame_start, &cud->sp ) ) return( MS_FAIL ); if( !GetAnOffset( cud->sp + sizeof( unsigned_32 ), &cud->ra ) ) return( MS_FAIL ); if( !GetAnOffset( frame_start + prev_fp_off, &cud->fp ) ) { return( MS_FAIL ); } } if( cud->ra == 0 ) return( MS_FAIL ); if( cud->sp <= frame_start ) return( MS_FAIL ); stack->mach.offset = cud->sp; execution->mach.offset = cud->ra; // if( VariableFrame( execution->mach.offset ) ) { // frame->mach.offset = cud->fp; // } else { frame->mach.offset = cud->sp; // } return( MS_OK ); }
static int HeuristicTraceBack( address *p_prev_sp, address *start, address *execution, address *frame, address *stack ) { mad_disasm_data dd; int word_size; long sp_adjust; long bp_adjust; long saved_bp_loc = 0; long bp_to_ra_offset = 0; // int found_inc_bp; int found_mov_bp_sp; int found_push_bp; char *jmplabel; address return_addr_location; address bp_value; address sp_value; address saved_return_location; int found_call; int i; InitCache( *start, 100 ); sp_value = *stack; bp_value = *frame; DbgAddr = *execution; DisAsm( &dd ); if( dd.ins.type == DI_X86_retf || dd.ins.type == DI_X86_retf2 ) { *execution = GetFarAddr( &sp_value ); found_call = 1; } else if( dd.ins.type == DI_X86_ret || dd.ins.type == DI_X86_ret2 ) { execution->mach.offset = GetAnOffset( &sp_value ); found_call = 1; } else { // Check for ADD SP,n right after current ip and adjust SP if its there // because it must be popping parms if( dd.ins.type == DI_X86_add3 && ConstOp( dd.ins.op[OP_2] ) && IsSPReg( dd.ins.op[OP_1] ) ){ sp_value.mach.offset += dd.ins.op[ OP_2 ].value.s._32[I64LO32]; } // Run through code from the known symbol until and collect prolog info word_size = Is32BitSegment ? 4 : 2; sp_adjust = 0; bp_adjust = 0; // found_inc_bp = 0; found_mov_bp_sp = 0; found_push_bp = 0; DbgAddr = *start; while( DbgAddr.mach.offset != execution->mach.offset ) { DisAsm( &dd ); switch( dd.ins.type ) { case DI_INVALID: return( 0 ); case DI_X86_call3: jmplabel = ToSegStr( dd.ins.op[ OP_1 ].value.s._32[I64LO32], dd.ins.op[ OP_1 ].extra, 0 ); if( IdentifyFunc( jmplabel, &sp_adjust ) ) continue; break; case DI_X86_call: jmplabel = JmpLabel( dd.ins.op[ OP_1 ].value.s._32[I64LO32], 0 ); if( IdentifyFunc( jmplabel, &sp_adjust ) ) continue; break; case DI_X86_enter: sp_adjust -= word_size; // push bp found_push_bp = 1; bp_to_ra_offset = sp_adjust; // mov bp,sp found_mov_bp_sp = 1; saved_bp_loc = 0; // 0[bp] sp_adjust -= dd.ins.op[ OP_1 ].value.s._32[I64LO32]; // sub sp,n break; case DI_X86_inc2: if( IsBPReg( dd.ins.op[ OP_1 ] ) ) { // found_inc_bp = 1; continue; } break; case DI_X86_mov: if( IsBPReg( dd.ins.op[ OP_1 ] ) && IsSPReg( dd.ins.op[ OP_2 ] ) ) { found_mov_bp_sp = 1; bp_to_ra_offset = sp_adjust; saved_bp_loc -= sp_adjust; } continue; case DI_X86_nop: continue; case DI_X86_pop: case DI_X86_pop2: case DI_X86_pop3d: case DI_X86_pop3e: case DI_X86_pop3s: case DI_X86_pop4f: case DI_X86_pop4g: sp_adjust += word_size; continue; case DI_X86_push: case DI_X86_push2: case DI_X86_push3: case DI_X86_push4f: case DI_X86_push4g: case DI_X86_push5: sp_adjust -= word_size; if( IsBPReg( dd.ins.op[ OP_1 ] ) ) { saved_bp_loc = sp_adjust; found_push_bp = 1; } continue; case DI_X86_sub: dd.ins.op[ OP_2 ].value.s._32[I64LO32] = -dd.ins.op[ OP_2 ].value.s._32[I64LO32]; /* fall through */ case DI_X86_add: if( !ConstOp( dd.ins.op[ OP_2 ] ) ) break; if( IsSPReg( dd.ins.op[ OP_1 ] ) ) { sp_adjust += dd.ins.op[ OP_2 ].value.s._32[I64LO32]; continue; } else if( IsBPReg( dd.ins.op[ OP_1 ] ) ) { bp_adjust += dd.ins.op[ OP_2 ].value.s._32[I64LO32]; continue; } break; default: break; } break; } // find the address of the return address (return_addr_location) if( found_mov_bp_sp ) { return_addr_location = bp_value; return_addr_location.mach.offset -= bp_adjust; GetBPFromStack( &return_addr_location, &bp_value ); return_addr_location.mach.offset -= bp_to_ra_offset; } else { if( found_push_bp ) { return_addr_location = sp_value; return_addr_location.mach.offset += saved_bp_loc - sp_adjust; GetBPFromStack( &return_addr_location, &bp_value ); } return_addr_location = sp_value; return_addr_location.mach.offset -= sp_adjust; } found_call = 0; if( found_mov_bp_sp ) { found_call = FindCall( execution, &return_addr_location ); if( !found_call ) { return_addr_location = sp_value; return_addr_location.mach.offset -= sp_adjust; } } if( !found_call ) { saved_return_location = return_addr_location; // limit the search to 512*word_size (W2K can cause us to search 4Gb!) for( i = 0; return_addr_location.mach.offset >= p_prev_sp->mach.offset && i < 512; ++i ) { found_call = FindCall( execution, &return_addr_location ); if( found_call ) break; return_addr_location.mach.offset -= word_size; } if( !found_call ) { return_addr_location = saved_return_location; for( i = 0; i < 10; ++i ) { return_addr_location.mach.offset += word_size; found_call = FindCall( execution, &return_addr_location ); if( found_call ) { break; } } } } } *stack = DbgAddr; *frame = bp_value; return( found_call ); }
mad_status DIGENTRY MICallUpStackLevel( mad_call_up_data *cud, const address *start, unsigned rtn_characteristics, long return_disp, const mad_registers *in, address *execution, address *frame, address *stack, mad_registers **out ) { axp_pdata pdata; mad_disasm_data dd; mad_status ms; address curr; addr_off prev_ra_off; addr_off prev_sp_off; addr_off prev_fp_off; addr_off frame_size; addr_off frame_start; return_disp = return_disp; start = start; rtn_characteristics = rtn_characteristics; in = in; *out = NULL; if( cud->ra == 0 ) return( MS_FAIL ); if( cud->sp == 0 ) return( MS_FAIL ); ms = GetPData( execution->mach.offset, &pdata ); if( ms != MS_OK ) return( ms ); frame_size = 0; frame_start = cud->sp; prev_ra_off = NO_OFF; prev_sp_off = NO_OFF; prev_fp_off = NO_OFF; curr = *execution; curr.mach.offset = pdata.beg_addr.u._32[0]; if( curr.mach.offset == 0 ) return( MS_FAIL ); for( ;; ) { if( curr.mach.offset >= execution->mach.offset ) break; if( curr.mach.offset >= pdata.pro_end_addr.u._32[0] ) break; ms = DisasmOne( &dd, &curr, 0 ); if( ms != MS_OK ) return( ms ); if( curr.mach.offset == (pdata.beg_addr.u._32[0] + sizeof( unsigned_32 )) ) { if( dd.ins.type != DI_AXP_LDA ) return( MS_FAIL ); frame_size = -dd.ins.op[1].value; } switch( dd.ins.type ) { case DI_AXP_STQ: switch( dd.ins.op[0].base ) { case DR_AXP_ra: case DR_AXP_r26: prev_ra_off = dd.ins.op[1].value; break; case DR_AXP_sp: case DR_AXP_r30: prev_sp_off = dd.ins.op[1].value; break; case DR_AXP_fp: case DR_AXP_r15: prev_fp_off = dd.ins.op[1].value; break; } break; case DI_AXP_BIS: if( dd.ins.op[0].type == DO_REG && dd.ins.op[1].type == DO_REG && dd.ins.op[2].type == DO_REG && dd.ins.op[0].base == DR_AXP_r31 /* zero */ && dd.ins.op[1].base == DR_AXP_r30 /* sp */ && dd.ins.op[2].base == DR_AXP_r15 /* fp */ ) { /* variable frame routine, and we've done all the prolog */ frame_start = cud->fp; } break; } } if( frame_start == 0 ) return( MS_FAIL ); if( prev_sp_off != NO_OFF ) { if( !GetAnOffset( frame_start + prev_sp_off, &cud->sp ) ) return( MS_FAIL ); } else { cud->sp = frame_start + frame_size; } if( prev_fp_off != NO_OFF ) { if( !GetAnOffset( frame_start + prev_fp_off, &cud->fp ) ) return( MS_FAIL ); } if( prev_ra_off != NO_OFF ) { if( !GetAnOffset( frame_start + prev_ra_off, &cud->ra ) ) return( MS_FAIL ); } stack->mach.offset = cud->sp; execution->mach.offset = cud->ra; if( VariableFrame( execution->mach.offset ) ) { frame->mach.offset = cud->fp; } else { frame->mach.offset = cud->sp; } return( MS_OK ); }