看看:
(gdb) x/x $esp
0xb720a621: 0x00000000
(gdb) info register eflags
eflags 0x200286 [ PF SF IF ID ]
(gdb) x/5i $pc
=> 0x15a965d <tables+11901>: popf
0x15a965e <tables+11902>: mov $0xd7fb0aa3,%ecx
0x15a9663 <tables+11907>: ret $0x849d
0x15a9666 <tables+11910>: xor (%ebx),%esi
0x15a9668 <tables+11912>: aam $0x78
(gdb) stepi
0x015a965e in tables () from /usr/local/apache2/modules/libphp5.so
(gdb) info register eflags
eflags 0x202 [ IF ]
(gdb) stepi
0x015a9663 in tables () from /usr/local/apache2/modules/libphp5.so
(gdb) info register eflags
eflags 0x302 [ TF IF ]
不确定为什么在下一条指令后设置TF。
答案 0 :(得分:1)
我认为这是一个内核错误。内核必须在单步执行时设置TF
,但用户模式也可能正在修改TF
。为了解决这个问题,内核尝试维护谁设置TF
:
/* Set TF on the kernel stack.. */
regs->flags |= X86_EFLAGS_TF;
/*
* ..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.
*
* Note that if we don't actually execute the popf because
* of a signal arriving right now or suchlike, we will lose
* track of the fact that it really was "us" that set it.
*/
if (is_setting_trap_flag(child, regs)) {
clear_tsk_thread_flag(child, TIF_FORCED_TF);
return 0;
}
请注意,它甚至承认一些极端情况可能会让它失去踪迹。
更糟糕的是is_setting_trap_flag
仅检查指令是否会修改TF
,而不检查它是否实际设置它:
switch (opcode[i]) {
/* popf and iret */
case 0x9d: case 0xcf:
return 1;
因此,即使已清除,它也会将TF
标记为用户设置。在get_flags
中,如果它由内核设置,它将尝试屏蔽TF
,如下所示:
/*
* If the debugger set TF, hide it from the readout.
*/
if (test_tsk_thread_flag(task, TIF_FORCED_TF))
retval &= ~X86_EFLAGS_TF;
由于错误地清除了TIF_FORCED_TF
,因此这种情况不会成立,因此实际上由内核设置的用于单步执行的TF
将返回给调试器。
我认为可以通过修改is_setting_trap_flag
来修复此问题,以便它检查堆栈中是否有新的标志值,如果实际设置了1
则只返回TF
。