|
沙发

楼主 |
发表于 2009-11-27 19:03:50
|
只看该作者
程序说明
movl $0, %eax
给 eax 送入 0 值,目的在于这个输入可以使 cpuid 输出厂商 ID 字符串。
cpuid
执行 CPUID 指令。
执行 cpuid 指令后,就要用 3 个输出寄存器 ebx, edx, ecx 来收集指令的相应信息:
movl $output, %edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)
上面的28, 32, 36 对应的是 "The processor Vendor ID is 'xxxxxxxxxxxx'\\n" 中的 'xxxxxxxxxxxx 的偏移位置。
movl $output, %edi 是创建了一个指针 edi ,output 标签的内存位置被加载到 EDI 中。这里需要注意,EBX, EDX, ECX 的顺序,这个顺序并不是按照 B,C,D 来排序,比较奇怪。
经过上面的几条指令后,就在内存中放置好厂商的 ID 字符串了,下面的几条指令用来显示这些信息:
movl $4, %eax
movl $1, %ebx
movl $output, %ecx
movl $42, %edx
int $0x80
第一条指令,把 4 放到 eax 寄存器中。这个 4 表示系统调用值。在较新的内核中,这些系统调用定义在:
arch/x86/include/asm/unistd_32.h
文件中。比如 4 号系统调用是:这里,每个系统调用都被定义为一个名称 (前面加上 __NR_) ,和它的系统调用号。
第二条指令,把 1 放到 ebx 寄存器中。这里的 1 是文件描述符,表示的标准输出 (STDOUT) .
所以,第 1 ,第 2 条指令合起来的意思是,调用 write() 系统调用,往标准输出上输出要显示的内容。
第三条指令,是字符串的开头送往 ecx 中。
第四条指令,是把字符串的长度送往 edx 中。
对比 write() 函数的原型:
write(int fd, const void *buf, size_t count);
从左到右,3 个参数分别对应着 ebx, ecx, edx 这 3 个寄存器中的值。可见:
最后,在显示了厂商的 ID 信息之后,就干净的退出程序。同样,Linux 系统调用可以为此提供帮助,这时使用系统 1 号调用 (#define __NR_exit 1) ,程序被正确的终止,并且返回到命令提示符。在使用 exit() 时,exit() 也有一个作为退出代码的参数,这个参数装载 ebx 寄存器中。
Linux 内核提供了许多可以很容易地从汇编应用程序访问的预置函数(如系统调用),为了访问这些内核函数,必须使用 int 指令码,它生成具有 0x80 值的软件中断。执行的具体函数由 EAX 寄存器中的值来确定。如果没有这个内核函数,就必须自己把每个输出字符发送到正确的显示器 I/O 地址,这无疑要花费很多的时间。 |
|