分段错误:11 使用 x86 GNU GAS 程序集在循环中进行数组分配

Segmentation fault: 11 With Array Assignment in Loop Using x86 GNU GAS Assembly

这个问题类似于我发布的另一个问题 。我正在尝试在 c/c++:

中编写以下程序集版本
int x[10];
for (int i = 0; i < 10; i++){
 x[i] = i;
}

本质上,创建一个存储值 19 的数组。

我目前的逻辑是创建一个循环到 10 的标签(调用自身直到达到最终值)。在标签中,我放置了在当前迭代索引处更新数组的说明。但是,在使用 gcc filename.s 和 运行 ./a.out 进行编译后,错误 Segmentation fault: 11 会打印到控制台。我的代码如下:

.data
  x:.fill 10, 4
  index:.int 0
  end:.int 10

.text
.globl _main
_main:
  pushq %rbp
  movq %rsp, %rbp
  subq , %rsp
  jmp outer_loop
  leave
  ret

outer_loop:
 movl index(%rip), %eax;
 cmpl end(%rip), %eax
 jge end_loop
 lea x(%rip), %rdi;
 mov index(%rip), %rsi;
 movl index(%rip), %eax;
 movl %eax, (%rdi, %rsi, 4)
 incl index(%rip)
 jmp outer_loop
 leave
 ret

end_loop:
  leave
  ret

奇怪的是下面的代码

lea x(%rip), %rdi;
mov index(%rip), %rsi;
movl index(%rip), %eax;
movl %eax, (%rdi, %rsi, 4)

仅当它不在重复调用的标签中时才有效。有谁知道我如何在不引发 Segmentation fault: 11 的情况下在循环中实现上面的代码?我在 MacOS 上使用 x86 程序集,GNU GAS 语法是用 gcc.

编译的

请注意,这个问题不是 this 问题的重复,因为使用了不同的汇编语法,问题的范围也不同。

您正在使用 64 位指令访问 32 位内存区域:

mov index(%rip), %rsi;

这导致 %rsi 被分配了从 indexend 的内存内容(我假设没有对齐,虽然我不记得 GAS 的规则关于它)。因此,%rsi 有效地分配了值 0xa00000000(假设循环的第一次迭代),并且执行以下 movl %eax, (%rdi, %rsi, 4) 导致 CPU 尝试访问不是的地址由您的进程映射。

解决方案是删除赋值,并将其后的行替换为 movl index(%rip), %esi。 32 位操作保证始终清除 64 位寄存器的高位,因此您可以在地址计算中安全地使用 %rsi,因为它将包含当前索引,仅此而已。

你的调试器会告诉你这个,所以请下次使用它。