繁体   English   中英

处理器如何知道断点?

[英]How does the processor know the breakpoints?

让我们考虑这个非常简单的程序:

#include<stdio.h>

int main () 
{
    int num1=4, num2=5;
    printf("Welcome\n");
    printf("num1 + num2 = %d\n", num1+num2);
    return 0;
}

使用gcc -S prog.c查看生成的汇编代码时:

    .file   "p.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "Welcome\0"
LC1:
    .ascii "num1 + num2 = %d\12\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB10:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    call    ___main
    movl    $4, 28(%esp)
    movl    $5, 24(%esp)
    movl    $LC0, (%esp)
    call    _puts
    movl    28(%esp), %edx
    movl    24(%esp), %eax
    addl    %edx, %eax
    movl    %eax, 4(%esp)
    movl    $LC1, (%esp)
    call    _printf
    call    _getchar
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE10:
    .ident  "GCC: (GNU) 5.3.0"
    .def    _puts;  .scl    2;  .type   32; .endef
    .def    _printf;    .scl    2;  .type   32; .endef
    .def    _getchar;   .scl    2;  .type   32; .endef

我知道CPU看到编译器为它生成的汇编代码,我不明白程序是如何在用户设置的breakpoint处停止的? 为什么CPU不继续运行程序? 这是怎么回事? 我的意思是,为什么在取指令后它会停止?

我有点困惑,是Code :: Blocks照顾这个或用户正在使用的程序吗?

提前致谢!

大多数现代指令集都包含breakpoint异常,用于允许调试器通过使用特殊软件中断指令临时替换相关程序指令,在程序代码中插入断点。 在x86 / x86-64 ISA上,该指令是“中断向量3”(也就是int3 ),它通常作为单字节指令0xcc发出。

关于断点指令的一个重要注意事项是它们通常必须至少与ISA上最小的可能指令一样小。 这有几个原因。 一些ISA需要最少的指令对齐; 较短的指令通常具有较不严格的对齐要求。 此外,用较长的指令替换某些指令意味着您可能会覆盖以后的指令。 这在单线程应用程序中可能不是什么大问题,但在多线程应用程序中,它是一个显示阻塞。 例如,考虑如果在可选分支的末尾用较长的指令替换短指令而另一个正在运行的线程跳过该分支可能会发生什么。

在其他情况下,可能不存在这样的特殊指令。 在缺少特定断点指令的硬件平台上,有时会提供特殊的硬件寄存器,以使处理器在尝试访问存储器中的特定位置时进行陷阱。 这些寄存器的数量通常相当有限,因此在使用多个断点进行调试时,专用断点指令非常有用。

当您在调试器中启动程序并添加启用软件的断点时,通常会发生以下情况:

调试器将程序加载到内存中并为您提供一些输入提示。 您告诉调试器添加断点。 它可能会使用一些信息来确定内存中断点实际上与程序的内存中表示形式相对应的位置。 然后,调试器对该地址处的指令进行解码(因为它通常要替换整个指令)并用断点指令替换它(在内存中)。 然后告诉调试器执行/继续执行程序。

当处理器遇到此指令时,它会生成陷阱。 此陷阱作为中断传递给操作系统,该操作系统注意到陷阱用于调试程序。 操作系统知道正在执行哪个程序(因此也正在执行它) - 因此它可以进行一些权限检查,以确保此时实际上允许用户调试应用程序。 如果一切看起来都很好,操作系统会通知调试器遇到断点,并告诉您它已停止。

这不是一个普遍的解释。 要实现上述要求,需要大量的OS支持。 在Linux和BSD上,大多数此功能是通过ptrace(2)系统调用公开的(它允许读取和替换指令,以及单步执行)。 在符合POSIX标准的情况下,OS X不实现ptrace(2) ,而是为此提供各种Mach端口。 Windows完全有其他功能。

在嵌入式系统上,可能会提供特殊硬件端口(如JTAG )以允许在硬件级别进行内省,从而允许开发外部调试器,该调试器使用JTAG直接与硬件“对话”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM