Skip to content

[DRAFT] add allocator libraries compatible with the new rustc mangling #3403

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions ffi/rs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")

# buildifier: disable=bzl-visibility
load("@rules_rust//rust/private:rust.bzl", "rust_allocator_libraries")

rust_allocator_libraries(
name = "allocator_libraries_with_mangling_support",
allocator_library = "@rules_rust//ffi/rs/allocator_library",
global_allocator_library = "@rules_rust//ffi/rs/global_allocator_library",
visibility = ["//visibility:public"],
)

rust_allocator_libraries(
name = "empty_allocator_libraries",
visibility = ["//visibility:public"],
)

alias(
name = "default_allocator_libraries",
actual = select({
"@rules_rust//rust/settings:experimental_use_allocator_libraries_with_mangled_symbols_on": ":allocator_libraries_with_mangling_support",
"//conditions:default": ":empty_allocator_libraries",
}),
visibility = ["//visibility:public"],
)

cc_library(
name = "empty",
visibility = ["//visibility:public"],
)

# Allocator libraries used while bootstrapping the process wrapper.
rust_allocator_libraries(
name = "allocator_libraries_with_mangling_support_without_process_wrapper",
allocator_library = "@rules_rust//ffi/rs/allocator_library:allocator_library_without_process_wrapper",
# no need for a global allocator library, since the process wrapper
# is always bootstrapped in exec mode, which always uses the default
# allocator.
visibility = ["@rules_rust//util/process_wrapper:__subpackages__"],
)
35 changes: 35 additions & 0 deletions ffi/rs/allocator_library/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
load("@rules_rust//rust:defs.bzl", "rust_library")

# buildifier: disable=bzl-visibility
load(
"@rules_rust//rust/private:rust.bzl",
"rust_library_without_process_wrapper",
)

package(
default_visibility = ["@rules_rust//ffi/rs:__subpackages__"],
)

srcs = select({
# Windows doesn't support weak symbol linkage.
# If someone can make this work on Windows, please do!
# 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.
"@platforms//os:windows": ["empty.rs"],
"//conditions:default": ["allocator_library.rs"],
})

rust_library(
name = "allocator_library",
srcs = srcs,
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
edition = "2024",
tags = ["manual"],
)

rust_library_without_process_wrapper(
name = "allocator_library_without_process_wrapper",
srcs = srcs,
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
edition = "2024",
tags = ["manual"],
)
86 changes: 86 additions & 0 deletions ffi/rs/allocator_library/allocator_library.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Workaround for Rust issue https://github.com/rust-lang/rust/issues/73632
// We provide the allocator functions that rustc leaves in rlibs. These are
// normally provided by rustc during the linking phase (since the allocator in
// use can vary), but if rustc doesn't do the final link we have to provide
// these manually. Hopefully we can make progress on the above bug and
// eventually not need this kludge.
//
// Recently rustc started mangling these symbols, so we rewrote them in
// rust.
// https://github.com/rust-lang/rust/pull/127173
//
// This code uses unstable internal rustc features that are only available when
// using a nightly toolchain. Also, it is only compatible with versions
// of rustc that include the symbol mangling, such as nightly/2025-04-08 or
// later.
//
// This has been translated from our c++ version
// rules_rust/ffi/cc/allocator_library/allocator_library.cc.
#![no_std]
#![allow(warnings)]
#![allow(internal_features)]
#![feature(rustc_attrs)]
#![feature(linkage)]

unsafe extern "C" {
#[rustc_std_internal_symbol]
fn __rdl_alloc(size: usize, align: usize) -> *mut u8;

#[rustc_std_internal_symbol]
fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize);

#[rustc_std_internal_symbol]
fn __rdl_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;

#[rustc_std_internal_symbol]
fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8;
}

#[linkage = "weak"]
#[rustc_std_internal_symbol]
fn __rust_alloc(size: usize, align: usize) -> *mut u8 {
unsafe {
return __rdl_alloc(size, align);
}
}

#[linkage = "weak"]
#[rustc_std_internal_symbol]
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize) {
unsafe {
return __rdl_dealloc(ptr, size, align);
}
}

#[linkage = "weak"]
#[rustc_std_internal_symbol]
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8 {
unsafe {
return __rdl_realloc(ptr, old_size, align, new_size);
}
}

#[linkage = "weak"]
#[rustc_std_internal_symbol]
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
unsafe {
return __rdl_alloc_zeroed(size, align);
}
}

#[linkage = "weak"]
#[rustc_std_internal_symbol]
fn __rust_alloc_error_handler(size: usize, align: usize) {
panic!();
}

// New feature as of https://github.com/rust-lang/rust/pull/88098.
// This symbol is normally emitted by rustc. 0 means OOMs should abort, 1 means OOMs should panic.
#[linkage = "weak"]
#[rustc_std_internal_symbol]
static mut __rust_alloc_error_handler_should_panic: u8 = 1;

// See https://github.com/rust-lang/rust/issues/73632#issuecomment-1563462239
#[linkage = "weak"]
#[rustc_std_internal_symbol]
static mut __rust_no_alloc_shim_is_unstable: u8 = 0;
Empty file.
21 changes: 21 additions & 0 deletions ffi/rs/global_allocator_library/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load("@rules_rust//rust:defs.bzl", "rust_library")

package(
default_visibility = ["@rules_rust//ffi/rs:__subpackages__"],
)

srcs = select({
# Windows doesn't support weak symbol linkage.
# If someone can make this work on Windows, please do!
# 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.
"@platforms//os:windows": ["empty.rs"],
"//conditions:default": ["global_allocator_library.rs"],
})

rust_library(
name = "global_allocator_library",
srcs = srcs,
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
edition = "2024",
tags = ["manual"],
)
Empty file.
48 changes: 48 additions & 0 deletions ffi/rs/global_allocator_library/global_allocator_library.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Workaround for Rust issue https://github.com/rust-lang/rust/issues/73632
// We provide the allocator functions that rustc leaves in rlibs. These are
// normally provided by rustc during the linking phase (since the allocator in
// use can vary), but if rustc doesn't do the final link we have to provide
// these manually. Hopefully we can make progress on the above bug and
// eventually not need this kludge.
//
// Recently rustc started mangling these symbols, so we rewrote them in
// rust.
// https://github.com/rust-lang/rust/pull/127173
//
// This code uses unstable internal rustc features that are only available when
// using a nightly toolchain. Also, it is only compatible with versions
// of rustc that include the symbol mangling, such as nightly/2025-04-08 or
// later.
//
// This has been translated from our c++ version
// rules_rust/ffi/cc/global_allocator_library/global_allocator_library.cc.
#![no_std]
#![allow(warnings)]
#![allow(internal_features)]
#![feature(rustc_attrs)]
#![feature(linkage)]

unsafe extern "C" {
#[rustc_std_internal_symbol]
fn __rg_oom(size: usize, align: usize) -> *mut u8;
}

#[linkage = "weak"]
#[rustc_std_internal_symbol]
fn __rust_alloc_error_handler(size: usize, align: usize) {
unsafe {
__rg_oom(size, align);
}
}


// New feature as of https://github.com/rust-lang/rust/pull/88098.
// This symbol is normally emitted by rustc. 0 means OOMs should abort, 1 means OOMs should panic.
#[linkage = "weak"]
#[rustc_std_internal_symbol]
static mut __rust_alloc_error_handler_should_panic: u8 = 1;

// See https://github.com/rust-lang/rust/issues/73632#issuecomment-1563462239
#[linkage = "weak"]
#[rustc_std_internal_symbol]
static mut __rust_no_alloc_shim_is_unstable: u8 = 0;
8 changes: 8 additions & 0 deletions rust/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,11 @@ LintsInfo = provider(
"rustdoc_lint_flags": "List[String]: rustc flags to specify when building rust_doc targets.",
},
)

AllocatorLibrariesInfo = provider(
doc = "AllocatorLibrariesInfo provides allocator libraries for linking rust code with a non-rust linker.",
fields = {
"allocator_library": "Optional[CcInfo]: used when the default rust allocator is used",
"global_allocator_library": "Optional[CcInfo]: used when a global rust allocator is used",
},
)
43 changes: 41 additions & 2 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
load("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common")
load("//rust/private:providers.bzl", "BuildInfo", "LintsInfo")
load("//rust/private:providers.bzl", "AllocatorLibrariesInfo", "BuildInfo", "LintsInfo")
load("//rust/private:rustc.bzl", "rustc_compile_action")
load(
"//rust/private:utils.bzl",
Expand Down Expand Up @@ -564,6 +564,19 @@ RUSTC_ATTRS = {
),
}

# Attributes for rust-based allocator library support.
# Can't add it directly to RUSTC_ATTRS above, as those are used as
# aspect parameters and only support simple types ('bool', 'int' or 'string').
_rustc_allocator_libraries_attrs = {
# This is really internal. Not prefixed with `_` since we need to adapt this
# in bootstrapping situations, e.g., when building the process wrapper
# or allocator libraries themselves.
"allocator_libraries": attr.label(
default = "//ffi/rs:default_allocator_libraries",
providers = [AllocatorLibrariesInfo],
),
}

_common_attrs = {
"aliases": attr.label_keyed_string_dict(
doc = dedent("""\
Expand Down Expand Up @@ -723,7 +736,7 @@ _common_attrs = {
doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
default = Label("//rust/private:stamp"),
),
} | RUSTC_ATTRS
} | RUSTC_ATTRS | _rustc_allocator_libraries_attrs

_coverage_attrs = {
"_collect_cc_coverage": attr.label(
Expand Down Expand Up @@ -1573,6 +1586,32 @@ rust_library_group = rule(
"""),
)

def _rust_allocator_libraries_impl(ctx):
allocator_library = ctx.attr.allocator_library[CcInfo] if ctx.attr.allocator_library else None
global_allocator_library = ctx.attr.global_allocator_library[CcInfo] if ctx.attr.global_allocator_library else None

providers = [AllocatorLibrariesInfo(
allocator_library = allocator_library,
global_allocator_library = global_allocator_library,
)]

return providers

rust_allocator_libraries = rule(
implementation = _rust_allocator_libraries_impl,
provides = [AllocatorLibrariesInfo],
attrs = {
"allocator_library": attr.label(
doc = "An optional library to provide when a default rust allocator is used.",
providers = [CcInfo],
),
"global_allocator_library": attr.label(
doc = "An optional library to provide when a default rust allocator is used.",
providers = [CcInfo],
),
},
)

def _replace_illlegal_chars(name):
"""Replaces illegal characters in a name with underscores.

Expand Down
43 changes: 40 additions & 3 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
load(":common.bzl", "rust_common")
load(":compat.bzl", "abs")
load(":lto.bzl", "construct_lto_arguments")
load(":providers.bzl", "LintsInfo", "RustcOutputDiagnosticsInfo", _BuildInfo = "BuildInfo")
load(":providers.bzl", "AllocatorLibrariesInfo", "LintsInfo", "RustcOutputDiagnosticsInfo", _BuildInfo = "BuildInfo")
load(":rustc_resource_set.bzl", "get_rustc_resource_set", "is_codegen_units_enabled")
load(":stamp.bzl", "is_stamping_enabled")
load(
Expand Down Expand Up @@ -1604,16 +1604,53 @@ def _is_no_std(ctx, toolchain, crate_info):
return False
return True

def _merge_attr_and_toolchain_alloc_info(attr_alloc_cc_info, toolchain_alloc_cc_info):
if not attr_alloc_cc_info:
fail("empty attr_alloc_cc_info")
return cc_common.merge_cc_infos(cc_infos = [attr_alloc_cc_info, toolchain_alloc_cc_info])

def _should_use_rustc_allocator_libraries(toolchain):
use_or_default = toolchain._experimental_use_allocator_libraries_with_mangled_symbols
if use_or_default not in [-1, 0, 1]:
fail("unexpected value of experimental_use_allocator_libraries_with_mangled_symbols (should be one of [-1, 0, 1]): " + use_or_default)
if use_or_default == -1:
return toolchain._experimental_use_allocator_libraries_with_mangled_symbols_setting
return bool(use_or_default)

def _get_std_and_alloc_info(ctx, toolchain, crate_info):
# Handles standard libraries and allocator shims.
#
# The standard libraries vary between "std" and "nostd" flavors.
#
# The allocator libraries vary along two dimensions:
# * the type of rust allocator used (default or global)
# * the mechanism providing the libraries (via the rust rules
# allocator_libraries attribute, or via the rust toolchain allocator
# attributes).
#
# When provided, the allocator_libraries attribute takes precedence over the
# toolchain allocator attributes.
attr_allocator_library = None
attr_global_allocator_library = None
if _should_use_rustc_allocator_libraries(toolchain) and hasattr(ctx.attr, "allocator_libraries"):
attr_allocator_library = ctx.attr.allocator_libraries[AllocatorLibrariesInfo].allocator_library
attr_global_allocator_library = ctx.attr.allocator_libraries[AllocatorLibrariesInfo].global_allocator_library
if is_exec_configuration(ctx):
if attr_allocator_library:
return _merge_attr_and_toolchain_alloc_info(attr_allocator_library, toolchain.libstd_no_allocator_ccinfo)
return toolchain.libstd_and_allocator_ccinfo
if toolchain._experimental_use_global_allocator:
if _is_no_std(ctx, toolchain, crate_info):
if attr_global_allocator_library:
return _merge_attr_and_toolchain_alloc_info(attr_global_allocator_library, toolchain.nostd_no_allocator_ccinfo)
return toolchain.nostd_and_global_allocator_cc_info
else:
if attr_global_allocator_library:
return _merge_attr_and_toolchain_alloc_info(attr_global_allocator_library, toolchain.libstd_no_allocator_ccinfo)
return toolchain.libstd_and_global_allocator_ccinfo
else:
return toolchain.libstd_and_allocator_ccinfo
if attr_allocator_library:
return _merge_attr_and_toolchain_alloc_info(attr_allocator_library, toolchain.libstd_no_allocator_ccinfo)
return toolchain.libstd_and_allocator_ccinfo

def _is_dylib(dep):
return not bool(dep.static_library or dep.pic_static_library)
Expand Down
Loading