diff --git a/Userspace/apps/base/Makefile b/Userspace/apps/base/Makefile deleted file mode 100644 index 8160aa65..00000000 --- a/Userspace/apps/base/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# Config file -include ../../../config.mk - -cwd := $(CURDIR) -CACHE_DIR := $(cwd)/../../cache -PREFIX := $(cwd)/../../out/ -TARGET := x86_64-fennix - -export CC := $(cwd)/../../../tools/cross/bin/$(TARGET)-gcc -export LD := $(cwd)/../../../tools/cross/bin/$(TARGET)-ld -export AR := $(cwd)/../../../tools/cross/bin/$(TARGET)-ar -export STRIP := $(cwd)/../../../tools/cross/bin/$(TARGET)-strip -export RANLIB := $(cwd)/../../../tools/cross/bin/$(TARGET)-ranlib -export LD_LIBRARY_PATH := $(cwd)/../../out/lib/ - -ifeq ($(DEBUG), 1) -export CFLAGS := --sysroot=$(cwd)/../../out/ -I$(cwd)/../../out/include/ -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm -export LDFLAGS := -ggdb3 -O0 -else -export CFLAGS := --sysroot=$(cwd)/../../out/ -I$(cwd)/../../out/include/ -endif - -build: - make -C utest build - make -C cross_test build - make -C echo build - make -C fsh build - -clean: - make -C utest clean - make -C cross_test clean - make -C echo clean - make -C fsh clean diff --git a/Userspace/apps/base/cross_test/Makefile b/Userspace/apps/base/cross_test/Makefile deleted file mode 100644 index 1caa63be..00000000 --- a/Userspace/apps/base/cross_test/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -WORKSPACE := ../../../ - -# Config file -include ../$(WORKSPACE)config.mk - -FILENAME = utest_ - -CFLAGS = -static -g -ggdb3 -O0 - -build: - $(info Compiling $(FILENAME)linux) - gcc linux_glibc.c $(CFLAGS) -o $(WORKSPACE)out/bin/$(FILENAME)linux - $(info Compiling $(FILENAME)win) - x86_64-w64-mingw32-gcc win_mingw.c $(CFLAGS) -o $(WORKSPACE)out/bin/$(FILENAME)win.exe - -clean: diff --git a/Userspace/apps/base/cross_test/expected_glibc.txt b/Userspace/apps/base/cross_test/expected_glibc.txt deleted file mode 100644 index 697392cd..00000000 --- a/Userspace/apps/base/cross_test/expected_glibc.txt +++ /dev/null @@ -1,22 +0,0 @@ -$ setarch `uname -m` -R strace ./utest_linux - -execve("./utest_linux", ["./utest_linux"], 0x7fffffffdec0 /* 56 vars */) = 0 -arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffde40) = -1 EINVAL (Invalid argument) -brk(NULL) = 0x4cd000 -brk(0x4cddc0) = 0x4cddc0 -arch_prctl(ARCH_SET_FS, 0x4cd3c0) = 0 -set_tid_address(0x4cd690) = 68565 -set_robust_list(0x4cd6a0, 24) = 0 -rseq(0x4cdd60, 0x20, 0, 0x53053053) = 0 -uname({sysname="Fennix", nodename="fennix", ...}) = 0 -prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 -readlink("/proc/self/exe", "/bin/utest_linux"..., 4096) = 50 -getrandom("\x1e\x3c\x20\xdd\x09\xe8\x46\x0d", 8, GRND_NONBLOCK) = 8 -brk(0x4eedc0) = 0x4eedc0 -brk(0x4ef000) = 0x4ef000 -mprotect(0x4c1000, 16384, PROT_READ) = 0 -newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}, AT_EMPTY_PATH) = 0 -write(1, "Hello, World!\n", 14Hello, World! -) = 14 -exit_group(0) = ? -+++ exited with 0 +++ diff --git a/Userspace/apps/base/echo/Makefile b/Userspace/apps/base/echo/Makefile deleted file mode 100644 index a94dd8bf..00000000 --- a/Userspace/apps/base/echo/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -WORKSPACE := ../../../ - -# Config file -include ../$(WORKSPACE)config.mk - -CC = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -CPP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)g++ -LD = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)ld -AS = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)as -OBJDUMP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)objdump - -GIT_COMMIT = $(shell git rev-parse HEAD) -GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) - -ifeq ($(OSARCH), amd64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), i386) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), aarch64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -endif -OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) - -SYSROOT = --sysroot=$(WORKSPACE)out/ -FILENAME = echo - -HEADERS = $(sort $(dir $(wildcard $(WORKSPACE)out/include/*))) - -LDFLAGS = -CFLAGS = -I$(WORKSPACE)out/include \ - -DGIT_COMMIT='"$(GIT_COMMIT)"' \ - -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' -WARNCFLAG = -Wall -Wextra - -ifeq ($(DEBUG), 1) - CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm - LDFLAGS += -ggdb3 -O0 -endif - -build: $(FILENAME) - mv $(FILENAME) $(WORKSPACE)out/bin/$(FILENAME) - -$(FILENAME): $(OBJ) - $(info Linking $@) - $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ - -%.o: %.c $(HEADERS) - $(info Compiling $<) - $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ - -%.o: %.cpp $(HEADERS) - $(info Compiling $<) - $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti - -%.o: %.S - $(info Compiling $<) - $(AS) -o $@ $< - -clean: - rm -f $(OBJ) diff --git a/Userspace/apps/base/echo/echo.c b/Userspace/apps/base/echo/echo.c deleted file mode 100644 index ce3b8e89..00000000 --- a/Userspace/apps/base/echo/echo.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - int enableEscapes = 0; - int disableNewline = 0; - - int opt; - while ((opt = getopt(argc, argv, "neE")) != -1) - { - switch (opt) - { - case 'n': - disableNewline = 1; - break; - case 'e': - enableEscapes = 1; - break; - case 'E': - enableEscapes = 0; - break; - case '?': - default: - exit(EXIT_FAILURE); - } - } - - (void)enableEscapes; - - size_t totalLength = 0; - for (int i = optind; i < argc; ++i) - totalLength += strlen(argv[i]) + 1; - - char *result = (char *)malloc(totalLength); - if (!result) - { - perror("Memory allocation error"); - exit(EXIT_FAILURE); - } - result[0] = '\0'; - - for (int i = optind; i < argc; ++i) - { - strcat(result, argv[i]); - if (i < argc - 1) - strcat(result, " "); - } - - printf("%s", result); - - if (!disableNewline) - printf("\n"); - - free(result); - return 0; -} diff --git a/Userspace/apps/base/fsh/Makefile b/Userspace/apps/base/fsh/Makefile deleted file mode 100644 index 7e8a8925..00000000 --- a/Userspace/apps/base/fsh/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -WORKSPACE := ../../../ - -# Config file -include ../$(WORKSPACE)config.mk - -CC = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -CPP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)g++ -LD = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)ld -AS = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)as -OBJDUMP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)objdump - -GIT_COMMIT = $(shell git rev-parse HEAD) -GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) - -ifeq ($(OSARCH), amd64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), i386) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), aarch64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -endif -OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) - -SYSROOT = --sysroot=$(WORKSPACE)out/ -FILENAME = sh - -HEADERS = $(sort $(dir $(wildcard $(WORKSPACE)out/include/*))) - -LDFLAGS = -CFLAGS = -I$(WORKSPACE)out/include \ - -DGIT_COMMIT='"$(GIT_COMMIT)"' \ - -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' -WARNCFLAG = -Wall -Wextra - -ifeq ($(DEBUG), 1) - CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm - LDFLAGS += -ggdb3 -O0 -endif - -build: $(FILENAME) - mv $(FILENAME) $(WORKSPACE)out/bin/$(FILENAME) - -$(FILENAME): $(OBJ) - $(info Linking $@) - $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ - -%.o: %.c $(HEADERS) - $(info Compiling $<) - $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ - -%.o: %.cpp $(HEADERS) - $(info Compiling $<) - $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti - -%.o: %.S - $(info Compiling $<) - $(AS) -o $@ $< - -clean: - rm -f $(OBJ) diff --git a/Userspace/apps/base/fsh/fsh.c b/Userspace/apps/base/fsh/fsh.c deleted file mode 100644 index bbad70c7..00000000 --- a/Userspace/apps/base/fsh/fsh.c +++ /dev/null @@ -1,160 +0,0 @@ -#include -#include - -#include -#include -#include -#define _GNU_SOURCE -#include -#include -#define _POSIX_SOURCE -#include - -#define MAX_COMMAND_LENGTH 1024 -#define MAX_ARGS 10 - -void DisableInputBuffering() -{ - struct termios tty_attr; - tcgetattr(STDIN_FILENO, &tty_attr); - tty_attr.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &tty_attr); -} - -void EnableInputBuffering() -{ - struct termios tty_attr; - tcgetattr(STDIN_FILENO, &tty_attr); - tty_attr.c_lflag |= ICANON | ECHO; - tcsetattr(STDIN_FILENO, TCSANOW, &tty_attr); -} - -void ReadLine(char *Buffer, size_t BufferSize) -{ - size_t index = 0; - int c; - while (1) - { - c = getchar(); - if (c == EOF || c == '\n') - { - Buffer[index] = '\0'; - break; - } - else if (c == 127 || c == 8) // Backspace - { - if (index > 0) - { - printf("\b \b"); - index--; - } - } - else if (c == 4) // Ctrl + D - { - if (index == 0) - kill(getpid(), SIGQUIT); - else - putchar('\a'); - } - else - { - if (index < BufferSize - 1) - { - putchar(c); - Buffer[index] = (char)c; - index++; - } - } - } -} - -void ExecuteCommand(char *command) -{ - char *args[MAX_ARGS]; - int i = 0; - - char *token = strtok(command, " "); - while (token != NULL && i < MAX_ARGS - 1) - { - args[i++] = token; - token = strtok(NULL, " "); - } - args[i] = NULL; - - pid_t pid = fork(); - if (pid == 0) - { - execvp(args[0], args); - perror("execvp"); - exit(EXIT_FAILURE); - } - else if (pid > 0) - wait(NULL); - else - { - perror("fork"); - exit(EXIT_FAILURE); - } -} - -void HandleSignal(int signal) -{ - if (signal == SIGQUIT) - { - EnableInputBuffering(); - printf("\n"); - exit(EXIT_SUCCESS); - } - else if (signal == SIGINT) - { - putchar('\n'); - } - else if (signal == SIGHUP) - { - EnableInputBuffering(); - exit(EXIT_SUCCESS); - } - else if (signal == SIGCHLD) - wait(NULL); - else if (signal == SIGSEGV) - { - printf("Segmentation fault\n"); - while (1) - sleep(1000); - } - else - { - printf("Signal %s(%d) received\n", - strsignal(signal), signal); - } -} - -int main() -{ - char command[MAX_COMMAND_LENGTH]; - for (int i = 0; i < NSIG; ++i) - signal(i, HandleSignal); - - char hostname[256]; - gethostname(hostname, sizeof(hostname)); - - while (1) - { - printf("\033[1;32m"); - printf("┌──(%s@%s)-[%s]\n", getenv("USER"), hostname, getenv("PWD")); - printf("└$ "); - printf("\033[0m"); - - DisableInputBuffering(); - ReadLine(command, sizeof(command)); - EnableInputBuffering(); - putchar('\n'); - - if (strcmp(command, "exit") == 0) - break; - - ExecuteCommand(command); - } - - return 0; -} diff --git a/Userspace/apps/base/utest/Makefile b/Userspace/apps/base/utest/Makefile deleted file mode 100644 index fe732e41..00000000 --- a/Userspace/apps/base/utest/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -WORKSPACE := ../../../ - -# Config file -include ../$(WORKSPACE)config.mk - -CC = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -CPP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)g++ -LD = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)ld -AS = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)as -OBJDUMP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)objdump - -GIT_COMMIT = $(shell git rev-parse HEAD) -GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) - -ifeq ($(OSARCH), amd64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), i386) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), aarch64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -endif -OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) - -SYSROOT = --sysroot=$(WORKSPACE)out/ -FILENAME = utest - -HEADERS = $(sort $(dir $(wildcard $(WORKSPACE)out/include/*))) - -LDFLAGS = -static -CFLAGS = -I$(WORKSPACE)out/include \ - -DGIT_COMMIT='"$(GIT_COMMIT)"' \ - -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' -static -WARNCFLAG = -Wall -Wextra - -ifeq ($(DEBUG), 1) - CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm - LDFLAGS += -ggdb3 -O0 -endif - -build: $(FILENAME) - mv $(FILENAME) $(WORKSPACE)out/bin/$(FILENAME) - -$(FILENAME): $(OBJ) - $(info Linking $@) - $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ - -%.o: %.c $(HEADERS) - $(info Compiling $<) - $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ - -%.o: %.cpp $(HEADERS) - $(info Compiling $<) - $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti - -%.o: %.S - $(info Compiling $<) - $(AS) -o $@ $< - -clean: - rm -f $(OBJ) diff --git a/Userspace/apps/sys/Makefile b/Userspace/apps/sys/Makefile new file mode 100644 index 00000000..8ea8a10f --- /dev/null +++ b/Userspace/apps/sys/Makefile @@ -0,0 +1,9 @@ +MAKE_TARGETS := build clean +DIRECTORIES := $(sort $(dir $(wildcard ./*/))) + +.PHONY: $(MAKE_TARGETS) $(DIRECTORIES) + +$(MAKE_TARGETS): $(DIRECTORIES) + +$(DIRECTORIES): + $(MAKE) -C $@ $(MAKECMDGOALS) diff --git a/Userspace/apps/sys/init/.gitignore b/Userspace/apps/sys/init/.gitignore new file mode 100644 index 00000000..b1b71610 --- /dev/null +++ b/Userspace/apps/sys/init/.gitignore @@ -0,0 +1 @@ +init diff --git a/Userspace/apps/sys/init/Makefile b/Userspace/apps/sys/init/Makefile new file mode 100644 index 00000000..fabb4352 --- /dev/null +++ b/Userspace/apps/sys/init/Makefile @@ -0,0 +1,33 @@ +default: + $(error Do not run this Makefile directly!) + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CXX_SOURCES = $(shell find ./ -type f -name '*.cpp') + +OBJ = $(S_SOURCES:.S=.o) $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) + +FILENAME = $(notdir $(shell pwd)) +WARNCFLAG = -Wall -Wextra + +build: $(FILENAME) + cp $(FILENAME) $(WORKSPACE_DIR)/out/bin/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CXX) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f $(OBJ) $(FILENAME) diff --git a/Userspace/apps/sys/init/init.c b/Userspace/apps/sys/init/init.c new file mode 100644 index 00000000..84a523bf --- /dev/null +++ b/Userspace/apps/sys/init/init.c @@ -0,0 +1,27 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include + +int main(int, char *[], char *[]) +{ + printf("Hello, World!\n"); + return 0; +} diff --git a/Userspace/apps/system/Makefile b/Userspace/apps/system/Makefile deleted file mode 100644 index d3cd073a..00000000 --- a/Userspace/apps/system/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -build: - make -C init build - -clean: - make -C init clean diff --git a/Userspace/apps/system/init/Makefile b/Userspace/apps/system/init/Makefile deleted file mode 100644 index 1d922878..00000000 --- a/Userspace/apps/system/init/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -WORKSPACE := ../../../ - -# Config file -include ../$(WORKSPACE)config.mk - -CC = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -CPP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)g++ -LD = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)ld -AS = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)as -OBJDUMP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)objdump - -GIT_COMMIT = $(shell git rev-parse HEAD) -GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) - -ifeq ($(OSARCH), amd64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), i386) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), aarch64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -endif -OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) - -SYSROOT = --sysroot=$(WORKSPACE)out/ -FILENAME = init - -HEADERS = $(sort $(dir $(wildcard $(WORKSPACE)out/include/*))) - -LDFLAGS = -CFLAGS = -I$(WORKSPACE)out/include \ - -DGIT_COMMIT='"$(GIT_COMMIT)"' \ - -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' -WARNCFLAG = -Wall -Wextra - -ifneq ($(OSARCH), aarch64) -# CFLAGS += -fstack-protector-all -fstack-clash-protection -endif - -ifeq ($(DEBUG), 1) - CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm - LDFLAGS += -ggdb3 -O0 -endif - -build: $(FILENAME) - mv $(FILENAME) $(WORKSPACE)out/bin/$(FILENAME) - -$(FILENAME): $(OBJ) - $(info Linking $@) - $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ - -%.o: %.c $(HEADERS) - $(info Compiling $<) - $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ - -%.o: %.cpp $(HEADERS) - $(info Compiling $<) - $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti - -%.o: %.S - $(info Compiling $<) - $(AS) -o $@ $< - -clean: - rm -f $(OBJ) diff --git a/Userspace/apps/system/init/init.c b/Userspace/apps/system/init/init.c deleted file mode 100644 index 03ac27b4..00000000 --- a/Userspace/apps/system/init/init.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(int, char *[], char *[]) -{ - return 0; -} diff --git a/Userspace/apps/test/Makefile b/Userspace/apps/test/Makefile new file mode 100644 index 00000000..8ea8a10f --- /dev/null +++ b/Userspace/apps/test/Makefile @@ -0,0 +1,9 @@ +MAKE_TARGETS := build clean +DIRECTORIES := $(sort $(dir $(wildcard ./*/))) + +.PHONY: $(MAKE_TARGETS) $(DIRECTORIES) + +$(MAKE_TARGETS): $(DIRECTORIES) + +$(DIRECTORIES): + $(MAKE) -C $@ $(MAKECMDGOALS) diff --git a/Userspace/apps/test/cross_test/.gitignore b/Userspace/apps/test/cross_test/.gitignore new file mode 100644 index 00000000..8347bbf5 --- /dev/null +++ b/Userspace/apps/test/cross_test/.gitignore @@ -0,0 +1,2 @@ +utest_linux +utest_win.exe diff --git a/Userspace/apps/test/cross_test/Makefile b/Userspace/apps/test/cross_test/Makefile new file mode 100644 index 00000000..770a06b2 --- /dev/null +++ b/Userspace/apps/test/cross_test/Makefile @@ -0,0 +1,30 @@ +default: + $(error Do not run this Makefile directly!) + +FILENAME = utest_ + +CFLAGS = -static -g -ggdb3 -O0 -w +UNAME_OS := $(shell uname -s) + +$(FILENAME)linux: +ifneq ($(shell uname -s), Linux) + $(info Only Linux is supported) + exit 0 +endif + $(info Compiling $(FILENAME)linux) + gcc linux_glibc.c $(CFLAGS) -o $(FILENAME)linux + cp $(FILENAME)linux $(WORKSPACE_DIR)/out/bin/$(FILENAME)linux + +$(FILENAME)win.exe: +ifneq ($(shell uname -s), Linux) + $(info Only Linux is supported) + exit 0 +endif + $(info Compiling $(FILENAME)win) + x86_64-w64-mingw32-gcc win_mingw.c $(CFLAGS) -o $(FILENAME)win.exe + cp $(FILENAME)win.exe $(WORKSPACE_DIR)/out/bin/$(FILENAME)win.exe + +build: $(FILENAME)linux $(FILENAME)win.exe + +clean: + rm -f $(FILENAME)linux $(FILENAME)win.exe diff --git a/Userspace/apps/base/cross_test/linux_glibc.c b/Userspace/apps/test/cross_test/linux_glibc.c similarity index 100% rename from Userspace/apps/base/cross_test/linux_glibc.c rename to Userspace/apps/test/cross_test/linux_glibc.c diff --git a/Userspace/apps/base/cross_test/win_mingw.c b/Userspace/apps/test/cross_test/win_mingw.c similarity index 100% rename from Userspace/apps/base/cross_test/win_mingw.c rename to Userspace/apps/test/cross_test/win_mingw.c diff --git a/Userspace/apps/test/utest/.gitignore b/Userspace/apps/test/utest/.gitignore new file mode 100644 index 00000000..599b612b --- /dev/null +++ b/Userspace/apps/test/utest/.gitignore @@ -0,0 +1 @@ +utest diff --git a/Userspace/apps/test/utest/Makefile b/Userspace/apps/test/utest/Makefile new file mode 100644 index 00000000..ebcc6aa7 --- /dev/null +++ b/Userspace/apps/test/utest/Makefile @@ -0,0 +1,36 @@ +default: + $(error Do not run this Makefile directly!) + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CXX_SOURCES = $(shell find ./ -type f -name '*.cpp') + +OBJ = $(S_SOURCES:.S=.o) $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) + +FILENAME = $(notdir $(shell pwd)) +WARNCFLAG = -Wall -Wextra + +build: $(FILENAME) +# cp $(FILENAME) $(WORKSPACE_DIR)/out/bin/$(FILENAME) + +# Use static linking +LDFLAGS += -static -fno-pic -fno-pie -Wl,-static + +$(FILENAME): $(OBJ) + $(info Linking $@) +# $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CXX) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f $(OBJ) $(FILENAME) diff --git a/Userspace/apps/base/utest/userspace_test.c b/Userspace/apps/test/utest/userspace_test.c similarity index 96% rename from Userspace/apps/base/utest/userspace_test.c rename to Userspace/apps/test/utest/userspace_test.c index 9250cf12..efae0af1 100644 --- a/Userspace/apps/base/utest/userspace_test.c +++ b/Userspace/apps/test/utest/userspace_test.c @@ -7,15 +7,11 @@ #include #include #include -#include #include -#include #include #include #include #include -#include -#include #include #include #include @@ -141,10 +137,10 @@ void test_passwd() else { printf("name: %s\n", p->pw_name); - printf("passwd: %s\n", p->pw_passwd); + // printf("passwd: %s\n", p->pw_passwd); printf("uid: %d\n", p->pw_uid); printf("gid: %d\n", p->pw_gid); - printf("gecos: %s\n", p->pw_gecos); + // printf("gecos: %s\n", p->pw_gecos); printf("dir: %s\n", p->pw_dir); printf("shell: %s\n", p->pw_shell); } @@ -251,7 +247,9 @@ void test_time() time_t t = mktime(&time); if (t != (time_t)-1) - stime(&t); + { + // stime(&t); + } else { perror("mktime"); @@ -401,29 +399,29 @@ void test_stat() void test_ptmx() { - printf("- Testing PTMX\n"); + // printf("- Testing PTMX\n"); - int master, slave; - char buffer[256]; + // int master, slave; + // char buffer[256]; - if (openpty(&master, &slave, NULL, NULL, NULL) == -1) - { - perror("openpty"); - fflush(stdout); - fflush(stderr); - return; - } + // if (openpty(&master, &slave, NULL, NULL, NULL) == -1) + // { + // perror("openpty"); + // fflush(stdout); + // fflush(stderr); + // return; + // } - write(master, "Hello, pty!\n", 12); - ssize_t bytesRead = read(slave, buffer, sizeof(buffer)); - if (bytesRead > 0) - { - buffer[bytesRead] = '\0'; - printf("Received from pty: %s", buffer); - } + // write(master, "Hello, pty!\n", 12); + // ssize_t bytesRead = read(slave, buffer, sizeof(buffer)); + // if (bytesRead > 0) + // { + // buffer[bytesRead] = '\0'; + // printf("Received from pty: %s", buffer); + // } - close(master); - close(slave); + // close(master); + // close(slave); } void test_system() @@ -654,6 +652,7 @@ char *create_directory() return path; } +#ifndef __fennix__ void test_watch_file() { printf("- Testing file watching\n"); @@ -788,6 +787,7 @@ void reboot_linux() sync(); reboot(RB_AUTOBOOT); } +#endif // __fennix__ void test_stdin() { @@ -892,10 +892,12 @@ int main(int argc, char *argv[], char *envp[]) test_stat(); test_dirent(); test_execve_fork(); +#ifndef __fennix__ test_watch_file(); test_watch_directory(); - // shutdown_linux(); - // reboot_linux(); + shutdown_linux(); + reboot_linux(); +#endif int status = 0; pid_t wpid; diff --git a/Userspace/apps/user/Makefile b/Userspace/apps/user/Makefile deleted file mode 100644 index 2b9da7d6..00000000 --- a/Userspace/apps/user/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -build: - make -C games build - -clean: - make -C games clean diff --git a/Userspace/apps/user/games/Makefile b/Userspace/apps/user/games/Makefile deleted file mode 100644 index 8fd1bba2..00000000 --- a/Userspace/apps/user/games/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -build: -# make -C doomgeneric build - -clean: -# make -C doomgeneric clean diff --git a/Userspace/libc/ElfInterpreter/Makefile b/Userspace/libc/ElfInterpreter/Makefile deleted file mode 100644 index 91916abb..00000000 --- a/Userspace/libc/ElfInterpreter/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -# Config file -include ../../../config.mk - -NAME=ld - -OBJECT_NAME=$(NAME).so -SO_NAME=$(OBJECT_NAME) - -OUTPUT_DIR=../../out/lib/ -SYSROOT = --sysroot=../../out/ - -CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as -AR = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ar -OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump -NASM = /usr/bin/nasm - -C_SOURCES = $(shell find ./ -type f -name '*.c') -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') -S_SOURCES = $(shell find ./ -type f -name '*.S') -ASM_SOURCES = $(shell find ./ -type f -name '*.asm') -OBJ = ${C_SOURCES:.c=.o} ${CPP_SOURCES:.cpp=.o} ${ASM_SOURCES:.asm=.o} ${S_SOURCES:.S=.o} - -INCLUDE = ../include - -SIMD_FLAGS := -mno-sse -mno-sse2 -mno-sse3 -mno-ssse3 -mno-sse4.1 -mno-sse4.2 -mno-sse4 -mno-avx -mno-avx2 -mno-avx512f - -CFLAGS := -I$(INCLUDE) $(SIMD_FLAGS) -fPIC -LDFLAGS := -nostartfiles -nostdlib -Wl,-soname,$(SO_NAME) $(SYSROOT) -fno-pie -fno-PIC - -ifeq ($(OSARCH), amd64) -ASM_ARCH := elf64 -LDFLAGS += -Ttext=0xFFFFFFFFF0001000 -CFLAGS += -m64 -else ifeq ($(OSARCH), i386) -ASM_ARCH := elf32 -LDFLAGS += -fixme -CFLAGS += -m32 -endif - -ifeq ($(DEBUG), 1) - CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm - LDFLAGS += -ggdb3 -O0 -endif - -build: $(OBJECT_NAME) - -$(OBJECT_NAME): $(OBJ) - $(info Linking $@) - $(CC) $(LDFLAGS) $(OBJ) -o $(OUTPUT_DIR)$@ - $(OBJDUMP) -d $(OUTPUT_DIR)$@ > file_dump.map - -%.o: %.c - $(info Compiling $<) - $(CC) $(CFLAGS) -std=c17 -c $< -o $@ - -%.o: %.cpp - $(info Compiling $<) - $(CC) $(CFLAGS) -std=c++20 -c $< -o $@ - -%.o: %.S - $(info Compiling $<) - $(AS) -c $< -o $@ - -%.o: %.asm - $(info Compiling $<) - $(NASM) $< -f $(ASM_ARCH) -o $@ - -clean: - rm -f file_dump.map $(OBJ) diff --git a/Userspace/libc/ElfInterpreter/elf.h b/Userspace/libc/ElfInterpreter/elf.h deleted file mode 100644 index 97cde9dc..00000000 --- a/Userspace/libc/ElfInterpreter/elf.h +++ /dev/null @@ -1,296 +0,0 @@ -#ifndef __FENNIX_LIB_ELF_LAZY_RESOLVE_H__ -#define __FENNIX_LIB_ELF_LAZY_RESOLVE_H__ - -typedef __UINT32_TYPE__ Elf32_Addr; -typedef __UINT16_TYPE__ Elf32_Half; -typedef __UINT32_TYPE__ Elf32_Off; -typedef __INT32_TYPE__ Elf32_Sword; -typedef __UINT32_TYPE__ Elf32_Word; - -typedef __UINT64_TYPE__ Elf64_Addr; -typedef __UINT16_TYPE__ Elf64_Half; -typedef __INT16_TYPE__ Elf64_SHalf; -typedef __UINT64_TYPE__ Elf64_Off; -typedef __INT32_TYPE__ Elf64_Sword; -typedef __UINT32_TYPE__ Elf64_Word; -typedef __UINT64_TYPE__ Elf64_Xword; -typedef __INT64_TYPE__ Elf64_Sxword; - -enum SectionHeaderType -{ - SHT_NULL = 0, - SHT_PROGBITS = 1, - SHT_SYMTAB = 2, - SHT_STRTAB = 3, - SHT_RELA = 4, - SHT_HASH = 5, - SHT_DYNAMIC = 6, - SHT_NOTE = 7, - SHT_NOBITS = 8, - SHT_REL = 9, - SHT_SHLIB = 10, - SHT_DYNSYM = 11, - SHT_INIT_ARRAY = 14, - SHT_FINI_ARRAY = 15, - SHT_PREINIT_ARRAY = 16, - SHT_GROUP = 17, - SHT_SYMTAB_SHNDX = 18, - SHT_NUM = 19, - SHT_LOOS = 0x60000000, - SHT_GNU_ATTRIBUTES = 0x6ffffff5, - SHT_GNU_HASH = 0x6ffffff6, - SHT_GNU_LIBLIST = 0x6ffffff7, - SHT_CHECKSUM = 0x6ffffff8, - SHT_LOSUNW = 0x6ffffffa, - SHT_SUNW_move = 0x6ffffffa, - SHT_SUNW_COMDAT = 0x6ffffffb, - SHT_SUNW_syminfo = 0x6ffffffc, - SHT_GNU_verdef = 0x6ffffffd, - SHT_GNU_verneed = 0x6ffffffe, - SHT_GNU_versym = 0x6fffffff, - SHT_HISUNW = 0x6fffffff, - SHT_HIOS = 0x6fffffff, - SHT_LOPROC = 0x70000000, - SHT_HIPROC = 0x7fffffff, - SHT_LOUSER = 0x80000000, - SHT_HIUSER = 0x8fffffff -}; - -#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 EI_NIDENT 16 - -#define SHN_UNDEF 0 -#define SHN_ABS 0xfff1 - -enum DynamicTags -{ - DT_NULL = 0, - DT_NEEDED = 1, - DT_PLTRELSZ = 2, - DT_PLTGOT = 3, - DT_HASH = 4, - DT_STRTAB = 5, - DT_SYMTAB = 6, - DT_RELA = 7, - DT_RELASZ = 8, - DT_RELAENT = 9, - DT_STRSZ = 10, - DT_SYMENT = 11, - DT_INIT = 12, - DT_FINI = 13, - DT_SONAME = 14, - DT_RPATH = 15, - DT_SYMBOLIC = 16, - DT_REL = 17, - DT_RELSZ = 18, - DT_RELENT = 19, - DT_PLTREL = 20, - DT_DEBUG = 21, - DT_TEXTREL = 22, - DT_JMPREL = 23, - DT_BIND_NOW = 24, - DT_INIT_ARRAY = 25, - DT_FINI_ARRAY = 26, - DT_INIT_ARRAYSZ = 27, - DT_FINI_ARRAYSZ = 28, - DT_RUNPATH = 29, - DT_FLAGS = 30, - DT_ENCODING = 32, - DT_PREINIT_ARRAY = 32, - DT_PREINIT_ARRAYSZ = 33, - DT_LOOS = 0x6000000d, - DT_SUNW_RTLDINF = 0x6000000e, - DT_HIOS = 0x6ffff000, - DT_VALRNGLO = 0x6ffffd00, - DT_CHECKSUM = 0x6ffffdf8, - DT_PLTPADSZ = 0x6ffffdf9, - DT_MOVEENT = 0x6ffffdfa, - DT_MOVESZ = 0x6ffffdfb, - DT_FEATURE_1 = 0x6ffffdfc, - DT_POSFLAG_1 = 0x6ffffdfd, - DT_SYMINSZ = 0x6ffffdfe, - DT_SYMINENT = 0x6ffffdff, - DT_VALRNGHI = 0x6ffffdff, - DT_ADDRRNGLO = 0x6ffffe00, - DT_CONFIG = 0x6ffffefa, - DT_DEPAUDIT = 0x6ffffefb, - DT_AUDIT = 0x6ffffefc, - DT_PLTPAD = 0x6ffffefd, - DT_MOVETAB = 0x6ffffefe, - DT_SYMINFO = 0x6ffffeff, - DT_ADDRRNGHI = 0x6ffffeff, - DT_RELACOUNT = 0x6ffffff9, - DT_RELCOUNT = 0x6ffffffa, - DT_FLAGS_1 = 0x6ffffffb, - DT_VERDEF = 0x6ffffffc, - DT_VERDEFNUM = 0x6ffffffd, - DT_VERNEED = 0x6ffffffe, - DT_VERNEEDNUM = 0x6fffffff, - DT_LOPROC = 0x70000000, - DT_SPARC_REGISTER = 0x70000001, - DT_AUXILIARY = 0x7ffffffd, - DT_USED = 0x7ffffffe, - DT_FILTER = 0x7fffffff, - DT_HIPROC = 0x7fffffff -}; - -enum SegmentTypes -{ - PT_NULL = 0, - PT_LOAD = 1, - PT_DYNAMIC = 2, - PT_INTERP = 3, - PT_NOTE = 4, - PT_SHLIB = 5, - PT_PHDR = 6, - PT_TLS = 7, - PT_LOSUNW = 0x6ffffffa, - PT_SUNWBSS = 0x6ffffffb, - PT_SUNWSTACK = 0x6ffffffa, - PT_HISUNW = 0x6fffffff, - PT_LOPROC = 0x70000000, - PT_HIPROC = 0x7fffffff -}; - -enum RtT_Types -{ - R_386_NONE = 0, - R_386_32 = 1, - R_386_PC32 = 2, - - R_X86_64_NONE = 0, - R_X86_64_64 = 1, - R_X86_64_PC32 = 2, - R_X86_64_GOT32 = 3, - R_X86_64_PLT32 = 4, - R_X86_64_COPY = 5, - R_X86_64_GLOB_DAT = 6, - R_X86_64_JUMP_SLOT = 7, - R_X86_64_RELATIVE = 8, - R_X86_64_GOTPCREL = 9, - R_X86_64_32 = 10, - R_X86_64_32S = 11, - R_X86_64_16 = 12, -}; - -typedef struct elf32_hdr -{ - 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; -} Elf32_Ehdr; - -typedef struct elf64_hdr -{ - 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; -} Elf64_Ehdr; - -typedef struct elf32_shdr -{ - 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; -} Elf32_Shdr; - -typedef struct elf64_shdr -{ - 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; -} Elf64_Shdr; - -struct Elf32_Dyn -{ - Elf32_Sword d_tag; - union - { - Elf32_Word d_val; - Elf32_Addr d_ptr; - } d_un; -}; - -struct Elf64_Dyn -{ - Elf64_Sxword d_tag; - union - { - Elf64_Xword d_val; - Elf64_Addr d_ptr; - } d_un; -}; - -typedef 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; -} Elf64_Phdr; - -typedef struct -{ - Elf64_Addr r_offset; - Elf64_Xword r_info; - Elf64_Sxword r_addend; -} Elf64_Rela; - -typedef struct elf64_sym -{ - Elf64_Word st_name; - unsigned char st_info; - unsigned char st_other; - Elf64_Half st_shndx; - Elf64_Addr st_value; - Elf64_Xword st_size; -} Elf64_Sym; - -#endif // !__FENNIX_LIB_ELF_LAZY_RESOLVE_H__ diff --git a/Userspace/libc/ElfInterpreter/fcts.c b/Userspace/libc/ElfInterpreter/fcts.c deleted file mode 100644 index bc821149..00000000 --- a/Userspace/libc/ElfInterpreter/fcts.c +++ /dev/null @@ -1,227 +0,0 @@ -#include "fcts.h" - -#include "../../../Kernel/syscalls.h" -#include "../../../Kernel/ipc.h" - -uintptr_t RequestPages(size_t Count) -{ - return syscall6(sc_mmap, NULL, Count * 0x1000, - sc_PROT_READ | sc_PROT_WRITE, - sc_MAP_ANONYMOUS | sc_MAP_PRIVATE, - 0, 0); -} - -int FreePages(uintptr_t Address, size_t Count) -{ - return syscall2(sc_munmap, Address, Count * 0x1000); -} - -int abs(int i) { return i < 0 ? -i : i; } - -void swap(char *x, char *y) -{ - char t = *x; - *x = *y; - *y = t; -} - -char *reverse(char *Buffer, int i, int j) -{ - while (i < j) - swap(&Buffer[i++], &Buffer[j--]); - return Buffer; -} - -char *ltoa(long Value, char *Buffer, int Base) -{ - if (Base < 2 || Base > 32) - return Buffer; - - long n = (long)abs((int)Value); - int i = 0; - - while (n) - { - int r = n % Base; - if (r >= 10) - Buffer[i++] = 65 + (r - 10); - else - Buffer[i++] = 48 + r; - n = n / Base; - } - - if (i == 0) - Buffer[i++] = '0'; - - if (Value < 0 && Base == 10) - Buffer[i++] = '-'; - - Buffer[i] = '\0'; - return reverse(Buffer, 0, i - 1); -} - -void PutCharToKernelConsole(char c) -{ - __asm__ __volatile__("syscall" - : - : "a"(1), "D"(c), "S"(0) - : "rcx", "r11", "memory"); -} - -void Print__(char *String) -{ - for (short i = 0; String[i] != '\0'; i++) - PutCharToKernelConsole(String[i]); -} - -void PrintNL__(char *String) -{ - Print__(String); - Print__("\n"); -} - -void *memcpy(void *dest, const void *src, size_t n) -{ - uint8_t *d = dest; - const uint8_t *s = src; - while (n--) - *d++ = *s++; - return dest; -} - -void *memset(void *s, int c, size_t n) -{ - uint8_t *p = s; - while (n--) - *p++ = c; -} - -int strcmp(const char *l, const char *r) -{ - for (; *l == *r && *l; l++, r++) - ; - return *(unsigned char *)l - *(unsigned char *)r; -} - -struct Elf64_Dyn ELFGetDynamicTag(char *Path, enum DynamicTags Tag) -{ - int fd = syscall2(sc_open, Path, (long)"r"); - if (fd < 0) - syscall1(sc_exit, -0xF17E); - - Elf64_Ehdr ELFHeader; - syscall3(sc_read, fd, &ELFHeader, sizeof(Elf64_Ehdr)); - - Elf64_Phdr ItrProgramHeader; - for (Elf64_Half i = 0; i < ELFHeader.e_phnum; i++) - { - // memcpy(&ItrProgramHeader, (uint8_t *)ElfFile + ELFHeader.e_phoff + ELFHeader.e_phentsize * i, sizeof(Elf64_Phdr)); - syscall3(sc_lseek, fd, ELFHeader.e_phoff + ELFHeader.e_phentsize * i, sc_SEEK_SET); - syscall3(sc_read, fd, &ItrProgramHeader, sizeof(Elf64_Phdr)); - if (ItrProgramHeader.p_type == PT_DYNAMIC) - { - struct Elf64_Dyn Dynamic; // = (struct Elf64_Dyn *)((uint8_t *)ElfFile + ItrProgramHeader.p_offset); - syscall3(sc_lseek, fd, ItrProgramHeader.p_offset, sc_SEEK_SET); - syscall3(sc_read, fd, &Dynamic, ItrProgramHeader.p_filesz); - for (size_t i = 0; i < ItrProgramHeader.p_filesz / sizeof(struct Elf64_Dyn); i++) - { - if (Dynamic.d_tag == Tag || Dynamic.d_tag == DT_NULL) - { - syscall1(sc_close, fd); - return Dynamic; - } - - syscall3(sc_lseek, fd, ItrProgramHeader.p_offset + (i + 1) * sizeof(struct Elf64_Dyn), sc_SEEK_SET); - syscall3(sc_read, fd, &Dynamic, sizeof(struct Elf64_Dyn)); - } - } - } - syscall1(sc_close, fd); - return (struct Elf64_Dyn){0}; -} - -Elf64_Shdr *GetELFSheader(Elf64_Ehdr *Header) -{ - Elf64_Off SheaderOffset = Header->e_shoff; - return (Elf64_Shdr *)((uintptr_t)Header + SheaderOffset); -} - -Elf64_Shdr *GetELFSection(Elf64_Ehdr *Header, uint64_t Index) -{ - Elf64_Shdr *Sheader = GetELFSheader(Header); - return &Sheader[Index]; -} - -char *GetELFStringTable(Elf64_Ehdr *Header) -{ - if (Header->e_shstrndx == SHN_UNDEF) - return NULL; - return (char *)Header + GetELFSection(Header, Header->e_shstrndx)->sh_offset; -} - -Elf64_Sym ELFLookupSymbol(char *Path, const char *Name) -{ - int fd = syscall2(sc_open, Path, (long)"r"); - if (fd < 0) - syscall1(sc_exit, -0xF17E); - - Elf64_Ehdr ELFHeader; - syscall3(sc_read, fd, &ELFHeader, sizeof(Elf64_Ehdr)); - - Elf64_Shdr SymbolTable; - Elf64_Shdr StringTable; - Elf64_Sym Symbol; - char *String = NULL; - - for (Elf64_Half i = 0; i < ELFHeader.e_shnum; i++) - { - Elf64_Shdr shdr; - syscall3(sc_lseek, fd, - ELFHeader.e_shoff + ELFHeader.e_shentsize * i, - sc_SEEK_SET); - syscall3(sc_read, fd, &shdr, sizeof(Elf64_Shdr)); - - switch (shdr.sh_type) - { - case SHT_SYMTAB: - { - SymbolTable = shdr; - syscall3(sc_lseek, fd, - ELFHeader.e_shoff + ELFHeader.e_shentsize * shdr.sh_link, - sc_SEEK_SET); - syscall3(sc_read, fd, &StringTable, sizeof(Elf64_Shdr)); - break; - } - default: - { - break; - } - } - } - - if (SymbolTable.sh_size == 0 || StringTable.sh_size == 0) - { - syscall1(sc_close, fd); - return (Elf64_Sym){0}; - } - - for (size_t i = 0; i < (SymbolTable.sh_size / sizeof(Elf64_Sym)); i++) - { - // Symbol = (Elf64_Sym *)((uintptr_t)Header + SymbolTable->sh_offset + (i * sizeof(Elf64_Sym))); - // String = (char *)((uintptr_t)Header + StringTable->sh_offset + Symbol->st_name); - syscall3(sc_lseek, fd, SymbolTable.sh_offset + (i * sizeof(Elf64_Sym)), sc_SEEK_SET); - syscall3(sc_read, fd, &Symbol, sizeof(Elf64_Sym)); - - syscall3(sc_lseek, fd, StringTable.sh_offset + Symbol.st_name, sc_SEEK_SET); - syscall3(sc_read, fd, &String, sizeof(char *)); - - if (strcmp(String, Name) == 0) - { - syscall1(sc_close, fd); - return Symbol; - } - } - - syscall1(sc_close, fd); - return (Elf64_Sym){0}; -} diff --git a/Userspace/libc/ElfInterpreter/fcts.h b/Userspace/libc/ElfInterpreter/fcts.h deleted file mode 100644 index 9775f765..00000000 --- a/Userspace/libc/ElfInterpreter/fcts.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef __FENNIX_LIBC_FUNCTIONS_H__ -#define __FENNIX_LIBC_FUNCTIONS_H__ - -#include -#include "elf.h" - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -uintptr_t RequestPages(size_t Count); -int FreePages(uintptr_t Address, size_t Count); -int IPC(int Command, int Type, int ID, int Flags, void *Buffer, size_t Size); -uintptr_t KernelCTL(int Command, uint64_t Arg1, uint64_t Arg2, uint64_t Arg3, uint64_t Arg4); -int abs(int i); -void swap(char *x, char *y); -char *reverse(char *Buffer, int i, int j); -char *ltoa(long Value, char *Buffer, int Base); -void PutCharToKernelConsole(char c); -void Print__(char *String); -void PrintNL__(char *String); -void *memcpy(void *dest, const void *src, size_t n); -void *memset(void *s, int c, size_t n); -int strcmp(const char *l, const char *r); - -struct Elf64_Dyn ELFGetDynamicTag(char *Path, enum DynamicTags Tag); -Elf64_Shdr *GetELFSheader(Elf64_Ehdr *Header); -Elf64_Shdr *GetELFSection(Elf64_Ehdr *Header, uint64_t Index); -char *GetELFStringTable(Elf64_Ehdr *Header); -Elf64_Sym ELFLookupSymbol(char *Path, const char *Name); - -#endif // !__FENNIX_LIBC_FUNCTIONS_H__ diff --git a/Userspace/libc/ElfInterpreter/hash.c b/Userspace/libc/ElfInterpreter/hash.c deleted file mode 100644 index b00e843d..00000000 --- a/Userspace/libc/ElfInterpreter/hash.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "ld.h" - -uint32_t ElfHash(const unsigned char *Name) -{ - uint32_t i = 0, j; - while (*Name) - { - i = (i << 4) + *Name++; - if ((j = i & 0xF0000000) != 0) - i ^= j >> 24; - i &= ~j; - } - return i; -} diff --git a/Userspace/libc/ElfInterpreter/ld.c b/Userspace/libc/ElfInterpreter/ld.c deleted file mode 100644 index 79f031df..00000000 --- a/Userspace/libc/ElfInterpreter/ld.c +++ /dev/null @@ -1,324 +0,0 @@ -#include "ld.h" -#include "fcts.h" - -#include "../../../Kernel/syscalls.h" -#include "../../../Kernel/ipc.h" -#include "elf.h" - -#define Print(x) Print__(x) -#define PrintNL(x) PrintNL__(x) - -#if (1) -#define PrintDbg(x) Print__(x) -#define PrintDbgNL(x) PrintNL__(x) -#define ltoaDbg(x, y, z) ltoa(x, y, z) -#else -#define PrintDbg(x) -#define PrintDbgNL(x) -#define ltoaDbg(x, y, z) -#endif - -struct InterpreterIPCDataLibrary -{ - char Name[64]; -}; - -typedef struct -{ - char Path[256]; - void *MemoryImage; - struct InterpreterIPCDataLibrary Libraries[64]; -} InterpreterIPCData; - -struct LibsCollection -{ - char ParentName[32]; - char LibraryName[32]; - uintptr_t ParentMemoryImage; - uintptr_t LibraryMemoryImage; - struct LibsCollection *Next; - char Valid; -}; - -static char ParentPath[256]; -static char Lock = 0; - -__attribute__((naked, used, no_stack_protector)) void ELF_LAZY_RESOLVE_STUB() -{ - while (Lock == 1) - ; - __asm__ __volatile__("mfence\n"); - Lock = 1; - __asm__ __volatile__("pop %r11\n" - "pop %r10\n" - - "push %rdi\n" - "push %rsi\n" - "push %rdx\n" - "push %rcx\n" - "push %r8\n" - "push %r9\n" - - "mov %r11, %rdi\n" // Move the first argument to rdi (libs collection) - "mov %r10, %rsi\n" // Move the second argument to rsi (rel index) - - "call ELF_LAZY_RESOLVE_MAIN\n" - - "mov %rax, %r11\n" // Move the return value to r11 - - "pop %r9\n" - "pop %r8\n" - "pop %rcx\n" - "pop %rdx\n" - "pop %rsi\n" - "pop %rdi\n" - - "jmp *%r11\n"); // Jump to the return value -} - -void (*ELF_LAZY_RESOLVE_MAIN(struct LibsCollection *Info, long RelIndex))() -{ - if (!Info) - goto FailEnd; - - char DbgBuff[32]; - char LibraryPathBuffer[256]; - - struct LibsCollection *CurLib = Info; - PrintDbgNL("_______"); - /* The last entry is the null entry (Valid == false) - which determines the end of the list. */ - while (CurLib->Valid) - { - PrintDbg("-- "); - PrintDbg(LibraryPathBuffer); - PrintDbg(" "); - ltoaDbg(RelIndex, DbgBuff, 10); - PrintDbg(DbgBuff); - PrintDbgNL(" --"); - uintptr_t lib_BaseAddress = __UINTPTR_MAX__; - uintptr_t app_BaseAddress = __UINTPTR_MAX__; - - Elf64_Ehdr lib_Header; - Elf64_Ehdr app_Header; - - int fd_lib = syscall2(sc_open, LibraryPathBuffer, "r"); - int fd_app = syscall2(sc_open, ParentPath, "r"); - - if (fd_lib < 0) - { - PrintNL("Failed to open library"); - goto RetryNextLib; - } - - if (fd_app < 0) - { - PrintNL("Failed to open application"); - goto RetryNextLib; - } - - syscall3(sc_read, fd_lib, &lib_Header, sizeof(Elf64_Ehdr)); - syscall3(sc_read, fd_app, &app_Header, sizeof(Elf64_Ehdr)); - - Elf64_Phdr ItrProgramHeader; - - for (Elf64_Half i = 0; i < lib_Header.e_phnum; i++) - { - syscall3(sc_lseek, fd_lib, - lib_Header.e_phoff + - lib_Header.e_phentsize * i, - sc_SEEK_SET); - - syscall3(sc_read, fd_lib, &ItrProgramHeader, sizeof(Elf64_Phdr)); - - lib_BaseAddress = MIN(lib_BaseAddress, ItrProgramHeader.p_vaddr); - } - - for (Elf64_Half i = 0; i < app_Header.e_phnum; i++) - { - syscall3(sc_lseek, fd_app, - app_Header.e_phoff + - app_Header.e_phentsize * i, - sc_SEEK_SET); - - syscall3(sc_read, fd_app, &ItrProgramHeader, sizeof(Elf64_Phdr)); - - app_BaseAddress = MIN(app_BaseAddress, ItrProgramHeader.p_vaddr); - } - - struct Elf64_Dyn lib_JmpRel = ELFGetDynamicTag(LibraryPathBuffer, DT_JMPREL); - struct Elf64_Dyn lib_SymTab = ELFGetDynamicTag(LibraryPathBuffer, DT_SYMTAB); - struct Elf64_Dyn lib_StrTab = ELFGetDynamicTag(LibraryPathBuffer, DT_STRTAB); - - struct Elf64_Dyn app_JmpRel = ELFGetDynamicTag(ParentPath, DT_JMPREL); - struct Elf64_Dyn app_SymTab = ELFGetDynamicTag(ParentPath, DT_SYMTAB); - struct Elf64_Dyn app_StrTab = ELFGetDynamicTag(ParentPath, DT_STRTAB); - - if (!lib_JmpRel.d_tag == 0) - { - PrintNL("No DT_JMPREL"); - // goto RetryNextLib; - } - else if (RelIndex >= lib_JmpRel.d_un.d_val / - sizeof(Elf64_Rela)) - { - PrintNL("RelIndex is greater than the number of relocations"); - goto RetryNextLib; - } - - if (!lib_SymTab.d_tag == 0) - { - PrintNL("No DT_SYMTAB"); - goto RetryNextLib; - } - - if (!lib_StrTab.d_tag == 0) - { - PrintNL("No DT_STRTAB"); - goto RetryNextLib; - } - - if (!lib_SymTab.d_tag == 0 && - !lib_StrTab.d_tag == 0) - goto RetryNextLib; - - Elf64_Rela *_lib_JmpRel = (Elf64_Rela *)(CurLib->LibraryMemoryImage + (lib_JmpRel.d_un.d_ptr - lib_BaseAddress)); - Elf64_Sym *_lib_SymTab = (Elf64_Sym *)(CurLib->LibraryMemoryImage + (lib_SymTab.d_un.d_ptr - lib_BaseAddress)); - - Elf64_Rela *_app_JmpRel = (Elf64_Rela *)(CurLib->ParentMemoryImage + (app_JmpRel.d_un.d_ptr - app_BaseAddress)); - Elf64_Sym *_app_SymTab = (Elf64_Sym *)(CurLib->ParentMemoryImage + (app_SymTab.d_un.d_ptr - app_BaseAddress)); - - char *lib_DynStr = (char *)(CurLib->LibraryMemoryImage + (lib_StrTab.d_un.d_ptr - lib_BaseAddress)); - char *app_DynStr = (char *)(CurLib->ParentMemoryImage + (app_StrTab.d_un.d_ptr - app_BaseAddress)); - - Elf64_Rela *Rel = _app_JmpRel + RelIndex; - Elf64_Addr *GOTEntry = (Elf64_Addr *)(Rel->r_offset); - - int RelType = ELF64_R_TYPE(Rel->r_info); - - switch (RelType) - { - case R_X86_64_NONE: - { - PrintDbgNL("R_X86_64_NONE"); - if (*GOTEntry == 0) - { - PrintDbgNL("GOTEntry is 0"); - break; - } - Lock = 0; - return (void (*)()) * GOTEntry; - } - case R_X86_64_JUMP_SLOT: - { - PrintDbgNL("R_X86_64_JUMP_SLOT"); - int SymIndex = ELF64_R_SYM(Rel->r_info); - Elf64_Sym *Sym = _app_SymTab + SymIndex; - - if (Sym->st_name) - { - char *SymName = app_DynStr + Sym->st_name; - PrintDbg("SymName: "); - PrintDbgNL(SymName); - - Elf64_Sym LibSym = ELFLookupSymbol(ParentPath, SymName); - - PrintDbg("LibSym: 0x"); - ltoaDbg((long)LibSym.st_size, DbgBuff, 16); - PrintDbgNL(DbgBuff); - - if (LibSym.st_value) - { - *GOTEntry = (Elf64_Addr)(CurLib->LibraryMemoryImage + LibSym.st_value); - - ltoa(*GOTEntry, DbgBuff, 16); - PrintDbg("*GOTEntry: 0x"); - PrintDbgNL(DbgBuff); - - Lock = 0; - return (void (*)()) * GOTEntry; - } - PrintDbgNL("Not found in lib"); - } - break; - } - default: - { - ltoa(RelType, DbgBuff, 10); - Print("RelType not supported "); - PrintNL(DbgBuff); - break; - } - } - - RetryNextLib: - PrintDbgNL("Retrying next lib"); - CurLib = CurLib->Next; - } - -FailEnd: - Lock = 0; - __asm__ __volatile__("mfence\n"); - - Print("Symbol index "); - ltoa(RelIndex, DbgBuff, 10); - Print(DbgBuff); - PrintNL(" not found"); - int ExitCode = 0x51801; - syscall1(sc_exit, ExitCode); - while (1) // Make sure we don't return - ; -} - -/* Preload */ -int ld_main() -{ - /* Prevent race condition. */ - // uintptr_t KCTL_ret = KernelCTL(KCTL_IS_CRITICAL, 0, 0, 0, 0); - // do - // { - // syscall1(sys_Sleep, 250); - // KCTL_ret = KernelCTL(KCTL_IS_CRITICAL, 0, 0, 0, 0); - // } while (KCTL_ret == false); - - // if (KCTL_ret == false) - // return -1; - - /* Everything is ok, continue. */ - return 0; -} - -bool ELFAddLazyResolverToGOT(void *MemoryImage, struct LibsCollection *Libs) -{ - struct Elf64_Dyn Dyn = ELFGetDynamicTag(ParentPath, DT_PLTGOT); - if (!Dyn.d_tag) - return false; - - Elf64_Addr *GOT = (Elf64_Addr *)Dyn.d_un.d_ptr; - - GOT[1] = (uintptr_t)Libs; - GOT[2] = (uintptr_t)ELF_LAZY_RESOLVE_STUB; - return true; -} - -/* Actual load */ -int ld_load(int argc, char *argv[], char *envp[]) -{ - PrintDbgNL("Calling entry point"); - - // void *KP = syscall2(sc_open, ParentPath, (long)"r"); - // if (KP == NULL) - // { - // PrintNL("Failed to open file"); - // syscall1(sys_Exit, -0xF17E); - // } - - // Elf64_Ehdr ELFHeader; - // syscall3(sc_read, KP, &ELFHeader, sizeof(Elf64_Ehdr)); - - // Elf64_Addr Entry = ELFHeader.e_entry; - - // syscall1(sys_FileClose, KP); - - // return ((int (*)(int, char *[], char *[]))Entry)(argc, argv, envp); -} diff --git a/Userspace/libc/ElfInterpreter/ld.h b/Userspace/libc/ElfInterpreter/ld.h deleted file mode 100644 index 8012f848..00000000 --- a/Userspace/libc/ElfInterpreter/ld.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __FENNIX_LIBC_LD_H__ -#define __FENNIX_LIBC_LD_H__ - -#include - -uint32_t ElfHash(const unsigned char *Name); - -#endif // !__FENNIX_LIBC_LD_H__ diff --git a/Userspace/libc/ElfInterpreter/ldstart.c b/Userspace/libc/ElfInterpreter/ldstart.c deleted file mode 100644 index 6ea9af80..00000000 --- a/Userspace/libc/ElfInterpreter/ldstart.c +++ /dev/null @@ -1,36 +0,0 @@ -void __attribute__((naked, used, no_stack_protector)) _start() -{ - __asm__("movq $0, %rbp\n" - "pushq %rbp\n" - "pushq %rbp\n" - "movq %rsp, %rbp\n" - - "pushq %rcx\n" - "pushq %rdx\n" - "pushq %rsi\n" - "pushq %rdi\n" - - "call ld_main\n" - "movl %eax, %edi\n" // Move return value to edi - "cmp $0, %edi\n" // Check if return value is 0 - "jne _exit\n" // If not, jump to _exit - - "popq %rdi\n" - "popq %rsi\n" - "popq %rdx\n" - "popq %rcx\n" - - "call ld_load\n" - "movl %eax, %edi\n" // Move return value to edi - "call _exit"); // Call _exit -} - -void _exit(int Code) -{ - __asm__ __volatile__("syscall" - : - : "a"(0), "D"(Code) - : "rcx", "r11", "memory"); - while (1) - ; -} diff --git a/Userspace/libc/ElfInterpreter/unused_code b/Userspace/libc/ElfInterpreter/unused_code deleted file mode 100644 index b6ae3f4a..00000000 --- a/Userspace/libc/ElfInterpreter/unused_code +++ /dev/null @@ -1,250 +0,0 @@ - -/* This function is a mess and needs to be cleaned up. */ -bool ELFDynamicReallocation(void *ElfFile, void *MemoryImage) -{ - debug("ELF dynamic reallocation for image at %#lx.", ElfFile); - - Elf64_Ehdr *ELFHeader = (Elf64_Ehdr *)ElfFile; - uintptr_t BaseAddress = UINTPTR_MAX; - size_t ElfAppSize = 0; - Elf64_Phdr ItrPhdr; - - for (Elf64_Half i = 0; i < ELFHeader->e_phnum; i++) - { - memcpy(&ItrPhdr, - (uint8_t *)ElfFile + ELFHeader->e_phoff + ELFHeader->e_phentsize * i, - sizeof(Elf64_Phdr)); - BaseAddress = MIN(BaseAddress, ItrPhdr.p_vaddr); - } - - for (Elf64_Half i = 0; i < ELFHeader->e_phnum; i++) - { - memcpy(&ItrPhdr, - (uint8_t *)ElfFile + ELFHeader->e_phoff + ELFHeader->e_phentsize * i, - sizeof(Elf64_Phdr)); - uintptr_t SegmentEnd = ItrPhdr.p_vaddr - BaseAddress + ItrPhdr.p_memsz; - ElfAppSize = MAX(ElfAppSize, SegmentEnd); - } - - debug("BaseAddress: %#lx Size: %ld", BaseAddress, ElfAppSize); - - Elf64_Dyn *_GOTEntry = (Elf64_Dyn *)ELFGetDynamicTag(ElfFile, DT_PLTGOT); - Elf64_Dyn *_Rela = ELFGetDynamicTag(ElfFile, DT_RELA); - Elf64_Dyn *_RelaEnt = ELFGetDynamicTag(ElfFile, DT_RELAENT); - Elf64_Dyn *_JmpRel = ELFGetDynamicTag(ElfFile, DT_JMPREL); - Elf64_Dyn *_SymTab = ELFGetDynamicTag(ElfFile, DT_SYMTAB); - Elf64_Dyn *_StrTab = ELFGetDynamicTag(ElfFile, DT_STRTAB); - Elf64_Dyn *RelaSize = ELFGetDynamicTag(ElfFile, DT_RELASZ); - Elf64_Dyn *PltRelSize = ELFGetDynamicTag(ElfFile, DT_PLTRELSZ); - - Elf64_Addr *GOTEntry = (Elf64_Addr *)((uintptr_t)(_GOTEntry->d_un.d_ptr - BaseAddress) + (uintptr_t)MemoryImage); - Elf64_Dyn *Rela = (Elf64_Dyn *)((uintptr_t)(_Rela->d_un.d_ptr - BaseAddress) + (uintptr_t)MemoryImage); - Elf64_Dyn *RelaEnt = (Elf64_Dyn *)((uintptr_t)(_RelaEnt->d_un.d_ptr - BaseAddress) + (uintptr_t)MemoryImage); - Elf64_Dyn *JmpRel = (Elf64_Dyn *)((uintptr_t)(_JmpRel->d_un.d_ptr - BaseAddress) + (uintptr_t)MemoryImage); - Elf64_Dyn *SymTab = (Elf64_Dyn *)((uintptr_t)(_SymTab->d_un.d_ptr - BaseAddress) + (uintptr_t)MemoryImage); - Elf64_Dyn *StrTab = (Elf64_Dyn *)((uintptr_t)(_StrTab->d_un.d_ptr - BaseAddress) + (uintptr_t)MemoryImage); - - debug("GOTEntry: %#lx [%#lx]", _GOTEntry, GOTEntry); - debug("Rela: %#lx [%#lx]", _Rela, Rela); - debug("RelaEnt: %#lx [%#lx]", _RelaEnt, RelaEnt); - debug("JmpRel: %#lx [%#lx]", _JmpRel, JmpRel); - debug("SymTab: %#lx [%#lx]", _SymTab, SymTab); - debug("StrTab: %#lx [%#lx]", _StrTab, StrTab); - if (RelaSize) - debug("RelaSize: %ld", RelaSize->d_un.d_val); - if (PltRelSize) - debug("PltRelSize: %ld", PltRelSize->d_un.d_val); - - Elf64_Xword PltRelSizeVal = PltRelSize ? PltRelSize->d_un.d_val : 0; - Elf64_Xword RelaSizeVal = RelaSize ? RelaSize->d_un.d_val : 0; - - Elf64_Xword PltRelSizeValCount = PltRelSizeVal / sizeof(Elf64_Rela); - Elf64_Xword RelaSizeValCount = RelaSizeVal / sizeof(Elf64_Rela); - - debug("PltRelSizeVal: %ld", PltRelSizeVal); - debug("RelaSizeVal: %ld", RelaSizeVal); - debug("PltRelSizeValCount: %ld", PltRelSizeValCount); - debug("RelaSizeValCount: %ld", RelaSizeValCount); - - for (Elf64_Xword i = 0; i < PltRelSizeValCount; i++) - { - Elf64_Rela *RelaF = (Elf64_Rela *)((uintptr_t)JmpRel + i); - Elf64_Xword RelaType = ELF64_R_TYPE(RelaF->r_info); - debug("Itr %ld Type %ld", i, RelaType); - - switch (RelaType) - { - case R_X86_64_NONE: - { - debug("No relocation needed"); - break; - } - case R_X86_64_JUMP_SLOT: - { - debug("Relocation for jump slot"); - Elf64_Xword SymIndex = ELF64_R_SYM(RelaF->r_info); - Elf64_Sym *Sym = (Elf64_Sym *)((uintptr_t)SymTab + SymIndex); - char *SymName = (char *)((uintptr_t)StrTab + Sym->st_name); - debug("Symbol %s at %#lx", SymName, Sym->st_value); - - Elf64_Addr *GOTEntry = (Elf64_Addr *)RelaF->r_offset; - if (Sym->st_value) - { - fixme("Not implemented"); - *GOTEntry = (Elf64_Addr)ElfFile + Sym->st_value; - } - // else - // *GOTEntry = (Elf64_Addr)ElfLazyResolver; - - // Elf64_Sym *Sym = (Elf64_Sym *)((uintptr_t)ElfFile + (uintptr_t)SymTab + ELF64_R_SYM(RelaF->r_info) * sizeof(Elf64_Sym)); - // char *SymName = (char *)((uintptr_t)ElfFile + (uintptr_t)StrTab + Sym->st_name); - // void *SymAddr = (void *)Lib->Address + Sym->st_value; - // debug("Symbol %s at %#lx", SymName, SymAddr); - // *(void **)(RelaF->r_offset + (uintptr_t)ElfFile) = SymAddr; - break; - } - case R_X86_64_RELATIVE: - { - debug("Relative relocation"); - uintptr_t *Ptr = (uintptr_t *)((uintptr_t)ElfFile + RelaF->r_offset); - *Ptr = (uintptr_t)MemoryImage + RelaF->r_addend; - break; - } - default: - { - fixme("RelaType %d", RelaType); - break; - } - } - } - - for (Elf64_Xword i = 0; i < RelaSizeValCount; i++) - { - Elf64_Rela *RelaF = (Elf64_Rela *)((uintptr_t)ElfFile + (uintptr_t)Rela + i); - Elf64_Xword RelaType = ELF64_R_TYPE(RelaF->r_info); - debug("Itr %ld Type %ld", i, RelaType); - - switch (RelaType) - { - case R_X86_64_NONE: - { - debug("No relocation needed"); - break; - } - case R_X86_64_64: - { - debug("64-bit relocation"); - Elf64_Xword SymIndex = ELF64_R_SYM(RelaF->r_info); - Elf64_Sym *Sym = (Elf64_Sym *)((uintptr_t)ElfFile + (uintptr_t)SymTab + SymIndex); - char *SymName = (char *)((uintptr_t)ElfFile + (uintptr_t)StrTab + Sym->st_name); - debug("Symbol %s at %#lx", SymName, Sym->st_value); - - uintptr_t *Ptr = (uintptr_t *)((uintptr_t)ElfFile + RelaF->r_offset); - *Ptr = (uintptr_t)MemoryImage + Sym->st_value + RelaF->r_addend; - break; - } - case R_X86_64_GLOB_DAT: - { - debug("Global data relocation"); - Elf64_Xword SymIndex = ELF64_R_SYM(RelaF->r_info); - Elf64_Sym *Sym = (Elf64_Sym *)((uintptr_t)ElfFile + (uintptr_t)SymTab + SymIndex); - char *SymName = (char *)((uintptr_t)ElfFile + (uintptr_t)StrTab + Sym->st_name); - debug("Symbol %s at %#lx", SymName, Sym->st_value); - - uintptr_t *Ptr = (uintptr_t *)((uintptr_t)ElfFile + RelaF->r_offset); - *Ptr = (uintptr_t)MemoryImage + Sym->st_value; - break; - } - case R_X86_64_RELATIVE: - { - debug("Relative relocation"); - Elf64_Xword SymIndex = ELF64_R_SYM(RelaF->r_info); - Elf64_Sym *Sym = (Elf64_Sym *)((uintptr_t)ElfFile + (uintptr_t)SymTab + SymIndex); - char *SymName = (char *)((uintptr_t)ElfFile + (uintptr_t)StrTab + Sym->st_name); - debug("Symbol %s at %#lx", SymName, Sym->st_value); - - uintptr_t *Ptr = (uintptr_t *)((uintptr_t)ElfFile + RelaF->r_offset); - *Ptr = (uintptr_t)MemoryImage + RelaF->r_addend; - break; - } - default: - { - fixme("RelaType %d", RelaType); - break; - } - } - } - return true; -} - - - ELFDynamicReallocation(ElfFile, MemoryImage); - - LibAddressCollection *LibsForLazyResolver = (LibAddressCollection *)ELFBase.TmpMem->RequestPages(TO_PAGES(sizeof(LibAddressCollection)), true); - memset(LibsForLazyResolver, 0, sizeof(LibAddressCollection)); - LibAddressCollection *LFLRTmp = LibsForLazyResolver; - debug("LibsForLazyResolver: %#lx", LibsForLazyResolver); - - if (NeededLibraries.size() > 0) - { - VirtualFileSystem::Node *ParentNode = ExFile->Node->Parent; // Working Directory - if (ParentNode) - { - char *WorkingDirAbsolutePath = vfs->GetPathFromNode(ParentNode); - debug("Working directory: \"%s\"", WorkingDirAbsolutePath); - - int LibCount = 0; - foreach (auto Library in NeededLibraries) - { - char LibPath[256]; - strcpy(LibPath, WorkingDirAbsolutePath); - strcat(LibPath, "/"); - strcat(LibPath, Library); - debug("Searching for \"%s\"...", LibPath); - - bool AlreadyTried = false; - - LibPathRetry: - VirtualFileSystem::FILE *LibNode = vfs->Open(LibPath); - - if (LibNode->Status != VirtualFileSystem::FileStatus::OK) - { - vfs->Close(LibNode); - if (!AlreadyTried) - { - debug("Library \"%s\" not found, retrying... (%#x)", LibPath, LibNode->Status); - memset(LibPath, 0, 256); - strcpy(LibPath, "/lib/"); - strcat(LibPath, Library); - AlreadyTried = true; - goto LibPathRetry; - } - else - warn("Failed to load library \"%s\" (%#x)", Library, LibNode->Status); - } - else - { - debug("Library found \"%s\" (%#x)", LibPath, LibNode->Status); - SharedLibraries *sl = AddLibrary(Library, (void *)LibNode->Node->Address, LibNode->Node->Length); - strcpy(LFLRTmp->Name, Library); - LFLRTmp->ElfFile = (uintptr_t *)sl->Address; - LFLRTmp->MemoryImage = (uintptr_t *)sl->MemoryImage; - LFLRTmp->ParentElfFile = (uintptr_t *)ElfFile; - LFLRTmp->ParentMemoryImage = (uintptr_t *)MemoryImage; - LFLRTmp->Valid = true; - debug("LIBRARY: %s, %#lx, %#lx", Library, LFLRTmp->ElfFile, LFLRTmp->MemoryImage); - - LFLRTmp->Next = (LibAddressCollection *)ELFBase.TmpMem->RequestPages(TO_PAGES(sizeof(LibAddressCollection)), true); - LFLRTmp = LFLRTmp->Next; - memset(LFLRTmp, 0, sizeof(LibAddressCollection)); - } - } - } - else - { - error("Couldn't get the parent node from path %s", vfs->GetPathFromNode(ExFile->Node)); - } - } - - ELFAddLazyResolverToGOT(ElfFile, MemoryImage, LibsForLazyResolver); - diff --git a/Userspace/libc/Makefile b/Userspace/libc/Makefile index 69c3b79c..f2654b8c 100644 --- a/Userspace/libc/Makefile +++ b/Userspace/libc/Makefile @@ -1,11 +1,12 @@ build: - cp -r include/* ../out/include - cp ../Kernel/include/interface/syscalls.h ../out/include/fennix/syscall.h + cp -f $(WORKSPACE_DIR)/../Kernel/include/interface/errno.h $(CURDIR)/include/errno.h + cp -f $(WORKSPACE_DIR)/../Kernel/include/interface/syscalls.h $(CURDIR)/include/fennix/syscalls.h + cp -a $(CURDIR)/include/. $(WORKSPACE_DIR)/out/include + make -C interpreter build make -C runtime build make -C src build - make -C ElfInterpreter build clean: + make -C interpreter clean make -C runtime clean make -C src clean - make -C ElfInterpreter clean diff --git a/Userspace/libc/STATUS.md b/Userspace/libc/STATUS.md new file mode 100644 index 00000000..f730e51d --- /dev/null +++ b/Userspace/libc/STATUS.md @@ -0,0 +1,39 @@ +# FENNIX C LIBRARY STATUS + +[IEEE Std 1003.1™-2024 Edition](https://pubs.opengroup.org/onlinepubs/9799919799/) + +| FILE | STATUS | +|---------------|-----------| +| | | +| fennix/syscalls.h | PROVIDE | +| sys/mman.h | TODO | +| sys/stat.h | TODO | +| sys/types.h | TODO | +| sys/wait.h | TODO | +| assert.h | COMPLETE| +| dirent.h | TODO | +| errno.h | PROVIDE | +| fcntl.h | TODO | +| float.h | TODO | +| inttypes.h | TODO | +| limits.h | TODO | +| locale.h | TODO | +| math.h | TODO | +| pthread.h | TODO | +| pwd.h | TODO | +| sched.h | TODO | +| signal.h | TODO | +| stdarg.h | TODO | +| stdbool.h | COMPLETE| +| stddef.h | TODO | +| stdint.h | TODO | +| stdio.h | TODO | +| stdlib.h | TODO | +| string.h | TODO | +| time.h | TODO | +| unistd.h | TODO | + +--- + +[functions](https://pubs.opengroup.org/onlinepubs/9799919799/functions/) +[basedefs](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/) diff --git a/Userspace/libc/include/assert.h b/Userspace/libc/include/assert.h index 91ab8a44..bdb972cf 100644 --- a/Userspace/libc/include/assert.h +++ b/Userspace/libc/include/assert.h @@ -1,4 +1,43 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _ASSERT_H #define _ASSERT_H -#endif +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + void __assert_fail(const char *file, int line, const char *func) __attribute__((noreturn)); + +#ifdef NDEBUG +#define assert(e) ((void)0) +#else // NDEBUG +#define assert(e) \ + do \ + { \ + if (__builtin_expect(!!(!(e)), 0)) \ + __assert_fail(__FILE__, __LINE__, __func__); \ + } while (0) +#endif // NDEBUG + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_ASSERT_H diff --git a/Userspace/libc/include/aux.h b/Userspace/libc/include/aux.h deleted file mode 100644 index ef1d7588..00000000 --- a/Userspace/libc/include/aux.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef __FENNIX_LIBC_AUX_H__ -#define __FENNIX_LIBC_AUX_H__ - -#include - -#define AT_NULL 0 -#define AT_IGNORE 1 -#define AT_EXECFD 2 -#define AT_PHDR 3 -#define AT_PHENT 4 -#define AT_PHNUM 5 -#define AT_PAGESZ 6 -#define AT_BASE 7 -#define AT_FLAGS 8 -#define AT_ENTRY 9 -#define AT_NOTELF 10 -#define AT_UID 11 -#define AT_EUID 12 -#define AT_GID 13 -#define AT_EGID 14 -#define AT_PLATFORM 15 -#define AT_HWCAP 16 -#define AT_CLKTCK 17 -#define AT_SECURE 23 -#define AT_BASE_PLATFORM 24 -#define AT_RANDOM 25 -#define AT_HWCAP2 26 -#define AT_EXECFN 31 -#define AT_SYSINFO 32 -#define AT_SYSINFO_EHDR 33 -#define AT_L1I_CACHESHAPE 34 -#define AT_L1D_CACHESHAPE 35 -#define AT_L2_CACHESHAPE 36 -#define AT_L3_CACHESHAPE 37 -#define AT_L1I_CACHESIZE 40 -#define AT_L1I_CACHEGEOMETRY 41 -#define AT_L1D_CACHESIZE 42 -#define AT_L1D_CACHEGEOMETRY 43 -#define AT_L2_CACHESIZE 44 -#define AT_L2_CACHEGEOMETRY 45 -#define AT_L3_CACHESIZE 46 -#define AT_L3_CACHEGEOMETRY 47 -#define AT_MINSIGSTKSZ 51 - -typedef struct -{ - uint32_t a_type; - union - { - uint32_t a_val; - } a_un; -} Elf32_auxv_t; - -typedef struct -{ - uint64_t a_type; - union - { - uint64_t a_val; - } a_un; -} Elf64_auxv_t; - -#ifdef __LP64__ -#define Elf_auxv_t Elf64_auxv_t -#else -#define Elf_auxv_t Elf32_auxv_t -#endif - -#endif // !__FENNIX_LIBC_AUX_H__ diff --git a/Userspace/libc/include/bits/types/sigset_t.h b/Userspace/libc/include/bits/types/sigset_t.h deleted file mode 100644 index ff879ef9..00000000 --- a/Userspace/libc/include/bits/types/sigset_t.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _BITS_TYPES_SIGSET_T_H -#define _BITS_TYPES_SIGSET_T_H - -typedef unsigned long sigset_t; - -#endif // !_BITS_TYPES_SIGSET_T_H diff --git a/Userspace/libc/include/bits/types/struct_sched_param.h b/Userspace/libc/include/bits/types/struct_sched_param.h deleted file mode 100644 index dd77bc70..00000000 --- a/Userspace/libc/include/bits/types/struct_sched_param.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _BITS_TYPES_STRUCT_SCHED_PARAM_T_H -#define _BITS_TYPES_STRUCT_SCHED_PARAM_T_H - -struct sched_param -{ - int sched_priority; -}; - -#endif // !_BITS_TYPES_STRUCT_SCHED_PARAM_T_H diff --git a/Userspace/libc/include/bits/types/timespec.h b/Userspace/libc/include/bits/types/timespec.h new file mode 100644 index 00000000..55ce167a --- /dev/null +++ b/Userspace/libc/include/bits/types/timespec.h @@ -0,0 +1,38 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _TIMESPEC_DEFINED +#define _TIMESPEC_DEFINED + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include + + typedef struct timespec + { + time_t tv_sec; /* Whole seconds. */ + long tv_nsec; /* Nanoseconds [0, 999999999]. */ + } timespec; + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_TIMESPEC_DEFINED diff --git a/Userspace/libc/include/bits/waitstatus.h b/Userspace/libc/include/bits/waitstatus.h deleted file mode 100644 index bdec239c..00000000 --- a/Userspace/libc/include/bits/waitstatus.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _BITS_WAITSTATUS_H -#define _BITS_WAITSTATUS_H - -#define __W_CONTINUED 0xFFFF - -#define __WEXITSTATUS(status) (((status)&0xFF00) >> 8) -#define __WTERMSIG(status) ((status)&0x7F) -#define __WSTOPSIG(status) __WEXITSTATUS(status) -#define __WIFEXITED(status) (__WTERMSIG(status) == 0) -#define __WIFSIGNALED(status) (((signed char)(((status)&0x7F) + 1) >> 1) > 0) -#define __WIFSTOPPED(status) (((status)&0xFF) == 0x7F) -#define __WIFCONTINUED(status) ((status) == __W_CONTINUED) - -#endif diff --git a/Userspace/libc/include/ctype.h b/Userspace/libc/include/ctype.h deleted file mode 100644 index d8925e10..00000000 --- a/Userspace/libc/include/ctype.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _CTYPE_H -#define _CTYPE_H - -int tolower(int c); -int toupper(int c); -int isspace(int c); - -#endif // !_CTYPE_H diff --git a/Userspace/libc/include/dirent.h b/Userspace/libc/include/dirent.h new file mode 100644 index 00000000..80e02154 --- /dev/null +++ b/Userspace/libc/include/dirent.h @@ -0,0 +1,78 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _DIRENT_H +#define _DIRENT_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include + + typedef struct dirent + { + ino_t d_ino; /* File serial number. */ + char d_name[]; /* Filename string of entry. */ + } dirent; + + typedef struct posix_dent + { + ino_t d_ino; /* File serial number. */ + reclen_t d_reclen; /* Length of this entry, including trailing padding if necessary. See posix_getdents(). */ + unsigned char d_type; /* File type or unknown-file-type indication. */ + char d_name[]; /* Filename string of this entry. */ + } posix_dent; + + typedef struct DIR + { + int __fd; + } DIR; + +#define DT_BLK +#define DT_CHR +#define DT_DIR +#define DT_FIFO +#define DT_LNK +#define DT_REG +#define DT_SOCK +#define DT_UNKNOWN + +#define DT_MQ +#define DT_SEM +#define DT_SHM +#define DT_TMO + + int alphasort(const struct dirent **, const struct dirent **); + int closedir(DIR *); + int dirfd(DIR *); + DIR *fdopendir(int); + DIR *opendir(const char *); + ssize_t posix_getdents(int, void *, size_t, int); + struct dirent *readdir(DIR *); + int readdir_r(DIR *restrict, struct dirent *restrict, struct dirent **restrict); + void rewinddir(DIR *); + int scandir(const char *, struct dirent ***, int (*)(const struct dirent *), int (*)(const struct dirent **, const struct dirent **)); + void seekdir(DIR *, long); + long telldir(DIR *); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_DIRENT_H diff --git a/Userspace/libc/include/dlfcn.h b/Userspace/libc/include/dlfcn.h deleted file mode 100644 index 49752100..00000000 --- a/Userspace/libc/include/dlfcn.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __FENNIX_LIBC_DLFCN_H__ -#define __FENNIX_LIBC_DLFCN_H__ - -#include - -void *dlopen(const char *filename, int flags); -void *dlsym(void *handle, const char *symbol); -int dlclose(void *handle); -char *dlerror(void); - -#endif // !__FENNIX_LIBC_DLFCN_H__ diff --git a/Userspace/libc/include/errno.h b/Userspace/libc/include/errno.h index a1ac4004..ee601518 100644 --- a/Userspace/libc/include/errno.h +++ b/Userspace/libc/include/errno.h @@ -1,137 +1,605 @@ -#ifndef _ERRNO_H -#define _ERRNO_H +/* + This file is part of Fennix Kernel. -#define EPERM 1 /* Operation not permitted */ -#define ENOENT 2 /* No such file or directory */ -#define ESRCH 3 /* No such process */ -#define EINTR 4 /* Interrupted system call */ -#define EIO 5 /* I/O error */ -#define ENXIO 6 /* No such device or address */ -#define E2BIG 7 /* Argument list too long */ -#define ENOEXEC 8 /* Exec format error */ -#define EBADF 9 /* Bad file number */ -#define ECHILD 10 /* No child processes */ -#define EAGAIN 11 /* Try again */ -#define ENOMEM 12 /* Out of memory */ -#define EACCES 13 /* Permission denied */ -#define EFAULT 14 /* Bad address */ -#define ENOTBLK 15 /* Block device required */ -#define EBUSY 16 /* Device or resource busy */ -#define EEXIST 17 /* File exists */ -#define EXDEV 18 /* Cross-device link */ -#define ENODEV 19 /* No such device */ -#define ENOTDIR 20 /* Not a directory */ -#define EISDIR 21 /* Is a directory */ -#define EINVAL 22 /* Invalid argument */ -#define ENFILE 23 /* File table overflow */ -#define EMFILE 24 /* Too many open files */ -#define ENOTTY 25 /* Not a typewriter */ -#define ETXTBSY 26 /* Text file busy */ -#define EFBIG 27 /* File too large */ -#define ENOSPC 28 /* No space left on device */ -#define ESPIPE 29 /* Illegal seek */ -#define EROFS 30 /* Read-only file system */ -#define EMLINK 31 /* Too many links */ -#define EPIPE 32 /* Broken pipe */ -#define EDOM 33 /* Math argument out of domain of func */ -#define ERANGE 34 /* Math result not representable */ -#define EDEADLK 35 /* Resource deadlock would occur */ -#define ENAMETOOLONG 36 /* File name too long */ -#define ENOLCK 37 /* No record locks available */ -#define ENOSYS 38 /* Function not implemented */ -#define ENOTEMPTY 39 /* Directory not empty */ -#define ELOOP 40 /* Too many symbolic links encountered */ -#define ENOMSG 42 /* No message of desired type */ -#define EIDRM 43 /* Identifier removed */ -#define ECHRNG 44 /* Channel number out of range */ -#define EL2NSYNC 45 /* Level 2 not synchronized */ -#define EL3HLT 46 /* Level 3 halted */ -#define EL3RST 47 /* Level 3 reset */ -#define ELNRNG 48 /* Link number out of range */ -#define EUNATCH 49 /* Protocol driver not attached */ -#define ENOCSI 50 /* No CSI structure available */ -#define EL2HLT 51 /* Level 2 halted */ -#define EBADE 52 /* Invalid exchange */ -#define EBADR 53 /* Invalid request descriptor */ -#define EXFULL 54 /* Exchange full */ -#define ENOANO 55 /* No anode */ -#define EBADRQC 56 /* Invalid request code */ -#define EBADSLT 57 /* Invalid slot */ -#define EBFONT 59 /* Bad font file format */ -#define ENOSTR 60 /* Device not a stream */ -#define ENODATA 61 /* No data available */ -#define ETIME 62 /* Timer expired */ -#define ENOSR 63 /* Out of streams resources */ -#define ENONET 64 /* Machine is not on the network */ -#define ENOPKG 65 /* Package not installed */ -#define EREMOTE 66 /* Object is remote */ -#define ENOLINK 67 /* Link has been severed */ -#define EADV 68 /* Advertise error */ -#define ESRMNT 69 /* Srmount error */ -#define ECOMM 70 /* Communication error on send */ -#define EPROTO 71 /* Protocol error */ -#define EMULTIHOP 72 /* Multihop attempted */ -#define EDOTDOT 73 /* RFS specific error */ -#define EBADMSG 74 /* Not a data message */ -#define EOVERFLOW 75 /* Value too large for defined data type */ -#define ENOTUNIQ 76 /* Name not unique on network */ -#define EBADFD 77 /* File descriptor in bad state */ -#define EREMCHG 78 /* Remote address changed */ -#define ELIBACC 79 /* Can not access a needed shared library */ -#define ELIBBAD 80 /* Accessing a corrupted shared library */ -#define ELIBSCN 81 /* .lib section in a.out corrupted */ -#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ -#define ELIBEXEC 83 /* Cannot exec a shared library directly */ -#define EILSEQ 84 /* Illegal byte sequence */ -#define ERESTART 85 /* Interrupted system call should be restarted */ -#define ESTRPIPE 86 /* Streams pipe error */ -#define EUSERS 87 /* Too many users */ -#define ENOTSOCK 88 /* Socket operation on non-socket */ -#define EDESTADDRREQ 89 /* Destination address required */ -#define EMSGSIZE 90 /* Message too long */ -#define EPROTOTYPE 91 /* Protocol wrong type for socket */ -#define ENOPROTOOPT 92 /* Protocol not available */ -#define EPROTONOSUPPORT 93 /* Protocol not supported */ -#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ -#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ -#define EPFNOSUPPORT 96 /* Protocol family not supported */ -#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ -#define EADDRINUSE 98 /* Address already in use */ -#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ -#define ENETDOWN 100 /* Network is down */ -#define ENETUNREACH 101 /* Network is unreachable */ -#define ENETRESET 102 /* Network dropped connection because of reset */ -#define ECONNABORTED 103 /* Software caused connection abort */ -#define ECONNRESET 104 /* Connection reset by peer */ -#define ENOBUFS 105 /* No buffer space available */ -#define EISCONN 106 /* Transport endpoint is already connected */ -#define ENOTCONN 107 /* Transport endpoint is not connected */ -#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ -#define ETOOMANYREFS 109 /* Too many references: cannot splice */ -#define ETIMEDOUT 110 /* Connection timed out */ -#define ECONNREFUSED 111 /* Connection refused */ -#define EHOSTDOWN 112 /* Host is down */ -#define EHOSTUNREACH 113 /* No route to host */ -#define EALREADY 114 /* Operation already in progress */ -#define EINPROGRESS 115 /* Operation now in progress */ -#define ESTALE 116 /* Stale NFS file handle */ -#define EUCLEAN 117 /* Structure needs cleaning */ -#define ENOTNAM 118 /* Not a XENIX named type file */ -#define ENAVAIL 119 /* No XENIX semaphores available */ -#define EISNAM 120 /* Is a named type file */ -#define EREMOTEIO 121 /* Remote I/O error */ -#define EDQUOT 122 /* Quota exceeded */ -#define ENOMEDIUM 123 /* No medium found */ -#define EMEDIUMTYPE 124 /* Wrong medium type */ -#define ECANCELED 125 /* Operation Canceled */ -#define ENOKEY 126 /* Required key not available */ -#define EKEYEXPIRED 127 /* Key has expired */ -#define EKEYREVOKED 128 /* Key has been revoked */ -#define EKEYREJECTED 129 /* Key was rejected by service */ -#define EOWNERDEAD 130 /* Owner died */ -#define ENOTRECOVERABLE 131 /* State not recoverable */ + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_API_ERRNO_H__ +#define __FENNIX_API_ERRNO_H__ + +/** + * The documentation for these error codes are from: + * https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html + * + * Full list: + * https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/errno.h.html + */ +typedef enum +{ + /** + * No Error + */ + EOK = 0, + + /** + * Argument list too long. The sum of the number of bytes used by the + * new process image's argument list and environment list is greater + * than the system-imposed limit of {ARG_MAX} bytes. + * or: + * Lack of space in an output buffer. + * or: + * Argument is greater than the system-imposed maximum. + */ + E2BIG = 1, + + /** + * Permission denied. An attempt was made to access a file in a way + * forbidden by its file access permissions. + */ + EACCES = 2, + + /** + * Address in use. The specified address is in use. + */ + EADDRINUSE = 3, + + /** + * Address not available. The specified address is not available from + * the local system. + */ + EADDRNOTAVAIL = 4, + + /** + * Address family not supported. The implementation does not support + * the specified address family, or the specified address is not a + * valid address for the address family of the specified socket. + */ + EAFNOSUPPORT = 5, + + /** + * Resource temporarily unavailable. This is a temporary condition + * and later calls to the same routine may complete normally. + */ + EAGAIN = 6, + + /** + * Connection already in progress. A connection request is already in + * progress for the specified socket. + */ + EALREADY = 7, + + /** + * Bad file descriptor. A file descriptor argument is out of range, + * refers to no open file, or a read (write) request is made to a + * file that is only open for writing (reading). + */ + EBADF = 8, + + /** + * Bad message. During a read(), getmsg(), getpmsg(), or ioctl() + * I_RECVFD request to a STREAMS device, a message arrived at the + * head of the STREAM that is inappropriate for the function + * receiving the message. + * read() + * Message waiting to be read on a STREAM is not a data message. + * getmsg() or getpmsg() + * A file descriptor was received instead of a control message. + * ioctl() + * Control or data information was received instead of a file + * descriptor when I_RECVFD was specified. + */ + EBADMSG = 9, + + /** + * Resource busy. An attempt was made to make use of a system + * resource that is not currently available, as it is being + * used by another process in a manner that would have + * conflicted with the request being made by this process. + */ + EBUSY = 10, + + /** + * Operation canceled. The associated asynchronous operation was + * canceled before completion. + */ + ECANCELED = 11, + + /** + * No child process. A wait(), waitid(), or waitpid() function was + * executed by a process that had no existing or unwaited-for + * child process. + */ + ECHILD = 12, + + /** + * Connection aborted. The connection has been aborted. + */ + ECONNABORTED = 13, + + /** + * Connection refused. An attempt to connect to a socket was refused + * because there was no process listening or because the queue of + * connection requests was full and the underlying protocol does not + * support retransmissions. + */ + ECONNREFUSED = 14, + + /** + * Connection reset. The connection was forcibly closed by the peer. + */ + ECONNRESET = 15, + + /** + * Resource deadlock would occur. An attempt was made to lock a system + * resource that would have resulted in a deadlock situation. + */ + EDEADLK = 16, + + /** + * Destination address required. No bind address was established. + */ + EDESTADDRREQ = 17, + + /** + * Domain error. An input argument is outside the defined domain of the + * mathematical function (defined in the ISO C standard). + */ + EDOM = 18, + + /** + * Reserved. + */ + EDQUOT = 19, + + /** + * File exists. An existing file was mentioned in an inappropriate + * context; for example, as a new link name in the link() function. + */ + EEXIST = 20, + + /** + * Bad address. The system detected an invalid address in attempting + * to use an argument of a call. The reliable detection of this error + * cannot be guaranteed, and when not detected may result in the + * generation of a signal, indicating an address violation, which is + * sent to the process. + */ + EFAULT = 21, + + /** + * File too large. The size of a file would exceed the maximum file + * size of an implementation or offset maximum established in the + * corresponding file description. + */ + EFBIG = 22, + + /** + * Host is unreachable. The destination host cannot be reached + * (probably because the host is down or a remote router cannot + * reach it). + */ + EHOSTUNREACH = 23, + + /** + * Identifier removed. Returned during XSI interprocess communication + * if an identifier has been removed from the system. + */ + EIDRM = 24, + + /** + * Illegal byte sequence. A wide-character code has been detected that + * does not correspond to a valid character, or a byte sequence does + * not form a valid wide-character code (defined in the ISO C standard). + */ + EILSEQ = 25, + + /** + * Operation in progress. This code is used to indicate that an + * asynchronous operation has not yet completed. + * or: + * O_NONBLOCK is set for the socket file descriptor and the connection + * cannot be immediately established. + */ + EINPROGRESS = 26, + + /** + * Interrupted function call. An asynchronous signal was caught by the + * process during the execution of an interruptible function. If the + * signal handler performs a normal return, the interrupted function + * call may return this condition (see the Base Definitions volume + * of POSIX.1-2017, ). + */ + EINTR = 27, + + /** + * Invalid argument. Some invalid argument was supplied; for example, + * specifying an undefined signal in a signal() function or a + * kill() function. + */ + EINVAL = 28, + + /** + * Input/output error. Some physical input or output error has occurred. + * This error may be reported on a subsequent operation on the same + * file descriptor. Any other error-causing operation on the same file + * descriptor may cause the [EIO] error indication to be lost. + */ + EIO = 29, + + /** + * Socket is connected. The specified socket is already connected. + */ + EISCONN = 30, + + /** + * Is a directory. An attempt was made to open a directory with write + * mode specified. + */ + EISDIR = 31, + + /** + * Symbolic link loop. A loop exists in symbolic links encountered + * during pathname resolution. This error may also be returned if + * more than {SYMLOOP_MAX} symbolic links are encountered during + * pathname resolution. + */ + ELOOP = 32, + + /** + * File descriptor value too large or too many open streams. An + * attempt was made to open a file descriptor with a value greater + * than or equal to {OPEN_MAX}, or an attempt was made to open more + * than the maximum number of streams allowed in the process. + */ + EMFILE = 33, + + /** + * Too many links. An attempt was made to have the link count of a + * single file exceed {LINK_MAX}. + */ + EMLINK = 34, + + /** + * Message too large. A message sent on a transport provider was + * larger than an internal message buffer or some other network limit. + * or: + * Inappropriate message buffer length. + */ + EMSGSIZE = 35, + + /** + * Reserved. + */ + EMULTIHOP = 36, + + /** + * Filename too long. The length of a pathname exceeds {PATH_MAX} and + * the implementation considers this to be an error, or a pathname + * component is longer than {NAME_MAX}. This error may also occur + * when pathname substitution, as a result of encountering a + * symbolic link during pathname resolution, results in a pathname + * string the size of which exceeds {PATH_MAX}. + */ + ENAMETOOLONG = 37, + + /** + * Network is down. The local network interface used to reach the + * destination is down. + */ + ENETDOWN = 38, + + /** + * The connection was aborted by the network. + */ + ENETRESET = 39, + + /** + * Network unreachable. No route to the network is present. + */ + ENETUNREACH = 40, + + /** + * Too many files open in system. Too many files are currently open + * in the system. The system has reached its predefined limit for + * simultaneously open files and temporarily cannot accept requests + * to open another one. + */ + ENFILE = 41, + + /** + * No buffer space available. Insufficient buffer resources were + * available in the system to perform the socket operation. + */ + ENOBUFS = 42, + + /** + * No message available. No message is available on the STREAM head + * read queue. + */ + ENODATA = 43, + + /** + * No such device. An attempt was made to apply an inappropriate + * function to a device; for example, trying to read a write-only + * device such as a printer. + */ + ENODEV = 44, + + /** + * No such file or directory. A component of a specified pathname + * does not exist, or the pathname is an empty string. + */ + ENOENT = 45, + + /** + * Executable file format error. A request is made to execute a file + * that, although it has appropriate privileges, is not in the + * format required by the implementation for executable files. + */ + ENOEXEC = 46, + + /** + * No locks available. A system-imposed limit on the number of + * simultaneous file and record locks has been reached and no more + * are currently available. + */ + ENOLCK = 47, + + /** + * Reserved. + */ + ENOLINK = 48, + + /** + * Not enough space. The new process image requires more memory than + * is allowed by the hardware or system-imposed memory management + * constraints. + */ + ENOMEM = 49, + + /** + * No message of the desired type. The message queue does not contain + * a message of the required type during XSI interprocess communication. + */ + ENOMSG = 50, + + /** + * Protocol not available. The protocol option specified to + * setsockopt() is not supported by the implementation. + */ + ENOPROTOOPT = 51, + + /** + * No space left on a device. During the write() function on a + * regular file or when extending a directory, there is no free + * space left on the device. + */ + ENOSPC = 52, + + /** + * No STREAM resources. Insufficient STREAMS memory resources are + * available to perform a STREAMS-related function. This is a + * temporary condition; it may be recovered from if other + * processes release resources. + */ + ENOSR = 53, + + /** + * Not a STREAM. A STREAM function was attempted on a file descriptor + * that was not associated with a STREAMS device. + */ + ENOSTR = 54, + + /** + * Functionality not supported. An attempt was made to use optional + * functionality that is not supported in this implementation. + */ + ENOSYS = 55, + + /** + * Socket not connected. The socket is not connected. + */ + ENOTCONN = 56, + + /** + * Not a directory. A component of the specified pathname exists, but + * it is not a directory, when a directory was expected; or an + * attempt was made to create a non-directory file, and the specified + * pathname contains at least one non- \ character and ends + * with one or more trailing \ characters. + */ + ENOTDIR = 57, + + /** + * Directory not empty. A directory other than an empty directory + * was supplied when an empty directory was expected. + */ + ENOTEMPTY = 58, + + /** + * State not recoverable. The state protected by a robust mutex + * is not recoverable. + */ + ENOTRECOVERABLE = 59, + + /** + * Not a socket. The file descriptor does not refer to a socket. + */ + ENOTSOCK = 60, + + /** + * Not supported. The implementation does not support the requested + * feature or value. + */ + ENOTSUP = 61, + + /** + * Inappropriate I/O control operation. A control function has been + * attempted for a file or special file for which the operation + * is inappropriate. + */ + ENOTTY = 62, + + /** + * No such device or address. Input or output on a special file + * refers to a device that does not exist, or makes a request + * beyond the capabilities of the device. It may also occur when, + * for example, a tape drive is not on-line. + */ + ENXIO = 63, + + /** + * Operation not supported on socket. The type of socket (address + * family or protocol) does not support the requested operation. + */ + EOPNOTSUPP = 64, + + /** + * Value too large to be stored in data type. An operation was + * attempted which would generate a value that is outside the + * range of values that can be represented in the relevant data + * type or that are allowed for a given data item. + */ + EOVERFLOW = 65, + + /** + * Previous owner died. The owner of a robust mutex terminated + * while holding the mutex lock. + */ + EOWNERDEAD = 66, + + /** + * Operation not permitted. An attempt was made to perform an + * operation limited to processes with appropriate privileges or + * to the owner of a file or other resource. + */ + EPERM = 67, + + /** + * Broken pipe. A write was attempted on a socket, pipe, or FIFO + * for which there is no process to read the data. + */ + EPIPE = 68, + + /** + * Protocol error. Some protocol error occurred. This error is + * device-specific, but is generally not related to a + * hardware failure. + */ + EPROTO = 69, + + /** + * Protocol not supported. The protocol is not supported by the + * address family, or the protocol is not supported by + * the implementation. + */ + EPROTONOSUPPORT = 70, + + /** + * Protocol wrong type for socket. The socket type is not + * supported by the protocol. + */ + EPROTOTYPE = 71, + + /** + * Result too large or too small. The result of the function + * is too large (overflow) or too small (underflow) to be + * represented in the available space. + */ + ERANGE = 72, + + /** + * Read-only file system. An attempt was made to modify a file + * or directory on a file system that is read-only. + */ + EROFS = 73, + + /** + * Invalid seek. An attempt was made to access the file offset + * associated with a pipe or FIFO. + */ + ESPIPE = 74, + + /** + * No such process. No process can be found corresponding to that + * specified by the given process ID. + */ + ESRCH = 75, + + /** + * Reserved. + */ + ESTALE = 76, + + /** + * STREAM ioctl() timeout. The timer set for a STREAMS ioctl() call + * has expired. The cause of this error is device-specific and could + * indicate either a hardware or software failure, or a timeout + * value that is too short for the specific operation. The status + * of the ioctl() operation is unspecified. + */ + ETIME = 77, + + /** + * Connection timed out. The connection to a remote machine has + * timed out. + * If the connection timed out during execution of the function that + * reported this error (as opposed to timing out prior to the + * function being called), it is unspecified whether the function + * has completed some or all of the documented behavior associated + * with a successful completion of the function. + * or: + * Operation timed out. The time limit associated with the operation + * was exceeded before the operation completed. + */ + ETIMEDOUT = 78, + + /** + * Text file busy. An attempt was made to execute a pure-procedure + * program that is currently open for writing, or an attempt has + * been made to open for writing a pure-procedure program that + * is being executed. + */ + ETXTBSY = 79, + + /** + * Operation would block. An operation on a socket marked as + * non-blocking has encountered a situation such as no data available + * that otherwise would have caused the function to suspend execution. + */ + EWOULDBLOCK = 80, + + /** + * Improper link. A link to a file on another file system was attempted. + */ + EXDEV = 81, + + __ERRNO_MAX +} KernelErrors; + +#ifdef __cplusplus +extern "C" +{ +#endif + + int *__errno_location(void) __attribute__((const)); + char *strerror(int errnum); + +#ifdef __cplusplus +} +#endif -extern int *__errno_location(void) __attribute__((const)) __attribute__ ((__nothrow__)); #define errno (*__errno_location()) -#endif +#endif // !__FENNIX_API_ERRNO_H__ diff --git a/Userspace/libc/include/fcntl.h b/Userspace/libc/include/fcntl.h index 38b7f7ce..039155b8 100644 --- a/Userspace/libc/include/fcntl.h +++ b/Userspace/libc/include/fcntl.h @@ -1,4 +1,108 @@ -#ifndef _FNCTL_H -#define _FNCTL_H +/* + This file is part of Fennix C Library. -#endif // !_FNCTL_H + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _FCNTL_H +#define _FCNTL_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + typedef struct f_owner_ex + { + int type; /* Discriminator for pid. */ + pid_t pid; /* Process ID or process group ID. */ + } f_owner_ex; + + typedef struct flock + { + short l_type; /* Type of lock; F_RDLCK, F_WRLCK, F_UNLCK. */ + short l_whence; /* Flag for starting offset. */ + off_t l_start; /* Relative offset in bytes. */ + off_t l_len; /* Size; if 0 then until EOF. */ + pid_t l_pid; /* For a process-owned file lock, ignored on input or the process ID of the owning process on output; for an OFD-owned file lock, zero on input or (pid_t)-1 on output. */ + } flock; + +#define F_DUPFD +#define F_DUPFD_CLOEXEC +#define F_DUPFD_CLOFORK +#define F_GETFD +#define F_SETFD +#define F_GETFL +#define F_SETFL +#define F_GETLK +#define F_SETLK +#define F_SETLKW +#define F_OFD_GETLK +#define F_OFD_SETLK +#define F_OFD_SETLKW +#define F_GETOWN +#define F_GETOWN_EX +#define F_SETOWN +#define F_SETOWN_EX +#define FD_CLOEXEC +#define FD_CLOFORK +#define F_RDLCK +#define F_UNLCK +#define F_WRLCK +#define F_OWNER_PID +#define F_OWNER_PGRP +#define O_CLOEXEC 02000000 +#define O_CLOFORK +#define O_CREAT 0100 +#define O_DIRECTORY +#define O_EXCL 0200 +#define O_NOCTTY +#define O_NOFOLLOW 0400000 +#define O_TRUNC 01000 +#define O_TTY_INIT +#define O_APPEND 02000 +#define O_DSYNC +#define O_NONBLOCK +#define O_RSYNC +#define O_SYNC +#define O_ACCMODE +#define O_EXEC +#define O_RDONLY 00 +#define O_RDWR 02 +#define O_SEARCH +#define O_WRONLY 01 +#define AT_FDCWD +#define AT_EACCESS +#define AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_FOLLOW +#define AT_REMOVEDIR +#define POSIX_FADV_DONTNEED +#define POSIX_FADV_NOREUSE +#define POSIX_FADV_NORMAL +#define POSIX_FADV_RANDOM +#define POSIX_FADV_SEQUENTIAL +#define POSIX_FADV_WILLNEED + + int creat(const char *, mode_t); + int fcntl(int, int, ...); + int open(const char *, int, ...); + int openat(int, const char *, int, ...); + int posix_fadvise(int, off_t, off_t, int); + int posix_fallocate(int, off_t, off_t); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_FCNTL_H diff --git a/Userspace/libc/include/features.h b/Userspace/libc/include/features.h deleted file mode 100644 index ce82341c..00000000 --- a/Userspace/libc/include/features.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _FEATURES_H -#define _FEATURES_H - -#define __FENNIX_LIBC__ 1 - -#endif diff --git a/Userspace/libc/include/fennix/.gitkeep b/Userspace/libc/include/fennix/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Userspace/libc/include/fennix/syscalls.h b/Userspace/libc/include/fennix/syscalls.h new file mode 100644 index 00000000..bde19d03 --- /dev/null +++ b/Userspace/libc/include/fennix/syscalls.h @@ -0,0 +1,1572 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifndef __FENNIX_API_SYSCALLS_LIST_H__ +#define __FENNIX_API_SYSCALLS_LIST_H__ + +#pragma region Syscall Wrappers + +#define scarg __UINTPTR_TYPE__ + +/** + * @brief Syscall wrapper with 0 arguments + * + * @details This wrapper is used to call syscalls with 0 arguments + * + * @param syscall #syscalls_t + * @return The return value of the syscall + */ +static inline scarg syscall0(scarg syscall) +{ + scarg ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall) + : "rcx", "r11", "memory"); + return ret; +} + +/** + * @brief Syscall wrapper with 1 argument + * + * @details This wrapper is used to call syscalls with 1 argument + * + * @param syscall #syscalls_t + * @param arg1 Argument 1 + * @return The return value of the syscall + */ +static inline scarg syscall1(scarg syscall, scarg arg1) +{ + scarg ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1) + : "rcx", "r11", "memory"); + return ret; +} + +/** + * @brief Syscall wrapper with 2 arguments + * + * @details This wrapper is used to call syscalls with 2 arguments + * + * @param syscall #syscalls_t + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @return The return value of the syscall + */ +static inline scarg syscall2(scarg syscall, scarg arg1, scarg arg2) +{ + scarg ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2) + : "rcx", "r11", "memory"); + return ret; +} + +/** + * @brief Syscall wrapper with 3 arguments + * + * @details This wrapper is used to call syscalls with 3 arguments + * + * @param syscall #syscalls_t + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @param arg3 Argument 3 + * @return The return value of the syscall + */ +static inline scarg syscall3(scarg syscall, scarg arg1, scarg arg2, scarg arg3) +{ + scarg ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3) + : "rcx", "r11", "memory"); + return ret; +} + +/** + * @brief Syscall wrapper with 4 arguments + * + * @details This wrapper is used to call syscalls with 4 arguments + * + * @param syscall #syscalls_t + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @param arg3 Argument 3 + * @param arg4 Argument 4 + * @return The return value of the syscall + */ +static inline scarg syscall4(scarg syscall, scarg arg1, scarg arg2, scarg arg3, scarg arg4) +{ + scarg ret; + register scarg r10 __asm__("r10") = arg4; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10) + : "rcx", "r11", "memory"); + return ret; +} + +/** + * @brief Syscall wrapper with 5 arguments + * + * @details This wrapper is used to call syscalls with 5 arguments + * + * @param syscall #syscalls_t + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @param arg3 Argument 3 + * @param arg4 Argument 4 + * @param arg5 Argument 5 + * @return The return value of the syscall + */ +static inline scarg syscall5(scarg syscall, scarg arg1, scarg arg2, scarg arg3, scarg arg4, scarg arg5) +{ + scarg ret; + register scarg r10 __asm__("r10") = arg4; + register scarg r8 __asm__("r8") = arg5; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8) + : "rcx", "r11", "memory"); + return ret; +} + +/** + * @brief Syscall wrapper with 6 arguments + * + * @details This wrapper is used to call syscalls with 6 arguments + * + * @param syscall #syscalls_t + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @param arg3 Argument 3 + * @param arg4 Argument 4 + * @param arg5 Argument 5 + * @param arg6 Argument 6 + * @return The return value of the syscall + */ +static inline scarg syscall6(scarg syscall, scarg arg1, scarg arg2, scarg arg3, scarg arg4, scarg arg5, scarg arg6) +{ + scarg ret; + register scarg r10 __asm__("r10") = arg4; + register scarg r8 __asm__("r8") = arg5; + register scarg r9 __asm__("r9") = arg6; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8), "r"(r9) + : "rcx", "r11", "memory"); + return ret; +} + +#pragma endregion Syscall Wrappers + +/** + * @brief NULL pointer + * + * This is a pointer to address 0, which is reserved and cannot be dereferenced. + * + * @note This macro is defined only for this documentation. + */ +#define __SYS_NULL ((void *)0) + +typedef enum +{ + __SYS_PROT_READ = 0x1, + __SYS_PROT_WRITE = 0x2, + __SYS_PROT_EXEC = 0x4, + __SYS_PROT_NONE = 0x0, + + __SYS_MAP_SHARED = 0x1, + __SYS_MAP_PRIVATE = 0x2, + __SYS_MAP_FIXED = 0x4, + __SYS_MAP_ANONYMOUS = 0x8, + __SYS_MAP_ANON = __SYS_MAP_ANONYMOUS +} syscall_mmap_flags_t; + +typedef enum +{ + __SYS_O_RDONLY = 0x1, + __SYS_O_WRONLY = 0x2, + __SYS_O_RDWR = 0x3, + __SYS_O_APPEND = 0x4, + __SYS_O_CREAT = 0x8, + __SYS_O_DSYNC = 0x10, + __SYS_O_EXCL = 0x20, + __SYS_O_NOCTTY = 0x40, + __SYS_O_NONBLOCK = 0x80, + __SYS_O_RSYNC = 0x100, + __SYS_O_SYNC = 0x200, + __SYS_O_TRUNC = 0x400 +} syscall_open_flags_t; + +typedef enum +{ + __SYS_F_OK = 0, + __SYS_R_OK = 1, + __SYS_W_OK = 2, + __SYS_X_OK = 3 +} syscall_access_flags_t; + +typedef enum +{ + __SYS_GET_GS = 0, + __SYS_SET_GS = 1, + __SYS_GET_FS = 2, + __SYS_SET_FS = 3, +} syscall_prctl_options_t; +#ifdef __kernel__ +typedef syscall_prctl_options_t prctl_options_t; +#endif + +typedef enum +{ + __SYS_SEEK_SET = 0, + __SYS_SEEK_CUR = 1, + __SYS_SEEK_END = 2 +} syscall_seek_whence_t; + +typedef enum +{ + __SYS_SIGNULL = 0, + /* Process abort signal. */ + __SYS_SIGABRT = 1, + /* Alarm clock. */ + __SYS_SIGALRM = 2, + /* Access to an undefined portion of a memory object. */ + __SYS_SIGBUS = 3, + /* Child process terminated, stopped, or continued. */ + __SYS_SIGCHLD = 4, + /* Continue executing, if stopped. */ + __SYS_SIGCONT = 5, + /* Erroneous arithmetic operation. */ + __SYS_SIGFPE = 6, + /* Hangup. */ + __SYS_SIGHUP = 7, + /* Illegal instruction. */ + __SYS_SIGILL = 8, + /* Terminal interrupt signal. */ + __SYS_SIGINT = 9, + /* Kill (cannot be caught or ignored). */ + __SYS_SIGKILL = 10, + /* Write on a pipe with no one to read it. */ + __SYS_SIGPIPE = 11, + /* Terminal quit signal. */ + __SYS_SIGQUIT = 12, + /* Invalid memory reference. */ + __SYS_SIGSEGV = 13, + /* Stop executing (cannot be caught or ignored). */ + __SYS_SIGSTOP = 14, + /* Termination signal. */ + __SYS_SIGTERM = 15, + /* Terminal stop signal. */ + __SYS_SIGTSTP = 16, + /* Background process attempting read. */ + __SYS_SIGTTIN = 17, + /* Background process attempting write. */ + __SYS_SIGTTOU = 18, + /* User-defined signal 1. */ + __SYS_SIGUSR1 = 19, + /* User-defined signal 2. */ + __SYS_SIGUSR2 = 20, + /* Pollable event. */ + __SYS_SIGPOLL = 21, + /* Profiling timer expired. */ + __SYS_SIGPROF = 22, + /* Bad system call. */ + __SYS_SIGSYS = 23, + /* Trace/breakpoint trap. */ + __SYS_SIGTRAP = 24, + /* High bandwidth data is available at a socket. */ + __SYS_SIGURG = 25, + /* Virtual timer expired. */ + __SYS_SIGVTALRM = 26, + /* CPU time limit exceeded. */ + __SYS_SIGXCPU = 27, + /* File size limit exceeded. */ + __SYS_SIGXFSZ = 28, + + /** + * Reserved + * These are just to match Linux's signal numbers. + */ + __SYS_SIGCOMP1 = 29, + __SYS_SIGCOMP2 = 30, + __SYS_SIGCOMP3 = 31, + + /* Real-time signals. */ + __SYS_SIGRTMIN = 32, + __SYS_SIGRT_1 = 33, + __SYS_SIGRT_2 = 34, + __SYS_SIGRT_3 = 35, + __SYS_SIGRT_4 = 36, + __SYS_SIGRT_5 = 37, + __SYS_SIGRT_6 = 38, + __SYS_SIGRT_7 = 39, + __SYS_SIGRT_8 = 40, + __SYS_SIGRT_9 = 41, + __SYS_SIGRT_10 = 42, + __SYS_SIGRT_11 = 43, + __SYS_SIGRT_12 = 44, + __SYS_SIGRT_13 = 45, + __SYS_SIGRT_14 = 46, + __SYS_SIGRT_15 = 47, + __SYS_SIGRT_16 = 48, + __SYS_SIGRT_17 = 49, + __SYS_SIGRT_18 = 50, + __SYS_SIGRT_19 = 51, + __SYS_SIGRT_20 = 52, + __SYS_SIGRT_21 = 53, + __SYS_SIGRT_22 = 54, + __SYS_SIGRT_23 = 55, + __SYS_SIGRT_24 = 56, + __SYS_SIGRT_25 = 57, + __SYS_SIGRT_26 = 58, + __SYS_SIGRT_27 = 59, + __SYS_SIGRT_28 = 60, + __SYS_SIGRT_29 = 61, + __SYS_SIGRT_30 = 62, + __SYS_SIGRT_31 = 63, + __SYS_SIGRTMAX = 64, + + /* Maximum signal number. */ + __SYS_SIGNAL_MAX = __SYS_SIGRTMAX +} syscall_signal_t; +#ifdef __kernel__ +typedef syscall_signal_t signal_t; +#endif + +typedef enum +{ + /** Terminate the process. */ + __SYS_SIG_TERM = 0, + /** Ignore the signal. */ + __SYS_SIG_IGN = 1, + /** Continue the process. */ + __SYS_SIG_CONT = 2, + /** Stop the process. */ + __SYS_SIG_STOP = 3, + /** Dump core. */ + __SYS_SIG_CORE = 4 +} syscall_signal_disposition_t; +#ifdef __kernel__ +typedef syscall_signal_disposition_t signal_disposition_t; +#endif + +typedef enum +{ + __SYS_SIG_BLOCK = 0, + __SYS_SIG_UNBLOCK = 1, + __SYS_SIG_SETMASK = 2 +} syscall_signal_actions_t; + +typedef enum +{ + __SYS_SA_NOCLDSTOP = 1, + __SYS_SA_ONSTACK = 0x08000000, + __SYS_SA_RESETHAND = 0x80000000, + __SYS_SA_RESTART = 0x10000000, + __SYS_SA_SIGINFO = 4, + __SYS_SA_NOCLDWAIT = 2, + __SYS_SA_NODEFER = 0x40000000, +} syscall_signal_action_flags_t; + +typedef enum +{ + __SYS_SIG_ERR = -1, + __SYS_SIG_DFL = 0, + ___SYS_SIG_IGN = 1 +} syscall_signal_action_disposition_t; + +typedef enum +{ + __SYS_CLOCK_MONOTONIC = 1, + __SYS_CLOCK_PROCESS_CPUTIME_ID = 2, + __SYS_CLOCK_REALTIME = 3, + __SYS_CLOCK_THREAD_CPUTIME_ID = 4, +} syscall_clockid_t; + +#ifndef __cplusplus +_Static_assert((int)__SYS_SIG_IGN == (int)___SYS_SIG_IGN, "SIG_IGN values do not match"); +#else +static_assert((int)__SYS_SIG_IGN == (int)___SYS_SIG_IGN, "SIG_IGN values do not match"); +#endif + +typedef int __SYS_clockid_t; +typedef unsigned int __SYS_socklen_t; + +/** + * @brief List of syscalls + * + * @details This list contains all the syscalls of the Fennix Kernel API. + * + */ +typedef enum +{ + /* Initialization */ + + /** + * @brief Set syscall version + * + * @code + * int api_version(int version); + * @endcode + * + * @details This syscall is used to set the version of the list. + * To prevent applications from breaking on major changes, this should + * be called at the very beginning of the program. + * + * @param version The version of the syscall list of which the program + * was compiled with + * + * @return + * - #EOK on success + * - #EINVAL if the requested version is invalid + * + * @note If this syscall is not used, the latest version will be used. + */ + SYS_API_VERSION = 0, + + /* I/O */ + + /** + * @brief Read from a file descriptor + * + * @code + * ssize_t sys_read(int fildes, void *buf, size_t nbyte); + * @endcode + * + * @details Reads up to `count` bytes from the file descriptor `fd` into + * the buffer starting at `buf`. + * + * @param fd File descriptor to read from + * @param buf Buffer where data will be stored + * @param count Maximum number of bytes to read + * + * @return + * - Number of bytes read on success + * - 0 if the end of file is reached + * - #EFAULT if the buffer is outside accessible address space + * - #EBADF if `fd` is not a valid file descriptor + */ + SYS_READ = 100, + /** + * @brief Read from a file descriptor + * + * @code + * ssize_t sys_pread(int fildes, void *buf, size_t nbyte, off_t offset); + * @endcode + * + * @details Reads up to `count` bytes from the file descriptor `fd` into + * the buffer starting at `buf`. + * + * @param fd File descriptor to read from + * @param buf Buffer where data will be stored + * @param count Maximum number of bytes to read + * @param offset Offset in the file + * + * @return + * - Number of bytes read on success + * - 0 if the end of file is reached + * - #EFAULT if the buffer is outside accessible address space + * - #EBADF if `fd` is not a valid file descriptor + */ + SYS_PREAD, + /** + * @brief Write to a file descriptor + * + * @code + * ssize_t sys_write(int fildes, const void *buf, size_t nbyte); + * @endcode + * + * @details Writes up to `count` bytes from the buffer starting at `buf` + * to the file descriptor `fd`. + * + * @param fd File descriptor to write to + * @param buf Buffer containing data to write + * @param count Number of bytes to write + * + * @return + * - Number of bytes written on success + * - #EFAULT if the buffer is outside accessible address space + * - #EBADF if `fd` is not a valid file descriptor + * - #EPIPE if writing to a pipe with no reader + */ + SYS_WRITE, + /** + * @brief Write to a file descriptor + * + * @code + * ssize_t sys_pwrite(int fildes, const void *buf, size_t nbyte, off_t offset); + * @endcode + * + * @details Writes up to `count` bytes from the buffer starting at `buf` + * to the file descriptor `fd`. + * + * @param fd File descriptor to write to + * @param buf Buffer containing data to write + * @param count Number of bytes to write + * @param offset Offset in the file + * + * @return + * - Number of bytes written on success + * - #EFAULT if the buffer is outside accessible address space + * - #EBADF if `fd` is not a valid file descriptor + */ + SYS_PWRITE, + /** + * @brief Open a file + * + * @code + * int open(const char *pathname, int flags, mode_t mode); + * @endcode + * + * @details Opens the file specified by `pathname`. + * + * @param pathname Path to the file + * @param flags Flags for file access mode\n + * Supported values: + * - #__SYS_O_RDONLY: Open file for reading only. + * - #__SYS_O_WRONLY: Open file for writing only. + * - #__SYS_O_RDWR: Open file for reading and writing. + * - #__SYS_O_APPEND: Append data to the end of file. + * - #__SYS_O_CREAT: Create file if it does not exist. + * - #__SYS_O_DSYNC: + * - #__SYS_O_EXCL: + * - #__SYS_O_NOCTTY: + * - #__SYS_O_NONBLOCK: + * - #__SYS_O_RSYNC: + * - #__SYS_O_SYNC: + * - #__SYS_O_TRUNC: Truncate file to zero length. + * @param mode Permissions for newly created file (if applicable) + * + * @return + * - File descriptor on success + * - #ENOENT if the file does not exist + * - #EACCES if permissions are insufficient + * + * @see #syscall_open_flags_t + */ + SYS_OPEN, + /** + * @brief Close a file descriptor + * + * @code + * int close(int fd); + * @endcode + * + * @details Closes the file descriptor `fd`, releasing its resources. + * + * @param fd File descriptor to close + * + * @return + * - #EOK on success + * - #EBADF if `fd` is not a valid file descriptor + */ + SYS_CLOSE, + /** + * @brief Control a device + * + * @code + * int ioctl(int fd, unsigned long request, void *argp); + * @endcode + * + * @details Manipulates the underlying parameters of a device. + * + * @param fd File descriptor referring to the device + * @param request Device-specific request code + * @param argp Argument for the request + * + * @return + * - #EOK on success + * - #EBADF if `fd` is not valid + * - #EINVAL if the request is invalid + */ + SYS_IOCTL, + + /* File Status */ + + /** + * @brief Retrieve file status + * + * @code + * int stat(const char *pathname, struct stat *statbuf); + * @endcode + * + * @details Gets the status of the file specified by `pathname`. + * + * @param pathname Path to the file + * @param statbuf Buffer to store file status + * + * @return + * - #EOK on success + * - #ENOENT if the file does not exist + * - #EACCES if permissions are insufficient + */ + SYS_STAT = 200, + /** + * @brief Retrieve file status for an open file descriptor + * + * @code + * int fstat(int fd, struct stat *statbuf); + * @endcode + * + * @details Gets the status of the file referred to by `fd`. + * + * @param fd File descriptor + * @param statbuf Buffer to store file status + * + * @return + * - #EOK on success + * - #EBADF if `fd` is not a valid file descriptor + * - #EFAULT if `statbuf` is outside accessible address space + */ + SYS_FSTAT, + /** + * @brief Retrieve file status with symbolic link resolution + * + * @code + * int lstat(const char *pathname, struct stat *statbuf); + * @endcode + * + * @details Gets the status of the file specified by `pathname`, + * but does not follow symbolic links. + * + * @param pathname Path to the file + * @param statbuf Buffer to store file status + * + * @return + * - #EOK on success + * - #ENOENT if the file does not exist + * - #EACCES if permissions are insufficient + */ + SYS_LSTAT, + /** + * @brief Check a file's accessibility + * + * @code + * int access(const char *pathname, int mode); + * @endcode + * + * @details Checks if the calling process can access the file specified + * by `pathname` according to the specified `mode`. + * + * @param pathname Path to the file + * @param mode Accessibility check mode\n + * Supported values: + * - #__SYS_F_OK: Check if the file exists + * - #__SYS_R_OK: Check if the file is readable + * - #__SYS_W_OK: Check if the file is writable + * - #__SYS_X_OK: Check if the file is executable + * + * @return + * - #EOK on success + * - #EACCES if access is denied + * - #ENOENT if the file does not exist + * + * @see #syscall_access_flags_t + */ + SYS_ACCESS, + /** + * @brief Change the size of a file + * + * @code + * int truncate(const char *pathname, off_t length); + * @endcode + * + * @details Sets the size of the file specified by `pathname` to `length`. + * If the file is shorter, it is extended and the extended part is zero-filled. + * + * @param pathname Path to the file + * @param length Desired file length + * + * @return + * - #EOK on success + * - #EINVAL if `length` is invalid + * - #EACCES if permissions are insufficient + */ + SYS_TRUNCATE, + /** + * @brief Change the size of a file referred by a file descriptor + * + * @code + * int ftruncate(int fd, off_t length); + * @endcode + * + * @details Sets the size of the file referred to by `fd` to `length`. + * + * @param fd File descriptor + * @param length Desired file length + * + * @return + * - #EOK on success + * - #EBADF if `fd` is not valid + * - #EINVAL if `length` is invalid + */ + SYS_FTRUNCATE, + /** + * @brief Get the current file offset + * + * @code + * off_t tell(int fd); + * @endcode + * + * @details Returns the current file offset for the file referred to by `fd`. + * + * @param fd File descriptor + * + * @return + * - Current file offset on success + * - #EBADF if `fd` is not a valid file descriptor + */ + SYS_TELL, + /** + * @brief Set the file offset + * + * @code + * off_t seek(int fd, off_t offset, int whence); + * @endcode + * + * @details Sets the file offset for the file referred to by `fd` to the + * specified `offset` according to the directive `whence`. + * + * @param fd File descriptor + * @param offset Offset to set + * @param whence Directive for setting the offset\n + * Supported values: + * - #__SYS_SEEK_SET: Set the offset to `offset` bytes + * - #__SYS_SEEK_CUR: Set the offset to the current offset plus `offset` + * - #__SYS_SEEK_END: Set the offset to the size of the file plus `offset` + * + * @return + * - New file offset on success + * - #EBADF if `fd` is not a valid file descriptor + * - #EINVAL if `whence` is invalid + */ + SYS_SEEK, + + /* Process Control */ + + /** + * @brief Terminate the calling process + * + * @code + * void exit(int status); + * @endcode + * + * @details Terminates the calling process with the specified `status`. + * The status code is made available to the parent process. + * + * @param status Exit status code + * + * @return This function does not return. + */ + SYS_EXIT = 300, + /** + * @brief Create a child process + * + * @code + * pid_t fork(void); + * @endcode + * + * @details Creates a new process by duplicating the calling process. + * The child process has its own copy of the parent's address space. + * + * @return + * - 0 to the child process + * - PID of the child to the parent process + * - #ENOMEM if memory is insufficient + */ + SYS_FORK, + /** + * @brief Execute a program + * + * @code + * int execve(const char *pathname, char *const argv[], char *const envp[]); + * @endcode + * + * @details Replaces the current process image with a new process image + * specified by `pathname`. + * + * @param pathname Path to the executable file + * @param argv Argument vector + * @param envp Environment variables + * + * @return + * - Does not return on success + * - #ENOENT if the file does not exist + * - #EACCES if permissions are insufficient + */ + SYS_EXECVE, + /** + * @brief Get the process ID of the calling process + * + * @code + * pid_t getpid(void); + * @endcode + * + * @details Returns the process ID of the calling process. + * + * @return + * - Process ID on success + */ + SYS_GETPID, + /** + * @brief Get the parent process ID + * + * @code + * pid_t getppid(void); + * @endcode + * + * @details Returns the parent process ID of the calling process. + * + * @return + * - Parent process ID on success + */ + SYS_GETPPID, + /** + * @brief Wait for a child process to change state + * + * @code + * pid_t waitpid(pid_t pid, int *wstatus, int options); + * @endcode + * + * @details Waits for the child process specified by `pid` to change state. + * + * @param pid Process ID to wait for + * @param wstatus Pointer to store the status information + * @param options Options for waiting behavior + * + * @return + * - Process ID of the child on success + * - #ECHILD if no child processes exist + */ + SYS_WAITPID, + /** + * @brief Send a signal to a process + * + * @code + * int kill(pid_t pid, int sig); + * @endcode + * + * @details Sends the signal `sig` to the process specified by `pid`. + * + * @param pid Process ID + * @param sig Signal to send + * + * @return + * - #EOK on success + * - #ESRCH if the process does not exist + * - #EINVAL if `sig` is invalid + */ + SYS_KILL, + /** + * @brief Process/Thread Control + * + * @code + * int prctl(syscall_prctl_options_t option, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4); + * @endcode + * + * @details Perform various operations on a process or thread. + * + * @param option Operation to perform + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @param arg3 Argument 3 + * @param arg4 Argument 4 + * + * @return + * - #EOK on success + * - #EINVAL if the operation is invalid + * - #EFAULT if one of the arguments is invalid + */ + SYS_PRCTL, + + /* Memory */ + + /** + * @brief Set the program break + * + * @code + * int brk(void *end_data); + * @endcode + * + * @details Increases or decreases the program’s data space, ending at `end_data`. + * + * @param end_data New program break location + * + * @return + * - #EOK on success + * - #ENOMEM if memory allocation fails + */ + SYS_BRK = 400, + /** + * @brief Map files or devices into memory + * + * @code + * void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); + * @endcode + * + * @details Maps a file or device into memory. This can be used for memory-mapped I/O or + * for sharing memory between processes. + * + * @param addr Desired starting address of the mapping (NULL for automatic allocation) + * @param length Length of the mapping + * @param prot Desired memory protection\n + * Supported values: + * - #__SYS_PROT_READ: Readable + * - #__SYS_PROT_WRITE: Writable + * - #__SYS_PROT_EXEC: Executable + * - #__SYS_PROT_NONE: No access + * @param flags Mapping options\n + * Supported values: + * - #__SYS_MAP_SHARED: Share memory with other processes + * - #__SYS_MAP_PRIVATE: Create a private copy of the file + * - #__SYS_MAP_FIXED: Use `addr` as the starting address of the mapping + * - #__SYS_MAP_ANONYMOUS: Create an anonymous mapping + * @param fd File descriptor for the file to map + * @param offset Offset in the file to start the mapping + * + * @return There are several possible return values: + * - Pointer to mapped area on success + * - #EACCES + * - #EAGAIN + * - #EBADF + * - #EINVAL + * - #EMFILE + * - #ENODEV + * - #ENOMEM + * - #ENOTSUP + * - #ENXIO + * - #EOVERFLOW + * + * @see #syscall_mmap_flags_t + */ + SYS_MMAP, + /** + * @brief Unmap a mapped memory region + * + * @code + * int munmap(void *addr, size_t length); + * @endcode + * + * @details Unmaps a previously mapped memory region, making the memory available for reuse. + * + * @param addr Start address of the memory region + * @param length Length of the memory region to unmap + * + * @return + * - #EOK on success + * - #EINVAL if the address or length is invalid + * - #EFAULT if the memory region is not currently mapped + */ + SYS_MUNMAP, + /** + * @brief Change memory protection + * + * @code + * int mprotect(void *addr, size_t length, int prot); + * @endcode + * + * @details Sets the protection on the memory region starting at `addr` for `length`. + * + * @param addr Start address of the memory region + * @param length Length of the memory region + * @param prot Desired memory protection (e.g., PROT_READ, PROT_WRITE) + * + * @return + * - #EOK on success + * - #EACCES if protection cannot be set + */ + SYS_MPROTECT, + /** + * @brief Provide advice about memory usage + * + * @code + * int madvise(void *addr, size_t length, int advice); + * @endcode + * + * @details Provides advice to the kernel about the expected behavior of the memory region + * starting at `addr` for `length`, such as whether it will be accessed randomly or sequentially. + * + * @param addr Start address of the memory region + * @param length Length of the memory region + * @param advice Desired advice (e.g., MADV_DONTNEED, MADV_SEQUENTIAL) + * + * @return + * - #EOK on success + * - #EINVAL if the parameters are invalid + */ + SYS_MADVISE, + + /* Communication */ + + /** + * @brief Create a pipe + * + * @code + * int pipe(int pipefd[2]); + * @endcode + * + * @details Creates a pipe, returning two file descriptors in `pipefd`. One is for reading, + * and the other is for writing. + * + * @param pipefd Array to store the two file descriptors + * + * @return + * - #EOK on success + * - #EMFILE if the process has too many open file descriptors + */ + SYS_PIPE = 500, + /** + * @brief Duplicate a file descriptor + * + * @code + * int dup(int oldfd); + * @endcode + * + * @details Duplicates the file descriptor `oldfd`, returning the new file descriptor. + * + * @param oldfd File descriptor to duplicate + * + * @return + * - New file descriptor on success + * - #EBADF if `oldfd` is invalid + */ + SYS_DUP, + /** + * @brief Duplicate a file descriptor to a specific value + * + * @code + * int dup2(int oldfd, int newfd); + * @endcode + * + * @details Duplicates `oldfd` to `newfd`. If `newfd` is already open, it will be closed first. + * + * @param oldfd File descriptor to duplicate + * @param newfd File descriptor to duplicate `oldfd` to + * + * @return + * - `newfd` on success + * - #EBADF if `oldfd` is invalid + * - #EINVAL if `newfd` is invalid + */ + SYS_DUP2, + /** + * @brief Create an endpoint for communication + * + * @code + * int socket(int domain, int type, int protocol); + * @endcode + * + * @details Creates an endpoint for communication, returning a socket file descriptor. + * + * @param domain Communication domain (e.g., AF_INET for IPv4) + * @param type Type of socket (e.g., SOCK_STREAM for TCP) + * @param protocol Protocol to use (e.g., IPPROTO_TCP) + * + * @return + * - Socket file descriptor on success + * - #EINVAL if parameters are invalid + */ + SYS_SOCKET, + /** + * @brief Bind a socket to a local address + * + * @code + * int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + * @endcode + * + * @details Binds a socket to a local address so it can listen for incoming connections. + * + * @param sockfd Socket file descriptor + * @param addr Address to bind to + * @param addrlen Length of the address + * + * @return + * - #EOK on success + * - #EINVAL if the socket is invalid + */ + SYS_BIND, + /** + * @brief Connect to a remote address + * + * @code + * int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + * @endcode + * + * @details Connects a socket to a remote address. + * + * @param sockfd Socket file descriptor + * @param addr Remote address to connect to + * @param addrlen Length of the address + * + * @return + * - #EOK on success + * - #EINVAL if parameters are invalid + */ + SYS_CONNECT, + /** + * @brief Listen for incoming connections on a socket + * + * @code + * int listen(int sockfd, int backlog); + * @endcode + * + * @details Sets a socket to listen for incoming connections, specifying the backlog queue size. + * + * @param sockfd Socket file descriptor + * @param backlog Number of pending connections to allow + * + * @return + * - #EOK on success + * - #EINVAL if parameters are invalid + */ + SYS_LISTEN, + /** + * @brief Accept an incoming connection on a socket + * + * @code + * int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + * @endcode + * + * @details Accepts an incoming connection on a listening socket, creating a new socket for communication. + * + * @param sockfd Socket file descriptor + * @param addr Client address + * @param addrlen Length of the address + * + * @return + * - New socket file descriptor on success + * - #EINVAL if parameters are invalid + */ + SYS_ACCEPT, + /** + * @brief Send data on a socket + * + * @code + * ssize_t send(int sockfd, const void *buf, size_t len, int flags); + * @endcode + * + * @details Sends data through a socket. + * + * @param sockfd Socket file descriptor + * @param buf Data to send + * @param len Length of the data + * @param flags Flags for the send operation + * + * @return + * - Number of bytes sent on success + * - #EINVAL if parameters are invalid + */ + SYS_SEND, + /** + * @brief Receive data on a socket + * + * @code + * ssize_t recv(int sockfd, void *buf, size_t len, int flags); + * @endcode + * + * @details Receives data from a socket. + * + * @param sockfd Socket file descriptor + * @param buf Buffer to store received data + * @param len Maximum number of bytes to receive + * @param flags Flags for the receive operation + * + * @return + * - Number of bytes received on success + * - #EINVAL if parameters are invalid + */ + SYS_RECV, + /** + * @brief Shut down part of a full-duplex connection + * + * @code + * int shutdown(int sockfd, int how); + * @endcode + * + * @details Shuts down part of a full-duplex connection on a socket. + * + * @param sockfd Socket file descriptor + * @param how Determines which operations to shut down (e.g., SHUT_RD, SHUT_WR) + * + * @return + * - #EOK on success + * - #EINVAL if parameters are invalid + */ + SYS_SHUTDOWN, + + /* Time */ + + /** + * @brief Get the current time + * + * @code + * time_t time(time_t *t); + * @endcode + * + * @details Retrieves the current calendar time as the number of seconds since the epoch. + * + * @param t Pointer to store the time (optional) + * + * @return + * - Current time in seconds on success + * - #__SYS_NULL if `t` is NULL + */ + SYS_TIME = 600, + /** + * @brief Get the current time of a specific clock + * + * @code + * int clock_gettime(clockid_t clockid, struct timespec *tp); + * @endcode + * + * @details Retrieves the current time for the specified clock (`CLOCK_REALTIME`, `CLOCK_MONOTONIC`, etc.). + * + * @param clockid Clock ID to query + * @param tp Pointer to store the time + * + * @return + * - #EOK on success + * - #EINVAL if parameters are invalid + */ + SYS_CLOCK_GETTIME, + /** + * @brief Set the current time of a specific clock + * + * @code + * int clock_settime(clockid_t clockid, const struct timespec *tp); + * @endcode + * + * @details Sets the time for the specified clock (`CLOCK_REALTIME`, `CLOCK_MONOTONIC`, etc.). + * + * @param clockid Clock ID to set + * @param tp Pointer to the time value + * + * @return + * - #EOK on success + * - #EINVAL if parameters are invalid + */ + SYS_CLOCK_SETTIME, + /** + * @brief Sleep for a specified time + * + * @code + * int nanosleep(const struct timespec *req, struct timespec *rem); + * @endcode + * + * @details Suspends the execution of the calling thread for the specified time duration. + * + * @param req Pointer to `timespec` specifying the time to sleep + * @param rem Pointer to store remaining time if interrupted + * + * @return + * - #EOK on success + * - #EINTR if interrupted by a signal + */ + SYS_NANOSLEEP, + + /* Miscellaneous */ + + /** + * @brief Get the current working directory + * + * @code + * char *getcwd(char *buf, size_t size); + * @endcode + * + * @details Retrieves the current working directory. + * + * @param buf Buffer to store the directory path + * @param size Size of the buffer + * + * @return + * - Pointer to `buf` on success + * - #__SYS_NULL on error + */ + SYS_GETCWD = 700, + /** + * @brief Change the current working directory + * + * @code + * int chdir(const char *path); + * @endcode + * + * @details Changes the current working directory to the specified `path`. + * + * @param path New directory path + * + * @return + * - #EOK on success + * - #ENOENT if the directory does not exist + * - #EACCES if permission is denied + */ + SYS_CHDIR, + /** + * @brief Create a new directory + * + * @code + * int mkdir(const char *path, mode_t mode); + * @endcode + * + * @details Creates a new directory at `path` with the specified permissions. + * + * @param path Path to the new directory + * @param mode Directory permissions + * + * @return + * - #EOK on success + * - #EEXIST if the directory already exists + * - #EACCES if permission is denied + */ + SYS_MKDIR, + /** + * @brief Remove an empty directory + * + * @code + * int rmdir(const char *path); + * @endcode + * + * @details Removes the empty directory specified by `path`. + * + * @param path Path to the directory + * + * @return + * - #EOK on success + * - #ENOTEMPTY if the directory is not empty + */ + SYS_RMDIR, + /** + * @brief Remove a file + * + * @code + * int unlink(const char *pathname); + * @endcode + * + * @details Removes the file specified by `pathname`. + * + * @param pathname Path to the file + * + * @return + * - #EOK on success + * - #ENOENT if the file does not exist + * - #EACCES if permission is denied + */ + SYS_UNLINK, + /** + * @brief Rename a file or directory + * + * @code + * int rename(const char *oldpath, const char *newpath); + * @endcode + * + * @details Renames a file or directory from `oldpath` to `newpath`. + * + * @param oldpath Current name of the file or directory + * @param newpath New name of the file or directory + * + * @return + * - #EOK on success + * - #EEXIST if the target exists + * - #EACCES if permission is denied + */ + SYS_RENAME, + + /** + * @brief Max number of syscalls + * + * @details This is used to determine the size of the `syscalls_t` array. + * + * @code + * syscalls_t syscalls[SYS_MAX]; + * @endcode + * + * @note This must be the last element in the list + */ + SYS_MAX +} syscalls_t; + +/* Initialization */ + +/** @copydoc SYS_API_VERSION */ +#define call_api_version(version) syscall1(SYS_API_VERSION, (scarg)version) + +/* I/O */ + +/** @copydoc SYS_READ */ +#define call_read(fd, buf, count) syscall3(SYS_READ, (scarg)fd, (scarg)buf, (scarg)count) + +/** @copydoc SYS_PREAD */ +#define call_pread(fd, buf, count, offset) syscall4(SYS_PREAD, (scarg)fd, (scarg)buf, (scarg)count, (scarg)offset) + +/** @copydoc SYS_WRITE */ +#define call_write(fd, buf, count) syscall3(SYS_WRITE, (scarg)fd, (scarg)buf, (scarg)count) + +/** @copydoc SYS_PWRITE */ +#define call_pwrite(fd, buf, count, offset) syscall4(SYS_PWRITE, (scarg)fd, (scarg)buf, (scarg)count, (scarg)offset) + +/** @copydoc SYS_OPEN */ +#define call_open(pathname, flags, mode) syscall3(SYS_OPEN, (scarg)pathname, (scarg)flags, (scarg)mode) + +/** @copydoc SYS_CLOSE */ +#define call_close(fd) syscall1(SYS_CLOSE, fd) + +/** @copydoc SYS_IOCTL */ +#define call_ioctl(fd, request, argp) syscall3(SYS_IOCTL, (scarg)fd, (scarg)request, (scarg)argp) + +/* File Status */ + +/** @copydoc SYS_STAT */ +#define call_stat(pathname, statbuf) syscall2(SYS_STAT, (scarg)pathname, (scarg)statbuf) + +/** @copydoc SYS_FSTAT */ +#define call_fstat(fd, statbuf) syscall2(SYS_FSTAT, (scarg)fd, (scarg)statbuf) + +/** @copydoc SYS_LSTAT */ +#define call_lstat(pathname, statbuf) syscall2(SYS_LSTAT, (scarg)pathname, (scarg)statbuf) + +/** @copydoc SYS_ACCESS */ +#define call_access(pathname, mode) syscall2(SYS_ACCESS, (scarg)pathname, (scarg)mode) + +/** @copydoc SYS_TRUNCATE */ +#define call_truncate(pathname, length) syscall2(SYS_TRUNCATE, (scarg)pathname, (scarg)length) + +/** @copydoc SYS_FTRUNCATE */ +#define call_ftruncate(fd, length) syscall2(SYS_FTRUNCATE, (scarg)fd, (scarg)length) + +/** @copydoc SYS_TELL */ +#define call_tell(fd) syscall1(SYS_TELL, (scarg)fd) + +/** @copydoc SYS_SEEK */ +#define call_seek(fd, offset, whence) syscall3(SYS_SEEK, (scarg)fd, (scarg)offset, (scarg)whence) + +/* Process Control */ + +/** @copydoc SYS_EXIT */ +#define call_exit(status) syscall1(SYS_EXIT, (scarg)status) + +/** @copydoc SYS_FORK */ +#define call_fork() syscall0(SYS_FORK) + +/** @copydoc SYS_EXECVE */ +#define call_execve(pathname, argv, envp) syscall3(SYS_EXECVE, (scarg)pathname, (scarg)argv, (scarg)envp) + +/** @copydoc SYS_GETPID */ +#define call_getpid() syscall0(SYS_GETPID) + +/** @copydoc SYS_GETPPID */ +#define call_getppid() syscall0(SYS_GETPPID) + +/** @copydoc SYS_WAITPID */ +#define call_waitpid(pid, wstatus, options) syscall3(SYS_WAITPID, (scarg)pid, (scarg)wstatus, (scarg)options) + +/** @copydoc SYS_KILL */ +#define call_kill(pid, sig) syscall2(SYS_KILL, (scarg)pid, (scarg)sig) + +/** @copydoc SYS_PRCTL */ +#define call_prctl(option, arg1, arg2, arg3, arg4) syscall5(SYS_PRCTL, (scarg)option, (scarg)arg1, (scarg)arg2, (scarg)arg3, (scarg)arg4) + +/* Memory */ + +/** @copydoc SYS_BRK */ +#define call_brk(end_data) syscall1(SYS_BRK, (scarg)end_data) + +/** @copydoc SYS_MMAP */ +#define call_mmap(addr, length, prot, flags, fd, offset) syscall6(SYS_MMAP, (scarg)addr, (scarg)length, (scarg)prot, (scarg)flags, (scarg)fd, (scarg)offset) + +/** @copydoc SYS_MUNMAP */ +#define call_munmap(addr, length) syscall2(SYS_MUNMAP, (scarg)addr, (scarg)length) + +/** @copydoc SYS_MPROTECT */ +#define call_mprotect(addr, length, prot) syscall3(SYS_MPROTECT, (scarg)addr, (scarg)length, (scarg)prot) + +/** @copydoc SYS_MADVISE */ +#define call_madvise(addr, length, advice) syscall3(SYS_MADVISE, (scarg)addr, (scarg)length, (scarg)advice) + +/* Communication */ + +/** @copydoc SYS_PIPE */ +#define call_pipe(pipefd) syscall1(SYS_PIPE, (scarg)pipefd) + +/** @copydoc SYS_DUP */ +#define call_dup(oldfd) syscall1(SYS_DUP, (scarg)oldfd) + +/** @copydoc SYS_DUP2 */ +#define call_dup2(oldfd, newfd) syscall2(SYS_DUP2, (scarg)oldfd, (scarg)newfd) + +/** @copydoc SYS_SOCKET */ +#define call_socket(domain, type, protocol) syscall3(SYS_SOCKET, (scarg)domain, (scarg)type, (scarg)protocol) + +/** @copydoc SYS_BIND */ +#define call_bind(sockfd, addr, addrlen) syscall3(SYS_BIND, (scarg)sockfd, (scarg)addr, (scarg)addrlen) + +/** @copydoc SYS_CONNECT */ +#define call_connect(sockfd, addr, addrlen) syscall3(SYS_CONNECT, (scarg)sockfd, (scarg)addr, (scarg)addrlen) + +/** @copydoc SYS_LISTEN */ +#define call_listen(sockfd, backlog) syscall2(SYS_LISTEN, (scarg)sockfd, (scarg)backlog) + +/** @copydoc SYS_ACCEPT */ +#define call_accept(sockfd, addr, addrlen) syscall3(SYS_ACCEPT, (scarg)sockfd, (scarg)addr, (scarg)addrlen) + +/** @copydoc SYS_SEND */ +#define call_send(sockfd, buf, len, flags) syscall4(SYS_SEND, (scarg)sockfd, (scarg)buf, (scarg)len, (scarg)flags) + +/** @copydoc SYS_RECV */ +#define call_recv(sockfd, buf, len, flags) syscall4(SYS_RECV, (scarg)sockfd, (scarg)buf, (scarg)len, (scarg)flags) + +/** @copydoc SYS_SHUTDOWN */ +#define call_shutdown(sockfd, how) syscall2(SYS_SHUTDOWN, (scarg)sockfd, (scarg)how) + +/* Time */ + +/** @copydoc SYS_TIME */ +#define call_time(t) syscall1(SYS_TIME, t) + +/** @copydoc SYS_CLOCK_GETTIME */ +#define call_clock_gettime(clockid, tp) syscall2(SYS_CLOCK_GETTIME, (scarg)clockid, (scarg)tp) + +/** @copydoc SYS_CLOCK_SETTIME */ +#define call_clock_settime(clockid, tp) syscall2(SYS_CLOCK_SETTIME, (scarg)clockid, (scarg)tp) + +/** @copydoc SYS_NANOSLEEP */ +#define call_nanosleep(req, rem) syscall2(SYS_NANOSLEEP, (scarg)req, (scarg)rem) + +/* Miscellaneous */ + +/** @copydoc SYS_GETCWD */ +#define call_getcwd(buf, size) syscall2(SYS_GETCWD, (scarg)buf, (scarg)size) + +/** @copydoc SYS_CHDIR */ +#define call_chdir(path) syscall1(SYS_CHDIR, (scarg)path) + +/** @copydoc SYS_MKDIR */ +#define call_mkdir(path, mode) syscall2(SYS_MKDIR, (scarg)path, (scarg)mode) + +/** @copydoc SYS_RMDIR */ +#define call_rmdir(path) syscall1(SYS_RMDIR, (scarg)path) + +/** @copydoc SYS_UNLINK */ +#define call_unlink(pathname) syscall1(SYS_UNLINK, (scarg)pathname) + +/** @copydoc SYS_RENAME */ +#define call_rename(oldpath, newpath) syscall2(SYS_RENAME, (scarg)oldpath, (scarg)newpath) + +#endif // !__FENNIX_API_SYSCALLS_LIST_H__ diff --git a/Userspace/libc/include/float.h b/Userspace/libc/include/float.h new file mode 100644 index 00000000..2757bc86 --- /dev/null +++ b/Userspace/libc/include/float.h @@ -0,0 +1,87 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _FLOAT_H +#define _FLOAT_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define INFINITY (__builtin_inff()) +#define NAN (__builtin_nanf("")) +#define FLT_ROUNDS 1 +#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ + +#define FLT_HAS_SUBNORM __FLT_HAS_DENORM__ +#define DBL_HAS_SUBNORM __DBL_HAS_DENORM__ +#define LDBL_HAS_SUBNORM __LDBL_HAS_DENORM__ + +#define FLT_RADIX __FLT_RADIX__ + +#define FLT_MANT_DIG __FLT_MANT_DIG__ +#define DBL_MANT_DIG __DBL_MANT_DIG__ +#define LDBL_MANT_DIG __LDBL_MANT_DIG__ + +#define FLT_DECIMAL_DIG __FLT_DECIMAL_DIG__ +#define DBL_DECIMAL_DIG __DBL_DECIMAL_DIG__ +#define LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__ + +#define DECIMAL_DIG __DECIMAL_DIG__ + +#define FLT_DIG __FLT_DIG__ +#define DBL_DIG __DBL_DIG__ +#define LDBL_DIG __LDBL_DIG__ + +#define FLT_MIN_EXP __FLT_MIN_EXP__ +#define DBL_MIN_EXP __DBL_MIN_EXP__ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ + +#define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ +#define DBL_MIN_10_EXP __DBL_MIN_10_EXP__ +#define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__ + +#define FLT_MAX_EXP __FLT_MAX_EXP__ +#define DBL_MAX_EXP __DBL_MAX_EXP__ +#define LDBL_MAX_EXP __LDBL_MAX_EXP__ + +#define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ +#define DBL_MAX_10_EXP __DBL_MAX_10_EXP__ +#define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__ + +#define FLT_MAX __FLT_MAX__ +#define DBL_MAX __DBL_MAX__ +#define LDBL_MAX __LDBL_MAX__ + +#define FLT_EPSILON __FLT_EPSILON__ +#define DBL_EPSILON __DBL_EPSILON__ +#define LDBL_EPSILON __LDBL_EPSILON__ + +#define FLT_MIN __FLT_MIN__ +#define DBL_MIN __DBL_MIN__ +#define LDBL_MIN __LDBL_MIN__ + +#define FLT_TRUE_MIN __FLT_DENORM_MIN__ +#define DBL_TRUE_MIN __DBL_DENORM_MIN__ +#define LDBL_TRUE_MIN __LDBL_DENORM_MIN__ + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_FLOAT_H diff --git a/Userspace/libc/include/inttypes.h b/Userspace/libc/include/inttypes.h index 091bc9e9..e7fab3ac 100644 --- a/Userspace/libc/include/inttypes.h +++ b/Userspace/libc/include/inttypes.h @@ -1,40 +1,69 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _INTTYPES_H #define _INTTYPES_H -typedef __INT8_TYPE__ int8_t; -typedef __INT16_TYPE__ int16_t; -typedef __INT32_TYPE__ int32_t; -typedef __INT64_TYPE__ int64_t; -typedef __UINT8_TYPE__ uint8_t; -typedef __UINT16_TYPE__ uint16_t; -typedef __UINT32_TYPE__ uint32_t; -typedef __UINT64_TYPE__ uint64_t; +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus -typedef __INT_LEAST8_TYPE__ int_least8_t; -typedef __INT_LEAST16_TYPE__ int_least16_t; -typedef __INT_LEAST32_TYPE__ int_least32_t; -typedef __INT_LEAST64_TYPE__ int_least64_t; -typedef __UINT_LEAST8_TYPE__ uint_least8_t; -typedef __UINT_LEAST16_TYPE__ uint_least16_t; -typedef __UINT_LEAST32_TYPE__ uint_least32_t; -typedef __UINT_LEAST64_TYPE__ uint_least64_t; + typedef __INT8_TYPE__ int8_t; + typedef __INT16_TYPE__ int16_t; + typedef __INT32_TYPE__ int32_t; + typedef __INT64_TYPE__ int64_t; -typedef __INT_FAST8_TYPE__ int_fast8_t; -typedef __INT_FAST16_TYPE__ int_fast16_t; -typedef __INT_FAST32_TYPE__ int_fast32_t; -typedef __INT_FAST64_TYPE__ int_fast64_t; -typedef __UINT_FAST8_TYPE__ uint_fast8_t; -typedef __UINT_FAST16_TYPE__ uint_fast16_t; -typedef __UINT_FAST32_TYPE__ uint_fast32_t; -typedef __UINT_FAST64_TYPE__ uint_fast64_t; + typedef __UINT8_TYPE__ uint8_t; + typedef __UINT16_TYPE__ uint16_t; + typedef __UINT32_TYPE__ uint32_t; + typedef __UINT64_TYPE__ uint64_t; -typedef __INTPTR_TYPE__ intptr_t; -typedef __UINTPTR_TYPE__ uintptr_t; + typedef __INTPTR_TYPE__ intptr_t; + typedef __UINTPTR_TYPE__ uintptr_t; -typedef __INTMAX_TYPE__ intmax_t; -typedef __UINTMAX_TYPE__ uintmax_t; + typedef __INT_LEAST8_TYPE__ int_least8_t; + typedef __INT_LEAST16_TYPE__ int_least16_t; + typedef __INT_LEAST32_TYPE__ int_least32_t; + typedef __INT_LEAST64_TYPE__ int_least64_t; -typedef __PTRDIFF_TYPE__ ptrdiff_t; -typedef __SIZE_TYPE__ size_t; + typedef __UINT_LEAST8_TYPE__ uint_least8_t; + typedef __UINT_LEAST16_TYPE__ uint_least16_t; + typedef __UINT_LEAST32_TYPE__ uint_least32_t; + typedef __UINT_LEAST64_TYPE__ uint_least64_t; + + typedef __INT_FAST8_TYPE__ int_fast8_t; + typedef __INT_FAST16_TYPE__ int_fast16_t; + typedef __INT_FAST32_TYPE__ int_fast32_t; + typedef __INT_FAST64_TYPE__ int_fast64_t; + + typedef __UINT_FAST8_TYPE__ uint_fast8_t; + typedef __UINT_FAST16_TYPE__ uint_fast16_t; + typedef __UINT_FAST32_TYPE__ uint_fast32_t; + typedef __UINT_FAST64_TYPE__ uint_fast64_t; + + typedef __INTPTR_TYPE__ intptr_t; + typedef __UINTPTR_TYPE__ uintptr_t; + + typedef __INTMAX_TYPE__ intmax_t; + typedef __UINTMAX_TYPE__ uintmax_t; + +#ifdef __cplusplus +} +#endif // __cplusplus #endif // !_INTTYPES_H diff --git a/Userspace/libc/include/limits.h b/Userspace/libc/include/limits.h index e259acf5..f2023777 100644 --- a/Userspace/libc/include/limits.h +++ b/Userspace/libc/include/limits.h @@ -1,119 +1,117 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _LIMITS_H #define _LIMITS_H -#undef CHAR_BIT +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define _POSIX_ARG_MAX 4096 +#define _POSIX_CHILD_MAX 25 +#define _POSIX_OPEN_MAX 20 +#define _POSIX_LINK_MAX 8 +#define _POSIX_MAX_CANON 255 +#define _POSIX_MAX_INPUT 255 +#define _POSIX_NAME_MAX 255 +#define _POSIX_PATH_MAX 4096 +#define _POSIX_PIPE_BUF 512 +#define _POSIX_SYMLOOP_MAX 8 +#define _POSIX_HOST_NAME_MAX 255 +#define _POSIX_LOGIN_NAME_MAX 9 +#define _POSIX_TTY_NAME_MAX 255 +#define _POSIX_TZNAME_MAX 6 + +#define _XOPEN_SHM_MAX 255 +#define _XOPEN_IOV_MAX 1024 +#define _XOPEN_SSTREAM_MAX 8 +#define _XOPEN_NAME_MAX 255 +#define _XOPEN_PATH_MAX 4096 +#define _XOPEN_SYMLOOP_MAX 8 +#define _XOPEN_LOGIN_NAME_MAX 9 + +#define _POSIX2_BC_BASE_MAX 99 +#define _POSIX2_BC_DIM_MAX 2048 +#define _POSIX2_BC_SCALE_MAX 99 +#define _POSIX2_BC_STRING_MAX 1000 +#define _POSIX2_COLL_WEIGHTS_MAX 255 +#define _POSIX2_EXPR_NEST_MAX 32 +#define _POSIX2_LINE_MAX 2048 +#define _POSIX2_RE_DUP_MAX 255 + #define CHAR_BIT __CHAR_BIT__ - -#ifndef MB_LEN_MAX -#define MB_LEN_MAX 1 -#endif - -#undef SCHAR_MIN #define SCHAR_MIN (-SCHAR_MAX - 1) -#undef SCHAR_MAX #define SCHAR_MAX __SCHAR_MAX__ -#undef UCHAR_MAX #if __SCHAR_MAX__ == __INT_MAX__ #define UCHAR_MAX (SCHAR_MAX * 2U + 1U) #else #define UCHAR_MAX (SCHAR_MAX * 2 + 1) #endif -#ifdef __CHAR_UNSIGNED__ -#undef CHAR_MIN -#if __SCHAR_MAX__ == __INT_MAX__ -#define CHAR_MIN 0U -#else -#define CHAR_MIN 0 -#endif -#undef CHAR_MAX -#define CHAR_MAX UCHAR_MAX -#else -#undef CHAR_MIN #define CHAR_MIN SCHAR_MIN -#undef CHAR_MAX #define CHAR_MAX SCHAR_MAX -#endif -#undef SHRT_MIN +#define MB_LEN_MAX 1 #define SHRT_MIN (-SHRT_MAX - 1) -#undef SHRT_MAX #define SHRT_MAX __SHRT_MAX__ -#undef USHRT_MAX #if __SHRT_MAX__ == __INT_MAX__ #define USHRT_MAX (SHRT_MAX * 2U + 1U) #else #define USHRT_MAX (SHRT_MAX * 2 + 1) #endif -#undef INT_MIN #define INT_MIN (-INT_MAX - 1) -#undef INT_MAX #define INT_MAX __INT_MAX__ -#undef UINT_MAX #define UINT_MAX (INT_MAX * 2U + 1U) -#undef LONG_MIN #define LONG_MIN (-LONG_MAX - 1L) -#undef LONG_MAX #define LONG_MAX __LONG_MAX__ -#undef ULONG_MAX #define ULONG_MAX (LONG_MAX * 2UL + 1UL) -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#undef LLONG_MIN -#define LLONG_MIN (-LLONG_MAX - 1LL) -#undef LLONG_MAX +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1LL) #define LLONG_MAX __LONG_LONG_MAX__ -#undef ULLONG_MAX #define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) -#endif -#if defined(__GNU_LIBRARY__) ? defined(__USE_GNU) : !defined(__STRICT_ANSI__) -#undef LONG_LONG_MIN -#define LONG_LONG_MIN (-LONG_LONG_MAX - 1LL) -#undef LONG_LONG_MAX -#define LONG_LONG_MAX __LONG_LONG_MAX__ +#define ARG_MAX 4096 +#define CHILD_MAX 25 +#define OPEN_MAX 20 +#define LINK_MAX 8 +#define MAX_CANON 255 +#define MAX_INPUT 255 +#define NAME_MAX 255 +#define PATH_MAX 4096 +#define PIPE_BUF 512 +#define SYMLOOP_MAX 8 +#define HOST_NAME_MAX 255 +#define LOGIN_NAME_MAX 9 +#define TTY_NAME_MAX 255 +#define TZNAME_MAX 6 -#undef ULONG_LONG_MAX -#define ULONG_LONG_MAX (LONG_LONG_MAX * 2ULL + 1ULL) -#endif +#define SSIZE_MAX ((size_t)(-1) / 2) -#if (defined __STDC_WANT_IEC_60559_BFP_EXT__ || (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L)) -#undef CHAR_WIDTH -#define CHAR_WIDTH __SCHAR_WIDTH__ -#undef SCHAR_WIDTH -#define SCHAR_WIDTH __SCHAR_WIDTH__ -#undef UCHAR_WIDTH -#define UCHAR_WIDTH __SCHAR_WIDTH__ -#undef SHRT_WIDTH -#define SHRT_WIDTH __SHRT_WIDTH__ -#undef USHRT_WIDTH -#define USHRT_WIDTH __SHRT_WIDTH__ -#undef INT_WIDTH -#define INT_WIDTH __INT_WIDTH__ -#undef UINT_WIDTH -#define UINT_WIDTH __INT_WIDTH__ -#undef LONG_WIDTH -#define LONG_WIDTH __LONG_WIDTH__ -#undef ULONG_WIDTH -#define ULONG_WIDTH __LONG_WIDTH__ -#undef LLONG_WIDTH -#define LLONG_WIDTH __LONG_LONG_WIDTH__ -#undef ULLONG_WIDTH -#define ULLONG_WIDTH __LONG_LONG_WIDTH__ -#endif - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L -#undef BOOL_MAX -#define BOOL_MAX 1 -#undef BOOL_WIDTH -#define BOOL_WIDTH 1 -#endif +#ifdef __cplusplus +} +#endif // __cplusplus #endif // !_LIMITS_H diff --git a/Userspace/libc/include/locale.h b/Userspace/libc/include/locale.h new file mode 100644 index 00000000..e345fd55 --- /dev/null +++ b/Userspace/libc/include/locale.h @@ -0,0 +1,79 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _LOCALE_H +#define _LOCALE_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + typedef struct lconv + { + char *currency_symbol; + char *decimal_point; + char frac_digits; + char *grouping; + char *int_curr_symbol; + char int_frac_digits; + char int_n_cs_precedes; + char int_n_sep_by_space; + char int_n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; + char int_p_sign_posn; + char *mon_decimal_point; + char *mon_grouping; + char *mon_thousands_sep; + char *negative_sign; + char n_cs_precedes; + char n_sep_by_space; + char n_sign_posn; + char *positive_sign; + char p_cs_precedes; + char p_sep_by_space; + char p_sign_posn; + char *thousands_sep; + } lconv; + + typedef struct locale_t + { + char data[1]; /* FIXME: implement locale_t */ + } locale_t; + +#define LC_ALL +#define LC_COLLATE +#define LC_CTYPE +#define LC_MESSAGES +#define LC_MONETARY +#define LC_NUMERIC +#define LC_TIME + + locale_t duplocale(locale_t); + void freelocale(locale_t); + const char *getlocalename_l(int, locale_t); + struct lconv *localeconv(void); + locale_t newlocale(int, const char *, locale_t); + char *setlocale(int, const char *); + locale_t uselocale(locale_t); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_LOCALE_H diff --git a/Userspace/libc/include/math.h b/Userspace/libc/include/math.h index 06529a81..3d654229 100644 --- a/Userspace/libc/include/math.h +++ b/Userspace/libc/include/math.h @@ -1,4 +1,226 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _MATH_H #define _MATH_H +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define FLT_EVAL_METHOD 0 + +#if FLT_EVAL_METHOD == 0 + typedef float float_t; + typedef double double_t; +#elif FLT_EVAL_METHOD == 1 +typedef float float_t; +typedef float double_t; +#elif FLT_EVAL_METHOD == 2 +typedef long double float_t; +typedef long double double_t; +#else +#error "Unsupported FLT_EVAL_METHOD" +#endif + + double acos(double); + float acosf(float); + double acosh(double); + float acoshf(float); + long double acoshl(long double); + long double acosl(long double); + double asin(double); + float asinf(float); + double asinh(double); + float asinhf(float); + long double asinhl(long double); + long double asinl(long double); + double atan(double); + double atan2(double, double); + float atan2f(float, float); + long double atan2l(long double, long double); + float atanf(float); + double atanh(double); + float atanhf(float); + long double atanhl(long double); + long double atanl(long double); + double cbrt(double); + float cbrtf(float); + long double cbrtl(long double); + double ceil(double); + float ceilf(float); + long double ceill(long double); + double copysign(double, double); + float copysignf(float, float); + long double copysignl(long double, long double); + double cos(double); + float cosf(float); + double cosh(double); + float coshf(float); + long double coshl(long double); + long double cosl(long double); + double erf(double); + double erfc(double); + float erfcf(float); + long double erfcl(long double); + float erff(float); + long double erfl(long double); + double exp(double); + double exp2(double); + float exp2f(float); + long double exp2l(long double); + float expf(float); + long double expl(long double); + double expm1(double); + float expm1f(float); + long double expm1l(long double); + double fabs(double); + float fabsf(float); + long double fabsl(long double); + double fdim(double, double); + float fdimf(float, float); + long double fdiml(long double, long double); + double floor(double); + float floorf(float); + long double floorl(long double); + double fma(double, double, double); + float fmaf(float, float, float); + long double fmal(long double, long double, long double); + double fmax(double, double); + float fmaxf(float, float); + long double fmaxl(long double, long double); + double fmin(double, double); + float fminf(float, float); + long double fminl(long double, long double); + double fmod(double, double); + float fmodf(float, float); + long double fmodl(long double, long double); + double frexp(double, int *); + float frexpf(float value, int *); + long double frexpl(long double value, int *); + double hypot(double, double); + float hypotf(float, float); + long double hypotl(long double, long double); + int ilogb(double); + int ilogbf(float); + int ilogbl(long double); + double j0(double); + double j1(double); + double jn(int, double); + double ldexp(double, int); + float ldexpf(float, int); + long double ldexpl(long double, int); + double lgamma(double); + float lgammaf(float); + long double lgammal(long double); + long long llrint(double); + long long llrintf(float); + long long llrintl(long double); + long long llround(double); + long long llroundf(float); + long long llroundl(long double); + double log(double); + double log10(double); + float log10f(float); + long double log10l(long double); + double log1p(double); + float log1pf(float); + long double log1pl(long double); + double log2(double); + float log2f(float); + long double log2l(long double); + double logb(double); + float logbf(float); + long double logbl(long double); + float logf(float); + long double logl(long double); + long lrint(double); + long lrintf(float); + long lrintl(long double); + long lround(double); + long lroundf(float); + long lroundl(long double); + double modf(double, double *); + float modff(float, float *); + long double modfl(long double, long double *); + double nan(const char *); + float nanf(const char *); + long double nanl(const char *); + double nearbyint(double); + float nearbyintf(float); + long double nearbyintl(long double); + double nextafter(double, double); + float nextafterf(float, float); + long double nextafterl(long double, long double); + double nexttoward(double, long double); + float nexttowardf(float, long double); + long double nexttowardl(long double, long double); + double pow(double, double); + float powf(float, float); + long double powl(long double, long double); + double remainder(double, double); + float remainderf(float, float); + long double remainderl(long double, long double); + double remquo(double, double, int *); + float remquof(float, float, int *); + long double remquol(long double, long double, int *); + double rint(double); + float rintf(float); + long double rintl(long double); + double round(double); + float roundf(float); + long double roundl(long double); + double scalb(double, double); + double scalbln(double, long); + float scalblnf(float, long); + long double scalblnl(long double, long); + double scalbn(double, int); + float scalbnf(float, int); + long double scalbnl(long double, int); + double sin(double); + float sinf(float); + double sinh(double); + float sinhf(float); + long double sinhl(long double); + long double sinl(long double); + double sqrt(double); + float sqrtf(float); + long double sqrtl(long double); + double tan(double); + float tanf(float); + double tanh(double); + float tanhf(float); + long double tanhl(long double); + long double tanl(long double); + double tgamma(double); + float tgammaf(float); + long double tgammal(long double); + double trunc(double); + float truncf(float); + long double truncl(long double); + double y0(double); + double y1(double); + double yn(int, double); + + extern int signgam; + +#ifdef __cplusplus +} +#endif // __cplusplus + #endif // !_MATH_H diff --git a/Userspace/libc/include/pthread.h b/Userspace/libc/include/pthread.h new file mode 100644 index 00000000..0bdcf61e --- /dev/null +++ b/Userspace/libc/include/pthread.h @@ -0,0 +1,137 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _PTHREAD_H +#define _PTHREAD_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include +#include +#include + +#define PTHREAD_CANCEL_ASYNCHRONOUS {0} +#define PTHREAD_CANCEL_ENABLE {0} +#define PTHREAD_CANCEL_DEFERRED {0} +#define PTHREAD_CANCEL_DISABLE {0} +#define PTHREAD_CANCELED {0} +#define PTHREAD_COND_INITIALIZER {0} +#define PTHREAD_CREATE_DETACHED {0} +#define PTHREAD_CREATE_JOINABLE {0} +#define PTHREAD_EXPLICIT_SCHED {0} +#define PTHREAD_INHERIT_SCHED {0} +#define PTHREAD_MUTEX_DEFAULT {0} +#define PTHREAD_MUTEX_ERRORCHECK {0} +#define PTHREAD_MUTEX_NORMAL {0} +#define PTHREAD_MUTEX_INITIALIZER {0} +#define PTHREAD_MUTEX_RECURSIVE {0} +#define PTHREAD_ONCE_INIT {0} +#define PTHREAD_PRIO_INHERIT {0} +#define PTHREAD_PRIO_NONE {0} +#define PTHREAD_PRIO_PROTECT {0} +#define PTHREAD_PROCESS_SHARED {0} +#define PTHREAD_PROCESS_PRIVATE {0} +#define PTHREAD_RWLOCK_INITIALIZER {0} +#define PTHREAD_SCOPE_PROCESS {0} +#define PTHREAD_SCOPE_SYSTEM {0} + + int pthread_attr_destroy(pthread_attr_t *); + int pthread_attr_getdetachstate(const pthread_attr_t *, int *); + int pthread_attr_getguardsize(const pthread_attr_t *, size_t *); + int pthread_attr_getinheritsched(const pthread_attr_t *, int *); + int pthread_attr_getschedparam(const pthread_attr_t *, struct sched_param *); + int pthread_attr_getschedpolicy(const pthread_attr_t *, int *); + int pthread_attr_getscope(const pthread_attr_t *, int *); + int pthread_attr_getstackaddr(const pthread_attr_t *, void **); + int pthread_attr_getstacksize(const pthread_attr_t *, size_t *); + int pthread_attr_init(pthread_attr_t *); + int pthread_attr_setdetachstate(pthread_attr_t *, int); + int pthread_attr_setguardsize(pthread_attr_t *, size_t); + int pthread_attr_setinheritsched(pthread_attr_t *, int); + int pthread_attr_setschedparam(pthread_attr_t *, const struct sched_param *); + int pthread_attr_setschedpolicy(pthread_attr_t *, int); + int pthread_attr_setscope(pthread_attr_t *, int); + int pthread_attr_setstackaddr(pthread_attr_t *, void *); + int pthread_attr_setstacksize(pthread_attr_t *, size_t); + int pthread_cancel(pthread_t); + void pthread_cleanup_push(void (*)(void *), void *); + void pthread_cleanup_pop(int); + int pthread_cond_broadcast(pthread_cond_t *); + int pthread_cond_destroy(pthread_cond_t *); + int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); + int pthread_cond_signal(pthread_cond_t *); + int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, const struct timespec *); + int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); + int pthread_condattr_destroy(pthread_condattr_t *); + int pthread_condattr_getpshared(const pthread_condattr_t *, int *); + int pthread_condattr_init(pthread_condattr_t *); + int pthread_condattr_setpshared(pthread_condattr_t *, int); + int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); + int pthread_detach(pthread_t); + int pthread_equal(pthread_t, pthread_t); + void pthread_exit(void *); + int pthread_getconcurrency(void); + int pthread_getschedparam(pthread_t, int *, struct sched_param *); + void *pthread_getspecific(pthread_key_t); + int pthread_join(pthread_t, void **); + int pthread_key_create(pthread_key_t *, void (*)(void *)); + int pthread_key_delete(pthread_key_t); + int pthread_mutex_destroy(pthread_mutex_t *); + int pthread_mutex_getprioceiling(const pthread_mutex_t *, int *); + int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); + int pthread_mutex_lock(pthread_mutex_t *mutex); + int pthread_mutex_setprioceiling(pthread_mutex_t *, int, int *); + int pthread_mutex_trylock(pthread_mutex_t *); + int pthread_mutex_unlock(pthread_mutex_t *mutex); + int pthread_mutexattr_destroy(pthread_mutexattr_t *); + int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *, int *); + int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *, int *); + int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *); + int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *); + int pthread_mutexattr_init(pthread_mutexattr_t *); + int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *, int); + int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int); + int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int); + int pthread_mutexattr_settype(pthread_mutexattr_t *, int); + int pthread_once(pthread_once_t *, void (*)(void)); + int pthread_rwlock_destroy(pthread_rwlock_t *); + int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); + int pthread_rwlock_rdlock(pthread_rwlock_t *); + int pthread_rwlock_tryrdlock(pthread_rwlock_t *); + int pthread_rwlock_trywrlock(pthread_rwlock_t *); + int pthread_rwlock_unlock(pthread_rwlock_t *); + int pthread_rwlock_wrlock(pthread_rwlock_t *); + int pthread_rwlockattr_destroy(pthread_rwlockattr_t *); + int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *); + int pthread_rwlockattr_init(pthread_rwlockattr_t *); + int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int); + pthread_t pthread_self(void); + int pthread_setcancelstate(int, int *); + int pthread_setcanceltype(int, int *); + int pthread_setconcurrency(int); + int pthread_setschedparam(pthread_t, int, const struct sched_param *); + int pthread_setspecific(pthread_key_t, const void *); + void pthread_testcancel(void); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_PTHREAD_H diff --git a/Userspace/libc/include/pwd.h b/Userspace/libc/include/pwd.h new file mode 100644 index 00000000..79d67523 --- /dev/null +++ b/Userspace/libc/include/pwd.h @@ -0,0 +1,49 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _PWD_H +#define _PWD_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include + + struct passwd + { + char *pw_name; /* User's login name. */ + uid_t pw_uid; /* Numerical user ID. */ + gid_t pw_gid; /* Numerical group ID. */ + char *pw_dir; /* Initial working directory. */ + char *pw_shell; /* Program to use as shell. */ + }; + + void endpwent(void); + struct passwd *getpwent(void); + struct passwd *getpwnam(const char *); + int getpwnam_r(const char *, struct passwd *, char *, size_t, struct passwd **); + struct passwd *getpwuid(uid_t); + int getpwuid_r(uid_t, struct passwd *, char *, size_t, struct passwd **); + void setpwent(void); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_PWD_H diff --git a/Userspace/libc/include/sched.h b/Userspace/libc/include/sched.h new file mode 100644 index 00000000..8c5f1d28 --- /dev/null +++ b/Userspace/libc/include/sched.h @@ -0,0 +1,41 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _SCHED_H +#define _SCHED_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include + + typedef struct sched_param + { + int sched_priority; /* Process or thread execution scheduling priority. */ + int sched_ss_low_priority; /* Low scheduling priority for sporadic server. */ + struct timespec sched_ss_repl_period; /* Replenishment period for sporadic server. */ + struct timespec sched_ss_init_budget; /* Initial budget for sporadic server. */ + int sched_ss_max_repl; /* Maximum pending replenishments for sporadic server. */ + } sched_param_t; + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_SCHED_H diff --git a/Userspace/libc/include/setjmp.h b/Userspace/libc/include/setjmp.h deleted file mode 100644 index 010025da..00000000 --- a/Userspace/libc/include/setjmp.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef _SETJMP_H -#define _SETJMP_H - -#include - -typedef struct -{ - uint64_t r15; - uint64_t r14; - uint64_t r13; - uint64_t r12; - uint64_t r11; - uint64_t r10; - uint64_t r9; - uint64_t r8; - uint64_t rbp; - uint64_t rsp; - uint64_t rdi; - uint64_t rsi; - uint64_t rdx; - uint64_t rcx; - uint64_t rbx; - uint64_t rax; - uint64_t rip; - uint64_t rflags; - uint64_t cs; - uint64_t fs; - uint64_t gs; -} jmp_buf[1]; - -int setjmp(jmp_buf env); -__attribute__((noreturn)) void longjmp(jmp_buf env, int value); - -#endif // !_SETJMP_H diff --git a/Userspace/libc/include/signal.h b/Userspace/libc/include/signal.h index 9be5e753..8c488ffe 100644 --- a/Userspace/libc/include/signal.h +++ b/Userspace/libc/include/signal.h @@ -1,102 +1,221 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _SIGNAL_H #define _SIGNAL_H -#include +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus -/* https://www-uxsup.csx.cam.ac.uk/courses/moved.Building/signals.pdf */ -/* https://man7.org/linux/man-pages/man7/signal.7.html */ +#include +#include +#include -/** @brief Hangup detected on controlling or death of controlling process */ -#define SIGHUP 1 +#define SIGNULL __SYS_SIGNULL +#define SIGABRT __SYS_SIGABRT +#define SIGALRM __SYS_SIGALRM +#define SIGBUS __SYS_SIGBUS +#define SIGCHLD __SYS_SIGCHLD +#define SIGCONT __SYS_SIGCONT +#define SIGFPE __SYS_SIGFPE +#define SIGHUP __SYS_SIGHUP +#define SIGILL __SYS_SIGILL +#define SIGINT __SYS_SIGINT +#define SIGKILL __SYS_SIGKILL +#define SIGPIPE __SYS_SIGPIPE +#define SIGQUIT __SYS_SIGQUIT +#define SIGSEGV __SYS_SIGSEGV +#define SIGSTOP __SYS_SIGSTOP +#define SIGTERM __SYS_SIGTERM +#define SIGTSTP __SYS_SIGTSTP +#define SIGTTIN __SYS_SIGTTIN +#define SIGTTOU __SYS_SIGTTOU +#define SIGUSR1 __SYS_SIGUSR1 +#define SIGUSR2 __SYS_SIGUSR2 +#define SIGPOLL __SYS_SIGPOLL +#define SIGPROF __SYS_SIGPROF +#define SIGSYS __SYS_SIGSYS +#define SIGTRAP __SYS_SIGTRAP +#define SIGURG __SYS_SIGURG +#define SIGVTALRM __SYS_SIGVTALRM +#define SIGXCPU __SYS_SIGXCPU +#define SIGXFSZ __SYS_SIGXFSZ +#define SIGCOMP1 __SYS_SIGCOMP1 +#define SIGCOMP2 __SYS_SIGCOMP2 +#define SIGCOMP3 __SYS_SIGCOMP3 +#define SIGRTMIN __SYS_SIGRTMIN +#define SIGRT_1 __SYS_SIGRT_1 +#define SIGRT_2 __SYS_SIGRT_2 +#define SIGRT_3 __SYS_SIGRT_3 +#define SIGRT_4 __SYS_SIGRT_4 +#define SIGRT_5 __SYS_SIGRT_5 +#define SIGRT_6 __SYS_SIGRT_6 +#define SIGRT_7 __SYS_SIGRT_7 +#define SIGRT_8 __SYS_SIGRT_8 +#define SIGRT_9 __SYS_SIGRT_9 +#define SIGRT_10 __SYS_SIGRT_10 +#define SIGRT_11 __SYS_SIGRT_11 +#define SIGRT_12 __SYS_SIGRT_12 +#define SIGRT_13 __SYS_SIGRT_13 +#define SIGRT_14 __SYS_SIGRT_14 +#define SIGRT_15 __SYS_SIGRT_15 +#define SIGRT_16 __SYS_SIGRT_16 +#define SIGRT_17 __SYS_SIGRT_17 +#define SIGRT_18 __SYS_SIGRT_18 +#define SIGRT_19 __SYS_SIGRT_19 +#define SIGRT_20 __SYS_SIGRT_20 +#define SIGRT_21 __SYS_SIGRT_21 +#define SIGRT_22 __SYS_SIGRT_22 +#define SIGRT_23 __SYS_SIGRT_23 +#define SIGRT_24 __SYS_SIGRT_24 +#define SIGRT_25 __SYS_SIGRT_25 +#define SIGRT_26 __SYS_SIGRT_26 +#define SIGRT_27 __SYS_SIGRT_27 +#define SIGRT_28 __SYS_SIGRT_28 +#define SIGRT_29 __SYS_SIGRT_29 +#define SIGRT_30 __SYS_SIGRT_30 +#define SIGRT_31 __SYS_SIGRT_31 +#define SIGRTMAX __SYS_SIGRTMAX +#define SIGNAL_MAX __SYS_SIGNAL_MAX -/** @brief Interrupt from keyboard */ -#define SIGINT 2 +#define SIG_TERM __SYS_SIG_TERM +// #define SIG_IGN __SYS_SIG_IGN +#define SIG_CORE __SYS_SIG_CORE +#define SIG_STOP __SYS_SIG_STOP +#define SIG_CONT __SYS_SIG_CONT -/** @brief Quit from keyboard */ -#define SIGQUIT 3 +#define SIG_BLOCK __SYS_SIG_BLOCK +#define SIG_UNBLOCK __SYS_SIG_UNBLOCK +#define SIG_SETMASK __SYS_SIG_SETMASK -/** @brief Illegal Instruction */ -#define SIGILL 4 +#define SA_NOCLDSTOP __SYS_SA_NOCLDSTOP +#define SA_ONSTACK __SYS_SA_ONSTACK +#define SA_RESETHAND __SYS_SA_RESETHAND +#define SA_RESTART __SYS_SA_RESTART +#define SA_SIGINFO __SYS_SA_SIGINFO +#define SA_NOCLDWAIT __SYS_SA_NOCLDWAIT +#define SA_NODEFER __SYS_SA_NODEFER -/** @brief Trace/breakpoint trap */ -#define SIGTRAP 5 +#define SS_ONSTACK +#define SS_DISABLE -/** @brief Abort signal from abort(3) */ -#define SIGABRT 6 +#define MINSIGSTKSZ +#define SIGSTKSZ -/** @brief Bus error (bad memory access) */ -#define SIGBUS 7 +#define SIG_ERR ((void (*)(int))__SYS_SIG_ERR) +#define SIG_DFL ((void (*)(int))__SYS_SIG_DFL) +#define SIG_IGN ((void (*)(int))__SYS_SIG_IGN) -/** @brief Floating point exception */ -#define SIGFPE 8 +#define SIGEV_NONE +#define SIGEV_SIGNAL +#define SIGEV_THREAD -/** @brief Kill signal */ -#define SIGKILL 9 + typedef unsigned long sigset_t; -/** @brief User-defined signal 1 */ -#define SIGUSR1 10 + union sigval + { + int sival_int; /* Integer signal value. */ + void *sival_ptr; /* Pointer signal value. */ + }; -/** @brief Invalid memory reference */ -#define SIGSEGV 11 + typedef struct siginfo_t + { + int si_signo; /* Signal number. */ + int si_code; /* Signal code. */ -/** @brief User-defined signal 2 */ -#define SIGUSR2 12 + int si_errno; /* If non-zero, an errno value associated with */ + /* this signal, as described in . */ -/** @brief Broken pipe: write to pipe with no readers */ -#define SIGPIPE 13 + pid_t si_pid; /* Sending process ID. */ + uid_t si_uid; /* Real user ID of sending process. */ + void *si_addr; /* Address that caused fault. */ + int si_status; /* Exit value or signal. */ + union sigval si_value; /* Signal value. */ + } siginfo_t; -/** @brief Timer signal from alarm(2) */ -#define SIGALRM 14 + typedef struct sigevent + { + int sigev_notify; /* Notification type. */ + int sigev_signo; /* Signal number. */ + union sigval sigev_value; /* Signal value. */ + void (*sigev_notify_function)(union sigval); /* Notification function. */ + pthread_attr_t *sigev_notify_attributes; /* Notification attributes. */ + } sigevent; -/** @brief Termination signal */ -#define SIGTERM 15 + struct sigaction + { + void (*sa_handler)(int); /* Pointer to a signal-catching function or one of the SIG_IGN or SIG_DFL. */ + sigset_t sa_mask; /* Set of signals to be blocked during execution of the signal handling function. */ + int sa_flags; /* Special flags. */ + void (*sa_sigaction)(int, siginfo_t *, void *); /* Pointer to a signal-catching function. */ + }; -/** @brief Stack fault on coprocessor (unused) */ -#define SIGSTKFLT 16 + typedef struct stack_t + { + void *ss_sp; /* Stack base or pointer. */ + size_t ss_size; /* Stack size. */ + int ss_flags; /* Flags. */ + } stack_t; -/** @brief Child stopped or terminated */ -#define SIGCHLD 17 + typedef struct mcontext_t + { + __UINTPTR_TYPE__ gregs[32]; /* General-purpose registers. */ + __UINTPTR_TYPE__ sp; /* Stack pointer. */ + __UINTPTR_TYPE__ pc; /* Program counter. */ + __UINTPTR_TYPE__ pstate; /* Processor state. */ + } mcontext_t; -/** @brief Continue if stopped */ -#define SIGCONT 18 + typedef struct ucontext_t + { + struct ucontext_t *uc_link; /* Pointer to the context that is resumed when this context returns. */ + sigset_t uc_sigmask; /* The set of signals that are blocked when this context is active. */ + stack_t uc_stack; /* The stack used by this context. */ + mcontext_t uc_mcontext; /* A machine-specific representation of the saved context. */ + } ucontext_t; -/** @brief Stop process */ -#define SIGSTOP 19 + int kill(pid_t pid, int sig); + int killpg(pid_t, int); + void psiginfo(const siginfo_t *, const char *); + void psignal(int, const char *); + int pthread_kill(pthread_t, int); + int pthread_sigmask(int, const sigset_t *restrict, sigset_t *restrict); + int raise(int); + int sig2str(int, char *); + int sigaction(int, const struct sigaction *restrict, struct sigaction *restrict); + int sigaddset(sigset_t *, int); + int sigaltstack(const stack_t *restrict, stack_t *restrict); + int sigdelset(sigset_t *, int); + int sigemptyset(sigset_t *); + int sigfillset(sigset_t *); + int sigismember(const sigset_t *, int); + void (*signal(int, void (*)(int)))(int); + int sigpending(sigset_t *); + int sigprocmask(int, const sigset_t *restrict, sigset_t *restrict); + int sigqueue(pid_t, int, union sigval); + int sigsuspend(const sigset_t *); + int sigtimedwait(const sigset_t *restrict, siginfo_t *restrict, const struct timespec *restrict); + int sigwait(const sigset_t *restrict, int *restrict); + int sigwaitinfo(const sigset_t *restrict, siginfo_t *restrict); + int str2sig(const char *restrict, int *restrict); -/** @brief Stop typed at terminal */ -#define SIGTSTP 20 - -/** @brief Terminal input for background process */ -#define SIGTTIN 21 - -/** @brief Terminal output for background process */ -#define SIGTTOU 22 - -/** @brief Urgent condition on socket */ -#define SIGURG 23 - -/** @brief CPU time limit exceeded */ -#define SIGXCPU 24 - -/** @brief File size limit exceeded */ -#define SIGXFSZ 25 - -/** @brief Virtual timer expired */ -#define SIGVTALRM 26 - -/** @brief Profiling timer expired */ -#define SIGPROF 27 - -/** @brief Window resize signal */ -#define SIGWINCH 28 - -/** @brief I/O now possible */ -#define SIGIO 29 - -/** @brief Power failure */ -#define SIGPWR 30 - -/** @brief Bad system call */ -#define SIGSYS 31 +#ifdef __cplusplus +} +#endif // __cplusplus #endif // !_SIGNAL_H diff --git a/Userspace/libc/include/spawn.h b/Userspace/libc/include/spawn.h deleted file mode 100644 index 9b9db5a3..00000000 --- a/Userspace/libc/include/spawn.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _SPAWN_H -#define _SPAWN_H - -#include -#include -#include -#include - -struct __spawn_action -{ - int __stub; -}; - -typedef struct -{ - short int __flags; - pid_t __pgrp; - sigset_t __sd; - sigset_t __ss; - struct sched_param __sp; - int __policy; - int __pad[16]; -} posix_spawnattr_t; - -typedef struct -{ - int __allocated; - int __used; - struct __spawn_action *__actions; - int __pad[16]; -} posix_spawn_file_actions_t; - -int posix_spawn(pid_t *pid, const char *path, - const posix_spawn_file_actions_t *file_actions, - const posix_spawnattr_t *attrp, - char *const argv[], - char *const envp[]); - -int posix_spawnp(pid_t *pid, const char *file, - const posix_spawn_file_actions_t *file_actions, - const posix_spawnattr_t *attrp, - char *const argv[], - char *const envp[]); - -#endif // !_SPAWN_H diff --git a/Userspace/libc/include/stdarg.h b/Userspace/libc/include/stdarg.h index 45202e9a..48d0366c 100644 --- a/Userspace/libc/include/stdarg.h +++ b/Userspace/libc/include/stdarg.h @@ -1,13 +1,37 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _STDARG_H #define _STDARG_H -#ifndef __va_list__ -typedef __builtin_va_list va_list; -#endif +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus -#define va_start(ap, last) __builtin_va_start(ap, last) -#define va_end(ap) __builtin_va_end(ap) -#define va_arg(ap, type) __builtin_va_arg(ap, type) + typedef __builtin_va_list va_list; + +#define va_start(ap, param) __builtin_va_start(ap, param) #define va_copy(dest, src) __builtin_va_copy(dest, src) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_end(ap) __builtin_va_end(ap) + +#ifdef __cplusplus +} +#endif // __cplusplus #endif // !_STDARG_H diff --git a/Userspace/libc/include/stdbool.h b/Userspace/libc/include/stdbool.h new file mode 100644 index 00000000..eea9050b --- /dev/null +++ b/Userspace/libc/include/stdbool.h @@ -0,0 +1,36 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define bool _Bool +#define true 1 +#define false 0 + +#define __bool_true_false_are_defined 1 + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_STDBOOL_H diff --git a/Userspace/libc/include/stddef.h b/Userspace/libc/include/stddef.h index d8932596..70c953c8 100644 --- a/Userspace/libc/include/stddef.h +++ b/Userspace/libc/include/stddef.h @@ -1,47 +1,44 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _STDDEF_H #define _STDDEF_H #ifdef __cplusplus -#define NULL 0 -#else +extern "C" +{ +#endif // __cplusplus + +#ifndef NULL #define NULL ((void *)0) -#define bool _Bool #endif -typedef __INT8_TYPE__ int8_t; -typedef __INT16_TYPE__ int16_t; -typedef __INT32_TYPE__ int32_t; -typedef __INT64_TYPE__ int64_t; -typedef __UINT8_TYPE__ uint8_t; -typedef __UINT16_TYPE__ uint16_t; -typedef __UINT32_TYPE__ uint32_t; -typedef __UINT64_TYPE__ uint64_t; + typedef struct + { + long long __a; + long double __b; + } max_align_t; -typedef __INT_LEAST8_TYPE__ int_least8_t; -typedef __INT_LEAST16_TYPE__ int_least16_t; -typedef __INT_LEAST32_TYPE__ int_least32_t; -typedef __INT_LEAST64_TYPE__ int_least64_t; -typedef __UINT_LEAST8_TYPE__ uint_least8_t; -typedef __UINT_LEAST16_TYPE__ uint_least16_t; -typedef __UINT_LEAST32_TYPE__ uint_least32_t; -typedef __UINT_LEAST64_TYPE__ uint_least64_t; + typedef __PTRDIFF_TYPE__ ptrdiff_t; + typedef __WCHAR_TYPE__ wchar_t; + typedef __SIZE_TYPE__ size_t; -typedef __INT_FAST8_TYPE__ int_fast8_t; -typedef __INT_FAST16_TYPE__ int_fast16_t; -typedef __INT_FAST32_TYPE__ int_fast32_t; -typedef __INT_FAST64_TYPE__ int_fast64_t; -typedef __UINT_FAST8_TYPE__ uint_fast8_t; -typedef __UINT_FAST16_TYPE__ uint_fast16_t; -typedef __UINT_FAST32_TYPE__ uint_fast32_t; -typedef __UINT_FAST64_TYPE__ uint_fast64_t; - -typedef __INTPTR_TYPE__ intptr_t; -typedef __UINTPTR_TYPE__ uintptr_t; - -typedef __INTMAX_TYPE__ intmax_t; -typedef __UINTMAX_TYPE__ uintmax_t; - -typedef __PTRDIFF_TYPE__ ptrdiff_t; -typedef __SIZE_TYPE__ size_t; +#ifdef __cplusplus +} +#endif // __cplusplus #endif // !_STDDEF_H diff --git a/Userspace/libc/include/stdint.h b/Userspace/libc/include/stdint.h index d653a307..067eba6f 100644 --- a/Userspace/libc/include/stdint.h +++ b/Userspace/libc/include/stdint.h @@ -1,6 +1,32 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _STDINT_H #define _STDINT_H -#include +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include + +#ifdef __cplusplus +} +#endif // __cplusplus #endif // !_STDINT_H diff --git a/Userspace/libc/include/stdio.h b/Userspace/libc/include/stdio.h index 4ba0ca20..ae09002c 100644 --- a/Userspace/libc/include/stdio.h +++ b/Userspace/libc/include/stdio.h @@ -1,87 +1,156 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _STDIO_H #define _STDIO_H -#include -#include -#include - -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 - -/** - * @brief EOF - End of file - * - * The value returned by several functions to indicate the end of file. - */ -#define EOF (-1) - -struct _IO_marker -{ - struct _IO_marker *_next; - struct _IO_FILE *_sbuf; - int _pos; -}; - -struct _IO_FILE -{ - int _flags; - - char *_IO_read_ptr; - char *_IO_read_end; - char *_IO_read_base; - char *_IO_write_base; - char *_IO_write_ptr; - char *_IO_write_end; - char *_IO_buf_base; - char *_IO_buf_end; - - __off_t _offset; - - struct _IO_marker *_markers; - struct _IO_FILE *_chain; - int _fileno; -}; - -typedef struct _IO_FILE FILE; - #ifdef __cplusplus extern "C" { +#endif // __cplusplus + +#include +#include +#include + +#define BUFSIZ 512 +#define FILENAME_MAX 255 +#define FOPEN_MAX 8 +#define _IOFBF 0 +#define _IOLBF 1 +#define _IONBF 2 +#define L_ctermid 20 +#define L_cuserid 20 +#define L_tmpnam 255 +#define SEEK_CUR 1 +#define SEEK_END 2 +#define SEEK_SET 0 +#define TMP_MAX 10000 + +#define EOF (-1) + +#ifndef NULL +#define NULL ((void *)0) #endif +#define P_tmpdir "/tmp/" + + typedef long fpos_t; + typedef unsigned long size_t; + + struct _IO_FILE + { + int fd; + char *buffer; + size_t buffer_size; + size_t buffer_pos; + int flags; + int error; + int eof; + }; + +#define _i_READ 1 +#define _i_WRITE 2 + extern struct _IO_FILE *_i_open_files[256]; + + typedef struct _IO_FILE FILE; + + extern char *optarg; + extern int opterr; + extern int optind; + extern int optopt; + extern FILE *stdin; extern FILE *stdout; extern FILE *stderr; + #define stdin stdin #define stdout stdout #define stderr stderr - FILE *freopen(const char *filename, const char *mode, FILE *stream); - FILE *fopen(const char *filename, const char *mode); - size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); - size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); - off_t fseek(FILE *stream, long offset, int whence); - long ftell(FILE *stream); - int fclose(FILE *fp); + void clearerr(FILE *); + char *ctermid(char *); + int dprintf(int, const char *restrict, ...); + int fclose(FILE *stream); + FILE *fdopen(int, const char *); + int feof(FILE *); + int ferror(FILE *); int fflush(FILE *stream); - int fprintf(FILE *stream, const char *format, ...); - int printf(const char *format, ...); - void setbuf(FILE *stream, char *buf); - int vfprintf(FILE *stream, const char *format, va_list arg); - int vsscanf(const char *s, const char *format, va_list arg); - int sscanf(const char *s, const char *format, ...); - + int fgetc(FILE *); + int fgetpos(FILE *restrict, fpos_t *restrict); + char *fgets(char *restrict, int, FILE *restrict); + int fileno(FILE *); + void flockfile(FILE *); + FILE *fmemopen(void *restrict, size_t, const char *restrict); + FILE *fopen(const char *restrict pathname, const char *restrict mode); + int fprintf(FILE *restrict, const char *restrict, ...); int fputc(int c, FILE *stream); + int fputs(const char *restrict s, FILE *restrict stream); + size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); + FILE *freopen(const char *restrict, const char *restrict, FILE *restrict); + int fscanf(FILE *restrict, const char *restrict, ...); + int fseek(FILE *stream, long offset, int whence); + int fseeko(FILE *, off_t, int); + int fsetpos(FILE *, const fpos_t *); + long ftell(FILE *); + off_t ftello(FILE *); + int ftrylockfile(FILE *); + void funlockfile(FILE *); + size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); + int getc(FILE *); + int getchar(void); + int getc_unlocked(FILE *); + int getchar_unlocked(void); + ssize_t getdelim(char **restrict, size_t *restrict, int, FILE *restrict); + ssize_t getline(char **restrict, size_t *restrict, FILE *restrict); + FILE *open_memstream(char **, size_t *); + int pclose(FILE *); + void perror(const char *); + FILE *popen(const char *, const char *); + int printf(const char *restrict format, ...); int putc(int c, FILE *stream); - int fputs(const char *s, FILE *stream); - int puts(const char *s); int putchar(int c); - - int fgetc(FILE *stream); + int putc_unlocked(int c, FILE *stream); + int putchar_unlocked(int c); + int puts(const char *s); + int remove(const char *); + int rename(const char *, const char *); + int renameat(int, const char *, int, const char *); + void rewind(FILE *); + int scanf(const char *restrict, ...); + void setbuf(FILE *restrict, char *restrict); + int setvbuf(FILE *restrict, char *restrict, int, size_t); + int snprintf(char *restrict, size_t, const char *restrict, ...); + int sprintf(char *restrict, const char *restrict, ...); + int sscanf(const char *restrict, const char *restrict, ...); + FILE *tmpfile(void); + char *tmpnam(char *); + int ungetc(int, FILE *); + int vdprintf(int, const char *restrict, va_list); + int vfprintf(FILE *restrict, const char *restrict, va_list); + int vfscanf(FILE *restrict, const char *restrict, va_list); + int vprintf(const char *restrict, va_list); + int vscanf(const char *restrict, va_list); + int vsnprintf(char *restrict, size_t, const char *restrict, va_list); + int vsprintf(char *restrict, const char *restrict, va_list); + int vsscanf(const char *restrict, const char *restrict, va_list); #ifdef __cplusplus } -#endif +#endif // __cplusplus -#endif +#endif // !_STDIO_H diff --git a/Userspace/libc/include/stdlib.h b/Userspace/libc/include/stdlib.h index ddcd03c5..a543744e 100644 --- a/Userspace/libc/include/stdlib.h +++ b/Userspace/libc/include/stdlib.h @@ -1,34 +1,115 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _STDLIB_H #define _STDLIB_H -#include - #ifdef __cplusplus extern "C" { -#endif +#endif // __cplusplus + +#include +#include +#include +#include #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 +#define NULL ((void *)0) +#define RAND_MAX 32767 +#define MB_CUR_MAX 4 + + typedef struct + { + int quot; + int rem; + } div_t; + typedef struct + { + long quot; + long rem; + } ldiv_t; + + typedef unsigned long size_t; + // typedef __WCHAR_TYPE__ wchar_t; + + long a64l(const char *); void abort(void); - int atexit(void (*function)(void)); + int abs(int); + int atexit(void (*func)(void)); + double atof(const char *); + int atoi(const char *); + long int atol(const char *); + void *bsearch(const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); + void *calloc(size_t, size_t); + div_t div(int, int); + double drand48(void); + char *ecvt(double, int, int *, int *); + double erand48(unsigned short int[3]); void exit(int status); - int atoi(const char *nptr); - char *getenv(const char *name); - - void *malloc(size_t Size); - void *realloc(void *Address, size_t Size); - void *calloc(size_t Count, size_t Size); - void free(void *Address); - int system(const char *command); - - double atof(const char *nptr); - - extern void perror(const char *s); + char *fcvt(double, int, int *, int *); + void free(void *ptr); + char *gcvt(double, int, char *); + char *getenv(const char *); + int getsubopt(char **, char *const *, char **); + int grantpt(int); + char *initstate(unsigned int, char *, size_t); + long int jrand48(unsigned short int[3]); + char *l64a(long); + long int labs(long int); + void lcong48(unsigned short int[7]); + ldiv_t ldiv(long int, long int); + long int lrand48(void); + void *malloc(size_t size); + int mblen(const char *, size_t); + size_t mbstowcs(wchar_t *, const char *, size_t); + int mbtowc(wchar_t *, const char *, size_t); + char *mktemp(char *); + int mkstemp(char *); + long int mrand48(void); + long int nrand48(unsigned short int[3]); + char *ptsname(int); + int putenv(char *); + void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); + int rand(void); + int rand_r(unsigned int *); + long random(void); + void *realloc(void *, size_t); + char *realpath(const char *, char *); + unsigned short int seed48(unsigned short int[3]); + void setkey(const char *); + char *setstate(const char *); + void srand(unsigned int); + void srand48(long int); + void srandom(unsigned); + double strtod(const char *, char **); + long int strtol(const char *, char **, int); + unsigned long int strtoul(const char *, char **, int); + int system(const char *); + int ttyslot(void); + int unlockpt(int); + void *valloc(size_t); + size_t wcstombs(char *, const wchar_t *, size_t); + int wctomb(char *, wchar_t); #ifdef __cplusplus } -#endif +#endif // __cplusplus -#endif +#endif // !_STDLIB_H diff --git a/Userspace/libc/include/string.h b/Userspace/libc/include/string.h index 1fa7500f..b77231cf 100644 --- a/Userspace/libc/include/string.h +++ b/Userspace/libc/include/string.h @@ -1,30 +1,71 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _STRING_H #define _STRING_H -#include - #ifdef __cplusplus extern "C" { -#endif +#endif // __cplusplus - size_t strlen(const char *str); - int strcmp(const char *l, const char *r); - int strncmp(const char *s1, const char *s2, size_t n); - int strcasecmp(const char *s1, const char *s2); - int strncasecmp(const char *string1, const char *string2, size_t count); - char *strstr(const char *haystack, const char *needle); - char *strncpy(char *destination, const char *source, unsigned long num); - char *strdup(const char *s); - char *strchr(char const *s, int c); - char *strrchr(char const *s, int c); +#include +#include - void *memcpy(void *dest, const void *src, size_t n); - void *memset(void *dest, int c, size_t n); - void *memmove(void *dest, const void *src, size_t n); + void *memccpy(void *restrict, const void *restrict, int, size_t); + void *memchr(const void *, int, size_t); + int memcmp(const void *, const void *, size_t); + void *memcpy(void *restrict s1, const void *restrict s2, size_t n); + void *memmem(const void *, size_t, const void *, size_t); + void *memmove(void *, const void *, size_t); + void *memset(void *, int, size_t); + char *stpcpy(char *restrict, const char *restrict); + char *stpncpy(char *restrict, const char *restrict, size_t); + char *strcat(char *restrict, const char *restrict); + char *strchr(const char *, int); + int strcmp(const char *s1, const char *s2); + int strcoll(const char *, const char *); + int strcoll_l(const char *, const char *, locale_t); + char *strcpy(char *restrict, const char *restrict); + size_t strcspn(const char *, const char *); + char *strdup(const char *); + char *strerror(int); + char *strerror_l(int, locale_t); + int strerror_r(int, char *, size_t); + size_t strlcat(char *restrict, const char *restrict, size_t); + size_t strlcpy(char *restrict, const char *restrict, size_t); + size_t strlen(const char *); + char *strncat(char *restrict, const char *restrict, size_t); + int strncmp(const char *, const char *, size_t); + char *strncpy(char *restrict, const char *restrict, size_t); + char *strndup(const char *, size_t); + size_t strnlen(const char *, size_t); + char *strpbrk(const char *, const char *); + char *strrchr(const char *, int); + char *strsignal(int); + size_t strspn(const char *, const char *); + char *strstr(const char *, const char *); + char *strtok(char *restrict, const char *restrict); + char *strtok_r(char *restrict, const char *restrict, char **restrict); + size_t strxfrm(char *restrict, const char *restrict, size_t); + size_t strxfrm_l(char *restrict, const char *restrict, size_t, locale_t); #ifdef __cplusplus } -#endif +#endif // __cplusplus -#endif +#endif // !_STRING_H diff --git a/Userspace/libc/include/strings.h b/Userspace/libc/include/strings.h deleted file mode 100644 index e445ee89..00000000 --- a/Userspace/libc/include/strings.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef _STRINGS_H -#define _STRINGS_H - -#endif // !_STRINGS_H diff --git a/Userspace/libc/include/sys/mman.h b/Userspace/libc/include/sys/mman.h new file mode 100644 index 00000000..9c58d113 --- /dev/null +++ b/Userspace/libc/include/sys/mman.h @@ -0,0 +1,73 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef _SYS_MMAN_H +#define _SYS_MMAN_H + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 +#define PROT_NONE 0x0 + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x4 +#define MAP_ANONYMOUS 0x8 +#define MAP_ANON MAP_ANONYMOUS + +#define MS_ASYNC 0x01 +#define MS_SYNC 0x02 +#define MS_INVALIDATE 0x04 + +#define MCL_CURRENT 0x01 +#define MCL_FUTURE 0x02 + +#define MAP_FAILED ((void *)-1) + + typedef struct posix_typed_mem_info + { + /* Maximum length which may be allocated from a typed memory object. */ + size_t posix_tmi_length; + } posix_typed_mem_info; + + int mlock(const void *, size_t); + int mlockall(int); + void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); + int mprotect(void *addr, size_t len, int prot); + int msync(void *, size_t, int); + int munlock(const void *, size_t); + int munlockall(void); + int munmap(void *addr, size_t len); + int posix_madvise(void *, size_t, int); + int posix_mem_offset(const void *restrict, size_t, off_t *restrict, size_t *restrict, int *restrict); + int posix_typed_mem_get_info(int, struct posix_typed_mem_info *); + int posix_typed_mem_open(const char *, int, int); + int shm_open(const char *, int, mode_t); + int shm_unlink(const char *); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_SYS_MMAN_H diff --git a/Userspace/libc/include/sys/stat.h b/Userspace/libc/include/sys/stat.h index 61e31c01..21a6b5c5 100644 --- a/Userspace/libc/include/sys/stat.h +++ b/Userspace/libc/include/sys/stat.h @@ -1,18 +1,108 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _SYS_STAT_H #define _SYS_STAT_H -typedef unsigned int __dev_t; -typedef unsigned short __ino_t; -typedef unsigned short __mode_t; -typedef long __off_t; +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus -#define dev_t __dev_t -#define ino_t __ino_t -#define mode_t __mode_t -#define off_t __off_t +#include -int mkdir(const char *path, mode_t mode); -int remove(const char *pathname); -int rename(const char *oldpath, const char *newpath); + struct stat + { + dev_t st_dev; /* Device ID of device containing file. */ + ino_t st_ino; /* File serial number. */ + mode_t st_mode; /* Mode of file (see below). */ + nlink_t st_nlink; /* Number of hard links to the file. */ + uid_t st_uid; /* User ID of file. */ + gid_t st_gid; /* Group ID of file. */ + dev_t st_rdev; /* Device ID (if file is character or block special). */ + off_t st_size; /* For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link. For a shared memory object, the length in bytes. For a typed memory object, the length in bytes. For other file types, the use of this field is unspecified. */ + struct timespec st_atim; /* Last data access timestamp. */ + struct timespec st_mtim; /* Last data modification timestamp. */ + struct timespec st_ctim; /* Last file status change timestamp. */ + blksize_t st_blksize; /* A file system-specific preferred I/O block size for this object. In some file system types, this may vary from file to file. */ + blkcnt_t st_blocks; /* Number of blocks allocated for this object. */ + }; -#endif +#define S_IFMT +#define S_IFBLK +#define S_IFCHR +#define S_IFIFO +#define S_IFREG +#define S_IFDIR +#define S_IFLNK +#define S_IFSOCK + +#define S_IRWXU 0700 +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +#define S_IRWXG 070 +#define S_IRGRP 040 +#define S_IWGRP 020 +#define S_IXGRP 010 +#define S_IRWXO 07 +#define S_IROTH 04 +#define S_IWOTH 02 +#define S_IXOTH 01 + +#define S_ISUID 04000 +#define S_ISGID 02000 +#define S_ISVTX 01000 + +#define S_ISBLK(m) +#define S_ISCHR(m) +#define S_ISDIR(m) +#define S_ISFIFO(m) +#define S_ISREG(m) +#define S_ISLNK(m) +#define S_ISSOCK(m) + +#define S_TYPEISMQ(buf) +#define S_TYPEISSEM(buf) +#define S_TYPEISSHM(buf) +#define S_TYPEISTMO(buf) + +#define UTIME_NOW +#define UTIME_OMIT + + int chmod(const char *, mode_t); + int fchmod(int, mode_t); + int fchmodat(int, const char *, mode_t, int); + int fstat(int, struct stat *); + int fstatat(int, const char *restrict, struct stat *restrict, int); + int futimens(int, const struct timespec[2]); + int lstat(const char *restrict, struct stat *restrict); + int mkdir(const char *, mode_t); + int mkdirat(int, const char *, mode_t); + int mkfifo(const char *, mode_t); + int mkfifoat(int, const char *, mode_t); + int mknod(const char *, mode_t, dev_t); + int mknodat(int, const char *, mode_t, dev_t); + int stat(const char *restrict, struct stat *restrict); + mode_t umask(mode_t); + int utimensat(int, const char *, const struct timespec[2], int); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_SYS_STAT_H diff --git a/Userspace/libc/include/sys/time.h b/Userspace/libc/include/sys/time.h deleted file mode 100644 index 71fa5830..00000000 --- a/Userspace/libc/include/sys/time.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SYS_TIME_H -#define _SYS_TIME_H - -#include - -#endif diff --git a/Userspace/libc/include/sys/types.h b/Userspace/libc/include/sys/types.h index b8147a00..61ebc2b3 100644 --- a/Userspace/libc/include/sys/types.h +++ b/Userspace/libc/include/sys/types.h @@ -1,37 +1,141 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _SYS_TYPES_H #define _SYS_TYPES_H -#ifndef PUBLIC -#define PUBLIC __attribute__((visibility("default"))) -#endif // !PUBLIC +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus -#ifndef PRIVATE -#define PRIVATE __attribute__((visibility("hidden"))) -#endif // !PRIVATE +#define __iptr __INTPTR_TYPE__ + __iptr __check_errno(__iptr status, __iptr err); -typedef int __pid_t; -typedef int __ssize_t; -typedef unsigned int __id_t; -typedef unsigned int __useconds_t; +#ifndef restrict +#define restrict __restrict__ +#endif // restrict -#ifndef __pid_t_defined -typedef __pid_t pid_t; -#define __pid_t_defined +#ifndef export +#define export __attribute__((__visibility__("default"))) +#endif // export + + typedef long blkcnt_t; + + typedef long blksize_t; + + typedef long clock_t; + typedef int clockid_t; + + typedef unsigned long dev_t; + + typedef unsigned long fsblkcnt_t; + typedef unsigned long fsfilcnt_t; + + typedef unsigned int gid_t; + typedef unsigned int id_t; + typedef unsigned long ino_t; + typedef unsigned short reclen_t; + + typedef int key_t; + + typedef unsigned int mode_t; + typedef unsigned int nlink_t; + + typedef long off_t; + + typedef int pid_t; + + typedef struct pthread_attr_t + { + char __data; + } pthread_attr_t; + + typedef struct pthread_cond_t + { + char __data; + } pthread_cond_t; + + typedef struct pthread_condattr_t + { + char __data; + } pthread_condattr_t; + + typedef unsigned int pthread_key_t; + + typedef struct pthread_mutex_t + { + short locked; + char __data; + } pthread_mutex_t; + + typedef struct pthread_mutexattr_t + { + char __data; + } pthread_mutexattr_t; + + typedef struct pthread_once_t + { + int __initialized; + } pthread_once_t; + + typedef struct pthread_rwlock_t + { + char __data; + } pthread_rwlock_t; + + typedef struct pthread_rwlockattr_t + { + char __data; + } pthread_rwlockattr_t; + + typedef struct pthread_barrier_t + { + char __data; + } pthread_barrier_t; + + typedef unsigned long size_t; + typedef long ssize_t; + + typedef long suseconds_t; + typedef long time_t; + + typedef int timer_t; + + typedef unsigned int uid_t; + typedef unsigned int useconds_t; + + typedef struct __pthread + { + struct __pthread *Self; + /* For __tls_get_addr */ + __UINTPTR_TYPE__ *Storage; + + int CurrentError; + } __pthread; + +#ifdef __cplusplus + typedef unsigned long pthread_t; +#else +typedef struct __pthread *pthread_t; #endif -#ifndef __id_t_defined -typedef __id_t id_t; -#define __id_t_defined -#endif +#ifdef __cplusplus +} +#endif // __cplusplus -#ifndef __useconds_t_defined -typedef __useconds_t useconds_t; -#define __useconds_t_defined -#endif - -#ifndef __ssize_t_defined -typedef __ssize_t ssize_t; -#define __ssize_t_defined -#endif - -#endif +#endif // !_SYS_TYPES_H diff --git a/Userspace/libc/include/sys/wait.h b/Userspace/libc/include/sys/wait.h index 0d58ad3e..032105de 100644 --- a/Userspace/libc/include/sys/wait.h +++ b/Userspace/libc/include/sys/wait.h @@ -1,108 +1,65 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _SYS_WAIT_H #define _SYS_WAIT_H -#include - -typedef enum +#ifdef __cplusplus +extern "C" { - P_ALL, /* Wait for any child. */ - P_PID, /* Wait for specified process. */ - P_PGID /* Wait for members of process group. */ -} idtype_t; +#endif // __cplusplus -typedef struct -{ - int stub; -} siginfo_t; +#include -#include +/* waitpid() */ +#define WCONTINUED 0x00000008 +#define WNOHANG 0x00000001 +#define WUNTRACED 0x00000002 -#define WCONTINUED 1 -#define WNOHANG 2 -#define WUNTRACED 4 -#define WEXITED 8 -#define WNOWAIT 16 -#define WSTOPPED 32 +/* waitid() */ +#define WEXITED 0x00000004 +#define WNOWAIT 0x00000020 +#define WSTOPPED 0x00000002 -/** - * @brief Macro for extracting the exit status from a status value. - * - * If the child process terminated normally by calling exit(3) or _exit(2), - * the macro WEXITSTATUS() returns the low-order 8 bits of the status value. - * - * @param status The status value to extract the exit status from. - * @return The exit status of the child process. - */ -#define WEXITSTATUS(status) __WEXITSTATUS(status) +#define WCOREDUMP(status) ((status) & 0x80) +#define WEXITSTATUS(status) (((status) >> 8) & 0xFF) +#define WIFCONTINUED(status) ((status) == 0xFFFF) +#define WIFEXITED(status) (((status) & 0x7F) == 0) +#define WIFSIGNALED(status) (((status) & 0x7F) > 0 && (((status) & 0x7F) != 0x7F)) +#define WIFSTOPPED(status) (((status) & 0xFF) == 0x7F) +#define WSTOPSIG(status) WEXITSTATUS(status) +#define WTERMSIG(status) ((status) & 0x7F) -/** - * @brief Macro for extracting the termination signal from a status value. - * - * If the child process was terminated by a signal, the macro WTERMSIG() - * returns the signal number of the terminating signal. - * - * @param status The status value to extract the termination signal from. - * @return The termination signal of the child process. - */ -#define WTERMSIG(status) __WTERMSIG(status) + typedef enum + { + P_ALL, + P_PGID, + P_PID + } idtype_t; -/** - * @brief Macro for extracting the stop signal from a status value. - * - * If the child process was stopped by a signal, the macro WSTOPSIG() - * returns the signal number of the stop signal. - * - * @param status The status value to extract the stop signal from. - * @return The stop signal of the child process. - */ -#define WSTOPSIG(status) __WSTOPSIG(status) + typedef unsigned int id_t; + typedef int pid_t; -/** - * @brief Macro for testing whether a process exited normally. - * - * If the child process terminated normally by calling exit(3) or _exit(2), - * the macro WIFEXITED() returns a nonzero value. Otherwise, it returns 0. - * - * @param status The status value to test. - * @return A nonzero value if the child process exited normally, 0 otherwise. - */ -#define WIFEXITED(status) __WIFEXITED(status) + pid_t wait(int *); + int waitid(idtype_t, id_t, siginfo_t *, int); + pid_t waitpid(pid_t, int *, int); -/** - * @brief Macro for testing whether a process was terminated by a signal. - * - * If the child process was terminated by a signal, the macro WIFSIGNALED() - * returns a nonzero value. Otherwise, it returns 0. - * - * @param status The status value to test. - * @return A nonzero value if the child process was terminated by a signal, 0 otherwise. - */ -#define WIFSIGNALED(status) __WIFSIGNALED(status) +#ifdef __cplusplus +} +#endif // __cplusplus -/** - * @brief Macro for testing whether a process was stopped by a signal. - * - * If the child process was stopped by a signal, the macro WIFSTOPPED() - * returns a nonzero value. Otherwise, it returns 0. - * - * @param status The status value to test. - * @return A nonzero value if the child process was stopped by a signal, 0 otherwise. - */ -#define WIFSTOPPED(status) __WIFSTOPPED(status) - -/** - * @brief Macro for testing whether a stopped process was continued. - * - * If the child process was stopped and has been resumed by delivery of SIGCONT, - * the macro WIFCONTINUED() returns a nonzero value. Otherwise, it returns 0. - * - * @param status The status value to test. - * @return A nonzero value if the child process was continued, 0 otherwise. - */ -#define WIFCONTINUED(status) __WIFCONTINUED(status) - -pid_t wait(int *wstatus); -pid_t waitpid(pid_t pid, int *wstatus, int options); -int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); - -#endif +#endif // !_SYS_WAIT_H diff --git a/Userspace/libc/include/time.h b/Userspace/libc/include/time.h index 4c01e32c..dd7b3efd 100644 --- a/Userspace/libc/include/time.h +++ b/Userspace/libc/include/time.h @@ -1,4 +1,96 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _TIME_H #define _TIME_H -#endif +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#include +#include +#include +#include + + typedef struct tm + { + int tm_sec; /* Seconds [0,60]. */ + int tm_min; /* Minutes [0,59]. */ + int tm_hour; /* Hour [0,23]. */ + int tm_mday; /* Day of month [1,31]. */ + int tm_mon; /* Month of year [0,11]. */ + int tm_year; /* Years since 1900. */ + int tm_wday; /* Day of week [0,6] (Sunday =0). */ + int tm_yday; /* Day of year [0,365]. */ + int tm_isdst; /* Daylight Saving flag. */ + long tm_gmtoff; /* Seconds east of UTC. */ + const char *tm_zone; /* Timezone abbreviation. */ + } tm; + + typedef struct itimerspec + { + struct timespec it_interval; /* Timer period. */ + struct timespec it_value; /* Timer expiration. */ + } itimerspec; + +#define CLOCKS_PER_SEC +#define TIME_UTC +#define CLOCK_MONOTONIC __SYS_CLOCK_MONOTONIC +#define CLOCK_PROCESS_CPUTIME_ID __SYS_CLOCK_PROCESS_CPUTIME_ID +#define CLOCK_REALTIME __SYS_CLOCK_REALTIME +#define CLOCK_THREAD_CPUTIME_ID __SYS_CLOCK_THREAD_CPUTIME_ID +#define TIMER_ABSTIME + + extern int daylight; + extern long timezone; + extern char *tzname[]; + + char *asctime(const struct tm *); + clock_t clock(void); + int clock_getcpuclockid(pid_t, clockid_t *); + int clock_getres(clockid_t, struct timespec *); + int clock_gettime(clockid_t, struct timespec *); + int clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); + int clock_settime(clockid_t, const struct timespec *); + char *ctime(const time_t *); + double difftime(time_t, time_t); + struct tm *getdate(const char *); + struct tm *gmtime(const time_t *); + struct tm *gmtime_r(const time_t *restrict, struct tm *restrict); + struct tm *localtime(const time_t *); + struct tm *localtime_r(const time_t *restrict, struct tm *restrict); + time_t mktime(struct tm *); + int nanosleep(const struct timespec *, struct timespec *); + size_t strftime(char *restrict, size_t, const char *restrict, const struct tm *restrict); + size_t strftime_l(char *restrict, size_t, const char *restrict, const struct tm *restrict, locale_t); + char *strptime(const char *restrict, const char *restrict, struct tm *restrict); + time_t time(time_t *); + int timer_create(clockid_t, struct sigevent *restrict, timer_t *restrict); + int timer_delete(timer_t); + int timer_getoverrun(timer_t); + int timer_gettime(timer_t, struct itimerspec *); + int timer_settime(timer_t, int, const struct itimerspec *restrict, struct itimerspec *restrict); + int timespec_get(struct timespec *, int); + void tzset(void); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !_TIME_H diff --git a/Userspace/libc/include/types.h b/Userspace/libc/include/types.h deleted file mode 100644 index 17230b46..00000000 --- a/Userspace/libc/include/types.h +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef __FENNIX_LIBC_TYPES_H__ -#define __FENNIX_LIBC_TYPES_H__ - -#include -#include - -#ifdef __cplusplus -#define EXTERNC extern "C" -#define START_EXTERNC \ - EXTERNC \ - { -#define END_EXTERNC \ - } -#else -#define EXTERNC -#define START_EXTERNC -#define END_EXTERNC -#endif - -#define asm __asm__ -#define asmv __asm__ volatile - -#define true 1 -#define false 0 - -#ifdef __cplusplus -#define foreach for -#define in : -#endif - -#define UNUSED(x) (void)(x) -#define CONCAT(x, y) x##y - -#define toupper(c) ((c)-0x20 * (((c) >= 'a') && ((c) <= 'z'))) -#define tolower(c) ((c) + 0x20 * (((c) >= 'A') && ((c) <= 'Z'))) - -#define ALIGN_UP(x, align) ((__typeof__(x))(((uint64_t)(x) + ((align)-1)) & (~((align)-1)))) -#define ALIGN_DOWN(x, align) ((__typeof__(x))((x) & (~((align)-1)))) - -#define offsetof(type, member) __builtin_offsetof(type, member) - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#define VPOKE(type, address) (*((volatile type *)(address))) -#define POKE(type, address) (*((type *)(address))) - -#ifndef __cplusplus - -#ifdef __STDC__ -#ifdef __STDC_VERSION__ -#if (__STDC_VERSION__ >= 201710L) -#define C_LANGUAGE_STANDARD 2018 -#elif (__STDC_VERSION__ >= 201112L) -#define C_LANGUAGE_STANDARD 2011 -#elif (__STDC_VERSION__ >= 199901L) -#define C_LANGUAGE_STANDARD 1999 -#elif (__STDC_VERSION__ >= 199409L) -#define C_LANGUAGE_STANDARD 1995 -#endif -#else -#define C_LANGUAGE_STANDARD 1990 -#endif -#else -#define C_LANGUAGE_STANDARD 1972 -#endif - -#else - -#ifdef __STDC__ -#ifdef __cplusplus -#if (__cplusplus >= 202100L) -#define CPP_LANGUAGE_STANDARD 2023 -#elif (__cplusplus >= 202002L) -#define CPP_LANGUAGE_STANDARD 2020 -#elif (__cplusplus >= 201703L) -#define CPP_LANGUAGE_STANDARD 2017 -#elif (__cplusplus >= 201402L) -#define CPP_LANGUAGE_STANDARD 2014 -#elif (__cplusplus >= 201103L) -#define CPP_LANGUAGE_STANDARD 2011 -#elif (__cplusplus >= 199711L) -#define CPP_LANGUAGE_STANDARD 1998 -#endif -#else -#define CPP_LANGUAGE_STANDARD __cplusplus -#endif -#else -#define CPP_LANGUAGE_STANDARD __cplusplus -#endif - -#endif // __cplusplus - -typedef __INT8_TYPE__ int8_t; -typedef __INT16_TYPE__ int16_t; -typedef __INT32_TYPE__ int32_t; -typedef __INT64_TYPE__ int64_t; -typedef __UINT8_TYPE__ uint8_t; -typedef __UINT16_TYPE__ uint16_t; -typedef __UINT32_TYPE__ uint32_t; -typedef __UINT64_TYPE__ uint64_t; - -typedef __INT_LEAST8_TYPE__ int_least8_t; -typedef __INT_LEAST16_TYPE__ int_least16_t; -typedef __INT_LEAST32_TYPE__ int_least32_t; -typedef __INT_LEAST64_TYPE__ int_least64_t; -typedef __UINT_LEAST8_TYPE__ uint_least8_t; -typedef __UINT_LEAST16_TYPE__ uint_least16_t; -typedef __UINT_LEAST32_TYPE__ uint_least32_t; -typedef __UINT_LEAST64_TYPE__ uint_least64_t; - -typedef __INT_FAST8_TYPE__ int_fast8_t; -typedef __INT_FAST16_TYPE__ int_fast16_t; -typedef __INT_FAST32_TYPE__ int_fast32_t; -typedef __INT_FAST64_TYPE__ int_fast64_t; -typedef __UINT_FAST8_TYPE__ uint_fast8_t; -typedef __UINT_FAST16_TYPE__ uint_fast16_t; -typedef __UINT_FAST32_TYPE__ uint_fast32_t; -typedef __UINT_FAST64_TYPE__ uint_fast64_t; - -typedef __INTPTR_TYPE__ intptr_t; -typedef __UINTPTR_TYPE__ uintptr_t; - -typedef __INTMAX_TYPE__ intmax_t; -typedef __UINTMAX_TYPE__ uintmax_t; - -typedef __PTRDIFF_TYPE__ ptrdiff_t; -typedef __SIZE_TYPE__ size_t; - -#define INT8_MAX __INT8_MAX__ -#define INT8_MIN (-INT8_MAX - 1) -#define UINT8_MAX __UINT8_MAX__ -#define INT16_MAX __INT16_MAX__ -#define INT16_MIN (-INT16_MAX - 1) -#define UINT16_MAX __UINT16_MAX__ -#define INT32_MAX __INT32_MAX__ -#define INT32_MIN (-INT32_MAX - 1) -#define UINT32_MAX __UINT32_MAX__ -#define INT64_MAX __INT64_MAX__ -#define INT64_MIN (-INT64_MAX - 1) -#define UINT64_MAX __UINT64_MAX__ - -#define INT_LEAST8_MAX __INT_LEAST8_MAX__ -#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1) -#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ -#define INT_LEAST16_MAX __INT_LEAST16_MAX__ -#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1) -#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ -#define INT_LEAST32_MAX __INT_LEAST32_MAX__ -#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1) -#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ -#define INT_LEAST64_MAX __INT_LEAST64_MAX__ -#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1) -#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ - -#define INT_FAST8_MAX __INT_FAST8_MAX__ -#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) -#define UINT_FAST8_MAX __UINT_FAST8_MAX__ -#define INT_FAST16_MAX __INT_FAST16_MAX__ -#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) -#define UINT_FAST16_MAX __UINT_FAST16_MAX__ -#define INT_FAST32_MAX __INT_FAST32_MAX__ -#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) -#define UINT_FAST32_MAX __UINT_FAST32_MAX__ -#define INT_FAST64_MAX __INT_FAST64_MAX__ -#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) -#define UINT_FAST64_MAX __UINT_FAST64_MAX__ - -#define INTPTR_MAX __INTPTR_MAX__ -#define INTPTR_MIN (-INTPTR_MAX - 1) -#define UINTPTR_MAX __UINTPTR_MAX__ - -#define INTMAX_MAX __INTMAX_MAX__ -#define INTMAX_MIN (-INTMAX_MAX - 1) -#define UINTMAX_MAX __UINTMAX_MAX__ - -#define PTRDIFF_MAX __PTRDIFF_MAX__ -#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) - -#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ -#define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__ - -#define SIZE_MAX __SIZE_MAX__ - -#define WCHAR_MAX __WCHAR_MAX__ -#define WCHAR_MIN __WCHAR_MIN__ - -#define WINT_MAX __WINT_MAX__ -#define WINT_MIN __WINT_MIN__ - -#define b4(x) ((x & 0x0F) << 4 | (x & 0xF0) >> 4) -#define b8(x) ((x)&0xFF) -#define b16(x) __builtin_bswap16(x) -#define b32(x) __builtin_bswap32(x) -#define b48(x) (((((x)&0x0000000000ff) << 40) | (((x)&0x00000000ff00) << 24) | (((x)&0x000000ff0000) << 8) | (((x)&0x0000ff000000) >> 8) | (((x)&0x00ff00000000) >> 24) | (((x)&0xff0000000000) >> 40))) -#define b64(x) __builtin_bswap64(x) - -#ifndef PUBLIC -#define PUBLIC __attribute__((visibility("default"))) -#endif // !PUBLIC - -#ifndef PRIVATE -#define PRIVATE __attribute__((visibility("hidden"))) -#endif // !PRIVATE - -#endif // !__FENNIX_LIBC_TYPES_H__ diff --git a/Userspace/libc/include/unistd.h b/Userspace/libc/include/unistd.h index 95c7bdda..068bad8f 100644 --- a/Userspace/libc/include/unistd.h +++ b/Userspace/libc/include/unistd.h @@ -1,29 +1,151 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + #ifndef _UNISTD_H #define _UNISTD_H -#include - #ifdef __cplusplus extern "C" { +#endif // __cplusplus + +#include +#include + +#define _POSIX_VERSION 200809L +#define _POSIX2_VERSION 200809L +#define _XOPEN_VERSION 700 + +#define F_OK 0 +#define R_OK 1 +#define W_OK 2 +#define X_OK 3 + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#ifndef NULL +#define NULL ((void *)0) #endif - extern char **environ; +#define _POSIX_JOB_CONTROL 1 +#define _POSIX_SAVED_IDS 1 +#define _POSIX_NO_TRUNC 1 +#define _POSIX_VDISABLE '\0' - int execl(const char *pathname, const char *arg, ...); - int execlp(const char *file, const char *arg, ...); - int execle(const char *pathname, const char *arg, ...); - int execv(const char *pathname, char *const argv[]); - int execvp(const char *file, char *const argv[]); - int execvpe(const char *file, char *const argv[], char *const envp[]); - int execve(const char *pathname, char *const argv[], char *const envp[]); +#define ARG_MAX 4096 +#define CHILD_MAX 25 +#define CLK_TCK 100 +#define _SC_PAGESIZE 4096 +#define _SC_OPEN_MAX 256 + + extern char *optarg; + extern int optind, opterr, optopt; + + int access(const char *, int); + unsigned int alarm(unsigned int); + int brk(void *); + int chdir(const char *); + int chroot(const char *); + int chown(const char *, uid_t, gid_t); + int close(int); + size_t confstr(int, char *, size_t); + char *crypt(const char *, const char *); + char *ctermid(char *); + char *cuserid(char *s); + int dup(int); + int dup2(int, int); + void encrypt(char[64], int); + int execl(const char *, const char *, ...); + int execle(const char *, const char *, ...); + int execlp(const char *, const char *, ...); + int execv(const char *, char *const[]); + int execve(const char *, char *const[], char *const[]); + int execvp(const char *, char *const[]); + void _exit(int); + int fchown(int, uid_t, gid_t); + int fchdir(int); + int fdatasync(int); pid_t fork(void); - - unsigned int sleep(unsigned int seconds); - int usleep(useconds_t usec); + long int fpathconf(int, int); + int fsync(int); + int ftruncate(int, off_t); + char *getcwd(char *, size_t); + int getdtablesize(void); + gid_t getegid(void); + uid_t geteuid(void); + gid_t getgid(void); + int getgroups(int, gid_t[]); + long gethostid(void); + char *getlogin(void); + int getlogin_r(char *, size_t); + int getopt(int, char *const[], const char *); + int getpagesize(void); + char *getpass(const char *); + pid_t getpgid(pid_t); + pid_t getpgrp(void); + pid_t getpid(void); + pid_t getppid(void); + pid_t getsid(pid_t); + uid_t getuid(void); + char *getwd(char *); + int isatty(int); + int lchown(const char *, uid_t, gid_t); + int link(const char *, const char *); + int lockf(int, int, off_t); + off_t lseek(int, off_t, int); + int nice(int); + long int pathconf(const char *, int); + int pause(void); + int pipe(int[2]); + ssize_t pread(int, void *, size_t, off_t); + int pthread_atfork(void (*)(void), void (*)(void), void (*)(void)); + ssize_t pwrite(int, const void *, size_t, off_t); + ssize_t read(int, void *, size_t); + int readlink(const char *, char *, size_t); + int rmdir(const char *); + void *sbrk(intptr_t); + int setgid(gid_t); + int setpgid(pid_t, pid_t); + pid_t setpgrp(void); + int setregid(gid_t, gid_t); + int setreuid(uid_t, uid_t); + pid_t setsid(void); + int setuid(uid_t); + unsigned int sleep(unsigned int); + void swab(const void *, void *, ssize_t); + int symlink(const char *, const char *); + void sync(void); + long int sysconf(int); + pid_t tcgetpgrp(int); + int tcsetpgrp(int, pid_t); + int truncate(const char *, off_t); + char *ttyname(int); + int ttyname_r(int, char *, size_t); + useconds_t ualarm(useconds_t, useconds_t); + int unlink(const char *); + int usleep(useconds_t); + pid_t vfork(void); + ssize_t write(int, const void *, size_t); #ifdef __cplusplus } -#endif -#endif +#endif // __cplusplus + +#endif // !_UNISTD_H diff --git a/Userspace/libc/interpreter/Makefile b/Userspace/libc/interpreter/Makefile new file mode 100644 index 00000000..d68811a5 --- /dev/null +++ b/Userspace/libc/interpreter/Makefile @@ -0,0 +1,40 @@ +default: + $(error Do not run this Makefile directly!) + +OBJECT_NAME := ld.so + +OUTPUT_DIR=$(WORKSPACE_DIR)/out/lib/ +SYSROOT = --sysroot=$(WORKSPACE_DIR)/out/ + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CXX_SOURCES = $(shell find ./ -type f -name '*.cpp') +OBJ = ${S_SOURCES:.S=.o} ${C_SOURCES:.c=.o} ${CXX_SOURCES:.cpp=.o} + +CFLAGS := -fvisibility=hidden -fPIC -I$(WORKSPACE_DIR)/out/include -DLIBC_GIT_COMMIT='"$(shell git rev-parse HEAD)"' + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm +endif + +build: $(OBJECT_NAME) + +$(OBJECT_NAME): $(OBJ) + $(info Linking $@) + $(CC) -nostdlib -shared -fPIC -fPIE -fno-plt -Wl,-soname,$(OBJECT_NAME) $(SYSROOT) $(OBJ) -o $(OBJECT_NAME) + cp $(OBJECT_NAME) $(OUTPUT_DIR)$(OBJECT_NAME) + +%.o: %.c + $(info Compiling $<) + $(CC) $(CFLAGS) -std=c17 -c $< -o $@ + +%.o: %.cpp + $(info Compiling $<) + $(CC) $(CFLAGS) -std=c++20 -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -c $< -o $@ + +clean: + rm -f $(OBJ) $(OBJECT_NAME) diff --git a/Userspace/libc/interpreter/alloc.c b/Userspace/libc/interpreter/alloc.c new file mode 100644 index 00000000..04246328 --- /dev/null +++ b/Userspace/libc/interpreter/alloc.c @@ -0,0 +1,117 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include "elf.h" +#include "misc.h" + +typedef struct MemoryBlock +{ + struct MemoryBlock *next; + void *slots; + size_t slot_size; + size_t slots_per_block; + uint8_t *bitmap; +} MemoryBlock; + +MemoryBlock *memory_pool = NULL; +#define PAGE_SIZE 0x1000 + +void *request_page(size_t size) +{ + size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + void *addr = (void *)call_mmap(NULL, aligned_size, __SYS_PROT_READ | __SYS_PROT_WRITE, __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE, -1, 0); + if ((intptr_t)addr < 0) + return NULL; + return addr; +} + +void free_page(void *addr, size_t size) +{ + size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + call_munmap(addr, aligned_size); +} + +MemoryBlock *allocate_block(size_t slot_size) +{ + size_t block_size = PAGE_SIZE; + size_t slots_per_block = block_size / slot_size; + size_t bitmap_size = (slots_per_block + 7) / 8; + + MemoryBlock *block = request_page(block_size); + if (!block) + return NULL; + + block->slots = (void *)((uintptr_t)block + sizeof(MemoryBlock) + bitmap_size); + block->slot_size = slot_size; + block->slots_per_block = slots_per_block; + block->bitmap = (uint8_t *)((uintptr_t)block + sizeof(MemoryBlock)); + memset(block->bitmap, 0, bitmap_size); + block->next = NULL; + + return block; +} + +void *mini_malloc(size_t size) +{ + MemoryBlock *block = memory_pool; + while (block) + { + for (size_t i = 0; i < block->slots_per_block; i++) + { + size_t byte = i / 8, bit = i % 8; + if (!(block->bitmap[byte] & (1 << bit))) + { + block->bitmap[byte] |= (1 << bit); + return (void *)((uintptr_t)block->slots + i * size); + } + } + block = block->next; + } + + block = allocate_block(size); + if (!block) + return NULL; + + block->next = memory_pool; + memory_pool = block; + + block->bitmap[0] |= 1; + return block->slots; +} + +void mini_free(void *ptr) +{ + MemoryBlock *block = memory_pool; + while (block) + { + if ((uintptr_t)ptr >= (uintptr_t)block->slots && + (uintptr_t)ptr < (uintptr_t)block->slots + block->slots_per_block * block->slot_size) + { + size_t index = ((uintptr_t)ptr - (uintptr_t)block->slots) / block->slot_size; + size_t byte = index / 8, bit = index % 8; + block->bitmap[byte] &= ~(1 << bit); + return; + } + block = block->next; + } +} diff --git a/Userspace/libc/interpreter/elf.h b/Userspace/libc/interpreter/elf.h new file mode 100644 index 00000000..e30e2a72 --- /dev/null +++ b/Userspace/libc/interpreter/elf.h @@ -0,0 +1,1013 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef __FENNIX_DL_ELF_H__ +#define __FENNIX_DL_ELF_H__ + +typedef __UINT32_TYPE__ Elf32_Addr; +typedef __UINT16_TYPE__ Elf32_Half; +typedef __UINT32_TYPE__ Elf32_Off; +typedef __INT32_TYPE__ Elf32_Sword; +typedef __UINT32_TYPE__ Elf32_Word; +typedef Elf32_Sword Elf32_pid_t; + +typedef __UINT64_TYPE__ Elf64_Addr; +typedef __UINT16_TYPE__ Elf64_Half; +typedef __INT16_TYPE__ Elf64_SHalf; +typedef __UINT64_TYPE__ Elf64_Off; +typedef __INT32_TYPE__ Elf64_Sword; +typedef __UINT32_TYPE__ Elf64_Word; +typedef __UINT64_TYPE__ Elf64_Xword; +typedef __INT64_TYPE__ Elf64_Sxword; +typedef Elf64_Sword Elf64_pid_t; + +#define ELF_NGREG 23 +typedef Elf32_Word Elf32_greg_t[ELF_NGREG]; +typedef Elf64_Xword Elf64_greg_t[ELF_NGREG]; + +enum IdentificationIndex +{ + EI_MAG0 = 0, + EI_MAG1 = 1, + EI_MAG2 = 2, + EI_MAG3 = 3, + EI_CLASS = 4, + EI_DATA = 5, + EI_VERSION = 6, + EI_OSABI = 7, + EI_ABIVERSION = 8, + EI_PAD = 9, + EI_NIDENT = 16 +}; + +enum Elf_OSABI +{ + ELFOSABI_NONE = 0, + ELFOSABI_SYSV = 0, + ELFOSABI_HPUX = 1, + ELFOSABI_NETBSD = 2, + ELFOSABI_GNU = 3, + ELFOSABI_LINUX = 3, + ELFOSABI_HURD = 4, + ELFOSABI_SOLARIS = 6, + ELFOSABI_AIX = 7, + ELFOSABI_IRIX = 8, + ELFOSABI_FREEBSD = 9, + ELFOSABI_TRU64 = 10, + ELFOSABI_MODESTO = 11, + ELFOSABI_OPENBSD = 12, + ELFOSABI_OPENVMS = 13, + ELFOSABI_NSK = 14, + ELFOSABI_AROS = 15, + ELFOSABI_FENIXOS = 16, + ELFOSABI_CLOUDABI = 17, + ELFOSABI_OPENVOS = 18, + ELFOSABI_C6000_ELFABI = 64, + ELFOSABI_C6000_LINUX = 65, + ELFOSABI_ARM = 97, + ELFOSABI_STANDALONE = 255 +}; + +enum FileIdentifiers +{ + ET_NONE = 0, + ET_REL = 1, + ET_EXEC = 2, + ET_DYN = 3, + ET_CORE = 4, + ET_LOPROC = 0xff00, + ET_HIPROC = 0xffff +}; + +enum RelocationTypes +{ + R_386_NONE = 0, + R_386_32 = 1, + R_386_PC32 = 2, + R_386_GOT32 = 3, + R_386_PLT32 = 4, + R_386_COPY = 5, + R_386_GLOB_DAT = 6, + R_386_JMP_SLOT = 7, + R_386_RELATIVE = 8, + R_386_GOTOFF = 9, + R_386_GOTPC = 10, + R_386_32PLT = 11, + R_386_TLS_TPOFF = 14, + R_386_TLS_IE = 15, + R_386_TLS_GOTIE = 16, + R_386_TLS_LE = 17, + R_386_TLS_GD = 18, + R_386_TLS_LDM = 19, + R_386_16 = 20, + R_386_PC16 = 21, + R_386_8 = 22, + R_386_PC8 = 23, + R_386_TLS_GD_32 = 24, + R_386_TLS_GD_PUSH = 25, + R_386_TLS_GD_CALL = 26, + R_386_TLS_GD_POP = 27, + R_386_TLS_LDM_32 = 28, + R_386_TLS_LDM_PUSH = 29, + R_386_TLS_LDM_CALL = 30, + R_386_TLS_LDM_POP = 31, + R_386_TLS_LDO_32 = 32, + R_386_TLS_IE_32 = 33, + R_386_TLS_LE_32 = 34, + R_386_TLS_DTPMOD32 = 35, + R_386_TLS_DTPOFF32 = 36, + R_386_TLS_TPOFF32 = 37, + R_386_SIZE32 = 38, + R_386_TLS_GOTDESC = 39, + R_386_TLS_DESC_CALL = 40, + R_386_TLS_DESC = 41, + R_386_IRELATIVE = 42, + R_386_GOT32X = 43, + R_386_NUM = 44, + + R_X86_64_NONE = 0, + R_X86_64_64 = 1, + R_X86_64_PC32 = 2, + R_X86_64_GOT32 = 3, + R_X86_64_PLT32 = 4, + R_X86_64_COPY = 5, + R_X86_64_GLOB_DAT = 6, + R_X86_64_JUMP_SLOT = 7, + R_X86_64_RELATIVE = 8, + R_X86_64_GOTPCREL = 9, + R_X86_64_32 = 10, + R_X86_64_32S = 11, + R_X86_64_16 = 12, + R_X86_64_PC16 = 13, + R_X86_64_8 = 14, + R_X86_64_PC8 = 15, + R_X86_64_DTPMOD64 = 16, + R_X86_64_DTPOFF64 = 17, + R_X86_64_TPOFF64 = 18, + R_X86_64_TLSGD = 19, + R_X86_64_TLSLD = 20, + R_X86_64_DTPOFF32 = 21, + R_X86_64_GOTTPOFF = 22, + R_X86_64_TPOFF32 = 23, + R_X86_64_PC64 = 24, + R_X86_64_GOTOFF64 = 25, + R_X86_64_GOTPC32 = 26, + R_X86_64_GOT64 = 27, + R_X86_64_GOTPCREL64 = 28, + R_X86_64_GOTPC64 = 29, + R_X86_64_GOTPLT64 = 30, + R_X86_64_PLTOFF64 = 31, + R_X86_64_SIZE32 = 32, + R_X86_64_SIZE64 = 33, + R_X86_64_GOTPC32_TLSDESC = 34, + R_X86_64_TLSDESC_CALL = 35, + R_X86_64_TLSDESC = 36, + R_X86_64_IRELATIVE = 37, + R_X86_64_RELATIVE64 = 38, + R_X86_64_GOTPCRELX = 41, + R_X86_64_REX_GOTPCRELX = 42, + R_X86_64_NUM = 43, + +#if defined(__x86_64__) + R_NONE = R_X86_64_NONE, + R_COPY = R_X86_64_COPY, + R_GLOB_DAT = R_X86_64_GLOB_DAT, + R_JMP_SLOT = R_X86_64_JUMP_SLOT, + R_RELATIVE = R_X86_64_RELATIVE, + R_DTPMOD64 = R_X86_64_DTPMOD64, + R_DTPOFF64 = R_X86_64_DTPOFF64, + R_TPOFF64 = R_X86_64_TPOFF64, +#elif defined(__i386__) + R_NONE = R_386_NONE, + R_COPY = R_386_COPY, + R_GLOB_DAT = R_386_GLOB_DAT, + R_JMP_SLOT = R_386_JMP_SLOT, + R_RELATIVE = R_386_RELATIVE, + R_DTPMOD64 = R_386_NONE, + R_DTPOFF64 = R_386_NONE, + R_TPOFF64 = R_386_NONE, +#endif +}; + +enum SegmentFlags +{ + PF_X = 1, + PF_W = 2, + PF_R = 4, + PF_MASKPROC = 0xf0000000 +}; + +enum SymbolBindings +{ + STB_LOCAL = 0, + STB_GLOBAL = 1, + STB_WEAK = 2, + STB_LOOS = 10, + STB_HIOS = 12, + STB_LOPROC = 13, + STB_HIPROC = 15 +}; + +enum SymbolTypes +{ + STT_NOTYPE = 0, + STT_OBJECT = 1, + STT_FUNC = 2, + STT_SECTION = 3, + STT_FILE = 4, + STT_COMMON = 5, + STT_LOOS = 10, + STT_HIOS = 12, + STT_LOPROC = 13, + STT_SPARC_REGISTER = 13, + STT_HIPROC = 15 +}; + +#define STN_UNDEF 0 + +enum SymbolVisibilities +{ + STV_DEFAULT = 0, + STV_INTERNAL = 1, + STV_HIDDEN = 2, + STV_PROTECTED = 3 +}; + +enum SegmentTypes +{ + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6, + PT_TLS = 7, + PT_LOPROC = 0x70000000, + PT_HIPROC = 0x7fffffff, +}; + +enum DynamicArrayTags +{ + DT_NULL = 0, + DT_NEEDED = 1, + DT_PLTRELSZ = 2, + DT_PLTGOT = 3, + DT_HASH = 4, + DT_STRTAB = 5, + DT_SYMTAB = 6, + DT_RELA = 7, + DT_RELASZ = 8, + DT_RELAENT = 9, + DT_STRSZ = 10, + DT_SYMENT = 11, + DT_INIT = 12, + DT_FINI = 13, + DT_SONAME = 14, + DT_RPATH = 15, + DT_SYMBOLIC = 16, + DT_REL = 17, + DT_RELSZ = 18, + DT_RELENT = 19, + DT_PLTREL = 20, + DT_DEBUG = 21, + DT_TEXTREL = 22, + DT_JMPREL = 23, + DT_BIND_NOW = 24, + DT_INIT_ARRAY = 25, + DT_FINI_ARRAY = 26, + DT_INIT_ARRAYSZ = 27, + DT_FINI_ARRAYSZ = 28, + DT_RUNPATH = 29, + DT_FLAGS = 30, + DT_ENCODING = 32, + DT_PREINIT_ARRAY = 32, + DT_PREINIT_ARRAYSZ = 33, + DT_LOOS = 0x6000000d, + DT_SUNW_RTLDINF = 0x6000000e, + DT_HIOS = 0x6ffff000, + DT_VALRNGLO = 0x6ffffd00, + DT_CHECKSUM = 0x6ffffdf8, + DT_PLTPADSZ = 0x6ffffdf9, + DT_MOVEENT = 0x6ffffdfa, + DT_MOVESZ = 0x6ffffdfb, + DT_FEATURE_1 = 0x6ffffdfc, + DT_POSFLAG_1 = 0x6ffffdfd, + DT_SYMINSZ = 0x6ffffdfe, + DT_SYMINENT = 0x6ffffdff, + DT_VALRNGHI = 0x6ffffdff, + DT_ADDRRNGLO = 0x6ffffe00, + DT_CONFIG = 0x6ffffefa, + DT_DEPAUDIT = 0x6ffffefb, + DT_AUDIT = 0x6ffffefc, + DT_PLTPAD = 0x6ffffefd, + DT_MOVETAB = 0x6ffffefe, + DT_SYMINFO = 0x6ffffeff, + DT_ADDRRNGHI = 0x6ffffeff, + DT_RELACOUNT = 0x6ffffff9, + DT_RELCOUNT = 0x6ffffffa, + DT_FLAGS_1 = 0x6ffffffb, + DT_VERDEF = 0x6ffffffc, + DT_VERDEFNUM = 0x6ffffffd, + DT_VERNEED = 0x6ffffffe, + DT_VERNEEDNUM = 0x6fffffff, + DT_LOPROC = 0x70000000, + DT_SPARC_REGISTER = 0x70000001, + DT_AUXILIARY = 0x7ffffffd, + DT_USED = 0x7ffffffe, + DT_FILTER = 0x7fffffff, + DT_HIPROC = 0x7fffffff +}; + +/* Used for Elf64_Sym st_info */ +#define ELF32_ST_BIND(info) ((info) >> 4) +#define ELF32_ST_TYPE(info) ((info) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#define ELF64_ST_BIND(info) ((info) >> 4) +#define ELF64_ST_TYPE(info) ((info) & 0xf) +#define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Used for Elf64_Sym st_other */ +#define ELF32_ST_VISIBILITY(o) ((o) & 0x3) +#define ELF64_ST_VISIBILITY(o) ((o) & 0x3) + +#define DO_386_32(S, A) ((S) + (A)) +#define DO_386_PC32(S, A, P) ((S) + (A) - (P)) + +#define DO_64_64(S, A) ((S) + (A)) +#define DO_64_PC32(S, A, P) ((S) + (A) - (P)) + +#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 SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_BEFORE 0xff00 +#define SHN_AFTER 0xff01 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#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_ORDERED 0x40000000 +#define SHF_EXCLUDE 0x80000000 +#define SHF_MASKPROC 0xf0000000 + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#define EM_IAMCU 6 /* Intel MCU */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS I Architecture */ +#define EM_S370 9 /* IBM System/370 Processor */ +#define EM_MIPS_RS3_LE 10 /* MIPS RS3000 Little-endian */ +#define EM_PARISC 15 /* Hewlett-Packard PA-RISC */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* 64-bit PowerPC */ +#define EM_S390 22 /* IBM System/390 Processor */ +#define EM_SPU 23 /* IBM SPU/SPC */ +#define EM_V800 36 /* NEC V800 */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM 32-bit architecture (AARCH32) */ +#define EM_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC Version 9 */ +#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ +#define EM_ARC 45 /* Argonaut RISC Core, Argonaut Technologies Inc. */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel IA-64 processor architecture */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola ColdFire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator */ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embedded RISC processor */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Star*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronics ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. TinyJ embedded processor family */ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ +#define EM_PDP10 64 /* Digital Equipment Corp. PDP-10 */ +#define EM_PDP11 65 /* Digital Equipment Corp. PDP-11 */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 bit microcontroller */ +#define EM_ST7 68 /* STMicroelectronics ST7 8-bit microcontroller */ +#define EM_68HC16 69 /* Motorola MC68HC16 Microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 Microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 Microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 Microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8-bit microcontroller */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_COMPACT 93 /* ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore processor */ +#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Processor */ +#define EM_NS32K 97 /* National Semiconductor 32000 series */ +#define EM_TPC 98 /* Tenor Network TPC processor */ +#define EM_SNP1K 99 /* Trebia SNP 1000 processor */ +#define EM_ST200 100 /* STMicroelectronics (www.st.com) ST200 microcontroller */ +#define EM_IP2K 101 /* Ubicom IP2xxx microcontroller family */ +#define EM_MAX 102 /* MAX Processor */ +#define EM_CR 103 /* National Semiconductor CompactRISC microprocessor */ +#define EM_F2MC16 104 /* Fujitsu F2MC16 */ +#define EM_MSP430 105 /* Texas Instruments embedded microcontroller msp430 */ +#define EM_BLACKFIN 106 /* Analog Devices Blackfin (DSP) processor */ +#define EM_SE_C33 107 /* S1C33 Family of Seiko Epson processors */ +#define EM_SEP 108 /* Sharp embedded microprocessor */ +#define EM_ARCA 109 /* Arca RISC Microprocessor */ +#define EM_UNICORE 110 /* Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University */ +#define EM_EXCESS 111 /* eXcess: 16/32/64-bit configurable embedded CPU */ +#define EM_DXP 112 /* Icera Semiconductor Inc. Deep Execution Processor */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ +#define EM_CRX 114 /* National Semiconductor CompactRISC CRX microprocessor */ +#define EM_XGATE 115 /* Motorola XGATE embedded processor */ +#define EM_C166 116 /* Infineon C16x/XC16x processor */ +#define EM_M16C 117 /* Renesas M16C series microprocessors */ +#define EM_DSPIC30F 118 /* Microchip Technology dsPIC30F Digital Signal Controller */ +#define EM_CE 119 /* Freescale Communication Engine RISC core */ +#define EM_M32C 120 /* Renesas M32C series microprocessors */ +#define EM_TSK3000 131 /* Altium TSK3000 core */ +#define EM_RS08 132 /* Freescale RS08 embedded processor */ +#define EM_SHARC 133 /* Analog Devices SHARC family of 32-bit DSP processors */ +#define EM_ECOG2 134 /* Cyan Technology eCOG2 microprocessor */ +#define EM_SCORE7 135 /* Sunplus S+core7 RISC processor */ +#define EM_DSP24 136 /* New Japan Radio (NJR) 24-bit DSP Processor */ +#define EM_VIDEOCORE3 137 /* Broadcom VideoCore III processor */ +#define EM_LATTICEMICO32 138 /* RISC processor for Lattice FPGA architecture */ +#define EM_SE_C17 139 /* Seiko Epson C17 family */ +#define EM_TI_C6000 140 /* The Texas Instruments TMS320C6000 DSP family */ +#define EM_TI_C2000 141 /* The Texas Instruments TMS320C2000 DSP family */ +#define EM_TI_C5500 142 /* The Texas Instruments TMS320C55x DSP family */ +#define EM_TI_ARP32 143 /* Texas Instruments Application Specific RISC Processor, 32bit fetch */ +#define EM_TI_PRU 144 /* Texas Instruments Programmable Realtime Unit */ +#define EM_MMDSP_PLUS 160 /* STMicroelectronics 64bit VLIW Data Signal Processor */ +#define EM_CYPRESS_M8C 161 /* Cypress M8C microprocessor */ +#define EM_R32C 162 /* Renesas R32C series microprocessors */ +#define EM_TRIMEDIA 163 /* NXP Semiconductors TriMedia architecture family */ +#define EM_QDSP6 164 /* QUALCOMM DSP6 Processor */ +#define EM_8051 165 /* Intel 8051 and variants */ +#define EM_STXP7X 166 /* STMicroelectronics STxP7x family of configurable and extensible RISC processors */ +#define EM_NDS32 167 /* Andes Technology compact code size embedded RISC processor family */ +#define EM_ECOG1 168 /* Cyan Technology eCOG1X family */ +#define EM_ECOG1X 168 /* Cyan Technology eCOG1X family */ +#define EM_MAXQ30 169 /* Dallas Semiconductor MAXQ30 Core Micro-controllers */ +#define EM_XIMO16 170 /* New Japan Radio (NJR) 16-bit DSP Processor */ +#define EM_MANIK 171 /* M2000 Reconfigurable RISC Microprocessor */ +#define EM_CRAYNV2 172 /* Cray Inc. NV2 vector architecture */ +#define EM_RX 173 /* Renesas RX family */ +#define EM_METAG 174 /* Imagination Technologies META processor architecture */ +#define EM_MCST_ELBRUS 175 /* MCST Elbrus general purpose hardware architecture */ +#define EM_ECOG16 176 /* Cyan Technology eCOG16 family */ +#define EM_CR16 177 /* National Semiconductor CompactRISC CR16 16-bit microprocessor */ +#define EM_ETPU 178 /* Freescale Extended Time Processing Unit */ +#define EM_SLE9X 179 /* Infineon Technologies SLE9X core */ +#define EM_L10M 180 /* Intel L10M */ +#define EM_K10M 181 /* Intel K10M */ +#define EM_AARCH64 183 /* ARM 64-bit architecture (AARCH64) */ +#define EM_AVR32 185 /* Atmel Corporation 32-bit microprocessor family */ +#define EM_STM8 186 /* STMicroeletronics STM8 8-bit microcontroller */ +#define EM_TILE64 187 /* Tilera TILE64 multicore architecture family */ +#define EM_TILEPRO 188 /* Tilera TILEPro multicore architecture family */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze 32-bit RISC soft processor core */ +#define EM_CUDA 190 /* NVIDIA CUDA architecture */ +#define EM_TILEGX 191 /* Tilera TILE-Gx multicore architecture family */ +#define EM_CLOUDSHIELD 192 /* CloudShield architecture family */ +#define EM_COREA_1ST 193 /* KIPO-KAIST Core-A 1st generation processor family */ +#define EM_COREA_2ND 194 /* KIPO-KAIST Core-A 2nd generation processor family */ +#define EM_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */ +#define EM_OPEN8 196 /* Open8 8-bit RISC soft processor core */ +#define EM_RL78 197 /* Renesas RL78 family */ +#define EM_VIDEOCORE5 198 /* Broadcom VideoCore V processor */ +#define EM_78KOR 199 /* Renesas 78KOR family */ +#define EM_56800EX 200 /* Freescale 56800EX Digital Signal Controller (DSC) */ +#define EM_BA1 201 /* Beyond BA1 CPU architecture */ +#define EM_BA2 202 /* Beyond BA2 CPU architecture */ +#define EM_XCORE 203 /* XMOS xCORE processor family */ +#define EM_MCHP_PIC 204 /* Microchip 8-bit PIC(r) family */ +#define EM_INTEL205 205 /* Reserved by Intel */ +#define EM_INTEL206 206 /* Reserved by Intel */ +#define EM_INTEL207 207 /* Reserved by Intel */ +#define EM_INTEL208 208 /* Reserved by Intel */ +#define EM_INTEL209 209 /* Reserved by Intel */ +#define EM_KM32 210 /* KM211 KM32 32-bit processor */ +#define EM_KMX32 211 /* KM211 KMX32 32-bit processor */ +#define EM_KMX16 212 /* KM211 KMX16 16-bit processor */ +#define EM_KMX8 213 /* KM211 KMX8 8-bit processor */ +#define EM_KVARC 214 /* KM211 KVARC processor */ +#define EM_CDP 215 /* Paneve CDP architecture family */ +#define EM_COGE 216 /* Cognitive Smart Memory Processor */ +#define EM_COOL 217 /* Bluechip Systems CoolEngine */ +#define EM_NORC 218 /* Nanoradio Optimized RISC */ +#define EM_CSR_KALIMBA 219 /* CSR Kalimba architecture family */ +#define EM_Z80 220 /* Zilog Z80 */ +#define EM_VISIUM 221 /* Controls and Data Services VISIUMcore processor */ +#define EM_FT32 222 /* FTDI Chip FT32 high performance 32-bit RISC architecture */ +#define EM_MOXIE 223 /* Moxie processor family */ +#define EM_AMDGPU 224 /* AMD GPU architecture */ +#define EM_RISCV 243 /* RISC-V */ + +#define EV_NONE 0x0 /* Invalid ELF Version */ +#define EV_CURRENT 0x1 /* ELF Current Version */ + +#define ELFMAG0 0x7F +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +enum SpecialSections +{ + SHT_NULL = 0, + SHT_PROGBITS = 1, + SHT_SYMTAB = 2, + SHT_STRTAB = 3, + SHT_RELA = 4, + SHT_HASH = 5, + SHT_DYNAMIC = 6, + SHT_NOTE = 7, + SHT_NOBITS = 8, + SHT_REL = 9, + SHT_SHLIB = 10, + SHT_DYNSYM = 11, + SHT_INIT_ARRAY = 14, + SHT_FINI_ARRAY = 15, + SHT_PREINIT_ARRAY = 16, + SHT_GROUP = 17, + SHT_SYMTAB_SHNDX = 18, + SHT_NUM = 19, + SHT_LOOS = 0x60000000, + SHT_GNU_ATTRIBUTES = 0x6ffffff5, + SHT_GNU_HASH = 0x6ffffff6, + SHT_GNU_LIBLIST = 0x6ffffff7, + SHT_CHECKSUM = 0x6ffffff8, + SHT_LOSUNW = 0x6ffffffa, + SHT_SUNW_move = 0x6ffffffa, + SHT_SUNW_COMDAT = 0x6ffffffb, + SHT_SUNW_syminfo = 0x6ffffffc, + SHT_GNU_verdef = 0x6ffffffd, + SHT_GNU_verneed = 0x6ffffffe, + SHT_GNU_versym = 0x6fffffff, + SHT_HISUNW = 0x6fffffff, + SHT_HIOS = 0x6fffffff, + SHT_LOPROC = 0x70000000, + SHT_HIPROC = 0x7fffffff, + SHT_LOUSER = 0x80000000, + SHT_HIUSER = 0x8fffffff +}; + +#define NT_PRSTATUS 1 +#define NT_PRFPREG 2 +#define NT_FPREGSET 2 +#define NT_PRPSINFO 3 +#define NT_PRXREG 4 +#define NT_TASKSTRUCT 4 +#define NT_PLATFORM 5 +#define NT_AUXV 6 +#define NT_GWINDOWS 7 +#define NT_ASRS 8 +#define NT_PSTATUS 10 +#define NT_PSINFO 13 +#define NT_PRCRED 14 +#define NT_UTSNAME 15 +#define NT_LWPSTATUS 16 +#define NT_LWPSINFO 17 +#define NT_PRFPXREG 20 +#define NT_SIGINFO 0x53494749 +#define NT_FILE 0x46494c45 +#define NT_PRXFPREG 0x46e62b7f +#define NT_PPC_VMX 0x100 +#define NT_PPC_SPE 0x101 +#define NT_PPC_VSX 0x102 +#define NT_PPC_TAR 0x103 +#define NT_PPC_PPR 0x104 +#define NT_PPC_DSCR 0x105 +#define NT_PPC_EBB 0x106 +#define NT_PPC_PMU 0x107 +#define NT_PPC_TM_CGPR 0x108 +#define NT_PPC_TM_CFPR 0x109 +#define NT_PPC_TM_CVMX 0x10a +#define NT_PPC_TM_CVSX 0x10b +#define NT_PPC_TM_SPR 0x10c +#define NT_PPC_TM_CTAR 0x10d +#define NT_PPC_TM_CPPR 0x10e +#define NT_PPC_TM_CDSCR 0x10f +#define NT_386_TLS 0x200 +#define NT_386_IOPERM 0x201 +#define NT_X86_XSTATE 0x202 +#define NT_S390_HIGH_GPRS 0x300 +#define NT_S390_TIMER 0x301 +#define NT_S390_TODCMP 0x302 +#define NT_S390_TODPREG 0x303 +#define NT_S390_CTRS 0x304 +#define NT_S390_PREFIX 0x305 +#define NT_S390_LAST_BREAK 0x306 +#define NT_S390_SYSTEM_CALL 0x307 +#define NT_S390_TDB 0x308 +#define NT_S390_VXRS_LOW 0x309 +#define NT_S390_VXRS_HIGH 0x30a +#define NT_S390_GS_CB 0x30b +#define NT_S390_GS_BC 0x30c +#define NT_S390_RI_CB 0x30d +#define NT_ARM_VFP 0x400 +#define NT_ARM_TLS 0x401 +#define NT_ARM_HW_BREAK 0x402 +#define NT_ARM_HW_WATCH 0x403 +#define NT_ARM_SYSTEM_CALL 0x404 +#define NT_ARM_SVE 0x405 +#define NT_ARM_PAC_MASK 0x406 +#define NT_ARM_PACA_KEYS 0x407 +#define NT_ARM_PACG_KEYS 0x408 +#define NT_ARM_TAGGED_ADDR_CTRL 0x409 +#define NT_ARM_PAC_ENABLED_KEYS 0x40a +#define NT_METAG_CBUF 0x500 +#define NT_METAG_RPIPE 0x501 +#define NT_METAG_TLS 0x502 +#define NT_ARC_V2 0x600 +#define NT_VMCOREDD 0x700 +#define NT_MIPS_DSP 0x800 +#define NT_MIPS_FP_MODE 0x801 +#define NT_MIPS_MSA 0x802 +#define NT_VERSION 1 + +typedef struct elf32_hdr +{ + 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; +} Elf32_Ehdr; + +typedef struct elf64_hdr +{ + 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; +} Elf64_Ehdr; + +typedef struct elf32_shdr +{ + 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; +} Elf32_Shdr; + +typedef struct elf64_shdr +{ + 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; +} Elf64_Shdr; + +typedef 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; +} Elf32_Phdr; + +typedef 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; +} Elf64_Phdr; + +typedef struct elf32_rel +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf64_rel +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct elf32_sym +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct elf64_sym +{ + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +typedef struct +{ + Elf32_Sword d_tag; + union + { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; + union + { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +struct Elf32_Nhdr +{ + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +}; + +struct Elf64_Nhdr +{ + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +}; + +typedef struct +{ + Elf32_Sword si_signo; + Elf32_Sword si_code; + Elf32_Sword si_errno; +} Elf32_Siginfo; + +typedef struct +{ + Elf64_Sword si_signo; + Elf64_Sword si_code; + Elf64_Sword si_errno; +} Elf64_Siginfo; + +typedef struct +{ + Elf32_Sword tv_sec; + Elf32_Sword tv_usec; +} Elf32_Prtimeval; + +typedef struct +{ + Elf64_Sxword tv_sec; + Elf64_Sxword tv_usec; +} Elf64_Prtimeval; + +typedef struct +{ + Elf32_Siginfo pr_info; + Elf32_Half pr_cursig; + Elf32_Word pr_sigpend; + Elf32_Word pr_sighold; + Elf32_pid_t pr_pid; + Elf32_pid_t pr_ppid; + Elf32_pid_t pr_pgrp; + Elf32_pid_t pr_sid; + Elf32_Prtimeval pr_utime; + Elf32_Prtimeval pr_stime; + Elf32_Prtimeval pr_cutime; + Elf32_Prtimeval pr_cstime; + Elf32_greg_t pr_reg; + Elf32_Word pr_fpvalid; +} Elf32_Prstatus; + +typedef struct +{ + Elf64_Siginfo pr_info; + Elf64_Half pr_cursig; + Elf64_Word pr_sigpend; + Elf64_Word pr_sighold; + Elf64_pid_t pr_pid; + Elf64_pid_t pr_ppid; + Elf64_pid_t pr_pgrp; + Elf64_pid_t pr_sid; + Elf64_Prtimeval pr_utime; + Elf64_Prtimeval pr_stime; + Elf64_Prtimeval pr_cutime; + Elf64_Prtimeval pr_cstime; + Elf64_greg_t pr_reg; + Elf64_Word pr_fpvalid; +} Elf64_Prstatus; + +#define ELF_PRARGSZ 80 +typedef struct +{ + char pr_state; + char pr_sname; + char pr_zomb; + char pr_nice; + Elf32_Word pr_flag; + Elf32_Half pr_uid; + Elf32_Half pr_gid; + Elf32_pid_t pr_pid; + Elf32_pid_t pr_ppid; + Elf32_pid_t pr_pgrp; + Elf32_pid_t pr_sid; + char pr_fname[16]; + char pr_psargs[ELF_PRARGSZ]; +} Elf32_Prpsinfo; + +typedef struct +{ + char pr_state; + char pr_sname; + char pr_zomb; + char pr_nice; + Elf64_Xword pr_flag; + Elf64_Half pr_uid; + Elf64_Half pr_gid; + Elf64_pid_t pr_pid; + Elf64_pid_t pr_ppid; + Elf64_pid_t pr_pgrp; + Elf64_pid_t pr_sid; + char pr_fname[16]; + char pr_psargs[ELF_PRARGSZ]; +} Elf64_Prpsinfo; + +#if defined(__amd64__) || defined(__aarch64__) +typedef Elf64_Addr Elf_Addr; +typedef Elf64_Half Elf_Half; +typedef Elf64_Off Elf_Off; +typedef Elf64_Sword Elf_Sword; +typedef Elf64_Word Elf_Word; + +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Shdr Elf_Shdr; +typedef Elf64_Phdr Elf_Phdr; +typedef Elf64_Rel Elf_Rel; +typedef Elf64_Sym Elf_Sym; +typedef Elf64_Dyn Elf_Dyn; +typedef Elf64_Rela Elf_Rela; + +#define ELF_R_SYM(i) ELF64_R_SYM(i) +#define ELF_R_TYPE(i) ELF64_R_TYPE(i) +#define ELF_R_INFO(s, t) ELF64_R_INFO(s, t) + +#define ELF_ST_BIND(info) ELF64_ST_BIND(info) +#define ELF_ST_TYPE(info) ELF64_ST_TYPE(info) +#define ELF_ST_INFO(bind, type) ELF64_ST_INFO(bind, type) +#elif defined(__i386__) +typedef Elf32_Addr Elf_Addr; +typedef Elf32_Half Elf_Half; +typedef Elf32_Off Elf_Off; +typedef Elf32_Sword Elf_Sword; +typedef Elf32_Word Elf_Word; + +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Shdr Elf_Shdr; +typedef Elf32_Phdr Elf_Phdr; +typedef Elf32_Rel Elf_Rel; +typedef Elf32_Sym Elf_Sym; +typedef Elf32_Dyn Elf_Dyn; +typedef Elf32_Rela Elf_Rela; + +#define ELF_R_SYM(i) ELF32_R_SYM(i) +#define ELF_R_TYPE(i) ELF32_R_TYPE(i) +#define ELF_R_INFO(s, t) ELF32_R_INFO(s, t) + +#define ELF_ST_BIND(info) ELF32_ST_BIND(info) +#define ELF_ST_TYPE(info) ELF32_ST_TYPE(info) +#define ELF_ST_INFO(bind, type) ELF32_ST_INFO(bind, type) +#endif + +#endif // !__FENNIX_DL_ELF_H__ diff --git a/Userspace/libc/interpreter/helper.c b/Userspace/libc/interpreter/helper.c new file mode 100644 index 00000000..d22d392d --- /dev/null +++ b/Userspace/libc/interpreter/helper.c @@ -0,0 +1,131 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include "elf.h" +#include "misc.h" + +void *memset(void *s, int c, size_t n) +{ + uint8_t *p = s; + while (n--) + *p++ = c; + return s; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + uint8_t *d = dest; + const uint8_t *s = src; + while (n--) + *d++ = *s++; + return dest; +} + +size_t strlen(const char *s) +{ + const char *p = s; + while (*p) + p++; + return p - s; +} + +char *strcpy(char *dest, const char *src) +{ + char *d = dest; + while ((*d++ = *src++)) + ; + return dest; +} + +int strcmp(const char *l, const char *r) +{ + while (*l && *l == *r) + { + l++; + r++; + } + return *l - *r; +} + +char *strcat(char *dest, const char *src) +{ + char *d = dest; + while (*d) + d++; + while ((*d++ = *src++)) + ; + return dest; +} + +unsigned long elf_hash(const unsigned char *name) +{ + unsigned long hash = 0, high; + while (*name) + { + hash = (hash << 4) + *name++; + if ((high = hash & 0xF0000000)) + hash ^= high >> 24; + hash &= ~high; + } + return hash; +} + +uint32_t gnu_hash(const char *name) +{ + uint32_t hash = 5381; + for (; *name; name++) + { + hash = (hash << 5) + hash + (unsigned char)(*name); // hash * 33 + c + } + return hash; +} + +Elf64_Sym *find_symbol(const char *name, uint32_t *hash_table, Elf64_Sym *symtab, const char *strtab) +{ + /* Symbol Hash Table + |-------------------| + | nbucket | + |-------------------| + | nchain | + |-------------------| + | bucket[0] | + | . . . | + |bucket[nbucket - 1]| + |-------------------| + | chain[0] | + | . . . | + | chain[nchain - 1] | + |-------------------| + */ + unsigned long h = elf_hash(name); // or gnu_hash(name) + unsigned long bucket = h % hash_table[0]; // hash_table[0] = nbucket + + for (unsigned long i = hash_table[2 + bucket]; + i != STN_UNDEF; + i = hash_table[2 + hash_table[0] + i]) + { + if (!strcmp(&strtab[symtab[i].st_name], name)) + return &symtab[i]; + } + return NULL; +} diff --git a/Userspace/libc/interpreter/load.c b/Userspace/libc/interpreter/load.c new file mode 100644 index 00000000..4670a75d --- /dev/null +++ b/Userspace/libc/interpreter/load.c @@ -0,0 +1,975 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include "elf.h" +#include "misc.h" + +typedef struct ElfInfo +{ + Elf_Ehdr Header; + Elf_Phdr DynamicHeader; + uintptr_t BaseAddress; + Elf_Dyn *Dynamic; + char *Path; + + struct + { + Elf_Addr PLTGOT, HASH, STRTAB, SYMTAB, RELA, REL, TEXTREL, JMPREL; + Elf_Addr BIND_NOW, INIT, FINI, RPATH, SYMBOLIC, INIT_ARRAY; + Elf_Addr FINI_ARRAY, PREINIT_ARRAY, RUNPATH, FLAGS; + } DynamicTable; + + struct + { +#ifdef __LP64__ + _Static_assert(sizeof(Elf64_Xword) == sizeof(size_t), "Elf64_Xword and size_t are not the same size"); +#else + _Static_assert(sizeof(Elf32_Word) == sizeof(size_t), "Elf32_Word and size_t are not the same size"); +#endif + size_t PLTRELSZ, RELASZ, RELAENT, STRSZ, SYMENT, RELSZ, RELENT, PLTREL; + size_t INIT_ARRAYSZ, FINI_ARRAYSZ, PREINIT_ARRAYSZ; + } DynamicSize; + + union + { + struct + { + uint8_t Relocated : 1; + uint8_t IsLibrary : 1; + uint8_t __padding : 6; + }; + uint8_t raw; + } Flags; + + struct ElfInfo *prev; + struct ElfInfo *next; +} ElfInfo; + +ElfInfo *elf_list_head = NULL; + +ElfInfo *AllocateLib() +{ + ElfInfo *new_node = mini_malloc(sizeof(ElfInfo)); + if (!new_node) + { + printf("Failed to allocate memory for new library\n"); + return NULL; + } + + memset(new_node, 0, sizeof(ElfInfo)); + + if (!elf_list_head) + { + elf_list_head = new_node; + return new_node; + } + + ElfInfo *current = elf_list_head; + while (current->next) + current = current->next; + + current->next = new_node; + new_node->prev = current; + return new_node; +} + +void FreeLib(ElfInfo *node) +{ + if (!node) + return; + + if (node->prev) + node->prev->next = node->next; + else + elf_list_head = node->next; + + if (node->next) + node->next->prev = node->prev; + + mini_free(node); +} + +ElfInfo *SearchLib(char *Path) +{ + ElfInfo *current = elf_list_head; + while (current) + { + if (strcmp(current->Path, Path) == 0) + return current; + current = current->next; + } + return NULL; +} + +__attribute__((naked, used, no_stack_protector)) void _dl_runtime_resolve() +{ + __asm__( + "pop %r11\n" /* Pop lazy resolve arguments */ + "pop %r10\n" + + "push %rdi\n" + "push %rsi\n" + "push %rdx\n" + "push %rcx\n" + "push %r8\n" + "push %r9\n" + + "mov %r11, %rdi\n" /* Move the first argument to rdi */ + "mov %r10, %rsi\n" /* Move the second argument to rsi (rel index) */ + "call _dl_fixup\n" /* Call _dl_fixup */ + "mov %rax, %r11\n" /* Move the return value to r11 */ + + "pop %r9\n" + "pop %r8\n" + "pop %rcx\n" + "pop %rdx\n" + "pop %rsi\n" + "pop %rdi\n" + + "jmp *%r11\n"); /* Jump to the return value */ +} + +int RelocateHelper(ElfInfo *Info, Elf_Rela *Rela, short IsRel, void **Relocated); +__attribute__((noinline)) void *_dl_fixup(ElfInfo *Info, long RelIndex) +{ + void *ret = NULL; + if (Info->DynamicSize.PLTREL == DT_REL) + RelocateHelper(Info, (Elf_Rela *)(Info->DynamicTable.JMPREL + RelIndex), 1, &ret); + else if (Info->DynamicSize.PLTREL == DT_RELA) + RelocateHelper(Info, &((Elf_Rela *)Info->DynamicTable.JMPREL)[RelIndex], 0, &ret); + return ret; +} + +int _dl_preload() +{ + call_api_version(0); + + /* TODO: Do aditional checks for miscellaneous things */ + + /* Everything is ok, continue. */ + return 0; +} + +void HandleGOT(ElfInfo *Info, Elf_Addr Offset) +{ + Elf_Addr *got = (Elf_Addr *)Offset; + got[0] = (Elf_Addr)Info->Dynamic; + got[1] = (Elf_Addr)Info; + got[2] = (Elf_Addr)&_dl_runtime_resolve; +} + +void AdjustDynamicTable(Elf_Dyn *elem, ElfInfo *Info) +{ + switch (elem->d_tag) + { + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + elem->d_un.d_ptr += Info->BaseAddress; + break; + + default: + break; + } +} + +void CreateInfoTables(Elf_Dyn *elem, ElfInfo *Info) +{ + switch (elem->d_tag) + { + case DT_NEEDED: + break; + case DT_PLTRELSZ: + Info->DynamicSize.PLTRELSZ = elem->d_un.d_val; + break; + case DT_PLTGOT: + HandleGOT(Info, elem->d_un.d_ptr); + Info->DynamicTable.PLTGOT = elem->d_un.d_ptr; + break; + case DT_HASH: + Info->DynamicTable.HASH = elem->d_un.d_ptr; + break; + case DT_STRTAB: + Info->DynamicTable.STRTAB = elem->d_un.d_ptr; + break; + case DT_SYMTAB: + Info->DynamicTable.SYMTAB = elem->d_un.d_ptr; + break; + case DT_RELA: + Info->DynamicTable.RELA = elem->d_un.d_ptr; + break; + case DT_RELASZ: + Info->DynamicSize.RELASZ = elem->d_un.d_val; + break; + case DT_RELAENT: + Info->DynamicSize.RELAENT = elem->d_un.d_val; + break; + case DT_STRSZ: + Info->DynamicSize.STRSZ = elem->d_un.d_val; + break; + case DT_SYMENT: + Info->DynamicSize.SYMENT = elem->d_un.d_val; + break; + case DT_INIT: + Info->DynamicTable.INIT = elem->d_un.d_ptr; + break; + case DT_FINI: + Info->DynamicTable.FINI = elem->d_un.d_ptr; + break; + case DT_RPATH: + Info->DynamicTable.RPATH = elem->d_un.d_ptr; + break; + case DT_SYMBOLIC: + Info->DynamicTable.SYMBOLIC = elem->d_un.d_ptr; + break; + case DT_REL: + Info->DynamicTable.REL = elem->d_un.d_ptr; + break; + case DT_RELSZ: + Info->DynamicSize.RELSZ = elem->d_un.d_val; + break; + case DT_RELENT: + Info->DynamicSize.RELENT = elem->d_un.d_val; + break; + case DT_PLTREL: + Info->DynamicSize.PLTREL = elem->d_un.d_val; + break; + // case DT_DEBUG: + case DT_TEXTREL: + Info->DynamicTable.TEXTREL = elem->d_un.d_ptr; + break; + case DT_JMPREL: + Info->DynamicTable.JMPREL = elem->d_un.d_ptr; + break; + case DT_BIND_NOW: + Info->DynamicTable.BIND_NOW = elem->d_un.d_ptr; + break; + case DT_INIT_ARRAY: + Info->DynamicTable.INIT_ARRAY = elem->d_un.d_ptr; + break; + case DT_FINI_ARRAY: + Info->DynamicTable.FINI_ARRAY = elem->d_un.d_ptr; + break; + case DT_INIT_ARRAYSZ: + Info->DynamicSize.INIT_ARRAYSZ = elem->d_un.d_val; + break; + case DT_FINI_ARRAYSZ: + Info->DynamicSize.FINI_ARRAYSZ = elem->d_un.d_val; + break; + case DT_RUNPATH: + Info->DynamicTable.RUNPATH = elem->d_un.d_ptr; + break; + case DT_FLAGS: + Info->DynamicTable.FLAGS = elem->d_un.d_ptr; + break; + // case DT_ENCODING: + case DT_PREINIT_ARRAY: + Info->DynamicTable.PREINIT_ARRAY = elem->d_un.d_ptr; + break; + case DT_PREINIT_ARRAYSZ: + Info->DynamicSize.PREINIT_ARRAYSZ = elem->d_un.d_val; + break; + case DT_LOOS: + case DT_SUNW_RTLDINF: + case DT_HIOS: + case DT_VALRNGLO: + case DT_CHECKSUM: + case DT_PLTPADSZ: + case DT_MOVEENT: + case DT_MOVESZ: + case DT_FEATURE_1: + case DT_POSFLAG_1: + case DT_SYMINSZ: + case DT_SYMINENT: + // case DT_VALRNGHI: + case DT_ADDRRNGLO: + case DT_CONFIG: + case DT_DEPAUDIT: + case DT_AUDIT: + case DT_PLTPAD: + case DT_MOVETAB: + case DT_SYMINFO: + // case DT_ADDRRNGHI: + case DT_RELACOUNT: + case DT_RELCOUNT: + case DT_FLAGS_1: + case DT_VERDEF: + case DT_VERDEFNUM: + case DT_VERNEED: + case DT_VERNEEDNUM: + case DT_LOPROC: + case DT_SPARC_REGISTER: + case DT_AUXILIARY: + case DT_USED: + case DT_FILTER: + // case DT_HIPROC: + default: + break; + } +} + +int LoadElf(int, char *, ElfInfo **); +void ProcessNeededLibraries(Elf_Dyn *elem, ElfInfo *Info) +{ + char *libPath = (char *)Info->DynamicTable.STRTAB + elem->d_un.d_val; + ElfInfo *info = NULL; + + char fullLibPath[PATH_MAX]; + strcpy(fullLibPath, "/lib/"); + strcat(fullLibPath, libPath); + /* TODO: more checks and also check environment variables */ + if (call_access(fullLibPath, __SYS_F_OK) != 0) + { + printf("dl: Can't access %s\n", fullLibPath); + return; + } + + int fd = call_open(fullLibPath, __SYS_O_RDONLY, 0644); + int status = LoadElf(fd, fullLibPath, &info); + elem->d_un.d_ptr = (uintptr_t)info; /* if LoadElf fails, info will still be NULL */ + call_close(fd); + if (status < 0) /* announce that LoadElf failed */ + printf("dl: Can't load %s\n", fullLibPath); +} + +void ProcessDynamicTable(ElfInfo *Info) +{ + for (size_t i = 0;; i++) + { + Elf_Dyn *elem = &Info->Dynamic[i]; + if (elem->d_tag == DT_NULL) + break; + AdjustDynamicTable(elem, Info); + CreateInfoTables(elem, Info); + } + + /* TODO: Optimize this, we don't have to recheck every element */ + for (size_t i = 0;; i++) + { + Elf_Dyn *elem = &Info->Dynamic[i]; + if (elem->d_tag == DT_NULL) + break; + if (elem->d_tag != DT_NEEDED) + continue; + ProcessNeededLibraries(elem, Info); + } +} + +uintptr_t GetASLR() +{ +#ifdef DEBUG + static uintptr_t __aslr_stub_next = 0; + __aslr_stub_next += 0x1000000; + return __aslr_stub_next; +#else + /* FIXME: implement real ASLR */ + static uintptr_t __aslr_stub_next = 0; + __aslr_stub_next += 0x1000000; + return __aslr_stub_next; +#endif +} + +int LoadElfPhdrEXEC(int fd, ElfInfo *Info) +{ + printf("dl: ET_EXEC not implemented yet\n"); + return -ENOSYS; +} + +uintptr_t DynamicCreateBase(int fd, ElfInfo *Info) +{ + uintptr_t base = GetASLR(); + if ((uintptr_t)base <= 0) + { + printf("dl: Can't get ASLR\n"); + _Exit(-1); + } + Info->BaseAddress = base; + return base; +} + +int LoadElfPhdrDYN(int fd, ElfInfo *Info) +{ + Elf_Ehdr header = Info->Header; + Info->Dynamic = NULL; + uintptr_t base = 0; + Elf_Phdr phdr, lastLOAD; + + for (Elf_Half i = 0; i < header.e_phnum; i++) + { + ssize_t read = call_pread(fd, &phdr, sizeof(Elf_Phdr), header.e_phoff + (header.e_phentsize * i)); + if (read != sizeof(Elf_Phdr)) + { + printf("dl: Can't read program header %d\n", i); + return (int)read; + } + + switch (phdr.p_type) + { + case PT_LOAD: + { + if (phdr.p_memsz == 0) + continue; + + if (base == 0) + base = DynamicCreateBase(fd, Info); + + int mmapProt = 0; + if (phdr.p_flags & PF_X) + mmapProt |= __SYS_PROT_EXEC; + if (phdr.p_flags & PF_W) + mmapProt |= __SYS_PROT_WRITE; + if (phdr.p_flags & PF_R) + mmapProt |= __SYS_PROT_READ; + + off_t sectionOffset = ALIGN_DOWN(phdr.p_vaddr, phdr.p_align); + size_t sectionSize = ALIGN_UP(phdr.p_memsz + (phdr.p_vaddr - sectionOffset), phdr.p_align); + uintptr_t section = call_mmap(base + sectionOffset, + sectionSize, mmapProt, + __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE | __SYS_MAP_FIXED, + -1, 0); + sectionOffset = phdr.p_vaddr - ALIGN_DOWN(phdr.p_vaddr, phdr.p_align); + + if (phdr.p_filesz > 0) + { + ssize_t read = call_pread(fd, section + sectionOffset, phdr.p_filesz, phdr.p_offset); + if (read != phdr.p_filesz) + { + printf("dl: Can't read segment %d in PT_LOAD\n", i); + return (int)read; + } + } + + if (phdr.p_memsz - phdr.p_filesz > 0) + { + /* TODO: Do we really have to do this? Kernel already zeros the memory for us */ + void *zero = (void *)(section + sectionOffset + phdr.p_filesz); + memset(zero, 0, phdr.p_memsz - phdr.p_filesz); + } + lastLOAD = phdr; + break; + } + case PT_DYNAMIC: + { + Elf_Dyn *dynamicTable = NULL; + if (phdr.p_vaddr == lastLOAD.p_vaddr && phdr.p_memsz < lastLOAD.p_memsz) + { + /* The dynamic section is inside the last LOAD segment */ + dynamicTable = (Elf_Dyn *)(base + phdr.p_vaddr); + } + else + { + int mmapProt = 0; + if (phdr.p_flags & PF_X) + mmapProt |= __SYS_PROT_EXEC; + if (phdr.p_flags & PF_W) + mmapProt |= __SYS_PROT_WRITE; + if (phdr.p_flags & PF_R) + mmapProt |= __SYS_PROT_READ; + + dynamicTable = (Elf_Dyn *)call_mmap(0, ALIGN_UP(phdr.p_memsz, phdr.p_align), + mmapProt, __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE | __SYS_MAP_FIXED, + -1, 0); + + if ((intptr_t)dynamicTable <= 0) + { + printf("dl: Can't allocate memory for PT_DYNAMIC\n"); + return (int)(uintptr_t)dynamicTable; + } + + read = call_pread(fd, dynamicTable, phdr.p_memsz, phdr.p_offset); + if (read != phdr.p_memsz) + { + printf("dl: Can't read PT_DYNAMIC\n"); + return (int)read; + } + } + + Info->Dynamic = dynamicTable; + Info->DynamicHeader = phdr; + break; + } + case PT_INTERP: + break; + case PT_NOTE: + break; + case PT_SHLIB: + break; + case PT_PHDR: + break; + case PT_TLS: + { + printf("dl: PT_TLS not implemented yet\n"); + break; + } + default: + { + printf("dl: Unimplemented program header type %d\n", phdr.p_type); + break; + } + } + } + + return 0; +} + +int CheckElfEhdr(Elf_Ehdr *ehdr, char *Path) +{ + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || + ehdr->e_ident[EI_MAG1] != ELFMAG1 || + ehdr->e_ident[EI_MAG2] != ELFMAG2 || + ehdr->e_ident[EI_MAG3] != ELFMAG3) + { + printf("dl: %s is not an ELF file\n", Path); + return -EINVAL; + } + +#ifdef __LP64__ + const int elfClass = ELFCLASS64; +#else + const int elfClass = ELFCLASS32; +#endif + + if (ehdr->e_ident[EI_CLASS] != elfClass) + { + printf("dl: %s is not a %s-bit ELF file\n", + Path, __LP64__ ? "64" : "32"); + return -EINVAL; + } + + /* TODO: check LSB MSB */ + + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT || ehdr->e_version != EV_CURRENT) + { + printf("dl: %s has an unsupported ELF version\n", Path); + return -EINVAL; + } + + if (ehdr->e_type != ET_DYN && ehdr->e_type != ET_EXEC) + { + printf("dl: %s is not a shared object or executable\n", Path); + return -EINVAL; + } + + return 0; +} + +int LoadElf(int fd, char *Path, ElfInfo **Out) +{ + ElfInfo *info = SearchLib(Path); + if (info != NULL) + { + *Out = info; + return 0; + } + + Elf_Ehdr header; + call_pread(fd, &header, sizeof(Elf_Ehdr), 0); + + int status = CheckElfEhdr(&header, Path); + if (status != 0) + return status; + + info = AllocateLib(); + info->Header = header; + info->Path = (char *)call_mmap(0, + ALIGN_UP(strlen(Path) + 1, 0x1000 /* TODO: get page size from kernel */), + __SYS_PROT_READ, + __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE, + -1, 0); + if ((intptr_t)info->Path <= 0) + { + printf("dl: Can't allocate memory for path\n"); + FreeLib(info); + return (int)(uintptr_t)info->Path; + } + + memcpy(info->Path, Path, strlen(Path) + 1); + + switch (header.e_type) + { + case ET_REL: + printf("dl: ET_REL not implemented yet\n"); + status = -ENOSYS; + break; + case ET_EXEC: + status = LoadElfPhdrEXEC(fd, info); + break; + case ET_DYN: + status = LoadElfPhdrDYN(fd, info); + break; + case ET_CORE: + printf("dl: ET_CORE not implemented yet\n"); + status = -ENOSYS; + break; + case ET_NONE: + printf("dl: ET_NONE???\n"); + status = -EINVAL; + break; + default: + printf("dl: Unsupported ELF type %d\n", header.e_type); + status = -EINVAL; + break; + } + + if (status < 0) + { + call_munmap((uintptr_t)info->Path, ALIGN_UP(strlen(Path) + 1, 0x1000)); + FreeLib(info); + return status; + } + + ProcessDynamicTable(info); + *Out = info; + return 0; +} + +uintptr_t GetSymbolAddress(ElfInfo *Info, const char *SymbolName) +{ + Elf64_Sym *sym = find_symbol(SymbolName, + (uint32_t *)Info->DynamicTable.HASH, + (Elf64_Sym *)Info->DynamicTable.SYMTAB, + (const char *)Info->DynamicTable.STRTAB); + if (sym == NULL) + return (-ENOSYS); + return Info->BaseAddress + sym->st_value; +} + +int ResolveExternalSymbol(ElfInfo *Info, uintptr_t *symAddress, Elf_Sym *sym, const char *symName) +{ + *symAddress = (-ENOSYS); + + for (size_t i = 0; *symAddress == (-ENOSYS); i++) + { + Elf_Dyn *dyn = &Info->Dynamic[i]; + if (dyn->d_tag == DT_NULL) + break; + + if (dyn->d_tag != DT_NEEDED) + continue; + + ElfInfo *lib = (ElfInfo *)dyn->d_un.d_ptr; + *symAddress = GetSymbolAddress(lib, symName); + } + + if (*symAddress != (-ENOSYS)) + return 0; + + printf("%s: Unresolved symbol: %s\n", Info->Path, symName); + if (ELF_ST_BIND(sym->st_info) != STB_WEAK) + return -EINVAL; + *symAddress = 0; + return 0; +} + +int ApplyRelocation(ElfInfo *Info, uintptr_t *reloc, Elf_Rela *Rela, size_t reloSize) +{ + switch (reloSize) + { + case 0: + break; + case sizeof(uint8_t): + *reloc += *(uint8_t *)(Info->BaseAddress + Rela->r_offset); + break; + case sizeof(uint16_t): + *reloc += *(uint16_t *)(Info->BaseAddress + Rela->r_offset); + break; + case sizeof(uint32_t): + *reloc += *(uint32_t *)(Info->BaseAddress + Rela->r_offset); + break; + case sizeof(uint64_t): + *reloc += *(uint64_t *)(Info->BaseAddress + Rela->r_offset); + break; + default: + { + printf("dl: Unsupported size for relocation\n"); + return -EINVAL; + } + } + return 0; +} + +int CalculateRelocation(ElfInfo *Info, uintptr_t *reloc, Elf_Rela *Rela, size_t reloSize) +{ + switch (reloSize) + { + case 0: + break; + case sizeof(uint8_t): + *(uint8_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; + break; + case sizeof(uint16_t): + *(uint16_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; + break; + case sizeof(uint32_t): + *(uint32_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; + break; + case sizeof(uint64_t): + *(uint64_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; + break; + default: + { + printf("dl: Unsupported size for relocation\n"); + return -EINVAL; + } + } + return 0; +} + +int RelocateHelper(ElfInfo *Info, Elf_Rela *Rela, short IsRel, void **Relocated) +{ + uintptr_t reloc = 0; + uintptr_t symAddress = 0; + size_t symSize = 0; + + uint32_t symIndex = ELF_R_SYM(Rela->r_info); + if (symIndex) + { + Elf_Sym *sym = (Elf_Sym *)(Info->DynamicTable.SYMTAB + symIndex * Info->DynamicSize.SYMENT); + const char *symName = (const char *)(Info->DynamicTable.STRTAB + sym->st_name); + symSize = sym->st_size; + + if (!(ELF_R_TYPE(Rela->r_info) == R_COPY) && sym->st_shndx) + symAddress = Info->BaseAddress + sym->st_value; + else if (ResolveExternalSymbol(Info, &symAddress, sym, symName) < 0) + return -EINVAL; + } + + size_t reloSize = 0; + int addAddend = 0; + + enum RelocationTypes relType = ELF_R_TYPE(Rela->r_info); + switch (relType) + { + case R_NONE: + break; + case R_X86_64_64: + { + reloSize = 8; + reloc = symAddress; + addAddend = 1; + break; + } + case R_COPY: + { + if (symAddress == 0) + { + printf("dl: Copy undefined weak symbol %d\n", ELF_R_SYM(Rela->r_info)); + return -EINVAL; + } + memcpy((void *)(Info->BaseAddress + Rela->r_offset), + (void *)symAddress, + symSize); + break; + } + case R_GLOB_DAT: + case R_JMP_SLOT: + { + reloSize = 8; + reloc = symAddress; + break; + } + case R_RELATIVE: + { + reloSize = 8; + reloc = Info->BaseAddress; + addAddend = 1; + break; + } + case R_DTPMOD64: + { + printf("dl: i don't know what to do with DTPMOD64\n"); + reloc = Info->BaseAddress; + break; + } + case R_DTPOFF64: + { + printf("dl: i don't know what to do with DTPOFF64\n"); + reloc = symAddress + Rela->r_addend; + break; + } + case R_TPOFF64: + { + printf("dl: i don't know what to do with TPOFF64\n"); + reloc = symAddress + Rela->r_addend; + break; + } + default: + { + printf("dl: Unsupported relocation type %d\n", relType); + return -EINVAL; + } + } + + if (addAddend) + { + if (IsRel == 0) + reloc += Rela->r_addend; + else if (ApplyRelocation(Info, &reloc, Rela, reloSize) < 0) + return -EINVAL; + } + + CalculateRelocation(Info, &reloc, Rela, reloSize); + if (Relocated != NULL) + *Relocated = (void *)reloc; + return 0; +} + +int HandleRelocations(ElfInfo *Info); +void SearchNeeded(ElfInfo *Info) +{ + for (size_t i = 0;; i++) + { + Elf_Dyn *elem = &Info->Dynamic[i]; + if (elem->d_tag == DT_NULL) + break; + + if (elem->d_tag != DT_NEEDED) + continue; + + HandleRelocations((ElfInfo *)elem->d_un.d_ptr); + } +} + +int HandleRelocations(ElfInfo *Info) +{ + if (Info->Flags.Relocated) + return 0; + SearchNeeded(Info); + + if (Info->DynamicTable.REL != ((Elf_Addr)0) && Info->DynamicSize.RELENT != 0) + { + for (size_t i = 0; i < Info->DynamicSize.RELSZ / Info->DynamicSize.RELENT; i++) + RelocateHelper(Info, (Elf_Rela *)(Info->DynamicTable.REL + i * Info->DynamicSize.RELENT), 1, NULL); + } + + if (Info->DynamicTable.RELA != ((Elf_Addr)0) && Info->DynamicSize.RELAENT != 0) + { + for (size_t i = 0; i < Info->DynamicSize.RELASZ / Info->DynamicSize.RELAENT; i++) + RelocateHelper(Info, (Elf_Rela *)(Info->DynamicTable.RELA + i * Info->DynamicSize.RELAENT), 0, NULL); + } + + if (Info->DynamicTable.JMPREL == ((Elf_Addr)0) || Info->DynamicSize.PLTRELSZ == 0) + return 0; + + if (Info->DynamicSize.PLTREL != DT_REL && Info->DynamicSize.PLTREL != DT_RELA) + { + printf("dl: Wrong PLT relocation type %d\n", Info->DynamicSize.PLTREL); + return -EINVAL; + } + + if (Info->DynamicTable.BIND_NOW != ((Elf_Addr)0)) + { + if (Info->DynamicSize.PLTREL == DT_REL) + { + for (size_t i = 0; i < Info->DynamicSize.PLTRELSZ / sizeof(Elf_Rel); i++) + RelocateHelper(Info, (Elf_Rela *)&((Elf_Rel *)Info->DynamicTable.JMPREL)[i], 1, NULL); + } + else if (Info->DynamicSize.PLTREL == DT_RELA) + { + for (size_t i = 0; i < Info->DynamicSize.PLTRELSZ / sizeof(Elf_Rela); i++) + RelocateHelper(Info, (Elf_Rela *)&((Elf_Rela *)Info->DynamicTable.JMPREL)[i], 0, NULL); + } + + Info->Flags.Relocated = 1; + return 0; + } + + size_t relsize = Info->DynamicSize.PLTREL == DT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela); + for (size_t i = 0; i < Info->DynamicSize.PLTRELSZ / relsize; i++) + { + Elf64_Xword info = (Info->DynamicSize.PLTREL == DT_REL + ? ((Elf_Rel *)Info->DynamicTable.JMPREL)[i].r_info + : ((Elf_Rela *)Info->DynamicTable.JMPREL)[i].r_info); + + Elf_Addr offset = (Info->DynamicSize.PLTREL == DT_REL + ? ((Elf_Rel *)Info->DynamicTable.JMPREL)[i].r_offset + : ((Elf_Rela *)Info->DynamicTable.JMPREL)[i].r_offset); + + if (ELF_R_TYPE(info) != R_JMP_SLOT) + { + printf("dl: Wrong JMPREL type %d\n", ELF_R_TYPE(info)); + return -EINVAL; + } + + /* FIXME: HANDLE THIS RIGHT */ + if (Info->DynamicSize.PLTREL == DT_REL) + { + Elf_Addr *slot = (Elf_Addr *)(Info->BaseAddress + offset); + *slot += Info->BaseAddress; + if (*slot == Info->BaseAddress) + RelocateHelper(Info, (Elf_Rela *)&((Elf_Rel *)Info->DynamicTable.JMPREL)[i], 1, NULL); + } + else if (Info->DynamicSize.PLTREL == DT_RELA) + { + Elf64_Sxword addend = ((Elf_Rela *)Info->DynamicTable.JMPREL)[i].r_addend; + Elf_Addr *slot = (Elf_Addr *)(Info->BaseAddress + offset); + *slot += Info->BaseAddress + addend; + if (*slot == Info->BaseAddress) + RelocateHelper(Info, (Elf_Rela *)&((Elf_Rela *)Info->DynamicTable.JMPREL)[i], 0, NULL); + } + } + + Info->Flags.Relocated = 1; + return 0; +} + +int _dl_main(int argc, char *argv[], char *envp[]) +{ + char *path = argv[0]; + ElfInfo *info = NULL; + if (call_access(path, __SYS_F_OK) < 0) + { + printf("dl: Can't access file %s\n", path); + return -EACCES; + } + + int fd = call_open(path, __SYS_O_RDONLY, 0644); + int status = LoadElf(fd, path, &info); + if (status < 0) + { + printf("%s: Can't load ELF file\n", path); + call_close(fd); + return status; + } + + status = HandleRelocations(info); + if (status < 0) + { + printf("%s: Can't relocate ELF file\n", path); + call_close(fd); + return status; + } + + call_close(fd); + Elf_Addr entry = info->BaseAddress + info->Header.e_entry; + return ((int (*)(int, char *[], char *[]))entry)(argc, argv, envp); +} diff --git a/Userspace/libc/interpreter/main.c b/Userspace/libc/interpreter/main.c new file mode 100644 index 00000000..1f7c49ba --- /dev/null +++ b/Userspace/libc/interpreter/main.c @@ -0,0 +1,51 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include + +int printf(const char *, ...); +int _dl_main(int, char *[], char *[]); + +void print_help() +{ + printf("Usage: ld.so [options] \n"); + printf("Options:\n"); + printf(" --help Display this help message\n"); +} + +int main(int argc, char *argv[], char *envp[]) +{ + if (argc < 2) + { + printf("Error: No program specified.\n"); + print_help(); + return -1; + } + + for (int i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--help") == 0) + { + print_help(); + return 0; + } + } + + int status = _dl_main(argc, argv, envp); + return status; +} diff --git a/Userspace/libc/interpreter/misc.h b/Userspace/libc/interpreter/misc.h new file mode 100644 index 00000000..5ccc8951 --- /dev/null +++ b/Userspace/libc/interpreter/misc.h @@ -0,0 +1,50 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#ifndef __FENNIX_DL_HELPER_H__ +#define __FENNIX_DL_HELPER_H__ + +#include +#include +#include + +#define ALIGN_UP(x, align) ((__typeof__(x))(((uintptr_t)(x) + ((align) - 1)) & (~((align) - 1)))) +#define ALIGN_DOWN(x, align) ((__typeof__(x))((x) & (~((align) - 1)))) + +#ifndef __FENNIX_DL_ELF_H__ +#error "Please include elf.h before misc.h" +#endif + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +size_t strlen(const char *s); +char *strcpy(char *dest, const char *src); +int strcmp(const char *l, const char *r); +char *strcat(char *dest, const char *src); + +unsigned long elf_hash(const unsigned char *name); +uint32_t gnu_hash(const char *name); +Elf64_Sym *find_symbol(const char *name, uint32_t *hash_table, Elf64_Sym *symtab, const char *strtab); + +void __init_print_buffer(); +void __fini_print_buffer(); +int printf(const char *format, ...); + +void *mini_malloc(size_t size); +void mini_free(void *ptr); + +#endif // !__FENNIX_DL_HELPER_H__ diff --git a/Userspace/libc/interpreter/nanoprintf.h b/Userspace/libc/interpreter/nanoprintf.h new file mode 100644 index 00000000..f8362e06 --- /dev/null +++ b/Userspace/libc/interpreter/nanoprintf.h @@ -0,0 +1,1460 @@ +/* nanoprintf v0.5.3: a tiny embeddable printf replacement written in C. + https://github.com/charlesnicholson/nanoprintf + charles.nicholson+nanoprintf@gmail.com + dual-licensed under 0bsd and unlicense, take your pick. see eof for details. */ + +#ifndef NANOPRINTF_H_INCLUDED +#define NANOPRINTF_H_INCLUDED + +#include +#include + +// Define this to fully sandbox nanoprintf inside of a translation unit. +#ifdef NANOPRINTF_VISIBILITY_STATIC +#define NPF_VISIBILITY static +#else +#define NPF_VISIBILITY extern +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ + __attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX))) +#else +#define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) +#endif + +// Public API + +#ifdef __cplusplus +extern "C" +{ +#endif + + // The npf_ functions all return the number of bytes required to express the + // fully-formatted string, not including the null terminator character. + // The npf_ functions do not return negative values, since the lack of 'l' length + // modifier support makes encoding errors impossible. + + NPF_VISIBILITY int npf_snprintf( + char *buffer, size_t bufsz, const char *format, ...) NPF_PRINTF_ATTR(3, 4); + + NPF_VISIBILITY int npf_vsnprintf( + char *buffer, size_t bufsz, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); + + typedef void (*npf_putc)(int c, void *ctx); + NPF_VISIBILITY int npf_pprintf( + npf_putc pc, void *pc_ctx, char const *format, ...) NPF_PRINTF_ATTR(3, 4); + + NPF_VISIBILITY int npf_vpprintf( + npf_putc pc, void *pc_ctx, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); + +#ifdef __cplusplus +} +#endif + +#endif // NANOPRINTF_H_INCLUDED + +/* The implementation of nanoprintf begins here, to be compiled only if + NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would + be nanoprintf.c. */ + +#ifdef NANOPRINTF_IMPLEMENTATION + +#ifndef NANOPRINTF_IMPLEMENTATION_INCLUDED +#define NANOPRINTF_IMPLEMENTATION_INCLUDED + +#include +#include + +// The conversion buffer must fit at least UINT64_MAX in octal format with the leading '0'. +#ifndef NANOPRINTF_CONVERSION_BUFFER_SIZE +#define NANOPRINTF_CONVERSION_BUFFER_SIZE 23 +#endif +#if NANOPRINTF_CONVERSION_BUFFER_SIZE < 23 +#error The size of the conversion buffer must be at least 23 bytes. +#endif + +// Pick reasonable defaults if nothing's been configured. +#if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) +#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 +#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 +#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 +#endif + +// If anything's been configured, everything must be configured. +#ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS +#error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS +#error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS +#error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS +#error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS +#error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS +#error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif + +// Ensure flags are compatible. +#if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0) +#error Precision format specifiers must be enabled if float support is enabled. +#endif + +// intmax_t / uintmax_t require stdint from c99 / c++11 +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 +#ifndef _MSC_VER +#ifdef __cplusplus +#if __cplusplus < 201103L +#error large format specifier support requires C++11 or later. +#endif +#else +#if __STDC_VERSION__ < 199409L +#error nanoprintf requires C99 or later. +#endif +#endif +#endif +#endif + +// Figure out if we can disable warnings with pragmas. +#ifdef __clang__ +#define NANOPRINTF_CLANG 1 +#define NANOPRINTF_GCC_PAST_4_6 0 +#else +#define NANOPRINTF_CLANG 0 +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))) +#define NANOPRINTF_GCC_PAST_4_6 1 +#else +#define NANOPRINTF_GCC_PAST_4_6 0 +#endif +#endif + +#if NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 +#define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 1 +#else +#define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 0 +#endif + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#ifdef __cplusplus +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#pragma GCC diagnostic ignored "-Wpadded" +#pragma GCC diagnostic ignored "-Wfloat-equal" +#if NANOPRINTF_CLANG +#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" +#pragma GCC diagnostic ignored "-Wcovered-switch-default" +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +#ifndef __APPLE__ +#pragma GCC diagnostic ignored "-Wunsafe-buffer-usage" +#endif +#elif NANOPRINTF_GCC_PAST_4_6 +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4619) // there is no warning number 'number' +// C4619 has to be disabled first! +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4505) // unreferenced local function has been removed +#pragma warning(disable : 4514) // unreferenced inline function has been removed +#pragma warning(disable : 4701) // potentially uninitialized local variable used +#pragma warning(disable : 4706) // assignment within conditional expression +#pragma warning(disable : 4710) // function not inlined +#pragma warning(disable : 4711) // function selected for inline expansion +#pragma warning(disable : 4820) // padding added after struct member +#pragma warning(disable : 5039) // potentially throwing function passed to extern C function +#pragma warning(disable : 5045) // compiler will insert Spectre mitigation for memory load +#pragma warning(disable : 5262) // implicit switch fall-through +#pragma warning(disable : 26812) // enum type is unscoped +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define NPF_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define NPF_NOINLINE __declspec(noinline) +#else +#define NPF_NOINLINE +#endif + +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) +enum +{ + NPF_FMT_SPEC_OPT_NONE, + NPF_FMT_SPEC_OPT_LITERAL, + NPF_FMT_SPEC_OPT_STAR, +}; +#endif + +enum +{ + NPF_FMT_SPEC_LEN_MOD_NONE, + NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h' + NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L' + NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh' + NPF_FMT_SPEC_LEN_MOD_LONG, // 'l' +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll' + NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j' + NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z' + NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT, // 't' +#endif +}; + +enum +{ + NPF_FMT_SPEC_CONV_NONE, + NPF_FMT_SPEC_CONV_PERCENT, // '%' + NPF_FMT_SPEC_CONV_CHAR, // 'c' + NPF_FMT_SPEC_CONV_STRING, // 's' + NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd' +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_BINARY, // 'b' +#endif + NPF_FMT_SPEC_CONV_OCTAL, // 'o' + NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X' + NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u' + NPF_FMT_SPEC_CONV_POINTER, // 'p' +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_WRITEBACK, // 'n' +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' + NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' + NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G' + NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' +#endif +}; + +typedef struct npf_format_spec +{ +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_width; + uint8_t field_width_opt; + char left_justified; // '-' + char leading_zero_pad; // '0' +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec; + uint8_t prec_opt; +#endif + char prepend; // ' ' or '+' + char alt_form; // '#' + char case_adjust; // 'a' - 'A' + uint8_t length_modifier; + uint8_t conv_spec; +} npf_format_spec_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 +typedef long npf_int_t; +typedef unsigned long npf_uint_t; +#else +typedef intmax_t npf_int_t; +typedef uintmax_t npf_uint_t; +#endif + +typedef struct npf_bufputc_ctx +{ + char *dst; + size_t len; + size_t cur; +} npf_bufputc_ctx_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 +typedef char npf_size_is_ptrdiff[(sizeof(size_t) == sizeof(ptrdiff_t)) ? 1 : -1]; +typedef ptrdiff_t npf_ssize_t; +#endif + +#ifdef _MSC_VER +#include +#endif + +static int npf_max(int x, int y) { return (x > y) ? x : y; } + +static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) +{ + char const *cur = format; + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->left_justified = 0; + out_spec->leading_zero_pad = 0; +#endif + out_spec->case_adjust = 'a' - 'A'; // lowercase + out_spec->prepend = 0; + out_spec->alt_form = 0; + + while (*++cur) + { // cur points at the leading '%' character + switch (*cur) + { // Optional flags +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + case '-': + out_spec->left_justified = '-'; + out_spec->leading_zero_pad = 0; + continue; + case '0': + out_spec->leading_zero_pad = !out_spec->left_justified; + continue; +#endif + case '+': + out_spec->prepend = '+'; + continue; + case ' ': + if (out_spec->prepend == 0) + { + out_spec->prepend = ' '; + } + continue; + case '#': + out_spec->alt_form = '#'; + continue; + default: + break; + } + break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '*') + { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } + else + { + out_spec->field_width = 0; + while ((*cur >= '0') && (*cur <= '9')) + { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0'); + } + } +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec = 0; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '.') + { + ++cur; + if (*cur == '*') + { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } + else + { + if (*cur == '-') + { + ++cur; + } + else + { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL; + } + while ((*cur >= '0') && (*cur <= '9')) + { + out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0'); + } + } + } +#endif + + uint_fast8_t tmp_conv = NPF_FMT_SPEC_CONV_NONE; + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE; + switch (*cur++) + { // Length modifier + case 'h': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT; + if (*cur == 'h') + { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR; + ++cur; + } + break; + case 'l': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG; +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + if (*cur == 'l') + { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG; + ++cur; + } +#endif + break; +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'L': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; + break; +#endif +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + case 'j': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; + break; + case 'z': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; + break; + case 't': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; + break; +#endif + default: + --cur; + break; + } + + switch (*cur++) + { // Conversion specifier + case '%': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + + case 'c': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + + case 's': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->leading_zero_pad = 0; +#endif + break; + + case 'i': + case 'd': + tmp_conv = NPF_FMT_SPEC_CONV_SIGNED_INT; + case 'o': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) + { + tmp_conv = NPF_FMT_SPEC_CONV_OCTAL; + } + case 'u': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) + { + tmp_conv = NPF_FMT_SPEC_CONV_UNSIGNED_INT; + } + case 'X': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) + { + out_spec->case_adjust = 0; + } + case 'x': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) + { + tmp_conv = NPF_FMT_SPEC_CONV_HEX_INT; + } + out_spec->conv_spec = (uint8_t)tmp_conv; +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) + if (out_spec->prec_opt != NPF_FMT_SPEC_OPT_NONE) + { + out_spec->leading_zero_pad = 0; + } +#endif + break; + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'F': + out_spec->case_adjust = 0; + case 'f': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DEC; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) + { + out_spec->prec = 6; + } + break; + + case 'E': + out_spec->case_adjust = 0; + case 'e': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SCI; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) + { + out_spec->prec = 6; + } + break; + + case 'G': + out_spec->case_adjust = 0; + case 'g': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) + { + out_spec->prec = 6; + } + break; + + case 'A': + out_spec->case_adjust = 0; + case 'a': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_HEX; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) + { + out_spec->prec = 6; + } + break; +#endif + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case 'n': + // todo: reject string if flags or width or precision exist + out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; +#endif + + case 'p': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case 'B': + out_spec->case_adjust = 0; + case 'b': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY; + break; +#endif + + default: + return 0; + } + + return (int)(cur - format); +} + +static NPF_NOINLINE int npf_utoa_rev( + npf_uint_t val, char *buf, uint_fast8_t base, char case_adj) +{ + uint_fast8_t n = 0; + do + { + int_fast8_t const d = (int_fast8_t)(val % base); + *buf++ = (char)(((d < 10) ? '0' : ('A' - 10 + case_adj)) + d); + ++n; + val /= base; + } while (val); + return (int)n; +} + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + +#include + +#if (DBL_MANT_DIG <= 11) && (DBL_MAX_EXP <= 16) +typedef uint_fast16_t npf_double_bin_t; +typedef int_fast8_t npf_ftoa_exp_t; +#elif (DBL_MANT_DIG <= 24) && (DBL_MAX_EXP <= 128) +typedef uint_fast32_t npf_double_bin_t; +typedef int_fast8_t npf_ftoa_exp_t; +#elif (DBL_MANT_DIG <= 53) && (DBL_MAX_EXP <= 1024) +typedef uint_fast64_t npf_double_bin_t; +typedef int_fast16_t npf_ftoa_exp_t; +#else +#error Unsupported width of the double type. +#endif + +// The floating point conversion code works with an unsigned integer type of any size. +#ifndef NANOPRINTF_CONVERSION_FLOAT_TYPE +#define NANOPRINTF_CONVERSION_FLOAT_TYPE unsigned int +#endif +typedef NANOPRINTF_CONVERSION_FLOAT_TYPE npf_ftoa_man_t; + +#if (NANOPRINTF_CONVERSION_BUFFER_SIZE <= UINT_FAST8_MAX) && (UINT_FAST8_MAX <= INT_MAX) +typedef uint_fast8_t npf_ftoa_dec_t; +#else +typedef int npf_ftoa_dec_t; +#endif + +enum +{ + NPF_DOUBLE_EXP_MASK = DBL_MAX_EXP * 2 - 1, + NPF_DOUBLE_EXP_BIAS = DBL_MAX_EXP - 1, + NPF_DOUBLE_MAN_BITS = DBL_MANT_DIG - 1, + NPF_DOUBLE_BIN_BITS = sizeof(npf_double_bin_t) * CHAR_BIT, + NPF_FTOA_MAN_BITS = sizeof(npf_ftoa_man_t) * CHAR_BIT, + NPF_FTOA_SHIFT_BITS = + ((NPF_FTOA_MAN_BITS < DBL_MANT_DIG) ? NPF_FTOA_MAN_BITS : DBL_MANT_DIG) - 1 +}; + +/* Generally, floating-point conversion implementations use + grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) algorithms, + which are mathematically exact and fast, but require large lookup tables. + + This implementation was inspired by Wojciech Muła's (zdjęcia@garnek.pl) + algorithm (http://0x80.pl/notesen/2015-12-29-float-to-string.html) and + extended further by adding dynamic scaling and configurable integer width by + Oskars Rubenis (https://github.com/Okarss). */ + +static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) +{ + char const *ret = NULL; + npf_double_bin_t bin; + { // Union-cast is UB pre-C11, compiler optimizes byte-copy loop. + char const *src = (char const *)&f; + char *dst = (char *)&bin; + for (uint_fast8_t i = 0; i < sizeof(f); ++i) + { + dst[i] = src[i]; + } + } + + // Unsigned -> signed int casting is IB and can raise a signal but generally doesn't. + npf_ftoa_exp_t exp = + (npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_DOUBLE_MAN_BITS) & NPF_DOUBLE_EXP_MASK); + + bin &= ((npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS) - 1; + if (exp == (npf_ftoa_exp_t)NPF_DOUBLE_EXP_MASK) + { // special value + ret = (bin) ? "NAN" : "FNI"; + goto exit; + } + if (spec->prec > (NANOPRINTF_CONVERSION_BUFFER_SIZE - 2)) + { + goto exit; + } + if (exp) + { // normal number + bin |= (npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS; + } + else + { // subnormal number + ++exp; + } + exp = (npf_ftoa_exp_t)(exp - NPF_DOUBLE_EXP_BIAS); + + uint_fast8_t carry; + carry = 0; + npf_ftoa_dec_t end, dec; + dec = (npf_ftoa_dec_t)spec->prec; + if (dec || spec->alt_form) + { + buf[dec++] = '.'; + } + + { // Integer part + npf_ftoa_man_t man_i; + + if (exp >= 0) + { + int_fast8_t shift_i = + (int_fast8_t)((exp > NPF_FTOA_SHIFT_BITS) ? (int)NPF_FTOA_SHIFT_BITS : exp); + npf_ftoa_exp_t exp_i = (npf_ftoa_exp_t)(exp - shift_i); + shift_i = (int_fast8_t)(NPF_DOUBLE_MAN_BITS - shift_i); + man_i = (npf_ftoa_man_t)(bin >> shift_i); + + if (exp_i) + { + if (shift_i) + { + carry = (bin >> (shift_i - 1)) & 0x1; + } + exp = NPF_DOUBLE_MAN_BITS; // invalidate the fraction part + } + + // Scale the exponent from base-2 to base-10. + for (; exp_i; --exp_i) + { + if (!(man_i & ((npf_ftoa_man_t)0x1 << (NPF_FTOA_MAN_BITS - 1)))) + { + man_i = (npf_ftoa_man_t)(man_i << 1); + man_i = (npf_ftoa_man_t)(man_i | carry); + carry = 0; + } + else + { + if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) + { + goto exit; + } + buf[dec++] = '0'; + carry = (((uint_fast8_t)(man_i % 5) + carry) > 2); + man_i /= 5; + } + } + } + else + { + man_i = 0; + } + end = dec; + + do + { // Print the integer + if (end >= NANOPRINTF_CONVERSION_BUFFER_SIZE) + { + goto exit; + } + buf[end++] = (char)('0' + (char)(man_i % 10)); + man_i /= 10; + } while (man_i); + } + + { // Fraction part + npf_ftoa_man_t man_f; + npf_ftoa_dec_t dec_f = (npf_ftoa_dec_t)spec->prec; + + if (exp < NPF_DOUBLE_MAN_BITS) + { + int_fast8_t shift_f = (int_fast8_t)((exp < 0) ? -1 : exp); + npf_ftoa_exp_t exp_f = (npf_ftoa_exp_t)(exp - shift_f); + npf_double_bin_t bin_f = + bin << ((NPF_DOUBLE_BIN_BITS - NPF_DOUBLE_MAN_BITS) + shift_f); + + // This if-else statement can be completely optimized at compile time. + if (NPF_DOUBLE_BIN_BITS > NPF_FTOA_MAN_BITS) + { + man_f = (npf_ftoa_man_t)(bin_f >> ((unsigned)(NPF_DOUBLE_BIN_BITS - + NPF_FTOA_MAN_BITS) % + NPF_DOUBLE_BIN_BITS)); + carry = (uint_fast8_t)((bin_f >> ((unsigned)(NPF_DOUBLE_BIN_BITS - + NPF_FTOA_MAN_BITS - 1) % + NPF_DOUBLE_BIN_BITS)) & + 0x1); + } + else + { + man_f = (npf_ftoa_man_t)((npf_ftoa_man_t)bin_f + << ((unsigned)(NPF_FTOA_MAN_BITS - + NPF_DOUBLE_BIN_BITS) % + NPF_FTOA_MAN_BITS)); + carry = 0; + } + + // Scale the exponent from base-2 to base-10 and prepare the first digit. + for (uint_fast8_t digit = 0; dec_f && (exp_f < 4); ++exp_f) + { + if ((man_f > ((npf_ftoa_man_t)-4 / 5)) || digit) + { + carry = (uint_fast8_t)(man_f & 0x1); + man_f = (npf_ftoa_man_t)(man_f >> 1); + } + else + { + man_f = (npf_ftoa_man_t)(man_f * 5); + if (carry) + { + man_f = (npf_ftoa_man_t)(man_f + 3); + carry = 0; + } + if (exp_f < 0) + { + buf[--dec_f] = '0'; + } + else + { + ++digit; + } + } + } + man_f = (npf_ftoa_man_t)(man_f + carry); + carry = (exp_f >= 0); + dec = 0; + } + else + { + man_f = 0; + } + + if (dec_f) + { + // Print the fraction + for (;;) + { + buf[--dec_f] = (char)('0' + (char)(man_f >> (NPF_FTOA_MAN_BITS - 4))); + man_f = (npf_ftoa_man_t)(man_f & ~((npf_ftoa_man_t)0xF << (NPF_FTOA_MAN_BITS - 4))); + if (!dec_f) + { + break; + } + man_f = (npf_ftoa_man_t)(man_f * 10); + } + man_f = (npf_ftoa_man_t)(man_f << 4); + } + if (exp < NPF_DOUBLE_MAN_BITS) + { + carry &= (uint_fast8_t)(man_f >> (NPF_FTOA_MAN_BITS - 1)); + } + } + + // Round the number + for (; carry; ++dec) + { + if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) + { + goto exit; + } + if (dec >= end) + { + buf[end++] = '0'; + } + if (buf[dec] == '.') + { + continue; + } + carry = (buf[dec] == '9'); + buf[dec] = (char)(carry ? '0' : (buf[dec] + 1)); + } + + return (int)end; +exit: + if (!ret) + { + ret = "RRE"; + } + uint_fast8_t i; + for (i = 0; ret[i]; ++i) + { + buf[i] = (char)(ret[i] + spec->case_adjust); + } + return (int)i; +} + +#endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +static int npf_bin_len(npf_uint_t u) +{ + // Return the length of the binary string format of 'u', preferring intrinsics. + if (!u) + { + return 1; + } + +#ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large. +#ifdef _M_X64 +#define NPF_HAVE_BUILTIN_CLZ +#define NPF_CLZ _BitScanReverse64 +#elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 +#define NPF_HAVE_BUILTIN_CLZ +#define NPF_CLZ _BitScanReverse +#endif +#ifdef NPF_HAVE_BUILTIN_CLZ + unsigned long idx; + NPF_CLZ(&idx, u); + return (int)(idx + 1); +#endif +#elif NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 +#define NPF_HAVE_BUILTIN_CLZ +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 +#define NPF_CLZ(X) ((sizeof(long long) * CHAR_BIT) - (size_t)__builtin_clzll(X)) +#else +#define NPF_CLZ(X) ((sizeof(long) * CHAR_BIT) - (size_t)__builtin_clzl(X)) +#endif + return (int)NPF_CLZ(u); +#endif + +#ifndef NPF_HAVE_BUILTIN_CLZ + int n; + for (n = 0; u; ++n, u >>= 1) + ; // slow but small software fallback + return n; +#else +#undef NPF_HAVE_BUILTIN_CLZ +#undef NPF_CLZ +#endif +} +#endif + +static void npf_bufputc(int c, void *ctx) +{ + npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx; + if (bpc->cur < bpc->len) + { + bpc->dst[bpc->cur++] = (char)c; + } +} + +static void npf_bufputc_nop(int c, void *ctx) +{ + (void)c; + (void)ctx; +} + +typedef struct npf_cnt_putc_ctx +{ + npf_putc pc; + void *ctx; + int n; +} npf_cnt_putc_ctx_t; + +static void npf_putc_cnt(int c, void *ctx) +{ + npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx; + ++pc_cnt->n; + pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization +} + +#define NPF_PUTC(VAL) \ + do \ + { \ + npf_putc_cnt((int)(VAL), &pc_cnt); \ + } while (0) + +#define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: \ + val = (CAST_TO)va_arg(args, EXTRACT_AS); \ + break + +#define NPF_WRITEBACK(MOD, TYPE) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: \ + *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; \ + break + +int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) +{ + npf_format_spec_t fs; + char const *cur = format; + npf_cnt_putc_ctx_t pc_cnt; + pc_cnt.pc = pc; + pc_cnt.ctx = pc_ctx; + pc_cnt.n = 0; + + while (*cur) + { + int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs); + if (!fs_len) + { + NPF_PUTC(*cur++); + continue; + } + cur += fs_len; + + // Extract star-args immediately +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) + { + fs.field_width = va_arg(args, int); + if (fs.field_width < 0) + { + fs.field_width = -fs.field_width; + fs.left_justified = 1; + } + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) + { + fs.prec = va_arg(args, int); + if (fs.prec < 0) + { + fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; + } + } +#endif + + union + { + char cbuf_mem[NANOPRINTF_CONVERSION_BUFFER_SIZE]; + npf_uint_t binval; + } u; + char *cbuf = u.cbuf_mem, sign_c = 0; + int cbuf_len = 0, need_0x = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_pad = 0; + char pad_c = 0; +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec_pad = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int zero = 0; +#endif +#endif + + // Extract and convert the argument to string, point cbuf at the text. + switch (fs.conv_spec) + { + case NPF_FMT_SPEC_CONV_PERCENT: + *cbuf = '%'; + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_CHAR: + *cbuf = (char)va_arg(args, int); + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_STRING: + { + cbuf = va_arg(args, char *); +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + for (char const *s = cbuf; + ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) || (cbuf_len < fs.prec)) && *s; + ++s, ++cbuf_len) + ; +#else + for (char const *s = cbuf; *s; ++s, ++cbuf_len) + ; // strlen +#endif + } + break; + + case NPF_FMT_SPEC_CONV_SIGNED_INT: + { + npf_int_t val = 0; + switch (fs.length_modifier) + { + NPF_EXTRACT(NONE, int, int); + NPF_EXTRACT(SHORT, short, int); + NPF_EXTRACT(LONG_DOUBLE, int, int); + NPF_EXTRACT(CHAR, char, int); + NPF_EXTRACT(LONG, long, long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, long long, long long); + NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t); + NPF_EXTRACT(LARGE_SIZET, npf_ssize_t, npf_ssize_t); + NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t); +#endif + default: + break; + } + + sign_c = (val < 0) ? '-' : fs.prepend; + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + // special case, if prec and value are 0, skip + if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) + { + cbuf_len = 0; + } + else +#endif + { + npf_uint_t uval = (npf_uint_t)val; + if (val < 0) + { + uval = 0 - uval; + } + cbuf_len = npf_utoa_rev(uval, cbuf, 10, fs.case_adjust); + } + } + break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_BINARY: +#endif + case NPF_FMT_SPEC_CONV_OCTAL: + case NPF_FMT_SPEC_CONV_HEX_INT: + case NPF_FMT_SPEC_CONV_UNSIGNED_INT: + { + npf_uint_t val = 0; + + switch (fs.length_modifier) + { + NPF_EXTRACT(NONE, unsigned, unsigned); + NPF_EXTRACT(SHORT, unsigned short, unsigned); + NPF_EXTRACT(LONG_DOUBLE, unsigned, unsigned); + NPF_EXTRACT(CHAR, unsigned char, unsigned); + NPF_EXTRACT(LONG, unsigned long, unsigned long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long); + NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t); + NPF_EXTRACT(LARGE_SIZET, size_t, size_t); + NPF_EXTRACT(LARGE_PTRDIFFT, size_t, size_t); +#endif + default: + break; + } + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) + { + // Zero value and explicitly-requested zero precision means "print nothing". + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) + { + fs.prec = 1; // octal special case, print a single '0' + } + } + else +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) + { + cbuf_len = npf_bin_len(val); + u.binval = val; + } + else +#endif + { + uint_fast8_t const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ? 8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) ? 16u : 10u); + cbuf_len = npf_utoa_rev(val, cbuf, base, fs.case_adjust); + } + + if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) + { + cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately. + } + + if (val && fs.alt_form) + { // 0x or 0b but can't write it yet. + if (fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) + { + need_0x = 'X'; + } +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) + { + need_0x = 'B'; + } +#endif + if (need_0x) + { + need_0x += fs.case_adjust; + } + } + } + break; + + case NPF_FMT_SPEC_CONV_POINTER: + { + cbuf_len = + npf_utoa_rev((npf_uint_t)(uintptr_t)va_arg(args, void *), cbuf, 16, 'a' - 'A'); + need_0x = 'x'; + } + break; + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_WRITEBACK: + switch (fs.length_modifier) + { + NPF_WRITEBACK(NONE, int); + NPF_WRITEBACK(SHORT, short); + NPF_WRITEBACK(LONG, long); + NPF_WRITEBACK(LONG_DOUBLE, double); + NPF_WRITEBACK(CHAR, signed char); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_WRITEBACK(LARGE_LONG_LONG, long long); + NPF_WRITEBACK(LARGE_INTMAX, intmax_t); + NPF_WRITEBACK(LARGE_SIZET, size_t); + NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t); +#endif + default: + break; + } + break; +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_FLOAT_DEC: + case NPF_FMT_SPEC_CONV_FLOAT_SCI: + case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: + case NPF_FMT_SPEC_CONV_FLOAT_HEX: + { + double val; + if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) + { + val = (double)va_arg(args, long double); + } + else + { + val = va_arg(args, double); + } + + sign_c = (val < 0.) ? '-' : fs.prepend; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = (val == 0.); +#endif + cbuf_len = npf_ftoa_rev(cbuf, &fs, val); + } + break; +#endif + default: + break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Compute the field width pad character + if (fs.field_width_opt != NPF_FMT_SPEC_OPT_NONE) + { + if (fs.leading_zero_pad) + { // '0' flag is only legal with numeric types + if ((fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_CHAR) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_PERCENT)) + { +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if ((fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec && zero) + { + pad_c = ' '; + } + else +#endif + { + pad_c = '0'; + } + } + } + else + { + pad_c = ' '; + } + } +#endif + + // Compute the number of bytes to truncate or '0'-pad. +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) + { +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + // float precision is after the decimal point + if ((fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DEC) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_SCI) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_SHORTEST) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_HEX)) +#endif + { + prec_pad = npf_max(0, fs.prec - cbuf_len); + } + } +#endif + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Given the full converted length, how many pad bytes? + field_pad = fs.field_width - cbuf_len - !!sign_c; + if (need_0x) + { + field_pad -= 2; + } +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + field_pad -= prec_pad; +#endif + field_pad = npf_max(0, field_pad); + + // Apply right-justified field width if requested + if (!fs.left_justified && pad_c) + { // If leading zeros pad, sign goes first. + if (pad_c == '0') + { + if (sign_c) + { + NPF_PUTC(sign_c); + sign_c = 0; + } + // Pad byte is '0', write '0x' before '0' pad chars. + if (need_0x) + { + NPF_PUTC('0'); + NPF_PUTC(need_0x); + } + } + while (field_pad-- > 0) + { + NPF_PUTC(pad_c); + } + // Pad byte is ' ', write '0x' after ' ' pad chars but before number. + if ((pad_c != '0') && need_0x) + { + NPF_PUTC('0'); + NPF_PUTC(need_0x); + } + } + else +#endif + { + if (need_0x) + { + NPF_PUTC('0'); + NPF_PUTC(need_0x); + } + } // no pad, '0x' requested. + + // Write the converted payload + if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) + { + for (int i = 0; i < cbuf_len; ++i) + { + NPF_PUTC(cbuf[i]); + } + } + else + { + if (sign_c) + { + NPF_PUTC(sign_c); + } +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + while (prec_pad-- > 0) + { + NPF_PUTC('0'); + } // int precision leads. +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) + { + while (cbuf_len) + { + NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); + } + } + else +#endif + { + while (cbuf_len-- > 0) + { + NPF_PUTC(cbuf[cbuf_len]); + } + } // payload is reversed + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.left_justified && pad_c) + { // Apply left-justified field width + while (field_pad-- > 0) + { + NPF_PUTC(pad_c); + } + } +#endif + } + + return pc_cnt.n; +} + +#undef NPF_PUTC +#undef NPF_EXTRACT +#undef NPF_WRITEBACK + +int npf_pprintf(npf_putc pc, void *pc_ctx, char const *format, ...) +{ + va_list val; + va_start(val, format); + int const rv = npf_vpprintf(pc, pc_ctx, format, val); + va_end(val); + return rv; +} + +int npf_snprintf(char *buffer, size_t bufsz, const char *format, ...) +{ + va_list val; + va_start(val, format); + int const rv = npf_vsnprintf(buffer, bufsz, format, val); + va_end(val); + return rv; +} + +int npf_vsnprintf(char *buffer, size_t bufsz, char const *format, va_list vlist) +{ + npf_bufputc_ctx_t bufputc_ctx; + bufputc_ctx.dst = buffer; + bufputc_ctx.len = bufsz; + bufputc_ctx.cur = 0; + + npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop; + int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist); + pc('\0', &bufputc_ctx); + + if (buffer && bufsz) + { +#ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW + if (n >= (int)bufsz) + { + buffer[0] = '\0'; + } +#else + buffer[bufsz - 1] = '\0'; +#endif + } + + return n; +} + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS +#pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // NANOPRINTF_IMPLEMENTATION_INCLUDED +#endif // NANOPRINTF_IMPLEMENTATION + +/* + nanoprintf is dual-licensed under both the "Unlicense" and the + "Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing + structure is to make nanoprintf as consumable as possible in as many + environments / countries / companies as possible without any + encumberances. + + The text of the two licenses follows below: + + ============================== UNLICENSE ============================== + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + + ================================ 0BSD ================================= + + Copyright (C) 2019- by Charles Nicholson + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ diff --git a/Userspace/libc/interpreter/print.c b/Userspace/libc/interpreter/print.c new file mode 100644 index 00000000..10d8ba6a --- /dev/null +++ b/Userspace/libc/interpreter/print.c @@ -0,0 +1,82 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include + +#include "elf.h" +#include "misc.h" +#define NANOPRINTF_IMPLEMENTATION 1 +#include "nanoprintf.h" + +char *print_buffer; +size_t print_buffer_size; +size_t print_buffer_offset; +void flush_buffer() +{ + if (print_buffer_offset > 0) + { + call_write(1, print_buffer, print_buffer_offset); + print_buffer_offset = 0; + } +} + +void print_wrapper(int c, void *) +{ + if (print_buffer_offset >= print_buffer_size - 1) + flush_buffer(); + print_buffer[print_buffer_offset++] = (char)c; +} + +void __init_print_buffer() +{ + print_buffer = (char *)call_mmap(0, + 0x1000, + __SYS_PROT_READ | __SYS_PROT_WRITE, + __SYS_MAP_PRIVATE | __SYS_MAP_ANONYMOUS, + -1, 0); + print_buffer_size = 0x1000; + print_buffer_offset = 0; +} + +void __fini_print_buffer() +{ + flush_buffer(); + if (print_buffer != NULL) + call_munmap(print_buffer, 0x1000); + print_buffer = NULL; +} + +int printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + int ret = npf_vpprintf(print_wrapper, NULL, format, args); + va_end(args); + return ret; +} + +int puts(const char *s) +{ + int len = strlen(s); + memcpy(print_buffer + print_buffer_offset, s, len); + print_buffer_offset += len; + print_buffer[print_buffer_offset++] = '\0'; + flush_buffer(); + return len + 1; +} diff --git a/Userspace/libc/interpreter/start.c b/Userspace/libc/interpreter/start.c new file mode 100644 index 00000000..4b695a61 --- /dev/null +++ b/Userspace/libc/interpreter/start.c @@ -0,0 +1,139 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include + +// const char __interp[] __attribute__((section(".interp"))) = "/boot/fennix.elf"; + +#ifndef LIBC_GIT_COMMIT +#define LIBC_GIT_COMMIT "0000000000000000000000000000000000000000" +#endif + +#define HEX_DIGIT(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'a' + 10)) +#define CONVERT_TO_BYTE(h, l) ((HEX_DIGIT(h) << 4) | HEX_DIGIT(l)) +#define HASH_BYTES(hex) \ + {CONVERT_TO_BYTE(hex[0], hex[1]), \ + CONVERT_TO_BYTE(hex[2], hex[3]), \ + CONVERT_TO_BYTE(hex[4], hex[5]), \ + CONVERT_TO_BYTE(hex[6], hex[7]), \ + CONVERT_TO_BYTE(hex[8], hex[9]), \ + CONVERT_TO_BYTE(hex[10], hex[11]), \ + CONVERT_TO_BYTE(hex[12], hex[13]), \ + CONVERT_TO_BYTE(hex[14], hex[15]), \ + CONVERT_TO_BYTE(hex[16], hex[17]), \ + CONVERT_TO_BYTE(hex[18], hex[19]), \ + CONVERT_TO_BYTE(hex[20], hex[21]), \ + CONVERT_TO_BYTE(hex[22], hex[23]), \ + CONVERT_TO_BYTE(hex[24], hex[25]), \ + CONVERT_TO_BYTE(hex[26], hex[27]), \ + CONVERT_TO_BYTE(hex[28], hex[29]), \ + CONVERT_TO_BYTE(hex[30], hex[31]), \ + CONVERT_TO_BYTE(hex[32], hex[33]), \ + CONVERT_TO_BYTE(hex[34], hex[35]), \ + CONVERT_TO_BYTE(hex[36], hex[37]), \ + CONVERT_TO_BYTE(hex[38], hex[39])} + +/* These are declared in GNU ld */ +enum +{ + NT_FNX_ABI_TAG = 1, + NT_FNX_VERSION = 2, + NT_FNX_BUILD_ID = 3, + NT_FNX_ARCH = 4 +}; + +typedef struct Elf_Nhdr +{ + __UINT32_TYPE__ n_namesz; + __UINT32_TYPE__ n_descsz; + __UINT32_TYPE__ n_type; + char n_name[]; +} __attribute__((packed)) Elf_Nhdr; + +const struct +{ + Elf_Nhdr header; + char name[4]; + __UINT32_TYPE__ desc[4]; +} __abi_tag __attribute__((aligned(4), section(".note.ABI-tag"))) = { + .header = { + .n_namesz = 4, /* "FNX" + '\0' */ + .n_descsz = sizeof(__UINT32_TYPE__) * 4, /* Description Size */ + .n_type = NT_FNX_ABI_TAG, /* Type */ + }, + .name = "FNX", + .desc = {0, 0, 0, 0}, +}; + +const struct +{ + Elf_Nhdr header; + char name[4]; + __UINT8_TYPE__ desc[20]; +} __build_id __attribute__((aligned(4), section(".note.build-id"))) = { + .header = { + .n_namesz = 4, /* "FNX" + '\0' */ + .n_descsz = sizeof(__UINT8_TYPE__) * 20, /* Description Size */ + .n_type = NT_FNX_BUILD_ID, /* Type */ + }, + .name = "FNX", + .desc = HASH_BYTES(LIBC_GIT_COMMIT), +}; + +void __init_print_buffer(); +void __fini_print_buffer(); + +__attribute__((naked, used, no_stack_protector)) void _start() +{ + __asm__( + "xorq %rbp, %rbp\n" /* Clear rbp */ + + "push %rdi\n" + "push %rsi\n" + "push %rdx\n" + "push %rcx\n" + "push %r8\n" + "push %r9\n" + + "call __init_print_buffer\n" /* Call __init_print_buffer */ + "call _dl_preload\n" /* Call _dl_preload */ + "movl %eax, %edi\n" /* Move return value to edi */ + "cmp $0, %edi\n" /* Check if return value is 0 */ + "jne _exit\n" /* If not, jump to _exit */ + + "pop %r9\n" + "pop %r8\n" + "pop %rcx\n" + "pop %rdx\n" + "pop %rsi\n" + "pop %rdi\n" + + "call main\n" /* Call _dl_main */ + "movl %eax, %edi\n" /* Move return value to edi */ + "call _exit\n"); /* Call _exit */ +} + +__attribute__((no_stack_protector)) _Noreturn void _exit(int status) +{ + __fini_print_buffer(); + call_exit(status); + /* At this point, the program *SHOULD* have exited. */ + __asm__("ud2\n"); + __builtin_unreachable(); +} + +__attribute__((no_stack_protector)) _Noreturn void _Exit(int status) { _exit(status); } diff --git a/Userspace/libc/runtime/Makefile b/Userspace/libc/runtime/Makefile index 3c8885d1..cc89b7db 100644 --- a/Userspace/libc/runtime/Makefile +++ b/Userspace/libc/runtime/Makefile @@ -1,20 +1,10 @@ -# Config file -include ../../../config.mk +default: + $(error Do not run this Makefile directly!) -CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as -NASM = /usr/bin/nasm - -C_SOURCES = $(shell find ./ -type f -name '*.c') S_SOURCES = $(shell find ./ -type f -name '*.S') -ASM_SOURCES = $(shell find ./ -type f -name '*.asm') -OBJ = ${C_SOURCES:.c=.o} ${ASM_SOURCES:.asm=.o} ${S_SOURCES:.S=.o} - -ifeq ($(OSARCH), amd64) -ASM_ARCH := elf64 -else ifeq ($(OSARCH), i386) -ASM_ARCH := elf32 -endif +C_SOURCES = $(shell find ./ -type f -name '*.c') +CXX_SOURCES = $(shell find ./ -type f -name '*.cpp') +OBJ = ${S_SOURCES:.S=.o} ${C_SOURCES:.c=.o} ${CXX_SOURCES:.cpp=.o} CRTBEGIN_PATH = $(shell $(CC) -print-file-name=crtbegin.o) CRTEND_PATH = $(shell $(CC) -print-file-name=crtend.o) @@ -22,20 +12,16 @@ CRTI_PATH = $(shell $(CC) -print-file-name=crti.o) CRTN_PATH = $(shell $(CC) -print-file-name=crtn.o) build: $(OBJ) - mv $^ ../../out/lib/ - cp $(CRTBEGIN_PATH) $(CRTEND_PATH) $(CRTI_PATH) $(CRTN_PATH) ../../out/lib/ + cp $^ ../../out/lib/ + cp $(CRTBEGIN_PATH) $(CRTEND_PATH) $(CRTI_PATH) $(CRTN_PATH) $(WORKSPACE_DIR)/out/lib/ %.o: %.c $(info Compiling $<) - $(CC) -nostdlib -mno-red-zone -std=c17 -c $< -o $@ - -%.o: %.asm - $(info Compiling $<) - $(NASM) $< -f $(ASM_ARCH) -o $@ + $(CC) -nostdlib -mno-red-zone -std=c17 -DLIBC_GIT_COMMIT='"$(shell git rev-parse HEAD)"' -c $< -o $@ %.o: %.S $(info Compiling $<) $(AS) -c $< -o $@ clean: - + rm -f $(OBJ) diff --git a/Userspace/libc/runtime/Scrt1.c b/Userspace/libc/runtime/Scrt1.c new file mode 100644 index 00000000..671767b1 --- /dev/null +++ b/Userspace/libc/runtime/Scrt1.c @@ -0,0 +1,100 @@ +/* + This file is part of Fennix Userspace. + + Fennix Userspace is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Userspace is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Userspace. If not, see . +*/ + +typedef void (*fct)(void); +#define asm __asm__ __volatile__ + +extern void (*__preinit_array_start[])(void) __attribute__((weak)); +extern void (*__preinit_array_end[])(void) __attribute__((weak)); +extern void (*__init_array_start[])(void) __attribute__((weak)); +extern void (*__init_array_end[])(void) __attribute__((weak)); +extern void (*__fini_array_start[])(void) __attribute__((weak)); +extern void (*__fini_array_end[])(void) __attribute__((weak)); + +void __crt_init_array(void) +{ + for (fct *func = __init_array_start; func != __init_array_end; func++) + (*func)(); +} + +void __crt_fini_array(void) +{ + for (fct *func = __fini_array_start; func != __fini_array_end; func++) + (*func)(); +} + +__attribute__((naked, used, no_stack_protector, section(".text"))) void _start() +{ + asm("movq $0, %rbp\n" + "pushq %rbp\n" + "pushq %rbp\n" + "movq %rsp, %rbp\n" + + "pushq %rcx\n" + "pushq %rdx\n" + "pushq %rsi\n" + "pushq %rdi\n" + + "call __libc_init\n" + "call __crt_init_array\n" + + "popq %rdi\n" + "popq %rsi\n" + "popq %rdx\n" + "popq %rcx\n" + + "call main\n" + + "pushq %rax\n" + "call __crt_fini_array\n" + "popq %rax\n" + + "movl %eax, %edi\n" + "call _exit\n"); +} + +/* These are declared in GNU ld */ +enum +{ + NT_FNX_ABI_TAG = 1, + NT_FNX_VERSION = 2, + NT_FNX_BUILD_ID = 3, + NT_FNX_ARCH = 4 +}; + +typedef struct Elf_Nhdr +{ + __UINT32_TYPE__ n_namesz; + __UINT32_TYPE__ n_descsz; + __UINT32_TYPE__ n_type; + char n_name[]; +} __attribute__((packed)) Elf_Nhdr; + +const struct +{ + Elf_Nhdr header; + char name[4]; + __UINT32_TYPE__ desc[4]; +} __abi_tag __attribute__((aligned(4), section(".note.ABI-tag"))) = { + .header = { + .n_namesz = 4, /* "FNX" + '\0' */ + .n_descsz = sizeof(__UINT32_TYPE__) * 4, /* Description Size */ + .n_type = NT_FNX_ABI_TAG, /* Type */ + }, + .name = "FNX", + .desc = {0, 0, 0, 0}, +}; diff --git a/Userspace/libc/runtime/crt0.c b/Userspace/libc/runtime/crt0.c deleted file mode 100644 index f9e599a3..00000000 --- a/Userspace/libc/runtime/crt0.c +++ /dev/null @@ -1,21 +0,0 @@ -// https://wiki.osdev.org/Creating_a_C_Library -#define asm __asm__ __volatile__ -__attribute__((naked, used, no_stack_protector, section(".text"))) void _start() -{ - asm("movq $0, %rbp\n"); - asm("pushq %rbp\n"); - asm("pushq %rbp\n"); - asm("movq %rsp, %rbp\n"); - asm("pushq %rcx\n" - "pushq %rdx\n" - "pushq %rsi\n" - "pushq %rdi\n"); - asm("call __libc_init\n"); - asm("popq %rdi\n" - "popq %rsi\n" - "popq %rdx\n" - "popq %rcx\n"); - asm("call main\n"); - asm("movl %eax, %edi\n"); - asm("call _exit\n"); -} diff --git a/Userspace/libc/runtime/crt1.c b/Userspace/libc/runtime/crt1.c index f9e599a3..671767b1 100644 --- a/Userspace/libc/runtime/crt1.c +++ b/Userspace/libc/runtime/crt1.c @@ -1,21 +1,100 @@ -// https://wiki.osdev.org/Creating_a_C_Library +/* + This file is part of Fennix Userspace. + + Fennix Userspace is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Userspace is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Userspace. If not, see . +*/ + +typedef void (*fct)(void); #define asm __asm__ __volatile__ + +extern void (*__preinit_array_start[])(void) __attribute__((weak)); +extern void (*__preinit_array_end[])(void) __attribute__((weak)); +extern void (*__init_array_start[])(void) __attribute__((weak)); +extern void (*__init_array_end[])(void) __attribute__((weak)); +extern void (*__fini_array_start[])(void) __attribute__((weak)); +extern void (*__fini_array_end[])(void) __attribute__((weak)); + +void __crt_init_array(void) +{ + for (fct *func = __init_array_start; func != __init_array_end; func++) + (*func)(); +} + +void __crt_fini_array(void) +{ + for (fct *func = __fini_array_start; func != __fini_array_end; func++) + (*func)(); +} + __attribute__((naked, used, no_stack_protector, section(".text"))) void _start() { - asm("movq $0, %rbp\n"); - asm("pushq %rbp\n"); - asm("pushq %rbp\n"); - asm("movq %rsp, %rbp\n"); - asm("pushq %rcx\n" + asm("movq $0, %rbp\n" + "pushq %rbp\n" + "pushq %rbp\n" + "movq %rsp, %rbp\n" + + "pushq %rcx\n" "pushq %rdx\n" "pushq %rsi\n" - "pushq %rdi\n"); - asm("call __libc_init\n"); - asm("popq %rdi\n" + "pushq %rdi\n" + + "call __libc_init\n" + "call __crt_init_array\n" + + "popq %rdi\n" "popq %rsi\n" "popq %rdx\n" - "popq %rcx\n"); - asm("call main\n"); - asm("movl %eax, %edi\n"); - asm("call _exit\n"); + "popq %rcx\n" + + "call main\n" + + "pushq %rax\n" + "call __crt_fini_array\n" + "popq %rax\n" + + "movl %eax, %edi\n" + "call _exit\n"); } + +/* These are declared in GNU ld */ +enum +{ + NT_FNX_ABI_TAG = 1, + NT_FNX_VERSION = 2, + NT_FNX_BUILD_ID = 3, + NT_FNX_ARCH = 4 +}; + +typedef struct Elf_Nhdr +{ + __UINT32_TYPE__ n_namesz; + __UINT32_TYPE__ n_descsz; + __UINT32_TYPE__ n_type; + char n_name[]; +} __attribute__((packed)) Elf_Nhdr; + +const struct +{ + Elf_Nhdr header; + char name[4]; + __UINT32_TYPE__ desc[4]; +} __abi_tag __attribute__((aligned(4), section(".note.ABI-tag"))) = { + .header = { + .n_namesz = 4, /* "FNX" + '\0' */ + .n_descsz = sizeof(__UINT32_TYPE__) * 4, /* Description Size */ + .n_type = NT_FNX_ABI_TAG, /* Type */ + }, + .name = "FNX", + .desc = {0, 0, 0, 0}, +}; diff --git a/Userspace/libc/src/InitArray.c b/Userspace/libc/src/InitArray.c deleted file mode 100644 index eea6ad82..00000000 --- a/Userspace/libc/src/InitArray.c +++ /dev/null @@ -1,22 +0,0 @@ -// extern void (*__preinit_array_start[])(void) __attribute__((weak)); -// extern void (*__preinit_array_end[])(void) __attribute__((weak)); -// extern void (*__init_array_start[])(void) __attribute__((weak)); -// extern void (*__init_array_end[])(void) __attribute__((weak)); -// extern void (*__fini_array_start []) (void) __attribute__((weak)); -// extern void (*__fini_array_end []) (void) __attribute__((weak)); - -typedef void (*fct)(void); -extern fct __init_array_start[0], __init_array_end[0]; -extern fct __fini_array_start[0], __fini_array_end[0]; - -void __libc_init_array(void) -{ - for (fct *func = __init_array_start; func != __init_array_end; func++) - (*func)(); -} - -void __libc_fini_array(void) -{ - for (fct *func = __fini_array_start; func != __fini_array_end; func++) - (*func)(); -} diff --git a/Userspace/libc/src/Lock.cpp b/Userspace/libc/src/Lock.cpp deleted file mode 100644 index acd39335..00000000 --- a/Userspace/libc/src/Lock.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "lock.hpp" -#include - -void LockClass::DeadLock(SpinLockData Lock) -{ - fprintf(stdout, "Potential deadlock in lock '%s' held by '%s'! %ld locks in queue.", - Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count); -} - -int LockClass::Lock(const char *FunctionName) -{ - LockData.AttemptingToGet = FunctionName; -Retry: - unsigned int i = 0; - while (__atomic_exchange_n(&IsLocked, true, __ATOMIC_ACQUIRE) && ++i < 0x10000000) - ; - if (i >= 0x10000000) - { - DeadLock(LockData); - goto Retry; - } - LockData.Count++; - LockData.CurrentHolder = FunctionName; - __sync_synchronize(); - return 0; -} - -int LockClass::Unlock() -{ - __sync_synchronize(); - __atomic_store_n(&IsLocked, false, __ATOMIC_RELEASE); - LockData.Count--; - IsLocked = false; - return 0; -} diff --git a/Userspace/libc/src/Makefile b/Userspace/libc/src/Makefile index d798f690..9ce59780 100644 --- a/Userspace/libc/src/Makefile +++ b/Userspace/libc/src/Makefile @@ -1,47 +1,36 @@ -# Config file -include ../../../config.mk +default: + $(error Do not run this Makefile directly!) -NAME=c +DYNAMIC_NAME := libc.so +STATIC_NAME := libc.a -ifeq ($(USERSPACE_STATIC_LIBS), 1) -OBJECT_NAME := lib$(NAME).a -else -OBJECT_NAME := lib$(NAME).so -endif +OUTPUT_DIR=$(WORKSPACE_DIR)/out/lib/ +SYSROOT = --sysroot=$(WORKSPACE_DIR)/out/ -OUTPUT_DIR=../../out/lib/ -SYSROOT = --sysroot=../../out/ - -CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as -AR = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ar - -C_SOURCES = $(shell find ./ -type f -name '*.c') -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') S_SOURCES = $(shell find ./ -type f -name '*.S') -OBJ = ${C_SOURCES:.c=.o} ${CPP_SOURCES:.cpp=.o} ${S_SOURCES:.S=.o} +C_SOURCES = $(shell find ./ -type f -name '*.c') +CXX_SOURCES = $(shell find ./ -type f -name '*.cpp') +OBJ = ${S_SOURCES:.S=.o} ${C_SOURCES:.c=.o} ${CXX_SOURCES:.cpp=.o} -ifeq ($(OSARCH), amd64) -ASM_ARCH := elf64 -else ifeq ($(OSARCH), i386) -ASM_ARCH := elf32 -endif - -CFLAGS := -fvisibility=hidden -fPIC -I../include -I../../out/include +CFLAGS := -fvisibility=hidden -fPIC -I../include -I$(WORKSPACE_DIR)/out/include -DLIBC_GIT_COMMIT='"$(shell git rev-parse HEAD)"' ifeq ($(DEBUG), 1) CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm endif -build: $(OBJECT_NAME) +build: $(DYNAMIC_NAME) $(STATIC_NAME) -$(OBJECT_NAME): $(OBJ) +.PHONY: $(DYNAMIC_NAME) $(STATIC_NAME) + +$(DYNAMIC_NAME): $(OBJ) $(info Linking $@) -ifeq ($(USERSPACE_STATIC_LIBS), 1) - $(AR) rcs $(OUTPUT_DIR)$@ $(OBJ) -else - $(CC) -nostdlib -shared -fPIC -fPIE -Wl,-soname,$(OBJECT_NAME) $(SYSROOT) $(OBJ) -o $(OUTPUT_DIR)$@ -endif + $(CC) -nostdlib -shared -fPIC -fPIE -e _start -Wl,-soname,$(DYNAMIC_NAME) $(SYSROOT) $(OBJ) -o $(DYNAMIC_NAME) + cp $(DYNAMIC_NAME) $(OUTPUT_DIR)$(DYNAMIC_NAME) + +$(STATIC_NAME): $(OBJ) + $(info Linking $@) + $(AR) -rcs $(STATIC_NAME) $(OBJ) + cp $(STATIC_NAME) $(OUTPUT_DIR)$(STATIC_NAME) %.o: %.c $(info Compiling $<) @@ -56,4 +45,4 @@ endif $(AS) -c $< -o $@ clean: - rm -f $(OBJ) + rm -f $(OBJ) $(DYNAMIC_NAME) $(STATIC_NAME) diff --git a/Userspace/libc/src/Runtime.c b/Userspace/libc/src/Runtime.c deleted file mode 100644 index c213f4cc..00000000 --- a/Userspace/libc/src/Runtime.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include // For PUBLIC - -extern void __libc_init_array(void); -extern void __libc_fini_array(void); - -extern void __libc_init_std(void); -extern void __libc_fini_std(void); - -PUBLIC void __libc_init(void) -{ - __libc_init_array(); - __libc_init_std(); -} - -PUBLIC void _exit(int Code) -{ - __libc_fini_std(); - __libc_fini_array(); - syscall1(sc_exit, (long)Code); - while (1) - ; -} diff --git a/Userspace/libc/src/dlfcn.c b/Userspace/libc/src/dlfcn.c deleted file mode 100644 index 7ac6d074..00000000 --- a/Userspace/libc/src/dlfcn.c +++ /dev/null @@ -1,23 +0,0 @@ -#include - -static char *error = "Not implemented"; - -__attribute__((weak)) void *dlopen(const char *filename, int flags) -{ - return NULL; -} - -__attribute__((weak)) void *dlsym(void *handle, const char *symbol) -{ - return NULL; -} - -__attribute__((weak)) int dlclose(void *handle) -{ - return -1; -} - -__attribute__((weak)) char *dlerror(void) -{ - return error; -} diff --git a/Userspace/libc/src/init.c b/Userspace/libc/src/init.c new file mode 100644 index 00000000..03bf04bb --- /dev/null +++ b/Userspace/libc/src/init.c @@ -0,0 +1,34 @@ +/* + This file is part of Fennix Userspace. + + Fennix Userspace is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Userspace is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Userspace. If not, see . +*/ + +#include + +int __init_pthread(void); +void __init_stdio(void); + +__attribute__((visibility("default"))) void __libc_init(void) +{ + __init_pthread(); + __init_stdio(); +} + +__attribute__((visibility("default"))) void _exit(int Code) +{ + call_exit(Code); + while (1) + ; +} diff --git a/Userspace/libc/src/lock.hpp b/Userspace/libc/src/lock.hpp deleted file mode 100644 index a364ef49..00000000 --- a/Userspace/libc/src/lock.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef __FENNIX_KERNEL_LOCK_H__ -#define __FENNIX_KERNEL_LOCK_H__ - -#include - -#ifdef __cplusplus -/** @brief Please use this macro to create a new lock. */ -class LockClass -{ - struct SpinLockData - { - uint64_t LockData = 0x0; - const char *CurrentHolder = "(nul)"; - const char *AttemptingToGet = "(nul)"; - uint64_t Count = 0; - }; - void DeadLock(SpinLockData Lock); - -private: - SpinLockData LockData; - bool IsLocked = false; - -public: - SpinLockData *GetLockData() { return &LockData; } - int Lock(const char *FunctionName); - int Unlock(); -}; - -/** @brief Please use this macro to create a new smart lock. */ -class SmartLockClass -{ -private: - LockClass *LockPointer = nullptr; - -public: - SmartLockClass(LockClass &Lock, const char *FunctionName) - { - this->LockPointer = &Lock; - this->LockPointer->Lock(FunctionName); - } - ~SmartLockClass() { this->LockPointer->Unlock(); } -}; - -/** @brief Create a new lock (can be used with SmartCriticalSection). */ -#define NewLock(Name) LockClass Name -/** @brief Simple lock that is automatically released when the scope ends. */ -#define SmartLock(LockClassName) SmartLockClass CONCAT(lock##_, __COUNTER__)(LockClassName, __FUNCTION__) - -#endif // __cplusplus -#endif // !__FENNIX_KERNEL_LOCK_H__ diff --git a/Userspace/libc/src/main.c b/Userspace/libc/src/main.c index a0642684..47d12bd0 100644 --- a/Userspace/libc/src/main.c +++ b/Userspace/libc/src/main.c @@ -1,5 +1,152 @@ -/* TODO: Show a message or something */ -int _start() +/* + This file is part of Fennix Userspace. + + Fennix Userspace is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Userspace is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Userspace. If not, see . +*/ + +typedef void (*fct)(void); +#define asm __asm__ __volatile__ +__attribute__((__visibility__("hidden"))) extern void (*__preinit_array_start[])(void) __attribute__((weak)); +__attribute__((__visibility__("hidden"))) extern void (*__preinit_array_end[])(void) __attribute__((weak)); +__attribute__((__visibility__("hidden"))) extern void (*__init_array_start[])(void) __attribute__((weak)); +__attribute__((__visibility__("hidden"))) extern void (*__init_array_end[])(void) __attribute__((weak)); +__attribute__((__visibility__("hidden"))) extern void (*__fini_array_start[])(void) __attribute__((weak)); +__attribute__((__visibility__("hidden"))) extern void (*__fini_array_end[])(void) __attribute__((weak)); +const char __interp[] __attribute__((section(".interp"))) = "/lib/ld.so"; + +#ifndef LIBC_GIT_COMMIT +#define LIBC_GIT_COMMIT "0000000000000000000000000000000000000000" +#endif + +#define HEX_DIGIT(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'a' + 10)) +#define CONVERT_TO_BYTE(h, l) ((HEX_DIGIT(h) << 4) | HEX_DIGIT(l)) +#define HASH_BYTES(hex) \ + {CONVERT_TO_BYTE(hex[0], hex[1]), \ + CONVERT_TO_BYTE(hex[2], hex[3]), \ + CONVERT_TO_BYTE(hex[4], hex[5]), \ + CONVERT_TO_BYTE(hex[6], hex[7]), \ + CONVERT_TO_BYTE(hex[8], hex[9]), \ + CONVERT_TO_BYTE(hex[10], hex[11]), \ + CONVERT_TO_BYTE(hex[12], hex[13]), \ + CONVERT_TO_BYTE(hex[14], hex[15]), \ + CONVERT_TO_BYTE(hex[16], hex[17]), \ + CONVERT_TO_BYTE(hex[18], hex[19]), \ + CONVERT_TO_BYTE(hex[20], hex[21]), \ + CONVERT_TO_BYTE(hex[22], hex[23]), \ + CONVERT_TO_BYTE(hex[24], hex[25]), \ + CONVERT_TO_BYTE(hex[26], hex[27]), \ + CONVERT_TO_BYTE(hex[28], hex[29]), \ + CONVERT_TO_BYTE(hex[30], hex[31]), \ + CONVERT_TO_BYTE(hex[32], hex[33]), \ + CONVERT_TO_BYTE(hex[34], hex[35]), \ + CONVERT_TO_BYTE(hex[36], hex[37]), \ + CONVERT_TO_BYTE(hex[38], hex[39])} + +/* These are declared in GNU ld */ +enum { + NT_FNX_ABI_TAG = 1, + NT_FNX_VERSION = 2, + NT_FNX_BUILD_ID = 3, + NT_FNX_ARCH = 4 +}; + +typedef struct Elf_Nhdr +{ + __UINT32_TYPE__ n_namesz; + __UINT32_TYPE__ n_descsz; + __UINT32_TYPE__ n_type; + char n_name[]; +} __attribute__((packed)) Elf_Nhdr; + +const struct +{ + Elf_Nhdr header; + char name[4]; + __UINT32_TYPE__ desc[4]; +} __abi_tag __attribute__((aligned(4), section(".note.ABI-tag"))) = { + .header = { + .n_namesz = 4, /* "FNX" + '\0' */ + .n_descsz = sizeof(__UINT32_TYPE__) * 4, /* Description Size */ + .n_type = NT_FNX_ABI_TAG, /* Type */ + }, + .name = "FNX", + .desc = {0, 0, 0, 0}, +}; + +const struct +{ + Elf_Nhdr header; + char name[4]; + __UINT8_TYPE__ desc[20]; +} __build_id __attribute__((aligned(4), section(".note.build-id"))) = { + .header = { + .n_namesz = 4, /* "FNX" + '\0' */ + .n_descsz = sizeof(__UINT8_TYPE__) * 20, /* Description Size */ + .n_type = NT_FNX_BUILD_ID, /* Type */ + }, + .name = "FNX", + .desc = HASH_BYTES(LIBC_GIT_COMMIT), +}; + +void __crt_init_array(void) +{ + for (fct *func = __init_array_start; func != __init_array_end; func++) + (*func)(); +} + +void __crt_fini_array(void) +{ + for (fct *func = __fini_array_start; func != __fini_array_end; func++) + (*func)(); +} + +__attribute__((naked, used, no_stack_protector, section(".text"))) void _start() +{ + asm("movq $0, %rbp\n" + "pushq %rbp\n" + "pushq %rbp\n" + "movq %rsp, %rbp\n" + + "pushq %rcx\n" + "pushq %rdx\n" + "pushq %rsi\n" + "pushq %rdi\n" + + "call __libc_init\n" + "call __crt_init_array\n" + + "popq %rdi\n" + "popq %rsi\n" + "popq %rdx\n" + "popq %rcx\n" + + "call main\n" + + "pushq %rax\n" + "call __crt_fini_array\n" + "popq %rax\n" + + "movl %eax, %edi\n" + "call _exit\n"); +} + +int main(int argc, char *argv[], char *envp[]) +{ + (void)argc; + (void)argv; + (void)envp; + /* FIXME: show printf license notice and some help and commands? */ return -1; } diff --git a/Userspace/libc/src/mem/LICENSE b/Userspace/libc/src/mem/LICENSE new file mode 100644 index 00000000..63d14cd2 --- /dev/null +++ b/Userspace/libc/src/mem/LICENSE @@ -0,0 +1,10 @@ + +This code is released into the public domain. Use this code at your own +risk. Feel free to use it for whatever purpose you want. I take no responsibilty or +whatever if anything goes wrong. Use it at your own risk. + +If you have any fixes or patches, please email me. + +Durand Miller + + diff --git a/Userspace/libc/src/mem/LibAllocImplementation.cpp b/Userspace/libc/src/mem/LibAllocImplementation.cpp deleted file mode 100644 index a9fe3818..00000000 --- a/Userspace/libc/src/mem/LibAllocImplementation.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include "../lock.hpp" - -#include "../../../../Kernel/syscalls.h" - -NewLock(liballocLock); - -extern "C" int liballoc_lock() -{ - return liballocLock.Lock(__FUNCTION__); -} - -extern "C" int liballoc_unlock() -{ - return liballocLock.Unlock(); -} - -extern "C" void *liballoc_alloc(size_t Pages) -{ - return (void *)syscall6(sc_mmap, NULL, - Pages * 0x1000, - sc_PROT_READ | sc_PROT_WRITE, - sc_MAP_ANONYMOUS | sc_MAP_PRIVATE, - -1, 0); -} - -extern "C" int liballoc_free(void *Address, size_t Pages) -{ - return syscall2(sc_munmap, (uintptr_t)Address, Pages * 0x1000); -} diff --git a/Userspace/libc/src/mem/liballoc.c b/Userspace/libc/src/mem/liballoc.c new file mode 100644 index 00000000..b3fde0d5 --- /dev/null +++ b/Userspace/libc/src/mem/liballoc.c @@ -0,0 +1,78 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +/* Taken directly from https://github.com/blanham/liballoc/blob/master/linux.c */ + +#include +#include +#include +#include +#include + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif +#if !defined(MAP_FAILED) +#define MAP_FAILED ((char *)-1) +#endif + +#ifndef MAP_NORESERVE +#ifdef MAP_AUTORESRV +#define MAP_NORESERVE MAP_AUTORESRV +#else +#define MAP_NORESERVE 0 +#endif +#endif + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static int page_size = -1; + +int liballoc_lock() +{ + pthread_mutex_lock(&mutex); + return 0; +} + +int liballoc_unlock() +{ + pthread_mutex_unlock(&mutex); + return 0; +} + +void *liballoc_alloc(int pages) +{ + if (page_size < 0) + page_size = getpagesize(); + unsigned int size = pages * page_size; + + char *p2 = (char *)mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0); + if (p2 == MAP_FAILED) + return NULL; + + if (mprotect(p2, size, PROT_READ | PROT_WRITE) != 0) + { + munmap(p2, size); + return NULL; + } + + return p2; +} + +int liballoc_free(void *ptr, int pages) +{ + return munmap(ptr, pages * page_size); +} diff --git a/Userspace/libc/src/mem/liballoc_1_1.c b/Userspace/libc/src/mem/liballoc_1_1.c index 55a8fd1b..d92529c9 100644 --- a/Userspace/libc/src/mem/liballoc_1_1.c +++ b/Userspace/libc/src/mem/liballoc_1_1.c @@ -43,17 +43,11 @@ #define LIBALLOC_MAGIC 0xc001c0de #define LIBALLOC_DEAD 0xdeaddead -// #define LIBALLOCDEBUG 1 -#define LIBALLOCINFO 1 - -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO #include #include -// #include #define FLUSH() fflush(stdout) -#define atexit(x) atexit(x) -// #define printf(m, ...) trace(m, ##__VA_ARGS__) #endif @@ -131,10 +125,10 @@ static void *liballoc_memcpy(void *s1, const void *s2, size_t n) return s1; } -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO static void liballoc_dump() { -#ifdef LIBALLOCDEBUG +#ifdef DEBUG struct liballoc_major *maj = l_memRoot; struct liballoc_minor *min = NULL; #endif @@ -146,7 +140,7 @@ static void liballoc_dump() printf("liballoc: Error count: %i\n", l_errorCount); printf("liballoc: Possible overruns: %i\n", l_possibleOverruns); -#ifdef LIBALLOCDEBUG +#ifdef DEBUG while (maj != NULL) { printf("liballoc: %x: total = %i, used = %i\n", @@ -198,7 +192,7 @@ static struct liballoc_major *allocate_new_page(unsigned int size) if (maj == NULL) { l_warningCount += 1; -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: WARNING: liballoc_alloc( %i ) return NULL\n", st); FLUSH(); #endif @@ -214,7 +208,7 @@ static struct liballoc_major *allocate_new_page(unsigned int size) l_allocated += maj->size; -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("liballoc: Resource allocated %x of %i pages (%i bytes) for %i size.\n", maj, st, maj->size, size); printf("liballoc: Total memory usage = %i KB\n", (int)((l_allocated / (1024)))); @@ -248,7 +242,7 @@ void *PREFIX(malloc)(size_t req_size) if (size == 0) { l_warningCount += 1; -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: WARNING: alloc( 0 ) called from %x\n", __builtin_return_address(0)); FLUSH(); @@ -259,8 +253,8 @@ void *PREFIX(malloc)(size_t req_size) if (l_memRoot == NULL) { -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO -#ifdef LIBALLOCDEBUG +#if defined DEBUG || defined INFO +#ifdef DEBUG printf("liballoc: initialization of liballoc " VERSION "\n"); #endif atexit(liballoc_dump); @@ -272,20 +266,20 @@ void *PREFIX(malloc)(size_t req_size) if (l_memRoot == NULL) { liballoc_unlock(); -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("liballoc: initial l_memRoot initialization failed\n", p); FLUSH(); #endif return NULL; } -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("liballoc: set up first memory major %x\n", l_memRoot); FLUSH(); #endif } -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("liballoc: %x PREFIX(malloc)( %i ): ", __builtin_return_address(0), size); @@ -326,7 +320,7 @@ void *PREFIX(malloc)(size_t req_size) // CASE 1: There is not enough space in this major block. if (diff < (size + sizeof(struct liballoc_minor))) { -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("CASE 1: Insufficient space in block %x\n", maj); FLUSH(); #endif @@ -378,7 +372,7 @@ void *PREFIX(malloc)(size_t req_size) ALIGN(p); -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("CASE 2: returning %x\n", p); FLUSH(); #endif @@ -414,7 +408,7 @@ void *PREFIX(malloc)(size_t req_size) p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor)); ALIGN(p); -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("CASE 3: returning %x\n", p); FLUSH(); #endif @@ -460,7 +454,7 @@ void *PREFIX(malloc)(size_t req_size) p = (void *)((uintptr_t)min + sizeof(struct liballoc_minor)); ALIGN(p); -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("CASE 4.1: returning %x\n", p); FLUSH(); #endif @@ -499,7 +493,7 @@ void *PREFIX(malloc)(size_t req_size) p = (void *)((uintptr_t)new_min + sizeof(struct liballoc_minor)); ALIGN(p); -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("CASE 4.2: returning %x\n", p); FLUSH(); #endif @@ -519,7 +513,7 @@ void *PREFIX(malloc)(size_t req_size) // CASE 5: Block full! Ensure next block and loop. if (maj->next == NULL) { -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("CASE 5: block full\n"); FLUSH(); #endif @@ -545,11 +539,11 @@ void *PREFIX(malloc)(size_t req_size) liballoc_unlock(); // release the lock -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("All cases exhausted. No memory available.\n"); FLUSH(); #endif -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n", size); liballoc_dump(); FLUSH(); @@ -565,7 +559,7 @@ void PREFIX(free)(void *ptr) if (ptr == NULL) { l_warningCount += 1; -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: WARNING: PREFIX(free)( NULL ) called from %x\n", __builtin_return_address(0)); FLUSH(); @@ -590,7 +584,7 @@ void PREFIX(free)(void *ptr) ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) { l_possibleOverruns += 1; -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %x != %x\n", min->magic, LIBALLOC_MAGIC); @@ -600,7 +594,7 @@ void PREFIX(free)(void *ptr) if (min->magic == LIBALLOC_DEAD) { -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %x from %x.\n", ptr, __builtin_return_address(0)); @@ -609,7 +603,7 @@ void PREFIX(free)(void *ptr) } else { -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called from %x\n", ptr, __builtin_return_address(0)); @@ -622,7 +616,7 @@ void PREFIX(free)(void *ptr) return; } -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("liballoc: %x PREFIX(free)( %x ): ", __builtin_return_address(0), ptr); @@ -674,7 +668,7 @@ void PREFIX(free)(void *ptr) } } -#ifdef LIBALLOCDEBUG +#ifdef DEBUG printf("OK\n"); FLUSH(); #endif @@ -734,7 +728,7 @@ void *PREFIX(realloc)(void *p, size_t size) ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) { l_possibleOverruns += 1; -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %x != %x\n", min->magic, LIBALLOC_MAGIC); @@ -744,7 +738,7 @@ void *PREFIX(realloc)(void *p, size_t size) if (min->magic == LIBALLOC_DEAD) { -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %x from %x.\n", ptr, __builtin_return_address(0)); @@ -753,7 +747,7 @@ void *PREFIX(realloc)(void *p, size_t size) } else { -#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#if defined DEBUG || defined INFO printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called from %x\n", ptr, __builtin_return_address(0)); diff --git a/Userspace/libc/src/mem/liballoc_1_1.h b/Userspace/libc/src/mem/liballoc_1_1.h index f7099d04..8f4eb8ee 100644 --- a/Userspace/libc/src/mem/liballoc_1_1.h +++ b/Userspace/libc/src/mem/liballoc_1_1.h @@ -1,7 +1,9 @@ #ifndef _LIBALLOC_H #define _LIBALLOC_H -#include +#include +#include +#include /** \defgroup ALLOCHOOKS liballoc hooks * @@ -17,6 +19,7 @@ // typedef unsigned long uintptr_t; // This lets you prefix malloc and friends +// #define PREFIX(func) k##func #define PREFIX(func) __##func #ifdef __cplusplus diff --git a/Userspace/libc/src/print/LICENSE b/Userspace/libc/src/print/LICENSE new file mode 100644 index 00000000..46ecf47e --- /dev/null +++ b/Userspace/libc/src/print/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2014 Marco Paland +Copyright (c) 2021 Eyal Rozenberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Userspace/libc/src/print/print.c b/Userspace/libc/src/print/print.c new file mode 100644 index 00000000..7de3662c --- /dev/null +++ b/Userspace/libc/src/print/print.c @@ -0,0 +1,20 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include + +void putchar_(char c) { putc(c, stdout); } diff --git a/Userspace/libc/src/print/printf.c b/Userspace/libc/src/print/printf.c index 95e8e02d..9d1b8e73 100644 --- a/Userspace/libc/src/print/printf.c +++ b/Userspace/libc/src/print/printf.c @@ -1,6 +1,6 @@ /** * @author (c) Eyal Rozenberg - * 2021-2022, Haifa, Palestine/Israel + * 2021-2023, Haifa, Palestine/Israel * @author (c) Marco Paland (info@paland.com) * 2014-2019, PALANDesign Hannover, Germany * @@ -11,7 +11,7 @@ * * @brief Small stand-alone implementation of the printf family of functions * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with - * a very limited resources. + * limited resources. * * @note the implementations are thread-safe; re-entrant; use no functions from * the standard library; and do not dynamically allocate any memory. @@ -49,11 +49,12 @@ #include #include #else -#include +#include #include +#include #endif // __cplusplus -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD #define printf_ printf #define sprintf_ sprintf #define vsprintf_ vsprintf @@ -95,6 +96,11 @@ #define PRINTF_DEFAULT_FLOAT_PRECISION 6 #endif +// Default choice of type to use for internal floating-point computations +#ifndef PRINTF_USE_DOUBLE_INTERNALLY +#define PRINTF_USE_DOUBLE_INTERNALLY 1 +#endif + // According to the C languages standard, printf() and related functions must be able to print any // integral number in floating-point notation, regardless of length, when using the %f specifier - // possibly hundreds of characters, potentially overflowing your buffers. In this implementation, @@ -104,7 +110,7 @@ #endif // Support for the long long integral types (with the ll, z and t length modifiers for specifiers -// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. +// %d,%i,%o,%x,%X,%u, and with the %p specifier). #ifndef PRINTF_SUPPORT_LONG_LONG #define PRINTF_SUPPORT_LONG_LONG 1 #endif @@ -134,7 +140,7 @@ // The following will convert the number-of-digits into an exponential-notation literal #define PRINTF_CONCATENATE(s1, s2) s1##s2 #define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) -#define PRINTF_FLOAT_NOTATION_THRESHOLD PRINTF_EXPAND_THEN_CONCATENATE(1e, PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL) +#define PRINTF_FLOAT_NOTATION_THRESHOLD ((floating_point_t)PRINTF_EXPAND_THEN_CONCATENATE(1e, PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) // internal flag definitions #define FLAGS_ZEROPAD (1U << 0U) @@ -154,6 +160,7 @@ #define FLAGS_POINTER (1U << 13U) // Note: Similar, but not identical, effect as FLAGS_HASH #define FLAGS_SIGNED (1U << 14U) +#define FLAGS_LONG_DOUBLE (1U << 15U) // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS #ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS @@ -232,60 +239,83 @@ typedef unsigned int printf_size_t; #error "Non-binary-radix floating-point types are unsupported." #endif -#if DBL_MANT_DIG == 24 +/** + * This library supports taking float-point arguments up to and including + * long double's; but - it currently does _not_ support internal + * representation and manipulation of values as long doubles; the options + * are either single-precision `float` or double-precision `double`. + */ +#if PRINTF_USE_DOUBLE_INTERNALLY +typedef double floating_point_t; +#define FP_TYPE_MANT_DIG DBL_MANT_DIG +#else +typedef float floating_point_t; +#define FP_TYPE_MANT_DIG FLT_MANT_DIG +#endif -#define DOUBLE_SIZE_IN_BITS 32 -typedef uint32_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0xFFU -#define DOUBLE_BASE_EXPONENT 127 -#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 -#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-38 +#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 -#elif DBL_MANT_DIG == 53 +#if FP_TYPE_MANT_DIG == 24 -#define DOUBLE_SIZE_IN_BITS 64 -typedef uint64_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0x7FFU -#define DOUBLE_BASE_EXPONENT 1023 -#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 -#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-308 +typedef uint32_t printf_fp_uint_t; +#define FP_TYPE_SIZE_IN_BITS 32 +#define FP_TYPE_EXPONENT_MASK 0xFFU +#define FP_TYPE_BASE_EXPONENT 127 +#define FP_TYPE_MAX FLT_MAX +#define FP_TYPE_MAX_10_EXP FLT_MAX_10_EXP +#define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 +#define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-38f +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 10 + +#elif FP_TYPE_MANT_DIG == 53 + +typedef uint64_t printf_fp_uint_t; +#define FP_TYPE_SIZE_IN_BITS 64 +#define FP_TYPE_EXPONENT_MASK 0x7FFU +#define FP_TYPE_BASE_EXPONENT 1023 +#define FP_TYPE_MAX DBL_MAX +#define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP +#define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP +#define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 +#define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-308 +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T - 1 #else -#error "Unsupported double type configuration" +#error "Unsupported floating point type configuration" #endif -#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) +#define FP_TYPE_STORED_MANTISSA_BITS (FP_TYPE_MANT_DIG - 1) typedef union { - double_uint_t U; - double F; -} double_with_bit_access; + printf_fp_uint_t U; + floating_point_t F; +} floating_point_with_bit_access; // This is unnecessary in C99, since compound initializers can be used, // but: // 1. Some compilers are finicky about this; // 2. Some people may want to convert this to C89; // 3. If you try to use it as C++, only C++20 supports compound literals -static inline double_with_bit_access get_bit_access(double x) +static inline floating_point_with_bit_access get_bit_access(floating_point_t x) { - double_with_bit_access dwba; - dwba.F = x; - return dwba; + floating_point_with_bit_access dwba; + dwba.F = x; + return dwba; } -static inline int get_sign_bit(double x) +static inline int get_sign_bit(floating_point_t x) { - // The sign is stored in the highest bit - return (int)(get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1)); + // The sign is stored in the highest bit + return (int)(get_bit_access(x).U >> (FP_TYPE_SIZE_IN_BITS - 1)); } -static inline int get_exp2(double_with_bit_access x) +static inline int get_exp2(floating_point_with_bit_access x) { - // The exponent in an IEEE-754 floating-point number occupies a contiguous - // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An - // unsigned offset from some negative value (with the extremal offset values reserved for - // special use). - return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS) & DOUBLE_EXPONENT_MASK) - DOUBLE_BASE_EXPONENT; + // The exponent in an IEEE-754 floating-point number occupies a contiguous + // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An + // unsigned offset from some negative value (with the extremal offset values reserved for + // special use). + return (int)((x.U >> FP_TYPE_STORED_MANTISSA_BITS) & FP_TYPE_EXPONENT_MASK) - FP_TYPE_BASE_EXPONENT; } #define PRINTF_ABS(_x) ((_x) > 0 ? (_x) : -(_x)) @@ -306,11 +336,11 @@ static inline int get_exp2(double_with_bit_access x) // ... otherwise bad things will happen. typedef struct { - void (*function)(char c, void *extra_arg); - void *extra_function_arg; - char *buffer; - printf_size_t pos; - printf_size_t max_chars; + void (*function)(char c, void *extra_arg); + void *extra_function_arg; + char *buffer; + printf_size_t pos; + printf_size_t max_chars; } output_gadget_t; // Note: This function currently assumes it is not passed a '\0' c, @@ -319,84 +349,84 @@ typedef struct // assumes that the output gadget has been properly initialized. static inline void putchar_via_gadget(output_gadget_t *gadget, char c) { - printf_size_t write_pos = gadget->pos++; - // We're _always_ increasing pos, so as to count how may characters - // _would_ have been written if not for the max_chars limitation - if (write_pos >= gadget->max_chars) - { - return; - } - if (gadget->function != NULL) - { - // No check for c == '\0' . - gadget->function(c, gadget->extra_function_arg); - } - else - { - // it must be the case that gadget->buffer != NULL , due to the constraint - // on output_gadget_t ; and note we're relying on write_pos being non-negative. - gadget->buffer[write_pos] = c; - } + printf_size_t write_pos = gadget->pos++; + // We're _always_ increasing pos, so as to count how may characters + // _would_ have been written if not for the max_chars limitation + if (write_pos >= gadget->max_chars) + { + return; + } + if (gadget->function != NULL) + { + // No check for c == '\0' . + gadget->function(c, gadget->extra_function_arg); + } + else + { + // it must be the case that gadget->buffer != NULL , due to the constraint + // on output_gadget_t ; and note we're relying on write_pos being non-negative. + gadget->buffer[write_pos] = c; + } } // Possibly-write the string-terminating '\0' character static inline void append_termination_with_gadget(output_gadget_t *gadget) { - if (gadget->function != NULL || gadget->max_chars == 0) - { - return; - } - if (gadget->buffer == NULL) - { - return; - } - printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; - gadget->buffer[null_char_pos] = '\0'; + if (gadget->function != NULL || gadget->max_chars == 0) + { + return; + } + if (gadget->buffer == NULL) + { + return; + } + printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; + gadget->buffer[null_char_pos] = '\0'; } // We can't use putchar_ as is, since our output gadget // only takes pointers to functions with an extra argument static inline void putchar_wrapper(char c, void *unused) { - (void)unused; - putchar(c); + (void)unused; + putchar_(c); } static inline output_gadget_t discarding_gadget(void) { - output_gadget_t gadget; - gadget.function = NULL; - gadget.extra_function_arg = NULL; - gadget.buffer = NULL; - gadget.pos = 0; - gadget.max_chars = 0; - return gadget; + output_gadget_t gadget; + gadget.function = NULL; + gadget.extra_function_arg = NULL; + gadget.buffer = NULL; + gadget.pos = 0; + gadget.max_chars = 0; + return gadget; } static inline output_gadget_t buffer_gadget(char *buffer, size_t buffer_size) { - printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t)buffer_size; - output_gadget_t result = discarding_gadget(); - if (buffer != NULL) - { - result.buffer = buffer; - result.max_chars = usable_buffer_size; - } - return result; + printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t)buffer_size; + output_gadget_t result = discarding_gadget(); + if (buffer != NULL) + { + result.buffer = buffer; + result.max_chars = usable_buffer_size; + } + return result; } static inline output_gadget_t function_gadget(void (*function)(char, void *), void *extra_arg) { - output_gadget_t result = discarding_gadget(); - result.function = function; - result.extra_function_arg = extra_arg; - result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; - return result; + output_gadget_t result = discarding_gadget(); + result.function = function; + result.extra_function_arg = extra_arg; + result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; + return result; } static inline output_gadget_t extern_putchar_gadget(void) { - return function_gadget(putchar_wrapper, NULL); + return function_gadget(putchar_wrapper, NULL); } // internal secure strlen @@ -405,693 +435,719 @@ static inline output_gadget_t extern_putchar_gadget(void) // variables - hence the signature. static inline printf_size_t strnlen_s_(const char *str, printf_size_t maxsize) { - const char *s; - for (s = str; *s && maxsize--; ++s) - ; - return (printf_size_t)(s - str); + const char *s; + for (s = str; *s && maxsize--; ++s) + ; + return (printf_size_t)(s - str); } // internal test if char is a digit (0-9) // @return true if char is a digit static inline bool is_digit_(char ch) { - return (ch >= '0') && (ch <= '9'); + return (ch >= '0') && (ch <= '9'); } // internal ASCII string to printf_size_t conversion static printf_size_t atou_(const char **str) { - printf_size_t i = 0U; - while (is_digit_(**str)) - { - i = i * 10U + (printf_size_t)(*((*str)++) - '0'); - } - return i; + printf_size_t i = 0U; + while (is_digit_(**str)) + { + i = i * 10U + (printf_size_t)(*((*str)++) - '0'); + } + return i; } // output the specified string in reverse, taking care of any zero-padding static void out_rev_(output_gadget_t *output, const char *buf, printf_size_t len, printf_size_t width, printf_flags_t flags) { - const printf_size_t start_pos = output->pos; + const printf_size_t start_pos = output->pos; - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) - { - for (printf_size_t i = len; i < width; i++) - { - putchar_via_gadget(output, ' '); - } - } + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (printf_size_t i = len; i < width; i++) + { + putchar_via_gadget(output, ' '); + } + } - // reverse string - while (len) - { - putchar_via_gadget(output, buf[--len]); - } + // reverse string + while (len) + { + putchar_via_gadget(output, buf[--len]); + } - // append pad spaces up to given width - if (flags & FLAGS_LEFT) - { - while (output->pos - start_pos < width) - { - putchar_via_gadget(output, ' '); - } - } + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (output->pos - start_pos < width) + { + putchar_via_gadget(output, ' '); + } + } } // Invoked by print_integer after the actual number has been printed, performing necessary // work on the number's prefix (as the number is initially printed in reverse order) static void print_integer_finalization(output_gadget_t *output, char *buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) { - printf_size_t unpadded_len = len; + printf_size_t unpadded_len = len; - // pad with leading zeros - { - if (!(flags & FLAGS_LEFT)) - { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) - { - width--; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) - { - buf[len++] = '0'; - } - } + // pad with leading zeros + { + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } - while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) - { - buf[len++] = '0'; - } + while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = '0'; + } - if (base == BASE_OCTAL && (len > unpadded_len)) - { - // Since we've written some zeros, we've satisfied the alternative format leading space requirement - flags &= ~FLAGS_HASH; - } - } + if (base == BASE_OCTAL && (len > unpadded_len)) + { + // Since we've written some zeros, we've satisfied the alternative format leading space requirement + flags &= ~FLAGS_HASH; + } + } - // handle hash - if (flags & (FLAGS_HASH | FLAGS_POINTER)) - { - if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) - { - // Let's take back some padding digits to fit in what will eventually - // be the format-specific prefix - if (unpadded_len < len) - { - len--; // This should suffice for BASE_OCTAL - } - if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) - { - len--; // ... and an extra one for 0x or 0b - } - } - if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) - { - buf[len++] = 'x'; - } - else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) - { - buf[len++] = 'X'; - } - else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) - { - buf[len++] = 'b'; - } - if (len < PRINTF_INTEGER_BUFFER_SIZE) - { - buf[len++] = '0'; - } - } + // handle hash + if (flags & (FLAGS_HASH | FLAGS_POINTER)) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) + { + // Let's take back some padding digits to fit in what will eventually + // be the format-specific prefix + if (unpadded_len < len) + { + len--; // This should suffice for BASE_OCTAL + } + if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) + { + len--; // ... and an extra one for 0x or 0b + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_INTEGER_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } - if (len < PRINTF_INTEGER_BUFFER_SIZE) - { - if (negative) - { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) - { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) - { - buf[len++] = ' '; - } - } + if (len < PRINTF_INTEGER_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } - out_rev_(output, buf, len, width, flags); + out_rev_(output, buf, len, width, flags); } // An internal itoa-like function static void print_integer(output_gadget_t *output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) { - char buf[PRINTF_INTEGER_BUFFER_SIZE]; - printf_size_t len = 0U; + char buf[PRINTF_INTEGER_BUFFER_SIZE]; + printf_size_t len = 0U; - if (!value) - { - if (!(flags & FLAGS_PRECISION)) - { - buf[len++] = '0'; - flags &= ~FLAGS_HASH; - // We drop this flag this since either the alternative and regular modes of the specifier - // don't differ on 0 values, or (in the case of octal) we've already provided the special - // handling for this mode. - } - else if (base == BASE_HEX) - { - flags &= ~FLAGS_HASH; - // We drop this flag this since either the alternative and regular modes of the specifier - // don't differ on 0 values - } - } - else - { - do - { - const char digit = (char)(value % base); - buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); - value /= base; - } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); - } + if (!value) + { + if (!(flags & FLAGS_PRECISION)) + { + buf[len++] = '0'; + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values, or (in the case of octal) we've already provided the special + // handling for this mode. + } + else if (base == BASE_HEX) + { + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values + } + } + else + { + do + { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); + } - print_integer_finalization(output, buf, len, negative, base, precision, width, flags); + print_integer_finalization(output, buf, len, negative, base, precision, width, flags); } #if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) -// Stores a fixed-precision representation of a double relative +// Stores a fixed-precision representation of a floating-point number relative // to a fixed precision (which cannot be determined by examining this structure) -struct double_components +struct floating_point_components { - int_fast64_t integral; - int_fast64_t fractional; - // ... truncation of the actual fractional part of the double value, scaled - // by the precision value - bool is_negative; + int_fast64_t integral; + int_fast64_t fractional; + // ... truncation of the actual fractional part of the floating_point_t value, scaled + // by the precision value + bool is_negative; }; -#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 -#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T -static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = { - 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, - 1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17}; +static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + 1] = { + 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, 1e10 +#if PRINTF_MAX_PRECOMPUTED_POWER_OF_10 > 10 + , + 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 +#endif +}; -#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1 +// Note: This value does not mean that all floating-point values printed with the +// library will be correct up to this precision; it is just an upper-bound for +// avoiding buffer overruns and such +#define PRINTF_MAX_SUPPORTED_PRECISION (NUM_DECIMAL_DIGITS_IN_INT64_T - 1) -// Break up a double number - which is known to be a finite non-negative number - +// Break up a floating-point number - which is known to be a finite non-negative number - // into its base-10 parts: integral - before the decimal point, and fractional - after it. // Taken the precision into account, but does not change it even internally. -static struct double_components get_components(double number, printf_size_t precision) +static struct floating_point_components get_components(floating_point_t number, printf_size_t precision) { - struct double_components number_; - number_.is_negative = get_sign_bit(number); - double abs_number = (number_.is_negative) ? -number : number; - number_.integral = (int_fast64_t)abs_number; - double remainder = (abs_number - (double)number_.integral) * powers_of_10[precision]; - number_.fractional = (int_fast64_t)remainder; + struct floating_point_components number_; + number_.is_negative = get_sign_bit(number); + floating_point_t abs_number = (number_.is_negative) ? -number : number; + number_.integral = (int_fast64_t)abs_number; + floating_point_t scaled_remainder = (abs_number - (floating_point_t)number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t)scaled_remainder; // for precision == 0U, this will be 0 - remainder -= (double)number_.fractional; + floating_point_t remainder = scaled_remainder - (floating_point_t)number_.fractional; + const floating_point_t one_half = (floating_point_t)0.5; - if (remainder > 0.5) - { - ++number_.fractional; - // handle rollover, e.g. case 0.99 with precision 1 is 1.0 - if ((double)number_.fractional >= powers_of_10[precision]) - { - number_.fractional = 0; - ++number_.integral; - } - } - else if ((remainder == 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) - { - // if halfway, round up if odd OR if last digit is 0 - ++number_.fractional; - } + if (remainder > one_half) + { + ++number_.fractional; + // handle rollover, e.g. case 0.99 with precision 1 is 1.0 + if ((floating_point_t)number_.fractional >= powers_of_10[precision]) + { + number_.fractional = 0; + ++number_.integral; + } + } + else if ((remainder == one_half) && (number_.fractional & 1U)) + { + // Banker's rounding, i.e. round half to even: + // 1.5 -> 2, but 2.5 -> 2 + ++number_.fractional; + } - if (precision == 0U) - { - remainder = abs_number - (double)number_.integral; - if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) - { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++number_.integral; - } - } - return number_; + if (precision == 0U) + { + remainder = abs_number - (floating_point_t)number_.integral; + if ((remainder == one_half) && (number_.integral & 1U)) + { + // Banker's rounding, i.e. round half to even: + // 1.5 -> 2, but 2.5 -> 2 + ++number_.integral; + } + } + return number_; } #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS struct scaling_factor { - double raw_factor; - bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it + floating_point_t raw_factor; + bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it }; -static double apply_scaling(double num, struct scaling_factor normalization) +static floating_point_t apply_scaling(floating_point_t num, struct scaling_factor normalization) { - return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; } -static double unapply_scaling(double normalized, struct scaling_factor normalization) +static floating_point_t unapply_scaling(floating_point_t normalized, struct scaling_factor normalization) { #ifdef __GNUC__ // accounting for a static analysis bug in GCC 6.x and earlier #pragma GCC diagnostic push +#if !defined(__has_warning) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif __has_warning("-Wmaybe-uninitialized") #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif - return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; +#endif + return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } -static struct scaling_factor update_normalization(struct scaling_factor sf, double extra_multiplicative_factor) +static struct scaling_factor update_normalization(struct scaling_factor sf, floating_point_t extra_multiplicative_factor) { - struct scaling_factor result; - if (sf.multiply) - { - result.multiply = true; - result.raw_factor = sf.raw_factor * extra_multiplicative_factor; - } - else - { - int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); - int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); + struct scaling_factor result; + if (sf.multiply) + { + result.multiply = true; + result.raw_factor = sf.raw_factor * extra_multiplicative_factor; + } + else + { + int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); + int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); - // Divide the larger-exponent raw raw_factor by the smaller - if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) - { - result.multiply = false; - result.raw_factor = sf.raw_factor / extra_multiplicative_factor; - } - else - { - result.multiply = true; - result.raw_factor = extra_multiplicative_factor / sf.raw_factor; - } - } - return result; + // Divide the larger-exponent raw raw_factor by the smaller + if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) + { + result.multiply = false; + result.raw_factor = sf.raw_factor / extra_multiplicative_factor; + } + else + { + result.multiply = true; + result.raw_factor = extra_multiplicative_factor / sf.raw_factor; + } + } + return result; } -static struct double_components get_normalized_components(bool negative, printf_size_t precision, double non_normalized, struct scaling_factor normalization, int floored_exp10) +static struct floating_point_components get_normalized_components(bool negative, printf_size_t precision, floating_point_t non_normalized, struct scaling_factor normalization, int floored_exp10) { - struct double_components components; - components.is_negative = negative; - double scaled = apply_scaling(non_normalized, normalization); + struct floating_point_components components; + components.is_negative = negative; + floating_point_t scaled = apply_scaling(non_normalized, normalization); - bool close_to_representation_extremum = ((-floored_exp10 + (int)precision) >= DBL_MAX_10_EXP - 1); - if (close_to_representation_extremum) - { - // We can't have a normalization factor which also accounts for the precision, i.e. moves - // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. - // So, we'll give up early on getting extra precision... - return get_components(negative ? -scaled : scaled, precision); - } - components.integral = (int_fast64_t)scaled; - double remainder = non_normalized - unapply_scaling((double)components.integral, normalization); - double prec_power_of_10 = powers_of_10[precision]; - struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); - double scaled_remainder = apply_scaling(remainder, account_for_precision); - double rounding_threshold = 0.5; + bool close_to_representation_extremum = ((-floored_exp10 + (int)precision) >= FP_TYPE_MAX_10_EXP - 1); + if (close_to_representation_extremum) + { + // We can't have a normalization factor which also accounts for the precision, i.e. moves + // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. + // So, we'll give up early on getting extra precision... + return get_components(negative ? -scaled : scaled, precision); + } + components.integral = (int_fast64_t)scaled; + floating_point_t remainder = non_normalized - unapply_scaling((floating_point_t)components.integral, normalization); + floating_point_t prec_power_of_10 = powers_of_10[precision]; + struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); + floating_point_t scaled_remainder = apply_scaling(remainder, account_for_precision); + floating_point_t rounding_threshold = 0.5; - components.fractional = (int_fast64_t)scaled_remainder; // when precision == 0, the assigned value should be 0 - scaled_remainder -= (double)components.fractional; // when precision == 0, this will not change scaled_remainder + components.fractional = (int_fast64_t)scaled_remainder; // when precision == 0, the assigned value should be 0 + scaled_remainder -= (floating_point_t)components.fractional; // when precision == 0, this will not change scaled_remainder - components.fractional += (scaled_remainder >= rounding_threshold); - if (scaled_remainder == rounding_threshold) - { - // banker's rounding: Round towards the even number (making the mean error 0) - components.fractional &= ~((int_fast64_t)0x1); - } - // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), - // and must then be corrected into (1, 0). - // Note: for precision = 0, this will "translate" the rounding effect from - // the fractional part to the integral part where it should actually be - // felt (as prec_power_of_10 is 1) - if ((double)components.fractional >= prec_power_of_10) - { - components.fractional = 0; - ++components.integral; - } - return components; + components.fractional += (scaled_remainder >= rounding_threshold); + if (scaled_remainder == rounding_threshold) + { + // banker's rounding: Round towards the even number (making the mean error 0) + components.fractional &= ~((int_fast64_t)0x1); + } + // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), + // and must then be corrected into (1, 0). + // Note: for precision = 0, this will "translate" the rounding effect from + // the fractional part to the integral part where it should actually be + // felt (as prec_power_of_10 is 1) + if ((floating_point_t)components.fractional >= prec_power_of_10) + { + components.fractional = 0; + ++components.integral; + } + return components; } #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS static void print_broken_up_decimal( - struct double_components number_, output_gadget_t *output, printf_size_t precision, - printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) + struct floating_point_components number_, output_gadget_t *output, printf_size_t precision, + printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) { - if (precision != 0U) - { - // do fractional part, as an unsigned number + if (precision != 0U) + { + // do fractional part, as an unsigned number - printf_size_t count = precision; + printf_size_t count = precision; - // %g/%G mandates we skip the trailing 0 digits... - if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) - { - while (true) - { - int_fast64_t digit = number_.fractional % 10U; - if (digit != 0) - { - break; - } - --count; - number_.fractional /= 10U; - } - // ... and even the decimal point if there are no - // non-zero fractional part digits (see below) - } + // %g/%G mandates we skip the trailing 0 digits... + if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) + { + while (true) + { + int_fast64_t digit = number_.fractional % 10U; + if (digit != 0) + { + break; + } + --count; + number_.fractional /= 10U; + } + // ... and even the decimal point if there are no + // non-zero fractional part digits (see below) + } - if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH)) - { - while (len < PRINTF_DECIMAL_BUFFER_SIZE) - { - --count; - buf[len++] = (char)('0' + number_.fractional % 10U); - if (!(number_.fractional /= 10U)) - { - break; - } - } - // add extra 0s - while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) - { - buf[len++] = '0'; - --count; - } - if (len < PRINTF_DECIMAL_BUFFER_SIZE) - { - buf[len++] = '.'; - } - } - } - else - { - if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) - { - buf[len++] = '.'; - } - } + if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH)) + { + while (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + --count; + buf[len++] = (char)('0' + number_.fractional % 10U); + if (!(number_.fractional /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) + { + buf[len++] = '0'; + --count; + } + if (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + buf[len++] = '.'; + } + } + } + else + { + if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) + { + buf[len++] = '.'; + } + } - // Write the integer part of the number (it comes after the fractional - // since the character order is reversed) - while (len < PRINTF_DECIMAL_BUFFER_SIZE) - { - buf[len++] = (char)('0' + (number_.integral % 10)); - if (!(number_.integral /= 10)) - { - break; - } - } + // Write the integer part of the number (it comes after the fractional + // since the character order is reversed) + while (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + buf[len++] = (char)('0' + (number_.integral % 10)); + if (!(number_.integral /= 10)) + { + break; + } + } - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) - { - if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) - { - width--; - } - while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) - { - buf[len++] = '0'; - } - } + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } - if (len < PRINTF_DECIMAL_BUFFER_SIZE) - { - if (number_.is_negative) - { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) - { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) - { - buf[len++] = ' '; - } - } + if (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + if (number_.is_negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } - out_rev_(output, buf, len, width, flags); + out_rev_(output, buf, len, width, flags); } // internal ftoa for fixed decimal floating point -static void print_decimal_number(output_gadget_t *output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) +static void print_decimal_number(output_gadget_t *output, floating_point_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) { - struct double_components value_ = get_components(number, precision); - print_broken_up_decimal(value_, output, precision, width, flags, buf, len); + struct floating_point_components value_ = get_components(number, precision); + print_broken_up_decimal(value_, output, precision, width, flags, buf, len); } #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS // A floor function - but one which only works for numbers whose // floor value is representable by an int. -static int bastardized_floor(double x) +static int bastardized_floor(floating_point_t x) { - if (x >= 0) - { - return (int)x; - } - int n = (int)x; - return (((double)n) == x) ? n : n - 1; + if (x >= 0) + { + return (int)x; + } + int n = (int)x; + return (((floating_point_t)n) == x) ? n : n - 1; } // Computes the base-10 logarithm of the input number - which must be an actual // positive number (not infinity or NaN, nor a sub-normal) -static double log10_of_positive(double positive_number) +static floating_point_t log10_of_positive(floating_point_t positive_number) { - // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). - // - // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of - // our input number, and need only solve log_10(M) for M between 1 and 2 (as - // the base-2 mantissa is always 1-point-something). In that limited range, a - // Taylor series expansion of log10(x) should serve us well enough; and we'll - // take the mid-point, 1.5, as the point of expansion. + // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). + // + // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of + // our input number, and need only solve log_10(M) for M between 1 and 2 (as + // the base-2 mantissa is always 1-point-something). In that limited range, a + // Taylor series expansion of log10(x) should serve us well enough; and we'll + // take the mid-point, 1.5, as the point of expansion. - double_with_bit_access dwba = get_bit_access(positive_number); - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - int exp2 = get_exp2(dwba); - // drop the exponent, so dwba.F comes into the range [1,2) - dwba.U = (dwba.U & (((double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | - ((double_uint_t)DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); - double z = (dwba.F - 1.5); - return ( - // Taylor expansion around 1.5: - 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) - + z * 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) + floating_point_with_bit_access dwba = get_bit_access(positive_number); + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + int exp2 = get_exp2(dwba); + // drop the exponent, so dwba.F comes into the range [1,2) + dwba.U = (dwba.U & (((printf_fp_uint_t)(1) << FP_TYPE_STORED_MANTISSA_BITS) - 1U)) | + ((printf_fp_uint_t)FP_TYPE_BASE_EXPONENT << FP_TYPE_STORED_MANTISSA_BITS); + floating_point_t z = (dwba.F - (floating_point_t)1.5); + return ( + // Taylor expansion around 1.5: + (floating_point_t)0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) + + z * (floating_point_t)0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) #if PRINTF_LOG10_TAYLOR_TERMS > 2 - - z * z * 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) + - z * z * (floating_point_t)0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) #if PRINTF_LOG10_TAYLOR_TERMS > 3 - + z * z * z * 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) + + z * z * z * (floating_point_t)0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) #endif #endif - // exact log_2 of the exponent x, with logarithm base change - + exp2 * 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) - ); + // exact log_2 of the exponent x, with logarithm base change + + (floating_point_t)exp2 * (floating_point_t)0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) + ); } -static double pow10_of_int(int floored_exp10) +static floating_point_t pow10_of_int(int floored_exp10) { - // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. - if (floored_exp10 == DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10) - { - return DOUBLE_MAX_SUBNORMAL_POWER_OF_10; - } - // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow - double_with_bit_access dwba; - int exp2 = bastardized_floor(floored_exp10 * 3.321928094887362 + 0.5); - const double z = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - dwba.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS; - // compute exp(z) using continued fractions, - // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - return dwba.F; + // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. + if (floored_exp10 == FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10) + { + return FP_TYPE_MAX_SUBNORMAL_POWER_OF_10; + } + // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow + floating_point_with_bit_access dwba; + int exp2 = bastardized_floor((floating_point_t)(floored_exp10 * 3.321928094887362 + 0.5)); + const floating_point_t z = (floating_point_t)(floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453); + const floating_point_t z2 = z * z; + dwba.U = ((printf_fp_uint_t)(exp2) + FP_TYPE_BASE_EXPONENT) << FP_TYPE_STORED_MANTISSA_BITS; + // compute exp(z) using continued fractions, + // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + return dwba.F; } -static void print_exponential_number(output_gadget_t *output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) +static void print_exponential_number(output_gadget_t *output, floating_point_t number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) { - const bool negative = get_sign_bit(number); - // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it - double abs_number = negative ? -number : number; + const bool negative = get_sign_bit(number); + // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + floating_point_t abs_number = negative ? -number : number; - int floored_exp10; - bool abs_exp10_covered_by_powers_table; - struct scaling_factor normalization; + int floored_exp10; + bool abs_exp10_covered_by_powers_table; + struct scaling_factor normalization; - // Determine the decimal exponent - if (abs_number == 0.0) - { - // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. - floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table - } - else - { - double exp10 = log10_of_positive(abs_number); - floored_exp10 = bastardized_floor(exp10); - double p10 = pow10_of_int(floored_exp10); - // correct for rounding errors - if (abs_number < p10) - { - floored_exp10--; - p10 /= 10; - } - abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; - normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; - } + // Determine the decimal exponent + if (abs_number == (floating_point_t)0.0) + { + // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. + floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table + } + else + { + floating_point_t exp10 = log10_of_positive(abs_number); + floored_exp10 = bastardized_floor(exp10); + floating_point_t p10 = pow10_of_int(floored_exp10); + // correct for rounding errors + if (abs_number < p10) + { + floored_exp10--; + p10 /= 10; + } + abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; + normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; + } - // We now begin accounting for the widths of the two parts of our printed field: - // the decimal part after decimal exponent extraction, and the base-10 exponent part. - // For both of these, the value of 0 has a special meaning, but not the same one: - // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width - // means "use as many characters as necessary". + // We now begin accounting for the widths of the two parts of our printed field: + // the decimal part after decimal exponent extraction, and the base-10 exponent part. + // For both of these, the value of 0 has a special meaning, but not the same one: + // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width + // means "use as many characters as necessary". - bool fall_back_to_decimal_only_mode = false; - if (flags & FLAGS_ADAPT_EXP) - { - int required_significant_digits = (precision == 0) ? 1 : (int)precision; - // Should we want to fall-back to "%f" mode, and only print the decimal part? - fall_back_to_decimal_only_mode = (floored_exp10 >= -4 && floored_exp10 < required_significant_digits); - // Now, let's adjust the precision - // This also decided how we adjust the precision value - as in "%g" mode, - // "precision" is the number of _significant digits_, and this is when we "translate" - // the precision value to an actual number of decimal digits. - int precision_ = fall_back_to_decimal_only_mode ? (int)precision - 1 - floored_exp10 : (int)precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point - precision = (precision_ > 0 ? (unsigned)precision_ : 0U); - flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above - } + bool fall_back_to_decimal_only_mode = false; + if (flags & FLAGS_ADAPT_EXP) + { + int required_significant_digits = (precision == 0) ? 1 : (int)precision; + // Should we want to fall-back to "%f" mode, and only print the decimal part? + fall_back_to_decimal_only_mode = (floored_exp10 >= -4 && floored_exp10 < required_significant_digits); + // Now, let's adjust the precision + // This also decided how we adjust the precision value - as in "%g" mode, + // "precision" is the number of _significant digits_, and this is when we "translate" + // the precision value to an actual number of decimal digits. + int precision_ = fall_back_to_decimal_only_mode ? (int)precision - 1 - floored_exp10 : (int)precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point + precision = (precision_ > 0 ? (unsigned)precision_ : 0U); + flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above + } - normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); - bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_exp10 == 0); - struct double_components decimal_part_components = - should_skip_normalization ? get_components(negative ? -abs_number : abs_number, precision) : get_normalized_components(negative, precision, abs_number, normalization, floored_exp10); +#ifdef __GNUC__ +// accounting for a static analysis bug in GCC 6.x and earlier +#pragma GCC diagnostic push +#if !defined(__has_warning) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif __has_warning("-Wmaybe-uninitialized") +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_exp10 == 0); + struct floating_point_components decimal_part_components = + should_skip_normalization ? get_components(negative ? -abs_number : abs_number, precision) : get_normalized_components(negative, precision, abs_number, normalization, floored_exp10); - // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects - // the exponent and may require additional tweaking of the parts - if (fall_back_to_decimal_only_mode) - { - if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) - { - floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used - precision--; - // ... and it should already be the case that decimal_part_components.fractional == 0 - } - // TODO: What about rollover strictly within the fractional part? - } - else - { - if (decimal_part_components.integral >= 10) - { - floored_exp10++; - decimal_part_components.integral = 1; - decimal_part_components.fractional = 0; - } - } + // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects + // the exponent and may require additional tweaking of the parts + if (fall_back_to_decimal_only_mode) + { + if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) + { + floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used + if (precision > 0U) + { + precision--; + } + // ... and it should already be the case that decimal_part_components.fractional == 0 + } + // TODO: What about rollover strictly within the fractional part? + } + else + { + if (decimal_part_components.integral >= 10) + { + floored_exp10++; + decimal_part_components.integral = 1; + decimal_part_components.fractional = 0; + } + } - // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double - // is "307" (for 2^1023), so we set aside 4-5 characters overall - printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U - : 5U; + // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double + // is "307" (for 2^1023), so we set aside 4-5 characters overall + printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U + : 5U; - printf_size_t decimal_part_width = - ((flags & FLAGS_LEFT) && exp10_part_width) ? - // We're padding on the right, so the width constraint is the exponent part's - // problem, not the decimal part's, so we'll use as many characters as we need: - 0U - : - // We're padding on the left; so the width constraint is the decimal part's - // problem. Well, can both the decimal part and the exponent part fit within our overall width? - ((width > exp10_part_width) ? - // Yes, so we limit our decimal part's width. - // (Note this is trivially valid even if we've fallen back to "%f" mode) - width - exp10_part_width - : - // No; we just give up on any restriction on the decimal part and use as many - // characters as we need - 0U); + printf_size_t decimal_part_width = + ((flags & FLAGS_LEFT) && exp10_part_width) ? + // We're padding on the right, so the width constraint is the exponent part's + // problem, not the decimal part's, so we'll use as many characters as we need: + 0U + : + // We're padding on the left; so the width constraint is the decimal part's + // problem. Well, can both the decimal part and the exponent part fit within our overall width? + ((width > exp10_part_width) ? + // Yes, so we limit our decimal part's width. + // (Note this is trivially valid even if we've fallen back to "%f" mode) + width - exp10_part_width + : + // No; we just give up on any restriction on the decimal part and use as many + // characters as we need + 0U); - const printf_size_t printed_exponential_start_pos = output->pos; - print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); + const printf_size_t printed_exponential_start_pos = output->pos; + print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); - if (!fall_back_to_decimal_only_mode) - { - putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); - print_integer(output, - ABS_FOR_PRINTING(floored_exp10), - floored_exp10 < 0, 10, 0, exp10_part_width - 1, - FLAGS_ZEROPAD | FLAGS_PLUS); - if (flags & FLAGS_LEFT) - { - // We need to right-pad with spaces to meet the width requirement - while (output->pos - printed_exponential_start_pos < width) - { - putchar_via_gadget(output, ' '); - } - } - } + if (!fall_back_to_decimal_only_mode) + { + putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); + print_integer(output, + ABS_FOR_PRINTING(floored_exp10), + floored_exp10 < 0, 10, 0, exp10_part_width - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + if (flags & FLAGS_LEFT) + { + // We need to right-pad with spaces to meet the width requirement + while (output->pos - printed_exponential_start_pos < width) + { + putchar_via_gadget(output, ' '); + } + } + } } #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -static void print_floating_point(output_gadget_t *output, double value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) +static void print_floating_point(output_gadget_t *output, floating_point_t value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) { - char buf[PRINTF_DECIMAL_BUFFER_SIZE]; - printf_size_t len = 0U; + char buf[PRINTF_DECIMAL_BUFFER_SIZE]; + printf_size_t len = 0U; - // test for special values - if (value != value) - { - out_rev_(output, "nan", 3, width, flags); - return; - } - if (value < -DBL_MAX) - { - out_rev_(output, "fni-", 4, width, flags); - return; - } - if (value > DBL_MAX) - { - out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - return; - } + // test for special values + if (value != value) + { + out_rev_(output, "nan", 3, width, flags); + return; + } + if (value < -FP_TYPE_MAX) + { + out_rev_(output, "fni-", 4, width, flags); + return; + } + if (value > FP_TYPE_MAX) + { + out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + return; + } - if (!prefer_exponential && - ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) - { - // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean - // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated - // implementation. + if (!prefer_exponential && + ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) + { + // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean + // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated + // implementation. #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - print_exponential_number(output, value, precision, width, flags, buf, len); + print_exponential_number(output, value, precision, width, flags, buf, len); #endif - return; - } + return; + } - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) - { - precision = PRINTF_DEFAULT_FLOAT_PRECISION; - } + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + precision = PRINTF_DEFAULT_FLOAT_PRECISION; + } - // limit precision so that our integer holding the fractional part does not overflow - while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) - { - buf[len++] = '0'; // This respects the precision in terms of result length only - precision--; - } + // limit precision so that our integer holding the fractional part does not overflow + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) + { + buf[len++] = '0'; // This respects the precision in terms of result length only + precision--; + } #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - if (prefer_exponential) - print_exponential_number(output, value, precision, width, flags, buf, len); - else + if (prefer_exponential) + print_exponential_number(output, value, precision, width, flags, buf, len); + else #endif - print_decimal_number(output, value, precision, width, flags, buf, len); + print_decimal_number(output, value, precision, width, flags, buf, len); } #endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) @@ -1100,489 +1156,505 @@ static void print_floating_point(output_gadget_t *output, double value, printf_s // due to the characters passed static printf_flags_t parse_flags(const char **format) { - printf_flags_t flags = 0U; - do - { - switch (**format) - { - case '0': - flags |= FLAGS_ZEROPAD; - (*format)++; - break; - case '-': - flags |= FLAGS_LEFT; - (*format)++; - break; - case '+': - flags |= FLAGS_PLUS; - (*format)++; - break; - case ' ': - flags |= FLAGS_SPACE; - (*format)++; - break; - case '#': - flags |= FLAGS_HASH; - (*format)++; - break; - default: - return flags; - } - } while (true); + printf_flags_t flags = 0U; + do + { + switch (**format) + { + case '0': + flags |= FLAGS_ZEROPAD; + (*format)++; + break; + case '-': + flags |= FLAGS_LEFT; + (*format)++; + break; + case '+': + flags |= FLAGS_PLUS; + (*format)++; + break; + case ' ': + flags |= FLAGS_SPACE; + (*format)++; + break; + case '#': + flags |= FLAGS_HASH; + (*format)++; + break; + default: + return flags; + } + } while (true); } static inline void format_string_loop(output_gadget_t *output, const char *format, va_list args) { #if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER #define ADVANCE_IN_FORMAT_STRING(cptr_) \ - do \ - { \ - (cptr_)++; \ - if (!*(cptr_)) \ - return; \ - } while (0) + do \ + { \ + (cptr_)++; \ + if (!*(cptr_)) \ + return; \ + } while (0) #else #define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ #endif - while (*format) - { - if (*format != '%') - { - // A regular content character - putchar_via_gadget(output, *format); - format++; - continue; - } - // We're parsing a format specifier: %[flags][width][.precision][length] - ADVANCE_IN_FORMAT_STRING(format); + while (*format) + { + if (*format != '%') + { + // A regular content character + putchar_via_gadget(output, *format); + format++; + continue; + } + // We're parsing a format specifier: %[flags][width][.precision][length] + ADVANCE_IN_FORMAT_STRING(format); - printf_flags_t flags = parse_flags(&format); + printf_flags_t flags = parse_flags(&format); - // evaluate width field - printf_size_t width = 0U; - if (is_digit_(*format)) - { - width = (printf_size_t)atou_(&format); - } - else if (*format == '*') - { - const int w = va_arg(args, int); - if (w < 0) - { - flags |= FLAGS_LEFT; // reverse padding - width = (printf_size_t)-w; - } - else - { - width = (printf_size_t)w; - } - ADVANCE_IN_FORMAT_STRING(format); - } + // evaluate width field + printf_size_t width = 0U; + if (is_digit_(*format)) + { + width = (printf_size_t)atou_(&format); + } + else if (*format == '*') + { + const int w = va_arg(args, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (printf_size_t)-w; + } + else + { + width = (printf_size_t)w; + } + ADVANCE_IN_FORMAT_STRING(format); + } - // evaluate precision field - printf_size_t precision = 0U; - if (*format == '.') - { - flags |= FLAGS_PRECISION; - ADVANCE_IN_FORMAT_STRING(format); - if (is_digit_(*format)) - { - precision = (printf_size_t)atou_(&format); - } - else if (*format == '*') - { - const int precision_ = va_arg(args, int); - precision = precision_ > 0 ? (printf_size_t)precision_ : 0U; - ADVANCE_IN_FORMAT_STRING(format); - } - } + // evaluate precision field + printf_size_t precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + ADVANCE_IN_FORMAT_STRING(format); + if (is_digit_(*format)) + { + precision = (printf_size_t)atou_(&format); + } + else if (*format == '*') + { + const int precision_ = va_arg(args, int); + precision = precision_ > 0 ? (printf_size_t)precision_ : 0U; + ADVANCE_IN_FORMAT_STRING(format); + } + } - // evaluate length field - switch (*format) - { + // evaluate length field + switch (*format) + { #ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS - case 'I': - { - ADVANCE_IN_FORMAT_STRING(format); - // Greedily parse for size in bits: 8, 16, 32 or 64 - switch (*format) - { - case '8': - flags |= FLAGS_INT8; - ADVANCE_IN_FORMAT_STRING(format); - break; - case '1': - ADVANCE_IN_FORMAT_STRING(format); - if (*format == '6') - { - format++; - flags |= FLAGS_INT16; - } - break; - case '3': - ADVANCE_IN_FORMAT_STRING(format); - if (*format == '2') - { - ADVANCE_IN_FORMAT_STRING(format); - flags |= FLAGS_INT32; - } - break; - case '6': - ADVANCE_IN_FORMAT_STRING(format); - if (*format == '4') - { - ADVANCE_IN_FORMAT_STRING(format); - flags |= FLAGS_INT64; - } - break; - default: - break; - } - break; - } + case 'I': + { + ADVANCE_IN_FORMAT_STRING(format); + // Greedily parse for size in bits: 8, 16, 32 or 64 + switch (*format) + { + case '8': + flags |= FLAGS_INT8; + ADVANCE_IN_FORMAT_STRING(format); + break; + case '1': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '6') + { + format++; + flags |= FLAGS_INT16; + } + break; + case '3': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '2') + { + ADVANCE_IN_FORMAT_STRING(format); + flags |= FLAGS_INT32; + } + break; + case '6': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '4') + { + ADVANCE_IN_FORMAT_STRING(format); + flags |= FLAGS_INT64; + } + break; + default: + break; + } + break; + } #endif - case 'l': - flags |= FLAGS_LONG; - ADVANCE_IN_FORMAT_STRING(format); - if (*format == 'l') - { - flags |= FLAGS_LONG_LONG; - ADVANCE_IN_FORMAT_STRING(format); - } - break; - case 'h': - flags |= FLAGS_SHORT; - ADVANCE_IN_FORMAT_STRING(format); - if (*format == 'h') - { - flags |= FLAGS_CHAR; - ADVANCE_IN_FORMAT_STRING(format); - } - break; - case 't': - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - case 'j': - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - case 'z': - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - default: - break; - } + case 'l': + flags |= FLAGS_LONG; + ADVANCE_IN_FORMAT_STRING(format); + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + } + break; + case 'L': + flags |= FLAGS_LONG_DOUBLE; + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'h': + flags |= FLAGS_SHORT; + ADVANCE_IN_FORMAT_STRING(format); + if (*format == 'h') + { + flags |= FLAGS_CHAR; + ADVANCE_IN_FORMAT_STRING(format); + } + break; + case 't': + flags |= (sizeof(ptrdiff_t) <= sizeof(int)) ? FLAGS_INT : (sizeof(ptrdiff_t) == sizeof(long)) ? FLAGS_LONG + : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'z': + flags |= (sizeof(size_t) <= sizeof(int)) ? FLAGS_INT : (sizeof(size_t) == sizeof(long)) ? FLAGS_LONG + : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + break; + default: + break; + } - // evaluate specifier - switch (*format) - { - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - case 'b': - { + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { - if (*format == 'd' || *format == 'i') - { - flags |= FLAGS_SIGNED; - } + if (*format == 'd' || *format == 'i') + { + flags |= FLAGS_SIGNED; + } - numeric_base_t base; - if (*format == 'x' || *format == 'X') - { - base = BASE_HEX; - } - else if (*format == 'o') - { - base = BASE_OCTAL; - } - else if (*format == 'b') - { - base = BASE_BINARY; - } - else - { - base = BASE_DECIMAL; - flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation - } + numeric_base_t base; + if (*format == 'x' || *format == 'X') + { + base = BASE_HEX; + } + else if (*format == 'o') + { + base = BASE_OCTAL; + } + else if (*format == 'b') + { + base = BASE_BINARY; + } + else + { + base = BASE_DECIMAL; + flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation + } - if (*format == 'X') - { - flags |= FLAGS_UPPERCASE; - } + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } - format++; - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) - { - flags &= ~FLAGS_ZEROPAD; - } + format++; + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } - if (flags & FLAGS_SIGNED) - { - // A signed specifier: d, i or possibly I + bit size if enabled + if (flags & FLAGS_SIGNED) + { + // A signed specifier: d, i or possibly I + bit size if enabled - if (flags & FLAGS_LONG_LONG) - { + if (flags & FLAGS_LONG_LONG) + { #if PRINTF_SUPPORT_LONG_LONG - const long long value = va_arg(args, long long); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + const long long value = va_arg(args, long long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) - { - const long value = va_arg(args, long); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); - } - else - { - // We never try to interpret the argument as something potentially-smaller than int, - // due to integer promotion rules: Even if the user passed a short int, short unsigned - // etc. - these will come in after promotion, as int's (or unsigned for the case of - // short unsigned when it has the same size as int) - const int value = - (flags & FLAGS_CHAR) ? (signed char)va_arg(args, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(args, int) - : va_arg(args, int); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); - } - } - else - { - // An unsigned specifier: u, x, X, o, b + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(args, long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + else + { + // We never try to interpret the argument as something potentially-smaller than int, + // due to integer promotion rules: Even if the user passed a short int, short unsigned + // etc. - these will come in after promotion, as int's (or unsigned for the case of + // short unsigned when it has the same size as int) + const int value = + (flags & FLAGS_CHAR) ? (signed char)va_arg(args, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(args, int) + : va_arg(args, int); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + } + else + { + // An unsigned specifier: u, x, X, o, b - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - if (flags & FLAGS_LONG_LONG) - { + if (flags & FLAGS_LONG_LONG) + { #if PRINTF_SUPPORT_LONG_LONG - print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long long), false, base, precision, width, flags); + print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long long), false, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) - { - print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long), false, base, precision, width, flags); - } - else - { - const unsigned int value = - (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) - : va_arg(args, unsigned int); - print_integer(output, (printf_unsigned_value_t)value, false, base, precision, width, flags); - } - } - break; - } + } + else if (flags & FLAGS_LONG) + { + print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) + : va_arg(args, unsigned int); + print_integer(output, (printf_unsigned_value_t)value, false, base, precision, width, flags); + } + } + break; + } #if PRINTF_SUPPORT_DECIMAL_SPECIFIERS - case 'f': - case 'F': - if (*format == 'F') - flags |= FLAGS_UPPERCASE; - print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL); - format++; - break; + case 'f': + case 'F': + { + floating_point_t value = (floating_point_t)(flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; + } #endif #if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g') || (*format == 'G')) - flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E') || (*format == 'G')) - flags |= FLAGS_UPPERCASE; - print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); - format++; - break; + case 'e': + case 'E': + case 'g': + case 'G': + { + floating_point_t value = (floating_point_t)(flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; + } #endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'c': - { - printf_size_t l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) - { - while (l++ < width) - { - putchar_via_gadget(output, ' '); - } - } - // char output - putchar_via_gadget(output, (char)va_arg(args, int)); - // post padding - if (flags & FLAGS_LEFT) - { - while (l++ < width) - { - putchar_via_gadget(output, ' '); - } - } - format++; - break; - } + case 'c': + { + printf_size_t l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + // char output + putchar_via_gadget(output, (char)va_arg(args, int)); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + format++; + break; + } - case 's': - { - const char *p = va_arg(args, char *); - if (p == NULL) - { - out_rev_(output, ")llun(", 6, width, flags); - } - else - { - printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); - // pre padding - if (flags & FLAGS_PRECISION) - { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) - { - while (l++ < width) - { - putchar_via_gadget(output, ' '); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) - { - putchar_via_gadget(output, *(p++)); - --precision; - } - // post padding - if (flags & FLAGS_LEFT) - { - while (l++ < width) - { - putchar_via_gadget(output, ' '); - } - } - } - format++; - break; - } + case 's': + { + const char *p = va_arg(args, char *); + if (p == NULL) + { + out_rev_(output, ")llun(", 6, width, flags); + } + else + { + printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) + { + putchar_via_gadget(output, *(p++)); + --precision; + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + } + format++; + break; + } - case 'p': - { - width = sizeof(void *) * 2U + 2; // 2 hex chars per byte + the "0x" prefix - flags |= FLAGS_ZEROPAD | FLAGS_POINTER; - uintptr_t value = (uintptr_t)va_arg(args, void *); - (value == (uintptr_t)NULL) ? out_rev_(output, ")lin(", 5, width, flags) : print_integer(output, (printf_unsigned_value_t)value, false, BASE_HEX, precision, width, flags); - format++; - break; - } + case 'p': + { + width = sizeof(void *) * 2U + 2; // 2 hex chars per byte + the "0x" prefix + flags |= FLAGS_ZEROPAD | FLAGS_POINTER; + uintptr_t value = (uintptr_t)va_arg(args, void *); + (value == (uintptr_t)NULL) ? out_rev_(output, ")lin(", 5, width, flags) : print_integer(output, (printf_unsigned_value_t)value, false, BASE_HEX, precision, width, flags); + format++; + break; + } - case '%': - putchar_via_gadget(output, '%'); - format++; - break; + case '%': + putchar_via_gadget(output, '%'); + format++; + break; - // Many people prefer to disable support for %n, as it lets the caller - // engineer a write to an arbitrary location, of a value the caller - // effectively controls - which could be a security concern in some cases. + // Many people prefer to disable support for %n, as it lets the caller + // engineer a write to an arbitrary location, of a value the caller + // effectively controls - which could be a security concern in some cases. #if PRINTF_SUPPORT_WRITEBACK_SPECIFIER - case 'n': - { - if (flags & FLAGS_CHAR) - *(va_arg(args, char *)) = (char)output->pos; - else if (flags & FLAGS_SHORT) - *(va_arg(args, short *)) = (short)output->pos; - else if (flags & FLAGS_LONG) - *(va_arg(args, long *)) = (long)output->pos; + case 'n': + { + if (flags & FLAGS_CHAR) + *(va_arg(args, char *)) = (char)output->pos; + else if (flags & FLAGS_SHORT) + *(va_arg(args, short *)) = (short)output->pos; + else if (flags & FLAGS_LONG) + *(va_arg(args, long *)) = (long)output->pos; #if PRINTF_SUPPORT_LONG_LONG - else if (flags & FLAGS_LONG_LONG) - *(va_arg(args, long long *)) = (long long int)output->pos; + else if (flags & FLAGS_LONG_LONG) + *(va_arg(args, long long *)) = (long long int)output->pos; #endif // PRINTF_SUPPORT_LONG_LONG - else - *(va_arg(args, int *)) = (int)output->pos; - format++; - break; - } + else + *(va_arg(args, int *)) = (int)output->pos; + format++; + break; + } #endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER - default: - putchar_via_gadget(output, *format); - format++; - break; - } - } + default: + putchar_via_gadget(output, *format); + format++; + break; + } + } } // internal vsnprintf - used for implementing _all library functions static int vsnprintf_impl(output_gadget_t *output, const char *format, va_list args) { - // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is - // possible to call this function with a non-zero pos value for some "remedial printing". - format_string_loop(output, format, args); + // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is + // possible to call this function with a non-zero pos value for some "remedial printing". + format_string_loop(output, format, args); - // termination - append_termination_with_gadget(output); + // termination + append_termination_with_gadget(output); - // return written chars without terminating \0 - return (int)output->pos; + // return written chars without terminating \0 + return (int)output->pos; } /////////////////////////////////////////////////////////////////////////////// -int vprintf(const char *format, va_list arg) +int vprintf_(const char *format, va_list arg) { - output_gadget_t gadget = extern_putchar_gadget(); - return vsnprintf_impl(&gadget, format, arg); + output_gadget_t gadget = extern_putchar_gadget(); + return vsnprintf_impl(&gadget, format, arg); } -int vsnprintf(char *s, size_t n, const char *format, va_list arg) +int vsnprintf_(char *s, size_t n, const char *format, va_list arg) { - output_gadget_t gadget = buffer_gadget(s, n); - return vsnprintf_impl(&gadget, format, arg); + output_gadget_t gadget = buffer_gadget(s, n); + return vsnprintf_impl(&gadget, format, arg); } -int vsprintf(char *s, const char *format, va_list arg) +int vsprintf_(char *s, const char *format, va_list arg) { - return vsnprintf(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); + return vsnprintf_(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); } int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, va_list arg) { - output_gadget_t gadget = function_gadget(out, extra_arg); - return vsnprintf_impl(&gadget, format, arg); + if (out == NULL) + { + return 0; + } + output_gadget_t gadget = function_gadget(out, extra_arg); + return vsnprintf_impl(&gadget, format, arg); } -int printf(const char *format, ...) +int printf_(const char *format, ...) { - va_list args; - va_start(args, format); - const int ret = vprintf(format, args); - va_end(args); - return ret; + va_list args; + va_start(args, format); + const int ret = vprintf_(format, args); + va_end(args); + return ret; } -int sprintf(char *s, const char *format, ...) +int sprintf_(char *s, const char *format, ...) { - va_list args; - va_start(args, format); - const int ret = vsprintf(s, format, args); - va_end(args); - return ret; + va_list args; + va_start(args, format); + const int ret = vsprintf_(s, format, args); + va_end(args); + return ret; } -int snprintf(char *s, size_t n, const char *format, ...) +int snprintf_(char *s, size_t n, const char *format, ...) { - va_list args; - va_start(args, format); - const int ret = vsnprintf(s, n, format, args); - va_end(args); - return ret; + va_list args; + va_start(args, format); + const int ret = vsnprintf_(s, n, format, args); + va_end(args); + return ret; } int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) { - va_list args; - va_start(args, format); - const int ret = vfctprintf(out, extra_arg, format, args); - va_end(args); - return ret; + va_list args; + va_start(args, format); + const int ret = vfctprintf(out, extra_arg, format, args); + va_end(args); + return ret; } diff --git a/Userspace/libc/src/print/printf.h b/Userspace/libc/src/print/printf.h index 745864c7..67202479 100644 --- a/Userspace/libc/src/print/printf.h +++ b/Userspace/libc/src/print/printf.h @@ -1,6 +1,6 @@ /** * @author (c) Eyal Rozenberg - * 2021-2022, Haifa, Palestine/Israel + * 2021-2023, Haifa, Palestine/Israel * @author (c) Marco Paland (info@paland.com) * 2014-2019, PALANDesign Hannover, Germany * @@ -9,8 +9,8 @@ * or ask one of the authors. * * @brief Small stand-alone implementation of the printf family of functions - * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with - * a very limited resources. + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems + * with a very limited resources. * * @note the implementations are thread-safe; re-entrant; use no functions from * the standard library; and do not dynamically allocate any memory. @@ -39,27 +39,40 @@ #ifndef PRINTF_H_ #define PRINTF_H_ -#include - #ifdef __cplusplus +#include +#include extern "C" { +#else +#include +#include #endif #ifdef __GNUC__ +#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4) #define ATTR_PRINTF(one_based_format_index, first_arg) \ - __attribute__((format(__printf__, (one_based_format_index), (first_arg)))) -#define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF((one_based_format_index), 0) + __attribute__((format(gnu_printf, (one_based_format_index), (first_arg)))) #else -#define ATTR_PRINTF((one_based_format_index), (first_arg)) +#define ATTR_PRINTF(one_based_format_index, first_arg) \ + __attribute__((format(printf, (one_based_format_index), (first_arg)))) +#endif +#define ATTR_VPRINTF(one_based_format_index) \ + ATTR_PRINTF((one_based_format_index), 0) +#else +#define ATTR_PRINTF(one_based_format_index, first_arg) #define ATTR_VPRINTF(one_based_format_index) #endif -#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0 +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 0 #endif -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 0 +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD #define printf_ printf #define sprintf_ sprintf #define vsprintf_ vsprintf @@ -69,126 +82,153 @@ extern "C" #endif // If you want to include this implementation file directly rather than -// link against, this will let you control the functions' visibility, +// link against it, this will let you control the functions' visibility, // e.g. make them static so as not to clash with other objects also // using them. #ifndef PRINTF_VISIBILITY -#define PRINTF_VISIBILITY __attribute__((visibility("default"))) +#define PRINTF_VISIBILITY #endif - /** - * Prints/send a single character to some opaque output entity - * - * @note This function is not implemented by the library, only declared; you must provide an - * implementation if you wish to use the @ref printf / @ref vprintf function (and possibly - * for linking against the library, if your toolchain does not support discarding unused functions) - * - * @note The output could be as simple as a wrapper for the `write()` system call on a Unix-like - * system, or even libc's @ref putchar , for replicating actual functionality of libc's @ref printf - * function; but on an embedded system it may involve interaction with a special output device, - * like a UART, etc. - * - * @note in libc's @ref putchar, the parameter type is an int; this was intended to support the - * representation of either a proper character or EOF in a variable - but this is really not - * meaningful to pass into @ref putchar and is discouraged today. See further discussion in: - * @link https://stackoverflow.com/q/17452847/1593077 - * - * @param c the single character to print - */ - PRINTF_VISIBILITY - int putchar(int c); + /** + * Prints/send a single character to some opaque output entity + * + * @note This function is not implemented by the library, only declared; you + * must provide an implementation if you wish to use the @ref printf / @ref + * vprintf function (and possibly for linking against the library, if your + * toolchain does not support discarding unused functions) + * + * @note The output could be as simple as a wrapper for the `write()` system + * call on a Unix-like * system, or even libc's @ref putchar , for replicating + * actual functionality of libc's @ref printf * function; but on an embedded + * system it may involve interaction with a special output device, like a UART, + * etc. + * + * @note in libc's @ref putchar, the parameter type is an int; this was intended + * to support the representation of either a proper character or EOF in a + * variable - but this is really not meaningful to pass into @ref putchar and is + * discouraged today. See further discussion in: + * @link https://stackoverflow.com/q/17452847/1593077 + * + * @param c the single character to print + */ + PRINTF_VISIBILITY + void putchar_(char c); - /** - * An implementation of the C standard's printf/vprintf - * - * @note you must implement a @ref putchar_ function for using this function - it invokes @ref putchar_ - * rather than directly performing any I/O (which insulates it from any dependence on the operating system - * and external libraries). - * - * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret - * additional arguments. - * @param arg Additional arguments to the function, one for each %-specifier in @p format string - * @return The number of characters written into @p s, not counting the terminating null character - */ - ///@{ - PRINTF_VISIBILITY - int printf(const char *format, ...) ATTR_PRINTF(1, 2); - PRINTF_VISIBILITY - int vprintf(const char *format, va_list arg) ATTR_VPRINTF(1); - ///@} + /** + * An implementation of the C standard's printf/vprintf + * + * @note you must implement a @ref putchar_ function for using this function - + * it invokes @ref putchar_ * rather than directly performing any I/O (which + * insulates it from any dependence on the operating system * and external + * libraries). + * + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each %-specifier in + * @p format + * @return The number of characters written into @p s, not counting the + * terminating null character + */ + ///@{ + PRINTF_VISIBILITY + int printf_(const char *format, ...) ATTR_PRINTF(1, 2); + PRINTF_VISIBILITY + int vprintf_(const char *format, va_list arg) ATTR_VPRINTF(1); + ///@} - /** - * An implementation of the C standard's sprintf/vsprintf - * - * @note For security considerations (the potential for exceeding the buffer bounds), please consider using - * the size-constrained variant, @ref snprintf / @ref vsnprintf , instead. - * - * @param s An array in which to store the formatted string. It must be large enough to fit the formatted - * output! - * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret - * additional arguments. - * @param arg Additional arguments to the function, one for each specifier in @p format - * @return The number of characters written into @p s, not counting the terminating null character - */ - ///@{ - PRINTF_VISIBILITY - int sprintf(char *s, const char *format, ...) ATTR_PRINTF(2, 3); - PRINTF_VISIBILITY - int vsprintf(char *s, const char *format, va_list arg) ATTR_VPRINTF(2); - ///@} + /** + * An implementation of the C standard's sprintf/vsprintf + * + * @note For security considerations (the potential for exceeding the buffer + * bounds), please consider using the size-constrained variant, @ref snprintf / + * @ref vsnprintf, instead. + * + * @param s An array in which to store the formatted string. It must be large + * enough to fit the formatted output! + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters written into @p s, not counting the + * terminating null character + */ + ///@{ + PRINTF_VISIBILITY + int sprintf_(char *s, const char *format, ...) ATTR_PRINTF(2, 3); + PRINTF_VISIBILITY + int vsprintf_(char *s, const char *format, va_list arg) ATTR_VPRINTF(2); + ///@} - /** - * An implementation of the C standard's snprintf/vsnprintf - * - * @param s An array in which to store the formatted string. It must be large enough to fit either the - * entire formatted output, or at least @p n characters. Alternatively, it can be NULL, in which case - * nothing will be printed, and only the number of characters which _could_ have been printed is - * tallied and returned. - * @param n The maximum number of characters to write to the array, including a terminating null character - * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret - * additional arguments. - * @param arg Additional arguments to the function, one for each specifier in @p format - * @return The number of characters that COULD have been written into @p s, not counting the terminating - * null character. A value equal or larger than @p n indicates truncation. Only when the returned value - * is non-negative and less than @p n, the null-terminated string has been fully and successfully printed. - */ - ///@{ - PRINTF_VISIBILITY - int snprintf(char *s, size_t count, const char *format, ...) ATTR_PRINTF(3, 4); - PRINTF_VISIBILITY - int vsnprintf(char *s, size_t count, const char *format, va_list arg) ATTR_VPRINTF(3); - ///@} + /** + * An implementation of the C standard's snprintf/vsnprintf + * + * @param s An array in which to store the formatted string. It must be large + * enough to fit either the entire formatted output, or at least @p n + * characters. Alternatively, it can be NULL, in which case nothing will + * be printed, and only the number of characters which _could_ have been + * printed is tallied and returned. + * @param n The maximum number of characters to write to the array, including + * a terminating null character + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters that COULD have been written into @p s, not + * counting the terminating null character. A value equal or larger than + * @p n indicates truncation. Only when the returned value is non-negative + * and less than @p n, the null-terminated string has been fully and + * successfully printed. + */ + ///@{ + PRINTF_VISIBILITY + int snprintf_(char *s, size_t count, const char *format, ...) ATTR_PRINTF(3, 4); + PRINTF_VISIBILITY + int vsnprintf_(char *s, size_t count, const char *format, va_list arg) ATTR_VPRINTF(3); + ///@} - /** - * printf/vprintf with user-specified output function - * - * An alternative to @ref printf_, in which the output function is specified dynamically - * (rather than @ref putchar_ being used) - * - * @param out An output function which takes one character and a type-erased additional parameters - * @param extra_arg The type-erased argument to pass to the output function @p out with each call - * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret - * additional arguments. - * @param arg Additional arguments to the function, one for each specifier in @p format - * @return The number of characters for which the output f unction was invoked, not counting the terminating null character - * - */ - PRINTF_VISIBILITY - int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) ATTR_PRINTF(3, 4); - PRINTF_VISIBILITY - int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, va_list arg) ATTR_VPRINTF(3); + /** + * printf/vprintf with user-specified output function + * + * An alternative to @ref printf_, in which the output function is specified + * dynamically (rather than @ref putchar_ being used) + * + * @param out An output function which takes one character and a type-erased + * additional parameters + * @param extra_arg The type-erased argument to pass to the output function @p + * out with each call + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters for which the output f unction was invoked, + * not counting the terminating null character + * + */ + PRINTF_VISIBILITY + int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) ATTR_PRINTF(3, 4); + PRINTF_VISIBILITY + int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, va_list arg) ATTR_VPRINTF(3); -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#ifdef __cplusplus +} // extern "C" +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD #undef printf_ #undef sprintf_ #undef vsprintf_ #undef snprintf_ #undef vsnprintf_ #undef vprintf_ +#else +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT +#define printf printf_ +#define sprintf sprintf_ +#define vsprintf vsprintf_ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +#define vprintf vprintf_ +#endif #endif -#ifdef __cplusplus -} -#endif - -#endif // PRINTF_H_ \ No newline at end of file +#endif // PRINTF_H_ diff --git a/Userspace/libc/src/setjmp.c b/Userspace/libc/src/setjmp.c deleted file mode 100644 index 7a1086c0..00000000 --- a/Userspace/libc/src/setjmp.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -PUBLIC int setjmp(jmp_buf env) -{ - return 0; -} - -PUBLIC __attribute__((noreturn)) void longjmp(jmp_buf env, int value) -{ - _exit(value); -} \ No newline at end of file diff --git a/Userspace/libc/src/std/assert.c b/Userspace/libc/src/std/assert.c new file mode 100644 index 00000000..b60034f1 --- /dev/null +++ b/Userspace/libc/src/std/assert.c @@ -0,0 +1,26 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include + +export void __assert_fail(const char *file, int line, const char *func) +{ + printf("assertion failed: %s:%d:%s\n", file, line, func); + abort(); +} diff --git a/Userspace/libc/src/std/ctype.c b/Userspace/libc/src/std/ctype.c deleted file mode 100644 index f7b5373f..00000000 --- a/Userspace/libc/src/std/ctype.c +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include // For PUBLIC - -PUBLIC int tolower(int c) -{ - if (c >= 'A' && c <= 'Z') - { - c -= 'A'; - c += 'a'; - } - - return c; -} - -PUBLIC int toupper(int c) -{ - if (c >= 'a' && c <= 'z') - { - c -= 'a'; - c += 'A'; - } - return c; -} - -PUBLIC int isspace(int c) -{ - return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || c == '\v'; -} diff --git a/Userspace/libc/src/std/errno.c b/Userspace/libc/src/std/errno.c index 91db9f64..2200f0e2 100644 --- a/Userspace/libc/src/std/errno.c +++ b/Userspace/libc/src/std/errno.c @@ -1,10 +1,212 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include #include -#include // For PUBLIC - -int __local_stub_errno = 0; - -PUBLIC int *__errno_location(void) +__iptr __check_errno(__iptr status, __iptr err) { - return &__local_stub_errno; + if (status >= EOK) + return status; + pthread_self()->CurrentError = status; + return err; +} + +export int *__errno_location(void) +{ + return &pthread_self()->CurrentError; +} + +export char *strerror(int errnum) +{ + if (errnum < 0) + errnum = -errnum; + + if (errnum > __ERRNO_MAX) + return (char *)"Not a valid error number"; + + switch (errnum) + { + case EOK: + return (char *)"No error"; + case E2BIG: + return (char *)"Argument list too long"; + case EACCES: + return (char *)"Permission denied"; + case EADDRINUSE: + return (char *)"Address in use"; + case EADDRNOTAVAIL: + return (char *)"Address not available"; + case EAFNOSUPPORT: + return (char *)"Address family not supported"; + case EAGAIN: + return (char *)"Resource unavailable, try again"; + case EALREADY: + return (char *)"Connection already in progress"; + case EBADF: + return (char *)"Bad file descriptor"; + case EBADMSG: + return (char *)"Bad message"; + case EBUSY: + return (char *)"Device or resource busy"; + case ECANCELED: + return (char *)"Operation canceled"; + case ECHILD: + return (char *)"No child processes"; + case ECONNABORTED: + return (char *)"Connection aborted"; + case ECONNREFUSED: + return (char *)"Connection refused"; + case ECONNRESET: + return (char *)"Connection reset"; + case EDEADLK: + return (char *)"Resource deadlock would occur"; + case EDESTADDRREQ: + return (char *)"Destination address required"; + case EDOM: + return (char *)"Mathematics argument out of domain of function"; + case EDQUOT: + return (char *)"Reserved"; + case EEXIST: + return (char *)"File exists"; + case EFAULT: + return (char *)"Bad address"; + case EFBIG: + return (char *)"File too large"; + case EHOSTUNREACH: + return (char *)"Host is unreachable"; + case EIDRM: + return (char *)"Identifier removed"; + case EILSEQ: + return (char *)"Illegal byte sequence"; + case EINPROGRESS: + return (char *)"Operation in progress"; + case EINTR: + return (char *)"Interrupted function"; + case EINVAL: + return (char *)"Invalid argument"; + case EIO: + return (char *)"I/O error"; + case EISCONN: + return (char *)"Socket is connected"; + case EISDIR: + return (char *)"Is a directory"; + case ELOOP: + return (char *)"Too many levels of symbolic links"; + case EMFILE: + return (char *)"File descriptor value too large"; + case EMLINK: + return (char *)"Too many links"; + case EMSGSIZE: + return (char *)"Message too large"; + case EMULTIHOP: + return (char *)"Reserved"; + case ENAMETOOLONG: + return (char *)"Filename too long"; + case ENETDOWN: + return (char *)"Network is down"; + case ENETRESET: + return (char *)"Connection aborted by network"; + case ENETUNREACH: + return (char *)"Network unreachable"; + case ENFILE: + return (char *)"Too many files open in system"; + case ENOBUFS: + return (char *)"No buffer space available"; + case ENODATA: + return (char *)"No message available on the STREAM head read queue"; + case ENODEV: + return (char *)"No such device"; + case ENOENT: + return (char *)"No such file or directory"; + case ENOEXEC: + return (char *)"Executable file format error"; + case ENOLCK: + return (char *)"No locks available"; + case ENOLINK: + return (char *)"Reserved"; + case ENOMEM: + return (char *)"Not enough space"; + case ENOMSG: + return (char *)"No message of the desired type"; + case ENOPROTOOPT: + return (char *)"Protocol not available"; + case ENOSPC: + return (char *)"No space left on device"; + case ENOSR: + return (char *)"No STREAM resources"; + case ENOSTR: + return (char *)"Not a STREAM"; + case ENOSYS: + return (char *)"Functionality not supported"; + case ENOTCONN: + return (char *)"The socket is not connected"; + case ENOTDIR: + return (char *)"Not a directory or a symbolic link to a directory"; + case ENOTEMPTY: + return (char *)"Directory not empty"; + case ENOTRECOVERABLE: + return (char *)"State not recoverable"; + case ENOTSOCK: + return (char *)"Not a socket"; + case ENOTSUP: + return (char *)"Not supported"; + case ENOTTY: + return (char *)"Inappropriate I/O control operation"; + case ENXIO: + return (char *)"No such device or address"; + case EOPNOTSUPP: + return (char *)"Operation not supported on socket"; + case EOVERFLOW: + return (char *)"Value too large to be stored in data type"; + case EOWNERDEAD: + return (char *)"Previous owner died"; + case EPERM: + return (char *)"Operation not permitted"; + case EPIPE: + return (char *)"Broken pipe"; + case EPROTO: + return (char *)"Protocol error"; + case EPROTONOSUPPORT: + return (char *)"Protocol not supported"; + case EPROTOTYPE: + return (char *)"Protocol wrong type for socket"; + case ERANGE: + return (char *)"Result too large"; + case EROFS: + return (char *)"Read-only file system"; + case ESPIPE: + return (char *)"Invalid seek"; + case ESRCH: + return (char *)"No such process"; + case ESTALE: + return (char *)"Reserved"; + case ETIME: + return (char *)"Stream ioctl() timeout"; + case ETIMEDOUT: + return (char *)"Connection timed out"; + case ETXTBSY: + return (char *)"Text file busy"; + case EWOULDBLOCK: + return (char *)"Operation would block"; + case EXDEV: + return (char *)"Cross-device link"; + default: + return (char *)"Unknown error"; + } } diff --git a/Userspace/libc/src/std/io/file.c b/Userspace/libc/src/std/io/file.c deleted file mode 100644 index b8bb52a2..00000000 --- a/Userspace/libc/src/std/io/file.c +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include - -#include -#include // For PUBLIC - -PUBLIC FILE *stdin = NULL; -PUBLIC FILE *stdout = NULL; -PUBLIC FILE *stderr = NULL; - -PUBLIC FILE *freopen(const char *filename, const char *mode, FILE *stream) -{ - errno = ENOSYS; - return NULL; -} - -PUBLIC FILE *fopen(const char *filename, const char *mode) -{ - int fd = syscall2(sc_open, (uint64_t)filename, (uint64_t)mode); - if (fd < 0) - return NULL; - - FILE *FilePtr = malloc(sizeof(FILE)); - FilePtr->_fileno = fd; - return FilePtr; -} - -PUBLIC size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) -{ - if (ptr == NULL || stream == NULL || size == 0 || nmemb == 0) - { - errno = EINVAL; - return 0; - } - - syscall3(sc_lseek, stream->_fileno, stream->_offset, SEEK_SET); - return syscall3(sc_read, (uint64_t)stream->_fileno, (uint64_t)ptr, size * nmemb); -} - -PUBLIC size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) -{ - if (ptr == NULL || stream == NULL || size == 0 || nmemb == 0) - { - errno = EINVAL; - return 0; - } - - syscall3(sc_lseek, stream->_fileno, stream->_offset, SEEK_SET); - return syscall3(sc_write, (uint64_t)stream->_fileno, (uint64_t)ptr, size * nmemb); -} - -PUBLIC int fclose(FILE *fp) -{ - if (fp == NULL) - { - errno = EINVAL; - return EOF; - } - - return syscall1(sc_close, fp->_fileno); -} - -PUBLIC off_t fseek(FILE *stream, off_t offset, int whence) -{ - if (stream == NULL || whence < 0 || whence > 2) - { - errno = EINVAL; - return -1; - } - - off_t new_offset = syscall3(sc_lseek, stream->_fileno, offset, whence); - if (new_offset < 0) - return -1; - stream->_offset = new_offset; - return new_offset; -} - -PUBLIC long ftell(FILE *stream) -{ - return stream->_offset; -} - -PUBLIC int fflush(FILE *stream) -{ - if (stream == NULL) - { - errno = EINVAL; - return EOF; - } - - errno = ENOSYS; - return EOF; -} - -PUBLIC int fprintf(FILE *stream, const char *format, ...) -{ - if (stream == NULL || format == NULL) - { - errno = EINVAL; - return -1; - } - - va_list args; - va_start(args, format); - const int ret = vfprintf(stream, format, args); - va_end(args); - return ret; -} - -PUBLIC void setbuf(FILE *stream, char *buf) -{ -} - -PUBLIC int vfprintf(FILE *stream, const char *format, va_list arg) -{ - return 0; -} - -PUBLIC int vsscanf(const char *s, const char *format, va_list arg) -{ -} - -PUBLIC int sscanf(const char *s, const char *format, ...) -{ - va_list args; - va_start(args, format); - const int ret = vsscanf(s, format, args); - va_end(args); - return ret; -} diff --git a/Userspace/libc/src/std/io/get.c b/Userspace/libc/src/std/io/get.c deleted file mode 100644 index 70e0ac0f..00000000 --- a/Userspace/libc/src/std/io/get.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include -#include - -#include // For PUBLIC - -PUBLIC int fgetc(FILE *stream) -{ -} diff --git a/Userspace/libc/src/std/io/put.c b/Userspace/libc/src/std/io/put.c deleted file mode 100644 index 90cc8b97..00000000 --- a/Userspace/libc/src/std/io/put.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include - -#include // For PUBLIC - -PUBLIC int fputc(int c, FILE *stream) -{ - // FIXME - // if (stream == NULL) - // { - // errno = EBADF; - // return EOF; - // } - char str[2] = {c, '\0'}; - // return syscall3(sys_KernelCTL, KCTL_PRINT, str, 0); -} - -PUBLIC int putc(int c, FILE *stream) { return fputc(c, stream); } - -PUBLIC int fputs(const char *s, FILE *stream) -{ - for (int i = 0; s[i] != '\0'; i++) - fputc(s[i], stream); -} - -PUBLIC int puts(const char *s) -{ - for (int i = 0; s[i] != '\0'; i++) - fputc(s[i], stdout); -} - -PUBLIC int putchar(int c) { return fputc(c, stdout); } -PUBLIC void perror(const char *s) { fputs(s, stderr); } diff --git a/Userspace/libc/src/std/lib.c b/Userspace/libc/src/std/lib.c deleted file mode 100644 index 94434703..00000000 --- a/Userspace/libc/src/std/lib.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include - -#include - -#include "../mem/liballoc_1_1.h" - -PUBLIC void abort(void) -{ - syscall1(sc_exit, -0xAB057); - while (1) - ; -} - -PUBLIC int atexit(void (*function)(void)) -{ - return 1; -} - -PUBLIC void exit(int status) -{ - _exit(status); -} - -PUBLIC int atoi(const char *nptr) -{ - uint64_t Length = strlen((char *)nptr); - if (nptr) - while (nptr[Length] != '\0') - ++Length; - uint64_t OutBuffer = 0; - uint64_t Power = 1; - for (uint64_t i = Length; i > 0; --i) - { - OutBuffer += (nptr[i - 1] - 48) * Power; - Power *= 10; - } - return OutBuffer; -} - -PUBLIC char **environ = NULL; - -PUBLIC char *getenv(const char *name) -{ - char **env = environ; - if (env == NULL) - return NULL; - size_t len = strlen(name); - while (*env != NULL) - { - if ((strncmp(*env, name, len) == 0) && ((*env)[len] == '=')) - return &(*env)[len + 1]; - ++env; - } -} - -PUBLIC void *malloc(size_t Size) { return PREFIX(malloc)(Size); } -PUBLIC void *realloc(void *Address, size_t Size) { return PREFIX(realloc)(Address, Size); } -PUBLIC void *calloc(size_t Count, size_t Size) { return PREFIX(calloc)(Count, Size); } -PUBLIC void free(void *Address) -{ - PREFIX(free) - (Address); -} - -PUBLIC int system(const char *command) -{ - return -1; -} - -PUBLIC double atof(const char *nptr) -{ - // FIXME: This is a very bad implementation of atof. - uint64_t Length = strlen((char *)nptr); - if (nptr) - while (nptr[Length] != '\0') - ++Length; - double OutBuffer = 0; - double Power = 1; - for (uint64_t i = Length; i > 0; --i) - { - OutBuffer += (nptr[i - 1] - 48) * Power; - Power *= 10; - } - return OutBuffer; -} diff --git a/Userspace/libc/src/std/pthread.c b/Userspace/libc/src/std/pthread.c new file mode 100644 index 00000000..d6f7d757 --- /dev/null +++ b/Userspace/libc/src/std/pthread.c @@ -0,0 +1,121 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include + +export int pthread_attr_destroy(pthread_attr_t *); +export int pthread_attr_getdetachstate(const pthread_attr_t *, int *); +export int pthread_attr_getguardsize(const pthread_attr_t *, size_t *); +export int pthread_attr_getinheritsched(const pthread_attr_t *, int *); +export int pthread_attr_getschedparam(const pthread_attr_t *, struct sched_param *); +export int pthread_attr_getschedpolicy(const pthread_attr_t *, int *); +export int pthread_attr_getscope(const pthread_attr_t *, int *); +export int pthread_attr_getstackaddr(const pthread_attr_t *, void **); +export int pthread_attr_getstacksize(const pthread_attr_t *, size_t *); +export int pthread_attr_init(pthread_attr_t *); +export int pthread_attr_setdetachstate(pthread_attr_t *, int); +export int pthread_attr_setguardsize(pthread_attr_t *, size_t); +export int pthread_attr_setinheritsched(pthread_attr_t *, int); +export int pthread_attr_setschedparam(pthread_attr_t *, const struct sched_param *); +export int pthread_attr_setschedpolicy(pthread_attr_t *, int); +export int pthread_attr_setscope(pthread_attr_t *, int); +export int pthread_attr_setstackaddr(pthread_attr_t *, void *); +export int pthread_attr_setstacksize(pthread_attr_t *, size_t); +export int pthread_cancel(pthread_t); +export void pthread_cleanup_push(void (*)(void *), void *); +export void pthread_cleanup_pop(int); +export int pthread_cond_broadcast(pthread_cond_t *); +export int pthread_cond_destroy(pthread_cond_t *); +export int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); +export int pthread_cond_signal(pthread_cond_t *); +export int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, const struct timespec *); +export int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); +export int pthread_condattr_destroy(pthread_condattr_t *); +export int pthread_condattr_getpshared(const pthread_condattr_t *, int *); +export int pthread_condattr_init(pthread_condattr_t *); +export int pthread_condattr_setpshared(pthread_condattr_t *, int); +export int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); +export int pthread_detach(pthread_t); +export int pthread_equal(pthread_t, pthread_t); +export void pthread_exit(void *); +export int pthread_getconcurrency(void); +export int pthread_getschedparam(pthread_t, int *, struct sched_param *); +export void *pthread_getspecific(pthread_key_t); +export int pthread_join(pthread_t, void **); +export int pthread_key_create(pthread_key_t *, void (*)(void *)); +export int pthread_key_delete(pthread_key_t); +export int pthread_mutex_destroy(pthread_mutex_t *); +export int pthread_mutex_getprioceiling(const pthread_mutex_t *, int *); +export int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); + +export int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + if (mutex->locked) + return EBUSY; + mutex->locked = 1; + return 0; +} + +export int pthread_mutex_setprioceiling(pthread_mutex_t *, int, int *); +export int pthread_mutex_trylock(pthread_mutex_t *); + +export int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + if (!mutex->locked) + return EPERM; + mutex->locked = 0; + return 0; +} + +export int pthread_mutexattr_destroy(pthread_mutexattr_t *); +export int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *, int *); +export int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *, int *); +export int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *); +export int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *); +export int pthread_mutexattr_init(pthread_mutexattr_t *); +export int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *, int); +export int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int); +export int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int); +export int pthread_mutexattr_settype(pthread_mutexattr_t *, int); +export int pthread_once(pthread_once_t *, void (*)(void)); +export int pthread_rwlock_destroy(pthread_rwlock_t *); +export int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); +export int pthread_rwlock_rdlock(pthread_rwlock_t *); +export int pthread_rwlock_tryrdlock(pthread_rwlock_t *); +export int pthread_rwlock_trywrlock(pthread_rwlock_t *); +export int pthread_rwlock_unlock(pthread_rwlock_t *); +export int pthread_rwlock_wrlock(pthread_rwlock_t *); +export int pthread_rwlockattr_destroy(pthread_rwlockattr_t *); +export int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *); +export int pthread_rwlockattr_init(pthread_rwlockattr_t *); +export int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int); + +export pthread_t pthread_self(void) +{ + pthread_t tid; + __asm__ __volatile__("mov %%fs:0, %0" + : "=r"(tid)); + return tid; +} + +export int pthread_setcancelstate(int, int *); +export int pthread_setcanceltype(int, int *); +export int pthread_setconcurrency(int); +export int pthread_setschedparam(pthread_t, int, const struct sched_param *); +export int pthread_setspecific(pthread_key_t, const void *); +export void pthread_testcancel(void); diff --git a/Userspace/libc/src/std/signal.c b/Userspace/libc/src/std/signal.c new file mode 100644 index 00000000..943a3a0c --- /dev/null +++ b/Userspace/libc/src/std/signal.c @@ -0,0 +1,47 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include + +export int kill(pid_t pid, int sig) +{ + return syscall2(SYS_KILL, pid, sig); +} + +export int killpg(pid_t, int); +export void psiginfo(const siginfo_t *, const char *); +export void psignal(int, const char *); +export int pthread_kill(pthread_t, int); +export int pthread_sigmask(int, const sigset_t *restrict, sigset_t *restrict); +export int raise(int); +export int sig2str(int, char *); +export int sigaction(int, const struct sigaction *restrict, struct sigaction *restrict); +export int sigaddset(sigset_t *, int); +export int sigaltstack(const stack_t *restrict, stack_t *restrict); +export int sigdelset(sigset_t *, int); +export int sigemptyset(sigset_t *); +export int sigfillset(sigset_t *); +export int sigismember(const sigset_t *, int); +export void (*signal(int, void (*)(int)))(int); +export int sigpending(sigset_t *); +export int sigprocmask(int, const sigset_t *restrict, sigset_t *restrict); +export int sigqueue(pid_t, int, union sigval); +export int sigsuspend(const sigset_t *); +export int sigtimedwait(const sigset_t *restrict, siginfo_t *restrict, const struct timespec *restrict); +export int sigwait(const sigset_t *restrict, int *restrict); +export int sigwaitinfo(const sigset_t *restrict, siginfo_t *restrict); +export int str2sig(const char *restrict, int *restrict); diff --git a/Userspace/libc/src/std/spawn.c b/Userspace/libc/src/std/spawn.c deleted file mode 100644 index f52ad488..00000000 --- a/Userspace/libc/src/std/spawn.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -int posix_spawn(pid_t *pid, const char *path, - const posix_spawn_file_actions_t *file_actions, - const posix_spawnattr_t *attrp, - char *const argv[], - char *const envp[]) -{ -} - -int posix_spawnp(pid_t *pid, const char *file, - const posix_spawn_file_actions_t *file_actions, - const posix_spawnattr_t *attrp, - char *const argv[], - char *const envp[]) -{ -} diff --git a/Userspace/libc/src/std/std_init.c b/Userspace/libc/src/std/std_init.c deleted file mode 100644 index 39bb930b..00000000 --- a/Userspace/libc/src/std/std_init.c +++ /dev/null @@ -1,13 +0,0 @@ -#include - -#include "../../../Kernel/syscalls.h" - -void __libc_init_std(void) -{ - /* FIXME: Temporal workaround */ - // int IsCritical = syscall1(_KernelCTL, 6 /* KCTL_IS_CRITICAL */); -} - -void __libc_fini_std(void) -{ -} diff --git a/Userspace/libc/src/std/stdio.c b/Userspace/libc/src/std/stdio.c new file mode 100644 index 00000000..bde21155 --- /dev/null +++ b/Userspace/libc/src/std/stdio.c @@ -0,0 +1,343 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include +#include "../print/printf.h" + +struct _IO_FILE *_i_open_files[256]; + +export FILE *stdin; +export FILE *stdout; +export FILE *stderr; + +void __init_stdio(void) +{ + stdin = malloc(sizeof(FILE)); + stdin->fd = 0; + stdin->buffer = malloc(4096); + stdin->buffer_size = 4096; + stdin->buffer_pos = 0; + stdin->flags = _i_READ; + stdin->error = 0; + stdin->eof = 0; + + stdout = malloc(sizeof(FILE)); + stdout->fd = 1; + stdout->buffer = malloc(4096); + stdout->buffer_size = 4096; + stdout->buffer_pos = 0; + stdout->flags = _i_WRITE; + stdout->error = 0; + stdout->eof = 0; + + stderr = malloc(sizeof(FILE)); + stderr->fd = 2; + stderr->buffer = malloc(4096); + stderr->buffer_size = 4096; + stderr->buffer_pos = 0; + stderr->flags = _i_WRITE; + stderr->error = 0; + stderr->eof = 0; + + _i_open_files[0] = stdin; + _i_open_files[1] = stdout; + _i_open_files[2] = stderr; +} + +export void clearerr(FILE *); +export char *ctermid(char *); +export int dprintf(int, const char *restrict, ...); + +export int fclose(FILE *stream) +{ + if (!stream) + return EOF; + + if (stream->buffer) + free(stream->buffer); + + call_close(stream->fd); + _i_open_files[stream->fd] = NULL; + free(stream); + return 0; +} + +export FILE *fdopen(int, const char *); +export int feof(FILE *); +export int ferror(FILE *); + +export int fflush(FILE *stream) +{ + if (!stream) + return EOF; + + if (stream->flags & _i_WRITE) + { + if (stream->buffer_pos > 0) + { + ssize_t written = call_write(stream->fd, stream->buffer, stream->buffer_pos); + if (written < 0) + { + stream->error = 1; + return EOF; + } + stream->buffer_pos = 0; + } + } + else if (stream->flags & _i_READ) + { + stream->buffer_pos = 0; + stream->buffer_size = 0; + } + return 0; +} + +export int fgetc(FILE *); +export int fgetpos(FILE *restrict, fpos_t *restrict); +export char *fgets(char *restrict, int, FILE *restrict); +export int fileno(FILE *); +export void flockfile(FILE *); +export FILE *fmemopen(void *restrict, size_t, const char *restrict); + +export FILE *fopen(const char *restrict pathname, const char *restrict mode) +{ + int flags = 0; + mode_t perm = 0; + + if (strcmp(mode, "r") == 0) + flags = __SYS_O_RDONLY; + else if (strcmp(mode, "r+") == 0) + flags = __SYS_O_RDWR; + else if (strcmp(mode, "w") == 0) + { + flags = __SYS_O_WRONLY | __SYS_O_CREAT | __SYS_O_TRUNC; + perm = 0644; + } + else if (strcmp(mode, "w+") == 0) + { + flags = __SYS_O_RDWR | __SYS_O_CREAT | __SYS_O_TRUNC; + perm = 0644; + } + else if (strcmp(mode, "a") == 0) + { + flags = __SYS_O_WRONLY | __SYS_O_CREAT | __SYS_O_APPEND; + perm = 0644; + } + else if (strcmp(mode, "a+") == 0) + { + flags = __SYS_O_RDWR | __SYS_O_CREAT | __SYS_O_APPEND; + perm = 0644; + } + else + return NULL; + + int fd = call_open(pathname, flags, perm); + if (fd < 0) + return NULL; + + FILE *file = malloc(sizeof(FILE)); + if (!file) + return NULL; + + file->fd = fd; + file->buffer = malloc(4096); + file->buffer_size = 4096; + file->buffer_pos = 0; + file->flags = flags; + file->error = 0; + file->eof = 0; + + _i_open_files[fd] = file; + return file; +} + +export int fprintf(FILE *restrict, const char *restrict, ...); + +export int fputc(int c, FILE *stream) +{ + if (!stream || !(stream->flags & _i_WRITE)) + return EOF; + + stream->buffer[stream->buffer_pos++] = (char)c; + + if (stream->buffer_pos >= stream->buffer_size) + { + if (fflush(stream) == EOF) + return EOF; + } + + return (unsigned char)c; +} + +export int fputs(const char *restrict s, FILE *restrict stream) +{ + if (!stream || !(stream->flags & _i_WRITE)) + return EOF; + + while (*s) + { + if (fputc(*s++, stream) == EOF) + return EOF; + } + + return 0; +} + +export size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream) +{ + size_t total_bytes = size * nitems; + size_t bytes_read = 0; + + while (bytes_read < total_bytes) + { + if (stream->buffer_pos >= stream->buffer_size) + { + int res = call_read(stream->fd, stream->buffer, stream->buffer_size); + if (res <= 0) + { + if (res == 0) + stream->eof = 1; + else + stream->error = 1; + break; + } + stream->buffer_pos = 0; + stream->buffer_size = res; + } + + size_t bytes_to_copy = total_bytes - bytes_read; + size_t available = stream->buffer_size - stream->buffer_pos; + if (bytes_to_copy > available) + bytes_to_copy = available; + + memcpy((char *)ptr + bytes_read, stream->buffer + stream->buffer_pos, bytes_to_copy); + stream->buffer_pos += bytes_to_copy; + bytes_read += bytes_to_copy; + } + + return bytes_read / size; +} + +export FILE *freopen(const char *restrict, const char *restrict, FILE *restrict); +export int fscanf(FILE *restrict, const char *restrict, ...); + +export int fseek(FILE *stream, long offset, int whence) +{ + int res = call_seek(stream->fd, offset, whence); + if (res < 0) + { + stream->error = 1; + return EOF; + } + stream->buffer_pos = 0; + stream->buffer_size = 0; + return 0; +} + +export int fseeko(FILE *, off_t, int); +export int fsetpos(FILE *, const fpos_t *); + +export long ftell(FILE *stream) +{ + return call_tell(stream->fd); +} + +export off_t ftello(FILE *); +export int ftrylockfile(FILE *); +export void funlockfile(FILE *); + +export size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream) +{ + size_t total_bytes = size * nitems; + size_t bytes_written = 0; + + while (bytes_written < total_bytes) + { + size_t bytes_to_copy = total_bytes - bytes_written; + size_t space_available = stream->buffer_size - stream->buffer_pos; + + if (bytes_to_copy > space_available) + bytes_to_copy = space_available; + + memcpy(stream->buffer + stream->buffer_pos, (const char *)ptr + bytes_written, bytes_to_copy); + stream->buffer_pos += bytes_to_copy; + bytes_written += bytes_to_copy; + + if (stream->buffer_pos == stream->buffer_size) + { + if (call_write(stream->fd, stream->buffer, stream->buffer_size) != stream->buffer_size) + { + stream->error = 1; + break; + } + stream->buffer_pos = 0; + } + } + + return bytes_written / size; +} + +export int getc(FILE *); +export int getchar(void); +export int getc_unlocked(FILE *); +export int getchar_unlocked(void); +export ssize_t getdelim(char **restrict, size_t *restrict, int, FILE *restrict); +export ssize_t getline(char **restrict, size_t *restrict, FILE *restrict); +export FILE *open_memstream(char **, size_t *); +export int pclose(FILE *); +export void perror(const char *); +export FILE *popen(const char *, const char *); + +export int printf(const char *restrict format, ...) +{ + va_list args; + va_start(args, format); + int ret = vprintf_(format, args); + va_end(args); + return ret; +} + +export int putc(int c, FILE *stream) { return fputc(c, stream); } +export int putchar(int c) { return putc(c, stdout); } +export int putc_unlocked(int c, FILE *stream) { return fputc(c, stream); } +export int putchar_unlocked(int c) { return putc_unlocked(c, stdout); } +export int puts(const char *s) { return fputs(s, stdout); } + +export int remove(const char *); +export int rename(const char *, const char *); +export int renameat(int, const char *, int, const char *); +export void rewind(FILE *); +export int scanf(const char *restrict, ...); +export void setbuf(FILE *restrict, char *restrict); +export int setvbuf(FILE *restrict, char *restrict, int, size_t); +export int snprintf(char *restrict, size_t, const char *restrict, ...); +export int sprintf(char *restrict, const char *restrict, ...); +export int sscanf(const char *restrict, const char *restrict, ...); +export FILE *tmpfile(void); +export char *tmpnam(char *); +export int ungetc(int, FILE *); +export int vdprintf(int, const char *restrict, va_list); +export int vfprintf(FILE *restrict, const char *restrict, va_list); +export int vfscanf(FILE *restrict, const char *restrict, va_list); +export int vprintf(const char *restrict, va_list); +export int vscanf(const char *restrict, va_list); +export int vsnprintf(char *restrict, size_t, const char *restrict, va_list); +export int vsprintf(char *restrict, const char *restrict, va_list); +export int vsscanf(const char *restrict, const char *restrict, va_list); diff --git a/Userspace/libc/src/std/stdlib.c b/Userspace/libc/src/std/stdlib.c new file mode 100644 index 00000000..4a31ac51 --- /dev/null +++ b/Userspace/libc/src/std/stdlib.c @@ -0,0 +1,105 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include "../mem/liballoc_1_1.h" + +#define MAX_ATEXIT_FUNCS 32 +typedef void (*atexit_func_t)(void); +static atexit_func_t __atexit_funcs[MAX_ATEXIT_FUNCS]; +static int __num_atexit_funcs = 0; + +export long a64l(const char *); +export _Noreturn void abort(void) +{ + kill(getpid(), SIGABRT); + __builtin_unreachable(); +} + +export int abs(int); + +export int atexit(void (*func)(void)) +{ + if (__num_atexit_funcs >= MAX_ATEXIT_FUNCS) + return -1; + __atexit_funcs[__num_atexit_funcs++] = func; + return 0; +} + +export double atof(const char *); +export int atoi(const char *); +export long int atol(const char *); +export void *bsearch(const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); +export void *calloc(size_t, size_t); +export div_t div(int, int); +export double drand48(void); +export char *ecvt(double, int, int *, int *); +export double erand48(unsigned short int[3]); + +export void exit(int status) +{ + for (int i = __num_atexit_funcs - 1; i >= 0; --i) + __atexit_funcs[i](); + _exit(status); + __builtin_unreachable(); +} + +export char *fcvt(double, int, int *, int *); +export void free(void *ptr) { return PREFIX(free)(ptr); } +export char *gcvt(double, int, char *); +export char *getenv(const char *); +export int getsubopt(char **, char *const *, char **); +export int grantpt(int); +export char *initstate(unsigned int, char *, size_t); +export long int jrand48(unsigned short int[3]); +export char *l64a(long); +export long int labs(long int); +export void lcong48(unsigned short int[7]); +export ldiv_t ldiv(long int, long int); +export long int lrand48(void); +export void *malloc(size_t size) { return PREFIX(malloc)(size); } +export int mblen(const char *, size_t); +export size_t mbstowcs(wchar_t *, const char *, size_t); +export int mbtowc(wchar_t *, const char *, size_t); +export char *mktemp(char *); +export int mkstemp(char *); +export long int mrand48(void); +export long int nrand48(unsigned short int[3]); +export char *ptsname(int); +export int putenv(char *); +export void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); +export int rand(void); +export int rand_r(unsigned int *); +export long random(void); +export void *realloc(void *, size_t); +export char *realpath(const char *, char *); +export unsigned short int seed48(unsigned short int[3]); +export void setkey(const char *); +export char *setstate(const char *); +export void srand(unsigned int); +export void srand48(long int); +export void srandom(unsigned); +export double strtod(const char *, char **); +export long int strtol(const char *, char **, int); +export unsigned long int strtoul(const char *, char **, int); +export int system(const char *); +export int ttyslot(void); +export int unlockpt(int); +export void *valloc(size_t); +export size_t wcstombs(char *, const wchar_t *, size_t); +export int wctomb(char *, wchar_t); diff --git a/Userspace/libc/src/std/string.c b/Userspace/libc/src/std/string.c new file mode 100644 index 00000000..a283583a --- /dev/null +++ b/Userspace/libc/src/std/string.c @@ -0,0 +1,86 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include + +export void *memccpy(void *restrict, const void *restrict, int, size_t); +export void *memchr(const void *, int, size_t); +export int memcmp(const void *, const void *, size_t); + +export void *memcpy(void *restrict s1, const void *restrict s2, size_t n) +{ + unsigned char *dest = (unsigned char *)s1; + const unsigned char *src = (const unsigned char *)s2; + + while (n >= sizeof(unsigned long)) + { + *(unsigned long *)dest = *(const unsigned long *)src; + dest += sizeof(unsigned long); + src += sizeof(unsigned long); + n -= sizeof(unsigned long); + } + + while (n--) + *dest++ = *src++; + return s1; +} + +export void *memmem(const void *, size_t, const void *, size_t); +export void *memmove(void *, const void *, size_t); +export void *memset(void *, int, size_t); +export char *stpcpy(char *restrict, const char *restrict); +export char *stpncpy(char *restrict, const char *restrict, size_t); +export char *strcat(char *restrict, const char *restrict); +export char *strchr(const char *, int); + +export int strcmp(const char *s1, const char *s2) +{ + while (*s1 && (*s1 == *s2)) + { + s1++; + s2++; + } + + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +export int strcoll(const char *, const char *); +export int strcoll_l(const char *, const char *, locale_t); +export char *strcpy(char *restrict, const char *restrict); +export size_t strcspn(const char *, const char *); +export char *strdup(const char *); +export char *strerror(int); +export char *strerror_l(int, locale_t); +export int strerror_r(int, char *, size_t); +export size_t strlcat(char *restrict, const char *restrict, size_t); +export size_t strlcpy(char *restrict, const char *restrict, size_t); +export size_t strlen(const char *); +export char *strncat(char *restrict, const char *restrict, size_t); +export int strncmp(const char *, const char *, size_t); +export char *strncpy(char *restrict, const char *restrict, size_t); +export char *strndup(const char *, size_t); +export size_t strnlen(const char *, size_t); +export char *strpbrk(const char *, const char *); +export char *strrchr(const char *, int); +export char *strsignal(int); +export size_t strspn(const char *, const char *); +export char *strstr(const char *, const char *); +export char *strtok(char *restrict, const char *restrict); +export char *strtok_r(char *restrict, const char *restrict, char **restrict); +export size_t strxfrm(char *restrict, const char *restrict, size_t); +export size_t strxfrm_l(char *restrict, const char *restrict, size_t, locale_t); diff --git a/Userspace/libc/src/std/string/memop.c b/Userspace/libc/src/std/string/memop.c deleted file mode 100644 index f68db02a..00000000 --- a/Userspace/libc/src/std/string/memop.c +++ /dev/null @@ -1,346 +0,0 @@ -#include - -#include // For PUBLIC - -/* Some of the functions are from musl library */ -/* https://www.musl-libc.org/ */ -/* -Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -PUBLIC void *memcpy(void *dest, const void *src, size_t n) -{ - unsigned char *d = dest; - const unsigned char *s = src; - -#ifdef __GNUC__ - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define LS >> -#define RS << -#else -#define LS << -#define RS >> -#endif - - typedef uint32_t __attribute__((__may_alias__)) u32; - uint32_t w, x; - - for (; (uintptr_t)s % 4 && n; n--) - *d++ = *s++; - - if ((uintptr_t)d % 4 == 0) - { - for (; n >= 16; s += 16, d += 16, n -= 16) - { - *(u32 *)(d + 0) = *(u32 *)(s + 0); - *(u32 *)(d + 4) = *(u32 *)(s + 4); - *(u32 *)(d + 8) = *(u32 *)(s + 8); - *(u32 *)(d + 12) = *(u32 *)(s + 12); - } - if (n & 8) - { - *(u32 *)(d + 0) = *(u32 *)(s + 0); - *(u32 *)(d + 4) = *(u32 *)(s + 4); - d += 8; - s += 8; - } - if (n & 4) - { - *(u32 *)(d + 0) = *(u32 *)(s + 0); - d += 4; - s += 4; - } - if (n & 2) - { - *d++ = *s++; - *d++ = *s++; - } - if (n & 1) - { - *d = *s; - } - return dest; - } - - if (n >= 32) - { - switch ((uintptr_t)d % 4) - { - case 1: - { - w = *(u32 *)s; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - n -= 3; - for (; n >= 17; s += 16, d += 16, n -= 16) - { - x = *(u32 *)(s + 1); - *(u32 *)(d + 0) = (w LS 24) | (x RS 8); - w = *(u32 *)(s + 5); - *(u32 *)(d + 4) = (x LS 24) | (w RS 8); - x = *(u32 *)(s + 9); - *(u32 *)(d + 8) = (w LS 24) | (x RS 8); - w = *(u32 *)(s + 13); - *(u32 *)(d + 12) = (x LS 24) | (w RS 8); - } - break; - } - case 2: - { - w = *(u32 *)s; - *d++ = *s++; - *d++ = *s++; - n -= 2; - for (; n >= 18; s += 16, d += 16, n -= 16) - { - x = *(u32 *)(s + 2); - *(u32 *)(d + 0) = (w LS 16) | (x RS 16); - w = *(u32 *)(s + 6); - *(u32 *)(d + 4) = (x LS 16) | (w RS 16); - x = *(u32 *)(s + 10); - *(u32 *)(d + 8) = (w LS 16) | (x RS 16); - w = *(u32 *)(s + 14); - *(u32 *)(d + 12) = (x LS 16) | (w RS 16); - } - break; - } - case 3: - { - w = *(u32 *)s; - *d++ = *s++; - n -= 1; - for (; n >= 19; s += 16, d += 16, n -= 16) - { - x = *(u32 *)(s + 3); - *(u32 *)(d + 0) = (w LS 8) | (x RS 24); - w = *(u32 *)(s + 7); - *(u32 *)(d + 4) = (x LS 8) | (w RS 24); - x = *(u32 *)(s + 11); - *(u32 *)(d + 8) = (w LS 8) | (x RS 24); - w = *(u32 *)(s + 15); - *(u32 *)(d + 12) = (x LS 8) | (w RS 24); - } - break; - } - default: - break; - } - } - - if (n & 16) - { - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - } - - if (n & 8) - { - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - } - - if (n & 4) - { - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - } - - if (n & 2) - { - *d++ = *s++; - *d++ = *s++; - } - - if (n & 1) - { - *d = *s; - } - return dest; -#endif - - for (; n; n--) - *d++ = *s++; - return dest; -} - -PUBLIC void *memset(void *dest, int c, size_t n) -{ - unsigned char *s = dest; - size_t k; - - if (!n) - return dest; - - s[0] = c; - s[n - 1] = c; - - if (n <= 2) - return dest; - - s[1] = c; - s[2] = c; - s[n - 2] = c; - s[n - 3] = c; - - if (n <= 6) - return dest; - - s[3] = c; - s[n - 4] = c; - - if (n <= 8) - return dest; - - k = -(uintptr_t)s & 3; - s += k; - n -= k; - n &= -4; - -#ifdef __GNUC__ - typedef uint32_t __attribute__((__may_alias__)) u32; - typedef uint64_t __attribute__((__may_alias__)) u64; - - u32 c32 = ((u32)-1) / 255 * (unsigned char)c; - *(u32 *)(s + 0) = c32; - *(u32 *)(s + n - 4) = c32; - - if (n <= 8) - return dest; - - *(u32 *)(s + 4) = c32; - *(u32 *)(s + 8) = c32; - *(u32 *)(s + n - 12) = c32; - *(u32 *)(s + n - 8) = c32; - - if (n <= 24) - return dest; - - *(u32 *)(s + 12) = c32; - *(u32 *)(s + 16) = c32; - *(u32 *)(s + 20) = c32; - *(u32 *)(s + 24) = c32; - *(u32 *)(s + n - 28) = c32; - *(u32 *)(s + n - 24) = c32; - *(u32 *)(s + n - 20) = c32; - *(u32 *)(s + n - 16) = c32; - - k = 24 + ((uintptr_t)s & 4); - s += k; - n -= k; - - u64 c64 = c32 | ((u64)c32 << 32); - for (; n >= 32; n -= 32, s += 32) - { - *(u64 *)(s + 0) = c64; - *(u64 *)(s + 8) = c64; - *(u64 *)(s + 16) = c64; - *(u64 *)(s + 24) = c64; - } -#else - for (; n; n--, s++) - *s = c; -#endif - - return dest; -} - -PUBLIC void *memmove(void *dest, const void *src, size_t n) -{ -#ifdef __GNUC__ - typedef __attribute__((__may_alias__)) size_t WT; -#define WS (sizeof(WT)) -#endif - - char *d = dest; - const char *s = src; - - if (d == s) - return d; - - if ((uintptr_t)s - (uintptr_t)d - n <= -2 * n) - return memcpy(d, s, n); - - if (d < s) - { -#ifdef __GNUC__ - if ((uintptr_t)s % WS == (uintptr_t)d % WS) - { - while ((uintptr_t)d % WS) - { - if (!n--) - return dest; - - *d++ = *s++; - } - for (; n >= WS; n -= WS, d += WS, s += WS) - *(WT *)d = *(WT *)s; - } -#endif - for (; n; n--) - *d++ = *s++; - } - else - { -#ifdef __GNUC__ - if ((uintptr_t)s % WS == (uintptr_t)d % WS) - { - while ((uintptr_t)(d + n) % WS) - { - if (!n--) - return dest; - - d[n] = s[n]; - } - while (n >= WS) - n -= WS, *(WT *)(d + n) = *(WT *)(s + n); - } -#endif - while (n) - n--, d[n] = s[n]; - } - - return dest; -} diff --git a/Userspace/libc/src/std/string/string.c b/Userspace/libc/src/std/string/string.c deleted file mode 100644 index 0e340c4c..00000000 --- a/Userspace/libc/src/std/string/string.c +++ /dev/null @@ -1,125 +0,0 @@ -#include - -#include "../../mem/liballoc_1_1.h" - -PUBLIC size_t strlen(const char *str) -{ - long unsigned i = 0; - if (str) - while (str[i] != '\0') - ++i; - return i; -} - -PUBLIC int strcmp(const char *l, const char *r) -{ - for (; *l == *r && *l; l++, r++) - ; - return *(unsigned char *)l - *(unsigned char *)r; -} - -PUBLIC int strncmp(const char *s1, const char *s2, size_t n) -{ - for (size_t i = 0; i < n; i++) - { - char c1 = s1[i], c2 = s2[i]; - if (c1 != c2) - return c1 - c2; - if (!c1) - return 0; - } - return 0; -} - -PUBLIC int strcasecmp(const char *s1, const char *s2) -{ - const unsigned char *p1 = (const unsigned char *)s1; - const unsigned char *p2 = (const unsigned char *)s2; - int result; - if (p1 == p2) - return 0; - while ((result = tolower(*p1) - tolower(*p2++)) == 0) - if (*p1++ == '\0') - break; - return result; -} - -PUBLIC int strncasecmp(const char *string1, const char *string2, size_t count) -{ - if (count) - { - const unsigned char *s1 = (const unsigned char *)string1; - const unsigned char *s2 = (const unsigned char *)string2; - int result; - do - { - if ((result = tolower(*s1) - tolower(*s2++)) != 0 || !*s1++) - break; - } while (--count); - return result; - } - return 0; -} - -PUBLIC char *strstr(const char *haystack, const char *needle) -{ - const char *a = haystack, *b = needle; - while (1) - { - if (!*b) - return (char *)haystack; - if (!*a) - return NULL; - if (*a++ != *b++) - { - a = ++haystack; - b = needle; - } - } -} - -PUBLIC char *strncpy(char *destination, const char *source, unsigned long num) -{ - if (destination == NULL) - return NULL; - char *ptr = destination; - while (*source && num--) - { - *destination = *source; - destination++; - source++; - } - *destination = '\0'; - return ptr; -} - -PUBLIC char *strdup(const char *s) -{ - char *buf = (char *)__malloc(strlen((char *)s) + 1); - strncpy(buf, s, strlen(s) + 1); - return buf; -} - -PUBLIC char *strchr(char const *s, int c) -{ - size_t len = strlen(s); - for (size_t i = 0; i < len; i++) - if (s[i] == c) - return (char *)s + i; - - return NULL; -} - -PUBLIC char *strrchr(char const *s, int c) -{ - size_t len = strlen(s); - size_t pos = len; - - while (s[pos] != c && pos-- != 0) - ; - - if (pos == len) - return NULL; - - return (char *)s + pos; -} diff --git a/Userspace/libc/src/std/sys/mman.c b/Userspace/libc/src/std/sys/mman.c new file mode 100644 index 00000000..29146709 --- /dev/null +++ b/Userspace/libc/src/std/sys/mman.c @@ -0,0 +1,49 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include + +export int mlock(const void *, size_t); +export int mlockall(int); + +export void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) +{ + return (void *)__check_errno((__iptr)call_mmap(addr, len, prot, flags, fildes, off), (__iptr)MAP_FAILED); +} + +export int mprotect(void *addr, size_t len, int prot) +{ + return __check_errno(call_mprotect(addr, len, prot), -1); +} + +export int msync(void *, size_t, int); +export int munlock(const void *, size_t); +export int munlockall(void); + +export int munmap(void *addr, size_t len) +{ + return __check_errno(call_munmap(addr, len), -1); +} + +export int posix_madvise(void *, size_t, int); +export int posix_mem_offset(const void *restrict, size_t, off_t *restrict, size_t *restrict, int *restrict); +export int posix_typed_mem_get_info(int, struct posix_typed_mem_info *); +export int posix_typed_mem_open(const char *, int, int); +export int shm_open(const char *, int, mode_t); +export int shm_unlink(const char *); diff --git a/Userspace/libc/src/std/sys/wait.c b/Userspace/libc/src/std/sys/wait.c deleted file mode 100644 index 1b643e09..00000000 --- a/Userspace/libc/src/std/sys/wait.c +++ /dev/null @@ -1,14 +0,0 @@ -#include - -PUBLIC pid_t wait(int *wstatus) -{ - return waitpid(-1, &wstatus, 0); -} - -PUBLIC pid_t waitpid(pid_t pid, int *wstatus, int options) -{ -} - -PUBLIC int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options) -{ -} diff --git a/Userspace/libc/src/std/sys_stat.c b/Userspace/libc/src/std/sys_stat.c deleted file mode 100644 index aa0b8253..00000000 --- a/Userspace/libc/src/std/sys_stat.c +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#include // For PUBLIC - -PUBLIC int mkdir(const char *path, mode_t mode) -{ - return 0; -} - -PUBLIC int remove(const char *pathname) -{ - return 0; -} - -PUBLIC int rename(const char *oldpath, const char *newpath) -{ - return 0; -} diff --git a/Userspace/libc/src/std/uni/exe.c b/Userspace/libc/src/std/uni/exe.c deleted file mode 100644 index 85274adb..00000000 --- a/Userspace/libc/src/std/uni/exe.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include "../../../Kernel/syscalls.h" - -PUBLIC int execl(const char *pathname, const char *arg, ...) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC int execlp(const char *file, const char *arg, ...) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC int execle(const char *pathname, const char *arg, ...) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC int execv(const char *pathname, char *const argv[]) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC int execvp(const char *file, char *const argv[]) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC int execvpe(const char *file, char *const argv[], char *const envp[]) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC int execve(const char *pathname, char *const argv[], char *const envp[]) -{ - errno = ENOSYS; - return -1; -} - -PUBLIC pid_t fork(void) -{ - return syscall0(sc_fork); -} diff --git a/Userspace/libc/src/std/uni/sleep.c b/Userspace/libc/src/std/uni/sleep.c deleted file mode 100644 index 4bb19086..00000000 --- a/Userspace/libc/src/std/uni/sleep.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include -#include "../../../Kernel/syscalls.h" - -PUBLIC unsigned int sleep(unsigned int seconds) -{ - // return syscall1(sys_Sleep, seconds * 1000000); -} - -PUBLIC int usleep(useconds_t usec) -{ - // return syscall1(sys_Sleep, usec); -} diff --git a/Userspace/libc/src/std/unistd.c b/Userspace/libc/src/std/unistd.c new file mode 100644 index 00000000..53388fb8 --- /dev/null +++ b/Userspace/libc/src/std/unistd.c @@ -0,0 +1,112 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include + +export char *optarg; +export int optind, opterr, optopt; + +export int access(const char *, int); +export unsigned int alarm(unsigned int); +export int brk(void *); +export int chdir(const char *); +export int chroot(const char *); +export int chown(const char *, uid_t, gid_t); +export int close(int); +export size_t confstr(int, char *, size_t); +export char *crypt(const char *, const char *); +export char *ctermid(char *); +export char *cuserid(char *s); +export int dup(int); +export int dup2(int, int); +export void encrypt(char[64], int); +export int execl(const char *, const char *, ...); +export int execle(const char *, const char *, ...); +export int execlp(const char *, const char *, ...); +export int execv(const char *, char *const[]); +export int execve(const char *, char *const[], char *const[]); +export int execvp(const char *, char *const[]); +export void _exit(int); +export int fchown(int, uid_t, gid_t); +export int fchdir(int); +export int fdatasync(int); +export pid_t fork(void); +export long int fpathconf(int, int); +export int fsync(int); +export int ftruncate(int, off_t); +export char *getcwd(char *, size_t); +export int getdtablesize(void); +export gid_t getegid(void); +export uid_t geteuid(void); +export gid_t getgid(void); +export int getgroups(int, gid_t[]); +export long gethostid(void); +export char *getlogin(void); +export int getlogin_r(char *, size_t); +export int getopt(int, char *const[], const char *); + +export int getpagesize(void) { return 0x1000; } /* TODO: getpagesize */ + +export char *getpass(const char *); +export pid_t getpgid(pid_t); +export pid_t getpgrp(void); + +export pid_t getpid(void) { return syscall0(SYS_GETPID); } +export pid_t getppid(void) { return syscall0(SYS_GETPPID); } + +export pid_t getsid(pid_t); +export uid_t getuid(void); +export char *getwd(char *); +export int isatty(int); +export int lchown(const char *, uid_t, gid_t); +export int link(const char *, const char *); +export int lockf(int, int, off_t); +export off_t lseek(int, off_t, int); +export int nice(int); +export long int pathconf(const char *, int); +export int pause(void); +export int pipe(int[2]); +export ssize_t pread(int, void *, size_t, off_t); +export int pthread_atfork(void (*)(void), void (*)(void), void (*)(void)); +export ssize_t pwrite(int, const void *, size_t, off_t); +export ssize_t read(int, void *, size_t); +export int readlink(const char *, char *, size_t); +export int rmdir(const char *); +export void *sbrk(intptr_t); +export int setgid(gid_t); +export int setpgid(pid_t, pid_t); +export pid_t setpgrp(void); +export int setregid(gid_t, gid_t); +export int setreuid(uid_t, uid_t); +export pid_t setsid(void); +export int setuid(uid_t); +export unsigned int sleep(unsigned int); +export void swab(const void *, void *, ssize_t); +export int symlink(const char *, const char *); +export void sync(void); +export long int sysconf(int); +export pid_t tcgetpgrp(int); +export int tcsetpgrp(int, pid_t); +export int truncate(const char *, off_t); +export char *ttyname(int); +export int ttyname_r(int, char *, size_t); +export useconds_t ualarm(useconds_t, useconds_t); +export int unlink(const char *); +export int usleep(useconds_t); +export pid_t vfork(void); +export ssize_t write(int, const void *, size_t); diff --git a/Userspace/libc/src/syscall_check.cpp b/Userspace/libc/src/syscall_check.cpp new file mode 100644 index 00000000..378b3392 --- /dev/null +++ b/Userspace/libc/src/syscall_check.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include +#include + +static_assert(__SYS_PROT_READ == PROT_READ); +static_assert(__SYS_PROT_WRITE == PROT_WRITE); +static_assert(__SYS_PROT_EXEC == PROT_EXEC); +static_assert(__SYS_PROT_NONE == PROT_NONE); + +static_assert(__SYS_MAP_SHARED == MAP_SHARED); +static_assert(__SYS_MAP_PRIVATE == MAP_PRIVATE); +static_assert(__SYS_MAP_FIXED == MAP_FIXED); +static_assert(__SYS_MAP_ANONYMOUS == MAP_ANONYMOUS); +static_assert(__SYS_MAP_ANON == MAP_ANON); + +static_assert(__SYS_F_OK == F_OK); +static_assert(__SYS_R_OK == R_OK); +static_assert(__SYS_W_OK == W_OK); +static_assert(__SYS_X_OK == X_OK); + +static_assert(sizeof(__SYS_clockid_t) == sizeof(clockid_t)); +// static_assert(sizeof(__SYS_socklen_t) == sizeof(socklen_t)); + +static_assert(__SYS_SEEK_SET == SEEK_SET); +static_assert(__SYS_SEEK_CUR == SEEK_CUR); +static_assert(__SYS_SEEK_END == SEEK_END); + +static_assert(__SYS_SA_NOCLDSTOP == SA_NOCLDSTOP); +static_assert(__SYS_SA_NOCLDWAIT == SA_NOCLDWAIT); +static_assert(__SYS_SA_SIGINFO == SA_SIGINFO); +static_assert(__SYS_SA_ONSTACK == SA_ONSTACK); +static_assert(__SYS_SA_RESTART == SA_RESTART); +static_assert(__SYS_SA_NODEFER == SA_NODEFER); +static_assert(__SYS_SA_RESETHAND == SA_RESETHAND); + +static_assert(__SYS_CLOCK_MONOTONIC == CLOCK_MONOTONIC); +static_assert(__SYS_CLOCK_PROCESS_CPUTIME_ID == CLOCK_PROCESS_CPUTIME_ID); +static_assert(__SYS_CLOCK_REALTIME == CLOCK_REALTIME); +static_assert(__SYS_CLOCK_THREAD_CPUTIME_ID == CLOCK_THREAD_CPUTIME_ID); + +// static_assert( == ); diff --git a/Userspace/libc/src/tls.c b/Userspace/libc/src/tls.c new file mode 100644 index 00000000..b826cf3a --- /dev/null +++ b/Userspace/libc/src/tls.c @@ -0,0 +1,41 @@ +/* + This file is part of Fennix C Library. + + Fennix C Library is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix C Library. If not, see . +*/ + +#include +#include +#include +#include + +#include + +export __attribute__((naked, used, no_stack_protector)) void *__tls_get_addr(void *__data) +{ + __asm__("ud2"); +} + +int __init_pthread(void) +{ + __pthread *ptr = (__pthread *)call_mmap(0, + 0x1000, + __SYS_PROT_READ | __SYS_PROT_WRITE, + __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE, + -1, 0); + call_prctl(__SYS_SET_FS, ptr, 0, 0, 0); + ptr->Self = ptr; + ptr->CurrentError = 0; + return 0; +} diff --git a/Userspace/libs/Makefile b/Userspace/libs/Makefile index 5691647c..5232914e 100644 --- a/Userspace/libs/Makefile +++ b/Userspace/libs/Makefile @@ -1,5 +1,5 @@ build: -# cp -r include/* ../out/include + cp -a $(CURDIR)/include/. $(WORKSPACE_DIR)/out/include make -C libgcc build clean: diff --git a/Userspace/libs/libgcc/Makefile b/Userspace/libs/libgcc/Makefile index f597dcea..878ec9c0 100644 --- a/Userspace/libs/libgcc/Makefile +++ b/Userspace/libs/libgcc/Makefile @@ -1,14 +1,6 @@ -# Config file -include ../../../config.mk - NAME=gcc - OBJECT_NAME=lib$(NAME).a - -OUTPUT_DIR=../../out/lib/ - -CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc - +OUTPUT_DIR=$(WORKSPACE_DIR)/out/lib/ CRTBEGIN_PATH = $(shell $(CC) -print-file-name=libgcc.a) build: $(OBJECT_NAME) @@ -17,4 +9,3 @@ $(OBJECT_NAME): cp $(CRTBEGIN_PATH) $(OUTPUT_DIR) clean: -