• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdlib.h>
6 
7 #include <algorithm>
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cstring>
11 
12 #include "build/build_config.h"
13 #include "build/rust/std/alias.h"
14 #include "build/rust/std/immediate_crash.h"
15 
16 #if BUILDFLAG(IS_ANDROID)
17 #include <malloc.h>
18 #endif
19 
20 // When linking a final binary, rustc has to pick between either:
21 // * The default Rust allocator
22 // * Any #[global_allocator] defined in *any rlib in its dependency tree*
23 //   (https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html)
24 //
25 // In this latter case, this fact will be recorded in some of the metadata
26 // within the .rlib file. (An .rlib file is just a .a file, but does have
27 // additional metadata for use by rustc. This is, as far as I know, the only
28 // such metadata we would ideally care about.)
29 //
30 // In all the linked rlibs,
31 // * If 0 crates define a #[global_allocator], rustc uses its default allocator
32 // * If 1 crate defines a #[global_allocator], rustc uses that
33 // * If >1 crates define a #[global_allocator], rustc bombs out.
34 //
35 // Because rustc does these checks, it doesn't just have the __rust_alloc
36 // symbols defined anywhere (neither in the stdlib nor in any of these
37 // crates which have a #[global_allocator] defined.)
38 //
39 // Instead:
40 // Rust's final linking stage invokes dynamic LLVM codegen to create symbols
41 // for the basic heap allocation operations. It literally creates a
42 // __rust_alloc symbol at link time. Unless any crate has specified a
43 // #[global_allocator], it simply calls from __rust_alloc into
44 // __rdl_alloc, which is the default Rust allocator. The same applies to a
45 // few other symbols.
46 //
47 // We're not (always) using rustc for final linking. For cases where we're not
48 // Rustc as the final linker, we'll define those symbols here instead.
49 //
50 // The Rust stdlib on Windows uses GetProcessHeap() which will bypass
51 // PartitionAlloc, so we do not forward these functions back to the stdlib.
52 // Instead, we pass them to PartitionAlloc, while replicating functionality from
53 // the unix stdlib to allow them to provide their increased functionality on top
54 // of the system functions.
55 //
56 // In future, we may build a crate with a #[global_allocator] and
57 // redirect these symbols back to Rust in order to use to that crate instead.
58 //
59 // Instead of going through system functions like malloc() we may want to call
60 // into PA directly if we wished for Rust allocations to be in a different
61 // partition, or similar, in the future.
62 //
63 // They're weak symbols, because this file will sometimes end up in targets
64 // which are linked by rustc, and thus we would otherwise get duplicate
65 // definitions. The following definitions will therefore only end up being
66 // used in targets which are linked by our C++ toolchain.
67 
68 extern "C" {
69 
70 #ifdef COMPONENT_BUILD
71 #if BUILDFLAG(IS_WIN)
72 #define REMAP_ALLOC_ATTRIBUTES __declspec(dllexport) __attribute__((weak))
73 #else
74 #define REMAP_ALLOC_ATTRIBUTES \
75   __attribute__((visibility("default"))) __attribute__((weak))
76 #endif
77 #else
78 #define REMAP_ALLOC_ATTRIBUTES __attribute__((weak))
79 #endif  // COMPONENT_BUILD
80 
81 // This must exist as the stdlib depends on it to prove that we know the
82 // alloc shims below are unstable. In the future we may be required to replace
83 // them with a #[global_allocator] crate (see file comment above for more).
84 //
85 // Marked as weak as when Rust drives linking it includes this symbol itself,
86 // and we don't want a collision due to C++ being in the same link target, where
87 // C++ causes us to explicitly link in the stdlib and this symbol here.
88 [[maybe_unused]] __attribute__((
89     weak)) unsigned char __rust_no_alloc_shim_is_unstable;
90 
__rust_alloc(size_t size,size_t align)91 REMAP_ALLOC_ATTRIBUTES void* __rust_alloc(size_t size, size_t align) {
92   // This mirrors kMaxSupportedAlignment from
93   // base/allocator/partition_allocator/src/partition_alloc/partition_alloc_constants.h.
94   // ParitionAlloc will crash if given an alignment larger than this.
95   constexpr size_t max_align = (1 << 21) / 2;
96   if (align > max_align) {
97     return nullptr;
98   }
99 
100   if (align <= alignof(std::max_align_t)) {
101     return malloc(size);
102   } else {
103     // Note: PartitionAlloc by default will route aligned allocations back to
104     // malloc() (the fast path) if they are for a small enough alignment. So we
105     // just unconditionally use aligned allocation functions here.
106     // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc;l=219-226;drc=31d99ff4aa0cc0b75063325ff243e911516a5a6a
107 
108 #if defined(COMPILER_MSVC)
109     return _aligned_malloc(size, align);
110 #elif BUILDFLAG(IS_ANDROID)
111     // Android has no posix_memalign() exposed:
112     // https://source.chromium.org/chromium/chromium/src/+/main:base/memory/aligned_memory.cc;l=24-30;drc=e4622aaeccea84652488d1822c28c78b7115684f
113     return memalign(align, size);
114 #else
115     // The `align` from Rust is always a power of 2:
116     // https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.from_size_align.
117     //
118     // We get here only if align > alignof(max_align_t), which guarantees that
119     // the alignment is both a power of 2 and even, which is required by
120     // posix_memalign().
121     //
122     // The PartitionAlloc impl requires that the alignment is at least the same
123     // as pointer-alignment. std::max_align_t is at least pointer-aligned as
124     // well, so we satisfy that.
125     void* p;
126     auto ret = posix_memalign(&p, align, size);
127     return ret == 0 ? p : nullptr;
128 #endif
129   }
130 }
131 
__rust_dealloc(void * p,size_t size,size_t align)132 REMAP_ALLOC_ATTRIBUTES void __rust_dealloc(void* p, size_t size, size_t align) {
133 #if defined(COMPILER_MSVC)
134   if (align <= alignof(std::max_align_t)) {
135     free(p);
136   } else {
137     _aligned_free(p);
138   }
139 #else
140   free(p);
141 #endif
142 }
143 
__rust_realloc(void * p,size_t old_size,size_t align,size_t new_size)144 REMAP_ALLOC_ATTRIBUTES void* __rust_realloc(void* p,
145                                             size_t old_size,
146                                             size_t align,
147                                             size_t new_size) {
148   if (align <= alignof(std::max_align_t)) {
149     return realloc(p, new_size);
150   } else {
151     void* out = __rust_alloc(align, new_size);
152     memcpy(out, p, std::min(old_size, new_size));
153     return out;
154   }
155 }
156 
__rust_alloc_zeroed(size_t size,size_t align)157 REMAP_ALLOC_ATTRIBUTES void* __rust_alloc_zeroed(size_t size, size_t align) {
158   if (align <= alignof(std::max_align_t)) {
159     return calloc(size, 1);
160   } else {
161     void* p = __rust_alloc(size, align);
162     memset(p, 0, size);
163     return p;
164   }
165 }
166 
__rust_alloc_error_handler(size_t size,size_t align)167 REMAP_ALLOC_ATTRIBUTES void __rust_alloc_error_handler(size_t size,
168                                                        size_t align) {
169   NO_CODE_FOLDING();
170   IMMEDIATE_CRASH();
171 }
172 
173 REMAP_ALLOC_ATTRIBUTES extern const unsigned char
174     __rust_alloc_error_handler_should_panic = 0;
175 
176 }  // extern "C"
177