/* * AddLib - a new library has loaded */ static void AddLib( struct link_map *lmap ) { lib_load_info *lli; /* This code is not terribly efficient */ ModuleTop++; lli = malloc( ModuleTop * sizeof( lib_load_info ) ); memset( lli, 0, ModuleTop * sizeof( lib_load_info ) ); memcpy( lli, moduleInfo, (ModuleTop - 1) * sizeof( lib_load_info ) ); free( moduleInfo ); moduleInfo = lli; lli = &moduleInfo[ModuleTop - 1]; lli->offset = lmap->l_addr; lli->dbg_dyn_sect = (addr_off)lmap->l_ld; dbg_strcpy( pid, lli->filename, lmap->l_name ); lli->newly_loaded = true; lli->newly_unloaded = false; lli->offset = lmap->l_addr; Out( "Added library: ofs/dyn = " ); OutNum( lmap->l_addr ); Out( "/" ); OutNum( (addr_off)lmap->l_ld ); Out( " " ); Out( lli->filename ); Out( "\n" ); }
unsigned ReqClear_break( void ) { clear_break_req *acc; bp_t opcode; acc = GetInPtr( 0 ); CONV_LE_32( acc->break_addr.offset ); CONV_LE_16( acc->break_addr.segment ); opcode = acc->old; WriteMem( pid, &opcode, acc->break_addr.offset, sizeof( opcode ) ); Out( "ReqClear_break at " ); OutNum( acc->break_addr.offset ); Out( " (setting to " ); OutNum( opcode ); Out( ")\n" ); return( 0 ); }
/* * AccGetLibName - get lib name of current module */ trap_retval ReqGet_lib_name( void ) { get_lib_name_req *acc; get_lib_name_ret *ret; char *name; unsigned i; trap_elen ret_len; acc = GetInPtr( 0 ); CONV_LE_32( acc->mod_handle ); ret = GetOutPtr( 0 ); name = GetOutPtr( sizeof( *ret ) ); ret->mod_handle = 0; ret_len = sizeof( *ret ); for( i = 0; i < ModuleTop; ++i ) { if( moduleInfo[i].newly_unloaded ) { Out( "(newly unloaded) " ); ret->mod_handle = i; *name = '\0'; moduleInfo[i].newly_unloaded = false; ++ret_len; break; } else if( moduleInfo[i].newly_loaded ) { Out( "(newly loaded) " ); ret->mod_handle = i; strcpy( name, moduleInfo[i].filename ); moduleInfo[i].newly_loaded = false; ret_len += strlen( name ) + 1; break; } } Out( "ReqGet_lib_name: in handle " ); OutNum( acc->mod_handle ); Out( " out handle " ); OutNum( ret->mod_handle ); Out( "\n" ); CONV_LE_32( ret->mod_handle ); return( ret_len ); }
static void OutBuff( TRACEBUF far *buf ) { Out( "pid = " ); OutNum( buf->pid ); Out( "\r\n" ); Out( "tid = " ); OutNum( buf->tid ); Out( "\r\n" ); Out( "cmd = " ); OutNum( buf->cmd ); Out( "\r\n" ); Out( "value = " ); OutNum( buf->value ); Out( "\r\n" ); Out( "offv = " ); OutNum( buf->offv ); Out( "\r\n" ); Out( "segv = " ); OutNum( buf->segv ); Out( "\r\n" ); Out( "mte = " ); OutNum( buf->mte ); Out( "\r\n" ); }
unsigned ReqSet_break( void ) { set_break_req *acc; set_break_ret *ret; bp_t opcode; acc = GetInPtr( 0 ); ret = GetOutPtr( 0 ); CONV_LE_32( acc->break_addr.offset ); CONV_LE_16( acc->break_addr.segment ); ReadMem( pid, &opcode, acc->break_addr.offset, sizeof( opcode ) ); ret->old = opcode; opcode = BRK_POINT; WriteMem( pid, &opcode, acc->break_addr.offset, sizeof( opcode ) ); Out( "ReqSet_break at " ); OutNum( acc->break_addr.offset ); Out( " (was " ); OutNum( ret->old ); Out( ")\n" ); return( sizeof( *ret ) ); }
/* * AccMapAddr - map address in image from link-time virtual address to * actual linear address as loaded in memory. For executables, this will * in effect return the address unchanged (image base 0x08048100 equals * linear 0x08048100), for shared libs this will typically add the offset * from zero (link time VA) to actual load base. */ trap_retval ReqMap_addr( void ) { map_addr_req *acc; map_addr_ret *ret; unsigned long val; lib_load_info *lli; // Note: Info about the process address space is stored in the user register // for GDB, so we can use that to find out what we need to convert these // values in here... acc = GetInPtr( 0 ); CONV_LE_32( acc->in_addr.offset ); CONV_LE_16( acc->in_addr.segment ); CONV_LE_32( acc->mod_handle ); ret = GetOutPtr( 0 ); ret->lo_bound = 0; ret->hi_bound = ~(addr_off)0; errno = 0; if( (val = ptrace( PTRACE_PEEKUSER, pid, (void *)(offsetof( user_struct, start_code )), 0 )) == -1 ) { if( errno ) { Out( "ReqMap_addr: first PTRACE_PEEKUSER failed!\n" ); val = 0; } } #ifdef __MIPS__ // Hack for MIPS - the above call seems to be failing but isn't returning // an error; it's possible that the call just isn't valid. val = 0; #endif ret->out_addr.offset = acc->in_addr.offset + val; if( acc->mod_handle > ModuleTop ) { Out( "ReqMap_addr: Invalid handle passed!\n" ); return( sizeof( *ret ) ); } else { lli = &moduleInfo[acc->mod_handle]; } Out( "ReqMap_addr: addr " ); OutNum( acc->in_addr.segment ); Out( ":" ); OutNum( acc->in_addr.offset ); Out( " in module " ); OutNum( acc->mod_handle ); if( acc->in_addr.segment == MAP_FLAT_DATA_SELECTOR || acc->in_addr.segment == flatDS ) { if( (val = ptrace( PTRACE_PEEKUSER, pid, (void *)offsetof( user_struct, u_tsize ), 0 )) == -1 ) { if( errno ) { Out( "ReqMap_addr: second PTRACE_PEEKUSER failed!\n" ); val = 0; } } ret->out_addr.offset += val; ret->out_addr.segment = flatDS; } else { ret->out_addr.segment = flatCS; } ret->out_addr.offset += lli->offset; Out( " to " ); OutNum( ret->out_addr.offset ); Out( "\n" ); CONV_LE_32( ret->out_addr.offset ); CONV_LE_16( ret->out_addr.segment ); CONV_LE_32( ret->lo_bound ); CONV_LE_32( ret->hi_bound ); return( sizeof( *ret ) ); }
static unsigned ProgRun( int step ) { static int ptrace_sig = 0; static int ld_state = 0; user_regs_struct regs; int status; prog_go_ret *ret; void (*old)(int); int debug_continue; if( pid == 0 ) return( 0 ); ret = GetOutPtr( 0 ); if( at_end ) { ptrace_sig = 0; ret->conditions = COND_TERMINATE; goto end; } /* we only want child-generated SIGINTs now */ do { old = setsig( SIGINT, SIG_IGN ); if( step ) { Out( "PTRACE_SINGLESTEP\n" ); if( ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)ptrace_sig ) == -1 ) perror( "PTRACE_SINGLESTEP" ); } else { Out( "PTRACE_CONT\n" ); if( ptrace( PTRACE_CONT, pid, NULL, (void *)ptrace_sig ) == -1 ) perror( "PTRACE_CONT" ); } waitpid( pid, &status, 0 ); setsig( SIGINT, old ); #if defined( MD_x86 ) ptrace( PTRACE_GETREGS, pid, NULL, ®s ); #elif defined( MD_ppc ) regs.eip = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_NIP, NULL ); regs.esp = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_R1, NULL ); #elif defined( MD_mips ) regs.eip = ptrace( PTRACE_PEEKUSER, pid, (void *)PC, NULL ); regs.esp = ptrace( PTRACE_PEEKUSER, pid, (void *)29, NULL ); #endif Out( " eip " ); OutNum( regs.eip ); Out( "\n" ); debug_continue = FALSE; if( WIFSTOPPED( status ) ) { switch( ( ptrace_sig = WSTOPSIG( status ) ) ) { case SIGSEGV: case SIGILL: case SIGFPE: case SIGABRT: case SIGBUS: case SIGQUIT: case SIGSYS: last_sig = ptrace_sig; ret->conditions = COND_EXCEPTION; ptrace_sig = 0; break; case SIGINT: ret->conditions = COND_USER; ptrace_sig = 0; break; case SIGTRAP: ret->conditions = step ? COND_TRACE : COND_BREAK; Out( "sigtrap\n" ); ptrace_sig = 0; break; default: /* For signals that we do not wish to handle, we need * to continue the debuggee until we get a signal * that we need to handle */ Out( "Unknown signal " ); OutNum( ptrace_sig ); Out( "\n" ); debug_continue = TRUE; break; } } else if( WIFEXITED( status ) ) { Out( "WIFEXITED\n" ); at_end = TRUE; ret->conditions = COND_TERMINATE; ptrace_sig = 0; goto end; } } while( debug_continue ); if( ret->conditions == COND_BREAK ) { #if defined( MD_x86 ) if( regs.eip == rdebug.r_brk + sizeof( old_ld_bp ) ) { #elif defined( MD_ppc ) || defined( MD_mips ) if( regs.eip == rdebug.r_brk ) { #endif int psig = 0; void (*oldsig)(int); bp_t opcode = BRK_POINT; /* The dynamic linker breakpoint was hit, meaning that * libraries are being loaded or unloaded. This gets a bit * tricky because we must restore the original code that was * at the breakpoint and execute it, but we still want to * keep the breakpoint. */ WriteMem( pid, &old_ld_bp, rdebug.r_brk, sizeof( old_ld_bp ) ); ReadMem( pid, &rdebug, (addr48_off)dbg_rdebug, sizeof( rdebug ) ); Out( "ld breakpoint hit, state is " ); switch( rdebug.r_state ) { case RT_ADD: Out( "RT_ADD\n" ); ld_state = RT_ADD; AddOneLib( rdebug.r_map ); break; case RT_DELETE: Out( "RT_DELETE\n" ); ld_state = RT_DELETE; break; case RT_CONSISTENT: Out( "RT_CONSISTENT\n" ); if( ld_state == RT_DELETE ) DelOneLib( rdebug.r_map ); ld_state = RT_CONSISTENT; break; default: Out( "error!\n" ); break; } regs.orig_eax = -1; #if defined( MD_x86 ) regs.eip--; ptrace( PTRACE_SETREGS, pid, NULL, ®s ); #endif oldsig = setsig( SIGINT, SIG_IGN ); ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)psig ); waitpid( pid, &status, 0 ); setsig( SIGINT, oldsig ); WriteMem( pid, &opcode, rdebug.r_brk, sizeof( old_ld_bp ) ); ret->conditions = COND_LIBRARIES; } else { #if defined( MD_x86 ) Out( "decrease eip(sigtrap)\n" ); regs.orig_eax = -1; regs.eip--; ptrace( PTRACE_SETREGS, pid, NULL, ®s ); #endif } } orig_eax = regs.orig_eax; last_eip = regs.eip; ret->program_counter.offset = regs.eip; ret->program_counter.segment = regs.cs; ret->stack_pointer.offset = regs.esp; ret->stack_pointer.segment = regs.ss; ret->conditions |= COND_CONFIG; /* If debuggee has dynamic section, try getting the r_debug struct * every time the debuggee stops. The r_debug data may not be available * immediately after the debuggee process loads. */ if( !have_rdebug && (dbg_dyn != NULL) ) { if( Get_ld_info( pid, dbg_dyn, &rdebug, &dbg_rdebug ) ) { bp_t opcode; AddInitialLibs( rdebug.r_map ); have_rdebug = TRUE; ret->conditions |= COND_LIBRARIES; /* Set a breakpoint in dynamic linker. That way we can be * informed on dynamic library load/unload events. */ ReadMem( pid, &old_ld_bp, rdebug.r_brk, sizeof( old_ld_bp ) ); Out( "Setting ld breakpoint at " ); OutNum( rdebug.r_brk ); Out( " old opcode was " ); OutNum( old_ld_bp ); Out( "\n" ); opcode = BRK_POINT; WriteMem( pid, &opcode, rdebug.r_brk, sizeof( opcode ) ); } } end: CONV_LE_32( ret->stack_pointer.offset ); CONV_LE_16( ret->stack_pointer.segment ); CONV_LE_32( ret->program_counter.offset ); CONV_LE_16( ret->program_counter.segment ); CONV_LE_16( ret->conditions ); return( sizeof( *ret ) ); } unsigned ReqProg_step( void ) { return( ProgRun( TRUE ) ); } unsigned ReqProg_go( void ) { return( ProgRun( FALSE ) ); } unsigned ReqRedirect_stdin( void ) { redirect_stdin_ret *ret; ret = GetOutPtr( 0 ); ret->err = 1; return( sizeof( *ret ) ); }