@@ -25,51 +25,71 @@ use hyperlight_guest::exit::abort_with_code;
25
25
/*
26
26
C-wrappers for Rust's registered global allocator.
27
27
28
- Each memory allocation via `malloc/calloc/realloc` is stored together with a `alloc::Layout` describing
29
- the size and alignment of the allocation. This layout is stored just before the actual raw memory returned to the caller.
28
+ Each memory allocation via `malloc/calloc/realloc` is stored together with a `Header` containing
29
+ an `alloc::Layout` and offset information. The header is stored just before the actual raw memory
30
+ returned to the caller.
30
31
31
- Example: A call to malloc(64) will allocate space for both an `alloc::Layout` and 64 bytes of memory :
32
+ Example: A call to malloc(64) with 16-byte alignment will allocate space for the header and 64 bytes:
32
33
33
34
----------------------------------------------------------------------------------------
34
- | Layout { size: 64 + size_of::<Layout>(), ... } | 64 bytes of memory | ...
35
+ | allocated memory... | Header { layout, offset } | 64 bytes of memory | ...
35
36
----------------------------------------------------------------------------------------
36
- ^
37
- |
38
- |
39
- ptr returned to caller
37
+ ^ ^ ^
38
+ | | |
39
+ raw_ptr header_ptr ptr returned to caller
40
+ (from allocator) (at data_offset - header_size) (at data_offset)
41
+
42
+ The header contains:
43
+ - layout: The Layout used for the entire allocation (total_size, actual_align)
44
+ - offset: The offset from raw_ptr to the user data (data_offset)
40
45
*/
41
46
42
47
// We assume the maximum alignment for any value is the alignment of u128.
43
- const MAX_ALIGN : usize = align_of :: < u128 > ( ) ;
48
+ const DEFAULT_ALIGN : usize = align_of :: < u128 > ( ) ;
49
+
50
+ #[ repr( C ) ]
51
+ struct Header {
52
+ layout : Layout ,
53
+ offset : usize ,
54
+ }
44
55
45
56
/// Allocates a block of memory with the given size. The memory is only guaranteed to be initialized to 0s if `zero` is true, otherwise
46
57
/// it may or may not be initialized.
47
58
///
48
59
/// # Safety
49
60
/// The returned pointer must be freed with `memory::free` when it is no longer needed, otherwise memory will leak.
50
- unsafe fn alloc_helper ( size : usize , zero : bool ) -> * mut c_void {
61
+ unsafe fn alloc_helper ( size : usize , alignment : usize , zero : bool ) -> * mut c_void {
51
62
if size == 0 {
52
63
return ptr:: null_mut ( ) ;
53
64
}
54
65
55
- // Allocate a block that includes space for both layout information and data
56
- let total_size = size
57
- . checked_add ( size_of :: < Layout > ( ) )
58
- . expect ( "data and layout size should not overflow in alloc" ) ;
59
- let layout = Layout :: from_size_align ( total_size, MAX_ALIGN ) . expect ( "Invalid layout" ) ;
66
+ let actual_align = alignment. max ( align_of :: < Header > ( ) ) ;
67
+ let header_size = size_of :: < Header > ( ) ;
68
+ let data_offset = header_size. next_multiple_of ( actual_align) ;
69
+ let total_size = data_offset + size;
70
+
71
+ // Create layout for entire allocation
72
+ let layout =
73
+ Layout :: from_size_align ( total_size, actual_align) . expect ( "Invalid layout parameters" ) ;
60
74
61
75
unsafe {
62
76
let raw_ptr = match zero {
63
77
true => alloc:: alloc:: alloc_zeroed ( layout) ,
64
78
false => alloc:: alloc:: alloc ( layout) ,
65
79
} ;
80
+
66
81
if raw_ptr. is_null ( ) {
67
82
abort_with_code ( & [ ErrorCode :: MallocFailed as u8 ] ) ;
68
- } else {
69
- let layout_ptr = raw_ptr as * mut Layout ;
70
- layout_ptr. write ( layout) ;
71
- layout_ptr. add ( 1 ) as * mut c_void
72
83
}
84
+
85
+ // Place Header immediately before the user data region
86
+ let header_ptr = raw_ptr. add ( data_offset - header_size) . cast :: < Header > ( ) ;
87
+
88
+ header_ptr. write ( Header {
89
+ layout,
90
+ offset : data_offset,
91
+ } ) ;
92
+ raw_ptr. add ( data_offset) as * mut c_void
73
93
}
74
94
}
75
95
@@ -80,7 +100,7 @@ unsafe fn alloc_helper(size: usize, zero: bool) -> *mut c_void {
80
100
/// The returned pointer must be freed with `memory::free` when it is no longer needed, otherwise memory will leak.
81
101
#[ unsafe( no_mangle) ]
82
102
pub unsafe extern "C" fn malloc ( size : usize ) -> * mut c_void {
83
- unsafe { alloc_helper ( size, false ) }
103
+ unsafe { alloc_helper ( size, DEFAULT_ALIGN , false ) }
84
104
}
85
105
86
106
/// Allocates a block of memory for an array of `nmemb` elements, each of `size` bytes.
@@ -95,8 +115,22 @@ pub unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut c_void {
95
115
. checked_mul ( size)
96
116
. expect ( "nmemb * size should not overflow in calloc" ) ;
97
117
98
- alloc_helper ( total_size, true )
118
+ alloc_helper ( total_size, DEFAULT_ALIGN , true )
119
+ }
120
+ }
121
+
122
+ /// Allocates aligned memory.
123
+ ///
124
+ /// # Safety
125
+ /// The returned pointer must be freed with `free` when it is no longer needed.
126
+ #[ unsafe( no_mangle) ]
127
+ pub unsafe extern "C" fn aligned_alloc ( alignment : usize , size : usize ) -> * mut c_void {
128
+ // Validate alignment
129
+ if alignment == 0 || ( alignment & ( alignment - 1 ) ) != 0 {
130
+ return ptr:: null_mut ( ) ;
99
131
}
132
+
133
+ unsafe { alloc_helper ( size, alignment, false ) }
100
134
}
101
135
102
136
/// Frees the memory block pointed to by `ptr`.
@@ -105,12 +139,20 @@ pub unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut c_void {
105
139
/// `ptr` must be a pointer to a memory block previously allocated by `memory::malloc`, `memory::calloc`, or `memory::realloc`.
106
140
#[ unsafe( no_mangle) ]
107
141
pub unsafe extern "C" fn free ( ptr : * mut c_void ) {
108
- if !ptr. is_null ( ) {
109
- unsafe {
110
- let block_start = ( ptr as * const Layout ) . sub ( 1 ) ;
111
- let layout = block_start. read ( ) ;
112
- alloc:: alloc:: dealloc ( block_start as * mut u8 , layout)
113
- }
142
+ if ptr. is_null ( ) {
143
+ return ;
144
+ }
145
+
146
+ let user_ptr = ptr as * const u8 ;
147
+
148
+ unsafe {
149
+ // Read the Header just before the user data
150
+ let header_ptr = user_ptr. sub ( size_of :: < Header > ( ) ) . cast :: < Header > ( ) ;
151
+ let Header { layout, offset } = header_ptr. read ( ) ;
152
+
153
+ // Deallocate from the original base pointer
154
+ let raw_ptr = user_ptr. sub ( offset) as * mut u8 ;
155
+ alloc:: alloc:: dealloc ( raw_ptr, layout) ;
114
156
}
115
157
}
116
158
@@ -134,26 +176,25 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void {
134
176
return ptr:: null_mut ( ) ;
135
177
}
136
178
137
- let total_new_size = size
138
- . checked_add ( size_of :: < Layout > ( ) )
139
- . expect ( "data and layout size should not overflow in realloc" ) ;
140
-
141
- let block_start = unsafe { ( ptr as * const Layout ) . sub ( 1 ) } ;
142
- let old_layout = unsafe { block_start. read ( ) } ;
143
- let new_layout = Layout :: from_size_align ( total_new_size, MAX_ALIGN ) . unwrap ( ) ;
179
+ unsafe {
180
+ let user_ptr = ptr as * const u8 ;
181
+ let header_ptr = user_ptr. sub ( size_of :: < Header > ( ) ) . cast :: < Header > ( ) ;
182
+ let Header {
183
+ layout : old_layout,
184
+ offset,
185
+ } = header_ptr. read ( ) ;
186
+ let old_user_size = old_layout. size ( ) - offset;
187
+
188
+ let new_ptr = alloc_helper ( size, old_layout. align ( ) , false ) ;
189
+ if new_ptr. is_null ( ) {
190
+ return ptr:: null_mut ( ) ;
191
+ }
144
192
145
- let new_block_start =
146
- unsafe { alloc :: alloc :: realloc ( block_start as * mut u8 , old_layout , total_new_size ) }
147
- as * mut Layout ;
193
+ // Copy the lesser of old and new sizes
194
+ let copy_size = old_user_size . min ( size ) ;
195
+ ptr :: copy_nonoverlapping ( user_ptr , new_ptr as * mut u8 , copy_size ) ;
148
196
149
- if new_block_start. is_null ( ) {
150
- // Realloc failed
151
- abort_with_code ( & [ ErrorCode :: MallocFailed as u8 ] ) ;
152
- } else {
153
- // Update the stored Layout, then return ptr to memory right after the Layout.
154
- unsafe {
155
- new_block_start. write ( new_layout) ;
156
- new_block_start. add ( 1 ) as * mut c_void
157
- }
197
+ free ( ptr) ;
198
+ new_ptr
158
199
}
159
200
}
0 commit comments