linux平台学x86汇编(二十):汇编库的使用

【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】
        汇编语言和C一样,可以通过使用库来简化阻止大量函数的目标文件的问题。GNU C编译器可以不在命令行中独立地包含每个独立地函数目标文件,它允许吧所有目标文件组合在单一存档文件中。在编译C程序时,要做的工作就是包含单一的目标库文件,在编译时,编译器可以从库文件中挑出所需的正确目标文件。在库文件中,经常按照应用程序类型或者函数类型把函数分组在一起,单一应用程序项目可以使用多个库文件。
        库又分静态库和动态库。静态库文件中包含的目标代码会被编译到主程序中,程序的运行就不需要库文件了。动态库文件的包含的目标代码不会在编译时编译到主程序中,而是主程序在运行时调用该部分代码,这样可以节省内存需求。
        ar命令可以创建静态库文件。下面为示例所用到的所有文件:
$ ls
add.s  libcal.a  main.c  Makefile  prt.s  sub.s
$
add.s文件包含2个汇编函数,sub.s包好2个汇编函数,prt.s包含1个汇编函数。现在将这三个文件打包为一个库文件libcal.a,在编译可执行文件是连接该库文件。下面分别为上面文件的内容:
add.s文件:
# add.s
.type add, @function
.globl add
add:    # add two integer
    pushl %ebp
    movl %esp, %ebp

    movl 8(%ebp), %eax
    addl 12(%ebp), %eax

    movl %ebp, %esp
    popl %ebp
    ret

.type add_inc, @function
.globl add_inc
add_inc:   # add 1 to int parameter
    pushl %ebp
    movl %esp, %ebp

    movl 8(%ebp), %eax
    inc %eax

    movl %ebp, %esp
    popl %ebp
    ret

sub.s文件:
# sub.s
.type sub, @function
.globl sub
sub:    # sub two integer
    pushl %ebp
    movl %esp, %ebp

    movl 8(%ebp), %eax
    subl 12(%ebp), %eax

    movl %ebp, %esp
    popl %ebp
    ret

.type sub_dec, @function
.globl sub_dec
sub_dec:   # sub 1 to int parameter
    pushl %ebp
    movl %esp, %ebp

    movl 8(%ebp), %eax
    dec %eax

    movl %ebp, %esp
    popl %ebp
    ret

.type print_hello, @function
.section .data
msg:
    .ascii "hello world!\n"
    len=.-msg
.section .text
.global print_hello
print_hello:
    movl $len, %edx
    movl $msg, %ecx
    movl $1, %ebx
    movl $4, %eax
    int $0x80
    movl $0, %ebx
    movl $1, %eax
    int $0x80

C文件:
# main.c
#include <stdio.h>

int add(int, int);
int add_inc(int);
int sub(int, int);
int sub_dec(int);
int print_hello(void);

int main(int argc, const char *argv[])
{
    int ret;

    ret = add(1989, 711);
    printf("The add() return is %d.\n", ret);

    ret = add_inc(1989);
    printf("The add_inc() return is %d.\n", ret);

    ret = sub(1989, 711);
    printf("The sub() return is %d.\n", ret);

    ret = sub_dec(1989);
    printf("The sub_dec() return is %d.\n", ret);

    print_hello();

    return 0;
}

Makefile:
# Makefile for linux as

CFLAGS= -Wall -g
ASFLAGS= -gstabs

SRC_BIN=target_bin

SRC_LIB=libcal.a

SRC_C=$(wildcard *.c)
SRC_S=$(wildcard *.s)

SRC_COBJ=$(SRC_C:.c=.o)
SRC_SOBJ=$(SRC_S:.s=.o)

SRC_OBJ=$(SRC_COBJ) $(SRC_SOBJ)

all: $(SRC_BIN)

$(SRC_BIN): $(SRC_COBJ) $(SRC_LIB)
 $(CC) -o $@ $(SRC_COBJ) -L./ -lcal

$(SRC_LIB) : $(SRC_SOBJ)
 ar rcs $@ $^

clean:
 $(RM) $(SRC_OBJ) $(SRC_BIN) *~
.PHONY:
 all clean


下面为执行输出结果:
$ make 
cc -Wall -g   -c -o main.o main.c
as -gstabs  -o add.o add.s
as -gstabs  -o prt.o prt.s
as -gstabs  -o sub.o sub.s
ar rcs libcal.a add.o prt.o sub.o
cc -o target_bin main.o -L./ -lcal
$ ./target_bin 
The add() return is 2700.
The add_inc() return is 1990.
The sub() return is 1278.
hello world!
$

使用 ar命令t参数可以查看库文件包含的目标文件。
$ ar t libcal.a
add.o
prt.o
sub.o
nm命令可以显示每个函数名,并且显示哪个目标文件包含哪个函数。
$ nm -s libcal.a
Archive index:
add in add.o
add_inc in add.o
print_hello in prt.o
sub in sub.o
sub_dec in sub.o
add.o:
00000000 T add
0000000d T add_inc
prt.o:
0000000d a len
00000000 d msg
00000000 T print_hello
sub.o:
00000000 T sub
0000000d T sub_dec
        当应用程序和静态库一起编译时,函数代码被编译到了应用程序中,也就是说程序所需所有代码都在可执行文件中。但是如果函数代码内容有改动,那么使用次函数的每个应用程序都必须使用新的版本程序编译,并且系统上多个程序可能会使用相同函数,那么内存会多次把相同函数加载到内存中。
       而共享库包含目标代码的文件被加载到操作系统的通用区域,当应用程序需要访问共享库中的函数时,操作系统自动把代码加载到内存中,允许应用程序访问它。当另一个应用程序也需要使用次函数代码时,操作系统允许它访问已经被加载到内存中的相同函数代码,这里只有函数代码的一个拷贝加载到了内存,使用该函数代码的每个程序都不会再把它加载到内存中。如果函数需要改动,只需要更新库文件就可以了,不需要重新编译新的新的应用程序。
        现在我们将上面静态库的例子改为动态库来实现。
修改Makefile如下:
$ cat Makefile.static
# Makefile for linux as
CFLAGS= -Wall -g
ASFLAGS= -gstabs
SRC_BIN=target_bin
SRC_LIB=libcal.a
SRC_C=$(wildcard *.c)
SRC_S=$(wildcard *.s)
SRC_COBJ=$(SRC_C:.c=.o)
SRC_SOBJ=$(SRC_S:.s=.o)
SRC_OBJ=$(SRC_COBJ) $(SRC_SOBJ)
all: $(SRC_BIN)
$(SRC_BIN): $(SRC_COBJ) $(SRC_LIB)
 $(CC) -o $@ $(SRC_COBJ) -L./ -lcal
$(SRC_LIB) : $(SRC_SOBJ)
 ar rcs $@ $^
clean:
 $(RM) $(SRC_OBJ) $(SRC_BIN) *~
.PHONY:
 all clean
编译如下:
$ make -f Makefile.share
cc -Wall -g   -c -o main.o main.c
as -gstabs  -o add.o add.s
as -gstabs  -o prt.o prt.s
as -gstabs  -o sub.o sub.s
gcc -shared -o libcal.so add.o prt.o sub.o
cc -o target_bin main.o -L./ -lcal
使用ldd命令可以查看应用程序依赖的库文件:
$ ldd target_bin
 linux-gate.so.1 =>  (0x00dac000)
 libcal.so => not found
 libc.so.6 => /lib/libc.so.6 (0x00a42000)
 /lib/ld-linux.so.2 (0x00a1c000)
发现ldd命令显示程序需要共享库文件libcal.so,并且没有找到它。此时试图运行会出现如下错误:
$ ./target_bin
./target_bin: error while loading shared libraries: libcal.so: cannot open shared object file: No such file or directory
出现该错误是因为动态加载器不知道如何访问共享库libcal.so。文件/etc/ld.so.conf文件保存动态加载器在那些目录中查找目录清单,笔者系统上该文件如下:
$ cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
即包含目录 ld.so.conf.d/下的所有conf文件。
$ ls /etc/ld.so.conf.d/
atlas-i386.conf  kernel-2.6.32-358.el6.i686.conf  mysql-i386.conf  qt-i386.conf  xulrunner-32.conf
上面每个配置文件就是对于每个应用查找其对应共享库的路径。
        一般做法是为应用程序创建单独的目录,并把目录添加到文件ld.so.conf。所以只需要将要查找的的共享库文件的目录添加到ld.so.conf文件中来就可以了。可以效仿上面的已有的例子,将路径放在/etc/ld.so.conf.d/目录下的一个.conf文件中,然后使用ldconfig命令更新文件ld.so.cache。最后再运行程序如下:
# ./target_bin
The add() return is 2700.
The add_inc() return is 1990.
The sub() return is 1278.
hello world!
本系列linux汇编学习到此为止,在这二十节的内容中,可能没有讲解到汇编的所有方面,但还是把汇编语言的比较常用的基本知识通过一些很简单很基础的示例展示了出来,我希望读者在看完本系列文章之后有所有收获,也希望读者在阅读文章是能指出其中的错误和不足。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。