Compare commits
55 Commits
86ca343cf0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ac8f669e6b | |||
| 7d6af0344c | |||
| 1e52036585 | |||
| 24fc8ecb97 | |||
| cc15bb54f3 | |||
| ed138d581e | |||
| eaf5e02ad4 | |||
| 88f8d4f18a | |||
| 5d9a3fa54d | |||
| 8236f99aef | |||
| 81b9e7777a | |||
| aa155824d3 | |||
| af424d85d8 | |||
| a37a07d708 | |||
| 2fab6687b0 | |||
| d1a9c5cd45 | |||
| 3bf995cdf6 | |||
| 8c09909d07 | |||
| a041bc55db | |||
| 32d0606d16 | |||
| 3bc331b9c5 | |||
| c2b59ed494 | |||
| c340f927bb | |||
| 7a00de28a1 | |||
| 9148349f44 | |||
| 766411d983 | |||
| 555989e74a | |||
| ffc2ed735e | |||
| 68ae449731 | |||
| 08a9627548 | |||
| 844f6d50eb | |||
| 863be171c1 | |||
| 0471838f30 | |||
| cf70352caa | |||
| 342b588b38 | |||
| cdf828be2d | |||
| c11d55d675 | |||
| a5cf0b050d | |||
| bd39cb11aa | |||
| a01b5ddb11 | |||
| 1e9fe24b39 | |||
| 9044281d1b | |||
| 5dd99bb0a6 | |||
| d03c750e4a | |||
| c66c7f3f3f | |||
| 29acfcee69 | |||
| fea89d675e | |||
| eb8d9c3512 | |||
| 0c56c645ac | |||
| 68b7783f32 | |||
| ea2b0d3986 | |||
| c9ccebacfc | |||
| 5ad5f57a76 | |||
| f441d633b2 | |||
| 9ea3441fcc |
@@ -1,6 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(Rosetta C CXX ASM)
|
||||
|
||||
include(CheckPIESupported)
|
||||
check_pie_supported()
|
||||
|
||||
set(sys_dir ${CMAKE_CURRENT_BINARY_DIR}/sys)
|
||||
|
||||
set(kernel_name mango_kernel)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
include(System-Disk)
|
||||
include(QEMU)
|
||||
include(Bochs)
|
||||
include(Test)
|
||||
|
||||
@@ -16,6 +16,11 @@ set(CMAKE_ASM_COMPILER ${ASM_COMPILER})
|
||||
SET(CMAKE_C_FLAGS "-ffreestanding -nostdlib -z max-page-size=0x1000 -m64 -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -D_64BIT -DBYTE_ORDER=1234" CACHE STRING "" FORCE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-shared" CACHE STRING "" FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--unresolved-symbols=report-all,--dynamic-linker=/lib/ld64.so" CACHE STRING "" FORCE)
|
||||
set(CMAKE_C_LINK_OPTIONS_PIE "-pie")
|
||||
set(CMAKE_C_LINK_PIE_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_NO_PIE_SUPPORTED TRUE)
|
||||
|
||||
set(CMAKE_C_OUTPUT_EXTENSION .o)
|
||||
set(CMAKE_CXX_OUTPUT_EXTENSION .o)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
|
||||
@@ -8,7 +8,8 @@ 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(generic_flags -m 128M -cpu qemu64,+rdrand)
|
||||
|
||||
set(no_debug_flags)
|
||||
|
||||
if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
|
||||
|
||||
35
arch/x86_64/Test.cmake
Normal file
35
arch/x86_64/Test.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
find_package(Python COMPONENTS Interpreter)
|
||||
if (NOT Python_EXECUTABLE)
|
||||
message(STATUS "QEMU: Cannot find python. Direct-kernel boot testing unavailable")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
find_program(QEMU qemu-system-${TARGET_ARCH} REQUIRED)
|
||||
if (NOT QEMU)
|
||||
message(STATUS "QEMU: Cannot find qemu-system-${TARGET_ARCH}. Direct-kernel boot testing unavailable")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(patched_kernel ${CMAKE_CURRENT_BINARY_DIR}/kernel/${kernel_name}.elf32)
|
||||
set(generic_flags -m 1G -cpu qemu64,+rdrand)
|
||||
set(this_dir ${CMAKE_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR})
|
||||
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 ()
|
||||
|
||||
message(STATUS "Test: Enable direct-kernel boot testing with QEMU")
|
||||
add_custom_target(test-successful-boot
|
||||
COMMAND
|
||||
${this_dir}/test/successful-boot
|
||||
${Python_EXECUTABLE}
|
||||
${this_dir}/test/check-results
|
||||
${QEMU}
|
||||
${patched_kernel}
|
||||
${sys_dir}/${bsp_name}
|
||||
USES_TERMINAL
|
||||
DEPENDS ${patched_kernel} bsp)
|
||||
52
arch/x86_64/test/check-results
Normal file
52
arch/x86_64/test/check-results
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: ft=python
|
||||
import sys
|
||||
|
||||
def log(f, msg):
|
||||
print(msg)
|
||||
f.write(msg)
|
||||
f.write('\n')
|
||||
|
||||
def successful_boot(boot_log, out):
|
||||
nr_panic = boot_log.count("---[ kernel panic")
|
||||
if nr_panic == 1:
|
||||
log(out, "Kernel panic!")
|
||||
return 1
|
||||
if nr_panic > 1:
|
||||
log(out, "Multiple kernel panics!")
|
||||
return 1
|
||||
|
||||
nr_boots = boot_log.count('Mango kernel version')
|
||||
if nr_boots == 0:
|
||||
log(out, "Kernel didn't start!")
|
||||
return 1
|
||||
if nr_boots > 1:
|
||||
log(out, "Kernel rebooted during test!")
|
||||
return 1
|
||||
|
||||
nr_finish = boot_log.count("ld finished")
|
||||
if nr_finish == 0:
|
||||
log(out, "Didn't reach end of boot sequence!")
|
||||
return 1
|
||||
if nr_finish > 1:
|
||||
log(out, "Boot sequence performed multiple times!")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
tests = {
|
||||
'successful-boot': successful_boot,
|
||||
}
|
||||
|
||||
test_name = sys.argv[1]
|
||||
boot_log_path = sys.argv[2]
|
||||
out_path = sys.argv[3]
|
||||
|
||||
boot_log_file = open(boot_log_path, 'r')
|
||||
boot_log = boot_log_file.read()
|
||||
boot_log_file.close()
|
||||
|
||||
out_file = open(out_path, 'a')
|
||||
|
||||
exit(tests[test_name](boot_log, out_file))
|
||||
69
arch/x86_64/test/successful-boot
Executable file
69
arch/x86_64/test/successful-boot
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# vim: ft=bash
|
||||
|
||||
|
||||
log_dir="test-results/successful-boot"
|
||||
rm -rf $log_dir
|
||||
mkdir -p $log_dir
|
||||
|
||||
logfile="$log_dir/main.log"
|
||||
|
||||
log() {
|
||||
if [ -n "$logfile" ]; then
|
||||
printf '%s\n' "$@" >> "$logfile"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$@"
|
||||
}
|
||||
|
||||
log "Running boot test. Press Ctrl+\\ to stop."
|
||||
|
||||
declare -i result
|
||||
declare -i count
|
||||
declare -i pass
|
||||
declare -i fail
|
||||
count=0
|
||||
pass=0
|
||||
fail=0
|
||||
python=$1
|
||||
validation_script=$2
|
||||
qemu=$3
|
||||
kernel=$4
|
||||
initrd=$5
|
||||
|
||||
while true; do
|
||||
log "Test $count"
|
||||
result_file="$log_dir/$count.log"
|
||||
$qemu \
|
||||
-kernel $kernel \
|
||||
-initrd $initrd \
|
||||
-serial file:$result_file \
|
||||
-cpu qemu64,+rdrand \
|
||||
--append kernel.early-console=ttyS0 -s > /dev/null &
|
||||
qemu_id=$!
|
||||
|
||||
sleep 1.2
|
||||
|
||||
$python $validation_script successful-boot $result_file $logfile
|
||||
result=$?
|
||||
|
||||
count=$count+1
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
pass=$pass+1
|
||||
else
|
||||
mv $result_file "$result_file.FAIL"
|
||||
fail=$fail+1
|
||||
lldb \
|
||||
-o "file kernel/mango_kernel.debug" \
|
||||
-o "gdb-remote localhost:1234"
|
||||
fi
|
||||
|
||||
kill -INT $qemu_id
|
||||
|
||||
log "---------------"
|
||||
log "Total tests: $count"
|
||||
log "Pass: $pass"
|
||||
log "Fail: $fail"
|
||||
log "---------------"
|
||||
done
|
||||
@@ -21,6 +21,7 @@ function(add_interface)
|
||||
add_custom_command(
|
||||
OUTPUT ${header_path}
|
||||
COMMAND ${XPCG} ${arg_PATH}
|
||||
DEPENDS ${arg_PATH}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${arg_PARENT_DIR}
|
||||
COMMENT "Generating interface: ${arg_NAME}")
|
||||
|
||||
|
||||
@@ -6,4 +6,6 @@ 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);
|
||||
func seek[4](offset: offset, origin: int) -> (err: int, new_pos: offset);
|
||||
|
||||
func map[5](prot: int, flags: int) -> (err: int, vmo: handle);
|
||||
|
||||
2
kernel
2
kernel
Submodule kernel updated: de520cdd2d...a2c89df195
@@ -28,3 +28,5 @@ bsp_add_library(
|
||||
|
||||
target_link_libraries(libc libmango libxpc-static interface::fs)
|
||||
target_compile_definitions(libc PRIVATE ENABLE_GLOBAL_HEAP=1)
|
||||
|
||||
add_subdirectory(pthread)
|
||||
|
||||
26
lib/libc/core/string/strcpy.c
Normal file
26
lib/libc/core/string/strcpy.c
Normal file
@@ -0,0 +1,26 @@
|
||||
char *strcpy(char *output, const char *input)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; input[i] != 0; i++) {
|
||||
output[i] = input[i];
|
||||
}
|
||||
|
||||
output[i] = '\0';
|
||||
return output;
|
||||
}
|
||||
|
||||
char *strncpy(char *output, const char *input, unsigned int count)
|
||||
{
|
||||
unsigned int size = count;
|
||||
unsigned int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
output[i] = input[i];
|
||||
|
||||
if (input[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output[i] = '\0';
|
||||
return output;
|
||||
}
|
||||
53
lib/libc/include/fcntl.h
Normal file
53
lib/libc/include/fcntl.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef FCNTL_H_
|
||||
#define FCNTL_H_
|
||||
|
||||
#define O_RDONLY 0x0000 /* open for reading only */
|
||||
#define O_WRONLY 0x0001 /* open for writing only */
|
||||
#define O_RDWR 0x0002 /* open for reading and writing */
|
||||
#define O_ACCMODE 0x0003 /* mask for above modes */
|
||||
|
||||
#define O_NONBLOCK 0x00000004 /* no delay */
|
||||
#define O_APPEND 0x00000008 /* set append mode */
|
||||
|
||||
#define O_SHLOCK 0x00000010 /* open with shared file lock */
|
||||
#define O_EXLOCK 0x00000020 /* open with exclusive file lock */
|
||||
#define O_ASYNC 0x00000040 /* signal pgrp when data ready */
|
||||
#define O_FSYNC O_SYNC /* source compatibility: do not use */
|
||||
#define O_NOFOLLOW 0x00000100 /* don't follow symlinks */
|
||||
#define O_CREAT 0x00000200 /* create if nonexistant */
|
||||
#define O_TRUNC 0x00000400 /* truncate to zero length */
|
||||
#define O_EXCL 0x00000800 /* error if already exists */
|
||||
#define O_RESOLVE_BENEATH \
|
||||
0x00001000 /* only for open(2), same value as FMARK \
|
||||
*/
|
||||
|
||||
#define O_EVTONLY \
|
||||
0x00008000 /* descriptor requested for event notifications only */
|
||||
|
||||
#define O_NOCTTY 0x00020000 /* don't assign controlling terminal */
|
||||
|
||||
#define O_DIRECTORY 0x00100000
|
||||
#define O_SYMLINK 0x00200000 /* allow open of a symlink */
|
||||
|
||||
#define O_CLOEXEC 0x01000000 /* implicitly set FD_CLOEXEC */
|
||||
|
||||
#define O_NOFOLLOW_ANY 0x20000000 /* no symlinks allowed in path */
|
||||
|
||||
#define O_EXEC 0x40000000 /* open file for execute only */
|
||||
#define O_SEARCH (O_EXEC | O_DIRECTORY) /* open directory for search only */
|
||||
|
||||
#define AT_FDCWD -2
|
||||
|
||||
#define AT_EACCESS 0x0010 /* Use effective ids in access check */
|
||||
#define AT_SYMLINK_NOFOLLOW \
|
||||
0x0020 /* Act on the symlink itself not the target */
|
||||
#define AT_SYMLINK_FOLLOW 0x0040 /* Act on target of symlink */
|
||||
#define AT_REMOVEDIR 0x0080 /* Path refers to directory */
|
||||
#define AT_REALDEV \
|
||||
0x0200 /* Return real device inodes resides on for fstatat(2) */
|
||||
#define AT_FDONLY \
|
||||
0x0400 /* Use only the fd and Ignore the path for fstatat(2) */
|
||||
#define AT_SYMLINK_NOFOLLOW_ANY \
|
||||
0x0800 /* Path should not contain any symlinks */
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,9 @@ 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 int strcpy(const char *s1, const char *s2);
|
||||
extern int strncpy(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);
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
#define PROT_READ 0x02u
|
||||
#define PROT_WRITE 0x04u
|
||||
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
#define MAP_SHARED 0x01u
|
||||
#define MAP_SHARED_VALIDATE 0x03u
|
||||
#define MAP_SHARED_VALIDATE 0x02u
|
||||
#define MAP_PRIVATE 0x04u
|
||||
#define MAP_32BIT 0x08u
|
||||
#define MAP_ANON MAP_ANONYMOUS
|
||||
@@ -39,5 +41,6 @@ extern void *mmap(
|
||||
int flags,
|
||||
int fd,
|
||||
off_t offset);
|
||||
extern int munmap(void *addr, size_t length);
|
||||
|
||||
#endif
|
||||
10
lib/libc/include/sys/types.h
Normal file
10
lib/libc/include/sys/types.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef SYS_TYPES_H_
|
||||
#define SYS_TYPES_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#define UNISTD_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
extern int open(const char *path, int flags);
|
||||
extern int close(int fd);
|
||||
@@ -9,4 +10,6 @@ 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);
|
||||
|
||||
extern off_t lseek(int fd, off_t offset, int whence);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/msg.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <sys/remote.h>
|
||||
|
||||
int close(int fd)
|
||||
{
|
||||
kern_handle_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
22
lib/libc/io/unistd/lseek.c
Normal file
22
lib/libc/io/unistd/lseek.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/msg.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <sys/remote.h>
|
||||
|
||||
off_t lseek(int fd, off_t offset, int whence)
|
||||
{
|
||||
int err;
|
||||
off_t new_offset;
|
||||
|
||||
kern_status_t status = fs_seek(fd, offset, whence, &err, &new_offset);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
if (err != SUCCESS) {
|
||||
return __set_errno(err);
|
||||
}
|
||||
|
||||
return new_offset;
|
||||
}
|
||||
@@ -1,6 +1,170 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/vm.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static vm_prot_t vm_prot_from_mmap_prot(int prot)
|
||||
{
|
||||
vm_prot_t vm_prot = VM_PROT_USER;
|
||||
|
||||
if (prot & PROT_READ) {
|
||||
vm_prot |= VM_PROT_READ;
|
||||
}
|
||||
|
||||
if (prot & PROT_WRITE) {
|
||||
vm_prot |= VM_PROT_WRITE;
|
||||
}
|
||||
|
||||
if (prot & PROT_EXEC) {
|
||||
vm_prot |= VM_PROT_EXEC;
|
||||
}
|
||||
|
||||
return vm_prot;
|
||||
}
|
||||
|
||||
static int get_vmo_anon(
|
||||
int fd,
|
||||
int prot,
|
||||
int flags,
|
||||
size_t length,
|
||||
kern_handle_t *out)
|
||||
{
|
||||
vm_prot_t vm_prot = vm_prot_from_mmap_prot(prot);
|
||||
kern_status_t status = vm_object_create(NULL, 0, length, vm_prot, out);
|
||||
return __errno_from_kern_status(status);
|
||||
}
|
||||
|
||||
static int get_vmo(
|
||||
int fd,
|
||||
int prot,
|
||||
int flags,
|
||||
size_t length,
|
||||
kern_handle_t *out)
|
||||
{
|
||||
if (fd == -1) {
|
||||
return get_vmo_anon(fd, prot, flags, length, out);
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
kern_status_t status = fs_map(fd, prot, flags, &err, out);
|
||||
if (status != KERN_OK) {
|
||||
return __errno_from_kern_status(status);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void *mreserve(kern_handle_t address_space, void *addr, size_t length)
|
||||
{
|
||||
virt_addr_t base = (virt_addr_t)addr;
|
||||
if (!base) {
|
||||
base = MAP_ADDRESS_ANY;
|
||||
}
|
||||
|
||||
kern_status_t status
|
||||
= address_space_reserve(address_space, base, length, &base);
|
||||
|
||||
if (status != KERN_OK) {
|
||||
__set_errno(__errno_from_kern_status(status));
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
return (void *)base;
|
||||
}
|
||||
|
||||
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||
{
|
||||
return NULL;
|
||||
int tmp = 0;
|
||||
|
||||
if (flags & MAP_SHARED) {
|
||||
tmp++;
|
||||
}
|
||||
|
||||
if (flags & MAP_SHARED_VALIDATE) {
|
||||
tmp++;
|
||||
}
|
||||
|
||||
if (flags & MAP_PRIVATE) {
|
||||
tmp++;
|
||||
}
|
||||
|
||||
if (tmp != 1) {
|
||||
__set_errno(EINVAL);
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
if ((flags & MAP_ANONYMOUS) && fd != -1) {
|
||||
__set_errno(EINVAL);
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
if (!(flags & MAP_ANONYMOUS) && fd == -1) {
|
||||
__set_errno(EINVAL);
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
kern_status_t status = KERN_OK;
|
||||
kern_handle_t self = KERN_HANDLE_INVALID,
|
||||
address_space = KERN_HANDLE_INVALID;
|
||||
|
||||
status = task_self(&self);
|
||||
if (status != KERN_OK) {
|
||||
__set_errno(__errno_from_kern_status(status));
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
status = task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
if (status != KERN_OK) {
|
||||
__set_errno(__errno_from_kern_status(status));
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
if ((flags & (MAP_ANONYMOUS | MAP_PRIVATE)) && (prot == PROT_NONE)) {
|
||||
void *ret = mreserve(address_space, addr, length);
|
||||
kern_handle_close(address_space);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kern_handle_t vmo = KERN_HANDLE_INVALID;
|
||||
int err = get_vmo(fd, prot, flags, length, &vmo);
|
||||
if (err != SUCCESS) {
|
||||
kern_handle_close(address_space);
|
||||
__set_errno(err);
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
virt_addr_t map_address = 0;
|
||||
vm_prot_t vm_prot = vm_prot_from_mmap_prot(prot);
|
||||
if (addr && (flags & MAP_FIXED)) {
|
||||
status = address_space_map(
|
||||
address_space,
|
||||
(virt_addr_t)addr,
|
||||
vmo,
|
||||
offset,
|
||||
length,
|
||||
vm_prot,
|
||||
&map_address);
|
||||
} else {
|
||||
status = address_space_map(
|
||||
address_space,
|
||||
MAP_ADDRESS_ANY,
|
||||
vmo,
|
||||
offset,
|
||||
length,
|
||||
vm_prot,
|
||||
&map_address);
|
||||
}
|
||||
|
||||
kern_handle_close(vmo);
|
||||
kern_handle_close(address_space);
|
||||
if (status != KERN_OK) {
|
||||
__set_errno(__errno_from_kern_status(status));
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
return (void *)map_address;
|
||||
}
|
||||
|
||||
44
lib/libc/io/unistd/munmap.c
Normal file
44
lib/libc/io/unistd/munmap.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/vm.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
int munmap(void *addr, size_t length)
|
||||
{
|
||||
kern_status_t status = KERN_OK;
|
||||
kern_handle_t self = KERN_HANDLE_INVALID,
|
||||
address_space = KERN_HANDLE_INVALID;
|
||||
|
||||
status = task_self(&self);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(EPERM);
|
||||
}
|
||||
|
||||
status = task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(EPERM);
|
||||
}
|
||||
|
||||
status = address_space_unmap(address_space, (virt_addr_t)addr, length);
|
||||
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(address_space);
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
status = address_space_release(
|
||||
address_space,
|
||||
(virt_addr_t)addr,
|
||||
length);
|
||||
kern_handle_close(address_space);
|
||||
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
return __set_errno(SUCCESS);
|
||||
}
|
||||
@@ -30,4 +30,5 @@ sysroot_add_library(
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_compile_definitions(libc-malloc PRIVATE ENABLE_GLOBAL_HEAP=1)
|
||||
target_link_libraries(libc-malloc libc-core libmango)
|
||||
|
||||
@@ -38,14 +38,10 @@ static kern_status_t init_heap_region(heap_t *heap)
|
||||
task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
|
||||
kern_status_t status = vm_region_create(
|
||||
kern_status_t status = address_space_reserve(
|
||||
address_space,
|
||||
"libc-heap",
|
||||
9,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
MAP_ADDRESS_ANY,
|
||||
HEAP_REGION_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&heap->heap_region,
|
||||
&heap->heap_base);
|
||||
|
||||
kern_handle_close(address_space);
|
||||
@@ -72,9 +68,14 @@ static kern_status_t expand_heap(heap_t *heap)
|
||||
|
||||
virt_addr_t base = 0;
|
||||
|
||||
status = vm_region_map_relative(
|
||||
heap->heap_region,
|
||||
heap->heap_sys_alloc,
|
||||
kern_handle_t self, address_space;
|
||||
task_self(&self);
|
||||
task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
|
||||
status = address_space_map(
|
||||
address_space,
|
||||
heap->heap_base + heap->heap_sys_alloc,
|
||||
vmo,
|
||||
0,
|
||||
HEAP_EXPAND_INCREMENT,
|
||||
@@ -90,7 +91,7 @@ static kern_status_t expand_heap(heap_t *heap)
|
||||
void *heap_expand(heap_t *heap, size_t size)
|
||||
{
|
||||
kern_status_t status = KERN_OK;
|
||||
if (heap->heap_region == KERN_HANDLE_INVALID) {
|
||||
if (!heap->heap_base) {
|
||||
status = init_heap_region(heap);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ struct liballoc_minor;
|
||||
|
||||
#define HEAP_INIT \
|
||||
{ \
|
||||
.heap_region = KERN_HANDLE_INVALID, \
|
||||
.heap_liballoc = { \
|
||||
.l_pageSize = 4096, \
|
||||
.l_pageCount = 16, \
|
||||
|
||||
@@ -12,7 +12,6 @@ typedef enum heap_result {
|
||||
} 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 */
|
||||
|
||||
42
lib/libc/pthread/CMakeLists.txt
Normal file
42
lib/libc/pthread/CMakeLists.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
set(source_dirs thread)
|
||||
|
||||
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 sys_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sys/${CMAKE_SYSTEM_PROCESSOR}/*.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sys/${CMAKE_SYSTEM_PROCESSOR}/*.S)
|
||||
set_property(SOURCE ${sys_sources} PROPERTY LANGUAGE C)
|
||||
|
||||
set(sources ${sources} ${sys_sources})
|
||||
|
||||
set(headers ${headers} ${CMAKE_CURRENT_SOURCE_DIR}/include/pthread.h)
|
||||
set(public_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
rosetta_add_library(STATIC
|
||||
NAME libc-pthread
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dir}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
rosetta_add_library(SHARED
|
||||
NAME libpthread
|
||||
PUBLIC_INCLUDE_DIRS ${public_include_dir}
|
||||
SOURCES ${sources}
|
||||
HEADERS ${headers})
|
||||
|
||||
sysroot_add_library(
|
||||
NAME libc-pthread
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
sysroot_add_library(
|
||||
NAME libpthread
|
||||
HEADER_DIR /usr/include
|
||||
LIB_DIR /usr/lib)
|
||||
|
||||
target_link_libraries(libc-pthread libc-io libmango)
|
||||
target_link_libraries(libpthread libmango libc)
|
||||
25
lib/libc/pthread/include/pthread.h
Normal file
25
lib/libc/pthread/include/pthread.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef PTHREAD_H_
|
||||
#define PTHREAD_H_
|
||||
|
||||
#define __PTHREAD_ATTR_SIZE__ 32
|
||||
|
||||
typedef struct __pthread *pthread_t;
|
||||
|
||||
typedef struct __pthread_attr {
|
||||
long __sig;
|
||||
char __opaque[__PTHREAD_ATTR_SIZE__];
|
||||
} pthread_attr_t;
|
||||
|
||||
extern int __pthread_init(void);
|
||||
|
||||
extern int pthread_create(
|
||||
pthread_t *thread,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_func)(void *),
|
||||
void *arg);
|
||||
extern pthread_t pthread_self(void);
|
||||
extern int pthread_join(pthread_t thread, void **retval);
|
||||
extern int pthread_detach(pthread_t thread);
|
||||
extern void pthread_exit(void *retval);
|
||||
|
||||
#endif
|
||||
0
lib/libc/pthread/init.c
Normal file
0
lib/libc/pthread/init.c
Normal file
13
lib/libc/pthread/sys/x86_64/pthread_self.S
Normal file
13
lib/libc/pthread/sys/x86_64/pthread_self.S
Normal file
@@ -0,0 +1,13 @@
|
||||
.code64
|
||||
|
||||
.global pthread_self
|
||||
.type pthread_self, @function
|
||||
|
||||
pthread_self:
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
mov %gs:0, %rax
|
||||
|
||||
pop %rbp
|
||||
ret
|
||||
35
lib/libc/pthread/sys/x86_64/pthread_unmap_exit.S
Normal file
35
lib/libc/pthread/sys/x86_64/pthread_unmap_exit.S
Normal file
@@ -0,0 +1,35 @@
|
||||
.code64
|
||||
|
||||
#include "mango/syscall.h"
|
||||
|
||||
.global __pthread_unmap_exit
|
||||
.type __pthread_unmap_exit, @function
|
||||
|
||||
/*
|
||||
%rdi = (kern_handle_t)address_space
|
||||
%rsi = (void *)unmap_base
|
||||
%rdx = (size_t)unmap_length
|
||||
*/
|
||||
__pthread_unmap_exit:
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
/* save the address space handle for later */
|
||||
mov %rdi, %rbx
|
||||
|
||||
/* first, unmap the specified region */
|
||||
mov $SYS_ADDRESS_SPACE_UNMAP, %rax
|
||||
/* args are already in the correct registers */
|
||||
syscall
|
||||
|
||||
/* next, close the handle to the address space */
|
||||
mov $SYS_THREAD_EXIT, %rax
|
||||
mov %rbx, %rdi
|
||||
syscall
|
||||
|
||||
/* finally, stop the current thread */
|
||||
mov $SYS_THREAD_EXIT, %rax
|
||||
syscall
|
||||
|
||||
/* unreachable */
|
||||
ret
|
||||
24
lib/libc/pthread/thread/pthread.h
Normal file
24
lib/libc/pthread/thread/pthread.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef _THREAD_PTHREAD_H_
|
||||
#define _THREAD_PTHREAD_H_
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
enum pthread_flags {
|
||||
THREAD_DETACHED = 0x01u,
|
||||
};
|
||||
|
||||
struct __pthread {
|
||||
struct __pthread *thr_self;
|
||||
enum pthread_flags thr_flags;
|
||||
kern_handle_t thr_handle;
|
||||
void *thr_map_base;
|
||||
size_t thr_map_size;
|
||||
void *thr_result;
|
||||
};
|
||||
|
||||
extern void __pthread_unmap_exit(
|
||||
kern_handle_t address_space,
|
||||
void *unmap_base,
|
||||
size_t unmap_length);
|
||||
|
||||
#endif
|
||||
0
lib/libc/pthread/thread/pthread_attr.h
Normal file
0
lib/libc/pthread/thread/pthread_attr.h
Normal file
89
lib/libc/pthread/thread/pthread_create.c
Normal file
89
lib/libc/pthread/thread/pthread_create.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "pthread.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/status.h>
|
||||
#include <mango/task.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define DEFAULT_STACK_SIZE 0x40000
|
||||
|
||||
static void thread_launchpad(uintptr_t thread, uintptr_t func, uintptr_t arg)
|
||||
{
|
||||
struct __pthread *self = (struct __pthread *)thread;
|
||||
void *(*start_func)(void *) = (void *(*)(void *))func;
|
||||
|
||||
void *result = start_func((void *)arg);
|
||||
|
||||
pthread_exit(result);
|
||||
}
|
||||
|
||||
int pthread_create(
|
||||
pthread_t *thread,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_func)(void *),
|
||||
void *arg)
|
||||
{
|
||||
kern_status_t status = KERN_OK;
|
||||
kern_handle_t task;
|
||||
|
||||
status = task_self(&task);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(EPERM);
|
||||
}
|
||||
|
||||
size_t stack_size = DEFAULT_STACK_SIZE;
|
||||
|
||||
void *base
|
||||
= mmap(NULL,
|
||||
stack_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
0);
|
||||
|
||||
if (!base) {
|
||||
return __set_errno(ENOMEM);
|
||||
}
|
||||
|
||||
virt_addr_t sp = (virt_addr_t)base + DEFAULT_STACK_SIZE;
|
||||
|
||||
sp -= sizeof(struct __pthread);
|
||||
struct __pthread *new = (struct __pthread *)sp;
|
||||
sp &= ~(virt_addr_t)0x10;
|
||||
|
||||
memset(new, 0x0, sizeof *new);
|
||||
new->thr_self = new;
|
||||
new->thr_map_base = base;
|
||||
new->thr_map_size = stack_size;
|
||||
|
||||
uintptr_t args[] = {
|
||||
(uintptr_t)new,
|
||||
(uintptr_t)start_func,
|
||||
(uintptr_t)arg,
|
||||
};
|
||||
size_t nr_args = sizeof args / sizeof args[0];
|
||||
|
||||
status = task_create_thread(
|
||||
task,
|
||||
(virt_addr_t)thread_launchpad,
|
||||
sp,
|
||||
args,
|
||||
nr_args,
|
||||
&new->thr_handle);
|
||||
if (status != KERN_OK) {
|
||||
munmap(base, stack_size);
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
thread_config_set(
|
||||
new->thr_handle,
|
||||
THREAD_CFG_GSBASE,
|
||||
&new,
|
||||
sizeof(void *));
|
||||
thread_start(new->thr_handle);
|
||||
|
||||
*thread = new;
|
||||
return __set_errno(SUCCESS);
|
||||
}
|
||||
17
lib/libc/pthread/thread/pthread_detach.c
Normal file
17
lib/libc/pthread/thread/pthread_detach.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "pthread.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/object.h>
|
||||
#include <mango/signal.h>
|
||||
#include <mango/task.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
int pthread_detach(struct __pthread *thread)
|
||||
{
|
||||
thread->thr_flags |= THREAD_DETACHED;
|
||||
kern_handle_close(thread->thr_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
lib/libc/pthread/thread/pthread_exit.c
Normal file
31
lib/libc/pthread/thread/pthread_exit.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "pthread.h"
|
||||
|
||||
#include <mango/handle.h>
|
||||
#include <mango/task.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void pthread_exit(void *retval)
|
||||
{
|
||||
struct __pthread *self = pthread_self();
|
||||
if (!self) {
|
||||
/* TODO: abort(); */
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->thr_flags & THREAD_DETACHED) {
|
||||
kern_handle_t task;
|
||||
kern_handle_t address_space;
|
||||
task_self(&task);
|
||||
task_get_address_space(task, &address_space);
|
||||
kern_handle_close(task);
|
||||
|
||||
__pthread_unmap_exit(
|
||||
address_space,
|
||||
self->thr_map_base,
|
||||
self->thr_map_size);
|
||||
} else {
|
||||
self->thr_result = retval;
|
||||
thread_exit();
|
||||
}
|
||||
}
|
||||
31
lib/libc/pthread/thread/pthread_join.c
Normal file
31
lib/libc/pthread/thread/pthread_join.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "pthread.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/object.h>
|
||||
#include <mango/signal.h>
|
||||
#include <mango/task.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
int pthread_join(struct __pthread *thread, void **retval)
|
||||
{
|
||||
kern_wait_item_t wait;
|
||||
wait.w_handle = thread->thr_handle;
|
||||
wait.w_waitfor = THREAD_SIGNAL_STOPPED;
|
||||
wait.w_observed = 0;
|
||||
|
||||
kern_status_t status = kern_object_wait(&wait, 1);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
*retval = thread->thr_result;
|
||||
}
|
||||
|
||||
kern_handle_close(thread->thr_handle);
|
||||
munmap(thread->thr_map_base, thread->thr_map_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
0
lib/libc/pthread/thread/pthread_self.c
Normal file
0
lib/libc/pthread/thread/pthread_self.c
Normal file
@@ -6,6 +6,9 @@
|
||||
.extern main
|
||||
.type main, @function
|
||||
|
||||
.extern task_exit
|
||||
.type task_exit, @function
|
||||
|
||||
_start:
|
||||
# Args (as provided by the ABI)
|
||||
# %rdi: int argc
|
||||
@@ -13,4 +16,7 @@ _start:
|
||||
# %rdx: kern_handle_t task
|
||||
# %rcx: kern_handle_t address_space
|
||||
call main
|
||||
|
||||
mov %rax, %rdi
|
||||
call task_exit
|
||||
1: jmp 1b
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "btree.h"
|
||||
#include "file.h"
|
||||
#include "interface.h"
|
||||
#include "mapping.h"
|
||||
|
||||
#include <fs/allocator.h>
|
||||
#include <fs/context.h>
|
||||
@@ -8,9 +9,16 @@
|
||||
#include <fs/inode.h>
|
||||
#include <fs/status.h>
|
||||
#include <fs/superblock.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/object.h>
|
||||
#include <mango/signal.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TEMP_OBJECT_SIZE 0x10000
|
||||
|
||||
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);
|
||||
|
||||
@@ -18,12 +26,21 @@ struct fs_context {
|
||||
struct fs_superblock *ctx_sb;
|
||||
struct fs_allocator *ctx_alloc;
|
||||
struct btree ctx_filelist;
|
||||
kern_handle_t ctx_vm_controller;
|
||||
kern_handle_t ctx_channel;
|
||||
kern_handle_t ctx_temp_object;
|
||||
void *ctx_temp_object_buf;
|
||||
|
||||
struct fs_vtable ctx_vtable;
|
||||
};
|
||||
|
||||
struct fs_context *fs_context_create(struct fs_allocator *alloc)
|
||||
{
|
||||
kern_handle_t self, address_space;
|
||||
task_self(&self);
|
||||
task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
|
||||
struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx);
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
@@ -31,10 +48,50 @@ struct fs_context *fs_context_create(struct fs_allocator *alloc)
|
||||
|
||||
memset(ctx, 0x0, sizeof *ctx);
|
||||
|
||||
kern_status_t status = vm_controller_create(&ctx->ctx_vm_controller);
|
||||
if (status != KERN_OK) {
|
||||
fs_free(alloc, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = vm_object_create(
|
||||
NULL,
|
||||
0,
|
||||
TEMP_OBJECT_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&ctx->ctx_temp_object);
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(ctx->ctx_vm_controller);
|
||||
fs_free(alloc, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virt_addr_t temp_buffer;
|
||||
status = address_space_map(
|
||||
address_space,
|
||||
MAP_ADDRESS_ANY,
|
||||
ctx->ctx_temp_object,
|
||||
0,
|
||||
TEMP_OBJECT_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&temp_buffer);
|
||||
kern_handle_close(address_space);
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(ctx->ctx_temp_object);
|
||||
kern_handle_close(ctx->ctx_vm_controller);
|
||||
fs_free(alloc, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->ctx_temp_object_buf = (void *)temp_buffer;
|
||||
ctx->ctx_alloc = alloc;
|
||||
|
||||
ctx->ctx_vtable.open = fs_msg_open;
|
||||
ctx->ctx_vtable.close = fs_msg_close;
|
||||
ctx->ctx_vtable.read = fs_msg_read;
|
||||
ctx->ctx_vtable.write = fs_msg_write;
|
||||
ctx->ctx_vtable.seek = fs_msg_seek;
|
||||
ctx->ctx_vtable.map = fs_msg_map;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
@@ -73,6 +130,112 @@ enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx)
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void fs_context_set_channel(struct fs_context *ctx, kern_handle_t channel)
|
||||
{
|
||||
ctx->ctx_channel = channel;
|
||||
}
|
||||
|
||||
kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx)
|
||||
{
|
||||
return ctx->ctx_vm_controller;
|
||||
}
|
||||
|
||||
static enum fs_status handle_msg(struct fs_context *ctx)
|
||||
{
|
||||
xpc_msg_t msg;
|
||||
kern_status_t status = xpc_msg_recv_nowait(ctx->ctx_channel, &msg);
|
||||
if (status == KERN_NO_ENTRY) {
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
kern_tracef("message recv error %d", status);
|
||||
return FS_ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
switch (msg.msg_header.hdr_interface) {
|
||||
case INTERFACE_FS:
|
||||
status = fs_context_dispatch_msg(ctx, &msg);
|
||||
break;
|
||||
default:
|
||||
kern_tracef(
|
||||
"unknown message protocol %u",
|
||||
msg.msg_header.hdr_interface);
|
||||
xpc_msg_reply_error(&msg, KERN_UNSUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
static enum fs_status handle_page_request(struct fs_context *ctx)
|
||||
{
|
||||
equeue_packet_page_request_t packet;
|
||||
vm_controller_recv(ctx->ctx_vm_controller, &packet);
|
||||
struct file_mapping *mapping = (struct file_mapping *)packet.req_vmo;
|
||||
kern_tracef(
|
||||
"received page request [%zx-%zx] for file %s",
|
||||
packet.req_offset,
|
||||
packet.req_offset + packet.req_length,
|
||||
mapping->m_file->f_dent->d_name);
|
||||
|
||||
size_t length = packet.req_length;
|
||||
if (length > TEMP_OBJECT_SIZE) {
|
||||
length = TEMP_OBJECT_SIZE;
|
||||
}
|
||||
|
||||
char *dst = ctx->ctx_temp_object_buf;
|
||||
xpc_buffer_t buf = XPC_LOCAL_BUFFER_OUT(dst, TEMP_OBJECT_SIZE);
|
||||
enum fs_status status = fs_file_read_at(
|
||||
mapping->m_file,
|
||||
&buf,
|
||||
packet.req_offset,
|
||||
length);
|
||||
if (status != FS_SUCCESS) {
|
||||
kern_tracef("map-read failed with code %d", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
vm_controller_supply_pages(
|
||||
ctx->ctx_vm_controller,
|
||||
mapping->m_vmo,
|
||||
packet.req_offset,
|
||||
ctx->ctx_temp_object,
|
||||
0,
|
||||
packet.req_length);
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
enum fs_status fs_context_handle_request(struct fs_context *ctx)
|
||||
{
|
||||
kern_wait_item_t waiters[] = {
|
||||
{
|
||||
.w_handle = ctx->ctx_channel,
|
||||
.w_waitfor = CHANNEL_SIGNAL_MSG_RECEIVED,
|
||||
},
|
||||
{
|
||||
.w_handle = ctx->ctx_vm_controller,
|
||||
.w_waitfor = VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED,
|
||||
},
|
||||
};
|
||||
const size_t nr_waiters = sizeof waiters / sizeof waiters[0];
|
||||
|
||||
kern_status_t kstatus = kern_object_wait(waiters, nr_waiters);
|
||||
if (kstatus != KERN_OK) {
|
||||
return FS_ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
if (waiters[0].w_observed & CHANNEL_SIGNAL_MSG_RECEIVED) {
|
||||
return handle_msg(ctx);
|
||||
}
|
||||
|
||||
if (waiters[1].w_observed & VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED) {
|
||||
return handle_page_request(ctx);
|
||||
}
|
||||
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id)
|
||||
{
|
||||
struct fs_file *f = get_file(&ctx->ctx_filelist, id);
|
||||
|
||||
@@ -25,9 +25,22 @@ enum fs_status fs_file_read(
|
||||
return status;
|
||||
}
|
||||
|
||||
enum fs_status fs_file_write(
|
||||
enum fs_status fs_file_read_at(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
off_t offset,
|
||||
size_t count)
|
||||
{
|
||||
if (!f->f_ops || !f->f_ops->f_read) {
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return f->f_ops->f_read(f, buf, count, &offset);
|
||||
}
|
||||
|
||||
enum fs_status fs_file_write(
|
||||
struct fs_file *f,
|
||||
const struct xpc_buffer *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (!f->f_ops || !f->f_ops->f_write) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _FS_FILE_H_
|
||||
|
||||
#include "btree.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <fs/dentry.h>
|
||||
#include <fs/file.h>
|
||||
@@ -18,6 +19,8 @@ struct fs_file {
|
||||
off_t f_seek;
|
||||
struct fs_inode *f_inode;
|
||||
struct fs_dentry *f_dent;
|
||||
|
||||
struct queue f_mappings;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,12 @@ extern enum fs_status fs_context_mount_filesystem(
|
||||
enum fs_mount_flags flags);
|
||||
extern enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx);
|
||||
|
||||
extern void fs_context_set_channel(
|
||||
struct fs_context *ctx,
|
||||
kern_handle_t channel);
|
||||
extern kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx);
|
||||
extern enum fs_status fs_context_handle_request(struct fs_context *ctx);
|
||||
|
||||
extern struct fs_file *fs_context_open_file(
|
||||
struct fs_context *ctx,
|
||||
unsigned long id);
|
||||
|
||||
@@ -27,9 +27,14 @@ 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(
|
||||
extern enum fs_status fs_file_read_at(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
off_t offset,
|
||||
size_t count);
|
||||
extern enum fs_status fs_file_write(
|
||||
struct fs_file *f,
|
||||
const struct xpc_buffer *buf,
|
||||
size_t count);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
#define FS_INODE_H_
|
||||
|
||||
#include <fs/status.h>
|
||||
#include <mango/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct fs_inode;
|
||||
struct fs_dentry;
|
||||
struct fs_superblock;
|
||||
struct fs_file_ops;
|
||||
struct file_mapping;
|
||||
|
||||
enum fs_inode_mode {
|
||||
FS_INODE_REG = 0x01u,
|
||||
@@ -27,6 +29,7 @@ struct fs_inode {
|
||||
const struct fs_inode_ops *i_ops;
|
||||
const struct fs_file_ops *i_fops;
|
||||
size_t i_size;
|
||||
struct file_mapping *i_shared_mapping;
|
||||
};
|
||||
|
||||
extern enum fs_status fs_inode_lookup(
|
||||
|
||||
@@ -17,9 +17,8 @@ extern kern_status_t fs_msg_open(
|
||||
int *out_err,
|
||||
void *arg);
|
||||
extern kern_status_t fs_msg_close(
|
||||
const struct msg_endpoint *sender,
|
||||
const char *path,
|
||||
int flags,
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
int *out_err,
|
||||
void *arg);
|
||||
|
||||
@@ -31,5 +30,29 @@ extern kern_status_t fs_msg_read(
|
||||
size_t *out_nr_read,
|
||||
xpc_buffer_t *out_data,
|
||||
void *arg);
|
||||
extern kern_status_t fs_msg_write(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
const xpc_buffer_t *data,
|
||||
int *out_err,
|
||||
size_t *out_nr_written,
|
||||
void *arg);
|
||||
|
||||
extern kern_status_t fs_msg_seek(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
off_t offset,
|
||||
int origin,
|
||||
int *out_err,
|
||||
off_t *out_new_pos,
|
||||
void *arg);
|
||||
extern kern_status_t fs_msg_map(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
int prot,
|
||||
int flags,
|
||||
int *out_err,
|
||||
kern_handle_t *out_vmo,
|
||||
void *arg);
|
||||
|
||||
#endif
|
||||
|
||||
14
lib/libfs/interface/close.c
Normal file
14
lib/libfs/interface/close.c
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "../interface.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/status.h>
|
||||
|
||||
extern kern_status_t fs_msg_close(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
int *out_err,
|
||||
void *arg)
|
||||
{
|
||||
*out_err = ENOSYS;
|
||||
return KERN_OK;
|
||||
}
|
||||
112
lib/libfs/interface/map.c
Normal file
112
lib/libfs/interface/map.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "../file.h"
|
||||
#include "../mapping.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static int create_file_mapping(
|
||||
struct fs_context *ctx,
|
||||
int prot,
|
||||
int flags,
|
||||
struct fs_file *f,
|
||||
struct file_mapping **out)
|
||||
{
|
||||
if ((flags & MAP_SHARED) && f->f_inode->i_shared_mapping) {
|
||||
*out = f->f_inode->i_shared_mapping;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
struct file_mapping *mapping = fs_context_alloc(ctx, sizeof *mapping);
|
||||
if (!mapping) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
memset(mapping, 0x0, sizeof *mapping);
|
||||
|
||||
vm_prot_t vm_prot = VM_PROT_USER;
|
||||
if (prot & PROT_READ) {
|
||||
vm_prot |= VM_PROT_READ;
|
||||
}
|
||||
|
||||
if (prot & PROT_WRITE) {
|
||||
vm_prot |= VM_PROT_WRITE;
|
||||
}
|
||||
|
||||
if (prot & PROT_EXEC) {
|
||||
vm_prot |= VM_PROT_EXEC;
|
||||
}
|
||||
|
||||
kern_handle_t vmo = KERN_HANDLE_INVALID;
|
||||
kern_status_t status = vm_controller_create_object(
|
||||
fs_context_get_vm_controller(ctx),
|
||||
f->f_dent->d_name,
|
||||
strlen(f->f_dent->d_name),
|
||||
(equeue_key_t)mapping,
|
||||
f->f_inode->i_size,
|
||||
vm_prot,
|
||||
&vmo);
|
||||
|
||||
if (status != KERN_OK) {
|
||||
fs_context_free(ctx, mapping);
|
||||
return __errno_from_kern_status(status);
|
||||
}
|
||||
|
||||
mapping->m_file = f;
|
||||
mapping->m_vmo = vmo;
|
||||
|
||||
if (flags & MAP_SHARED) {
|
||||
mapping->m_type = FILE_MAPPING_SHARED;
|
||||
f->f_inode->i_shared_mapping = mapping;
|
||||
} else {
|
||||
mapping->m_type = FILE_MAPPING_PRIVATE;
|
||||
queue_push_back(&f->f_mappings, &mapping->m_entry);
|
||||
}
|
||||
|
||||
*out = mapping;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
extern kern_status_t fs_msg_map(
|
||||
xpc_context_t *xpc,
|
||||
const xpc_endpoint_t *sender,
|
||||
int prot,
|
||||
int flags,
|
||||
int *out_err,
|
||||
kern_handle_t *out_vmo,
|
||||
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;
|
||||
}
|
||||
|
||||
struct file_mapping *mapping = NULL;
|
||||
int err = create_file_mapping(ctx, prot, flags, f, &mapping);
|
||||
if (err != SUCCESS) {
|
||||
*out_err = err;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_tracef(
|
||||
"mapping file %s (%s) using vmo %zx",
|
||||
f->f_dent->d_name,
|
||||
(flags & MAP_SHARED) ? "shared" : "private",
|
||||
mapping->m_vmo);
|
||||
|
||||
kern_handle_t vmo;
|
||||
kern_handle_duplicate(mapping->m_vmo, &vmo);
|
||||
|
||||
*out_err = SUCCESS;
|
||||
*out_vmo = vmo;
|
||||
return KERN_OK;
|
||||
}
|
||||
52
lib/libfs/interface/seek.c
Normal file
52
lib/libfs/interface/seek.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "../file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
extern kern_status_t fs_msg_seek(
|
||||
xpc_context_t *xpc,
|
||||
const xpc_endpoint_t *sender,
|
||||
off_t rel_offset,
|
||||
int origin,
|
||||
int *out_err,
|
||||
off_t *out_new_pos,
|
||||
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;
|
||||
}
|
||||
|
||||
off_t new_offset = 0;
|
||||
|
||||
switch (origin) {
|
||||
case SEEK_SET:
|
||||
new_offset = rel_offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
new_offset = f->f_seek + rel_offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
new_offset = f->f_inode->i_size + rel_offset;
|
||||
break;
|
||||
default:
|
||||
*out_err = EINVAL;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
if (new_offset > f->f_inode->i_size) {
|
||||
*out_err = EINVAL;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
f->f_seek = new_offset;
|
||||
*out_err = SUCCESS;
|
||||
*out_new_pos = new_offset;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
29
lib/libfs/interface/write.c
Normal file
29
lib/libfs/interface/write.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
|
||||
extern kern_status_t fs_msg_write(
|
||||
xpc_context_t *xpc,
|
||||
const xpc_endpoint_t *sender,
|
||||
const xpc_buffer_t *data,
|
||||
int *out_err,
|
||||
size_t *out_nr_written,
|
||||
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_write(f, data, data->buf_len);
|
||||
size_t end = fs_file_get_cursor(f);
|
||||
|
||||
*out_err = fs_status_to_errno(status);
|
||||
*out_nr_written = end - start;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
22
lib/libfs/mapping.h
Normal file
22
lib/libfs/mapping.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef MAPPING_H_
|
||||
#define MAPPING_H_
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
struct fs_file;
|
||||
|
||||
enum file_mapping_type {
|
||||
FILE_MAPPING_PRIVATE,
|
||||
FILE_MAPPING_SHARED,
|
||||
};
|
||||
|
||||
struct file_mapping {
|
||||
enum file_mapping_type m_type;
|
||||
struct fs_file *m_file;
|
||||
kern_handle_t m_vmo;
|
||||
struct queue_entry m_entry;
|
||||
};
|
||||
|
||||
#endif
|
||||
138
lib/libfs/queue.c
Normal file
138
lib/libfs/queue.c
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "queue.h"
|
||||
|
||||
size_t queue_length(struct queue *q)
|
||||
{
|
||||
size_t i = 0;
|
||||
struct queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
i++;
|
||||
x = x->qe_next;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void queue_insert_before(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *before)
|
||||
{
|
||||
struct queue_entry *x = before->qe_prev;
|
||||
if (x) {
|
||||
x->qe_next = entry;
|
||||
} else {
|
||||
q->q_first = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
before->qe_prev = entry;
|
||||
entry->qe_next = before;
|
||||
}
|
||||
|
||||
void queue_insert_after(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *after)
|
||||
{
|
||||
struct queue_entry *x = after->qe_next;
|
||||
if (x) {
|
||||
x->qe_prev = entry;
|
||||
} else {
|
||||
q->q_last = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
after->qe_next = entry;
|
||||
entry->qe_prev = after;
|
||||
}
|
||||
|
||||
void queue_push_front(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (q->q_first) {
|
||||
q->q_first->qe_prev = entry;
|
||||
}
|
||||
|
||||
entry->qe_next = q->q_first;
|
||||
entry->qe_prev = NULL;
|
||||
|
||||
q->q_first = entry;
|
||||
|
||||
if (!q->q_last) {
|
||||
q->q_last = entry;
|
||||
}
|
||||
}
|
||||
|
||||
void queue_push_back(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (q->q_last) {
|
||||
q->q_last->qe_next = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = q->q_last;
|
||||
entry->qe_next = NULL;
|
||||
|
||||
q->q_last = entry;
|
||||
|
||||
if (!q->q_first) {
|
||||
q->q_first = entry;
|
||||
}
|
||||
}
|
||||
|
||||
struct queue_entry *queue_pop_front(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_first;
|
||||
if (x) {
|
||||
queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
struct queue_entry *queue_pop_back(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_last;
|
||||
if (x) {
|
||||
queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void queue_delete(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry == q->q_first) {
|
||||
q->q_first = q->q_first->qe_next;
|
||||
}
|
||||
|
||||
if (entry == q->q_last) {
|
||||
q->q_last = q->q_last->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_next) {
|
||||
entry->qe_next->qe_prev = entry->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_prev) {
|
||||
entry->qe_prev->qe_next = entry->qe_next;
|
||||
}
|
||||
|
||||
entry->qe_next = entry->qe_prev = NULL;
|
||||
}
|
||||
|
||||
void queue_delete_all(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
struct queue_entry *next = x->qe_next;
|
||||
x->qe_next = x->qe_prev = NULL;
|
||||
x = next;
|
||||
}
|
||||
|
||||
q->q_first = q->q_last = NULL;
|
||||
}
|
||||
100
lib/libfs/queue.h
Normal file
100
lib/libfs/queue.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef QUEUE_H_
|
||||
#define QUEUE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define QUEUE_CONTAINER(t, m, v) \
|
||||
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
|
||||
|
||||
#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL})
|
||||
#define QUEUE_ENTRY_INIT \
|
||||
((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL})
|
||||
|
||||
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
|
||||
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_first(queue_name)); \
|
||||
iter_name; \
|
||||
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_next(&((iter_name)->node_member))))
|
||||
|
||||
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
|
||||
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_last(queue_name)); \
|
||||
iter_name; \
|
||||
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_prev(&((iter_name)->node_member))))
|
||||
|
||||
struct queue_entry {
|
||||
struct queue_entry *qe_next;
|
||||
struct queue_entry *qe_prev;
|
||||
};
|
||||
|
||||
struct queue {
|
||||
struct queue_entry *q_first;
|
||||
struct queue_entry *q_last;
|
||||
};
|
||||
|
||||
static inline void queue_init(struct queue *q)
|
||||
{
|
||||
memset(q, 0x00, sizeof *q);
|
||||
}
|
||||
static inline bool queue_empty(struct queue *q)
|
||||
{
|
||||
return q->q_first == NULL;
|
||||
}
|
||||
|
||||
static inline struct queue_entry *queue_first(struct queue *q)
|
||||
{
|
||||
return q->q_first;
|
||||
}
|
||||
static inline struct queue_entry *queue_last(struct queue *q)
|
||||
{
|
||||
return q->q_last;
|
||||
}
|
||||
static inline struct queue_entry *queue_next(struct queue_entry *entry)
|
||||
{
|
||||
return entry->qe_next;
|
||||
}
|
||||
static inline struct queue_entry *queue_prev(struct queue_entry *entry)
|
||||
{
|
||||
return entry->qe_prev;
|
||||
}
|
||||
|
||||
extern size_t queue_length(struct queue *q);
|
||||
|
||||
extern void queue_insert_before(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *before);
|
||||
extern void queue_insert_after(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *after);
|
||||
|
||||
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
|
||||
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
|
||||
|
||||
extern struct queue_entry *queue_pop_front(struct queue *q);
|
||||
extern struct queue_entry *queue_pop_back(struct queue *q);
|
||||
|
||||
extern void queue_delete(struct queue *q, struct queue_entry *entry);
|
||||
extern void queue_delete_all(struct queue *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -160,15 +160,10 @@ static kern_status_t create_exec_regions(struct elf_image *image)
|
||||
image->e_total_size);
|
||||
kern_status_t status = KERN_OK;
|
||||
if (image->e_local_space != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_create(
|
||||
status = address_space_reserve(
|
||||
image->e_local_space,
|
||||
NULL,
|
||||
0,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
MAP_ADDRESS_ANY,
|
||||
image->e_total_size,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC
|
||||
| VM_PROT_USER,
|
||||
&image->e_local_exec,
|
||||
&image->e_local_base);
|
||||
}
|
||||
|
||||
@@ -177,22 +172,18 @@ static kern_status_t create_exec_regions(struct elf_image *image)
|
||||
}
|
||||
|
||||
if (image->e_remote_space != KERN_HANDLE_INVALID) {
|
||||
status = vm_region_create(
|
||||
status = address_space_reserve(
|
||||
image->e_remote_space,
|
||||
NULL,
|
||||
0,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
MAP_ADDRESS_ANY,
|
||||
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;
|
||||
address_space_release(
|
||||
image->e_local_space,
|
||||
image->e_local_base,
|
||||
image->e_total_size);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -200,6 +191,19 @@ static kern_status_t create_exec_regions(struct elf_image *image)
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static kern_status_t release_exec_regions(struct elf_image *image)
|
||||
{
|
||||
address_space_release(
|
||||
image->e_local_space,
|
||||
image->e_local_base,
|
||||
image->e_total_size);
|
||||
address_space_release(
|
||||
image->e_remote_space,
|
||||
image->e_remote_base,
|
||||
image->e_total_size);
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static enum launch_status map_executable(struct elf_image *image)
|
||||
{
|
||||
elf_phdr_t phdr;
|
||||
@@ -256,10 +260,10 @@ static enum launch_status map_executable(struct elf_image *image)
|
||||
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,
|
||||
if (image->e_local_space != KERN_HANDLE_INVALID) {
|
||||
status = address_space_map(
|
||||
image->e_local_space,
|
||||
image->e_local_base + phdr.p_vaddr,
|
||||
vmo,
|
||||
offset,
|
||||
phdr.p_memsz,
|
||||
@@ -271,10 +275,10 @@ static enum launch_status map_executable(struct elf_image *image)
|
||||
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,
|
||||
if (image->e_remote_space != KERN_HANDLE_INVALID) {
|
||||
status = address_space_map(
|
||||
image->e_remote_space,
|
||||
image->e_remote_base + phdr.p_vaddr,
|
||||
vmo,
|
||||
offset,
|
||||
phdr.p_memsz,
|
||||
@@ -330,6 +334,28 @@ static enum launch_status do_rela(struct elf_image *image, elf_rela_t *rela)
|
||||
ELF64_R_SYM(rela->r_info),
|
||||
rela->r_addend);
|
||||
break;
|
||||
case R_X86_64_GLOB_DAT:
|
||||
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(
|
||||
"GLOB_DAT: offset=%zx, symbol=%zu, addend=%zx",
|
||||
rela->r_offset,
|
||||
ELF64_R_SYM(rela->r_info),
|
||||
rela->r_addend);
|
||||
break;
|
||||
case R_X86_64_RELATIVE:
|
||||
*(uint64_t *)(image->e_local_base + rela->r_offset)
|
||||
= image->e_remote_base + rela->r_addend;
|
||||
kern_tracef(
|
||||
"RELATIVE: offset=%zx, addend=%zx",
|
||||
rela->r_offset,
|
||||
rela->r_addend);
|
||||
break;
|
||||
default:
|
||||
kern_trace("Unknown relocation type");
|
||||
return LAUNCH_ERR_UNSUPPORTED_EXECUTABLE;
|
||||
@@ -499,8 +525,6 @@ void elf_image_init(struct elf_image *out)
|
||||
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(
|
||||
@@ -547,6 +571,11 @@ enum launch_status elf_image_load(
|
||||
return status;
|
||||
}
|
||||
|
||||
status = release_exec_regions(image);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = relocate(image);
|
||||
if (status != LAUNCH_OK) {
|
||||
return status;
|
||||
@@ -557,9 +586,9 @@ enum launch_status elf_image_load(
|
||||
|
||||
void elf_image_cleanup(struct elf_image *image)
|
||||
{
|
||||
vm_region_unmap_relative(image->e_local_exec, 0, image->e_total_size);
|
||||
address_space_unmap(
|
||||
image->e_local_space,
|
||||
image->e_local_base,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -292,7 +292,6 @@ 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;
|
||||
|
||||
@@ -45,7 +45,9 @@ struct launch_ctx {
|
||||
struct launch_parameters {
|
||||
kern_handle_t p_parent_task;
|
||||
kern_handle_t p_local_address_space;
|
||||
kern_handle_t p_executable;
|
||||
|
||||
const char *p_exec_path;
|
||||
kern_handle_t p_exec_image;
|
||||
|
||||
const char *p_task_name;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ static kern_handle_t get_library(
|
||||
|
||||
static virt_addr_t write_bootstrap_data(
|
||||
struct stack_writer *stack,
|
||||
const char *interpreter,
|
||||
const struct launch_parameters *params)
|
||||
{
|
||||
virt_addr_t bs_remote;
|
||||
@@ -51,9 +52,15 @@ static virt_addr_t write_bootstrap_data(
|
||||
bs->bs_handles_count = params->p_handle_count;
|
||||
bs->bs_channels_count = params->p_channel_count;
|
||||
|
||||
if (interpreter) {
|
||||
/* two extra args: interpreter path and path to executable */
|
||||
bs->bs_argc += 2;
|
||||
}
|
||||
|
||||
const char **argv, **envp;
|
||||
|
||||
if (bs->bs_argc > 0) {
|
||||
int argc = bs->bs_argc;
|
||||
virt_addr_t remote_argv;
|
||||
argv = stack_writer_put(
|
||||
stack,
|
||||
@@ -73,13 +80,22 @@ static virt_addr_t write_bootstrap_data(
|
||||
bs->bs_envp = (const char **)remote_envp;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < params->p_argc; i++) {
|
||||
size_t i = 0, j = 0;
|
||||
if (interpreter) {
|
||||
virt_addr_t arg_ptr;
|
||||
stack_writer_put_string(stack, params->p_argv[i], &arg_ptr);
|
||||
stack_writer_put_string(stack, interpreter, &arg_ptr);
|
||||
argv[i++] = (const char *)arg_ptr;
|
||||
stack_writer_put_string(stack, params->p_exec_path, &arg_ptr);
|
||||
argv[i++] = (const char *)arg_ptr;
|
||||
}
|
||||
|
||||
for (; i < bs->bs_argc; i++, j++) {
|
||||
virt_addr_t arg_ptr;
|
||||
stack_writer_put_string(stack, params->p_argv[j], &arg_ptr);
|
||||
argv[i] = (const char *)arg_ptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < params->p_envc; i++) {
|
||||
for (size_t i = 0; i < bs->bs_envc; i++) {
|
||||
virt_addr_t env_ptr;
|
||||
stack_writer_put_string(stack, params->p_envp[i], &env_ptr);
|
||||
envp[i] = (const char *)env_ptr;
|
||||
@@ -118,16 +134,20 @@ enum launch_status launch_ctx_execute(
|
||||
return LAUNCH_ERR_TASK_CREATION_FAILED;
|
||||
}
|
||||
|
||||
char interp_path[4096];
|
||||
interp_path[0] = 0;
|
||||
|
||||
struct elf_image image;
|
||||
elf_image_init(&image);
|
||||
|
||||
enum launch_status status = elf_image_load(
|
||||
&image,
|
||||
params->p_executable,
|
||||
params->p_exec_image,
|
||||
params->p_local_address_space,
|
||||
remote_address_space);
|
||||
|
||||
if (status == LAUNCH_ERR_INTERPRETER_REQUIRED) {
|
||||
snprintf(interp_path, sizeof interp_path, "%s", image.e_interp);
|
||||
kern_handle_t interp = get_library(
|
||||
ctx,
|
||||
image.e_interp,
|
||||
@@ -157,17 +177,17 @@ enum launch_status launch_ctx_execute(
|
||||
}
|
||||
|
||||
virt_addr_t remote_stack_buf, local_stack_buf;
|
||||
kstatus = vm_region_map_relative(
|
||||
kstatus = address_space_map(
|
||||
remote_address_space,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
MAP_ADDRESS_ANY,
|
||||
stack_vmo,
|
||||
0,
|
||||
STACK_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&remote_stack_buf);
|
||||
kstatus = vm_region_map_relative(
|
||||
kstatus = address_space_map(
|
||||
params->p_local_address_space,
|
||||
VM_REGION_ANY_OFFSET,
|
||||
MAP_ADDRESS_ANY,
|
||||
stack_vmo,
|
||||
0,
|
||||
STACK_SIZE,
|
||||
@@ -187,7 +207,7 @@ enum launch_status launch_ctx_execute(
|
||||
&stack,
|
||||
local_stack_buf + STACK_SIZE,
|
||||
remote_stack_buf + STACK_SIZE);
|
||||
virt_addr_t bsdata = write_bootstrap_data(&stack, params);
|
||||
virt_addr_t bsdata = write_bootstrap_data(&stack, interp_path, params);
|
||||
|
||||
virt_addr_t ip = image.e_hdr.e_entry + image.e_remote_base;
|
||||
|
||||
@@ -208,7 +228,10 @@ enum launch_status launch_ctx_execute(
|
||||
|
||||
thread_start(thread);
|
||||
|
||||
kern_handle_close(thread);
|
||||
result->r_task = remote_task;
|
||||
result->r_thread = thread;
|
||||
result->r_address_space = remote_address_space;
|
||||
|
||||
elf_image_cleanup(&image);
|
||||
|
||||
return LAUNCH_OK;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <mango/status.h>
|
||||
#include <string.h>
|
||||
#include <xpc/buffer.h>
|
||||
#include <xpc/msg.h>
|
||||
|
||||
@@ -36,8 +37,7 @@ kern_status_t xpc_buffer_write(
|
||||
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)) {
|
||||
if (!(buf->buf_flags & XPC_BUFFER_F_OUT)) {
|
||||
return KERN_BAD_STATE;
|
||||
}
|
||||
|
||||
@@ -46,14 +46,16 @@ kern_status_t xpc_buffer_write(
|
||||
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;
|
||||
if (!(buf->buf_flags & XPC_BUFFER_F_REMOTE)) {
|
||||
memcpy(buf->buf_ptr, in, to_write);
|
||||
*nr_written = to_write;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
*nr_written = to_write;
|
||||
|
||||
return KERN_OK;
|
||||
return xpc_msg_write(
|
||||
buf->buf_origin,
|
||||
buf->buf_offset,
|
||||
in,
|
||||
to_write,
|
||||
nr_written);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
.buf_flags = XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE, \
|
||||
.buf_origin = (msg), \
|
||||
.buf_offset = (offset), \
|
||||
.buf_len = (size), \
|
||||
.buf_max = (size), \
|
||||
}
|
||||
#define XPC_LOCAL_BUFFER_OUT(ptr, size) \
|
||||
{ \
|
||||
.buf_flags = XPC_BUFFER_F_OUT, \
|
||||
.buf_ptr = (ptr), \
|
||||
.buf_max = (size), \
|
||||
}
|
||||
|
||||
struct xpc_msg;
|
||||
@@ -39,27 +45,18 @@ typedef enum xpc_buffer_flags {
|
||||
|
||||
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_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 */
|
||||
void *buf_ptr;
|
||||
/* valid for F_IN and F_OUT. 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
|
||||
|
||||
@@ -29,6 +29,7 @@ extern void xpc_msg_header_init(
|
||||
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_recv_nowait(kern_handle_t channel, xpc_msg_t *out);
|
||||
extern kern_status_t xpc_msg_read(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
@@ -38,7 +39,8 @@ extern kern_status_t xpc_msg_write(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
const void *p,
|
||||
size_t count);
|
||||
size_t count,
|
||||
size_t *nr_written);
|
||||
|
||||
extern kern_status_t xpc_msg_reply(
|
||||
const xpc_msg_t *msg,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
.s_flags = XPC_STRING_F_OUT | XPC_STRING_F_REMOTE, \
|
||||
.s_origin = (msg), \
|
||||
.s_offset = (offset), \
|
||||
.s_len = (size), \
|
||||
.s_max = (size), \
|
||||
}
|
||||
|
||||
struct xpc_msg;
|
||||
@@ -41,25 +41,19 @@ typedef enum xpc_string_flags {
|
||||
|
||||
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;
|
||||
};
|
||||
/* 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;
|
||||
};
|
||||
};
|
||||
/* valid for F_IN and F_OUT. 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. */
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <mango/msg.h>
|
||||
#include <mango/object.h>
|
||||
#include <mango/signal.h>
|
||||
#include <string.h>
|
||||
#include <xpc/msg.h>
|
||||
|
||||
@@ -20,8 +22,25 @@ 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)
|
||||
static kern_status_t __msg_recv(
|
||||
kern_handle_t channel,
|
||||
xpc_msg_t *out,
|
||||
bool nowait)
|
||||
{
|
||||
kern_status_t status = KERN_OK;
|
||||
|
||||
if (!nowait) {
|
||||
kern_wait_item_t wait = {
|
||||
.w_handle = channel,
|
||||
.w_waitfor = CHANNEL_SIGNAL_MSG_RECEIVED,
|
||||
};
|
||||
|
||||
status = kern_object_wait(&wait, 1);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
kern_iovec_t iov = IOVEC(&out->msg_header, sizeof out->msg_header);
|
||||
kern_msg_t msg = {
|
||||
.msg_data = &iov,
|
||||
@@ -29,7 +48,8 @@ kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out)
|
||||
.msg_handles = out->msg_handles,
|
||||
.msg_handles_count = KERN_MSG_MAX_HANDLES,
|
||||
};
|
||||
kern_status_t status = msg_recv(channel, &msg);
|
||||
|
||||
status = msg_recv(channel, &msg);
|
||||
if (status != KERN_OK) {
|
||||
return status;
|
||||
}
|
||||
@@ -46,6 +66,25 @@ kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out)
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out)
|
||||
{
|
||||
kern_status_t status = KERN_OK;
|
||||
while (1) {
|
||||
status = __msg_recv(channel, out, false);
|
||||
|
||||
if (status != KERN_NO_ENTRY) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_recv_nowait(kern_handle_t channel, xpc_msg_t *out)
|
||||
{
|
||||
return __msg_recv(channel, out, true);
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_read(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
@@ -67,17 +106,17 @@ kern_status_t xpc_msg_write(
|
||||
const xpc_msg_t *msg,
|
||||
size_t offset,
|
||||
const void *p,
|
||||
size_t count)
|
||||
size_t count,
|
||||
size_t *nr_written)
|
||||
{
|
||||
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);
|
||||
nr_written);
|
||||
}
|
||||
|
||||
kern_status_t xpc_msg_reply(
|
||||
|
||||
@@ -47,14 +47,10 @@ xpc_status_t xpc_string_write(
|
||||
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;
|
||||
return xpc_msg_write(
|
||||
s->s_origin,
|
||||
s->s_offset,
|
||||
in,
|
||||
to_write,
|
||||
nr_written);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
int main(void)
|
||||
#include <mango/log.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
kern_logf("systemd");
|
||||
kern_logf("args:");
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
kern_logf("[%d]: %s", i, argv[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <mango/log.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
const char *s
|
||||
@@ -152,6 +154,14 @@ const char *s
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const char *s = "Hello, world!";
|
||||
return strlen(s);
|
||||
kern_logf("Test 1");
|
||||
kern_logf("Test 2");
|
||||
kern_logf("Test 3");
|
||||
|
||||
const char *text = "Hello, world!";
|
||||
char s2[32];
|
||||
snprintf(s2, sizeof s2, "%s", text);
|
||||
size_t v = strlen(s);
|
||||
kern_logf("%s, %zu", s2, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ set_property(SOURCE ${arch_sources} PROPERTY LANGUAGE C)
|
||||
add_executable(bootstrap ${c_sources} ${arch_sources})
|
||||
|
||||
target_link_libraries(bootstrap
|
||||
libmango libc-core libc-malloc libfs-static liblaunch libxpc-static
|
||||
libmango libc-core libc-malloc libc-pthread libfs-static liblaunch libxpc-static
|
||||
interface::fs)
|
||||
|
||||
target_compile_options(bootstrap PRIVATE
|
||||
-fno-stack-protector -nostdlib -ffreestanding)
|
||||
-fno-stack-protector -nostdlib -ffreestanding -fno-PIC)
|
||||
target_link_options(bootstrap PRIVATE
|
||||
-static -nostdlib -ffreestanding)
|
||||
#-T ${CMAKE_CURRENT_SOURCE_DIR}/arch/${TARGET_ARCH}/layout.ld)
|
||||
-static -nostdlib -ffreestanding)
|
||||
|
||||
set_target_properties(bootstrap PROPERTIES POSITIION_INDEPENDENT_CODE FALSE)
|
||||
|
||||
@@ -7,16 +7,18 @@
|
||||
#include <fs/context.h>
|
||||
#include <heap/heap.h>
|
||||
#include <launch.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/msg.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/types.h>
|
||||
#include <pthread.h>
|
||||
#include <rosetta/bootstrap.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define INIT_PATH "/usr/bin/test"
|
||||
#define INIT_PATH "/usr/bin/systemd"
|
||||
|
||||
static enum launch_status resolve_dependency(
|
||||
struct launch_ctx *ctx,
|
||||
@@ -29,14 +31,12 @@ static enum launch_status resolve_dependency(
|
||||
name++;
|
||||
}
|
||||
|
||||
snprintf(s, sizeof s, "searching for library %s", name);
|
||||
kern_log(s);
|
||||
kern_tracef("searching for library %s", name);
|
||||
|
||||
struct tar *fs = arg;
|
||||
struct tar_file file = {0};
|
||||
if (tar_open(fs, name, &file) != 0) {
|
||||
snprintf(s, sizeof s, "cannot find library %s", name);
|
||||
kern_log(s);
|
||||
kern_tracef("cannot find library %s", name);
|
||||
return LAUNCH_ERR_CANNOT_RESOLVE_DEPENDENCY;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,8 @@ int main(
|
||||
};
|
||||
|
||||
struct launch_parameters params = {
|
||||
.p_executable = image,
|
||||
.p_exec_image = image,
|
||||
.p_exec_path = INIT_PATH,
|
||||
.p_parent_task = task,
|
||||
.p_task_name = "init",
|
||||
.p_local_address_space = address_space,
|
||||
@@ -169,6 +170,10 @@ int main(
|
||||
return -1;
|
||||
}
|
||||
|
||||
kern_handle_close(result.r_task);
|
||||
kern_handle_close(result.r_thread);
|
||||
kern_handle_close(result.r_address_space);
|
||||
|
||||
heap_t heap = HEAP_INIT;
|
||||
|
||||
struct fs_allocator fs_allocator = {
|
||||
@@ -185,17 +190,20 @@ int main(
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs_context_set_channel(fs, channel);
|
||||
enum fs_status fs_status = fs_context_mount_filesystem(
|
||||
fs,
|
||||
tar_mount,
|
||||
(void *)bsp_base,
|
||||
0);
|
||||
if (fs_status != FS_SUCCESS) {
|
||||
kern_logf("cannot mount filesustem (%d)", fs_status);
|
||||
kern_logf("cannot mount filesystem (%d)", fs_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
fs_context_handle_request(fs);
|
||||
#if 0
|
||||
xpc_msg_t msg;
|
||||
kern_status_t status = xpc_msg_recv(channel, &msg);
|
||||
if (status != KERN_OK) {
|
||||
@@ -219,6 +227,7 @@ int main(
|
||||
kern_logf("message reply error %d", status);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -74,8 +74,10 @@ int tar_open(struct tar *tar, const char *path, struct tar_file *out)
|
||||
}
|
||||
|
||||
s += header.size;
|
||||
s += ((sizeof *bin_header)
|
||||
- ((uintptr_t)s % (sizeof *bin_header)));
|
||||
if ((uintptr_t)s % (sizeof *bin_header)) {
|
||||
s += ((sizeof *bin_header)
|
||||
- ((uintptr_t)s % (sizeof *bin_header)));
|
||||
}
|
||||
|
||||
bin_header = (struct tar_bin_header *)s;
|
||||
}
|
||||
@@ -189,7 +191,7 @@ static enum fs_status file_read(
|
||||
size_t w;
|
||||
xpc_buffer_write(buf, src, count, &w);
|
||||
|
||||
offset += count;
|
||||
offset += w;
|
||||
*seek = offset;
|
||||
|
||||
return FS_SUCCESS;
|
||||
@@ -382,8 +384,10 @@ static enum fs_status build_dentry_tree(
|
||||
}
|
||||
|
||||
s += header.size;
|
||||
s += ((sizeof *bin_header)
|
||||
- ((uintptr_t)s % (sizeof *bin_header)));
|
||||
if ((uintptr_t)s % (sizeof *bin_header)) {
|
||||
s += ((sizeof *bin_header)
|
||||
- ((uintptr_t)s % (sizeof *bin_header)));
|
||||
}
|
||||
|
||||
bin_header = (struct tar_bin_header *)s;
|
||||
}
|
||||
|
||||
47
sys/ld/arch/x86_64/resolve.S
Normal file
47
sys/ld/arch/x86_64/resolve.S
Normal file
@@ -0,0 +1,47 @@
|
||||
.code64
|
||||
|
||||
.global _dl_runtime_resolve
|
||||
.type _dl_runtime_resolve, @function
|
||||
|
||||
.extern dl_runtime_resolve
|
||||
.type dl_runtime_resolve, @function
|
||||
|
||||
_dl_runtime_resolve:
|
||||
// pop %rdi
|
||||
// pop %rsi
|
||||
pop %rax
|
||||
pop %r11
|
||||
|
||||
push %rsp
|
||||
push %rbp
|
||||
push %rdi
|
||||
push %rsi
|
||||
push %rdx
|
||||
push %rcx
|
||||
push %r8
|
||||
push %r9
|
||||
push %rbx
|
||||
push %r12
|
||||
push %r13
|
||||
push %r14
|
||||
push %r15
|
||||
|
||||
mov %rax, %rdi
|
||||
mov %r11, %rsi
|
||||
call dl_runtime_resolve
|
||||
|
||||
pop %r15
|
||||
pop %r14
|
||||
pop %r13
|
||||
pop %r12
|
||||
pop %rbx
|
||||
pop %r9
|
||||
pop %r8
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
pop %rbp
|
||||
pop %rsp
|
||||
|
||||
jmp *%rax
|
||||
691
sys/ld/btree.c
Normal file
691
sys/ld/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
sys/ld/btree.h
Normal file
475
sys/ld/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 BTREE_H_
|
||||
#define 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
|
||||
800
sys/ld/elf.c
Normal file
800
sys/ld/elf.c
Normal file
@@ -0,0 +1,800 @@
|
||||
#include "elf.h"
|
||||
|
||||
#include "resolve.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <mango/config.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.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
|
||||
|
||||
const char *elf_image_status_to_string(enum elf_image_status status)
|
||||
{
|
||||
#define ENUM_STR(s) \
|
||||
case s: \
|
||||
return #s
|
||||
switch (status) {
|
||||
ENUM_STR(ELF_IMAGE_NONE);
|
||||
ENUM_STR(ELF_IMAGE_OPEN);
|
||||
ENUM_STR(ELF_IMAGE_PARSED);
|
||||
ENUM_STR(ELF_IMAGE_LOADED);
|
||||
ENUM_STR(ELF_IMAGE_LINKED);
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
#undef ENUM_STR
|
||||
}
|
||||
|
||||
static bool elf_validate_ehdr(elf_ehdr_t *hdr)
|
||||
{
|
||||
if (hdr->e_ident[EI_MAG0] != ELF_MAG0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_MAG1] != ELF_MAG1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_MAG2] != ELF_MAG2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_MAG3] != ELF_MAG3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_machine != EM_X86_64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->e_ident[EI_VERSION] != EV_CURRENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int map_image(struct elf_image *image)
|
||||
{
|
||||
elf_phdr_t phdr;
|
||||
size_t r = 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);
|
||||
lseek(image->e_fd, phdr_offset, SEEK_SET);
|
||||
int r = read(image->e_fd, &phdr, sizeof phdr);
|
||||
|
||||
if (r < 0) {
|
||||
return -r;
|
||||
}
|
||||
|
||||
if (r != sizeof phdr) {
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int prot = 0;
|
||||
size_t offset = phdr.p_offset & ~PAGE_MASK;
|
||||
|
||||
phdr.p_flags &PF_R && (prot |= PROT_READ);
|
||||
phdr.p_flags &PF_W && (prot |= PROT_WRITE);
|
||||
phdr.p_flags &PF_X && (prot |= PROT_EXEC);
|
||||
|
||||
virt_addr_t vaddr = phdr.p_vaddr;
|
||||
virt_addr_t vlimit = phdr.p_vaddr + phdr.p_memsz;
|
||||
if (vaddr & PAGE_MASK) {
|
||||
vaddr &= ~PAGE_MASK;
|
||||
}
|
||||
|
||||
if (vlimit & PAGE_MASK) {
|
||||
vlimit &= ~PAGE_MASK;
|
||||
vlimit += PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (image->e_hdr.e_type == ET_DYN) {
|
||||
vaddr += image->e_base;
|
||||
vlimit += image->e_base;
|
||||
}
|
||||
|
||||
int fd = image->e_fd;
|
||||
int flags = MAP_SHARED | MAP_EXECUTABLE | MAP_FIXED;
|
||||
|
||||
if (phdr.p_flags & PF_W) {
|
||||
fd = -1;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
void *p
|
||||
= mmap((void *)vaddr,
|
||||
vlimit - vaddr,
|
||||
prot,
|
||||
flags,
|
||||
fd,
|
||||
offset);
|
||||
if (p == MAP_FAILED) {
|
||||
return EIO;
|
||||
}
|
||||
|
||||
kern_tracef(
|
||||
"mapped PHDR %u [%zx-%zx] at %p",
|
||||
i,
|
||||
phdr.p_vaddr,
|
||||
phdr.p_vaddr + phdr.p_memsz,
|
||||
p);
|
||||
|
||||
if (phdr.p_flags & PF_W) {
|
||||
lseek(image->e_fd, phdr.p_offset, SEEK_SET);
|
||||
void *dst = (void *)image->e_base + phdr.p_vaddr;
|
||||
r = read(image->e_fd, dst, phdr.p_filesz);
|
||||
if (r < 0) {
|
||||
return -r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int parse_phdr(struct elf_image *image)
|
||||
{
|
||||
elf_phdr_t phdr;
|
||||
size_t r = 0;
|
||||
image->e_length = 0;
|
||||
image->e_data_length = 0;
|
||||
off_t vaddr, vlimit;
|
||||
|
||||
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);
|
||||
lseek(image->e_fd, phdr_offset, SEEK_SET);
|
||||
int r = read(image->e_fd, &phdr, sizeof phdr);
|
||||
|
||||
if (r < 0) {
|
||||
return -r;
|
||||
}
|
||||
|
||||
if (r != sizeof phdr) {
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
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_length = MAX(image->e_length, vlimit);
|
||||
break;
|
||||
#if 0
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (phdr.p_flags & PF_W) {
|
||||
image->e_data_length
|
||||
= MAX(image->e_data_length, vlimit - vaddr);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#if 1
|
||||
static elf_sym_t *get_dynsym(struct elf_image *image, size_t index)
|
||||
{
|
||||
elf_sym_t *sym = (elf_sym_t *)(image->e_base + image->e_dynsym
|
||||
+ (index * image->e_dynsym_entsize));
|
||||
|
||||
if (!sym->st_value) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
static void resolve_symbol(unsigned int slot)
|
||||
{
|
||||
kern_tracef("request for symbol %u", slot);
|
||||
}
|
||||
|
||||
static int do_rela(struct elf_image *image, elf_rela_t *rela, bool lazy)
|
||||
{
|
||||
kern_tracef(
|
||||
"do_rela(%p, %d, %d, %d)",
|
||||
image,
|
||||
rela->r_info,
|
||||
rela->r_addend,
|
||||
rela->r_offset);
|
||||
int type = ELF64_R_TYPE(rela->r_info);
|
||||
elf_sym_t *sym = NULL;
|
||||
|
||||
switch (type) {
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
*(uint64_t *)(image->e_base + rela->r_offset) += image->e_base;
|
||||
kern_tracef(
|
||||
"JUMP_SLOT: offset=%zx, symbol=%zu, addend=%zx",
|
||||
rela->r_offset,
|
||||
ELF64_R_SYM(rela->r_info),
|
||||
rela->r_addend);
|
||||
break;
|
||||
case R_X86_64_RELATIVE:
|
||||
*(uint64_t *)(image->e_base + rela->r_offset)
|
||||
= image->e_base + rela->r_addend;
|
||||
kern_tracef(
|
||||
"RELATIVE: offset=%zx, addend=%zx",
|
||||
rela->r_offset,
|
||||
rela->r_addend);
|
||||
break;
|
||||
default:
|
||||
kern_log("Unknown relocation type");
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int relocate_pltrel(
|
||||
struct elf_image *image,
|
||||
off_t offset,
|
||||
size_t size,
|
||||
size_t entsize)
|
||||
{
|
||||
size_t entries = size / entsize;
|
||||
elf_rela_t *rela = (elf_rela_t *)(image->e_base + offset);
|
||||
int status = SUCCESS;
|
||||
|
||||
for (size_t i = 0; i < entries; i++) {
|
||||
status = do_rela(image, rela, true);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
rela = (elf_rela_t *)((char *)rela + entsize);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int relocate_rela(
|
||||
struct elf_image *image,
|
||||
off_t offset,
|
||||
size_t size,
|
||||
size_t entsize)
|
||||
{
|
||||
size_t entries = size / entsize;
|
||||
elf_rela_t *rela = (elf_rela_t *)(image->e_base + offset);
|
||||
int status = SUCCESS;
|
||||
|
||||
for (size_t i = 0; i < entries; i++) {
|
||||
status = do_rela(image, rela, false);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
rela = (elf_rela_t *)((char *)rela + entsize);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int relocate_rel(
|
||||
struct elf_image *image,
|
||||
off_t offset,
|
||||
size_t size,
|
||||
size_t entsize)
|
||||
{
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
static int do_rel(
|
||||
struct elf_image *image,
|
||||
off_t offset,
|
||||
size_t size,
|
||||
size_t entsize)
|
||||
|
||||
{
|
||||
kern_tracef("do_rel (unsupported)");
|
||||
return ENOEXEC;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int load_dependency(struct elf_image *image, const char *name)
|
||||
{
|
||||
kern_tracef("required library: %s", name);
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
static int parse_dynamic(struct elf_image *image)
|
||||
{
|
||||
if (image->e_dynamic.p_type != PT_DYNAMIC) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
image->e_dyn = (elf_dyn_t *)(image->e_base + image->e_dynamic.p_vaddr);
|
||||
|
||||
int status = SUCCESS;
|
||||
|
||||
size_t nr_dyn = image->e_dynamic.p_filesz / sizeof *image->e_dyn;
|
||||
for (size_t i = 0; i < nr_dyn; i++) {
|
||||
if (image->e_dyn[i].d_tag == DT_NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (image->e_dyn[i].d_tag) {
|
||||
case DT_NEEDED:
|
||||
image->e_nr_links++;
|
||||
break;
|
||||
case DT_STRTAB:
|
||||
image->e_strtab = image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
image->e_dynsym = image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_SYMENT:
|
||||
image->e_dynsym_entsize = image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_PLTGOT:
|
||||
image->e_got_plt = image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_HASH:
|
||||
image->e_hash_type = ELF_HASH_STANDARD;
|
||||
image->e_hash_table = image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_GNU_HASH:
|
||||
image->e_hash_type = ELF_HASH_GNU;
|
||||
image->e_hash_table = image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_REL:
|
||||
image->e_rel_offset[ELF_RT_REL]
|
||||
= image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_RELSZ:
|
||||
image->e_rel_size[ELF_RT_REL]
|
||||
= image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELENT:
|
||||
image->e_rel_entsize[ELF_RT_REL]
|
||||
= image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELA:
|
||||
image->e_rel_offset[ELF_RT_RELA]
|
||||
= image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
image->e_rel_size[ELF_RT_RELA]
|
||||
= image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
image->e_rel_entsize[ELF_RT_RELA]
|
||||
= image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_PLTREL:
|
||||
image->e_pltrel_type = image->e_dyn[i].d_un.d_val;
|
||||
switch (image->e_pltrel_type) {
|
||||
case DT_REL:
|
||||
image->e_rel_entsize[ELF_RT_PLTREL] = 0;
|
||||
break;
|
||||
case DT_RELA:
|
||||
image->e_rel_entsize[ELF_RT_PLTREL]
|
||||
= sizeof(elf_rela_t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DT_JMPREL:
|
||||
image->e_rel_offset[ELF_RT_PLTREL]
|
||||
= image->e_dyn[i].d_un.d_ptr;
|
||||
break;
|
||||
case DT_PLTRELSZ:
|
||||
image->e_rel_size[ELF_RT_PLTREL]
|
||||
= image->e_dyn[i].d_un.d_val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image->e_dyn_count++;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int reserve_exec_region(struct elf_image *image)
|
||||
{
|
||||
void *base
|
||||
= mmap(NULL,
|
||||
image->e_length,
|
||||
PROT_NONE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE,
|
||||
-1,
|
||||
0);
|
||||
if (base == MAP_FAILED) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
image->e_base = (virt_addr_t)base;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
static int create_image_with_name(const char *name, struct elf_image **out)
|
||||
{
|
||||
struct elf_image *elf = malloc(sizeof *elf);
|
||||
if (!elf) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
memset(elf, 0x0, sizeof *elf);
|
||||
|
||||
snprintf(elf->e_leaf.l_name, sizeof elf->e_leaf.l_name, "%s", name);
|
||||
|
||||
kern_config_get(
|
||||
KERN_CFG_PAGE_SIZE,
|
||||
&elf->e_page_size,
|
||||
sizeof elf->e_page_size);
|
||||
|
||||
*out = elf;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int elf_image_open(const char *path, struct elf_image **out)
|
||||
{
|
||||
struct elf_image *elf = malloc(sizeof *elf);
|
||||
if (!elf) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
memset(elf, 0x0, sizeof *elf);
|
||||
|
||||
kern_config_get(
|
||||
KERN_CFG_PAGE_SIZE,
|
||||
&elf->e_page_size,
|
||||
sizeof elf->e_page_size);
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
elf_image_close(elf);
|
||||
return -fd;
|
||||
}
|
||||
|
||||
elf->e_status = ELF_IMAGE_OPEN;
|
||||
elf->e_fd = fd;
|
||||
|
||||
*out = elf;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int elf_image_parse(struct elf_image *img)
|
||||
{
|
||||
if (img->e_status != ELF_IMAGE_OPEN) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int e = read(img->e_fd, &img->e_hdr, sizeof img->e_hdr);
|
||||
if (e < 0) {
|
||||
return -e;
|
||||
}
|
||||
|
||||
if (e != sizeof img->e_hdr) {
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
if (!elf_validate_ehdr(&img->e_hdr)) {
|
||||
return ENOEXEC;
|
||||
}
|
||||
|
||||
e = parse_phdr(img);
|
||||
if (e != SUCCESS) {
|
||||
return e;
|
||||
}
|
||||
|
||||
img->e_status = ELF_IMAGE_PARSED;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int elf_image_load(struct elf_image *img)
|
||||
{
|
||||
if (img->e_status != ELF_IMAGE_PARSED) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int e = reserve_exec_region(img);
|
||||
if (e != SUCCESS) {
|
||||
return e;
|
||||
}
|
||||
|
||||
e = map_image(img);
|
||||
if (e != SUCCESS) {
|
||||
return e;
|
||||
}
|
||||
|
||||
e = parse_dynamic(img);
|
||||
if (e != SUCCESS) {
|
||||
return e;
|
||||
}
|
||||
|
||||
img->e_status = ELF_IMAGE_LOADED;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int elf_image_link(struct elf_image *img)
|
||||
{
|
||||
if (img->e_status != ELF_IMAGE_LOADED) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int status = SUCCESS;
|
||||
|
||||
if (img->e_rel_offset[ELF_RT_REL]) {
|
||||
status = relocate_rel(
|
||||
img,
|
||||
img->e_rel_offset[ELF_RT_REL],
|
||||
img->e_rel_size[ELF_RT_REL],
|
||||
img->e_rel_entsize[ELF_RT_REL]);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (img->e_rel_offset[ELF_RT_RELA]) {
|
||||
status = relocate_rela(
|
||||
img,
|
||||
img->e_rel_offset[ELF_RT_RELA],
|
||||
img->e_rel_size[ELF_RT_RELA],
|
||||
img->e_rel_entsize[ELF_RT_RELA]);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
if (img->e_rel_offset[ELF_RT_PLTREL]) {
|
||||
status = relocate_pltrel(
|
||||
img,
|
||||
img->e_rel_offset[ELF_RT_PLTREL],
|
||||
img->e_rel_size[ELF_RT_PLTREL],
|
||||
img->e_rel_entsize[ELF_RT_PLTREL]);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
*(uintptr_t *)(img->e_base + img->e_got_plt + 16)
|
||||
= (uintptr_t)_dl_runtime_resolve;
|
||||
*(uintptr_t *)(img->e_base + img->e_got_plt + 8) = (uintptr_t)img;
|
||||
|
||||
img->e_entry = (virt_addr_t)img->e_base + img->e_hdr.e_entry;
|
||||
img->e_status = ELF_IMAGE_LINKED;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
extern int elf_image_collect_dependencies(
|
||||
struct elf_image *img,
|
||||
struct image_list *dest)
|
||||
{
|
||||
if (!img->e_nr_links || img->e_links) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int nr_added = 0;
|
||||
img->e_links = calloc(img->e_nr_links, sizeof(struct elf_image *));
|
||||
|
||||
for (size_t i = 0; i < img->e_dyn_count; i++) {
|
||||
if (img->e_dyn[i].d_tag != DT_NEEDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *name = (const char *)img->e_base + img->e_strtab
|
||||
+ img->e_dyn[i].d_un.d_val;
|
||||
|
||||
if (image_list_get(dest, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct elf_image *dep = NULL;
|
||||
int status = create_image_with_name(name, &dep);
|
||||
if (status != SUCCESS) {
|
||||
return -status;
|
||||
}
|
||||
|
||||
image_list_put(dest, &dep->e_leaf);
|
||||
img->e_links[nr_added] = dep;
|
||||
nr_added++;
|
||||
}
|
||||
|
||||
return nr_added;
|
||||
}
|
||||
|
||||
void elf_image_close(struct elf_image *image)
|
||||
{
|
||||
if (image->e_fd) {
|
||||
close(image->e_fd);
|
||||
}
|
||||
|
||||
free(image);
|
||||
}
|
||||
|
||||
static uint32_t std_hash(const char *name)
|
||||
{
|
||||
uint32_t h = 0, g;
|
||||
for (; *name; name++) {
|
||||
h = (h << 4) + *name;
|
||||
if ((g = h & 0xf0000000)) {
|
||||
h ^= g >> 24;
|
||||
}
|
||||
h &= ~g;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static uint32_t gnu_hash(const char *name)
|
||||
{
|
||||
uint32_t h = 5381;
|
||||
|
||||
for (; *name; name++) {
|
||||
h = (h << 5) + h + *name;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static virt_addr_t find_symbol_stdhash(
|
||||
struct elf_image *img,
|
||||
const char *name,
|
||||
uint32_t hash)
|
||||
{
|
||||
const uint32_t *hashtab
|
||||
= (void *)((virt_addr_t)img->e_base + img->e_hash_table);
|
||||
|
||||
const char *strtab = (void *)((virt_addr_t)img->e_base + img->e_strtab);
|
||||
|
||||
const elf_sym_t *symtab
|
||||
= (void *)((virt_addr_t)img->e_base + img->e_dynsym);
|
||||
|
||||
const uint32_t nbucket = hashtab[0];
|
||||
const uint32_t nchain = hashtab[1];
|
||||
const uint32_t *bucket = &hashtab[2];
|
||||
const uint32_t *chain = &bucket[nbucket];
|
||||
|
||||
for (uint32_t i = bucket[hash % nbucket]; i; i = chain[i]) {
|
||||
if (strcmp(name, strtab + symtab[i].st_name) == 0) {
|
||||
return img->e_base + symtab[i].st_value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static virt_addr_t find_symbol_gnuhash(
|
||||
struct elf_image *img,
|
||||
const char *name,
|
||||
uint32_t hash)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static virt_addr_t find_symbol_slow(struct elf_image *img, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static virt_addr_t find_symbol(
|
||||
struct elf_image *img,
|
||||
const char *name,
|
||||
uint32_t std_hash,
|
||||
uint32_t gnu_hash)
|
||||
{
|
||||
switch (img->e_hash_type) {
|
||||
case ELF_HASH_STANDARD:
|
||||
return find_symbol_stdhash(img, name, std_hash);
|
||||
case ELF_HASH_GNU:
|
||||
return find_symbol_gnuhash(img, name, gnu_hash);
|
||||
default:
|
||||
return find_symbol_slow(img, name);
|
||||
}
|
||||
}
|
||||
|
||||
virt_addr_t elf_image_find_symbol(struct elf_image *img, const char *name)
|
||||
{
|
||||
uint32_t std_hash_val = std_hash(name);
|
||||
uint32_t gnu_hash_val = gnu_hash(name);
|
||||
return find_symbol(img, name, std_hash_val, gnu_hash_val);
|
||||
}
|
||||
|
||||
virt_addr_t elf_image_find_linked_symbol(
|
||||
struct elf_image *img,
|
||||
const char *name)
|
||||
{
|
||||
uint32_t std_hash_val = std_hash(name);
|
||||
uint32_t gnu_hash_val = gnu_hash(name);
|
||||
|
||||
virt_addr_t sym = 0;
|
||||
for (size_t i = 0; i < img->e_nr_links; i++) {
|
||||
sym = find_symbol(
|
||||
img->e_links[i],
|
||||
name,
|
||||
std_hash_val,
|
||||
gnu_hash_val);
|
||||
if (sym) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sym;
|
||||
}
|
||||
365
sys/ld/elf.h
Normal file
365
sys/ld/elf.h
Normal file
@@ -0,0 +1,365 @@
|
||||
#ifndef LD_ELF_H_
|
||||
#define LD_ELF_H_
|
||||
|
||||
#include "image-list.h"
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum elf_image_status {
|
||||
ELF_IMAGE_NONE = 0,
|
||||
ELF_IMAGE_OPEN,
|
||||
ELF_IMAGE_PARSED,
|
||||
ELF_IMAGE_LOADED,
|
||||
ELF_IMAGE_LINKED,
|
||||
};
|
||||
|
||||
enum elf_hash_type {
|
||||
ELF_HASH_NONE = 0,
|
||||
ELF_HASH_STANDARD,
|
||||
ELF_HASH_GNU,
|
||||
};
|
||||
|
||||
enum elf_relocation_type {
|
||||
ELF_RT_NONE = 0,
|
||||
ELF_RT_REL,
|
||||
ELF_RT_RELA,
|
||||
ELF_RT_PLTREL,
|
||||
ELF_RT_COUNT,
|
||||
};
|
||||
|
||||
#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 {
|
||||
enum elf_image_status e_status;
|
||||
struct image_list_leaf e_leaf;
|
||||
|
||||
int e_fd;
|
||||
size_t e_page_size;
|
||||
elf_ehdr_t e_hdr;
|
||||
virt_addr_t e_base;
|
||||
size_t e_length, e_data_length;
|
||||
virt_addr_t e_entry;
|
||||
|
||||
virt_addr_t e_strtab;
|
||||
|
||||
enum elf_hash_type e_hash_type;
|
||||
virt_addr_t e_hash_table;
|
||||
|
||||
virt_addr_t e_got_plt;
|
||||
virt_addr_t e_dynsym;
|
||||
size_t e_dynsym_entsize;
|
||||
|
||||
elf_phdr_t e_dynamic;
|
||||
elf_dyn_t *e_dyn;
|
||||
size_t e_dyn_count;
|
||||
|
||||
int e_pltrel_type;
|
||||
off_t e_rel_offset[ELF_RT_COUNT];
|
||||
size_t e_rel_size[ELF_RT_COUNT];
|
||||
size_t e_rel_entsize[ELF_RT_COUNT];
|
||||
|
||||
struct elf_image **e_links;
|
||||
size_t e_nr_links;
|
||||
};
|
||||
|
||||
extern const char *elf_image_status_to_string(enum elf_image_status status);
|
||||
|
||||
extern int elf_image_open(const char *path, struct elf_image **out);
|
||||
extern int elf_image_parse(struct elf_image *img);
|
||||
extern int elf_image_load(struct elf_image *img);
|
||||
extern int elf_image_collect_dependencies(
|
||||
struct elf_image *img,
|
||||
struct image_list *dest);
|
||||
extern int elf_image_link(struct elf_image *img);
|
||||
extern void elf_image_close(struct elf_image *img);
|
||||
|
||||
extern virt_addr_t elf_image_find_symbol(
|
||||
struct elf_image *img,
|
||||
const char *name);
|
||||
extern virt_addr_t elf_image_find_linked_symbol(
|
||||
struct elf_image *img,
|
||||
const char *name);
|
||||
|
||||
#endif
|
||||
16
sys/ld/hash.c
Normal file
16
sys/ld/hash.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define BASIS 0xcbf29ce484222325
|
||||
#define PRIME 0x100000001b3
|
||||
|
||||
uint64_t hash_string(const char *s)
|
||||
{
|
||||
uint64_t result = BASIS;
|
||||
|
||||
for (unsigned int i = 0; s[i]; i++) {
|
||||
result ^= s[i];
|
||||
result *= PRIME;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
8
sys/ld/hash.h
Normal file
8
sys/ld/hash.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef HASH_H_
|
||||
#define HASH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern uint64_t hash_string(const char *s);
|
||||
|
||||
#endif
|
||||
211
sys/ld/image-list.c
Normal file
211
sys/ld/image-list.c
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "image-list.h"
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
#include <mango/log.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static BTREE_DEFINE_SIMPLE_GET(
|
||||
struct image_list_entry,
|
||||
uint64_t,
|
||||
e_node,
|
||||
e_hash,
|
||||
get_entry);
|
||||
static BTREE_DEFINE_SIMPLE_INSERT(
|
||||
struct image_list_entry,
|
||||
e_node,
|
||||
e_hash,
|
||||
put_entry);
|
||||
|
||||
void image_list_init(struct image_list *list)
|
||||
{
|
||||
memset(list, 0x0, sizeof *list);
|
||||
}
|
||||
|
||||
void image_list_cleanup(struct image_list *list)
|
||||
{
|
||||
}
|
||||
|
||||
extern struct image_list_bucket *convert_to_bucket(
|
||||
struct btree *tree,
|
||||
struct image_list_leaf *leaf)
|
||||
{
|
||||
btree_delete(tree, &leaf->l_base.e_node);
|
||||
|
||||
struct image_list_bucket *bucket = malloc(sizeof *bucket);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bucket->b_base.e_hash = leaf->l_base.e_hash;
|
||||
bucket->b_base.e_type = IMAGE_LIST_ENTRY_BUCKET;
|
||||
put_entry(tree, &bucket->b_base);
|
||||
|
||||
queue_push_back(&bucket->b_items, &leaf->l_base.e_entry);
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
extern void image_list_put(
|
||||
struct image_list *list,
|
||||
struct image_list_leaf *item)
|
||||
{
|
||||
uint64_t hash = hash_string(item->l_name);
|
||||
item->l_base.e_type = IMAGE_LIST_ENTRY_LEAF;
|
||||
item->l_base.e_hash = hash;
|
||||
|
||||
struct image_list_entry *entry = get_entry(&list->l_items, hash);
|
||||
if (!entry) {
|
||||
put_entry(&list->l_items, &item->l_base);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry->e_type == IMAGE_LIST_ENTRY_BUCKET) {
|
||||
struct image_list_bucket *bucket
|
||||
= (struct image_list_bucket *)entry;
|
||||
queue_push_back(&bucket->b_items, &item->l_base.e_entry);
|
||||
return;
|
||||
}
|
||||
|
||||
struct image_list_leaf *leaf = (struct image_list_leaf *)entry;
|
||||
struct image_list_bucket *bucket
|
||||
= convert_to_bucket(&list->l_items, leaf);
|
||||
if (!bucket) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue_push_back(&bucket->b_items, &item->l_base.e_entry);
|
||||
}
|
||||
|
||||
extern struct image_list_leaf *image_list_get(
|
||||
struct image_list *list,
|
||||
const char *name)
|
||||
{
|
||||
uint64_t hash = hash_string(name);
|
||||
struct image_list_entry *entry = get_entry(&list->l_items, hash);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (entry->e_type) {
|
||||
case IMAGE_LIST_ENTRY_LEAF: {
|
||||
struct image_list_leaf *leaf = (struct image_list_leaf *)entry;
|
||||
if (!strcmp(leaf->l_name, name)) {
|
||||
return leaf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IMAGE_LIST_ENTRY_BUCKET: {
|
||||
struct image_list_bucket *bucket
|
||||
= (struct image_list_bucket *)entry;
|
||||
struct queue_entry *cur = queue_first(&bucket->b_items);
|
||||
while (cur) {
|
||||
struct image_list_leaf *leaf = QUEUE_CONTAINER(
|
||||
struct image_list_leaf,
|
||||
l_base.e_entry,
|
||||
cur);
|
||||
if (!strcmp(leaf->l_name, name)) {
|
||||
return leaf;
|
||||
}
|
||||
|
||||
cur = queue_next(cur);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void image_list_iterator_begin(
|
||||
struct image_list_iterator *it,
|
||||
struct image_list *list)
|
||||
{
|
||||
memset(it, 0x0, sizeof *it);
|
||||
struct btree_node *node = btree_first(&list->l_items);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
it->it_cur = QUEUE_CONTAINER(
|
||||
struct image_list_entry,
|
||||
e_node,
|
||||
node);
|
||||
if (it->it_cur->e_type == IMAGE_LIST_ENTRY_LEAF) {
|
||||
it->it_leaf = (struct image_list_leaf *)it->it_cur;
|
||||
return;
|
||||
}
|
||||
|
||||
struct image_list_bucket *bucket
|
||||
= (struct image_list_bucket *)it->it_cur;
|
||||
struct queue_entry *entry = queue_first(&bucket->b_items);
|
||||
if (!entry) {
|
||||
node = btree_next(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
it->it_leaf = QUEUE_CONTAINER(
|
||||
struct image_list_leaf,
|
||||
l_base.e_entry,
|
||||
entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void image_list_iterator_move_next(struct image_list_iterator *it)
|
||||
{
|
||||
if (!it->it_cur || !it->it_leaf) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (it->it_cur->e_type == IMAGE_LIST_ENTRY_LEAF) {
|
||||
/* current entry is a leaf */
|
||||
struct queue_entry *next
|
||||
= queue_next(&it->it_leaf->l_base.e_entry);
|
||||
if (next) {
|
||||
it->it_leaf = QUEUE_CONTAINER(
|
||||
struct image_list_leaf,
|
||||
l_base.e_entry,
|
||||
next);
|
||||
}
|
||||
}
|
||||
|
||||
struct btree_node *node = btree_next(&it->it_cur->e_node);
|
||||
if (!node) {
|
||||
it->it_cur = NULL;
|
||||
it->it_leaf = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
it->it_cur = BTREE_CONTAINER(
|
||||
struct image_list_entry,
|
||||
e_node,
|
||||
node);
|
||||
if (it->it_cur->e_type == IMAGE_LIST_ENTRY_LEAF) {
|
||||
/* next entry is a leaf */
|
||||
it->it_leaf = (struct image_list_leaf *)it->it_cur;
|
||||
return;
|
||||
}
|
||||
|
||||
struct image_list_bucket *bucket
|
||||
= (struct image_list_bucket *)it->it_cur;
|
||||
struct queue_entry *entry = queue_first(&bucket->b_items);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
it->it_leaf = QUEUE_CONTAINER(
|
||||
struct image_list_leaf,
|
||||
l_base.e_entry,
|
||||
entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
59
sys/ld/image-list.h
Normal file
59
sys/ld/image-list.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef IMAGE_LIST_H_
|
||||
#define IMAGE_LIST_H_
|
||||
|
||||
#include "btree.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define IMAGE_NAME_MAX 256
|
||||
|
||||
enum image_list_entry_type {
|
||||
IMAGE_LIST_ENTRY_NONE = 0,
|
||||
IMAGE_LIST_ENTRY_LEAF,
|
||||
IMAGE_LIST_ENTRY_BUCKET,
|
||||
};
|
||||
|
||||
struct image_list_entry {
|
||||
enum image_list_entry_type e_type;
|
||||
uint64_t e_hash;
|
||||
union {
|
||||
struct btree_node e_node;
|
||||
struct queue_entry e_entry;
|
||||
};
|
||||
};
|
||||
|
||||
struct image_list_bucket {
|
||||
struct image_list_entry b_base;
|
||||
struct queue b_items;
|
||||
};
|
||||
|
||||
struct image_list_leaf {
|
||||
struct image_list_entry l_base;
|
||||
char l_name[IMAGE_NAME_MAX];
|
||||
};
|
||||
|
||||
struct image_list {
|
||||
struct btree l_items;
|
||||
};
|
||||
|
||||
struct image_list_iterator {
|
||||
struct image_list_entry *it_cur;
|
||||
struct image_list_leaf *it_leaf;
|
||||
};
|
||||
|
||||
extern void image_list_init(struct image_list *list);
|
||||
extern void image_list_cleanup(struct image_list *list);
|
||||
|
||||
extern void image_list_put(
|
||||
struct image_list *list,
|
||||
struct image_list_leaf *item);
|
||||
extern struct image_list_leaf *image_list_get(
|
||||
struct image_list *list,
|
||||
const char *name);
|
||||
|
||||
extern void image_list_iterator_begin(
|
||||
struct image_list_iterator *it,
|
||||
struct image_list *list);
|
||||
extern void image_list_iterator_move_next(struct image_list_iterator *it);
|
||||
#endif
|
||||
216
sys/ld/main.c
216
sys/ld/main.c
@@ -1,7 +1,10 @@
|
||||
#define MSG_IMPLEMENTATION
|
||||
#define MSG_NO_MALLOC
|
||||
|
||||
#include "elf.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <heap/heap.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/msg.h>
|
||||
@@ -10,55 +13,212 @@
|
||||
#include <mango/vm.h>
|
||||
#include <rosetta/bootstrap.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/remote.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char *search_paths[] = {
|
||||
"/usr/lib",
|
||||
};
|
||||
static const size_t nr_search_paths
|
||||
= sizeof search_paths / sizeof search_paths[0];
|
||||
|
||||
static void report_error(const char *name, int err, const char *msg, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list arg;
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof buf, msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
kern_tracef("%s: %s: %s", name, buf, strerror(err));
|
||||
}
|
||||
|
||||
static const char *get_image_name(const char *path)
|
||||
{
|
||||
const char *last_slash = NULL;
|
||||
for (size_t i = 0; path[i]; i++) {
|
||||
if (path[i] == '/') {
|
||||
last_slash = path + i;
|
||||
}
|
||||
}
|
||||
|
||||
return last_slash ? last_slash + 1 : path;
|
||||
}
|
||||
|
||||
static int find_image(struct elf_image *img)
|
||||
{
|
||||
const char *name = img->e_leaf.l_name;
|
||||
char path[4096];
|
||||
|
||||
for (size_t i = 0; i < nr_search_paths; i++) {
|
||||
snprintf(path, sizeof path, "%s/%s", search_paths[i], name);
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
kern_tracef("found %s -> %s", name, path);
|
||||
img->e_fd = fd;
|
||||
img->e_status = ELF_IMAGE_OPEN;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
static int load_images(struct image_list *list)
|
||||
{
|
||||
int status = SUCCESS;
|
||||
struct image_list_iterator it;
|
||||
image_list_iterator_begin(&it, list);
|
||||
|
||||
while (it.it_leaf) {
|
||||
struct elf_image *image
|
||||
= QUEUE_CONTAINER(struct elf_image, e_leaf, it.it_leaf);
|
||||
kern_tracef(
|
||||
"image: %s [%s]",
|
||||
it.it_leaf->l_name,
|
||||
elf_image_status_to_string(image->e_status));
|
||||
|
||||
int new_dependencies = 0;
|
||||
switch (image->e_status) {
|
||||
case ELF_IMAGE_NONE:
|
||||
/* Find the image using its name */
|
||||
status = find_image(image);
|
||||
if (status != SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
case ELF_IMAGE_OPEN:
|
||||
/* parse the image */
|
||||
status = elf_image_parse(image);
|
||||
if (status != SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
case ELF_IMAGE_PARSED:
|
||||
/* load the image */
|
||||
status = elf_image_load(image);
|
||||
if (status != SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
case ELF_IMAGE_LOADED:
|
||||
/* collect dependencies */
|
||||
new_dependencies
|
||||
= elf_image_collect_dependencies(image, list);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_dependencies < 0) {
|
||||
return -new_dependencies;
|
||||
}
|
||||
|
||||
if (new_dependencies > 0) {
|
||||
image_list_iterator_begin(&it, list);
|
||||
} else {
|
||||
image_list_iterator_move_next(&it);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int link_images(struct image_list *list)
|
||||
{
|
||||
int status = SUCCESS;
|
||||
struct image_list_iterator it;
|
||||
image_list_iterator_begin(&it, list);
|
||||
|
||||
kern_trace("linking all images");
|
||||
while (it.it_leaf) {
|
||||
struct elf_image *image
|
||||
= QUEUE_CONTAINER(struct elf_image, e_leaf, it.it_leaf);
|
||||
kern_tracef(
|
||||
"image: %s [%s]",
|
||||
it.it_leaf->l_name,
|
||||
elf_image_status_to_string(image->e_status));
|
||||
|
||||
status = elf_image_link(image);
|
||||
|
||||
image_list_iterator_move_next(&it);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int main(const struct rosetta_bootstrap *bs)
|
||||
{
|
||||
kern_handle_t task, address_space;
|
||||
task_self(&task);
|
||||
task_get_address_space(task, &address_space);
|
||||
kern_tracef("ld");
|
||||
|
||||
for (size_t i = 0; i < bs->bs_argc; i++) {
|
||||
kern_logf("argv[%zu]: %s", i, bs->bs_argv[i]);
|
||||
kern_tracef("argv[%zu]: %s", i, bs->bs_argv[i]);
|
||||
}
|
||||
|
||||
sys_remote_set(SYS_REMOTE_NSD, 0, 0);
|
||||
const char *path = "/usr/lib/libc.so";
|
||||
int flags = 4;
|
||||
int fd = open(path, flags);
|
||||
const char *exec_path = bs->bs_argv[1];
|
||||
const char *task_name = bs->bs_argv[2];
|
||||
const char *image_name = get_image_name(exec_path);
|
||||
|
||||
kern_logf("sending msg: open(%s, %d)", path, flags);
|
||||
struct image_list images;
|
||||
image_list_init(&images);
|
||||
|
||||
if (fd < 0) {
|
||||
kern_logf(
|
||||
"open(%s, %d) = %s (%s)",
|
||||
path,
|
||||
flags,
|
||||
strerror_code(fd),
|
||||
strerror(fd));
|
||||
struct elf_image *exec = NULL;
|
||||
int err = elf_image_open(exec_path, &exec);
|
||||
if (err != SUCCESS) {
|
||||
report_error(
|
||||
task_name,
|
||||
err,
|
||||
"error while loading %s",
|
||||
exec_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
kern_logf(
|
||||
"open(%s, %d) = %s (%s)",
|
||||
path,
|
||||
flags,
|
||||
strerror_code(SUCCESS),
|
||||
strerror(SUCCESS));
|
||||
snprintf(
|
||||
exec->e_leaf.l_name,
|
||||
sizeof exec->e_leaf.l_name,
|
||||
"%s",
|
||||
image_name);
|
||||
|
||||
unsigned char buf[32] = {0};
|
||||
int nr = read(fd, buf, sizeof buf);
|
||||
|
||||
if (nr < 0) {
|
||||
kern_logf("read call failed (%s)", strerror(nr));
|
||||
image_list_put(&images, &exec->e_leaf);
|
||||
err = load_images(&images);
|
||||
if (err != SUCCESS) {
|
||||
report_error(
|
||||
task_name,
|
||||
err,
|
||||
"error while loading %s",
|
||||
exec_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
kern_logf("data: %x %c %c %c", buf[0], buf[1], buf[2], buf[3]);
|
||||
err = link_images(&images);
|
||||
if (err != SUCCESS) {
|
||||
report_error(
|
||||
task_name,
|
||||
err,
|
||||
"error while loading %s",
|
||||
exec_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
struct image_list_iterator it;
|
||||
image_list_iterator_begin(&it, &images);
|
||||
|
||||
while (it.it_leaf) {
|
||||
struct elf_image *image
|
||||
= QUEUE_CONTAINER(struct elf_image, e_leaf, it.it_leaf);
|
||||
kern_tracef(
|
||||
"image: %s [%s]",
|
||||
it.it_leaf->l_name,
|
||||
elf_image_status_to_string(image->e_status));
|
||||
|
||||
image_list_iterator_move_next(&it);
|
||||
}
|
||||
|
||||
kern_tracef("ld finished");
|
||||
int (*entry)(int, const char **)
|
||||
= (int (*)(int, const char **))exec->e_entry;
|
||||
return entry(bs->bs_argc - 2, bs->bs_argv + 2);
|
||||
}
|
||||
|
||||
138
sys/ld/queue.c
Normal file
138
sys/ld/queue.c
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "queue.h"
|
||||
|
||||
size_t queue_length(struct queue *q)
|
||||
{
|
||||
size_t i = 0;
|
||||
struct queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
i++;
|
||||
x = x->qe_next;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void queue_insert_before(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *before)
|
||||
{
|
||||
struct queue_entry *x = before->qe_prev;
|
||||
if (x) {
|
||||
x->qe_next = entry;
|
||||
} else {
|
||||
q->q_first = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
before->qe_prev = entry;
|
||||
entry->qe_next = before;
|
||||
}
|
||||
|
||||
void queue_insert_after(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *after)
|
||||
{
|
||||
struct queue_entry *x = after->qe_next;
|
||||
if (x) {
|
||||
x->qe_prev = entry;
|
||||
} else {
|
||||
q->q_last = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
after->qe_next = entry;
|
||||
entry->qe_prev = after;
|
||||
}
|
||||
|
||||
void queue_push_front(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (q->q_first) {
|
||||
q->q_first->qe_prev = entry;
|
||||
}
|
||||
|
||||
entry->qe_next = q->q_first;
|
||||
entry->qe_prev = NULL;
|
||||
|
||||
q->q_first = entry;
|
||||
|
||||
if (!q->q_last) {
|
||||
q->q_last = entry;
|
||||
}
|
||||
}
|
||||
|
||||
void queue_push_back(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (q->q_last) {
|
||||
q->q_last->qe_next = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = q->q_last;
|
||||
entry->qe_next = NULL;
|
||||
|
||||
q->q_last = entry;
|
||||
|
||||
if (!q->q_first) {
|
||||
q->q_first = entry;
|
||||
}
|
||||
}
|
||||
|
||||
struct queue_entry *queue_pop_front(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_first;
|
||||
if (x) {
|
||||
queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
struct queue_entry *queue_pop_back(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_last;
|
||||
if (x) {
|
||||
queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void queue_delete(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry == q->q_first) {
|
||||
q->q_first = q->q_first->qe_next;
|
||||
}
|
||||
|
||||
if (entry == q->q_last) {
|
||||
q->q_last = q->q_last->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_next) {
|
||||
entry->qe_next->qe_prev = entry->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_prev) {
|
||||
entry->qe_prev->qe_next = entry->qe_next;
|
||||
}
|
||||
|
||||
entry->qe_next = entry->qe_prev = NULL;
|
||||
}
|
||||
|
||||
void queue_delete_all(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
struct queue_entry *next = x->qe_next;
|
||||
x->qe_next = x->qe_prev = NULL;
|
||||
x = next;
|
||||
}
|
||||
|
||||
q->q_first = q->q_last = NULL;
|
||||
}
|
||||
100
sys/ld/queue.h
Normal file
100
sys/ld/queue.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef QUEUE_H_
|
||||
#define QUEUE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define QUEUE_CONTAINER(t, m, v) \
|
||||
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
|
||||
|
||||
#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL})
|
||||
#define QUEUE_ENTRY_INIT \
|
||||
((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL})
|
||||
|
||||
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
|
||||
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_first(queue_name)); \
|
||||
iter_name; \
|
||||
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_next(&((iter_name)->node_member))))
|
||||
|
||||
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
|
||||
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_last(queue_name)); \
|
||||
iter_name; \
|
||||
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_prev(&((iter_name)->node_member))))
|
||||
|
||||
struct queue_entry {
|
||||
struct queue_entry *qe_next;
|
||||
struct queue_entry *qe_prev;
|
||||
};
|
||||
|
||||
struct queue {
|
||||
struct queue_entry *q_first;
|
||||
struct queue_entry *q_last;
|
||||
};
|
||||
|
||||
static inline void queue_init(struct queue *q)
|
||||
{
|
||||
memset(q, 0x00, sizeof *q);
|
||||
}
|
||||
static inline bool queue_empty(struct queue *q)
|
||||
{
|
||||
return q->q_first == NULL;
|
||||
}
|
||||
|
||||
static inline struct queue_entry *queue_first(struct queue *q)
|
||||
{
|
||||
return q->q_first;
|
||||
}
|
||||
static inline struct queue_entry *queue_last(struct queue *q)
|
||||
{
|
||||
return q->q_last;
|
||||
}
|
||||
static inline struct queue_entry *queue_next(struct queue_entry *entry)
|
||||
{
|
||||
return entry->qe_next;
|
||||
}
|
||||
static inline struct queue_entry *queue_prev(struct queue_entry *entry)
|
||||
{
|
||||
return entry->qe_prev;
|
||||
}
|
||||
|
||||
extern size_t queue_length(struct queue *q);
|
||||
|
||||
extern void queue_insert_before(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *before);
|
||||
extern void queue_insert_after(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *after);
|
||||
|
||||
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
|
||||
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
|
||||
|
||||
extern struct queue_entry *queue_pop_front(struct queue *q);
|
||||
extern struct queue_entry *queue_pop_back(struct queue *q);
|
||||
|
||||
extern void queue_delete(struct queue *q, struct queue_entry *entry);
|
||||
extern void queue_delete_all(struct queue *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
22
sys/ld/resolve.c
Normal file
22
sys/ld/resolve.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "elf.h"
|
||||
|
||||
#include <mango/log.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
uintptr_t dl_runtime_resolve(struct elf_image *img, unsigned long sym_id)
|
||||
{
|
||||
elf_sym_t *sym
|
||||
= (elf_sym_t *)((virt_addr_t)img->e_base + img->e_dynsym);
|
||||
const char *sym_name = (const char *)img->e_base + img->e_strtab
|
||||
+ sym[sym_id + 1].st_name;
|
||||
// kern_logf("%s: request for symbol %s", img->e_leaf.l_name, sym_name);
|
||||
virt_addr_t sym_addr = elf_image_find_linked_symbol(img, sym_name);
|
||||
virt_addr_t *sym_slot
|
||||
= (virt_addr_t *)((virt_addr_t)img->e_base + img->e_got_plt
|
||||
+ ((sym_id + 3) * sizeof(virt_addr_t)));
|
||||
// kern_logf("symbol %s = %zx", sym_name, sym_addr);
|
||||
// kern_logf("slot %s = %zx", sym_name, sym_slot);
|
||||
*sym_slot = sym_addr;
|
||||
return sym_addr;
|
||||
}
|
||||
7
sys/ld/resolve.h
Normal file
7
sys/ld/resolve.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef RESOLVE_H_
|
||||
#define RESOLVE_H_
|
||||
|
||||
extern void _dl_runtime_resolve(void);
|
||||
extern virt_addr_t dl_runtime_resolve(unsigned int slot);
|
||||
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||
|
||||
find_package(Bluelib REQUIRED COMPONENTS Core Ds Term Cmd Io)
|
||||
find_package(FX REQUIRED COMPONENTS Core Ds Term Cmd Io)
|
||||
|
||||
add_subdirectory(
|
||||
../kernel/tools
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
#[=======================================================================[.rst:
|
||||
FindBluelib
|
||||
------------
|
||||
|
||||
Find the Bluelib library and header directories
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following :prop_tgt:`IMPORTED` target:
|
||||
|
||||
``Bluelib::Bluelib``
|
||||
The Bluelib library, if found
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module will set the following variables in your project:
|
||||
|
||||
``Bluelib_FOUND``
|
||||
true if the Bluelib C headers and libraries were found
|
||||
``Bluelib_INCLUDE_DIR``
|
||||
directories containing the Bluelib C headers.
|
||||
|
||||
``Bluelib_LIBRARY``
|
||||
the C library to link against
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
The user may set the environment variable ``Bluelib_PREFIX`` to the root
|
||||
directory of a Bluelib library installation.
|
||||
#]=======================================================================]
|
||||
|
||||
set (Bluelib_SEARCH_PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr/local/share
|
||||
/usr
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
${Bluelib_PREFIX}
|
||||
$ENV{Bluelib_PREFIX})
|
||||
|
||||
if (Bluelib_STATIC)
|
||||
set(_lib_suffix "-s")
|
||||
endif ()
|
||||
|
||||
set(supported_components
|
||||
Core Ds Term Cmd Io Serial Compress
|
||||
Core-MM)
|
||||
set(components ${Bluelib_FIND_COMPONENTS})
|
||||
string(REPLACE ";" ", " supported_components_string_list "${supported_components}")
|
||||
|
||||
if (NOT components)
|
||||
set(components ${supported_components})
|
||||
endif ()
|
||||
|
||||
set(required_vars)
|
||||
|
||||
foreach (component ${components})
|
||||
if (NOT "${component}" IN_LIST supported_components)
|
||||
message(FATAL_ERROR "'${component}' is not a valid Bluelib module.\nSupported modules: ${supported_components_string_list}")
|
||||
endif ()
|
||||
|
||||
string(TOLOWER ${component} header_name)
|
||||
string(REPLACE "-mm" "" header_name "${header_name}")
|
||||
|
||||
string(TOLOWER ${component} lib_name)
|
||||
set(lib_name ${lib_name}${_lib_suffix})
|
||||
|
||||
set(cmake_lib_name ${component})
|
||||
string(REPLACE "-" "_" cmake_lib_name "${cmake_lib_name}")
|
||||
|
||||
if (NOT Bluelib_${cmake_lib_dir}_INCLUDE_DIR)
|
||||
find_path(Bluelib_${cmake_lib_name}_INCLUDE_DIR
|
||||
NAMES blue/${header_name}.h blue/${header_name}.hpp
|
||||
${Bluelib_FIND_ARGS}
|
||||
PATH_SUFFIXES include
|
||||
PATHS ${Bluelib_SEARCH_PATHS})
|
||||
endif ()
|
||||
|
||||
if (NOT Bluelib_${cmake_lib_dir}_LIBRARY)
|
||||
find_library(Bluelib_${cmake_lib_name}_LIBRARY
|
||||
NAMES blue-${lib_name} ${Bluelib_FIND_ARGS}
|
||||
PATH_SUFFIXES lib
|
||||
PATHS ${Bluelib_SEARCH_PATHS})
|
||||
else ()
|
||||
# on Windows, ensure paths are in canonical format (forward slahes):
|
||||
file(TO_CMAKE_PATH "${Bluelib_${cmake_lib_name}_LIBRARY}" Bluelib_${cmake_lib_name}_LIBRARY)
|
||||
endif()
|
||||
|
||||
list(APPEND required_vars Bluelib_${cmake_lib_name}_INCLUDE_DIR Bluelib_${cmake_lib_name}_LIBRARY)
|
||||
endforeach (component)
|
||||
|
||||
unset(Bluelib_FIND_ARGS)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(Bluelib
|
||||
REQUIRED_VARS ${required_vars})
|
||||
|
||||
if (Bluelib_FOUND)
|
||||
set(created_targets)
|
||||
foreach (component ${components})
|
||||
string(TOLOWER ${component} header_name)
|
||||
string(REPLACE "-mm" "" header_name "${header_name}")
|
||||
|
||||
string(TOLOWER ${component} lib_name)
|
||||
set(lib_name ${lib_name}${_lib_suffix})
|
||||
|
||||
set(cmake_lib_name ${component})
|
||||
string(REPLACE "-" "_" cmake_lib_name "${cmake_lib_name}")
|
||||
|
||||
if(NOT TARGET Bluelib::${component})
|
||||
add_library(Bluelib::${component} UNKNOWN IMPORTED)
|
||||
set_target_properties(Bluelib::${component} PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Bluelib_${cmake_lib_name}_INCLUDE_DIR}")
|
||||
target_compile_definitions(Bluelib::${component} INTERFACE _CRT_SECURE_NO_WARNINGS=1)
|
||||
|
||||
if (Bluelib_STATIC)
|
||||
target_compile_definitions(Bluelib::${component} INTERFACE BLUELIB_STATIC=1)
|
||||
endif ()
|
||||
|
||||
set_target_properties(Bluelib::${component} PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${Bluelib_${cmake_lib_name}_LIBRARY}")
|
||||
set(created_targets ${created_targets} ${component})
|
||||
endif ()
|
||||
endforeach (component)
|
||||
|
||||
foreach (component ${created_targets})
|
||||
if ("${component}" STREQUAL "Ds")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Ds' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Ds INTERFACE Bluelib::Core)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Term")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Term' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET Bluelib::Ds)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Term' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Term INTERFACE Bluelib::Core Bluelib::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Serial")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Serial' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET Bluelib::Ds)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Serial' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Serial INTERFACE Bluelib::Core Bluelib::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Cmd")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Cmd' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET Bluelib::Ds)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Cmd' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET Bluelib::Term)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Cmd' depends on 'Term', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Cmd INTERFACE Bluelib::Core Bluelib::Ds Bluelib::Term)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Io")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Io' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET Bluelib::Ds)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Io' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Io INTERFACE Bluelib::Core Bluelib::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Compress")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Compress' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Compress INTERFACE Bluelib::Core Bluelib::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Core-MM")
|
||||
if (NOT TARGET Bluelib::Core)
|
||||
message(FATAL_ERROR "Bluelib: Module 'Core-MM' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(Bluelib::Core-MM INTERFACE Bluelib::Core)
|
||||
endif ()
|
||||
endforeach (component)
|
||||
endif()
|
||||
189
toolchain/cmake/FindFX.cmake
Normal file
189
toolchain/cmake/FindFX.cmake
Normal file
@@ -0,0 +1,189 @@
|
||||
#[=======================================================================[.rst:
|
||||
FindFX
|
||||
------------
|
||||
|
||||
Find the FX library and header directories
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following :prop_tgt:`IMPORTED` target:
|
||||
|
||||
``FX::FX``
|
||||
The FX library, if found
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module will set the following variables in your project:
|
||||
|
||||
``FX_FOUND``
|
||||
true if the FX C headers and libraries were found
|
||||
``FX_INCLUDE_DIR``
|
||||
directories containing the FX C headers.
|
||||
|
||||
``FX_LIBRARY``
|
||||
the C library to link against
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
The user may set the environment variable ``FX_PREFIX`` to the root
|
||||
directory of a FX library installation.
|
||||
#]=======================================================================]
|
||||
|
||||
set (FX_SEARCH_PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr/local/share
|
||||
/usr
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
${FX_PREFIX}
|
||||
$ENV{FX_PREFIX})
|
||||
|
||||
if (FX_STATIC)
|
||||
set(_lib_suffix "-s")
|
||||
endif ()
|
||||
|
||||
set(supported_components Core Ds Term Cmd Io Serial Compress)
|
||||
set(components ${FX_FIND_COMPONENTS})
|
||||
string(REPLACE ";" ", " supported_components_string_list "${supported_components}")
|
||||
|
||||
if (NOT components)
|
||||
set(components ${supported_components})
|
||||
endif ()
|
||||
|
||||
set(required_vars)
|
||||
|
||||
foreach (component ${components})
|
||||
if (NOT "${component}" IN_LIST supported_components)
|
||||
message(FATAL_ERROR "'${component}' is not a valid FX module.\nSupported modules: ${supported_components_string_list}")
|
||||
endif ()
|
||||
|
||||
string(TOLOWER ${component} header_name)
|
||||
set(lib_name ${header_name}${_lib_suffix})
|
||||
|
||||
if (NOT FX_${component}_INCLUDE_DIR)
|
||||
find_path(FX_${component}_INCLUDE_DIR
|
||||
NAMES fx/${header_name}.h ${FX_FIND_ARGS}
|
||||
PATH_SUFFIXES include
|
||||
PATHS ${FX_SEARCH_PATHS})
|
||||
endif ()
|
||||
|
||||
if (NOT FX_${component}_LIBRARY)
|
||||
find_library(FX_${component}_LIBRARY
|
||||
NAMES fx-${lib_name} ${FX_FIND_ARGS}
|
||||
PATH_SUFFIXES lib
|
||||
PATHS ${FX_SEARCH_PATHS})
|
||||
else ()
|
||||
# on Windows, ensure paths are in canonical format (forward slahes):
|
||||
file(TO_CMAKE_PATH "${FX_${component}_LIBRARY}" FX_${component}_LIBRARY)
|
||||
endif()
|
||||
|
||||
list(APPEND required_vars FX_${component}_INCLUDE_DIR FX_${component}_LIBRARY)
|
||||
endforeach (component)
|
||||
|
||||
unset(FX_FIND_ARGS)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(FX
|
||||
REQUIRED_VARS ${required_vars})
|
||||
|
||||
if (FX_FOUND)
|
||||
set(created_targets)
|
||||
foreach (component ${components})
|
||||
string(TOLOWER ${component} header_name)
|
||||
set(lib_name ${header_name}${_lib_suffix})
|
||||
|
||||
if(NOT TARGET FX::${component})
|
||||
add_library(FX::${component} UNKNOWN IMPORTED)
|
||||
set_target_properties(FX::${component} PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${FX_${component}_INCLUDE_DIR}")
|
||||
target_compile_definitions(FX::${component} INTERFACE _CRT_SECURE_NO_WARNINGS=1)
|
||||
|
||||
if (FX_STATIC)
|
||||
target_compile_definitions(FX::${component} INTERFACE FX_STATIC=1)
|
||||
endif ()
|
||||
|
||||
set_target_properties(FX::${component} PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${FX_${component}_LIBRARY}")
|
||||
set(created_targets ${created_targets} ${component})
|
||||
endif ()
|
||||
endforeach (component)
|
||||
|
||||
foreach (component ${created_targets})
|
||||
if ("${component}" STREQUAL "Ds")
|
||||
if (NOT TARGET FX::Core)
|
||||
message(FATAL_ERROR "FX: Module 'Ds' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(FX::Ds INTERFACE FX::Core)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Term")
|
||||
if (NOT TARGET FX::Core)
|
||||
message(FATAL_ERROR "FX: Module 'Term' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET FX::Ds)
|
||||
message(FATAL_ERROR "FX: Module 'Term' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(FX::Term INTERFACE FX::Core FX::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Serial")
|
||||
if (NOT TARGET FX::Core)
|
||||
message(FATAL_ERROR "FX: Module 'Serial' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET FX::Ds)
|
||||
message(FATAL_ERROR "FX: Module 'Serial' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(FX::Serial INTERFACE FX::Core FX::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Cmd")
|
||||
if (NOT TARGET FX::Core)
|
||||
message(FATAL_ERROR "FX: Module 'Cmd' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET FX::Ds)
|
||||
message(FATAL_ERROR "FX: Module 'Cmd' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET FX::Term)
|
||||
message(FATAL_ERROR "FX: Module 'Cmd' depends on 'Term', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(FX::Cmd INTERFACE FX::Core FX::Ds FX::Term)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Io")
|
||||
if (NOT TARGET FX::Core)
|
||||
message(FATAL_ERROR "FX: Module 'Io' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
if (NOT TARGET FX::Ds)
|
||||
message(FATAL_ERROR "FX: Module 'Io' depends on 'Ds', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(FX::Io INTERFACE FX::Core FX::Ds)
|
||||
endif ()
|
||||
|
||||
if ("${component}" STREQUAL "Compress")
|
||||
if (NOT TARGET FX::Core)
|
||||
message(FATAL_ERROR "FX: Module 'Compress' depends on 'Core', which was not specified in find_package()")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(FX::Compress INTERFACE FX::Core FX::Ds)
|
||||
endif ()
|
||||
endforeach (component)
|
||||
endif()
|
||||
@@ -4,4 +4,4 @@ file(GLOB sources
|
||||
|
||||
add_executable(xpcg ${sources})
|
||||
|
||||
target_link_libraries(xpcg Bluelib::Core Bluelib::Ds Bluelib::Cmd Bluelib::Io)
|
||||
target_link_libraries(xpcg FX::Core FX::Ds FX::Cmd FX::Io)
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
#include "../../msg.h"
|
||||
#include "../../type.h"
|
||||
|
||||
#include <blue/core/hash.h>
|
||||
#include <blue/io/file.h>
|
||||
#include <blue/io/path.h>
|
||||
#include <fx/core/hash.h>
|
||||
#include <fx/io/file.h>
|
||||
#include <fx/io/path.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct emit_ctx {
|
||||
b_stream *ctx_out;
|
||||
fx_stream *ctx_out;
|
||||
size_t ctx_max_handle_params;
|
||||
};
|
||||
|
||||
@@ -19,23 +19,23 @@ static void emit(struct emit_ctx *ctx, const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
b_stream_write_vfmt(ctx->ctx_out, NULL, format, arg);
|
||||
fx_stream_write_vfmt(ctx->ctx_out, NULL, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
static void emit_indent(struct emit_ctx *ctx)
|
||||
{
|
||||
b_stream_push_indent(ctx->ctx_out, 4);
|
||||
fx_stream_push_indent(ctx->ctx_out, 4);
|
||||
}
|
||||
|
||||
static void emit_indent_zero(struct emit_ctx *ctx)
|
||||
{
|
||||
b_stream_push_indent(ctx->ctx_out, -100);
|
||||
fx_stream_push_indent(ctx->ctx_out, -100);
|
||||
}
|
||||
|
||||
static void emit_unindent(struct emit_ctx *ctx)
|
||||
{
|
||||
b_stream_pop_indent(ctx->ctx_out);
|
||||
fx_stream_pop_indent(ctx->ctx_out);
|
||||
}
|
||||
|
||||
static int emit_include(struct emit_ctx *ctx, const char *path, bool system)
|
||||
@@ -53,15 +53,15 @@ static int emit_header_guard_start(
|
||||
struct emit_ctx *ctx,
|
||||
const struct interface_definition *iface)
|
||||
{
|
||||
b_string *header_guard = b_string_create();
|
||||
b_string_append_cstrf(header_guard, "%s_H_", iface->if_name);
|
||||
b_string_toupper(header_guard);
|
||||
fx_string *header_guard = fx_string_create();
|
||||
fx_string_append_cstrf(header_guard, "%s_H_", iface->if_name);
|
||||
fx_string_toupper(header_guard);
|
||||
|
||||
emit(ctx,
|
||||
"#ifndef %s\n#define %s\n\n",
|
||||
b_string_ptr(header_guard),
|
||||
b_string_ptr(header_guard));
|
||||
b_string_unref(header_guard);
|
||||
fx_string_ptr(header_guard),
|
||||
fx_string_ptr(header_guard));
|
||||
fx_string_unref(header_guard);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -76,15 +76,15 @@ static int emit_header_guard_end(
|
||||
static size_t get_msg_handle_params(const struct msg_definition *msg)
|
||||
{
|
||||
size_t count = 0;
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
const struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
if (param->p_type->ty_id == TYPE_HANDLE) {
|
||||
count++;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -93,17 +93,17 @@ static size_t get_msg_handle_params(const struct msg_definition *msg)
|
||||
static size_t get_max_handle_params(const struct interface_definition *iface)
|
||||
{
|
||||
size_t count = 0;
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
const struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
size_t msg_count = get_msg_handle_params(msg);
|
||||
|
||||
if (msg_count > count) {
|
||||
count = msg_count;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -113,51 +113,51 @@ static int emit_interface_id_macros(
|
||||
struct emit_ctx *ctx,
|
||||
const struct interface_definition *iface)
|
||||
{
|
||||
b_string *iface_define_name = b_string_create();
|
||||
b_string_append_cstr(iface_define_name, iface->if_name);
|
||||
b_string_toupper(iface_define_name);
|
||||
fx_string *iface_define_name = fx_string_create();
|
||||
fx_string_append_cstr(iface_define_name, iface->if_name);
|
||||
fx_string_toupper(iface_define_name);
|
||||
|
||||
uint32_t protocol_id = iface->if_id;
|
||||
|
||||
emit(ctx,
|
||||
"/* %s protocol ID */\n#define INTERFACE_%s 0x%08xU\n\n",
|
||||
iface->if_name,
|
||||
b_string_ptr(iface_define_name),
|
||||
fx_string_ptr(iface_define_name),
|
||||
protocol_id);
|
||||
b_string_unref(iface_define_name);
|
||||
fx_string_unref(iface_define_name);
|
||||
|
||||
b_string *msg_name = b_string_create();
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_string *msg_name = fx_string_create();
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
const struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
b_string_clear(msg_name);
|
||||
b_string_append_cstrf(
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
fx_string_clear(msg_name);
|
||||
fx_string_append_cstrf(
|
||||
msg_name,
|
||||
"%s.%s",
|
||||
iface->if_name,
|
||||
msg->msg_name);
|
||||
|
||||
emit(ctx, "/* %s message ID */\n", b_string_ptr(msg_name));
|
||||
emit(ctx, "/* %s message ID */\n", fx_string_ptr(msg_name));
|
||||
uint16_t msg_id = msg->msg_id;
|
||||
|
||||
b_string_clear(msg_name);
|
||||
b_string_append_cstrf(
|
||||
fx_string_clear(msg_name);
|
||||
fx_string_append_cstrf(
|
||||
msg_name,
|
||||
"%s_%s",
|
||||
iface->if_name,
|
||||
msg->msg_name);
|
||||
b_string_toupper(msg_name);
|
||||
fx_string_toupper(msg_name);
|
||||
emit(ctx,
|
||||
"#define MSG_%s 0x%04xU\n",
|
||||
b_string_ptr(msg_name),
|
||||
fx_string_ptr(msg_name),
|
||||
msg_id);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, "\n");
|
||||
b_string_unref(msg_name);
|
||||
fx_string_unref(msg_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -176,10 +176,12 @@ static int emit_msg_struct_member(
|
||||
case TYPE_SIZE:
|
||||
emit(ctx, "uint64_t %s;\n", param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx, "int64_t %s;\n", param->p_name);
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx, "uint16_t %s_offset;\n", param->p_name);
|
||||
emit(ctx, "uint16_t %s_len;\n", param->p_name);
|
||||
emit(ctx, "uint32_t %s_len;\n", param->p_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -192,33 +194,41 @@ static int emit_msg_struct_params(
|
||||
struct emit_ctx *ctx,
|
||||
const struct msg_definition *msg)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
const struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_STRING:
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx, "uint32_t %s_offset;\n", param->p_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emit_msg_struct_member(ctx, param);
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
const struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_STRING:
|
||||
emit(ctx, "uint16_t %s_offset;\n", param->p_name);
|
||||
emit(ctx, "uint16_t %s_max;\n", param->p_name);
|
||||
emit(ctx, "uint32_t %s_offset;\n", param->p_name);
|
||||
emit(ctx, "uint32_t %s_max;\n", param->p_name);
|
||||
break;
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx, "uint16_t %s_offset;\n", param->p_name);
|
||||
emit(ctx, "uint32_t %s_offset;\n", param->p_name);
|
||||
emit(ctx, "uint32_t %s_max;\n", param->p_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -228,12 +238,13 @@ static int emit_msg_struct_results(
|
||||
struct emit_ctx *ctx,
|
||||
const struct msg_definition *msg)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_results);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
const struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
emit_msg_struct_member(ctx, param);
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -244,7 +255,7 @@ static int emit_interface_msg_struct(
|
||||
const struct interface_definition *iface,
|
||||
const struct msg_definition *msg)
|
||||
{
|
||||
b_string *tmp = b_string_create();
|
||||
fx_string *tmp = fx_string_create();
|
||||
|
||||
emit(ctx, "struct %s_%s_msg {\n", iface->if_name, msg->msg_name);
|
||||
emit_indent(ctx);
|
||||
@@ -277,7 +288,7 @@ static int emit_interface_msg_struct(
|
||||
emit_unindent(ctx);
|
||||
emit(ctx, "};\n");
|
||||
|
||||
b_string_unref(tmp);
|
||||
fx_string_unref(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -285,14 +296,14 @@ static int emit_interface_msg_structs(
|
||||
struct emit_ctx *ctx,
|
||||
const struct interface_definition *iface)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
const struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
|
||||
emit_interface_msg_struct(ctx, iface, msg);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, "\n");
|
||||
@@ -304,10 +315,10 @@ static int emit_interface_msg_function_send_impl(
|
||||
const struct interface_definition *iface,
|
||||
const struct msg_definition *msg)
|
||||
{
|
||||
b_string *iface_ucase = b_string_create_from_cstr(iface->if_name);
|
||||
b_string_toupper(iface_ucase);
|
||||
b_string *msg_ucase = b_string_create_from_cstr(msg->msg_name);
|
||||
b_string_toupper(msg_ucase);
|
||||
fx_string *iface_ucase = fx_string_create_from_cstr(iface->if_name);
|
||||
fx_string_toupper(iface_ucase);
|
||||
fx_string *msg_ucase = fx_string_create_from_cstr(msg->msg_name);
|
||||
fx_string_toupper(msg_ucase);
|
||||
|
||||
emit(ctx,
|
||||
"struct %s_%s_msg msg_data = {0};\n",
|
||||
@@ -316,12 +327,12 @@ static int emit_interface_msg_function_send_impl(
|
||||
emit(ctx,
|
||||
"xpc_msg_header_init(&msg_data.msg_header, INTERFACE_%s, "
|
||||
"MSG_%s_%s);\n",
|
||||
b_string_ptr(iface_ucase),
|
||||
b_string_ptr(iface_ucase),
|
||||
b_string_ptr(msg_ucase));
|
||||
fx_string_ptr(iface_ucase),
|
||||
fx_string_ptr(iface_ucase),
|
||||
fx_string_ptr(msg_ucase));
|
||||
|
||||
b_string_unref(iface_ucase);
|
||||
b_string_unref(msg_ucase);
|
||||
fx_string_unref(iface_ucase);
|
||||
fx_string_unref(msg_ucase);
|
||||
|
||||
emit(ctx, "uint32_t in_offset = sizeof msg_data;\n\n");
|
||||
emit(ctx, "uint32_t out_offset = sizeof msg_data;\n\n");
|
||||
@@ -329,13 +340,14 @@ static int emit_interface_msg_function_send_impl(
|
||||
size_t nr_req_handles = 0;
|
||||
size_t nr_resp_handles = 0;
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_INT:
|
||||
case TYPE_SIZE:
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx,
|
||||
"msg_data.msg_request.%s = %s;\n\n",
|
||||
param->p_name,
|
||||
@@ -362,7 +374,7 @@ static int emit_interface_msg_function_send_impl(
|
||||
break;
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx,
|
||||
"msg_data.msg_request.%s_offset = offset;\n",
|
||||
"msg_data.msg_request.%s_offset = in_offset;\n",
|
||||
param->p_name);
|
||||
emit(ctx,
|
||||
"msg_data.msg_request.%s_len = %s_len;\n",
|
||||
@@ -376,13 +388,13 @@ static int emit_interface_msg_function_send_impl(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_STRING:
|
||||
case TYPE_BUFFER:
|
||||
@@ -403,7 +415,7 @@ static int emit_interface_msg_function_send_impl(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, "kern_iovec_t req_iov[] = {\n");
|
||||
@@ -411,10 +423,10 @@ static int emit_interface_msg_function_send_impl(
|
||||
emit(ctx, "IOVEC(&msg_data, sizeof msg_data),\n");
|
||||
size_t nr_req_iov = 1;
|
||||
|
||||
entry = b_queue_first(&msg->msg_params);
|
||||
entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_STRING:
|
||||
@@ -429,7 +441,7 @@ static int emit_interface_msg_function_send_impl(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit_unindent(ctx);
|
||||
@@ -446,10 +458,10 @@ static int emit_interface_msg_function_send_impl(
|
||||
emit(ctx, "IOVEC(&msg_data, sizeof msg_data),\n");
|
||||
size_t nr_resp_iov = 1;
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_STRING:
|
||||
@@ -464,7 +476,7 @@ static int emit_interface_msg_function_send_impl(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit_unindent(ctx);
|
||||
@@ -511,10 +523,10 @@ static int emit_interface_msg_function_send_impl(
|
||||
"if (msg_data.msg_header.hdr_status != KERN_OK) return "
|
||||
"msg_data.msg_header.hdr_status;\n\n");
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_HANDLE:
|
||||
emit(ctx, "*out_%s = ", param->p_name);
|
||||
@@ -540,7 +552,7 @@ static int emit_interface_msg_function_send_impl(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, "return status;\n");
|
||||
@@ -563,10 +575,10 @@ static int emit_interface_msg_function_send(
|
||||
|
||||
emit(ctx, "kern_handle_t port");
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
emit(ctx, ",\n");
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_INT:
|
||||
@@ -578,6 +590,9 @@ static int emit_interface_msg_function_send(
|
||||
case TYPE_SIZE:
|
||||
emit(ctx, "size_t %s", param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx, "off_t %s", param->p_name);
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
emit(ctx, "const char *%s", param->p_name);
|
||||
break;
|
||||
@@ -589,13 +604,13 @@ static int emit_interface_msg_function_send(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
emit(ctx, ",\n");
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_INT:
|
||||
@@ -604,6 +619,9 @@ static int emit_interface_msg_function_send(
|
||||
case TYPE_SIZE:
|
||||
emit(ctx, "size_t *out_%s", param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx, "off_t *out_%s", param->p_name);
|
||||
break;
|
||||
case TYPE_HANDLE:
|
||||
emit(ctx, "kern_handle_t *out_%s", param->p_name);
|
||||
break;
|
||||
@@ -622,7 +640,7 @@ static int emit_interface_msg_function_send(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, ")");
|
||||
@@ -663,10 +681,10 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
"status = xpc_msg_read(msg, 0, &" MSG_STRUCT_NAME
|
||||
", sizeof " MSG_STRUCT_NAME ");\n");
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_INT:
|
||||
emit(ctx,
|
||||
@@ -681,6 +699,12 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
param->p_name,
|
||||
param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx,
|
||||
"off_t %s = " MSG_STRUCT_NAME ".msg_request.%s;\n",
|
||||
param->p_name,
|
||||
param->p_name);
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
emit(ctx,
|
||||
"xpc_string_t %s = "
|
||||
@@ -693,7 +717,7 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
break;
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx,
|
||||
"xpc_string_t %s = "
|
||||
"xpc_buffer_t %s = "
|
||||
"XPC_BUFFER_IN(msg, " MSG_STRUCT_NAME
|
||||
".msg_request.%s_offset, " MSG_STRUCT_NAME
|
||||
".msg_request.%s_len);\n",
|
||||
@@ -705,13 +729,13 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_INT:
|
||||
emit(ctx, "int %s = 0;\n", param->p_name);
|
||||
@@ -719,6 +743,9 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
case TYPE_SIZE:
|
||||
emit(ctx, "size_t %s = 0;\n", param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx, "off_t %s = 0;\n", param->p_name);
|
||||
break;
|
||||
case TYPE_HANDLE:
|
||||
emit(ctx,
|
||||
"kern_handle_t %s = KERN_HANDLE_INVALID;\n",
|
||||
@@ -748,16 +775,16 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, "status = vtable->%s(\n", msg->msg_name);
|
||||
emit_indent(ctx);
|
||||
emit(ctx, "ctx,\n&msg->msg_sender");
|
||||
entry = b_queue_first(&msg->msg_params);
|
||||
entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_STRING:
|
||||
@@ -769,15 +796,15 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
emit(ctx, ",\n&%s", param->p_name);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx, ",\narg);\n");
|
||||
@@ -789,12 +816,14 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
|
||||
emit(ctx, MSG_STRUCT_NAME ".msg_header.hdr_status = 0;\n");
|
||||
size_t handle_index = 0;
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_INT:
|
||||
case TYPE_SIZE:
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx,
|
||||
MSG_STRUCT_NAME ".msg_response.%s = %s;\n",
|
||||
param->p_name,
|
||||
@@ -811,7 +840,7 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit(ctx,
|
||||
@@ -820,10 +849,10 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
|
||||
emit(ctx, "kern_msg_handle_t out_handles[] = {\n");
|
||||
emit_indent(ctx);
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
switch (param->p_type->ty_id) {
|
||||
case TYPE_HANDLE:
|
||||
emit(ctx,
|
||||
@@ -834,7 +863,7 @@ static int emit_interface_dispatcher_impl_msg(
|
||||
break;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
emit_unindent(ctx);
|
||||
emit(ctx, "};\n");
|
||||
@@ -850,41 +879,41 @@ static int emit_interface_dispatcher_impl(
|
||||
struct emit_ctx *ctx,
|
||||
const struct interface_definition *iface)
|
||||
{
|
||||
b_string *iface_ucase = b_string_create_from_cstr(iface->if_name);
|
||||
b_string_toupper(iface_ucase);
|
||||
b_string *msg_ucase = b_string_create();
|
||||
fx_string *iface_ucase = fx_string_create_from_cstr(iface->if_name);
|
||||
fx_string_toupper(iface_ucase);
|
||||
fx_string *msg_ucase = fx_string_create();
|
||||
|
||||
emit(ctx,
|
||||
"if (msg->msg_header.hdr_interface != INTERFACE_%s) return "
|
||||
"KERN_INVALID_ARGUMENT;\n\n",
|
||||
b_string_ptr(iface_ucase));
|
||||
fx_string_ptr(iface_ucase));
|
||||
|
||||
emit(ctx, "kern_status_t status = KERN_OK;\n");
|
||||
emit(ctx, "switch (msg->msg_header.hdr_func) {\n");
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
const struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
|
||||
b_string_clear(msg_ucase);
|
||||
b_string_append_cstr(msg_ucase, msg->msg_name);
|
||||
b_string_toupper(msg_ucase);
|
||||
fx_string_clear(msg_ucase);
|
||||
fx_string_append_cstr(msg_ucase, msg->msg_name);
|
||||
fx_string_toupper(msg_ucase);
|
||||
|
||||
emit(ctx,
|
||||
"case MSG_%s_%s: {\n",
|
||||
b_string_ptr(iface_ucase),
|
||||
b_string_ptr(msg_ucase));
|
||||
fx_string_ptr(iface_ucase),
|
||||
fx_string_ptr(msg_ucase));
|
||||
emit_indent(ctx);
|
||||
emit_interface_dispatcher_impl_msg(ctx, iface, msg);
|
||||
emit(ctx, "break;\n");
|
||||
emit_unindent(ctx);
|
||||
emit(ctx, "}\n");
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
b_string_unref(iface_ucase);
|
||||
fx_string_unref(iface_ucase);
|
||||
|
||||
emit(ctx, "default:\n");
|
||||
emit_indent(ctx);
|
||||
@@ -933,10 +962,10 @@ static int emit_interface_functions(
|
||||
const struct interface_definition *iface,
|
||||
bool prototype_only)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
const struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
|
||||
emit_interface_msg_function_send(
|
||||
ctx,
|
||||
@@ -944,7 +973,7 @@ static int emit_interface_functions(
|
||||
msg,
|
||||
prototype_only);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit_interface_dispatcher(ctx, iface, prototype_only);
|
||||
@@ -963,10 +992,10 @@ static int emit_interface_vtable_entry(
|
||||
emit(ctx, "const xpc_endpoint_t *sender");
|
||||
|
||||
size_t i = 0;
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
emit(ctx, ",\n");
|
||||
|
||||
switch (param->p_type->ty_id) {
|
||||
@@ -976,6 +1005,9 @@ static int emit_interface_vtable_entry(
|
||||
case TYPE_SIZE:
|
||||
emit(ctx, "size_t %s", param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx, "off_t %s", param->p_name);
|
||||
break;
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx, "const xpc_buffer_t *%s", param->p_name);
|
||||
break;
|
||||
@@ -990,13 +1022,13 @@ static int emit_interface_vtable_entry(
|
||||
}
|
||||
|
||||
i++;
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
emit(ctx, ",\n");
|
||||
|
||||
switch (param->p_type->ty_id) {
|
||||
@@ -1012,6 +1044,9 @@ static int emit_interface_vtable_entry(
|
||||
case TYPE_SIZE:
|
||||
emit(ctx, "size_t *out_%s", param->p_name);
|
||||
break;
|
||||
case TYPE_OFFSET:
|
||||
emit(ctx, "off_t *out_%s", param->p_name);
|
||||
break;
|
||||
case TYPE_BUFFER:
|
||||
emit(ctx, "xpc_buffer_t *out_%s", param->p_name);
|
||||
break;
|
||||
@@ -1020,7 +1055,7 @@ static int emit_interface_vtable_entry(
|
||||
}
|
||||
|
||||
i++;
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
@@ -1042,14 +1077,14 @@ static int emit_interface_vtable(
|
||||
emit(ctx, "struct %s_vtable {\n", iface->if_name);
|
||||
emit_indent(ctx);
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
const struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
|
||||
emit_interface_vtable_entry(ctx, iface, msg);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
emit_unindent(ctx);
|
||||
@@ -1092,16 +1127,16 @@ static int emit_header(
|
||||
static int emit_interface(const struct interface_definition *iface)
|
||||
{
|
||||
char path[4096];
|
||||
b_file *file = NULL;
|
||||
fx_file *file = NULL;
|
||||
|
||||
snprintf(path, sizeof path, "%s.h", iface->if_name);
|
||||
b_result result = b_file_open(
|
||||
fx_result result = fx_file_open(
|
||||
NULL,
|
||||
B_RV_PATH(path),
|
||||
B_FILE_WRITE_ONLY | B_FILE_CREATE,
|
||||
FX_RV_PATH(path),
|
||||
FX_FILE_WRITE_ONLY | FX_FILE_CREATE | FX_FILE_TRUNCATE,
|
||||
&file);
|
||||
if (b_result_is_error(result)) {
|
||||
b_throw(result);
|
||||
if (fx_result_is_error(result)) {
|
||||
fx_throw(result);
|
||||
fprintf(stderr, "cannot create C header file %s\n", path);
|
||||
return -1;
|
||||
}
|
||||
@@ -1112,7 +1147,7 @@ static int emit_interface(const struct interface_definition *iface)
|
||||
};
|
||||
|
||||
int err = emit_header(&ctx, iface);
|
||||
b_file_unref(file);
|
||||
fx_file_unref(file);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ const struct type *ctx_get_type(struct ctx *ctx, const char *name)
|
||||
return ctx_get_builtin_type(ctx, TYPE_SIZE);
|
||||
}
|
||||
|
||||
if (!strcmp(name, "offset")) {
|
||||
return ctx_get_builtin_type(ctx, TYPE_OFFSET);
|
||||
}
|
||||
|
||||
if (!strcmp(name, "buffer")) {
|
||||
return ctx_get_builtin_type(ctx, TYPE_BUFFER);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "msg.h"
|
||||
|
||||
#include <blue/ds/string.h>
|
||||
#include <fx/ds/string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -17,7 +17,7 @@ struct interface_definition *interface_definition_create(
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->if_name = b_strdup(name);
|
||||
out->if_name = fx_strdup(name);
|
||||
out->if_id = id;
|
||||
|
||||
return out;
|
||||
@@ -25,12 +25,12 @@ struct interface_definition *interface_definition_create(
|
||||
|
||||
void interface_definition_destroy(struct interface_definition *iface)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_pop_front(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_pop_front(&iface->if_msg);
|
||||
while (entry) {
|
||||
struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
msg_definition_destroy(msg);
|
||||
entry = b_queue_pop_front(&iface->if_msg);
|
||||
entry = fx_queue_pop_front(&iface->if_msg);
|
||||
}
|
||||
|
||||
free(iface->if_name);
|
||||
@@ -41,15 +41,15 @@ bool interface_definition_has_msg(
|
||||
const struct interface_definition *iface,
|
||||
const char *name)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
|
||||
while (entry) {
|
||||
struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
if (!strcmp(msg->msg_name, name)) {
|
||||
return true;
|
||||
}
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -59,5 +59,5 @@ void interface_definition_add_msg(
|
||||
struct interface_definition *iface,
|
||||
struct msg_definition *msg)
|
||||
{
|
||||
b_queue_push_back(&iface->if_msg, &msg->msg_entry);
|
||||
fx_queue_push_back(&iface->if_msg, &msg->msg_entry);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#ifndef XPCG_INTERFACE_H_
|
||||
#define XPCG_INTERFACE_H_
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <fx/core/queue.h>
|
||||
|
||||
struct msg_definition;
|
||||
|
||||
struct interface_definition {
|
||||
char *if_name;
|
||||
long long if_id;
|
||||
b_queue if_msg;
|
||||
fx_queue if_msg;
|
||||
};
|
||||
|
||||
extern struct interface_definition *interface_definition_create(
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
#include "line-source.h"
|
||||
#include "token.h"
|
||||
|
||||
#include <blue/core/hash.h>
|
||||
#include <blue/core/misc.h>
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/ds/dict.h>
|
||||
#include <blue/ds/number.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <fx/core/hash.h>
|
||||
#include <fx/core/misc.h>
|
||||
#include <fx/core/queue.h>
|
||||
#include <fx/ds/dict.h>
|
||||
#include <fx/ds/number.h>
|
||||
#include <fx/ds/string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
@@ -21,10 +21,10 @@
|
||||
#define LEX_TOKEN_DEF(i, n) {.id = (i), .name = (n)}
|
||||
|
||||
#define IS_VALID_IDENT_CHAR(c) \
|
||||
(b_wchar_is_alnum(c) || c == '.' || c == '-' || c == '_')
|
||||
(fx_wchar_is_alnum(c) || c == '.' || c == '-' || c == '_')
|
||||
#define IS_VALID_IDENT_START_CHAR(c) \
|
||||
(b_wchar_is_alpha(c) || c == '.' || c == '_')
|
||||
#define IS_VALID_REG_START_CHAR(c) (b_wchar_is_alnum(c) || c == '.' || c == '_')
|
||||
(fx_wchar_is_alpha(c) || c == '.' || c == '_')
|
||||
#define IS_VALID_REG_START_CHAR(c) (fx_wchar_is_alnum(c) || c == '.' || c == '_')
|
||||
|
||||
static struct lex_token_def symbols[] = {
|
||||
LEX_TOKEN_DEF(SYM_COMMA, ","),
|
||||
@@ -51,27 +51,27 @@ static struct lex_symbol_node *get_symbol_node(
|
||||
struct lex_symbol_node *node,
|
||||
char c)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&node->s_children);
|
||||
fx_queue_entry *entry = fx_queue_first(&node->s_children);
|
||||
while (entry) {
|
||||
struct lex_symbol_node *child
|
||||
= b_unbox(struct lex_symbol_node, entry, s_entry);
|
||||
= fx_unbox(struct lex_symbol_node, entry, s_entry);
|
||||
if (child->s_char == c) {
|
||||
return child;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static b_string *get_temp_string(struct lex *lex)
|
||||
static fx_string *get_temp_string(struct lex *lex)
|
||||
{
|
||||
if (!lex->lex_temp) {
|
||||
lex->lex_temp = b_string_create();
|
||||
lex->lex_temp = fx_string_create();
|
||||
}
|
||||
|
||||
b_string_clear(lex->lex_temp);
|
||||
fx_string_clear(lex->lex_temp);
|
||||
return lex->lex_temp;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ static enum status put_symbol(
|
||||
child->s_def = NULL;
|
||||
child->s_char = c;
|
||||
|
||||
b_queue_push_back(&tree->s_children, &child->s_entry);
|
||||
fx_queue_push_back(&tree->s_children, &child->s_entry);
|
||||
tree = child;
|
||||
}
|
||||
|
||||
@@ -107,12 +107,12 @@ static enum status put_symbol(
|
||||
|
||||
static void destroy_symbol_tree(struct lex_symbol_node *tree)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&tree->s_children);
|
||||
fx_queue_entry *entry = fx_queue_first(&tree->s_children);
|
||||
while (entry) {
|
||||
struct lex_symbol_node *node
|
||||
= b_unbox(struct lex_symbol_node, entry, s_entry);
|
||||
b_queue_entry *next = b_queue_next(entry);
|
||||
b_queue_delete(&tree->s_children, entry);
|
||||
= fx_unbox(struct lex_symbol_node, entry, s_entry);
|
||||
fx_queue_entry *next = fx_queue_next(entry);
|
||||
fx_queue_delete(&tree->s_children, entry);
|
||||
|
||||
destroy_symbol_tree(node);
|
||||
|
||||
@@ -168,12 +168,12 @@ struct lex *lex_create(struct line_source *src)
|
||||
|
||||
void lex_destroy(struct lex *lex)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&lex->lex_queue);
|
||||
fx_queue_entry *entry = fx_queue_first(&lex->lex_queue);
|
||||
|
||||
while (entry) {
|
||||
struct token *tok = b_unbox(struct token, entry, tok_entry);
|
||||
b_queue_entry *next = b_queue_next(entry);
|
||||
b_queue_delete(&lex->lex_queue, entry);
|
||||
struct token *tok = fx_unbox(struct token, entry, tok_entry);
|
||||
fx_queue_entry *next = fx_queue_next(entry);
|
||||
fx_queue_delete(&lex->lex_queue, entry);
|
||||
|
||||
token_destroy(tok);
|
||||
|
||||
@@ -185,7 +185,7 @@ void lex_destroy(struct lex *lex)
|
||||
}
|
||||
|
||||
if (lex->lex_temp) {
|
||||
b_string_unref(lex->lex_temp);
|
||||
fx_string_unref(lex->lex_temp);
|
||||
}
|
||||
|
||||
free(lex);
|
||||
@@ -245,7 +245,7 @@ static enum status push_token(struct lex *lex, struct token *tok)
|
||||
tok->tok_location.s_start = lex->lex_token_start;
|
||||
tok->tok_location.s_end = lex->lex_token_end;
|
||||
|
||||
b_queue_push_back(&lex->lex_queue, &tok->tok_entry);
|
||||
fx_queue_push_back(&lex->lex_queue, &tok->tok_entry);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ static enum status push_int(struct lex *lex, unsigned long long v)
|
||||
static enum status read_line_comment(struct lex *lex)
|
||||
{
|
||||
while (true) {
|
||||
b_wchar c = line_source_getc(lex->lex_source);
|
||||
fx_wchar c = line_source_getc(lex->lex_source);
|
||||
|
||||
if (c == -ERR_EOF || c == '\n') {
|
||||
break;
|
||||
@@ -345,14 +345,14 @@ static enum status read_number(struct lex *lex, bool negate)
|
||||
int token_len = 0;
|
||||
int base = 10;
|
||||
int dots = 0;
|
||||
b_string *str = get_temp_string(lex);
|
||||
fx_string *str = get_temp_string(lex);
|
||||
|
||||
if (!negate) {
|
||||
set_token_start(lex);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
if (c == -ERR_EOF) {
|
||||
break;
|
||||
}
|
||||
@@ -372,7 +372,7 @@ static enum status read_number(struct lex *lex, bool negate)
|
||||
return ERR_BAD_SYNTAX;
|
||||
}
|
||||
|
||||
if (b_wchar_is_space(c) || b_wchar_is_punct(c)) {
|
||||
if (fx_wchar_is_space(c) || fx_wchar_is_punct(c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ static enum status read_number(struct lex *lex, bool negate)
|
||||
return ERR_BAD_SYNTAX;
|
||||
}
|
||||
|
||||
b_string_append_wc(str, c);
|
||||
fx_string_append_wc(str, c);
|
||||
set_token_end(lex);
|
||||
line_source_getc(lex->lex_source);
|
||||
token_len++;
|
||||
@@ -414,7 +414,7 @@ static enum status read_number(struct lex *lex, bool negate)
|
||||
return push_int(lex, 0);
|
||||
}
|
||||
|
||||
const char *s = b_string_ptr(str);
|
||||
const char *s = fx_string_ptr(str);
|
||||
char *ep = NULL;
|
||||
|
||||
/* negative numbers will be lexed as a hyphen followed by a positive
|
||||
@@ -447,15 +447,15 @@ static enum token_keyword find_keyword(const char *s)
|
||||
static enum status read_ident(struct lex *lex, enum token_type type)
|
||||
{
|
||||
int dots = 0;
|
||||
b_string *str = get_temp_string(lex);
|
||||
b_wchar prev = 0;
|
||||
fx_string *str = get_temp_string(lex);
|
||||
fx_wchar prev = 0;
|
||||
|
||||
if (type == TOK_NONE) {
|
||||
set_token_start(lex);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
|
||||
if ((c == '.' || c == '-') && prev == c) {
|
||||
return ERR_BAD_SYNTAX;
|
||||
@@ -470,7 +470,7 @@ static enum status read_ident(struct lex *lex, enum token_type type)
|
||||
}
|
||||
|
||||
prev = c;
|
||||
b_string_append_wc(str, c);
|
||||
fx_string_append_wc(str, c);
|
||||
set_token_end(lex);
|
||||
line_source_getc(lex->lex_source);
|
||||
}
|
||||
@@ -479,7 +479,7 @@ static enum status read_ident(struct lex *lex, enum token_type type)
|
||||
type = dots > 0 ? TOK_NAME : TOK_WORD;
|
||||
}
|
||||
|
||||
char *s = b_string_steal(str);
|
||||
char *s = fx_string_steal(str);
|
||||
enum token_keyword kw = find_keyword(s);
|
||||
if (kw != KW_NONE) {
|
||||
free(s);
|
||||
@@ -491,9 +491,9 @@ static enum status read_ident(struct lex *lex, enum token_type type)
|
||||
|
||||
static enum status read_string(struct lex *lex)
|
||||
{
|
||||
b_string *str = get_temp_string(lex);
|
||||
fx_string *str = get_temp_string(lex);
|
||||
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
bool esc = false;
|
||||
|
||||
if (c != '"') {
|
||||
@@ -503,13 +503,13 @@ static enum status read_string(struct lex *lex)
|
||||
line_source_getc(lex->lex_source);
|
||||
|
||||
while (1) {
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
|
||||
if (esc) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
b_string_append_wc(str, c);
|
||||
fx_string_append_wc(str, c);
|
||||
break;
|
||||
default:
|
||||
return ERR_BAD_SYNTAX;
|
||||
@@ -531,11 +531,11 @@ static enum status read_string(struct lex *lex)
|
||||
break;
|
||||
}
|
||||
|
||||
b_string_append_wc(str, c);
|
||||
fx_string_append_wc(str, c);
|
||||
line_source_getc(lex->lex_source);
|
||||
}
|
||||
|
||||
char *s = b_string_steal(str);
|
||||
char *s = fx_string_steal(str);
|
||||
return push_string_token(lex, TOK_STRING, s);
|
||||
}
|
||||
|
||||
@@ -543,10 +543,10 @@ static enum status read_symbol(struct lex *lex)
|
||||
{
|
||||
struct lex_symbol_node *node = lex->lex_sym_tree;
|
||||
set_token_start(lex);
|
||||
b_wchar prev = 0;
|
||||
fx_wchar prev = 0;
|
||||
|
||||
while (true) {
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -576,17 +576,17 @@ static enum status read_symbol(struct lex *lex)
|
||||
|
||||
static void skip_whitespace(struct lex *lex)
|
||||
{
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
|
||||
while (b_wchar_is_space(c)) {
|
||||
while (fx_wchar_is_space(c)) {
|
||||
line_source_getc(lex->lex_source);
|
||||
c = line_source_peekc(lex->lex_source);
|
||||
}
|
||||
}
|
||||
|
||||
static bool should_skip(b_wchar c, bool skip_linefeeds)
|
||||
static bool should_skip(fx_wchar c, bool skip_linefeeds)
|
||||
{
|
||||
bool skip = b_wchar_is_space(c);
|
||||
bool skip = fx_wchar_is_space(c);
|
||||
|
||||
if (!skip_linefeeds) {
|
||||
skip = (skip && c != '\n');
|
||||
@@ -597,7 +597,7 @@ static bool should_skip(b_wchar c, bool skip_linefeeds)
|
||||
|
||||
static void skip_ignored_chars(struct lex *lex, bool include_linefeeds)
|
||||
{
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
|
||||
while (1) {
|
||||
while (should_skip(c, include_linefeeds)) {
|
||||
@@ -624,14 +624,14 @@ static void skip_ignored_chars(struct lex *lex, bool include_linefeeds)
|
||||
|
||||
static enum status pump_tokens(struct lex *lex)
|
||||
{
|
||||
b_wchar c = line_source_peekc(lex->lex_source);
|
||||
fx_wchar c = line_source_peekc(lex->lex_source);
|
||||
|
||||
if (c < 0) {
|
||||
return -c;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (c == '#' || (b_wchar_is_space(c) && c != '\n')) {
|
||||
if (c == '#' || (fx_wchar_is_space(c) && c != '\n')) {
|
||||
skip_ignored_chars(lex, false);
|
||||
} else {
|
||||
break;
|
||||
@@ -667,7 +667,7 @@ static enum status pump_tokens(struct lex *lex)
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
while (b_wchar_is_space(c) && c != '\n') {
|
||||
while (fx_wchar_is_space(c) && c != '\n') {
|
||||
line_source_getc(lex->lex_source);
|
||||
c = line_source_peekc(lex->lex_source);
|
||||
}
|
||||
@@ -695,7 +695,7 @@ struct token *lex_peek(struct lex *lex)
|
||||
{
|
||||
enum status status = SUCCESS;
|
||||
|
||||
while (b_queue_empty(&lex->lex_queue)) {
|
||||
while (fx_queue_empty(&lex->lex_queue)) {
|
||||
status = pump_tokens(lex);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
@@ -706,8 +706,8 @@ struct token *lex_peek(struct lex *lex)
|
||||
|
||||
lex->lex_status = status;
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&lex->lex_queue);
|
||||
struct token *tok = b_unbox(struct token, entry, tok_entry);
|
||||
fx_queue_entry *entry = fx_queue_first(&lex->lex_queue);
|
||||
struct token *tok = fx_unbox(struct token, entry, tok_entry);
|
||||
return tok;
|
||||
}
|
||||
|
||||
@@ -715,7 +715,7 @@ void lex_advance(struct lex *lex)
|
||||
{
|
||||
enum status status = SUCCESS;
|
||||
|
||||
while (b_queue_empty(&lex->lex_queue)) {
|
||||
while (fx_queue_empty(&lex->lex_queue)) {
|
||||
status = pump_tokens(lex);
|
||||
|
||||
if (status != SUCCESS) {
|
||||
@@ -724,14 +724,14 @@ void lex_advance(struct lex *lex)
|
||||
}
|
||||
}
|
||||
|
||||
b_queue_entry *entry = b_queue_pop_front(&lex->lex_queue);
|
||||
struct token *tok = b_unbox(struct token, entry, tok_entry);
|
||||
fx_queue_entry *entry = fx_queue_pop_front(&lex->lex_queue);
|
||||
struct token *tok = fx_unbox(struct token, entry, tok_entry);
|
||||
token_destroy(tok);
|
||||
}
|
||||
|
||||
bool lex_tokens_available(struct lex *lex)
|
||||
{
|
||||
if (!b_queue_empty(&lex->lex_queue)) {
|
||||
if (!fx_queue_empty(&lex->lex_queue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#include "status.h"
|
||||
#include "token.h"
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/ds/dict.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <fx/core/queue.h>
|
||||
#include <fx/ds/dict.h>
|
||||
#include <fx/ds/string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct lex {
|
||||
@@ -14,10 +14,10 @@ struct lex {
|
||||
struct line_source *lex_source;
|
||||
enum status lex_status;
|
||||
|
||||
b_queue lex_queue;
|
||||
fx_queue lex_queue;
|
||||
|
||||
b_string *lex_temp;
|
||||
b_queue lex_state;
|
||||
fx_string *lex_temp;
|
||||
fx_queue lex_state;
|
||||
unsigned int lex_brace_depth;
|
||||
|
||||
struct file_cell lex_token_start, lex_token_end;
|
||||
@@ -27,8 +27,8 @@ struct lex_symbol_node {
|
||||
char s_char;
|
||||
struct lex_token_def *s_def;
|
||||
|
||||
b_queue_entry s_entry;
|
||||
b_queue s_children;
|
||||
fx_queue_entry s_entry;
|
||||
fx_queue s_children;
|
||||
};
|
||||
|
||||
struct lex_token_def {
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
enum status line_source_init(
|
||||
struct line_source *src,
|
||||
const char *path,
|
||||
b_stream *stream)
|
||||
fx_stream *stream)
|
||||
{
|
||||
memset(src, 0x0, sizeof *src);
|
||||
|
||||
src->s_lines = b_array_create();
|
||||
src->s_lines = fx_array_create();
|
||||
|
||||
if (!src->s_lines) {
|
||||
return ERR_NO_MEMORY;
|
||||
@@ -24,11 +24,11 @@ enum status line_source_init(
|
||||
void line_source_cleanup(struct line_source *src)
|
||||
{
|
||||
if (src->s_linebuf_ptr) {
|
||||
b_iterator_unref(src->s_linebuf_ptr);
|
||||
fx_iterator_unref(src->s_linebuf_ptr);
|
||||
}
|
||||
|
||||
if (src->s_lines) {
|
||||
b_array_unref(src->s_lines);
|
||||
fx_array_unref(src->s_lines);
|
||||
}
|
||||
|
||||
memset(src, 0x0, sizeof *src);
|
||||
@@ -51,31 +51,31 @@ static enum status refill_linebuf(struct line_source *src)
|
||||
}
|
||||
|
||||
if (src->s_linebuf_ptr) {
|
||||
b_iterator_unref(src->s_linebuf_ptr);
|
||||
fx_iterator_unref(src->s_linebuf_ptr);
|
||||
src->s_linebuf_ptr = NULL;
|
||||
}
|
||||
|
||||
b_stringstream *s = b_stringstream_create();
|
||||
fx_stringstream *s = fx_stringstream_create();
|
||||
|
||||
b_status status = b_stream_read_line_s(src->s_stream, s);
|
||||
fx_status status = fx_stream_read_line_s(src->s_stream, s);
|
||||
|
||||
if (status == B_ERR_NO_DATA) {
|
||||
if (status == FX_ERR_NO_DATA) {
|
||||
return ERR_EOF;
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
if (!FX_OK(status)) {
|
||||
return ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
b_string *line = b_string_create();
|
||||
b_string_replace_all_with_stringstream(line, s);
|
||||
b_stringstream_unref(s);
|
||||
fx_string *line = fx_string_create();
|
||||
fx_string_replace_all_with_stringstream(line, s);
|
||||
fx_stringstream_unref(s);
|
||||
|
||||
b_array_append(src->s_lines, line);
|
||||
b_string_unref(line);
|
||||
fx_array_append(src->s_lines, line);
|
||||
fx_string_unref(line);
|
||||
|
||||
src->s_linebuf = line;
|
||||
src->s_linebuf_ptr = b_iterator_begin(src->s_linebuf);
|
||||
src->s_linebuf_ptr = fx_iterator_begin(src->s_linebuf);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -84,7 +84,7 @@ static int peek(struct line_source *src)
|
||||
{
|
||||
enum status status = SUCCESS;
|
||||
|
||||
if (!src->s_linebuf_ptr || !b_iterator_is_valid(src->s_linebuf_ptr)) {
|
||||
if (!src->s_linebuf_ptr || !fx_iterator_is_valid(src->s_linebuf_ptr)) {
|
||||
status = refill_linebuf(src);
|
||||
}
|
||||
|
||||
@@ -92,11 +92,11 @@ static int peek(struct line_source *src)
|
||||
return -status;
|
||||
}
|
||||
|
||||
if (b_string_get_size(src->s_linebuf, B_STRLEN_NORMAL) == 0) {
|
||||
if (fx_string_get_size(src->s_linebuf, FX_STRLEN_NORMAL) == 0) {
|
||||
return -ERR_EOF;
|
||||
}
|
||||
|
||||
b_wchar c = b_iterator_get_value(src->s_linebuf_ptr).v_int;
|
||||
fx_wchar c = fx_iterator_get_value(src->s_linebuf_ptr).v_int;
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ static int advance(struct line_source *src)
|
||||
{
|
||||
enum status status = SUCCESS;
|
||||
|
||||
if (!b_iterator_is_valid(src->s_linebuf_ptr)) {
|
||||
if (!fx_iterator_is_valid(src->s_linebuf_ptr)) {
|
||||
status = refill_linebuf(src);
|
||||
}
|
||||
|
||||
@@ -112,12 +112,12 @@ static int advance(struct line_source *src)
|
||||
return -status;
|
||||
}
|
||||
|
||||
if (b_string_get_size(src->s_linebuf, B_STRLEN_NORMAL) == 0) {
|
||||
if (fx_string_get_size(src->s_linebuf, FX_STRLEN_NORMAL) == 0) {
|
||||
return -ERR_EOF;
|
||||
}
|
||||
|
||||
b_wchar c = b_iterator_get_value(src->s_linebuf_ptr).v_int;
|
||||
b_iterator_move_next(src->s_linebuf_ptr);
|
||||
fx_wchar c = fx_iterator_get_value(src->s_linebuf_ptr).v_int;
|
||||
fx_iterator_move_next(src->s_linebuf_ptr);
|
||||
|
||||
src->s_cursor.c_col++;
|
||||
if (c == '\n') {
|
||||
@@ -127,12 +127,12 @@ static int advance(struct line_source *src)
|
||||
return c;
|
||||
}
|
||||
|
||||
b_wchar line_source_peekc(struct line_source *src)
|
||||
fx_wchar line_source_peekc(struct line_source *src)
|
||||
{
|
||||
return peek(src);
|
||||
}
|
||||
|
||||
b_wchar line_source_getc(struct line_source *src)
|
||||
fx_wchar line_source_getc(struct line_source *src)
|
||||
{
|
||||
return advance(src);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ b_wchar line_source_getc(struct line_source *src)
|
||||
enum status line_source_get_row(
|
||||
struct line_source *src,
|
||||
size_t row,
|
||||
const b_string **out)
|
||||
const fx_string **out)
|
||||
{
|
||||
if (row == 0) {
|
||||
return ERR_INVALID_ARGUMENT;
|
||||
@@ -148,11 +148,11 @@ enum status line_source_get_row(
|
||||
|
||||
row--;
|
||||
|
||||
if (row >= b_array_size(src->s_lines)) {
|
||||
if (row >= fx_array_size(src->s_lines)) {
|
||||
return ERR_EOF;
|
||||
}
|
||||
|
||||
b_string *line = b_array_at(src->s_lines, row);
|
||||
fx_string *line = fx_array_at(src->s_lines, row);
|
||||
*out = line;
|
||||
|
||||
return SUCCESS;
|
||||
@@ -160,5 +160,5 @@ enum status line_source_get_row(
|
||||
|
||||
bool line_source_input_available(struct line_source *src)
|
||||
{
|
||||
return src->s_linebuf_ptr && b_iterator_is_valid(src->s_linebuf_ptr);
|
||||
return src->s_linebuf_ptr && fx_iterator_is_valid(src->s_linebuf_ptr);
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
#include "file-span.h"
|
||||
#include "status.h"
|
||||
|
||||
#include <blue/core/stream.h>
|
||||
#include <blue/ds/array.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <fx/core/stream.h>
|
||||
#include <fx/ds/array.h>
|
||||
#include <fx/ds/string.h>
|
||||
|
||||
struct line_source {
|
||||
b_stream *s_stream;
|
||||
fx_stream *s_stream;
|
||||
const char *s_path;
|
||||
b_string *s_linebuf;
|
||||
b_iterator *s_linebuf_ptr;
|
||||
b_array *s_lines;
|
||||
fx_string *s_linebuf;
|
||||
fx_iterator *s_linebuf_ptr;
|
||||
fx_array *s_lines;
|
||||
|
||||
struct file_cell s_cursor;
|
||||
};
|
||||
@@ -21,18 +21,18 @@ struct line_source {
|
||||
extern enum status line_source_init(
|
||||
struct line_source *src,
|
||||
const char *path,
|
||||
b_stream *stream);
|
||||
fx_stream *stream);
|
||||
extern void line_source_cleanup(struct line_source *src);
|
||||
|
||||
extern const char *line_source_get_path(const struct line_source *src);
|
||||
extern const struct file_cell *line_source_get_cursor(
|
||||
const struct line_source *src);
|
||||
extern b_wchar line_source_peekc(struct line_source *src);
|
||||
extern b_wchar line_source_getc(struct line_source *src);
|
||||
extern fx_wchar line_source_peekc(struct line_source *src);
|
||||
extern fx_wchar line_source_getc(struct line_source *src);
|
||||
extern enum status line_source_get_row(
|
||||
struct line_source *src,
|
||||
size_t row,
|
||||
const b_string **out);
|
||||
const fx_string **out);
|
||||
|
||||
extern bool line_source_input_available(struct line_source *src);
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include "msg.h"
|
||||
#include "parse.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/io/file.h>
|
||||
#include <blue/io/path.h>
|
||||
#include <fx/cmd.h>
|
||||
#include <fx/io/file.h>
|
||||
#include <fx/io/path.h>
|
||||
|
||||
#define CMD_ID 0
|
||||
|
||||
@@ -20,71 +20,71 @@ enum {
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
return b_command_dispatch(CMD_ID, argc, argv);
|
||||
return fx_command_dispatch(CMD_ID, argc, argv);
|
||||
}
|
||||
|
||||
static void print_msg(struct msg_definition *msg)
|
||||
{
|
||||
printf(" msg: %s\n", msg->msg_name);
|
||||
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
printf(" param:");
|
||||
type_print(param->p_type);
|
||||
printf(" %s\n", param->p_name);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
entry = b_queue_first(&msg->msg_results);
|
||||
entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
|
||||
printf(" result:");
|
||||
type_print(param->p_type);
|
||||
printf(" %s\n", param->p_name);
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_interface(struct interface_definition *iface)
|
||||
{
|
||||
printf("interface: %s\n", iface->if_name);
|
||||
b_queue_entry *entry = b_queue_first(&iface->if_msg);
|
||||
fx_queue_entry *entry = fx_queue_first(&iface->if_msg);
|
||||
while (entry) {
|
||||
struct msg_definition *msg
|
||||
= b_unbox(struct msg_definition, entry, msg_entry);
|
||||
= fx_unbox(struct msg_definition, entry, msg_entry);
|
||||
print_msg(msg);
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int xpcg(
|
||||
const b_command *self,
|
||||
const b_arglist *opt,
|
||||
const b_array *args)
|
||||
const fx_command *self,
|
||||
const fx_arglist *opt,
|
||||
const fx_array *args)
|
||||
{
|
||||
const char *path = NULL;
|
||||
b_arglist_get_string(opt, B_COMMAND_INVALID_ID, ARG_SRCPATH, 0, &path);
|
||||
fx_arglist_get_string(opt, FX_COMMAND_INVALID_ID, ARG_SRCPATH, 0, &path);
|
||||
if (!path) {
|
||||
b_arglist_report_missing_args(
|
||||
fx_arglist_report_missing_args(
|
||||
opt,
|
||||
B_COMMAND_INVALID_ID,
|
||||
FX_COMMAND_INVALID_ID,
|
||||
ARG_SRCPATH,
|
||||
0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
b_file *file = NULL;
|
||||
b_result result
|
||||
= b_file_open(NULL, B_RV_PATH(path), B_FILE_READ_ONLY, &file);
|
||||
if (b_result_is_error(result)) {
|
||||
b_throw(result);
|
||||
fx_file *file = NULL;
|
||||
fx_result result
|
||||
= fx_file_open(NULL, FX_RV_PATH(path), FX_FILE_READ_ONLY, &file);
|
||||
if (fx_result_is_error(result)) {
|
||||
fx_throw(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -133,37 +133,37 @@ static int xpcg(
|
||||
return err;
|
||||
}
|
||||
|
||||
B_COMMAND(CMD_ID, B_COMMAND_INVALID_ID)
|
||||
FX_COMMAND(CMD_ID, FX_COMMAND_INVALID_ID)
|
||||
{
|
||||
B_COMMAND_NAME("xpcg");
|
||||
B_COMMAND_DESC("xpc interface generator.");
|
||||
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
|
||||
B_COMMAND_FUNCTION(xpcg);
|
||||
B_COMMAND_HELP_OPTION();
|
||||
FX_COMMAND_NAME("xpcg");
|
||||
FX_COMMAND_DESC("xpc interface generator.");
|
||||
FX_COMMAND_FLAGS(FX_COMMAND_SHOW_HELP_BY_DEFAULT);
|
||||
FX_COMMAND_FUNCTION(xpcg);
|
||||
FX_COMMAND_HELP_OPTION();
|
||||
|
||||
B_COMMAND_ARG(ARG_SRCPATH)
|
||||
FX_COMMAND_ARG(ARG_SRCPATH)
|
||||
{
|
||||
B_ARG_NAME("source-file");
|
||||
B_ARG_DESC("the interface file to compile.");
|
||||
B_ARG_NR_VALUES(1);
|
||||
FX_ARG_NAME("source-file");
|
||||
FX_ARG_DESC("the interface file to compile.");
|
||||
FX_ARG_NR_VALUES(1);
|
||||
}
|
||||
|
||||
B_COMMAND_OPTION(OPT_BACKEND)
|
||||
FX_COMMAND_OPTION(OPT_BACKEND)
|
||||
{
|
||||
B_OPTION_LONG_NAME("backend");
|
||||
B_OPTION_SHORT_NAME('b');
|
||||
B_OPTION_DESC("which backend to use.");
|
||||
B_OPTION_ARG(OPT_BACKEND_ARG_NAME)
|
||||
FX_OPTION_LONG_NAME("backend");
|
||||
FX_OPTION_SHORT_NAME('b');
|
||||
FX_OPTION_DESC("which backend to use.");
|
||||
FX_OPTION_ARG(OPT_BACKEND_ARG_NAME)
|
||||
{
|
||||
B_ARG_NAME("backend-name");
|
||||
B_ARG_NR_VALUES(1);
|
||||
B_ARG_ALLOWED_VALUES("c-mpc");
|
||||
FX_ARG_NAME("backend-name");
|
||||
FX_ARG_NR_VALUES(1);
|
||||
FX_ARG_ALLOWED_VALUES("c-mpc");
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND_USAGE()
|
||||
FX_COMMAND_USAGE()
|
||||
{
|
||||
B_COMMAND_USAGE_ARG(ARG_SRCPATH);
|
||||
B_COMMAND_USAGE_OPT(OPT_BACKEND);
|
||||
FX_COMMAND_USAGE_ARG(ARG_SRCPATH);
|
||||
FX_COMMAND_USAGE_OPT(OPT_BACKEND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "msg.h"
|
||||
|
||||
#include <blue/ds/string.h>
|
||||
#include <fx/ds/string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -13,7 +13,7 @@ struct msg_definition *msg_definition_create(const char *name, long long id)
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->msg_name = b_strdup(name);
|
||||
out->msg_name = fx_strdup(name);
|
||||
out->msg_id = id;
|
||||
|
||||
return out;
|
||||
@@ -21,22 +21,22 @@ struct msg_definition *msg_definition_create(const char *name, long long id)
|
||||
|
||||
void msg_definition_destroy(struct msg_definition *msg)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_pop_front(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_pop_front(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
free(param->p_name);
|
||||
free(param);
|
||||
entry = b_queue_pop_front(&msg->msg_params);
|
||||
entry = fx_queue_pop_front(&msg->msg_params);
|
||||
}
|
||||
|
||||
entry = b_queue_pop_front(&msg->msg_results);
|
||||
entry = fx_queue_pop_front(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
free(param->p_name);
|
||||
free(param);
|
||||
entry = b_queue_pop_front(&msg->msg_results);
|
||||
entry = fx_queue_pop_front(&msg->msg_results);
|
||||
}
|
||||
|
||||
free(msg->msg_name);
|
||||
@@ -45,15 +45,15 @@ void msg_definition_destroy(struct msg_definition *msg)
|
||||
|
||||
bool msg_definition_has_param(struct msg_definition *msg, const char *name)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_params);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_params);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
if (!strcmp(param->p_name, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -61,15 +61,15 @@ bool msg_definition_has_param(struct msg_definition *msg, const char *name)
|
||||
|
||||
bool msg_definition_has_result(struct msg_definition *msg, const char *name)
|
||||
{
|
||||
b_queue_entry *entry = b_queue_first(&msg->msg_results);
|
||||
fx_queue_entry *entry = fx_queue_first(&msg->msg_results);
|
||||
while (entry) {
|
||||
struct msg_parameter *param
|
||||
= b_unbox(struct msg_parameter, entry, p_entry);
|
||||
= fx_unbox(struct msg_parameter, entry, p_entry);
|
||||
if (!strcmp(param->p_name, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
entry = b_queue_next(entry);
|
||||
entry = fx_queue_next(entry);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -88,9 +88,9 @@ int msg_definition_add_param(
|
||||
memset(param, 0x0, sizeof *param);
|
||||
|
||||
param->p_type = type;
|
||||
param->p_name = b_strdup(name);
|
||||
param->p_name = fx_strdup(name);
|
||||
|
||||
b_queue_push_back(&msg->msg_params, ¶m->p_entry);
|
||||
fx_queue_push_back(&msg->msg_params, ¶m->p_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ int msg_definition_add_result(
|
||||
memset(param, 0x0, sizeof *param);
|
||||
|
||||
param->p_type = type;
|
||||
param->p_name = b_strdup(name);
|
||||
param->p_name = fx_strdup(name);
|
||||
|
||||
b_queue_push_back(&msg->msg_results, ¶m->p_entry);
|
||||
fx_queue_push_back(&msg->msg_results, ¶m->p_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
#ifndef XPCG_MSG_H_
|
||||
#define XPCG_MSG_H_
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <fx/core/queue.h>
|
||||
|
||||
struct type;
|
||||
|
||||
struct msg_parameter {
|
||||
char *p_name;
|
||||
const struct type *p_type;
|
||||
b_queue_entry p_entry;
|
||||
fx_queue_entry p_entry;
|
||||
};
|
||||
|
||||
struct msg_definition {
|
||||
char *msg_name;
|
||||
long long msg_id;
|
||||
b_queue_entry msg_entry;
|
||||
fx_queue_entry msg_entry;
|
||||
|
||||
b_queue msg_params;
|
||||
b_queue msg_results;
|
||||
fx_queue msg_params;
|
||||
fx_queue msg_results;
|
||||
};
|
||||
|
||||
extern struct msg_definition *msg_definition_create(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "file-span.h"
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <fx/core/queue.h>
|
||||
|
||||
#define TOKEN_TYPE(tok) ((tok) ? (tok)->tok_type : TOK_NONE)
|
||||
#define TOKEN_IS(tok, type) ((tok) && ((tok)->tok_type & (type)) != 0)
|
||||
@@ -55,7 +55,7 @@ struct token {
|
||||
struct file_span tok_location;
|
||||
enum token_type tok_type;
|
||||
enum token_value_type tok_value_type;
|
||||
b_queue_entry tok_entry;
|
||||
fx_queue_entry tok_entry;
|
||||
|
||||
union {
|
||||
char *tok_str;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
enum type_id {
|
||||
TYPE_NONE,
|
||||
TYPE_INT,
|
||||
TYPE_OFFSET,
|
||||
TYPE_SIZE,
|
||||
TYPE_STRING,
|
||||
TYPE_BUFFER,
|
||||
|
||||
Reference in New Issue
Block a user