getenv works

This commit is contained in:
nora 2023-09-29 23:04:49 +02:00
parent 0c69d7db10
commit 9464ea4829
7 changed files with 206 additions and 33 deletions

View file

@ -1,35 +1,38 @@
use core::ffi::CStr;
use core::{
ffi::{c_char, CStr},
iter,
ptr::NonNull,
};
use crate::{println, utils::SharedThinCstr};
mod global {
use core::cell::UnsafeCell;
use core::{cell::UnsafeCell, ptr::NonNull};
use crate::utils::{SharedThinCstr, SyncUnsafeCell};
use super::EnvP;
static ENVP: SyncUnsafeCell<EnvP> =
SyncUnsafeCell(UnsafeCell::new(EnvP(core::ptr::null_mut())));
static ENVP: SyncUnsafeCell<EnvP> = SyncUnsafeCell(UnsafeCell::new(EnvP(None)));
pub(super) unsafe fn init(envp: *mut SharedThinCstr) {
assert!((*ENVP.0.get()).0.is_null());
*ENVP.0.get() = EnvP(envp);
pub(super) unsafe fn init(envp: *mut Option<SharedThinCstr>) {
assert!((*ENVP.0.get()).0.is_none());
*ENVP.0.get() = EnvP(Some(NonNull::new(envp).unwrap()));
}
pub(super) fn get() -> EnvP {
let ptr = unsafe { *ENVP.0.get() };
assert!(!ptr.0.is_null());
assert!(ptr.0.is_some());
ptr
}
}
pub(crate) unsafe fn init(envp: *mut SharedThinCstr) {
pub(crate) unsafe fn init(envp: *mut Option<SharedThinCstr>) {
global::init(envp);
}
#[derive(Clone, Copy)]
struct EnvP(*mut SharedThinCstr);
struct EnvP(Option<NonNull<Option<SharedThinCstr>>>);
unsafe impl Sync for EnvP {}
@ -38,13 +41,12 @@ impl Iterator for EnvP {
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let value: SharedThinCstr = self.0.read();
if value.0.is_null() {
None
} else {
self.0 = self.0.add(1);
Some(value)
}
let value: Option<SharedThinCstr> = self.0.unwrap().as_ptr().read();
value.map(|value| {
self.0 = Some(NonNull::new_unchecked(self.0.unwrap().as_ptr().add(1)));
value
})
}
}
}
@ -57,3 +59,99 @@ pub(crate) fn debug_env() {
});
println!("end vars");
}
pub fn getenv(name: SharedThinCstr) -> Option<SharedThinCstr> {
getenv_inner(global::get(), name)
}
fn getenv_inner(mut envp: EnvP, name: SharedThinCstr) -> Option<SharedThinCstr> {
let mut eq_idx = 0;
envp.find(|env| {
// Find ENV
// EN=x
// ENV=x <- this one
// ENVNO=x
let mut name_iter = name.into_iter();
let mut env_iter = env.into_iter();
eq_idx = 0;
loop {
let name = name_iter.next().map(|c| c as u8);
let env = env_iter.next().map(|c| c as u8);
if let (None, Some(b'=')) = (name, env) {
return true;
}
if name.is_none() || env == Some(b'=') {
return false;
}
eq_idx += 1;
}
})
.map(|elem| {
let value_idx = eq_idx + 1;
unsafe { elem.add(value_idx) }
})
}
#[cfg(test)]
mod tests {
use core::ptr::NonNull;
use std::string::ToString;
use std::{ffi::CString, vec::Vec};
use crate::utils::SharedThinCstr;
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)) {
let cstrs = env
.iter()
.map(|s| CString::new(s.to_string()).unwrap())
.collect::<Vec<_>>();
let mut envs: Vec<Option<SharedThinCstr>> = cstrs
.iter()
.map(|cstr| unsafe {
Some(SharedThinCstr::from_ptr(
NonNull::new(cstr.as_ptr() as _).unwrap(),
))
})
.collect();
envs.push(None);
let envp = EnvP(Some(NonNull::new(envs.as_ptr() as _).unwrap()));
f(envp)
}
#[test]
fn getenv_exact_first() {
with_envp(&["UWU=a"], |envp| {
assert_eq!(
super::getenv_inner(envp, cstr("UWU\0")).unwrap(),
cstr("a\0")
);
})
}
#[test]
fn getenv_previous_mismatches() {
with_envp(&["UW=a", "UWUU=b", "UWU=c"], |envp| {
assert_eq!(
super::getenv_inner(envp, cstr("UWU\0")).unwrap(),
cstr("c\0")
);
})
}
#[test]
fn getenv_name_long() {
with_envp(&["U=w"], |envp| {
assert_eq!(super::getenv_inner(envp, cstr("LONG_NAME\0")), None);
})
}
}

View file

@ -1,13 +1,16 @@
#![no_std]
#![warn(unreachable_pub)]
#[cfg(test)]
extern crate std;
mod basic_mem;
mod env;
pub mod env;
pub mod io;
pub mod start;
mod stubs;
mod sys;
mod utils;
pub mod utils;
pub mod syscall {
pub use crate::sys::syscall::*;

View file

@ -5,7 +5,7 @@ use crate::utils::SharedThinCstr;
/// The entrypoint of the program.
/// This is called by a bit of assembly handling architecture-specific _start.
pub(crate) unsafe extern "C" fn start(argc: u64, argv: *const *const c_char, rsp: u64) -> ! {
let envp = (8 + 8 * argc + rsp + 8) as *mut SharedThinCstr;
let envp = (8 + 8 * argc + rsp + 8) as *mut Option<SharedThinCstr>;
crate::env::init(envp);
@ -13,7 +13,7 @@ pub(crate) unsafe extern "C" fn start(argc: u64, argv: *const *const c_char, rsp
fn main(argc: c_int, argv: *const *const c_char) -> c_int;
}
crate::env::debug_env();
// crate::env::debug_env();
let result = main(argc as i32, argv);

View file

@ -1,7 +0,0 @@
// libcore seems to require this symbol, even though it's unused.
#[no_mangle]
fn rust_eh_personality() {
unsafe {
crate::sys::helpers::trap!();
}
}

View file

@ -1,3 +1,4 @@
#[macro_export]
macro_rules! trap {
() => {
::core::arch::asm!("ud2");

View file

@ -1,6 +1,8 @@
use core::{
cell::UnsafeCell,
ffi::{c_char, CStr},
fmt::Debug,
ptr::NonNull,
};
#[repr(transparent)]
@ -9,18 +11,84 @@ pub(crate) struct SyncUnsafeCell<T>(pub(crate) UnsafeCell<T>);
unsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub(crate) struct SharedThinCstr(pub(crate) *const c_char);
pub struct SharedThinCstr(pub NonNull<c_char>);
impl SharedThinCstr {
pub unsafe fn from_ptr(ptr: NonNull<c_char>) -> Self {
Self(ptr)
}
pub fn from_array<const N: usize>(arr: &[u8; N]) -> Self {
assert!(arr[N - 1] == 0);
unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) }
}
pub fn as_ptr(self) -> NonNull<c_char> {
self.0
}
pub unsafe fn add(self, amount: usize) -> Self {
Self(NonNull::new_unchecked(self.0.as_ptr().add(amount)))
}
}
impl IntoIterator for SharedThinCstr {
type Item = c_char;
type IntoIter = CStrIter;
fn into_iter(self) -> Self::IntoIter {
CStrIter(self)
}
}
pub struct CStrIter(SharedThinCstr);
impl Iterator for CStrIter {
type Item = c_char;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let c = self.0 .0.as_ptr().read();
if c == 0 {
return None;
}
self.0 = self.0.add(1);
Some(c)
}
}
}
unsafe impl Send for SharedThinCstr {}
unsafe impl Sync for SharedThinCstr {}
impl From<SharedThinCstr> for &CStr {
fn from(value: SharedThinCstr) -> Self {
unsafe { CStr::from_ptr(value.0) }
impl Debug for SharedThinCstr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let str = <&CStr>::from(*self).to_str();
match str {
Ok(str) => f.write_str(str),
Err(_) => f.write_str("<invalid UTF-8>"),
}
}
}
impl From<SharedThinCstr> for &CStr {
fn from(value: SharedThinCstr) -> Self {
unsafe { CStr::from_ptr(value.0.as_ptr()) }
}
}
impl PartialEq for SharedThinCstr {
fn eq(&self, other: &Self) -> bool {
self.into_iter().eq(other.into_iter())
}
}
impl Eq for SharedThinCstr {}
#[repr(transparent)]
pub(crate) struct SyncPtr<T>(pub(crate) *mut T);