diff --git a/Cargo.lock b/Cargo.lock index 45e6ba1..8675aaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.68" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" [[package]] name = "neco" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/safe_libc/Cargo.lock b/safe_libc/Cargo.lock deleted file mode 100644 index e659af2..0000000 --- a/safe_libc/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "libc" -version = "0.2.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" - -[[package]] -name = "safe_libc" -version = "0.1.0" -dependencies = [ - "libc", -] diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml index f776d09..34f8f79 100644 --- a/safe_libc/Cargo.toml +++ b/safe_libc/Cargo.toml @@ -7,4 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = { version = "0.2.68", default-features = false } +libc = { version = "0.2.71", default-features = false } diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs index fa3e45d..a0bbfb4 100644 --- a/safe_libc/src/boxed.rs +++ b/safe_libc/src/boxed.rs @@ -1,150 +1,78 @@ -use crate::util; - -use core::{fmt, mem, ptr}; +use core::{marker, mem, ptr}; use core::ops::{Deref, DerefMut}; -fn alloc(len: usize) -> *mut T { - if util::zst::(len) { - return ptr::null_mut(); - } - let size = len.checked_mul(mem::size_of::()).expect("allocation overflow"); - let align = mem::align_of::(); - util::must_succeed( - unsafe { - libc::memalign(align as libc::size_t, size as libc::size_t) - } - ).cast() +pub trait Dealloc { + unsafe fn dealloc(p: *mut T); } -#[inline] -const fn dangling() -> *mut T { - mem::align_of::() as *mut T +#[repr(transparent)] +pub struct BoxLike> { + inner: ptr::NonNull, + _phantom: marker::PhantomData, + _dropper: marker::PhantomData, } -#[inline] -fn slice_len(p: *const [T]) -> usize { - unsafe { mem::transmute::<_, [usize; 2]>(p)[1] } -} - -pub trait SafePtr { - fn safe_ptr(p: *mut Self) -> *mut Self; -} - -impl SafePtr for T { +impl> BoxLike { #[inline] - fn safe_ptr(p: *mut T) -> *mut T { - if util::zst::(1) { - return dangling(); + pub unsafe fn from_raw_unchecked(p: *mut T) -> Self { + BoxLike { + inner: ptr::NonNull::new_unchecked(p), + _phantom: marker::PhantomData, + _dropper: marker::PhantomData, } - - debug_assert!(!p.is_null(), "NULL ptr"); - p } -} -impl SafePtr for [T] { #[inline] - fn safe_ptr(p: *mut [T]) -> *mut [T] { - let len = slice_len(p); - if util::zst::(len) { - return ptr::slice_from_raw_parts_mut(dangling(), len); + pub unsafe fn from_raw(p: *mut T) -> Option { + if p.is_null() { + None + } else { + Some(Self::from_raw_unchecked(p)) } - - debug_assert!(!p.is_null(), "NULL ptr"); - p - } -} - -pub struct CBox(*mut T); - -impl CBox { - #[inline] - pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { - CBox(p) } #[inline] pub fn into_raw(self) -> *mut T { - let p = self.0; + let p = self.inner.as_ptr(); mem::forget(self); p } - - #[inline] - pub fn as_ptr(&self) -> *const T { - self.0 - } - - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - self.0 - } } -impl CBox { - #[inline] - pub fn new(value: T) -> CBox { - let p = alloc(1); - unsafe { - ptr::write(p, value); - CBox::from_raw_unchecked(p) - } - } - - #[inline] - pub unsafe fn from_raw(p: *mut T) -> CBox { - util::check_ptr(p, 1); - CBox(p) - } - - #[inline] - pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { - CBox(ptr::slice_from_raw_parts_mut(p, len)) - } - - #[inline] - pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { - util::check_ptr(p, len); - CBox::slice_from_raw_parts_unchecked(p, len) - } -} - -impl Drop for CBox { +impl> Drop for BoxLike { #[inline] fn drop(&mut self) { + let p = self.inner.as_ptr(); unsafe { - ptr::drop_in_place(self.0); - libc::free(self.0.cast()); + ptr::drop_in_place(p); + D::dealloc(p); } } } -impl Deref for CBox { +impl> Deref for BoxLike { type Target = T; #[inline] fn deref(&self) -> &T { - unsafe { &*T::safe_ptr(self.0) } + unsafe { self.inner.as_ref() } } } -impl DerefMut for CBox { +impl> DerefMut for BoxLike { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *T::safe_ptr(self.0) } + unsafe { self.inner.as_mut() } } } -impl fmt::Debug for CBox { +pub struct DeallocFree; + +impl Dealloc for DeallocFree { #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + unsafe fn dealloc(p: *mut T) { + libc::free(p as _); } } -impl fmt::Display for CBox { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} +pub type CBox = BoxLike; diff --git a/safe_libc/src/errno.rs b/safe_libc/src/errno.rs new file mode 100644 index 0000000..fdb1246 --- /dev/null +++ b/safe_libc/src/errno.rs @@ -0,0 +1,34 @@ +use crate::string; + +use core::fmt; + +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct Errno(pub libc::c_int); + +extern "C" { + #[ffi_const] + pub fn __errno_location() -> *mut libc::c_int; +} + +#[inline] +pub fn errno() -> Errno { + unsafe { Errno(*__errno_location()) } +} + +impl fmt::Display for Errno { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [0u8; 1024]; + let cstr = unsafe { + if libc::strerror_r(self.0, buf.as_mut_ptr().cast(), buf.len()) != 0 { + return Err(fmt::Error); + } + string::CStr::from_bytes_with_nul_unchecked(&buf) + }; + match cstr.to_str() { + Err(_) => Err(fmt::Error), + Ok(s) => f.write_str(s), + } + } +} diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index 0ab0e89..cbc4071 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -1,17 +1,20 @@ #![no_std] +#![feature(extern_types)] +#![feature(slice_ptr_len)] +#![feature(ffi_const)] + +extern crate alloc; pub use libc::*; mod util; pub mod boxed; +pub mod errno; pub mod stdio; pub mod string; - extern "C" { - pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; - // pub static stdin: *mut libc::FILE; pub static stdout: *mut libc::FILE; pub static stderr: *mut libc::FILE; diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index 271f023..2942405 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -1,47 +1,104 @@ -use crate as libc; -use crate::string; +use crate::{self as libc, errno, string}; use core::fmt; +use core::ops::{Deref, DerefMut}; -pub struct OStream { - file: *mut libc::FILE +pub struct Error { + pub errno: errno::Errno, } -#[inline] -pub unsafe fn stdout() -> OStream { - OStream { file: libc::stdout } -} - -#[inline] -pub unsafe fn stderr() -> OStream { - OStream { file: libc::stderr } -} - -impl OStream { - #[inline] - pub fn write(&mut self, b: &[u8]) { - unsafe { - libc::fwrite( - b.as_ptr().cast(), - 1, - b.len(), - self.file, - ); - } - } - - #[inline] - pub fn puts(&mut self, s: &string::CStr) { - unsafe { - libc::fputs(s.as_ptr(), self.file); - } +impl From for fmt::Error { + fn from(_err: Error) -> fmt::Error { + fmt::Error } } -impl fmt::Write for OStream { +pub type Result = core::result::Result; + +#[inline] +fn check_io(ok: bool) -> Result<()> { + if ok { + Ok(()) + } else { + Err(Error { + errno: errno::errno(), + }) + } +} + +pub struct BasicOStream(*mut libc::FILE); + +unsafe impl Sync for BasicOStream {} +unsafe impl Send for BasicOStream {} + +pub type Stdout = BasicOStream; +pub type Stderr = BasicOStream; + +#[inline] +pub fn stdout() -> Stdout { + BasicOStream(unsafe { libc::stdout }) +} + +#[inline] +pub fn stderr() -> Stderr { + BasicOStream(unsafe { libc::stderr }) +} + +impl BasicOStream { + #[inline] + pub fn flush(&mut self) -> Result<()> { + check_io(unsafe { libc::fflush(self.0) } == 0) + } + + #[inline] + pub fn write(&mut self, b: &[u8]) -> Result<()> { + check_io(unsafe { libc::fwrite(b.as_ptr().cast(), 1, b.len(), self.0) } == b.len()) + } + + #[inline] + pub fn puts(&mut self, s: &string::CStr) -> Result<()> { + check_io(unsafe { libc::fputs(s.as_ptr(), self.0) } != libc::EOF) + } +} + +impl fmt::Write for BasicOStream { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { - self.write(s.as_bytes()); + self.write(s.as_bytes())?; Ok(()) } } + +pub struct OStream(BasicOStream); + +impl OStream { + #[inline] + pub unsafe fn from_raw(file: *mut libc::FILE) -> OStream { + OStream(BasicOStream(file)) + } +} + +impl Drop for OStream { + #[inline] + fn drop(&mut self) { + unsafe { + libc::fclose((self.0).0); + } + } +} + +impl Deref for OStream { + type Target = BasicOStream; + + #[inline] + fn deref(&self) -> &BasicOStream { + &self.0 + } +} + +impl DerefMut for OStream { + #[inline] + fn deref_mut(&mut self) -> &mut BasicOStream { + &mut self.0 + } +} diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index 8f973ad..5483e08 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -1,40 +1,31 @@ -use crate as libc; -use crate::util; -use crate::boxed::CBox; +use crate::{self as libc, boxed::CBox, util}; -use core::slice; +use core::{mem, slice}; use core::ops::{Deref, DerefMut}; //pub struct FromBytesWithNulError {} -pub struct CStr { inner: libc::c_char } +extern "C" { + type RawCStr; +} + +#[repr(transparent)] +pub struct CStr(RawCStr); impl CStr { - #[inline] - pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { - &*p.cast() - } - #[inline] pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { - util::check_ptr(p, 1); - CStr::from_ptr_unchecked(p) - } - - #[inline] - pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { - &mut *p.cast() + &*(p as *const CStr) } #[inline] pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { - util::check_ptr(p, 1); - CStr::from_mut_ptr_unchecked(p) + &mut *(p as *mut CStr) } #[inline] pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr_unchecked(bytes.as_ptr().cast()) + CStr::from_ptr(bytes.as_ptr().cast()) } // TODO @@ -43,33 +34,37 @@ impl CStr { #[inline] pub fn len(&self) -> usize { - unsafe { libc::strlen(self.as_ptr()) as usize } + unsafe { libc::strlen(self.as_ptr()) } } #[inline] pub const fn as_ptr(&self) -> *const libc::c_char { - &self.inner + (self as *const CStr).cast() } #[inline] pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { - &mut self.inner + (self as *mut CStr).cast() } #[inline] - pub fn as_bytes(&self) -> &[u8] { - unsafe { slice::from_raw_parts( - self.as_ptr().cast(), - self.len(), - ) } + pub fn to_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.as_ptr().cast(), self.len()) } } #[inline] - pub fn as_mut_bytes(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut( - self.as_mut_ptr().cast(), - self.len(), - ) } + pub fn to_mut_bytes(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) } + } + + #[inline] + pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> { + core::str::from_utf8(self.to_bytes()) + } + + #[inline] + pub fn to_mut_str(&mut self) -> Result<&mut str, core::str::Utf8Error> { + core::str::from_utf8_mut(self.to_mut_bytes()) } #[inline] @@ -80,28 +75,28 @@ impl CStr { #[macro_export] macro_rules! cstr { - ($s:expr) => ( + ($s:expr) => { unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } - ) + }; } -pub struct CString { inner: CBox } +pub struct CString { + inner: CBox, +} impl CString { - #[inline] - pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { - CString { inner: CBox::from_raw_unchecked(p) } - } - #[inline] pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { - util::check_ptr(p, 1); - CString::from_raw_unchecked(p) + CString { + inner: CBox::from_raw_unchecked(p as *mut CStr) + } } #[inline] - pub fn into_raw(self) -> *mut libc::c_char { - self.inner.into_raw() + pub fn into_raw(mut self) -> *mut libc::c_char { + let p = self.as_mut_ptr(); + mem::forget(self); + p } } @@ -110,26 +105,24 @@ impl Deref for CString { #[inline] fn deref(&self) -> &CStr { - unsafe { CStr::from_ptr(&*self.inner) } + &*self.inner } } impl DerefMut for CString { #[inline] fn deref_mut(&mut self) -> &mut CStr { - unsafe { CStr::from_mut_ptr(&mut *self.inner) } + &mut *self.inner } } impl From<&[u8]> for CString { fn from(s: &[u8]) -> CString { unsafe { - CString::from_raw_unchecked( - util::must_succeed(libc::strndup( - s.as_ptr().cast(), - s.len() as libc::size_t, - )) - ) + CString::from_raw(util::must_succeed(libc::strndup( + s.as_ptr().cast(), + s.len(), + ))) } } } @@ -144,8 +137,6 @@ impl From<&str> for CString { impl From<&CStr> for CString { #[inline] fn from(s: &CStr) -> CString { - unsafe { - CString::from_raw_unchecked(util::must_succeed(libc::strdup(s.as_ptr()))) - } + unsafe { CString::from_raw(util::must_succeed(libc::strdup(s.as_ptr()))) } } } diff --git a/safe_libc/src/util.rs b/safe_libc/src/util.rs index b8a4ed5..d37954c 100644 --- a/safe_libc/src/util.rs +++ b/safe_libc/src/util.rs @@ -1,18 +1,9 @@ -use core::mem; - -#[inline] -pub fn zst(len: usize) -> bool { - mem::size_of::() == 0 || len == 0 -} - -#[inline] -pub fn check_ptr(p: *const T, len: usize) { - debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); - assert!(zst::(len) || !p.is_null(), "NULL ptr"); -} +use alloc::alloc; #[inline] pub fn must_succeed(p: *mut T) -> *mut T { - assert!(!p.is_null(), "allocation failure"); + if p.is_null() { + alloc::handle_alloc_error(alloc::Layout::new::()) + } p } diff --git a/src/main.rs b/src/main.rs index c79f1da..4283396 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,39 +1,53 @@ #![no_main] #![no_std] +#![feature(alloc_error_handler)] -use safe_libc as libc; +mod system_alloc; + +extern crate alloc; use libc::cstr; +use safe_libc as libc; use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let mut stdout = unsafe { libc::stdio::stdout() }; + let mut stdout = libc::stdio::stdout(); { let foo = cstr!("Foo!\n"); - stdout.puts(foo); + let _ = stdout.puts(foo); } { let x = libc::string::CString::from("foo"); let l = x.len(); let y = x.into_raw(); - let z = unsafe { - libc::boxed::CBox::slice_from_raw_parts(y, l) - }; + let z = unsafe { alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut(y, l)) }; let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); } { - let b = libc::boxed::CBox::new(42); + let x = libc::string::CString::from("bar"); + let _ = stdout.puts(&x); + } + + { + let b = alloc::boxed::Box::new(42); let _ = writeln!(stdout, "Bar: {}", b); } + + { + let x = alloc::boxed::Box::new(()); + let _ = writeln!(stdout, "Box: {:?}", x); + } + 0 } +#[cfg(not(test))] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info); + let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info); unsafe { libc::abort() } } diff --git a/src/system_alloc.rs b/src/system_alloc.rs new file mode 100644 index 0000000..7c0bac9 --- /dev/null +++ b/src/system_alloc.rs @@ -0,0 +1,22 @@ +use safe_libc as libc; + +struct System; + +unsafe impl alloc::alloc::GlobalAlloc for System { + unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 { + libc::memalign(layout.align(), layout.size()).cast() + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: alloc::alloc::Layout) { + libc::free(ptr.cast()); + } +} + +#[global_allocator] +static SYSTEM_ALLOC: System = System; + +#[cfg(not(test))] +#[alloc_error_handler] +fn alloc_error(_: core::alloc::Layout) -> ! { + panic!("allocation failure"); +}