分类: LINUX
2009-08-30 16:16:52
-----------------------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:http://sjj0412.cublog.cn/
-----------------------------------------------------------
下面以一个实例来说明:
当我们执行gdb xx时,就会fork(),ptarce(ptraceme)然后execve(xx),这时发现ptrace位excve创建后就会使xx 阻塞TASK_INTERRUPTIABLE.
然后我们设置bp,这时bp地址所在单元换成trap指令。
当run,xx运行,然后在bp处进入trap中断程序,然后就进入do_signal,这是current->state是ptrace,所以不会执行ptrace_cancel_bpt(current);然后就supsend,通知gdb,gdb然后就读取系统信息,并恢复当前pc指令,即将trap指令换成真正的指令。当用户执行next时,gdb设置为single_step,然后就wake_up xx,xx这是就发现是singlestep,就会执行ptrace_set_bpt(current),这个根据分支预测添加一个临时断点。然后就执行上面断点指令,然后在下一条指令处再次trap中断,,这次gdb不用自己并恢复当前pc指令,即将trap指令换成真正的指令,因为这次是singlestep状态,这时ptrace_cancel_bpt(current)会执行,所以不需要gdb作恢复工作,但是第一次进入singlestep需将断点指令恢复为trap insn;。
如果这时再执行continue,则取消single_step即可并会ptrace_cancel_bpt(child);这样就会和最开始状态一样。
case PTRACE_CONT:
ret = -EIO;
if (!valid_signal(data))
break;
if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* make sure single-step breakpoint is gone. */
child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
wake_up_process(child);
ret = 0;
break;.
#define PT_SINGLESTEP_BIT 31
#define PT_SINGLESTEP (1<
进入断点后:为了能够恢复原来断点(因为可能接下来执行的指令又会调到这里,但是这里在断点执行时将断点消除了,这样就不能再次断点了,所以尽快将这个恢复断点,最好方案是执行下一条指令的时候,恢复这个断点),需要至少执行一个step,不论用户在断点后执行step,next还是run,continue。
设置单步
X86设置单步是唤醒进程,设置为singlestep标识,并标志TF标志位。
case PTRACE_SINGLESTEP: /* set the trap flag. */
ret = -EIO;
if (!valid_signal(data))
break;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
set_singlestep(child);
//设置TF标志位,这个标识的是用户态的EFLAGS,所以只有用户态的程序执行才会单步,实际上也需要如此,因为应用程序就是用户态。
child->exit_code = data;
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
break;
static void set_singlestep(struct task_struct *child)
{
struct pt_regs *regs = get_child_regs(child);
/*
* Always set TIF_SINGLESTEP - this guarantees that
* we single-step system calls etc.. This will also
* cause us to set TF when returning to user mode.
*/
set_tsk_thread_flag(child, TIF_SINGLESTEP);
/*
* If TF was already set, don't do anything else
*/
if (regs->eflags & TRAP_FLAG)
return;
/* Set TF on the kernel stack.. */
regs->eflags |= TRAP_FLAG;
/*
* ..but if TF is changed by the instruction we will trace,
* don't mark it as being "us" that set it, so that we
* won't clear it by hand later.
*/
if (is_at_popf(child, regs))
return;
child->ptrace |= PT_DTRACE;
}
case PTRACE_SINGLESTEP:
ret = -EIO;
if (!valid_signal(data))
break;
child->ptrace |= PT_SINGLESTEP;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
break;
其实硬件调试器和软件调试器,原理基本一样:
硬件调试器相当于gdb,被调试的arm芯片,相当于被调试的应用程序。
软件调试中:
应用程序进入断点了,通过trap中断停止自己,并通知gdb.
硬件调试中:
arm板遇到硬件断点了,会进入调试模式,调试模式是由dclk提供时钟,而这个由jtag产生,当没有jtag命令时就没有时钟,故也达到了停止作用,而这里获取调试状态,是调试器不断轮询扫描链2读取debug_state_register的是否进入调试状态,这样也就达到断点通知目的了。
DBGACK 信号用来标识当前系统是否处于调试状态,当ARM7TDMI 进入调试状态
后,该信号会被自动置“1” 。 所以,通过查询该位,就可以判断ARM7TDMI 当前的
状
软件调试器的断点:
是通过向内存写入int3,trap指令来达到断点目的。
硬件调试器断点:
arm有两组观察点寄存器 ,可以将其中一组设置为读指令,地址屏蔽,Value为特定值比如eeeeeeee。这时要将一个内存地址设为断点,只需将其值替换为eeeeeeee,这样当运行到此地址时,就会匹配,就会进入调试模式。
软件调试单步: