llvm / llvm/llvm-project

Clang miscompiles when mixing pic and non-pic object files with lto enabled

Open
#189,203 0 comments 0 reactions 0 assignees View on GitHub
LTO miscompilation
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Depending on the settings it was compiled with, builds of Qt6 may use `-Bsymbolic`, and therefore require that binaries that reference Qt symbols are compiled with PIC enabled. They conveniently add PIC as a public requirement in their cmake file, and include an `#ifndef __PIC__ #error` in Qt headers to make sure that files are compiled correctly in this case. One would expect this would be enough to ensure that anything that knows about Qt objects has PIC enabled. Yet if you enable LTO, projects that use Qt can somehow find themselves with `R_X86_64_COPY` entries for Qt symbols, which breaks Qt metaobject stuff.

This appears to affect only symbols that are themselves referenced by const global variables (rather than referenced by functions). Using LTO to merge PIC and non-PIC files will use `R_X86_64_COPY` instead of `R_X86_64_64` relocations for those symbols, even if the symbols were only ever referenced in code from PIC files. An easy way to end up with the mix of PIC types is to make multiple cmake targets, and link one to Qt6, which will enable PIC on that target and anything that depends on it, but not targets it depends on.

To reproduce:
1. [Download the attached zip file](https://github.com/user-attachments/files/26327321/PICBreak.zip) and unzip it. The zip file contains a makefile project that generates one .so compiled with Bsymbolic and two .o files, one of which references symbols in the .so and uses PIC, and the other which does not reference symbols in the .so or use PIC. The two .o files are then linked with LTO into a final executable which then checks that all references to the symbols in the .so have the same address.
2. Run `make CC=/path/to/clang run inspect` on a clang that isn't configured to add PIC to everything (e.g. the one that ships with Fedora)

Expected result:
```
0x7f4ee33e1238
0x7f4ee33e1238
0x7f4ee33e1238

Relocation section '.rela.dyn' at offset 0x1168 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000402de8 0000000500000001 R_X86_64_64 0000000000000000 aaa + 0
0000000000402fe0 0000000500000006 R_X86_64_GLOB_DAT 0000000000000000 aaa + 0
0000000000402fd0 0000000100000006 R_X86_64_GLOB_DAT 0000000000000000 [__libc_start_main@GLIBC_2.34](mailto:__libc_start_main@GLIBC_2.34) + 0
0000000000402fd8 0000000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0

Relocation section '.rela.plt' at offset 0x11c8 contains 2 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000403000 0000000200000007 R_X86_64_JUMP_SLOT 0000000000000000 [printf@GLIBC_2.2.5](mailto:printf@GLIBC_2.2.5) + 0
0000000000403008 0000000400000007 R_X86_64_JUMP_SLOT 0000000000000000 getAAA + 0
```

Actual result:
```
0x7f3b23091238
0x402de8
0x402de8

Relocation section '.rela.dyn' at offset 0x1170 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000402fe0 0000000500000006 R_X86_64_GLOB_DAT 0000000000402de8 aaa + 0
0000000000402fd0 0000000100000006 R_X86_64_GLOB_DAT 0000000000000000 [__libc_start_main@GLIBC_2.34](mailto:__libc_start_main@GLIBC_2.34) + 0
0000000000402fd8 0000000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
0000000000402de8 0000000500000005 R_X86_64_COPY 0000000000402de8 aaa + 0

Relocation section '.rela.plt' at offset 0x11d0 contains 2 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000403000 0000000200000007 R_X86_64_JUMP_SLOT 0000000000000000 [printf@GLIBC_2.2.5](mailto:printf@GLIBC_2.2.5) + 0
0000000000403008 0000000400000007 R_X86_64_JUMP_SLOT 0000000000000000 getAAA + 0
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.