How to fix Rust clang crate: symbol lookup error: undefined symbol: LLVMInitializeAArch64TargetInfo

Problem

When trying to run a Rust executable using the clang crate, you see an error message such as

example.txt
$ cargo run clangtest.cpp
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/asx clangtest.cpp`
target/debug/clangtest: symbol lookup error: /lib/x86_64-linux-gnu/libclang-19.so.19: undefined symbol: LLVMInitializeAArch64TargetInfo, version LLVM_19.1

Solution

This error occurs because Cargo links the wrong libLLVM. A common cause for this is that you have installed rocm (AMD’s compute stack) which comes with its own libLLVM that is incompatible with the one used by libclang.

In order to fix that, add build.rs

build.rs
use std::{env, path::{Path, PathBuf}};

fn main() {
    // Allow override via environment variable for flexibility.
    // Example: LLVM19_LIB_DIR=/custom/llvm19/lib cargo build
    let llvm_dir = env::var("LLVM19_LIB_DIR").unwrap_or_else(|_| "/usr/lib/llvm-19/lib".to_string());
    let llvm_so_candidates = [
        "libLLVM.so",            // soname symlink
        "libLLVM-19.so",         // alternative naming pattern
        "libLLVM.so.19",         // versioned pattern
        "libLLVM.so.19.1",       // specific minor version
    ];

    let dir_path = Path::new(&llvm_dir);
    if !dir_path.is_dir() {
        panic!("LLVM directory '{llvm_dir}' does not exist. Set LLVM19_LIB_DIR to the correct path.");
    }

    // Try to locate an actual existing lib file we can pass explicitly to the linker to avoid picking one from elsewhere (e.g., /opt/amdgpu)
    let chosen_lib: Option<PathBuf> = llvm_so_candidates.iter()
        .map(|name| dir_path.join(name))
        .find(|p| p.exists());

    if let Some(lib_path) = chosen_lib {
        let lib_dir = lib_path.parent().unwrap();
        // Ensure our desired directory is searched first
        println!("cargo:rustc-link-search=native={}", lib_dir.display());
        // We still emit the generic link-lib to satisfy symbols possibly split across components.
        println!("cargo:rustc-link-lib=dylib=LLVM");
        // Force the exact library by passing its absolute path as a linker arg (placed after objects so it is used for resolution)
        println!("cargo:rustc-link-arg={}", lib_path.display());
        // Add an rpath so the runtime loader also prefers this directory.
        println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display());
        // Optionally prefer our path by adding it at RUNPATH as well (GNU ld treats rpath/runpath subtly; using rpath here is sufficient for most systems)
        println!("cargo:rerun-if-env-changed=LLVM19_LIB_DIR");
    } else {
        panic!("Could not locate any libLLVM* candidate in {llvm_dir}. Candidates tried: {:?}", llvm_so_candidates);
    }
}

Also you need to configure Cargo.toml to use that build script:

Cargo.toml
[package]
build = "build.rs"

Check out similar posts by category: Rust