https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126290

            Bug ID: 126290
           Summary: x86-64 -fPIC drops `register asm` operands of an `asm`
                    when a `__thread` address is another operand
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ammarfaizi2 at gnuweeb dot org
  Target Milestone: ---

When an inline assembly statement uses local register variables alongside a
thread-local (`__thread`) variable in PIC, GCC generates an implicit call to
`__tls_get_addr`. This hidden function call clobbers the specifically assigned
registers, passing garbage values to the assembly block.

Godbolt Link: https://godbolt.org/z/T7xdET554
-------------------------------------------

Reproducer:
```
#define _GNU_SOURCE
#include <sys/socket.h>

static long __recvfrom(int fd, void *buf, size_t len, int flags,
                       struct sockaddr *src_addr, socklen_t *addrlen)
{
    register long r10 __asm__("r10") = flags;
    register struct sockaddr *r8 __asm__("r8") = src_addr;
    register socklen_t *r9 __asm__("r9") = addrlen;
    long ret = 45; /* __NR_recvfrom */

    __asm__ volatile(
        "syscall"
        : "+a"(ret)     /* %rax */
        : "D"(fd),      /* %rdi */
          "S"(buf),     /* %rsi */
          "d"(len),     /* %rdx */
          "r"(r10),
          "r"(r8),
          "r"(r9)
        : "rcx", "r11", "memory");

    return ret;
}

long test(int fd, struct sockaddr *src_addr, socklen_t *addrlen)
{
    static __thread char b1[8];
    return __recvfrom(fd, b1, 8, MSG_PEEK, src_addr, addrlen);
}
```

-------------------------------------------


GCC (-O2 -fPIC), wrong. Notice the `call __tls_get_addr@PLT` occurs without
`%r10`, `%r8`, or `%r9` being loaded before the `syscall`.
```
test:
        pushq   %rbx
        movl    %edi, %ebx
        leaq    b1.0@tlsld(%rip), %rdi
        call    __tls_get_addr@PLT
        movl    $8, %edx
        movl    %ebx, %edi
        leaq    b1.0@dtpoff(%rax), %rsi
        movl    $45, %eax
        syscall
        popq    %rbx
        ret
b1.0:
        .zero   8

```

-------------------------------------------

Clang (-O2 -fPIC), correct. Notice `%r10`, `%r8`, and `%r9` are safely loaded
after the `__tls_get_addr` call.
```
test:
        pushq   %rbp
        pushq   %r14
        pushq   %rbx
        movq    %rdx, %rbx
        movq    %rsi, %r14
        movl    %edi, %ebp
        leaq    test.b1@TLSLD(%rip), %rdi
        callq   __tls_get_addr@PLT
        leaq    test.b1@DTPOFF(%rax), %rsi
        movl    $8, %edx
        movl    $2, %r10d
        movl    $45, %eax
        movq    %r14, %r8
        movq    %rbx, %r9
        movl    %ebp, %edi
        syscall
        popq    %rbx
        popq    %r14
        popq    %rbp
        retq

test.b1:
        .zero   8
```
-------------------------------------------

Reply via email to