assemblygcclinux-kernelx86inline-assembly

gnu inline assembly constraint `i` for memory address


Recetly, this function rip_rel_ptr has been added to Linux kernel. https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/asm.h#L118.

I can compile the kernel, but when I copy this function for myself,

static inline
__attribute__((__always_inline__))
__attribute__((__pure__)) void *rip_rel_ptr(void *var)
{
        asm("leaq %c1(%%rip), %0" : "=r"(var) : "i"(var));

        return var;
}

#define RIP_REL_REF(var)    (*(typeof(&(var)))rip_rel_ptr(&(var)))

int x;

int main(void)
{
        RIP_REL_REF(x);

        return 0;
}

I see this error:

a.c: In function ‘main’:
a.c:8:2: warning: ‘asm’ operand 1 probably does not match constraints
    8 |  asm("leaq %c1(%%rip), %0" : "=r"(var) : "i"(var));
      |  ^~~
a.c:8:2: error: impossible constraint in ‘asm’

I can not figure out why it compiles in the kernel on the same machine but can not compile it separately.


Solution

  • Are you sure kernel code uses this macro on automatic storage (non-static local variables)? The address of a local on the stack isn't a build-time constant (neither absolute nor relative to .text).

    I'd expect that to work with a global or static variable, since its address can be obtained with a RIP-relative LEA. Even with ASLR, the distance between .text and static storage (.data, .bss, .rodata) is fixed at link time.

    This also only works with optimization enabled; you need constant-propagation through the function arg void *var to the asm statement. always_inline doesn't stop function parameters from being actual variables which (in -O0 "debug" builds) actually exist with their own address. (Why is this C++ wrapper class not being inlined away? - inlined yes, optimized away no)