Bundled patchelf 0.9 breaks Qt 6 plugin loading by moving .note.qt.metadata out of PT_NOTE coverage when setting RPATH. Well-researched bug with reproduction steps, awaiting maintainer triage.
Suggested action: needs triage
Reason: This is a fresh, well-documented bug report with clear reproduction steps and upstream references (NixOS/patchelf#403, #217), but it has zero maintainer interaction, no labels, and no assignee. The issue requires triage to assess whether updating the vendored patchelf from 0.9 to at least 0.16.0 is feasible given the two local patches (from 2021) that may need rebasing.
### Check existing issues
- [x] I've verified that this bug isn't described by any existing issues.
### Bug description
*English is not my first language, so I used Claude to help me phrase this report. Every measurement below is one I ran myself and checked, and the conclusions are my own.*
When Qt 6 plugins are staged from distribution packages and patched by snapcraft (`build-attributes: [enable-patchelf]`), Qt refuses to load any of them, and the application dies at startup with no usable platform plugin.
The visible symptom points in the wrong direction, which is why this took me a long time to pin down:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
That empty string is `QT_QPA_PLATFORM_PLUGIN_PATH`, not the plugin search path. With `QT_DEBUG_PLUGINS=1` it becomes clear that Qt searches the correct directory and rejects everything it finds there as `not a Qt plugin (metadata not found)`.
The cause is in the ELF layout. Qt 6.2 and later keep plugin metadata in an ELF note and locate it by walking the `PT_NOTE` program headers. The bundled patchelf relocates `.note.qt.metadata` when it grows `.dynstr` to make room for the RPATH, without extending `PT_NOTE` coverage to the new location. The note data survives, sitting in a `LOAD` segment where Qt never looks. The section-to-segment mapping before and after staging is in the log field below.
Three things that may save you time:
The damage cannot be repaired afterwards. Running a newer patchelf over an already mangled plugin does not restore the coverage, so post-processing in a recipe is not a workaround.
It is the patchelf version and not the way snapcraft invokes it. patchelf 0.18 from the Ubuntu archive keeps the note inside a `PT_NOTE` segment with every flag combination `snapcraft/elf/_patcher.py` uses, including the full `--no-default-lib --force-rpath --set-rpath`.
The reported version number is not very informative. `/snap/snapcraft/current/bin/patchelf --version` says `patchelf 0.9`, and as far as I can tell `snap/snapcraft.yaml` builds it from `canonical/patchelf` branch `0.9+snapcraft`, where fixes are cherry-picked without the version string moving. So I am reporting observed behaviour rather than assuming what is or is not in that branch.
Upstream fixed this well before that branch was last touched:
* [NixOS/patchelf#403](https://github.com/NixOS/patchelf/issues/403), fixed by [PR #415](https://github.com/NixOS/patchelf/pull/415), released in patchelf 0.16.0 on 2022-10-27. That issue describes `.dynstr` growth driven by RPATH changes, which is exactly the trigger here.
* [NixOS/patchelf#217](https://github.com/NixOS/patchelf/issues/217), fixed by [PR #218](https://github.com/NixOS/patchelf/pull/218), released in patchelf 0.12 on 2020-08-27, covering `PT_NOTE` not being updated when `SHT_NOTE` sections move.
This is not new. There is a forum thread from April 2023 describing the same failure, [Cannot use Qt6 in classic core22 snap](https://forum.snapcraft.io/t/cannot-use-qt6-in-classic-core22-snap/34771): same `enable-patchelf`, same `metadata not found`, fine on Qt 6.2.4 and broken on 6.3, which matches the Qt release where note-based metadata arrived. It received no reply and never became an issue here, so I am filing one.
Avoiding `enable-patchelf` is the obvious answer, and it is what that forum thread did, by setting the RPATH at compile time. That only works if you build Qt yourself. Under classic confinement you frequently cannot, because snapcraft refuses to combine a desktop extension with classic confinement (`Extension 'kde-neon-6' does not support confinement 'classic'`), so the content snaps are out and Qt has to come from already-built distribution packages.
What I ended up doing in my own recipe is staging the plugin tree in a separate part that snapcraft does not patch, then setting the RPATH there myself with patchelf 0.18 from `build-packages`. It works and it is a lot of recipe for something that should not be a problem.
Updating the vendored patchelf to at least upstream 0.16.0 would be enough for my case. If that is risky for other reasons, it would already help if the patchelf pass warned when it moves a `.note.qt.metadata` section out of `PT_NOTE` coverage, since the resulting failure gives no hint whatsoever about its cause.
I am happy to test a build with an updated patchelf if that is useful.
### Steps to reproduce
The recipe in the snapcraft.yaml field below is a minimal reproducer. It stages the Qt 6 platform plugins from the Ubuntu archive and does nothing else, so no application or Qt code is needed to see the problem.
1. Put that `snapcraft.yaml` in `snap/` of an empty directory and run `snapcraft`.
2. Unpack the resulting snap and look at where `.note.qt.metadata` ended up:
unsquashfs -d out patchelf-qt-note_1_amd64.snap \
usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqxcb.so
readelf -lW out/usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqxcb.so
In the section-to-segment mapping, `.note.qt.metadata` is in a `LOAD` segment, and none of the `PT_NOTE` segments covers it.
3. Compare against the same file as shipped, which has it inside a `PT_NOTE` segment:
apt-get download libqt6gui6
dpkg-deb -x libqt6gui6_*.deb orig
readelf -lW orig/usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqxcb.so
4. To see the runtime effect, point a Qt 6 application at the staged plugin directory. Qt reports `is not a Qt plugin (metadata not found)` for every plugin and exits because no platform plugin could be initialised.
Removing `build-attributes: [enable-patchelf]` from the part makes the note survive, which confirms the patchelf pass as the origin.
### Environment
* snapcraft 9.0.1, installed from the snap store
* LXD provider, not destructive mode
* Host: Ubuntu 26.04 amd64
* Recipe: `base: core26`, `confinement: classic`, `platforms: amd64`
* Qt 6.10.2 from the Ubuntu archive (`libqt6gui6`, `python3-pyqt6`, `qt6-wayland`, `kde-style-breeze`, all 6.10.2)
* Bundled patchelf: `/snap/snapcraft/current/bin/patchelf --version` reports `patchelf 0.9`
* Comparison patchelf: 0.18.0-1.4build1 from the Ubuntu archive
Affected files are everything under `usr/lib/$ARCH/qt6/plugins`, so platform plugins, widget styles, image format plugins and icon engines are all hit the same way.
### snapcraft.yaml
```yaml
name: patchelf-qt-note
base: core26
version: "1"
summary: Minimal reproducer for patchelf mangling Qt 6 plugin metadata
description: |
Stages the Qt 6 platform plugins from the Ubuntu archive and lets snapcraft
patch them. Afterwards, .note.qt.metadata is no longer covered by a PT_NOTE
program header, and Qt rejects the plugin as "not a Qt plugin".
grade: devel
confinement: classic
platforms:
amd64:
parts:
qt:
plugin: nil
build-attributes: [enable-patchelf]
stage-packages:
- libqt6gui6
lint:
ignore:
- classic
- library
```
### Log output
```shell
# readelf -lW, section-to-segment mapping, libqxcb.so
#
# as shipped in libqt6gui6_6.10.2+dfsg-7_amd64.deb:
00 .note.qt.metadata .note.gnu.build-id .gnu.hash .dynsym .dynstr ...
05 .note.qt.metadata
# after staging with build-attributes: [enable-patchelf]:
13 .dynamic .dynstr .note.qt.metadata
# Segment 05 above is a PT_NOTE. Segment 13 is a LOAD. None of the four
# PT_NOTE segments left in the patched file covers the note:
NOTE 0x000318 0x0000000000000318 0x0000000000000318 0x000088 R 0x8
NOTE 0x0003a0 0x00000000000003a0 0x00000000000003a0 0x000024 R 0x4
NOTE 0x003328 0x0000000000003328 0x0000000000003328 0x000020 R 0x8
NOTE 0x003348 0x0000000000003348 0x0000000000003348 0x000070 R 0x4
# What Qt makes of it, with QT_DEBUG_PLUGINS=1:
qt.core.plugin.factoryloader: checking directory path ".../qt6/plugins/platforms" ...
qt.core.plugin.factoryloader: looking at "libqxcb.so"
qt.core.plugin.loader: Failed to find metadata in lib .../platforms/libqxcb.so:
'.../platforms/libqxcb.so' is not a Qt plugin (metadata not found)
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in ""
This application failed to start because no Qt platform plugin could be initialized.
```
Evaluation history
Date
Model
Scores
Action
Summary
qwen3.6-35b-a3b-mtp-q6
Impact:
80
Quick Win:
32.0
Staleness:
5
Complexity:
60
Confidence:
70
Support Request:
10
needs triage
Bundled patchelf 0.9 breaks Qt 6 plugin loading by moving .note.qt.metadata out of PT_NOTE coverage when setting RPATH. Well-researched bug with reproduction steps, awaiting maintainer triage.