在 Linux 开发中,你是否遇到过这样的窘境:代码编译一切正常,但运行时报错找不到某个库文件?这背后的根源,往往在于你对动态库静态库的理解还停留在表面。本文将从零开始,带你深入剖析 Linux 库的底层原理、封装流程与使用技巧,彻底告别“编译通过、运行失败”的尴尬。

一、库的本质:为什么我们需要它?

库,本质上就是预先编译好的、可复用的二进制代码集合。无论是 C、C++ 还是 Python、Java,任何语言的生态都离不开库的支持。在 Linux 中,库分为两种形态:

  • 静态库:格式为 .a(Linux) / .lib(Windows),在编译链接时被完整复制进可执行文件。
  • 动态库:格式为 .so(Linux) / .dll(Windows),在程序运行时才被加载到内存。

Linux 库的命名遵循统一规范:静态库为 lib<库名>.a,如 libc.alibstdc++.a;动态库为 lib<库名>.so<版本号>,如 libc.so.6libstdc++.so.6.0.30。动态库通常通过软链接管理多版本,例如:

libstdc++.so.6 -> libstdc++.so.6.0.30

以 Ubuntu 和 CentOS 为例,常见的 C/C++ 库文件如下:

# C 语言动态库
xqq@ubuntu-server:~$ ls -l /lib/x86_64-linux-gnu/libc.so.6
-rwxr-xr-x 1 root root 2220400 Jan 30 16:20 /lib/x86_64-linux-gnu/libc.so.6
# C 语言静态库
xqq@ubuntu-server:~$ ls -l /usr/lib/x86_64-linux-gnu/libc.a
-rw-r--r-- 1 root root 6028186 Jan 30 16:20 /usr/lib/x86_64-linux-gnu/libc.a
# C++ 动态库
xqq@ubuntu-server:~$ ls -l /usr/lib/x86_64-linux-gnu/libstdc++*
lrwxrwxrwx 1 root root      19 Dec 20 05:52 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 -> libstdc++.so.6.0.30
-rw-r--r-- 1 root root 2260296 Dec 20 05:52 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30
# C++ 静态库
xqq@ubuntu-server:~$ ls -l /usr/lib/gcc/x86_64-linux-gnu/11/libstdc++.a
-rw-r--r-- 1 root root 6018836 Dec 18 15:31 /usr/lib/gcc/x86_64-linux-gnu/11/libstdc++.a
# C 语言动态库
$ ls /lib64/libc-2.17.so -l
-rwxr-xr-x 1 root root 2156592 Jun 4 23:05 /lib64/libc-2.17.so
# C 语言静态库
$ ls /lib64/libc.a -l
-rw-r--r-- 1 root root 5105516 Jun 4 23:05 /lib64/libc.a
# C++ 动态库
$ ls /lib64/libstdc++.so.6 -l
lrwxrwxrwx 1 root root 19 Sep 18 20:59 /lib64/libstdc++.so.6 -> libstdc++.so.6.0.19
# C++ 静态库
$ ls /usr/lib/gcc/x86_64-redhat-linux/4.8.2/libstdc++.a -l
-rw-r--r-- 1 root root 2932366 Sep 30 2020 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/libstdc++.a

为什么库如此重要?

  • 提升开发效率:不用重复造轮子,直接调用现成功能。
  • 隐藏源码:以二进制分发,保护知识产权,用户只能通过头文件(.h)了解接口。
  • 标准化与协作:经过测试的库能减少 Bug,方便团队分工。
  • 模块化设计:独立升级、独立维护,让系统更灵活。

二、静态库 vs 动态库:核心差异

静态库(.a / .lib在编译链接时就将库代码合并进可执行文件,因此运行时不再依赖外部库。它的优点是部署简单,缺点是体积大、库更新后需重新编译。默认情况下,编译器优先使用动态库(.so),只有找不到时才会回退到静态库;你也可以通过 gcc-static 选项强制静态链接。

动态库(.so / .dll则是在程序启动时才由操作系统加载库代码。可执行文件中只包含函数入口地址的表,真正的机器码在运行时从磁盘复制到内存,这个过程称为动态链接。动态库的优势是共享内存、节省空间、升级方便,但部署时必须保证目标系统有对应版本的库,否则就会“编译通过、运行失败”。

三、静态库的封装与使用

我们以自定义的 mystdiomystring 为例,模拟库的制作过程。⚠️ 注意:库中不能包含 main 函数,否则会与用户程序的入口冲突。

3.1 静态库的本质与打包

静态库本质上是 .o 文件的聚合归档。编译器先将每个源文件(.c)编译成目标文件(.o),再用 ar 命令打包成库。头文件(.h)作为接口文档原样提供,而二进制目标文件(.o)则被归档。

ar [options] archive-file object-files

常用选项包括

选项含义
创建库文件,如果库已存在则会被覆盖
向库中添加  文件,如果已存在则替换
列出库文件中包含的  文件列表
在执行过程中显示详细信息
。有人会问:能否用 tar 打包?理论上可以,但用户需要手动解压,而 ar 生成的归档可直接被链接器识别,因此更优。

第一步,编译生成目标文件:

xqq@ubuntu-server:~/linux/module2$ ls
mystdio.c  mystdio.h  mystring.c  mystring.h
xqq@ubuntu-server:~/linux/module2$ gcc -c mystdio.c -o mystdio.o
xqq@ubuntu-server:~/linux/module2$ gcc -c mystring.c -o mystring.o
xqq@ubuntu-server:~/linux/module2$ ls
mystdio.c  mystdio.h  mystdio.o  mystring.c  mystring.h  mystring.o

第二步,打包成静态库:

xqq@ubuntu-server:~/linux/module2$ ar -rc libmyc.a *.o
xqq@ubuntu-server:~/linux/module2$ ll
total 40
drwxrwxr-x 2 xqq xqq 4096 May 24 18:40 ./
drwxrwxr-x 7 xqq xqq 4096 May 22 10:48 ../
-rw-rw-r-- 1 xqq xqq 5346 May 24 18:40 libmyc.a   # 实际库名就是 myc
# ......

库文件命名规则:,这里的库名是 ,使用时用  指定。

3.2 静态库的使用与链接

用户代码编译成目标文件后,直接与静态库链接会报错,因为链接器不知道库的位置:

xqq@ubuntu-server:~/linux/module2$ gcc -o usercode.exe usercode.o
/usr/bin/ld: usercode.o: in function `main':
usercode.c:(.text+0x2c): undefined reference to `MyFopen'
/usr/bin/ld: usercode.c:(.text+0x77): undefined reference to `MyFwrite'
/usr/bin/ld: usercode.c:(.text+0xa2): undefined reference to `MyFFlush'
/usr/bin/ld: usercode.c:(.text+0xc5): undefined reference to `MyFclose'
collect2: error: ld returned 1 exit status

此时需要用到 -l(指定库名,去掉前缀和后缀)和 -L(指定搜索路径)。默认搜索路径包括系统目录(/usr/include/usr/local/include)和 GCC 内置路径,但我们的库不在其中,必须显式指定:

xqq@ubuntu-server:~/linux/module2$ gcc -o usercode.exe usercode.o -lmyc
/usr/bin/ld: cannot find -lmyc: No such file or directory
collect2: error: ld returned 1 exit status
xqq@ubuntu-server:~/linux/module2$ gcc -o usercode.exe usercode.o -L . -l myc
# (选项可以加空格也可以不加:-L. -lmyc 同样有效)
xqq@ubuntu-server:~/linux/module2$ ls
libmyc.a   mystdio.h  mystring.c  mystring.o  usercode.exe
mystdio.c  mystdio.o  mystring.h  usercode.c  usercode.o
xqq@ubuntu-server:~/linux/module2$ ./usercode.exe
缓冲区:hello world
缓冲区:hello world
# ......程序正常运行

3.3 标准化发布与安装

为了方便分发,通常将头文件放入 include/ 目录,库文件放入 lib/(或 mylib/)目录:

xqq@ubuntu-server:~/linux/module2$ mv *.h lib/include
xqq@ubuntu-server:~/linux/module2$ mv *.a lib/mylib
xqq@ubuntu-server:~/linux/module2$ tree lib
lib
├── include
│   ├── mystdio.h
│   └── mystring.h
└── mylib
    └── libmyc.a
2 directories, 3 files

然后打包成 SDK 压缩包:

xqq@ubuntu-server:~/linux/module2$ tar czf lib.tgz lib
xqq@ubuntu-server:~/linux/module2$ ll
total 40
drwxrwxr-x 3 xqq xqq  4096 May 24 19:31 ./
drwxrwxr-x 7 xqq xqq  4096 May 22 10:48 ../
drwxrwxr-x 4 xqq xqq  4096 May 24 19:23 lib/
-rw-rw-r-- 1 xqq xqq  2167 May 24 19:27 lib.tgz
-rw-rw-r-- 1 xqq xqq   387 May 24 19:31 usercode.c
-rw-rw-r-- 1 xqq xqq  2208 May 24 18:47 usercode.o
-rwxrwxr-x 1 xqq xqq 16672 May 24 19:18 usercode.exe*

模拟用户“张三”拿到包后,编译时需用 -I(大写 i)指定头文件路径:

# 张三下载并解压库
xqq@ubuntu-server:~/linux/module2$ mkdir zhansan
xqq@ubuntu-server:~/linux/module2$ cd zhansan
xqq@ubuntu-server:~/linux/module2/zhansan$ cp ../lib.tgz .
xqq@ubuntu-server:~/linux/module2/zhansan$ tar xzf lib.tgz
xqq@ubuntu-server:~/linux/module2/zhansan$ rm lib.tgz
xqq@ubuntu-server:~/linux/module2/zhansan$ tree .
.
└── lib
    ├── include
    │   ├── mystdio.h
    │   └── mystring.h
    └── mylib
        └── libmyc.a
3 directories, 3 files
# 张三写好自己的代码
xqq@ubuntu-server:~/linux/module2/zhansan$ cp ../usercode.c .
# 尝试编译
xqq@ubuntu-server:~/linux/module2/zhansan$ gcc -c usercode.c -o usercode.o
usercode.c:1:9: fatal error: mystdio.h: No such file or directory
    1 | #include"mystdio.h"
      |         ^~~~~~~~~~~
compilation terminated.
xqq@ubuntu-server:~/linux/module2/zhansan$ gcc -c usercode.c -o usercode.o -I ./lib/include

也可以一步到位:

xqq@ubuntu-server:~/linux/module2/zhansan$ gcc usercode.c -o usercode.exe -I ./lib/include -L ./lib/mylib -lmyc
xqq@ubuntu-server:~/linux/module2/zhansan$ ./usercode.exe
缓冲区:hello world
缓冲区:hello world
# ......程序正常运行

⚠️ 不要在代码中硬编码路径(如 #include "./lib/include/mystdio.h"),这会破坏可移植性。正确做法是代码中写 #include "mystdio.h",编译时用 -I 指定路径。

若想免去每次编译的麻烦,可将库“安装”到系统默认目录:

sudo cp lib/include/*.h /usr/local/include/
sudo cp lib/mylib/libmyc.a /usr/local/lib/

之后只需指定库名即可:

gcc usercode.c -o usercode.exe -lmyc

使用 Makefile 自动化整个流程:

libmyc.a: mystring.o mystdio.o
	ar -rc $@ $^
mystring.o: mystring.c
	gcc -c $^ -o $@
mystdio.o: mystdio.c
	gcc -c $^ -o $@
.PHONY: output
output:
	mkdir -p lib/include
	mkdir -p lib/mylib
	cp *.h ./lib/include
	cp libmyc.a ./lib/mylib
	tar czf lib.tgz lib
.PHONY: clean
clean:
	rm -rf *.o *.a *.tgz lib
xqq@ubuntu-server:~/linux/module2/mkStaticLibrary$ make
gcc -c mystring.c -o mystring.o
gcc -c mystdio.c -o mystdio.o
ar -rc libmyc.a mystring.o mystdio.o
xqq@ubuntu-server:~/linux/module2/mkStaticLibrary$ make output
mkdir -p lib/include
mkdir -p lib/mylib
cp *.h ./lib/include
cp libmyc.a ./lib/mylib
tar czf lib.tgz lib
xqq@ubuntu-server:~/linux/module2/mkStaticLibrary$ ls
lib/  lib.tgz  libmyc.a  Makefile  mystdio.c  mystdio.h  mystdio.o  mystring.c  mystring.h  mystring.o

四、动态库的封装与使用

动态库的封装流程与静态库类似,但更常用,因为它更节省资源。核心区别在于:动态库在编译时只需要头文件,可执行文件保存的是符号引用;运行时才需要动态库文件。

4.1 生成位置无关代码

第一步,用 -fPIC 选项编译生成 .o 文件。PIC(Position Independent Code)让代码可以在内存任意位置执行,这是动态库的关键。

xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ ls
mystdio.c  mystdio.h  mystring.c  mystring.h
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ gcc -fPIC -c *.c
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ ls
mystdio.c  mystdio.h  mystdio.o  mystring.c  mystring.h  mystring.o

第二步,用 -shared 打包。注意必须加上 -shared 选项,否则编译器会尝试生成可执行文件而报错:

# 错误示范:不加 -shared
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ gcc -o libmyc.so *.o
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o:
in function `_start':
(.text+0x1b): undefined reference to `main'
collect2: error: ld returned 1 exit status
# 正确做法:加上 -shared
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ gcc -shared -o libmyc.so *.o
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ ls
libmyc.so  mystdio.c  mystdio.h  mystdio.o  mystring.c  mystring.h  mystring.o

file 验证文件类型:

xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ file libmyc.so
libmyc.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, BuildID[sha1]=e89fe9bd038b4b00ee16d6281b7cc13498714d6a,
not stripped

看到 shared object 说明是动态库。Makefile 构建如下:

libmyc.so: mystring.o mystdio.o
	gcc -shared $^ -o $@
mystring.o: mystring.c
	gcc -fPIC -c $^ -o $@
mystdio.o: mystdio.c
	gcc -fPIC -c $^ -o $@
.PHONY: output
output:
	mkdir -p lib/include
	mkdir -p lib/mylib
	cp *.h ./lib/include
	cp libmyc.so ./lib/mylib
	tar czf lib.tgz lib
.PHONY: clean
clean:
	rm -rf *.o *.so *.tgz lib
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ make
gcc -fPIC -c mystring.c -o mystring.o
gcc -fPIC -c mystdio.c -o mystdio.o
gcc -shared mystring.o mystdio.o -o libmyc.so
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ make output
mkdir -p lib/include
mkdir -p lib/mylib
cp *.h ./lib/include
cp libmyc.so ./lib/mylib
tar czf lib.tgz lib
xqq@ubuntu-server:~/linux/module2/mkDynamicLibrary$ ls
lib/  lib.tgz  libmyc.so*  Makefile  mystdio.c  mystdio.h  mystdio.o
mystring.c  mystring.h  mystring.o

五、经典问题:编译通过,运行失败

模拟用户“李四”拿到动态库 SDK 后编译程序:

xqq@ubuntu-server:~/linux/module2$ mkdir lisi
xqq@ubuntu-server:~/linux/module2$ cp ./mkDynamicLibrary/lib.tgz ./lisi
xqq@ubuntu-server:~/linux/module2$ cd lisi
xqq@ubuntu-server:~/linux/module2/lisi$ tar xzf lib.tgz
xqq@ubuntu-server:~/linux/module2/lisi$ tree lib
lib
├── include
│   ├── mystdio.h
│   └── mystring.h
└── mylib
    └── libmyc.so
2 directories, 3 files
xqq@ubuntu-server:~/linux/module2/lisi$ cp ../usercode.c .
xqq@ubuntu-server:~/linux/module2/lisi$ gcc usercode.c -o usercode.exe \
    -I ./lib/include -L ./lib/mylib -lmyc
xqq@ubuntu-server:~/linux/module2/lisi$ ls
lib/  lib.tgz  usercode.c  usercode.exe*

编译成功,但运行时报错:

xqq@ubuntu-server:~/linux/module2/lisi$ ./usercode.exe
./usercode.exe: error while loading shared libraries: libmyc.so:
cannot open shared object file: No such file or directory

ldd 检查依赖:

xqq@ubuntu-server:~/linux/module2/lisi$ ldd usercode.exe
    linux-vdso.so.1 (0x00007ffcf8b4b000)
    libmyc.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5195696000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f51958cf000)

程序依赖 libmyc.so,但系统找不到它。原因在于动态库的搜索路径与静态库不同,它由 环境变量 not found 控制。解决方案有三种:

  1. 设置环境变量:export LD_LIBRARY_PATH=你的库路径:$LD_LIBRARY_PATH
  2. 将库安装到系统默认路径(如 /usr/lib)
  3. 使用 ldconfig 配置缓存
# 直接复制到 /lib64(需要 sudo)
xqq@ubuntu-server:~/linux/module2/lisi$ sudo cp ./lib/mylib/libmyc.so /lib64
xqq@ubuntu-server:~/linux/module2/lisi$ sudo ldconfig
xqq@ubuntu-server:~/linux/module2/lisi$ ./usercode.exe
# 可能仍然报错 not found!
# 排查:检查缓存中是否有我们的库
xqq@ubuntu-server:~/linux/module2/lisi$ ldconfig -p | grep libmyc
# 没有输出,说明 ldconfig 没有扫到
# 解决:强制扫描指定目录
xqq@ubuntu-server:~/linux/module2/lisi$ sudo ldconfig /lib64
# 再次验证
xqq@ubuntu-server:~/linux/module2/lisi$ ldd ./usercode.exe
    libmyc.so => /lib64/libmyc.so (0x00007f1455bbc000)
    # 找到了!
xqq@ubuntu-server:~/linux/module2/lisi$ ./usercode.exe
缓冲区:hello world
缓冲区:hello world
# 正常运行
xqq@ubuntu-server:~/linux/module2/lisi$ sudo ln -s \
    /home/xqq/linux/module2/lisi/lib/mylib/libmyc.so /lib64/libmyc.so
xqq@ubuntu-server:~/linux/module2/lisi$ ll /lib64/libmyc.so
lrwxrwxrwx 1 root root 48 May 25 00:45 /lib64/libmyc.so ->
    /home/xqq/linux/module2/lisi/lib/mylib/libmyc.so*
xqq@ubuntu-server:~/linux/module2/lisi$ ldd usercode.exe
    libmyc.so => /lib64/libmyc.so (0x00007f98586fc000)
xqq@ubuntu-server:~/linux/module2/lisi$ ./usercode.exe
缓冲区:hello world
缓冲区:hello world
xqq@ubuntu-server:~/linux/module2/lisi$ echo $LD_LIBRARY_PATH
# 输出空行,说明未设置
xqq@ubuntu-server:~/linux/module2/lisi$ export LD_LIBRARY_PATH=\
/home/xqq/linux/module2/lisi/lib/mylib:$LD_LIBRARY_PATH
xqq@ubuntu-server:~/linux/module2/lisi$ echo $LD_LIBRARY_PATH
/home/xqq/linux/module2/lisi/lib/mylib:
xqq@ubuntu-server:~/linux/module2/lisi$ ldd usercode.exe
    libmyc.so => /home/xqq/linux/module2/lisi/lib/mylib/libmyc.so (0x00007fd2582a1000)
xqq@ubuntu-server:~/linux/module2/lisi$ ./usercode.exe
缓冲区:hello world
缓冲区:hello world
echo 'export LD_LIBRARY_PATH=/your/path:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
xqq@ubuntu-server:~/linux/module2/lisi$ sudo echo "/home/xqq/linux/module2/lisi/lib/mylib" \
    > /etc/ld.so.conf.d/test_config.conf
-bash: /etc/ld.so.conf.d/test_config.conf: Permission denied
echo "/path/to/lib" | sudo tee /etc/ld.so.conf.d/test_config.conf
su
echo "/path/to/lib" > /etc/ld.so.conf.d/test_config.conf
root@ubuntu-server:/home/xqq/linux/module2/lisi# echo \
    "/home/xqq/linux/module2/lisi/lib/mylib" > /etc/ld.so.conf.d/test_config.conf
root@ubuntu-server:/home/xqq/linux/module2/lisi# cat /etc/ld.so.conf.d/test_config.conf
/home/xqq/linux/module2/lisi/lib/mylib
# 更新缓存前,仍然找不到
root@ubuntu-server:/home/xqq/linux/module2/lisi# ldd usercode.exe
    libmyc.so => not found
# 执行 ldconfig 更新缓存
root@ubuntu-server:/home/xqq/linux/module2/lisi# ldconfig
# 再次查看,找到了
root@ubuntu-server:/home/xqq/linux/module2/lisi# ldd usercode.exe
    libmyc.so => /home/xqq/linux/module2/lisi/lib/mylib/libmyc.so (0x00007f4d5582a000)
root@ubuntu-server:/home/xqq/linux/module2/lisi# ./usercode.exe
缓冲区:hello world
缓冲区:hello world
xqq@ubuntu-server:~/linux/module2/zhansan$ tree .
.
├── lib
│   ├── include
│   │   ├── mystdio.h
│   │   └── mystring.h
│   └── mylib
│       ├── libmyc.a      # 静态库
│       └── libmyc.so     # 动态库
└── usercode.c
xqq@ubuntu-server:~/linux/module2/zhansan$ gcc usercode.c -o usercode.exe \
    -I ./lib/include/ -L ./lib/mylib/ -lmyc
xqq@ubuntu-server:~/linux/module2/zhansan$ file usercode.exe
usercode.exe: ELF 64-bit LSB pie executable, ... dynamically linked ...
# 动态链接
xqq@ubuntu-server:~/linux/module2/zhansan$ gcc usercode.c -o usercode.exe \
    -I ./lib/include/ -L ./lib/mylib/ -lmyc -static
xqq@ubuntu-server:~/linux/module2/zhansan$ file usercode.exe
usercode.exe: ELF 64-bit LSB executable, ... statically linked ...
xqq@ubuntu-server:~/linux/module2/zhansan$ ldd usercode.exe
    not a dynamic executable
[AFFILIATE_SLOT_1]

六、动静态库对比与选型建议

下表总结了两种库的关键差异:

推荐做法:将第三方库放在 ,这是约定给用户自行安装的第三方库存放的位置,通常已在  的默认扫描路径中(通过  配置),直接  即可生效,省去排查的麻烦。

补充:/ 默认使用动态库,在 Linux 中默认优先安装的也是动态库。如果只想对某一个库使用静态链接而其他库保持动态链接,可以用  来精确控制。

在实际开发中,选择哪种库取决于场景:

  • 追求部署便捷:选静态库,免去运行时依赖烦恼。
  • 追求资源节省:选动态库,适合内存受限或大型系统。
  • 频繁升级:动态库只需替换文件,无需重编程序。
  • 跨平台分发:动态库需考虑目标系统的兼容性,静态库则更安全。

无论你使用 C++、Java、Python 还是 TypeScript,理解库的加载机制都是成为高级工程师的必经之路。例如,Java 的 JNI 库、Python 的 C 扩展、Node.js 的 native addon,都遵循类似的动态链接原理。

七、总结

本文从库的本质出发,详细剖析了静态库与动态库的封装、使用及常见问题。核心要点:静态库在编译期绑定,动态库在运行期加载。理解这一点,你就能轻松解决“编译通过、运行失败”的难题。掌握库的原理,不仅能提升你的调试能力,还能让你在设计模块化系统时更加游刃有余。希望这篇文章能成为你 Linux 开发路上的坚实基石。

[AFFILIATE_SLOT_2] -c-r.o-t.o-vlib<库名>.amyc-lmyc/usr/local/libldconfig/etc/ld.so.conf.d/libc.confsudo ldconfiggccg++-Wl,-Bstatic -lxxx -Wl,-Bdynamic