libc: libc++ works

This commit is contained in:
Mark Poliakov 2024-11-15 20:37:59 +02:00
parent bc6a5b115c
commit 465fc53e02
49 changed files with 597 additions and 212 deletions

13
test.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <iostream>
int main() {
std::vector<int> items;
items.push_back(1);
items.push_back(2);
items.push_back(3);
for (const auto &item: items) {
std::cout << "Item: " << item << std::endl;
}
return 0;
}

View File

@ -54,10 +54,16 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> {
}
// Call global constructors before handing off control to the program
// .preinit_array, .preinit, ...
for (_, lib) in libraries.iter_mut() {
lib.call_constructors();
unsafe { lib.call_early_constructors() };
}
root.call_constructors();
unsafe { root.call_early_constructors() };
// .init_array, .init, ...
for (_, lib) in libraries.iter_mut() {
unsafe { lib.call_constructors() };
}
unsafe { root.call_constructors() };
let entry = root.entry().ok_or(Error::NoEntrypoint)?;
debug_trace!("entry = {:p}", entry);

View File

@ -1,14 +1,19 @@
use std::{
fs::File,
io::{BufReader, Read, Seek, SeekFrom},
mem,
ops::Range,
path::{Path, PathBuf},
ptr,
rc::Rc,
};
use elf::{
abi::{
DF_1_PIE, DT_FINI, DT_FINI_ARRAY, DT_FINI_ARRAYSZ, DT_FLAGS_1, DT_INIT, DT_INIT_ARRAY, DT_INIT_ARRAYSZ, DT_NEEDED, DT_PREINIT_ARRAY, DT_PREINIT_ARRAYSZ, ET_DYN, ET_EXEC, PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_INTERP, PT_LOAD, PT_NOTE, PT_NULL, PT_PHDR, SHN_UNDEF, SHT_REL, SHT_RELA, STB_GLOBAL, STB_LOCAL, STB_WEAK
DF_1_PIE, DT_FINI, DT_FINI_ARRAY, DT_FINI_ARRAYSZ, DT_FLAGS_1, DT_INIT, DT_INIT_ARRAY,
DT_INIT_ARRAYSZ, DT_NEEDED, DT_PREINIT_ARRAY, DT_PREINIT_ARRAYSZ, ET_DYN, ET_EXEC,
PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_INTERP, PT_LOAD, PT_NOTE,
PT_NULL, PT_PHDR, SHN_UNDEF, SHT_REL, SHT_RELA, STB_GLOBAL, STB_LOCAL, STB_WEAK,
},
endian::AnyEndian,
symbol::Symbol,
@ -56,6 +61,7 @@ pub struct Object {
dynamic_symbol_array: Vec<DynamicSymbol>,
init_array: Option<Range<usize>>,
pre_init_array: Option<Range<usize>>,
}
impl ResolvedSymbol<'_> {
@ -144,7 +150,8 @@ impl Object {
mapping: None,
dynamic_symbol_array,
init_array: None
init_array: None,
pre_init_array: None,
})
}
@ -177,6 +184,8 @@ impl Object {
if let Some(dynamic) = self.elf.dynamic()? {
let mut dt_init_array = None;
let mut dt_init_array_sz = None;
let mut dt_preinit_array = None;
let mut dt_preinit_array_sz = None;
for dynamic in dynamic {
match dynamic.d_tag {
@ -186,12 +195,18 @@ impl Object {
DT_FINI_ARRAYSZ => todo!(),
DT_INIT => todo!(),
DT_FINI => todo!(),
DT_PREINIT_ARRAY => todo!(),
DT_PREINIT_ARRAYSZ => todo!(),
_ => ()
DT_PREINIT_ARRAY => dt_preinit_array = Some(dynamic.d_ptr()),
DT_PREINIT_ARRAYSZ => dt_preinit_array_sz = Some(dynamic.d_val()),
_ => (),
}
}
if let (Some(ptr), Some(size)) = (dt_preinit_array, dt_preinit_array_sz) {
debug_trace!("{:?}: DT_PREINIT_ARRAY: {:#x?}", self.path, ptr..ptr + size);
let ptr = ptr as i64 + mapping.base() as i64 - self.vma_start as i64;
let ptr = ptr as usize;
self.pre_init_array = Some(ptr..ptr + size as usize);
}
if let (Some(ptr), Some(size)) = (dt_init_array, dt_init_array_sz) {
// This address is subject to relocation
debug_trace!("{:?}: DT_INIT_ARRAY: {:#x?}", self.path, ptr..ptr + size);
@ -253,21 +268,32 @@ impl Object {
Ok(())
}
pub fn call_constructors(&mut self) {
unsafe fn call_constructor_list(range: Range<usize>) {
type Constructor = extern "C" fn();
if let Some(init_array) = self.init_array.as_ref() {
for slot in init_array.clone().step_by(size_of::<usize>()) {
let func = unsafe { core::ptr::with_exposed_provenance::<usize>(slot).read_unaligned() };
for slot in range.step_by(size_of::<usize>()) {
let func = ptr::with_exposed_provenance::<usize>(slot).read_unaligned();
if func == 0 || func == usize::MAX {
continue;
}
let func = unsafe { core::mem::transmute::<_, Constructor>(func) };
func();
if func == 0 || func == usize::MAX {
continue;
}
let func = mem::transmute::<_, Constructor>(func);
debug_trace!("ld: call constructor {func:p}");
func();
}
}
pub unsafe fn call_early_constructors(&mut self) {
if let Some(pre_init_array) = self.pre_init_array.as_ref() {
Self::call_constructor_list(pre_init_array.clone());
}
}
pub unsafe fn call_constructors(&mut self) {
if let Some(init_array) = self.init_array.as_ref() {
Self::call_constructor_list(init_array.clone());
}
}

View File

@ -1,6 +1,7 @@
use std::{
env,
fs::{self, DirEntry},
fs::{self, DirEntry, File},
io::Write,
path::{Path, PathBuf},
process::Command,
};
@ -66,6 +67,7 @@ fn compile_crt0(arch: &str, output_dir: impl AsRef<Path>) {
command
.arg(format!("--target={}-unknown-none", arch))
.arg("-nostdlib")
.arg("-fPIC")
.arg("-c")
.arg("-o")
.arg(output_dir.join("crt0.o"))
@ -76,14 +78,88 @@ fn compile_crt0(arch: &str, output_dir: impl AsRef<Path>) {
}
}
fn ctype_item(ch: u8) -> String {
let mut traits = vec![];
// TODO include vertical tab here
if ch.is_ascii_whitespace() {
traits.push("_ISspace");
}
if ch.is_ascii_graphic() || ch == b' ' {
traits.push("_ISprint");
}
if ch.is_ascii_control() {
traits.push("_IScntrl");
}
if ch.is_ascii_uppercase() {
traits.push("_ISupper");
}
if ch.is_ascii_lowercase() {
traits.push("_ISlower");
}
if ch.is_ascii_alphabetic() {
traits.push("_ISalpha");
}
if ch.is_ascii_digit() {
traits.push("_ISdigit");
}
if ch.is_ascii_punctuation() {
traits.push("_ISpunct");
}
if ch.is_ascii_hexdigit() {
traits.push("_ISxdigit");
}
if ch == b' ' || ch == b'\t' {
traits.push("_ISblank");
}
if traits.is_empty() {
"0,".to_owned()
} else {
let mut str = String::new();
for (i, t) in traits.iter().enumerate() {
if i != 0 {
str.push('|');
}
str.push_str(t);
}
str.push(',');
str
}
}
fn generate_ctype_table(output_dir: impl AsRef<Path>) {
// Table size: 384, __ctype_b_loc points to 128th element to allow the table to be indexed
// by a signed char or EOF (-1) as well as any unsigned char.
let mut output = File::create(output_dir.as_ref().join("ctype_b.rs")).unwrap();
output
.write_all(b"pub(crate) static __ctype_b_table: [c_ushort; 384] = [")
.unwrap();
for ch in 128..=255u8 {
let traits_string = ctype_item(ch);
output.write_all(traits_string.as_bytes()).unwrap();
}
for ch in 0..=255u8 {
let traits_string = ctype_item(ch);
output.write_all(traits_string.as_bytes()).unwrap();
}
output.write_all(b"];").unwrap();
}
fn main() {
let target = env::var("TARGET").expect("$TARGET is not set");
let profile = env::var("PROFILE").expect("$PROFILE is not set");
let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("$CARGO_CFG_TARGET_ARCH is not set");
let compile_output_dir = env::var("OUT_DIR").expect("$OUT_DIR is not set");
let output_dir = PathBuf::from(format!("target/{target}/{profile}"));
let header_output = output_dir.join("include");
generate_ctype_table(compile_output_dir);
compile_crt0(&arch, output_dir);
fs::read_dir("src/headers")

View File

@ -1,6 +1,7 @@
#include <stddef.h>
extern __attribute__((noreturn)) void __ygglibc_entry(const void *);
extern __attribute__((noreturn)) void __ygglibc_entry(const void *, const void *);
extern int main(int, const char **, const char **);
// // rust's shlib export breaks memset symbol
// void *memset(void *dst, int val, size_t len) __attribute__((weak)) {
@ -11,5 +12,5 @@ extern __attribute__((noreturn)) void __ygglibc_entry(const void *);
// }
[[noreturn]] void _start(const void *arg) {
__ygglibc_entry(arg);
__ygglibc_entry(main, arg);
}

View File

@ -0,0 +1,3 @@
#ifndef _YGGDRASIL_LIMITS_H
#define _YGGDRASIL_LIMITS_H 1
#endif

View File

@ -7,6 +7,10 @@ extern "C" {
[[noreturn]] void abort(void);
// No long double implementation yet
long double strtold(const char *s, char **endptr);
long double strtold_l(const char *s, char **endptr, locale_t locale);
#if defined(__cplusplus)
}
#endif

View File

@ -24,6 +24,9 @@
#define PRIu8 "hhu"
#define PRIx8 "hhx"
#define PRIdPTR "zd"
#define PRIxPTR "zx"
#define SCNo64 PRIo64
#define SCNd64 PRId64
#define SCNu64 PRIu64

View File

@ -0,0 +1,16 @@
#ifndef _MACHINE_ENDIAN_H
#define _MACHINE_ENDIAN_H 1
#if !defined(LITTLE_ENDIAN)
#define LITTLE_ENDIAN 1234
#endif
#if !defined(BIG_ENDIAN)
#define BIG_ENDIAN 4321
#endif
#if !defined(BYTE_ORDER)
#define BYTE_ORDER LITTLE_ENDIAN
#endif
#endif

View File

@ -1,6 +1,5 @@
use core::{
alloc::{GlobalAlloc, Layout},
cmp::Ordering,
ffi::c_void,
ptr::{self, null_mut, NonNull},
};
@ -73,7 +72,7 @@ unsafe fn get_allocation(ptr: NonNull<u8>) -> (NonNull<u8>, Layout) {
(real_ptr, layout)
}
pub fn c_alloc(size: usize, mut align: usize, zero: bool) -> EResult<NonNull<c_void>> {
pub fn c_alloc(size: usize, align: usize, zero: bool) -> EResult<NonNull<c_void>> {
// allocation layout:
// [ header ] [ ... ]
// header: 16 bytes
@ -85,6 +84,12 @@ pub fn c_alloc(size: usize, mut align: usize, zero: bool) -> EResult<NonNull<c_v
let layout = Layout::from_size_align(size, align).expect("Couldn't setup malloc() layout");
let ptr = YALLOC.lock().allocate(layout).e_ok_or(errno::ENOMEM)?;
if zero {
unsafe {
memset(ptr.cast().as_ptr(), 0, size);
}
}
unsafe {
ptr.cast::<usize>().write(size);
ptr.cast::<usize>().add(1).write(align);

View File

@ -1,87 +1,11 @@
use core::ffi::{c_int, c_void};
#[no_mangle]
unsafe extern "C" fn __cxa_begin_catch(_exception: *mut c_void) -> *mut c_void {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __cxa_end_catch() {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __cxa_pure_virtual() {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __cxa_guard_acquire(_guard: *mut u64) -> c_int {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __cxa_guard_release(_guard: *mut u64) {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __cxa_atexit(
_destructor: extern "C" fn(*mut c_void),
_arg: *mut c_void,
_dso_handle: *mut c_void,
) -> c_int {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __gxx_personality_v0() {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn _Unwind_Resume(_exception: *mut c_void) {
unimplemented!()
}
// void *operator new(size_t x)
#[no_mangle]
unsafe extern "C" fn _Znwm(_x: usize) -> *mut c_void {
unimplemented!()
}
// void *operator new(size_t x, std::align_val_t y)
#[no_mangle]
unsafe extern "C" fn _ZnwmSt11align_val_t(_x: usize, _y: usize) -> *mut c_void {
unimplemented!()
}
// void *operator new[](size_t x)
#[no_mangle]
unsafe extern "C" fn _Znam(_x: usize) -> *mut c_void {
unimplemented!()
}
// void operator delete(void *x)
#[no_mangle]
unsafe extern "C" fn _ZdlPv(_x: *mut c_void) {
unimplemented!()
}
// void operator delete(void *x, size_t y)
#[no_mangle]
unsafe extern "C" fn _ZdlPvm(_x: *mut c_void, _y: usize) {
unimplemented!()
}
// void operator delete(void *x, size_t y, std::align_val_t z)
#[no_mangle]
unsafe extern "C" fn _ZdlPvmSt11align_val_t(_x: *mut c_void, _y: usize) {
unimplemented!()
}
// void operator delete[](void *x)
#[no_mangle]
unsafe extern "C" fn _ZdaPv(_x: *mut c_void) {
unimplemented!()
yggdrasil_rt::debug_trace!("TODO: __cxa_atexit()");
0
}

View File

@ -1,4 +1,4 @@
#![allow(non_upper_case_globals)]
#![allow(non_upper_case_globals, static_mut_refs)]
use core::{
ffi::{c_char, CStr},
@ -14,10 +14,7 @@ use crate::{
error::EResult,
headers::{
errno,
string::{
mem::memcpy,
str::{strcpy, strlen},
},
string::{mem::memcpy, str::strlen},
},
util::PointerExt,
};
@ -62,7 +59,7 @@ unsafe fn reclaim_env() -> EResult<()> {
}
unsafe fn push_env(str: NonNull<c_char>) -> EResult<()> {
reclaim_env();
reclaim_env()?;
if shadow.try_reserve(1).is_err() {
return EResult::Err(errno::ENOMEM);
@ -168,7 +165,8 @@ pub unsafe fn remove_env(name: &[u8]) -> EResult<bool> {
return EResult::Err(errno::EINVAL);
}
reclaim_env();
reclaim_env()?;
for i in 0..shadow.len() {
let Some(entry) = NonNull::new(shadow[i]) else {
continue;

View File

@ -37,6 +37,8 @@ pub trait OptionExt<T> {
pub trait CResult {
const ERROR: Self;
type Inner;
fn into_inner(self) -> Self::Inner;
}
#[must_use = "EResult must be converted into its output type with proper errno set"]
@ -202,6 +204,11 @@ impl<T> CPtrResult<T> {
impl<T> CResult for CPtrResult<T> {
const ERROR: Self = Self(null_mut());
type Inner = *mut T;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl<T> FromResidual<EResult<Infallible>> for CPtrResult<T> {
@ -222,6 +229,11 @@ impl CIsizeResult {
impl CResult for CIsizeResult {
const ERROR: Self = Self(-1);
type Inner = isize;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl CIntZeroResult {
@ -230,6 +242,11 @@ impl CIntZeroResult {
impl CResult for CIntZeroResult {
const ERROR: Self = Self(-1);
type Inner = c_int;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl CEofResult {
@ -240,6 +257,11 @@ impl CEofResult {
impl CResult for CEofResult {
const ERROR: Self = Self(-1);
type Inner = c_int;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl CIntCountResult {
@ -250,6 +272,11 @@ impl CIntCountResult {
impl CResult for CIntCountResult {
const ERROR: Self = Self(-1);
type Inner = c_int;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl CFdResult {
@ -260,6 +287,11 @@ impl CFdResult {
impl CResult for CFdResult {
const ERROR: Self = Self(-1);
type Inner = c_int;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl COffsetResult {
@ -270,6 +302,11 @@ impl COffsetResult {
impl CResult for COffsetResult {
const ERROR: Self = Self(-1);
type Inner = off_t;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl CUsizeResult {
@ -280,6 +317,11 @@ impl CUsizeResult {
impl CResult for CUsizeResult {
const ERROR: Self = Self(0);
type Inner = usize;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl_from_residual!(

View File

@ -0,0 +1,25 @@
use core::ffi::c_ushort;
pub const _ISspace: c_ushort = 1 << 0;
pub const _ISprint: c_ushort = 1 << 1;
pub const _IScntrl: c_ushort = 1 << 2;
pub const _ISupper: c_ushort = 1 << 3;
pub const _ISlower: c_ushort = 1 << 4;
pub const _ISalpha: c_ushort = 1 << 5;
pub const _ISdigit: c_ushort = 1 << 6;
// regex_word: 1 << 7
pub const _ISpunct: c_ushort = 1 << 8;
pub const _ISxdigit: c_ushort = 1 << 9;
pub const _ISblank: c_ushort = 1 << 10;
include!(concat!(env!("OUT_DIR"), "/ctype_b.rs"));
// # define __isctype(c, type) \
// ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
static mut __ctype_b_ptr: *const c_ushort = unsafe { __ctype_b_table.as_ptr().add(128) };
#[no_mangle]
unsafe extern "C" fn __ctype_b_loc() -> *mut *mut c_ushort {
(&raw mut __ctype_b_ptr).cast()
}

View File

@ -1,7 +1,9 @@
use core::ffi::{c_int, c_short};
use core::ffi::c_int;
use super::locale::locale_t;
mod ctype_b;
#[no_mangle]
unsafe extern "C" fn isalnum(ch: c_int) -> c_int {
(ch < 0xFF && (ch as u8).is_ascii_alphanumeric()) as _
@ -159,8 +161,3 @@ unsafe extern "C" fn tolower_l(_ch: c_int, _locale: locale_t) -> c_int {
unsafe extern "C" fn toupper_l(_ch: c_int, _locale: locale_t) -> c_int {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn __ctype_b_loc() -> *mut *mut c_short {
todo!()
}

View File

@ -0,0 +1,12 @@
language = "C"
style = "Type"
sys_includes = [
"bits/limits.h"
]
no_includes = true
include_guard = "_LIMITS_H"
usize_type = "size_t"
isize_type = "ssize_t"

View File

@ -0,0 +1 @@
pub const PATH_MAX: usize = 4096;

View File

@ -0,0 +1,12 @@
language = "C"
style = "Type"
sys_includes = [
"stddef.h",
]
no_includes = true
include_guard = "_LINK_H"
usize_type = "size_t"
isize_type = "ssize_t"

View File

@ -3,6 +3,8 @@ use core::{
ptr::null_mut,
};
use alloc::boxed::Box;
#[derive(Clone, Copy)]
#[repr(C)]
pub struct lconv {
@ -62,12 +64,12 @@ pub const LC_ALL: c_int = 6;
pub const __LC_LAST: usize = 6;
#[no_mangle]
unsafe extern "C" fn duplocale(locobj: locale_t) -> locale_t {
unsafe extern "C" fn duplocale(_locobj: locale_t) -> locale_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn freelocale(locobj: locale_t) {
unsafe extern "C" fn freelocale(_locobj: locale_t) {
todo!()
}
@ -78,20 +80,24 @@ unsafe extern "C" fn localeconv() -> *mut lconv {
#[no_mangle]
unsafe extern "C" fn newlocale(
category_mask: c_int,
locale: *const c_char,
base: locale_t,
_category_mask: c_int,
_locale: *const c_char,
_base: locale_t,
) -> locale_t {
let locale = Box::new(__locale_struct {
__locales: [null_mut(); __LC_LAST]
});
Box::into_raw(locale)
}
#[no_mangle]
unsafe extern "C" fn setlocale(_category: c_int, _locale: *const c_char) -> *mut c_char {
// TODO
todo!()
}
#[no_mangle]
unsafe extern "C" fn setlocale(category: c_int, locale: *const c_char) -> *mut c_char {
// TODO
unsafe extern "C" fn uselocale(_locobj: locale_t) -> locale_t {
null_mut()
}
#[no_mangle]
unsafe extern "C" fn uselocale(locobj: locale_t) -> locale_t {
todo!()
}

View File

@ -100,6 +100,7 @@ pub mod grp;
pub mod iconv;
pub mod langinfo;
pub mod libgen;
pub mod limits;
pub mod locale;
pub mod monetary;
pub mod nl_types;
@ -131,3 +132,6 @@ pub mod sys_times;
pub mod sys_types;
pub mod sys_utsname;
pub mod sys_wait;
// TODO Generate those as part of dyn-loader (and make dyn-loader a shared library)
pub mod link;

View File

@ -1,5 +1,3 @@
use core::ffi::c_int;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub mod x86_64;

View File

@ -1,10 +1,10 @@
use core::{arch::global_asm, ffi::c_int};
use core::ffi::c_int;
pub type __jmp_buf = [usize; 8];
#[no_mangle]
#[naked]
unsafe extern "C" fn setjmp(buf: __jmp_buf) -> c_int {
unsafe extern "C" fn setjmp(buf: *mut __jmp_buf) -> c_int {
// %rdi -- jmp_buf pointer
core::arch::naked_asm!(
r#"
@ -30,16 +30,17 @@ unsafe extern "C" fn setjmp(buf: __jmp_buf) -> c_int {
#[no_mangle]
#[naked]
unsafe extern "C" fn _setjmp(buf: __jmp_buf) -> c_int {
unsafe extern "C" fn _setjmp(buf: *mut __jmp_buf) -> c_int {
core::arch::naked_asm!("jmp setjmp", options(att_syntax))
}
#[no_mangle]
#[naked]
unsafe extern "C" fn longjmp(buf: __jmp_buf, val: c_int) -> c_int {
unsafe extern "C" fn longjmp(buf: *mut __jmp_buf, val: c_int) -> c_int {
// %rdi -- jmp_buf pointer
// %rsi -- return value
core::arch::naked_asm!(r#"
core::arch::naked_asm!(
r#"
// Restore the registers
movq 0(%rdi), %rbx
movq 8(%rdi), %rbp
@ -55,11 +56,13 @@ unsafe extern "C" fn longjmp(buf: __jmp_buf, val: c_int) -> c_int {
movq %rsi, %rax
ret
"#, options(att_syntax))
"#,
options(att_syntax)
)
}
#[no_mangle]
#[naked]
unsafe extern "C" fn _longjmp(buf: __jmp_buf, val: c_int) -> c_int {
unsafe extern "C" fn _longjmp(buf: *mut __jmp_buf, val: c_int) -> c_int {
core::arch::naked_asm!("jmp longjmp", options(att_syntax))
}

View File

@ -141,7 +141,22 @@ unsafe extern "C" fn putchar_unlocked(ch: c_int) -> CEofResult {
#[no_mangle]
unsafe extern "C" fn puts(str: *const c_char) -> CEofResult {
let str = str.ensure_cstr();
let out = stdout.ensure_mut();
let out = match stdout.as_mut() {
Some(out) => out,
None => {
// TODO this hack is needed because currently global constructors are called
// before ygglibc is entered and I/O streams are initialized.
// this will be removed when I properly implement passing DT_INIT_*** ranges
// from dyn-loader to ygglibc.
use yggdrasil_rt::io::RawFd;
unsafe {
yggdrasil_rt::sys::write(RawFd::STDOUT, str.to_bytes()).ok();
yggdrasil_rt::sys::write(RawFd::STDOUT, b"\n").ok();
}
return CEofResult::success(0)
},
};
out.write_all(str.to_bytes())?;
out.write_all(b"\n")?;
CEofResult::success(0)

View File

@ -1,9 +1,9 @@
use core::{ffi::{c_char, c_int, c_void}, ptr::NonNull};
use core::ffi::c_void;
use crate::{
error::{CEofResult, CPtrResult, CResult, CUsizeResult},
io::{managed::{stdin, stdout, FILE}, Read, Write},
util::{PointerExt, PointerStrExt},
error::CUsizeResult,
io::{managed::FILE, Read, Write},
util::PointerExt,
};
#[no_mangle]
@ -27,10 +27,12 @@ unsafe extern "C" fn fwrite(
nmemb: usize,
fp: *mut FILE,
) -> CUsizeResult {
if fp.is_null() {
loop {}
}
let size = size.checked_mul(nmemb).expect("size * nmemb too large");
let buf = buf.cast::<u8>().ensure_slice(size);
let fp = fp.ensure_mut();
let len = fp.write(buf)?;
CUsizeResult::success(len)
}

View File

@ -191,7 +191,7 @@ unsafe extern "C" fn asprintf(dst: *mut *mut c_char, fmt: *const c_char, mut arg
}
#[no_mangle]
unsafe extern "C" fn vasprintf(_dst: *mut *mut c_char, _fmt: *const c_char, args: VaList) -> c_int {
unsafe extern "C" fn vasprintf(_dst: *mut *mut c_char, _fmt: *const c_char, _args: VaList) -> c_int {
todo!()
}

View File

@ -76,7 +76,7 @@ impl ScanCharSet {
return Err(FormatError);
}
} else {
if let Some(start) = start.replace(ch) {
if let Some(_start) = start.replace(ch) {
// "c" without range
set.insert(ch);
}

View File

@ -4,6 +4,7 @@ style = "Type"
sys_includes = [
"stddef.h",
"locale.h",
"limits.h",
"bits/stdlib.h"
]
no_includes = true
@ -14,4 +15,4 @@ usize_type = "size_t"
isize_type = "ssize_t"
[export]
exclude = ["abort"]
exclude = ["abort", "strtold", "strtold_l"]

View File

@ -35,8 +35,8 @@ unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_flo
}
#[no_mangle]
unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
str_to_float_ext(s, endptr) as _
unsafe extern "C" fn strtold(_s: *const c_char, _endptr: *mut *mut c_char) -> c_double {
unimplemented!()
}
// ints
@ -130,7 +130,7 @@ unsafe extern "C" fn strtold_l(
_endptr: *mut *mut c_char,
_locale: locale_t,
) -> c_double {
todo!()
unimplemented!()
}
#[derive(Clone, Copy, PartialEq)]

View File

@ -30,6 +30,15 @@ unsafe extern "C" fn malloc(size: usize) -> CPtrResult<c_void> {
CPtrResult::success(ptr)
}
#[no_mangle]
unsafe extern "C" fn realloc(ptr: *mut c_void, size: usize) -> CPtrResult<c_void> {
let old_ptr = NonNull::new(ptr);
let new_ptr = allocator::c_realloc(old_ptr, size)?;
CPtrResult::success(new_ptr)
}
// Aligned allocations
#[no_mangle]
unsafe extern "C" fn posix_memalign(
_memptr: *mut *mut c_void,
@ -40,8 +49,6 @@ unsafe extern "C" fn posix_memalign(
}
#[no_mangle]
unsafe extern "C" fn realloc(ptr: *mut c_void, size: usize) -> CPtrResult<c_void> {
let old_ptr = NonNull::new(ptr);
let new_ptr = allocator::c_realloc(old_ptr, size)?;
CPtrResult::success(new_ptr)
unsafe extern "C" fn aligned_alloc(_align: usize, _size: usize) -> *mut c_void {
todo!()
}

View File

@ -1,7 +1,14 @@
use core::{ffi::{c_char, c_int}, ptr::NonNull};
use core::{
ffi::{c_char, c_int},
ptr::NonNull,
};
use crate::{
env, error::{CIntZeroResult, CPtrResult, CResult, OptionExt}, headers::errno, process, util::{PointerExt, PointerStrExt}
env,
error::{CIntZeroResult, CPtrResult, CResult, OptionExt},
headers::errno,
process,
util::PointerStrExt,
};
#[no_mangle]

View File

@ -1,6 +1,6 @@
use core::ffi::{c_char, c_int};
use crate::types::wchar_t;
use crate::{error::CResult, headers::wchar::{mbstate_t, multibyte::mbrtowc}, types::wchar_t};
#[no_mangle]
unsafe extern "C" fn mblen(_s: *const c_char, _n: usize) -> c_int {
@ -13,8 +13,9 @@ unsafe extern "C" fn mbstowcs(_dst: *mut wchar_t, _src: *const c_char, _n: usize
}
#[no_mangle]
unsafe extern "C" fn mbtowc(_dst: *mut wchar_t, _src: *const c_char, _n: usize) -> c_int {
todo!()
unsafe extern "C" fn mbtowc(dst: *mut wchar_t, src: *const c_char, n: usize) -> c_int {
let mut state = mbstate_t { __dummy: 0 };
mbrtowc(dst, src, n, &mut state).into_inner() as c_int
}
#[no_mangle]

View File

@ -1,4 +1,2 @@
use core::ffi::{c_char, c_int, c_void};
pub mod mem;
pub mod str;

View File

@ -1,4 +1,4 @@
use core::ffi::{c_int, c_long, c_void};
use core::ffi::{c_char, c_int, c_long, c_void};
use super::sys_types::{suseconds_t, time_t};
@ -44,3 +44,8 @@ unsafe extern "C" fn gettimeofday(_time: *mut timeval, _d: *mut c_void) -> c_int
unsafe extern "C" fn setitimer(_timer: c_int, _new: *const itimerval, _old: *mut itimerval) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn utimes(_path: *const c_char, _times: *const timeval) -> c_int {
todo!()
}

View File

@ -6,6 +6,7 @@ use crate::headers::{sys_types::time_t, time::tm};
use super::{get_timezone, CDateTime};
#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn gmtime(tp: *const time_t) -> *mut tm {
static mut BUFFER: MaybeUninit<tm> = MaybeUninit::uninit();
@ -26,6 +27,7 @@ unsafe extern "C" fn gmtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm {
}
}
#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn localtime(tp: *const time_t) -> *mut tm {
static mut BUFFER: MaybeUninit<tm> = MaybeUninit::uninit();

View File

@ -37,6 +37,7 @@ unsafe fn fmt_time<Tz: TimeZone, T: CDateTime>(
writer.finish()
}
#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn asctime(timep: *const tm) -> *mut c_char {
static mut BUFFER: [c_char; 32] = [0; 32];
@ -54,6 +55,7 @@ unsafe extern "C" fn asctime_r(timep: *const tm, buffer: *mut c_char) -> *mut c_
}
}
#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn ctime(tp: *const time_t) -> *mut c_char {
static mut BUFFER: [c_char; 32] = [0; 32];

View File

@ -2,6 +2,7 @@ language = "C"
style = "Type"
sys_includes = [
"limits.h",
"stddef.h",
"sys/types.h"
]

View File

@ -2,6 +2,8 @@ use core::ffi::{c_char, c_int, c_long};
use crate::headers::sys_types::{gid_t, off_t, uid_t};
pub const _PC_PATH_MAX: c_int = 0;
#[no_mangle]
unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
todo!()

View File

@ -1,5 +1,7 @@
use core::ffi::{c_char, c_int, c_long, c_uint, c_void};
pub const _SC_PAGE_SIZE: c_int = 1;
#[no_mangle]
unsafe extern "C" fn alarm(value: c_uint) -> c_uint {
todo!()

View File

@ -0,0 +1,61 @@
use core::{ffi::c_char, ptr::NonNull, slice, str};
use crate::{error::EResult, headers::errno, types::wchar_t};
use super::mbstate_t;
// NOTE: stolen from relibc
// https://tools.ietf.org/html/rfc3629
static UTF8_CHAR_WIDTH: [u8; 256] = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, // 0x1F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, // 0x3F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, // 0x5F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, // 0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, // 0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, // 0xBF
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, // 0xDF
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF
];
// Given a first byte, determines how many bytes are in this UTF-8 character.
#[inline]
fn utf8_char_width(b: u8) -> usize {
UTF8_CHAR_WIDTH[usize::from(b)].into()
}
pub unsafe fn mbrtowc(
dst: *mut wchar_t,
src: NonNull<c_char>,
n: usize,
_state: &mut mbstate_t,
) -> EResult<usize> {
let size = utf8_char_width(src.cast::<u8>().read());
// TODO EILSEQ
if size > n || size == 0 {
return EResult::Err(errno::EINVAL);
}
let slice = slice::from_raw_parts(src.cast::<u8>().as_ptr(), size);
let Ok(decoded) = str::from_utf8(slice) else {
return EResult::Err(errno::EINVAL);
};
let result = decoded.chars().next().unwrap() as wchar_t;
if let Some(dst) = dst.as_mut() {
*dst = result;
}
if result != 0 {
EResult::Ok(size)
} else {
EResult::Ok(0)
}
}

View File

@ -1,6 +1,4 @@
use core::ffi::{c_int, c_void};
use crate::types::wchar_t;
use core::ffi::c_int;
use super::wctype::wint_t;
@ -8,9 +6,11 @@ pub mod io;
pub mod multibyte;
pub mod string;
pub const WEOF: wchar_t = -1;
mod imp;
pub const WEOF: wint_t = (-1i32) as wint_t;
#[repr(C)]
pub struct mbstate_t {
__dummy: c_int
pub __dummy: c_int,
}

View File

@ -1,6 +1,6 @@
use core::ffi::{c_char, c_int};
use core::{ffi::{c_char, c_int}, ptr::NonNull};
use crate::types::wchar_t;
use crate::{error::CUsizeResult, headers::wchar::imp, types::wchar_t};
use super::mbstate_t;
@ -14,14 +14,32 @@ unsafe extern "C" fn wcrtomb(_dst: *mut c_char, _wc: wchar_t, _state: *mut mbsta
todo!()
}
#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn mbrtowc(
_dst: *mut wchar_t,
_src: *const c_char,
_n: usize,
_state: *mut mbstate_t,
) -> usize {
todo!()
pub unsafe extern "C" fn mbrtowc(
dst: *mut wchar_t,
src: *const c_char,
n: usize,
state: *mut mbstate_t,
) -> CUsizeResult {
static mut GLOBAL: mbstate_t = mbstate_t { __dummy: 0 };
let state = match state.as_mut() {
Some(state) => state,
None => &mut GLOBAL
};
let res = match NonNull::new(src.cast_mut()) {
Some(src) => {
imp::mbrtowc(dst, src, n, state)?
}
None => {
let x: [c_char; 1] = [0];
imp::mbrtowc(dst, NonNull::from_ref(&x[0]), 1, state)?
}
};
CUsizeResult::success(res)
}
#[no_mangle]

View File

@ -0,0 +1,21 @@
use core::sync::atomic::{AtomicBool, Ordering};
use crate::{io, ssp};
static INIT_COMPLETE: AtomicBool = AtomicBool::new(false);
// TODO implement topological sorting for proper .init_array calls in dyn-loader
#[link_section = ".preinit_array"]
#[used]
static PREINIT_ARRAY: [extern "C" fn (); 1] = [init];
pub extern "C" fn init() {
if INIT_COMPLETE.swap(true, Ordering::Acquire) {
return;
}
unsafe {
ssp::init();
io::init();
}
}

View File

@ -58,11 +58,6 @@ impl<'a> ReadBuffer<'a> {
}
}
pub fn reset(&mut self) {
self.position = 0;
self.len = 0;
}
pub fn fill_from<R: Read + ?Sized>(&mut self, source: &mut R) -> EResult<&[u8]> {
let buffer = unsafe { self.data.as_slice_mut() };
if self.position == self.len {

View File

@ -9,11 +9,15 @@
slice_internals,
linkage,
rustc_private,
naked_functions
naked_functions,
non_null_from_ref
)]
#![allow(internal_features)]
#![cfg_attr(not(test), no_std)]
use core::{ffi::{c_char, c_int}, ptr::null};
use alloc::vec::Vec;
use env::environ;
extern crate alloc;
@ -24,8 +28,10 @@ extern crate compiler_builtins;
extern crate std;
mod allocator;
mod cxxabi;
mod env;
mod error;
mod init;
mod io;
mod panic;
mod process;
@ -33,25 +39,15 @@ mod ssp;
mod sync;
mod types;
mod util;
mod cxxabi;
pub mod headers;
#[no_mangle]
unsafe extern "C" fn __ygglibc_entry(arg: usize) {
use core::{
ffi::{c_char, c_int},
ptr::null,
};
use alloc::vec::Vec;
extern "C" {
fn main(argc: c_int, argv: *const *const c_char, envp: *const *const c_char) -> c_int;
}
ssp::init();
io::init();
unsafe extern "C" fn __ygglibc_entry(
main: extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int,
arg: usize,
) {
init::init();
// Setup args
let args = env::handle_kernel_argument(arg);

View File

@ -33,7 +33,7 @@ impl PanicPrinter {
let text = &self.buffer[..self.pos];
if !text.is_empty() {
// Print to debug trace
unsafe { yggdrasil_rt::debug::trace_raw(text) };
yggdrasil_rt::debug::trace_raw(text);
// Print to stderr
if let Some(print) = self.print.as_mut() {
@ -74,11 +74,11 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
if let Some(location) = pi.location() {
writeln!(printer, "{}:{}:", location.file(), location.line()).ok();
}
writeln!(printer, " {}", pi.message());
writeln!(printer, " {}", pi.message()).ok();
printer.flush();
}
1 => unsafe {
1 => {
yggdrasil_rt::debug_trace!("!!! ygglibc panicked while panicking !!!");
}
_ => {}

View File

@ -1,8 +1,18 @@
use core::{ffi::{c_char, c_int, CStr}, ptr::NonNull, time::Duration};
use core::{
ffi::{c_char, c_int, CStr},
time::Duration,
};
use yggdrasil_rt::{process::{ExitCode, Signal}, sys as syscall};
use yggdrasil_rt::{
process::{ExitCode, Signal},
sys as syscall,
};
use crate::{error::EResult, headers::sys_types::pid_t, io::{self, managed::stderr}};
use crate::{
error::EResult,
headers::sys_types::pid_t,
io::{self, managed::stderr},
};
pub fn getpid() -> pid_t {
let pid = unsafe { syscall::get_pid() };
@ -48,12 +58,14 @@ unsafe extern "C" fn __assert_fail(file: *const c_char, line: c_int, message: *c
let err = stderr.as_mut().unwrap();
let file = match file.is_null() {
false => CStr::from_ptr(file).to_str().ok(),
true => None
}.unwrap_or("???");
true => None,
}
.unwrap_or("???");
let expr = match message.is_null() {
false => CStr::from_ptr(message).to_str().ok(),
true => None
}.unwrap_or("???");
true => None,
}
.unwrap_or("???");
writeln!(err, "Assertion failed: '{}' at {}:{}", expr, file, line).ok();

View File

@ -1,7 +1,5 @@
use core::{
ffi::{c_char, c_int, CStr},
fmt,
ptr::NonNull,
ffi::{c_char, c_int, CStr}, fmt, panic::Location, ptr::NonNull
};
use yggdrasil_rt::io::RawFd;
@ -38,8 +36,13 @@ impl<T> PointerExt for T {
unsafe { self.as_ref().expect("ensure() failed: NULL pointer") }
}
#[track_caller]
unsafe fn ensure_mut(self: *mut Self) -> &'static mut Self {
unsafe { self.as_mut().expect("ensure_mut() failed: NULL pointer") }
let caller = Location::caller();
match self.as_mut() {
Some(ptr) => ptr,
None => panic!("{caller:?}: ensure_mut() failed: NULL pointer"),
}
}
unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self]
@ -130,9 +133,9 @@ pub unsafe fn cstr_prefix<F: Fn(u8, u8) -> bool>(a: NonNull<u8>, b: &[u8], cmp:
true
}
pub unsafe fn cstr_matches(a: NonNull<u8>, b: &[u8]) -> bool {
cstr_prefix(a, b, |a, b| a == b)
}
// pub unsafe fn cstr_matches(a: NonNull<u8>, b: &[u8]) -> bool {
// cstr_prefix(a, b, |a, b| a == b)
// }
pub unsafe fn cstr_matches_insensitive(a: NonNull<u8>, b: &[u8]) -> bool {
cstr_prefix(a, b, |a, b| {

View File

@ -39,11 +39,52 @@ impl Llvm {
command
}
pub fn c_clangpp(&self, env: &BuildEnv) -> Command {
let mut command = Command::new(self.root.join("bin/clang++"));
command.arg(format!("--target={}-unknown-yggdrasil", env.arch.name()));
command.arg(format!("--sysroot={}", env.llvm_sysroot.display()));
// TODO: Not yet implemented features:
command.arg("-fno-exceptions");
command.arg("-fno-rtti");
command
}
pub fn clang(&self) -> PathBuf {
self.root.join("bin/clang")
}
}
fn build_test_cpp_program(
env: &BuildEnv,
llvm: &Llvm,
install: &mut Vec<(PathBuf, PathBuf)>,
) -> Result<(), Error> {
log::info!("Building a test C++ program [PIE]");
let target_dir = &env.userspace_output_dir;
let mut command = llvm.c_clangpp(env);
command
.args([
"-v",
"-fpie",
"-Bdynamic",
"-O0",
"-ggdb",
"-fstack-protector-strong",
])
.arg("-o")
.arg(target_dir.join("cpp-test"))
.arg(env.workspace_root.join("test.cpp"));
if !command.status()?.success() {
return Err(Error::ExternalCommandFailed);
}
install.push((target_dir.join("cpp-test"), "cpp-test".into()));
Ok(())
}
fn build_test_c_program(
env: &BuildEnv,
llvm: &Llvm,
@ -184,7 +225,19 @@ fn install_openlibm(env: &BuildEnv, libm: &Openlibm) -> Result<(), Error> {
fn build_llvm(env: &BuildEnv) -> Result<Llvm, Error> {
// Configuration:
/*
cmake -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt" -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_SYSTEM_NAME=yggdrasil -DLLVM_USE_LINKER=lld -DBUILD_SHARED_LIBS=false -DLLVM_BUILD_TOOLS=false -DLLVM_CCACHE_BUILD=true -DCMAKE_BUILD_TYPE=Release -DCMAKE_HOST_TRIPLE=x86_64-unknown-linux-gnu -DUNIX=1 -GNinja -DCMAKE_INSTALL_PREFIX=/home/alnyan/build/ygg/toolchain-c/prefix/host ../llvm
cmake
-DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt"
-DLLVM_TARGETS_TO_BUILD=X86
-DCMAKE_SYSTEM_NAME=yggdrasil
-DLLVM_USE_LINKER=lld
-DBUILD_SHARED_LIBS=false
-DLLVM_BUILD_TOOLS=false
-DLLVM_CCACHE_BUILD=true
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_HOST_TRIPLE=x86_64-unknown-linux-gnu
-DUNIX=1
-GNinja
-DCMAKE_INSTALL_PREFIX=/home/alnyan/build/ygg/toolchain-c/prefix/host ../llvm
Build:
cmake --build . -j 16
@ -207,7 +260,13 @@ pub fn build_c(env: &BuildEnv, install: &mut Vec<(PathBuf, PathBuf)>) -> Result<
install_openlibm(env, &libm)?;
build_test_c_program(env, &llvm, install)?;
build_test_cpp_program(env, &llvm, install)?;
// TODO
install.push((
env.llvm_sysroot.join("lib/libc++.so"),
"lib/libc++.so".into(),
));
install.push((ygglibc.shared_lib_file, "lib/libygglibc.so".into()));
install.push((libm.shared_lib_file, "lib/libopenlibm.so.4".into()));