通过.data变量或通过寄存器访问数组

时间:2015-11-12 12:24:21

标签: assembly x86 att

我有一个数组,我将这个数组的基地址加载到%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

有人可以向我解释为什么这不起作用,以及是否可以这种方式访问​​数组元素?

1 个答案:

答案 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