Rust custom Triplet

от автора

Целевые триплеты описывают платформу, на которой выполняется код, и являются основной концепцией системы сборки GNU. Обычно триплет содержит три поля: название семейства/модели CPU, поставщика и имя операционной системы. Кроме того, триплет может иметь дополнительное поле, отражающее Application Binary Interface (ABI), например: gnu, gnueabihf, gnu_ilp32.

Rust Custom Triplet

Rust Custom Triplet

Просмотреть целевой триплет текущей системы можно с помощью команды `gcc -dumpmachine’:

gcc -dumpmachine x86_64-slackware-linux

Если вы создаете новую систему или собственный GNU/Linux дистрибутив, у вас может возникнуть необходимость иметь собственный целевой триплет. Например, x86_64-radix-linux-gnu для CPU Intel или AMD.

При создании инструментария (toolchain-а) на базе GCC все выглядит элементарно. Достаточно сконфигурировать binutils с опцией —enable-targets=x86_64-radix-linux-gnu. Однако добавление собственного триплета компиляторов LLVM и Rust выглядит уже не совсем тривиально.

Здесь мы рассмотрим добавление нового триплета инструментария языка Rust.

Прежде всего необходимо загрузить исходный код:

git clone https://github.com/rust-lang/rust.git rust-1.82.0 ( cd rust-1.82.0   git checkout -b 1.82.0 tags/1.82.0   git submodule update --init --recursive )

Поскольку репозиторий исходного кода Rust содержит исходный код проекта LLVM, мы сможем одновременно рассмотреть добавление нового триплета и для случая LLVM, и для случая Rust.

LLVM Project

Итак, для того чтобы добавить новый триплет компилятору clang нам необходимо внести изменения в файлы:

rust-1.82.0/src/llvm-project/clang/lib/Driver/ToolChains/Gnu.cpp rust-1.82.0/src/llvm-project/llvm/include/llvm/TargetParser/Triple.h rust-1.82.0/src/llvm-project/llvm/lib/TargetParser/Triple.cpp

Патч для Rust 1.82.0 представлен на следующем листинге:

diff --unified -Nr rust-1.82.0-orig/src/llvm-project/clang/lib/Driver/ToolChains/Gnu.cpp rust-1.82.0/src/llvm-project/clang/lib/Driver/ToolChains/Gnu.cpp --- rust-1.82.0-orig/src/llvm-project/clang/lib/Driver/ToolChains/Gnu.cpp2024-11-10 22:33:14.000000000 +0300 +++ rust-1.82.0/src/llvm-project/clang/lib/Driver/ToolChains/Gnu.cpp2024-11-11 01:34:23.586151730 +0300 @@ -2491,7 +2491,7 @@        "x86_64-linux-gnu",       "x86_64-unknown-linux-gnu",        "x86_64-pc-linux-gnu",    "x86_64-redhat-linux6E",        "x86_64-redhat-linux",    "x86_64-suse-linux", -      "x86_64-manbo-linux-gnu", "x86_64-slackware-linux", +      "x86_64-manbo-linux-gnu", "x86_64-slackware-linux", "x86_64-radix-linux-gnu",        "x86_64-unknown-linux",   "x86_64-amazon-linux"};    static const char *const X32Triples[] = {"x86_64-linux-gnux32",                                             "x86_64-pc-linux-gnux32"}; diff --unified -Nr rust-1.82.0-orig/src/llvm-project/llvm/include/llvm/TargetParser/Triple.h rust-1.82.0/src/llvm-project/llvm/include/llvm/TargetParser/Triple.h --- rust-1.82.0-orig/src/llvm-project/llvm/include/llvm/TargetParser/Triple.h2024-11-10 22:33:17.000000000 +0300 +++ rust-1.82.0/src/llvm-project/llvm/include/llvm/TargetParser/Triple.h2024-11-11 01:34:23.585151730 +0300 @@ -183,6 +183,7 @@        Apple,      PC, +    Radix,      SCEI,      Freescale,      IBM, diff --unified -Nr rust-1.82.0-orig/src/llvm-project/llvm/lib/TargetParser/Triple.cpp rust-1.82.0/src/llvm-project/llvm/lib/TargetParser/Triple.cpp --- rust-1.82.0-orig/src/llvm-project/llvm/lib/TargetParser/Triple.cpp2024-11-10 22:33:17.000000000 +0300 +++ rust-1.82.0/src/llvm-project/llvm/lib/TargetParser/Triple.cpp2024-11-11 01:34:23.585151730 +0300 @@ -251,6 +251,7 @@    case NVIDIA: return "nvidia";    case OpenEmbedded: return "oe";    case PC: return "pc"; +  case Radix: return "radix";    case SCEI: return "scei";    case SUSE: return "suse";    } @@ -625,6 +626,7 @@    return StringSwitch(VendorName)      .Case("apple", Triple::Apple)      .Case("pc", Triple::PC) +    .Case("radix", Triple::Radix)      .Case("scei", Triple::SCEI)      .Case("sie", Triple::SCEI)      .Case("fsl", Triple::Freescale) 

Первый файл содержит массивы имен целевых триплетов для различных архитектур CPU, и здесь мы добавили наш триплет в массив X86_64Triples[].

Остальные два файла необходимо редактировать на случай того, если вы захотите изменять некоторые величины динамически, во время сборки LLVM. Например, в зависимости от имени операционной системы в триплете, выбирать путь к интерпретатору:

  if (Triple.getVendor() == llvm::Triple::Radix ) {     LibDir = X32 ? "libx32" : "lib";   } else {     LibDir = X32 ? "libx32" : "lib64";   }   Loader = X32 ? "ld-linux-x32.so.2" : "ld-linux-x86-64.so.2";

Здесь переменная Triple.getVendor() содержит текущее имя системы и вы всегда можете сравнивать его с тем именем, для которого необходимо выполнить определенные действия.

Rust Compiler

Итак, мы добавили новый триплет в проект LLVM. Теперь необходимо изменить еще несколько файлов, но уже непосредственно для компилятора Rust:

rust-1.82.0/compiler/rustc_target/src/spec/mod.rs rust-1.82.0/library/std/Cargo.toml rust-1.82.0/src/bootstrap/src/core/sanity.rs

И кроме того, в каталог rust-1.82.0/compiler/rustc_target/src/spec/targets/, добавить файл описания целевой архитектуры, соотвествующей новому триплету:

x86_64_radix_linux_gnu.rs:

use crate::spec::{base, Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target};  pub fn target() -> Target {     let mut base = base::linux_gnu::opts();     base.cpu = "x86-64".into();     base.plt_by_default = false;     base.max_atomic_width = Some(64);     base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);     base.stack_probes = StackProbeType::Inline;     base.static_position_independent_executables = true;     base.supported_sanitizers = SanitizerSet::ADDRESS         | SanitizerSet::CFI         | SanitizerSet::KCFI         | SanitizerSet::DATAFLOW         | SanitizerSet::LEAK         | SanitizerSet::MEMORY         | SanitizerSet::SAFESTACK         | SanitizerSet::THREAD;     base.supports_xray = true;      // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using     // linker flavor, and self-contained linker component.     if option_env!("CFG_USE_SELF_CONTAINED_LINKER").is_some() {         base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes);         base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker();     }      Target {         llvm_target: "x86_64-radix-linux-gnu".into(),         metadata: crate::spec::TargetMetadata {             description: Some("64-bit Linux (kernel 3.2+, glibc 2.17+)".into()),             tier: Some(1),             host_tools: Some(true),             std: Some(true),         },         pointer_width: 64,         data_layout:             "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(),         arch: "x86_64".into(),         options: base,     } } 

В случае x86_64 создание такого файла достаточно просто, его содержимое можно скопировать из файла x86_64_unknown_linux_gnu.rs и изменить в нем имя триплета на x86_64-radix-linux-gnu.

Патч для Rust 1.82.0 представлен на следующем листинге:

diff --unified -Nr rust-1.82.0-orig/compiler/rustc_target/src/spec/mod.rs rust-1.82.0/compiler/rustc_target/src/spec/mod.rs --- rust-1.82.0-orig/compiler/rustc_target/src/spec/mod.rs2024-11-10 22:28:19.000000000 +0300 +++ rust-1.82.0/compiler/rustc_target/src/spec/mod.rs2024-11-11 01:47:03.927109832 +0300 @@ -1646,6 +1646,9 @@        ("i686-unknown-hurd-gnu", i686_unknown_hurd_gnu),   +    // RcL Triples: +    ("x86_64-radix-linux-gnu", x86_64_radix_linux_gnu), +      ("aarch64-apple-darwin", aarch64_apple_darwin),      ("arm64e-apple-darwin", arm64e_apple_darwin),      ("x86_64-apple-darwin", x86_64_apple_darwin), diff --unified -Nr rust-1.82.0-orig/compiler/rustc_target/src/spec/targets/x86_64_radix_linux_gnu.rs rust-1.82.0/compiler/rustc_target/src/spec/targets/x86_64_radix_linux_gnu.rs --- rust-1.82.0-orig/compiler/rustc_target/src/spec/targets/x86_64_radix_linux_gnu.rs1970-01-01 03:00:00.000000000 +0300 +++ rust-1.82.0/compiler/rustc_target/src/spec/targets/x86_64_radix_linux_gnu.rs2024-11-11 01:47:03.927109832 +0300 @@ -0,0 +1,42 @@ +use crate::spec::{base, Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target}; + +pub fn target() -> Target { +    let mut base = base::linux_gnu::opts(); +    base.cpu = "x86-64".into(); +    base.plt_by_default = false; +    base.max_atomic_width = Some(64); +    base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); +    base.stack_probes = StackProbeType::Inline; +    base.static_position_independent_executables = true; +    base.supported_sanitizers = SanitizerSet::ADDRESS +        | SanitizerSet::CFI +        | SanitizerSet::KCFI +        | SanitizerSet::DATAFLOW +        | SanitizerSet::LEAK +        | SanitizerSet::MEMORY +        | SanitizerSet::SAFESTACK +        | SanitizerSet::THREAD; +    base.supports_xray = true; + +    // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using +    // linker flavor, and self-contained linker component. +    if option_env!("CFG_USE_SELF_CONTAINED_LINKER").is_some() { +        base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes); +        base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker(); +    } + +    Target { +        llvm_target: "x86_64-radix-linux-gnu".into(), +        metadata: crate::spec::TargetMetadata { +            description: Some("64-bit Linux (kernel 3.2+, glibc 2.17+)".into()), +            tier: Some(1), +            host_tools: Some(true), +            std: Some(true), +        }, +        pointer_width: 64, +        data_layout: +            "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), +        arch: "x86_64".into(), +        options: base, +    } +} diff --unified -Nr rust-1.82.0-orig/library/std/Cargo.toml rust-1.82.0/library/std/Cargo.toml --- rust-1.82.0-orig/library/std/Cargo.toml2024-11-10 22:28:19.000000000 +0300 +++ rust-1.82.0/library/std/Cargo.toml2024-11-11 01:47:03.927109832 +0300 @@ -141,7 +141,7 @@  level = "warn"  check-cfg = [      'cfg(bootstrap)', -    'cfg(target_arch, values("xtensa"))', +    'cfg(target_arch, values("xtensa", "x86_64-radix-linux-gnu"))',      # std use #[path] imports to portable-simd `std_float` crate      # and to the `backtrace` crate which messes-up with Cargo list      # of declared features, we therefor expect any feature cfg diff --unified -Nr rust-1.82.0-orig/src/bootstrap/src/core/sanity.rs rust-1.82.0/src/bootstrap/src/core/sanity.rs --- rust-1.82.0-orig/src/bootstrap/src/core/sanity.rs2024-11-10 22:28:19.000000000 +0300 +++ rust-1.82.0/src/bootstrap/src/core/sanity.rs2024-11-11 01:47:03.927109832 +0300 @@ -34,6 +34,7 @@  // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap).  const STAGE0_MISSING_TARGETS: &[&str] = &[      // just a dummy comment so the list doesn't get onelined +    "x86_64-radix-linux-gnu"  ];    /// Minimum version threshold for libstdc++ required when using prebuilt LLVM  

Make and Install

Теперь мы готовы к сборке Rust инструментария.

Сконфигурировать исходный код Rust можно с помощью следующей команды:

./configure --prefix=/opt/toolchains/RUST/1.82.0 \             --sysconfdir=/opt/toolchains/RUST/1.82.0/etc \             --disable-codegen-tests \             --disable-vendor \             --build=x86_64-unknown-linux-gnu \             --host=x86_64-unknown-linux-gnu \             --target=x86_64-unknown-linux-gnu,x86_64-radix-linux-gnu \             --enable-clang

Здесь мы выбрали стандартный целевой триплет для архитектуры x86_64, а также наш новый триплет x86_64-radix-linux-gnu.

После выполнения данной команды, будет создан файл config.toml следующего содержания:

profile = 'dist' change-id = 129295  [llvm] clang = true  [build] build = 'x86_64-unknown-linux-gnu' host = ['x86_64-unknown-linux-gnu'] target = ['x86_64-unknown-linux-gnu', 'x86_64-radix-linux-gnu'] vendor = false configure-args = ['--prefix=/opt/toolchains/RUST/1.82.0', '--sysconfdir=/opt/toolchains/RUST/1.82.0/etc', '--disable-codegen-tests', '--disable-vendor', '--build=x86_64-unknown-linux-gnu', '--host=x86_64-unknown-linux-gnu', '--target=x86_64-unknown-linux-gnu,x86_64-radix-linux-gnu', '--enable-clang']  [install] prefix = '/opt/toolchains/RUST/1.82.0' sysconfdir = '/opt/toolchains/RUST/1.82.0/etc'  [rust] codegen-tests = false  [target.x86_64-unknown-linux-gnu]  [target.x86_64-radix-linux-gnu]  [dist]  

Этот файл также необходимо отредактировать, добавив идентификатор изменений:

change-id = 129295

Его можно легко получить попробовав команду make, которая приведет к неудаче, но выведет на экран необходимый идентификатор. Вообще, правильный путь поиска величины change-id состоит в том, чтобы посмотреть последнюю запись ChangeInfo в файле:

rust-1.82.0/src/bootstrap/src/utils/change_tracker.rs

В приведенном выше файле config.toml мы уже сделали эти изменения.

В нашем случае, секцию [target.x86_64-radix-linux-gnu] изменять нет необходимости. Однако если вы захотите создать триплет для другой архитектуры, требующей cross-сборки, то вам будет необходимо собрать GCC-toolchain и добавить в соответствующую секцию сведения об основных утилитах, например, так:

[target.aarch64-m1000-linux-gnu] linker = '/opt/toolchains/aarch64-M1000-linux-glibc/1.11.3/bin/aarch64-m1000-linux-gnu-gcc' cc = '/opt/toolchains/aarch64-M1000-linux-glibc/1.11.3/bin/aarch64-m1000-linux-gnu-gcc' cxx = '/opt/toolchains/aarch64-M1000-linux-glibc/1.11.3/bin/aarch64-m1000-linux-gnu-g++' ar = '/opt/toolchains/aarch64-M1000-linux-glibc/1.11.3/bin/aarch64-m1000-linux-gnu-ar' ranlib = '/opt/toolchains/aarch64-M1000-linux-glibc/1.11.3/bin/aarch64-m1000-linux-gnu-ranlib'

Здесь мы не будем рассматривать тонкости создания cross-компилятора Rust.

После того, как исходный код Rust сконфигурирован, собрать и инсталлировать компилятор можно с помощью следующих команд:

make make install

Далее необходимо инсталлировать утилиту cbindgen:

export PATH=/opt/toolchains/RUST/1.82.0/bin:$PATH cargo  install --root /opt/toolchains/RUST/1.82.0 --version 0.27.0 cbindgen

Теперь Rust инструментарий готов к работе.

Если вы уже инсталлировали cargo в домашний каталог, то вы можете подключить собранный вами инструментарий (toolchain) в набор уже инсталлированых ранее toolchain-ов, например, с именем RcL-1.82.0-x86_64-unknown :

rustup toolchain link RcL-1.82.0-x86_64-unknown-linux-gnu /opt/toolchains/RUST/1.82.0

По завершении данной команды, список ваших toolchain-ов может выглядеть, например, так:

rustup toolchain list  stable-x86_64-unknown-linux-gnu RcL-1.82.0-x86_64-unknown-linux-gnu 1.71.1-x86_64-unknown-linux-gnu (default)

Итак, в отличие от GNU Коллекции Компиляторов, проекты LLVM и Rust построены так, что элементарные настройки превращаются в достаточно нетривиальную последовательность действий. А ведь все начиналось весьма просто, но в какой-то момент в очередной выпуск доступных cross-компиляторов Rust (после версии 1.71.1) забыли добавить триплет mipsel-unknown-linux-gnu. Тогда-то и пришлось задуматься о сборке собственных кросс-инструментариев Rust, чтобы обрести некоторую независимость, ведь сейчас все больше и больше открытых проектов выбирают язык Rust.

Источники:

Enjoy.


ссылка на оригинал статьи https://habr.com/ru/articles/857564/