@@ -25,19 +25,42 @@ use crate::entrypoint::abort_with_code;
25
25
26
26
extern crate alloc;
27
27
28
- #[ no_mangle]
29
- pub extern "C" fn hlmalloc ( size : usize ) -> * mut c_void {
30
- alloc_helper ( size, false )
31
- }
28
+ /*
29
+ C-wrappers for Rust's registered global allocator.
32
30
33
- pub fn alloc_helper ( size : usize , zero : bool ) -> * mut c_void {
34
- // Allocate a block that includes space for both layout information and data
31
+ Each memory allocation via `malloc/calloc/realloc` is stored together with a `alloc::Layout` describing
32
+ the size and alignment of the allocation. This layout is stored just before the actual raw memory returned to the caller.
33
+
34
+ Example: A call to malloc(64) will allocate space for both an `alloc::Layout` and 64 bytes of memory:
35
+
36
+ ----------------------------------------------------------------------------------------
37
+ | Layout { size: 64 + size_of::<Layout>(), ... } | 64 bytes of memory | ...
38
+ ----------------------------------------------------------------------------------------
39
+ ^
40
+ |
41
+ |
42
+ ptr returned to caller
43
+ */
44
+
45
+ // We assume the maximum alignment for any value is the alignment of u128.
46
+ const MAX_ALIGN : usize = align_of :: < u128 > ( ) ;
47
+
48
+ /// Allocates a block of memory with the given size. The memory is only guaranteed to be initialized to 0s if `zero` is true, otherwise
49
+ /// it may or may not be initialized.
50
+ ///
51
+ /// # Safety
52
+ /// The returned pointer must be freed with `memory::free` when it is no longer needed, otherwise memory will leak.
53
+ unsafe fn alloc_helper ( size : usize , zero : bool ) -> * mut c_void {
35
54
if size == 0 {
36
55
return ptr:: null_mut ( ) ;
37
56
}
38
57
39
- let total_size = size + size_of :: < Layout > ( ) ;
40
- let layout = Layout :: from_size_align ( total_size, align_of :: < usize > ( ) ) . unwrap ( ) ;
58
+ // Allocate a block that includes space for both layout information and data
59
+ let total_size = size
60
+ . checked_add ( size_of :: < Layout > ( ) )
61
+ . expect ( "data and layout size should not overflow in alloc" ) ;
62
+ let layout = Layout :: from_size_align ( total_size, MAX_ALIGN ) . expect ( "Invalid layout" ) ;
63
+
41
64
unsafe {
42
65
let raw_ptr = match zero {
43
66
true => alloc:: alloc:: alloc_zeroed ( layout) ,
@@ -53,14 +76,36 @@ pub fn alloc_helper(size: usize, zero: bool) -> *mut c_void {
53
76
}
54
77
}
55
78
79
+ /// Allocates a block of memory with the given size.
80
+ /// The memory is not guaranteed to be initialized to 0s.
81
+ ///
82
+ /// # Safety
83
+ /// The returned pointer must be freed with `memory::free` when it is no longer needed, otherwise memory will leak.
84
+ #[ no_mangle]
85
+ pub unsafe extern "C" fn malloc ( size : usize ) -> * mut c_void {
86
+ alloc_helper ( size, false )
87
+ }
88
+
89
+ /// Allocates a block of memory for an array of `nmemb` elements, each of `size` bytes.
90
+ /// The memory is initialized to 0s.
91
+ ///
92
+ /// # Safety
93
+ /// The returned pointer must be freed with `memory::free` when it is no longer needed, otherwise memory will leak.
56
94
#[ no_mangle]
57
- pub extern "C" fn hlcalloc ( n : usize , size : usize ) -> * mut c_void {
58
- let total_size = n * size;
95
+ pub unsafe extern "C" fn calloc ( nmemb : usize , size : usize ) -> * mut c_void {
96
+ let total_size = nmemb
97
+ . checked_mul ( size)
98
+ . expect ( "nmemb * size should not overflow in calloc" ) ;
99
+
59
100
alloc_helper ( total_size, true )
60
101
}
61
102
103
+ /// Frees the memory block pointed to by `ptr`.
104
+ ///
105
+ /// # Safety
106
+ /// `ptr` must be a pointer to a memory block previously allocated by `memory::malloc`, `memory::calloc`, or `memory::realloc`.
62
107
#[ no_mangle]
63
- pub extern "C" fn hlfree ( ptr : * mut c_void ) {
108
+ pub unsafe extern "C" fn free ( ptr : * mut c_void ) {
64
109
if !ptr. is_null ( ) {
65
110
unsafe {
66
111
let block_start = ( ptr as * const Layout ) . sub ( 1 ) ;
@@ -70,11 +115,22 @@ pub extern "C" fn hlfree(ptr: *mut c_void) {
70
115
}
71
116
}
72
117
118
+ /// Changes the size of the memory block pointed to by `ptr` to `size` bytes. If the returned ptr is non-null,
119
+ /// any usage of the old memory block is immediately undefined behavior.
120
+ ///
121
+ /// # Safety
122
+ /// `ptr` must be a pointer to a memory block previously allocated by `memory::malloc`, `memory::calloc`, or `memory::realloc`.
73
123
#[ no_mangle]
74
- pub extern "C" fn hlrealloc ( ptr : * mut c_void , size : usize ) -> * mut c_void {
124
+ pub unsafe extern "C" fn realloc ( ptr : * mut c_void , size : usize ) -> * mut c_void {
75
125
if ptr. is_null ( ) {
76
126
// If the pointer is null, treat as a malloc
77
- return hlmalloc ( size) ;
127
+ return malloc ( size) ;
128
+ }
129
+
130
+ if size == 0 {
131
+ // If the size is 0, treat as a free and return null
132
+ free ( ptr) ;
133
+ return ptr:: null_mut ( ) ;
78
134
}
79
135
80
136
unsafe {
@@ -84,10 +140,11 @@ pub extern "C" fn hlrealloc(ptr: *mut c_void, size: usize) -> *mut c_void {
84
140
85
141
let block_start = ( ptr as * const Layout ) . sub ( 1 ) ;
86
142
let old_layout = block_start. read ( ) ;
87
- let new_layout = Layout :: from_size_align ( total_new_size, align_of :: < usize > ( ) ) . unwrap ( ) ;
143
+ let new_layout = Layout :: from_size_align ( total_new_size, MAX_ALIGN ) . unwrap ( ) ;
88
144
89
145
let new_block_start =
90
- alloc:: alloc:: realloc ( block_start as * mut u8 , layout, total_new_size) as * mut Layout ;
146
+ alloc:: alloc:: realloc ( block_start as * mut u8 , old_layout, total_new_size)
147
+ as * mut Layout ;
91
148
92
149
if new_block_start. is_null ( ) {
93
150
// Realloc failed
0 commit comments