153 lines
6.0 KiB
C++
153 lines
6.0 KiB
C++
|
|
// Copyright 2021 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cstddef>
|
||
|
|
#include <cstdlib>
|
||
|
|
#include <cstring>
|
||
|
|
|
||
|
|
#include "build/build_config.h"
|
||
|
|
#include "build/rust/std/immediate_crash.h"
|
||
|
|
|
||
|
|
#if BUILDFLAG(IS_ANDROID)
|
||
|
|
#include <malloc.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// When linking a final binary, rustc has to pick between either:
|
||
|
|
// * The default Rust allocator
|
||
|
|
// * Any #[global_allocator] defined in *any rlib in its dependency tree*
|
||
|
|
// (https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html)
|
||
|
|
//
|
||
|
|
// In this latter case, this fact will be recorded in some of the metadata
|
||
|
|
// within the .rlib file. (An .rlib file is just a .a file, but does have
|
||
|
|
// additional metadata for use by rustc. This is, as far as I know, the only
|
||
|
|
// such metadata we would ideally care about.)
|
||
|
|
//
|
||
|
|
// In all the linked rlibs,
|
||
|
|
// * If 0 crates define a #[global_allocator], rustc uses its default allocator
|
||
|
|
// * If 1 crate defines a #[global_allocator], rustc uses that
|
||
|
|
// * If >1 crates define a #[global_allocator], rustc bombs out.
|
||
|
|
//
|
||
|
|
// Because rustc does these checks, it doesn't just have the __rust_alloc
|
||
|
|
// symbols defined anywhere (neither in the stdlib nor in any of these
|
||
|
|
// crates which have a #[global_allocator] defined.)
|
||
|
|
//
|
||
|
|
// Instead:
|
||
|
|
// Rust's final linking stage invokes dynamic LLVM codegen to create symbols
|
||
|
|
// for the basic heap allocation operations. It literally creates a
|
||
|
|
// __rust_alloc symbol at link time. Unless any crate has specified a
|
||
|
|
// #[global_allocator], it simply calls from __rust_alloc into
|
||
|
|
// __rdl_alloc, which is the default Rust allocator. The same applies to a
|
||
|
|
// few other symbols.
|
||
|
|
//
|
||
|
|
// We're not (always) using rustc for final linking. For cases where we're not
|
||
|
|
// Rustc as the final linker, we'll define those symbols here instead.
|
||
|
|
//
|
||
|
|
// The Rust stdlib on Windows uses GetProcessHeap() which will bypass
|
||
|
|
// PartitionAlloc, so we do not forward these functions back to the stdlib.
|
||
|
|
// Instead, we pass them to PartitionAlloc, while replicating functionality from
|
||
|
|
// the unix stdlib to allow them to provide their increased functionality on top
|
||
|
|
// of the system functions.
|
||
|
|
//
|
||
|
|
// In future, we may build a crate with a #[global_allocator] and
|
||
|
|
// redirect these symbols back to Rust in order to use to that crate instead.
|
||
|
|
//
|
||
|
|
// Instead of going through system functions like malloc() we may want to call
|
||
|
|
// into PA directly if we wished for Rust allocations to be in a different
|
||
|
|
// partition, or similar, in the future.
|
||
|
|
//
|
||
|
|
// They're weak symbols, because this file will sometimes end up in targets
|
||
|
|
// which are linked by rustc, and thus we would otherwise get duplicate
|
||
|
|
// definitions. The following definitions will therefore only end up being
|
||
|
|
// used in targets which are linked by our C++ toolchain.
|
||
|
|
|
||
|
|
extern "C" {
|
||
|
|
|
||
|
|
#ifdef COMPONENT_BUILD
|
||
|
|
#define REMAP_ALLOC_ATTRIBUTES \
|
||
|
|
__attribute__((visibility("default"))) __attribute__((weak))
|
||
|
|
#else
|
||
|
|
#define REMAP_ALLOC_ATTRIBUTES __attribute__((weak))
|
||
|
|
#endif // COMPONENT_BUILD
|
||
|
|
|
||
|
|
void* REMAP_ALLOC_ATTRIBUTES __rust_alloc(size_t size, size_t align) {
|
||
|
|
// This mirrors kMaxSupportedAlignment from
|
||
|
|
// base/allocator/partition_allocator/partition_alloc_constants.h.
|
||
|
|
// ParitionAlloc will crash if given an alignment larger than this.
|
||
|
|
constexpr size_t max_align = (1 << 21) / 2;
|
||
|
|
if (align > max_align) {
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (align <= alignof(std::max_align_t)) {
|
||
|
|
return malloc(size);
|
||
|
|
} else {
|
||
|
|
// Note: PartitionAlloc by default will route aligned allocations back to
|
||
|
|
// malloc() (the fast path) if they are for a small enough alignment. So we
|
||
|
|
// just unconditionally use aligned allocation functions here.
|
||
|
|
// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc;l=219-226;drc=31d99ff4aa0cc0b75063325ff243e911516a5a6a
|
||
|
|
|
||
|
|
#if defined(COMPILER_MSVC)
|
||
|
|
// Because we use PartitionAlloc() as the allocator, free() is able to find
|
||
|
|
// this allocation, instead of the usual requirement to use _aligned_free().
|
||
|
|
return _aligned_malloc(size, align);
|
||
|
|
#elif BUILDFLAG(IS_ANDROID)
|
||
|
|
// Android has no posix_memalign() exposed:
|
||
|
|
// https://source.chromium.org/chromium/chromium/src/+/main:base/memory/aligned_memory.cc;l=24-30;drc=e4622aaeccea84652488d1822c28c78b7115684f
|
||
|
|
return memalign(align, size);
|
||
|
|
#else
|
||
|
|
// The `align` from Rust is always a power of 2:
|
||
|
|
// https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.from_size_align.
|
||
|
|
//
|
||
|
|
// We get here only if align > alignof(max_align_t), which guarantees that
|
||
|
|
// the alignment is both a power of 2 and even, which is required by
|
||
|
|
// posix_memalign().
|
||
|
|
//
|
||
|
|
// The PartitionAlloc impl requires that the alignment is at least the same
|
||
|
|
// as pointer-alignment. std::max_align_t is at least pointer-aligned as
|
||
|
|
// well, so we satisfy that.
|
||
|
|
void* p;
|
||
|
|
auto ret = posix_memalign(&p, align, size);
|
||
|
|
return ret == 0 ? p : nullptr;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void REMAP_ALLOC_ATTRIBUTES __rust_dealloc(void* p, size_t size, size_t align) {
|
||
|
|
free(p);
|
||
|
|
}
|
||
|
|
|
||
|
|
void* REMAP_ALLOC_ATTRIBUTES __rust_realloc(void* p,
|
||
|
|
size_t old_size,
|
||
|
|
size_t align,
|
||
|
|
size_t new_size) {
|
||
|
|
if (align <= alignof(std::max_align_t)) {
|
||
|
|
return realloc(p, new_size);
|
||
|
|
} else {
|
||
|
|
void* out = __rust_alloc(align, new_size);
|
||
|
|
memcpy(out, p, std::min(old_size, new_size));
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void* REMAP_ALLOC_ATTRIBUTES __rust_alloc_zeroed(size_t size, size_t align) {
|
||
|
|
if (align <= alignof(std::max_align_t)) {
|
||
|
|
return calloc(size, 1);
|
||
|
|
} else {
|
||
|
|
void* p = __rust_alloc(size, align);
|
||
|
|
memset(p, 0, size);
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void REMAP_ALLOC_ATTRIBUTES __rust_alloc_error_handler(size_t size,
|
||
|
|
size_t align) {
|
||
|
|
IMMEDIATE_CRASH();
|
||
|
|
}
|
||
|
|
|
||
|
|
extern const unsigned char REMAP_ALLOC_ATTRIBUTES
|
||
|
|
__rust_alloc_error_handler_should_panic = 0;
|
||
|
|
|
||
|
|
} // extern "C"
|