Bug 16360 - clang LTO fails to link: `ld-is-lld` never reaches the native/cross clang
Summary: clang LTO fails to link: `ld-is-lld` never reaches the native/cross clang
Status: NEW
Alias: None
Product: OE-Core
Classification: Build System, Metadata & Runtime
Component: devtools / tool chain (show other bugs)
Version: 6.0
Hardware: All Multiple
: Medium normal
Target Milestone: 6.1
Assignee: Sundeep Kokkonda
QA Contact:
URL:
Whiteboard:
Depends on:
Blocks:
 
Reported: 2026-07-16 06:21 UTC by Altair Bueno Calvente
Modified: 2026-07-16 14:48 UTC (History)
4 users (show)

See Also:
OS type for building Yocto: ---
Type of Regression: Regression (Used to work)
Verified:
Documentation change: Yes (doc changes required)


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Altair Bueno Calvente 2026-07-16 06:21:13 UTC
## Summary

With the `lto` distro feature enabled and `ld-is-lld` added to
`DISTRO_FEATURES`, any clang-toolchain target that links a shared library fails
to build with:

```log
x86_64-poky-linux-ld: .../recipe-sysroot-native/usr/bin/x86_64-poky-linux/../lib/LLVMgold.so:
    error loading plugin: ... cannot open shared object file: No such file or directory
x86_64-poky-linux-clang++: error: linker command failed with exit code 1
```

`ld-is-lld` is (allegedly) supposed to make clang default to lld, which reads
LTO bitcode directly and needs no `LLVMgold.so` plugin. It does not take effect
for the clang **cross-compiler**, so clang keeps defaulting to GNU bfd, and bfd
cannot link the LTO bitcode without the missing gold plugin.

> As a side note, `ld-is-lld` is not listed as a `DISTRO_FEATURE` in the
> reference manual
> ([ref-manual/features.html](https://docs.yoctoproject.org/ref-manual/features.html))
> on `wrynose`, nor the manual mentions the need for
> `require conf/distro/include/lto.inc`.

Even if you only want GCC's LTO and don't use clang directly, this still bites
you: the Rust build configures LLVM with `use-libcxx` (`rust_1.94.1.bb`), which
pulls in `libcxx`, and `libcxx` is built with clang, so it hits this.

I've seen the commit where LLD was separated from the clang recipe
(5212b69f892af8c9b080fee2c21533a2bbdd2755) and the rationale was to reduce build
time. I dont think 178.93 seconds for compilation (on my machine) is meaningful
enough to justify not using LLD. Why would anyone build with clang without LLD?
GNU's bfd linker is pretty bad compared with what LLD offers. And if you are
already using clang, it seems like a no brainer to use LLD too so you get all
the benefits.

## Environment

- openembedded-core `wrynose`, reproduced at commit `42fa856a00`; the relevant
  recipe/class logic is unchanged at branch tip `c2746a4a16` (clang/llvm 22.1.3
  to 22.1.8), so this is **not** fixed upstream as of that tip.
- clang/llvm recipes as migrated into oe-core (`meta/recipes-devtools/clang/`),
  i.e. **not** the external `meta-clang` layer.
- Default `TC_CXX_RUNTIME` (gnu). Host/build machine is x86-64; the target arch
  is irrelevant (reproduced on `qemux86-64` and on `cortexa55`/aarch64) because
  the defect is in the **build-host** clang.

## TL;DR root cause

1. `ld-is-lld` is a **target** `DISTRO_FEATURE`. `clang_git.bb` turns it into
   `PACKAGECONFIG[lld] = "-DCLANG_DEFAULT_LINKER=lld"`.
2. The clang cross-compiler is produced by the **native** `clang-cross` recipe,
   which _copies the `clang-native` binary_. A clang binary's default linker is
   baked in at **its own** build time.
3. Native recipes filter `DISTRO_FEATURES` down to `DISTRO_FEATURES_NATIVE` +
   `DISTRO_FEATURES_FILTER_NATIVE`. `ld-is-lld` is in **neither** (see
   `default-distrovars.inc`).
4. So `clang-native` builds **without** `-DCLANG_DEFAULT_LINKER=lld`, the cross
   compiler defaults to **bfd**, target `-flto` recipes emit bitcode that bfd
   can't link, resulting in `LLVMgold.so` missing.

## Kas minimal reproduction example

```yaml
header:
    version: 14

build_system: oe
distro: poky
machine: qemux86-64
target: libcxx

repos:
    bitbake:
        url: https://github.com/openembedded/bitbake
        branch: "2.18"
        commit: a2dd9be788274d9c7280be5b1be5eb5c3990cf54
        path: sources/bitbake
        layers: {}

    openembedded-core:
        url: https://github.com/openembedded/openembedded-core
        branch: wrynose
        commit: 42fa856a00ac16b2a7a83d7ecfa60a5be192b16c
        path: sources/openembedded-core
        layers:
            meta:

    meta-yocto:
        url: https://git.yoctoproject.org/meta-yocto
        branch: wrynose
        commit: 904846ae078ee20de073040ebb77c86e19250f56
        path: sources/meta-yocto
        layers:
            meta-poky:

local_conf_header:
    lto-mre: |
        require conf/distro/include/lto.inc
        # Drop `ld-is-lld` to see the baseline failure with just `lto`.
        DISTRO_FEATURES:append = " lto ld-is-lld"
```

### Actual vs expected

- **Actual:** `libcxx:do_compile` fails at the `libc++`/`libc++abi`
  shared-object link with the `LLVMgold.so … cannot open shared object file`
  error above; the linker invoked is the GNU `<triple>-ld` (bfd), never
  `ld.lld`.
- **Expected:** with `ld-is-lld`, clang defaults to lld, links the LTO bitcode
  natively, and `libcxx` builds.

## Current workaround

Expose `ld-is-lld` to native recipes so `clang-native` (and therefore the copied
cross compiler) is built with `-DCLANG_DEFAULT_LINKER=lld`. In our case, we
appended the following to our distro config:

```bitbake
DISTRO_FEATURES_FILTER_NATIVE:append = " ld-is-lld"
```

With this change, the `clang-native` build sees the `ld-is-lld` feature, adds
`lld` to its `PACKAGECONFIG`, and sets `-DCLANG_DEFAULT_LINKER=lld`. The copied
cross compiler then defaults to lld, and the target `libcxx`/`compiler-rt` LTO
links succeed. We are able to boot the resulting image and confirm that LTO was
active (target binary sizes shrank).

We are not sure if this is the "right" fix, but it works and is the only way we
can migrate to wrynose with LTO enabled.

### Appendix: related issues

- [#16026](https://bugzilla.yoctoproject.org/show_bug.cgi?id=16026): covers the
  libstdc++/libc++ runtime mismatch under `TC_CXX_RUNTIME=llvm`; this bug is
  about the linker-default (bfd vs lld) not propagating to native/cross clang
  under the default gnu runtime.

### Appendix: build log excerpt

Relevant excerpt from
`build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/temp/log.do_compile.1387567`:

```text
26431-[1731/1833] : && /opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/x86_64-poky-linux-clang++ --sysroot=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot -fPIC -m64 -march=x86-64-v3  -fstack-protector-strong  -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security  --sysroot=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot  -O2 -g -flto    -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot=  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native=  -pipe -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wno-comment -Wstring-conversion -Wno-pass-failed -fdiagnostics-color -ffunction-sections -fdata-sections  -O2 -g -DNDEBUG  -shared -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -flto    -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot=  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native=  -Wl,-z,relro,-z,now -Wl,-z,defs -Wl,-z,nodelete -Wl,-soname,libc++abi.so.1 -o lib/libc++abi.so.1.0 libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_aux_runtime.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_default_handlers.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_exception_storage.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_guard.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_handlers.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_vector.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_virtual.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_exception.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_stdexcept.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_typeinfo.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/fallback_malloc.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/private_typeinfo.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_new_delete.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_exception.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_personality.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_thread_atexit.cpp.o   && :
26432-FAILED: [code=1] lib/libc++abi.so.1.0
26433-: && /opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/x86_64-poky-linux-clang++ --sysroot=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot -fPIC -m64 -march=x86-64-v3  -fstack-protector-strong  -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security  --sysroot=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot  -O2 -g -flto    -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot=  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native=  -pipe -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wno-comment -Wstring-conversion -Wno-pass-failed -fdiagnostics-color -ffunction-sections -fdata-sections  -O2 -g -DNDEBUG  -shared -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -flto    -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot=  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native=  -Wl,-z,relro,-z,now -Wl,-z,defs -Wl,-z,nodelete -Wl,-soname,libc++abi.so.1 -o lib/libc++abi.so.1.0 libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_aux_runtime.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_default_handlers.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_exception_storage.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_guard.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_handlers.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_vector.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_virtual.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_exception.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_stdexcept.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_typeinfo.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/fallback_malloc.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/private_typeinfo.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/stdlib_new_delete.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_exception.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_personality.cpp.o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_thread_atexit.cpp.o   && :
26434:/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/x86_64-poky-linux-ld: /opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/../lib/LLVMgold.so: error loading plugin: /opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/../lib/LLVMgold.so: cannot open shared object file: No such file or directory
26435:x86_64-poky-linux-clang++: error: linker command failed with exit code 1 (use -v to see invocation)
26436-[1732/1833] /opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/x86_64-poky-linux-clang++ --sysroot=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot -DLIBCXX_BUILDING_LIBCXXABI -DLIBC_NAMESPACE=__llvm_libc_common_utils -D_LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION=2 -D_LIBCPP_BUILDING_LIBRARY -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src/libcxx/src -I/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build/include/c++/v1 -I/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src/libcxxabi/include -I/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src/cmake/Modules/../../libc -m64 -march=x86-64-v3  -fstack-protector-strong  -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security  --sysroot=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot  -O2 -g -flto    -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build=/usr/src/debug/libcxx/22.1.3  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot=  -ffile-prefix-map=/opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/recipe-sysroot-native=  -pipe -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wno-comment -Wstring-conversion -Wno-pass-failed -fdiagnostics-color -ffunction-sections -fdata-sections -O2 -g -DNDEBUG -std=c++23 -fPIC -MD -MT libcxx/src/CMakeFiles/cxx_shared.dir/barrier.cpp.o -MF libcxx/src/CMakeFiles/cxx_shared.dir/barrier.cpp.o.d -o libcxx/src/CMakeFiles/cxx_shared.dir/barrier.cpp.o -c /opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src/libcxx/src/barrier.cpp
26437-In file included from /opt/yocto/imx9/build/tmp/work-shared/llvm-project-source-22.1.3-r0/sources/llvm-project-22.1.3.src/libcxx/src/barrier.cpp:9:
26438-In file included from /opt/yocto/imx9/build/tmp/work/x86-64-v3-poky-linux/libcxx/22.1.3/build/include/c++/v1/barrier:56:
```

## Appendix: LLM reasoning chain. How this was diagnosed?

This issue was debugged with the help of Claude, and a summary of the reasoning
chain is provided below:

1. **Symptom vs. real error.** `libcxx:do_compile` exits 1, and the log is
   dominated by `-Wnullability-completeness` warnings. Those are noise. The real
   failure is a **link** error far down the log:
   `LLVMgold.so … cannot open shared object file`, from the GNU **bfd** linker.
   → _Read to the actual failing command, not the first/most-frequent message._

2. **First hypothesis (wrong): "disable LTO for the clang libs."** LTO is on
   distro-wide; the clang recipes link with bfd; bfd needs `LLVMgold.so` for
   bitcode; `meta-clang`'s `nonclangable.conf` already disables LTO per-recipe
   for exactly this reason (zlib/bluez5/systemd-boot, with the same LLVMgold
   comment). So: add `LTO:pn-libcxx = ""` etc. → **Refuted by a good question:**
   if there's a long LTO-incompatibility list, why aren't clang's _own_
   libraries on it? That means the list isn't the mechanism here — something
   else is off.

3. **Discovery: `meta-clang` isn't even in the build.** `bblayers.conf` has no
   `meta-clang`; the clang recipes are served from **openembedded-core** itself.
   In `wrynose`, clang/llvm/libcxx/compiler-rt were **upstreamed from meta-clang
   into oe-core** (Khem Raj series, Nov 2024; Ross Burton follow-ups, Sep 2025).
   So `clang-legacy.bbclass` (the old LTO+lld wiring) and `nonclangable.conf`
   never applied — the whole first hypothesis was chasing a dormant layer.

4. **Second hypothesis: "just enable `ld-is-lld`."** oe-core's clang toolchain
   selects lld via the `ld-is-lld` distro feature (`clang_git.bb`:
   `PACKAGECONFIG[lld] → -DCLANG_DEFAULT_LINKER=lld`) — a feature that, notably,
   isn't documented in the ref manual. With lld, LTO bitcode links with no
   plugin. Looked like the answer — and `ld-is-lld` was even present already. →
   **Still failed:** the build kept using bfd.

5. **Narrowing: inspect the actual compiler.** `clang++ -###` from the recipe
   sysroot showed the cross clang defaulting to `<triple>-ld` (bfd), _despite_
   `ld-is-lld`. `clang-cross` turned out to just **copy the `clang-native`
   binary**, so the default linker is fixed when `clang-native` is built — not
   by the target-side feature.

6. **Root cause via `bitbake-getvar -r clang-native DISTRO_FEATURES`.** The
   variable's computed history showed the raw value _did_ contain `ld-is-lld`,
   but the final op was
   `class_filter_features(..., "DISTRO_FEATURES_NATIVE", ...)` — native recipes
   filter `DISTRO_FEATURES` down to `DISTRO_FEATURES_NATIVE` +
   `DISTRO_FEATURES_FILTER_NATIVE`, and `ld-is-lld` is in neither. So
   `clang-native`'s `PACKAGECONFIG` never got `lld` →
   `-DCLANG_DEFAULT_LINKER=lld` was never set → bfd default.

7. **Fix + verification.** `DISTRO_FEATURES_FILTER_NATIVE:append = " ld-is-lld"`
   → `clang-native` gets the `lld` packageconfig (`bitbake-getvar` confirms
   `CLANG_DEFAULT_LINKER=lld`) → cross clang defaults to lld →
   `libcxx`/`compiler-rt` LTO links succeed. Confirmed end-to-end (clean-room
   MRE reproduces the failure; the fix builds the full image; LTO verified
   active on the board).

**Lessons:** (a) read to the real failing command; (b) verify which layer/recipe
actually serves the failing target before trusting a layer's conventions; (c) a
distro-level `DISTRO_FEATURE` does not imply the _native_ tooling sees it —
check the native feature filters; (d) `bitbake-getvar -r`'s operation history is
the fastest way to see _who_ set/filtered a variable.