为什么断点位置是函数地址+4字节?

问题描述 投票:0回答:1

说我有这个

main.cpp

#include <iostream>

int foo() {
  return 123;
}

int main() {
  int a = 1234;
  std::cout << foo() + a << std::endl;
  return 0;
}

然后我编译它:

clang++ -g -O0 main.cpp

现在我有了这个

a.out
,我可以通过
dsymutil -s a.out
查看符号表:

...
[    11] 00000001 2e (N_BNSYM      ) 01     0000   0000000100003ce0
[    12] 00000002 24 (N_FUN        ) 01     0000   0000000100003ce0 '__Z3foov'
[    13] 00000001 24 (N_FUN        ) 00     0000   0000000000000008
[    14] 00000001 4e (N_ENSYM      ) 01     0000   0000000100003ce0
...

所以,这个

foo
函数位于
3ce0

现在,使用 LLDB 在此函数处设置断点:

% lldb a.out
(lldb) target create "a.out"
Current executable set to '/Users/royshi/tmp/a.out' (arm64).
(lldb) b foo
Breakpoint 1: where = a.out`foo() + 4 at main.cpp:4:3, address = 0x0000000100003ce4
(lldb)

现在,请注意断点位于

(0x000000010000)3ce4
,它显示为“where = a.out`foo() + 4 at main.cpp:4:3”,这与
3ce4
地址一致。

所以显然断点超出

foo
的地址 (
3ce0
) 4 个字节。

我想知道为什么会有 4 个字节的差异?


编辑:

按照上面的步骤,我尝试删除 dSYM 文件夹(将其移走),然后再次设置断点,它最终位于

foo
的地址(即没有额外的 4 个字节)。所以,它似乎与调试符号以某种方式相关。

# moving a.out.dSYM to somewhere unaccessible
% mv a.out.dSYM tmp2

% lldb a.out
(lldb) target create "a.out"
Current executable set to '/Users/royshi/tmp/a.out' (arm64).
(lldb) b foo
Breakpoint 1: where = a.out`foo(), address = 0x0000000100003ce0
(lldb) ^D
macos debugging breakpoints clang++ lldb
1个回答
0
投票

jasonharper 明白这样做的原因。如果您在函数的最开始处停止,在运行序言并设置新的堆栈帧之前,那么堆栈展开可能看起来不正确,并且在序言之后以堆栈形式给出的变量值将全部为关闭,如果您不知道发生了什么,这会令人不安。因此,默认情况下,当您在函数符号上设置断点时,lldb 将其向前移动到函数序言的末尾。

至于为什么调试信息可能会影响 lldb 如何确定序言结束位置:事实证明,调试信息中的 DWARF 行表有一个“序言结束”标记。然而,如果您没有调试信息,lldb 会回退到从函数开头扫描指令并将它们与“已知的序言模式”进行匹配。后者更像是有根据的猜测,而编译器可以准确地知道这一点。所以这两种计算有时确实不同。

顺便说一句,如果您不希望出现这种行为,请执行以下操作:

(lldb) break set -n <my_function> --skip-prologue 0
© www.soinside.com 2019 - 2024. All rights reserved.