cpuid.s程序使用Linux的系统调用(int $0x80)来显示字符串和退出程序,如果系统中装有C语言函数库的话,也可以使用C函数来达到同样的目的。
本文介绍如何在汇编语言中使用C语言的库函数,例子程序是那个显示CPU厂商信息的cpuid.s的改进版本cpuid2.s。在cpuid2.s中使用了标准C库的printf和exit函数来替换Linux的系统调用。例程如下:
#cpuid2.s -- Using C labrary calls
.section .data
output:
.asciz "The processor Vender is '%s'\n"
.section .bss
.lcomm buffer, 12
.section .text
.globl _start
_start:
movl $0, %eax
cpuid
movl $buffer, %edi
movl %ebx, (%edi) //含义同cpuid.s,向%edi所指向的buffer内存
movl %edx, 4(%edi) //位置写入3个寄存器的内容,这3个寄存器存储了
movl %ecx, 8(%edi) //CPU厂商信息的字符串(12字节)
pushl $buffer //将buffer和output入栈,为printf提供参数
pushl $output
call printf //调用C的printf函数
addl $8, %esp //将堆栈指针回滚8个字节,达到清除printf参数
//的目的
pushl $0
call exit
其中,.asciz是在定义字符串的时侯在字符串结尾加上空字符(即C语言的\0),这样做的目的是为了让printf能读懂字符串。.lcomm是在本地内存区域中声明固定长度的未初始化数据,这里初始化了12个字节的空间。
程序里buffer和output内存位置的内容是要向printf传递的参数值,一个是"The processor Vender is '%s'\n"字符串,另外一个是由cpuid返回结果(在ebx,edx,ecx三个寄存器中)填充的buffer。需要通过堆栈来传递参数,所以在程序中使用
pushl $buffer
pushl $output
将参数入栈,printf获取参数是自右向左,即先buffer后output,所以要把buffer后入栈。参数入栈之后,用call指令调用printf。exit的情况同上,使用了一个参数--常数0。
然后汇编连接程序:
$as -o cpuid2.o cpuid2.s
$ld -o cpuid2 cpuid2.o
cpuid2.o: In function '_start':
cpuid2.o(.text+0x3f): undefined reference to 'printf'
cpuid2.o(.text+0x46): undefined reference to 'exit'
$
汇编的时侯没有问题,但是按原来的方式连接程序的时侯出现错误了。原因在于ld不会自己去寻找C库,必须手动指定C库文件来连接程序。使用命令-l,此-l和gcc的-l基本相同,-l的参数是库的名字(比如libc.so中c就是库的名字)而不是库文件的全名
$ld -o cpuid2 -lc cpuid2.o
$./cpuid2
bash: ./cpuid2: No such file or directory
$
连接没有问题,但是运行程序的时侯又出问题了。这是因为由于采用了动态连接的方式,所以C函数没有包含在可执行程序中,需要由另外的程序在运行时加载,ld不知道这个程序在哪里,所以我们还得手动指定
$ld -dynamic-linker /lib/ld-linux.so.2 -lc -o cpuid2 cpuid2.o
$./cpuid2
The processor Vender is 'GenuineIntel'
$
运行成功了,其中ld-linux.so.2是动态加载器,用来查找libc.so
总结:
如果汇编程序中使用了外部的C库函数,汇编连接程序的时侯需要指定相关的参数以连接库,相比之下GCC编译C程序的时侯很多事情都是自动完成的,要方便很多。
hl_linux 于 2006-08-25 23:50:44发表:
只看懂了"GCC编译C程序的时侯很多事情都是自动完成的,要方便很多",看来C真是太重要了.
pengxin 于 2006-08-25 16:58:56发表:
can't understand!
坏坏大少 于 2006-08-23 21:27:25发表:
虽然事看不懂 但也帮顶~~~~~~