@@ -25,19 +25,42 @@ use crate::entrypoint::abort_with_code;
2525
2626extern crate alloc;
2727
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.
3230
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 {
3554 if size == 0 {
3655 return ptr:: null_mut ( ) ;
3756 }
3857
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+
4164 unsafe {
4265 let raw_ptr = match zero {
4366 true => alloc:: alloc:: alloc_zeroed ( layout) ,
@@ -53,14 +76,36 @@ pub fn alloc_helper(size: usize, zero: bool) -> *mut c_void {
5376 }
5477}
5578
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.
5694#[ 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+
59100 alloc_helper ( total_size, true )
60101}
61102
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`.
62107#[ no_mangle]
63- pub extern "C" fn hlfree ( ptr : * mut c_void ) {
108+ pub unsafe extern "C" fn free ( ptr : * mut c_void ) {
64109 if !ptr. is_null ( ) {
65110 unsafe {
66111 let block_start = ( ptr as * const Layout ) . sub ( 1 ) ;
@@ -70,11 +115,22 @@ pub extern "C" fn hlfree(ptr: *mut c_void) {
70115 }
71116}
72117
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`.
73123#[ 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 {
75125 if ptr. is_null ( ) {
76126 // 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 ( ) ;
78134 }
79135
80136 unsafe {
@@ -84,10 +140,11 @@ pub extern "C" fn hlrealloc(ptr: *mut c_void, size: usize) -> *mut c_void {
84140
85141 let block_start = ( ptr as * const Layout ) . sub ( 1 ) ;
86142 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 ( ) ;
88144
89145 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 ;
91148
92149 if new_block_start. is_null ( ) {
93150 // Realloc failed
0 commit comments