| Jan Schär | 548cbe6 | 2025-07-03 16:26:11 +0000 | [diff] [blame^] | 1 | commit e48fd8490d497fdfa6d5a7f8a3af63cc8c6db538 |
| 2 | Author: Jan Schär <jan@monogon.tech> |
| 3 | Date: Thu Jul 3 16:03:25 2025 +0000 |
| 4 | |
| 5 | Use musl toolchain and add loader_wrapper |
| 6 | |
| 7 | This patches rules_rust to use the Rust toolchain built for musl instead |
| 8 | of glibc, and adds a wrapper to locate the dynamic loader. What we |
| 9 | achieve with this is that we no longer need a specific loader installed |
| 10 | at an absolute path, which makes the build less dependent on the host |
| 11 | OS. |
| 12 | |
| 13 | The tools are linked dynamically and need libc.so and libgcc_s.so.1. At |
| 14 | least rustc cannot be linked statically, because proc macros are loaded |
| 15 | with dlopen. When building proc macros, we need to add library paths |
| 16 | containing the two .so files such that lld can find them. |
| 17 | |
| 18 | Usually, musl would be installed at /lib/ld-musl-x86_64.so.1, but we |
| 19 | can't do that. Instead, the binaries are renamed to {name}_real and a |
| 20 | wrapper script is placed there instead. The wrapper runs musl as a |
| 21 | dynamic loader, passing it the path to the real binary. |
| 22 | |
| 23 | diff --git a/rust/platform/triple.bzl b/rust/platform/triple.bzl |
| 24 | index 87cd6242..766bc08e 100644 |
| 25 | --- a/rust/platform/triple.bzl |
| 26 | +++ b/rust/platform/triple.bzl |
| 27 | @@ -138,7 +138,7 @@ def get_host_triple(repository_ctx, abi = {}): |
| 28 | prefix = "{}-unknown-linux".format(arch) |
| 29 | return triple("{}-{}".format( |
| 30 | prefix, |
| 31 | - abi.get(prefix, "gnu"), |
| 32 | + abi.get(prefix, "musl"), |
| 33 | )) |
| 34 | |
| 35 | if "mac" in repository_ctx.os.name: |
| 36 | diff --git a/rust/private/loader_wrapper.sh b/rust/private/loader_wrapper.sh |
| 37 | new file mode 100755 |
| 38 | index 00000000..29805d8d |
| 39 | --- /dev/null |
| 40 | +++ b/rust/private/loader_wrapper.sh |
| 41 | @@ -0,0 +1,18 @@ |
| 42 | +#!/bin/sh |
| 43 | + |
| 44 | +# Running rustc and other tools directly doesn't work, because that would |
| 45 | +# require musl to be installed at /lib/ld-musl-x86_64.so.1, but we can't install |
| 46 | +# things at absolute paths. Instead, this script calls musl as a dynamic loader, |
| 47 | +# giving it the path to the real binary which has been renamed to {name}_real. |
| 48 | + |
| 49 | +set -eu |
| 50 | + |
| 51 | +selfDir="$(dirname "$0")" |
| 52 | +rootPath="$(dirname "$selfDir")" |
| 53 | + |
| 54 | +exec "${rootPath}/sysroot/usr/lib/libc.so" \ |
| 55 | + "--library-path=${rootPath}/sysroot/usr/lib" \ |
| 56 | + "--argv0" "$0" \ |
| 57 | + "--" \ |
| 58 | + "${0}_real" \ |
| 59 | + "$@" |
| 60 | diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl |
| 61 | index 27c08e78..0761c34c 100644 |
| 62 | --- a/rust/private/repository_utils.bzl |
| 63 | +++ b/rust/private/repository_utils.bzl |
| 64 | @@ -41,12 +41,16 @@ filegroup( |
| 65 | name = "rustc_lib", |
| 66 | srcs = glob( |
| 67 | [ |
| 68 | + "bin/*_real", |
| 69 | "bin/*{dylib_ext}", |
| 70 | "lib/*{dylib_ext}*", |
| 71 | "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}", |
| 72 | "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}", |
| 73 | "lib/rustlib/{target_triple}/lib/*{dylib_ext}*", |
| 74 | "lib/rustlib/{target_triple}/lib/*.rmeta", |
| 75 | + "sysroot/usr/lib/libc.so", |
| 76 | + "sysroot/usr/lib/libgcc_s.so*", |
| 77 | + "sysroot/usr/lib/libunwind.so*", |
| 78 | ], |
| 79 | allow_empty = True, |
| 80 | ), |
| 81 | diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl |
| 82 | index d78c2890..fbc00591 100644 |
| 83 | --- a/rust/private/rustc.bzl |
| 84 | +++ b/rust/private/rustc.bzl |
| 85 | @@ -1012,6 +1012,12 @@ def construct_arguments( |
| 86 | if linker_script: |
| 87 | rustc_flags.add(linker_script, format = "--codegen=link-arg=-T%s") |
| 88 | |
| 89 | + if is_exec_configuration(ctx): |
| 90 | + # This is needed when building proc macros, for lld to find libgcc_s.so |
| 91 | + # and libc.so. This must be before the Rust standard library paths so |
| 92 | + # lld uses libc.so instead of the libc.a shipped by Rust. |
| 93 | + rustc_flags.add("-L", "native={}/sysroot/usr/lib".format(tool_path.rsplit("/", 2)[0])) |
| 94 | + |
| 95 | # Tell Rustc where to find the standard library (or libcore) |
| 96 | rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s") |
| 97 | rustc_flags.add_all(rust_flags, map_each = map_flag) |
| 98 | diff --git a/rust/repositories.bzl b/rust/repositories.bzl |
| 99 | index a5df3842..323a26ae 100644 |
| 100 | --- a/rust/repositories.bzl |
| 101 | +++ b/rust/repositories.bzl |
| 102 | @@ -42,13 +42,13 @@ load_arbitrary_tool = _load_arbitrary_tool |
| 103 | DEFAULT_TOOLCHAIN_TRIPLES = { |
| 104 | "aarch64-apple-darwin": "rust_macos_aarch64", |
| 105 | "aarch64-pc-windows-msvc": "rust_windows_aarch64", |
| 106 | - "aarch64-unknown-linux-gnu": "rust_linux_aarch64", |
| 107 | + "aarch64-unknown-linux-musl": "rust_linux_aarch64", |
| 108 | "powerpc64le-unknown-linux-gnu": "rust_linux_powerpc64le", |
| 109 | "s390x-unknown-linux-gnu": "rust_linux_s390x", |
| 110 | "x86_64-apple-darwin": "rust_macos_x86_64", |
| 111 | "x86_64-pc-windows-msvc": "rust_windows_x86_64", |
| 112 | "x86_64-unknown-freebsd": "rust_freebsd_x86_64", |
| 113 | - "x86_64-unknown-linux-gnu": "rust_linux_x86_64", |
| 114 | + "x86_64-unknown-linux-musl": "rust_linux_x86_64", |
| 115 | } |
| 116 | |
| 117 | _COMPACT_WINDOWS_NAMES = True |
| 118 | @@ -349,6 +349,33 @@ def rust_repositories(**kwargs): |
| 119 | |
| 120 | rust_register_toolchains(**kwargs) |
| 121 | |
| 122 | +def load_sysroot(ctx, target_triple): |
| 123 | + # Download the sysroot. We only need libc.so and libunwind.so from this; |
| 124 | + # they are runtime dependencies of rustc, cargo, etc. |
| 125 | + ARTIFACTS_VERSION = "20250603" |
| 126 | + if target_triple.arch == "x86_64": |
| 127 | + ctx.download_and_extract( |
| 128 | + ["https://monogon-llvm-toolchain.storage.googleapis.com/%s/sysroot-x86_64-unknown-linux-musl.tar.zst" % ARTIFACTS_VERSION], |
| 129 | + sha256 = "ba0fce4438de6d4dbc1678d2e672f377d30327d24e10b0de09f090032d34c1ba", |
| 130 | + output = "sysroot", |
| 131 | + ) |
| 132 | + elif target_triple.arch == "aarch64": |
| 133 | + ctx.download_and_extract( |
| 134 | + ["https://monogon-llvm-toolchain.storage.googleapis.com/%s/sysroot-aarch64-unknown-linux-musl.tar.zst" % ARTIFACTS_VERSION], |
| 135 | + sha256 = "0cee4d5c1d24a9e9e2d1d12fd5e05ea1afcf69ea0b5d43979290988143ce1a0f", |
| 136 | + output = "sysroot", |
| 137 | + ) |
| 138 | + else: |
| 139 | + fail("missing sysroot for architecture {}".format(target_triple.arch)) |
| 140 | + # Rust uses either libgcc_s.so or libunwind.so for unwinding. Both have the |
| 141 | + # same interface for unwinding, so we can make libgcc_s.so a symlink to |
| 142 | + # libunwind.so and it will work. This could potentially break in unknown |
| 143 | + # ways, but it didn't break so far. Unwinding was tested by inserting a |
| 144 | + # panic in a proc macro, which worked as expected. This avoids us needing to |
| 145 | + # either build libgcc_s.so or build Rust configured to use libunwind.so. |
| 146 | + ctx.symlink("sysroot/usr/lib/libunwind.so.1.0", "sysroot/usr/lib/libgcc_s.so.1") |
| 147 | + ctx.symlink("sysroot/usr/lib/libunwind.so.1.0", "sysroot/usr/lib/libgcc_s.so") |
| 148 | + |
| 149 | _RUST_TOOLCHAIN_REPOSITORY_ATTRS = { |
| 150 | "allocator_library": attr.string( |
| 151 | doc = "Target that provides allocator functions when rust_library targets are embedded in a cc_binary.", |
| 152 | @@ -539,6 +566,13 @@ def _rust_toolchain_tools_repository_impl(ctx): |
| 153 | repro[key] = getattr(ctx.attr, key) |
| 154 | repro["sha256s"] = sha256s |
| 155 | |
| 156 | + load_sysroot(ctx = ctx, target_triple = exec_triple) |
| 157 | + loader_wrapper = ctx.read(Label("//rust/private:loader_wrapper.sh")) |
| 158 | + ctx.rename("bin/cargo", "bin/cargo_real") |
| 159 | + ctx.file("bin/cargo", loader_wrapper) |
| 160 | + ctx.rename("bin/rustc", "bin/rustc_real") |
| 161 | + ctx.file("bin/rustc", loader_wrapper) |
| 162 | + |
| 163 | return repro |
| 164 | |
| 165 | rust_toolchain_tools_repository = repository_rule( |
| 166 | @@ -928,6 +962,11 @@ def _rustfmt_toolchain_tools_repository_impl(repository_ctx): |
| 167 | repro[key] = getattr(repository_ctx.attr, key) |
| 168 | repro["sha256s"] = sha256s |
| 169 | |
| 170 | + load_sysroot(ctx = repository_ctx, target_triple = exec_triple) |
| 171 | + loader_wrapper = repository_ctx.read(Label("//rust/private:loader_wrapper.sh")) |
| 172 | + repository_ctx.rename("bin/rustfmt", "bin/rustfmt_real") |
| 173 | + repository_ctx.file("bin/rustfmt", loader_wrapper) |
| 174 | + |
| 175 | return repro |
| 176 | |
| 177 | rustfmt_toolchain_tools_repository = repository_rule( |