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