与C中的静态库链接 [英] linking with static library in C

查看:128
本文介绍了与C中的静态库链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,我是C和链接的初学者,我正在读一本书,该书对与静态库的链接有疑问:

Hi I'm a beginner in C and Linking, I was reading a book that has a question in linking with static library:

a和b表示当前目录中的对象模块或静态库,并且 让a→b表示a依赖于b,因为b定义了一个符号,即 被a引用.对于以下每种情况,显示最小命令 行(即,目标文件和库参数最少的行)将 允许静态链接程序解析所有符号引用:

Let a and b denote object modules or static libraries in the current directory, and let a→b denote that a depends on b, in the sense that b defines a symbol that is referenced by a. For each of the following scenarios, show the minimal command line (i.e., one with the least number of object file and library arguments) that will allow the static linker to resolve all symbol references:

p.o → libx.a → liby.a and liby.a → libx.a →p.o

书中给出的答案是:

gcc p.o libx.a liby.a libx.a

我很困惑,答案不应该是:

I'm confused, shouldn't the answer be :

gcc p.o libx.a liby.a libx.a p.o

否则p.o如何解析libx.a中的未定义符号?

otherwise how the undefined symbol in libx.a resolved by p.o?

推荐答案

如果您的C教科书不清楚,请联系 作者试图以此说明的行为 锻炼不是C标准规定的,实际上是行为 GNU binutils链接器ld的形式-Linux中的默认系统链接器, 通常由gcc|g++|gfortran等代表您调用-可能 但不是不必要您可能会遇到的其他链接器的行为.

In case your C textbook does not make it clear, the linkage behaviour that the author is attempting to illustrate with this exercise is not mandated by the C Standard and is in fact behaviour of the GNU binutils linker ld - the default system linker in Linux, usually invoked on your behalf by gcc|g++|gfortran, etc - and possibly but not necessarily the behaviour of other linkers you might encounter.

如果您已经正确地给了我们练习,那么作者可能是不懂静态的人 链接得很好,这对于编写有关它的教科书是最好的,或者也许是这样. 表达自己的态度.

If you've given us the exercise accurately, the author may be someone who does not understand static linking quite as well as would be best for writing textbooks about it, or perhaps just doesn't express themselves with great care.

除非我们要链接程序,否则默认情况下,链接器不会 甚至坚持解决所有符号引用.所以大概我们是 链接程序(不是共享库),如果答案为:

Unless we are linking a program, the linker by default will not even insist on resolving all symbol references. So presumably we're linking a program (not a shared library), and if the answer:

 gcc p.o libx.a liby.a libx.a

实际上是教科书上所说的,然后就是一个程序.

is actually what the text-book says, then a program is what it has to be.

但是程序必须具有main功能. main函数在哪里 它与p.olibx.aliby.a的链接关系是什么?这 很重要,我们没有被告知.

But a program must have a main function. Where is the main function and what are its linkage relationships to p.o, libx.a and liby.a? This matters and we're not told.

因此,我们假设p代表 program ,并且主函数位于 至少在p.o中定义.奇怪,尽管liby.a依赖 在p.o上,其中p.o是程序的主要对象模块,甚至 在静态库的成员中定义main函数的方法很奇怪.

So let's assume that p stands for program, and that the main function is at least defined in p.o. Weird though it would be for liby.a to depend on p.o where p.o is the main object module of the program, it would be even weirder for the main function to be defined in a member of a static library.

假设很多,下面是一些源文件:

Assuming that much, here are some source files:

p.c

#include <stdio.h>

extern void x(void);

void p(void)
{
    puts(__func__);
}

int main(void)
{
    x();
    return 0;
}

x.c

#include <stdio.h>

void x(void)
{
    puts(__func__);
}

y.c

#include <stdio.h>

void y(void)
{
    puts(__func__);
}

callx.c

extern void x(void);

void callx(void)
{
    x();
}

cally.c

extern void y(void);

void cally(void)
{
    y();
}

callp.c

extern void p(void);

void callp(void)
{
    p();
}

将它们全部编译为目标文件:

Compile them all to object files:

 $ gcc -Wall -Wextra -c p.c x.c y.c callx.c cally.c callp.c

并创建静态库libx.aliby.a:

$ ar rcs libx.a x.o cally.o callp.o
$ ar rcs liby.a y.o callx.o

现在,p.olibx.aliby.a满足练习的条件:

Now, p.o, libx.a and liby.a fulfil the conditions of the exercise:

 p.o → libx.a → liby.a and liby.a → libx.a →p.o

因为:

  • p.o引用但未定义x,这是 在libx.a中定义.

  • p.o refers to but does not define x, which is defined in libx.a.

libx.a定义了cally,它引用但未定义y, 在liby.a

libx.a defines cally, which refers to but does not define y, which is defined in liby.a

liby.a定义了callx,它引用但未定义x, 在libx.a中定义.

liby.a defines callx, which refers to but does not define x, which is defined in libx.a.

libx.a定义了callp,它引用但未定义p, 在p.o中定义.

libx.a defines callp, which refers to but does not define p, which is defined in p.o.

我们可以通过nm进行确认:

We can confirm with nm:

 $ nm p.o
 0000000000000000 r __func__.2252
                  U _GLOBAL_OFFSET_TABLE_
 0000000000000013 T main
 0000000000000000 T p
                  U puts
                  U x

p.o定义p(= T p)并引用x(= U x)

p.o defines p ( = T p) and references x ( = U x)

$ nm libx.a

x.o:
0000000000000000 r __func__.2250
                 U _GLOBAL_OFFSET_TABLE_
                 U puts
0000000000000000 T x

cally.o:
0000000000000000 T cally
                 U _GLOBAL_OFFSET_TABLE_
                 U y

callp.o:
0000000000000000 T callp
                 U _GLOBAL_OFFSET_TABLE_
                 U p

libx.a定义x(= T x)并引用y(= U y)和 引用p(= U p)

libx.a defines x ( = T x) and references y ( = U y) and references p ( = U p)

$ nm liby.a

y.o:
0000000000000000 r __func__.2250
                 U _GLOBAL_OFFSET_TABLE_
                 U puts
0000000000000000 T y

callx.o:
0000000000000000 T callx
                 U _GLOBAL_OFFSET_TABLE_
                 U x

liby.a定义y(= T y)并引用x(= U x)

liby.a defines y ( = T y) and references x ( = U x)

现在,教科书的链接肯定会成功:

Now the textbook's linkage certainly succeeds:

$ gcc p.o libx.a liby.a libx.a
$ ./a.out
x

但这是最短的链接吗?不,这是:

But is it the shortest possible linkage? No. This is:

$ gcc p.o libx.a
$ ./a.out
x

为什么?让我们重新运行与诊断的链接,以显示哪个对象 文件实际上已链接:

Why? Lets rerun the linkage with diagnostics to show which of our object files were actually linked:

$ gcc p.o libx.a -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
p.o
(libx.a)x.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o

他们是:

 p.o
 (libx.a)x.o

首先将

p.o链接到程序中,因为输入的.o文件是 总是无条件链接.

p.o was first linked into the program because an input .o file is always linked, unconditionally.

然后是libx.a.读 静态库 了解链接器如何处理它.链接p.o后,它具有 仅一个未解决的引用-对x的引用.它检查libx.a寻找 定义x的目标文件.找到(libx.a)x.o.它从libx.a中提取了x.o 并将其链接,然后完成 . 1

Then came libx.a. Read static-libaries to understand how the linker handled it. After linking p.o, it had only one unresolved reference - the reference to x. It inspected libx.a looking for an object file that defines x. It found (libx.a)x.o. It extracted x.o from libx.a and linked it, and then it was done.1

所有涉及liby.a的依赖关系:-

All of the dependency relationships involving liby.a:-

  • (libx.a)cally.o取决于(liby.a)y.o
  • (liby.a)callx.o取决于(libx.a)x.o
  • (libx.a)cally.o depends on (liby.a)y.o
  • (liby.a)callx.o depends on (libx.a)x.o

与链接无关,因为该链接不需要任何 liby.a中的目标文件.

are irrelevant to the linkage, because the linkage does not need any of the object files in liby.a.

鉴于作者所说的是正确的答案,我们可以对产品进行反向工程 他们努力陈述自己的观点.就是这样:

Given what the author says is the right answer, we can reverse engineer the exercise that they were striving to state. This is it:

  • 定义main的对象模块p.o引用了一个符号x 没有定义,并且x是在静态库libxz.a

  • An object module p.o that defines main refers to a symbol x that it does not define, and x is defined in member x.o of a static library libxz.a

(libxz.a)x.o引用未定义的符号y,并且y是在静态库liby.a

(libxz.a)x.o refers to a symbol y that it does not define, and y is defined in member y.o of a static library liby.a

(liby.a)y.o指的是未定义的符号z,并且z是在libxz.a的成员z.o中定义的.

(liby.a)y.o refers to a symbol z that it does not define, and z is defined in member z.o of libxz.a.

(liby.a)y.o指的是未定义的符号p,并且p是在p.o

(liby.a)y.o refers to a symbol p that it does not define, and p is defined in p.o

使用p.olibxz.aliby.a的最小链接命令是什么 会成功吗?

What is the minimal linkage command using p.o, libxz.a, liby.a that will succeed?

新的源文件:

p.c

Stays as before.

x.c

#include <stdio.h>

extern void y();

void cally(void)
{
    y();
}

void x(void)
{
    puts(__func__);
}

y.c

#include <stdio.h>

extern void z(void);
extern void p(void);

void callz(void)
{
    z();
}

void callp(void)
{
    p();
}

void y(void)
{
    puts(__func__);
}

z.c

#include <stdio.h>

void z(void)
{
    puts(__func__);
}

新的静态库:

$ ar rcs libxz.a x.o z.o
$ ar rcs liby.a y.o

现在建立联系:

$ gcc p.o libxz.a
libxz.a(x.o): In function `cally':
x.c:(.text+0xa): undefined reference to `y'
collect2: error: ld returned 1 exit status

失败,同样如此:

$ gcc p.o libxz.a liby.a
liby.a(y.o): In function `callz':
y.c:(.text+0x5): undefined reference to `z'
collect2: error: ld returned 1 exit status

和:

$ gcc p.o liby.a libxz.a
libxz.a(x.o): In function `cally':
x.c:(.text+0xa): undefined reference to `y'
collect2: error: ld returned 1 exit status

和(您自己选择):

$ gcc p.o liby.a libxz.a p.o
p.o: In function `p':
p.c:(.text+0x0): multiple definition of `p'
p.o:p.c:(.text+0x0): first defined here
p.o: In function `main':
p.c:(.text+0x13): multiple definition of `main'
p.o:p.c:(.text+0x13): first defined here
libxz.a(x.o): In function `cally':
x.c:(.text+0xa): undefined reference to `y'
collect2: error: ld returned 1 exit status

出现未定义引用错误多定义错误的情况.

fails with both undefined-reference errors and multiple-definition errors.

但教科书回答:

$ gcc p.o libxz.a liby.a libxz.a
$ ./a.out
x

现在正确了.

作者试图描述两个之间的相互依赖 静态库在程序的链接中,却摸索了这样一个相互依赖的事实 链接每个库中至少需要一个目标文件时,链接需要存在 指由 other 库中的目标文件定义的某些符号.

The author was attempting to describe a mutual dependency between two static libraries in the linkage of a program, but fumbled the fact that such a mutual dependency can only exist when the the linkage needs at least one object file from each library that refers to some symbol that is defined by an object file in the other library.

从更正后的练习中学到的教训是:

The lessons to be learned from the corrected exercise are:

  • 出现在链接器输入中的目标文件foo.o永远不需要出现 不止一次,因为它会无条件链接,并且在链接时 链接它提供的任何符号s的定义将用于解决 所有其他链接器输入都包含对s的所有引用.如果foo.o是 输入两次,只能得到s的多重定义错误.

  • An object file foo.o that appears in the linker inputs never needs to appear more than once, because it will be linked unconditionally, and when it is linked the definition that it provides of any symbol s will serve to resolve all references to s that accrue for any other linker inputs. If foo.o is input twice you can only get errors for multiple-definition of s.

但是在链接中的静态库之间存在相互依赖性的地方 输入两个库之一可以 解决.因为一个目标文件 是从静态库中提取的,并且仅当该目标文件为 需要定义链接器试图定义的未解析符号引用 在输入库时 .因此,在更正的示例中:

But where there is a mutual dependency between static libraries in the linkage it can be resolved by inputting one of the libraries twice. Because an object file is extracted from a static library and linked if and only if that object file is needed to define an unresolved symbol reference that the linker is seeking to define at the point when the library is input. So in the corrected example:

    输入
  • p.o并无条件链接.
  • x成为未解决的引用.
  • 输入
  • libxz.a.
  • (libxz.a)x.o中找到了x的定义.
  • 提取并链接
  • (libxz.a)x.o.
  • x已解决.
  • 但是(libxz.a)x.o是指y.
  • y成为未解决的引用.
  • 输入
  • liby.a.
  • (liby.a)y.o中找到了y的定义.
  • 提取并链接
  • (liby.a)y.o.
  • y已解决.
  • 但是(liby.a)y.o是指z.
  • z成为未解决的引用.
  • 再次输入
  • libxz.a .
  • libxz.a(z.o)
  • 中找到了z的定义.
  • libxz.a(z.o)被提取并链接.
  • z已解决.
  • p.o is input and unconditionally linked.
  • x becomes an unresolved reference.
  • libxz.a is input.
  • A definition of x is found in (libxz.a)x.o.
  • (libxz.a)x.o is extracted and linked.
  • x is resolved.
  • But (libxz.a)x.o refers to y.
  • y becomes an unresolved reference.
  • liby.a is input.
  • A definition of y is found in (liby.a)y.o.
  • (liby.a)y.o is extracted and linked.
  • y is resolved.
  • But (liby.a)y.o refers to z.
  • z becomes an unresolved reference.
  • libxz.a is input again.
  • A definition of z is found in libxz.a(z.o)
  • libxz.a(z.o) is extracted and linked.
  • z is resolved.


[1]如-trace输出所示,严格来说,链接不是 完成直到(libx.a)x.o之后的所有样板都被链接, 但是每个C程序链接都是相同的样板.


[1] As the -trace output shows, strictly speaking the linkage was not done until all the boilerplate following (libx.a)x.o was also linked, but it's the same boilerplate for every C program linkage.

这篇关于与C中的静态库链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆