将C库从Linux(Ubuntu)移植到OS X时,链接器出现问题。 C 代码是从 Matlab 自动生成的,所以理想情况下我不想更改代码本身。
问题似乎出在一个C文件中,该文件仅包含未初始化的变量声明,然后由其他C文件进行EXTERNed以实现Matlab算法。 OS X 链接器显然无法识别此文件中的符号。 相同的源代码在 Linux 上运行良好,因此我想了解 OS X 链接器的行为有何不同,以及是否可以传递给它以更改行为的标志。
静态库构建时没有错误/警告。 但是,在构建引用静态库的应用程序时,会抛出以下错误消息(在OS X上):
Undefined symbols for architecture x86_64:
"_my_var", referenced from:
_algorithm in libtestlibrary.a(algorithm.o)
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
"nm"表示libtestlibrary.a确实包含符号_my_var。
下面是一个来自Matlab的代码的简化版本。
图书馆代码:
// DATA.C : declaration of data
#include "data.h"
int my_var;
// DATA.H - extern declaration of data
#ifndef H_DATA
#define H_DATA
extern int my_var;
#endif
// ALGORITHM.C - performs the calculation
#include "data.h"
int algorithm(int x) {
my_var += x;
return my_var;
}
//ALGORITHM.H - declaration of library API
#ifndef H_ALGORITHM
#define H_ALGORITHM
int algorithm(int x);
#endif
库构建命令:
gcc -c algorithm.c
gcc -c data.c
ar rcs libtestlibrary.a data.o algorithm.o
应用程序代码:
// MAIN.C : Code which calls into the static library
#include "algorithm.h"
int main() {
int x = 1;
x = algorithm(x);
return 0;
}
应用程序构建命令:
gcc -c main.c
gcc -o testapp main.o -ltestlibrary
如果我将 data.c 中的定义更改为"int my_var=0",以便初始化变量,那么库和应用程序在 Linux 和 OS X 上都可以正确构建。 但是,正如我上面所说,我不想更改代码,因为它是从 Matlab 自动生成的。
提前感谢您的帮助!
您的问题是您没有初始化may_var
。
如果您不初始化数据符号并将其放入静态库中,则它将被创建为公共符号。链接静态库时,我的OS X链接器无法识别这些。
如果您初始化它 (data.c):
#include "data.h"
int my_var = 0;
然后编译器会把它放到不同的部分,它会在静态库中正确链接。
编辑:
或者,您可以将-fno-common
选项传递给 gcc
gcc -c data.c -fno-common
然后,这将指示gcc
不要为未初始化的变量生成公共符号,然后您可以在库中链接它们。
这是 OS X 上的 Mach-O 可执行文件格式的问题,此处对此进行了描述。
@Sergey L.的回答非常好,会解决问题。但是,它并没有抓住正在发生的事情的本质。该行为实际上在OS X存档器ar
中,该存档不会在存档的目录(TOC)中列出常用符号。我按上述方式创建了文件,这是与存档器(OS X 10.9.4,Xcode 5.1.1)的会话:
$ gcc -c algorithm.c
$ gcc -c data.c
$ ar rcs libtestlibrary.a data.o algorithm.o
$ ar x libtestlibrary.a '__.SYMDEF SORTED'
$ od -c __.SYMDEF SORTED
0000000 b 002 020
0000020 _ a l g o r i t h m 240
0000040
目录在存档中显示为名为 __.SYMDEF SORTED
的条目。如您所见,目录不包含my_var
。这就是问题的本质。
您可以使用 ranlib -c
将常用符号添加到目录中。这是如何工作的:
$ ranlib -c libtestlibrary.a
$ ar x libtestlibrary.a '__.SYMDEF SORTED'
$ od -c __.SYMDEF SORTED
0000000 020 b 020 002
0000020 210 030 _ m y _ v a r
0000040 _ a l g o r i t h m Ѓ ** 177
0000060
如您所见,ranlib -c
my_var
添加到目录中。
现在链接步骤将起作用:
$ gcc -L . -o testapp main.o -ltestlibrary
$
因此,并非绝对有必要重新编译代码以使链接正常工作。
ranlib 的手册页是这样说的:
-c Include common symbols as definitions with respect to the table
of contents. This is seldom the intended behavior for linking
from a library, as it forces the linking of a library member
just because it uses an uninitialized global that is undefined
at that point in the linking. This option is included only
because this was the original behavior of ranlib. This option
is not the default.
因此,这是有意偏离传统行为,以避免将不需要的模块链接到输出中。我可能会在自己的开发中使用-fno-common
。