linux静态与动态库创建及使用实例
一,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’)。
小写字母,快速追加,即将参数 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环境变量的使用方法:
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 <-- 是不推荐的
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静态与动态库创建及使用实例的更多相关文章
- Linux中的动态库和静态库(.a/.la/.so/.o)
Linux中的动态库和静态库(.a/.la/.so/.o) Linux中的动态库和静态库(.a/.la/.so/.o) C/C++程序编译的过程 .o文件(目标文件) 创建atoi.o 使用atoi. ...
- Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名
Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名 转载自:http://b ...
- Linux系统中“动态库”和“静态库”那点事儿【转】
转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...
- Linux系统中“动态库”和“静态库”那点事儿
摘自http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在 ...
- VC 静态库与动态库(三)动态库创建与使用_隐式链接
动态库分为二种,一种隐式链接,另一种显示调用.不论哪种动态库,本质都是运行时动态加载 隐式链接:程序运行时,由编译系统自动加载动态库,然后根据程序的引入表进行重定位,当程序退出时自动卸载动态库 显示调 ...
- 【转】分析Linux和windows动态库
原文地址:http://www.cnblogs.com/chio/archive/2008/11/13/1333119.html 摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Lin ...
- linux下so动态库一些不为人知的秘密(转)
linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识.基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些动态库, ...
- linux下so动态库一些不为人知的秘密
linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名.二者都使用广泛.本文主要讲动态库方面知识. 基本上每一个linux 程序都至少会有一个动态库,查看某个程序使用了那些 ...
- Linux和windows动态库
转载:http://www.cnblogs.com/chio/archive/2008/11/13/1333119.html 态链接库技术实现和设计程序常用的技术,在Windows和Linux系 统中 ...
随机推荐
- Eclipse项目的导入跟导出
1.导入项目 当下载了包含Eclipse 项目的源代码文件后,我们可以把它导入到当前的Eclipse 工作区然后编辑和查看.点击菜单File > Import,然后在弹出的Import 对话框中 ...
- java Process的waitFor()
java Process的waitFor() 在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本.在Java中提供了两种方法来启动其他程序: (1) 使用Runtim ...
- Spring AOP 创建增强类
AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强: 1)前置增强:org.springframework.aop.BeforeAd ...
- javascript 原生方法监听DOM结构改变事件
js原生方法监听DOM结构改变事件 document.addEventListener('DOMNodeInserted',function(){alert(1)},false);document.a ...
- spring mvc 国际化
spring mvc 国际化 使用CookieLocaleResolver实现国际化的步骤:1.注册 messageSource,localeResolver 两个bean2.调用localeReso ...
- 批处理 —— 每天生成一个以日期命名的文件(Win XP)
想达到这样一个效果:每天在某个目录下生成一个以日期命名的文件(如,0705.txt). 第一步,新建一个批处理文件 新建一个文件,比如[create_day_file.bat].编辑,输入以下内容 : ...
- vim 添加到右键 windows
>>>> 在windows下 <<<< ++ 在鼠标右键显示“用vim编辑”++ 1.删掉注册表中的HKEY_CLASSES_ROOT\*\shelle ...
- spring.net异常处理
接下来我们看一下SpringNetAop层的内容:此层主要是实现一些AOP的代码,以及特性和一个统一调用spring.net的类:记录日志我们使用Log4Net来实现: 1:Aspects 文件夹里存 ...
- 自动化测试LoadRunner
这个地址应该比较的好下载,以前找的地址都是需要输入一些相关的信息.这个只需要有一个HP的注册账号就可下载,记下来.以备后用: 下载地址: http://www8.hp.com/us/en/softwa ...
- leetcode:Palindrome Linked List
Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) time ...