| // Copyright 2023-2024, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! This library provides implementation for a few libc functions for building third party C |
| //! libraries. |
| |
| #![cfg_attr(not(test), no_std)] |
| |
| extern crate alloc; |
| |
| use alloc::alloc::{alloc, dealloc}; |
| use core::{ |
| alloc::Layout, |
| ffi::{c_char, c_int, c_ulong, c_void}, |
| mem::size_of_val, |
| ptr::{null_mut, NonNull}, |
| }; |
| use safemath::SafeNum; |
| |
| pub use strcmp::{strcmp, strncmp}; |
| |
| pub mod strchr; |
| pub mod strcmp; |
| pub mod strtoul; |
| |
| // Linking compiler built-in intrinsics to expose libc compatible implementations |
| // 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 |
| extern "C" { |
| /// int memcmp(const void *src1, const void *src2, size_t n) |
| pub fn memcmp(src1: *const c_void, src2: *const c_void, n: usize) -> c_int; |
| /// void *memset(void *dest, int c, size_t n) |
| pub fn memset(dest: *mut c_void, c: c_int, n: usize) -> *mut c_void; |
| /// void *memcpy(void *dest, const void *src, size_t n) |
| pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; |
| /// size_t strlen(const char *s) |
| pub fn strlen(s: *const c_char) -> usize; |
| } |
| |
| /// Extended version of void *malloc(size_t size) with ptr alignment configuration support. |
| /// Libraries may have a different alignment requirements. |
| /// |
| /// # Safety |
| /// |
| /// * Returns a valid pointer to a memory block of `size` bytes, aligned to `alignment`, or null |
| /// on failure. |
| #[no_mangle] |
| pub unsafe extern "C" fn gbl_malloc(request_size: usize, alignment: usize) -> *mut c_void { |
| (|| { |
| // Prefix data: |
| let mut size = 0usize; |
| let mut offset = 0usize; |
| |
| // Determine prefix size necessary to store data required for [gbl_free]: size, offset |
| let prefix_size: usize = size_of_val(&size) + size_of_val(&offset); |
| |
| // Determine padding necessary to guarantee alignment. Padding includes prefix data. |
| let pad: usize = (SafeNum::from(alignment) + prefix_size).try_into().ok()?; |
| |
| // Actual size to allocate. It includes padding to guarantee alignment. |
| size = (SafeNum::from(request_size) + pad).try_into().ok()?; |
| |
| // SAFETY: |
| // * On success, `alloc` guarantees to allocate enough memory. |
| let ptr = unsafe { |
| // Due to manual aligning, there is no need for specific layout alignment. |
| NonNull::new(alloc(Layout::from_size_align(size, 1).ok()?))?.as_ptr() |
| }; |
| |
| // Calculate the aligned address to return the caller. |
| let ret_address = (SafeNum::from(ptr as usize) + prefix_size).round_up(alignment); |
| |
| // Calculate the offsets from the allocation start. |
| let ret_offset = ret_address - (ptr as usize); |
| let align_offset: usize = (ret_offset - size_of_val(&size)).try_into().ok()?; |
| let size_offset: usize = (align_offset - size_of_val(&offset)).try_into().ok()?; |
| offset = usize::try_from(ret_offset).ok()?; |
| |
| // SAFETY: |
| // 'ptr' is guarantied to be valid: |
| // - not NULL; Checked with `NonNull` |
| // - it points to single block of memory big enough to hold size+offset (allocated this |
| // way) |
| // - memory is 1-byte aligned for [u8] slice |
| // - ptr+offset is guarantied to point to the buffer of size 'size' as per allocation that |
| // takes into account padding and prefix. |
| unsafe { |
| // Write metadata and return the caller's pointer. |
| core::slice::from_raw_parts_mut(ptr.add(size_offset), size_of_val(&size)) |
| .copy_from_slice(&size.to_ne_bytes()); |
| core::slice::from_raw_parts_mut(ptr.add(align_offset), size_of_val(&offset)) |
| .copy_from_slice(&offset.to_ne_bytes()); |
| |
| Some(ptr.add(offset)) |
| } |
| })() |
| .unwrap_or(null_mut()) as _ |
| } |
| |
| /// Extended version of void free(void *ptr) with ptr alignment configuration support. |
| /// |
| /// # Safety |
| /// |
| /// * `ptr` must be allocated by `gbl_malloc` and guarantee enough memory for a preceding |
| /// `usize` value and payload or null. |
| /// * `gbl_free` must be called with the same `alignment` as the corresponding `gbl_malloc` call. |
| #[no_mangle] |
| pub unsafe extern "C" fn gbl_free(ptr: *mut c_void, alignment: usize) { |
| if ptr.is_null() { |
| // follow libc free behavior |
| return; |
| } |
| let mut ptr = ptr as *mut u8; |
| |
| let mut offset = 0usize; |
| let mut size = 0usize; |
| |
| // Calculate offsets for size of align data |
| let align_offset: usize = size_of_val(&size); |
| let size_offset: usize = align_offset + size_of_val(&size); |
| |
| // Read size used in allocation from prefix data. |
| offset = usize::from_ne_bytes( |
| // SAFETY: |
| // * `ptr` is allocated by `gbl_malloc` and has enough padding before `ptr` to hold |
| // prefix data. Which consists of align and size values. |
| // * Alignment is 1 for &[u8] |
| unsafe { core::slice::from_raw_parts(ptr.sub(align_offset), size_of_val(&offset)) } |
| .try_into() |
| .unwrap(), |
| ); |
| |
| // Read offset for unaligned pointer from prefix data. |
| size = usize::from_ne_bytes( |
| // SAFETY: |
| // * `ptr` is allocated by `gbl_malloc` and has enough padding before `ptr` to hold |
| // prefix data. Which consists of align and size values. |
| // * Alignment is 1 for &[u8] |
| unsafe { core::slice::from_raw_parts(ptr.sub(size_offset), size_of_val(&size)) } |
| .try_into() |
| .unwrap(), |
| ); |
| |
| // SAFETY: |
| // * `ptr` is allocated by `gbl_malloc` and has enough padding before `ptr` to hold |
| // prefix data. ptr - offset must point to unaligned pointer to buffer, which was returned by |
| // `alloc`, and must be passed to `dealloc` |
| unsafe { |
| // Calculate unaligned pointer returned by [alloc], which must be used in [dealloc] |
| ptr = ptr.sub(offset); |
| |
| // Call to global allocator. |
| dealloc(ptr, Layout::from_size_align(size, alignment).unwrap()); |
| }; |
| } |
| |
| /// void *memchr(const void *ptr, int ch, size_t count); |
| /// |
| /// # Safety |
| /// |
| /// * `ptr` needs to be a buffer with at least `count` bytes. |
| /// * Returns the pointer within `ptr` buffer, or null if not found. |
| #[no_mangle] |
| pub unsafe extern "C" fn memchr(ptr: *const c_void, ch: c_int, count: c_ulong) -> *mut c_void { |
| assert!(!ptr.is_null()); |
| let start = ptr as *const u8; |
| let target = (ch & 0xff) as u8; |
| for i in 0..count { |
| // SAFETY: `ptr` buffer is assumed valid and bounded by count. |
| let curr = unsafe { start.add(i.try_into().unwrap()) }; |
| // SAFETY: `ptr` buffer is assumed valid and bounded by count. |
| if *unsafe { curr.as_ref().unwrap() } == target { |
| return curr as *mut _; |
| } |
| } |
| null_mut() |
| } |
| |
| /// size_t strnlen(const char *s, size_t maxlen); |
| /// |
| /// # Safety |
| /// |
| /// * `s` must be a valid pointer to a null terminated C string. |
| #[no_mangle] |
| pub unsafe extern "C" fn strnlen(s: *const c_char, maxlen: usize) -> usize { |
| // SAFETY: `s` is a valid pointer to a null terminated string. |
| match unsafe { memchr(s as *const _, 0, maxlen.try_into().unwrap()) } { |
| p if p.is_null() => maxlen, |
| p => (p as usize) - (s as usize), |
| } |
| } |