乐趣区

使用gcc生成静态库和动态库

盘古开天辟地。我们写了个程序,想要给终端输出一些内容,不可避免地我们需要使用系统库,在我们写程序的过程中我们经常会碰到需要使用库的过程,无论是系统库还是第三方库,我们统称为 lib 库。

而库的链接分为两种,分别有静态库和动态库。

1. 静态库

静态库可以看作一堆的目标文件的集合,可能包含了很多函数的实现。在 linux 最常用的 C 语言静态库 libc 位于 /usr/lib/libc.a,它由成百上千个 C 语言程序,比如输入输出有printf.o,scanf.o,文件操作有fread.o,fwrite.o 等等。把这些零散的文件提供给使用者会造成不便,于是通常人们用 ar 压缩程序将这些目标文件压缩到一起,然后对这些文件进行编号和索引,最后形成了 libc.a 这个文件。

运行命令查看其中包含的内容,然后过滤查看fread

$  ar -t /usr/lib/x86_64-linux-gnu/libc.a | grep fread
iofread.o
__freading.o
__freadable.o
iofread_u.o
fread_chk.o
fread_u_chk.o

如果我们要在其中寻找 printf.o 文件可以使用命令

$ objdump -t /usr/lib/x86_64-linux-gnu/libc.a | grep printf.o
vfprintf.o:文件格式 elf64-x86-64
vprintf.o:文件格式 elf64-x86-64
reg-printf.o:文件格式 elf64-x86-64
fprintf.o:文件格式 elf64-x86-64
printf.o:文件格式 elf64-x86-64
snprintf.o:文件格式 elf64-x86-64
sprintf.o:文件格式 elf64-x86-64
asprintf.o:文件格式 elf64-x86-64
dprintf.o:文件格式 elf64-x86-64
vfwprintf.o:文件格式 elf64-x86-64
fxprintf.o:文件格式 elf64-x86-64
iovsprintf.o:文件格式 elf64-x86-64
fwprintf.o:文件格式 elf64-x86-64
swprintf.o:文件格式 elf64-x86-64
vwprintf.o:文件格式 elf64-x86-64
wprintf.o:文件格式 elf64-x86-64
vswprintf.o:文件格式 elf64-x86-64
vasprintf.o:文件格式 elf64-x86-64
iovdprintf.o:文件格式 elf64-x86-64
vsnprintf.o:文件格式 elf64-x86-64
obprintf.o:文件格式 elf64-x86-64

可以看到 printf.o 文件就在其中。

假如以我们写的 helloworld.c 程序为例

#include <stdio.h>

int main(int argc, char *argv[])
{printf("Hello,World!\n");
    return 0;
}

默认情况下直接运行

$ gcc helloworld.c -o helloworld

会进行动态链接,我们查看生成的文件大小

$ ls -l helloworld
-rwxr-xr-x 1 gnc gnc 8304 10 月 11 15:06 helloworld

可以看到大小为 8303bytes,我们使用选项-static 来使 gcc 进行静态链接:

$ gcc -static helloworld.c -o helloworld

查看大小

$ ls -l helloworld
-rwxr-xr-x 1 gnc gnc 844704 10 月 11 15:09 helloworld

可以看到大小显著变大。

2. 动态库

静态链接好处就是链接完成以后,运行程序不需要原来的支持库,但缺点是文件大小较大,并且更新困难。

所以出现了动态链接,也就是不要静态地进行链接,而是等到动态运行时再进行链接。动态链接的符号定位是发生在运行时首先进行地址空间分配,然后再来运行程序。

具体的链接过程也比较复杂,我们只需要知道如何来创建这种动态库即可。

3. gcc 创建静态和动态库

源文件add.c

/* add.h */
void setSummand(int summand);
int  add(int summand);

/* add.c */
#include <stdio.h>

int gSummand;


void setSummand(int summand) {gSummand = summand;}

int add(int summand) {return gSummand + summand;}

void __attribute__ ((constructor)) initLibrary(void) {
 //
 // Function that is called when the library is loaded
 //
    printf("Library is initialized\n"); 
    gSummand = 0;
}
void __attribute__ ((destructor)) cleanUpLibrary(void) {
 //
 // Function that is called when the library is »closed«.
 //
    printf("Library is exited\n"); 
}

源文件answer.c

/* answer.h */
int answer();

/* answer.c */
#include "add.h"

int answer() {setSummand(20);
  return add(22);  // Will return 42 (=20+22)

}

源文件main.c

#include <stdio.h>
#include "add.h"
#include "answer.h"

int main(int argc, char* argv[]) {setSummand(5);

  printf("5 + 7 = %d\n", add(7));

  printf("And the answer is: %d\n", answer());

  return 0;
}

然后我们创建目录 .bin/static./bin/shared

完成以后的目录结构如下

$ tree
.
├── add.c
├── add.h
├── answer.c
├── answer.h
├── bin
│   ├── shared
│   └── static
└── main.c

3 directories, 5 files

我们运行命令生成目标文件

$ gcc -c       main.c        -o bin/main.o

# 静态库
$ gcc -c       add.c    -o bin/static/add.o
$ gcc -c       answer.c -o bin/static/answer.o

# 动态库
gcc -c -fPIC add.c    -o bin/shared/add.o
gcc -c -fPIC answer.c -o bin/shared/answer.o

3.1 创建静态库

运行命令

$ ar rcs bin/static/libtq84.a bin/static/add.o bin/static/answer.o

将两个目标文件打包压缩为一个静态库文件。

静态链接

$ gcc bin/main.o -Lbin/static -ltq84 -o bin/static-main

运行

$ ./bin/static-main 
Library is initialized
5 + 7 = 12
And the answer is: 42
Library is exited

3.2 创建动态库

运行命令

$ gcc -shared bin/shared/add.o bin/shared/answer.o -o bin/shared/libtq84.so

然后进行动态链接

$ gcc  bin/main.o -Lbin/shared -ltq84 -o bin/use-shared-library

运行

$ ./bin/use-shared-library 
./bin/use-shared-library: error while loading shared libraries: libtq84.so: cannot open shared object file: No such file or directory

会提示找不到动态链接库,我们需要修改 LD_LIBRARY_PATH 变量来指向我们的动态库路径

export LD_LIBRARY_PATH=$(pwd)/bin/shared

然后再运行

./bin/use-shared-library 
Library is initialized
5 + 7 = 12
And the answer is: 42
Library is exited

就可以了,当然你也可以选择将该动态库放置到路径 /usr/lib 下,运行下面的命令

sudo mv bin/shared/libtq84.so /usr/lib
sudo chmod 755 /usr/lib/libtq84.so
退出移动版