support aligned_alloc for unixes support.
This commit is contained in:
parent
7e5b9e215d
commit
5ea21ca486
6 changed files with 128 additions and 0 deletions
|
|
@ -172,4 +172,42 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn aligned_alloc(
|
||||
&mut self,
|
||||
align: u64,
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
|
||||
let this = self.eval_context_mut();
|
||||
// Alignment must be a power of 2, and "supported by the implementation".
|
||||
// We decide that "supported by the implementation" means that the
|
||||
// size must be a multiple of the alignment. (This restriction seems common
|
||||
// enough that it is stated on <https://en.cppreference.com/w/c/memory/aligned_alloc>
|
||||
// as a general rule, but the actual standard has no such rule.)
|
||||
// If any of these are violated, we have to return NULL.
|
||||
// All fundamental alignments must be supported.
|
||||
//
|
||||
// macOS and Illumos are buggy in that they require the alignment
|
||||
// to be at least the size of a pointer, so they do not support all fundamental
|
||||
// alignments. We do not emulate those platform bugs.
|
||||
//
|
||||
// Linux also sets errno to EINVAL, but that's non-standard behavior that we do not
|
||||
// emulate.
|
||||
// FreeBSD says some of these cases are UB but that's violating the C standard.
|
||||
// http://en.cppreference.com/w/cpp/memory/c/aligned_alloc
|
||||
// Linux: https://linux.die.net/man/3/aligned_alloc
|
||||
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html
|
||||
match size.checked_rem(align) {
|
||||
Some(0) if align.is_power_of_two() => {
|
||||
let align = align.max(this.malloc_align(size).bytes());
|
||||
let ptr = this.allocate_ptr(
|
||||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
Ok(ptr.into())
|
||||
}
|
||||
_ => Ok(Pointer::null()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,6 +295,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
"aligned_alloc" => {
|
||||
// This is a C11 function, we assume all Unixes have it.
|
||||
// (MSVC explicitly does not support this.)
|
||||
let [align, size] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let align = this.read_target_usize(align)?;
|
||||
let size = this.read_target_usize(size)?;
|
||||
|
||||
let res = this.aligned_alloc(align, size)?;
|
||||
this.write_pointer(res, dest)?;
|
||||
}
|
||||
|
||||
// Dynamic symbol loading
|
||||
"dlsym" => {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let result = this.posix_memalign(memptr, align, size)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"aligned_alloc" => {
|
||||
let [align, size] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let align = this.read_target_usize(align)?;
|
||||
let size = this.read_target_usize(size)?;
|
||||
|
||||
let res = this.aligned_alloc(align, size)?;
|
||||
this.write_pointer(res, dest)?;
|
||||
}
|
||||
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc.
|
||||
|
||||
fn main() {
|
||||
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
|
||||
// so we declare it ourselves.
|
||||
extern "C" {
|
||||
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
|
||||
}
|
||||
|
||||
// Make sure even zero-sized allocations need to be freed.
|
||||
|
||||
unsafe {
|
||||
aligned_alloc(2, 0); //~ERROR: memory leaked
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here:
|
||||
--> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
|
||||
|
|
||||
LL | aligned_alloc(2, 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -241,6 +241,44 @@ fn test_reallocarray() {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_aligned_alloc() {
|
||||
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
|
||||
// so we declare it ourselves.
|
||||
extern "C" {
|
||||
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
|
||||
}
|
||||
// size not a multiple of the alignment
|
||||
unsafe {
|
||||
let p = aligned_alloc(16, 3);
|
||||
assert_eq!(p, ptr::null_mut());
|
||||
}
|
||||
|
||||
// alignment not power of 2
|
||||
unsafe {
|
||||
let p = aligned_alloc(63, 8);
|
||||
assert_eq!(p, ptr::null_mut());
|
||||
}
|
||||
|
||||
// alignment lesser than a word but still a successful allocation
|
||||
unsafe {
|
||||
let p = aligned_alloc(1, 4);
|
||||
assert!(!p.is_null());
|
||||
assert!(p.is_aligned_to(4));
|
||||
libc::free(p);
|
||||
}
|
||||
|
||||
// repeated tests on correct alignment/size
|
||||
for _ in 0..16 {
|
||||
unsafe {
|
||||
let p = aligned_alloc(16, 16);
|
||||
assert!(!p.is_null());
|
||||
assert!(p.is_aligned_to(16));
|
||||
libc::free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_malloc();
|
||||
test_calloc();
|
||||
|
|
@ -254,6 +292,8 @@ fn main() {
|
|||
target_os = "wasi",
|
||||
)))]
|
||||
test_reallocarray();
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
test_aligned_alloc();
|
||||
|
||||
test_memcpy();
|
||||
test_strcpy();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue