分段错误:11使用x86 GNU GAS程序集在循环中分配阵列

时间:2018-07-10 18:21:40

标签: gcc assembly x86 gnu gas

该问题与我发布的here相似。我试图在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 $16, %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问题的重复,因为正在使用不同的汇编语法,并且问题的范围也不同。

1 个答案:

答案 0 :(得分:3)

您使用的是64位指令来访问32位内存区域:

mov index(%rip), %rsi;

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

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

您的调试器会告诉您这一点,因此请下次使用它。