← Back to context

Comment by msla

12 days ago

When I compile it with GCC 12, this machine code results:

    1129:       f3 0f 1e fa             endbr64 
    112d:       55                      push   rbp
    112e:       48 89 e5                mov    rbp,rsp
    1131:       b8 01 00 00 00          mov    eax,0x1
    1136:       bf 01 00 00 00          mov    edi,0x1
    113b:       48 8d 05 c2 0e 00 00    lea    rax,[rip+0xec2]        # 2004 <_IO_stdin_used+0x4>
    1142:       48 89 c6                mov    rsi,rax
    1145:       ba 0f 00 00 00          mov    edx,0xf
    114a:       0f 05                   syscall 
    114c:       b8 00 00 00 00          mov    eax,0x0
    1151:       5d                      pop    rbp
    1152:       c3                      ret    

Can you spot the error?

. . . . . .

The code biffs rax when it loads the string address, so the system call number is lost, and the code ends up not printing anything. Moving the string assignment to be the very first line in main fixes it.

BTW, Clang 14 with no optimization accepts the code without issue but compiles it without using any of the registers; it just stores the values to memory locations and runs the syscall opcode. With O1 optimization or higher, it optimizes away everything except the syscall opcode.

The exact same thing happens with GCC 12 with 32-bit MIPS.

  #include <asm/unistd.h>
   
  char msg[] = "hello, world!\n";
   
  int main(void)
  {
      register int syscall_no asm("v0") = __NR_write;
      register int arg1       asm("a0") = 1;
      register char *arg2     asm("a1") = msg;
      register int arg3       asm("a2") = sizeof(msg) - 1;
   
      asm("syscall");
   
      return 0;
  }

  root@OpenWrt:~# objdump --disassemble=main
  ...
  00400580 <main>:
    400580: 27bdfff8  addiu sp,sp,-8
    400584: afbe0004  sw s8,4(sp)
    400588: 03a0f025  move s8,sp
    40058c: 24020fa4  li v0,4004
    400590: 24040001  li a0,1
    400594: 3c020041  lui v0,0x41
    400598: 24450650  addiu a1,v0,1616
    40059c: 2406000e  li a2,14
    4005a0: 0000000c  syscall

With an older version, it works (as long as there is no optimization at least, with -O2 all the register init code disappears):

$ gcc -v

... gcc version 10.2.1 20210110 (Debian 10.2.1-6)

    0000000000001125 <main>:
        1125: 55                    push   %rbp
        1126: 48 89 e5              mov    %rsp,%rbp
        1129: b8 01 00 00 00        mov    $0x1,%eax
        112e: bf 01 00 00 00        mov    $0x1,%edi
        1133: 48 8d 35 ca 0e 00 00  lea    0xeca(%rip),%rsi        # 2004 <_IO_stdin_used+0x4>
        113a: ba 0e 00 00 00        mov    $0xe,%edx
        113f: 0f 05                 syscall 

No idea why a newer version produces worse code in this case (though of course, this way of doing inline assembly isn't "correct" anyway, so nasal demons may result)