汇编程序由定义好的段组成,一般有如下三个段:
1、 数据段
2、 BSS段
3、 文本段
所有汇编语言必须有文本段。数据段与BSS段是可选的。数据段一般是放置带有初始值的数据元素。BSS段一般使用0值或NULL值初始化的数据元素。这些区一般是局部变量区。
一般使用.section命令来声明段。.section后面跟上段的类型。一般布局如下
1、BSS段一定是在text段之前。
2、data段可以放在text段之后。但是一般的放法是按照图中所示。
一般的汇编程序的模板如下图所示:
在第一个程序中,我们使用汇编语言来得到关于CPU的一些信息。书上讲得太多,现在只关心一个功能,也就是CPU的字符串信息。
1、 首先把0值放入%eax寄存器中。
2、 cpuid指令根据%eax中的值,输出字符串至%ebx, %edx, %ecx三个字符串。
3、 再把这三个寄存器中的值依次从一个数组头开始放即可。相当于C语言中的char[12]的数组依次从头开始放,每次占四个字符。
可以写出代码如下:可以先编译着试一下。后面再看一下具体意思。
.section .bss .lcomm output, 12 .section .text .globl _start _start: movl $0, %eax cpuid movl $output, %edi movl %ebx, (%edi) movl %edx, 4(%edi) movl %ecx, 8(%edi) movl $4, %eax movl $1, %ebx movl $output, %ecx movl $12, %edx int $0x80 movl $1, %eax movl $0, %ebx int $0x80
编译:
$ as -o cpuid.o cpuid.s $ ld -o cpuid cpuid.o
$gcc -o cpuid cpuid.s
as是编译,ld是链接。
如果编译链接都没有出错,那么可以执行代码,可以得到以下结果
这里是因为代码中没有处理换行的原因。
可以看到可以正确地输出CPU字符串信息。
现在分段讲解一下程序中的各个功能:
movl $0, %eax cpuid
movl %ebx, (%edi) movl %edx, 4(%edi) movl %ecx, 8(%edi)
movl $4, %eax movl $1, %ebx movl $output, %ecx movl $12, %edx int $0x80
%eax指定系统调用号
%ebx指定要写入的文件描述符
%ecx指向要输出的字符串的开头
%edx指定要输出的字符串的个数
这个中断调用使用的是Linux的软中断功能。通过int $0x80来执行。
movl $1, %eax movl $0, %ebx int $0x80
使用gdb调试汇编的时候,需要额外在_start:加一条nop指令。并且如果断点要从开头开始,
需要执行break *_start+1代码如下:
.section .bss .lcomm output, 12 .section .text .globl _start _start: nop movl $0, %eax cpuid movl $output, %edi movl %ebx, (%edi) movl %edx, 4(%edi) movl %ecx, 8(%edi) movl $4, %eax movl $1, %ebx movl $output, %ecx movl $12, %edx int $0x80 movl $1, %eax movl $0, %ebx int $0x80
rabbit:/tmp # as -gstabs -o cpuid.o cpuid.s rabbit:/tmp # ld -o cpuid cpuid.o使用gdb命令:
$ gdb ./cpuid则会出现如下画面
接下输入break *_start+1,把断点设置在开头:再执行run命令,可以看到如下结果
接下来再介绍几个命令
s, n: 表示执行下一条指令
q表示退出gdb
s n; 表示执行接下来n条指令,同理n n亦然。后面的n表示数值。
info registers 输出所有寄存器信息。
print/x %ebx把%ebx寄存器按16进制输出。/t表示2进制,/d表示10进制。
x/nyz &memaddress, 输出内存地址memaddressr的n个字段的信息,
y的取值, c 表示char, d 表示10进制,x表示16进制。
z的取值, b表示byte, 8位,h表示16位,w表示32位。
下面表示了x指令的用法。
下面的代码示例如何使用C函数。
.section .data format: .asciz "%s\n" .section .bss .lcomm output, 12 .section .text .globl _start _start: movl $0, %eax cpuid movl $output, %edi movl %ebx, (%edi) movl %edx, 4(%edi) movl %ecx, 8(%edi) pushl $output pushl $format call printf addl $8, %esp pushl $0 call exit
$ as -o asc.o asc.s $ ld -dynamic-linker /lib/ld-linux.so.2 -lc -o asc asc.o
rabbit:/tmp # as --32 -o asc.o asc.s rabbit:/tmp # ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/libc.so -o asc asc.o rabbit:/tmp # ./asc GenuineIntel