编译链接相关总结

一、SO 查找路径

运行时,Linux动态链接库的搜索路径按优先级排序为:
1、编译目标代码时 ”-Wl,-rpath,” 指定的动态库搜索路径(当指定多个动态库搜索路径时,路径之间用冒号”:”分隔。);
2、环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径;
3、配置文件 /etc/ld.so.conf 中指定的动态库搜索路径;
4、默认的动态库搜索路径 /lib,如果是64位系统还包括 /lib64;
5、默认的动态库搜索路径 /usr/lib,如果是64位系统还包括 /usr/lib64;
注意: /usr/local/lib 和 /usr/local/lib64 不在标准路径之列。

LD_PRELOAD 预加载库

两种方式设置 rpath

# 两种方式设置 rpath
# 1、在可执行文件中添加动态库绝对路径到 DT_RPATH
gcc -Wl,-R/$LIBRARY_PATH -l$LIB_NAME
# 2、设置 LD_RUN_PATH 环境变量
export LD_RUN_PATH=$LIBRARY_PATH:$LD_RUN_PATH

# runpath  在设置了runpath 后,rpath 就会被加载器忽略了。
gcc -Wl,-R$LIBRARY_PATH -Wl,--enable-new-dtags -l$LIB_NAME

1、设置了 RUNPATH 的情况:
LD_PRELOAD -> LD__LIBRARY__PATH -> runpath -> ld.so.cache -> /lib 和 /usr/lib
2、没有设置 RUNPATH的情况:
rpath -> LD__LIBRARY__PATH -> ld.so.cache -> /lib 和 /usr/lib


二、gcc相关编译参数

2.1 静态检查

-pedantic     //检查代码是否符合 ANSI/ISO 标准:
-Wall         //输出警告信息
-Wextra       //输出更多的警告信息
-Weffc++      //检查是否符合 《Effective C++》这本书中提到的标准
-w            //关闭所有警告

2.2 调试与优化

-g           //在编译时生成原生格式的调试符号信息,可以使用 gdb 或 ddx 等调试器调试。-g 分为三个级别,默认为 -g2,其中 -g3 除包含 -g2 中的所有调试信息外,还包含源代码中定义的宏。
-ggdb          //在编译时生成 gdb 专用格式的调试符号信息,信息更为丰富,但只能使用 gdb 调试,而不能使用其它调试器。级别设定与 -g 基本相同。
-gdwarf        //如果升级 GCC 之后,发现 GDB 不能用了,那么在编译的时候加上 -gdwarf 选项试一下,如果还不行,再使用 -gdwarf-2 试试。
-p           //将剖析(Profiling)信息加入到最终生成的二进制代码中,生成可以被通用剖析工具(Prof)能够识别的统计信息,它对于找出程序瓶颈很有帮助。生成后的可执行文件要运行一次,才能生成剖析文件,默认文件名是 gmon.out。
-pg             //生成只有 GNU 剖析工具(Gprof)才能识别的统计信息。
-save-temps      //保存编译过程中生成的一系列中间文件。
-On           //n 可以为 0~3,默认为 1,数字越大优化越高,一般使用 -O2 可以在优化长度、编译时间和代码大小之间取得较好平衡,开发调试建议使用 -O0
-Os          //相当于 -O2.5,使用了所有 -O2 的优化选项,但却没有使用增加大小。也就是说,相对于 -O2,能减少一点可执行文件的大小。
-s          //从执行文件中删除符号表与重定向信息,以减少执行文件的大小。跟直接编译之后,使用 strip 命令减小文件大小效果一致。

2.3 编译器扩展

这时候用gcc来编译C++程序会报缺少一些C++标准库,这时候可以使用 gcc -lstdc++ 或直接使用 g++ 命令。

解决 GDB 的问题
升级 gcc 之后,编译代码加上 -g 选项,在使用 gdb 调试时,可能会出现 ** Missing separate debuginfos, use: debuginfo-install libgcc-4.4.7-3.el6.x86_64** 这样的错误,是因为只加 -g 选项,此时已经无法生成符号表了。解决方案有两种:
◆ 编译时加上 -gdwarf 选项试一下,如果还不行,再使用 -gdwarf-2 试试。
◆ 升级 gdb 版本,可升级至 7.6 ,正常的 ./configure && make && make install 之后,就可以正常使用了。如果 make 过程中出现 configure: error: no termcap library found 的错误,可以 yum install ncurses-devel 来解决。

预编译头:
gcc -x c+±header -c stdc++.h -o stdc++.h.gch -std=c++0x

6、当执行一个可执行文件时,如果报类似 `GLIBCXX_3.4.15’ not found 的错误,是因为编译这个可执行文件的 GCC版本和当前环境中的GCC版本不同,可以选择升级GCC版本,如下网页:
http://blog.csdn.net/davidwang9527/article/details/19197511

7、如果只是将GCC4.8.2编译的可执行文件配置到一台LINUX机器上运行,并不需要安装GCC4.8.2,只需要将原机器上的 /usr/local/gcc-4.8.2/lib64/libstdc++.so.6 文件复制到新机器的 /usr/lib64/libstdc++.so.6 即可(注意备份原文件)

2.4 链接不以lib前缀的so

-l
如-ldl,链接的就是libdl.so

-L 后面直接+ so即可
如-L /usr/lib64/libdl.so

2.5 -g选项与-rdynamic选项的差别

这主要是对可执行程序而言的,而编译动态库时,即使没有rdynamic选项,默认也会将非静态函数放入动态符号表中
默认情况下,可执行程序(非动态库)文件内我们定义的非静态函数,是不放到动态符号表中的,链接时只有加上-rdynamic才能将所有非静态函数加到动态符号表中。

1、-g选项新添加的是调试信息(一系列.debug_xxx段),被相关调试工具,比如gdb使用,可以被strip掉。
2、-rdynamic选项新添加的是动态连接符号信息,用于动态连接功能,比如dlopen()系列函数、backtrace()系列函数使用,不能被strip掉,即强制strip将导致程序无法执行:

[root@www c]# ./t.rd
test[root@www c]# strip -R .dynsym t.rd
[root@www c]# ./t.rd
./t.rd: relocation error: ./t.rd: symbol , version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
[root@www c]#

3、.symtab表在程序加载时会被加载器 丢弃 ,gdb等调试工具由于可以直接访问到磁盘上的二进制程序文件:

[root@www c]# gdb t.g -q
Reading symbols from /home/work/dladdr/c/t.g...done.
(gdb)

因此可以使用所有的调试信息,这包括.symtab表;而backtrace()系列函数作为程序执行的逻辑功能,无法去读取磁盘上的二进制程序文件,因此只能使用.dynsym表。
4、-rdynamic选项不产生任何调试信息,因此在一般情况下,新增的附加信息比-g选项要少得多。除非是完全的静态连接,否则即便是没有加-rdynamic选项,程序使用到的外部动态符号,比如前面示例里的printf,也会被自动加入到.dynsym表。

示例项目

bar.c
extern void foo(void);

void bar(void)
{
    foo();
}
main.c

main.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

void foo(void)
{
    puts("Hello world");
}

int main(void)
{
    void * dlh = dlopen("./libbar.so", RTLD_NOW);
    if (!dlh) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE); 
    }
    void (*bar)(void) = dlsym(dlh,"bar");
    if (!bar) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE); 
    }
    bar();
    return 0;
}

在这里,bar.c成为共享库libbar.so,而main.c成为共享库 一个dlopen s libbar并从该库中调用bar()的程序. bar()调用foo(),它在bar.c中是外部的,并且是在main.c中定义的.
因此,没有-rdynamic:

$ make test
gcc -c -Wall -o main.o main.c
gcc -c -Wall -fpic -o bar.o bar.c
gcc -shared -o libbar.so bar.o
gcc  -o prog main.o -L. -lbar -ldl
./prog
./libbar.so: undefined symbol: foo
Makefile:23: recipe for target 'test' failed

并使用-rdynamic:

$ make clean
rm -f *.o *.so prog
$ make test LDEXTRAFLAGS=-rdynamic
gcc -c -Wall -o main.o main.c
gcc -c -Wall -fpic -o bar.o bar.c
gcc -shared -o libbar.so bar.o
gcc -rdynamic -o prog main.o -L. -lbar -ldl
./prog
Hello world
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值