说一个共享库 A 依赖于另一个共享库 B 。
在这种情况下,当我构建 A 时,是否只需要 B 的头文件? 因为仅当我构建一些需要 A 的程序时才需要 B 。这样对吗?你能解释一下细节吗?
答案 0 :(得分:1)
你是对的。这是一个具体的例子。
a.h
#ifndef A_H
#define A_H
extern void aa(void);
#endif
交流
#include "a.h"
#include "b.h"
void aa(void)
{
bb();
}
b.h
#ifndef B_H
#define B_H
extern void bb(void);
#endif
b.c
#include "b.h"
#include <stdio.h>
void bb(void)
{
puts(__func__);
}
main.c
#include "a.h"
int main(void)
{
aa();
return 0;
}
我们制作了一个共享库liba.so
。首先编译PIC (Position Independent)
目标文件。
$ gcc -Wall -Wextra -o a.o -c -fPIC a.c
现在目标文件a.o
包含对bb
的未定义引用:
$ readelf -s a.o
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
...
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND bb
链接共享库:
$ gcc -shared -o liba.so a.o
现在共享库也对bb
进行了未定义的引用:
$ readelf --dyn-syms liba.so
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
...
2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND bb
...
很好。 链接器将创建一个包含未定义引用的共享库。
以相同的方式制作另一个共享库libb.so
:
$ gcc -Wall -Wextra -o b.o -c -fPIC b.c
$ gcc -shared -o libb.so b.o
此共享库定义了bb
:
$ readelf --dyn-syms libb.so
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
...
11: 000000000000060a 19 FUNC GLOBAL DEFAULT 12 bb
下一步尝试制作程序:
$ gcc -Wall -Wextra -o main.o -c main.c
$ gcc -o prog main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `aa'
collect2: error: ld returned 1 exit status
当我们既不链接liba
也不链接libb
时,就会发生这种情况。链接器将
不要创建包含未定义引用的程序。所以:
$ gcc -o prog main.o liba.so
liba.so: undefined reference to `bb'
collect2: error: ld returned 1 exit status
这是当我们与liba
而不是libb
链接时发生的情况。如果我们与
两者都一样,
$ gcc -o prog main.o liba.so libb.so
成功!但是请注意。如果我们交换库的顺序:
$ gcc -o prog main.o libb.so liba.so
liba.so: undefined reference to `bb'
collect2: error: ld returned 1 exit status
链接再次失败。
$ gcc -o prog libb.so liba.so main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `aa'
collect2: error: ld returned 1 exit status
链接器需要在其他之后看到一个库文件
库或依赖它的目标文件。因此,main.o
必须在liba
之前链接,
和liba
必须在libb
之前链接。
最后一个障碍。
$ gcc -o prog main.o liba.so libb.so
或等效地:
$ gcc -o prog main.o -L. -la -lb
成功链接程序prog
,但是:
$ ./prog
./prog: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
它不能运行。因为运行时加载程序仍然不知道在哪里寻找liba
或libb
加载程序知道prog
需要一些名为liba.so
和libb.so
的共享库,因为链接器已经写了
信息放入prog
:
$ readelf -d prog
Dynamic section at offset 0xda8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [liba.so]
0x0000000000000001 (NEEDED) Shared library: [libb.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
就像链接器一样,有directories where the loader will search for shared libraries by default。
它将在这些默认目录之一中找到libc.so.6
(GNU C库)。但是找不到我已经找到的liba.so
或libb.so
只是内置在其中任何一个中。
但是我可以通过链接告诉链接器为加载器提供缺少的信息 程序如下:
$ gcc -o prog main.o -L. -la -lb -Wl,-rpath=$PWD
使用-Wl,-rpath=$PWD
,我告诉gcc
传递(扩展)选项-rpath=$PWD
链接器,如果这样做,我们将看到:
$ readelf -d prog
Dynamic section at offset 0xd98 contains 30 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [liba.so]
0x0000000000000001 (NEEDED) Shared library: [libb.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/imk/develop/so/scrap1]
...
因此,现在加载器加载prog
时,就会看到RUNPATH=/home/imk/develop/so/scrap1
是非默认目录,在该目录中还应搜索任何NEEDED
共享库。 prog
然后可以成功加载其所有运行时依赖项:
$ ./prog
bb