由cmp引起的分段错误

7

我目前正在学习汇编语言,但是遇到了困难。

假设我有以下C代码:

for ( int i = 100; i > 0; i-- ) {
  // Some code
}

现在我想用汇编语言做同样的事情。我尝试了这样做:
__asm__ ("movq $100, %rax;"
        ".loop:"
        //Some code
        "decq %rax;"
        "cmpq $0, (%rax);"
        "jnz .loop;"
);

编译并运行结果导致段错误。如果我删除cmpq行,则不会发生段错误。但是,程序当然也无法终止。

因此,我的问题基本上是我在这里做错了什么?


2
如果你正在编写内联汇编,那么纯粹的 asm 可能不是你想要的。也许你需要学习 extended asm。https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html。 - llllllllll
2
错误:1)如答案所述的内存访问 2)不同的循环类型(您的汇编是i = 100; do { .. } while (--i);,而不是for。3)根本不需要cmpdec指令也会设置零标志,因此decq %rax``jnz .loop就足够了。“将不会终止”=其他问题4)内联汇编比普通汇编更难,可能需要先学习x86-64汇编基础知识,然后再转向C中的内联汇编5)使用一些教程+书籍,至少要理解您的语法(“gas”或“AT&T”),最重要的是指令参考指南。...这对我来说是一个“低投入”的问题。 - Ped7g
只有一个注:如果您已经有了 C 代码,请使用编译器来查看它的编译情况。如果没有进行任何可观察的操作,要强制使用优化同时保留您的代码可能会很棘手,因此最好使用真实的应用程序代码进行检查,打印一些结果等,以避免优化器完全删除代码。 - Ped7g
3
除了实际答案之外,如果您在内联汇编中更改寄存器,则需要告诉编译器(特别是如果您打开了优化)。如果您修改内联汇编中的RAX,编译器可能会认为RAX在您的代码运行之前和之后是相同的。如果您进行修改,这可能会导致程序运行不正确。您需要查看GCC的扩展内联汇编文档。至少,您需要为所有修改的寄存器添加占位符。 - Michael Petch
@PeterCordes 当然可以...但是C语言中的for就是这个意思。:) 而且优化器会将其更改为do {} while,因为它很聪明,而不是因为C源代码是这样编写的。我的意思是,我们可能在所有方面都达成了一致,只是我从不同的角度写了出来? - Ped7g
显示剩余5条评论
3个回答

9
cmpq $0, (%rax)

这条指令将尝试读取rax中地址的内存。

第一次rax的值为99。地址99未映射,因此程序崩溃。

您打算将rax中的值与0进行比较,因此请删除括号。

cmpq $0, %rax

非常感谢,问题已解决。 - Giganull
不客气。请记得给有帮助的答案点赞,并接受解决您问题的答案。欢迎来到 Stack Overflow。 - Jonathon Reinhart
2
一个观察是 cmpq 可以完全消除。 - Michael Petch

8
以下指令:
cmpq $0, (%rax)

正在访问由rax寄存器指定的内存地址,其值为99

第一个内存页未被映射。该内存地址99属于第一个内存页。因此,上述访问导致分段错误。


您不需要间接引用,而是需要:

cmpq $0, %rax

也就是说,你想要比较的是rax寄存器中的内容,而不是rax指定内存地址中的内容


考虑优化cmp指令:

decq %rax紧接着cmp $0, %rax指令,如果rax为零,则设置ZF标志。条件跳转是基于ZF标志的状态执行的:

decq %rax
cmpq $0, %rax
jnz .loop
< p > dec 指令会影响 ZF 标志位(就像 cmp 一样),所以如果将 rax 减一的结果为零,ZF 将被设置。你可以利用这个事实,在 dec 之后直接放置 jnz。你根本不需要使用 cmp

decq %rax
jnz .loop

1
而你 * 真正 * 想要的是在已由 dec 设置的标志上进行 jnz - Peter Cordes
没错,整个指令可以被优化掉。 - JFMR
1
相关:当您需要重新测试寄存器是否为零/非零时,test %rax,%rax通常是最优的方法;它设置FLAGS与cmp $0, %rax相同。无论如何,现在已经点赞,因为您解决了XY问题的一部分,但只有MichaelPetch的评论解决了将其放入GNU C“Basic”asm(不安全,因为没有clobber列表)的问题。 - Peter Cordes

3

从%rax中去掉括号可以得到rax的值。添加括号基本上告诉汇编器,“嘿!rax保存了一个地址,请返回该地址中的内容”。因此,

cmpq $0, %rax

这是您需要做的事情。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接