Search code examples
cassemblyx86inline-assembly

C Assembly : Return value from %eax beyond jump instruction error: expected ‘)’ before ‘:’ token


In following c function

#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”
 );
}

Return value is stored in the eax register

This compiles fine on gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 But complains in other place due to werror enforcement

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

So to make it acceptable to gcc with werror I added a stack variable as a return value to #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;
}

The gcc doesn’t allow this to compile even with non werror case :

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

This complains at the first : in the movl instruction

I tried with the “=r” register operand constraints for output as well but still it doesn’t compile. I also tried explicitly giving clobber registers as "eax" but that also doesn't help.
It seems gcc complains about ret_value modification after the jmp .

Other thing I tried was #1 with another mov from eax to ret_val which logically didn’t make sense to me. (What I mean is adding a movl instruction after the done: that moves value of %eax to %0 which is ret_val) And that didn’t compile either.

Any thing I am missing here ?


Solution

  • @margaretBloom's suggestion helped. I removed multiple mentions of output operands and just kept one at the end. This helped compile but here's the disassembled output looks like :

      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   
    

    Here the compiler is trying to write the stack variable contents to eax . Thus overwriting the real output that was originally stored in the eax.

    With further exploration and bug fixes following thing works :

       "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"
    );
    

    The trick was to record the results in the eax and then copy those to the return value explicitly along with appropriate clobbering. The other thing was provided register operands rather than the registers themselves for the instructions.