C组装:从%eax返回的值超出跳转指令错误:“:”标记前应为“)”

46qrfjad  于 2023-01-29  发布在  其他
关注(0)|答案(1)|浏览(116)

在以下c函数中

1

int check()
{
__asm__ __volatile__ (
   <snip some activity that has a jump to not_supported>
   "movl $1, %eax \n\t"       \
   "jmp done \n\t"            \
"not_supported:\n\t"          \
   "movl $0, %eax \n\t"       \
“done:\n\t”
 );
}

返回值存储在eax寄存器中
这在gcc(Ubuntu 7.5.0- 3ubuntu 1 ~18.04)7.5.0上编译得很好,但是在其他地方由于werror的执行而抱怨

error: no return statement in function returning non-void [-Werror=return-type]

因此,为了使gcc可以接受werror,我添加了一个堆栈变量作为#1的返回值

int check()
 {
   int ret_value =0;

__asm__ __volatile__ (
         <snip some activity that has a jump to not_supported>
         "movl $1, %0 \n\t" : "=a"(ret_value) ::  \
         "jmp done \n\t"                          \
"not_supported:\n\t"                              \
         "movl $0, %0 \n\t" : "=a"(ret_value) ::  \
 "done:\n\t"
  );
 
 return ret_value;
}

gcc不允许编译,即使是非werror情况:

: error: expected ‘)’ before ‘:’ token 
    "movl $0, %0 \n\t" : "=r"(ret_value) :: \

它首先抱怨:在movl指令中
我也试过用“=r”寄存器操作数约束输出,但仍然不能编译。我也试过显式地将clobber寄存器指定为“eax”,但也没有帮助。
似乎gcc在jmp之后抱怨ret_value修改。
我尝试的另一件事是#1,另一个moveaxret_val,这在逻辑上对我来说没有意义。(我的意思是在done:之后添加一个movl指令,将%eax的值移动到%0,即ret_val),这也没有编译。
我错过什么了吗?

chhqkbe1

chhqkbe11#

@margaretBloom的建议很有帮助。我删除了多个输出操作数,只在最后保留了一个。这有助于编译,但反汇编后的输出如下所示:

0x000055555555519f <+53>: jmp    0x5555555551a6 <dummy_funct+60>
   0x00005555555551a1 <+55>:    mov    $0x0,%eax
   0x00005555555551a6 <+60>:    mov    %edx,-0x4(%rbp)
   0x00005555555551a9 <+63>:    mov    -0x4(%rbp),%eax
   0x00005555555551ac <+66>:    pop    %rbp
   0x00005555555551ad <+67>:    retq

这里编译器试图将堆栈变量的内容写入eax,从而覆盖了原来存储在eax中的真实的输出。
通过进一步的探索和错误修复,以下内容可以正常工作:

"movl $1, %%eax \n\t"  \
   "movl %%eax, %0 \n\t"  \
   "jmp done \n\t"   \
not_supported:\n\t" \
   "movl $0, %%eax \n\t"  \
   "movl %%eax, %0 \n\t"  \
"done:\n\t"          \
   : "=r" (ret_value)
   :
   :"%eax", "%ebx", "%ecx"
);

诀窍是在eax中记录结果,然后显式地将这些结果复制到返回值中沿着并进行适当的重写。另一件事是为指令提供寄存器操作数,而不是寄存器本身。

相关问题