Як виправити Rust clang crate: symbol lookup error: undefined symbol: LLVMInitializeAArch64TargetInfo

Проблема

Під час спроби запустити Rust-виконуваний файл, що використовує крейт clang, ви бачите таке повідомлення про помилку:

clang_symbol_lookup_error.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

Рішення

Ця помилка виникає через те, що Cargo компонує неправильну libLLVM. Поширеною причиною є те, що у вас встановлено rocm (обчислювальний стек AMD), який постачається зі власною libLLVM, несумісною з тією, що використовується libclang.

Щоб це виправити, додайте build.rs

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

fn main() {
    // Дозволяємо перевизначення через змінну середовища для гнучкості.
    // Приклад: 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-симлінк
        "libLLVM-19.so",         // альтернативний шаблон найменування
        "libLLVM.so.19",         // версіонований шаблон
        "libLLVM.so.19.1",       // конкретна мінорна версія
    ];

    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.");
    }

    // Намагаємося знайти реальний існуючий файл бібліотеки, який можна явно передати лінкеру, щоб уникнути вибору з іншого місця (наприклад, /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();
        // Забезпечуємо, щоб наш бажаний каталог шукався першим
        println!("cargo:rustc-link-search=native={}", lib_dir.display());
        // Ми все одно випускаємо загальну link-lib, щоб задовольнити символи, які можуть бути розподілені між компонентами.
        println!("cargo:rustc-link-lib=dylib=LLVM");
        // Примусово вказуємо точну бібліотеку, передаючи її абсолютний шлях як аргумент лінкера (розміщується після об'єктів, тому використовується для розв'язання символів)
        println!("cargo:rustc-link-arg={}", lib_path.display());
        // Додаємо rpath, щоб завантажувач під час виконання також віддавав перевагу цьому каталогу.
        println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display());
        // За бажанням віддаємо перевагу нашому шляху, також додаючи його до RUNPATH (GNU ld трактує rpath/runpath по-різному; використання rpath тут достатньо для більшості систем)
        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);
    }
}

Також потрібно налаштувати Cargo.toml для використання цього скрипта збірки:

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

Дивіться схожі статті за категоріями: Rust