如何从链接器错误转到源代码行?

时间:2013-04-08 21:50:00

标签: c++ linux gcc linker g++

链接器产生这种输出

/var/tmp/ccITB4j2.o: In function `main':
/var/tmp/ccITB4j2.o(.text+0x4): undefined reference to `myFunction(void)'

如何查找与实际调用该函数的.text + 0x4指令对应的源代码行?

3 个答案:

答案 0 :(得分:6)

首先,你问题的另一个答案是错误的:在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的引用(C++ myFunction(void)的{​​{1}}名称,以便在目标文件中发出。

答案 1 :(得分:2)

理解链接器错误的关键是了解声明和定义之间的区别。

这是一个声明:

int myFunction();

这是一个定义:

int myFunction() {
  // do something
  return val;
}

当您声明某些内容时,编译器会将其视为您的承诺,您最终也会定义它(可能稍后在同一个翻译单元中,或者可能在另一个单元中)。链接器实际上会在链接时检查这些promise。

因此,链接器错误(例如此错误)实际上是在抱怨您违背了定义某些内容的承诺。由于这是在编译之后发生的,并且因为这涉及“不在那里”的东西,所以要求“不在那里的行号”是没有意义的。

希望这有助于解释为什么您不会将链接号添加到链接器的“未解析的符号”错误消息上。

答案 2 :(得分:1)

我有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

回答问题&#34;调试器如何做到这一点&#34;,这里有一篇关于该主题的好文章:How debuggers work: Part 3 - Debugging informationobjdump选项来自哪里从

如果链接中断,我会提前为后代道歉。


我在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被引用的地方感到头疼。事实证明,编译器正在插入额外的代码来分配东西和删除它。如果它正确地打印了行号(函数的紧密支撑),那么编译器正在做一些与众不同的事情会更加明显。