OS X链接器无法从仅包含变量的C文件中查找符号

时间:2013-10-16 08:40:11

标签: c macos linker ld ar

将C库从Linux(Ubuntu)移植到OS X时,我遇到链接器问题.C代码是从Matlab自动生成的,所以理想情况下我不想更改代码本身。

问题似乎出现在一个C文件中,该文件只包含未初始化的未声明的变量声明,然后由其他C文件进行EXTERN,以实现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自动生成的。

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:8)

您的问题是您没有初始化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可执行格式的问题,并且描述为here

答案 1 :(得分:4)

@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  \0  \0  \0  \0  \0  \0  \0  \0 002  \0  \0 020  \0  \0  \0
0000020    _   a   l   g   o   r   i   t   h   m  \0  \0  \0  \0  \0 240
0000040

TOC在档案中显示为名为__.SYMDEF SORTED的条目。如您所见,TOC不包含my_var。这是问题的实质。

您可以使用ranlib -c向TOC添加常用符号。以下是它的工作原理:

$ ranlib -c libtestlibrary.a
$ ar x libtestlibrary.a '__.SYMDEF SORTED'
$ od -c __.SYMDEF\ SORTED
0000000  020  \0  \0  \0  \b  \0  \0  \0 020 002  \0  \0  \0  \0  \0  \0
0000020  210  \0  \0  \0 030  \0  \0  \0   _   m   y   _   v   a   r  \0
0000040    _   a   l   g   o   r   i   t   h   m  \0   Ѓ  ** 177  \0  \0
0000060

如您所见,ranlib -cmy_var添加到TOC。

现在链接步骤将起作用:

$ 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