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 все выглядит элементарно. Достаточно сконфигурировать buinutils с опцией --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.cpp	2024-11-10 22:33:14.000000000 +0300
+++ rust-1.82.0/src/llvm-project/clang/lib/Driver/ToolChains/Gnu.cpp	2024-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.h	2024-11-10 22:33:17.000000000 +0300
+++ rust-1.82.0/src/llvm-project/llvm/include/llvm/TargetParser/Triple.h	2024-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.cpp	2024-11-10 22:33:17.000000000 +0300
+++ rust-1.82.0/src/llvm-project/llvm/lib/TargetParser/Triple.cpp	2024-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_radix_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.rs	2024-11-10 22:28:19.000000000 +0300
+++ rust-1.82.0/compiler/rustc_target/src/spec/mod.rs	2024-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.rs	1970-01-01 03:00:00.000000000 +0300
+++ rust-1.82.0/compiler/rustc_target/src/spec/targets/x86_64_radix_linux_gnu.rs	2024-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.toml	2024-11-10 22:28:19.000000000 +0300
+++ rust-1.82.0/library/std/Cargo.toml	2024-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.rs	2024-11-10 22:28:19.000000000 +0300
+++ rust-1.82.0/src/bootstrap/src/core/sanity.rs	2024-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.

© Habrahabr.ru