Zhuan t2

来自 ChinaUnix Wiki

这个文件是我从<<Linux权威指南(第三版)>>中第13章程序设计语言中摘录的. 感到很容易得明白了很多东西,在此与大家分享.

dzyseu 2002/6/11


编译程序时,gcc会自动帮你连接所有编译的文件名及标准函数库. 首先gcc会把所有的原始文件都转成目标文件,然后会自动调用linker 来连接相关文件名(事实上linker是个文件名为ld的程序,而不是gcc 本身提供的功能,我们可以说gcc和ld的关系是相当密切的),gcc同样 也知道标准函数库的位置,并且在调用ld时传入相关信息.

如果你只想编译出目标文件而不想执行连接程序,你可以在使用gcc 时使用-c参数.

$gcc -o hello hello.c

就相当于

$gcc -c hello.c
$gcc -o hello hello.o
                                                                                                                           

虽然可以将优化参数与调试参数一起使用,但是最好不要这样做.

静态(static)和共享(shared)两种函数库格式.

所谓静态函数库,就是所有使用到的函数的程序代码都直接连接到执行 文件内.然而有些函数(例如printf())可能会很冗长,如果每份执行文 件都包含有同样的程序代码,就会显得不经济,这也是共享函数库(shared library)产生的原因.

共享函数库的做法是把所有通用函数的程序代码合并到单独的一个函数 库中.当linker连接共享函数库时,会以一小段前置命令(stub code)代替 实际的函数代码,在执行期间这段前置代码会告诉载入程序(loader)该到 哪里去找函数库. 实际上Linux共享函数库使用了一个叫跳跃表格(jump tables)的数据结 构,使得函数库在升级时不必重新连接那些使用到这些函数库的可执行文 件,而执行文件内的前置代码实际上则会在函数库的跳跃表格中找寻真正 的函数位置.利用这种方法,即使我们更改了函数库的内容,只要我们将对 应的跳跃表格更正,可执行文件的内容就可以不必改变.

使用了调试参数后,就只能以静态连接的方式处理.

自建函数库

#include <stdio.h>
int hello() {
       (void) printf("Hello, World!\n");
       return 0; /* Just to be nice */
}

首先编译生成目标代码,例如:

$gcc -c hello.c

产生hello.o文件,接着用ar程序建立库:

$ar r libhello.a hello.o

就产生libhello.a文件,最后要用ranlib命令来产生这个函数库的索引.索引 的作用就是让linker能够找到函数库中的函数:

$ranlib libhello.a
                                                                                                                           

我们现在看看怎么使用这个函数库 先写一个头文件libhello.h

/* libhello.h: routines in libhello.a */
extern hello();

再写一个usehello.c文件

#include "libhello.h"
int main(void) {
       hello();
       return 0;
}

我们来编译连接

$gcc -I./ -L./ -o usehello usehello.c -lhello

就OK啦!生成usehello可执行文件!

其中,-I参数要gcc在搜寻头文件时,也要到所指定的目录去找;

    -L参数要gcc在搜寻函数库时,也要到所指定的目录去找.

我们这里把所有文件都放在当前目录下面. -lhello是告诉linker要连接libhello.a这个函数库.上述文件名的开头 为lib,这也是函数库默认的命名格式. 也可以直接用函数库的名字:

$gcc -I./ -L./ -o usehello usehello.c libhello.a
                                                                                                                           

-l用在你需要连接非标准库的时候,例如数学库,要用-lm来指定连接libm 这个函数库(头文件是math.h).

-l选项的顺序是有意义的,例如,假如我们在libhello函数库里使用了libm 中的函数,-lm就必须加在-lhello之后: $gcc -I./ -L./ -o usehello usehello.c -lhello -lm 这样就强迫指定linker先连接libhello再连接libm,这样libhello里头使用 的libm函数才能被连接到.

gcc内定要搜寻函数库的地方,最重要的是/usr/lib,其中.a结尾的文件是静 态函数库,而.so(或者.so.version)结尾的则是共享函数库,它包含了程序执 行时动态连接所需要的程序代码,以及动态linker(ld.so)寻找共享函数库所 需要的前置代码.

/lib下面有libc.so.6(或其他版本)的共享函数库,它属于标准函数库.

默认时,linker尽量连接共享函数库,但是有时会使用静态函数库,例如使用 调试功能加了-g参数或者用-static来告诉gcc用静态函数库.

建立共享函数库 共享函数库中的程序代码必须是与位置无关的(position-independent).通过 -fPIC参数,gcc就会产生与位置无关的程序代码.例如:

$gcc -c -fPIC hello.c

然后:

$gcc -shared -o libhello.so hello.o

就产生共享函数库libhello.so了.共享函数库不用制作索引这一步. 使用的时候: $gcc -I./ -L./ -o usehello usehello.c -lhello 或者 $gcc -I./ -o usehello usehello.c libhello.so 这里的libhello.so如果不在当前路径,需要带上路径才可,-L参数不管它的.

同时存在静态函数库和共享函数库的时候,默认的是连接动态函数库,除非特别 指明.

ldd命令

$ldd usehello
       libhello.so => not found
       libc.so.6 => /lib/i686/libc.so.6 (0x40025000)
       /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
                                                                                                                           

库找不到,当然运行出错,但是我们把libhello.so拷贝到/lib或者/usr/lib下面 就可以运行usrhello.为什么?

因为loader在默认情况下只会去/lib和/usr/lib中找寻共享函数库.不在这两个 目录需要加一个环境变量LD_LIBRARY_PATH,把你的目录加进去: $LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/myhome/mylib (注意第一个$是shell提示符号,第二个$才是自己输入的)然后不要忘了: $export LD_LIBRARY_PATH 还有另一个方法,在/etc/ld.so.conf里加入自己的目录,然后执行: $ldconfig -v

个主工具