• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023-2025, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This library provides implementation for a few libc functions for building third party C
16 //! libraries.
17 
18 #![cfg_attr(not(test), no_std)]
19 
20 extern crate alloc;
21 
22 use alloc::alloc::{alloc, dealloc};
23 use core::{
24     alloc::Layout,
25     ffi::{c_char, c_int, c_ulong, c_void},
26     mem::size_of_val,
27     ptr::{null_mut, NonNull},
28 };
29 use safemath::SafeNum;
30 
31 pub use strcmp::{strcmp, strncmp};
32 
33 pub mod print;
34 pub mod strchr;
35 pub mod strcmp;
36 pub mod strtoul;
37 
38 // Linking compiler built-in intrinsics to expose libc compatible implementations
39 // https://cs.android.com/android/platform/superproject/main/+/2e15fc2eadcb7db07bf6656086c50153bbafe7b6:prebuilts/rust/linux-x86/1.78.0/lib/rustlib/src/rust/vendor/compiler_builtins/src/mem/mod.rs;l=22
40 extern "C" {
41     /// int memcmp(const void *src1, const void *src2, size_t n)
memcmp(src1: *const c_void, src2: *const c_void, n: usize) -> c_int42     pub fn memcmp(src1: *const c_void, src2: *const c_void, n: usize) -> c_int;
43     /// void *memset(void *dest, int c, size_t n)
memset(dest: *mut c_void, c: c_int, n: usize) -> *mut c_void44     pub fn memset(dest: *mut c_void, c: c_int, n: usize) -> *mut c_void;
45     /// void *memcpy(void *dest, const void *src, size_t n)
memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void46     pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void;
47     /// size_t strlen(const char *s)
strlen(s: *const c_char) -> usize48     pub fn strlen(s: *const c_char) -> usize;
49 }
50 
51 // Linking the platform-specific functionality expected to be provided by the
52 // library/app, which includes the GBL `libc`.
53 extern "Rust" {
54     /// GBL `libc` expects user to provide platform-specific text output implementation
55     /// to allow libc to expose it for external C libraries.
56     ///
57     /// A default POSIX-based implementation is available at `libc/deps/posix.rs`.
58     /// An EFI-specific implementation is provided by `libefi/src/libc.rs`.
gbl_print(d: &dyn core::fmt::Display)59     fn gbl_print(d: &dyn core::fmt::Display);
60 }
61 
62 /// Extended version of void *malloc(size_t size) with ptr alignment configuration support.
63 /// Libraries may have a different alignment requirements.
64 ///
65 /// # Safety
66 ///
67 /// * Returns a valid pointer to a memory block of `size` bytes, aligned to `alignment`, or null
68 ///   on failure.
69 #[no_mangle]
gbl_malloc(request_size: usize, alignment: usize) -> *mut c_void70 pub unsafe extern "C" fn gbl_malloc(request_size: usize, alignment: usize) -> *mut c_void {
71     (|| {
72         // Prefix data:
73         let mut size = 0usize;
74         let mut offset = 0usize;
75 
76         // Determine prefix size necessary to store data required for [gbl_free]: size, offset
77         let prefix_size: usize = size_of_val(&size) + size_of_val(&offset);
78 
79         // Determine padding necessary to guarantee alignment. Padding includes prefix data.
80         let pad: usize = (SafeNum::from(alignment) + prefix_size).try_into().ok()?;
81 
82         // Actual size to allocate. It includes padding to guarantee alignment.
83         size = (SafeNum::from(request_size) + pad).try_into().ok()?;
84 
85         // SAFETY:
86         // *  On success, `alloc` guarantees to allocate enough memory.
87         let ptr = unsafe {
88             // Due to manual aligning, there is no need for specific layout alignment.
89             NonNull::new(alloc(Layout::from_size_align(size, 1).ok()?))?.as_ptr()
90         };
91 
92         // Calculate the aligned address to return the caller.
93         let ret_address = (SafeNum::from(ptr as usize) + prefix_size).round_up(alignment);
94 
95         // Calculate the offsets from the allocation start.
96         let ret_offset = ret_address - (ptr as usize);
97         let align_offset: usize = (ret_offset - size_of_val(&size)).try_into().ok()?;
98         let size_offset: usize = (align_offset - size_of_val(&offset)).try_into().ok()?;
99         offset = usize::try_from(ret_offset).ok()?;
100 
101         // SAFETY:
102         // 'ptr' is guarantied to be valid:
103         // - not NULL; Checked with `NonNull`
104         // - it points to single block of memory big enough to hold size+offset (allocated this
105         // way)
106         // - memory is 1-byte aligned for [u8] slice
107         // - ptr+offset is guarantied to point to the buffer of size 'size' as per allocation that
108         // takes into account padding and prefix.
109         unsafe {
110             // Write metadata and return the caller's pointer.
111             core::slice::from_raw_parts_mut(ptr.add(size_offset), size_of_val(&size))
112                 .copy_from_slice(&size.to_ne_bytes());
113             core::slice::from_raw_parts_mut(ptr.add(align_offset), size_of_val(&offset))
114                 .copy_from_slice(&offset.to_ne_bytes());
115 
116             Some(ptr.add(offset))
117         }
118     })()
119     .unwrap_or(null_mut()) as _
120 }
121 
122 /// Extended version of void free(void *ptr) with ptr alignment configuration support.
123 ///
124 /// # Safety
125 ///
126 /// * `ptr` must be allocated by `gbl_malloc` and guarantee enough memory for a preceding
127 ///   `usize` value and payload or null.
128 /// * `gbl_free` must be called with the same `alignment` as the corresponding `gbl_malloc` call.
129 #[no_mangle]
gbl_free(ptr: *mut c_void, alignment: usize)130 pub unsafe extern "C" fn gbl_free(ptr: *mut c_void, alignment: usize) {
131     if ptr.is_null() {
132         // follow libc free behavior
133         return;
134     }
135     let mut ptr = ptr as *mut u8;
136 
137     let mut offset = 0usize;
138     let mut size = 0usize;
139 
140     // Calculate offsets for size of align data
141     let align_offset: usize = size_of_val(&size);
142     let size_offset: usize = align_offset + size_of_val(&size);
143 
144     // Read size used in allocation from prefix data.
145     offset = usize::from_ne_bytes(
146         // SAFETY:
147         // * `ptr` is allocated by `gbl_malloc` and has enough padding before `ptr` to hold
148         // prefix data. Which consists of align and size values.
149         // * Alignment is 1 for &[u8]
150         unsafe { core::slice::from_raw_parts(ptr.sub(align_offset), size_of_val(&offset)) }
151             .try_into()
152             .unwrap(),
153     );
154 
155     // Read offset for unaligned pointer from prefix data.
156     size = usize::from_ne_bytes(
157         // SAFETY:
158         // * `ptr` is allocated by `gbl_malloc` and has enough padding before `ptr` to hold
159         // prefix data. Which consists of align and size values.
160         // * Alignment is 1 for &[u8]
161         unsafe { core::slice::from_raw_parts(ptr.sub(size_offset), size_of_val(&size)) }
162             .try_into()
163             .unwrap(),
164     );
165 
166     // SAFETY:
167     // * `ptr` is allocated by `gbl_malloc` and has enough padding before `ptr` to hold
168     // prefix data. ptr - offset must point to unaligned pointer to buffer, which was returned by
169     // `alloc`, and must be passed to `dealloc`
170     unsafe {
171         // Calculate unaligned pointer returned by [alloc], which must be used in [dealloc]
172         ptr = ptr.sub(offset);
173 
174         // Call to global allocator.
175         dealloc(ptr, Layout::from_size_align(size, alignment).unwrap());
176     };
177 }
178 
179 /// void *memchr(const void *ptr, int ch, size_t count);
180 ///
181 /// # Safety
182 ///
183 /// * `ptr` needs to be a buffer with at least `count` bytes.
184 /// * Returns the pointer within `ptr` buffer, or null if not found.
185 #[no_mangle]
memchr(ptr: *const c_void, ch: c_int, count: c_ulong) -> *mut c_void186 pub unsafe extern "C" fn memchr(ptr: *const c_void, ch: c_int, count: c_ulong) -> *mut c_void {
187     assert!(!ptr.is_null());
188     let start = ptr as *const u8;
189     let target = (ch & 0xff) as u8;
190     for i in 0..count {
191         // SAFETY: `ptr` buffer is assumed valid and bounded by count.
192         let curr = unsafe { start.add(i.try_into().unwrap()) };
193         // SAFETY: `ptr` buffer is assumed valid and bounded by count.
194         if *unsafe { curr.as_ref().unwrap() } == target {
195             return curr as *mut _;
196         }
197     }
198     null_mut()
199 }
200 
201 /// size_t strnlen(const char *s, size_t maxlen);
202 ///
203 /// # Safety
204 ///
205 /// * `s` must be a valid pointer to a null terminated C string.
206 #[no_mangle]
strnlen(s: *const c_char, maxlen: usize) -> usize207 pub unsafe extern "C" fn strnlen(s: *const c_char, maxlen: usize) -> usize {
208     // SAFETY: `s` is a valid pointer to a null terminated string.
209     match unsafe { memchr(s as *const _, 0, maxlen.try_into().unwrap()) } {
210         p if p.is_null() => maxlen,
211         p => (p as usize) - (s as usize),
212     }
213 }
214