库使用创建相关

头文件搜索路径

  1. 当前目录(如果使用“ ”符号引入的头文件,如果用的<>则不会搜索当前目录)
  2. -I(大写的i)指定的目录
  3. CPLUS_INCLUDE_PATH指定路径(c语言是C_INCLUDE_PATH)
  4. gcc默认的路径/usr/include、/usr/local/include、/usr/lib/gcc/x86_64-redhat-linux(platfrom)/4.4.7(version)/include

        这里提一下默认目录应该是和安装gcc的时候的prefix相关。如果安装时指定了prefix,那么默认路径就成了prefix/include、prefix/local/include、prefix/lib/gcc/x86_64-redhat-linux(platfrom)/4.4.7(version)/include,换句话说默认prefix=/usr

动态库搜索路径

        编译时需要用到第三方动态库的时候,编译时需要使用 –l (小写的L)指定用哪些动态库;使用 –L 指定编译时查找动态库的编译路径路径,-L指定的路径查找的顺序高于连接器默认路径。编译时查找路径不代表运行时找库文件的路径,但是在编译时可以添加编译选项 -WI,-rpath= 来指定运行时动态库的搜索路径。

        运行时动态库的运行搜索路径由全局变量 LD_LIBRARY_PATH 定义。可以使用export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH 将当前路径加入到程序运行时搜索动态库的路径集合中,但是这种方法支队当前的窗口有效,换一个窗口时LD_LIBRARY_PATH还是原来的值。编译时采用 -WI,-rpath=的方式指定运行时动态库搜索路径那么该路径信息会保存到可执行程序里面,运行时直接到该路径去找动态库。

        同时程序运行时的搜索路径还定义在/etc/ld.so.conf文件列出的搜索路径中。

        可以使用dlopen()函数和dlsym()函数(#include <dlfcn.h> )来装载动态库,这样便可以在编译和运行时都不去指定动态库,运行时由函数来完成动态库的搜索和加载,而不是编译器来完成。

编译时目录查找顺序

  1. 首先去 -L 指定的目录下查找动态库
  2. 环境变量LIBRARY_PATH指定的目录下查找动态库
  3. 默认目录下查找/lib、/usr/lib、/usr/local/lib

运行时目录查找顺序

  1. 编译目标代码时指定的动态库搜索路径(通过-Wl,-rpath指定的目录,用这个指定多个目录时需要使用冒号分隔多个目录)
  2. 环境变量LD_LIBRARY_PATH指定的目录下查找动态库
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  4. 默认搜索路径/lib、/usr/lib、/usr/local/lib

动态库依赖动态库

        如果动态库A依赖动态库B,在编译生成动态库A时,可以不使用 -l和-L 显示指定动态库B的名字和编译路径,只需要动态库B的头文件即可,但是在最后编译可执行文件的时候,需要显示的指定动态库A和动态库B的名字和编译路径。

        在编译生成动态库A时,如果使用 -l和-L 显示的指定了其所需要的动态库和编译路径,对生成的libA.so使用ldd指令时,可以看到其依赖于libB.so,编译可执行文件的时候,如果编译器使用了编译选项 –Wl,copy-dt-needed-entries,那么只需要显示指定动态库A的名字和编译路径以及动态库B的编译路径即可,编译器会递归的查找libB.so,当编译器使用选项 –Wl,--no-copy-dt-needed-entries 时,编译器不会递归的查找动态库A所依赖的libB.so,需要使用-l来指定。

注意:动态库的编译路径和运行路径是两回事,上面所说的都是编译路径。

动态库生成方法:
(1)
1、gcc -c -fPIC xxx1.c
   gcc -c -fPIC xxx2.c
   gcc -c -fPIC xxx3.c
2、gcc -shared  xxx1.o xxx2.o xxx3.o -o libxxx.so

(2)
gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so


静态库生成方法:
(1)
1、gcc -o xxx1.o -c xxx1.c
   gcc -o xxx2.o -c xxx2.c
   gcc -o xxx3.o -c xxx3.c
2、2、ar -rc libxxx.a xxx1.o xxx2.o xxx3.o

(2)
1、gcc -o xxx1.o -shared -fPIC -c xxx1.c
   gcc -o xxx2.o -shared -fPIC -c xxx2.c
   gcc -o xxx3.o -shared -fPIC -c xxx3.c
2、ar -rc libxxx.a xxx1.o xxx2.o xxx3.o

         如果生成静态库所需的.o文件用-shared -fPIC编译选项,那么生成的静态库可以被动态库链接,反之则不能被动态库连接。动态库链接静态库的情况总结

        可执行程序编译链接动态库时命令一般为gcc  main.c  -L ./  -lfun

1、gcc总体选项列表
1) -c :指编译,不链接,生成目标文件“.o”。
2) -S :只编译,不汇编,生成汇编代码“.S”。
3) -E :只进行预编译/预处理,不做其他处理。
4) -o file:把输出文件输出到file里。
5) -g :在可执行程序或动态库中包含标准调试信息。
6) -v :打印出编译器内部编译各过程的命令行信息和编译器的版本。
7) -I dir :在头文件的搜索路径列表中添加dir目录
8) -L dir :在库文件的搜索路径列表中添加dir目录
9) -static :连接静态库(静态库也可以用动态库链接方式链接)
10) -llibrary :连接名为library的库文件(显示指定需要链接的动态库文件)
11) -Wl:该选项选项告诉编译器将后面的参数传递给链接器
12) -std:指定c/c++的标准,例如C99、C++11等等标准
13) -static 用于编译一个程序时,会使此程序静态编译

2、gcc告警和出错选项
1) -ansi :支持符合ANSI标准的C程序
2) -pedantic :允许发出ANSI C标准所列出的全部警告信息
3) -pedantic-error :允许发出ANSI C标准所列出的全部错误信息
4) -w :关闭所有警告
5) -Wall :允许发出gcc提供的所有有用的报警信息
6) -werror :把所有的告警信息转化为错误信息,并在告警发生时终止编译过程

3、gcc优化选项
   gcc可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的
整数。对于不同版本的gcc,n的取值范围不一致,比较典型的范围为0变化到2或者3。虽然优化选项可以加
速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明
和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也可能因为循环展开而变
得到处都有。

静态库依赖动态库

        静态库A依赖于动态库B。在编译生成静态库A时不需要使用-l和-L显示的指定动态库B的名字和编译路径。但是静态库A中也只是保留了其使用动态库B中对应函数的符号表,类型为U,编译可执行文件时还是需要使用-l和-L显示指定动态库的名称和编译路径。也就是说这个静态库A并不是完全独立的,静态库A对动态库B中的函数仍然是动态调用。

        所以在打包静态库的时候,静态库源码最好不要依赖第三方动态库,否则静态库无法做到完全独立,失去静态库的意义。

注意:编译时,gcc参数的顺序会直接影响编译的结果。

动态库依赖静态库

        动态库A依赖静态库B时,编译生成动态库A时不使用 -l和-L 显示的指定静态库B的名字和编译路径时,生成的动态库A只有静态库B中对应函数的符号,类型为U,在使用动态库A编译可执行文件时必须显示的使用-l和-L指定静态库的名字和编译路径。

        编译生成动态库A时使用 -l和-L 显示的指定静态库B的名字和编译路径时,生成的动态库A中有对应静态库B中的函数,类型为T,即将静态库B中的内容拷贝到了动态库A中。在使用动态库A编译可执行文件时不要要静态库A的参与。

静态库依赖静态库

        在使用两个静态库编译可执行文件时,必须使用-l和-L显示指定两个动态库的名称和编译路径,最后的可执行文件将会直接将两个静态库中的内容拷贝进去,类型为T。

动态库使用时的注意事项

        动态库中有static变量和全局变量时,当同一个进程的多个线程使用这个动态库时,那么这个static变量和全局变量是该进程的所有线程公用的,即同一个变量;当不同的进程使用同一个动态库时,这个动态库中的静态变量和全局变量不是同一个变量,但是需要注意的是,在fork创建子进程时,子进程使用的动态库中的static变量和全局变量是拷贝的父进程的数据,即子进程中动态库中的static变量和全局变量的值不是动态库给的原始值,而是父进程修改过后的值。

        需要注意的是,当可执行程序中的全局变量和动态库中的全局变量重名时,那么动态库中的全局变量会被忽略,可执行程序和动态库本生都会使用可执行程序中的那个全局变量(通过打印地址得出的结论)。

        当两个动态库中有变量名相同的全局变量,主程序通过extern引用该重名的变量时,具体引用的是哪个库中的全局变量,和编译可执行程序时链接两个库的顺序有关。(盲猜可能和编译器也有关系)

处理目标文件的工具

        使用readelf -s查看elf文件中的显示符号表段中的项(如果有的话),其中Ndx列会出现数字、UND、ABS等可能。数字表示该符号是本模块中的符号;UND表示该符号没有定义在这个模块中,定义在其它模块中;ABS表示不该被重定位的符号。Bind列表示该符号的属性LOCAL表示本地符号,对其它模块不可见,GLOBAL表示全局符号,对其它模块可见;Type表示符号类型,SECTION为段,FILE表示文件,OBJECT表示对象,FUNC表示函数。

        自己实验发现,当源文件是.c结尾时,用gcc编译,函数产生c风格的符号;用g++编译时,函数产生c++风格的符号。源文件是.cpp文件时,gcc编译产生c++风格的符号;用g++编译时,函数产生c++风格的符号。

        注意g++编译c++11是需要加-std=c++11编译选项,否则c++11的特性会编译报错(推测可能c++14的也是类似的办法)。

        ldd(list dynamic  dependencies)命令可以查看库的依赖关系。

        strings命令,列出一个目标文件中所有的可打印的字符串。

/shiyan/c$ strings a.out 
/lib/ld-linux.so.2
libc.so.6
printf
getchar
__libc_start_main
__gmon_start__
GLIBC_2.0
PTRh
......
%s(line%d)+++
%s(line%d)---
fun.c
main.c
long long int
__FUNCTION__
long long unsigned int
GNU C11 5.4.0 20160609 -mtune=generic -march=i686 -g -fstack-protector-strong
/mnt/hgfs/temp/user/shiyan/c
unsigned char
short unsigned int
fun.c

        strip命令,从目标文件中删除符号表信息

        nm命令,列出目标文件符号表中定义的符号,第一列表示改符号在目标文件的地址;第二列表示符号的一些属性,比如位于哪个段,常见的:'T' 't'表示符号属于代码段(.text);'B' 'b'表示符号属于未初始化数据区(BBS)(未初始化或者初始化为0的静态和全局变量),大写表示全局变量,小写表示静态变量;'D' 'd'表示符号位于初始化数据部分(.data)(初始化的静态和全局变量,且初始化值不为0)大写表示全局变量,小写表示静态变量。第三列表示符号名称。

        size命令,列出目标文件中节的名字和大小

        objdump命令,能够显示一个目标文件中所有的信息。最大的作用是反汇编.text中的二进制命令

        

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值