我一直想知道调试器是如何工作的?特别是可以“附加”到已经运行的可执行文件。我知道编译器将代码翻译成机器语言,但调试器如何“知道”它被附加到什么?


当前回答

我的理解是,当你编译一个应用程序或DLL文件时,无论它编译到什么,都包含表示函数和变量的符号。

When you have a debug build, these symbols are far more detailed than when it's a release build, thus allowing the debugger to give you more information. When you attach the debugger to a process, it looks at which functions are currently being accessed and resolves all the available debugging symbols from here (since it knows what the internals of the compiled file looks like, it can acertain what might be in the memory, with contents of ints, floats, strings, etc.). Like the first poster said, this information and how these symbols work greatly depends on the environment and the language.

其他回答

在Linux中,调试进程从ptrace(2)系统调用开始。本文提供了一个很好的教程,介绍如何使用ptrace实现一些简单的调试构造。

如果你使用的是Windows操作系统,John Robbins写的《调试。net和Windows应用程序》是一个很好的参考资料:

http://www.amazon.com/dp/0735615365

(甚至是旧版本:“调试应用程序”)

这本书有一章是关于调试器如何工作的,其中包括几个简单(但可以工作的)调试器的代码。

由于我不熟悉Unix/Linux调试的细节,这些东西可能根本不适用于其他操作系统。但我猜,作为一个非常复杂的主题的介绍,这些概念(如果不是细节和api)应该“移植”到大多数操作系统。

我的理解是,当你编译一个应用程序或DLL文件时,无论它编译到什么,都包含表示函数和变量的符号。

When you have a debug build, these symbols are far more detailed than when it's a release build, thus allowing the debugger to give you more information. When you attach the debugger to a process, it looks at which functions are currently being accessed and resolves all the available debugging symbols from here (since it knows what the internals of the compiled file looks like, it can acertain what might be in the memory, with contents of ints, floats, strings, etc.). Like the first poster said, this information and how these symbols work greatly depends on the environment and the language.

我认为这里有两个主要问题需要回答:

1. 调试器如何知道发生了异常?

When an exception occurs in a process that’s being debugged, the debugger gets notified by the OS before any user exception handlers defined in the target process are given a chance to respond to the exception. If the debugger chooses not to handle this (first-chance) exception notification, the exception dispatching sequence proceeds further and the target thread is then given a chance to handle the exception if it wants to do so. If the SEH exception is not handled by the target process, the debugger is then sent another debug event, called a second-chance notification, to inform it that an unhandled exception occurred in the target process. Source


2. 调试器如何知道如何在断点处停止?

简单的回答是:当您在程序中放入断点时,调试器将在该点用int3指令替换您的代码,这是一个软件中断。结果是程序被挂起并调用调试器。

了解调试的另一个有价值的来源是英特尔CPU手册(英特尔®64和IA-32架构) 软件开发者手册)。在第3A卷第16章中,介绍了硬件对调试的支持,如特殊异常和硬件调试寄存器。以下是该章的节选:

T (trap)标志,TSS -当尝试时产生一个调试异常(#DB) 切换到TSS中设置了T标志的任务。

我不确定windows或Linux是否使用这个标志,但读那一章非常有趣。

希望这能帮助到一些人。