ADR-1337: C++ Placement New Visibility — Hide Inline Run-time Symbols¶
- Status: Accepted
- Date: 2026-09-25
- Deciders: Lusoris fork maintainers
- Tags:
build,api,security,abi,fork-local
Context¶
A fresh relink using GCC 16 in C++26 mode triggered a regression on core/test/check_exported_symbols.py: the symbols _ZnwmPv (placement new) and _ZdlPvS_ (placement delete) from the C++ runtime were suddenly exported into the public ABI of libvmaf.so.
ADR-0379 previously enforced -fvisibility=hidden on C compiles (vmaf_cflags_common) to prevent silent symbol interposition. However, vmaf_cppflags_common did not include -fvisibility-inlines-hidden because no public C++ API surface existed at the time.
In C++26, placement new and delete are declared as constexpr in <new>. Due to the default visibility pragma in libstdc++ headers, these inline functions receive default visibility and are emitted as weak symbols. Since -fvisibility=hidden does not override the visibility of inline functions, these symbols leaked into the dynamic symbol table, violating our fail-closed ABI gate.
Decision¶
We update vmaf_cppflags_common in core/src/meson.build via cxx.get_supported_arguments(vmaf_cflags_common + ['-fvisibility-inlines-hidden']) to explicitly include -fvisibility-inlines-hidden alongside the existing -fvisibility=hidden.
Cross-platform compiler behavior is handled cleanly:
- On GCC (including GCC 16) and Clang (including Clang 22, Apple Clang on macOS, and MinGW GCC/Clang on Windows),
-fvisibility-inlines-hiddenis supported and enforced, hiding inline standard library symbols and preventing_ZnwmPvand_ZdlPvS_from leaking into dynamic export tables. - On Windows MSVC and clang-cl (
cxx.get_argument_syntax() == 'msvc'), symbol visibility is governed by__declspec(dllexport)rather than ELF/Mach-O visibility flags. Meson'scxx.get_supported_arguments(...)probes compiler support and cleanly drops-fvisibility-inlines-hiddenwithout warning or build failure.
Additionally, we added a fail-closed red-cap regression check directly to core/test/check_exported_symbols.py that specifically asserts _ZnwmPv and _ZdlPvS_ are not leaked, ensuring they are never inadvertently allowlisted or suppressed in the future.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Allowlist _ZnwmPv and _ZdlPvS_ in check_exported_symbols.py runtime_owned() | Zero build flag changes | Violates fail-closed ABI gate; leaks C++ runtime placement symbols into public shared library; risks symbol interposition collisions with caller runtimes | Rejected: libvmaf provides a pure C ABI (vmaf_*); runtime leaks are defects |
Add linker version script (--version-script) or export map | Rigid symbol control at link time | Toolchain- and OS-specific (GNU ld vs Apple ld64 vs Windows link.exe); high maintenance overhead; does not solve compiler unit visibility | Rejected: compiler visibility flags are portable across GCC and Clang |
Add manual #pragma GCC visibility push(hidden) guards to C++ sources | Scoped to individual translation units | Fragile, intrusive, easily omitted in future C++ files or standard library includes | Rejected: build-flag inheritance provides uniform coverage |
Append -fvisibility-inlines-hidden to vmaf_cppflags_common and add fail-closed red-cap test | Directly addresses inline C++ standard library visibility; portable across GCC and Clang; guarded by red-cap gate | None; libvmaf does not expose a public C++ API | Chosen |
Consequences¶
- Positive:
libvmaf.sorestores its strict adherence to the public API surface. Silent symbol interposition risks from standard library placement new/delete are eliminated. - Positive: The gate is hardened via a red-cap regression test against future C++ standard library internal changes.
- Negative: No negative impacts on downstream consumers, as no public C++ ABI exists for libvmaf.
- Neutral: No changes to Netflix golden assertions, models, snapshots, tuning, benchmarks, retraining, public C API headers (
core/include/libvmaf/), CLI flags, or GPU kernels.
References¶
- ADR-0379 — libvmaf Symbol Visibility.
- ADR-0108 — Every fork-local PR ships the six deep-dive deliverables.
- ADR-1311 — Bind source ADR citations to exact decisions.
- Research-2119 — GCC 16 C++ placement new/delete symbol visibility investigation, red-cap verification, and evidence.
docs/state.md::T-CPP-PLACEMENT-NEW-ABI-LEAK-2026-09-25.- Source:
req— "because we fix everything until we cant find anything anymore for now and then we will tune for speed"