Compare commits
79 Commits
9c8fbc72ac
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 86ca343cf0 | |||
| b680ffdd5b | |||
| 14799e0d58 | |||
| b7452a449b | |||
| ea6ec785a9 | |||
| 6d88cf4bf3 | |||
| aef0163017 | |||
| b0fda122e0 | |||
| 79af171384 | |||
| 5931642cc2 | |||
| 26a49162e6 | |||
| 3a06c18e10 | |||
| e46c20b572 | |||
| 0a07db360c | |||
| 1f2bd09d57 | |||
| 72b5801292 | |||
| 5ad1babd03 | |||
| d0abe3333d | |||
| 5658e27445 | |||
| c4fd252f86 | |||
| a6a2526502 | |||
| faeefe28ab | |||
| 1c89acddbf | |||
| f47be0cd67 | |||
| 68714fa0e5 | |||
| 267b893bf4 | |||
| d6b3af6e67 | |||
| 4be6f231e6 | |||
| 598bdb1410 | |||
| 285a80530d | |||
| 3b50715559 | |||
| bdb2e0ed50 | |||
| 01109948b2 | |||
| dc93f7db4c | |||
| b631fca0fd | |||
| e2b19c3e9a | |||
| 411423bf30 | |||
| 0183790af3 | |||
| 418c426e83 | |||
| eca9d3915b | |||
| d32988c56c | |||
| 02d4f43151 | |||
| 1645fdfb42 | |||
| dbb2d72bcb | |||
| 67187e457f | |||
| f778762545 | |||
| c39e0b206a | |||
| 562a729c25 | |||
| 5721daa049 | |||
| fe3b014e09 | |||
| 67ae16a756 | |||
| 5bc9ff4fd1 | |||
| 728e1f057c | |||
| b62052f619 | |||
| 2c41f1a77a | |||
| 7a51a56909 | |||
| cff399fdba | |||
| c476b08c03 | |||
| 32aaacb95f | |||
| 0b7b8d3f29 | |||
| cc4b9d4a9b | |||
| 10cf618834 | |||
| 06760906b9 | |||
| 95bb04e866 | |||
| f1e71cafc4 | |||
| 75bd11d5cc | |||
| efad96ce63 | |||
| 1e6748b4fc | |||
| ff66c8d89f | |||
| ba455059ac | |||
| 9cc60cf3f1 | |||
| 281a3d5801 | |||
| ba8d6908fa | |||
| c426afce67 | |||
| f5daf972a8 | |||
| 2e6d7df38c | |||
| 0e562f6a79 | |||
| 3d28ed2cb8 | |||
| 0ca0bb39ca |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -185,7 +185,6 @@ dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(Rosetta C CXX ASM)
|
||||
|
||||
set(sys_dir ${CMAKE_CURRENT_BINARY_DIR}/sys)
|
||||
|
||||
set(kernel_name mango_kernel)
|
||||
set(bsp_name rosetta-system.bsp)
|
||||
|
||||
@@ -9,22 +11,32 @@ include(Meta)
|
||||
include(Sysroot)
|
||||
include(BSP)
|
||||
include(Arch)
|
||||
include(Msg-Interface)
|
||||
include(Templates)
|
||||
|
||||
bsp_reset()
|
||||
sysroot_reset()
|
||||
|
||||
sysroot_set_base(
|
||||
PATH ${CMAKE_SOURCE_DIR}/base)
|
||||
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(programs)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(sys)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(services)
|
||||
add_subdirectory(programs)
|
||||
|
||||
sysroot_add_program(NAME ${kernel_name} BIN_DIR /boot)
|
||||
|
||||
bsp_finalise(BSP_NAME ${bsp_name})
|
||||
bsp_finalise(
|
||||
BSP_NAME ${bsp_name}
|
||||
DEST_DIR ${sys_dir}
|
||||
BOOTSTRAP_PROGRAM bootstrap)
|
||||
|
||||
sysroot_add_file(
|
||||
ID bsp
|
||||
SRC_PATH ${CMAKE_BINARY_DIR}/${bsp_name}
|
||||
SRC_PATH ${CMAKE_BINARY_DIR}/sys/${bsp_name}
|
||||
DEST_DIR /boot
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/${bsp_name})
|
||||
DEPENDS bsp)
|
||||
sysroot_finalise()
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
include(System-Disk)
|
||||
include(QEMU)
|
||||
include(Bochs)
|
||||
|
||||
18
arch/x86_64/Bochs.cmake
Normal file
18
arch/x86_64/Bochs.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
find_program(BOCHS bochs)
|
||||
|
||||
if (NOT BOCHS)
|
||||
message(STATUS "Bochs: cannot find bochs")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET cdrom)
|
||||
message(STATUS "Bochs: CD-ROM image creation is unavailable. Cannot use bochs")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
message(STATUS "Bochs: Enable CD-ROM boot")
|
||||
add_custom_target(run-cdrom-bochs
|
||||
COMMAND ${BOCHS} -q -f ${CMAKE_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/bochsrc.bxrc
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
USES_TERMINAL
|
||||
DEPENDS cdrom)
|
||||
@@ -7,8 +7,15 @@ find_program(C_COMPILER x86_64-elf-gcc REQUIRED)
|
||||
find_program(CXX_COMPILER x86_64-elf-g++ REQUIRED)
|
||||
find_program(ASM_COMPILER x86_64-elf-as REQUIRED)
|
||||
|
||||
add_compile_definitions(__mango__=1)
|
||||
|
||||
set(CMAKE_C_COMPILER ${C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${CXX_COMPILER})
|
||||
set(CMAKE_ASM_COMPILER ${ASM_COMPILER})
|
||||
|
||||
SET(CMAKE_C_FLAGS "-ffreestanding -nostdlib" CACHE STRING "" FORCE)
|
||||
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,--unresolved-symbols=report-all,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
|
||||
|
||||
set(CMAKE_C_OUTPUT_EXTENSION .o)
|
||||
set(CMAKE_CXX_OUTPUT_EXTENSION .o)
|
||||
|
||||
@@ -1,5 +1,123 @@
|
||||
find_program(QEMU qemu-system-${TARGET_ARCH} REQUIRED)
|
||||
if (NOT QEMU)
|
||||
message(STATUS "QEMU: Cannot find qemu-system-${TARGET_ARCH}. Direct-kernel boot unavailable")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
find_program(LLDB lldb)
|
||||
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(no_debug_flags ${no_debug_flags} -enable-kvm)
|
||||
else ()
|
||||
message(STATUS "QEMU: Host system is not Linux. KVM acceleration unavailable")
|
||||
endif ()
|
||||
|
||||
add_custom_command(OUTPUT ${patched_kernel}
|
||||
DEPENDS ${kernel_name}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
$<TARGET_FILE:${kernel_name}>
|
||||
${patched_kernel}
|
||||
COMMAND ${BUILD_TOOLS_DIR}/e64patch ${patched_kernel}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Patching kernel elf64 image"
|
||||
)
|
||||
|
||||
message(STATUS "QEMU: Enable direct-kernel boot")
|
||||
add_custom_target(run-kernel
|
||||
COMMAND ${QEMU} -kernel $<TARGET_FILE:${kernel_name}>
|
||||
DEPENDS ${kernel_name} ${bsp_name})
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags} ${no_debug_flags}
|
||||
-serial stdio
|
||||
--append kernel.early-console=ttyS0
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
add_custom_target(run-kernel-monitor
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags} ${no_debug_flags}
|
||||
-monitor stdio
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
if (image_cdrom)
|
||||
message(STATUS "QEMU: Enable CD-ROM boot")
|
||||
add_custom_target(run-cdrom
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-cdrom ${image_cdrom}
|
||||
${generic_flags} ${no_debug_flags}
|
||||
-serial stdio
|
||||
USES_TERMINAL
|
||||
DEPENDS ${image_cdrom})
|
||||
endif ()
|
||||
|
||||
if (LLDB)
|
||||
message(STATUS "QEMU: Enable direct-kernel debug with LLDB")
|
||||
add_custom_target(debug-kernel
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${LLDB}
|
||||
-o "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-o "gdb-remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
if (image_cdrom)
|
||||
message(STATUS "QEMU: Enable CD-ROM debug with LLDB")
|
||||
add_custom_target(debug-cdrom
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-cdrom ${image_cdrom}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${LLDB}
|
||||
-o "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-o "target remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS cdrom)
|
||||
endif ()
|
||||
elseif (GDB)
|
||||
message(STATUS "QEMU: Enable direct-kernel debug with GDB")
|
||||
add_custom_target(debug-kernel
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-kernel ${patched_kernel}
|
||||
-initrd ${sys_dir}/${bsp_name}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${GDB} -tui
|
||||
-ex "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-ex "target remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
|
||||
if (image_cdrom)
|
||||
message(STATUS "QEMU: Enable CD-ROM debug with GDB")
|
||||
add_custom_target(debug-cdrom
|
||||
COMMAND
|
||||
${QEMU}
|
||||
-cdrom ${image_cdrom}
|
||||
${generic_flags}
|
||||
-s -S &
|
||||
${GDB} -tui
|
||||
-ex "file ${CMAKE_BINARY_DIR}/kernel/${kernel_name}.debug"
|
||||
-ex "target remote localhost:1234"
|
||||
USES_TERMINAL
|
||||
DEPENDS ${image_cdrom})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
17
arch/x86_64/System-Disk.cmake
Normal file
17
arch/x86_64/System-Disk.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
find_program(GRUB_MKRESCUE grub-mkrescue)
|
||||
|
||||
if (GRUB_MKRESCUE)
|
||||
message(STATUS "GRUB: Found grub-mkrescue. Bootable CD-ROM image creation is enabled")
|
||||
set(image_cdrom ${CMAKE_CURRENT_BINARY_DIR}/rosetta-system.iso)
|
||||
|
||||
add_custom_target(cdrom
|
||||
DEPENDS sysroot
|
||||
COMMAND ${GRUB_MKRESCUE}
|
||||
-o ${image_cdrom}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sysroot
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Building GRUB CD-ROM image"
|
||||
)
|
||||
else ()
|
||||
message(STATUS "Cannot find grub-mkrescue. Bootable CD-ROM image creation is unavailable")
|
||||
endif ()
|
||||
53
arch/x86_64/bochsrc.bxrc
Normal file
53
arch/x86_64/bochsrc.bxrc
Normal file
@@ -0,0 +1,53 @@
|
||||
# configuration file generated by Bochs
|
||||
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true
|
||||
config_interface: textconfig
|
||||
display_library: sdl2
|
||||
memory: guest=128, host=128, block_size=128
|
||||
romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none, flash_data=none
|
||||
vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest.bin"
|
||||
boot: cdrom
|
||||
floppy_bootsig_check: disabled=0
|
||||
floppya: type=1_44
|
||||
# no floppyb
|
||||
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||
ata0-master: type=cdrom, path="rosetta-system.iso", status=inserted, model="Generic 1234", biosdetect=auto
|
||||
ata0-slave: type=none
|
||||
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||
ata1-master: type=none
|
||||
ata1-slave: type=none
|
||||
ata2: enabled=false
|
||||
ata3: enabled=false
|
||||
optromimage1: file=none
|
||||
optromimage2: file=none
|
||||
optromimage3: file=none
|
||||
optromimage4: file=none
|
||||
optramimage1: file=none
|
||||
optramimage2: file=none
|
||||
optramimage3: file=none
|
||||
optramimage4: file=none
|
||||
pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none
|
||||
vga: extension=vbe, update_freq=10, realtime=1, ddc=builtin, vbe_memsize=16
|
||||
cpu: count=1:1:1, ips=4000000, quantum=16, model=core2_penryn_t9600, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
|
||||
print_timestamps: enabled=0
|
||||
debugger_log: -
|
||||
magic_break: enabled=1 0x0
|
||||
port_e9_hack: enabled=false, all_rings=false
|
||||
iodebug: all_rings=0
|
||||
private_colormap: enabled=0
|
||||
clock: sync=none, time0=local, rtc_sync=0
|
||||
# no cmosimage
|
||||
log: -
|
||||
logprefix: %t%e%d
|
||||
debug: action=ignore
|
||||
info: action=report
|
||||
error: action=report
|
||||
panic: action=ask
|
||||
keyboard: type=mf, serial_delay=150, paste_delay=100000, user_shortcut=none
|
||||
mouse: type=ps2, enabled=false, toggle=ctrl+mbutton
|
||||
speaker: enabled=true, mode=system
|
||||
parport1: enabled=true, file=none
|
||||
parport2: enabled=false
|
||||
com1: enabled=true, mode=null
|
||||
com2: enabled=false
|
||||
com3: enabled=false
|
||||
com4: enabled=false
|
||||
11
base/boot/grub/grub.cfg
Normal file
11
base/boot/grub/grub.cfg
Normal file
@@ -0,0 +1,11 @@
|
||||
menuentry "Rosetta" {
|
||||
multiboot /boot/mango_kernel
|
||||
module /boot/rosetta-system.bsp
|
||||
boot
|
||||
}
|
||||
|
||||
menuentry "Rosetta (Serial Log)" {
|
||||
multiboot /boot/mango_kernel kernel.early-console=ttyS0
|
||||
module /boot/rosetta-system.bsp
|
||||
boot
|
||||
}
|
||||
@@ -34,9 +34,6 @@ function(bsp_add_library)
|
||||
COMMAND ${Python_EXECUTABLE} ${bsp_tool}
|
||||
add-binary ${bsp_manifest} ${target_name}
|
||||
${arg_LIB_DIR} $<TARGET_FILE:${target_name}>
|
||||
COMMAND ${Python_EXECUTABLE} ${bsp_tool}
|
||||
add-headers ${bsp_manifest} ${target_name}
|
||||
${arg_HEADER_DIR} ${header_dirs}
|
||||
COMMENT "Preparing bsp component: ${target_name}"
|
||||
DEPENDS ${target_name} ${serialiser})
|
||||
|
||||
@@ -75,7 +72,7 @@ endfunction(bsp_add_program)
|
||||
|
||||
function(bsp_finalise)
|
||||
set(options)
|
||||
set(one_value_args BSP_NAME)
|
||||
set(one_value_args BOOTSTRAP_PROGRAM DEST_DIR BSP_NAME)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
@@ -83,13 +80,12 @@ function(bsp_finalise)
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(bsp_output_path ${CMAKE_CURRENT_BINARY_DIR}/${arg_BSP_NAME})
|
||||
set(bsp_output_path ${arg_DEST_DIR}/${arg_BSP_NAME})
|
||||
get_property(bsp_targets GLOBAL PROPERTY bsp_target_list)
|
||||
add_custom_command(OUTPUT ${bsp_output_path}
|
||||
COMMAND ${Python_EXECUTABLE} ${bsp_tool}
|
||||
build-bsp ${bsp_manifest} ${bsp_output_path}
|
||||
DEPENDS ${bsp_targets}
|
||||
COMMENT "Building bsp: ${arg_BSP_NAME}")
|
||||
add_custom_target(bsp
|
||||
DEPENDS ${bsp_output_path})
|
||||
COMMAND ${Python_EXECUTABLE} ${bsp_tool}
|
||||
build-bsp ${bsp_manifest}
|
||||
$<TARGET_FILE:${arg_BOOTSTRAP_PROGRAM}> ${bsp_output_path}
|
||||
DEPENDS ${bsp_targets} ${arg_BOOTSTRAP_PROGRAM}
|
||||
COMMENT "Building bsp: ${arg_BSP_NAME}")
|
||||
endfunction(bsp_finalise)
|
||||
|
||||
75
cmake/Msg-Interface.cmake
Normal file
75
cmake/Msg-Interface.cmake
Normal 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)
|
||||
@@ -9,6 +9,34 @@ function(sysroot_reset)
|
||||
COMMAND_ERROR_IS_FATAL ANY)
|
||||
endfunction(sysroot_reset)
|
||||
|
||||
function(sysroot_set_base)
|
||||
set(options)
|
||||
set(one_value_args PATH)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(sysroot_target_name _sysroot-base)
|
||||
|
||||
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}
|
||||
set-base ${sysroot_manifest} ${arg_PATH}
|
||||
COMMENT "Preparing sysroot base"
|
||||
DEPENDS ${serialiser})
|
||||
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_set_base)
|
||||
|
||||
function(sysroot_add_library)
|
||||
set(options)
|
||||
set(one_value_args NAME HEADER_DIR LIB_DIR)
|
||||
@@ -30,19 +58,73 @@ function(sysroot_add_library)
|
||||
list(GET sysroot_targets ${serialiser_index} serialiser)
|
||||
endif ()
|
||||
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-binary ${sysroot_manifest} ${target_name}
|
||||
${arg_LIB_DIR} $<TARGET_FILE:${target_name}>
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-headers ${sysroot_manifest} ${target_name}
|
||||
${arg_HEADER_DIR} ${header_dirs}
|
||||
COMMENT "Preparing sysroot component: ${target_name}"
|
||||
DEPENDS ${target_name} ${serialiser})
|
||||
if (arg_HEADER_DIR)
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-binary ${sysroot_manifest} ${target_name}
|
||||
${arg_LIB_DIR} $<TARGET_FILE:${target_name}>
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-headers ${sysroot_manifest} ${target_name}
|
||||
${arg_HEADER_DIR} ${header_dirs}
|
||||
COMMENT "Preparing sysroot component: ${target_name}"
|
||||
DEPENDS ${target_name} ${serialiser})
|
||||
else()
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-binary ${sysroot_manifest} ${target_name}
|
||||
${arg_LIB_DIR} $<TARGET_FILE:${target_name}>
|
||||
COMMENT "Preparing sysroot component: ${target_name}"
|
||||
DEPENDS ${target_name} ${serialiser})
|
||||
endif ()
|
||||
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_add_library)
|
||||
|
||||
function(sysroot_add_object_library)
|
||||
set(options)
|
||||
set(one_value_args NAME HEADER_DIR LIB_DIR)
|
||||
set(multi_value_args)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(target_name ${arg_NAME})
|
||||
set(sysroot_target_name _sysroot-${target_name})
|
||||
meta_target_get_header_directories(TARGET ${target_name} OUT header_dirs)
|
||||
|
||||
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 ()
|
||||
|
||||
if (arg_HEADER_DIR)
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-binary ${sysroot_manifest} ${target_name}
|
||||
${arg_LIB_DIR} $<TARGET_OBJECTS:${target_name}>
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-headers ${sysroot_manifest} ${target_name}
|
||||
${arg_HEADER_DIR} ${header_dirs}
|
||||
COMMENT "Preparing sysroot component: ${target_name}"
|
||||
DEPENDS ${target_name} ${serialiser})
|
||||
else()
|
||||
add_custom_target(${sysroot_target_name}
|
||||
COMMAND ${Python_EXECUTABLE} ${sysroot_tool}
|
||||
add-binary ${sysroot_manifest} ${target_name}
|
||||
${arg_LIB_DIR} $<TARGET_OBJECTS:${target_name}>
|
||||
COMMENT "Preparing sysroot component: ${target_name}"
|
||||
DEPENDS ${target_name} ${serialiser})
|
||||
endif ()
|
||||
|
||||
get_property(tmp TARGET ${target_name} PROPERTY SUFFIX)
|
||||
|
||||
set_property(GLOBAL PROPERTY sysroot_target_list ${sysroot_targets} ${sysroot_target_name})
|
||||
endfunction(sysroot_add_object_library)
|
||||
|
||||
function(sysroot_add_program)
|
||||
set(options)
|
||||
set(one_value_args NAME BIN_DIR)
|
||||
@@ -104,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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
function(rosetta_add_executable)
|
||||
set(options)
|
||||
set(one_value_args NAME SYSROOT_PATH)
|
||||
set(one_value_args NAME)
|
||||
set(multi_value_args
|
||||
SUBDIRS
|
||||
EXTRA_SOURCES)
|
||||
@@ -33,11 +33,85 @@ function(rosetta_add_executable)
|
||||
endfunction(rosetta_add_executable)
|
||||
|
||||
function(rosetta_add_library)
|
||||
set(options SHARED)
|
||||
set(one_value_args NAME SYSROOT_PATH)
|
||||
set(options STATIC SHARED)
|
||||
set(one_value_args NAME)
|
||||
set(multi_value_args
|
||||
SOURCE_DIRS
|
||||
EXTRA_SOURCES)
|
||||
PUBLIC_INCLUDE_DIRS
|
||||
SOURCES
|
||||
HEADERS)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(lib_name ${arg_NAME})
|
||||
set(targets)
|
||||
|
||||
if ((NOT ${arg_STATIC}) AND (NOT ${arg_SHARED}))
|
||||
message(FATAL_ERROR "rosetta_add_library(${arg_NAME}): must specified SHARED and/or STATIC")
|
||||
endif ()
|
||||
|
||||
set(static_lib_name ${lib_name})
|
||||
set(shared_lib_name ${lib_name})
|
||||
|
||||
if (${arg_STATIC} AND ${arg_SHARED})
|
||||
set(static_lib_name ${lib_name}-static)
|
||||
endif ()
|
||||
|
||||
message(STATUS "Building library ${lib_name}")
|
||||
if (${arg_STATIC})
|
||||
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)
|
||||
meta_target_add_header_directory(
|
||||
TARGET ${static_lib_name}
|
||||
PATH ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (${arg_SHARED})
|
||||
add_library(${shared_lib_name} SHARED
|
||||
${arg_SOURCES}
|
||||
${arg_HEADERS})
|
||||
set(targets ${targets} ${shared_lib_name})
|
||||
set(soname ${shared_lib_name}.so)
|
||||
|
||||
if (arg_PUBLIC_INCLUDE_DIRS)
|
||||
meta_target_add_header_directory(
|
||||
TARGET ${shared_lib_name}
|
||||
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()
|
||||
|
||||
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
|
||||
PREFIX "")
|
||||
endfunction(rosetta_add_library)
|
||||
|
||||
function(rosetta_add_object_library)
|
||||
set(options)
|
||||
set(one_value_args NAME)
|
||||
set(multi_value_args
|
||||
PUBLIC_INCLUDE_DIRS
|
||||
SOURCES
|
||||
HEADERS)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
@@ -45,36 +119,70 @@ function(rosetta_add_library)
|
||||
|
||||
set(lib_name ${arg_NAME})
|
||||
|
||||
get_property(libs GLOBAL PROPERTY rosetta_library_list)
|
||||
set_property(GLOBAL PROPERTY rosetta_library_list ${libs} ${lib_name})
|
||||
|
||||
file(GLOB sources *.c *.h)
|
||||
file(GLOB_RECURSE headers include/*.h)
|
||||
|
||||
foreach (dir ${arg_SOURCE_DIRS})
|
||||
file(GLOB dir_sources ${dir}/*.c ${dir}/*.h)
|
||||
set(sources ${sources} ${dir_sources})
|
||||
endforeach (dir)
|
||||
|
||||
message(STATUS "Building library ${lib_name}")
|
||||
if (arg_SHARED)
|
||||
add_library(${lib_name} SHARED
|
||||
${sources}
|
||||
${headers}
|
||||
${arg_EXTRA_SOURCES})
|
||||
else ()
|
||||
add_library(${lib_name} STATIC
|
||||
${sources}
|
||||
${headers}
|
||||
${arg_EXTRA_SOURCES})
|
||||
endif ()
|
||||
add_library(${lib_name} OBJECT
|
||||
${arg_SOURCES}
|
||||
${arg_HEADERS})
|
||||
|
||||
target_include_directories(${lib_name} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_include_directories(${lib_name} PUBLIC ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
set_target_properties(${lib_name} PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
src_header_dir ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
sys_header_dir ${arg_SYSROOT_PATH}/include
|
||||
sys_bin_dir ${arg_SYSROOT_PATH}/lib
|
||||
PREFIX "")
|
||||
endfunction(rosetta_add_library)
|
||||
endfunction(rosetta_add_object_library)
|
||||
|
||||
function(rosetta_wrap_library)
|
||||
set(options)
|
||||
set(one_value_args NAME)
|
||||
set(multi_value_args
|
||||
PUBLIC_INCLUDE_DIRS)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(lib_name ${arg_NAME})
|
||||
|
||||
message(STATUS "Building library ${lib_name}")
|
||||
|
||||
if (arg_PUBLIC_INCLUDE_DIRS)
|
||||
meta_target_add_header_directory(
|
||||
TARGET ${lib_name}
|
||||
PATH ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
set_target_properties(${lib_name} PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
src_header_dir ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
PREFIX "")
|
||||
endfunction(rosetta_wrap_library)
|
||||
|
||||
function(rosetta_add_object_library)
|
||||
set(options)
|
||||
set(one_value_args NAME)
|
||||
set(multi_value_args
|
||||
PUBLIC_INCLUDE_DIRS
|
||||
SOURCES
|
||||
HEADERS)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi_value_args}")
|
||||
|
||||
set(lib_name ${arg_NAME})
|
||||
|
||||
message(STATUS "Building library ${lib_name}")
|
||||
add_library(${lib_name} OBJECT
|
||||
${arg_SOURCES}
|
||||
${arg_HEADERS})
|
||||
#add_library(${lib_name} STATIC
|
||||
# ${sources}
|
||||
# ${headers}
|
||||
# ${arg_EXTRA_SOURCES})
|
||||
|
||||
target_include_directories(${lib_name} PUBLIC ${arg_PUBLIC_INCLUDE_DIRS})
|
||||
set_target_properties(${lib_name} PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
src_header_dir ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
PREFIX "")
|
||||
endfunction(rosetta_add_object_library)
|
||||
|
||||
@@ -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
7
interface/CMakeLists.txt
Normal 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
9
interface/fs.xpc
Normal 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
2
kernel
Submodule kernel updated: af0d97d6f5...de520cdd2d
26
lib/CMakeLists.txt
Normal file
26
lib/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
file(GLOB items *)
|
||||
|
||||
add_subdirectory(
|
||||
${CMAKE_SOURCE_DIR}/kernel/libmango
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libmango)
|
||||
|
||||
rosetta_wrap_library(
|
||||
NAME libmango
|
||||
PUBLIC_INCLUDE_DIRS
|
||||
${CMAKE_SOURCE_DIR}/kernel/libmango/include
|
||||
${CMAKE_SOURCE_DIR}/kernel/libmango/include-user
|
||||
SOURCES ${asm_sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libmango
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
foreach(item ${items})
|
||||
if (NOT IS_DIRECTORY ${item})
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
add_subdirectory(${item})
|
||||
endforeach (item)
|
||||
30
lib/libc/CMakeLists.txt
Normal file
30
lib/libc/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
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})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libc
|
||||
HEADER_DIR /usr/include
|
||||
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)
|
||||
25
lib/libc/core/CMakeLists.txt
Normal file
25
lib/libc/core/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
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)
|
||||
|
||||
set(sources ${sources} ${dir_sources})
|
||||
set(headers ${headers} ${dir_headers})
|
||||
endforeach (dir)
|
||||
|
||||
set(component_sources ${sources} PARENT_SCOPE)
|
||||
set(component_headers ${headers} PARENT_SCOPE)
|
||||
|
||||
rosetta_add_library(STATIC
|
||||
NAME libc-core
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libc-core
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libc-core libmango)
|
||||
54
lib/libc/core/errno/errno.c
Normal file
54
lib/libc/core/errno/errno.c
Normal 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;
|
||||
}
|
||||
}
|
||||
1215
lib/libc/core/stdio/printf.c
Normal file
1215
lib/libc/core/stdio/printf.c
Normal file
File diff suppressed because it is too large
Load Diff
107
lib/libc/core/string/memcpy.c
Normal file
107
lib/libc/core/string/memcpy.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*-
|
||||
* 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);
|
||||
}
|
||||
13
lib/libc/core/string/memset.c
Normal file
13
lib/libc/core/string/memset.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#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;
|
||||
}
|
||||
21
lib/libc/core/string/strcmp.c
Normal file
21
lib/libc/core/string/strcmp.c
Normal file
@@ -0,0 +1,21 @@
|
||||
int strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; s1[i] == s2[i]; i++)
|
||||
if (s1[i] == '\0')
|
||||
return 0;
|
||||
|
||||
return s1[i] - s2[i];
|
||||
}
|
||||
|
||||
int strncmp(const char *s1, const char *s2, unsigned long n)
|
||||
{
|
||||
for (; n > 0; s1++, s2++, --n)
|
||||
if (*s1 != *s2)
|
||||
return ((*(unsigned char *)s1 < *(unsigned char *)s2)
|
||||
? -1
|
||||
: 1);
|
||||
else if (*s1 == '\0')
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
342
lib/libc/core/string/strerror.c
Normal file
342
lib/libc/core/string/strerror.c
Normal 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 "";
|
||||
}
|
||||
}
|
||||
11
lib/libc/core/string/strlen.c
Normal file
11
lib/libc/core/string/strlen.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <string.h>
|
||||
|
||||
size_t strlen(const char *str)
|
||||
{
|
||||
size_t res = 0;
|
||||
while (str[res]) {
|
||||
res++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
145
lib/libc/include/errno.h
Normal file
145
lib/libc/include/errno.h
Normal 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
|
||||
22
lib/libc/include/stdio.h
Normal file
22
lib/libc/include/stdio.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
13
lib/libc/include/stdlib.h
Normal file
13
lib/libc/include/stdlib.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef STDLIB_H_
|
||||
#define STDLIB_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern void abort(void);
|
||||
|
||||
extern void *malloc(size_t count);
|
||||
extern void *calloc(size_t count, size_t size);
|
||||
extern void *realloc(void *p, size_t count);
|
||||
extern void free(void *p);
|
||||
|
||||
#endif
|
||||
19
lib/libc/include/string.h
Normal file
19
lib/libc/include/string.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef STRING_H_
|
||||
#define STRING_H_
|
||||
|
||||
#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);
|
||||
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
12
lib/libc/include/unistd.h
Normal 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
|
||||
33
lib/libc/io/CMakeLists.txt
Normal file
33
lib/libc/io/CMakeLists.txt
Normal 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
2
lib/libc/io/fs.c
Normal file
@@ -0,0 +1,2 @@
|
||||
#define MSG_IMPLEMENTATION
|
||||
#include <rosetta/fs.h>
|
||||
43
lib/libc/io/include/sys/mman.h
Normal file
43
lib/libc/io/include/sys/mman.h
Normal 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
|
||||
18
lib/libc/io/include/sys/remote.h
Normal file
18
lib/libc/io/include/sys/remote.h
Normal 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
45
lib/libc/io/remote.c
Normal 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;
|
||||
}
|
||||
0
lib/libc/io/unistd/close.c
Normal file
0
lib/libc/io/unistd/close.c
Normal file
6
lib/libc/io/unistd/mmap.c
Normal file
6
lib/libc/io/unistd/mmap.c
Normal 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
40
lib/libc/io/unistd/open.c
Normal 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
21
lib/libc/io/unistd/read.c
Normal 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;
|
||||
}
|
||||
0
lib/libc/io/unistd/write.c
Normal file
0
lib/libc/io/unistd/write.c
Normal file
33
lib/libc/malloc/CMakeLists.txt
Normal file
33
lib/libc/malloc/CMakeLists.txt
Normal 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
112
lib/libc/malloc/heap.c
Normal 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;
|
||||
}
|
||||
16
lib/libc/malloc/include/heap/_liballoc.h
Normal file
16
lib/libc/malloc/include/heap/_liballoc.h
Normal 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
|
||||
50
lib/libc/malloc/include/heap/heap.h
Normal file
50
lib/libc/malloc/include/heap/heap.h
Normal 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
834
lib/libc/malloc/liballoc.c
Normal 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;
|
||||
}
|
||||
76
lib/libc/malloc/liballoc.h
Normal file
76
lib/libc/malloc/liballoc.h
Normal 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
|
||||
44
lib/libc/malloc/stdlib/malloc.c
Normal file
44
lib/libc/malloc/stdlib/malloc.c
Normal 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
|
||||
14
lib/libc/malloc/string/strdup.c
Normal file
14
lib/libc/malloc/string/strdup.c
Normal 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;
|
||||
}
|
||||
11
lib/libc/runtime/CMakeLists.txt
Normal file
11
lib/libc/runtime/CMakeLists.txt
Normal 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)
|
||||
16
lib/libc/runtime/x86_64/crt0.s
Normal file
16
lib/libc/runtime/x86_64/crt0.s
Normal file
@@ -0,0 +1,16 @@
|
||||
.code64
|
||||
|
||||
.global _start
|
||||
.type _start, @function
|
||||
|
||||
.extern main
|
||||
.type main, @function
|
||||
|
||||
_start:
|
||||
# Args (as provided by the ABI)
|
||||
# %rdi: int argc
|
||||
# %rsi: const char **argv
|
||||
# %rdx: kern_handle_t task
|
||||
# %rcx: kern_handle_t address_space
|
||||
call main
|
||||
1: jmp 1b
|
||||
29
lib/libfs/CMakeLists.txt
Normal file
29
lib/libfs/CMakeLists.txt
Normal 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
35
lib/libfs/allocator.c
Normal 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
691
lib/libfs/btree.c
Normal 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
475
lib/libfs/btree.h
Normal 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
194
lib/libfs/context.c
Normal 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
41
lib/libfs/file.c
Normal 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
23
lib/libfs/file.h
Normal 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
|
||||
19
lib/libfs/include/fs/allocator.h
Normal file
19
lib/libfs/include/fs/allocator.h
Normal 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
|
||||
55
lib/libfs/include/fs/context.h
Normal file
55
lib/libfs/include/fs/context.h
Normal 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
|
||||
19
lib/libfs/include/fs/dentry.h
Normal file
19
lib/libfs/include/fs/dentry.h
Normal 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
|
||||
35
lib/libfs/include/fs/file.h
Normal file
35
lib/libfs/include/fs/file.h
Normal 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
|
||||
37
lib/libfs/include/fs/inode.h
Normal file
37
lib/libfs/include/fs/inode.h
Normal 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
|
||||
18
lib/libfs/include/fs/status.h
Normal file
18
lib/libfs/include/fs/status.h
Normal 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
|
||||
20
lib/libfs/include/fs/superblock.h
Normal file
20
lib/libfs/include/fs/superblock.h
Normal 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
13
lib/libfs/inode.c
Normal 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
35
lib/libfs/interface.h
Normal 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
|
||||
53
lib/libfs/interface/open.c
Normal file
53
lib/libfs/interface/open.c
Normal 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;
|
||||
}
|
||||
30
lib/libfs/interface/read.c
Normal file
30
lib/libfs/interface/read.c
Normal 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
25
lib/libfs/status.c
Normal 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
11
lib/libfs/superblock.c
Normal 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);
|
||||
}
|
||||
21
lib/liblaunch/CMakeLists.txt
Normal file
21
lib/liblaunch/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
file(GLOB sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
file(GLOB headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/launch.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
|
||||
set(public_include_dirs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(
|
||||
NAME liblaunch STATIC
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dirs}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME liblaunch
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(liblaunch librosetta libmango libc-core)
|
||||
565
lib/liblaunch/elf.c
Normal file
565
lib/liblaunch/elf.c
Normal file
@@ -0,0 +1,565 @@
|
||||
#include "elf.h"
|
||||
|
||||
#include <mango/config.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
#define NEEDS_NOTHING 0
|
||||
#define NEEDS_VDSO 1
|
||||
#define NEEDS_MORE 2
|
||||
|
||||
#define ACL (PF_R | PF_W | PF_X)
|
||||
#define ACCESS(x) ((x) & ACL)
|
||||
|
||||
/* TODO in case we ever support ELF32 images */
|
||||
#define elf_class_bits(x) (64)
|
||||
|
||||
#define PAGE_SIZE (image->e_page_size)
|
||||
#define PAGE_MASK (image->e_page_size - 1)
|
||||
#define PAGE_OFFSET(v) ((v) & (PAGE_SIZE - 1))
|
||||
#define PAGE_ALIGN_DOWN(v) (v) &= ~(PAGE_SIZE - 1)
|
||||
#define PAGE_ALIGN_UP(v) \
|
||||
do { \
|
||||
if ((v) & (PAGE_SIZE - 1)) { \
|
||||
v &= ~(PAGE_SIZE - 1); \
|
||||
v += PAGE_SIZE; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#undef DEBUG_LOG
|
||||
|
||||
static enum launch_status elf_validate_ehdr(elf_ehdr_t *hdr)
|
||||
{
|
||||
if (hdr->e_ident[EI_MAG0] != ELF_MAG0) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_MAG1] != ELF_MAG1) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_MAG2] != ELF_MAG2) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_MAG3] != ELF_MAG3) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_machine != EM_X86_64) {
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_VERSION] != EV_CURRENT) {
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
static enum launch_status read_header(struct elf_image *image)
|
||||
{
|
||||
size_t nr_read = 0;
|
||||
vm_object_read(
|
||||
image->e_image,
|
||||
&image->e_hdr,
|
||||
0,
|
||||
sizeof image->e_hdr,
|
||||
&nr_read);
|
||||
if (nr_read != sizeof image->e_hdr) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
return elf_validate_ehdr(&image->e_hdr);
|
||||
}
|
||||
|
||||
static enum launch_status parse_phdr(struct elf_image *image)
|
||||
{
|
||||
elf_phdr_t phdr;
|
||||
size_t r = 0;
|
||||
image->e_total_size = 0;
|
||||
image->e_data_size = 0;
|
||||
off_t vaddr, vlimit;
|
||||
|
||||
for (size_t i = 0; i < image->e_hdr.e_phnum; i++) {
|
||||
off_t offset
|
||||
= image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize);
|
||||
kern_status_t status = vm_object_read(
|
||||
image->e_image,
|
||||
&phdr,
|
||||
offset,
|
||||
sizeof phdr,
|
||||
&r);
|
||||
|
||||
if (status != KERN_OK || r != sizeof phdr) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
vaddr = phdr.p_vaddr;
|
||||
vlimit = phdr.p_vaddr + phdr.p_memsz;
|
||||
if (vaddr & (PAGE_SIZE - 1)) {
|
||||
vaddr &= ~(PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
if (vlimit & (PAGE_SIZE - 1)) {
|
||||
vlimit &= ~(PAGE_SIZE - 1);
|
||||
vlimit += PAGE_SIZE;
|
||||
}
|
||||
|
||||
switch (phdr.p_type) {
|
||||
case PT_DYNAMIC:
|
||||
image->e_dynamic = phdr;
|
||||
break;
|
||||
case PT_LOAD:
|
||||
|
||||
image->e_total_size = MAX(image->e_total_size, vlimit);
|
||||
break;
|
||||
case PT_INTERP: {
|
||||
size_t r = 0;
|
||||
vm_object_read(
|
||||
image->e_image,
|
||||
image->e_interp,
|
||||
phdr.p_offset,
|
||||
MIN(sizeof image->e_interp - 1, phdr.p_filesz),
|
||||
&r);
|
||||
image->e_interp[r] = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (phdr.p_flags & PF_W) {
|
||||
image->e_data_size = MAX(image->e_data_size, vlimit);
|
||||
}
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
static kern_status_t create_exec_regions(struct elf_image *image)
|
||||
{
|
||||
kern_tracef(
|
||||
"launch: executable region size=%zu %zx",
|
||||
image->e_total_size,
|
||||
image->e_total_size);
|
||||
kern_status_t status = KERN_OK;
|
||||
if (image->e_local_space != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_create(
|
||||
image->e_local_space,
|
||||
NULL,
|
||||
0,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
image->e_total_size,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC
|
||||
| VM_PROT_USER,
|
||||
&image->e_local_exec,
|
||||
&image->e_local_base);
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (image->e_remote_space != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_create(
|
||||
image->e_remote_space,
|
||||
NULL,
|
||||
0,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
image->e_total_size,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC
|
||||
| VM_PROT_USER,
|
||||
&image->e_remote_exec,
|
||||
&image->e_remote_base);
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
vm_region_kill(image->e_local_exec);
|
||||
kern_handle_close(image->e_local_exec);
|
||||
image->e_local_exec = KERN_HANDLE_INVALID;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static enum launch_status map_executable(struct elf_image *image)
|
||||
{
|
||||
elf_phdr_t phdr;
|
||||
size_t r = 0;
|
||||
image->e_total_size = 0;
|
||||
image->e_data_size = 0;
|
||||
|
||||
size_t data_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < image->e_hdr.e_phnum; i++) {
|
||||
off_t phdr_offset
|
||||
= image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize);
|
||||
kern_status_t status = vm_object_read(
|
||||
image->e_image,
|
||||
&phdr,
|
||||
phdr_offset,
|
||||
sizeof phdr,
|
||||
&r);
|
||||
|
||||
if (status != KERN_OK || r != sizeof phdr) {
|
||||
return LAUNCH_ERR_INVALID_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
kern_handle_t vmo = image->e_image;
|
||||
vm_prot_t prot = VM_PROT_USER;
|
||||
size_t offset = phdr.p_offset;
|
||||
|
||||
phdr.p_flags &PF_R && (prot |= VM_PROT_READ);
|
||||
phdr.p_flags &PF_W && (prot |= VM_PROT_WRITE);
|
||||
phdr.p_flags &PF_X && (prot |= VM_PROT_EXEC);
|
||||
if (phdr.p_flags & PF_W) {
|
||||
vmo = image->e_data;
|
||||
offset = data_offset;
|
||||
size_t tmp = 0;
|
||||
|
||||
status = vm_object_copy(
|
||||
image->e_data,
|
||||
data_offset + (phdr.p_offset & PAGE_MASK),
|
||||
image->e_image,
|
||||
phdr.p_offset,
|
||||
phdr.p_filesz,
|
||||
&tmp);
|
||||
|
||||
if (tmp != phdr.p_filesz) {
|
||||
return LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED;
|
||||
}
|
||||
|
||||
if (image->e_local_exec != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_map_relative(
|
||||
image->e_local_exec,
|
||||
phdr.p_vaddr,
|
||||
vmo,
|
||||
offset,
|
||||
phdr.p_memsz,
|
||||
prot,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return LAUNCH_ERR_MEMORY_MAP_FAILED;
|
||||
}
|
||||
|
||||
if (image->e_remote_exec != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_map_relative(
|
||||
image->e_remote_exec,
|
||||
phdr.p_vaddr,
|
||||
vmo,
|
||||
offset,
|
||||
phdr.p_memsz,
|
||||
prot,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return LAUNCH_ERR_MEMORY_MAP_FAILED;
|
||||
}
|
||||
|
||||
if (phdr.p_flags & PF_W) {
|
||||
data_offset += phdr.p_memsz;
|
||||
if (data_offset & (PAGE_SIZE - 1)) {
|
||||
data_offset &= (PAGE_SIZE - 1);
|
||||
data_offset += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
static elf_sym_t *get_dynsym(struct elf_image *image, size_t index)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int type = ELF64_R_TYPE(rela->r_info);
|
||||
elf_sym_t *sym = NULL;
|
||||
|
||||
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(
|
||||
"JUMP_SLOT: offset=%zx, symbol=%zu, addend=%zx",
|
||||
rela->r_offset,
|
||||
ELF64_R_SYM(rela->r_info),
|
||||
rela->r_addend);
|
||||
break;
|
||||
default:
|
||||
kern_trace("Unknown relocation type");
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
static enum launch_status do_rela_list(
|
||||
struct elf_image *image,
|
||||
off_t offset,
|
||||
size_t size,
|
||||
size_t entsize)
|
||||
{
|
||||
kern_tracef(
|
||||
"do_rela_list(%p, %d, %d, %d)",
|
||||
image,
|
||||
offset,
|
||||
size,
|
||||
entsize);
|
||||
size_t entries = size / entsize;
|
||||
elf_rela_t *rela = (elf_rela_t *)(image->e_local_base + offset);
|
||||
enum launch_status status = LAUNCH_OK;
|
||||
|
||||
for (size_t i = 0; i < entries; i++) {
|
||||
status = do_rela(image, rela);
|
||||
|
||||
if (status != LAUNCH_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
rela = (elf_rela_t *)((char *)rela + entsize);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum launch_status do_rel(
|
||||
struct elf_image *image,
|
||||
off_t offset,
|
||||
size_t size,
|
||||
size_t entsize)
|
||||
|
||||
{
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
}
|
||||
|
||||
static enum launch_status relocate(struct elf_image *image)
|
||||
{
|
||||
elf_dyn_t *dyn
|
||||
= (elf_dyn_t *)(image->e_local_base + image->e_dynamic.p_vaddr);
|
||||
|
||||
enum {
|
||||
RT_REL,
|
||||
RT_RELA,
|
||||
RT_PLTREL,
|
||||
RT_COUNT,
|
||||
};
|
||||
|
||||
int pltrel_type = DT_NULL;
|
||||
off_t offsets[RT_COUNT] = {0};
|
||||
size_t sizes[RT_COUNT] = {0}, entsizes[RT_COUNT] = {0};
|
||||
|
||||
size_t nr_dyn = image->e_dynamic.p_filesz / sizeof *dyn;
|
||||
for (size_t i = 0; i < nr_dyn; i++) {
|
||||
kern_tracef("DYN:%zx", dyn[i].d_tag);
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_SYMTAB:
|
||||
image->e_dynsym = dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_SYMENT:
|
||||
image->e_dynsym_entsize = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_REL:
|
||||
offsets[RT_REL] = dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_RELSZ:
|
||||
sizes[RT_REL] = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELENT:
|
||||
entsizes[RT_REL] = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELA:
|
||||
offsets[RT_RELA] = dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
sizes[RT_RELA] = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
entsizes[RT_RELA] = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_PLTREL:
|
||||
pltrel_type = dyn[i].d_un.d_val;
|
||||
switch (pltrel_type) {
|
||||
case DT_REL:
|
||||
entsizes[RT_PLTREL] = 0;
|
||||
break;
|
||||
case DT_RELA:
|
||||
entsizes[RT_PLTREL] = sizeof(elf_rela_t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DT_JMPREL:
|
||||
offsets[RT_PLTREL] = dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_PLTRELSZ:
|
||||
sizes[RT_PLTREL] = dyn[i].d_un.d_val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (dyn[i].d_tag == DT_NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum launch_status status = LAUNCH_OK;
|
||||
if (offsets[RT_RELA] && sizes[RT_RELA] && entsizes[RT_RELA]) {
|
||||
kern_trace("RELA");
|
||||
status = do_rela_list(
|
||||
image,
|
||||
offsets[RT_RELA],
|
||||
sizes[RT_RELA],
|
||||
entsizes[RT_RELA]);
|
||||
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (offsets[RT_PLTREL] && entsizes[RT_PLTREL]) {
|
||||
kern_trace("PLTREL");
|
||||
if (pltrel_type == DT_REL) {
|
||||
status = do_rel(
|
||||
image,
|
||||
offsets[RT_PLTREL],
|
||||
sizes[RT_PLTREL],
|
||||
entsizes[RT_PLTREL]);
|
||||
} else {
|
||||
status = do_rela_list(
|
||||
image,
|
||||
offsets[RT_PLTREL],
|
||||
sizes[RT_PLTREL],
|
||||
entsizes[RT_PLTREL]);
|
||||
}
|
||||
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
void elf_image_init(struct elf_image *out)
|
||||
{
|
||||
memset(out, 0x0, sizeof(*out));
|
||||
|
||||
kern_config_get(
|
||||
KERN_CFG_PAGE_SIZE,
|
||||
&out->e_page_size,
|
||||
sizeof out->e_page_size);
|
||||
out->e_image = KERN_HANDLE_INVALID;
|
||||
out->e_data = KERN_HANDLE_INVALID;
|
||||
out->e_local_space = KERN_HANDLE_INVALID;
|
||||
out->e_remote_space = KERN_HANDLE_INVALID;
|
||||
out->e_local_exec = KERN_HANDLE_INVALID;
|
||||
out->e_remote_exec = KERN_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
enum launch_status elf_image_load(
|
||||
struct elf_image *image,
|
||||
kern_handle_t exec_object,
|
||||
kern_handle_t local_space,
|
||||
kern_handle_t remote_space)
|
||||
{
|
||||
image->e_image = exec_object;
|
||||
image->e_local_space = local_space;
|
||||
image->e_remote_space = remote_space;
|
||||
|
||||
enum launch_status status = read_header(image);
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = parse_phdr(image);
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (image->e_interp[0] != 0) {
|
||||
return LAUNCH_ERR_INTERPRETER_REQUIRED;
|
||||
}
|
||||
|
||||
kern_status_t kstatus = vm_object_create(
|
||||
".data",
|
||||
5,
|
||||
image->e_data_size,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&image->e_data);
|
||||
if (kstatus != KERN_OK) {
|
||||
return LAUNCH_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = create_exec_regions(image);
|
||||
if (status != KERN_OK) {
|
||||
return LAUNCH_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = map_executable(image);
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = relocate(image);
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
void elf_image_cleanup(struct elf_image *image)
|
||||
{
|
||||
vm_region_unmap_relative(image->e_local_exec, 0, image->e_total_size);
|
||||
kern_handle_close(image->e_data);
|
||||
vm_region_kill(image->e_local_exec);
|
||||
kern_handle_close(image->e_local_exec);
|
||||
kern_handle_close(image->e_remote_exec);
|
||||
}
|
||||
315
lib/liblaunch/elf.h
Normal file
315
lib/liblaunch/elf.h
Normal file
@@ -0,0 +1,315 @@
|
||||
#ifndef USERBOOT_ELF_H_
|
||||
#define USERBOOT_ELF_H_
|
||||
|
||||
#include <launch.h>
|
||||
#include <mango/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define ELF_LOAD_ERR -1
|
||||
#define ELF_LOADED_EXEC 0
|
||||
#define ELF_LOADED_INTERP 1
|
||||
|
||||
#define ELF_MAG0 0x7f
|
||||
#define ELF_MAG1 'E'
|
||||
#define ELF_MAG2 'L'
|
||||
#define ELF_MAG3 'F'
|
||||
#define ELF_NIDENT 16
|
||||
|
||||
#define SHT_NONE 0
|
||||
#define SHT_PROGBITS 1
|
||||
#define SHT_SYMTAB 2
|
||||
#define SHT_STRTAB 3
|
||||
#define SHT_RELA 4
|
||||
#define SHT_DYNAMIC 6
|
||||
#define SHT_NOBITS 8
|
||||
#define SHT_REL 9
|
||||
#define SHT_DYNSYM 11
|
||||
|
||||
/** Little endian. */
|
||||
#define ELFDATA2LSB (1)
|
||||
|
||||
/** 64-bit. */
|
||||
#define ELFCLASS64 (2)
|
||||
|
||||
/** x86_64 machine type. */
|
||||
#define EM_X86_64 (62)
|
||||
|
||||
/** ELF current version. */
|
||||
#define EV_CURRENT (1)
|
||||
|
||||
/** Dynamic section tags. */
|
||||
#define DT_NULL 0
|
||||
#define DT_NEEDED 1
|
||||
#define DT_PLTRELSZ 2
|
||||
#define DT_PLTGOT 3
|
||||
#define DT_HASH 4
|
||||
#define DT_STRTAB 5
|
||||
#define DT_SYMTAB 6
|
||||
#define DT_RELA 7
|
||||
#define DT_RELASZ 8
|
||||
#define DT_RELAENT 9
|
||||
#define DT_STRSZ 10
|
||||
#define DT_SYMENT 11
|
||||
#define DT_INIT 12
|
||||
#define DT_FINI 13
|
||||
#define DT_REL 17
|
||||
#define DT_RELSZ 18
|
||||
#define DT_RELENT 19
|
||||
#define DT_PLTREL 20
|
||||
#define DT_JMPREL 23
|
||||
#define DT_GNU_HASH 0x6ffffef5
|
||||
#define DT_AUXILIARY 0x7ffffffd
|
||||
|
||||
#define R_386_32 1
|
||||
#define R_386_PC32 2
|
||||
#define R_386_GOT32 3
|
||||
#define R_386_PLT32 4
|
||||
#define R_386_GOTOFF 9
|
||||
#define R_386_GOTPC 10
|
||||
#define R_386_GOT32X 43
|
||||
|
||||
#define R_X86_64_64 1
|
||||
#define R_X86_64_PC32 2
|
||||
#define R_X86_64_GOT32 3
|
||||
#define R_X86_64_PLT32 4
|
||||
#define R_X86_64_COPY 5
|
||||
#define R_X86_64_GLOB_DAT 6
|
||||
#define R_X86_64_JUMP_SLOT 7
|
||||
#define R_X86_64_RELATIVE 8
|
||||
#define R_X86_64_GOTPCREL 9
|
||||
#define R_X86_64_32 10
|
||||
|
||||
#define STT_NOTYPE 0
|
||||
#define STT_OBJECT 1
|
||||
#define STT_FUNC 2
|
||||
#define STT_SECTION 3
|
||||
#define STT_FILE 4
|
||||
#define STT_LOPROC 13
|
||||
#define STT_HIPROC 15
|
||||
|
||||
/* Section flags */
|
||||
#define SHF_WRITE 0x1
|
||||
#define SHF_ALLOC 0x2
|
||||
#define SHF_EXECINSTR 0x4
|
||||
|
||||
#define SHN_UNDEF 0
|
||||
|
||||
#define ELF64_R_SYM(i) ((i) >> 32)
|
||||
#define ELF64_R_TYPE(i) ((elf_word_t)(i))
|
||||
#define ELF64_ST_BIND(i) ((i) >> 4)
|
||||
#define ELF64_ST_TYPE(i) ((i) & 0xf)
|
||||
|
||||
#define STB_LOCAL 0
|
||||
#define STB_GLOBAL 1
|
||||
#define STB_WEAK 2
|
||||
#define STB_NUM 3
|
||||
|
||||
typedef uint64_t elf_addr_t;
|
||||
typedef uint64_t elf_off_t;
|
||||
typedef uint16_t elf_half_t;
|
||||
typedef uint32_t elf_word_t;
|
||||
typedef int32_t elf_sword_t;
|
||||
typedef uint64_t elf_xword_t;
|
||||
typedef int64_t elf_sxword_t;
|
||||
|
||||
/**
|
||||
* ELF file header.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t e_ident[ELF_NIDENT];
|
||||
elf_half_t e_type;
|
||||
elf_half_t e_machine;
|
||||
elf_word_t e_version;
|
||||
elf_addr_t e_entry;
|
||||
elf_off_t e_phoff;
|
||||
elf_off_t e_shoff;
|
||||
elf_word_t e_flags;
|
||||
elf_half_t e_ehsize;
|
||||
elf_half_t e_phentsize;
|
||||
elf_half_t e_phnum;
|
||||
elf_half_t e_shentsize;
|
||||
elf_half_t e_shnum;
|
||||
elf_half_t e_shstrndx;
|
||||
} elf_ehdr_t;
|
||||
|
||||
/**
|
||||
* ELF section header.
|
||||
*/
|
||||
typedef struct {
|
||||
elf_word_t sh_name;
|
||||
elf_word_t sh_type;
|
||||
elf_xword_t sh_flags;
|
||||
elf_addr_t sh_addr;
|
||||
elf_off_t sh_offset;
|
||||
elf_xword_t sh_size;
|
||||
elf_word_t sh_link;
|
||||
elf_word_t sh_info;
|
||||
elf_xword_t sh_addralign;
|
||||
elf_xword_t sh_entsize;
|
||||
} elf_shdr_t;
|
||||
|
||||
/**
|
||||
* ELF symbol.
|
||||
*/
|
||||
typedef struct {
|
||||
elf_word_t st_name;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
elf_half_t st_shndx;
|
||||
elf_addr_t st_value;
|
||||
elf_xword_t st_size;
|
||||
} elf_sym_t;
|
||||
|
||||
/**
|
||||
* ELF program header.
|
||||
*/
|
||||
typedef struct {
|
||||
elf_word_t p_type;
|
||||
elf_word_t p_flags;
|
||||
elf_off_t p_offset;
|
||||
elf_addr_t p_vaddr;
|
||||
elf_addr_t p_paddr;
|
||||
elf_xword_t p_filesz;
|
||||
elf_xword_t p_memsz;
|
||||
elf_xword_t p_align;
|
||||
} elf_phdr_t;
|
||||
|
||||
/**
|
||||
* Extended ELF relocation information.
|
||||
*/
|
||||
typedef struct {
|
||||
elf_addr_t r_offset;
|
||||
elf_xword_t r_info;
|
||||
elf_sxword_t r_addend;
|
||||
} elf_rela_t;
|
||||
|
||||
/**
|
||||
* Dynamic section entries
|
||||
*/
|
||||
typedef struct {
|
||||
elf_sxword_t d_tag;
|
||||
union {
|
||||
elf_xword_t d_val;
|
||||
elf_addr_t d_ptr;
|
||||
} d_un;
|
||||
} elf_dyn_t;
|
||||
|
||||
/**
|
||||
* Section header types.
|
||||
*/
|
||||
enum elf_stype {
|
||||
ST_NONE = 0,
|
||||
ST_PROGBITS = 1,
|
||||
ST_SYMTAB = 2,
|
||||
ST_STRTAB = 3,
|
||||
ST_NOBITS = 8,
|
||||
ST_REL = 9
|
||||
};
|
||||
|
||||
/**
|
||||
* Program header types.
|
||||
*/
|
||||
enum elf_ptype {
|
||||
PT_NULL = 0,
|
||||
PT_LOAD = 1,
|
||||
PT_DYNAMIC = 2,
|
||||
PT_INTERP = 3,
|
||||
PT_NOTE = 4,
|
||||
PT_SHLIB = 5,
|
||||
PT_PHDR = 6
|
||||
};
|
||||
|
||||
#define PF_X 0x1
|
||||
#define PF_W 0x2
|
||||
#define PF_R 0x4
|
||||
|
||||
#define PT_LOPROC 0x70000000
|
||||
#define PT_HIPROC 0x7FFFFFFF
|
||||
|
||||
/**
|
||||
* ELF identification byte locations.
|
||||
*/
|
||||
enum elf_ident {
|
||||
EI_MAG0 = 0,
|
||||
EI_MAG1 = 1,
|
||||
EI_MAG2 = 2,
|
||||
EI_MAG3 = 3,
|
||||
EI_CLASS = 4,
|
||||
EI_DATA = 5,
|
||||
EI_VERSION = 6,
|
||||
EI_OSABI = 7,
|
||||
EI_ABIVERSION = 8,
|
||||
EI_PAD = 9
|
||||
};
|
||||
|
||||
enum elf_type {
|
||||
ET_NONE = 0,
|
||||
ET_REL = 1,
|
||||
ET_EXEC = 2,
|
||||
ET_DYN = 3,
|
||||
};
|
||||
|
||||
#define AT_NULL 0
|
||||
#define AT_IGNORE 1
|
||||
#define AT_EXECFD 2
|
||||
#define AT_PHDR 3
|
||||
#define AT_PHENT 4
|
||||
#define AT_PHNUM 5
|
||||
#define AT_PAGESZ 6
|
||||
#define AT_BASE 7
|
||||
#define AT_FLAGS 8
|
||||
#define AT_ENTRY 9
|
||||
#define AT_NOTELF 10
|
||||
#define AT_UID 11
|
||||
#define AT_EUID 12
|
||||
#define AT_GID 13
|
||||
#define AT_EGID 14
|
||||
#define AT_CLKTCK 17
|
||||
#define AT_PLATFORM 15
|
||||
#define AT_HWCAP 16
|
||||
#define AT_FPUCW 18
|
||||
#define AT_DCACHEBSIZE 19
|
||||
#define AT_ICACHEBSIZE 20
|
||||
#define AT_UCACHEBSIZE 21
|
||||
#define AT_IGNOREPPC 22
|
||||
#define AT_SECURE 23
|
||||
#define AT_BASE_PLATFORM 24
|
||||
#define AT_RANDOM 25
|
||||
#define AT_HWCAP2 26
|
||||
#define AT_EXECFN 31
|
||||
#define AT_SYSINFO 32
|
||||
#define AT_SYSINFO_EHDR 33
|
||||
#define AT_L1I_CACHESHAPE 34
|
||||
#define AT_L1D_CACHESHAPE 35
|
||||
#define AT_L2_CACHESHAPE 36
|
||||
#define AT_L3_CACHESHAPE 37
|
||||
#define AT_ENTRY_COUNT 38
|
||||
|
||||
struct bootdata;
|
||||
struct bootfs_file;
|
||||
|
||||
struct elf_image {
|
||||
size_t e_page_size;
|
||||
kern_handle_t e_image, e_data;
|
||||
kern_handle_t e_local_space, e_remote_space;
|
||||
kern_handle_t e_local_exec, e_remote_exec;
|
||||
virt_addr_t e_local_base, e_remote_base;
|
||||
elf_ehdr_t e_hdr;
|
||||
elf_phdr_t e_dynamic;
|
||||
off_t e_dynsym;
|
||||
size_t e_dynsym_entsize;
|
||||
|
||||
char e_interp[256];
|
||||
size_t e_total_size, e_data_size;
|
||||
};
|
||||
|
||||
extern void elf_image_init(struct elf_image *out);
|
||||
extern enum launch_status elf_image_load(
|
||||
struct elf_image *image,
|
||||
kern_handle_t exec_object,
|
||||
kern_handle_t local_space,
|
||||
kern_handle_t remote_space);
|
||||
|
||||
extern void elf_image_cleanup(struct elf_image *image);
|
||||
|
||||
#endif
|
||||
82
lib/liblaunch/include/launch.h
Normal file
82
lib/liblaunch/include/launch.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef LAUNCH_H_
|
||||
#define LAUNCH_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
enum launch_status {
|
||||
LAUNCH_OK,
|
||||
/* a memory allocation failed */
|
||||
LAUNCH_ERR_NO_MEMORY,
|
||||
/* executable file is corrupt or of an unrecognised format. */
|
||||
LAUNCH_ERR_INVALID_EXECUTABLE,
|
||||
/* executable file IS valid and IS of a recognised format, but is
|
||||
* not supported by this machine (different class, architecture,
|
||||
* 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,
|
||||
LAUNCH_ERR_IMAGE_DATA_LOAD_FAILED,
|
||||
LAUNCH_ERR_INTERPRETER_REQUIRED,
|
||||
LAUNCH_ERR_TASK_CREATION_FAILED,
|
||||
LAUNCH_ERR_THREAD_CREATION_FAILED,
|
||||
};
|
||||
|
||||
enum launch_flags {
|
||||
LAUNCH_F_NONE = 0,
|
||||
};
|
||||
|
||||
struct launch_ctx;
|
||||
struct rosetta_bootstrap_handle;
|
||||
struct rosetta_bootstrap_channel;
|
||||
|
||||
typedef enum launch_status (*launch_resolve_library_function)(
|
||||
struct launch_ctx *,
|
||||
const char *,
|
||||
kern_handle_t *,
|
||||
void *);
|
||||
|
||||
struct launch_ctx {
|
||||
launch_resolve_library_function ctx_resolve_library;
|
||||
};
|
||||
|
||||
struct launch_parameters {
|
||||
kern_handle_t p_parent_task;
|
||||
kern_handle_t p_local_address_space;
|
||||
kern_handle_t p_executable;
|
||||
|
||||
const char *p_task_name;
|
||||
|
||||
int p_argc;
|
||||
const char **p_argv;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct launch_result {
|
||||
kern_handle_t r_task;
|
||||
kern_handle_t r_thread;
|
||||
kern_handle_t r_address_space;
|
||||
};
|
||||
|
||||
extern enum launch_status launch_ctx_init(struct launch_ctx *ctx);
|
||||
extern void launch_ctx_cleanup(struct launch_ctx *ctx);
|
||||
|
||||
extern enum launch_status launch_ctx_execute(
|
||||
struct launch_ctx *ctx,
|
||||
const struct launch_parameters *params,
|
||||
enum launch_flags flags,
|
||||
struct launch_result *result);
|
||||
|
||||
#endif
|
||||
215
lib/liblaunch/launch.c
Normal file
215
lib/liblaunch/launch.c
Normal file
@@ -0,0 +1,215 @@
|
||||
#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>
|
||||
|
||||
#define STACK_SIZE 0x10000
|
||||
|
||||
enum launch_status launch_ctx_init(struct launch_ctx *ctx)
|
||||
{
|
||||
memset(ctx, 0x0, sizeof *ctx);
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
|
||||
void launch_ctx_cleanup(struct launch_ctx *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static kern_handle_t get_library(
|
||||
struct launch_ctx *ctx,
|
||||
const char *name,
|
||||
void *arg)
|
||||
{
|
||||
enum launch_status status = LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY;
|
||||
kern_handle_t result = KERN_HANDLE_INVALID;
|
||||
if (ctx->ctx_resolve_library) {
|
||||
status = ctx->ctx_resolve_library(ctx, name, &result, arg);
|
||||
}
|
||||
|
||||
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,
|
||||
enum launch_flags flags,
|
||||
struct launch_result *result)
|
||||
{
|
||||
kern_status_t kstatus;
|
||||
kern_handle_t stack_vmo;
|
||||
|
||||
kstatus = vm_object_create(
|
||||
"stack",
|
||||
5,
|
||||
STACK_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&stack_vmo);
|
||||
|
||||
size_t name_len = params->p_task_name ? strlen(params->p_task_name) : 0;
|
||||
kern_handle_t remote_task = KERN_HANDLE_INVALID,
|
||||
remote_address_space = KERN_HANDLE_INVALID;
|
||||
kstatus = task_create(
|
||||
params->p_parent_task,
|
||||
params->p_task_name,
|
||||
name_len,
|
||||
&remote_task,
|
||||
&remote_address_space);
|
||||
if (kstatus != KERN_OK) {
|
||||
kern_handle_close(stack_vmo);
|
||||
return LAUNCH_ERR_TASK_CREATION_FAILED;
|
||||
}
|
||||
|
||||
struct elf_image image;
|
||||
elf_image_init(&image);
|
||||
|
||||
enum launch_status status = elf_image_load(
|
||||
&image,
|
||||
params->p_executable,
|
||||
params->p_local_address_space,
|
||||
remote_address_space);
|
||||
|
||||
if (status == LAUNCH_ERR_INTERPRETER_REQUIRED) {
|
||||
kern_handle_t interp = get_library(
|
||||
ctx,
|
||||
image.e_interp,
|
||||
params->p_resolver_arg);
|
||||
if (interp == KERN_HANDLE_INVALID) {
|
||||
elf_image_cleanup(&image);
|
||||
kern_handle_close(stack_vmo);
|
||||
kern_handle_close(remote_address_space);
|
||||
kern_handle_close(remote_task);
|
||||
return status;
|
||||
}
|
||||
|
||||
elf_image_init(&image);
|
||||
status = elf_image_load(
|
||||
&image,
|
||||
interp,
|
||||
params->p_local_address_space,
|
||||
remote_address_space);
|
||||
}
|
||||
|
||||
if (status != LAUNCH_OK) {
|
||||
elf_image_cleanup(&image);
|
||||
kern_handle_close(stack_vmo);
|
||||
kern_handle_close(remote_address_space);
|
||||
kern_handle_close(remote_task);
|
||||
return status;
|
||||
}
|
||||
|
||||
virt_addr_t remote_stack_buf, local_stack_buf;
|
||||
kstatus = vm_region_map_relative(
|
||||
remote_address_space,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
stack_vmo,
|
||||
0,
|
||||
STACK_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&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) {
|
||||
elf_image_cleanup(&image);
|
||||
kern_handle_close(remote_address_space);
|
||||
kern_handle_close(remote_task);
|
||||
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;
|
||||
|
||||
kern_handle_t 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);
|
||||
kern_handle_close(remote_task);
|
||||
return LAUNCH_ERR_THREAD_CREATION_FAILED;
|
||||
}
|
||||
|
||||
thread_start(thread);
|
||||
|
||||
kern_handle_close(thread);
|
||||
elf_image_cleanup(&image);
|
||||
|
||||
return LAUNCH_OK;
|
||||
}
|
||||
59
lib/liblaunch/stack.c
Normal file
59
lib/liblaunch/stack.c
Normal 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
26
lib/liblaunch/stack.h
Normal 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
|
||||
15
lib/librosetta/CMakeLists.txt
Normal file
15
lib/librosetta/CMakeLists.txt
Normal 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)
|
||||
6
lib/librosetta/bootstrap.c
Normal file
6
lib/librosetta/bootstrap.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <rosetta/bootstrap.h>
|
||||
|
||||
const struct rosetta_bootstrap_channel *rosetta_bootstrap_get_channel(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
45
lib/librosetta/include/rosetta/bootstrap.h
Normal file
45
lib/librosetta/include/rosetta/bootstrap.h
Normal 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
28
lib/libxpc/CMakeLists.txt
Normal 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
59
lib/libxpc/buffer.c
Normal 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;
|
||||
}
|
||||
86
lib/libxpc/include/xpc/buffer.h
Normal file
86
lib/libxpc/include/xpc/buffer.h
Normal 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
|
||||
8
lib/libxpc/include/xpc/context.h
Normal file
8
lib/libxpc/include/xpc/context.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef XPC_CONTEXT_H_
|
||||
#define XPC_CONTEXT_H_
|
||||
|
||||
typedef struct xpc_context {
|
||||
|
||||
} xpc_context_t;
|
||||
|
||||
#endif
|
||||
13
lib/libxpc/include/xpc/endpoint.h
Normal file
13
lib/libxpc/include/xpc/endpoint.h
Normal 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
|
||||
53
lib/libxpc/include/xpc/msg.h
Normal file
53
lib/libxpc/include/xpc/msg.h
Normal 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
|
||||
12
lib/libxpc/include/xpc/status.h
Normal file
12
lib/libxpc/include/xpc/status.h
Normal 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
|
||||
85
lib/libxpc/include/xpc/string.h
Normal file
85
lib/libxpc/include/xpc/string.h
Normal 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
112
lib/libxpc/msg.c
Normal 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
60
lib/libxpc/string.c
Normal 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;
|
||||
}
|
||||
10
programs/systemd/CMakeLists.txt
Normal file
10
programs/systemd/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
file(GLOB sources *.c)
|
||||
add_executable(systemd ${sources})
|
||||
target_link_libraries(systemd libc libc-runtime liblaunch libmango)
|
||||
|
||||
sysroot_add_program(
|
||||
NAME systemd
|
||||
BIN_DIR /usr/bin)
|
||||
bsp_add_program(
|
||||
NAME systemd
|
||||
BIN_DIR /usr/bin)
|
||||
4
programs/systemd/main.c
Normal file
4
programs/systemd/main.c
Normal file
@@ -0,0 +1,4 @@
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
add_executable(test test.c)
|
||||
target_link_libraries(test c)
|
||||
target_link_libraries(test libc libc-runtime liblaunch)
|
||||
|
||||
sysroot_add_program(
|
||||
NAME test
|
||||
|
||||
@@ -1,6 +1,157 @@
|
||||
#include <function.h>
|
||||
#include <string.h>
|
||||
|
||||
int _start(void)
|
||||
const char *s
|
||||
= "RHZVNYYFPCGZIKWYLOZAKKLZGKWXDPXSTWNSWDBVDCPUMTAZCRNGNHGQPAGEWVAOCJST"
|
||||
"PBXVWPKXQAHGETDTDETVYAEQGNBLEQIPHGGWKEDJAULOVMZKNABLUUXOTMHJNAVLSGCZ"
|
||||
"QIFHIUCBCIRZIOFVHBPSGVBBZSRAQZYRPMYBNJDFTWSUUZVBQZTBUFUDJDYKUXWHDMXD"
|
||||
"JKBSNWLFPUDGRMQPBCWVJZHOTJBTMGUSXQUZVXLDKWIKINXRFJKSYJEKDAPRQPMQMJPS"
|
||||
"ICCTMELLKDFFJXOXISIXNVANFHLRPYMNPYCMUOYSPAYCKYJKRBZENHXYRJWRYXZGETMJ"
|
||||
"XBFTCMSIHZQSCMLESDFKGTTNMCZSXEFMGXZYPWVPODMYTLDUOKGPMTMFBTQQHPBHMNCM"
|
||||
"LYRGGUAIRSFFBRSKXOLJBWEMEODRQLXZJDRSTXBJOKOMUQKCJVKFHDYXCUUDHDEITHNH"
|
||||
"VQLJJQMLYWGIDVLYCEJTJFJQLTKSAPZGEEZKVLQPHIVJNJVTXJUGPZODIFXTQNLBSFLG"
|
||||
"NSPMGLUSEBOFJWXFFRBHIHYGGTILVVOJRPOFOIGDFCHLAZXLSOUCPCLZCBVWGZVKGDON"
|
||||
"RPYOTSRWUNAGQSPHSGEQHOLUSOZCQQGJBLPKQNGKOPCVKLACDBPAPXDMGKLFPUOFQDWY"
|
||||
"INIKZPLVFSZZZZHAYKXTETJDPMBHKNRCRLXZHFENZCABDTZULCRHCCVZFETBEBEJFVKJ"
|
||||
"ADWFSHKKSMMKGTIHPAIAMWXTRJILBMWMBDZGRPZXHMJVCWPKSNPYPPNSAVQDSMANBFZO"
|
||||
"LJBYKDOZNAPZRWEQDIIZRPNGZGHQWPIONPSAMBNNZERYMIQOVHRGZFXWVUARJMFWNPQP"
|
||||
"GHDCZABLOYHCBCXAIDSPMDKZVBBOKHAHGTEPRQAIBVWTFBQDGDJPQGMVAGQQVMULVPMG"
|
||||
"UPGEJUIZZXQRQKRJUCKDDZFTAHAAHMJLSISGFFOXOYNMJCAPPQXAVFSAFFRPRTEQLNCX"
|
||||
"JVKTHBPZLAEXSIGVJASAERWDGDQDXASXHRSSCNAUMRWSQDZTOJHJGJLLXMJSTXBHOYPH"
|
||||
"ELYKSXNJSPWPMFAKOOTXXTOEBLYFSOIJDWAOCTHDFOEBEEXVUBXOJDUCJWQOUUDOJNZG"
|
||||
"PULPYTAOQEXMHSGOOUXHAJOKRHOOMZYMBHQNTQDWZPCXATDEKTYLNBKNUEIINHQTEGTR"
|
||||
"ZKLMAKIIHHZBQIPXLAGADCFDYWEOJVHFPEMRDIOJYAMWSWEUOPJFEDGRGJOQNTSHRIJY"
|
||||
"JPTOSWYZXJCGXLYVOWKAFGULLNCKIUZDWUXTHNYSWMMCJGTFVVVPJHEKYQVFRWLIZBBK"
|
||||
"CNCRITFZYTQZZGZHXELBEYXSVVYRFBGRFPRDROUXKUMAGFYOJRMCLLHJVMQFYOBCXSEL"
|
||||
"OQAQTRMLSGDAXWMBRSQHCIYYMBQQHMUQOKIANZCBGKHLCPUVUEZVKDTTSOWKKWIUBAIW"
|
||||
"SOCJAUALJFEQQXJBHRRZBFMJZZMIWTFKQDPOBFIGABJTHFLLSZPWWGGLHLYXKBODKBIV"
|
||||
"GAYGIKHNMTMJHCPRBQYAACGSFZPJWXUTRZFCGTLFQBVHZBKYBRMYTTCGIDKYWVRPJDTX"
|
||||
"RKXGOPOQLNSEIGHTSAXGPBROFHQACSIVSLCXTEDUOEPRMGJDYWTKEHCXWINUDCAZWAEY"
|
||||
"RQDKZRZXKWGHVWJDYHGFLCGKCLYCVHZTWWXPDBKTMBKBASERMURDREKNYVOCPHFSEGBQ"
|
||||
"LAUHDDCAPPGOAFZYJYXPPAQLUQVKSDEHPPDXMNWAAYLHXBFEFXRLSMQNIYDECYVVPHEE"
|
||||
"WXFFPEYLSHBXLYJWRABFKJYMJNWAUMVYJKZRDMPUQJKWVNTPRMHTAQGNSDLFTXVNIAIJ"
|
||||
"HOIISROAJCWEOAIHYMDGWEOABPGIMBTTGWYVJDZOOMUSHYDPCMKWDIXDGJCGTQVXWBKP"
|
||||
"DBCVJJDYIAPXKINQXACEMTJRCIAENGTTMWXVSQCCXRDVNZZNQDZTTYJHQGXZIJIIKPAU"
|
||||
"TQJDDQQEPGYZNKCKNMVFCRHUECPVFZYVUWVMSCIQZBVLSTNFAHDDEQRKDOUAQVRVQAVB"
|
||||
"ZCEMAJRJBVWKVBNEIQWWQSJPUVUKMBJIISCWXGGMWANYYLPCXHCBARMFTFMDXWSMKXPW"
|
||||
"DZUGFBSGGWXLOFGYJVIDWNTSGODTHQNCKPWRJENDZNSCEYZRLEPNNYVBNUZMXAUWNAJD"
|
||||
"XGTOLUAAIAHMDJERSESVFSMMHJKHIVIBWZLEAXUKRSWJOTFODRBZIJBRJQTFBVRQHITD"
|
||||
"TDBAJZCZUKSIZJYDXWTSRPLXULXHGEKMWMICUYVAGNGEICEMMVWLLYWTAKWNGLRYOCIG"
|
||||
"BEYTLVAZFHBYIJPUAQHITKHPWAQNEBQVAYZEINLRCKUZILAQPAGBJDWWLGCPQZOZVDQP"
|
||||
"MWTIOAFMEMFKLGVGGHTTKNERTLPPQFALZMCWSOMJQQZMRABNKBCYPFJOWQCXJKXTNOMJ"
|
||||
"MXAWMPFBJHOYHVOBDNWTKHYTISUQMFSNVBGUHDQFYCSZLZAFABKYQSZRQGKXOXORQPSJ"
|
||||
"NKRVVAXMMVVPBSWMTHUNXBLSVIOOQPLRPROIBBQGNQVOXQXRNNMSFGUZEIGIYMLMLYYL"
|
||||
"VINTZYXYXHUFMTQFPDGSFFDVCDMEZXSGQMMGJWMWANFSZNHDIIHVJFOZGMHAOVRUWWVX"
|
||||
"RCOJJKZLTMAOGSRWNNXPYDCQWSSOFWUKFPKQGYLFSMZBZBKWSBMMZMFPOYYMLVYHQQQF"
|
||||
"HORVESQYIKEBBKSEUYXUFRNNXZPUYESZWAKQQPAWZUHYJLXRXBFPRFSCHIHHDVAIKYDZ"
|
||||
"IDLVQBCSGOSGFOUKVMKTODAHQVTACKAONRDYENPGSQFKGKYQROFOEKMJXKFIAEKWNRJH"
|
||||
"RZCCSMNSSHZNSRTBGFJJDWAPVIOBDCQKMDEMUIWGMETBUTCGMMGXLHWWTXXQYAPMQQGU"
|
||||
"CHECLYRWNFKHGRWYZISFYKSGTJXKTKIMJKTUWOQFHXUVTOBYUCOZKLCQCUIURHSIGUAU"
|
||||
"FFSNEHFLHNGBNVTGPKCVCQLHEHLJRWPGSWHKFMMXSSCVTCUOEEZOFOPTDRUPPCYTRSKI"
|
||||
"NJBPLOHMHQLWKMVBSZLZVDZEKXKSCNWPZTGZUXRYJEPENAYFCEKCAMWQLNYDHCSUUSTJ"
|
||||
"GFZCTKDQYPEZKOOJQTYMWHZDDTCMYUXCAKFXISWQZDVETOAYIANBRSXLYKOGGHEEEGAX"
|
||||
"SRIASWZBXTFCHLKMXQDYNLZICZZANCBUVGWFWFHYAWVLQXTQPGPQGRGBEZVIIGZJUVQU"
|
||||
"PNXEMZDIFXAKSHTCCBQSAXWFBNLRYQKXKXQHAFZTUICFQVYOXQEHASXNSVUTYKSVYTMH"
|
||||
"UTPBEVILWSKGQXPAEEOPUSMGQVPWFMRZRIMJZRRQRIZBTTRROUWENBHUYVMOMVPFDLZH"
|
||||
"XMXYRNASODBTLCNRDSLGPTZBZHKTSMHIVNFJOMOFPHDANVSVOYZSTKOQJLDPKLOMAYUU"
|
||||
"CKWTOTDONTXKCEGVXKZNNFIUXHLJPFFNVMWSFPXZKROEIGQNGXWDRFJWDCUEGJCUJGVG"
|
||||
"PHYKQEWVOHHPURHBBIEZCKQCFUFFBDXYRNOHAVJISOQYXMRXIEFEISFEZHLNTJXYEAKM"
|
||||
"EKZPJQVQBKPXPFSOANBBJBAFJQEMPIELYUHYCHUTADTCLFXUQLXWTJEFLJXVBRQXYSIR"
|
||||
"RPMTNMFMUCHQJEVXUSFYUUYJHLEQBHUQOTVADYQGRXVLUPQXVXEDIGNSCYNMKPWLFLHZ"
|
||||
"LMQGPTMBDVUJOBSXDFHKSIEXJJQTURPNZVQDLUEJHQZOQSBMPMBEQOCOSEKVXIVSQIQL"
|
||||
"GSQAUMIAHHLQCBCQWFJWHSYNRFFTBKORISDYNRSMPVERKRJBWGYJRXMKHJDAKRANHFDH"
|
||||
"WTZOHYRVCTTUXCRAFNOLYPRGDYTXNSOTCDNFVVURJILWVDWOCGPQOZIGGNOEAHCBYGMC"
|
||||
"XGXAADYMYDAUXPDFADTVEQTHZRGYASPJRDIKJUFIRXGMFSCIURDNFIDUUDEKPFGWECZC"
|
||||
"OOFZWESHSPSOOBWZKIGODSLXCALULNOLQLCMMLSQDWJDTEOIYXFCLSLKJKGCGURXEEIN"
|
||||
"SCUQTRDGMYXFZEFBATVSYVAJISCBVDBZAJAPKBBQTQNNRZYBLGWPCIORYJJEKIZXRRCG"
|
||||
"ZHGQNMGWVNIANJXYJMWRCGDGDFFQISSZOWTQOKWRGXSGRUSJOHABJUEUIHNTLCXJQPNF"
|
||||
"YUKTQCGRFGZOQWYLJOGOGSRDXESAOTHZVHBEOOYJZYTMOSUUXDQNKTQBVUMRBPJEJIBU"
|
||||
"TOVXSGYSTADWQKFUEFWJDCAYTEYVZYDCQTHXJWYUESZSLRBKRAMLVVVBMEYSYFNBLKTY"
|
||||
"UJRQBOKJQTYXTOFPWGWEEANVFYMAVRKMNJARUOKTZTMMJKNVFEVSECABUZGGUEHRJIHO"
|
||||
"JODXJOOGFZWNURNEXBUCCUHUDYXBZTNJZSQGHAGYLJQSSJERWEGUFAJXGNBXDWVFSCEG"
|
||||
"SUCLQLHHTRQADZIBKFCBBEXUDLPCFUDONSHUUCREKHDUBQBKMECPPOGFYIUZNUDKTILB"
|
||||
"IHMYNAMJIDTJEQTVPFZDNONMWFIJEAUOLPWOZVKEFTXRCXNWHWPYDHLWNSWTMUEIBMSK"
|
||||
"FWROAFBMEOJAVLMNCNWEMGRUDKRPENXCJQQGLPCODNOTGPOQFZTOBEBJIDAMMZARXTMC"
|
||||
"EAHKYNTYZKWCUFYOSCOPICKIDAUSZHWNTVRRSTIMHFBUALQECIZKYFUYJHDTFDODXXNJ"
|
||||
"AKZNUMYZSHIGZQQXBTORWFCGQFKFURMZYWBAQSHAJEASIYAFQZDOUHBJXODDUAWNKWVB"
|
||||
"NABZSUNRULZDXKBRGVCKUIYVRVRMTDFSWCTCDYKBZDELJBDIHLOALYEKHNMECBWRZQBK"
|
||||
"XLWFYJYECKOJOGXJYBKFSUQZKEUWBHEWNHSZKJPRQRMLRFLJWBDZEJQVYRAFQGEOGUBU"
|
||||
"SVVWUGKXHSXHRXWCZKASIYPZZDLRUVBNBUQEEPZPHMSNUETUKMYWNJLEZWVOLZBQMLWE"
|
||||
"YPPVBOTADNFNJWUZKDWRXCJCMDQPGPBIVAVQJVTHEPLXEKPSPJQFNGILKTUETORGMHGH"
|
||||
"HHXTZIXUXPDLKNYGHNNTKAZFCGCUONFANKRXHGPQLPDJACZDMSSFPJHRPPGGSVEYKQHF"
|
||||
"JKASAYIFKXXVEYRCLIMLDEUQWIHZXPLKDCHUFYAHLOQUJMJCXTHFAOSOEYWOMFAZHGPE"
|
||||
"UNYKWLFYQPMRYXDVGWWMOLXHCHQADSYAAQMLBGGNQELFWMYHPWNIDOIFLGHGQUPCVPHS"
|
||||
"WDGERQMWOZBWFHTOSINKTPXQLFGHLVALHCYSPKFBWSYTUHMZQNZSDAQTAZLPHSYZKROA"
|
||||
"PSKJEWCRABAGYIIAYAUOVMTYIQOWYWHLLEXOOVZNLXOIPFNRYHDJYVTJLOHODYRBBBSB"
|
||||
"NPQCNUZHYTWDAQSEBEMDSEDTKORHUCILESZYYYLJNRCFHAWNUMQHDQKXGCJIZOGBDVTU"
|
||||
"PWNKAHKIRBHINKVRRBOPVAWCSPVMTNQQYDYLCKHPKRFYCAYUPGAOQLJSTHGMCXUUQIVI"
|
||||
"MNPISKLETFEWNDDTDHLCQXNGRJJZHGJYYWNXKQIWXENKWQAGAXTCTLGWNVXDMMPHYPBQ"
|
||||
"GWWNCWJWYVBVYOWOEJJPZAZGKJEQHPDBYUQBMOLOIMZXYXFOBNNPMGDCTXLCHBEBHCOS"
|
||||
"HGKAEBGPLANNUHMOHWHAQEWFJSWPIFTWZNKWHKZYDXDAJODTHPXPIGVFYVDNWEQFKKIC"
|
||||
"EBMGPSBVTPXODJVAYJAURNSFOCUNJROYEMOELHMIGLFDMJQFVEOSINHIWDUUIPNSBHEC"
|
||||
"TKUFRERFNYCWSSGCYQWMXOQFCZPCSAVRBMSFZEYDBSWWHYLHIGGIDQJRTLNJOMWQVKES"
|
||||
"KTFWQIRKJEMAZSMFQQSSTCXKOUZLJJWNYJJKSHPAOTEWEKKABTJDOFRGKVBMJFKFFVSP"
|
||||
"GPMUCDWAFPHLKKZGEYTQNFJBGJTSATHNVDWRKSMLAJAPHYEJXYEKCTKDFGDILOIRDWLV"
|
||||
"LAMTOCSMRMXYYHPHYBMKAVDRWYSXVPLZUBPAVUUQDNRCNYPKUSWBTCHJMIHQJNXXXXQX"
|
||||
"LIUZQDFCTJBHELXALVTAJDFIPIFAKJKCPPPPXAVPTOUTLTIGMBUWOIERHBYOIMWTTXOY"
|
||||
"KCZKDVRSARRBMSQFZGGSVPVBHKBYXZBITZDBQDBZLQNPVEQTXECOHOJKEXUUIBXSORPF"
|
||||
"THLTMDDDOYSQZBGMBGZFYAJMHWZOLRUUJAIHOLSCIYGHMRAEIKFLFNLEMHOPKVRTJCMJ"
|
||||
"DKBKJCMDBPGUGPPZBCXYRLHZUYPMIQOXYOCGKBEHZFHGAAKQINMHUNTSJHPPZGNKFREX"
|
||||
"HGGFEFDAWMCMIXEDLUPDAXNCTHFDMHJPZOGJILKJXRUQBGXKDXTBZXSPZLZUCNCZZYRU"
|
||||
"DRUEVXRELACIWMGUIBKEXSYTBIJTJPJJLCIQQBVJGXHPCTSHPASIIAMPATSDCTXZAPCJ"
|
||||
"ESVMBOTOLKGHVRZSBVOBSAAKRPSAFYNPIDVFUMNMJRGKWOANKHZYCABHWIWUJFLDPSFY"
|
||||
"SPBXQEFLNEEMIGMWWXYTXNTHRXUZQKXCMBLEHGRBFPSUMGMBJFFWTAEFCLDBOHMNAICE"
|
||||
"YAZCTBCHKXEIBYUTQEAOVDJVOLTYDJJUCPSXUEPTFZPSJOMQDSSKBAHRIVYHQJFVUQJH"
|
||||
"HRAQYZLYTOAWWIIPEUPQEBYSKTRETZEDWVALVPISUBTOWJZQLVRWKLLLMWEAZZIGMTRV"
|
||||
"DXJHBFOTBFYSKQYJSNVKINMYRAMBFMBZUVHEEUWRRCMLAPOKKIODTFIIVTIPTBMVMZIP"
|
||||
"HKDIQRFOKYTVDLAPPUMYNZBJMZMDDQDTXZWOMAJSJETWLGSAJDNNCODMAMCGADNXJGPP"
|
||||
"GMPQXTZYICNPVATOCYCCGVSAKGCSGCPVUFGNGJPCRVZQXIDIZYCEBNMLMYHUMJZNHGCZ"
|
||||
"TIYTNXTCGMGSBGLIDHYABMLEBGAHLOEYVOAMROXQAFDNEIZAOFWDETNEZWJFHTYOEVDH"
|
||||
"RZDIZNSNBDCERUYZRLFWAANFAETBEWWPNNMUYXVBVDKMWPJUZLEPXOJAZOAYNKZDTBJO"
|
||||
"MKXEMIAGHIQIHXPZGWDEQJKBNTDIWPLDANSOQJTGVPPSROOXGEBBWKLXMUEKZBKTTQTN"
|
||||
"HILWSXGGYZZFYPGDVMNGLGGBSZJYWXGVHAACMVKQLPYXWWJMOQJJCXQOUIRCXPYCITBW"
|
||||
"WCOKSSDXXWHHSOPANMWVIKJFLYNBPQAUHWKZEQBDVWDDULWFVBXUBJVOMHNAELALFGZT"
|
||||
"FHSVLXJTMSOAKGBXKHKDEKVVFRZPKLTRRKCREVPXQYEVGIHAUNEJDLWERMKWJJBONLPW"
|
||||
"BNTDWFILGFYOOAPNXVBDGGYDCZWUSJLPLZRVYAVSQFEXRZACHLLGGDEJGURDPYFCQUCX"
|
||||
"OTNJJUJMMBEYSKJPKBGMQAAWOSPYAWDPJEQRLDJVZMRCYSKBAOQYWTJXSRRRMSFGNFRX"
|
||||
"VEWFGPXVPZQXGRGOLKIYCCAOAGCZSHGPEGVMWETUWVZXZKCJVGQNEJBICWINFFHIUSUE"
|
||||
"SVKYPVAOKBQEMQNKAXHFDETVNNFPQAXQXFJSKJCFLAHNRQUQPXYELGDYJDEETNEYGTMQ"
|
||||
"MYIGQUDGOUMZDQORHNVDXBCJYBFTCDEMATBUCGAEKONTAIRCOHHKJFORZVDQSOWKDLKU"
|
||||
"UOTTLLKQHVKVYOLJWNRRWXEOJMDZIKPMZFBBJCQSVTMCFFZDCWLRQDKAOKEXFVDZCPHE"
|
||||
"UGNBVSILDBRPBJKNZXIWTNOZEDDESKOGYFOWPZJBQIQXKQERGWTJCUXGTOHLAOWDLGPF"
|
||||
"RHVNLXVTKDDCWWZUAXKJZKPMOZCZCYVOIRCJNIJAMFRPAWNKFYAGKHNEPKPFTTRXXGTN"
|
||||
"XFHFJXQWMKDSODYMNWJIFQZJOCYHUKYNYKXSLJNMBKCUXETKQTSZAXZUEERRGNBFDXJW"
|
||||
"IKSVAVDNXLHJXVNDGLFZNZYTMJPDDXCOPGEROKJOGHXLDZJTSWOXIVCJKLYYTKANVFIF"
|
||||
"KRVVEXSZXREAOBPVQRZUQKIWNMPTFUWARTQJCDTPEOVDHTDCWAWTTMNLRTOPYGPBROQA"
|
||||
"QSMTGDCATMYQDJZQAUBQPOZLXDIJCKEVXDTPDNCHNJXMHNRVLWLKSAUYTHVOIYAAHUCY"
|
||||
"XUWPHXMFXBDNBSNZIVKZANBYCHTUDOLHVVXGFDRDOIRCCRTZRNPWFTHSQRYTLGETDTGZ"
|
||||
"KQWQPZYUTFFVFRQCAJAFDJAUMHPRXGUTLMFKOGXQHGOACGELSACTSNJTSOGNJAODFCIB"
|
||||
"EQGGZETTRWOLAPRYJIGCYSUIMDCRHRGBKJFPDXVGPSPGDMBOLAVARRJSNDMZIOPKTAMA"
|
||||
"NRZOSWGMPBPDBPCUADFMIEWOEBNNDFHUJCFMWXAFDKSWUTMMSQZSFBOGSSKOITDUGLLA"
|
||||
"NFCZSGCDFDWUDTBPFKMHROQMIQRLCNGGCSFTNCVXSJJGPUZQREUASWLVOFVHRZPQUYGE"
|
||||
"TBCJUSBKOJVBDWDEGXWBJPFTBFEALLVFFREQSGIONNYQFBFNKENZKSWLEWGEJJPZLRPV"
|
||||
"RLDHLELHFXXIEXDJOSAUGITKSWYCPMEBLWOLAEJXXARMXQCBEFKLOOIYRBYVWLNZKNCA"
|
||||
"ISOZEWVWPUDRGLYYKWWTNCWPZLYYDBKARCATEJHLYYQFNYATGYJIUNMVLEGUYBJVEVVM"
|
||||
"WBVJYQEGPZUOJYLWNEMODVPHTWTLURFETDHELDHPGEQWLRKZRFCEORRGYSBIJHPVNSNZ"
|
||||
"CAVXFCESMJIXGJPGLKDREKYGBPQSACESXAAHVQYFBSYWDPHDNTOZTEGSCISTBUMLDOQP"
|
||||
"WNQCSJUAENKYFCYIYENHTIAARBRTOCYDVMHPCPRGWQVDIFLHWMZWRGCWQTAIKCAJAMLG"
|
||||
"OKGURQTGNNUSCBKTPUXIGKDUWUGMTLCAOTKMXVTAEYWNWBTIQMLVDIEVBAGJMCWMMGZI"
|
||||
"USESSUIHYSYOXHFLNZVTCVATIUVWGDETJUEHTVQBJDGHIIDFYHTFDIDPLNGXLIBMKAYG"
|
||||
"DOZLOFKQXVWSQPRYUKAMGEICLKOMYNLWEMKLWDOPEEZGTXVDQWMULORFGNLKNVVCGQXQ"
|
||||
"CUIYKIAMJSJVQJKRBNBIEELCZLMPQILCEBLZZTKCPBOQLTMZRGTNWHPIUYWMBCNGIABH"
|
||||
"WIAILEDQVNWKJGYWMZYWOZGRHQOUFGCAETYUTLYZBCHKANZYPXTLMVREDIWBPAISSWKP"
|
||||
"IJIBRLWPFXQOLOFIXLUGDVMXNPJWBMMZYJUKUZPPTGVCRIMITUTPYLJOIDQGOMYUFSJB"
|
||||
"MRAZFVZSFZUSNJYDWBUDMFTFDBRCZCZZERWZSOXVAZISSEOMPKHESJRLUMZBBLBIXPZR"
|
||||
"UYRFMLZQMMZWMNXULVXZQPOUKPMXISKOPTEDXASVPAIENMUWMBNGIVWVOQQXKGYEAMAT"
|
||||
"YLDPYAASJSACUYILVJBXLHEMYKRKXEIVCDWKPQUBHACBNBGVTQLFDFPLAGUWUPBNBSIT"
|
||||
"UEENOTZFWDWNNYZTHJIBDYMCERWQNSDKDDUPPXPJTGDYQTFNSRQZZSZBYGSQHRRGSVAQ"
|
||||
"QEICLCSLMWQYXGGJEPJWZXKJUCFHJRACHRHLCRQWKXSUFJNBOMGWAIBKNWUDBJTFWVBW"
|
||||
"UPIUAKBMXXDVVKBEUAEHMOELYCJVEFJEDFBTITDNTEGRAIOACOHFCVERCTRMUZHPRNPQ"
|
||||
"YCFDKGTRGWFEJAXVTTBMIAYLJZEJAAEOBCTIXHYNDWPXIWOVGXSOLTXIBPBLHYKHIDGX"
|
||||
"HHXNVRCMUXBZGFIEDZDBZOKSMKRNYTWJGJBIMQIOZQRFROWMLNYPDDKTRESDVHHJNRMN"
|
||||
"GASH";
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return function();
|
||||
const char *s = "Hello, world!";
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
9
services/CMakeLists.txt
Normal file
9
services/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
file(GLOB items *)
|
||||
|
||||
foreach(item ${items})
|
||||
if (NOT IS_DIRECTORY ${item})
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
add_subdirectory(${item})
|
||||
endforeach (item)
|
||||
10
services/ldd/CMakeLists.txt
Normal file
10
services/ldd/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
file(GLOB sources *.c)
|
||||
add_executable(ldd ${sources})
|
||||
target_link_libraries(ldd libc-core libc-runtime libmango)
|
||||
|
||||
sysroot_add_program(
|
||||
NAME ldd
|
||||
BIN_DIR /usr/bin)
|
||||
bsp_add_program(
|
||||
NAME ldd
|
||||
BIN_DIR /usr/bin)
|
||||
4
services/ldd/main.c
Normal file
4
services/ldd/main.c
Normal file
@@ -0,0 +1,4 @@
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(ld)
|
||||
add_subdirectory(bootstrap)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user