GCC链接器忽略我已确认存在的.a库中的符号

时间:2015-02-11 21:22:27

标签: gcc linker .a

我在海湾合作委员会中遇到了一个非常令人困惑的问题。

我收到以下错误:

gcc -Wall -Werror -L/Users/red_angel/chorebox_sys/lib -o products/chbc2c -lchorebox ofiles/main.o
Undefined symbols for architecture x86_64:
  "_chbclib_flushout", referenced from:
      _main in main.o
  "_chorebox_argc", referenced from:
      _chorebox_command_line in libchorebox.a(chorebox_command_line.o)
  "_chorebox_argv", referenced from:
      _chorebox_command_line in libchorebox.a(chorebox_command_line.o)
  "_chorebox_env", referenced from:
      _chorebox_command_line in libchorebox.a(chorebox_command_line.o)
  "_mn_command_options", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [products/chbc2c] Error 1

此错误有什么问题?我已确认“libchorebox.a”中确实存在_chorebox_argc符号。

我通过运行命令确认了它:

nm /Users/red_angel/chorebox_sys/lib/libchorebox.a | cat -n | chodo -_chorebox_argc flip

由于“chodo”命令是我写的命令,你可能不熟悉 - 我会解释它的作用。它从标准输入读取,并且每个与搜索模式匹配的行转发到标准输出。在这种情况下(长话短说)它会输出包含“_chorebox_argc”字符串的每一行。

我得到以下输出:

     3  0000000000000004 C _chorebox_argc
    55                   U _chorebox_argc

为了仔细查看文件的相关部分,我输入相同的命令,这次只是在管道系列命令的末尾省略了“chodo”命令---因此将复制/粘贴到你是该文件的相关部分:

     1  
     2  /Users/red_angel/chorebox_sys/lib/libchorebox.a(vars.o):
     3  0000000000000004 C _chorebox_argc
     4  0000000000000008 C _chorebox_argv
     5  0000000000000008 C _chorebox_env
     6  
     7  /Users/red_angel/chorebox_sys/lib/libchorebox.a(chorebox_mlc.o):
     8  00000000000000c8 s EH_frame0
     9  0000000000000075 s L_.str
    10                   U ___stderrp
    11                   U _chorebox_argv
    12  0000000000000000 T _chorebox_mlc
    13  00000000000000e0 S _chorebox_mlc.eh
    14                   U _exit
    15                   U _fflush
    16                   U _fprintf
    17                   U _malloc
    18  
    19  /Users/red_angel/chorebox_sys/lib/libchorebox.a(chorebox_apend_string.o):
    20  0000000000000078 s EH_frame0
    21  0000000000000000 T _chorebox_apend_string
    22  0000000000000090 S _chorebox_apend_string.eh
    23                   U _chorebox_join_string
    24                   U _free
    25  

不用说----符号肯定是存在于“libchorebox.a”文件中-----那么为什么GCC链接器会抱怨它找不到?

2 个答案:

答案 0 :(得分:2)

将<{1}}选项放在需要它的文件之后(-l

有关链接顺序的详情,请参阅this question

答案 1 :(得分:2)

在聊天中进行了一些讨论之后,我们发现问题在于“常见”定义。下面是导致问题的代码的简化版本。该系统是Mac OS X(Mavericks和Yosemite)。

仅使用通用定义

vars.h

extern int    chorebox_argc;
extern char **chorebox_argv;
extern char **chorebox_envp;

vars.c

#include "vars.h"
int    chorebox_argc;
char **chorebox_argv;
char **chorebox_envp;

main.c

#include "vars.h"

int main(int argc, char **argv, char **envp)
{
    chorebox_argc = argc;
    chorebox_argv = argv;
    chorebox_envp = envp;
    return argc;
}

编译1

$ gcc -c vars.c
$ nm vars.o
0000000000000004 C _chorebox_argc
0000000000000008 C _chorebox_argv
0000000000000008 C _chorebox_envp
$ ar rv libvars.a vars.o
ar: creating archive libvars.a
a - vars.o
$ ranlib libvars.a
warning: ranlib: warning for library: libvars.a the table of contents is
empty (no object file members in the library define global symbols)
$ gcc -c main.c
$ gcc -o program main.o vars.o
$ gcc -o program main.o -L. -lvars
Undefined symbols for architecture x86_64:
  "_chorebox_argc", referenced from:
      _main in main.o
  "_chorebox_argv", referenced from:
      _main in main.o
  "_chorebox_envp", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status
$

请注意nm输出中的C.这表明变量的“共同”定义。单凭它们变成可变定义是不够的 - 见证来自ranlib的消息。

我不确定这是否是Mac OS X中的新行为。但是,当在库中定义变量时,似乎只有一个仅定义未初始化变量的源文件是不够的,尽管直接链接目标文件就足够了。

使用变量定义

vardefs.c

#include "vars.h"
int    chorebox_argc = 0;
char **chorebox_argv = 0;
char **chorebox_envp = 0;

这已经明确初始化了变量的版本。初始值设定值与默认值相同,但显式初始化会产生差异。

编译2

$ rm libvars.a
$ gcc -c vardefs.c
$ ar rv libvars.a vardefs.o
ar: creating archive libvars.a
a - vardefs.o
$ gcc -o program main.o -L. -lvars
$ 

显式初始化的变量从库中获取没有问题。

使用一个变量定义

vars.h

extern int    chorebox_argc;
extern char **chorebox_argv;
extern char **chorebox_envp;
extern int make_believe;

vars.c

#include "vars.h"
int    chorebox_argc;
char **chorebox_argv;
char **chorebox_envp;

int make_believe = 59;

main.c

#include "vars.h"

int main(int argc, char **argv, char **envp)
{
    chorebox_argc = argc;
    chorebox_argv = argv;
    chorebox_envp = envp;
    make_believe  = 1;
    return argc;
}

编译3

$ gcc -c vars.c
$ ar rv libvars.a vars.o
ar: creating archive libvars.a
a - vars.o
$ nm vars.o
0000000000000004 C _chorebox_argc
0000000000000008 C _chorebox_argv
0000000000000008 C _chorebox_envp
0000000000000000 D _make_believe
$ gcc -c main.c
$ gcc -o program main.o -L. -lvars
$ 

请注意,添加初始化的make_believe足以从库中提取目标文件,其他变量的通用定义足以满足链接器。

吸取

虽然链接顺序是问题的一部分,但并不是整个问题。

在库中提供未初始化的全局变量并不总是有效,特别是如果同一源文件中没有其他定义。

正如我在聊天中所提到的,提供对全局变量的直接访问通常不是一个好主意。最好提供访问(获取和设置)变量的功能接口。