本文笔记学习自《Professional Assembly Language》
静态库
当我们将不同的函数写在不同的文件中,如果主程序调用这些函数,就需要相应的目标文件才能生成可执行文件。目标文件很多的话,不便于管理。我们可以将目标文件整合到一个存档文件中,编译器从存档文件挑选出需要的目标文件,这样的存档文件称之为库文件(library file)。
假设库文件的目标代码被编译到了主程序中,就称之为静态库文件(windows下对应着LIB文件)。
linux下有ar工具可以创建静态库文件。
ar - create, modify, and extract from archives
ar [--plugin name] [-X32_64] [-]p[mod [relpos] [count]] archive [member...]
以计算圆的面积areafunc.s进行说明。
areafunc.s:
.section .text
.type areafunc,@function
.globl areafunc
areafunc:
pushl %ebp
movl %esp,%ebp #store esp register
fldpi #store PI at st(0)
filds 8(%ebp) #store first parameter at st(0) and PI moved to st(1)
fmul %st(0),%st(0)
fmul %st(1),%st(0) #make sure st(0) has current anwser.
movl %ebp,%esp
popl %ebp
ret
areafunc.c:
#include <stdio.h>
float areafunc(int);
int main(){
float result;
result = areafunc(1);
printf("result is %f\n",result);
result = areafunc(10);
printf("result is %f\n",result);
return 0;
}
生成目标文件 gcc -c areafunc.s -o areafunc.o
linux操作系统下的静态库文件通常命名为libx.a
,x是自己的文件命名,.a表示这是一个静态库文件。
使用ar生成库文件liball.a:
下面我用所有的.o文件生成一个库文件。
[edemon@CentOS workspace]$ ls *.o
areafunc.o area.o ftest.o movl.o mov.o quadtest.o
[edemon@CentOS workspace]$ ar -r liball.a *.o
ar: creating liball.a
[edemon@CentOS workspace]$ file liball.a
liball.a: current ar archive
[edemon@CentOS workspace]$ ar -t liball.a
areafunc.o
area.o
ftest.o
movl.o
mov.o
quadtest.o
创建索引提高连接速度: ranlib liball.a
再介绍一个工具nm
nm - list symbols from object files
nm [-a|--debug-syms]
[-g|--extern-only][--plugin name]
[-B] [-C|--demangle[=style]] [-D|--dynamic]
[-S|--print-size] [-s|--print-armap]
[-A|-o|--print-file-name][--special-syms]
[-n|-v|--numeric-sort] [-p|--no-sort]
[-r|--reverse-sort] [--size-sort] [-u|--undefined-only]
[-t radix|--radix=radix] [-P|--portability]
[--target=bfdname] [-fformat|--format=format]
[--defined-only] [-l|--line-numbers] [--no-demangle]
[-V|--version] [-X 32_64] [--help] [objfile...]
可查看函数所在文件位置
[edemon@CentOS workspace]$ nm -s liball.a
Archive index:
areafunc in areafunc.o
area in area.o
main in ftest.o
_start in movl.o
_start in mov.o
_start in quadtest.o
areafunc.o:
00000000 T areafunc
area.o:
00000000 T area
ftest.o:
U area
00000000 T main
00000000 d precision
00000002 d report
movl.o:
00000000 T _start
00000000 d value
mov.o:
00000000 T _start
quadtest.o:
00000000 T _start
00000000 d data1
00000014 d data2
使用静态库进行编译:
[edemon@CentOS workspace]$ gcc areafunc.c liball.a -o areafunc
[edemon@CentOS workspace]$ ./areafunc
result is 3.141593
result is 314.159271
因为gcc是提取所需函数代码得到可执行文件,所以areafunc的大小并不会因为使用库文件编译而变大。
[edemon@CentOS workspace]$ gcc areafunc.c areafunc.o -o areafunc1
[edemon@CentOS workspace]$ du -h areafunc areafunc1
8.0K areafunc
8.0K areafunc1
共享库
和静态库文件不同,有一类存档文件的目标代码不被编译到可执行文件中,主程序不用把函数代码加入它的内存空间,这类存档文件称之为共享库文件。
Linux操作系统的命名格式为libx.so,其中x是自己的文件命名,.so表明这是共享库(windows下对应着DLL文件)。
静态编译(将函数代码连接到可执行文件)得到的可执行文件,我们使用objdump进行反汇编,可以查看函数调用的痕迹。
以areafunc为例:
[edemon@CentOS workspace]$ objdump -D areafunc > read
[edemon@CentOS workspace]$ vim read
#查找含有areafunc的地方:/areafunc
345 8048432: e8 65 00 00 00 call 804849c <areafunc>
359 8048462: e8 35 00 00 00 call 804849c <areafunc>
379 0804849c <areafunc>:
380 804849c: 55 push %ebp
381 804849d: 89 e5 mov %esp,%ebp
382 804849f: d9 eb fldpi
383 80484a1: df 45 08 fild 0x8(%ebp)
384 80484a4: d8 c8 fmul %st(0),%st
385 80484a6: d8 c9 fmul %st(1),%st
386 80484a8: 89 ec mov %ebp,%esp
387 80484aa: 5d pop %ebp
388 80484ab: c3 ret
以共享库编译方式得到的可执行文件是不含有函数代码的。
共享库编译方式shell> gcc sourcefile -Ldir -llibname -o executefile
-L 告诉编译器库文件在哪个位置(假设不在系统定义的位置)
-l 后跟上库的名字(x)
生成共享库文件:
[edemon@CentOS workspace]$ gcc -shared -o libshared.so areafunc.o area.o ftest.o
[edemon@CentOS workspace]$ file libshared.so
libshared.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped
生成可执行文件:
[edemon@CentOS workspace]$ gcc -o areafunc2 -L. -lshared areafunc.c
[edemon@CentOS workspace]$ ./areafunc2
result is 3.141593
result is 314.159271
再次反汇编,查找函数areafunc的踪迹:
[edemon@CentOS workspace]$ objdump -D areafunc2 > read
[edemon@CentOS workspace]$ vim read
361 08048440 <areafunc@plt>:
362 8048440: ff 25 b8 98 04 08 jmp *0x80498b8
363 8048446: 68 10 00 00 00 push $0x10
364 804844b: e9 c0 ff ff ff jmp 8048410 <_init+0x30>
505 80485a2: e8 99 fe ff ff call 8048440 <areafunc@plt>
519 80485d2: e8 69 fe ff ff call 8048440 <areafunc@plt>
可以发现,函数的代码并没有在这份反汇编后的文件中。
ldd可查看依赖的共享库:
[edemon@CentOS workspace]$ ldd areafunc2
linux-gate.so.1 => (0x00bb1000)
libshared.so (0x00395000)
libc.so.6 => /lib/libc.so.6 (0x00397000)
/lib/ld-linux.so.2 (0x001ee000)
可执行文件需要Linux动态加载器(/lib/ld-linux.so.2)自动加载所需的函数。
假设动态加载器找不到库文件的位置,怎么办?
系统文件/etc/ld.so.conf
含有定位共享库文件位置的信息,我们改变它(增添位置信息),再使用ldconfig
更新。还有一种添加库文件位置信息的方法,就是更改LD_LIBRARY_PATH
变量,例如:
[edemon@CentOS workspace]$ echo $LD_LIBRARY_PATH
/home/edemon/workspace/
[edemon@CentOS workspace]$ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/home/edemon/"
[edemon@CentOS workspace]$ echo $LD_LIBRARY_PATH
/home/edemon/workspace/:/home/edemon/