GCC 链接器忽略了我已确认存在的 .a 库中的符号
GCC linker ignores symbols in a .a library that I have confirmed are present
我在 GCC 中遇到了一个非常令人困惑的问题。
我收到以下错误:
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
这个错误是怎么回事?我已确认 _chorebox_argc
符号确实存在于 "libchorebox.a".
中
我通过运行命令确认:
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" 命令 --- 特此将copy/paste 给你那个文件的相关部分:
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 链接器会抱怨它是没找到?
将 -l
选项 放在需要它的文件 之后 (ofiles/main.o
)
有关 link 订单的详细信息,请参阅 this question。
经过聊天讨论,我们发现问题出在 'common' 定义上。下面是导致问题的代码的简化版本。系统是MacOSX(小牛和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。这表示变量的 'common' 定义。仅仅将它们变成变量定义是不够的——看看来自 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
足以从库中提取目标文件,然后其他变量的通用定义足以满足链接器的要求。
课程
虽然链接顺序是问题的一部分,但不是全部问题。
在库中提供未初始化的全局变量并不总是有效,尤其是在同一个源文件中没有其他定义的情况下。
正如我在聊天中指出的那样,提供对全局变量的直接访问通常不是一个好主意。最好提供功能接口来访问(获取和设置)变量。
我在 GCC 中遇到了一个非常令人困惑的问题。
我收到以下错误:
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
这个错误是怎么回事?我已确认 _chorebox_argc
符号确实存在于 "libchorebox.a".
我通过运行命令确认:
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" 命令 --- 特此将copy/paste 给你那个文件的相关部分:
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 链接器会抱怨它是没找到?
将 -l
选项 放在需要它的文件 之后 (ofiles/main.o
)
有关 link 订单的详细信息,请参阅 this question。
经过聊天讨论,我们发现问题出在 'common' 定义上。下面是导致问题的代码的简化版本。系统是MacOSX(小牛和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。这表示变量的 'common' 定义。仅仅将它们变成变量定义是不够的——看看来自 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
足以从库中提取目标文件,然后其他变量的通用定义足以满足链接器的要求。
课程
虽然链接顺序是问题的一部分,但不是全部问题。
在库中提供未初始化的全局变量并不总是有效,尤其是在同一个源文件中没有其他定义的情况下。
正如我在聊天中指出的那样,提供对全局变量的直接访问通常不是一个好主意。最好提供功能接口来访问(获取和设置)变量。