构建共享库时,是否只需要依赖库的头文件?

Her*_*coa 2 build shared-libraries

假设共享库A依赖于另一个共享库B

在这种情况下,当我构建A 时,我是否只需要B的头文件?因为只有在我构建一些需要A 的程序时才需要B。这样对吗?你能解释一下细节吗?

Mik*_*han 5

你是对的。这是一个具体的说明。

#ifndef A_H
#define A_H

extern void aa(void);

#endif
Run Code Online (Sandbox Code Playgroud)

交流电

#include "a.h"
#include "b.h"

void aa(void)
{
    bb();
}
Run Code Online (Sandbox Code Playgroud)

#ifndef B_H
#define B_H

extern void bb(void);

#endif
Run Code Online (Sandbox Code Playgroud)

公元前

#include "b.h"
#include <stdio.h>

void bb(void)
{
    puts(__func__);
}
Run Code Online (Sandbox Code Playgroud)

主文件

#include "a.h"

int main(void)
{
    aa();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我们制作了一个共享库liba.so。首先编译一个PIC(Position Independent) 目标文件。

$ gcc -Wall -Wextra -o a.o -c -fPIC a.c
Run Code Online (Sandbox Code Playgroud)

现在目标文件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
Run Code Online (Sandbox Code Playgroud)

链接共享库:

$ gcc -shared -o liba.so a.o
Run Code Online (Sandbox Code Playgroud)

现在共享库还对 进行了未定义的引用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
   ...
Run Code Online (Sandbox Code Playgroud)

那没关系。链接器将创建一个包含未定义引用的共享库

libb.so以同样的方式制作另一个共享库:

$ gcc -Wall -Wextra -o b.o -c -fPIC b.c
$ gcc -shared -o libb.so b.o
Run Code Online (Sandbox Code Playgroud)

这个共享库定义了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
Run Code Online (Sandbox Code Playgroud)

接下来尝试制作一个程序:

$ 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
Run Code Online (Sandbox Code Playgroud)

这就是当我们既不链接liba也不链接libb. 链接器不会创建包含未定义引用的程序。所以:

$ gcc -o prog main.o liba.so
liba.so: undefined reference to `bb'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

这就是当我们链接到liba但不链接时会发生的事情libb。如果我们同时链接两者,例如:

$ gcc -o prog main.o liba.so libb.so
Run Code Online (Sandbox Code Playgroud)

成功!但要小心。如果我们交换库的顺序:

$ gcc -o prog main.o libb.so liba.so
liba.so: undefined reference to `bb'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

联动再次失败。也是如此:

$ 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
Run Code Online (Sandbox Code Playgroud)

链接器需要在依赖它的其他库或目标文件之后看到一个库。所以main.o一定要先联系liba,而且liba一定要先联系libb

还有最后一个障碍

$ gcc -o prog main.o liba.so libb.so
Run Code Online (Sandbox Code Playgroud)

或等效地:

$ gcc -o prog main.o -L. -la -lb
Run Code Online (Sandbox Code Playgroud)

成功链接程序prog,但是:

$ ./prog
./prog: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
Run Code Online (Sandbox Code Playgroud)

它不运行。因为运行时加载器仍然不知道去哪里寻找libalibb

加载器知道prog需要调用一些共享库liba.solibb.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]
 ...
Run Code Online (Sandbox Code Playgroud)

就像链接器一样,加载器会在默认情况下搜索共享库的目录。它将libc.so.6在这些默认目录之一中找到(GNU C 库)。但它不会找到liba.so或者libb.so说我只是建在其中任何一个。

但是我可以告诉链接器为加载器提供缺少的信息,而是通过像这样链接程序:

$ gcc -o prog main.o -L. -la -lb -Wl,-rpath=$PWD
Run Code Online (Sandbox Code Playgroud)

使用-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]
 ...
Run Code Online (Sandbox Code Playgroud)

所以现在当加载器加载时prog,它会看到RUNPATH=/home/imk/develop/so/scrap1 是一个非默认目录,它也应该搜索任何NEEDED共享库。prog 然后可以成功加载其所有运行时依赖项:

$ ./prog
bb
Run Code Online (Sandbox Code Playgroud)