GCC vs Clang differences in protected visibility implementation


I’ve been trying to solve some issues related to the usage of hidden visibility with LLD:


And as a result of that I’ve come across some diverging behaviour between GCC and Clang regarding the implementation of protected visibility:

Given the following code:

#pragma GCC visibility push(protected)

void (*foo)(void);

void test(void)
    asm volatile (".long %c[addr]" :: [addr] "i" (&(foo)));

The above compiles with clang and -fpic, while it doesn’t build with GCC:


It’s my understanding that foo could be set from a different module, and hence could be set to go through the GOT/PTL? In which case GCC is correct in failing to compile the snippet?

Thanks, Roger.

1 Like

foo is a variable. i seems to work if GCC/Clang uses direct access without going through a GOT relocation.

If you have a function, you may use:

#pragma GCC visibility push(protected)

void foo(void);

void test(void) {
  asm(".pushsection foo,\"aw\"");
#if defined(__aarch64__) || defined(__riscv)
  asm volatile(".quad %0" :: "S"(foo));
#elif defined(__x86_64)
  // This does not work with -mcmodel=large.
  asm volatile(".quad %p0" :: "i"(foo));

For other architectures, there may not be a way to get the raw symbol name with a machine constraint or operand modifier.

But it does work, see the goldbolt link example I’ve pasted on the original post. That example works when using ‘hidden’ visibility with GCC, and with both ‘protected’ or ‘hidden’ visibility with clang.

OK, I modified the previous comment.

A protected variable cannot be referenced via machine constraint i in gcc -fpic is because of 65248 – Copy relocation against protected symbol doesn't work . See Copy relocations, canonical PLT entries and protected visibility | MaskRay for a write-up. I am trying to eliminate some code debt in GNU land recently: The Libc-alpha May 2022 Archive by thread