This commit is contained in:
nora 2023-09-30 18:54:43 +02:00
parent ae189e8b18
commit 043c960708
15 changed files with 206 additions and 23 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target /target
/example-user/target /example-user/target
/tests/target
/.vscode /.vscode

View file

@ -1,6 +1,8 @@
#include<stdio.h> #include<stdio.h>
#include<sys/mman.h>
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
PROT_WRITE; int result = printf("Hello, world!\n");
puts("Hello, world!"); if (result != 15) {
return 1;
}
} }

View file

@ -107,6 +107,7 @@ pub unsafe fn mmap(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]
#[ignore = "uh"]
fn malloc_free() { fn malloc_free() {
unsafe { unsafe {
let x = super::malloc_zeroed(10, 8); let x = super::malloc_zeroed(10, 8);

View file

@ -99,15 +99,10 @@ mod tests {
use std::string::ToString; use std::string::ToString;
use std::{ffi::CString, vec::Vec}; use std::{ffi::CString, vec::Vec};
use crate::utils::SharedThinCstr; use crate::utils::{cstr, SharedThinCstr};
use super::EnvP; use super::EnvP;
fn cstr(s: &str) -> SharedThinCstr {
assert_eq!(s.as_bytes()[s.len() - 1], 0);
unsafe { SharedThinCstr::from_ptr(NonNull::new(s.as_ptr() as _).unwrap()) }
}
fn with_envp(env: &[&str], f: impl FnOnce(EnvP)) { fn with_envp(env: &[&str], f: impl FnOnce(EnvP)) {
let cstrs = env let cstrs = env
.iter() .iter()

1
libuwuc/src/fmt/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod printf;

52
libuwuc/src/fmt/printf.rs Normal file
View file

@ -0,0 +1,52 @@
use core::ffi::VaList;
use crate::{io::IoWrite, utils::SharedThinCstr};
pub unsafe fn printf_generic(
mut sink: impl IoWrite,
format: SharedThinCstr,
args: VaList<'_, '_>,
) -> Result<(), i32> {
let mut chars = format.into_iter();
while let Some(c) = chars.next() {
if c == b'%' {
todo!();
}
sink.write_byte(c)?;
}
Ok(())
}
#[cfg(test)]
#[allow(improper_ctypes_definitions)]
mod tests {
extern crate std;
use std::string::String;
use std::vec::Vec;
use crate::utils::{cstr, SharedThinCstr};
use super::printf_generic;
unsafe extern "C" fn test_printf(expected: &str, fmt: SharedThinCstr, mut args: ...) {
let mut sink = Vec::new();
printf_generic(&mut sink, fmt, args.as_va_list()).unwrap();
let result = String::from_utf8(sink).unwrap();
assert_eq!(result, expected);
}
#[test]
fn empty_format() {
unsafe { test_printf("\0", cstr("\0")) }
}
#[test]
fn constant_string() {
unsafe { test_printf("hello, world\0", cstr("hello, world\0")) }
}
}

View file

@ -1,4 +1,7 @@
pub mod stream; pub mod stream;
pub mod traits;
pub use traits::IoWrite;
use core::ffi::c_char; use core::ffi::c_char;
@ -30,13 +33,19 @@ macro_rules! println {
} }
pub use println; pub use println;
pub unsafe fn write(fd: i32, buf: &[u8]) -> Result<usize, i32> {
let result = syscall::syscall!(syscall::SYS_WRITE, fd, buf.as_ptr(), buf.len()) as i64;
if result < 0 {
Err(result as _)
} else {
Ok(result as _)
}
}
pub unsafe fn write_all(fd: i32, mut buf: &[u8]) -> Result<(), i64> { pub unsafe fn write_all(fd: i32, mut buf: &[u8]) -> Result<(), i64> {
while !buf.is_empty() { while !buf.is_empty() {
let result = syscall::syscall!(syscall::SYS_WRITE, fd, buf.as_ptr(), buf.len()) as i64; let result = write(fd, buf)?;
if result < 0 { buf = &buf[result..];
return Err(result);
}
buf = &buf[(result as usize)..];
} }
Ok(()) Ok(())
} }

View file

@ -1,6 +1,6 @@
use core::ffi::c_int; use core::ffi::c_int;
use super::EOF; use super::{IoWrite, EOF};
/// A `FILE`. /// A `FILE`.
#[repr(C)] #[repr(C)]
@ -18,6 +18,12 @@ impl FileStream {
} }
} }
impl IoWrite for &FileStream {
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
unsafe { super::write(self.fd, buf) }
}
}
pub fn fputc(c: u8, stream: &FileStream) -> i32 { pub fn fputc(c: u8, stream: &FileStream) -> i32 {
match stream.write_byte(c) { match stream.write_byte(c) {
Ok(_) => c as _, Ok(_) => c as _,

46
libuwuc/src/io/traits.rs Normal file
View file

@ -0,0 +1,46 @@
pub trait IoWrite {
fn write(&mut self, buf: &[u8]) -> Result<usize, i32>;
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), i32> {
while !buf.is_empty() {
let n = self.write(buf)?;
buf = &buf[n..];
}
Ok(())
}
fn write_byte(&mut self, byte: u8) -> Result<(), i32> {
self.write_all(&[byte])
}
}
impl<W: IoWrite> IoWrite for &mut W {
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
W::write(self, buf)
}
}
pub struct WriteCounter<W>(pub W, pub usize);
impl<W: IoWrite> IoWrite for WriteCounter<W> {
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
let n = self.0.write(buf)?;
self.1 += n;
Ok(n)
}
}
#[cfg(test)]
mod test_impls {
extern crate std;
use super::IoWrite;
use std::vec::Vec;
impl IoWrite for Vec<u8> {
fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
self.extend(buf);
Ok(buf.len())
}
}
}

View file

@ -1,6 +1,6 @@
#![no_std] #![no_std]
#![feature(c_variadic)]
#![warn(unreachable_pub)] #![warn(unreachable_pub)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#[cfg(test)] #[cfg(test)]
@ -8,6 +8,7 @@ extern crate std;
pub mod alloc; pub mod alloc;
pub mod env; pub mod env;
pub mod fmt;
pub mod io; pub mod io;
pub mod mem; pub mod mem;
pub mod start; pub mod start;

View file

@ -20,6 +20,10 @@ impl SharedThinCstr {
Self(ptr) Self(ptr)
} }
pub unsafe fn from_raw(ptr: *const c_char) -> Self {
Self(NonNull::new_unchecked(ptr as _))
}
pub fn from_array<const N: usize>(arr: &[u8; N]) -> Self { pub fn from_array<const N: usize>(arr: &[u8; N]) -> Self {
assert!(arr[N - 1] == 0); assert!(arr[N - 1] == 0);
unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) } unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) }
@ -35,7 +39,7 @@ impl SharedThinCstr {
} }
impl IntoIterator for SharedThinCstr { impl IntoIterator for SharedThinCstr {
type Item = c_char; type Item = u8;
type IntoIter = CStrIter; type IntoIter = CStrIter;
@ -47,7 +51,7 @@ impl IntoIterator for SharedThinCstr {
pub struct CStrIter(SharedThinCstr); pub struct CStrIter(SharedThinCstr);
impl Iterator for CStrIter { impl Iterator for CStrIter {
type Item = c_char; type Item = u8;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
unsafe { unsafe {
@ -57,7 +61,7 @@ impl Iterator for CStrIter {
} }
self.0 = self.0.add(1); self.0 = self.0.add(1);
Some(c) Some(c as u8)
} }
} }
} }
@ -89,6 +93,12 @@ impl PartialEq for SharedThinCstr {
impl Eq for SharedThinCstr {} impl Eq for SharedThinCstr {}
pub fn cstr(s: &str) -> SharedThinCstr {
assert_eq!(s.as_bytes()[s.len() - 1], 0);
unsafe { SharedThinCstr::from_ptr(NonNull::new(s.as_ptr() as _).unwrap()) }
}
#[repr(transparent)] #[repr(transparent)]
pub(crate) struct SyncPtr<T>(pub(crate) *mut T); pub(crate) struct SyncPtr<T>(pub(crate) *mut T);

View file

@ -1,4 +1,5 @@
#![no_std] #![no_std]
#![feature(c_variadic)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![deny(clippy::no_mangle_with_rust_abi)] #![deny(clippy::no_mangle_with_rust_abi)]

View file

@ -1,12 +1,33 @@
use core::ffi::{c_char, c_int}; use core::ffi::{c_char, c_int};
use libuwuc::io::{stream::FileStream, STDERR, STDIN, STDOUT}; use libuwuc::{
io::{stream::FileStream, traits::WriteCounter, STDERR, STDIN, STDOUT},
utils::SharedThinCstr,
};
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn puts(s: *const c_char) -> i32 { pub unsafe extern "C" fn puts(s: *const c_char) -> i32 {
libuwuc::io::puts(s) libuwuc::io::puts(s)
} }
// PRINTF:
#[no_mangle]
pub unsafe extern "C" fn __printf_chk(_flag: c_int, format: *const c_char, mut args: ...) -> c_int {
let mut sink = WriteCounter(stdout, 0);
let result = libuwuc::fmt::printf::printf_generic(
&mut sink,
SharedThinCstr::from_raw(format),
args.as_va_list(),
);
match result {
Ok(()) => sink.1 as _,
Err(err) => err,
}
}
// STREAMS: // STREAMS:
#[no_mangle] #[no_mangle]

View file

@ -1,5 +1,34 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cargo build SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
./uwuc-gcc hello.c -o target/hello
./target/hello cargo build --manifest-path "$SCRIPT_DIR/Cargo.toml"
test_dir=$(mktemp -d)
clean() {
rm -r "${test_dir}"
}
for test in tests/c/*; do
name=$(basename $test .c)
"$SCRIPT_DIR/uwuc-gcc" "$test" -o "$test_dir/$name"
if [ "$?" -ne "0" ]; then
echo "error: failed to compile test $test"
clean
exit 1
fi
OUTPUT=$("$test_dir/$name")
code="$?"
if [ "$code" -ne "0" ]; then
echo "error: test failed with code $code: $test, running $test_dir/$name"
echo "------ output:"
echo "$OUTPUT"
echo "-----"
fi
done
clean

8
tests/c/simple_printf.c Normal file
View file

@ -0,0 +1,8 @@
#include<stdio.h>
int main(int argc, char *argv[]) {
int result = printf("Hello, world!\n");
if (result != 14) {
return 1;
}
}