如何从链接器错误到源代码中的代码行
How to go from linker error to line of code in the sources?
链接器产生这种输出
/var/tmp/ccITB4j2.o: In function `main':
/var/tmp/ccITB4j2.o(.text+0x4): undefined reference to `myFunction(void)'
我怎样才能找出对应于.text+0x4指令的源代码行,其中实际调用了函数?
首先,你的问题的另一个答案是错误的:在Linux上,你做从链接器获取文件和行号:
$ cat foo.cc
extern int myFunction(void);
int main()
{
return myFunction();
}
$ g++ -g foo.cc
/tmp/cc3twlhL.o: In function `main':
/tmp/foo.cc:5: undefined reference to `myFunction()'
collect2: ld returned 1 exit status
上面的输出来自gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
和链接器GNU ld (GNU Binutils for Ubuntu) 2.22
,但是对于更老版本的GCC和ld也是如此。
你没有得到文件/行的原因一定是
- 你没有使用
-g
标志,或者 - 你有一个真的旧的
ld
,或者 - 你已经配置了你的
ld
没有支持调试(我不确定这是可能的)。
然而,即使您的ld
拒绝告诉您文件和行,也不是所有的都丢失了。您可以将源代码编译为对象,然后使用objdump -rdS foo.o
来获得相同的信息:
g++ -g -c foo.cc
objdump -rdS foo.o
Disassembly of section .text:
0000000000000000 <main>:
extern int myFunction(void);
int main()
{
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
return myFunction();
4: e8 00 00 00 00 callq 9 <main+0x9>
5: R_X86_64_PC32 _Z10myFunctionv-0x4
}
9: 5d pop %rbp
a: c3 retq
在上面的输出中,您可以清楚地看到哪个源行导致在目标文件中发出对_Z10myFunctionv
的引用(这是myFunction(void)
的C++
变形名称)。
理解链接器错误的关键是要知道声明和定义之间的区别。
是一个声明:
int myFunction();
是一个定义:
int myFunction() {
// do something
return val;
}
当你声明一些东西时,编译器把它当作你的一个承诺,你最终也会定义它(可能稍后在同一个翻译单元中,也可能在不同的翻译单元中)。这些承诺实际上是由链接器在链接时检查的。
所以一个链接器错误(比如这个)实际上是在抱怨你没有遵守你的定义承诺。由于这发生在编译之后,并且由于这处理的是"不存在"的东西,因此要求"不存在的行号"是没有意义的。
我有3个答案(到目前为止),还有一个关于我需要知道如何做到这一点的特定案例的分析
addr2line
可以做到,前提是编译器生成的调试信息与工具兼容。
~ cat asdf.txt
/var/tmp/ccITB4j2.o: In function `main':
/var/tmp/ccITB4j2.o(.text+0x4): undefined reference to `myFunction(void)'
~ cat asdf.txt | addr2line -e /var/tmp/ccITB4j2.o
... it should print src:line info here
还有objdump
:
objdump --dwarf=decodedline test.o
test.o: file format elf64-x86-64
Decoded dump of debug contents of section .debug_line:
CU: test.cc:
File name Line number Starting address
test.cc 2 0x23
test.cc 3 0x84
为了回答"调试器是如何做的"这个问题,这里有一篇关于这个主题的好文章:调试器是如何工作的:第3部分-调试信息,这就是objdump
选项的来源。
如果链接中断,我将提前为后代道歉。
我在Linux中的Solaris Studio 12.3中遇到了类似的问题。看起来,它生成的信息(无论是在.debug_line
还是其他部分)调试信息与addr2line
不兼容,但只有在使用优化构建时才会这样。下面的代码将导致类似的链接错误:
~ cat test.cc
struct Test {int x; Test(); };
inline void test() { Test *t = new Test(); }
void blah() { test(); }
~ CC -g -Kpic test.cc -shared -o libtest.so -Wl,--unresolved-symbols=ignore-in-shared-libs
(...)
test.cc:2: undefined reference to `void operator delete(void*)'
(...)
~ CC -g -Kpic test.cc -shared -o libtest.so -Wl,--unresolved-symbols=ignore-in-shared-libs -O0
(...)
test.cc:(.text+0x45): undefined reference to `void operator delete(void*)'
(...)
~ CC -g -Kpic test.cc -c
~ addr2line -e test.o +0x45
test.cc:2
~ CC -g -Kpic test.cc -O0 -c
~ addr2line -e test.o +0x45
??:?
解决这种情况下的链接错误需要链接到编译器库libCrun
。作为一个反例,反对者评论说它是无用的,知道行号:它确实让我抓耳挠腮的地方delete
被引用。事实证明,编译器插入了额外的代码来分配东西&删除它。如果它正确地打印了行号(函数的右括号),那么编译器正在做一些不寻常的事情就会明显得多。
- 如何在使用 LLVM 编译时获取部分源代码的二进制代码
- 我在区分源代码、目标代码、汇编代码和机器代码时感到困惑
- 如何处理C和C 源代码以计算静态代码分析的指标
- QTCreator GUI应用程序中包含窗口基源代码并在代码中打开
- Objective-C源代码和clang -rewrite-objc C++代码之间有什么关系?
- 哪一个更好?使用C 代码中的System()函数或使用源代码
- 在 GDB 中调试生成的代码时显示原始源代码
- 如何从可从C++代码调用的 Ada 源代码构建静态库?
- 从代码内部编译QT源代码
- 记录代码标头和源代码
- 在哪里可以在ASIO的源代码中获取有关IOCP的代码
- 如何编译没有项目文件的源代码C++代码?
- 验证给定C++编译器是否将源代码读取为 C++11 的快速简单代码是什么?
- 为什么 c++ 源代码可以在没有外部"C"的情况下调用 c 代码?
- 反汇编的 exe 文件包含比源代码多得多的代码
- 使用更新的 gcc 编译器从 c++ 切换到代码块,这会导致我的源代码中出现错误,这些源代码使用旧的 gcc 编译器 v
- arm汇编代码-理解cpp源代码的反汇编
- 试图调试简单代码时,源代码不可用
- 当没有任何源代码更改时,代码块如何从找到头文件变为无法找到它们?
- 我可以使用clang解析C代码,即使我必须在我自己的源代码上使用RTTI