Compare commits

..

No commits in common. "58260af4d8afd2079a50f2444e8bf721044d7a35" and "fff906a78ba545e77d087a82711b30bcc8e0d0c5" have entirely different histories.

12 changed files with 239 additions and 266 deletions

4
Cargo.lock generated
View file

@ -2,9 +2,9 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.71" version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
[[package]] [[package]]
name = "neco" name = "neco"

View file

@ -1 +0,0 @@
hard_tabs = true

14
safe_libc/Cargo.lock generated Normal file
View file

@ -0,0 +1,14 @@
# 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",
]

View file

@ -7,4 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
libc = { version = "0.2.71", default-features = false } libc = { version = "0.2.68", default-features = false }

View file

@ -1,78 +1,150 @@
use core::{marker, mem, ptr}; use crate::util;
use core::{fmt, mem, ptr};
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
pub trait Dealloc<T: ?Sized> { fn alloc<T>(len: usize) -> *mut T {
unsafe fn dealloc(p: *mut T); if util::zst::<T>(len) {
return ptr::null_mut();
}
let size = len.checked_mul(mem::size_of::<T>()).expect("allocation overflow");
let align = mem::align_of::<T>();
util::must_succeed(
unsafe {
libc::memalign(align as libc::size_t, size as libc::size_t)
}
).cast()
} }
#[repr(transparent)] #[inline]
pub struct BoxLike<T: ?Sized, D: Dealloc<T>> { const fn dangling<T>() -> *mut T {
inner: ptr::NonNull<T>, mem::align_of::<T>() as *mut T
_phantom: marker::PhantomData<T>,
_dropper: marker::PhantomData<D>,
} }
impl<T: ?Sized, D: Dealloc<T>> BoxLike<T, D> { #[inline]
fn slice_len<T>(p: *const [T]) -> usize {
unsafe { mem::transmute::<_, [usize; 2]>(p)[1] }
}
pub trait SafePtr {
fn safe_ptr(p: *mut Self) -> *mut Self;
}
impl<T> SafePtr for T {
#[inline] #[inline]
pub unsafe fn from_raw_unchecked(p: *mut T) -> Self { fn safe_ptr(p: *mut T) -> *mut T {
BoxLike { if util::zst::<T>(1) {
inner: ptr::NonNull::new_unchecked(p), return dangling();
_phantom: marker::PhantomData,
_dropper: marker::PhantomData,
}
} }
#[inline] debug_assert!(!p.is_null(), "NULL ptr");
pub unsafe fn from_raw(p: *mut T) -> Option<Self> {
if p.is_null() {
None
} else {
Some(Self::from_raw_unchecked(p))
}
}
#[inline]
pub fn into_raw(self) -> *mut T {
let p = self.inner.as_ptr();
mem::forget(self);
p p
} }
} }
impl<T: ?Sized, D: Dealloc<T>> Drop for BoxLike<T, D> { impl<T> SafePtr for [T] {
#[inline]
fn safe_ptr(p: *mut [T]) -> *mut [T] {
let len = slice_len(p);
if util::zst::<T>(len) {
return ptr::slice_from_raw_parts_mut(dangling(), len);
}
debug_assert!(!p.is_null(), "NULL ptr");
p
}
}
pub struct CBox<T: SafePtr + ?Sized>(*mut T);
impl<T: SafePtr + ?Sized> CBox<T> {
#[inline]
pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox<T> {
CBox(p)
}
#[inline]
pub fn into_raw(self) -> *mut T {
let p = self.0;
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<T> CBox<T> {
#[inline]
pub fn new(value: T) -> CBox<T> {
let p = alloc(1);
unsafe {
ptr::write(p, value);
CBox::from_raw_unchecked(p)
}
}
#[inline]
pub unsafe fn from_raw(p: *mut T) -> CBox<T> {
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<T: SafePtr + ?Sized> Drop for CBox<T> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
let p = self.inner.as_ptr();
unsafe { unsafe {
ptr::drop_in_place(p); ptr::drop_in_place(self.0);
D::dealloc(p); libc::free(self.0.cast());
} }
} }
} }
impl<T: ?Sized, D: Dealloc<T>> Deref for BoxLike<T, D> { impl<T: SafePtr + ?Sized> Deref for CBox<T> {
type Target = T; type Target = T;
#[inline] #[inline]
fn deref(&self) -> &T { fn deref(&self) -> &T {
unsafe { self.inner.as_ref() } unsafe { &*T::safe_ptr(self.0) }
} }
} }
impl<T: ?Sized, D: Dealloc<T>> DerefMut for BoxLike<T, D> { impl<T: SafePtr + ?Sized> DerefMut for CBox<T> {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
unsafe { self.inner.as_mut() } unsafe { &mut *T::safe_ptr(self.0) }
} }
} }
pub struct DeallocFree; impl<T: fmt::Debug + SafePtr + ?Sized> fmt::Debug for CBox<T> {
impl<T: ?Sized> Dealloc<T> for DeallocFree {
#[inline] #[inline]
unsafe fn dealloc(p: *mut T) { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
libc::free(p as _); fmt::Debug::fmt(&**self, f)
} }
} }
pub type CBox<T> = BoxLike<T, DeallocFree>; impl<T: fmt::Display + SafePtr + ?Sized> fmt::Display for CBox<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}

View file

@ -1,34 +0,0 @@
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),
}
}
}

View file

@ -1,20 +1,17 @@
#![no_std] #![no_std]
#![feature(extern_types)]
#![feature(slice_ptr_len)]
#![feature(ffi_const)]
extern crate alloc;
pub use libc::*; pub use libc::*;
mod util; mod util;
pub mod boxed; pub mod boxed;
pub mod errno;
pub mod stdio; pub mod stdio;
pub mod string; pub mod string;
extern "C" { 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 stdin: *mut libc::FILE;
pub static stdout: *mut libc::FILE; pub static stdout: *mut libc::FILE;
pub static stderr: *mut libc::FILE; pub static stderr: *mut libc::FILE;

View file

@ -1,104 +1,47 @@
use crate::{self as libc, errno, string}; use crate as libc;
use crate::string;
use core::fmt; use core::fmt;
use core::ops::{Deref, DerefMut};
pub struct Error { pub struct OStream {
pub errno: errno::Errno, file: *mut libc::FILE
}
impl From<Error> for fmt::Error {
fn from(_err: Error) -> fmt::Error {
fmt::Error
}
}
pub type Result<T> = core::result::Result<T, Error>;
#[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] #[inline]
pub fn stderr() -> Stderr { pub unsafe fn stdout() -> OStream {
BasicOStream(unsafe { libc::stderr }) OStream { file: libc::stdout }
} }
impl BasicOStream { #[inline]
#[inline] pub unsafe fn stderr() -> OStream {
pub fn flush(&mut self) -> Result<()> { OStream { file: libc::stderr }
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())?;
Ok(())
}
}
pub struct OStream(BasicOStream);
impl OStream { impl OStream {
#[inline] #[inline]
pub unsafe fn from_raw(file: *mut libc::FILE) -> OStream { pub fn write(&mut self, b: &[u8]) {
OStream(BasicOStream(file))
}
}
impl Drop for OStream {
#[inline]
fn drop(&mut self) {
unsafe { unsafe {
libc::fclose((self.0).0); libc::fwrite(
b.as_ptr().cast(),
1,
b.len(),
self.file,
);
} }
} }
}
impl Deref for OStream {
type Target = BasicOStream;
#[inline] #[inline]
fn deref(&self) -> &BasicOStream { pub fn puts(&mut self, s: &string::CStr) {
&self.0 unsafe {
libc::fputs(s.as_ptr(), self.file);
}
} }
} }
impl DerefMut for OStream { impl fmt::Write for OStream {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut BasicOStream { fn write_str(&mut self, s: &str) -> fmt::Result {
&mut self.0 self.write(s.as_bytes());
Ok(())
} }
} }

View file

@ -1,31 +1,40 @@
use crate::{self as libc, boxed::CBox, util}; use crate as libc;
use crate::util;
use crate::boxed::CBox;
use core::{mem, slice}; use core::slice;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
//pub struct FromBytesWithNulError {} //pub struct FromBytesWithNulError {}
extern "C" { pub struct CStr { inner: libc::c_char }
type RawCStr;
}
#[repr(transparent)]
pub struct CStr(RawCStr);
impl CStr { impl CStr {
#[inline]
pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr {
&*p.cast()
}
#[inline] #[inline]
pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr {
&*(p as *const 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()
} }
#[inline] #[inline]
pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr {
&mut *(p as *mut CStr) util::check_ptr(p, 1);
CStr::from_mut_ptr_unchecked(p)
} }
#[inline] #[inline]
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
CStr::from_ptr(bytes.as_ptr().cast()) CStr::from_ptr_unchecked(bytes.as_ptr().cast())
} }
// TODO // TODO
@ -34,37 +43,33 @@ impl CStr {
#[inline] #[inline]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
unsafe { libc::strlen(self.as_ptr()) } unsafe { libc::strlen(self.as_ptr()) as usize }
} }
#[inline] #[inline]
pub const fn as_ptr(&self) -> *const libc::c_char { pub const fn as_ptr(&self) -> *const libc::c_char {
(self as *const CStr).cast() &self.inner
} }
#[inline] #[inline]
pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { pub fn as_mut_ptr(&mut self) -> *mut libc::c_char {
(self as *mut CStr).cast() &mut self.inner
} }
#[inline] #[inline]
pub fn to_bytes(&self) -> &[u8] { pub fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr().cast(), self.len()) } unsafe { slice::from_raw_parts(
self.as_ptr().cast(),
self.len(),
) }
} }
#[inline] #[inline]
pub fn to_mut_bytes(&mut self) -> &mut [u8] { pub fn as_mut_bytes(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) } 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] #[inline]
@ -75,28 +80,28 @@ impl CStr {
#[macro_export] #[macro_export]
macro_rules! cstr { macro_rules! cstr {
($s:expr) => { ($s:expr) => (
unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) }
}; )
} }
pub struct CString { pub struct CString { inner: CBox<libc::c_char> }
inner: CBox<CStr>,
}
impl CString { impl CString {
#[inline] #[inline]
pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString {
CString { CString { inner: CBox::from_raw_unchecked(p) }
inner: CBox::from_raw_unchecked(p as *mut CStr)
}
} }
#[inline] #[inline]
pub fn into_raw(mut self) -> *mut libc::c_char { pub unsafe fn from_raw(p: *mut libc::c_char) -> CString {
let p = self.as_mut_ptr(); util::check_ptr(p, 1);
mem::forget(self); CString::from_raw_unchecked(p)
p }
#[inline]
pub fn into_raw(self) -> *mut libc::c_char {
self.inner.into_raw()
} }
} }
@ -105,24 +110,26 @@ impl Deref for CString {
#[inline] #[inline]
fn deref(&self) -> &CStr { fn deref(&self) -> &CStr {
&*self.inner unsafe { CStr::from_ptr(&*self.inner) }
} }
} }
impl DerefMut for CString { impl DerefMut for CString {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut CStr { fn deref_mut(&mut self) -> &mut CStr {
&mut *self.inner unsafe { CStr::from_mut_ptr(&mut *self.inner) }
} }
} }
impl From<&[u8]> for CString { impl From<&[u8]> for CString {
fn from(s: &[u8]) -> CString { fn from(s: &[u8]) -> CString {
unsafe { unsafe {
CString::from_raw(util::must_succeed(libc::strndup( CString::from_raw_unchecked(
util::must_succeed(libc::strndup(
s.as_ptr().cast(), s.as_ptr().cast(),
s.len(), s.len() as libc::size_t,
))) ))
)
} }
} }
} }
@ -137,6 +144,8 @@ impl From<&str> for CString {
impl From<&CStr> for CString { impl From<&CStr> for CString {
#[inline] #[inline]
fn from(s: &CStr) -> CString { fn from(s: &CStr) -> CString {
unsafe { CString::from_raw(util::must_succeed(libc::strdup(s.as_ptr()))) } unsafe {
CString::from_raw_unchecked(util::must_succeed(libc::strdup(s.as_ptr())))
}
} }
} }

View file

@ -1,9 +1,18 @@
use alloc::alloc; use core::mem;
#[inline]
pub fn zst<T>(len: usize) -> bool {
mem::size_of::<T>() == 0 || len == 0
}
#[inline]
pub fn check_ptr<T>(p: *const T, len: usize) {
debug_assert!((p as usize) % mem::align_of::<T>() == 0, "unaligned ptr");
assert!(zst::<T>(len) || !p.is_null(), "NULL ptr");
}
#[inline] #[inline]
pub fn must_succeed<T>(p: *mut T) -> *mut T { pub fn must_succeed<T>(p: *mut T) -> *mut T {
if p.is_null() { assert!(!p.is_null(), "allocation failure");
alloc::handle_alloc_error(alloc::Layout::new::<T>())
}
p p
} }

View file

@ -1,53 +1,39 @@
#![no_main] #![no_main]
#![no_std] #![no_std]
#![feature(alloc_error_handler)]
mod system_alloc;
extern crate alloc;
use libc::cstr;
use safe_libc as libc; use safe_libc as libc;
use libc::cstr;
use core::fmt::Write; use core::fmt::Write;
#[no_mangle] #[no_mangle]
pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int {
let mut stdout = libc::stdio::stdout(); let mut stdout = unsafe { libc::stdio::stdout() };
{ {
let foo = cstr!("Foo!\n"); let foo = cstr!("Foo!\n");
let _ = stdout.puts(foo); stdout.puts(foo);
} }
{ {
let x = libc::string::CString::from("foo"); let x = libc::string::CString::from("foo");
let l = x.len(); let l = x.len();
let y = x.into_raw(); let y = x.into_raw();
let z = unsafe { alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut(y, l)) }; let z = unsafe {
libc::boxed::CBox::slice_from_raw_parts(y, l)
};
let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]);
} }
{ {
let x = libc::string::CString::from("bar"); let b = libc::boxed::CBox::new(42);
let _ = stdout.puts(&x);
}
{
let b = alloc::boxed::Box::new(42);
let _ = writeln!(stdout, "Bar: {}", b); let _ = writeln!(stdout, "Bar: {}", b);
} }
{
let x = alloc::boxed::Box::new(());
let _ = writeln!(stdout, "Box: {:?}", x);
}
0 0
} }
#[cfg(not(test))]
#[panic_handler] #[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { fn panic(info: &core::panic::PanicInfo) -> ! {
let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info); let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info);
unsafe { libc::abort() } unsafe { libc::abort() }
} }

View file

@ -1,22 +0,0 @@
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");
}