##承接上一篇
**4.4**在汇编语言中使用C语言库函数
上一个程序使用Linux系统调用显示CPU厂商信息。还有一种不使用系统调用的方法,那就是使用C语言库函数。
接下来通过调用C库函数的printf()函数,编写一个调用printf()函数的新cpuid.s。
*4.4.1*使用printf
C库包含很多C程序通用的很多函数。比如exit()和printf()。新的cpuid.s中的Linux系统调用将被C库函数所替代。
clib_cpuid.s如下:
.section .data
output:
.asciz “The cpu id ‘%s’\n”
.section .bss
. lcomm buffer, 12
.section .text
.globl _start
_start:
movl $0 ,%eax
cpuid
movl $buffer, %edi
movl %ebx, (%edi)
movl %edx, 4(%edi)
movl %ecx, 8(%edi)
pushl $output
pushl $buffer
call printf
addl $8, %esp
pushl $0
call exit
编译 & 连接:
$ as -o clib_cpuid.o clib_cpuid.s
$ ld -dynamic-linker /lib/l-linux.so.2 -o clib_cpuid -lc clib_cpuid.o
如果你是32bit系统,不出意外可以成功的运行的。
接下来解释下。
printf()函数需要两个参数,第一个是输出的字符串,这里字符串是带有显示变量的字符串。
而且printf函数要求以空字符结尾的字符串。.asciz命令定义的字符串末尾是空字符。
接下来在.bss段创建了一个缓冲区,这个缓冲区将要包含cpuid的信息。使用.lcomm命令将其声明为12字节的缓冲区。在将CPUID信息传递到缓冲区的过程和原来的程序的过程是一样的。
为了使用C库函数printf,需要使用pushl将需要的参数入栈。
需要注意的是入栈顺序和printf函数的参数表顺序是相反的。即就是首先将缓冲区buffer入栈,随后是output入栈。入栈完成之后,使用call指令调用printf函数。addl指令用于清空printf函数放入堆栈的指令。使用exit函数的技术和printf函数相同。
*4.4.2*连接C库函数
简单地说,在汇编语言程序中调用C库函数,必须把C库文件连接到程序目标代码。如果C库函数不可用,显然要报错。
Linux系统上,把C函数连接到汇编语言方法有两种。第一种称为静态连接:将函数目标代码直接连接到程序可执行文件中。这样造成创建了巨大的可执行程序,如果同时运行程序的多个实例(也就是进程),将会造成内存的浪费。第二种称为动态链接:动态链接使用库的方式允许程序员可以在应用程序中引用函数。但是不把函数代码连接到可执行程序文件,程序运行时由操作系统调用动态链接库。还有个好处是多个程序可以共享动态链接库。
Linux系统中,标准的C动态链接库位于lib.so.x,x代表版本。这个库文件包含了标准C函数:包括printf和exit。
当我们使用gcc时,自动帮我们连接到了动态链接库。但是在这里我们需要手动地将动态链接库连接到程序目标代码以便C函数可以操作。
GNU连接器使用 -l 参数,在使用这个参数时不需要指定库名称,默认的库是 :/lib/libx.so。但是还必须指定在运行时加载动态库的程序。Linux中这个程序是ld-linux.so.2,位于/lib下。为了指定这个程序,使用GNU连接器参数:dynamic-linker。如下:
$ ld -dynamic-linker /lib/ld-linux-so.2 -o cpuid -lc cpuid.o
这个才可以保证程序征程运行。它使用ld-linux-so.2动态加载程序加载了libc.so库。
当然也可以使用gcc来自动编译和链接汇编语言和C库函数,gcc自动链接必须的C库。做出如下修改,即可实现:将_start改为main,然后编译:
$ gcc -o cpuid cpuid.s
同样可以运行并且得到相同的结果。
————————第四章完结—————