# array.s
.section .rodata
msg:
.asciz "The value is: %d.\n"
array:
.int 5, 10, 15, 20, 25
.section .text
.globl main
main:
movl $0, %edi
loop1:
pushl array(, %edi, 4)
pushl $msg
call printf
addl $8, %esp
inc %edi
cmp $5, %edi
jne loop1
pushl $0
call exit
第 7 行声明了 5 个整数,它们在内存里的位置是连续的,通过起始位置 array 可以顺序访问每一个值。第 12 行把寄存器 edi 初始化为 0。edi 和 esi 也是通用寄存器,它们经常成对出现,多用于字符串操作,esi 表示源字符串的地址,edi 表示目的地址。
循环中依次打印每个数组元素的值。第 15 行采用了一个访问连续相同大小元素的方法:
base_address (offset, index, size)
得到要访问的元素的起始地址为
base_address + offset + index * size
括号内的三个元素中如果某个值为 0 可以不写。第一次执行第 15 行的语句时,因为 edi 的值为 0,得到的内存位置是 array + 0 * 4 = array,即第一个元素的值,以后随着 edi 的递增依次访问后面的元素。
第 19 行把 edi 的值加 1。对某个值进行递增递减是常见的操作,AT&T 汇编中为此提供了单独的 inc 指令,另外还有 dec 指令用于递减的操作,有点类似于 c 语言中的“++”和“–”操作符。另外 inc 和 dec 不会影响进位标志(CF),这个不同于使用 add 和 sub 指令。
上面这段程序有点像下面的 c 语言程序:
#include <stdio.h>
int main()
{
register int i;
int array[] = {5, 10, 15, 20, 25};
for (i = 0; i < 5; ++i)
printf("The value is: %d.\n", array[i]);
return 0;
}
另外一种方法可以使用寄存器间接寻址,也就是 c 语言中的“指针”来访问。
# ptr.s
.section .rodata
array:
.int 5, 10, 15, 20, 25
msg:
.asciz "The value is: %d.\n"
.section .text
.globl main
main:
movl $array, %esi
movl $msg, %edi # end position
loop1:
pushl (%esi)
pushl $msg
call printf
addl $8, %esp
addl $4, %esi
cmp %esi, %edi
jne loop1
pushl $0
call exit
第 12 行把 array 的起始地址保存到 esi 中。第 16
行采用了寄存器间接寻址的方法:esi 中保存的是 array 的地址,“(%esi)”表示取 esi 中的地址所在的内容。第 21 行把地址递增 4,指向下一个元素。这类似于下面的 c 程序:
#include <stdio.h>
int main()
{
int array[] = {5, 10, 15, 20, 25};
register int *ptr, *end;
for (ptr = array, end = array + 5; ptr != end; ++ptr)
printf("The value is: %d.\n", *ptr);
return 0;
}
如果元素的大小不相同,要访问当前地址的某个偏移量的值,可以采用基址+偏移量的方法。在 AT&T
的语法中不允许把值与寄存器直接相加,而要把值放在括号外。
# offset.s
.section .rodata
int64:
.quad 120
int32:
.int 5
msg:
.asciz "The second value is: %d.\n"
.section .text
.globl main
main:
movl $int64, %eax
pushl 8(%eax)
pushl $msg
call printf
addl $8, %esp
pushl $0
call exit
第 4,5 行声明了一个 64 位的整数 int64,接着是一个 32 位的整数 int32。第 14 行把 int64 的地址放到 eax 中,接着第 15 行的“8(%eax)”表示 eax 保存的地址之后的 8 个字节的位置,也就是 int32 的起始地址。最后打印出 int32 的值。如果想要定位当前地址之前的位置只需把括号左边的正数换成负数即可。
offset.s 是否可以变成这样:
#offset.s
.section .rodata
int32:
.int 5
msg:
.asciz “The second value is: %d.\n”
.section .text
.globl main
main:
movl $int32, %eax
pushl (%eax)
pushl $msg
call printf
addl $8, %esp
pushl $0
call exit
这样就相当于去除了int64这个变量。因为看不出int64有什么作用。更不知道
.quad是什么意思,能解释下吗?
.quad表示64位整数,.int是32位整数。文件名叫offset.s,”int64″只是为了演示“基址+偏移量”这个语法而加上的一个偏移量而已,或者”int64″改名叫”offset64″比较贴切 :)
明白了,谢谢!