diff --git a/cpp2rust/compat/errno.h b/cpp2rust/compat/errno.h new file mode 100644 index 00000000..d6b34c30 --- /dev/null +++ b/cpp2rust/compat/errno.h @@ -0,0 +1,10 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include_next + +#undef errno + +int *cpp2rust_errno(void); + +#define errno (*cpp2rust_errno()) diff --git a/libcc2rs/src/compat.rs b/libcc2rs/src/compat.rs index 486c260b..38873938 100644 --- a/libcc2rs/src/compat.rs +++ b/libcc2rs/src/compat.rs @@ -11,6 +11,14 @@ unsafe extern "C" { #[cfg(target_os = "macos")] #[link_name = "malloc_size"] fn platform_malloc_size(ptr: *const c_void) -> usize; + + #[cfg(target_os = "linux")] + #[link_name = "__errno_location"] + fn platform_errno_location() -> *mut i32; + + #[cfg(target_os = "macos")] + #[link_name = "__error"] + fn platform_errno_location() -> *mut i32; } /// # Safety @@ -28,3 +36,10 @@ pub unsafe fn malloc_usable_size(ptr: *mut c_void) -> usize { unsafe { platform_malloc_size(ptr as *const c_void) } } } + +/// # Safety +/// +/// Invokes the platform specific errno. +pub unsafe fn cpp2rust_errno() -> *mut i32 { + unsafe { platform_errno_location() } +} diff --git a/rules/errno/ir_unsafe.json b/rules/errno/ir_unsafe.json new file mode 100644 index 00000000..d910da01 --- /dev/null +++ b/rules/errno/ir_unsafe.json @@ -0,0 +1,13 @@ +{ + "f1": { + "body": [ + { + "text": "libcc2rs::cpp2rust_errno()" + } + ], + "return_type": { + "type": "*mut i32", + "is_unsafe_pointer": true + } + } +} diff --git a/rules/errno/src.c b/rules/errno/src.c new file mode 100644 index 00000000..0e32ea0e --- /dev/null +++ b/rules/errno/src.c @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +int *f1(void) { return cpp2rust_errno(); } diff --git a/rules/errno/tgt_unsafe.rs b/rules/errno/tgt_unsafe.rs new file mode 100644 index 00000000..57796bc2 --- /dev/null +++ b/rules/errno/tgt_unsafe.rs @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +unsafe fn f1() -> *mut i32 { + libcc2rs::cpp2rust_errno() +} diff --git a/rules/src/modules.rs b/rules/src/modules.rs index 6357cfc6..0e9cde66 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -34,6 +34,8 @@ pub mod cstring_tgt_unsafe; pub mod deque_tgt_refcount; #[path = r#"../deque/tgt_unsafe.rs"#] pub mod deque_tgt_unsafe; +#[path = r#"../errno/tgt_unsafe.rs"#] +pub mod errno_tgt_unsafe; #[path = r#"../fstream/tgt_refcount.rs"#] pub mod fstream_tgt_refcount; #[path = r#"../fstream/tgt_unsafe.rs"#] diff --git a/tests/unit/errno.c b/tests/unit/errno.c new file mode 100644 index 00000000..8a0be0bf --- /dev/null +++ b/tests/unit/errno.c @@ -0,0 +1,40 @@ +// no-compile: refcount +#include +#include +#include +#include +#include + +static void test_errno(void) { + errno = 0; + assert(errno == 0); + errno = 42; + assert(errno == 42); + int saved = errno; + assert(saved == 42); + errno = 0; +} + +static void test_errno_preserved_across_strdup(void) { + errno = 99; + char *d = strdup("hello"); + assert(d != NULL); + assert(errno == 99); + free(d); + errno = 0; +} + +static void test_errno_from_fseek(void) { + errno = 0; + int r = fseek(stdin, 0, SEEK_SET); + assert(r == -1); + assert(errno == ESPIPE); + errno = 0; +} + +int main(void) { + test_errno(); + test_errno_preserved_across_strdup(); + test_errno_from_fseek(); + return 0; +} diff --git a/tests/unit/out/unsafe/errno.rs b/tests/unit/out/unsafe/errno.rs new file mode 100644 index 00000000..d0f8f8e7 --- /dev/null +++ b/tests/unit/out/unsafe/errno.rs @@ -0,0 +1,55 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn test_errno_0() { + (*libcc2rs::cpp2rust_errno()) = 0; + assert!(((((*libcc2rs::cpp2rust_errno()) == (0)) as i32) != 0)); + (*libcc2rs::cpp2rust_errno()) = 42; + assert!(((((*libcc2rs::cpp2rust_errno()) == (42)) as i32) != 0)); + let mut saved: i32 = (*libcc2rs::cpp2rust_errno()); + assert!(((((saved) == (42)) as i32) != 0)); + (*libcc2rs::cpp2rust_errno()) = 0; +} +pub unsafe fn test_errno_preserved_across_strdup_1() { + (*libcc2rs::cpp2rust_errno()) = 99; + let mut d: *mut u8 = + libc::strdup((b"hello\0".as_ptr().cast_mut()).cast_const() as *const i8) as *mut u8; + assert!((((!((d).is_null())) as i32) != 0)); + assert!(((((*libcc2rs::cpp2rust_errno()) == (99)) as i32) != 0)); + libc::free((d as *mut u8 as *mut ::libc::c_void)); + (*libcc2rs::cpp2rust_errno()) = 0; +} +pub unsafe fn test_errno_from_fseek_2() { + (*libcc2rs::cpp2rust_errno()) = 0; + let mut r: i32 = if (match 0 { + 0 => (*libcc2rs::cin_unsafe()).seek(std::io::SeekFrom::Start(0_i64 as u64)), + 1 => (*libcc2rs::cin_unsafe()).seek(std::io::SeekFrom::Current(0_i64)), + 2 => (*libcc2rs::cin_unsafe()).seek(std::io::SeekFrom::End(0_i64)), + _ => Err(std::io::Error::other("unsupported whence for fseek.")), + }) + .is_ok() + { + 0 + } else { + -1 + }; + assert!(((((r) == (-1_i32)) as i32) != 0)); + assert!(((((*libcc2rs::cpp2rust_errno()) == (29)) as i32) != 0)); + (*libcc2rs::cpp2rust_errno()) = 0; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + (unsafe { test_errno_0() }); + (unsafe { test_errno_preserved_across_strdup_1() }); + (unsafe { test_errno_from_fseek_2() }); + return 0; +}