Added libc code

This commit is contained in:
2025-06-04 01:22:53 +10:00
parent aa00420121
commit 1ed9e8eeab
39 changed files with 4245 additions and 5 deletions

View File

@@ -2,16 +2,14 @@
SecureLang Library for C
THIS IS A TEST EDIT.
## Features
* A subset of traditional libc features (e.g. `printf`)
* A subset of traditional libc features (e.g. provides `printf`)
* Some support for high level language features like garbage collection
* Simple licensing, with only small exceptions the code was written by me
* Simple copyright, with only small exceptions the code was written by me
## Limitations
* Doesn't support all legacy functions
* Doesn't support all legacy functions (e.g. doesn't provide `scanf`)
* Incomplete support for error handling, multithreading, signals etc.
* Currently used for testing in-house systems so not readily buildable/testable on commodity platforms

113
ctype.c Normal file
View File

@@ -0,0 +1,113 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include <ctype.h>
int isspace(int ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
}
int isdigit(int ch) {
return (ch >= '0') && (ch <= '9');
}
int isxdigit(int ch) {
return isdigit(ch) || ((ch >= 'a') && (ch <= 'f')) || ((ch >= 'A') && (ch <= 'F'));
}
int isalpha(int ch) {
return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'));
}
int isalnum(int ch) {
return isalpha(ch) || isdigit(ch);
}
int isprint(int ch) {
return isalnum(ch) || isspace(ch) || ispunct(ch);
}
int ispunct(int ch) {
switch (ch) {
case ',':
case '<':
case '.':
case '>':
case '/':
case '?':
case ';':
case ':':
case '\'':
case '\"':
case '[':
case ']':
case '{':
case '}':
case '`':
case '~':
case '@':
case '#':
case '$':
case '%':
case '^':
case '&':
case '*':
case '(':
case ')':
case '-':
case '_':
case '=':
case '+':
return 1;
default:
return 0;
}
}
int isupper(int ch) {
if (ch >= 'A' && ch <= 'Z') {
return 1;
} else {
return 0;
}
}
int islower(int ch) {
if (ch >= 'a' && ch <= 'a') {
return 1;
} else {
return 0;
}
}
int toupper(int ch) {
if (ch >= 'a' && ch <= 'z') {
return ((int)'A') + (ch - ((int)'a'));
} else {
return ch;
}
}
int tolower(int ch) {
if (ch >= 'A' && ch <= 'Z') {
return ((int)'a') + (ch - ((int)'A'));
} else {
return ch;
}
}
int isascii(int ch) {
int ascii = ch & 0x7F;
if (ch == ascii) {
return 1;
} else {
return 0;
}
}

370
elf.h Normal file
View File

@@ -0,0 +1,370 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_ELF
#define _LIBC_ELF
// For the purposes of the audit program this is NEW CODE (but was written by me a while ago)
/* NOTE: The specification used as a reference is the draft at https://refspecs.linuxfoundation.org/elf/gabi4+/contents.html
* This header includes both 32-bit and 64-bit types.
* For x86-64 specific information I used as a reference https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf
*/
/*
#include <stdint.h>
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint16_t Elf32_Half;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Off;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint16_t Elf64_Half;
*/
typedef unsigned int Elf32_Addr;
typedef unsigned int Elf32_Off;
typedef unsigned int Elf32_Word;
typedef int Elf32_Sword;
typedef unsigned short Elf32_Half;
typedef unsigned long long Elf64_Addr;
typedef unsigned long long Elf64_Off;
typedef unsigned int Elf64_Word;
typedef int Elf64_Sword;
typedef unsigned long long Elf64_Xword;
typedef long long Elf64_Sxword;
typedef unsigned short Elf64_Half;
#define EV_NONE 0
#define EV_CURRENT 1
#define EI_MAG0 0
#define EI_MAG1 1
#define EI_MAG2 2
#define EI_MAG3 3
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_OSABI 7
#define EI_ABIVERSION 8
#define EI_PAD 9
#define EI_NIDENT 16
#define ELFMAG0 0x7F
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
#define ET_NONE 0
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
#define ET_LOOS 0xFE00
#define ET_HIOS 0xFEFF
#define ET_LOPROC 0xFF00
#define ET_HIPROC 0xFFFF
#define EM_X86_64 62
#define ELFOSABI_NONE 0
/* NOTE: SYSV is apparently the same as "NONE". */
#define ELFOSABI_SYSV 0
#define ELFOSABI_NETBSD 2
#define ELFOSABI_LINUX 3
#define ELFOSABI_SOLARIS 6
#define ELFOSABI_FREEBSD 9
#define ELFOSABI_OPENBSD 12
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_INIT_ARRAY 14
#define SHT_FINI_ARRAY 15
#define SHT_PREINIT_ARRAY 16
#define SHT_GROUP 17
#define SHT_SYMTAB_SHNDX 18
#define SHT_LOOS 0x60000000
#define SHT_HIOS 0x6fffffff
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xffffffff
#define SHN_UNDEF 0
#define SHN_LORESERVE 0xff00
#define SHN_LOPROC 0xff00
#define SHN_HIPROC 0xff1f
#define SHN_LOOS 0xff20
#define SHN_HIOS 0xff3f
#define SHN_ABS 0xfff1
#define SHN_COMMON 0xfff2
#define SHN_XINDEX 0xffff
#define SHN_HIRESERVE 0xffff
#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
#define SHF_MERGE 0x10
#define SHF_STRINGS 0x20
#define SHF_INFO_LINK 0x40
#define SHF_LINK_ORDER 0x80
#define SHF_OS_NONCONFORMING 0x100
#define SHF_GROUP 0x200
#define SHF_TLS 0x400
#define SHF_MASKOS 0x0ff00000
#define SHF_MASKPROC 0xf0000000
#define STN_UNDEF 0
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_TLS 7
#define PT_LOOS 0x60000000
#define PT_HIOS 0x6fffffff
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
#define STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define STT_COMMON 5
#define STT_TLS 6
#define STT_LOOS 10
#define STT_HIOS 12
#define STT_LOPROC 13
#define STT_HIPROC 15
#define PF_X 0x1
#define PF_W 0x2
#define PF_R 0x4
#define PF_MASKOS 0x0ff00000
#define PF_MASKPROC 0xf0000000
/* These macros are defined in the draft specification: */
/* NOTE: I think at least my _INFO is broken here. */
#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))
#define ELF64_R_SYM(i) ((i)>>32)
#define ELF64_R_TYPE(i) ((i)&0xffffffffL)
#define ELF64_R_INFO(s,t) (((s)<<32)+((t)&0xffffffffL))
#define ELF32_ST_BIND(i) ((i)>>4)
#define ELF32_ST_TYPE(i) ((i)&0xf)
#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
#define ELF64_ST_BIND(i) ((i)>>4)
#define ELF64_ST_TYPE(i) ((i)&0xf)
#define ELF64_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
#define ELF32_ST_VISIBILITY(o) ((o)&0x3)
#define ELF64_ST_VISIBILITY(o) ((o)&0x3)
/* These (relocation types?) are specific to x86-64. I still find it weird that ELF works that way (why not just encode the expressions so it'd be portable?) */
#define R_X86_64_NONE 0 //none none
#define R_X86_64_64 1 //word64 S + A
#define R_X86_64_PC32 2 //word32 S + A - P
#define R_X86_64_GOT32 3 //word32 G + A
#define R_X86_64_PLT32 4 //word32 L + A - P
#define R_X86_64_COPY 5 //none none
#define R_X86_64_GLOB_DAT 6 //word64 S
#define R_X86_64_JUMP_SLOT 7 //word64 S
#define R_X86_64_RELATIVE 8 //word64 B + A
#define R_X86_64_GOTPCREL 9 //word32 G + GOT + A - P
#define R_X86_64_32 10 //word32 S + A
#define R_X86_64_32S 11 //word32 S + A
#define R_X86_64_16 12 //word16 S + A
#define R_X86_64_PC16 13 //word16 S + A - P
#define R_X86_64_8 14 //word8 S + A
#define R_X86_64_PC8 15 //word8 S + A - P
#define R_X86_64_DPTMOD64 16 //word64
#define R_X86_64_DTPOFF64 17 //word64
#define R_X86_64_TPOFF64 18 //word64
#define R_X86_64_TLSGD 19 //word32
#define R_X86_64_TLSLD 20 //word32
#define R_X86_64_DTPOFF32 21 //word32
#define R_X86_64_GOTTPOFF 22 //word32
#define R_X86_64_TPOFF32 23 //word32
#define R_X86_64_PC64 24 //word64 S + A - P
#define R_X86_64_GOTOFF64 25 //word64 S + A - GOT
#define R_X86_64_GOTPC32 26 //word32 GOT + A - P
typedef struct Elf32_Ehdr_struct Elf32_Ehdr;
typedef struct Elf64_Ehdr_struct Elf64_Ehdr;
typedef struct Elf32_Shdr_struct Elf32_Shdr;
typedef struct Elf64_Shdr_struct Elf64_Shdr;
typedef struct Elf32_Sym_struct Elf32_Sym;
typedef struct Elf64_Sym_struct Elf64_Sym;
typedef struct Elf32_Rel_struct Elf32_Rel;
typedef struct Elf64_Rel_struct Elf64_Rel;
typedef struct Elf32_Rela_struct Elf32_Rela;
typedef struct Elf64_Rela_struct Elf64_Rela;
typedef struct Elf32_Phdr_struct Elf32_Phdr;
typedef struct Elf64_Phdr_struct Elf64_Phdr;
struct Elf32_Ehdr_struct {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
};
struct Elf64_Ehdr_struct {
unsigned char e_ident[EI_NIDENT];
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
};
struct Elf32_Shdr_struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
};
struct Elf64_Shdr_struct {
Elf64_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
};
struct Elf32_Sym_struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
};
struct Elf64_Sym_struct {
Elf64_Word st_name;
unsigned char st_info;
unsigned char st_other;
Elf64_Half st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
};
struct Elf32_Rel_struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
};
struct Elf32_Rela_struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
};
struct Elf64_Rel_struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
};
struct Elf64_Rela_struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
Elf64_Sxword r_addend;
};
struct Elf32_Phdr_struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
};
struct Elf64_Phdr_struct {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
};
/* From ifndef at top of file: */
#endif

1001
elmalloc.h Normal file

File diff suppressed because it is too large Load Diff

13
include.mk Normal file
View File

@@ -0,0 +1,13 @@
L=libc
LIBC = $L/_libc_internal.o $L/_libc_stdio.o $L/_libc_memory.o $L/_libc_string.o $L/_libc_stdlib.o $L/_libc_ctype.o $L/_libc_misc.o $L/_libc_math.o $U/usys.o
LIBCH = $L/include/
# Note: Add -DLIBC_OOP for object-oriented FILE
_libc_%.o: %.c $(LIBCH)
$(CC) $(CFLAGS_MANDATORY) -I$L/include -c -o $@ $<
_libc_%: _libc_%.o $(LIBC)
$(LD) $(LDFLAGS) -T $L/link.ld -o $@ $^
$(OBJDUMP) -S $@ > $*.asm
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym

17
include/assert.h Normal file
View File

@@ -0,0 +1,17 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_ASSERT_H
#define _LIBC_ASSERT_H
#define assert(...) do {} while(0)
/* From ifndef at top of file: */
#endif

27
include/ctype.h Normal file
View File

@@ -0,0 +1,27 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_CTYPE_H
#define _LIBC_CTYPE_H
int isspace(int ch);
int isdigit(int ch);
int isxdigit(int ch);
int isalpha(int ch);
int isalnum(int ch);
int isprint(int ch);
int ispunct(int ch);
int isupper(int ch);
int islower(int ch);
int toupper(int ch);
int tolower(int ch);
/* From ifndef at top of file: */
#endif

9
include/dirent.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.

30
include/errno.h Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_ERRNO_H
#define _LIBC_ERRNO_H
//extern int errno;
/* On modern Linux platforms the errno is simulated. This is presumably so that each
* thread can have it's own errno without the ABI becoming a huge mess.
*/
#ifdef __MAC
int errno;
#else
int* __errno_location();
#define errno __errno_location()[0]
#endif
#define ENOENT 2
/* From ifndef at top of file: */
#endif

9
include/fcntl.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.

17
include/float.h Normal file
View File

@@ -0,0 +1,17 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_FLOAT_H
#define _LIBC_FLOAT_H
#define DBL_MAX_EXP 1024
#define DBL_MANT_DIG 53
#endif

18
include/limits.h Normal file
View File

@@ -0,0 +1,18 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_LIMITS_H
#define _LIBC_LIMITS_H
#define CHAR_BIT 8
#define UINT_MAX 0xFFFFFFFFU
/* From ifndef at top of file: */
#endif

21
include/math.h Normal file
View File

@@ -0,0 +1,21 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_MATH_H
#define _LIBC_MATH_H
double fabs(double n);
double pow(double x, double p);
double exp(double x);
double ldexp(double x, int exp);
// From ifndef at top of file:
#endif

15
include/memory.h Normal file
View File

@@ -0,0 +1,15 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_MEMORY_H
#define _LIBC_MEMORY_H
/* From ifndef at top of file: */
#endif

9
include/pwd.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.

28
include/setjmp.h Normal file
View File

@@ -0,0 +1,28 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_SETJMP_H
#define _LIBC_SETJMP_H
/*struct jmp_buf_struct {
int todo;
};
typedef struct jmp_buf_struct jmp_buf;*/
typedef int jmp_buf;
#define setjmp(x) \
0
// (printf("WARNING: Unimplemented: setjmp\n") && 0)
#define longjmp(x,y) \
printf("WARNING: Unimplemented: longjmp\n")
/* From ifndef at top of file: */
#endif

9
include/signal.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.

45
include/stdarg.h Normal file
View File

@@ -0,0 +1,45 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STDARG_H
#define _LIBC_STDARG_H
#ifdef _ZCC
typedef long long** va_list;
#define _VA_CHECK() \
if (__builtin_func_callconv != 101) {\
printf("ERROR: Unpacking varargs currently only works with __classic_call (#101). Function %s uses convention %d instead.\n", __func__, __builtin_func_callconv);\
}
#define va_start(list,lastarg) \
do {\
_VA_CHECK();\
list = &lastarg;\
list++;\
} while(0)
#define va_arg(list,T) \
(*((T*)(list++)))
#define va_end(list) \
do {list = (void*)0;} while(0)
/* Not new C compiler, use GCC ABI */
#else
#define va_list __builtin_va_list
#define va_start __builtin_va_start
#define va_end __builtin_va_end
#define va_arg __builtin_va_arg
#endif
/* From ifndef at top of file: */
#endif

20
include/stdbool.h Normal file
View File

@@ -0,0 +1,20 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STDBOOL_H
#define _LIBC_STDBOOL_H
typedef int bool;
#define true 1
#define false 0
/* From ifndef at top of file: */
#endif

23
include/stddef.h Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STDDEF_H
#define _LIBC_STDDEF_H
//#ifndef _LIBC_STDLIB_H
typedef long size_t;
//#endif
#ifndef NULL
#define NULL ((void*) 0)
#endif
/* From ifndef at top of file: */
#endif

36
include/stdint.h Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STDINT_H
#define _LIBC_STDINT_H
typedef char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef int64_t intptr_t;
typedef uint64_t uintptr_t;
typedef uint32_t uint_fast8_t;
typedef int32_t int_fast8_t;
typedef uint32_t uint_fast16_t;
typedef int32_t int_fast16_t;
typedef uint32_t uint_fast32_t;
typedef int32_t int_fast32_t;
typedef uint64_t uint_fast64_t;
typedef int64_t int_fast64_t;
/* From ifndef at top of file: */
#endif

120
include/stdio.h Normal file
View File

@@ -0,0 +1,120 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STDIO_H
#define _LIBC_STDIO_H
#include <stdarg.h>
#include <stddef.h>
#ifdef LIBC_OOP
// Theoretical OOP version of FILE, this would be ideal for C+OOP apps
// This is split into FILE (base class for interoperable stream objects)
// and _libc_FILE (the default FILE implementation using system file
// descriptors)
// Class hierarchy doesn't exist yet...
//#import <Resource.h>
@interface Resource {
}
@end
/* The extensible FILE base type. */
@interface FILE : Resource {
FILE* backend;
}
-(id) init;
@end
/* The subclass of FILE which works over an OS file descriptor. */
@interface _libc_FILE : FILE {
int fd;
int ungot; // Character pushed back or -1
int eof;
}
@end
#else
// Simplified data structure, only intended for bootstrapping (does not
// provide complex buffering or overloading)
struct _libc_FILE_internals {
int fd;
int ungot; // Character pushed back or -1
int eof;
};
typedef struct _libc_FILE_internals FILE;
#endif
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
extern FILE* stderr;
extern FILE* stdin;
extern FILE* stdout;
#define EOF ((int)-1)
#ifdef _ZCC
#define _LIBC_PRINTF_CALLCONV __classic_call
#else
#define _LIBC_PRINTF_CALLCONV
#endif
int _LIBC_PRINTF_CALLCONV printf(const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV sprintf(char *buf, const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV snprintf(char *buf, size_t n, const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV dprintf(int fd, const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV fprintf(FILE* f, const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV vfprintf(FILE* f, const char* fmt, va_list list);
int _LIBC_PRINTF_CALLCONV vsnprintf(char* str, size_t n, const char* fmt, va_list list);
// These are non-standard, but the other *printf functions need to be implemented somehow,
// so fnprintf/vfnprintf just use a callback function for output of n bytes of string output.
typedef int(*_libc_fnprintf_fn_t)(const char* str, int n, void* udata);
int _LIBC_PRINTF_CALLCONV _libc_fnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, ...);
int _LIBC_PRINTF_CALLCONV _libc_vfnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, va_list list);
FILE* fopen(const char* name, const char* mode);
FILE* freopen(const char* name, const char* mode, FILE* f);
int fclose(FILE* f);
int fflush(FILE* f);
long fread(void* buffer, long size, long count, FILE* f);
long fwrite(void* buffer, long size, long count, FILE* f);
char* fgets(char*, int, FILE*);
int fputs(const char*, FILE*);
int fputc(int, FILE*);
int feof(FILE* f);
void perror(const char*);
int putc(int c, FILE* f);
int putchar(int c);
int getc(FILE* f);
int ungetc(int c, FILE* f);
int fseek(FILE* f, long offset, int wh);
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
long ftell(FILE* f);
long getline(char** linevar, long *nvar, FILE* f);
/* From ifndef at top of file: */
#endif

50
include/stdlib.h Normal file
View File

@@ -0,0 +1,50 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STDLIB_H
#define _LIBC_STDLIB_H
#include <stddef.h>
// By default, this libc includes both traditional and garbage collected
// memory managers. _libc_gcalloc is therefore preconfigured as a malloc
// alternative with garbage collection, but internally
// malloc/calloc/realloc/free are also just wrappers over a flexible
// memory manager.
void* malloc(size_t sz);
void* calloc(size_t n, size_t sz);
void* realloc(void* mem, size_t sz);
void free(void* mem);
void* _libc_gcalloc(size_t sz);
char* getenv(const char* key);
int setenv(const char* key, const char* value, int overwrite);
int unsetenv(const char* key);
void exit(int x);
void abort();
long strtol(const char* str, char**endvar, int base);
long long strtoll(const char* str, char**endvar, int base);
unsigned long strtoul(const char* str, char**endvar, int base);
unsigned long long strtoull(const char* str, char**endvar, int base);
float strtof(const char* str, char**endvar);
double strtod(const char* str, char**endvar);
long double strtold(const char* str, char**endvar);
double atof(const char* str);
int atoi(const char* str);
int rand();
void srand(unsigned int r);
#define RAND_MAX 999999999
/* From ifndef at top of file: */
#endif

33
include/string.h Normal file
View File

@@ -0,0 +1,33 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_STRING_H
#define _LIBC_STRING_H
#include <stddef.h>
size_t strlen(const char* foo);
char *strchr(const char* str, int chr);
char *strrchr(const char* str, int chr);
char* strcat(char* str, const char* cat);
char* strcpy(char* buffer, const char* str);
char* strncpy(char* buffer, const char* str, size_t n);
const char* strpbrk(const char* str, const char* search);
int strcmp(const char* a, const char* b);
char* strdup(const char* str);
char* strndup(const char* str, size_t n);
void* memcpy(void* dst, const void* src, size_t nbytes);
void* memset(void* mem, int byt, size_t nbytes);
/* From ifndef at top of file: */
#endif

48
include/sys/event.h Normal file
View File

@@ -0,0 +1,48 @@
// Zak's kqueue implementation
#ifndef SYS_EVENT_H
#define SYS_EVENT_H
#include <stdint.h>
// The kevent structure is used for waiting/receiving notifications.
struct kevent {
uintptr_t ident;
int16_t filter;
uint16_t flags;
uint32_t fflags;
int64_t data;
void* udata;
uint64_t ext[4];
};
// kqueue1 is the kqueue system call with a flags argument,
// as defined on NetBSD & OpenBSD.
int kqueue1(int flags);
// kqueue() without flags is equivalent to kqueue1(0)
#define kqueue() kqueue1(0)
// On FreeBSD, kqueue1 is also known as kqueuex
#define kqueuex(flags) kqueue1(flags)
// After creating a kqueue, kevent is the main syscall used.
// This accepts a list of changes and can receive multiple events.
int kevent(int queue, const struct kevent* changes, int nch, struct kevent* events, int nev, void* todo_timeout);
// The EV_SET() macro is for initialising a struct kevent*
#define EV_SET(ev,idn,flt,flg,ffl,dat,udt) \
do { \
(ev)->ident = idn; \
(ev)->filter = flt; \
(ev)->flags = flg; \
(ev)->fflags = ffl; \
(ev)->data = dat; \
(ev)->udata = udt; \
(ev)->ext[0] = 0; \
(ev)->ext[1] = 0; \
(ev)->ext[2] = 0; \
(ev)->ext[3] = 0; \
} while(0)
// From ifndef at top of file:
#endif

23
include/syscalls.h Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_SYSCALLS_H
#define _LIBC_SYSCALLS_H
#include <stdint.h>
// NOTE: This is NOT the definitive list of system calls, this is just any
// syscall definitions which didn't fit anywhere else!
// TODO: Add some platform-checking here
void* sbrk(int incr);
/* From ifndef at top of file: */
#endif

40
include/time.h Normal file
View File

@@ -0,0 +1,40 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_TIME_H
#define _LIBC_TIME_H
typedef long time_t;
char* ctime(const time_t* timevar);
time_t time(time_t* timevar);
/* TODO: This is not implemented on the current OS. */
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
struct timespec {
int tv_sec;
long tv_nsec;
};
struct tm* localtime(const time_t* timep);
/* From ifndef at top of file: */
#endif

18
include/unistd.h Normal file
View File

@@ -0,0 +1,18 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_UNISTD_H
#define _LIBC_UNISTD_H
void exit(int status);
typedef int pid_t;
pid_t getpid();
/* From ifndef at top of file: */
#endif

9
include/utime.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.

65
internal.c Normal file
View File

@@ -0,0 +1,65 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include "internal.h"
#include "simgc.h"
int main(int argc, char** argv, char** envv); // Note, the third argument is usually not implemented by the main function.
void _libc_malloc_init(void* stackptr);
void _libc_env_init(const char** envv);
void _libc_malloc_finish();
void _libc_stdio_init();
void _libc_start(int argc, char** argv, char** envv) {
_libc_env_init(envv);
_libc_malloc_init(&argc);
_libc_stdio_init();
/*
for (int i = 0; i < argc; i++) {
write(1, "ARG: ", 5);
write(1, argv[i], strlen(argv[i]));
write(1, "\n", 1);
}
for (int i = 0; envv[i]; i++) {
write(1, "ENV: ", 5);
write(1, envv[i], strlen(envv[i]));
write(1, "\n", 1);
}
*/
int result = main(argc, argv, envv);
_libc_malloc_finish();
exit(result);
FATAL("Faulty 'exit' syscall");
}
/* This is used to report missing functions or other critical errors, so
* it shouldn't use any additional library functions it should just
* print the error and exit using the simplest possible code path.
*/
void _libc_fatal(const char* msg, const char* fnc) {
const char* l1 = "FATAL ERROR:\n";
const char* l3 = "FUNCTION:\n";
write(2, l1, strlen(l1));
write(2, "\t", 1);
write(2, msg, strlen(msg));
write(2, "\n", 1);
write(2, l3, strlen(l3));
write(2, "\t", 1);
write(2, fnc, strlen(fnc));
write(2, "\n", 1);
// TODO: Trigger debugger here?
exit(-1);
l1 = "FAULTY EXIT? HOT LOOP FOREVER";
write(2, l1, strlen(l1));
while(1) {
// Hot loop forever
}
}

34
internal.h Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#ifndef _LIBC_INTERNAL_H
#define _LIBC_INTERNAL_H
/* This should be called when a function isn't yet implemented or some similar
* irreconcilable error exists.
*/
void _libc_fatal(const char* errorstr, const char* function);
/* The _libc_fatalf function is like _libc_fatal except prints a formatted
* string ("printf style").
*/
/*void
#ifdef _ZCC
__classic_call
#endif
_libc_fatalf(const char* fmt, ...);*/
#define FATAL(msg) \
_libc_fatal(msg, __func__);
#define UNIMPLEMENTED() \
_libc_fatal("Unimplemented libc function", __func__)
/* From ifndef at top of file: */
#endif

25
link.ld Normal file
View File

@@ -0,0 +1,25 @@
OUTPUT_ARCH("riscv")
ENTRY(_libc_start)
SECTIONS {
. = 0;
.text : {
. = ALIGN(4);
*(.text)
}
.rodata : {
. = ALIGN(16);
*(.rodata)
}
. = ALIGN(4096);
.data : {
. = ALIGN(16);
*(.data)
}
.bss : {
. = ALIGN(16);
*(.bss)
}
PROVIDE(_libc_programend = .);
}

216
math.c Normal file
View File

@@ -0,0 +1,216 @@
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include "internal.h"
unsigned int _libc_rand_seed = 12345;
int rand() {
_libc_rand_seed ^= _libc_rand_seed << 13;
_libc_rand_seed ^= _libc_rand_seed >> 17;
_libc_rand_seed ^= _libc_rand_seed << 5;
return _libc_rand_seed % (RAND_MAX+1);
}
void srand(unsigned int r) {
if (r == 0) {
_libc_rand_seed = 1;
} else {
_libc_rand_seed = r;
}
}
double fabs(double n) {
if (n < 0) {
return 0.0 - n;
} else {
return n;
}
}
double ldexp(double x, int exp) {
UNIMPLEMENTED();
}
double frexp(double x, int* expptr) {
// TODO: Check special cases?
uint64_t bits = *((uint64_t*)((void*)&x));
uint64_t exp = (bits>>52)&0x7FF;
*expptr = (int) exp - 1023;
bits &= 0x800FFFFFFFFFFFFFULL;
bits |= 1023ULL << 52;
return *((double*)((void*)&bits));
}
/*
double _libc_factorial(int n) {
double x = 1.0;
for (int i = 1; i <= n; i++) {
x *= i;
}
return x;
}
// This implementation is very very dumb.
double pow(double x, double p) {
double result = x;
for (int i = 2; i < p; i++) {
result *= x;
}
return result;
}
#define EXP_EPSILON 0.0000000001
// NOTE: This function was written with the help of Qwen AI
double exp(double x) {
double r = 1.0;
double t = 1.0;
int n = 1;
while (fabs(t) < EXP_EPSILON) {
t = pow(x, n) / _libc_factorial(n);
r += t;
n++;
}
return r;
}
*/
////////////////////////////////////////////////////////////////////
// NOTE: OLD CODE taken from PDPC400's math.c ("public domain")
////////////////////////////////////////////////////////////////////
#define HUGE_VAL 9.999999999999999999999E72
/*
Some constants to make life easier elsewhere
(These should I guess be in math.h)
*/
double _libc_pi = 3.1415926535897932384626433832795;
double _libc_ln10 = 2.3025850929940456840179914546844;
double _libc_ln2 = 0.69314718055994530941723212145818;
/*
exp(x) = 1 + x + x2/2 + x3/6 + x4/24 + x5/120 + ... + xn/n! + ...
*/
double exp (double x)
{
int i;
double term,answer,work;
i=2;
term=x;
answer=x;
while (1)
{
work = i;
term = (term * x)/work;
if ( answer == (answer + term) )break;
answer = answer + (term);
i++;
}
answer=answer+1.0;
return(answer);
}
/*
Calculate LOG using Taylor series.
log(1+ x) = x - x**2 + x**3 - x**4 + x**5
==== ==== ==== ==== .........
2 3 4 8
Note this only works for small x so we scale....
*/
double log (double x)
{
int i,scale;
double term,answer,work,xs;
if (x <= 0 )
{
/* need to set signal */
// TODO: Proper errno errno=EDOM;
return (HUGE_VAL);
}
if( x == 1.0)return(0.0);
/*
Scale arguments to be in range 1 < x <= 10
*/
/*
scale = 0;
xs = x;
while ( xs > 10.0 ) { scale ++; xs=xs/10.0;}
while ( xs < 1.0 ) { scale --; xs=xs*10.0;}
*/
xs = frexp(x,&scale);
xs = (1.0 * xs) - 1.0;
scale = scale - 0;
i=2;
term=answer=xs;
while (1)
{
work = i;
term = - (term * xs);
if ( answer == (answer + (term/work)) )break;
answer = answer + (term/work);
i++;
}
answer = answer + (double)scale * _libc_ln2;
return(answer);
}
double log10(double x)
{
return ( log(x) / _libc_ln10 );
}
/*
This code uses log and exp to calculate x to the power y.
If
*/
double pow(double x,double y)
{
int j,neg;
double yy,xx;
neg=0;
j=y;
yy=j;
if( yy == y) {
xx = x;
if ( y < 0 ){neg = 1; j = -j;}
if ( y == 0) return (1.0);
--j;
while(j>0){ xx=xx * x; j--;}
if(neg)xx=1.0/xx;
return (xx);
}
if (x < 0.0)
{
// TODO: Proper errno errno=EDOM;
return(0.0);
}
if (y == 0.0) return (1.0);
return (exp(y*log(x)));
}

86
memory.c Normal file
View File

@@ -0,0 +1,86 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <syscalls.h>
#include "elmalloc.h"
#include "internal.h"
elmm_t _libc_mm;
elmm_sbrk_data_t _libc_sbrkdata;
#define SIMGC_SYSMALLOC(x) elmm_malloc(&_libc_mm, x)
#define SIMGC_FREE(x) elmm_free(&_libc_mm, x)
#define SIMGC_IMPLEMENTATION
#include "simgc.h"
simgc_t _libc_gc;
simgc_thread_t* _libc_gcmainthread;
void _libc_malloc_init(void* stackptr) {
// Initialising the memory manager is currently a bit of work, but in
// theory should pay off as the memory manager code is easy to fine
// tune and adapt for more specialised allocators.
_libc_sbrkdata.func = (void*)(&sbrk); // Hooking it straight to the syscall should work, the userdata argument will be ignored
_libc_sbrkdata.max = 1024 * 1024 * 8;
_libc_sbrkdata.onlyChunk = NULL;
_libc_sbrkdata.udata = NULL;
_libc_mm.bigchunkMinimum = 1024 * 1024;
_libc_mm.bigchunkGranularity = 1024;
_libc_mm.initialised = false;
_libc_mm.lockFunction = NULL;
_libc_mm.bigchunkData = &_libc_sbrkdata;
_libc_mm.bigchunkFunction = &elmm_bigchunk_sbrk;
if (!elmm_init(&_libc_mm)) {
const char* err = "ERROR: Failed to initialise memory manager!\n";
write(2, err, strlen(err));
exit(-1);
}
if (!elmm_growheap(&_libc_mm, 1024*1024*4)) {
printf("Growheap failed.\n");
} else {
printf("Growheap success!\n");
}
simgc_init(&_libc_gc);
_libc_gcmainthread = simgc_begin_inner(&_libc_gc, stackptr);
}
void _libc_malloc_finish() {
simgc_end_inner(_libc_gcmainthread);
simgc_reclaim(&_libc_gc);
}
void* _libc_gcalloc(size_t sz) {
return simgc_alloc(&_libc_gc, sz);
}
void* malloc(size_t sz) {
return elmm_malloc(&_libc_mm, sz);
}
void free(void* mem) {
return elmm_free(&_libc_mm, mem);
}
void* calloc(size_t n, size_t sz) {
/* TODO: check for overflow of n * sz? Probably not a problem IRL (may
* have been more of a thing back in the 16-bit era but I guess math
* overflow is not an important issue on 64-bit or even 32-bit memory
* allocator calls)
*/
return elmm_calloc(&_libc_mm, n, sz);
}
void* realloc(void* mem, size_t sz) {
return elmm_realloc(&_libc_mm, mem, sz);
}

22
misc.c Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include "internal.h"
char* ctime(const time_t* timevar) {
UNIMPLEMENTED();
}
time_t time(time_t* timevar) {
UNIMPLEMENTED();
}

438
simgc.h Normal file
View File

@@ -0,0 +1,438 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
// NOTE: This is entirely my code, except for the hashmap code being
// based on ChatGPT output. Don't worry, I'm using better AIs now, and
// will try to mark anywhere with AI generated code in case IP issues
// arise from them (or in case clients want pure AI-free code for policy
// reasons). Btw the AIs are pretty good at that kind of code
#ifndef SIMGC_H
#define SIMGC_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#ifndef SIMGC_SYSMALLOC
#include <stdlib.h>
#define SIMGC_SYSMALLOC malloc
#endif
#ifndef SIMGC_SYSFREE
#include <stdlib.h>
#define SIMGC_SYSFREE free
#endif
#ifndef SIMGC_LOCK_TYPE
#define SIMGC_LOCK_TYPE int
#define SIMGC_CREATELOCK(l) l = 1
#define SIMGC_DELETELOCK(l) l = 0
#define SIMGC_LOCK(l) l = 2
#define SIMGC_UNLOCK(l) l = 1
#endif
#ifndef SIMGC_GRANULARITY
#define SIMGC_GRANULARITY 8
#endif
typedef struct simgc simgc_t;
typedef struct simgc_ptrset simgc_ptrset_t;
typedef struct simgc_ptrset_node simgc_ptrset_node_t;
typedef struct simgc_allocation simgc_allocation_t;
typedef struct simgc_thread simgc_thread_t;
struct simgc {
SIMGC_LOCK_TYPE alloclock; // Locking is not used properly yet but partly implemented as an example
void* sdata; // Any data which may be needed by any complex SIMGC_SYSMALLOC/SIMGC_SYSFREE macros
void* udata; // Any data which may be associated with the heap by program or libraries
simgc_ptrset_t* pointers;
simgc_allocation_t* allocations;
simgc_thread_t* threads;
};
struct simgc_ptrset_node {
void* key;
simgc_ptrset_node_t* next;
};
struct simgc_ptrset {
simgc_t* gc;
simgc_ptrset_node_t** buckets;
size_t capacity;
size_t size;
};
struct simgc_allocation {
simgc_allocation_t* next;
size_t sizeflg; // Size/flags
// NOTE: The data will start immediately after this struct
};
struct simgc_thread {
void* sdata; // The system thread pointer, if applicable
void* udata; // Userdata, pointer to info assigned by the program or libraries
void* stackbase; // Base of stack (NOTE: may be numerically higher or lower than "top")
void* stacktop; // Top of stack (NOTE: may be numerically higher or lower than "top")
simgc_thread_t* next; // Always NULL for now (until I add locking etc.)
simgc_t* gc;
int iomode;
};
int simgc_init(simgc_t* gc);
// simgc_beginio should be called before entering a blocking I/O or pause operation
void simgc_beginio(simgc_t* gc);
// simgc_endio should be called after exiting a blocking I/O or pause operation
void simgc_endio(simgc_t* gc);
simgc_thread_t* simgc_begin_inner(simgc_t* gc, void* stackbase);
void simgc_end_inner(simgc_thread_t* ctx);
#define SIMGC_RUN(c,f) \
do { \
intptr_t dummyval = 123; \
simgc_thread_t* ctx = simgc_begin_inner(c, &dummyval); \
f; \
simgc_end_inner(ctx); \
} while(0)
void* simgc_alloc(simgc_t* gc, size_t sz);
// From ifndef SIMGC_H
#endif
#ifdef SIMGC_IMPLEMENTATION
#ifndef SIMGC_IMPLEMENTATION_ALREADY
#define SIMGC_IMPLEMENTATION_ALREADY
// stdio is only used for debugging
#include <stdio.h>
#define SIMGC_PTRSET_INITIAL_CAPACITY 16
static uint32_t simgc_ptrset_hash(void* ptr) {
//return (uint32_t)(uintptr_t)ptr;
uintptr_t val = (uintptr_t)ptr;
return ((uint32_t)(val ^ (val >> 32))) & 0x7FFFFFFF;
}
simgc_ptrset_t* simgc_ptrset_create(simgc_t* gc) {
simgc_ptrset_t* set = (simgc_ptrset_t*)SIMGC_SYSMALLOC(sizeof(simgc_ptrset_t));
if (!set) {
return NULL;
}
set->gc = gc;
set->capacity = SIMGC_PTRSET_INITIAL_CAPACITY;
set->size = 0; // TODO: No calloc!
set->buckets = (simgc_ptrset_node_t**)calloc(set->capacity, sizeof(simgc_ptrset_node_t*));
if (!set->buckets) {
SIMGC_SYSFREE(set);
return NULL;
}
return set;
}
void simgc_ptrset_free(simgc_ptrset_t* set) {
simgc_t* gc = set->gc;
for (size_t i = 0; i < set->capacity; ++i) {
simgc_ptrset_node_t* node = set->buckets[i];
while (node) {
simgc_ptrset_node_t* next = node->next;
SIMGC_SYSFREE(node);
node = next;
}
}
SIMGC_SYSFREE(set->buckets);
SIMGC_SYSFREE(set);
}
static bool simgc_ptrset_resize(simgc_ptrset_t* set, size_t new_capacity) {
simgc_ptrset_node_t** new_buckets = (simgc_ptrset_node_t**)calloc(new_capacity, sizeof(simgc_ptrset_node_t*));
if (!new_buckets) {
return false;
}
for (size_t i = 0; i < set->capacity; ++i) {
simgc_ptrset_node_t* node = set->buckets[i];
while (node) {
simgc_ptrset_node_t* next = node->next;
uint32_t hash = simgc_ptrset_hash(node->key);
size_t index = hash % new_capacity;
node->next = new_buckets[index];
new_buckets[index] = node;
node = next;
}
}
SIMGC_SYSFREE(set->buckets);
set->buckets = new_buckets;
set->capacity = new_capacity;
return true;
}
bool simgc_ptrset_add(simgc_ptrset_t* set, void* ptr) {
if ((set->size + 1) * 2 > set->capacity) {
if (!simgc_ptrset_resize(set, set->capacity * 2)) {
return false;
}
}
uint32_t hash = simgc_ptrset_hash(ptr);
size_t index = ((uint64_t)hash) % ((uint64_t)(set->capacity));
simgc_ptrset_node_t* node = set->buckets[index];
while (node) {
if (node->key == ptr) {
return true; // Already in the set
}
node = node->next;
}
simgc_ptrset_node_t* new_node = (simgc_ptrset_node_t*)SIMGC_SYSMALLOC(sizeof(simgc_ptrset_node_t));
if (!new_node) {
return false;
}
new_node->key = ptr;
new_node->next = set->buckets[index];
set->buckets[index] = new_node;
set->size++;
return true;
}
bool simgc_ptrset_remove(simgc_ptrset_t* set, void* ptr) {
uint32_t hash = simgc_ptrset_hash(ptr);
size_t index = hash % set->capacity;
simgc_ptrset_node_t* node = set->buckets[index];
simgc_ptrset_node_t* prev = NULL;
while (node) {
if (node->key == ptr) {
if (prev) {
prev->next = node->next;
} else {
set->buckets[index] = node->next;
}
SIMGC_SYSFREE(node);
set->size--;
return true;
}
prev = node;
node = node->next;
}
return false;
}
bool simgc_ptrset_has(simgc_ptrset_t* set, void* ptr) {
//fprintf(stderr, "LOOKING FOR %p\n", ptr);fflush(stderr);
uint32_t hash = simgc_ptrset_hash(ptr);
//fprintf(stderr, "HASH %x CAPACITY %llu\n", hash, set->capacity);fflush(stderr);
size_t index = hash % set->capacity;
//fprintf(stderr, "INDEX %p\n", index);fflush(stderr);
simgc_ptrset_node_t* node = set->buckets[index];
while (node) {
//fprintf(stderr, "CHECKING %p\n", node);fflush(stderr);
if (node->key == ptr) {
//fprintf(stderr, "FOUND %p\n", ptr);fflush(stderr);
return true;
}
node = node->next;
}
fprintf(stderr, "NOT FOUND %p\n", ptr);fflush(stderr);
return false;
}
int simgc_init(simgc_t* gc) {
SIMGC_CREATELOCK(gc->alloclock);
gc->sdata = NULL;
gc->udata = NULL;
//gc->allocations = NULL;
gc->pointers = simgc_ptrset_create(gc);
gc->threads = NULL;
}
simgc_thread_t* simgc_begin_inner(simgc_t* gc, void* stackbase) {
simgc_thread_t* t = SIMGC_SYSMALLOC(sizeof(simgc_thread_t));
if (t == NULL) {
return NULL;
}
t->sdata = NULL;
t->udata = NULL;
t->stackbase = stackbase;
t->stacktop = NULL;
t->gc = gc;
t->iomode = 0;
t->next = gc->threads;
gc->threads = t;
return t;
}
void simgc_end_inner(simgc_thread_t* ctx) {
if (ctx == NULL) {
return;
}
simgc_t* gc = ctx->gc;
simgc_thread_t* prev = NULL;
simgc_thread_t* thr = gc->threads;
while (thr != NULL) {
if (thr == ctx) {
if (prev == NULL) {
gc->threads = ctx->next;
} else {
prev->next = ctx->next;
}
SIMGC_SYSFREE(ctx);
return;
}
thr = thr->next;
}
}
void* simgc_alloc(simgc_t* gc, size_t sz) {
if (gc == NULL) {
return NULL;
}
while ((sz & SIMGC_GRANULARITY) != 0) sz++;
simgc_allocation_t* alloc = SIMGC_SYSMALLOC(sizeof(simgc_allocation_t) + sz);
if (alloc == NULL) {
return NULL;
}
void* ptr = alloc+1;
alloc->sizeflg = sz;
alloc->next = gc->allocations;
gc->allocations = alloc;
simgc_ptrset_add(gc->pointers, ptr);
return ptr;
}
simgc_allocation_t* simgc_lookup(simgc_t* gc, void* ptr) {
if ((((uintptr_t)ptr) > 1000) && simgc_ptrset_has(gc->pointers, ptr)) {
return ptr-sizeof(simgc_allocation_t);
} else {
return NULL;
}
}
int simgc_recmark(simgc_t* gc, void* ptr);
int simgc_recmark(simgc_t* gc, void* ptr) {
fprintf(stderr, "MARK %p\n", ptr);fflush(stderr);
int n = 0;
simgc_allocation_t* alloc = simgc_lookup(gc, ptr);
if (alloc != NULL) {
fprintf(stderr, "SPECULATIVE HIT %p\n", ptr);fflush(stderr);
}
if (alloc == NULL || alloc->sizeflg & 1) {
fprintf(stderr, "MISS %p\n", ptr);fflush(stderr);
return 0;
}
fprintf(stderr, "HIT %p\n", ptr);fflush(stderr);
n = 1;
alloc->sizeflg = alloc->sizeflg | 1;
size_t sz = (alloc->sizeflg >> 3) << 3;
size_t i;
for (i = 0; i < sz; i += sizeof(void*)) {
n += simgc_recmark(gc, *((void**)(ptr + i)));
}
return n;
}
int simgc_markstack(simgc_t* gc, void* stackbase, void* stacktop) {
int n = 0;
// The stack generally grows top-down, but here we simplify it
if (((uintptr_t)stackbase) > ((uintptr_t)stacktop)) {
void* x = stacktop;
stacktop = stackbase;
stackbase = x;
}
while ((((uintptr_t)stackbase)%SIMGC_GRANULARITY) != 0) stackbase++;
while ((((uintptr_t)stacktop)%SIMGC_GRANULARITY) != 0) stacktop--;
void* p = stackbase;
while (((uintptr_t)p) < ((uintptr_t)stacktop)) {
n += simgc_recmark(gc, *((void**)p));
p += sizeof(void*);
}
return n;
}
int simgc_quicksweep(simgc_t* gc) {
int n = 0;
simgc_allocation_t* prev = NULL;
simgc_allocation_t* alloc = gc->allocations;
while (alloc != NULL) {
if (alloc->sizeflg & 1) {
alloc->sizeflg = (alloc->sizeflg >> 1) << 1; // Clear the "marked" bit
prev = alloc;
alloc = alloc->next;
} else {
simgc_allocation_t* next = alloc->next;
if (prev == NULL) {
gc->allocations = alloc->next;
} else {
prev->next = alloc->next;
}
simgc_ptrset_remove(gc->pointers, alloc);
SIMGC_SYSFREE(alloc);
alloc = next;
n++;
}
}
return n;
}
simgc_thread_t* simgc_threadbystack(simgc_t* gc, void* stacktop) {
return gc->threads;
}
void simgc_beginio(simgc_t* gc) {
simgc_thread_t* t;
t = simgc_threadbystack(gc, &t);
if (t == NULL) {
return;
}
t->iomode = 1;
}
void simgc_endio(simgc_t* gc) {
simgc_thread_t* t;
t = simgc_threadbystack(gc, &t);
if (t == NULL) {
return;
}
t->iomode = 0;
}
int simgc_reclaim(simgc_t* gc) {
intptr_t dummyval = 123;
void* stacktop = &dummyval;
simgc_thread_t* t = simgc_threadbystack(gc, stacktop);
if (t == NULL) {
return -1;
}
t->stacktop = stacktop;
int n = simgc_markstack(gc, t->stackbase, t->stacktop);
fprintf(stderr, "Marked %d\n", n);
int o = simgc_quicksweep(gc);
fprintf(stderr, "Swept %d\n", o);
return o;
}
// From ifndef SIMGC_IMPLEMENTATION_ALREADY:
#endif
// From ifdef SIMGC_IMPLEMENTATION
#endif

591
stdio.c Normal file
View File

@@ -0,0 +1,591 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "../kernel/fcntl.h"
#include "internal.h"
FILE* stdin;
FILE* stdout;
FILE* stderr;
// In the object-oriented version, we can just malloc from the class
// like it's a struct, as long as the class pointer is filled in!
#ifdef LIBC_OOP
#define FILE_internals _libc_FILE
#else
#define FILE_internals FILE
#endif
FILE* _libc_openfd(int fd) {
FILE_internals* result = malloc(sizeof(FILE_internals));
if (result) {
#ifdef LIBC_OOP
// Need to set the special class field as well as regular fields
#warning TODO class field
#else
// Nothing special to set, just the regular fields
#endif
result->fd = fd;
result->ungot = -1;
}
return (void*) result;
}
void _libc_stdio_init() {
stdin = _libc_openfd(0);
stdout = _libc_openfd(1);
stderr = _libc_openfd(2);
}
FILE* fopen(const char* name, const char* mode) {
int has_r = 0;
int has_w = 0;
int has_a = 0;
int has_b = 0;
int has_plus = 0;
printf("open mode: \"%s\"\n", mode);
for (char* m = mode; *m; m++) {
switch (*m) {
case 'r':
if (has_r || has_w || has_a) {
return NULL; // Invalid mode
}
has_r = 1;
break;
case 'w':
if (has_r || has_w || has_a) {
return NULL; // Invalid mode
}
has_w = 1;
break;
case 'a':
if (has_r || has_w || has_a) {
return NULL; // Invalid mode
}
has_a = 1;
break;
case 'b':
if (has_b) {
return NULL; // Invalid mode
}
has_b = 1;
break;
case '+':
if (has_plus) {
return NULL; // Invalid mode
}
has_plus = 1;
break;
default:
return NULL; // Invalid mode
}
}
int o_mode = 0;
if (has_r) {
if (has_plus) {
o_mode |= O_RDWR;
} else {
o_mode = O_RDONLY;
}
} else if (has_w) {
o_mode = O_WRONLY;
o_mode |= O_CREATE;
if (has_plus) {
o_mode |= O_TRUNC;
}
} else if (has_a) {
o_mode = O_WRONLY;
o_mode = O_APPEND;
if (has_plus) {
o_mode |= O_CREATE;
}
} else {
FATAL("This should be unreachable.");
}
FILE_internals* result = malloc(sizeof(FILE_internals));
if (!result) {
//printf("Malloc failed\n");
return NULL;
}
result->fd = open(name, o_mode);
if (result->fd <= 0) {
//printf("Open failed, used mode %d got %d\n", o_mode, result->fd);
free(result);
return NULL;
}
result->ungot = -1;
result->eof = 0;
return (FILE*)result;
}
FILE* freopen(const char* name, const char* mode, FILE* f) {
UNIMPLEMENTED();
}
int fclose(FILE* f) {
if (f == NULL) {
return -1;
}
close(((FILE_internals*)f)->fd);
free(f);
return 0; // TODO: Return -1 if it fails
}
int fflush(FILE* f) {
return 0; // TODO: This function needs to be implemented for buffered streams
}
int feof(FILE* f) {
if (f == NULL) {
return 1;
}
return f->eof;
}
size_t fread(void* buffer, size_t size, size_t nmemb, FILE* f) {
// TODO: See note in calloc() about possibly checking size calculation
char* charbuff = buffer;
size_t realsize = size*nmemb;
if (realsize > 0 && ((FILE_internals*)f)->ungot != -1) {
*charbuff++ = (char) ((FILE_internals*)f)->ungot;
((FILE_internals*)f)->ungot = -1;
realsize--;
}
size_t realresult = read(((FILE_internals*)f)->fd, charbuff, realsize);
if (realresult == realsize) {
return nmemb;
} else {
((FILE_internals*)f)->eof = 1;
return realresult / size;
}
}
size_t fwrite(void* buffer, size_t size, size_t nmemb, FILE* f) {
// TODO: See note in calloc() about possibly checking size calculation
size_t realsize = size*nmemb;
size_t realresult = write(((FILE_internals*)f)->fd, buffer, realsize);
if (realresult == realsize) {
return nmemb;
} else {
return realresult / size;
}
}
int getc(FILE* f) {
unsigned char foo;
if (fread(&foo, 1, 1, f) == 1) {
return (int) foo;
} else {
return EOF;
}
}
int fgetc(FILE* f) {
return getc(f);
}
int ungetc(int c, FILE* f) {
if (((FILE_internals*)f)->ungot == -1) {
return EOF;
} else {
((FILE_internals*)f)->ungot = (int)((unsigned char)c);
}
}
int putc(int c, FILE* f) {
unsigned char chbuf = (unsigned char) c;
if (fwrite(&chbuf, 1, 1, f) != 1) {
return EOF;
}
return chbuf;
}
int putchar(int c) {
return putc(c, stdout);
}
int fputc(int c, FILE* f) {
return putc(c, f);
}
char* fgets(char* str, int size, FILE* f) {
int i = 0;
int ch;
while (i < size-1) {
ch = getc(f);
//printf("Got character '%c'\n", ch);
if (ch == EOF) {
if (i == 0) {
return NULL; // This is important for a program to be able to detect EOF!
}
goto fgets_finished;
}
str[i++] = (char) ch;
if (ch == '\n') {
goto fgets_finished;
}
}
fgets_finished: // Write terminating zero at i and return
str[i] = 0;
return str;
}
int fputs(const char* str, FILE* f) {
int n = strlen(str);
if (fwrite(str, 1, n, f) != n) {
return EOF;
} else {
return n;
}
}
int _libc_dprintf_backend(const char* str, int n, void* udata) {
int fd = (int)((intptr_t)udata);
return write(fd, str, n);
}
int _libc_fprintf_backend(const char* str, int n, void* udata) {
FILE* f = udata;
return fwrite(str, 1, n, f);
}
int _libc_sprintf_backend(const char* str, int n, void* udata) {
char** strvar = udata;
char* s = *strvar;
for (int i = 0; i < n; i++) {
*s++ = str[i];
}
*s = 0;
*strvar = s;
return n;
}
struct _libc_snprintf_args {
char* str;
size_t i;
size_t sz;
};
int _libc_snprintf_backend(const char* str, int n, void* udata) {
struct _libc_snprintf_args* args = udata;
char* s = args->str;
for (int i = 0; i < n; i++) {
if (args->i >= args->sz-1) {
return i;
}
args->str[args->i] = str[i];
args->i++;
}
args->str[args->i+1] = 0;
return n;
}
int _LIBC_PRINTF_CALLCONV printf(const char* fmt, ...) {
void* udata = (void*)((uintptr_t)1); // stdout
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(&_libc_dprintf_backend, udata, fmt, varargs);
va_end(varargs);
return result;
}
int _LIBC_PRINTF_CALLCONV sprintf(char *buf, const char* fmt, ...) {
void* udata = &buf;
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(&_libc_sprintf_backend, udata, fmt, varargs);
va_end(varargs);
return result;
}
int _LIBC_PRINTF_CALLCONV snprintf(char *buf, size_t n, const char* fmt, ...) {
struct _libc_snprintf_args args;
args.str = buf;
args.sz = n;
args.i = 0;
void* udata = &args;
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(&_libc_snprintf_backend, udata, fmt, varargs);
va_end(varargs);
return result;
}
int _LIBC_PRINTF_CALLCONV dprintf(int fd, const char* fmt, ...) {
void* udata = (void*)((uintptr_t)fd);
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(&_libc_dprintf_backend, udata, fmt, varargs);
va_end(varargs);
return result;
}
int _LIBC_PRINTF_CALLCONV fprintf(FILE* f, const char* fmt, ...) {
void* udata = f;
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(&_libc_fprintf_backend, udata, fmt, varargs);
va_end(varargs);
return result;
}
int _LIBC_PRINTF_CALLCONV vfprintf(FILE* f, const char* fmt, va_list list) {
void* udata = f;
uintptr_t result = _libc_vfnprintf(&_libc_fprintf_backend, udata, fmt, list);
return result;
}
int _LIBC_PRINTF_CALLCONV vsnprintf(char* buf, size_t n, const char* fmt, va_list list) {
struct _libc_snprintf_args args;
args.str = buf;
args.sz = n;
args.i = 0;
void* udata = &args;
uintptr_t result = _libc_vfnprintf(&_libc_snprintf_backend, udata, fmt, list);
return result;
}
// These are non-standard, but the other *printf functions need to be implemented somehow,
// so fnprintf/vfnprintf just use a callback function for output of n bytes of string output.
int _LIBC_PRINTF_CALLCONV _libc_fnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, ...) {
va_list varargs;
va_start(varargs, fmt);
uintptr_t result = _libc_vfnprintf(fn, udata, fmt, varargs);
va_end(varargs);
return result;
}
#define DUMPTOFN() \
if (dayswithoutincident) { \
if (fn(f-dayswithoutincident, dayswithoutincident, udata) != dayswithoutincident) { \
return EOF; \
} \
written += dayswithoutincident; \
dayswithoutincident = 0; \
}
int _LIBC_PRINTF_CALLCONV _libc_vfnprintf(_libc_fnprintf_fn_t fn, void* udata, const char* fmt, va_list list) {
int dayswithoutincident = 0;
int written = 0;
const char* f = fmt;
int c;
while ((c = *f)) {
if (c == '%') {
DUMPTOFN();
int has_decimals = 0;
int decimals = 0;
nextctrl:
int tc = *(f+1);
int longcount = 0; // for counting %ld/%lld
switch (tc) {
case '%': {
f++;
dayswithoutincident++; // Just let the second '%' be printed with the next string part
} break;
case '.': {
has_decimals = 1;
f++;
} goto nextctrl;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
decimals = decimals * 10 + (tc - '0');
f++;
} goto nextctrl;
case 'f': {
char buf[20];
int i = 20;
double fval = va_arg(list, double);
//fval = -543.210987654321;
long ival = (long) fval;
int isneg = ival < 0;
unsigned long uval = isneg ? -ival : ival;
do {
buf[--i] = (char)('0' + uval % 10);
uval /= 10;
} while (uval > 0);
if (ival < 0) {
buf[--i] = '-';
}
int nwr = fn(buf+i, 20-i, udata);
if (nwr != 20-i) {
return EOF;
}
written += nwr;
if (!has_decimals) {
decimals = 6;
}
double x = fabs(fval - (double) ival);
if (fn(".", 1, udata) != 1) {
return EOF;
}
written++;
while (decimals > 0) {
x *= 10;
decimals--;
int ix = (int) x;
char xch = (char) ('0'+(ix%10));
if (fn(&xch, 1, udata) != 1) {
return EOF;
}
written++;
}
f++;
} break;
case 'S': { // Formatted substring, I thought there was a standard % for it
char* s = va_arg(list, char*);
if (!s) {
s = "(null)";
}
int len = strlen(s);
int nwr = _libc_vfnprintf(fn, udata, s, list);
if (nwr == EOF) {
return EOF;
}
written += nwr;
f++;
} break;
case 's': {
char* s = va_arg(list, char*);
if (!s) {
s = "(null)";
}
int len = strlen(s);
int nwr = fn(s, len, udata);
if (nwr != len) {
return EOF;
}
written += nwr;
f++;
} break;
case 'c': {
int chr = va_arg(list, int);
char chrc = (char) chr;
if (fn(&chrc, 1, udata) != 1) {
return EOF;
}
written++;
f++;
} break;
case 'l': // %ld/%lx is just handled as a special case of %d/%x
longcount++;
f++;
goto nextctrl;
case 'u': // Unsigned is just handled as a special case of %d
case 'd': {
// TODO: Testing/edge case for negative maximum?
char buf[20];
int i = 20;
if (longcount) {
long val = va_arg(list, long);
int isneg = ((tc == 'd') && (val < 0));
unsigned long uval = isneg ? -val : val;
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = (char)('0' + uval % 10);
uval /= 10;
} while (uval > 0);
if (isneg) {
buf[--i] = '-';
}
} else {
int val = va_arg(list, int);
int isneg = ((tc == 'd') && (val < 0));
unsigned int uval = isneg ? -val : val;
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = (char)('0' + uval % 10);
uval /= 10;
} while (uval > 0);
if (isneg) {
buf[--i] = '-';
}
}
int nwr = fn(buf+i, 20-i, udata);
if (nwr != 20-i) {
return EOF;
}
written += nwr;
f++;
} break;
case 'p': // Pointer is treated as %lx
longcount++;
case 'x':
case 'X': { // Hex is handled a separate case to %d because the loop is slightly slower and sign is never used
const char* digits = (tc == 'x') ? "0123456789abcdef" : "0123456789ABCDEF";
char buf[16]; // Size is easier to predict for hex, two characters of digits per byte
int i = 16;
if (longcount) {
unsigned long uval = va_arg(list, unsigned long);
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = digits[uval % 16];
uval /= 16;
} while (uval > 0);
} else {
unsigned int uval = va_arg(list, unsigned int);
// Not actually needed, unless you want a full string: buf[i--] = '0';
do {
buf[--i] = digits[uval % 16];
uval /= 16;
} while (uval > 0);
}
int nwr = fn(buf+i, 16-i, udata);
if (nwr != 16-i) {
return EOF;
}
written += nwr;
f++;
} break;
default: { // For now just print %x??? when 'x' is unknown, but this is probably unsafe and an error should be reported properly instead (TODO)
// Also skip the argument (assume all arguments are word-sized for now)
void* _ignored = va_arg(list, void*);
if (fn(f, 2, udata) != 2) {
return EOF;
}
if (fn("???", 3, udata) != 3) {
return EOF;
}
written+=5;
f++;
}
}
} else {
dayswithoutincident++;
}
f++;
}
DUMPTOFN();
return written;
}
void perror(const char* errormsg) {
UNIMPLEMENTED();
}

195
stdlib.c Normal file
View File

@@ -0,0 +1,195 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include <stdlib.h>
#include <unistd.h>
#include "internal.h"
/* DESIGN NOTE: At program entry the environment variables are just an
* array of "key=value" strings allocated on/near the stack by the
* kernel. When actively modifying environment variables the structure
* needs to be reallocated, so the system initially just uses the
* preallocated copy but keeps a _libc_env_reallocated flag to tell it
* if it's using a malloc'd copy so it can be freed properly. This means
* the system is optimised for programs that don't use environment
* variables much so it doesn't rely on memory allocation in those
* cases but may be slightly inefficient in cases where you do make
* heavy use of them (copying the whole table on every change instead of
* just using an optimised structure from the start).
*/
const char** _libc_env_variable;
int _libc_env_count;
int _libc_env_reallocated;
void _libc_env_init(const char** envv) {
_libc_env_variable = envv;
_libc_env_count = 0;
_libc_env_reallocated = 0;
while (envv != NULL && envv[_libc_env_count] != NULL) {
_libc_env_count++;
}
}
const char* _libc_env_match(const char* env, const char* key) {
int i;
for (i = 0; key[i] != 0; i++) {
if (env[i] != key[i]) {
return 0; // false, env doesn't match given key
}
}
if (env[i] != '=') {
return 0; // false, env key is longer than given key
}
return env+i+1; // true, env string starts with key followed by "="
}
char* getenv(const char* key) {
for (int i = 0; i < _libc_env_count; i++) {
const char* tmp;
if ((tmp = _libc_env_match(_libc_env_variable[i], key)) != NULL) {
return tmp;
}
}
return NULL;
}
int setenv(const char* key, const char* value, int overwrite) {
int exists = -1;
int i;
for (i = 0; i < _libc_env_count; i++) {
const char* tmp;
if ((tmp = _libc_env_match(_libc_env_variable[i], key)) != NULL) {
if (overwrite) {
exists = i;
break;
} else {
return 0;
}
}
}
int newsize;
newsize = _libc_env_count + (exists >= 0 ? 1 : 0);
char* newentry = malloc(strlen(key)+strlen(value)+2);
sprintf(newentry, "%s=%s", key, value);
const char** oldptr = _libc_env_variable;
const char** newptr = malloc(newsize * sizeof(char*));
for (i = 0; i < _libc_env_count; i++) {
const char* tmp;
if (i == exists) {
newptr[i] = newentry;
} else {
newptr[i] = strdup(oldptr[i]);
}
if (_libc_env_reallocated) {
free(oldptr[i]);
}
}
if (i < newsize) {
newptr[i] = newentry;
}
_libc_env_variable = newptr;
if (_libc_env_reallocated) {
free(oldptr);
}
_libc_env_reallocated = 1;
return 0;
}
int unsetenv(const char* key) {
int exists = -1;
int i;
for (i = 0; i < _libc_env_count; i++) {
const char* tmp;
if ((tmp = _libc_env_match(_libc_env_variable[i], key)) != NULL) {
exists = i;
break;
}
}
if (exists < 0) {
return 0; // Nothing to do, env var already doesn't exist
}
int newsize = _libc_env_count - 1;
const char** oldptr = _libc_env_variable;
const char** newptr = malloc(newsize * sizeof(char*));
for (i = 0; i < _libc_env_count; i++) {
const char* tmp;
if (i >= exists) {
newptr[i] = strdup(oldptr[i-1]);
} else {
newptr[i] = strdup(oldptr[i]);
}
if (_libc_env_reallocated) {
free(oldptr[i]);
}
}
_libc_env_variable = newptr;
if (_libc_env_reallocated) {
free(oldptr);
}
_libc_env_reallocated = 1;
return 0;
}
int system(const char* cmd) {
UNIMPLEMENTED();
}
void abort() {
UNIMPLEMENTED();
}
double atof(const char* str) {
UNIMPLEMENTED();
}
int _libc_charnum(char c, int base) {
int n;
if (c >= '0' && c <= '9') {
n = c - '0';
} else if (c >= 'A' && c <= 'F') {
n = 10 + c - 'A';
} else if (c >= 'a' && c <= 'f') {
n = 10 + c - 'a';
} else {
return -1;
}
if (n < base) {
return n;
} else {
return -1;
}
}
long strtol(const char* str, char**endvar, int base) {
UNIMPLEMENTED();
}
long long strtoll(const char* str, char**endvar, int base) {
UNIMPLEMENTED();
}
unsigned long strtoul(const char* str, char**endvar, int base) {
return strtoull(str, endvar, base);
}
unsigned long long strtoull(const char* str, char**endvar, int base) {
UNIMPLEMENTED();
}
float strtof(const char* str, char**endvar) {
UNIMPLEMENTED();
}
double strtod(const char* str, char**endvar) {
UNIMPLEMENTED();
}
long double strtold(const char* str, char**endvar) {
UNIMPLEMENTED();
}

399
string.c Normal file
View File

@@ -0,0 +1,399 @@
// Copyright (c) 2025, Zak Fenton
// Zak Fenton's libc is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
// For auditing purposes, this is NEW CODE
#ifdef KERNEL_MODE
#define size_t long
#define NULL ((void*)0ULL)
#else
#include <string.h>
#include "internal.h"
#endif
//ifdef _ZCC
#ifdef NOT_USED_YET
/* Works like memset except assumes n is a multiple of 8 and src>=dst+8.
* Doesn't return a useful value.
*/
void __naked memset_quick64(void* dst, uint64 v, uint64 n) __asm {
add a3, a0, a2
.setloop:
sd a1, 0(a0)
addi a0, a0, 8
bne a0, a3, .setloop
ret
}
void __naked memset_quick(void* dst, uint64 v, uint64 n) __asm {
add a3, a0, a2
.setloopa:
sb a1, 0(a0)
addi a0, a0, 1
bne a0, a3, .setloopa
ret
}
void* __naked memset_opt(void* dst, uint64 v, uint64 n) __asm {
add a3, a0, a2
add a4, a0, zero
beq a2, zero, .endopt
addi a6, zero, 16
blt a2, a6, .setloopoptshort
addi a6, zero, 128
blt a6, a2, .setloopoptlong
.setloopopt:
sb a1, 0(a4)
addi a4, a4, 1
andi a5, a4, 7
beq a5, zero, .setloopopt64
.setloopstartopt:
bne a4, a3, .setloopopt
.endopt:
ret
.setloopoptshort:
sb a1, 0(a4)
addi a4, a4, 1
bne a4, a3, .setloopoptshort
ret
.setloopopt64adjust:
addi a3, a3, -1
sb a1, 0(a3)
.setloopopt64:
bne a1, zero, .setloopstartopt
andi a6, a3, 7
bne a6, zero, .setloopopt64adjust
//addi a6, zero, 8
.setloopopt64b:
//sub a5, a3, a4
//blt a5, a6, .setloopstartopt
sd a1, 0(a4)
addi a4, a4, 8
blt a4, a3, .setloopopt64b
//j .setloopstartopt
ret
.setloopoptlong:
bne a1, zero, .setloopstartopt
andi a6, a2, 127
bne a6, zero, .setloopstartopt
.setloopoptlongb:
sd zero, 0(a4)
sd zero, 8(a4)
sd zero, 16(a4)
sd zero, 24(a4)
sd zero, 32(a4)
sd zero, 40(a4)
sd zero, 48(a4)
sd zero, 56(a4)
sd zero, 64(a4)
sd zero, 72(a4)
sd zero, 80(a4)
sd zero, 88(a4)
sd zero, 96(a4)
sd zero, 104(a4)
sd zero, 112(a4)
sd zero, 120(a4)
addi a4, a4, 128
bne a4, a3, .setloopoptlongb
ret
}
void* __naked memcpy_opt(void* dst, void* src, uint64 n) __asm {
add a3, a0, a2
add a4, a0, zero
beq a2, zero, .endcpy
addi a6, zero, 16
blt a2, a6, .cpyloopoptshort
addi a6, zero, 128
blt a6, a2, .cpyloopoptlong
.cpyloopopt:
lb a6, 0(a1)
sb a6, 0(a4)
addi a4, a4, 1
addi a1, a1, 1
andi a5, a4, 7
beq a5, zero, .cpyloopopt64
.cpyloopstartopt:
bne a4, a3, .cpyloopopt
.endcpy:
ret
.cpyloopoptshort:
lb a6, 0(a1)
sb a6, 0(a4)
addi a4, a4, 1
addi a1, a1, 1
bne a4, a3, .cpyloopoptshort
ret
.cpyloopopt64adjust:
addi a3, a3, -1
sub a5, a3, a4
add a5, a5, a1
lb a6, 0(a5)
sb a6, 0(a3)
.cpyloopopt64:
andi a6, a3, 7
bne a6, zero, .cpyloopopt64adjust
//addi a6, zero, 8
.cpyloopopt64b:
//sub a5, a3, a4
//blt a5, a6, .setloopstartopt
ld a6, 0(a1)
sd a6, 0(a4)
addi a4, a4, 8
addi a1, a1, 8
blt a4, a3, .cpyloopopt64b
//j .setloopstartopt
ret
.cpyloopoptlong:
andi a6, a2, 127
bne a6, zero, .cpyloopstartopt
.cpyloopoptlongb:
ld a6, 0(a1)
sd a6, 0(a4)
ld a6, 8(a1)
sd a6, 8(a4)
ld a6, 16(a1)
sd a6, 16(a4)
ld a6, 24(a1)
sd a6, 24(a4)
ld a6, 32(a1)
sd a6, 32(a4)
ld a6, 40(a1)
sd a6, 40(a4)
ld a6, 48(a1)
sd a6, 48(a4)
ld a6, 56(a1)
sd a6, 56(a4)
ld a6, 64(a1)
sd a6, 64(a4)
ld a6, 72(a1)
sd a6, 72(a4)
ld a6, 80(a1)
sd a6, 80(a4)
ld a6, 88(a1)
sd a6, 88(a4)
ld a6, 96(a1)
sd a6, 96(a4)
ld a6, 104(a1)
sd a6, 104(a4)
ld a6, 112(a1)
sd a6, 112(a4)
ld a6, 120(a1)
sd a6, 120(a4)
addi a4, a4, 128
addi a1, a1, 128
bne a4, a3, .cpyloopoptlongb
ret
}
// Works like memcpy except assumes n is a multiple of 8 and src>=dst+8.
// Doesn't return a useful value.
void __naked memcpy_quick64(void* dst, void* src, uint64 n) __asm {
add a3, a0, a2
.cpyloop:
ld a4, 0(a1)
addi a1, a1, 8
sd a4, 0(a0)
addi a0, a0, 8
bne a0, a3, .cpyloop
ret
}
// Expands a byte to a word.
uint64 __naked expandbyte_quick64(int b) __asm {
andi a1, a0, 255
slli a0, a0, 8
or a0, a0, a1
slli a0, a0, 8
or a0, a0, a1
slli a0, a0, 8
or a0, a0, a1
slli a0, a0, 8
or a0, a0, a1
slli a0, a0, 8
or a0, a0, a1
slli a0, a0, 8
or a0, a0, a1
slli a0, a0, 8
or a0, a0, a1
ret
}
#endif
// Copied from my preexisting number parser in numbered.c
unsigned long long _libc_scandec(const char** strvar) {
unsigned long long val = 0;
const char* s = *strvar;
char c;
while ((c = *s) >= '0' && c <= '9') {
unsigned long long oldval = val;
val = val * 10 + c - '0';
if (val / 10 != oldval) {
return 0;
}
s++;
}
*strvar = s;
return val;
}
long long atoll(const char* str) {
if (*str == '-') {
str++;
long long x = (long long) _libc_scandec(&str);
return -x;
} else if (*str == '+') {
str++;
return (long long) _libc_scandec(&str);
} else {
return (long long) _libc_scandec(&str);
}
}
long atol(const char* str) {
return (long) atoll(str);
}
int atoi(const char* str) {
return (int) atoll(str);
}
size_t strlen(const char* foo) {
if (foo == NULL) {
return 0;
} else {
size_t i = 0;
while (foo[i] != 0) i++;
return i;
}
}
void* memcpy(void* dst, const void* src, size_t nbytes) {
char* cdst = (char*) dst;
const char* csrc = (char*) src;
size_t i = 0;
for (i = 0; i < nbytes; i++) {
cdst[i] = csrc[i];
}
return dst;
}
void* memset(void* mem, int byt, size_t nbytes) {
char* tmp = mem;
for (size_t i = 0; i < nbytes; i++) {
tmp[i] = (char) byt;
}
return mem;
}
int strcmp(const char* a, const char* b) {
while (*a == *b) {
if (*a == 0) {
return 0; // Got to the end, strings are equal
}
a++;
b++;
}
return *a - *b;
}
char* strcat(char* dst, const char* src) {
char* end = dst;
while (*end) { // Find the terminating zero of the dst string
end++;
}
while (*src) { // Overwrite the end of dst from src until it's terminating zero
*end++ = *src++;
}
*end = 0; // Add terminating zero back to dst
return dst; // Return the start of the dst string
}
char* strcpy(char *dst, const char* src) {
char *d = dst;
while (*src) {
*d++ = *src++;
}
*d = 0;
return dst;
}
char* strncpy(char* dst, const char* src, size_t n) {
size_t i = 0;
char *d = dst;
while (*src) {
*d++ = *src++;
i++;
}
while (i < n) { // TODO: Is this right??
dst[i] = 0;
i++;
}
return dst;
}
#ifndef KERNEL_MODE
char* strdup(const char* src) {
char* result = malloc(strlen(src) + 1);
if (result) {
strcpy(result, src);
}
return result;
}
char* strndup(const char* str, size_t n) {
size_t resultlen = strlen(str);
if (resultlen > n) {
resultlen = n;
}
char* result = malloc(resultlen + 1);
if (result) {
for (size_t i = 0; i < resultlen; i++) {
result[i] = str[i];
}
str[resultlen] = 0;
}
return result;
}
#endif
const char* strpbrk(const char* str, const char* search) {
char c;
while ((c = *str)) {
const char* s = search;
while (*s++) {
if (c == *s) {
return str;
}
}
str++;
}
return NULL;
}
char *strchr(const char* str, int chr) {
int c;
while ((c = *str)) {
if (c == chr) {
return (char*) str;
}
str++;
}
return NULL;
}
char *strrchr(const char* str, int chr) {
int c;
char* result = NULL;
while ((c = *str)) {
if (c == chr) {
result = (char*) str;
}
str++;
}
return result;
}