GCC 编译常用的选项

参考:http://abcdxyzk.github.io/blog/2014/11/06/compiler-gcc-lib/
http://abcdxyzk.github.io/blog/2013/10/31/compiler-binutil-static/

gcc 制作Library 分为 static、shared、dynamically loaded

1.static libraries

Static 链接库用于静态链接,简单讲是把一堆object檔用ar(archiver)包装集合起来,文件名以 `.a’ 结尾。优点是执行效能通常会比后两者快,而且因为是静态链接,所以不易发生执行时找不到library或版本错置而无法执行的问题。缺点则是档案较大,维护度较低;例如library如果发现bug需要更新,那么就必须重新连结执行档.

1.1 编译

编译方式很简单,先例用 `-c’ 编出 object ,再用 ar 包起来即可。

____ hello.c ____
#include
void hello(){ printf("Hello "); }
____ world.c ____
#include
void world(){ printf("world."); }
____ mylib.h ____
void hello();
void world();
$ gcc -c hello.c world.c /* 编出 hello.o 与 world.o */
$ ar rcs libmylib.a hello.o world.o /* 包成 limylib.a */

这样就可以建出一个档名为 libmylib.a 的library。输出的档名其实没有硬性规定,但如果想要配合 gcc 的 ‘-l’ 参数来连结,一定要以 ‘lib’ 开头,中间是你要的library名称,然后紧接着 ‘.a’ 结尾

1.2 使用

____ main.c ____
#include "mylib.h"
int main() {
hello();
world();
}

使用上就像与一般的 object 档连结没有差别。

$ gcc main.c libmylib.a

也可以配合 gcc 的 `-l’ 参数使用

$ gcc main.c -L. -lmylib

‘-Ldir’ 参数用来指定要搜寻链接库的目录,’.‘ 表示搜寻现在所在的目录。通常默认会搜 /usr/lib 或 /lib 等目录。
’-llibrary’ 参数用来指定要连结的链接库,‘mylib’ 表示要与mylib进行连结,他会搜寻library名称前加’lib’后接’.a’的档案来连结。

2. shared library

Shared library 会在程序执行起始时才被自动加载。因为链接库与执行档是分离的,所以维护弹性较好。有两点要注意,shared library是在程序起始时就要被加载,而不是执行中用到才加载,而且在连结阶段需要有该链接库才能进行连结

首先有一些名词要弄懂,soname、real name与linker name。
soname 用来表示是一个特定 library 的名称,像是 libmylib.so.1 。前面以 ‘lib’ 开头,接着是该 library 的名称,然后是 ‘.so’ ,接着是版号,用来表名他的界面;如果接口改变时,就会增加版号来维护兼容度。

real name 是实际放有library程序的文件名,后面会再加上 minor 版号与release 版号,像是 libmylib.so.1.0.0 。

一般来说,版号的改变规则是(在 APress-Difinitive Guide to GCC),最后缀的release版号用于程序内容的修正,接口完全没有改变。中间的minor用于有新增加接口,但相旧接口没改变,所以与旧版本兼容。最前面的version版号用于原接口有移除或改变,与旧版不兼容时。

linker name是用于连结时的名称,是不含版号的 soname ,如: libmylib.so。
通常 linker name与 real name是用 ln 指到对应的 real name ,用来提供弹性与维护性。

2.1 编译

shared library的制作过程较复杂。

$ gcc -c -fPIC hello.c world.c

编译时要加上 -fPIC 用来产生 position-independent code。也可以用 -fpic参数。 (不太清楚差异,只知道 -fPIC 较通用于不同平台,但产生的code较大,而且编译速度较慢)。

$ gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.0 hello.o world.o

-shared 表示要编译成 shared library
-Wl 用于参递参数给linker,因此-soname与libmylib.so.1会被传给linker处理。
-soname用来指名 soname 为 limylib.so.1
library会被输出成libmylib.so.1.0.0 (也就是real name)
若不指定 soname 的话,在编译结连后的执行档会以连时的library档名为soname,并载入他。否则是载入soname指定的library档案。

可以利用 objdump 来看 library 的 soname。

$ objdump -p libmylib.so | grep SONAME

SONAME libmylib.so.1

若不指名-soname参数的话,则library不会有这个字段数据。
在编译后再用 ln 来建立 soname 与 linker name 两个链接。

$ ln -s libmylib.so.1.0.0 libmylib.so
$ ln -s libmylib.so.1.0.0 libmylib.so.1

2.2 使用

与使用 static library 同。

$ gcc main.c libmylib.so

以上直接指定与 libmylib.so 连结。
或用

$ gcc main.c -L. -lmylib

linker会搜寻 libmylib.so 来进行连结。
如果目录下同时有static与shared library的话,会以shared为主。
使用 -static 参数可以避免使用shared连结。

$ gcc main.c -static -L. -lmylib

此时可以用 ldd 看编译出的执行档与shared链接库的相依性
$ldd a.out
linux-gate.so.1 => (0xffffe000)
libmylib.so.1 => not found
libc.so.6 => /lib/libc.so.6 (0xb7dd6000)
/lib/ld-linux.so.2 (0xb7f07000)

输出结果显示出该执行文件需要 libmylib.so.1 这个shared library。
会显示 not found 因为没指定该library所在的目录,所找不到该library。
因为编译时有指定-soname参数为 libmylib.so.1 的关系,所以该执行档会加载libmylib.so.1。否则以libmylib.so连结,执行档则会变成要求加载libmylib.so

$ ./a.out

./a.out: error while loading shared libraries: libmylib.so.1:
cannot open shared object file: No such file or directory

因为找不到 libmylib.so.1 所以无法执行程序。
有几个方式可以处理。
a. 把 libmylib.so.1 安装到系统的library目录,如/usr/lib下
b. 设定 /etc/ld.so.conf ,加入一个新的library搜寻目录,并执行ldconfig
更新快取
c. 设定 LD_LIBRARY_PATH 环境变量来搜寻library
这个例子是加入当前目录来搜寻要载作的library

$ LD_LIBRARY_PATH=. ./a.out

同时使用静态库和动态库

gcc –static a.c -Wl,-Bstatic -lm -Wl,-Bdynamic -lc

其中用到的两个选项:-Wl,-Bstatic和-Wl,-Bdynamic。这两个选项是gcc的特殊选项,它会将选项的参数传递给链接器,作为 链接器的选项。比如-Wl,-Bstatic告诉链接器使用-Bstatic选项,该选项是告诉链接器,对接下来的-l选项使用静态链 接;-Wl,-Bdynamic就是告诉链接器对接下来的-l选项使用动态链接。下面是man gcc对-Wl,option的描述

-Wl,option  
   Pass option as an option to the linker.  If option contains commas, it is   
   split into multiple options at the commas.  You can use this syntax to pass  
   an argument to the option.  For example, -Wl,-Map,output.map passes -Map output.map  
   to the linker.  When using the GNU linker, you can also get the same effect with   
   -Wl,-Map=output.map.  

下面是man ld分别对-Bstatic和-Bdynamic的描述,

-Bdynamic  
-dy  
-call_shared  
   Link against dynamic libraries. You may use this option multiple times on the  
   command line: it affects library searching for -l options which follow it.  

-Bstatic  
-dn  
-non_shared  
-static  
   Do not link against shared libraries. You may use this option multiple times on   
   the command line: it affects library searching for -l options which follow it.   
   This option also implies --unresolved-symbols=report-all.  This option can be   
   used with -shared.  Doing so means that a shared library is being created but   
   that all of the library's external references must be resolved by pulling in   
   entries from static libraries.  

值得注意的是对-static的描述:-static和-shared可以同时存在,这样会创建共享库,但该共享库引用的其他库会静态地链接到该共享库中。

Dynamically loaded libraries

Dynamicaaly loaded libraries 才是像 windows 所用的 DLL ,在使用到
时才加载,编译连结时不需要相关的library。动态载入库常被用于像plug-ins的应用。

3.1 使用方式

动态加载是透过一套 dl function来处理。
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
开启加载 filename 指定的 library。
void *dlsym(void *handle, const char *symbol);
取得 symbol 指定的symbol name在library被加载的内存地址。
int dlclose(void *handle);
关闭dlopen开启的handle。
char *dlerror(void);
传回最近所发生的错误讯息。

____ dltest.c ____
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <dlfcn.h>
int main() {
void *handle;
void (*f)();
char *error;
/* 开启之前所撰写的 libmylib.so 链接库 */
handle = dlopen("./libmylib.so", RTLD_LAZY);
if( !handle ) {
fputs( dlerror(), stderr);
exit(1);
}
/* 取得 hello function 的 address */
f = dlsym(handle, "hello");
if(( error=dlerror())!=NULL) {
fputs(error, stderr);
exit(1);
}
/* 呼叫该 function */
f();
dlclose(handle);
}

编译时要加上 -ldl 参数来与 dl library 连结

$ gcc dltest.c -ldl

部分GCC选项

-Werror 和 -I 很有用

链接

命令描述
-l library 或者 -llibrary进行链接时搜索名为library的库。例子: $ gcc test.c -lm -o test
-Idir把dir加入到搜索头文件的路径列表中。例子: $ gcc test.c -I…/inc -o test
-Dname预定义一个名为name的宏,值为1。例子: $ gcc -DTEST_CONFIG test.c -o test
-Dname=definition预定名为name, 值为definition的宏
-g 或者 -glevel生成操作系统本地格式的调试信息。-g和-ggdb并不相同,-g会生成gdb之外的信息.
-s去除可执行文件中的符号表和重定位信息。用于减少可执行文件的大小
-M告诉预处理器输出一个适合make的规则,用于描述各目标文件的依赖关系。对于每个源文件,预处理器会输出一个make规则,该规则的目标项(target)是源文件对应的目标文件名,依赖项(dependency)是源文件中#include 引用的所有的文件。
-C告诉预处理器不要丢弃注释。配合’-E’使用
-P告诉预处理器不要产生 ‘#line’ 命令。配合’-E’使用。
-static在支持链接的系统上,阻止链接共享库。
-nostdlib不链接系统标准文件和标准库文件,只把指定的文件传递给链接器

Warnings

命令描述
-Wall会打开一些很有用的警告选项,建议编译时加此选项。
-W -Wextra打印一些额外的警告信息。
-w禁止显示所有警告信息。
-Wshadow当一个局部变量遮盖住了另一个局部变量,或者全局变量时,给出警告。很有用的选项,建议打开。 -Wall 并不会打开此项。
-Wpointer-arith对函数指针或者void *类型的指针进行算术操作时给出警告。也很有用。 -Wall 并不会打开此项。
-Wcast-qual当强制转化丢掉了类型修饰符时给出警告。 -Wall 并不会打开此项。
-Waggregate-return如果定义或调用了返回结构体或联合体的函数,编译器就发出警告。
-Winline无论是声明为 inline 或者是指定了-finline-functions 选项,如果某函数不能内联,编译器都将发出警告。如果你的代码含有很多 inline 函数的话,这是很有用的选项。
-Werror把警告当作错误。出现任何警告就放弃编译。
-Wunreachable-code如果编译器探测到永远不会执行到的代码,就给出警告。也是比较有用的选项。
-Wcast-align一旦某个指针类型强制转换导致目标所需的地址对齐增加时,编译器就发出警告。
-Wundef当一个没有定义的符号出现在 #if 中时,给出警告。
-Wredundant-decls如果在同一个可见域内某定义多次声明,编译器就发出警告,即使这些重复声明有效并且毫无差别。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝鲸123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值