| Issue |
203691
|
| Summary |
[clang] -fpch-codegen: PCH object skips emitting an instantiation it itself calls, producing unresolved symbols
|
| Labels |
clang
|
| Assignees |
|
| Reporter |
Fedr
|
With `-fpch-instantiate-templates -fpch-codegen`, an implicit instantiation performed while building the PCH can end up emitted **nowhere**: the consuming TUs treat it as `available_externally` (expecting the PCH object to provide it), but the PCH object itself does not emit it — while still *calling* it from other functions it does emit. If the missing symbol is an internal of the standard library (not exported from `libstdc++.so`), the final shared library carries a dangling reference that the link silently accepts (`-shared` permits undefined symbols) and that only explodes at `dlopen` time.
Observed in production as `ImportError: undefined symbol: _ZNKSt12__shared_ptrINSt10filesystem7__cxx114_DirELN9__gnu_cxx12_Lock_policyE2EEcvbEv` (`std::__shared_ptr<std::filesystem::__cxx11::_Dir>::operator bool() const`) in a pybind11 module built with a large PCH.
## Reproducer (stock ubuntu:22.04 container)
`pch.hpp`:
```cpp
#include <filesystem>
#include <iterator>
// Mimics headers using the sentinel comparison, whose body odr-uses
// std::__shared_ptr<std::filesystem::__cxx11::_Dir>::operator bool().
inline bool _pch_touch(std::filesystem::directory_iterator &it)
{
return it == std::default_sentinel;
}
```
`tu.cpp`:
```cpp
bool use(std::filesystem::directory_iterator &it)
{
return _pch_touch(it) || it == std::default_sentinel;
}
```
```sh
FLAGS="-std=c++23 -O2 -fno-inline -fPIC" # -fno-inline only to keep the calls visible; flags must match PCH/TU
clang++ $FLAGS -fpch-instantiate-templates -fpch-codegen -xc++-header pch.hpp -o pch.hpp.gch
clang++ $FLAGS -c -o pch.o pch.hpp.gch
clang++ $FLAGS -includepch.hpp -c -o tu.o tu.cpp
clang++ -std=c++23 -shared -o repro.so tu.o pch.o
ldd -r repro.so
```
Output:
```
undefined symbol: _ZNKSt12__shared_ptrINSt10filesystem7__cxx114_DirELN9__gnu_cxx12_Lock_policyE2EEcvbEv (./repro.so)
undefined symbol: _ZNKSt12__shared_ptrINSt10filesystem7__cxx1128recursive_directory_iterator10_Dir_stackELN9__gnu_cxx12_Lock_policyE2EEcvbEv (./repro.so)
```
`nm --defined-only pch.o` shows the PCH object emitted the neighboring instantiations from the same headers — `__shared_ptr<_Dir>::owner_before`, the destructors, the `__shared_ptr_access` constructors — but not `operator bool`, even though the `directory_iterator::operator==(default_sentinel_t)` body it *does* emit calls it. Nothing else in the program emits it either (the TU assumes it is available externally), and libstdc++.so does not export these internals.
## Versions
Reproduces identically with:
* Ubuntu clang 18.1.8 (apt.llvm.org), ubuntu 22.04, libstdc++ 12 — **reproduces**
* Ubuntu clang 22.1.7 (apt.llvm.org, latest release), ubuntu 22.04, libstdc++ 12 — **reproduces**
* Ubuntu clang 18.1.8 / 22.1.7, ubuntu 24.04, libstdc++ 13/14 — does **not** reproduce
So the gap depends on the standard library version, not the compiler version — but it is not "fixed by newer libstdc++" in any principled sense: libstdc++-13's `bits/fs_dir.h` contains the same `operator==(default_sentinel_t) { return !_M_dir; }` pattern; whatever decides the PCH object's emission set just happens to land differently there. (For libstdc++-12, `g++-12` must be installed on ubuntu 22.04; the default g++-11 predates the sentinel comparison.)
## Notes
* #56357 reported what looks like the same defect on clang 14 (`-fpch-codegen` + libstdc++-12, missing symbols, libc++ OK) and was closed as a duplicate of #55560 — but #55560 was the `std::u16string` extern-template issue (no PCH involved), whose fix did not resolve this: the behavior persists seven major versions later.
* The failure is build-silent: `-shared` accepts the dangling reference, so it surfaces only at run time. `ldd -r` (or `-Wl,-z,defs` where applicable) exposes it at build time.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs