Skip to content

Commit 906d1fa

Browse files
authored
add allocator libraries compatible with the new rustc mangling (#3403)
This adds a version of the allocator support libraries implemented in rust, to account for the new internal allocator symbol mangling applied by rustc (rust-lang/rust#127173). This mechanism relies on unstable language features and requires a nightly rustc from 2025-04-05 or later. It is designed to be compatible with: * all allocator flavors (the default and global) * all flavors of `no_std` * both situations for when a `rust_library` is used as a dependency of a `cc_binary` and when the `experimental_use_cc_common_link` setting is used. Rustc generates references to internal allocator symbols when building rust libraries. At link time, rustc generates the definitions of these symbols. When rustc is not used as the final linker, we need to generate the definitions ourselves. This happens for example when a `rust_library` is used as a dependency of a `rust_binary`, or when the `experimental_use_cc_common_link` setting is used. For older versions of rustc, the allocator symbol definitions can be provided via the `rust_toolchain`'s `allocator_library` or `global_allocator_library` attributes, with sample targets like `//ffi/cc/allocator_library` and `//ffi/cc/global_allocator_library`. Recent versions of rustc started mangling these allocator symbols (rust-lang/rust#127173). The mangling uses a scheme that is specific to the exact version of the compiler. This makes the cc allocator library definitions ineffective. To work around this, we provide rust versions of the symbol definitions annotated with an unstable language attribute that instructs rustc to mangle them consistently. Because of the usage of unstable language features, this is only compatible with nightly versions of the compiler. Since the new symbol definitions are written in rust, we cannot just attach them as attributes on the `rust_toolchain` as the old cc versions, as that would create a build graph cycle: 1. any `rust_library` depends on a `rust_toolchain`, 2. the `rust_toolchain` depends on the allocator library, 3. so the allocator library cannot just be a `rust_library` directly. The bootstrapping cycle can be avoided by defining a separate internal "initial" rust toolchain specifically for building the rust allocator libraries, and use a transition to attach the generated allocator libraries to the "main" rust toolchain. But that duplicates the whole subgraph of the build around the rust toolchains, repository and supporting tools used for them. Instead, we define a new custom `rust_allocator_libraries` rule, which builds the new rust allocator libraries and exposes them via a new `AllocatorLibrariesInfo` provider. Since we cannot attach such a target as an attribute to the `rust_toolchain`, we attach it as a new `allocator_libraries` common attribute to the rust rules. We ported the cc versions into the new rust implementations of the (default) allocator and global allocator flavors in `//ffi/rs/allocator_library` and `//ffi/rs/global_allocator_library`. The rust standard libraries themselves have references to the allocator libraries. For correct linking order of the standard libraries, we need to establish the new rust allocator libraries as link-time dependencies of the standard libraries. The specific set of linker inputs depends on the no_std flavor and the choice of (default) allocator vs. global allocator. We establish the linking dependencies by extending the `rust_toolchain` `_make_libstd_and_allocator_ccinfo` feature to let us inject a custom allocator library as a standard library dependency and using that in the `rust_allocator_libraries` implementation to generate the final standard libraries linker graph. The new functionality is opt-in and gated via: 1. the new setting `//rust/settings:experimental_use_allocator_libraries_with_mangled_symbols`, which supplies the default value for the repository, and 2. the new `rust_toolchain` attribute `experimental_use_allocator_libraries_with_mangled_symbols`, which lets the user override the choice of using the new-style rust-based allocator symbols, or the old-style cc-based ones for a specific toolchain. For testing, I created variants for the cc_common.link / no_std / global_allocator test matrix. I found it useful to be able to declare `target_settings` via the `rust.repository_set` rule, similarly to the `target_compatible_with`. This lets us include additional toolchains in the same repository that are distinguished by the setting, like in: https://github.com/bazelbuild/rules_rust/pull/3403/files#diff-164dd740b73baa94f9a84ed0d4a7ec29f8adeb34ff8430492747e2443f7f39d3
1 parent 46378c2 commit 906d1fa

File tree

24 files changed

+645
-37
lines changed

24 files changed

+645
-37
lines changed

.bazelci/presubmit.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,18 @@ tasks:
534534
- "//..."
535535
test_targets:
536536
- "//..."
537+
cc_common_link_and_mangling_ubuntu2004:
538+
name: Build via cc_common.link with rustc mangling alloc symbols
539+
platform: ubuntu2004
540+
working_directory: test/integration/cc_common_link
541+
build_targets:
542+
- "//..."
543+
test_targets:
544+
- "//..."
545+
build_flags:
546+
- "--config=mangled_alloc_symbols"
547+
test_flags:
548+
- "--config=mangled_alloc_symbols"
537549
cc_common_link_with_global_alloc_ubuntu2004:
538550
name: Build via cc_common.link using a global allocator
539551
platform: ubuntu2004
@@ -550,6 +562,18 @@ tasks:
550562
- "//..."
551563
test_targets:
552564
- "//..."
565+
cc_common_link_with_global_alloc_and_mangling_ubuntu2004:
566+
name: Build via cc_common.link using a global allocator with rustc mangling alloc symbols
567+
platform: ubuntu2004
568+
working_directory: test/integration/cc_common_link_with_global_alloc
569+
build_targets:
570+
- "//..."
571+
test_targets:
572+
- "//..."
573+
build_flags:
574+
- "--config=mangled_alloc_symbols"
575+
test_flags:
576+
- "--config=mangled_alloc_symbols"
553577
cc_common_link_no_std_ubuntu2004:
554578
name: Build with no_std + alloc using cc_common.link infrastructure for linking
555579
platform: ubuntu2004
@@ -562,6 +586,18 @@ tasks:
562586
- "--config=no_std_alloc_using_cc_common_link"
563587
test_flags:
564588
- "--config=no_std_alloc_using_cc_common_link"
589+
cc_common_link_no_std_and_mangling_ubuntu2004:
590+
name: Build with no_std + alloc using cc_common.link infrastructure for linking with rust toolchain mangling alloc symbols
591+
platform: ubuntu2004
592+
working_directory: test/integration/no_std
593+
build_targets:
594+
- "//..."
595+
test_targets:
596+
- "//..."
597+
build_flags:
598+
- "--config=no_std_alloc_using_cc_common_link_and_mangled_alloc_symbols"
599+
test_flags:
600+
- "--config=no_std_alloc_using_cc_common_link_and_mangled_alloc_symbols"
565601
no_std_ubuntu2004:
566602
name: Build with no_std + alloc
567603
platform: ubuntu2004

ffi/rs/BUILD.bazel

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
load("@rules_cc//cc:cc_library.bzl", "cc_library")
2+
3+
# buildifier: disable=bzl-visibility
4+
load("@rules_rust//rust/private:rust.bzl", "rust_allocator_libraries")
5+
6+
rust_allocator_libraries(
7+
name = "allocator_libraries_with_mangling_support",
8+
allocator_library = "@rules_rust//ffi/rs/allocator_library",
9+
global_allocator_library = "@rules_rust//ffi/rs/global_allocator_library",
10+
visibility = ["//visibility:public"],
11+
)
12+
13+
rust_allocator_libraries(
14+
name = "empty_allocator_libraries",
15+
visibility = ["//visibility:public"],
16+
)
17+
18+
alias(
19+
name = "default_allocator_libraries",
20+
actual = select({
21+
"@rules_rust//rust/settings:experimental_use_allocator_libraries_with_mangled_symbols_on": ":allocator_libraries_with_mangling_support",
22+
"//conditions:default": ":empty_allocator_libraries",
23+
}),
24+
visibility = ["//visibility:public"],
25+
)
26+
27+
cc_library(
28+
name = "empty",
29+
visibility = ["//visibility:public"],
30+
)
31+
32+
# Allocator libraries used while bootstrapping the process wrapper.
33+
rust_allocator_libraries(
34+
name = "allocator_libraries_with_mangling_support_without_process_wrapper",
35+
allocator_library = "@rules_rust//ffi/rs/allocator_library:allocator_library_without_process_wrapper",
36+
# no need for a global allocator library, since the process wrapper
37+
# is always bootstrapped in exec mode, which always uses the default
38+
# allocator.
39+
visibility = ["@rules_rust//util/process_wrapper:__subpackages__"],
40+
)

ffi/rs/allocator_library/BUILD.bazel

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
load("@rules_rust//rust:defs.bzl", "rust_library")
2+
3+
# buildifier: disable=bzl-visibility
4+
load(
5+
"@rules_rust//rust/private:rust.bzl",
6+
"rust_library_without_process_wrapper",
7+
)
8+
9+
package(
10+
default_visibility = ["@rules_rust//ffi/rs:__subpackages__"],
11+
)
12+
13+
srcs = select({
14+
# Windows doesn't support weak symbol linkage.
15+
# If someone can make this work on Windows, please do!
16+
# For now we will silently not supply any symbols, because it would be very messy to conditionally define the default allocator library on toolchains depending on the platform.
17+
"@platforms//os:windows": ["empty.rs"],
18+
"//conditions:default": ["allocator_library.rs"],
19+
})
20+
21+
rust_library(
22+
name = "allocator_library",
23+
srcs = srcs,
24+
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
25+
edition = "2024",
26+
tags = ["manual"],
27+
)
28+
29+
rust_library_without_process_wrapper(
30+
name = "allocator_library_without_process_wrapper",
31+
srcs = srcs,
32+
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
33+
edition = "2024",
34+
tags = ["manual"],
35+
)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Workaround for Rust issue https://github.com/rust-lang/rust/issues/73632
2+
// We provide the allocator functions that rustc leaves in rlibs. These are
3+
// normally provided by rustc during the linking phase (since the allocator in
4+
// use can vary), but if rustc doesn't do the final link we have to provide
5+
// these manually. Hopefully we can make progress on the above bug and
6+
// eventually not need this kludge.
7+
//
8+
// Recently rustc started mangling these symbols, so we rewrote them in
9+
// rust.
10+
// https://github.com/rust-lang/rust/pull/127173
11+
//
12+
// This code uses unstable internal rustc features that are only available when
13+
// using a nightly toolchain. Also, it is only compatible with versions
14+
// of rustc that include the symbol mangling, such as nightly/2025-04-08 or
15+
// later.
16+
//
17+
// This has been translated from our c++ version
18+
// rules_rust/ffi/cc/allocator_library/allocator_library.cc.
19+
#![no_std]
20+
#![allow(warnings)]
21+
#![allow(internal_features)]
22+
#![feature(rustc_attrs)]
23+
#![feature(linkage)]
24+
25+
unsafe extern "C" {
26+
#[rustc_std_internal_symbol]
27+
fn __rdl_alloc(size: usize, align: usize) -> *mut u8;
28+
29+
#[rustc_std_internal_symbol]
30+
fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize);
31+
32+
#[rustc_std_internal_symbol]
33+
fn __rdl_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
34+
35+
#[rustc_std_internal_symbol]
36+
fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8;
37+
}
38+
39+
#[linkage = "weak"]
40+
#[rustc_std_internal_symbol]
41+
fn __rust_alloc(size: usize, align: usize) -> *mut u8 {
42+
unsafe {
43+
return __rdl_alloc(size, align);
44+
}
45+
}
46+
47+
#[linkage = "weak"]
48+
#[rustc_std_internal_symbol]
49+
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize) {
50+
unsafe {
51+
return __rdl_dealloc(ptr, size, align);
52+
}
53+
}
54+
55+
#[linkage = "weak"]
56+
#[rustc_std_internal_symbol]
57+
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8 {
58+
unsafe {
59+
return __rdl_realloc(ptr, old_size, align, new_size);
60+
}
61+
}
62+
63+
#[linkage = "weak"]
64+
#[rustc_std_internal_symbol]
65+
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
66+
unsafe {
67+
return __rdl_alloc_zeroed(size, align);
68+
}
69+
}
70+
71+
#[linkage = "weak"]
72+
#[rustc_std_internal_symbol]
73+
fn __rust_alloc_error_handler(size: usize, align: usize) {
74+
panic!();
75+
}
76+
77+
// New feature as of https://github.com/rust-lang/rust/pull/88098.
78+
// This symbol is normally emitted by rustc. 0 means OOMs should abort, 1 means OOMs should panic.
79+
#[linkage = "weak"]
80+
#[rustc_std_internal_symbol]
81+
static mut __rust_alloc_error_handler_should_panic: u8 = 1;
82+
83+
// See https://github.com/rust-lang/rust/issues/73632#issuecomment-1563462239
84+
#[linkage = "weak"]
85+
#[rustc_std_internal_symbol]
86+
static mut __rust_no_alloc_shim_is_unstable: u8 = 0;

ffi/rs/allocator_library/empty.rs

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
load("@rules_rust//rust:defs.bzl", "rust_library")
2+
3+
package(
4+
default_visibility = ["@rules_rust//ffi/rs:__subpackages__"],
5+
)
6+
7+
srcs = select({
8+
# Windows doesn't support weak symbol linkage.
9+
# If someone can make this work on Windows, please do!
10+
# For now we will silently not supply any symbols, because it would be very messy to conditionally define the global allocator library on toolchains depending on the platform.
11+
"@platforms//os:windows": ["empty.rs"],
12+
"//conditions:default": ["global_allocator_library.rs"],
13+
})
14+
15+
rust_library(
16+
name = "global_allocator_library",
17+
srcs = srcs,
18+
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
19+
edition = "2024",
20+
tags = ["manual"],
21+
)

ffi/rs/global_allocator_library/empty.rs

Whitespace-only changes.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Workaround for Rust issue https://github.com/rust-lang/rust/issues/73632
2+
// We provide the allocator functions that rustc leaves in rlibs. These are
3+
// normally provided by rustc during the linking phase (since the allocator in
4+
// use can vary), but if rustc doesn't do the final link we have to provide
5+
// these manually. Hopefully we can make progress on the above bug and
6+
// eventually not need this kludge.
7+
//
8+
// Recently rustc started mangling these symbols, so we rewrote them in
9+
// rust.
10+
// https://github.com/rust-lang/rust/pull/127173
11+
//
12+
// This code uses unstable internal rustc features that are only available when
13+
// using a nightly toolchain. Also, it is only compatible with versions
14+
// of rustc that include the symbol mangling, such as nightly/2025-04-08 or
15+
// later.
16+
//
17+
// This has been translated from our c++ version
18+
// rules_rust/ffi/cc/global_allocator_library/global_allocator_library.cc.
19+
#![no_std]
20+
#![allow(warnings)]
21+
#![allow(internal_features)]
22+
#![feature(rustc_attrs)]
23+
#![feature(linkage)]
24+
25+
unsafe extern "C" {
26+
#[rustc_std_internal_symbol]
27+
fn __rg_oom(size: usize, align: usize) -> *mut u8;
28+
}
29+
30+
#[linkage = "weak"]
31+
#[rustc_std_internal_symbol]
32+
fn __rust_alloc_error_handler(size: usize, align: usize) {
33+
unsafe {
34+
__rg_oom(size, align);
35+
}
36+
}
37+
38+
39+
// New feature as of https://github.com/rust-lang/rust/pull/88098.
40+
// This symbol is normally emitted by rustc. 0 means OOMs should abort, 1 means OOMs should panic.
41+
#[linkage = "weak"]
42+
#[rustc_std_internal_symbol]
43+
static mut __rust_alloc_error_handler_should_panic: u8 = 1;
44+
45+
// See https://github.com/rust-lang/rust/issues/73632#issuecomment-1563462239
46+
#[linkage = "weak"]
47+
#[rustc_std_internal_symbol]
48+
static mut __rust_no_alloc_shim_is_unstable: u8 = 0;

rust/extensions.bzl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def _rust_impl(module_ctx):
6464
"name": repository_set.name,
6565
"rustfmt_version": repository_set.rustfmt_version,
6666
"sha256s": repository_set.sha256s,
67+
"target_settings": [str(v) for v in repository_set.target_settings],
6768
"urls": repository_set.urls,
6869
"versions": repository_set.versions,
6970
}
@@ -119,6 +120,7 @@ def _rust_impl(module_ctx):
119120
register_toolchains = False,
120121
aliases = toolchain.aliases,
121122
toolchain_triples = toolchain_triples,
123+
target_settings = [str(v) for v in toolchain.target_settings],
122124
extra_toolchain_infos = extra_toolchain_infos,
123125
)
124126
metadata_kwargs = {}
@@ -170,6 +172,9 @@ _RUST_REPOSITORY_SET_TAG_ATTRS = {
170172
"target_compatible_with": attr.label_list(
171173
doc = "List of platform constraints this toolchain produces, for the particular target_triple this call is for.",
172174
),
175+
"target_settings": attr.label_list(
176+
doc = "A list of `config_settings` that must be satisfied by the target configuration in order for this toolchain to be selected during toolchain resolution.",
177+
),
173178
"target_triple": attr.string(
174179
doc = "target_triple to configure.",
175180
),
@@ -214,6 +219,9 @@ _RUST_TOOLCHAIN_TAG = tag_class(
214219
"rust_analyzer_version": attr.string(
215220
doc = "The version of Rustc to pair with rust-analyzer.",
216221
),
222+
"target_settings": attr.label_list(
223+
doc = "A list of `config_settings` that must be satisfied by the target configuration in order for this toolchain to be selected during toolchain resolution.",
224+
),
217225
"versions": attr.string_list(
218226
doc = (
219227
"A list of toolchain versions to download. This parameter only accepts one version " +

rust/private/providers.bzl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,21 @@ LintsInfo = provider(
188188
"rustdoc_lint_flags": "List[String]: rustc flags to specify when building rust_doc targets.",
189189
},
190190
)
191+
192+
AllocatorLibrariesInfo = provider(
193+
doc = "AllocatorLibrariesInfo provides allocator libraries for linking rust code with a non-rust linker.",
194+
fields = {
195+
"allocator_library": "Optional[CcInfo]: used when the default rust allocator is used",
196+
"global_allocator_library": "Optional[CcInfo]: used when a global rust allocator is used",
197+
"libstd_and_allocator_ccinfo": "Optional[CcInfo]: used when the default rust allocator is used",
198+
"libstd_and_global_allocator_ccinfo": "Optional[CcInfo]: used when a global rust allocator is used",
199+
"nostd_and_global_allocator_ccinfo": "Optional[CcInfo]: used when nostd with a global rust allocator is used",
200+
},
201+
)
202+
203+
AllocatorLibrariesImplInfo = provider(
204+
doc = "AllocatorLibrariesImplInfo provides the rust-generated linker input for linking rust code with a non-rust linker.",
205+
fields = {
206+
"static_archive": "Optional[File]: the allocator library archive (typically .a file).",
207+
},
208+
)

0 commit comments

Comments
 (0)