Compare commits

...

43 Commits

Author SHA1 Message Date
86ca343cf0 interface: fs: remove old ifc definition file 2026-03-10 19:20:18 +00:00
b680ffdd5b meta: update kernel 2026-03-10 19:16:51 +00:00
14799e0d58 ld: use unistd i/o functions to open/read library files 2026-03-10 19:16:46 +00:00
b7452a449b bootstrap: update tarfs driver with libfs support 2026-03-10 19:16:22 +00:00
ea6ec785a9 lib: c: re-organise into separate static modules, plus a single shared library 2026-03-10 19:15:59 +00:00
6d88cf4bf3 lib: fs: implement mounting filesystems; reading, writing from files 2026-03-10 19:15:26 +00:00
aef0163017 lib: add libxpc to implement functionality needed by xpc interfaces 2026-03-10 19:14:37 +00:00
b0fda122e0 cmake: define BUILD_STATIC and BUILD_SHARED for library builds 2026-03-10 19:14:00 +00:00
79af171384 interface: fs: replace ifc definition with xpcg 2026-03-10 19:13:24 +00:00
5931642cc2 cmake: update inteface functions to use xpcg 2026-03-10 19:13:01 +00:00
26a49162e6 toolchain: replace ifc interface compiler with xpcg
xpcg is used to generate xpc interfaces
2026-03-10 19:12:14 +00:00
3a06c18e10 meta: update kernel 2026-03-06 20:20:55 +00:00
e46c20b572 services: ldd: update dependencies 2026-03-06 20:20:48 +00:00
0a07db360c test: update dependencies 2026-03-06 20:20:36 +00:00
1f2bd09d57 systemd: update dependencies 2026-03-06 20:20:18 +00:00
72b5801292 ld: remove test messages 2026-03-06 20:19:46 +00:00
5ad1babd03 bootstrap: replace fs stub msg handlers with libfs 2026-03-06 20:18:54 +00:00
d0abe3333d interface: fs: remmove uppercase message 2026-03-06 20:17:43 +00:00
5658e27445 lib: fs: add library for implementing a filesystem service 2026-03-06 20:17:23 +00:00
c4fd252f86 lib: launch: implement passing args and environment variables to new tasks 2026-03-06 20:16:51 +00:00
a6a2526502 lib: rosetta: add library to contain os-wide definitions 2026-03-06 20:16:25 +00:00
faeefe28ab toolchain: ifc: mpc: add support for sending/receiving handles 2026-03-06 20:15:36 +00:00
1c89acddbf cmake: fix some issues when building both shared and static versions of a library 2026-03-06 20:15:05 +00:00
f47be0cd67 cmake: report any missing symbols when building a shared library 2026-03-06 20:14:23 +00:00
68714fa0e5 lib: c: combine libc and ulibc
libc is now made up of several independent components, each of which is individually compiled into a static library.
they are then all combined into a single shared library.
2026-03-06 20:12:58 +00:00
267b893bf4 meta: update kernel 2026-02-26 20:55:40 +00:00
d6b3af6e67 toolchain: ifc: update mpc api usage 2026-02-26 20:51:41 +00:00
4be6f231e6 bootstrap: update channel creation 2026-02-26 20:51:19 +00:00
598bdb1410 ld: switch to ifc-generated ipc interface 2026-02-26 19:48:06 +00:00
285a80530d bootstrap: switch to ifc-generated ipc interface 2026-02-26 19:47:58 +00:00
3b50715559 lib: delete hand-written msg interface libraries 2026-02-26 19:47:37 +00:00
bdb2e0ed50 interface: add sample filesystem interface 2026-02-26 19:47:16 +00:00
01109948b2 meta: add tool to combine multiple compile-commands database files
this allows once instance of an lsp to understand multiple
cmake build directories.
2026-02-26 19:46:13 +00:00
dc93f7db4c cmake: add support for building and installing interfaces 2026-02-26 19:46:02 +00:00
b631fca0fd toolchain: add a program for compiling ipc interface definitions
ifc can be used to compile .if files into self-contained header-only C
libraries, which can be used to send/receive messages that conform
to the described interface.
2026-02-26 19:44:32 +00:00
e2b19c3e9a meta: update kernel 2026-02-23 21:58:35 +00:00
411423bf30 meta: add build type variable to configure_build 2026-02-23 21:58:09 +00:00
0183790af3 ld: send fs.open() message to bootstrap 2026-02-23 21:57:46 +00:00
418c426e83 bootstrap: replace bare string message with fs.open() 2026-02-23 21:57:01 +00:00
eca9d3915b lib: msg-fs: implement open() send/recv 2026-02-23 21:56:33 +00:00
d32988c56c lib: msg: implement generic messave init and receive 2026-02-23 21:56:16 +00:00
02d4f43151 cmake: fix run-kernel-monitor using unpatched kernel executable 2026-02-23 21:55:47 +00:00
1645fdfb42 cmake: disable kvm when debugging 2026-02-23 21:54:40 +00:00
143 changed files with 8999 additions and 8723 deletions

View File

@@ -11,6 +11,7 @@ include(Meta)
include(Sysroot)
include(BSP)
include(Arch)
include(Msg-Interface)
include(Templates)
bsp_reset()
@@ -20,6 +21,7 @@ sysroot_set_base(
PATH ${CMAKE_SOURCE_DIR}/base)
add_subdirectory(kernel)
add_subdirectory(interface)
add_subdirectory(sys)
add_subdirectory(lib)
add_subdirectory(services)

View File

@@ -15,7 +15,7 @@ set(CMAKE_ASM_COMPILER ${ASM_COMPILER})
SET(CMAKE_C_FLAGS "-ffreestanding -nostdlib -z max-page-size=0x1000 -m64 -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -D_64BIT -DBYTE_ORDER=1234" CACHE STRING "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-shared" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--unresolved-symbols=report-all,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
set(CMAKE_C_OUTPUT_EXTENSION .o)
set(CMAKE_CXX_OUTPUT_EXTENSION .o)

View File

@@ -9,10 +9,11 @@ find_program(GDB gdb)
set(patched_kernel ${CMAKE_CURRENT_BINARY_DIR}/kernel/${kernel_name}.elf32)
set(generic_flags -m 1G)
set(no_debug_flags)
if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
message(STATUS "QEMU: Enabling KVM acceleration")
set(generic_flags ${generic_flags} -enable-kvm)
set(no_debug_flags ${no_debug_flags} -enable-kvm)
else ()
message(STATUS "QEMU: Host system is not Linux. KVM acceleration unavailable")
endif ()
@@ -33,7 +34,7 @@ add_custom_target(run-kernel
${QEMU}
-kernel ${patched_kernel}
-initrd ${sys_dir}/${bsp_name}
${generic_flags}
${generic_flags} ${no_debug_flags}
-serial stdio
--append kernel.early-console=ttyS0
USES_TERMINAL
@@ -42,12 +43,12 @@ add_custom_target(run-kernel
add_custom_target(run-kernel-monitor
COMMAND
${QEMU}
-kernel $<TARGET_FILE:${kernel_name}>
-kernel ${patched_kernel}
-initrd ${sys_dir}/${bsp_name}
${generic_flags}
${generic_flags} ${no_debug_flags}
-monitor stdio
USES_TERMINAL
DEPENDS ${kernel_name} bsp)
DEPENDS ${patched_kernel} bsp)
if (image_cdrom)
message(STATUS "QEMU: Enable CD-ROM boot")
@@ -55,7 +56,7 @@ if (image_cdrom)
COMMAND
${QEMU}
-cdrom ${image_cdrom}
${generic_flags}
${generic_flags} ${no_debug_flags}
-serial stdio
USES_TERMINAL
DEPENDS ${image_cdrom})
@@ -99,9 +100,9 @@ elseif (GDB)
-initrd ${sys_dir}/${bsp_name}
${generic_flags}
-s -S &
${GDB}
-o "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
-o "remote localhost:1234"
${GDB} -tui
-ex "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
-ex "target remote localhost:1234"
USES_TERMINAL
DEPENDS ${patched_kernel} bsp)
@@ -114,10 +115,9 @@ elseif (GDB)
${generic_flags}
-s -S &
${GDB} -tui
-s "${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
-ex "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
-ex "target remote localhost:1234"
USES_TERMINAL
DEPENDS ${image_cdrom})
endif ()
endif ()

75
cmake/Msg-Interface.cmake Normal file
View File

@@ -0,0 +1,75 @@
find_program(XPCG
NAMES xpcg
REQUIRED
HINTS ${BUILD_TOOLS_DIR})
message(STATUS "Found interface generator: ${XPCG}")
function(add_interface)
set(options)
set(one_value_args NAME PARENT_DIR PATH)
set(multi_value_args)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${options}"
"${one_value_args}"
"${multi_value_args}")
message(STATUS "Building interface ${arg_NAME}")
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR})
set(header_path ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR}/${arg_NAME}.h)
add_custom_command(
OUTPUT ${header_path}
COMMAND ${XPCG} ${arg_PATH}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR}
COMMENT "Generating interface: ${arg_NAME}")
add_custom_target(ifgen-${arg_NAME} ALL
DEPENDS ${header_path})
set_target_properties(ifgen-${arg_NAME} PROPERTIES
iface_header_path ${header_path}
iface_parent_dir ${arg_PARENT_DIR})
add_library(iflib-${arg_NAME} INTERFACE)
set_target_properties(iflib-${arg_NAME} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR})
add_dependencies(iflib-${arg_NAME} ifgen-${arg_NAME})
add_library(interface::${arg_NAME} ALIAS iflib-${arg_NAME})
endfunction(add_interface)
function(iface_get_header_path)
set(options)
set(one_value_args NAME OUT)
set(multi_value_args)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${options}"
"${one_value_args}"
"${multi_value_args}")
get_property(
value
TARGET ifgen-${name}
PROPERTY iface_header_path)
set(${arg_OUT} ${value} PARENT_SCOPE)
endfunction(iface_get_header_path)
function(iface_get_parent_dir)
set(options)
set(one_value_args NAME OUT)
set(multi_value_args)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${options}"
"${one_value_args}"
"${multi_value_args}")
get_property(
value
TARGET ifgen-${name}
PROPERTY iface_parent_dir)
set(${arg_OUT} ${value} PARENT_SCOPE)
endfunction(iface_get_parent_dir)

View File

@@ -186,6 +186,37 @@ function(sysroot_add_file)
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
endfunction(sysroot_add_file)
function(sysroot_add_interface)
set(options)
set(one_value_args NAME DEST_DIR)
set(multi_value_args DEPENDS)
cmake_parse_arguments(PARSE_ARGV 0 arg
"${options}"
"${one_value_args}"
"${multi_value_args}")
iface_get_header_path(NAME ${arg_NAME} OUT src_path)
iface_get_parent_dir(NAME ${arg_NAME} OUT parent_dir)
set(dest_dir ${arg_DEST_DIR}/${parent_dir})
set(sysroot_target_name _sysroot-iface-${arg_NAME})
get_property(sysroot_targets GLOBAL PROPERTY sysroot_target_list)
list(LENGTH sysroot_targets nr_sysroot_targets)
if (${nr_sysroot_targets} GREATER 0)
math(EXPR serialiser_index "${nr_sysroot_targets}-1")
list(GET sysroot_targets ${serialiser_index} serialiser)
endif ()
add_custom_target(${sysroot_target_name}
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
add-binary ${sysroot_manifest} ${arg_NAME}
${dest_dir} ${src_path}
COMMENT "Preparing sysroot component: ${arg_ID}"
DEPENDS ifgen-${arg_NAME} ${serialiser} ${arg_DEPENDS})
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
endfunction(sysroot_add_interface)
function(sysroot_finalise)
get_property(sysroot_targets GLOBAL PROPERTY sysroot_target_list)

View File

@@ -63,6 +63,9 @@ function(rosetta_add_library)
add_library(${static_lib_name} STATIC
${arg_SOURCES}
${arg_HEADERS})
set_target_properties(${static_lib_name} PROPERTIES OUTPUT_NAME "${lib_name}")
target_compile_definitions(${static_lib_name} PRIVATE
BUILD_STATIC=1)
set(targets ${targets} ${static_lib_name})
if (arg_PUBLIC_INCLUDE_DIRS)
@@ -85,12 +88,17 @@ function(rosetta_add_library)
PATH ${arg_PUBLIC_INCLUDE_DIRS})
endif ()
target_compile_definitions(${shared_lib_name} PRIVATE
BUILD_SHARED=1)
set_target_properties(${shared_lib_name} PROPERTIES
SOVERSION 1)
target_link_options(${shared_lib_name} PRIVATE -Wl,--soname,${soname})
endif()
target_include_directories(${targets} PUBLIC ${arg_PUBLIC_INCLUDE_DIRS})
foreach (target ${targets})
target_include_directories(${target} PUBLIC ${arg_PUBLIC_INCLUDE_DIRS})
endforeach (target)
set_target_properties(${targets} PROPERTIES
POSITION_INDEPENDENT_CODE ON
src_header_dir ${CMAKE_CURRENT_SOURCE_DIR}/include

View File

@@ -8,8 +8,9 @@ if [ ! -n "$target_arch" ]; then
exit -1
fi
build_type=Debug
source_dir=$(realpath $(dirname "$0"))
native_build_dir=$source_dir/build-native
toolchain_build_dir=$source_dir/build/toolchain
target_build_dir=$source_dir/build
sysroot_dir=$target_build_dir/sysroot
@@ -18,30 +19,47 @@ if [ ! -d "$source_dir/arch/$target_arch" ]; then
exit -1
fi
rm -rf $native_build_dir $target_build_dir
mkdir -p $native_build_dir $target_build_dir
rm -rf $toolchain_build_dir $target_build_dir
mkdir -p $toolchain_build_dir $target_build_dir
pushd $native_build_dir > /dev/null
pushd $toolchain_build_dir > /dev/null
cmake \
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY="$native_build_dir/bin" \
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$native_build_dir/lib" \
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$native_build_dir/lib" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
$source_dir/kernel/tools
-DCMAKE_BUILD_TYPE=$build_type \
$source_dir/toolchain
case $CMAKE_GENERATOR in
Ninja)
ninja
;;
"Unix Makefiles")
make
;;
*)
make
;;
esac
popd
pushd $target_build_dir > /dev/null
cmake \
-DBUILD_TOOLS_DIR=$native_build_dir/bin \
-DBUILD_TOOLS_DIR=$toolchain_build_dir/bin \
-DCMAKE_INSTALL_PREFIX=$sysroot_dir \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DTARGET_ARCH=$target_arch \
-DCMAKE_BUILD_TYPE=$build_type \
$source_dir \
-DCMAKE_MODULE_PATH=$source_dir/arch/$target_arch \
-DCMAKE_SYSTEM_NAME=Rosetta \
-DCMAKE_TOOLCHAIN_FILE=$source_dir/arch/$target_arch/Platform/Rosetta.cmake
python3 \
$source_dir/util/merge-compiledb.py \
compile_commands.json \
toolchain/compile_commands.json
popd > /dev/null

7
interface/CMakeLists.txt Normal file
View File

@@ -0,0 +1,7 @@
file(GLOB if_files *.xpc)
foreach (file ${if_files})
get_filename_component(name ${file} NAME_WLE)
add_interface(NAME ${name} PATH ${file} PARENT_DIR rosetta)
sysroot_add_interface(NAME ${name} DEST_DIR /usr/include)
endforeach (file)

9
interface/fs.xpc Normal file
View File

@@ -0,0 +1,9 @@
interface fs 6400;
func open[0](path: string, flags: int) -> (err: int);
func close[1]() -> (err: int);
func read[2](count: size) -> (err: int, nr_read: size, data: buffer);
func write[3](data: buffer) -> (err: int, nr_written: size);
func map[4](prot: int, flags: int) -> (vmo: handle);

2
kernel

Submodule kernel updated: 5f0654430d...de520cdd2d

View File

@@ -1,36 +1,30 @@
set(source_dirs string stdio)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
set(sources ${sources} ${dir_sources})
set(headers ${headers} ${dir_headers})
endforeach (dir)
file(GLOB runtime_sources
${CMAKE_CURRENT_SOURCE_DIR}/runtime/${CMAKE_SYSTEM_PROCESSOR}/*.s)
set_property(SOURCE ${runtime_sources} PROPERTY LANGUAGE C)
set(source_dirs core malloc io)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
add_subdirectory(runtime)
foreach (dir ${source_dirs})
add_subdirectory(${dir})
set(sources ${sources} ${component_sources})
set(headers ${headers} ${component_headers})
set(public_include_dirs ${public_include_dirs} ${component_public_include_dirs})
endforeach (dir)
rosetta_add_library(SHARED
NAME libc
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
rosetta_add_object_library(
NAME libc-rt STATIC
SOURCES ${runtime_sources})
sysroot_add_library(
NAME libc
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
sysroot_add_object_library(
NAME libc-rt
LIB_DIR /usr/lib)
bsp_add_library(
NAME libc
LIB_DIR /usr/lib)
target_link_libraries(libc libmango libxpc-static interface::fs)
target_compile_definitions(libc PRIVATE ENABLE_GLOBAL_HEAP=1)

View File

@@ -1,4 +1,5 @@
set(source_dirs string stdio unistd stdlib)
set(source_dirs stdio string errno)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
@@ -7,13 +8,18 @@ foreach (dir ${source_dirs})
set(headers ${headers} ${dir_headers})
endforeach (dir)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
set(component_sources ${sources} PARENT_SCOPE)
set(component_headers ${headers} PARENT_SCOPE)
rosetta_add_library(
NAME ulibc STATIC
rosetta_add_library(STATIC
NAME libc-core
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
target_link_libraries(ulibc libmango)
sysroot_add_library(
NAME libc-core
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libc-core libmango)

View File

@@ -0,0 +1,54 @@
#include <errno.h>
#include <mango/status.h>
#if defined(BUILD_STATIC)
int __set_errno(int err)
{
return -err;
}
#endif
#if defined(BUILD_SHARED)
int __set_errno(int err)
{
/* TODO */
return -1;
}
#endif
int __errno_from_kern_status(unsigned int err)
{
switch (err) {
case KERN_OK:
return SUCCESS;
case KERN_UNIMPLEMENTED:
return ENOSYS;
case KERN_NAME_EXISTS:
return EEXIST;
case KERN_INVALID_ARGUMENT:
return EINVAL;
case KERN_UNSUPPORTED:
return ENOTSUP;
case KERN_NO_MEMORY:
return ENOMEM;
case KERN_NO_ENTRY:
return ENOENT;
case KERN_WOULD_BLOCK:
return EWOULDBLOCK;
case KERN_NO_DEVICE:
return ENODEV;
case KERN_DEVICE_STUCK:
case KERN_IO_ERROR:
return EIO;
case KERN_FATAL_ERROR:
return ENXIO;
case KERN_BAD_STATE:
return EPERM;
case KERN_MEMORY_FAULT:
return EFAULT;
case KERN_ACCESS_DENIED:
return EACCES;
default:
return EINVAL;
}
}

View File

@@ -0,0 +1,342 @@
#include <errno.h>
const char *strerror(int errnum)
{
switch (errnum) {
case SUCCESS:
return "Success";
case EPERM:
return "Operation not permitted";
case ENOENT:
return "No such file or directory";
case ESRCH:
return "No such process";
case EINTR:
return "Interrupted system call";
case EIO:
return "Input/output error";
case ENXIO:
return "Device not configured";
case E2BIG:
return "Argument list too long";
case ENOEXEC:
return "Exec format error";
case EBADF:
return "Bad file descriptor";
case ECHILD:
return "No child processes";
case EDEADLK:
return "Resource deadlock avoided";
case ENOMEM:
return "Cannot allocate memory";
case EACCES:
return "Permission denied";
case EFAULT:
return "Bad address";
case ENOTBLK:
return "Block device required";
case EBUSY:
return "Resource busy";
case EEXIST:
return "File exists";
case EXDEV:
return "Cross-device link";
case ENODEV:
return "Operation not supported by device";
case ENOTDIR:
return "Not a directory";
case EISDIR:
return "Is a directory";
case EINVAL:
return "Invalid argument";
case ENFILE:
return "Too many open files in system";
case EMFILE:
return "Too many open files";
case ENOTTY:
return "Inappropriate ioctl for device";
case ETXTBSY:
return "Text file busy";
case EFBIG:
return "File too large";
case ENOSPC:
return "No space left on device";
case ESPIPE:
return "Illegal seek";
case EROFS:
return "Read-only file system";
case EMLINK:
return "Too many links";
case EPIPE:
return "Broken pipe";
case EDOM:
return "Numerical argument out of domain";
case ERANGE:
return "Result too large";
case EAGAIN:
return "Resource temporarily unavailable";
case EINPROGRESS:
return "Operation now in progress";
case EALREADY:
return "Operation already in progress";
case ENOTSOCK:
return "Socket operation on non-socket";
case EDESTADDRREQ:
return "Destination address required";
case EMSGSIZE:
return "Message too long";
case EPROTOTYPE:
return "Protocol wrong type for socket";
case ENOPROTOOPT:
return "Protocol not available";
case EPROTONOSUPPORT:
return "Protocol not supported";
case ESOCKTNOSUPPORT:
return "Socket type not supported";
case ENOTSUP:
return "Operation not supported";
case EPFNOSUPPORT:
return "Protocol family not supported";
case EAFNOSUPPORT:
return "Address family not supported by protocol family";
case EADDRINUSE:
return "Address already in use";
case EADDRNOTAVAIL:
return "Can't assign requested address";
case ENETDOWN:
return "Network is down";
case ENETUNREACH:
return "Network is unreachable";
case ENETRESET:
return "Network dropped connection on reset";
case ECONNABORTED:
return "Software caused connection abort";
case ECONNRESET:
return "Connection reset by peer";
case ENOBUFS:
return "No buffer space available";
case EISCONN:
return "Socket is already connected";
case ENOTCONN:
return "Socket is not connected";
case ESHUTDOWN:
return "Can't send after socket shutdown";
case ETOOMANYREFS:
return "Too many references: can't splice";
case ETIMEDOUT:
return "Operation timed out";
case ECONNREFUSED:
return "Connection refused";
case ELOOP:
return "Too many levels of symbolic links";
case ENAMETOOLONG:
return "File name too long";
case EHOSTDOWN:
return "Host is down";
case EHOSTUNREACH:
return "No route to host";
case ENOTEMPTY:
return "Directory not empty";
case EPROCLIM:
return "Too many processes";
case EUSERS:
return "Too many users";
case EDQUOT:
return "Disc quota exceeded";
case ESTALE:
return "Stale NFS file handle";
case EREMOTE:
return "Too many levels of remote in path";
case EBADRPC:
return "RPC struct is bad";
case ERPCMISMATCH:
return "RPC version wrong";
case EPROGUNAVAIL:
return "RPC prog. not avail";
case EPROGMISMATCH:
return "Program version wrong";
case EPROCUNAVAIL:
return "Bad procedure for program";
case ENOLCK:
return "No locks available";
case ENOSYS:
return "Function not implemented";
case EFTYPE:
return "Inappropriate file type or format";
case EAUTH:
return "Authentication error";
case ENEEDAUTH:
return "Need authenticator";
case EPWROFF:
return "Device power is off";
case EDEVERR:
return "Device error";
case EOVERFLOW:
return "Value too large to be stored in data type";
case EBADEXEC:
return "Bad executable (or shared library)";
case EBADARCH:
return "Bad CPU type in executable";
case ESHLIBVERS:
return "Shared library version mismatch";
case EBADMACHO:
return "Malformed Mach-o file";
case ECANCELED:
return "Operation canceled";
case EIDRM:
return "Identifier removed";
case ENOMSG:
return "No message of desired type";
case EILSEQ:
return "Illegal byte sequence";
case ENOATTR:
return "Attribute not found";
case EBADMSG:
return "Bad message";
case EMULTIHOP:
return "EMULTIHOP (Reserved)";
case ENODATA:
return "No message available on STREAM";
case ENOLINK:
return "ENOLINK (Reserved)";
case ENOSR:
return "No STREAM resources";
case ENOSTR:
return "Not a STREAM";
case EPROTO:
return "Protocol error";
case ETIME:
return "STREAM ioctl timeout";
case EOPNOTSUPP:
return "Operation not supported on socket";
case ENOPOLICY:
return "Policy not found";
case ENOTRECOVERABLE:
return "State not recoverable";
case EOWNERDEAD:
return "Previous owner died";
case EQFULL:
return "Interface output queue is full";
default:
return "Unknown error";
}
}
#define ENUM_STR(x) \
case x: \
return #x
const char *strerror_code(int errnum)
{
switch (errnum) {
ENUM_STR(SUCCESS);
ENUM_STR(EPERM);
ENUM_STR(ENOENT);
ENUM_STR(ESRCH);
ENUM_STR(EINTR);
ENUM_STR(EIO);
ENUM_STR(ENXIO);
ENUM_STR(E2BIG);
ENUM_STR(ENOEXEC);
ENUM_STR(EBADF);
ENUM_STR(ECHILD);
ENUM_STR(EDEADLK);
ENUM_STR(ENOMEM);
ENUM_STR(EACCES);
ENUM_STR(EFAULT);
ENUM_STR(ENOTBLK);
ENUM_STR(EBUSY);
ENUM_STR(EEXIST);
ENUM_STR(EXDEV);
ENUM_STR(ENODEV);
ENUM_STR(ENOTDIR);
ENUM_STR(EISDIR);
ENUM_STR(EINVAL);
ENUM_STR(ENFILE);
ENUM_STR(EMFILE);
ENUM_STR(ENOTTY);
ENUM_STR(ETXTBSY);
ENUM_STR(EFBIG);
ENUM_STR(ENOSPC);
ENUM_STR(ESPIPE);
ENUM_STR(EROFS);
ENUM_STR(EMLINK);
ENUM_STR(EPIPE);
ENUM_STR(EDOM);
ENUM_STR(ERANGE);
ENUM_STR(EAGAIN);
ENUM_STR(EINPROGRESS);
ENUM_STR(EALREADY);
ENUM_STR(ENOTSOCK);
ENUM_STR(EDESTADDRREQ);
ENUM_STR(EMSGSIZE);
ENUM_STR(EPROTOTYPE);
ENUM_STR(ENOPROTOOPT);
ENUM_STR(EPROTONOSUPPORT);
ENUM_STR(ESOCKTNOSUPPORT);
ENUM_STR(ENOTSUP);
ENUM_STR(EPFNOSUPPORT);
ENUM_STR(EAFNOSUPPORT);
ENUM_STR(EADDRINUSE);
ENUM_STR(EADDRNOTAVAIL);
ENUM_STR(ENETDOWN);
ENUM_STR(ENETUNREACH);
ENUM_STR(ENETRESET);
ENUM_STR(ECONNABORTED);
ENUM_STR(ECONNRESET);
ENUM_STR(ENOBUFS);
ENUM_STR(EISCONN);
ENUM_STR(ENOTCONN);
ENUM_STR(ESHUTDOWN);
ENUM_STR(ETOOMANYREFS);
ENUM_STR(ETIMEDOUT);
ENUM_STR(ECONNREFUSED);
ENUM_STR(ELOOP);
ENUM_STR(ENAMETOOLONG);
ENUM_STR(EHOSTDOWN);
ENUM_STR(EHOSTUNREACH);
ENUM_STR(ENOTEMPTY);
ENUM_STR(EPROCLIM);
ENUM_STR(EUSERS);
ENUM_STR(EDQUOT);
ENUM_STR(ESTALE);
ENUM_STR(EREMOTE);
ENUM_STR(EBADRPC);
ENUM_STR(ERPCMISMATCH);
ENUM_STR(EPROGUNAVAIL);
ENUM_STR(EPROGMISMATCH);
ENUM_STR(EPROCUNAVAIL);
ENUM_STR(ENOLCK);
ENUM_STR(ENOSYS);
ENUM_STR(EFTYPE);
ENUM_STR(EAUTH);
ENUM_STR(ENEEDAUTH);
ENUM_STR(EPWROFF);
ENUM_STR(EDEVERR);
ENUM_STR(EOVERFLOW);
ENUM_STR(EBADEXEC);
ENUM_STR(EBADARCH);
ENUM_STR(ESHLIBVERS);
ENUM_STR(EBADMACHO);
ENUM_STR(ECANCELED);
ENUM_STR(EIDRM);
ENUM_STR(ENOMSG);
ENUM_STR(EILSEQ);
ENUM_STR(ENOATTR);
ENUM_STR(EBADMSG);
ENUM_STR(EMULTIHOP);
ENUM_STR(ENODATA);
ENUM_STR(ENOLINK);
ENUM_STR(ENOSR);
ENUM_STR(ENOSTR);
ENUM_STR(EPROTO);
ENUM_STR(ETIME);
ENUM_STR(EOPNOTSUPP);
ENUM_STR(ENOPOLICY);
ENUM_STR(ENOTRECOVERABLE);
ENUM_STR(EOWNERDEAD);
ENUM_STR(EQFULL);
default:
return "";
}
}

145
lib/libc/include/errno.h Normal file
View File

@@ -0,0 +1,145 @@
#ifndef ERRNO_H_
#define ERRNO_H_
#define SUCCESS 0 /* Success */
#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 /* Input/output error */
#define ENXIO 6 /* Device not configured */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file descriptor */
#define ECHILD 10 /* No child processes */
#define EDEADLK 11 /* Resource deadlock avoided */
#define ENOMEM 12 /* Cannot allocate memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device / Resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* Operation not supported by device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* Too many open files in system */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Inappropriate ioctl for device */
#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 */
/* math software */
#define EDOM 33 /* Numerical argument out of domain */
#define ERANGE 34 /* Result too large */
/* non-blocking and interrupt i/o */
#define EAGAIN 35 /* Resource temporarily unavailable */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define EINPROGRESS 36 /* Operation now in progress */
#define EALREADY 37 /* Operation already in progress */
/* ipc/network software -- argument errors */
#define ENOTSOCK 38 /* Socket operation on non-socket */
#define EDESTADDRREQ 39 /* Destination address required */
#define EMSGSIZE 40 /* Message too long */
#define EPROTOTYPE 41 /* Protocol wrong type for socket */
#define ENOPROTOOPT 42 /* Protocol not available */
#define EPROTONOSUPPORT 43 /* Protocol not supported */
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
#define ENOTSUP 45 /* Operation not supported */
#define EPFNOSUPPORT 46 /* Protocol family not supported */
#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */
#define EADDRINUSE 48 /* Address already in use */
#define EADDRNOTAVAIL 49 /* Can't assign requested address */
/* ipc/network software -- operational errors */
#define ENETDOWN 50 /* Network is down */
#define ENETUNREACH 51 /* Network is unreachable */
#define ENETRESET 52 /* Network dropped connection on reset */
#define ECONNABORTED 53 /* Software caused connection abort */
#define ECONNRESET 54 /* Connection reset by peer */
#define ENOBUFS 55 /* No buffer space available */
#define EISCONN 56 /* Socket is already connected */
#define ENOTCONN 57 /* Socket is not connected */
#define ESHUTDOWN 58 /* Can't send after socket shutdown */
#define ETOOMANYREFS 59 /* Too many references: can't splice */
#define ETIMEDOUT 60 /* Operation timed out */
#define ECONNREFUSED 61 /* Connection refused */
#define ELOOP 62 /* Too many levels of symbolic links */
#define ENAMETOOLONG 63 /* File name too long */
#define EHOSTDOWN 64 /* Host is down */
#define EHOSTUNREACH 65 /* No route to host */
#define ENOTEMPTY 66 /* Directory not empty */
/* quotas & mush */
#define EPROCLIM 67 /* Too many processes */
#define EUSERS 68 /* Too many users */
#define EDQUOT 69 /* Disc quota exceeded */
/* Network File System */
#define ESTALE 70 /* Stale NFS file handle */
#define EREMOTE 71 /* Too many levels of remote in path */
#define EBADRPC 72 /* RPC struct is bad */
#define ERPCMISMATCH 73 /* RPC version wrong */
#define EPROGUNAVAIL 74 /* RPC prog. not avail */
#define EPROGMISMATCH 75 /* Program version wrong */
#define EPROCUNAVAIL 76 /* Bad procedure for program */
#define ENOLCK 77 /* No locks available */
#define ENOSYS 78 /* Function not implemented */
#define EFTYPE 79 /* Inappropriate file type or format */
#define EAUTH 80 /* Authentication error */
#define ENEEDAUTH 81 /* Need authenticator */
/* Intelligent device errors */
#define EPWROFF 82 /* Device power is off */
#define EDEVERR 83 /* Device error, e.g. paper out */
#define EOVERFLOW 84 /* Value too large to be stored in data type */
/* Program loading errors */
#define EBADEXEC 85 /* Bad executable */
#define EBADARCH 86 /* Bad CPU type in executable */
#define ESHLIBVERS 87 /* Shared library version mismatch */
#define EBADMACHO 88 /* Malformed Macho file */
#define ECANCELED 89 /* Operation canceled */
#define EIDRM 90 /* Identifier removed */
#define ENOMSG 91 /* No message of desired type */
#define EILSEQ 92 /* Illegal byte sequence */
#define ENOATTR 93 /* Attribute not found */
#define EBADMSG 94 /* Bad message */
#define EMULTIHOP 95 /* Reserved */
#define ENODATA 96 /* No message available on STREAM */
#define ENOLINK 97 /* Reserved */
#define ENOSR 98 /* No STREAM resources */
#define ENOSTR 99 /* Not a STREAM */
#define EPROTO 100 /* Protocol error */
#define ETIME 101 /* STREAM ioctl timeout */
#define EOPNOTSUPP 102 /* Operation not supported on socket */
#define ENOPOLICY 103 /* No such policy registered */
#define ENOTRECOVERABLE 104 /* State not recoverable */
#define EOWNERDEAD 105 /* Previous owner died */
#define EQFULL 106 /* Interface output queue is full */
#define ELAST 106 /* Must be equal largest errno */
extern int __set_errno(int err);
extern int __errno_from_kern_status(unsigned int err);
#endif

View File

@@ -3,6 +3,9 @@
#include <stddef.h>
extern const char *strerror(int errnum);
extern const char *strerror_code(int errnum);
extern size_t strlen(const char *s);
extern int strcmp(const char *s1, const char *s2);
@@ -11,4 +14,6 @@ extern int strncmp(const char *s1, const char *s2, unsigned long n);
extern void *memset(void *str, int c, size_t n);
extern void *memcpy(void *dst, const void *src, size_t len);
extern char *strdup(char *s);
#endif

12
lib/libc/include/unistd.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef UNISTD_H_
#define UNISTD_H_
#include <stddef.h>
extern int open(const char *path, int flags);
extern int close(int fd);
extern int read(int fd, void *buf, size_t count);
extern int write(int fd, const void *buf, size_t count);
#endif

View File

@@ -0,0 +1,33 @@
set(source_dirs unistd stdio)
file(GLOB sources *.c *.h)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
set(sources ${sources} ${dir_sources})
set(headers ${headers} ${dir_headers})
endforeach (dir)
file(GLOB_RECURSE sub_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
set(headers ${headers} ${sub_headers})
set(component_sources ${sources} PARENT_SCOPE)
set(component_headers ${headers} PARENT_SCOPE)
set(component_public_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE)
rosetta_add_library(STATIC
NAME libc-io
PUBLIC_INCLUDE_DIRS
${public_include_dirs}
${CMAKE_CURRENT_SOURCE_DIR}/include
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libc-io
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libc-io libc-core interface::fs libxpc-static libmango)

2
lib/libc/io/fs.c Normal file
View File

@@ -0,0 +1,2 @@
#define MSG_IMPLEMENTATION
#include <rosetta/fs.h>

View File

@@ -0,0 +1,43 @@
#ifndef SYS_MMAN_H_
#define SYS_MMAN_H_
#include <mango/types.h>
#include <stddef.h>
#define PROT_NONE 0x00u
#define PROT_EXEC 0x01u
#define PROT_READ 0x02u
#define PROT_WRITE 0x04u
#define MAP_SHARED 0x01u
#define MAP_SHARED_VALIDATE 0x03u
#define MAP_PRIVATE 0x04u
#define MAP_32BIT 0x08u
#define MAP_ANON MAP_ANONYMOUS
#define MAP_ANONYMOUS 0x10u
#define MAP_DENYWRITE 0x20u
#define MAP_EXECUTABLE 0x40u
#define MAP_FILE 0x80u
#define MAP_FIXED 0x100u
#define MAP_FIXED_NOREPLACE 0x300u
#define MAP_GROWSDOWN 0x400u
#define MAP_HUGETLB 0x800u
#define MAP_HUGE_2MB 0x1000u
#define MAP_HUGE_1GB 0x2000u
#define MAP_LOCKED 0x4000u
#define MAP_NONBLOCK 0x8000u
#define MAP_NORESERVE 0x10000u
#define MAP_POPULATE 0x20000u
#define MAP_STACK 0x40000u
#define MAP_SYNC 0x80000u
#define MAP_UNINITIALIZED 0x100000u
extern void *mmap(
void *addr,
size_t length,
int prot,
int flags,
int fd,
off_t offset);
#endif

View File

@@ -0,0 +1,18 @@
#ifndef SYS_REMOTE_H_
#define SYS_REMOTE_H_
#include <mango/types.h>
#include <stdbool.h>
enum sys_remote_id {
SYS_REMOTE_NONE,
SYS_REMOTE_NSD,
};
extern bool sys_remote_get(
enum sys_remote_id id,
tid_t *out_tid,
unsigned int *out_chid);
extern void sys_remote_set(enum sys_remote_id id, tid_t tid, unsigned int chid);
#endif

45
lib/libc/io/remote.c Normal file
View File

@@ -0,0 +1,45 @@
#include <sys/remote.h>
#define TID_INVALID ((tid_t) - 1)
#define CHID_INVALID ((unsigned int)-1)
struct remote {
tid_t tid;
unsigned int chid;
};
static struct remote remotes[] = {
[SYS_REMOTE_NONE] = {.tid = TID_INVALID, .chid = CHID_INVALID},
[SYS_REMOTE_NSD] = {.tid = TID_INVALID, .chid = CHID_INVALID},
};
static const size_t nr_remotes = sizeof remotes / sizeof remotes[0];
bool sys_remote_get(
enum sys_remote_id id,
tid_t *out_tid,
unsigned int *out_chid)
{
if (id < 0 || id >= nr_remotes) {
return false;
}
const struct remote *remote = &remotes[id];
if (remote->tid == TID_INVALID || remote->chid == CHID_INVALID) {
return false;
}
*out_tid = remote->tid;
*out_chid = remote->chid;
return true;
}
void sys_remote_set(enum sys_remote_id id, tid_t tid, unsigned int chid)
{
if (id < 0 || id >= nr_remotes) {
return;
}
struct remote *remote = &remotes[id];
remote->tid = tid;
remote->chid = chid;
}

View File

View File

@@ -0,0 +1,6 @@
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
{
return NULL;
}

40
lib/libc/io/unistd/open.c Normal file
View File

@@ -0,0 +1,40 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/msg.h>
#include <rosetta/fs.h>
#include <sys/remote.h>
int open(const char *path, int flags)
{
tid_t remote_tid;
unsigned int remote_chid;
if (!sys_remote_get(SYS_REMOTE_NSD, &remote_tid, &remote_chid)) {
return __set_errno(ENXIO);
}
kern_handle_t port;
kern_status_t status = port_create(&port);
if (status != KERN_OK) {
return __set_errno(__errno_from_kern_status(status));
}
status = port_connect(port, remote_tid, remote_chid);
if (status != KERN_OK) {
kern_handle_close(port);
return __set_errno(__errno_from_kern_status(status));
}
int err = SUCCESS;
status = fs_open(port, path, flags, &err);
if (status != KERN_OK) {
kern_handle_close(port);
return __set_errno(__errno_from_kern_status(status));
}
if (err != SUCCESS) {
kern_handle_close(port);
return __set_errno(err);
}
return (int)port;
}

21
lib/libc/io/unistd/read.c Normal file
View File

@@ -0,0 +1,21 @@
#include <errno.h>
#include <mango/handle.h>
#include <mango/msg.h>
#include <rosetta/fs.h>
#include <sys/remote.h>
int read(int fd, void *buf, size_t count)
{
int err;
size_t nr_read;
kern_status_t status = fs_read(fd, count, &err, &nr_read, buf, count);
if (status != KERN_OK) {
return __set_errno(__errno_from_kern_status(status));
}
if (err != SUCCESS) {
return __set_errno(err);
}
return nr_read;
}

View File

View File

@@ -0,0 +1,33 @@
set(source_dirs stdlib string)
file(GLOB sources *.c *.h)
foreach (dir ${source_dirs})
file(GLOB dir_sources ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.c)
file(GLOB dir_headers ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*.h)
set(sources ${sources} ${dir_sources})
set(headers ${headers} ${dir_headers})
endforeach (dir)
file(GLOB_RECURSE sub_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
set(headers ${headers} ${sub_headers})
set(component_sources ${sources} PARENT_SCOPE)
set(component_headers ${headers} PARENT_SCOPE)
set(component_public_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE)
rosetta_add_library(STATIC
NAME libc-malloc
PUBLIC_INCLUDE_DIRS
${public_include_dirs}
${CMAKE_CURRENT_SOURCE_DIR}/include
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libc-malloc
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libc-malloc libc-core libmango)

112
lib/libc/malloc/heap.c Normal file
View File

@@ -0,0 +1,112 @@
#include "liballoc.h"
#include <heap/heap.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/status.h>
#include <mango/task.h>
#include <mango/vm.h>
#include <stdio.h>
#define HEAP_REGION_SIZE 0x40000000
#define HEAP_EXPAND_INCREMENT 0x100000
void *heap_alloc(heap_t *heap, size_t sz)
{
return _lbmalloc(heap, sz);
}
void *heap_realloc(heap_t *heap, void *p, size_t sz)
{
return _lbrealloc(heap, p, sz);
}
void *heap_calloc(heap_t *heap, size_t num, size_t itemsz)
{
return _lbcalloc(heap, num, itemsz);
}
void heap_free(heap_t *heap, void *p)
{
_lbfree(heap, p);
}
static kern_status_t init_heap_region(heap_t *heap)
{
kern_handle_t self, address_space;
task_self(&self);
task_get_address_space(self, &address_space);
kern_handle_close(self);
kern_status_t status = vm_region_create(
address_space,
"libc-heap",
9,
VM_REGION_ANY_OFFSET,
HEAP_REGION_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&heap->heap_region,
&heap->heap_base);
kern_handle_close(address_space);
if (status != KERN_OK) {
return status;
}
return KERN_OK;
}
static kern_status_t expand_heap(heap_t *heap)
{
kern_handle_t vmo;
kern_status_t status = vm_object_create(
"libc-heap-data",
14,
HEAP_EXPAND_INCREMENT,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&vmo);
if (status != KERN_OK) {
return status;
}
virt_addr_t base = 0;
status = vm_region_map_relative(
heap->heap_region,
heap->heap_sys_alloc,
vmo,
0,
HEAP_EXPAND_INCREMENT,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&base);
kern_handle_close(vmo);
heap->heap_sys_alloc += HEAP_EXPAND_INCREMENT;
return status;
}
void *heap_expand(heap_t *heap, size_t size)
{
kern_status_t status = KERN_OK;
if (heap->heap_region == KERN_HANDLE_INVALID) {
status = init_heap_region(heap);
}
if (status != KERN_OK) {
return NULL;
}
while (heap->heap_req_alloc + size > heap->heap_sys_alloc) {
status = expand_heap(heap);
if (status != KERN_OK) {
return NULL;
}
}
void *p = (void *)(heap->heap_base + heap->heap_req_alloc);
heap->heap_req_alloc += size;
return p;
}

View File

@@ -0,0 +1,16 @@
#ifndef HEAP__LIBALLOC_H_
#define HEAP__LIBALLOC_H_
struct liballoc_major;
struct liballoc_minor;
#define HEAP_INIT \
{ \
.heap_region = KERN_HANDLE_INVALID, \
.heap_liballoc = { \
.l_pageSize = 4096, \
.l_pageCount = 16, \
}, \
}
#endif

View File

@@ -0,0 +1,50 @@
#ifndef HEAP_H_
#define HEAP_H_
#include <heap/_liballoc.h>
#include <mango/types.h>
#include <stddef.h>
typedef enum heap_result {
HEAP_OK = 0,
HEAP_ERR_NO_MEMORY,
HEAP_ERR_INVALID_ARGUMENT,
} heap_result_t;
typedef struct heap {
kern_handle_t heap_region;
/* amount of space requested from the heap by the user */
size_t heap_req_alloc;
/* amount of space requested from the system by the heap */
size_t heap_sys_alloc;
/* base address of the heap */
virt_addr_t heap_base;
union {
struct {
struct liballoc_major *l_memRoot;
struct liballoc_major *l_bestBet;
unsigned int l_pageSize;
unsigned int l_pageCount;
unsigned long long l_allocated;
unsigned long long l_inuse;
long long l_warningCount;
long long l_errorCount;
long long l_possibleOverruns;
} heap_liballoc;
};
} heap_t;
extern kern_status_t heap_init(heap_t *heap, size_t size);
extern kern_status_t heap_destroy(heap_t *heap);
extern void *heap_alloc(heap_t *heap, size_t sz);
extern void *heap_realloc(heap_t *heap, void *p, size_t sz);
extern void *heap_calloc(heap_t *heap, size_t num, size_t itemsz);
extern void heap_free(heap_t *heap, void *p);
extern void *heap_expand(heap_t *heap, size_t size);
#endif

834
lib/libc/malloc/liballoc.c Normal file
View File

@@ -0,0 +1,834 @@
#include "liballoc.h"
/** Durand's Amazing Super Duper Memory functions. */
#define VERSION "1.1"
#define ALIGNMENT \
16ul // 4ul ///< This is the byte alignment
// that memory must be allocated on. IMPORTANT for GTK and other
// stuff.
#define ALIGN_TYPE char /// unsigned char[16] /// unsigned short
#define ALIGN_INFO \
sizeof(ALIGN_TYPE) * 16 ///< Alignment information is stored right
///< before the pointer. This is the number of
///< bytes of information stored there.
#define USE_CASE1
#define USE_CASE2
#define USE_CASE3
#define USE_CASE4
#define USE_CASE5
/** This macro will conveniently align our pointer upwards */
#define ALIGN(ptr) \
if (ALIGNMENT > 1) { \
uintptr_t diff; \
ptr = (void *)((uintptr_t)ptr + ALIGN_INFO); \
diff = (uintptr_t)ptr & (ALIGNMENT - 1); \
if (diff != 0) { \
diff = ALIGNMENT - diff; \
ptr = (void *)((uintptr_t)ptr + diff); \
} \
*((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)) \
= diff + ALIGN_INFO; \
}
#define UNALIGN(ptr) \
if (ALIGNMENT > 1) { \
uintptr_t diff \
= *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)); \
if (diff < (ALIGNMENT + ALIGN_INFO)) { \
ptr = (void *)((uintptr_t)ptr - diff); \
} \
}
#define LIBALLOC_MAGIC 0xc001c0de
#define LIBALLOC_DEAD 0xdeaddead
#if defined DEBUG || defined INFO
#include <stdio.h>
#include <stdlib.h>
#define FLUSH() fflush(stdout)
#endif
/** A structure found at the top of all system allocated
* memory blocks. It details the usage of the memory block.
*/
struct liballoc_major {
struct liballoc_major *prev; ///< Linked list information.
struct liballoc_major *next; ///< Linked list information.
unsigned int pages; ///< The number of pages in the block.
unsigned int size; ///< The number of pages in the block.
unsigned int usage; ///< The number of bytes used in the block.
struct liballoc_minor *first; ///< A pointer to the first allocated
///< memory in the block.
};
/** This is a structure found at the beginning of all
* sections in a major block which were allocated by a
* malloc, calloc, realloc call.
*/
struct liballoc_minor {
struct liballoc_minor *prev; ///< Linked list information.
struct liballoc_minor *next; ///< Linked list information.
struct liballoc_major *block; ///< The owning block. A pointer to the
///< major structure.
unsigned int magic; ///< A magic number to idenfity correctness.
unsigned int size; ///< The size of the memory allocated. Could be 1
///< byte or more.
unsigned int req_size; ///< The size of memory requested.
};
#if 0
static struct liballoc_major *l_memRoot
= NULL; ///< The root memory block acquired from the system.
static struct liballoc_major *l_bestBet
= NULL; ///< The major with the most free memory.
static unsigned int l_pageSize
= 4096; ///< The size of an individual page. Set up in liballoc_init.
static unsigned int l_pageCount = 16; ///< The number of pages to request per
///< chunk. Set up in liballoc_init.
static unsigned long long l_allocated
= 0; ///< Running total of allocated memory.
static unsigned long long l_inuse = 0; ///< Running total of used memory.
static long long l_warningCount = 0; ///< Number of warnings encountered
static long long l_errorCount = 0; ///< Number of actual errors
static long long l_possibleOverruns = 0; ///< Number of possible overruns
#endif
// *********** HELPER FUNCTIONS *******************************
static void *liballoc_memset(void *s, int c, size_t n)
{
unsigned int i;
for (i = 0; i < n; i++)
((char *)s)[i] = c;
return s;
}
static void *liballoc_memcpy(void *s1, const void *s2, size_t n)
{
char *cdest;
char *csrc;
unsigned int *ldest = (unsigned int *)s1;
unsigned int *lsrc = (unsigned int *)s2;
while (n >= sizeof(unsigned int)) {
*ldest++ = *lsrc++;
n -= sizeof(unsigned int);
}
cdest = (char *)ldest;
csrc = (char *)lsrc;
while (n > 0) {
*cdest++ = *csrc++;
n -= 1;
}
return s1;
}
#if defined DEBUG || defined INFO
static void liballoc_dump()
{
#ifdef DEBUG
struct liballoc_major *maj = l_memRoot;
struct liballoc_minor *min = NULL;
#endif
printf("liballoc: ------ Memory data ---------------\n");
printf("liballoc: System memory allocated: %i bytes\n", l_allocated);
printf("liballoc: Memory in used (malloc'ed): %i bytes\n", l_inuse);
printf("liballoc: Warning count: %i\n", l_warningCount);
printf("liballoc: Error count: %i\n", l_errorCount);
printf("liballoc: Possible overruns: %i\n", l_possibleOverruns);
#ifdef DEBUG
while (maj != NULL) {
printf("liballoc: %x: total = %i, used = %i\n",
maj,
maj->size,
maj->usage);
min = maj->first;
while (min != NULL) {
printf("liballoc: %x: %i bytes\n", min, min->size);
min = min->next;
}
maj = maj->next;
}
#endif
FLUSH();
}
#endif
#define CTX(n) (heap->heap_liballoc.n)
// ***************************************************************
static struct liballoc_major *allocate_new_page(heap_t *heap, unsigned int size)
{
unsigned int st;
struct liballoc_major *maj;
// This is how much space is required.
st = size + sizeof(struct liballoc_major);
st += sizeof(struct liballoc_minor);
// Perfect amount of space?
if ((st % CTX(l_pageSize)) == 0)
st = st / (CTX(l_pageSize));
else
st = st / (CTX(l_pageSize)) + 1;
// No, add the buffer.
// Make sure it's >= the minimum size.
if (st < CTX(l_pageCount))
st = CTX(l_pageCount);
maj = (struct liballoc_major *)liballoc_alloc(heap, st);
if (maj == NULL) {
CTX(l_warningCount) += 1;
#if defined DEBUG || defined INFO
printf("liballoc: WARNING: liballoc_alloc( %i ) return NULL\n",
st);
FLUSH();
#endif
return NULL; // uh oh, we ran out of memory.
}
maj->prev = NULL;
maj->next = NULL;
maj->pages = st;
maj->size = st * CTX(l_pageSize);
maj->usage = sizeof(struct liballoc_major);
maj->first = NULL;
CTX(l_allocated) += maj->size;
#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))));
FLUSH();
#endif
return maj;
}
void *PREFIX(malloc)(heap_t *heap, size_t req_size)
{
int startedBet = 0;
unsigned long long bestSize = 0;
void *p = NULL;
uintptr_t diff;
struct liballoc_major *maj;
struct liballoc_minor *min;
struct liballoc_minor *new_min;
unsigned long size = req_size;
// For alignment, we adjust size so there's enough space to align.
if (ALIGNMENT > 1) {
size += ALIGNMENT + ALIGN_INFO;
}
// So, ideally, we really want an alignment of 0 or 1 in order
// to save space.
liballoc_lock(heap);
if (size == 0) {
CTX(l_warningCount) += 1;
#if defined DEBUG || defined INFO
printf("liballoc: WARNING: alloc( 0 ) called from %x\n",
__builtin_return_address(0));
FLUSH();
#endif
liballoc_unlock(heap);
return PREFIX(malloc)(heap, 1);
}
if (CTX(l_memRoot) == NULL) {
#if defined DEBUG || defined INFO
#ifdef DEBUG
printf("liballoc: initialization of liballoc " VERSION "\n");
#endif
atexit(liballoc_dump);
FLUSH();
#endif
// This is the first time we are being used.
CTX(l_memRoot) = allocate_new_page(heap, size);
if (CTX(l_memRoot) == NULL) {
liballoc_unlock(heap);
#ifdef DEBUG
printf("liballoc: initial l_memRoot initialization "
"failed\n",
p);
FLUSH();
#endif
return NULL;
}
#ifdef DEBUG
printf("liballoc: set up first memory major %x\n", l_memRoot);
FLUSH();
#endif
}
#ifdef DEBUG
printf("liballoc: %x PREFIX(malloc)( %i ): ",
__builtin_return_address(0),
size);
FLUSH();
#endif
// Now we need to bounce through every major and find enough space....
maj = CTX(l_memRoot);
startedBet = 0;
// Start at the best bet....
if (CTX(l_bestBet) != NULL) {
bestSize = CTX(l_bestBet)->size - CTX(l_bestBet)->usage;
if (bestSize > (size + sizeof(struct liballoc_minor))) {
maj = CTX(l_bestBet);
startedBet = 1;
}
}
while (maj != NULL) {
diff = maj->size - maj->usage;
// free memory in the block
if (bestSize < diff) {
// Hmm.. this one has more memory then our bestBet.
// Remember!
CTX(l_bestBet) = maj;
bestSize = diff;
}
#ifdef USE_CASE1
// CASE 1: There is not enough space in this major block.
if (diff < (size + sizeof(struct liballoc_minor))) {
#ifdef DEBUG
printf("CASE 1: Insufficient space in block %x\n", maj);
FLUSH();
#endif
// Another major block next to this one?
if (maj->next != NULL) {
maj = maj->next; // Hop to that one.
continue;
}
if (startedBet == 1) // If we started at the best bet,
{ // let's start all over again.
maj = CTX(l_memRoot);
startedBet = 0;
continue;
}
// Create a new major block next to this one and...
maj->next = allocate_new_page(
heap,
size); // next one will be okay.
if (maj->next == NULL)
break; // no more memory.
maj->next->prev = maj;
maj = maj->next;
// .. fall through to CASE 2 ..
}
#endif
#ifdef USE_CASE2
// CASE 2: It's a brand new block.
if (maj->first == NULL) {
maj->first
= (struct liballoc_minor
*)((uintptr_t)maj
+ sizeof(struct liballoc_major));
maj->first->magic = LIBALLOC_MAGIC;
maj->first->prev = NULL;
maj->first->next = NULL;
maj->first->block = maj;
maj->first->size = size;
maj->first->req_size = req_size;
maj->usage += size + sizeof(struct liballoc_minor);
CTX(l_inuse) += size;
p = (void *)((uintptr_t)(maj->first)
+ sizeof(struct liballoc_minor));
ALIGN(p);
#ifdef DEBUG
printf("CASE 2: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(heap); // release the lock
return p;
}
#endif
#ifdef USE_CASE3
// CASE 3: Block in use and enough space at the start of the
// block.
diff = (uintptr_t)(maj->first);
diff -= (uintptr_t)maj;
diff -= sizeof(struct liballoc_major);
if (diff >= (size + sizeof(struct liballoc_minor))) {
// Yes, space in front. Squeeze in.
maj->first->prev
= (struct liballoc_minor
*)((uintptr_t)maj
+ sizeof(struct liballoc_major));
maj->first->prev->next = maj->first;
maj->first = maj->first->prev;
maj->first->magic = LIBALLOC_MAGIC;
maj->first->prev = NULL;
maj->first->block = maj;
maj->first->size = size;
maj->first->req_size = req_size;
maj->usage += size + sizeof(struct liballoc_minor);
CTX(l_inuse) += size;
p = (void *)((uintptr_t)(maj->first)
+ sizeof(struct liballoc_minor));
ALIGN(p);
#ifdef DEBUG
printf("CASE 3: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(heap); // release the lock
return p;
}
#endif
#ifdef USE_CASE4
// CASE 4: There is enough space in this block. But is it
// contiguous?
min = maj->first;
// Looping within the block now...
while (min != NULL) {
// CASE 4.1: End of minors in a block. Space from last
// and end?
if (min->next == NULL) {
// the rest of this block is free... is it big
// enough?
diff = (uintptr_t)(maj) + maj->size;
diff -= (uintptr_t)min;
diff -= sizeof(struct liballoc_minor);
diff -= min->size;
// minus already existing usage..
if (diff
>= (size + sizeof(struct liballoc_minor))) {
// yay....
min->next
= (struct liballoc_minor
*)((uintptr_t)min
+ sizeof(
struct
liballoc_minor)
+ min->size);
min->next->prev = min;
min = min->next;
min->next = NULL;
min->magic = LIBALLOC_MAGIC;
min->block = maj;
min->size = size;
min->req_size = req_size;
maj->usage += size
+ sizeof(struct
liballoc_minor);
CTX(l_inuse) += size;
p = (void *)((uintptr_t)min
+ sizeof(struct
liballoc_minor));
ALIGN(p);
#ifdef DEBUG
printf("CASE 4.1: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(
heap); // release the lock
return p;
}
}
// CASE 4.2: Is there space between two minors?
if (min->next != NULL) {
// is the difference between here and next big
// enough?
diff = (uintptr_t)(min->next);
diff -= (uintptr_t)min;
diff -= sizeof(struct liballoc_minor);
diff -= min->size;
// minus our existing usage.
if (diff
>= (size + sizeof(struct liballoc_minor))) {
// yay......
new_min = (struct liballoc_minor
*)((uintptr_t)min
+ sizeof(
struct
liballoc_minor)
+ min->size);
new_min->magic = LIBALLOC_MAGIC;
new_min->next = min->next;
new_min->prev = min;
new_min->size = size;
new_min->req_size = req_size;
new_min->block = maj;
min->next->prev = new_min;
min->next = new_min;
maj->usage += size
+ sizeof(struct
liballoc_minor);
CTX(l_inuse) += size;
p = (void *)((uintptr_t)new_min
+ sizeof(struct
liballoc_minor));
ALIGN(p);
#ifdef DEBUG
printf("CASE 4.2: returning %x\n", p);
FLUSH();
#endif
liballoc_unlock(
heap); // release the lock
return p;
}
} // min->next != NULL
min = min->next;
} // while min != NULL ...
#endif
#ifdef USE_CASE5
// CASE 5: Block full! Ensure next block and loop.
if (maj->next == NULL) {
#ifdef DEBUG
printf("CASE 5: block full\n");
FLUSH();
#endif
if (startedBet == 1) {
maj = CTX(l_memRoot);
startedBet = 0;
continue;
}
// we've run out. we need more...
maj->next = allocate_new_page(
heap,
size); // next one guaranteed to be okay
if (maj->next == NULL)
break; // uh oh, no more memory.....
maj->next->prev = maj;
}
#endif
maj = maj->next;
} // while (maj != NULL)
liballoc_unlock(heap); // release the lock
#ifdef DEBUG
printf("All cases exhausted. No memory available.\n");
FLUSH();
#endif
#if defined DEBUG || defined INFO
printf("liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n",
size);
liballoc_dump();
FLUSH();
#endif
return NULL;
}
void PREFIX(free)(heap_t *heap, void *ptr)
{
struct liballoc_minor *min;
struct liballoc_major *maj;
if (ptr == NULL) {
CTX(l_warningCount) += 1;
#if defined DEBUG || defined INFO
printf("liballoc: WARNING: PREFIX(free)( NULL ) called from "
"%x\n",
__builtin_return_address(0));
FLUSH();
#endif
return;
}
UNALIGN(ptr);
liballoc_lock(heap); // lockit
min = (struct liballoc_minor *)((uintptr_t)ptr
- sizeof(struct liballoc_minor));
if (min->magic != LIBALLOC_MAGIC) {
CTX(l_errorCount) += 1;
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
if (((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF))
|| ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF))
|| ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) {
CTX(l_possibleOverruns) += 1;
#if defined DEBUG || defined INFO
printf("liballoc: ERROR: Possible 1-3 byte overrun for "
"magic %x != %x\n",
min->magic,
LIBALLOC_MAGIC);
FLUSH();
#endif
}
if (min->magic == LIBALLOC_DEAD) {
#if defined DEBUG || defined INFO
printf("liballoc: ERROR: multiple PREFIX(free)() "
"attempt on %x from %x.\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
} else {
#if defined DEBUG || defined INFO
printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called "
"from %x\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
}
// being lied to...
liballoc_unlock(heap); // release the lock
return;
}
#ifdef DEBUG
printf("liballoc: %x PREFIX(free)( %x ): ",
__builtin_return_address(0),
ptr);
FLUSH();
#endif
maj = min->block;
CTX(l_inuse) -= min->size;
maj->usage -= (min->size + sizeof(struct liballoc_minor));
min->magic = LIBALLOC_DEAD; // No mojo.
if (min->next != NULL)
min->next->prev = min->prev;
if (min->prev != NULL)
min->prev->next = min->next;
if (min->prev == NULL)
maj->first = min->next;
// Might empty the block. This was the first
// minor.
// We need to clean up after the majors now....
if (maj->first == NULL) // Block completely unused.
{
if (CTX(l_memRoot) == maj)
CTX(l_memRoot) = maj->next;
if (CTX(l_bestBet) == maj)
CTX(l_bestBet) = NULL;
if (maj->prev != NULL)
maj->prev->next = maj->next;
if (maj->next != NULL)
maj->next->prev = maj->prev;
CTX(l_allocated) -= maj->size;
liballoc_free(heap, maj, maj->pages);
} else {
if (CTX(l_bestBet) != NULL) {
int bestSize
= CTX(l_bestBet)->size - CTX(l_bestBet)->usage;
int majSize = maj->size - maj->usage;
if (majSize > bestSize)
CTX(l_bestBet) = maj;
}
}
#ifdef DEBUG
printf("OK\n");
FLUSH();
#endif
liballoc_unlock(heap); // release the lock
}
void *PREFIX(calloc)(heap_t *heap, size_t nobj, size_t size)
{
int real_size;
void *p;
real_size = nobj * size;
p = PREFIX(malloc)(heap, real_size);
liballoc_memset(p, 0, real_size);
return p;
}
void *PREFIX(realloc)(heap_t *heap, void *p, size_t size)
{
void *ptr;
struct liballoc_minor *min;
unsigned int real_size;
// Honour the case of size == 0 => free old and return NULL
if (size == 0) {
PREFIX(free)(heap, p);
return NULL;
}
// In the case of a NULL pointer, return a simple malloc.
if (p == NULL)
return PREFIX(malloc)(heap, size);
// Unalign the pointer if required.
ptr = p;
UNALIGN(ptr);
liballoc_lock(heap); // lockit
min = (struct liballoc_minor *)((uintptr_t)ptr
- sizeof(struct liballoc_minor));
// Ensure it is a valid structure.
if (min->magic != LIBALLOC_MAGIC) {
CTX(l_errorCount) += 1;
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
if (((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF))
|| ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF))
|| ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) {
CTX(l_possibleOverruns) += 1;
#if defined DEBUG || defined INFO
printf("liballoc: ERROR: Possible 1-3 byte overrun for "
"magic %x != %x\n",
min->magic,
LIBALLOC_MAGIC);
FLUSH();
#endif
}
if (min->magic == LIBALLOC_DEAD) {
#if defined DEBUG || defined INFO
printf("liballoc: ERROR: multiple PREFIX(free)() "
"attempt on %x from %x.\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
} else {
#if defined DEBUG || defined INFO
printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called "
"from %x\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
}
// being lied to...
liballoc_unlock(heap); // release the lock
return NULL;
}
// Definitely a memory block.
real_size = min->req_size;
if (real_size >= size) {
min->req_size = size;
liballoc_unlock(heap);
return p;
}
liballoc_unlock(heap);
// If we got here then we're reallocating to a block bigger than us.
ptr = PREFIX(malloc)(heap, size); // We need to allocate new memory
liballoc_memcpy(ptr, p, real_size);
PREFIX(free)(heap, p);
return ptr;
}
int liballoc_lock(heap_t *heap)
{
/* TODO */
return 0;
}
int liballoc_unlock(heap_t *heap)
{
/* TODO */
return 0;
}
void *liballoc_alloc(heap_t *heap, size_t sz)
{
return heap_expand(heap, sz);
}
int liballoc_free(heap_t *heap, void *p, size_t sz)
{
/* TODO */
return 0;
}

View File

@@ -0,0 +1,76 @@
#ifndef _LIBALLOC_H
#define _LIBALLOC_H
#include <heap/heap.h>
#include <stddef.h>
/** \defgroup ALLOCHOOKS liballoc hooks
*
* These are the OS specific functions which need to
* be implemented on any platform that the library
* is expected to work on.
*/
/** @{ */
// If we are told to not define our own size_t, then we skip the define.
// #define _HAVE_UINTPTR_T
// typedef unsigned long uintptr_t;
// This lets you prefix malloc and friends
#define PREFIX(func) _lb##func
#ifdef __cplusplus
extern "C" {
#endif
/** This function is supposed to lock the memory data structures. It
* could be as simple as disabling interrupts or acquiring a spinlock.
* It's up to you to decide.
*
* \return 0 if the lock was acquired successfully. Anything else is
* failure.
*/
extern int liballoc_lock(heap_t *);
/** This function unlocks what was previously locked by the liballoc_lock
* function. If it disabled interrupts, it enables interrupts. If it
* had acquiried a spinlock, it releases the spinlock. etc.
*
* \return 0 if the lock was successfully released.
*/
extern int liballoc_unlock(heap_t *);
/** This is the hook into the local system which allocates pages. It
* accepts an integer parameter which is the number of pages
* required. The page size was set up in the liballoc_init function.
*
* \return NULL if the pages were not allocated.
* \return A pointer to the allocated memory.
*/
extern void *liballoc_alloc(heap_t *, size_t);
/** This frees previously allocated memory. The void* parameter passed
* to the function is the exact same value returned from a previous
* liballoc_alloc call.
*
* The integer value is the number of pages to free.
*
* \return 0 if the memory was successfully freed.
*/
extern int liballoc_free(heap_t *, void *, size_t);
extern void *PREFIX(malloc)(heap_t *, size_t); ///< The standard function.
extern void *PREFIX(
realloc)(heap_t *, void *, size_t); ///< The standard function.
extern void *PREFIX(
calloc)(heap_t *, size_t, size_t); ///< The standard function.
extern void PREFIX(free)(heap_t *, void *); ///< The standard function.
#ifdef __cplusplus
}
#endif
/** @} */
#endif

View File

@@ -0,0 +1,44 @@
#include <heap/heap.h>
#ifdef ENABLE_GLOBAL_HEAP
static heap_t global_heap = HEAP_INIT;
void *malloc(size_t count)
{
return heap_alloc(&global_heap, count);
}
void *calloc(size_t count, size_t size)
{
return heap_calloc(&global_heap, count, size);
}
void *realloc(void *p, size_t count)
{
return heap_realloc(&global_heap, p, count);
}
void free(void *p)
{
heap_free(&global_heap, p);
}
#else
void *malloc(size_t count)
{
return NULL;
}
void *calloc(size_t count, size_t size)
{
return NULL;
}
void *realloc(void *p, size_t count)
{
return NULL;
}
void free(void *p)
{
}
#endif

View File

@@ -0,0 +1,14 @@
#include <stdlib.h>
#include <string.h>
char *strdup(char *s)
{
size_t len = strlen(s);
char *out = malloc(len + 1);
if (!out) {
return NULL;
}
memcpy(out, s, len + 1);
return out;
}

View File

@@ -0,0 +1,11 @@
file(GLOB runtime_sources
${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_SYSTEM_PROCESSOR}/*.s)
set_property(SOURCE ${runtime_sources} PROPERTY LANGUAGE C)
rosetta_add_object_library(
NAME libc-runtime STATIC
SOURCES ${runtime_sources})
sysroot_add_object_library(
NAME libc-runtime
LIB_DIR /usr/lib)

29
lib/libfs/CMakeLists.txt Normal file
View File

@@ -0,0 +1,29 @@
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.c
${CMAKE_CURRENT_SOURCE_DIR}/interface/*.c)
file(GLOB headers
${CMAKE_CURRENT_SOURCE_DIR}/*.h
${CMAKE_CURRENT_SOURCE_DIR}/include/fs/*.h)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(
NAME libfs SHARED STATIC
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libfs
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
sysroot_add_library(
NAME libfs-static
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libfs libmango interface::fs libc libxpc)
target_link_libraries(libfs-static libmango interface::fs libc-core libxpc-static)
set_target_properties(libfs-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE)

35
lib/libfs/allocator.c Normal file
View File

@@ -0,0 +1,35 @@
#include <fs/allocator.h>
void *fs_alloc(struct fs_allocator *alloc, size_t count)
{
if (alloc->fs_alloc) {
return alloc->fs_alloc(alloc, count);
}
return NULL;
}
void *fs_calloc(struct fs_allocator *alloc, size_t count, size_t sz)
{
if (alloc->fs_calloc) {
return alloc->fs_calloc(alloc, count, sz);
}
return NULL;
}
void *fs_realloc(struct fs_allocator *alloc, void *p, size_t count)
{
if (alloc->fs_realloc) {
return alloc->fs_realloc(alloc, p, count);
}
return NULL;
}
void fs_free(struct fs_allocator *alloc, void *p)
{
if (alloc->fs_free) {
alloc->fs_free(alloc, p);
}
}

691
lib/libfs/btree.c Normal file
View File

@@ -0,0 +1,691 @@
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
/* templated AVL binary tree implementation
this file implements an extensible AVL binary tree data structure.
the primary rule of an AVL binary tree is that for a given node N,
the heights of N's left and right subtrees can differ by at most 1.
the height of a subtree is the length of the longest path between
the root of the subtree and a leaf node, including the root node itself.
the height of a leaf node is 1.
when a node is inserted into or deleted from the tree, this rule may
be broken, in which the tree must be rotated to restore the balance.
no more than one rotation is required for any insert operations,
while multiple rotations may be required for a delete operation.
there are four types of rotations that can be applied to a tree:
- left rotation
- right rotation
- double left rotations
- double right rotations
by enforcing the balance rule, for a tree with n nodes, the worst-case
performance for insert, delete, and search operations is guaranteed
to be O(log n).
this file intentionally excludes any kind of search function implementation.
it is up to the programmer to implement their own tree node type
using struct btree_node, and their own search function using struct btree.
this allows the programmer to define their own node types with complex
non-integer key types. btree.h contains a number of macros to help
define these functions. the macros do all the work, you just have to
provide a comparator function.
*/
#include "btree.h"
#include <stddef.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c)))
#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c)))
#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left))
#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right))
#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right))
#define HAS_ONE_CHILD(x) \
((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \
|| (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)))
#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))
#define HEIGHT(x) ((x) ? (x)->b_height : 0)
static inline void update_height(struct btree_node *x)
{
x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1;
}
static inline int bf(struct btree_node *x)
{
int bf = 0;
if (!x) {
return bf;
}
if (x->b_right) {
bf += x->b_right->b_height;
}
if (x->b_left) {
bf -= x->b_left->b_height;
}
return bf;
}
/* perform a left rotation on a subtree
if you have a tree like this:
Z
/ \
X .
/ \
. Y
/ \
. .
and you perform a left rotation on node X,
you will get the following tree:
Z
/ \
Y .
/ \
X .
/ \
. .
note that this function does NOT update b_height for the rotated
nodes. it is up to you to call update_height_to_root().
*/
static void rotate_left(struct btree *tree, struct btree_node *x)
{
struct btree_node *y = x->b_right;
struct btree_node *p = x->b_parent;
if (y->b_left) {
y->b_left->b_parent = x;
}
x->b_right = y->b_left;
if (!p) {
tree->b_root = y;
} else if (x == p->b_left) {
p->b_left = y;
} else {
p->b_right = y;
}
x->b_parent = y;
y->b_left = x;
y->b_parent = p;
}
static void update_height_to_root(struct btree_node *x)
{
while (x) {
update_height(x);
x = x->b_parent;
}
}
/* perform a right rotation on a subtree
if you have a tree like this:
Z
/ \
. X
/ \
Y .
/ \
. .
and you perform a right rotation on node X,
you will get the following tree:
Z
/ \
. Y
/ \
. X
/ \
. .
note that this function does NOT update b_height for the rotated
nodes. it is up to you to call update_height_to_root().
*/
static void rotate_right(struct btree *tree, struct btree_node *y)
{
struct btree_node *x = y->b_left;
struct btree_node *p = y->b_parent;
if (x->b_right) {
x->b_right->b_parent = y;
}
y->b_left = x->b_right;
if (!p) {
tree->b_root = x;
} else if (y == p->b_left) {
p->b_left = x;
} else {
p->b_right = x;
}
y->b_parent = x;
x->b_right = y;
x->b_parent = p;
}
/* for a given node Z, perform a right rotation on Z's right child,
followed by a left rotation on Z itself.
if you have a tree like this:
Z
/ \
. X
/ \
Y .
/ \
. .
and you perform a double-left rotation on node Z,
you will get the following tree:
Y
/ \
/ \
Z X
/ \ / \
. . . .
note that, unlike rotate_left and rotate_right, this function
DOES update b_height for the rotated nodes (since it needs to be
done in a certain order).
*/
static void rotate_double_left(struct btree *tree, struct btree_node *z)
{
struct btree_node *x = z->b_right;
struct btree_node *y = x->b_left;
rotate_right(tree, x);
rotate_left(tree, z);
update_height(z);
update_height(x);
while (y) {
update_height(y);
y = y->b_parent;
}
}
/* for a given node Z, perform a left rotation on Z's left child,
followed by a right rotation on Z itself.
if you have a tree like this:
Z
/ \
X .
/ \
. Y
/ \
. .
and you perform a double-right rotation on node Z,
you will get the following tree:
Y
/ \
/ \
X Z
/ \ / \
. . . .
note that, unlike rotate_left and rotate_right, this function
DOES update b_height for the rotated nodes (since it needs to be
done in a certain order).
*/
static void rotate_double_right(struct btree *tree, struct btree_node *z)
{
struct btree_node *x = z->b_left;
struct btree_node *y = x->b_right;
rotate_left(tree, x);
rotate_right(tree, z);
update_height(z);
update_height(x);
while (y) {
update_height(y);
y = y->b_parent;
}
}
/* run after an insert operation. checks that the balance factor
of the local subtree is within the range -1 <= BF <= 1. if it
is not, rotate the subtree to restore balance.
note that at most one rotation should be required after a node
is inserted into the tree.
this function depends on all nodes in the tree having
correct b_height values.
@param w the node that was just inserted into the tree
*/
static void insert_fixup(struct btree *tree, struct btree_node *w)
{
struct btree_node *z = NULL, *y = NULL, *x = NULL;
z = w;
while (z) {
if (bf(z) >= -1 && bf(z) <= 1) {
goto next_ancestor;
}
if (IS_LEFT_CHILD(z, y)) {
if (IS_LEFT_CHILD(y, x)) {
rotate_right(tree, z);
update_height_to_root(z);
} else {
rotate_double_right(tree, z);
}
} else {
if (IS_LEFT_CHILD(y, x)) {
rotate_double_left(tree, z);
} else {
rotate_left(tree, z);
update_height_to_root(z);
}
}
next_ancestor:
x = y;
y = z;
z = z->b_parent;
}
}
/* run after a delete operation. checks that the balance factor
of the local subtree is within the range -1 <= BF <= 1. if it
is not, rotate the subtree to restore balance.
note that, unlike insert_fixup, multiple rotations may be required
to restore balance after a node is deleted.
this function depends on all nodes in the tree having
correct b_height values.
@param w one of the following:
- the parent of the node that was deleted if the node
had no children.
- the parent of the node that replaced the deleted node
if the deleted node had two children.
- the node that replaced the node that was deleted, if
the node that was deleted had one child.
*/
static void delete_fixup(struct btree *tree, struct btree_node *w)
{
struct btree_node *z = w;
while (z) {
if (bf(z) > 1) {
if (bf(z->b_right) >= 0) {
rotate_left(tree, z);
update_height_to_root(z);
} else {
rotate_double_left(tree, z);
}
} else if (bf(z) < -1) {
if (bf(z->b_left) <= 0) {
rotate_right(tree, z);
update_height_to_root(z);
} else {
rotate_double_right(tree, z);
}
}
z = z->b_parent;
}
}
/* updates b_height for all nodes between the inserted node and the root
of the tree, and calls insert_fixup.
@param node the node that was just inserted into the tree.
*/
void btree_insert_fixup(struct btree *tree, struct btree_node *node)
{
node->b_height = 0;
struct btree_node *cur = node;
while (cur) {
update_height(cur);
cur = cur->b_parent;
}
insert_fixup(tree, node);
}
/* remove a node from a tree.
this function assumes that `node` has no children, and therefore
doesn't need to be replaced.
updates b_height for all nodes between `node` and the tree root.
@param node the node to delete.
*/
static struct btree_node *remove_node_with_no_children(
struct btree *tree,
struct btree_node *node)
{
struct btree_node *w = node->b_parent;
struct btree_node *p = node->b_parent;
node->b_parent = NULL;
if (!p) {
tree->b_root = NULL;
} else if (IS_LEFT_CHILD(p, node)) {
p->b_left = NULL;
} else {
p->b_right = NULL;
}
while (p) {
update_height(p);
p = p->b_parent;
}
return w;
}
/* remove a node from a tree.
this function assumes that `node` has one child.
the child of `node` is inherited by `node`'s parent, and `node` is removed.
updates b_height for all nodes between the node that replaced
`node` and the tree root.
@param node the node to delete.
*/
static struct btree_node *replace_node_with_one_subtree(
struct btree *tree,
struct btree_node *node)
{
struct btree_node *p = node->b_parent;
struct btree_node *z = NULL;
if (HAS_LEFT_CHILD(node)) {
z = node->b_left;
} else {
z = node->b_right;
}
struct btree_node *w = z;
if (!p) {
tree->b_root = z;
} else if (IS_LEFT_CHILD(p, node)) {
p->b_left = z;
} else if (IS_RIGHT_CHILD(p, node)) {
p->b_right = z;
}
z->b_parent = p;
node->b_parent = NULL;
node->b_left = node->b_right = NULL;
while (z) {
update_height(z);
z = z->b_parent;
}
return w;
}
/* remove a node from a tree.
this function assumes that `node` has two children.
find the in-order successor Y of `node` (the largest node in `node`'s left
sub-tree), removes `node` from the tree and moves Y to where `node` used to
be.
if Y has a child (it will never have more than one), have Y's parent inherit
Y's child.
updates b_height for all nodes between the deepest node that was modified
and the tree root.
@param z the node to delete.
*/
static struct btree_node *replace_node_with_two_subtrees(
struct btree *tree,
struct btree_node *z)
{
/* x will replace z */
struct btree_node *x = z->b_left;
while (x->b_right) {
x = x->b_right;
}
/* y is the node that will replace x (if x has a left child) */
struct btree_node *y = x->b_left;
/* w is the starting point for the height update and fixup */
struct btree_node *w = x;
if (w->b_parent != z) {
w = w->b_parent;
}
if (y) {
w = y;
}
if (IS_LEFT_CHILD(x->b_parent, x)) {
x->b_parent->b_left = y;
} else if (IS_RIGHT_CHILD(x->b_parent, x)) {
x->b_parent->b_right = y;
}
if (y) {
y->b_parent = x->b_parent;
}
if (IS_LEFT_CHILD(z->b_parent, z)) {
z->b_parent->b_left = x;
} else if (IS_RIGHT_CHILD(z->b_parent, z)) {
z->b_parent->b_right = x;
}
x->b_parent = z->b_parent;
x->b_left = z->b_left;
x->b_right = z->b_right;
if (x->b_left) {
x->b_left->b_parent = x;
}
if (x->b_right) {
x->b_right->b_parent = x;
}
if (!x->b_parent) {
tree->b_root = x;
}
struct btree_node *cur = w;
while (cur) {
update_height(cur);
cur = cur->b_parent;
}
return w;
}
/* delete a node from the tree and re-balance it afterwards */
void btree_delete(struct btree *tree, struct btree_node *node)
{
struct btree_node *w = NULL;
if (HAS_NO_CHILDREN(node)) {
w = remove_node_with_no_children(tree, node);
} else if (HAS_ONE_CHILD(node)) {
w = replace_node_with_one_subtree(tree, node);
} else if (HAS_TWO_CHILDREN(node)) {
w = replace_node_with_two_subtrees(tree, node);
}
if (w) {
delete_fixup(tree, w);
}
node->b_left = node->b_right = node->b_parent = NULL;
}
struct btree_node *btree_first(struct btree *tree)
{
/* the first node in the tree is the node with the smallest key.
we keep moving left until we can't go any further */
struct btree_node *cur = tree->b_root;
if (!cur) {
return NULL;
}
while (cur->b_left) {
cur = cur->b_left;
}
return cur;
}
struct btree_node *btree_last(struct btree *tree)
{
/* the first node in the tree is the node with the largest key.
we keep moving right until we can't go any further */
struct btree_node *cur = tree->b_root;
if (!cur) {
return NULL;
}
while (cur->b_right) {
cur = cur->b_right;
}
return cur;
}
struct btree_node *btree_next(struct btree_node *node)
{
if (!node) {
return NULL;
}
/* there are two possibilities for the next node:
1. if `node` has a right sub-tree, every node in this sub-tree is
bigger than node. the in-order successor of `node` is the smallest
node in this subtree.
2. if `node` has no right sub-tree, we've reached the largest node in
the sub-tree rooted at `node`. we need to go back to our parent
and continue the search elsewhere.
*/
if (node->b_right) {
/* case 1: step into `node`'s right sub-tree and keep going
left to find the smallest node */
struct btree_node *cur = node->b_right;
while (cur->b_left) {
cur = cur->b_left;
}
return cur;
}
/* case 2: keep stepping back up towards the root of the tree.
if we encounter a step where we are our parent's left child,
we've found a parent with a value larger than us. this parent
is the in-order successor of `node` */
while (node->b_parent && node->b_parent->b_left != node) {
node = node->b_parent;
}
return node->b_parent;
}
struct btree_node *btree_prev(struct btree_node *node)
{
if (!node) {
return NULL;
}
/* there are two possibilities for the previous node:
1. if `node` has a left sub-tree, every node in this sub-tree is
smaller than `node`. the in-order predecessor of `node` is the
largest node in this subtree.
2. if `node` has no left sub-tree, we've reached the smallest node in
the sub-tree rooted at `node`. we need to go back to our parent
and continue the search elsewhere.
*/
if (node->b_left) {
/* case 1: step into `node`'s left sub-tree and keep going
right to find the largest node */
struct btree_node *cur = node->b_left;
while (cur->b_right) {
cur = cur->b_right;
}
return cur;
}
/* case 2: keep stepping back up towards the root of the tree.
if we encounter a step where we are our parent's right child,
we've found a parent with a value smaller than us. this parent
is the in-order predecessor of `node`. */
while (node->b_parent && node->b_parent->b_right != node) {
node = node->b_parent;
}
return node->b_parent;
}

475
lib/libfs/btree.h Normal file
View File

@@ -0,0 +1,475 @@
/*
The Clear BSD License
Copyright (c) 2023 Max Wash
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
*/
#ifndef _FS_BTREE_H_
#define _FS_BTREE_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* if your custom structure contains a struct btree_node (i.e. it can be part of
a btree), you can use this macro to convert a struct btree_node* to a
your_type*
@param t the name of your custom type (something that can be passed to
offsetof)
@param m the name of the struct btree_node member variable within your custom
type.
@param v the struct btree_node pointer that you wish to convert. if this is
NULL, NULL will be returned.
*/
#define BTREE_CONTAINER(t, m, v) \
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
/* defines a simple node insertion function.
this function assumes that your nodes have simple integer keys that can be
compared with the usual operators.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
int key;
struct btree_node base;
}
You would use the following call to generate an insert function for a tree
with this node type:
BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key,
my_tree_node_insert);
Which would emit a function defined like:
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
*node);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
*/
#define BTREE_DEFINE_SIMPLE_INSERT( \
node_type, \
container_node_member, \
container_key_member, \
function_name) \
void function_name(struct btree *tree, node_type *node) \
{ \
if (!tree->b_root) { \
tree->b_root = &node->container_node_member; \
btree_insert_fixup( \
tree, \
&node->container_node_member); \
return; \
} \
\
struct btree_node *cur = tree->b_root; \
while (1) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
struct btree_node *next = NULL; \
\
if (node->container_key_member \
> cur_node->container_key_member) { \
next = btree_right(cur); \
\
if (!next) { \
btree_put_right( \
cur, \
&node->container_node_member); \
break; \
} \
} else if ( \
node->container_key_member \
< cur_node->container_key_member) { \
next = btree_left(cur); \
\
if (!next) { \
btree_put_left( \
cur, \
&node->container_node_member); \
break; \
} \
} else { \
return; \
} \
\
cur = next; \
} \
\
btree_insert_fixup(tree, &node->container_node_member); \
}
/* defines a node insertion function.
this function should be used for trees with complex node keys that cannot be
directly compared. a comparator for your keys must be supplied.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
complex_key_t key;
struct btree_node base;
}
You would need to define a comparator function or macro with the following
signature:
int my_comparator(struct my_tree_node *a, struct my_tree_node *b);
Which implements the following:
return -1 if a < b
return 0 if a == b
return 1 if a > b
You would use the following call to generate an insert function for a tree
with this node type:
BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert,
my_comparator);
Which would emit a function defined like:
static void my_tree_node_insert(struct btree *tree, struct my_tree_node
*node);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
@param comparator the name of a comparator function or functional-macro that
conforms to the requirements listed above.
*/
#define BTREE_DEFINE_INSERT( \
node_type, \
container_node_member, \
container_key_member, \
function_name, \
comparator) \
void function_name(struct btree *tree, node_type *node) \
{ \
if (!tree->b_root) { \
tree->b_root = &node->container_node_member; \
btree_insert_fixup( \
tree, \
&node->container_node_member); \
return; \
} \
\
struct btree_node *cur = tree->b_root; \
while (1) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
struct btree_node *next = NULL; \
int cmp = comparator(node, cur_node); \
\
if (cmp == 1) { \
next = btree_right(cur); \
\
if (!next) { \
btree_put_right( \
cur, \
&node->container_node_member); \
break; \
} \
} else if (cmp == -1) { \
next = btree_left(cur); \
\
if (!next) { \
btree_put_left( \
cur, \
&node->container_node_member); \
break; \
} \
} else { \
return; \
} \
\
cur = next; \
} \
\
btree_insert_fixup(tree, &node->container_node_member); \
}
/* defines a simple tree search function.
this function assumes that your nodes have simple integer keys that can be
compared with the usual operators.
EXAMPLE:
if you have a tree node type like this:
struct my_tree_node {
int key;
struct btree_node base;
}
You would use the following call to generate a search function for a tree
with this node type:
BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key,
my_tree_node_get);
Which would emit a function defined like:
static struct my_tree_node *my_tree_node_get(struct btree *tree, int key);
@param node_type your custom tree node type. usually a structure that
contains a struct btree_node member.
@param key_type the type name of the key embedded in your custom tree node
type. this type must be compatible with the builtin comparison operators.
@param container_node_member the name of the struct btree_node member
variable within your custom type.
@param container_key_member the name of the key member variable within your
custom type.
@param function_name the name of the function to generate.
*/
#define BTREE_DEFINE_SIMPLE_GET( \
node_type, \
key_type, \
container_node_member, \
container_key_member, \
function_name) \
node_type *function_name(struct btree *tree, key_type key) \
{ \
struct btree_node *cur = tree->b_root; \
while (cur) { \
node_type *cur_node = BTREE_CONTAINER( \
node_type, \
container_node_member, \
cur); \
if (key > cur_node->container_key_member) { \
cur = btree_right(cur); \
} else if (key < cur_node->container_key_member) { \
cur = btree_left(cur); \
} else { \
return cur_node; \
} \
} \
\
return NULL; \
}
/* perform an in-order traversal of a binary tree
If you have a tree defined like:
struct btree my_tree;
with nodes defined like:
struct my_tree_node {
int key;
struct btree_node base;
}
and you want to do something like:
foreach (struct my_tree_node *node : my_tree) { ... }
you should use this:
btree_foreach (struct my_tree_node, node, &my_tree, base) { ... }
@param iter_type the type name of the iterator variable. this should be the
tree's node type, and shouldn't be a pointer.
@param iter_name the name of the iterator variable.
@param tree_name a pointer to the tree to traverse.
@param node_member the name of the struct btree_node member variable within
the tree node type.
*/
#define btree_foreach(iter_type, iter_name, tree_name, node_member) \
for (iter_type *iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_first(tree_name)); \
iter_name; \
iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_next(&((iter_name)->node_member))))
/* perform an reverse in-order traversal of a binary tree
If you have a tree defined like:
struct btree my_tree;
with nodes defined like:
struct my_tree_node {
int key;
struct btree_node base;
}
and you want to do something like:
foreach (struct my_tree_node *node : reverse(my_tree)) { ... }
you should use this:
btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... }
@param iter_type the type name of the iterator variable. this should be the
tree's node type, and shouldn't be a pointer.
@param iter_name the name of the iterator variable.
@param tree_name a pointer to the tree to traverse.
@param node_member the name of the struct btree_node member variable within
the tree node type.
*/
#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \
for (iter_type *iter_name \
= BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \
iter_name; \
iter_name = BTREE_CONTAINER( \
iter_type, \
node_member, \
btree_prev(&((iter_name)->node_member))))
/* binary tree nodes. this *cannot* be used directly. you need to define a
custom node type that contains a member variable of type struct btree_node.
you would then use the supplied macros to define functions to manipulate your
custom binary tree.
*/
struct btree_node {
struct btree_node *b_parent, *b_left, *b_right;
unsigned short b_height;
};
/* binary tree. unlike struct btree_node, you can define variables of type
* struct btree. */
struct btree {
struct btree_node *b_root;
};
/* re-balance a binary tree after an insertion operation.
NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or
similar, this function will automatically called for you.
@param tree the tree to re-balance.
@param node the node that was just inserted into the tree.
*/
extern void btree_insert_fixup(struct btree *tree, struct btree_node *node);
/* delete a node from a binary tree and re-balance the tree afterwards.
@param tree the tree to delete from
@param node the node to delete.
*/
extern void btree_delete(struct btree *tree, struct btree_node *node);
/* get the first node in a binary tree.
this will be the node with the smallest key (i.e. the node that is
furthest-left from the root)
*/
extern struct btree_node *btree_first(struct btree *tree);
/* get the last node in a binary tree.
this will be the node with the largest key (i.e. the node that is
furthest-right from the root)
*/
extern struct btree_node *btree_last(struct btree *tree);
/* for any binary tree node, this function returns the node with the
* next-largest key value */
extern struct btree_node *btree_next(struct btree_node *node);
/* for any binary tree node, this function returns the node with the
* next-smallest key value */
extern struct btree_node *btree_prev(struct btree_node *node);
static inline bool btree_empty(const struct btree *tree)
{
return tree->b_root == NULL;
}
/* sets `child` as the immediate left-child of `parent` */
static inline void btree_put_left(
struct btree_node *parent,
struct btree_node *child)
{
parent->b_left = child;
child->b_parent = parent;
}
/* sets `child` as the immediate right-child of `parent` */
static inline void btree_put_right(
struct btree_node *parent,
struct btree_node *child)
{
parent->b_right = child;
child->b_parent = parent;
}
/* get the immediate left-child of `node` */
static inline struct btree_node *btree_left(struct btree_node *node)
{
return node->b_left;
}
/* get the immediate right-child of `node` */
static inline struct btree_node *btree_right(struct btree_node *node)
{
return node->b_right;
}
/* get the immediate parent of `node` */
static inline struct btree_node *btree_parent(struct btree_node *node)
{
return node->b_parent;
}
/* get the height of `node`.
the height of a node is defined as the length of the longest path
between the node and a leaf node.
this count includes the node itself, so the height of a leaf node will be 1.
*/
static inline unsigned short btree_height(struct btree_node *node)
{
return node->b_height;
}
#ifdef __cplusplus
}
#endif
#endif

194
lib/libfs/context.c Normal file
View File

@@ -0,0 +1,194 @@
#include "btree.h"
#include "file.h"
#include "interface.h"
#include <fs/allocator.h>
#include <fs/context.h>
#include <fs/dentry.h>
#include <fs/inode.h>
#include <fs/status.h>
#include <fs/superblock.h>
#include <mango/log.h>
#include <stdio.h>
BTREE_DEFINE_SIMPLE_GET(struct fs_file, unsigned long, f_node, f_id, get_file);
BTREE_DEFINE_SIMPLE_INSERT(struct fs_file, f_node, f_id, put_file);
struct fs_context {
struct fs_superblock *ctx_sb;
struct fs_allocator *ctx_alloc;
struct btree ctx_filelist;
struct fs_vtable ctx_vtable;
};
struct fs_context *fs_context_create(struct fs_allocator *alloc)
{
struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx);
if (!ctx) {
return NULL;
}
memset(ctx, 0x0, sizeof *ctx);
ctx->ctx_alloc = alloc;
ctx->ctx_vtable.open = fs_msg_open;
ctx->ctx_vtable.read = fs_msg_read;
return ctx;
}
void fs_context_destroy(struct fs_context *ctx)
{
fs_free(ctx->ctx_alloc, ctx);
}
enum fs_status fs_context_mount_filesystem(
struct fs_context *ctx,
fs_mount_function_t func,
void *arg,
enum fs_mount_flags flags)
{
if (!func) {
return FS_ERR_INVALID_ARGUMENT;
}
struct fs_superblock *sb = NULL;
enum fs_status status = func(ctx, arg, flags, &sb);
if (status != FS_SUCCESS) {
return status;
}
if (!sb) {
return FS_ERR_INTERNAL_FAILURE;
}
ctx->ctx_sb = sb;
return FS_SUCCESS;
}
enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx)
{
return FS_ERR_NOT_IMPLEMENTED;
}
struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id)
{
struct fs_file *f = get_file(&ctx->ctx_filelist, id);
if (!f) {
f = fs_alloc(ctx->ctx_alloc, sizeof *f);
if (!f) {
return NULL;
}
memset(f, 0x0, sizeof *f);
f->f_id = id;
put_file(&ctx->ctx_filelist, f);
}
return f;
}
struct fs_file *fs_context_get_file(struct fs_context *ctx, unsigned long id)
{
return get_file(&ctx->ctx_filelist, id);
}
void fs_context_close_file(struct fs_context *ctx, struct fs_file *f)
{
}
static size_t get_first_path_component(const char *in, char *out, size_t max)
{
size_t i = 0;
while (i < max - 1) {
if (in[i] == '\0' || in[i] == '/') {
break;
}
out[i] = in[i];
i++;
}
out[i] = '\0';
return i;
}
extern enum fs_status fs_context_resolve_path(
struct fs_context *ctx,
const char *path,
struct fs_dentry **out)
{
if (!ctx->ctx_sb || !ctx->ctx_sb->s_root) {
return FS_ERR_NO_ENTRY;
}
struct fs_dentry *cur = ctx->ctx_sb->s_root;
char tok[256];
while (*path != '\0') {
while (*path == '/') {
path++;
}
size_t tok_len
= get_first_path_component(path, tok, sizeof tok);
if (!tok_len) {
break;
}
bool is_dir = *(path + tok_len) != '\0';
if (cur->d_inode->i_mode != FS_INODE_DIR) {
return FS_ERR_NOT_DIRECTORY;
}
struct fs_dentry *next = NULL;
enum fs_status status
= fs_inode_lookup(cur->d_inode, tok, &next);
if (status != FS_SUCCESS) {
return status;
}
if (!next) {
return FS_ERR_INTERNAL_FAILURE;
}
cur = next;
path += tok_len;
}
*out = cur;
return FS_SUCCESS;
}
kern_status_t fs_context_dispatch_msg(struct fs_context *ctx, xpc_msg_t *msg)
{
return fs_dispatch(NULL, msg, &ctx->ctx_vtable, ctx);
}
void *fs_context_alloc(struct fs_context *ctx, size_t count)
{
return fs_alloc(ctx->ctx_alloc, count);
}
void *fs_context_calloc(struct fs_context *ctx, size_t count, size_t sz)
{
return fs_calloc(ctx->ctx_alloc, count, sz);
}
void *fs_context_realloc(struct fs_context *ctx, void *p, size_t count)
{
return fs_realloc(ctx->ctx_alloc, p, count);
}
void fs_context_free(struct fs_context *ctx, void *p)
{
fs_free(ctx->ctx_alloc, p);
}

41
lib/libfs/file.c Normal file
View File

@@ -0,0 +1,41 @@
#include "file.h"
struct fs_inode *fs_file_get_inode(const struct fs_file *f)
{
return f->f_inode;
}
size_t fs_file_get_cursor(const struct fs_file *f)
{
return f->f_seek;
}
enum fs_status fs_file_read(
struct fs_file *f,
struct xpc_buffer *buf,
size_t count)
{
if (!f->f_ops || !f->f_ops->f_read) {
return FS_ERR_NOT_IMPLEMENTED;
}
off_t seek = f->f_seek;
enum fs_status status = f->f_ops->f_read(f, buf, count, &seek);
f->f_seek = seek;
return status;
}
enum fs_status fs_file_write(
struct fs_file *f,
struct xpc_buffer *buf,
size_t count)
{
if (!f->f_ops || !f->f_ops->f_write) {
return FS_ERR_NOT_IMPLEMENTED;
}
off_t seek = f->f_seek;
enum fs_status status = f->f_ops->f_write(f, buf, count, &seek);
f->f_seek = seek;
return status;
}

23
lib/libfs/file.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef _FS_FILE_H_
#define _FS_FILE_H_
#include "btree.h"
#include <fs/dentry.h>
#include <fs/file.h>
#include <fs/inode.h>
struct fs_file {
/* id of the open file, equal to the koid of the port being used to
* access the file */
unsigned long f_id;
struct btree_node f_node;
const struct fs_file_ops *f_ops;
off_t f_seek;
struct fs_inode *f_inode;
struct fs_dentry *f_dent;
};
#endif

View File

@@ -0,0 +1,19 @@
#ifndef FS_ALLOCATOR_H_
#define FS_ALLOCATOR_H_
#include <stddef.h>
struct fs_allocator {
void *fs_arg;
void *(*fs_alloc)(struct fs_allocator *, size_t);
void *(*fs_calloc)(struct fs_allocator *, size_t, size_t);
void *(*fs_realloc)(struct fs_allocator *, void *, size_t);
void (*fs_free)(struct fs_allocator *, void *);
};
extern void *fs_alloc(struct fs_allocator *alloc, size_t count);
extern void *fs_calloc(struct fs_allocator *alloc, size_t count, size_t sz);
extern void *fs_realloc(struct fs_allocator *alloc, void *p, size_t count);
extern void fs_free(struct fs_allocator *alloc, void *p);
#endif

View File

@@ -0,0 +1,55 @@
#ifndef FS_CONTEXT_H_
#define FS_CONTEXT_H_
#include <rosetta/fs.h>
#include <xpc/msg.h>
struct fs_file;
struct fs_dentry;
struct fs_context;
struct fs_allocator;
struct fs_superblock;
enum fs_mount_flags {
FS_MOUNT_READONLY = 0x01u,
};
typedef enum fs_status (*fs_mount_function_t)(
struct fs_context *,
void *arg,
enum fs_mount_flags,
struct fs_superblock **);
extern struct fs_context *fs_context_create(struct fs_allocator *alloc);
extern void fs_context_destroy(struct fs_context *ctx);
extern enum fs_status fs_context_mount_filesystem(
struct fs_context *ctx,
fs_mount_function_t func,
void *arg,
enum fs_mount_flags flags);
extern enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx);
extern struct fs_file *fs_context_open_file(
struct fs_context *ctx,
unsigned long id);
extern struct fs_file *fs_context_get_file(
struct fs_context *ctx,
unsigned long id);
extern void fs_context_close_file(struct fs_context *ctx, struct fs_file *f);
extern enum fs_status fs_context_resolve_path(
struct fs_context *ctx,
const char *path,
struct fs_dentry **out);
extern kern_status_t fs_context_dispatch_msg(
struct fs_context *ctx,
xpc_msg_t *msg);
extern void *fs_context_alloc(struct fs_context *ctx, size_t count);
extern void *fs_context_calloc(struct fs_context *ctx, size_t count, size_t sz);
extern void *fs_context_realloc(struct fs_context *ctx, void *p, size_t count);
extern void fs_context_free(struct fs_context *ctx, void *p);
#endif

View File

@@ -0,0 +1,19 @@
#ifndef FS_DENTRY_H_
#define FS_DENTRY_H_
struct fs_inode;
struct fs_superblock;
struct fs_dentry_ops {
};
struct fs_dentry {
struct fs_inode *d_inode;
struct fs_dentry *d_parent;
struct fs_superblock *d_sb;
const struct fs_dentry_ops *d_ops;
void *d_fsdata;
char *d_name;
};
#endif

View File

@@ -0,0 +1,35 @@
#ifndef FS_FILE_H_
#define FS_FILE_H_
#include <mango/types.h>
#include <stddef.h>
struct fs_file;
struct xpc_buffer;
struct fs_file_ops {
enum fs_status (*f_read)(
struct fs_file *,
struct xpc_buffer *,
size_t,
off_t *);
enum fs_status (*f_write)(
struct fs_file *,
const struct xpc_buffer *,
size_t,
off_t *);
enum fs_status (*f_seek)(struct fs_file *, off_t, int);
};
extern struct fs_inode *fs_file_get_inode(const struct fs_file *f);
extern size_t fs_file_get_cursor(const struct fs_file *f);
extern enum fs_status fs_file_read(
struct fs_file *f,
struct xpc_buffer *buf,
size_t count);
extern enum fs_status fs_file_write(
struct fs_file *f,
struct xpc_buffer *buf,
size_t count);
#endif

View File

@@ -0,0 +1,37 @@
#ifndef FS_INODE_H_
#define FS_INODE_H_
#include <fs/status.h>
#include <stddef.h>
struct fs_inode;
struct fs_dentry;
struct fs_superblock;
struct fs_file_ops;
enum fs_inode_mode {
FS_INODE_REG = 0x01u,
FS_INODE_DIR = 0x02u,
};
struct fs_inode_ops {
enum fs_status (*i_lookup)(
struct fs_inode *,
const char *,
struct fs_dentry **);
};
struct fs_inode {
enum fs_inode_mode i_mode;
struct fs_superblock *i_sb;
const struct fs_inode_ops *i_ops;
const struct fs_file_ops *i_fops;
size_t i_size;
};
extern enum fs_status fs_inode_lookup(
struct fs_inode *inode,
const char *name,
struct fs_dentry **out);
#endif

View File

@@ -0,0 +1,18 @@
#ifndef FS_STATUS_H_
#define FS_STATUS_H_
enum fs_status {
FS_SUCCESS = 0,
FS_ERR_NO_ENTRY,
FS_ERR_NO_MEMORY,
FS_ERR_INVALID_ARGUMENT,
FS_ERR_NOT_IMPLEMENTED,
FS_ERR_IS_DIRECTORY,
FS_ERR_NOT_DIRECTORY,
FS_ERR_BAD_STATE,
FS_ERR_INTERNAL_FAILURE,
};
extern int fs_status_to_errno(enum fs_status status);
#endif

View File

@@ -0,0 +1,20 @@
#ifndef FS_SUPERBLOCK_H_
#define FS_SUPERBLOCK_H_
struct fs_inode;
struct fs_dentry;
struct fs_superblock;
struct fs_superblock_ops {
struct fs_inode *(*s_alloc_inode)(struct fs_superblock *);
};
struct fs_superblock {
const struct fs_superblock_ops *s_ops;
struct fs_dentry *s_root;
};
extern struct fs_inode *fs_superblock_alloc_inode(struct fs_superblock *sb);
#endif

13
lib/libfs/inode.c Normal file
View File

@@ -0,0 +1,13 @@
#include <fs/inode.h>
enum fs_status fs_inode_lookup(
struct fs_inode *inode,
const char *name,
struct fs_dentry **out)
{
if (!inode->i_ops || !inode->i_ops->i_lookup) {
return FS_ERR_NOT_IMPLEMENTED;
}
return inode->i_ops->i_lookup(inode, name, out);
}

35
lib/libfs/interface.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef _FS_INTERFACE_H_
#define _FS_INTERFACE_H_
#include <mango/types.h>
#include <xpc/buffer.h>
#include <xpc/context.h>
#include <xpc/endpoint.h>
#include <xpc/string.h>
struct msg_endpoint;
extern kern_status_t fs_msg_open(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
const xpc_string_t *path,
int flags,
int *out_err,
void *arg);
extern kern_status_t fs_msg_close(
const struct msg_endpoint *sender,
const char *path,
int flags,
int *out_err,
void *arg);
extern kern_status_t fs_msg_read(
xpc_context_t *ctx,
const xpc_endpoint_t *sender,
size_t count,
int *out_err,
size_t *out_nr_read,
xpc_buffer_t *out_data,
void *arg);
#endif

View File

@@ -0,0 +1,53 @@
#include "../file.h"
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
extern kern_status_t fs_msg_open(
xpc_context_t *xpc,
xpc_endpoint_t *sender,
const xpc_string_t *path,
int flags,
int *out_err,
void *arg)
{
char path_buf[4096];
size_t path_len = 0;
kern_status_t status
= xpc_string_read(path, path_buf, sizeof path_buf, &path_len);
if (status != KERN_OK) {
return status;
}
struct fs_context *ctx = arg;
struct fs_file *f = fs_context_open_file(ctx, sender->e_port);
if (!f) {
*out_err = ENOMEM;
return KERN_OK;
}
if (f->f_inode) {
*out_err = EBUSY;
return KERN_OK;
}
struct fs_dentry *dent = NULL;
enum fs_status fs_status
= fs_context_resolve_path(ctx, path_buf, &dent);
if (fs_status != FS_SUCCESS) {
fs_context_close_file(ctx, f);
*out_err = fs_status_to_errno(status);
return KERN_OK;
}
f->f_seek = 0;
f->f_dent = dent;
f->f_inode = dent->d_inode;
f->f_ops = dent->d_inode->i_fops;
*out_err = SUCCESS;
return KERN_OK;
}

View File

@@ -0,0 +1,30 @@
#include <errno.h>
#include <fs/context.h>
#include <fs/file.h>
#include <fs/status.h>
extern kern_status_t fs_msg_read(
xpc_context_t *xpc,
xpc_endpoint_t *sender,
size_t count,
int *out_err,
size_t *out_nr_read,
xpc_buffer_t *out_data,
void *arg)
{
struct fs_context *ctx = arg;
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
if (!f) {
*out_err = EBADF;
return KERN_OK;
}
size_t start = fs_file_get_cursor(f);
enum fs_status status = fs_file_read(f, out_data, count);
size_t end = fs_file_get_cursor(f);
*out_err = fs_status_to_errno(status);
*out_nr_read = end - start;
return KERN_OK;
}

25
lib/libfs/status.c Normal file
View File

@@ -0,0 +1,25 @@
#include <errno.h>
#include <fs/status.h>
int fs_status_to_errno(enum fs_status status)
{
switch (status) {
case FS_SUCCESS:
return SUCCESS;
case FS_ERR_NO_ENTRY:
return ENOENT;
case FS_ERR_NO_MEMORY:
return ENOMEM;
case FS_ERR_INVALID_ARGUMENT:
return EINVAL;
case FS_ERR_NOT_IMPLEMENTED:
return ENOSYS;
case FS_ERR_IS_DIRECTORY:
return EISDIR;
case FS_ERR_NOT_DIRECTORY:
return ENOTDIR;
default:
return EINVAL;
}
}

11
lib/libfs/superblock.c Normal file
View File

@@ -0,0 +1,11 @@
#include <fs/superblock.h>
#include <stddef.h>
struct fs_inode *fs_superblock_alloc_inode(struct fs_superblock *sb)
{
if (!sb->s_ops->s_alloc_inode) {
return NULL;
}
return sb->s_ops->s_alloc_inode(sb);
}

View File

@@ -18,4 +18,4 @@ sysroot_add_library(
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(liblaunch libmango ulibc)
target_link_libraries(liblaunch librosetta libmango libc-core)

View File

@@ -300,8 +300,14 @@ static enum launch_status map_executable(struct elf_image *image)
static elf_sym_t *get_dynsym(struct elf_image *image, size_t index)
{
return (elf_sym_t *)(image->e_local_base + image->e_dynsym
+ (index * image->e_dynsym_entsize));
elf_sym_t *sym = (elf_sym_t *)(image->e_local_base + image->e_dynsym
+ (index * image->e_dynsym_entsize));
if (!sym->st_value) {
return NULL;
}
return sym;
}
static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela)
@@ -312,6 +318,10 @@ static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela)
switch (type) {
case R_X86_64_JUMP_SLOT:
sym = get_dynsym(image, ELF64_R_SYM(rela->r_info));
if (!sym) {
return LAUNCH_ERR_MISSING_SYMBOL;
}
*(uint64_t *)(image->e_local_base + rela->r_offset)
= image->e_remote_base + sym->st_value + rela->r_addend;
kern_tracef(
@@ -354,7 +364,7 @@ static enum launch_status do_rela_list(
rela = (elf_rela_t *)((char *)rela + entsize);
}
return LAUNCH_OK;
return status;
}
static enum launch_status do_rel(

View File

@@ -14,6 +14,7 @@ enum launch_status {
* version, etc).
*/
LAUNCH_ERR_UNSUPPORTED_EXECUTABLE,
LAUNCH_ERR_MISSING_SYMBOL,
/* a particular dependency of the executable could not be resolved. */
LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY,
LAUNCH_ERR_MEMORY_MAP_FAILED,
@@ -28,6 +29,8 @@ enum launch_flags {
};
struct launch_ctx;
struct rosetta_bootstrap_handle;
struct rosetta_bootstrap_channel;
typedef enum launch_status (*launch_resolve_library_function)(
struct launch_ctx *,
@@ -52,6 +55,12 @@ struct launch_parameters {
int p_envc;
const char **p_envp;
const struct rosetta_bootstrap_handle *p_handles;
size_t p_handle_count;
const struct rosetta_bootstrap_channel *p_channels;
size_t p_channel_count;
void *p_resolver_arg;
};

View File

@@ -1,10 +1,12 @@
#include "elf.h"
#include "stack.h"
#include <launch.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/task.h>
#include <mango/vm.h>
#include <rosetta/bootstrap.h>
#include <stdio.h>
#include <string.h>
@@ -34,6 +36,58 @@ static kern_handle_t get_library(
return result;
}
static virt_addr_t write_bootstrap_data(
struct stack_writer *stack,
const struct launch_parameters *params)
{
virt_addr_t bs_remote;
struct rosetta_bootstrap *bs
= stack_writer_put(stack, NULL, sizeof *bs, &bs_remote);
memset(bs, 0x0, sizeof *bs);
bs->bs_argc = params->p_argc;
bs->bs_envc = params->p_envc;
bs->bs_handles_count = params->p_handle_count;
bs->bs_channels_count = params->p_channel_count;
const char **argv, **envp;
if (bs->bs_argc > 0) {
virt_addr_t remote_argv;
argv = stack_writer_put(
stack,
NULL,
bs->bs_argc * sizeof(char *),
&remote_argv);
bs->bs_argv = (const char **)remote_argv;
}
if (bs->bs_envc > 0) {
virt_addr_t remote_envp;
envp = stack_writer_put(
stack,
NULL,
bs->bs_envc * sizeof(char *),
&remote_envp);
bs->bs_envp = (const char **)remote_envp;
}
for (size_t i = 0; i < params->p_argc; i++) {
virt_addr_t arg_ptr;
stack_writer_put_string(stack, params->p_argv[i], &arg_ptr);
argv[i] = (const char *)arg_ptr;
}
for (size_t i = 0; i < params->p_envc; i++) {
virt_addr_t env_ptr;
stack_writer_put_string(stack, params->p_envp[i], &env_ptr);
envp[i] = (const char *)env_ptr;
}
return bs_remote;
}
enum launch_status launch_ctx_execute(
struct launch_ctx *ctx,
const struct launch_parameters *params,
@@ -102,7 +156,7 @@ enum launch_status launch_ctx_execute(
return status;
}
virt_addr_t stack_buf;
virt_addr_t remote_stack_buf, local_stack_buf;
kstatus = vm_region_map_relative(
remote_address_space,
VM_REGION_ANY_OFFSET,
@@ -110,7 +164,15 @@ enum launch_status launch_ctx_execute(
0,
STACK_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&stack_buf);
&remote_stack_buf);
kstatus = vm_region_map_relative(
params->p_local_address_space,
VM_REGION_ANY_OFFSET,
stack_vmo,
0,
STACK_SIZE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&local_stack_buf);
kern_handle_close(stack_vmo);
if (kstatus != KERN_OK) {
@@ -120,11 +182,23 @@ enum launch_status launch_ctx_execute(
return LAUNCH_ERR_MEMORY_MAP_FAILED;
}
struct stack_writer stack;
stack_writer_init(
&stack,
local_stack_buf + STACK_SIZE,
remote_stack_buf + STACK_SIZE);
virt_addr_t bsdata = write_bootstrap_data(&stack, params);
virt_addr_t ip = image.e_hdr.e_entry + image.e_remote_base;
virt_addr_t sp = stack_buf + STACK_SIZE;
kern_handle_t thread;
kstatus = task_create_thread(remote_task, ip, sp, NULL, 0, &thread);
kstatus = task_create_thread(
remote_task,
ip,
stack.w_remote_sp,
&bsdata,
1,
&thread);
if (kstatus != KERN_OK) {
elf_image_cleanup(&image);
kern_handle_close(remote_address_space);

59
lib/liblaunch/stack.c Normal file
View File

@@ -0,0 +1,59 @@
#include "stack.h"
#include <mango/log.h>
#include <string.h>
void stack_writer_init(
struct stack_writer *w,
virt_addr_t local_sp,
virt_addr_t remote_sp)
{
memset(w, 0x0, sizeof *w);
w->w_local_sp = local_sp;
w->w_remote_sp = remote_sp;
}
void *stack_writer_put_string(
struct stack_writer *w,
const char *s,
virt_addr_t *out_remote)
{
size_t len = strlen(s);
w->w_local_sp -= (len + 1);
w->w_remote_sp -= (len + 1);
char *local_ptr = (char *)w->w_local_sp;
virt_addr_t remote_ptr = w->w_remote_sp;
memcpy(local_ptr, s, len);
local_ptr[len] = '\0';
if (out_remote) {
*out_remote = remote_ptr;
}
return local_ptr;
}
void *stack_writer_put(
struct stack_writer *w,
const void *p,
size_t len,
virt_addr_t *out_remote)
{
w->w_local_sp -= len;
w->w_remote_sp -= len;
void *local_ptr = (char *)w->w_local_sp;
virt_addr_t remote_ptr = w->w_remote_sp;
memset(local_ptr, 0x0, len);
if (out_remote) {
*out_remote = remote_ptr;
}
return local_ptr;
}

26
lib/liblaunch/stack.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef LIBLAUNCH_STACK_H_
#define LIBLAUNCH_STACK_H_
#include <mango/types.h>
struct stack_writer {
virt_addr_t w_local_sp;
virt_addr_t w_remote_sp;
};
extern void stack_writer_init(
struct stack_writer *w,
virt_addr_t local_sp,
virt_addr_t remote_sp);
extern void *stack_writer_put_string(
struct stack_writer *w,
const char *s,
virt_addr_t *out_remote);
extern void *stack_writer_put(
struct stack_writer *w,
const void *p,
size_t len,
virt_addr_t *out_remote);
#endif

View File

@@ -1,21 +0,0 @@
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB headers
${CMAKE_CURRENT_SOURCE_DIR}/include/rosetta/msg/fs.h
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(
NAME libmsg-fs STATIC
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libmsg-fs
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libmsg-fs libmsg libmango)

View File

@@ -1,3 +0,0 @@
void msg_fs_tmp(void)
{
}

View File

@@ -1,27 +0,0 @@
#ifndef ROSETTA_MSG_FS_H_
#define ROSETTA_MSG_FS_H_
#include <rosetta/msg.h>
/* rosetta.msg.fs protocol ID */
#define ROSETTA_MSG_FS 0x01409DD2U
/* rosetta.msg.fs.open message ID */
#define ROSETTA_MSG_FS_OPEN 0x7D837778U
struct rosetta_msg_fs_open {
struct rosetta_msg msg_base;
union {
struct {
uint16_t o_path;
uint16_t o_flags;
} msg_request;
struct {
int o_err;
uint16_t o_fd_index;
} msg_response;
};
};
#endif

View File

@@ -1,21 +0,0 @@
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB headers
${CMAKE_CURRENT_SOURCE_DIR}/include/rosetta/msg/ld.h
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(
NAME libmsg-ld STATIC
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libmsg-ld
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libmsg-ld libmsg libmango)

View File

@@ -1,26 +0,0 @@
#ifndef ROSETTA_MSG_LD_H_
#define ROSETTA_MSG_LD_H_
#include <rosetta/msg.h>
/* rosetta.msg.ld protocol ID */
#define ROSETTA_MSG_LD 0x5563D896U
/* rosetta.msg.ld.get-object message ID */
#define ROSETTA_MSG_LD_GET_OBJECT 1
struct rosetta_msg_ld_get_object {
struct rosetta_msg msg_base;
union {
struct {
uint16_t go_object_name;
} msg_request;
struct {
int go_err;
uint16_t go_object_index;
} msg_response;
};
};
#endif

View File

@@ -1,3 +0,0 @@
void msg_ld_tmp(void)
{
}

View File

@@ -1,21 +0,0 @@
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB headers
${CMAKE_CURRENT_SOURCE_DIR}/include/rosetta/msg.h
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(
NAME libmsg STATIC
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libmsg
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libmsg libmango)

View File

@@ -1,15 +0,0 @@
#ifndef ROSETTA_MSG_H_
#define ROSETTA_MSG_H_
#define ROSETTA_MSG_MAGIC 0x9AB07D10U
#include <stdint.h>
struct rosetta_msg {
uint32_t msg_magic;
uint32_t msg_protocol;
uint16_t msg_id;
uint16_t msg_reserved;
};
#endif

View File

@@ -1,3 +0,0 @@
void msg_tmp(void)
{
}

View File

@@ -0,0 +1,15 @@
file(GLOB sources *.c)
file(GLOB headers include/rosetta/*.h)
rosetta_add_library(
NAME librosetta STATIC
PUBLIC_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME librosetta
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(librosetta libmango)

View File

@@ -0,0 +1,6 @@
#include <rosetta/bootstrap.h>
const struct rosetta_bootstrap_channel *rosetta_bootstrap_get_channel(void)
{
return NULL;
}

View File

@@ -0,0 +1,45 @@
#ifndef ROSETTA_BOOTSTRAP_H_
#define ROSETTA_BOOTSTRAP_H_
#include <mango/types.h>
enum rosetta_bootstrap_handle_type {
RSBS_HANDLE_NONE = 0,
RSBS_HANDLE_TASK,
RSBS_HANDLE_ADDRESS_SPACE,
};
enum rosetta_bootstrap_channel_type {
RSBS_CHANNEL_NONE,
RSBS_CHANNEL_SYSTEM,
};
struct rosetta_bootstrap_handle {
enum rosetta_bootstrap_handle_type h_type;
kern_handle_t h_value;
};
struct rosetta_bootstrap_channel {
enum rosetta_bootstrap_channel_type c_type;
tid_t c_tid;
unsigned int c_cid;
};
struct rosetta_bootstrap {
int bs_argc;
const char **bs_argv;
int bs_envc;
const char **bs_envp;
const struct rosetta_bootstrap_handle *bs_handles;
size_t bs_handles_count;
const struct rosetta_bootstrap_channel *bs_channels;
size_t bs_channels_count;
};
extern const struct rosetta_bootstrap_channel *rosetta_bootstrap_get_channel(
void);
#endif

28
lib/libxpc/CMakeLists.txt Normal file
View File

@@ -0,0 +1,28 @@
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB headers
${CMAKE_CURRENT_SOURCE_DIR}/*.h
${CMAKE_CURRENT_SOURCE_DIR}/include/xpc/*.h)
set(public_include_dirs
${CMAKE_CURRENT_SOURCE_DIR}/include)
rosetta_add_library(
NAME libxpc SHARED STATIC
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
SOURCES ${sources}
HEADERS ${headers})
sysroot_add_library(
NAME libxpc
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
sysroot_add_library(
NAME libxpc-static
HEADER_DIR /usr/include
LIB_DIR /usr/lib)
target_link_libraries(libxpc libmango libc)
target_link_libraries(libxpc-static libmango libc-core)
#set_target_properties(libxpc-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE)

59
lib/libxpc/buffer.c Normal file
View File

@@ -0,0 +1,59 @@
#include <mango/status.h>
#include <xpc/buffer.h>
#include <xpc/msg.h>
kern_status_t xpc_buffer_read(
const xpc_buffer_t *buf,
void *out,
size_t max,
size_t *nr_read)
{
if ((buf->buf_flags & (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE))
!= (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_read = max;
if (to_read > buf->buf_len) {
to_read = buf->buf_len;
}
kern_status_t status
= xpc_msg_read(buf->buf_origin, buf->buf_offset, out, to_read);
if (status != KERN_OK) {
return status;
}
/* TODO */
*nr_read = to_read;
return KERN_OK;
}
kern_status_t xpc_buffer_write(
xpc_buffer_t *buf,
const void *in,
size_t len,
size_t *nr_written)
{
if ((buf->buf_flags & (XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE))
!= (XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_write = len;
if (to_write > buf->buf_max) {
to_write = buf->buf_max;
}
kern_status_t status
= xpc_msg_write(buf->buf_origin, buf->buf_offset, in, to_write);
if (status != KERN_OK) {
return status;
}
/* TODO */
*nr_written = to_write;
return KERN_OK;
}

View File

@@ -0,0 +1,86 @@
#ifndef XPC_BUFFER_H_
#define XPC_BUFFER_H_
#include <stddef.h>
#include <xpc/status.h>
#define XPC_BUFFER_IN(msg, offset, size) \
{ \
.buf_flags = XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE, \
.buf_origin = (msg), \
.buf_offset = (offset), \
.buf_len = (size), \
}
#define XPC_BUFFER_OUT(msg, offset, size) \
{ \
.buf_flags = XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE, \
.buf_origin = (msg), \
.buf_offset = (offset), \
.buf_len = (size), \
}
struct xpc_msg;
typedef enum xpc_buffer_flags {
/* the buffer can be read from */
XPC_BUFFER_F_IN = 0x01u,
/* the buffer can be written to */
XPC_BUFFER_F_OUT = 0x02u,
/* the buffer is backed by a buffer located in another address space.
* the buffer can only be accessed via xpc_buffer_read and/or
* xpc_buffer_write */
XPC_BUFFER_F_REMOTE = 0x04u,
/* free the buffer backing this buffer when the buffer is discarded.
* this is only used for out-buffers. the buffer must have been
* allocated using xpc_context_alloc, as it will be freed via a call
* to xpc_context_free */
XPC_BUFFER_F_FREE_ON_DISCARD = 0x08u,
} xpc_buffer_flags_t;
typedef struct xpc_buffer {
xpc_buffer_flags_t buf_flags;
union {
/* fields that are only valid if F_OUT is set */
struct {
/* only valid if F_OUT is set. specifies the maximum
* number of chars that can be written to buf_buf,
* including the null terminator. */
size_t buf_max;
/* only valid if F_OUT is set.
* if F_FREE_ON_DISCARD is set, must be either NULL or
* allocated via xpc_context_alloc */
const char *buf_ptr;
};
/* fields that are only valid if F_IN is set */
struct {
/* only valid if F_IN is set. offset of the buffer data
* within the associated message. used when reading
* buffer data from a message. */
size_t buf_offset;
};
};
/* only valid if F_REMOTE is set.
* used to read/write buffer data from/to the sender's address
* space. */
const struct xpc_msg *buf_origin;
/* valid for both F_IN and F_OUT buffers.
* F_IN: specifies the length of the incoming buffer data.
* F_OUT: specifies how many bytes from buf_ptr to send. */
size_t buf_len;
} xpc_buffer_t;
extern xpc_status_t xpc_buffer_read(
const xpc_buffer_t *s,
void *out,
size_t max,
size_t *nr_read);
extern xpc_status_t xpc_buffer_write(
xpc_buffer_t *s,
const void *in,
size_t len,
size_t *nr_written);
#endif

View File

@@ -0,0 +1,8 @@
#ifndef XPC_CONTEXT_H_
#define XPC_CONTEXT_H_
typedef struct xpc_context {
} xpc_context_t;
#endif

View File

@@ -0,0 +1,13 @@
#ifndef XPC_ENDPOINT_H_
#define XPC_ENDPOINT_H_
#include <mango/types.h>
typedef struct xpc_endpoint {
kern_handle_t e_channel;
tid_t e_task;
koid_t e_port;
msgid_t e_msg;
} xpc_endpoint_t;
#endif

View File

@@ -0,0 +1,53 @@
#ifndef XPC_MSG_H_
#define XPC_MSG_H_
#include <mango/types.h>
#include <stdbool.h>
#include <stdint.h>
#include <xpc/endpoint.h>
#define XPC_MSG_MAGIC 0x5850434D
typedef struct xpc_msg_header {
uint32_t hdr_magic;
uint32_t hdr_interface;
uint16_t hdr_func;
uint16_t hdr_status;
} xpc_msg_header_t;
typedef struct xpc_msg {
xpc_endpoint_t msg_sender;
xpc_msg_header_t msg_header;
size_t msg_handles_count;
kern_msg_handle_t msg_handles[KERN_MSG_MAX_HANDLES];
} xpc_msg_t;
extern void xpc_msg_header_init(
xpc_msg_header_t *msg,
unsigned long interface,
unsigned short func);
extern bool xpc_msg_header_validate(const xpc_msg_header_t *msg);
extern kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out);
extern kern_status_t xpc_msg_read(
const xpc_msg_t *msg,
size_t offset,
void *p,
size_t count);
extern kern_status_t xpc_msg_write(
const xpc_msg_t *msg,
size_t offset,
const void *p,
size_t count);
extern kern_status_t xpc_msg_reply(
const xpc_msg_t *msg,
kern_iovec_t *iov,
size_t iov_count,
kern_msg_handle_t *handles,
size_t handle_count);
extern kern_status_t xpc_msg_reply_error(
const xpc_msg_t *msg,
unsigned short code);
#endif

View File

@@ -0,0 +1,12 @@
#ifndef XPC_STATUS_H_
#define XPC_STATUS_H_
typedef enum xpc_status {
XPC_SUCCESS = 0,
XPC_ERR_BAD_STATE,
XPC_ERR_INVALID_ARGUMENT,
XPC_ERR_NO_MEMORY,
XPC_ERR_MEMORY_FAULT,
} xpc_status_t;
#endif

View File

@@ -0,0 +1,85 @@
#ifndef XPC_STRING_H_
#define XPC_STRING_H_
#include <stddef.h>
#include <xpc/status.h>
#define XPC_STRING_NPOS ((size_t)-1)
#define XPC_STRING_IN(msg, offset, size) \
{ \
.s_flags = XPC_STRING_F_IN | XPC_STRING_F_REMOTE, \
.s_origin = (msg), \
.s_offset = (offset), \
.s_len = (size), \
}
#define XPC_STRING_OUT(msg, offset, size) \
{ \
.s_flags = XPC_STRING_F_OUT | XPC_STRING_F_REMOTE, \
.s_origin = (msg), \
.s_offset = (offset), \
.s_len = (size), \
}
struct xpc_msg;
typedef enum xpc_string_flags {
/* the string can be read from */
XPC_STRING_F_IN = 0x01u,
/* the string can be written to */
XPC_STRING_F_OUT = 0x02u,
/* the string is backed by a buffer located in another address space.
* the string can only be accessed via xpc_string_read and/or
* xpc_string_write */
XPC_STRING_F_REMOTE = 0x04u,
/* free the buffer backing this string when the string is discarded.
* this is only used for out-strings. the buffer must have been
* allocated using xpc_context_alloc, as it will be freed via a call
* to xpc_context_free */
XPC_STRING_F_FREE_ON_DISCARD = 0x08u,
} xpc_string_flags_t;
typedef struct xpc_string {
xpc_string_flags_t s_flags;
union {
struct {
/* only valid if F_OUT is set. specifies the maximum
* number of chars that can be written to s_buf,
* including the null terminator. */
size_t s_max;
/* only valid if F_OUT is set.
* if F_FREE_ON_DISCARD is set, must be either NULL or
* allocated via xpc_context_alloc */
const char *s_buf;
};
struct {
/* only valid if F_IN is set. offset of the string data
* within the associated message. used when reading
* string data from a message. */
size_t s_offset;
};
};
/* only valid if F_REMOTE is set.
* used to read/write string data from/to the sender's address space. */
const struct xpc_msg *s_origin;
/* valid for both F_IN and F_OUT strings.
* F_IN: specifies the length of the incoming string data.
* F_OUT: specifies how many characters from s_buf to send. */
size_t s_len;
} xpc_string_t;
extern xpc_status_t xpc_string_read(
const xpc_string_t *s,
char *out,
size_t max,
size_t *nr_read);
extern xpc_status_t xpc_string_write(
xpc_string_t *s,
const char *in,
size_t len,
size_t *nr_written);
#endif

112
lib/libxpc/msg.c Normal file
View File

@@ -0,0 +1,112 @@
#include <mango/msg.h>
#include <string.h>
#include <xpc/msg.h>
void xpc_msg_header_init(
xpc_msg_header_t *msg,
unsigned long interface,
unsigned short func)
{
memset(msg, 0x0, sizeof *msg);
msg->hdr_magic = XPC_MSG_MAGIC;
msg->hdr_interface = interface;
msg->hdr_func = func;
msg->hdr_status = 0;
}
bool xpc_msg_header_validate(const xpc_msg_header_t *msg)
{
return msg->hdr_magic == XPC_MSG_MAGIC;
}
kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out)
{
kern_iovec_t iov = IOVEC(&out->msg_header, sizeof out->msg_header);
kern_msg_t msg = {
.msg_data = &iov,
.msg_data_count = 1,
.msg_handles = out->msg_handles,
.msg_handles_count = KERN_MSG_MAX_HANDLES,
};
kern_status_t status = msg_recv(channel, &msg);
if (status != KERN_OK) {
return status;
}
if (!xpc_msg_header_validate(&out->msg_header)) {
return KERN_INVALID_ARGUMENT;
}
out->msg_sender.e_channel = channel;
out->msg_sender.e_task = msg.msg_sender;
out->msg_sender.e_port = msg.msg_endpoint;
out->msg_sender.e_msg = msg.msg_id;
return KERN_OK;
}
kern_status_t xpc_msg_read(
const xpc_msg_t *msg,
size_t offset,
void *p,
size_t count)
{
kern_iovec_t iov = IOVEC(p, count);
size_t r = 0;
return msg_read(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
offset,
&iov,
1,
&r);
}
kern_status_t xpc_msg_write(
const xpc_msg_t *msg,
size_t offset,
const void *p,
size_t count)
{
kern_iovec_t iov = IOVEC(p, count);
size_t w = 0;
return msg_write(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
offset,
&iov,
1,
&w);
}
kern_status_t xpc_msg_reply(
const xpc_msg_t *msg,
kern_iovec_t *iov,
size_t iov_count,
kern_msg_handle_t *handles,
size_t handle_count)
{
kern_msg_t reply = MSG(iov, iov_count, handles, handle_count);
return msg_reply(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
&reply);
}
kern_status_t xpc_msg_reply_error(const xpc_msg_t *msg, unsigned short code)
{
xpc_msg_header_t reply_data = {
.hdr_magic = XPC_MSG_MAGIC,
.hdr_interface = msg->msg_header.hdr_interface,
.hdr_func = msg->msg_header.hdr_func,
.hdr_status = code,
};
kern_iovec_t iov = IOVEC(&reply_data, sizeof reply_data);
kern_msg_t reply = MSG(&iov, 1, NULL, 0);
return msg_reply(
msg->msg_sender.e_channel,
msg->msg_sender.e_msg,
&reply);
}

60
lib/libxpc/string.c Normal file
View File

@@ -0,0 +1,60 @@
#include <mango/status.h>
#include <xpc/msg.h>
#include <xpc/string.h>
xpc_status_t xpc_string_read(
const xpc_string_t *s,
char *out,
size_t max,
size_t *nr_read)
{
if ((s->s_flags & (XPC_STRING_F_IN | XPC_STRING_F_REMOTE))
!= (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_read = max - 1;
if (to_read > s->s_len) {
to_read = s->s_len;
}
kern_status_t status
= xpc_msg_read(s->s_origin, s->s_offset, out, to_read);
if (status != KERN_OK) {
return status;
}
out[to_read] = '\0';
/* TODO */
*nr_read = to_read;
return KERN_OK;
}
xpc_status_t xpc_string_write(
xpc_string_t *s,
const char *in,
size_t len,
size_t *nr_written)
{
if ((s->s_flags & (XPC_STRING_F_OUT | XPC_STRING_F_REMOTE))
!= (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) {
return KERN_BAD_STATE;
}
size_t to_write = len;
if (to_write > s->s_max - 1) {
to_write = s->s_max - 1;
}
kern_status_t status
= xpc_msg_write(s->s_origin, s->s_offset, in, to_write);
if (status != KERN_OK) {
return status;
}
/* TODO */
*nr_written = to_write;
return KERN_OK;
}

View File

@@ -1,7 +0,0 @@
#ifndef ERRNO_H_
#define ERRNO_H_
#define EINVAL 1
#define ENOMEM 2
#endif

View File

@@ -1,22 +0,0 @@
#ifndef STDIO_H_
#define STDIO_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int snprintf(char *buffer, size_t count, const char *format, ...);
extern int vsnprintf(
char *buffer,
size_t count,
const char *format,
va_list va);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,14 +0,0 @@
#ifndef STRING_H_
#define STRING_H_
#include <stddef.h>
extern size_t strlen(const char *s);
extern int strcmp(const char *s1, const char *s2);
extern int strncmp(const char *s1, const char *s2, unsigned long n);
extern void *memset(void *str, int c, size_t n);
extern void *memcpy(void *dst, const void *src, size_t len);
#endif

View File

@@ -1,8 +0,0 @@
#ifndef UNISTD_H_
#define UNISTD_H_
#include <stdint.h>
extern void *sbrk(intptr_t increment);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +0,0 @@
void abort(void)
{
/* TODO */
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,637 +0,0 @@
/*
Copyright 2023 Doug Lea
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.
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.
Default header file for malloc-2.8.x
Re-licensed 25 Sep 2023 with MIT-0 replacing obsolete CC0
See https://opensource.org/license/mit-0/
This header is for ANSI C/C++ only. You can set any of
the following #defines before including:
* If USE_DL_PREFIX is defined, it is assumed that malloc.c
was also compiled with this option, so all routines
have names starting with "dl".
* If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this
file will be #included AFTER <malloc.h>. This is needed only if
your system defines a struct mallinfo that is incompatible with the
standard one declared here. Otherwise, you can include this file
INSTEAD of your system system <malloc.h>. At least on ANSI, all
declarations should be compatible with system versions
* If MSPACES is defined, declarations for mspace versions are included.
*/
#ifndef MALLOC_280_H
#define MALLOC_280_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h> /* for size_t */
#ifndef ONLY_MSPACES
#define ONLY_MSPACES 0 /* define to a value */
#elif ONLY_MSPACES != 0
#define ONLY_MSPACES 1
#endif /* ONLY_MSPACES */
#ifndef NO_MALLINFO
#define NO_MALLINFO 0
#endif /* NO_MALLINFO */
#ifndef MSPACES
#if ONLY_MSPACES
#define MSPACES 1
#else /* ONLY_MSPACES */
#define MSPACES 0
#endif /* ONLY_MSPACES */
#endif /* MSPACES */
#if !ONLY_MSPACES
#ifndef USE_DL_PREFIX
#define dlcalloc calloc
#define dlfree free
#define dlmalloc malloc
#define dlmemalign memalign
#define dlposix_memalign posix_memalign
#define dlrealloc realloc
#define dlvalloc valloc
#define dlpvalloc pvalloc
#define dlmallinfo mallinfo
#define dlmallopt mallopt
#define dlmalloc_trim malloc_trim
#define dlmalloc_stats malloc_stats
#define dlmalloc_usable_size malloc_usable_size
#define dlmalloc_footprint malloc_footprint
#define dlmalloc_max_footprint malloc_max_footprint
#define dlmalloc_footprint_limit malloc_footprint_limit
#define dlmalloc_set_footprint_limit malloc_set_footprint_limit
#define dlmalloc_inspect_all malloc_inspect_all
#define dlindependent_calloc independent_calloc
#define dlindependent_comalloc independent_comalloc
#define dlbulk_free bulk_free
#endif /* USE_DL_PREFIX */
#if !NO_MALLINFO
#ifndef HAVE_USR_INCLUDE_MALLOC_H
#ifndef _MALLOC_H
#ifndef MALLINFO_FIELD_TYPE
#define MALLINFO_FIELD_TYPE size_t
#endif /* MALLINFO_FIELD_TYPE */
#ifndef STRUCT_MALLINFO_DECLARED
#define STRUCT_MALLINFO_DECLARED 1
struct mallinfo {
MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */
MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */
MALLINFO_FIELD_TYPE smblks; /* always 0 */
MALLINFO_FIELD_TYPE hblks; /* always 0 */
MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */
MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */
MALLINFO_FIELD_TYPE fsmblks; /* always 0 */
MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
MALLINFO_FIELD_TYPE fordblks; /* total free space */
MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
};
#endif /* STRUCT_MALLINFO_DECLARED */
#endif /* _MALLOC_H */
#endif /* HAVE_USR_INCLUDE_MALLOC_H */
#endif /* !NO_MALLINFO */
/*
malloc(size_t n)
Returns a pointer to a newly allocated chunk of at least n bytes, or
null if no space is available, in which case errno is set to ENOMEM
on ANSI C systems.
If n is zero, malloc returns a minimum-sized chunk. (The minimum
size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
systems.) Note that size_t is an unsigned type, so calls with
arguments that would be negative if signed are interpreted as
requests for huge amounts of space, which will often fail. The
maximum supported value of n differs across systems, but is in all
cases less than the maximum representable value of a size_t.
*/
void* dlmalloc(size_t);
/*
free(void* p)
Releases the chunk of memory pointed to by p, that had been previously
allocated using malloc or a related routine such as realloc.
It has no effect if p is null. If p was not malloced or already
freed, free(p) will by default cuase the current program to abort.
*/
void dlfree(void*);
/*
calloc(size_t n_elements, size_t element_size);
Returns a pointer to n_elements * element_size bytes, with all locations
set to zero.
*/
void* dlcalloc(size_t, size_t);
/*
realloc(void* p, size_t n)
Returns a pointer to a chunk of size n that contains the same data
as does chunk p up to the minimum of (n, p's size) bytes, or null
if no space is available.
The returned pointer may or may not be the same as p. The algorithm
prefers extending p in most cases when possible, otherwise it
employs the equivalent of a malloc-copy-free sequence.
If p is null, realloc is equivalent to malloc.
If space is not available, realloc returns null, errno is set (if on
ANSI) and p is NOT freed.
if n is for fewer bytes than already held by p, the newly unused
space is lopped off and freed if possible. realloc with a size
argument of zero (re)allocates a minimum-sized chunk.
The old unix realloc convention of allowing the last-free'd chunk
to be used as an argument to realloc is not supported.
*/
void* dlrealloc(void*, size_t);
/*
realloc_in_place(void* p, size_t n)
Resizes the space allocated for p to size n, only if this can be
done without moving p (i.e., only if there is adjacent space
available if n is greater than p's current allocated size, or n is
less than or equal to p's size). This may be used instead of plain
realloc if an alternative allocation strategy is needed upon failure
to expand space; for example, reallocation of a buffer that must be
memory-aligned or cleared. You can use realloc_in_place to trigger
these alternatives only when needed.
Returns p if successful; otherwise null.
*/
void* dlrealloc_in_place(void*, size_t);
/*
memalign(size_t alignment, size_t n);
Returns a pointer to a newly allocated chunk of n bytes, aligned
in accord with the alignment argument.
The alignment argument should be a power of two. If the argument is
not a power of two, the nearest greater power is used.
8-byte alignment is guaranteed by normal malloc calls, so don't
bother calling memalign with an argument of 8 or less.
Overreliance on memalign is a sure way to fragment space.
*/
void* dlmemalign(size_t, size_t);
/*
int posix_memalign(void** pp, size_t alignment, size_t n);
Allocates a chunk of n bytes, aligned in accord with the alignment
argument. Differs from memalign only in that it (1) assigns the
allocated memory to *pp rather than returning it, (2) fails and
returns EINVAL if the alignment is not a power of two (3) fails and
returns ENOMEM if memory cannot be allocated.
*/
int dlposix_memalign(void**, size_t, size_t);
/*
valloc(size_t n);
Equivalent to memalign(pagesize, n), where pagesize is the page
size of the system. If the pagesize is unknown, 4096 is used.
*/
void* dlvalloc(size_t);
/*
mallopt(int parameter_number, int parameter_value)
Sets tunable parameters The format is to provide a
(parameter-number, parameter-value) pair. mallopt then sets the
corresponding parameter to the argument value if it can (i.e., so
long as the value is meaningful), and returns 1 if successful else
0. SVID/XPG/ANSI defines four standard param numbers for mallopt,
normally defined in malloc.h. None of these are use in this malloc,
so setting them has no effect. But this malloc also supports other
options in mallopt:
Symbol param # default allowed param values
M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming)
M_GRANULARITY -2 page size any power of 2 >= page size
M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support)
*/
int dlmallopt(int, int);
#define M_TRIM_THRESHOLD (-1)
#define M_GRANULARITY (-2)
#define M_MMAP_THRESHOLD (-3)
/*
malloc_footprint();
Returns the number of bytes obtained from the system. The total
number of bytes allocated by malloc, realloc etc., is less than this
value. Unlike mallinfo, this function returns only a precomputed
result, so can be called frequently to monitor memory consumption.
Even if locks are otherwise defined, this function does not use them,
so results might not be up to date.
*/
size_t dlmalloc_footprint(void);
/*
malloc_max_footprint();
Returns the maximum number of bytes obtained from the system. This
value will be greater than current footprint if deallocated space
has been reclaimed by the system. The peak number of bytes allocated
by malloc, realloc etc., is less than this value. Unlike mallinfo,
this function returns only a precomputed result, so can be called
frequently to monitor memory consumption. Even if locks are
otherwise defined, this function does not use them, so results might
not be up to date.
*/
size_t dlmalloc_max_footprint(void);
/*
malloc_footprint_limit();
Returns the number of bytes that the heap is allowed to obtain from
the system, returning the last value returned by
malloc_set_footprint_limit, or the maximum size_t value if
never set. The returned value reflects a permission. There is no
guarantee that this number of bytes can actually be obtained from
the system.
*/
size_t dlmalloc_footprint_limit(void);
/*
malloc_set_footprint_limit();
Sets the maximum number of bytes to obtain from the system, causing
failure returns from malloc and related functions upon attempts to
exceed this value. The argument value may be subject to page
rounding to an enforceable limit; this actual value is returned.
Using an argument of the maximum possible size_t effectively
disables checks. If the argument is less than or equal to the
current malloc_footprint, then all future allocations that require
additional system memory will fail. However, invocation cannot
retroactively deallocate existing used memory.
*/
size_t dlmalloc_set_footprint_limit(size_t bytes);
/*
malloc_inspect_all(void(*handler)(void *start,
void *end,
size_t used_bytes,
void* callback_arg),
void* arg);
Traverses the heap and calls the given handler for each managed
region, skipping all bytes that are (or may be) used for bookkeeping
purposes. Traversal does not include include chunks that have been
directly memory mapped. Each reported region begins at the start
address, and continues up to but not including the end address. The
first used_bytes of the region contain allocated data. If
used_bytes is zero, the region is unallocated. The handler is
invoked with the given callback argument. If locks are defined, they
are held during the entire traversal. It is a bad idea to invoke
other malloc functions from within the handler.
For example, to count the number of in-use chunks with size greater
than 1000, you could write:
static int count = 0;
void count_chunks(void* start, void* end, size_t used, void* arg) {
if (used >= 1000) ++count;
}
then:
malloc_inspect_all(count_chunks, NULL);
malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined.
*/
void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
void* arg);
#if !NO_MALLINFO
/*
mallinfo()
Returns (by copy) a struct containing various summary statistics:
arena: current total non-mmapped bytes allocated from system
ordblks: the number of free chunks
smblks: always zero.
hblks: current number of mmapped regions
hblkhd: total bytes held in mmapped regions
usmblks: the maximum total allocated space. This will be greater
than current total if trimming has occurred.
fsmblks: always zero
uordblks: current total allocated space (normal or mmapped)
fordblks: total free space
keepcost: the maximum number of bytes that could ideally be released
back to system via malloc_trim. ("ideally" means that
it ignores page restrictions etc.)
Because these fields are ints, but internal bookkeeping may
be kept as longs, the reported values may wrap around zero and
thus be inaccurate.
*/
struct mallinfo dlmallinfo(void);
#endif /* NO_MALLINFO */
/*
independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
independent_calloc is similar to calloc, but instead of returning a
single cleared space, it returns an array of pointers to n_elements
independent elements that can hold contents of size elem_size, each
of which starts out cleared, and can be independently freed,
realloc'ed etc. The elements are guaranteed to be adjacently
allocated (this is not guaranteed to occur with multiple callocs or
mallocs), which may also improve cache locality in some
applications.
The "chunks" argument is optional (i.e., may be null, which is
probably the most typical usage). If it is null, the returned array
is itself dynamically allocated and should also be freed when it is
no longer needed. Otherwise, the chunks array must be of at least
n_elements in length. It is filled in with the pointers to the
chunks.
In either case, independent_calloc returns this pointer array, or
null if the allocation failed. If n_elements is zero and "chunks"
is null, it returns a chunk representing an array with zero elements
(which should be freed if not wanted).
Each element must be freed when it is no longer needed. This can be
done all at once using bulk_free.
independent_calloc simplifies and speeds up implementations of many
kinds of pools. It may also be useful when constructing large data
structures that initially have a fixed number of fixed-sized nodes,
but the number is not known at compile time, and some of the nodes
may later need to be freed. For example:
struct Node { int item; struct Node* next; };
struct Node* build_list() {
struct Node** pool;
int n = read_number_of_nodes_needed();
if (n <= 0) return 0;
pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
if (pool == 0) die();
// organize into a linked list...
struct Node* first = pool[0];
for (i = 0; i < n-1; ++i)
pool[i]->next = pool[i+1];
free(pool); // Can now free the array (or not, if it is needed later)
return first;
}
*/
void** dlindependent_calloc(size_t, size_t, void**);
/*
independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
independent_comalloc allocates, all at once, a set of n_elements
chunks with sizes indicated in the "sizes" array. It returns
an array of pointers to these elements, each of which can be
independently freed, realloc'ed etc. The elements are guaranteed to
be adjacently allocated (this is not guaranteed to occur with
multiple callocs or mallocs), which may also improve cache locality
in some applications.
The "chunks" argument is optional (i.e., may be null). If it is null
the returned array is itself dynamically allocated and should also
be freed when it is no longer needed. Otherwise, the chunks array
must be of at least n_elements in length. It is filled in with the
pointers to the chunks.
In either case, independent_comalloc returns this pointer array, or
null if the allocation failed. If n_elements is zero and chunks is
null, it returns a chunk representing an array with zero elements
(which should be freed if not wanted).
Each element must be freed when it is no longer needed. This can be
done all at once using bulk_free.
independent_comallac differs from independent_calloc in that each
element may have a different size, and also that it does not
automatically clear elements.
independent_comalloc can be used to speed up allocation in cases
where several structs or objects must always be allocated at the
same time. For example:
struct Head { ... }
struct Foot { ... }
void send_message(char* msg) {
int msglen = strlen(msg);
size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
void* chunks[3];
if (independent_comalloc(3, sizes, chunks) == 0)
die();
struct Head* head = (struct Head*)(chunks[0]);
char* body = (char*)(chunks[1]);
struct Foot* foot = (struct Foot*)(chunks[2]);
// ...
}
In general though, independent_comalloc is worth using only for
larger values of n_elements. For small values, you probably won't
detect enough difference from series of malloc calls to bother.
Overuse of independent_comalloc can increase overall memory usage,
since it cannot reuse existing noncontiguous small chunks that
might be available for some of the elements.
*/
void** dlindependent_comalloc(size_t, size_t*, void**);
/*
bulk_free(void* array[], size_t n_elements)
Frees and clears (sets to null) each non-null pointer in the given
array. This is likely to be faster than freeing them one-by-one.
If footers are used, pointers that have been allocated in different
mspaces are not freed or cleared, and the count of all such pointers
is returned. For large arrays of pointers with poor locality, it
may be worthwhile to sort this array before calling bulk_free.
*/
size_t dlbulk_free(void**, size_t n_elements);
/*
pvalloc(size_t n);
Equivalent to valloc(minimum-page-that-holds(n)), that is,
round up n to nearest pagesize.
*/
void* dlpvalloc(size_t);
/*
malloc_trim(size_t pad);
If possible, gives memory back to the system (via negative arguments
to sbrk) if there is unused memory at the `high' end of the malloc
pool or in unused MMAP segments. You can call this after freeing
large blocks of memory to potentially reduce the system-level memory
requirements of a program. However, it cannot guarantee to reduce
memory. Under some allocation patterns, some large free blocks of
memory will be locked between two used chunks, so they cannot be
given back to the system.
The `pad' argument to malloc_trim represents the amount of free
trailing space to leave untrimmed. If this argument is zero, only
the minimum amount of memory to maintain internal data structures
will be left. Non-zero arguments can be supplied to maintain enough
trailing space to service future expected allocations without having
to re-obtain memory from the system.
Malloc_trim returns 1 if it actually released any memory, else 0.
*/
int dlmalloc_trim(size_t);
/*
malloc_stats();
Prints on stderr the amount of space obtained from the system (both
via sbrk and mmap), the maximum amount (which may be more than
current if malloc_trim and/or munmap got called), and the current
number of bytes allocated via malloc (or realloc, etc) but not yet
freed. Note that this is the number of bytes allocated, not the
number requested. It will be larger than the number requested
because of alignment and bookkeeping overhead. Because it includes
alignment wastage as being in use, this figure may be greater than
zero even when no user-level chunks are allocated.
The reported current and maximum system memory can be inaccurate if
a program makes other calls to system memory allocation functions
(normally sbrk) outside of malloc.
malloc_stats prints only the most commonly interesting statistics.
More information can be obtained by calling mallinfo.
malloc_stats is not compiled if NO_MALLOC_STATS is defined.
*/
void dlmalloc_stats(void);
#endif /* !ONLY_MSPACES */
/*
malloc_usable_size(void* p);
Returns the number of bytes you can actually use in
an allocated chunk, which may be more than you requested (although
often not) due to alignment and minimum size constraints.
You can use this many bytes without worrying about
overwriting other allocated objects. This is not a particularly great
programming practice. malloc_usable_size can be more useful in
debugging and assertions, for example:
p = malloc(n);
assert(malloc_usable_size(p) >= 256);
*/
size_t dlmalloc_usable_size(const void*);
#if MSPACES
/*
mspace is an opaque type representing an independent
region of space that supports mspace_malloc, etc.
*/
typedef void* mspace;
/*
create_mspace creates and returns a new independent space with the
given initial capacity, or, if 0, the default granularity size. It
returns null if there is no system memory available to create the
space. If argument locked is non-zero, the space uses a separate
lock to control access. The capacity of the space will grow
dynamically as needed to service mspace_malloc requests. You can
control the sizes of incremental increases of this space by
compiling with a different DEFAULT_GRANULARITY or dynamically
setting with mallopt(M_GRANULARITY, value).
*/
mspace create_mspace(size_t capacity, int locked);
/*
destroy_mspace destroys the given space, and attempts to return all
of its memory back to the system, returning the total number of
bytes freed. After destruction, the results of access to all memory
used by the space become undefined.
*/
size_t destroy_mspace(mspace msp);
/*
create_mspace_with_base uses the memory supplied as the initial base
of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
space is used for bookkeeping, so the capacity must be at least this
large. (Otherwise 0 is returned.) When this initial space is
exhausted, additional memory will be obtained from the system.
Destroying this space will deallocate all additionally allocated
space (if possible) but not the initial base.
*/
mspace create_mspace_with_base(void* base, size_t capacity, int locked);
/*
mspace_track_large_chunks controls whether requests for large chunks
are allocated in their own untracked mmapped regions, separate from
others in this mspace. By default large chunks are not tracked,
which reduces fragmentation. However, such chunks are not
necessarily released to the system upon destroy_mspace. Enabling
tracking by setting to true may increase fragmentation, but avoids
leakage when relying on destroy_mspace to release all memory
allocated using this space. The function returns the previous
setting.
*/
int mspace_track_large_chunks(mspace msp, int enable);
#if !NO_MALLINFO
/*
mspace_mallinfo behaves as mallinfo, but reports properties of
the given space.
*/
struct mallinfo mspace_mallinfo(mspace msp);
#endif /* NO_MALLINFO */
/*
An alias for mallopt.
*/
int mspace_mallopt(int, int);
/*
The following operate identically to their malloc counterparts
but operate only for the given mspace argument
*/
void* mspace_malloc(mspace msp, size_t bytes);
void mspace_free(mspace msp, void* mem);
void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
void* mspace_realloc(mspace msp, void* mem, size_t newsize);
void* mspace_realloc_in_place(mspace msp, void* mem, size_t newsize);
void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
void** mspace_independent_calloc(mspace msp, size_t n_elements,
size_t elem_size, void* chunks[]);
void** mspace_independent_comalloc(mspace msp, size_t n_elements,
size_t sizes[], void* chunks[]);
size_t mspace_bulk_free(mspace msp, void**, size_t n_elements);
size_t mspace_usable_size(const void* mem);
void mspace_malloc_stats(mspace msp);
int mspace_trim(mspace msp, size_t pad);
size_t mspace_footprint(mspace msp);
size_t mspace_max_footprint(mspace msp);
size_t mspace_footprint_limit(mspace msp);
size_t mspace_set_footprint_limit(mspace msp, size_t bytes);
void mspace_inspect_all(mspace msp,
void(*handler)(void *, void *, size_t, void*),
void* arg);
#endif /* MSPACES */
#ifdef __cplusplus
}; /* end of extern "C" */
#endif
#endif /* MALLOC_280_H */

View File

@@ -1,107 +0,0 @@
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stddef.h>
#include <stdint.h>
typedef uintptr_t word;
#define wsize sizeof(word)
#define wmask (wsize - 1)
void *memcpy(void *dst0, const void *src0, size_t length)
{
char *dst = dst0;
const char *src = src0;
size_t t;
if (length == 0 || dst == src) /* nothing to do */
goto done;
/*
* Macros: loop-t-times; and loop-t-times, t>0
*/
#define TLOOP(s) \
if (t) \
TLOOP1(s)
#define TLOOP1(s) \
do { \
s; \
} while (--t)
if ((uintptr_t)dst < (uintptr_t)src) {
/*
* Copy forward.
*/
t = (uintptr_t)src; /* only need low bits */
if ((t | (uintptr_t)dst) & wmask) {
/*
* Try to align operands. This cannot be done
* unless the low bits match.
*/
if ((t ^ (uintptr_t)dst) & wmask || length < wsize)
t = length;
else
t = wsize - (t & wmask);
length -= t;
TLOOP1(*dst++ = *src++);
}
/*
* Copy whole words, then mop up any trailing bytes.
*/
t = length / wsize;
TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
t = length & wmask;
TLOOP(*dst++ = *src++);
} else {
/*
* Copy backwards. Otherwise essentially the same.
* Alignment works as before, except that it takes
* (t&wmask) bytes to align, not wsize-(t&wmask).
*/
src += length;
dst += length;
t = (uintptr_t)src;
if ((t | (uintptr_t)dst) & wmask) {
if ((t ^ (uintptr_t)dst) & wmask || length <= wsize)
t = length;
else
t &= wmask;
length -= t;
TLOOP1(*--dst = *--src);
}
t = length / wsize;
TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
t = length & wmask;
TLOOP(*--dst = *--src);
}
done:
return (dst0);
}

View File

@@ -1,13 +0,0 @@
#include <stddef.h>
void *memset(void *str, int c, size_t n)
{
unsigned char val = (unsigned char)c;
unsigned char *buf = str;
for (size_t i = 0; i < n; i++) {
buf[i] = val;
}
return str;
}

Some files were not shown because too many files have changed in this diff Show More