我有一个数组,我将这个数组的基地址加载到%esi寄存器中。我可以访问数组中的第一个元素,例如:pushl 0(%esi)
,第二个像这样:pushl 4(%esi)
,依此类推。但是如果我用.data部分中的变量替换(%esi)
之前的数字,我会遇到分段错误:
.data
array: .long 1,2,3,4,5
k: .long 4
out: .string "out: %d\n"
.globl main
main:
#load array-baseaddress into %esi
leal array, %esi
# Will print 1
pushl 0(%esi)
pushl $out
call printf
# Will print 2
pushl 4(%esi)
pushl $out
call printf
# Will result in segmentation fault
pushl k(%esi)
pushl $out
call printf
call exit
有人可以向我解释为什么这不起作用,以及是否可以这种方式访问数组元素?
答案 0 :(得分:0)
首先,你在函数 main 的顶部有这个:
main:
# Will print 1
pushl 0(%esi)
pushl $out
call printf
第一个问题是您使用索引寻址(带位移)和 ESI 中的值作为地址。所以你有相当于内存地址 ESI + 0 的值。问题是您没有初始化 ESI 。您应该使用 array 的地址对其进行初始化。所以将代码更改为:
main:
mov $array, %esi /* initialize ESI with address of array. */
# Will print 1
pushl 0(%esi)
pushl $out
call printf
在代码的最后一部分中:
# Will result in segmentation fault
pushl k(%esi)
pushl $out
call printf
当您执行k(%esi)
时,您希望将变量 k 中的值用作位移。不幸的是,索引寻址(带位移)仅支持位移作为常量。在您的情况下,k(%esi)
将获取 k 的地址并将其添加到 ESI 并推送堆栈上该地址的值。那不是你想要的。您无法在一次操作中执行此操作。您必须检索变量 k 中的值并将其放入空闲寄存器,然后使用该寄存器使用基于索引的寻址模式计算地址。您可以使用以下代码执行此操作:
movl k, %eax /* move 32-bit value in k to temporary register */
pushl (%esi, %eax) /* Push 32-bit value @ memory location %esi+%eax to stack */
pushl $out
call printf
输出如下:
out: 1
out: 2
out: 2
从代码中可以确定 k 是否在数组中保存了一个元素编号,或者它是否只是数组中的一个字节偏移量。如果您要访问数组中的 k 元素(基于0),那么您需要在基于索引的寻址模式上使用缩放因子,如下所示:
movl k, %eax /* move 32-bit value in k to temporary register */
pushl (%esi, %eax, 4) /* Push 32-bit value @ memory location %esi+(%eax*4) to stack */
pushl $out
call printf
这将打印出数组中的第4个元素(基于0)或您的案例中的值5
。输出看起来像:
out: 1
out: 2
out: 5