0

How does one specify multiple outputs with an inline asm statement using gcc? I don't follow how the garbage value for ret is printed, but I suspect it's possibly related to both syscall and the mov at the top of the inline assembly section both writing to an output register.

Source:

#include <string.h>
#include <iostream>

int main() {
    const char* str = "Hello World\n";
    long len = strlen(str);
    long ret = 0;
   long test = 0;

    __asm__ __volatile__ (
      "mov $22, %0\n\t"
      "movq $1, %%rax \n\t"
        "movq $1, %%rdi \n\t"
        "movq %2, %%rsi \n\t"
        "movl %3, %%edx \n\t"
        "syscall"
        : "=r"(test), "=g"(ret)
        : "g"(str), "g" (len));
   std::cout << ret << "\n"; 
    return 0;
}

Output:

Hello World
4202512

Disassembly

Dump of assembler code for function main():
   0x0000000000401080 <+0>: sub    $0x8,%rsp
   0x0000000000401084 <+4>: mov    $0x16,%rax
   0x000000000040108b <+11>:    mov    $0x1,%rax
   0x0000000000401092 <+18>:    mov    $0x1,%rdi
   0x0000000000401099 <+25>:    mov    $0x402010,%rsi
   0x00000000004010a0 <+32>:    mov    $0xc,%edx
   0x00000000004010a5 <+37>:    syscall 
   0x00000000004010a7 <+39>:    mov    $0x404080,%edi
   0x00000000004010ac <+44>:    callq  0x401040 <_ZNSo9_M_insertIlEERSoT_@plt>
   0x00000000004010b1 <+49>:    mov    $0x1,%edx
   0x00000000004010b6 <+54>:    mov    $0x40201b,%esi
   0x00000000004010bb <+59>:    mov    %rax,%rdi
   0x00000000004010be <+62>:    callq  0x401050 <_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l@plt>
   0x00000000004010c3 <+67>:    xor    %eax,%eax
   0x00000000004010c5 <+69>:    add    $0x8,%rsp
   0x00000000004010c9 <+73>:    retq   
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
user3882729
  • 1,339
  • 8
  • 11
  • 1
    `"=g"(ret)` tells the compiler it can pick any register or memory for the output operand, but you never write anything to `%1`. If it doesn't pick RAX, there's no reason to expect any sensible result. Maybe you want `"=a"(ret)`, but your asm is broken in a bunch of other ways: missing early-clobber on the other output operand was the first thing I noticed, but also you're missing clobber declarations on RCX and R11 for `syscall` itself. And a `"memory"` clobber since you only take a pointer input, not `"m"`. – Peter Cordes Jun 04 '21 at 05:11
  • 1
    [How to invoke a system call via syscall or sysenter in inline assembly?](//stackoverflow.com/q/9506353) covers how to make system calls. You can add another output operand to already-working inline asm just fine; that was never the problem here, instead that lots of other things with your asm statement were broken, except for the `"=r"(test)` output and the `mov` to it . (The linked Q&A uses specific-register constraints for everything, so you don't even have to adjust the numbering. But normally using named operands makes it easier to add more operands, like `%[temp]` instead of `%0`.) – Peter Cordes Jun 04 '21 at 05:16
  • @PeterCordes I updated the clobber list to include both ```rcx``` and ```r11```, thanks for pointing that out. It did not however change the output. What else is technically broken with the assembly? Yes, nothing is written to ```%1``` which does not imply ```ret``` should contain garbage as it's initialized before the inline assembler. – user3882729 Jun 04 '21 at 06:37
  • 1
    If you want the incoming value to matter, use `"+g"(ret)`, and tell the compiler that syscall writes RAX as well. As written, you promised the compiler that the value of `ret` after this statement is whatever is in the register or memory location it picks. It's an output-only operand, so there's no reason for it to pick the the same location it was previously using. It prefers registers, and debug mode keeps vars in memory. (And separately, if you compile with optimization, the earlier location is "dead" and will actually be optimized away, along with its initialization). – Peter Cordes Jun 04 '21 at 06:44
  • 1
    If you want to see what the asm statement actually picked, expand the operand in an asm comment, and look at the compiler's asm output (`gcc -S` or https://godbolt.org/, not disassembly). e.g. `mov ... # and also ret in %1`. For example: https://godbolt.org/z/cYGb3171r shows GCC picking %rsi, where it needs `ret` as an arg for `cout::operator<<`. Or with optimization disabled, picking RDX, which it of course didn't waste an instruction loading from `-24(%rbp)`. https://godbolt.org/z/T9zdc4Kez – Peter Cordes Jun 04 '21 at 06:50
  • 1
    [inline assembly constraint for value that might be overwritten](https://stackoverflow.com/q/51938034) covers that. – Peter Cordes Jun 04 '21 at 06:52

0 Answers0