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