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.
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)