1
magiclx 2020-11-20 10:08:13 +08:00 23
因为当在 debug 模式下一个断点时,IDE 将断点处程序的二进制替换为了 0xCC,即 INT 3 指令,当程序执行到 INT 3 时,再替换回原指令执行,而 run 模式并未做这个事。
|
2
ZSeptember 2020-11-20 10:08:30 +08:00
debug 模式,编译器什么的会给生成的可执行文件加入很多调试相关的信息
|
3
Guaidaodl 2020-11-20 10:10:45 +08:00
不同的语言采用的方式略有区别. 建议明确一下语言
|
4
LuckyKoala 2020-11-20 10:25:33 +08:00 1
调试模式运行的时候,会有类似 DWARF 的调试文件生成,包含运行的指令与源文件行号、函数等信息,然后运行的时候会执行 ptrace 类似的函数,如 #1 所说,替换断点处的机器指令,可以查询当时各寄存器的值等信息。
如果有源文件的话,IDE 支持在程序运行之后再把调试器挂载到指定进程。 而普通模式运行的时候是很少与源文件相关的信息,不方便你对着源码去调试。 |
6
luckyrayyy 2020-11-20 10:42:35 +08:00 13
为什么你们连这个都懂...
|
7
wqgogogo OP |
8
atonku 2020-11-20 10:45:09 +08:00
一拍脑门,为什么我就没想到呢
|
10
msaionyc 2020-11-20 10:57:13 +08:00
因为 run 模式不需要进断点
|
11
scar263 2020-11-20 10:58:54 +08:00
楼主你这是想得太多学得太少,建议多看看基础知识的书籍。
|
12
ju5t4fun 2020-11-20 11:32:05 +08:00
硬件支持,操作系统提供 api,编译器提供符号,调试器提供功能,想要理解调试的原理需要很多知识
《软件调试》张银奎,这本书很不错,楼主有兴趣的话可以读一下 |
13
yangJunKing 2020-11-20 11:36:19 +08:00
。
|
14
GrayXu 2020-11-20 12:48:45 +08:00
正经科班都学过编译原理这门课吧。。
|
15
cheng6563 2020-11-20 12:57:14 +08:00 via Android
Java 其实就是开了个特殊端口给调试器调用,其他没区别了
|
18
chaleaoch 2020-11-20 13:01:17 +08:00
C 语言的话 GCC 编译参数不一样.
生成的.o 文件也不一样. 譬如 需要将机器码和代码做映射. 另外执行方式也不同. 参考 ptrace. |
20
msg7086 2020-11-20 13:23:24 +08:00
除了加入调试相关信息外,debug 模式通常还会禁用代码优化,保证你写的什么就生成什么。
release 下代码会经过大量优化,有可能你洋洋洒洒写了一大段的计算,最后被优化成一个常量什么的。 像是 C 系的还会做 unroll 或者 vectorize,不关优化的话根本没法调试的。 |
21
raaaaaar 2020-11-20 13:26:07 +08:00 via Android
好强。。
|
22
fl2d 2020-11-20 13:28:25 +08:00
matlab 随时可停🤔
|
23
no1xsyzy 2020-11-20 14:15:20 +08:00
以前 NOIP 用 Free Pascal 的时候看到文章说简单花指令 asm push pop 一下
然后看到 FP 有 asm,顺便玩了下,然后发现编译输出 debug 的话仅仅 asm push xxx; pop xxx; end; fc 一看,差了非常非常多的部分。而如果编译输出为 release,就只多了几个字节。 |
24
YenvY 2020-11-20 14:23:54 +08:00 1
|
25
atempcode 2020-11-20 17:22:20 +08:00
Java 的 debug 不是加 int 3 的,是 JVM 处理的。
|
27
GrayXu 2020-11-20 19:58:49 +08:00
|
28
akira 2020-11-20 20:08:07 +08:00
release 模式的话,可以用 ollydb 之类的来设置断点,唔。。。。
|
30
xiangyuecn 2020-11-20 20:23:18 +08:00
@magiclx #1 我胡乱猜测的:目测你说的这个应该是执行的时候发生的替换(几乎不可能是修改的编译后的文件),那么非 debug 模式,按道理也能进行替换,甚至插入指令。意思就是不管是不是 debug 模式都能 debug 。
唯一想到的可能就是:非 debug 模式下不好去定位到源文件,也不好去处理变量名(也许都丢失了),然后调试器的开发者懒得去支持非 debug 模式调试😂 任何语言通用,瞎猜的,无非就是为了避免使用者来找麻烦。 |
32
luhe 2020-11-20 21:53:26 +08:00
软件工程专业...没学过编译原理...我确定学校没有安排这门课程...
|
34
irytu 2020-11-20 22:21:54 +08:00 via iPhone 1
@magiclx 是的 再补充一个小细节,就是替换回原来的指令之后还需要把用户态 eip 往前挪一个字节,因为 trap 到内核后,内核栈存的 eip 是被替换指令字节的下一条指令,不然再次执行被替换指令就被 skipped 了
|
35
lujie2012 2020-11-20 22:35:03 +08:00
前面有楼主说到位了,建议看一下 LLVM 编译原理这本书。简单说,run 的编译过程,做了指令集的优化处理,而 debug 模式可以不做指令集的优化,另外会在编译的代码中加入调试代码,只有这样才能实时的 IDE 中看到内存和执行位置。
release 模式直接是汇编或者说是到了机器码,无调试,所以为了找到那一段代码 crash,会生成其他的文件来定位。 debug 过程生产的代码未必到了汇编或者机器码,而只是编译器自己解析的逻辑代码,当然可以 debug 。 |
36
MineDog 2020-11-21 09:48:36 +08:00
java 的话 debug 是 jvm 层面实现的,ide 只是在调用接口,具体没深入了解过
|
37
amimo 2020-11-21 10:15:16 +08:00
如果是 native 程序的话,这个问题的主要跟“调试器”相关,debug 模式下运行的程序受调试器控制。native 调试器实现主要依赖硬件和操作系统提供的调试机制,跟编译器,编译原理无关。
|
38
lewis89 2020-11-21 13:29:31 +08:00
@MineDog #36 道理是差不多的,都是在二进制指令 生成了中断指令,或者使用 mprotect 这种系统调用 触发一个软中断调用
|
39
mingl0280 2020-11-21 16:57:50 +08:00 via Android 1
归根结底不是什么 INT3 的问题(说这个的你用过 od/IDA 么),是 release 模式有优化,编译器会打乱代码行序并且清除大量的编译相关信息以提升运行性能。这样你最多能看到调用了啥函数(某些 stripped 了的程序你连调用栈信息都看不到,里面只剩各种指针了),内部有什么指令偏移多少的信息没了,调试器看到你的源文件的时候不知道去哪里找对应的指令下断点。
为啥 vs 的 release 模式就支持下断点呢,人家有 pdb 文件啊! debug 模式下编译器会在程序中插入调试用的符号表,以及使用未优化的代码(确保每行源代码都能对应上特定的二进制指令),所以下断点容易得多。 |