上为GCC 编译过程示意图
gcc
预处理后的 C 文件,预处理后即停止,不进行编译,预处理后的代码送往标准输出,所以要用-o来输出成文件;
gcc
编译后即停止,不进行汇编,对于每个输入的非汇编语言文件,输出文件是汇编语言文件;
如果没有使用`-o'选项,默认的输出结果是:文件名.s <-- 汇编文件
gcc
编译或汇编源文件,但是不作连接,编译器输出对应于源文件的目标文件;
如果没有使用`-o'选项,默认的输出结果是: 文件名.o <-- 目标文件
注:以上三个选项不能组合使用,因为它们各是在产生一个新可执行程序的一个阶段后停止输出。
gcc
指定输出文件为指定文件名,该选项不在乎 gcc 产生什么输出
(可执行文件,目标文件,汇编文件还是预处理后的 C 代码)
如果没有使用`-o'选项,默认的输出结果是:可执行文件为 a.out
如果gcc后没有以上三个选项之一,就会完成:预处理,编译,汇编,连结,四个阶段产生新可执行程序。
基本语法结构:(由以下四部分组成)
gcc
gcc
注意两点:1. gcc永远在首,2. 库永远在依赖文件(*.c/*.cpp/*.o)之后;
gcc编译时链接库选项问题:
gcc -o test
无法通过编译,说不能正确链接库函数
而改为
gcc -o
就正确编译了!!!
经查找资料发现,
-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
因为,指定 -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
注:当 静态库(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
或
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 中指定的成员插入到归档文件中,替换掉归档文件中原来的同名成员。
如果写成这样,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与.之间空格可有可无,-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.sousr/lib
注:修改系统目录需要 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,动态库路径 或
注:当指定多个动态库搜索路径时,路径之间用冒号":"分隔。
readelf -d testdynamiclib_1
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环境变量的使用方法:
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)对操作系统的依赖,这两种动态库运行依赖于各自的操作系统,不能跨平台使用。因此,对于实现相同功能的动态库,必须为两种不同的操作系统提供不同的动态库版本。