Linux下用gcc进行静态编译


=Start=

缘由:

之前有记录过「静态库和动态库」、「GCC的常用编译选项」两篇文章,但说来惭愧,还一直没有真正实践过特定场景的静态编译,趁着周末有时间,再加上之前刚好有一个场景需要用到静态编译的功能,实际验证了一下Linux下gcc的静态编译的相关选项及其功能和注意事项。在此记录一下,方便以后参考。

正文:

参考解答:

之前有篇文章记录了如何使用OpenSSL库来实现字符串的base64编解码功能「Linux下C语言实现的base64加解密」,这里使用该文章中的代码进行演示说明:

$ gcc openssl_base64.c -lcrypto
#默认情况下,gcc会优先找动态库,找不到了再找静态库。所以上面的编译选项会去/usr/lib和/lib下(64位系统对应的是/usr/lib64和/lib64)查找 libcrypto.so ,如果你是自己手动编译安装的OpenSSL,动态库不在上述目录中,可以使用-L选项来显示指定库搜索路径。

&

# gcc openssl_base64.c -static -lcrypto -lssl -o oabc
/usr/bin/ld: cannot find -lc
collect2: ld returned 1 exit status
#
# gcc openssl_base64.c -Wl,-Bstatic -lcrypto -lssl -Wl,-Bdynamic -o oabc -Wl,--no-as-needed -ldl -lzlib
/usr/bin/ld: cannot find -lzlib
collect2: ld returned 1 exit status
#
# gcc openssl_base64.c -Wl,-Bstatic -lcrypto -lssl -Wl,-Bdynamic -o oabc -Wl,--no-as-needed -ldl -lz
#
# ldd oabc
	linux-vdso.so.1 =>  (0x00007fff42dff000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007ffbe717b000)
	libz.so.1 => /lib64/libz.so.1 (0x00007ffbe6f65000)
	libc.so.6 => /lib64/libc.so.6 (0x00007ffbe6bd0000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ffbe738c000)
#
# rpm -qf /lib64/libz.so.1
zlib-1.2.3-29.el6.x86_64
# yum search zlib | grep --color "static"
mingw32-zlib-static.noarch : Static libraries for mingw32-zlib development.
mingw64-zlib-static.noarch : Static libraries for mingw64-zlib development
zlib-static.x86_64 : Static libraries for Zlib development
#
# yum install zlib-static -y
#
# rpm -qf /lib64/libdl.so.2
glibc-2.12-1.149.el6_6.7.x86_64
# rpm -qf /lib64/libz.so.1
zlib-1.2.3-29.el6.x86_64
# rpm -qf /lib64/libc.so.6
glibc-2.12-1.149.el6_6.7.x86_64
#
# gcc openssl_base64.c -Wl,-Bstatic -lcrypto -lssl -lz -Wl,-Bdynamic -o oabc -Wl,--no-as-needed -ldl
# ldd oabc
	linux-vdso.so.1 =>  (0x00007fffe27ff000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f765c3fc000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f765c068000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f765c60d000)
#
# gcc openssl_base64.c -static -lcrypto -lssl -lz -ldl -o oabc
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/libcrypto.a(fips.o): In function `verify_checksums':
(.text+0x62b): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
#
# echo $?
0
# gcc openssl_base64.c -static -lcrypto -lssl -lz -ldl -lc -o oabc
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/libcrypto.a(fips.o): In function `verify_checksums':
(.text+0x62b): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

# ldd oabc
	not a dynamic executable
#
# readelf -d oabc

There is no dynamic section in this file.
#

0、静态编译之前,需要知道可执行程序依赖哪些库;
一、先用常规的gcc选项将源代码动态编译成一个可执行文件(假设可执行文件名为 a.out);
二、然后用ldd/readelf命令查看该可执行文件依赖哪些共享库;
1、要想进行静态编译,还需要系统安装有对应的静态库(.a结尾);
#这里以zlib库为例说明该如何用yum安装对应的静态库
# yum search zlib | grep --color "static"
mingw32-zlib-static.noarch : Static libraries for mingw32-zlib development.
mingw64-zlib-static.noarch : Static libraries for mingw64-zlib development
zlib-static.x86_64 : Static libraries for Zlib development
#
# yum install zlib-static -y
# find / -type f -iname "libz.*"
/lib64/libz.so.1.2.3
/usr/lib64/libz.a #刚刚安装的静态库文件
2、静态编译需要为gcc指定一些特定选项;
如果直接使用 -static 或者 -dynamic 会使所有库都使用这种形式链接,显示不是我们想要的。

当我们想要为某些库链接静态库,某些链接静态库,其余的按默认链接,则需要下面的样子:
-L/path/to/lib/ -Wl,-Bstatic -l<static_lib>   #指定静态库
-L/path/to/lib/ -Wl,-Bdynamic -l<dynamic_lib> #指定动态库
-Wl,-Bdynamic #恢复默认设置

==

gcc使用-Wl传递连接器参数,ld使用-Bdynamic强制连接动态库,-Bstatic强制连接静态库。所以部分静态,部分动态连接这么写:
# gcc ... -Wl,-Bstatic -l<your-static-lib> -Wl,-Bdynamic -l<your-dynamic-lib> ...

举个例子,你想动态连接libA.so同时静态连接libB.a,(先保证你的连接路径-L里面能找到对应的静态或者动态库),这么写:
# gcc ... -Wl,-Bstatic -lA -Wl,-Bdynamic -lB ...
3、如何定位手动编译的静态库文件;
Linux下链接库时使用-L来指定路径,使用-l来指定库名。
4、验证编译后的可执行程序是否满足要求;
# ldd oabc
	not a dynamic executable
#
# readelf -d oabc

There is no dynamic section in this file.
#
# readelf -d oabc | grep --color "NEEDED"
#
参考链接:

=END=


《 “Linux下用gcc进行静态编译” 》 有 3 条评论

  1. 宋宝华: 关于Linux编译优化几个必须掌握的姿势
    https://mp.weixin.qq.com/s/CIYzI6SAWcHWTD6z3PvOuQ
    `
    下面给几条实践指南:
    1、尽量不要尝试用O0去编译内核,这不符合真实的工程实践,也不太被主流Linux社区所支持;内核依赖O2/Os去做较多的优化;
    2、追求你的代码在O2的情况下,仍然是正确的,代码要经得起编译优化;比如O0工作正常,而O2不正常,应该尽可能从自身找原因,分析汇编;
    3、如果在全局优化的情况下,想针对某个局部避免优化,可以尝试用noinline,__attribute__((optimize(“O0”)))等进行外科手术式地调整。
    `

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注