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

access array through .data variable or through register

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

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

首先你在函数的顶部有这个 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

这将打印出 array 中的第 4 个元素(基于 0)或您的情况下的值 5。输出看起来像:

out: 1
out: 2
out: 5