linux静态与动态库创建及使用实例

时间:2021-12-21 21:45:49

一,gcc基础语法:

基本语法结构:(由以下四部分组成)
gcc -o 可执行文件名
依赖文件集(*.c/*.o)
依赖库文件及其头文件集(由-I或-L与-l指明)
gcc 依赖文件集(*.c/*.o)
依赖库文件及其头文件集(由-I或-L与-l指明)
-o
可执行文件名
注意两点:1. gcc永远在首,2. 库永远在依赖文件(*.c/*.cpp/*.o)之后;

Q:gcc编译时链接库选项问题:
gcc -o test -I. str_out.h -L. -lstr_out main.c: 提示无法通过编译,说不能正确链接库函数
A:改为gcc -o test main.c -I. str_out.h -L. -lstr_out

注:当 静态库(libstr_out.a) 和 动态库(libstr_out.so) 同名时, gcc命令将优先使用动态库,无论是静态还是动态库都是由 *.o 目标文件生成,所以第一步都是生成 *.o 目标文件!

二,静态库生成步骤如下:
步骤一:生成 str_out.o 目标文件
gcc -c str_out.c
注:不加 -o 则生成同名的 *.o 目标文件(str_out.o),加上 -o 可以指定生成任意名的目标文件。

步骤二:生成 libstr_out.a 静态库,Linux规定静态库的命名规则一定是以 lib 开头且以 .a 结尾!
ar -crs
libstr_out.a str_out.o


ar -cqs libstr_out.a str_out.o
强调一下:ar后面必先是 -crs 或 -cqs 引导的 lib*.a 静态库名,再是
*.o文件名,这个格式是固定的!
     如果写成 ar str_out.o -crs libstr_out.a 或 ar str_out.o -cqs
libstr_out.a 都会报如下错误:
      ar: two different operation options specified
注:-c -> 小写字母,如果该库不存在,则有些版本的 ar 必须指定 c 选项,才会进行创建,GNU
ar 不需要。
  -r -> 小写字母,将参数
member 中指定的成员插入到归档文件中,替换掉归档文件中原来的同名成员。
     
     
  即先删除原来同名的成员,然后将新成员插入到归档文件中。
     
     
  默认情况下,新插入的成员会被添加到归档文件的末尾,但可以通过选项`a’, `b’,
或`i’指明插入的位置。

-a
-> 在归档文件中一个已经存在的成员后面增加由参数 member 给出的新文件。
     
     
 如果使用选项`a’,则应该为命令行参数 member 指定一个已经存在的成员名(即给出参数
relpos)。

-b ->
在归档文件中一个已经存在的成员前面增加由参数 member 给出的新文件。
   
     
   如果使用任选项`b’,则应该为命令行参数 member
指定一个已经存在的成员名(即给出参数 relpos)。
 
   -i ->
在归档文件中一个已经存在的成员前面增加一个新的文件。
     
     
如果使用任选项i,则应该为命令行参数 member指定一个已经存在的成员名
     
     (即给出参数
relpos,类似于选项`b’)。

   -q ->
小写字母,快速追加,即将参数 member 中给出的成员追加到归档文件的末尾,
     
     
 而不检查是否要进行替换,也不更新符号表索引。
   -s
-> 小写字母,写入一个目标文件索引到归档文件中,或者更新一个存在的目标文件索引。 
     
     
 甚至对于没有任何变化的归档文件也作该动作(等同于对该库做ranlib)。
如果写成这样,r与q共存的话,
ar str_out.o -crqs
libstr_out.a

则会出现如下错误提示:
ar: two different operation options
specified

以上两步其实已经创建了一个静态库!

步骤三:将静态库合并入可执行程序
gcc main.c -o teststaticlib -L. -l str_out
也可写成,如下:
gcc -c main.c -o main.o
gcc main.o -L . -l str_out -o teststaticlib
注:-L ->
大写字母,指定静态库的查找位置,-L后面的.(点)表示静态库在当前目录下查找。
     
 -l  ->
小写字母,指定静态库名,由于静态库的命名规则是以lib开头且以.a结尾(lib*.a),
     
     
     
     
     
     
   故此,lib与.a一定要忽略,如上所示。
     
     
     
     
     
 
 如果要合并入多个静态库,则每个静态库名前都要加-l。
  -L与.之间空格可有可无,-l与静态库名之间空格可有可无。

nm *.a 命令可以用来列出静态库文件中的符号清单。

三、动态库生成步骤如下:
步骤一:生成 str_out.o 目标文件
gcc -c str_out.c -o str_out.o

步骤二:生成 libstr_out.so 动态库文件,Linux规定其动态库命名方式为“lib*.so.*”。
    在这个命名方式中,第一个*表示动态链接库的库名,第二个*通常表示该动态库的版本号,
    也可以没有版本号!例如:libc-2.9.so、libc.so.6、libcap.so.2.11。
gcc -shared -fPIC -Wall str_out.o -o
libstr_out.so

步骤三:将生成的动态库放到系统动态库默认目录(/usr/lib)中去,以便系统可以搜索到它。
sudo cp libstr_out.so /usr/lib
注:修改系统目录需要 root 用户权限,

   
 故此,使用 sudo 命令来前缀 cp 命令,一会系统会让你输入root用户的密码的!

步骤四:链接动态库并生成可执行文件
gcc -c main.c -o main.o
gcc main.o -l str_out -o testdynamiclib_1

也可以省略第三步用 -Wl,-rpath=./ -L .
在编译时记录运行时搜索动态库的路径,如下:
gcc -c main.c -o main.o
gcc -o testdynamiclib_2 main.o -L . -l str_out
-Wl,-rpath,./
写成如下这样也可以:
gcc main.o -Wl,-rpath,./ -L . -l str_out -o
testdynamiclib_2

gcc main.o -Wl,-rpath=./ -L . -l str_out -o
testdynamiclib_2
强调:1)
-Wl,-rpath,动态库路径 或 
-Wl,-rpath=动态库路径 这两种写法都可以!
     
     
  都是在elf文件中增加记录动态库的搜索路径(除系统默认的外,再搜索指定的)。
     
    2) 如果少了-L 动态库路径
来指定动态库路径同样编译不过去!
注:当指定多个动态库搜索路径时,路径之间用冒号":"分隔。
     
 用 readelf -d 可行文件名 命令查看ELF文件的动态节(Dynamic
Section)。(如下面所示)
     
 对比 testdynamiclib_1 和 testdynamiclib_2
的结果我们可以发现,
     
 testdynamiclib_2 中多出来了RPATH项,指定”Library rpath:
[./]”,与可执行文件同一个目录。
     
 通过这种方式,我们可以用非常小的代价(仅增加几乎可以忽略的空间开销),
     
 对每个ELF文件都指定最优化的搜索路径,达到提升性能的目的。这是我们比较推荐的一种方法。

readelf -d testdynamiclib_1

Dynamic section at offset 0xf18 contains 22 entries:
Tag

   
     
    Type  
     
     
   
 Name/Value
0x00000001   (NEEDED)
     
     Shared
library: [libstr_out.so]
0x00000001   (NEEDED)  
     
   Shared library:
[libc.so.6]
0x0000000c   (INIT)  
     
     
    0x8048380
0x0000000d   (FINI)  
     
     
    0x804856c
0x00000004   (HASH)  
     
     
 0x8048168
 0x6ffffef5    
  (GNU_HASH)    
   0x80481a8
0x00000005   (STRTAB)  
     
    0x8048294
0x00000006   (SYMTAB)  
     
   0x80481e4
0x0000000a   (STRSZ)  
    
      147
(bytes)
0x0000000b   (SYMENT)  
     
   16 (bytes)
0x00000015   (DEBUG)  
     
     0x0
0x00000003   (PLTGOT)  
     
    0x8049ff4
0x00000002   (PLTRELSZ)  
      24
(bytes)
0x00000014   (PLTREL)  
     
    REL
0x00000017   (JMPREL)  
     
   0x8048368
0x00000011   (REL)  
     
     
   0x8048360
0x00000012   (RELSZ)  
     
     8
(bytes)
0x00000013   (RELENT)  
     
   8 (bytes)
0x6ffffffe      
  (VERNEED)    
    0x8048340
0x6fffffff      
   (VERNEEDNUM)
 1
0x6ffffff0      
  (VERSYM)    
     
0x8048328
0x00000000   (NULL)  
     
     
 0x0

readelf -d testdynamiclib_2

Dynamic section at offset 0xf10 contains 23 entries:
Tag      
     
   Type  
     
     
     
Name/Value
0x00000001  (NEEDED)  
     
     Shared
library: [libstr_out.so]
0x00000001  (NEEDED)  
     
     Shared
library: [libc.so.6]
0x0000000f   (RPATH)
     
     
    Library rpath:
[./]
0x0000000c  (INIT)  
     
     
    
 0x8048380
0x0000000d  (FINI)  
     
     
     
0x804856c
0x00000004  (HASH)  
     
     
   0x8048168
0x6ffffef5      
(GNU_HASH)      
   0x80481a8
0x00000005  (STRTAB)  
     
     
0x8048294
0x00000006  (SYMTAB)  
     
   
 0x80481e4
0x0000000a  (STRSZ)  
     
     
 150 (bytes)
0x0000000b  (SYMENT)  
     
     16
(bytes)
0x00000015  (DEBUG)  
     
     
 0x0
0x00000003  (PLTGOT)  
     
     
0x8049ff4
0x00000002  (PLTRELSZ)  
     
  24 (bytes)
0x00000014  (PLTREL)  
     
      REL
0x00000017  (JMPREL)  
     
   
 0x8048368
0x00000011  (REL)  
     
     
   
 0x8048360
0x00000012  (RELSZ)  
     
     
 8 (bytes)
0x00000013  (RELENT)  
     
     8
(bytes)
0x6ffffffe      
 (VERNEED)    
     
0x8048340
0x6fffffff      
  (VERNEEDNUM)  
 1
0x6ffffff0      
 (VERSYM)    
     
  0x804832a
0x00000000  (NULL)  
     
     
   0x0

ldd 可执行文件名 -> 查看库链接状况;
objdump <选项> 文件名 ->
对象文件信息;

还可以指定要链接的动态库所在的路径,如下:
gcc -c main.c -o main.o
gcc main.o -l str_out -B ./ -o testdynamiclib_2
上下两句意思相同,都可编译通过,但运行时找不到动态库!
gcc main.o -L . -l str_out -o testdynamiclib_2
注:-B -> 大写字母,指定要链接的动态库所在的路径,./ 表示为当前目录。
     
     
     
     
     
   但是存在一个问题是,编译过去了,但运行时还是找不到动态库!
运行时报错:
./testdynamiclib_2: error while loading shared libraries:
libstr_out.so: cannot open shared object file: No such file or
directory

还可以添加系统变量来指定新的搜索目录路路,如下:
export LD_LIBRARY_PATH=./
注:一旦LD_LIBRARY_PATH被设定,则在这个环境变量生效的范围之内,
  所有其他的ELF可执行程序也会按照这个顺序去搜索动态库,这样势必会造成搜索时的一些浪费。

删除添中的系统变量,如下:
unset LD_LIBRARY_PATH

也还可以用 LD_PRELOAD 环境变量,它允许你定义在程序运行前优先加载的动态链接库。
这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。

一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码);
另一方面,我们也可以以向别人的程序注入恶意程序,从而达到那不可告人的目的;
在一些UNIX版本上,如果你想要使用LD_PRELOAD环境变量,你需要有root权限。
通过设置执行文件的setgid / setuid标志。
在有SUID权限的执行文件,系统会忽略LD_PRELOAD环境变量。
也就是说,如果你有以root方式运行的程序,最好设置上SUID权限。(如:chmod 4755 daemon)
可以通过下面的实例来了解LD_PRELOAD环境变量的使用方法:

linux静态与动态库创建及使用实例
  Windows和Linux采用动态链接库技术目的是基本一致的,但由于操作系统的不同,他们在许多方面还是不尽相同,下面从以下几个方面进行阐述。

 
 1)动态库程序编写,在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数作为初始化的人口,通常在导出函数的声明时
需要有_declspec(dllexport)关键字。Linux下的gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要到函数做特别
声明,编写比较方便。
  2)动态库编译,在windows系统下面,有方便的调试编译环境,通常不用自己去编写makefile文件,但在linux下面,需要自己动手去编写makefile文件,因此,必须掌握一定的makefile编写技巧,另外,通常Linux编译规则相对严格。

  3)动态库调用方面,Windows和Linux对其下编制的动态库都可以采用显式调用或隐式调用,但具体的调用方式也不尽相同。

 
 4)动态库输出函数查看,在Windows中,有许多工具和软件可以进行查看DLL中所输出的函数,例如命令行方式的dumpbin以及VC++工具
中的DEPENDS程序。在Linux系统中通常采用nm来查看输出函数,也可以使用ldd查看程序隐式链接的共享对象文件。

  5)对操作系统的依赖,这两种动态库运行依赖于各自的操作系统,不能跨平台使用。因此,对于实现相同功能的动态库,必须为两种不同的操作系统提供不同的动态库版本。

四,Q&A:

1, -I选项的深度探讨:
l 选项的位置是有意义的,
gcc在处理 -l
选项链接的库的时候,只会查找出现在它前面的文件中所需要链接的符号,如:
gcc -o foo file1.c -lm
file2.c
中 gcc 处理 m 库时只会链接 file1.c 中用到的库函数,
而如果 file2.c 中也用到 m 库,它是不能正确链接的。
所以一般将 -l 选项放在 依赖文件集(*.c/*.cpp/*.o) 的后面。

多个库文件要链接时,一定要每个库文件前都有一个 -l 选项!
也可以写成如下形式:
gcc -o test main.c -I. str_out.h -L. ./libstr_out.a
因为,指定 -l 选项 和 指定文件名 的唯一区别是:
-l 选项用 lib 和 .a 或 .so 把 library 包裹起来,而且搜索一些目录。
(默认的库文件位于/usr/lib/或/usr/local/lib/目录中)

原文:http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Link-Options.html#Link-Options

-llibrary
-l library <-- 是不推荐的

   Search the
library named library when linking.
    当链接时搜索名为 library 的库文件。
   
    (The second
alternative with
    
the library as a separate argument is only for POSIX compliance and
is not recommended.)
   
(有第二种供选择的方式,即参数 库文件名
是与-l分开的,
    
这种方式只有 POSIX 遵守,并且是不推荐的)

It makes a
difference where in the command you write this option;
    the linker
searches and processes libraries and object files in the order they
are specified.
    你可以在命令中使用选项以上两种不同形式,链接器搜索处理库和对象文件的顺序是指定的。

Thus, `foo.o
-lz bar.o' searches library `z' after file foo.o but before
bar.o.
    因此,‘foo.o -lz
bar.o’中搜索名为z的库在文件foo.o之后,但是在文件bar.o之前。

If bar.o
refers to functions in `z', those functions may not be
loaded.
    如果bar.o引用了名为z的库中的函数,这些函数是加载不上的。

The linker
searches a standard list of directories for the library,
    which is
actually a file named liblibrary.a.
    连接器实际上是以lib与.a括起库名在标准搜索目录中寻找库文件。
  
    The linker
then uses this file as if it had been specified precisely by
name.
    链接器然后使用精确地明确说明名字的这个文件。

The
directories searched include several standard system
directories
    plus any
that you specify with -L.
    目录搜索除了若干系统标准目录外,还包括用户以 -L
选项指定的路径。

Normally the
files found this way are library files—archive files whose
    members are
object files.
    一般说来用这个方法找到的文件是库文件,即由目标文件组成的归档文件。

The linker
handles an archive file by scanning through it for members
    which define
symbols that have so far been referenced but not defined.
    连接器处理归档文件是通过从头至尾扫描归档文件中一直是引用已定义符号的成员。

But if the
file that is found is an ordinary object file, it is linked in the
usual fashion.
    但是,如果连接器找到的是一个普通的目标文件,就用平常的方式把这个目标文件连接进来。

The only
difference between using an -l option and specifying a file name is
that
    -l surrounds
library with `lib' and `.a' and searches several directories.
    在指定文件名和使用一个 -l 选项之间的唯一区别是,
    -l 选项用 lib 和 .a 把 library
包裹起来并搜索若干目录。

linux静态与动态库创建及使用实例的更多相关文章

  1. Linux中的动态库和静态库&lpar;&period;a&sol;&period;la&sol;&period;so&sol;&period;o&rpar;

    Linux中的动态库和静态库(.a/.la/.so/.o) Linux中的动态库和静态库(.a/.la/.so/.o) C/C++程序编译的过程 .o文件(目标文件) 创建atoi.o 使用atoi. ...

  2. Linux系统中&OpenCurlyDoubleQuote;动态库”和&OpenCurlyDoubleQuote;静态库”那点事儿 &sol;etc&sol;ld&period;so&period;conf 动态库的后缀为&ast;&period;so 静态库的后缀为 libxxx&period;a ldconfig 目录名

    Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名 转载自:http://b ...

  3. Linux系统中&OpenCurlyDoubleQuote;动态库”和&OpenCurlyDoubleQuote;静态库”那点事儿【转】

    转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...

  4. Linux系统中&OpenCurlyDoubleQuote;动态库”和&OpenCurlyDoubleQuote;静态库”那点事儿

    摘自http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在 ...

  5. VC 静态库与动态库&lpar;三&rpar;动态库创建与使用&lowbar;隐式链接

    动态库分为二种,一种隐式链接,另一种显示调用.不论哪种动态库,本质都是运行时动态加载 隐式链接:程序运行时,由编译系统自动加载动态库,然后根据程序的引入表进行重定位,当程序退出时自动卸载动态库 显示调 ...

  6. 【转】分析Linux和windows动态库

    原文地址:http://www.cnblogs.com/chio/archive/2008/11/13/1333119.html 摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Lin ...

  7. linux下so动态库一些不为人知的秘密&lpar;转&rpar;

    linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识.基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些动态库, ...

  8. linux下so动态库一些不为人知的秘密

    linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识.    基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些 ...

  9. Linux和windows动态库

    转载:http://www.cnblogs.com/chio/archive/2008/11/13/1333119.html 态链接库技术实现和设计程序常用的技术,在Windows和Linux系 统中 ...

随机推荐

  1. angular js 自定义指令

    我们有些时候需要把后台返回过来的带有html标签的字符串binding到界面中一个指定的div或者其他的控制器中. 使用普通ng-bind不会自动解析出html语句. js中这样定义: app.dir ...

  2. 程序员的sql金典

    1.数据库基础概念 2.数据类型 3.通过SQL语句管理数据表 4.数据的增删改 5.Select的基本用法 6.高级数据过滤 7.数据分组 8.限制结果集行数和抑制重复数据 9.计算字段 10.不从 ...

  3. Unity AssetBundles and Resources指引 (一)

    本文内容主要翻译自下面这篇文章 https://unity3d.com/cn/learn/tutorials/topics/best-practices/guide-assetbundles-and- ...

  4. Linux下oracle导入&lpar;exp&rpar;导出&lpar;imp&rpar;出现&quot&semi;Failed to open &period;&period;&period;for reader&sol;write&quot&semi;错误

  5. P - 奔小康赚大钱 - hdu 2255&lpar;带权值的匹配&rpar;

    分析:这是一个KM的模板题,也就不多说了,KM最复杂的情况都能过,下面是没有优化过的代码: ****************************************************** ...

  6. Xcode快捷键 ---- 提高效率

    Mac中主要有四个修饰键,分别是Command,Control,Option和Shift.     1. ⌘ + L 搜索行数,输入行数,调到指定行数   2.⌘ + shift + O 查询flie ...

  7. 【 D3&period;js 入门系列 — 2 】 绑定数据和选择元素

    1. 如何绑定数据 D3 有一个很独特的功能:能将数据绑定到 DOM 上,也就是绑定到文档上.这么说可能不好理解,例如网页中有段落元素<p>,我们可以将整数 5 与 <p>绑定 ...

  8. ssl证书验证

    当我们在访问https网站时,浏览器就会自动下载该网站的SSL证书,并对证书的安全性进行检查. 其他概念不说了,有效期之类的验证也不说了.只说数字证书的真实性和可信性验证. 1.CA下发给网站的证书是 ...

  9. MyBatis-Plugins

    MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: Executor (update, query, flushStatem ...

  10. Android系统分区理解及分区目录细解【转】

    本文转载自:https://blog.csdn.net/u010001503/article/details/51853822 Android 通常有以下分区: System分区: 就是我们刷ROM的 ...