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

llvm-link failed to link files {:?}

Error message

llvm-link failed to link files {:?}

What it means

Bailed by Session::link when the external llvm-link process exits with a non-zero status. The tool invokes llvm-link --ignore-non-bitcode over all added files; failure means LLVM could not merge the bitcode (corrupt input, incompatible modules, unresolved symbols). The detailed stdout/stderr is logged at error level before the bail.

Source

Thrown at src/tools/llvm-bitcode-linker/src/linker.rs:80

    fn link(&mut self) -> anyhow::Result<()> {
        tracing::info!("Linking {} files using llvm-link", self.files.len());

        let llvm_link_output = std::process::Command::new("llvm-link")
            .arg("--ignore-non-bitcode")
            .args(&self.files)
            .arg("-o")
            .arg(&self.link_path)
            .output()
            .context("An error occurred when calling llvm-link. Make sure the llvm-tools component is installed.")?;

        if !llvm_link_output.status.success() {
            tracing::error!(
                "llvm-link returned with Exit status: {}\n stdout: {}\n stderr: {}",
                llvm_link_output.status,
                String::from_utf8(llvm_link_output.stdout).unwrap(),
                String::from_utf8(llvm_link_output.stderr).unwrap(),
            );
            anyhow::bail!("llvm-link failed to link files {:?}", self.files);
        }

        Ok(())
    }

    /// Optimize and compile to native format using `opt` and `llc`
    ///
    /// Before this can be called `link` needs to be called
    fn optimize(&mut self, optimization: Optimization, debug: bool) -> anyhow::Result<()> {
        let mut passes = format!("default<{}>", optimization);

        // We add an internalize pass as the rust compiler as we require exported symbols to be explicitly marked
        passes.push_str(",internalize,globaldce");
        let symbol_file_content = self.symbols.iter().fold(String::new(), |s, x| s + &x + "\n");
        std::fs::write(&self.sym_path, symbol_file_content)
            .context(format!("Failed to write symbol file: {}", self.sym_path.display()))?;

        tracing::info!("optimizing bitcode with passes: {}", passes);

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Check the tracing error logs for llvm-link's stderr — it names the offending file and reason.
  2. Rebuild all input rlibs/bitcode with the same toolchain so their LLVM versions match.
  3. Verify each input is valid bitcode: llvm-dis <file> -o - | head, or llvm-bcanalyzer <file>.
  4. Ensure the llvm-tools/llvm-link component is installed and on PATH and matches the rustc LLVM.

Example fix

// before: generic message, logs only on tracing channel
anyhow::bail!("llvm-link failed to link files {:?}", self.files);

// after: include captured stderr in the error for the caller
let stderr = String::from_utf8_lossy(&llvm_link_output.stderr);
anyhow::bail!(
    "llvm-link failed to link {} files: {}",
    self.files.len(),
    stderr.trim()
);
Defensive patterns

Strategy: validation

Validate before calling

// Before linking, verify every input is bitcode the matching LLVM accepts:
use std::process::Command;
fn all_valid_bitcode(files: &[PathBuf], llvm_link: &str) -> bool {
    files.iter().all(|f| Command::new(llvm_link)
        .arg("--check").arg(f).output().map(|o| o.status.success()).unwrap_or(false))
}

Type guard

null

Try / catch

// Surface llvm-link stderr to the caller instead of swallowing it:
match session.lto(opt, debug) {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("llvm-link failed") => {
        eprintln!("llvm-link rejected an input; ensure all bitcode is from the same LLVM");
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling Session::lto (or link directly) after add_file with one or more bitcode/archive files, when llvm-link returns non-zero. Caused by: a non-bitcode file that --ignore-non-bitcode still rejects, mismatched LLVM bitcode versions across inputs, duplicate module definitions, or a corrupt .bc file.

Common situations: Mixing rlibs/bitcode produced by different rustc versions; passing an archive that contains no valid bitcode member; an LLVM upgrade that changed the bitcode format; running without the llvm-tools component (though that path fails earlier with the context message).

Related errors


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