11

在将 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 自动生成的。

在此先感谢您的帮助!

4

2 回答 2

12

你的问题是你没有初始化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 可执行格式的问题,在此处进行了描述。

于 2013-10-16T09:59:36.573 回答
5

@Sergey L. 的答案非常好,将解决问题。然而,它并没有抓住正在发生的事情的本质。该行为实际上是在 OS X archiverar中,它没有在存档的目录 (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. 如您所见,目录不包含my_var. 这是问题的本质。

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

$ 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 -c添加my_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在我自己的开发中使用。

于 2014-10-27T05:37:52.400 回答