rust-lang/rust · error · io::Error

LTO object in C static library is not supported

Error message

LTO object in C static library is not supported

What it means

Emitted by add_c_staticlib_symbols alongside error 167, but for a different LTO representation. After parsing each archive member as an object file, the linker checks whether any section name starts with .gnu.lto_ or equals .llvm.lto (link.rs:2842-2843). These section names indicate gcc or clang ELF/Mach-O intermediate LTO representation — the object contains linker-ir LTO data instead of final machine code. rustc's archive symbol scanner cannot handle these, so it aborts.

Solutions

  1. Recompile the C static library without -flto and relink the Rust cdylib.
  2. Check the distro build flags (e.g., Arch Linux's /etc/makepkg.conf LTOFLAGS) and disable LTO for the library in question.
  3. Build the dependency from source with CFLAGS="-fno-lto" if a non-LTO prebuilt variant is unavailable.
  4. Verify the fix with: objdump -h libfoo.a | grep lto — output should be empty.

Example fix

# before: system .a built with gcc -flto (has .gnu.lto_ sections)
# after: rebuild from source without LTO
CFLAGS="-fno-lto" ./configure && make
# or with cmake:
cmake -DCMAKE_C_FLAGS="-fno-lto" -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF ..
Defensive patterns

Strategy: validation

Validate before calling

// Before linking, scan the C static library for ELF/Mach-O LTO sections.
use std::fs;
use std::path::Path;

fn check_no_elf_lto(lib_path: &Path) -> Result<(), String> {
    let data = fs::read(lib_path).map_err(|e| format!("read error: {e}"))?;
    let archive = object::read::archive::ArchiveFile::parse(&data)
        .map_err(|e| format!("archive parse error: {e}"))?;
    for member in archive.members() {
        let member = member.map_err(|e| format!("member error: {e}"))?;
        let member_data = member.data(&data).map_err(|e| format!("data error: {e}"))?;
        let obj = object::File::parse(member_data).map_err(|e| format!("parse error: {e}"))?;
        for section in obj.sections() {
            if let Ok(name) = section.name() {
                if name.starts_with(".gnu.lto_") || name == ".llvm.lto" {
                    return Err(format!("library contains LTO section '{}': recompile without -flto", name));
                }
            }
        }
    }
    Ok(())
}

Try / catch

// Same as error 167 — surfaces as a rustc fatal diagnostic.
// Prevent by recompiling the C dependency without -flto.
// Quick check from the command line:
//   objdump -h libfoo.a | grep -E '\.gnu\.lto_|\.llvm\.lto'
// If output is non-empty, the library has LTO sections.

Prevention

When it happens

Trigger: Building a Rust cdylib that links a C static library compiled with gcc -flto or clang -flto where the output uses ELF/Mach-O LTO sections (rather than raw bitcode). Triggered only when crate_type is Cdylib and the lib has NativeLibKind::Static { export_symbols: Some(true), .. }.

Common situations: C/C++ dependency built with gcc -flto on Linux (producing .gnu.lto_* sections); a prebuilt .a from a distribution that enables LTO by default (e.g., some Alpine or Gentoo packages); FFIs wrapping system libraries that were built with distro-wide LTO flags.

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/3c7e04bcbf349e84. Report an issue: GitHub.

Appendix: source

Thrown at compiler/rustc_codegen_ssa/src/back/link.rs:2845

            .data(&*archive_map)
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

        // clang LTO: raw LLVM bitcode
        if data.starts_with(b"BC\xc0\xde") {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "LLVM bitcode object in C static library (LTO not supported)",
            ));
        }

        let object = object::File::parse(&*data)
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

        // gcc / clang ELF / Mach-O LTO
        if object.sections().any(|s| {
            s.name().map(|n| n.starts_with(".gnu.lto_") || n == ".llvm.lto").unwrap_or(false)
        }) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "LTO object in C static library is not supported",
            ));
        }

        for symbol in object.symbols() {
            // The `object` crate returns `Dynamic` for ELF/Mach-O global symbols,
            // but always returns `Linkage` for COFF external symbols.
            // Accept both for COFF (Windows and UEFI).
            let scope = symbol.scope();
            if scope != object::SymbolScope::Dynamic
                && !(sess.target.binary_format == BinaryFormat::Coff
                    && scope == object::SymbolScope::Linkage)
            {
                continue;
            }

            let name = match symbol.name() {

View on GitHub (pinned to 7088e4b63a)