更新Wine+更新menuconfig

This commit is contained in:
2026-04-19 16:07:44 +08:00
parent 863f85ae4f
commit 739be1d7f7
13 changed files with 1642 additions and 229 deletions

View File

@@ -125,10 +125,27 @@ if(EXISTS "${CLEONOS_MENUCONFIG_CMAKE}")
endif() endif()
function(cl_set_bool_cache VAR_NAME DEFAULT_VALUE DOC_TEXT) function(cl_set_bool_cache VAR_NAME DEFAULT_VALUE DOC_TEXT)
if(NOT DEFINED ${VAR_NAME}) set(_raw_value "")
set(_has_value OFF)
if(DEFINED ${VAR_NAME})
set(_raw_value "${${VAR_NAME}}")
set(_has_value ON)
elseif(DEFINED ${VAR_NAME}_IS_ENABLED)
set(_raw_value "${${VAR_NAME}_IS_ENABLED}")
set(_has_value ON)
endif()
if(NOT _has_value)
set(${VAR_NAME} ${DEFAULT_VALUE} CACHE BOOL "${DOC_TEXT}") set(${VAR_NAME} ${DEFAULT_VALUE} CACHE BOOL "${DOC_TEXT}")
else() else()
set(${VAR_NAME} ${${VAR_NAME}} CACHE BOOL "${DOC_TEXT}" FORCE) string(TOUPPER "${_raw_value}" _raw_upper)
if(_raw_upper STREQUAL "Y" OR _raw_upper STREQUAL "M" OR _raw_upper STREQUAL "ON" OR _raw_upper STREQUAL "TRUE" OR _raw_upper STREQUAL "1")
set(_bool_value ON)
else()
set(_bool_value OFF)
endif()
set(${VAR_NAME} ${_bool_value} CACHE BOOL "${DOC_TEXT}" FORCE)
endif() endif()
endfunction() endfunction()

View File

@@ -53,6 +53,7 @@ static clks_bool clks_userland_probe_elf(const char *path, const char *tag) {
return CLKS_TRUE; return CLKS_TRUE;
} }
#if CLKS_CFG_USER_INIT_SCRIPT_PROBE
static void clks_userland_probe_init_script(void) { static void clks_userland_probe_init_script(void) {
const void *data; const void *data;
u64 size = 0ULL; u64 size = 0ULL;
@@ -67,6 +68,7 @@ static void clks_userland_probe_init_script(void) {
clks_log(CLKS_LOG_INFO, "USER", "INIT SCRIPT READY /SHELL/INIT.CMD"); clks_log(CLKS_LOG_INFO, "USER", "INIT SCRIPT READY /SHELL/INIT.CMD");
clks_log_hex(CLKS_LOG_INFO, "USER", "INIT_SCRIPT_SIZE", size); clks_log_hex(CLKS_LOG_INFO, "USER", "INIT_SCRIPT_SIZE", size);
} }
#endif
static clks_bool clks_userland_request_shell_exec(void) { static clks_bool clks_userland_request_shell_exec(void) {
u64 status = (u64)-1; u64 status = (u64)-1;

View File

@@ -4,193 +4,256 @@
"key": "CLEONOS_CLKS_ENABLE_AUDIO", "key": "CLEONOS_CLKS_ENABLE_AUDIO",
"title": "Audio Driver Init", "title": "Audio Driver Init",
"description": "Initialize kernel audio subsystem during boot.", "description": "Initialize kernel audio subsystem during boot.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_MOUSE", "key": "CLEONOS_CLKS_ENABLE_MOUSE",
"title": "PS/2 Mouse Input", "title": "PS/2 Mouse Input",
"description": "Initialize kernel PS/2 mouse input subsystem.", "description": "Initialize kernel PS/2 mouse input subsystem.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_DESKTOP", "key": "CLEONOS_CLKS_ENABLE_DESKTOP",
"title": "TTY2 Desktop", "title": "TTY2 Desktop",
"description": "Enable desktop compositor tick/update path on TTY2.", "description": "Enable desktop compositor tick/update path on TTY2.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_MOUSE"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_DRIVER_MANAGER", "key": "CLEONOS_CLKS_ENABLE_DRIVER_MANAGER",
"title": "Driver Manager", "title": "Driver Manager",
"description": "Initialize kernel ELF driver manager.", "description": "Initialize kernel ELF driver manager.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KELF", "key": "CLEONOS_CLKS_ENABLE_KELF",
"title": "KELF Executor", "title": "KELF Executor",
"description": "Enable kernel ELF app dispatcher and kelfd task.", "description": "Enable kernel ELF app dispatcher and kelfd task.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_DRIVER_MANAGER && CLEONOS_CLKS_ENABLE_ELFRUNNER_INIT",
"imply": [
"CLEONOS_CLKS_ENABLE_ELFRUNNER_PROBE",
"CLEONOS_CLKS_ENABLE_USER_SYSTEM_APP_PROBE"
]
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_USERLAND_AUTO_EXEC", "key": "CLEONOS_CLKS_ENABLE_USERLAND_AUTO_EXEC",
"title": "Auto Enter User Shell", "title": "Auto Enter User Shell",
"description": "Auto-exec /shell/shell.elf after kernel boot.", "description": "Auto-exec /shell/shell.elf after kernel boot.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USRD_TASK && CLEONOS_CLKS_ENABLE_KEYBOARD",
"select": [
"CLEONOS_CLKS_ENABLE_USRD_TASK",
"CLEONOS_CLKS_ENABLE_KEYBOARD"
],
"imply": [
"CLEONOS_CLKS_ENABLE_USER_INIT_SCRIPT_PROBE",
"CLEONOS_CLKS_ENABLE_SHELL_MODE_LOG"
]
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_HEAP_SELFTEST", "key": "CLEONOS_CLKS_ENABLE_HEAP_SELFTEST",
"title": "Heap Selftest", "title": "Heap Selftest",
"description": "Run kmalloc/kfree selftest during kernel boot.", "description": "Run kmalloc/kfree selftest during kernel boot.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_EXTERNAL_PSF", "key": "CLEONOS_CLKS_ENABLE_EXTERNAL_PSF",
"title": "Load External PSF Font", "title": "Load External PSF Font",
"description": "Load /system/tty.psf and apply it to framebuffer TTY.", "description": "Load /system/tty.psf and apply it to framebuffer TTY.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KEYBOARD", "key": "CLEONOS_CLKS_ENABLE_KEYBOARD",
"title": "PS/2 Keyboard Input", "title": "PS/2 Keyboard Input",
"description": "Initialize PS/2 keyboard input subsystem.", "description": "Initialize PS/2 keyboard input subsystem.",
"default": true "type": "bool",
"default": true,
"imply": [
"CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY",
"CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS"
]
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_ELFRUNNER_PROBE", "key": "CLEONOS_CLKS_ENABLE_ELFRUNNER_PROBE",
"title": "ELFRUNNER Probe", "title": "ELFRUNNER Probe",
"description": "Probe kernel ELF runtime metadata after ELFRUNNER init.", "description": "Probe kernel ELF runtime metadata after ELFRUNNER init.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_ELFRUNNER_INIT && CLEONOS_CLKS_ENABLE_KELF"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KLOGD_TASK", "key": "CLEONOS_CLKS_ENABLE_KLOGD_TASK",
"title": "Scheduler Task: klogd", "title": "Scheduler Task: klogd",
"description": "Enable periodic klogd maintenance task.", "description": "Enable periodic klogd maintenance task.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KWORKER_TASK", "key": "CLEONOS_CLKS_ENABLE_KWORKER_TASK",
"title": "Scheduler Task: kworker", "title": "Scheduler Task: kworker",
"description": "Enable periodic kernel worker service-heartbeat task.", "description": "Enable periodic kernel worker service-heartbeat task.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_USRD_TASK", "key": "CLEONOS_CLKS_ENABLE_USRD_TASK",
"title": "Scheduler Task: usrd", "title": "Scheduler Task: usrd",
"description": "Enable user/runtime dispatch task (shell tick, tty tick, exec tick).", "description": "Enable user/runtime dispatch task (shell tick, tty tick, exec tick).",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_BOOT_VIDEO_LOG", "key": "CLEONOS_CLKS_ENABLE_BOOT_VIDEO_LOG",
"title": "Boot Video Geometry Logs", "title": "Boot Video Geometry Logs",
"description": "Print framebuffer width/height/pitch/bpp logs at boot.", "description": "Print framebuffer width/height/pitch/bpp logs at boot.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_PMM_STATS_LOG", "key": "CLEONOS_CLKS_ENABLE_PMM_STATS_LOG",
"title": "PMM Stats Logs", "title": "PMM Stats Logs",
"description": "Print PMM managed/free/used/dropped pages at boot.", "description": "Print PMM managed/free/used/dropped pages at boot.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_HEAP_STATS_LOG", "key": "CLEONOS_CLKS_ENABLE_HEAP_STATS_LOG",
"title": "Heap Stats Logs", "title": "Heap Stats Logs",
"description": "Print heap total/free bytes at boot.", "description": "Print heap total/free bytes at boot.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_FS_ROOT_LOG", "key": "CLEONOS_CLKS_ENABLE_FS_ROOT_LOG",
"title": "FS Root Children Log", "title": "FS Root Children Log",
"description": "Print root directory children count during FS init.", "description": "Print root directory children count during FS init.",
"default": true "type": "tristate",
"default": "y",
"depends_on": "CLEONOS_CLKS_ENABLE_SYSTEM_DIR_CHECK"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_SYSTEM_DIR_CHECK", "key": "CLEONOS_CLKS_ENABLE_SYSTEM_DIR_CHECK",
"title": "FS /SYSTEM Sanity Check", "title": "FS /SYSTEM Sanity Check",
"description": "Require /system directory check during boot.", "description": "Require /system directory check during boot.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_ELFRUNNER_INIT", "key": "CLEONOS_CLKS_ENABLE_ELFRUNNER_INIT",
"title": "ELFRUNNER Init", "title": "ELFRUNNER Init",
"description": "Initialize ELFRUNNER framework in kernel boot path.", "description": "Initialize ELFRUNNER framework in kernel boot path.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_SYSCALL_TICK_QUERY", "key": "CLEONOS_CLKS_ENABLE_SYSCALL_TICK_QUERY",
"title": "SYSCALL Tick Query", "title": "SYSCALL Tick Query",
"description": "Query timer ticks via syscall and log result during boot.", "description": "Query timer ticks via syscall and log result during boot.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_PROCFS"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_TTY_READY_LOG", "key": "CLEONOS_CLKS_ENABLE_TTY_READY_LOG",
"title": "TTY Ready Logs", "title": "TTY Ready Logs",
"description": "Print TTY count/active/cursor ready logs.", "description": "Print TTY count/active/cursor ready logs.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG", "key": "CLEONOS_CLKS_ENABLE_IDLE_DEBUG_LOG",
"title": "Idle Loop Debug Log", "title": "Idle Loop Debug Log",
"description": "Print debug log before entering kernel idle loop.", "description": "Print debug log before entering kernel idle loop.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_PROCFS", "key": "CLEONOS_CLKS_ENABLE_PROCFS",
"title": "Virtual /proc", "title": "Virtual /proc",
"description": "Enable virtual procfs paths (/proc, /proc/list, /proc/self, /proc/<pid>) in syscall FS layer.", "description": "Enable virtual procfs paths (/proc, /proc/list, /proc/self, /proc/<pid>) in syscall FS layer.",
"type": "bool",
"default": true "default": true
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG", "key": "CLEONOS_CLKS_ENABLE_EXEC_SERIAL_LOG",
"title": "EXEC Serial Logs", "title": "EXEC Serial Logs",
"description": "Print EXEC run/return/path logs to serial output.", "description": "Print EXEC run/return/path logs to serial output.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY", "key": "CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY",
"title": "Keyboard TTY Switch Hotkey", "title": "Keyboard TTY Switch Hotkey",
"description": "Enable ALT+F1..F4 keyboard hotkey for active TTY switching.", "description": "Enable ALT+F1..F4 keyboard hotkey for active TTY switching.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_KEYBOARD"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS", "key": "CLEONOS_CLKS_ENABLE_KBD_CTRL_SHORTCUTS",
"title": "Keyboard Ctrl Shortcuts", "title": "Keyboard Ctrl Shortcuts",
"description": "Enable Ctrl+A/C/V shortcuts for input selection/copy/paste.", "description": "Enable Ctrl+A/C/V shortcuts for input selection/copy/paste.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_KEYBOARD",
"imply": [
"CLEONOS_CLKS_ENABLE_KBD_TTY_SWITCH_HOTKEY"
]
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY", "key": "CLEONOS_CLKS_ENABLE_KBD_FORCE_STOP_HOTKEY",
"title": "Keyboard Force-Stop Hotkey", "title": "Keyboard Force-Stop Hotkey",
"description": "Enable Ctrl+Alt+C force-stop for current running user process.", "description": "Enable Ctrl+Alt+C force-stop for current running user process.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_KEYBOARD && CLEONOS_CLKS_ENABLE_PROCFS"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_USER_INIT_SCRIPT_PROBE", "key": "CLEONOS_CLKS_ENABLE_USER_INIT_SCRIPT_PROBE",
"title": "User Init Script Probe", "title": "User Init Script Probe",
"description": "Probe and log /shell/init.cmd presence during userland init.", "description": "Probe and log /shell/init.cmd presence during userland init.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_USERLAND_AUTO_EXEC"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_USER_SYSTEM_APP_PROBE", "key": "CLEONOS_CLKS_ENABLE_USER_SYSTEM_APP_PROBE",
"title": "User System App Probe", "title": "User System App Probe",
"description": "Probe /system/elfrunner.elf and /system/memc.elf during userland init.", "description": "Probe /system/elfrunner.elf and /system/memc.elf during userland init.",
"default": true "type": "bool",
"default": true,
"depends_on": "CLEONOS_CLKS_ENABLE_KELF"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_SCHED_TASK_COUNT_LOG", "key": "CLEONOS_CLKS_ENABLE_SCHED_TASK_COUNT_LOG",
"title": "Scheduler Task Count Log", "title": "Scheduler Task Count Log",
"description": "Print scheduler task count after scheduler initialization.", "description": "Print scheduler task count after scheduler initialization.",
"default": true "type": "tristate",
"default": "y",
"depends_on": "CLEONOS_CLKS_ENABLE_KLOGD_TASK || CLEONOS_CLKS_ENABLE_KWORKER_TASK || CLEONOS_CLKS_ENABLE_USRD_TASK"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_INTERRUPT_READY_LOG", "key": "CLEONOS_CLKS_ENABLE_INTERRUPT_READY_LOG",
"title": "Interrupt Ready Log", "title": "Interrupt Ready Log",
"description": "Print IDT/PIC initialized log after interrupt setup.", "description": "Print IDT/PIC initialized log after interrupt setup.",
"default": true "type": "tristate",
"default": "y"
}, },
{ {
"key": "CLEONOS_CLKS_ENABLE_SHELL_MODE_LOG", "key": "CLEONOS_CLKS_ENABLE_SHELL_MODE_LOG",
"title": "Shell Mode Log", "title": "Shell Mode Log",
"description": "Print whether boot default mode is user shell or kernel shell.", "description": "Print whether boot default mode is user shell or kernel shell.",
"default": true "type": "tristate",
"default": "y"
} }
] ]
} }

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -37,13 +37,17 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b
## 支持 ## 支持
- ELF64 (x86_64) PT_LOAD 段装载 - ELF64 (x86_64) PT_LOAD 段装载
- CLeonOS `int 0x80` syscall 0..60 - CLeonOS `int 0x80` syscall 0..80`FD_*``PROC_*``STATS_*``EXEC_PATHV_IO`
- TTY 输出与键盘输入队列 - TTY 输出与键盘输入队列
- rootfs 文件/目录访问(`FS_*` - rootfs 文件/目录访问(`FS_*`
- `/temp` 写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE` - `/temp` 写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE`
- `EXEC_PATH/EXEC_PATHV` 执行 ELF带深度限制 - `EXEC_PATH/EXEC_PATHV` 执行 ELF带深度限制
- `EXEC_PATHV_IO`(支持 stdio fd 继承/重定向)
- `SPAWN_PATH/SPAWN_PATHV/WAITPID/EXIT/SLEEP_TICKS/YIELD` - `SPAWN_PATH/SPAWN_PATHV/WAITPID/EXIT/SLEEP_TICKS/YIELD`
- 进程 `argv/env` 查询(`PROC_ARGC/PROC_ARGV/PROC_ENVC/PROC_ENV` - 进程 `argv/env` 查询(`PROC_ARGC/PROC_ARGV/PROC_ENVC/PROC_ENV`
- 进程枚举与快照(`PROC_COUNT/PROC_PID_AT/PROC_SNAPSHOT/PROC_KILL`
- syscall 统计(`STATS_TOTAL/STATS_ID_COUNT/STATS_RECENT_*`
- 文件描述符(`FD_OPEN/FD_READ/FD_WRITE/FD_CLOSE/FD_DUP`
- 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*` - 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*`
## 参数 ## 参数

View File

@@ -69,6 +69,48 @@ SYS_PROC_LAST_SIGNAL = 57
SYS_PROC_FAULT_VECTOR = 58 SYS_PROC_FAULT_VECTOR = 58
SYS_PROC_FAULT_ERROR = 59 SYS_PROC_FAULT_ERROR = 59
SYS_PROC_FAULT_RIP = 60 SYS_PROC_FAULT_RIP = 60
SYS_PROC_COUNT = 61
SYS_PROC_PID_AT = 62
SYS_PROC_SNAPSHOT = 63
SYS_PROC_KILL = 64
SYS_KDBG_SYM = 65
SYS_KDBG_BT = 66
SYS_KDBG_REGS = 67
SYS_STATS_TOTAL = 68
SYS_STATS_ID_COUNT = 69
SYS_STATS_RECENT_WINDOW = 70
SYS_STATS_RECENT_ID = 71
SYS_FD_OPEN = 72
SYS_FD_READ = 73
SYS_FD_WRITE = 74
SYS_FD_CLOSE = 75
SYS_FD_DUP = 76
SYS_DL_OPEN = 77
SYS_DL_CLOSE = 78
SYS_DL_SYM = 79
SYS_EXEC_PATHV_IO = 80
# proc states (from cleonos/c/include/cleonos_syscall.h)
PROC_STATE_UNUSED = 0
PROC_STATE_PENDING = 1
PROC_STATE_RUNNING = 2
PROC_STATE_EXITED = 3
PROC_STATE_STOPPED = 4
# signals (from cleonos/c/include/cleonos_syscall.h)
SIGKILL = 9
SIGTERM = 15
SIGCONT = 18
SIGSTOP = 19
# open flags (from cleonos/c/include/cleonos_syscall.h)
O_RDONLY = 0x0000
O_WRONLY = 0x0001
O_RDWR = 0x0002
O_CREAT = 0x0040
O_TRUNC = 0x0200
O_APPEND = 0x0400
FD_INHERIT = U64_MASK
def u64(value: int) -> int: def u64(value: int) -> int:

View File

@@ -6,40 +6,48 @@ import sys
import time import time
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple from typing import Dict, List, Optional, Tuple
from .constants import ( from .constants import (
DEFAULT_MAX_EXEC_DEPTH, DEFAULT_MAX_EXEC_DEPTH,
FD_INHERIT,
FS_NAME_MAX, FS_NAME_MAX,
MAX_CSTR, MAX_CSTR,
MAX_IO_READ, MAX_IO_READ,
O_APPEND,
O_CREAT,
O_RDONLY,
O_RDWR,
O_TRUNC,
O_WRONLY,
PAGE_SIZE, PAGE_SIZE,
PROC_STATE_EXITED,
PROC_STATE_PENDING,
PROC_STATE_RUNNING,
PROC_STATE_STOPPED,
SIGCONT,
SIGKILL,
SIGSTOP,
SIGTERM,
SYS_AUDIO_AVAILABLE, SYS_AUDIO_AVAILABLE,
SYS_AUDIO_PLAY_TONE, SYS_AUDIO_PLAY_TONE,
SYS_AUDIO_STOP, SYS_AUDIO_STOP,
SYS_CONTEXT_SWITCHES, SYS_CONTEXT_SWITCHES,
SYS_CUR_TASK, SYS_CUR_TASK,
SYS_DL_CLOSE,
SYS_DL_OPEN,
SYS_DL_SYM,
SYS_EXEC_PATH, SYS_EXEC_PATH,
SYS_EXEC_PATHV, SYS_EXEC_PATHV,
SYS_EXEC_PATHV_IO,
SYS_EXEC_REQUESTS, SYS_EXEC_REQUESTS,
SYS_EXEC_SUCCESS, SYS_EXEC_SUCCESS,
SYS_EXIT, SYS_EXIT,
SYS_GETPID, SYS_FD_CLOSE,
SYS_PROC_ARGC, SYS_FD_DUP,
SYS_PROC_ARGV, SYS_FD_OPEN,
SYS_PROC_ENVC, SYS_FD_READ,
SYS_PROC_ENV, SYS_FD_WRITE,
SYS_PROC_FAULT_ERROR,
SYS_PROC_FAULT_RIP,
SYS_PROC_FAULT_VECTOR,
SYS_PROC_LAST_SIGNAL,
SYS_SLEEP_TICKS,
SYS_SPAWN_PATH,
SYS_SPAWN_PATHV,
SYS_WAITPID,
SYS_YIELD,
SYS_SHUTDOWN,
SYS_RESTART,
SYS_FS_APPEND, SYS_FS_APPEND,
SYS_FS_CHILD_COUNT, SYS_FS_CHILD_COUNT,
SYS_FS_GET_CHILD_NAME, SYS_FS_GET_CHILD_NAME,
@@ -50,6 +58,7 @@ from .constants import (
SYS_FS_STAT_SIZE, SYS_FS_STAT_SIZE,
SYS_FS_STAT_TYPE, SYS_FS_STAT_TYPE,
SYS_FS_WRITE, SYS_FS_WRITE,
SYS_GETPID,
SYS_KBD_BUFFERED, SYS_KBD_BUFFERED,
SYS_KBD_DROPPED, SYS_KBD_DROPPED,
SYS_KBD_GET_CHAR, SYS_KBD_GET_CHAR,
@@ -61,8 +70,29 @@ from .constants import (
SYS_LOG_JOURNAL_COUNT, SYS_LOG_JOURNAL_COUNT,
SYS_LOG_JOURNAL_READ, SYS_LOG_JOURNAL_READ,
SYS_LOG_WRITE, SYS_LOG_WRITE,
SYS_PROC_ARGC,
SYS_PROC_ARGV,
SYS_PROC_COUNT,
SYS_PROC_ENVC,
SYS_PROC_ENV,
SYS_PROC_FAULT_ERROR,
SYS_PROC_FAULT_RIP,
SYS_PROC_FAULT_VECTOR,
SYS_PROC_KILL,
SYS_PROC_LAST_SIGNAL,
SYS_PROC_PID_AT,
SYS_PROC_SNAPSHOT,
SYS_RESTART,
SYS_SERVICE_COUNT, SYS_SERVICE_COUNT,
SYS_SERVICE_READY_COUNT, SYS_SERVICE_READY_COUNT,
SYS_SHUTDOWN,
SYS_SLEEP_TICKS,
SYS_SPAWN_PATH,
SYS_SPAWN_PATHV,
SYS_STATS_ID_COUNT,
SYS_STATS_RECENT_ID,
SYS_STATS_RECENT_WINDOW,
SYS_STATS_TOTAL,
SYS_TASK_COUNT, SYS_TASK_COUNT,
SYS_TIMER_TICKS, SYS_TIMER_TICKS,
SYS_TTY_ACTIVE, SYS_TTY_ACTIVE,
@@ -75,6 +105,8 @@ from .constants import (
SYS_USER_LAUNCH_OK, SYS_USER_LAUNCH_OK,
SYS_USER_LAUNCH_TRIES, SYS_USER_LAUNCH_TRIES,
SYS_USER_SHELL_READY, SYS_USER_SHELL_READY,
SYS_WAITPID,
SYS_YIELD,
page_ceil, page_ceil,
page_floor, page_floor,
u64, u64,
@@ -124,6 +156,15 @@ class ELFImage:
segments: List[ELFSegment] segments: List[ELFSegment]
@dataclass
class FDEntry:
kind: str
flags: int
offset: int = 0
path: str = ""
tty_index: int = 0
EXEC_PATH_MAX = 192 EXEC_PATH_MAX = 192
EXEC_ARG_LINE_MAX = 256 EXEC_ARG_LINE_MAX = 256
EXEC_ENV_LINE_MAX = 512 EXEC_ENV_LINE_MAX = 512
@@ -131,6 +172,8 @@ EXEC_MAX_ARGS = 24
EXEC_MAX_ENVS = 24 EXEC_MAX_ENVS = 24
EXEC_ITEM_MAX = 128 EXEC_ITEM_MAX = 128
EXEC_STATUS_SIGNAL_FLAG = 1 << 63 EXEC_STATUS_SIGNAL_FLAG = 1 << 63
PROC_PATH_MAX = 192
FD_MAX = 64
class CLeonOSWineNative: class CLeonOSWineNative:
@@ -150,6 +193,7 @@ class CLeonOSWineNative:
ppid: int = 0, ppid: int = 0,
argv_items: Optional[List[str]] = None, argv_items: Optional[List[str]] = None,
env_items: Optional[List[str]] = None, env_items: Optional[List[str]] = None,
inherited_fds: Optional[Dict[int, FDEntry]] = None,
) -> None: ) -> None:
self.elf_path = elf_path self.elf_path = elf_path
self.rootfs = rootfs self.rootfs = rootfs
@@ -175,9 +219,97 @@ class CLeonOSWineNative:
self._stack_size = 0x0000000000020000 self._stack_size = 0x0000000000020000
self._ret_sentinel = 0x00007FFF10000000 self._ret_sentinel = 0x00007FFF10000000
self._mapped_ranges: List[Tuple[int, int]] = [] self._mapped_ranges: List[Tuple[int, int]] = []
self._tty_index = int(self.state.tty_active)
self._fds: Dict[int, FDEntry] = {}
self._fd_inherited = inherited_fds if inherited_fds is not None else {}
default_path = self._normalize_guest_path(self.guest_path_hint or f"/{self.elf_path.name}") default_path = self._normalize_guest_path(self.guest_path_hint or f"/{self.elf_path.name}")
self.argv_items, self.env_items = self._prepare_exec_items(default_path, self.argv_items, self.env_items) self.argv_items, self.env_items = self._prepare_exec_items(default_path, self.argv_items, self.env_items)
self._init_default_fds()
@staticmethod
def _clone_fd_entry(entry: FDEntry) -> FDEntry:
return FDEntry(kind=entry.kind, flags=int(entry.flags), offset=int(entry.offset), path=str(entry.path), tty_index=int(entry.tty_index))
@staticmethod
def _fd_access_mode(flags: int) -> int:
return int(flags) & 0x3
@classmethod
def _fd_access_mode_valid(cls, flags: int) -> bool:
mode = cls._fd_access_mode(flags)
return mode in (O_RDONLY, O_WRONLY, O_RDWR)
@classmethod
def _fd_can_read(cls, flags: int) -> bool:
mode = cls._fd_access_mode(flags)
return mode in (O_RDONLY, O_RDWR)
@classmethod
def _fd_can_write(cls, flags: int) -> bool:
mode = cls._fd_access_mode(flags)
return mode in (O_WRONLY, O_RDWR)
def _init_default_fds(self) -> None:
self._fds = {
0: FDEntry(kind="tty", flags=O_RDONLY, offset=0, tty_index=self._tty_index),
1: FDEntry(kind="tty", flags=O_WRONLY, offset=0, tty_index=self._tty_index),
2: FDEntry(kind="tty", flags=O_WRONLY, offset=0, tty_index=self._tty_index),
}
for target in (0, 1, 2):
inherited = self._fd_inherited.get(target)
if inherited is not None:
self._fds[target] = self._clone_fd_entry(inherited)
for target in (0, 1, 2):
entry = self._fds.get(target)
if entry is not None and entry.kind == "tty":
self._tty_index = int(entry.tty_index)
break
def _fd_lookup(self, fd: int) -> Optional[FDEntry]:
if fd < 0 or fd >= FD_MAX:
return None
return self._fds.get(int(fd))
def _fd_find_free(self) -> int:
for fd in range(FD_MAX):
if fd not in self._fds:
return fd
return -1
def _stdio_entry_for_child(self, target_fd: int, override_fd: int, require_read: bool, require_write: bool) -> Optional[FDEntry]:
if override_fd == FD_INHERIT:
src = self._fd_lookup(target_fd)
else:
src = self._fd_lookup(override_fd)
if src is None:
return None
if require_read and not self._fd_can_read(src.flags):
return None
if require_write and not self._fd_can_write(src.flags):
return None
return self._clone_fd_entry(src)
def _build_child_stdio_map(self, stdin_fd: int, stdout_fd: int, stderr_fd: int) -> Optional[Dict[int, FDEntry]]:
child_map: Dict[int, FDEntry] = {}
in_entry = self._stdio_entry_for_child(0, stdin_fd, require_read=True, require_write=False)
out_entry = self._stdio_entry_for_child(1, stdout_fd, require_read=False, require_write=True)
err_entry = self._stdio_entry_for_child(2, stderr_fd, require_read=False, require_write=True)
if in_entry is None or out_entry is None or err_entry is None:
return None
child_map[0] = in_entry
child_map[1] = out_entry
child_map[2] = err_entry
return child_map
def run(self) -> Optional[int]: def run(self) -> Optional[int]:
if self.pid == 0: if self.pid == 0:
@@ -187,6 +319,7 @@ class CLeonOSWineNative:
self.state.set_current_pid(self.pid) self.state.set_current_pid(self.pid)
self.state.set_proc_cmdline(self.pid, self.argv_items, self.env_items) self.state.set_proc_cmdline(self.pid, self.argv_items, self.env_items)
self.state.set_proc_fault(self.pid, 0, 0, 0, 0) self.state.set_proc_fault(self.pid, 0, 0, 0, 0)
self.state.set_proc_running(self.pid, self.argv_items[0] if self.argv_items else self.guest_path_hint, self._tty_index)
uc = Uc(UC_ARCH_X86, UC_MODE_64) uc = Uc(UC_ARCH_X86, UC_MODE_64)
self._install_hooks(uc) self._install_hooks(uc)
@@ -252,6 +385,7 @@ class CLeonOSWineNative:
arg1 = self._reg_read(uc, UC_X86_REG_RCX) arg1 = self._reg_read(uc, UC_X86_REG_RCX)
arg2 = self._reg_read(uc, UC_X86_REG_RDX) arg2 = self._reg_read(uc, UC_X86_REG_RDX)
self.state.record_syscall(syscall_id)
self.state.context_switches = u64(self.state.context_switches + 1) self.state.context_switches = u64(self.state.context_switches + 1)
ret = self._dispatch_syscall(uc, syscall_id, arg0, arg1, arg2) ret = self._dispatch_syscall(uc, syscall_id, arg0, arg1, arg2)
self._reg_write(uc, UC_X86_REG_RAX, u64(ret)) self._reg_write(uc, UC_X86_REG_RAX, u64(ret))
@@ -291,6 +425,8 @@ class CLeonOSWineNative:
return self._exec_path(uc, arg0) return self._exec_path(uc, arg0)
if sid == SYS_EXEC_PATHV: if sid == SYS_EXEC_PATHV:
return self._exec_pathv(uc, arg0, arg1, arg2) return self._exec_pathv(uc, arg0, arg1, arg2)
if sid == SYS_EXEC_PATHV_IO:
return self._exec_pathv_io(uc, arg0, arg1, arg2)
if sid == SYS_SPAWN_PATH: if sid == SYS_SPAWN_PATH:
return self._spawn_path(uc, arg0) return self._spawn_path(uc, arg0)
if sid == SYS_SPAWN_PATHV: if sid == SYS_SPAWN_PATHV:
@@ -315,6 +451,14 @@ class CLeonOSWineNative:
return self._proc_fault_error() return self._proc_fault_error()
if sid == SYS_PROC_FAULT_RIP: if sid == SYS_PROC_FAULT_RIP:
return self._proc_fault_rip() return self._proc_fault_rip()
if sid == SYS_PROC_COUNT:
return self._proc_count()
if sid == SYS_PROC_PID_AT:
return self._proc_pid_at(uc, arg0, arg1)
if sid == SYS_PROC_SNAPSHOT:
return self._proc_snapshot(uc, arg0, arg1, arg2)
if sid == SYS_PROC_KILL:
return self._proc_kill(uc, arg0, arg1)
if sid == SYS_EXIT: if sid == SYS_EXIT:
return self._request_exit(uc, arg0) return self._request_exit(uc, arg0)
if sid == SYS_SLEEP_TICKS: if sid == SYS_SLEEP_TICKS:
@@ -400,6 +544,30 @@ class CLeonOSWineNative:
return self.state.kbd_drop_count return self.state.kbd_drop_count
if sid == SYS_KBD_HOTKEY_SWITCHES: if sid == SYS_KBD_HOTKEY_SWITCHES:
return self.state.kbd_hotkey_switches return self.state.kbd_hotkey_switches
if sid == SYS_STATS_TOTAL:
return self.state.stats_total()
if sid == SYS_STATS_ID_COUNT:
return self.state.stats_id_count(arg0)
if sid == SYS_STATS_RECENT_WINDOW:
return self.state.stats_recent_window()
if sid == SYS_STATS_RECENT_ID:
return self.state.stats_recent_id_count(arg0)
if sid == SYS_FD_OPEN:
return self._fd_open(uc, arg0, arg1, arg2)
if sid == SYS_FD_READ:
return self._fd_read(uc, arg0, arg1, arg2)
if sid == SYS_FD_WRITE:
return self._fd_write(uc, arg0, arg1, arg2)
if sid == SYS_FD_CLOSE:
return self._fd_close(arg0)
if sid == SYS_FD_DUP:
return self._fd_dup(arg0)
if sid == SYS_DL_OPEN:
return u64_neg1()
if sid == SYS_DL_CLOSE:
return 0
if sid == SYS_DL_SYM:
return 0
return u64_neg1() return u64_neg1()
@@ -409,6 +577,15 @@ class CLeonOSWineNative:
sys.stdout.write(text) sys.stdout.write(text)
sys.stdout.flush() sys.stdout.flush()
def _host_write_bytes(self, data: bytes) -> None:
if not data:
return
if hasattr(sys.stdout, "buffer"):
sys.stdout.buffer.write(data)
sys.stdout.flush()
return
self._host_write(data.decode("utf-8", errors="replace"))
def _load_segments(self, uc: Uc) -> None: def _load_segments(self, uc: Uc) -> None:
for seg in self.image.segments: for seg in self.image.segments:
start = page_floor(seg.vaddr) start = page_floor(seg.vaddr)
@@ -504,6 +681,30 @@ class CLeonOSWineNative:
except UcError: except UcError:
return b"" return b""
def _read_guest_bytes_exact(self, uc: Uc, addr: int, size: int) -> Optional[bytes]:
if size < 0 or addr == 0:
return None
if size == 0:
return b""
out = bytearray()
cursor = int(addr)
left = int(size)
while left > 0:
chunk = min(left, MAX_IO_READ)
try:
data = uc.mem_read(cursor, chunk)
except UcError:
return None
if len(data) != chunk:
return None
out.extend(data)
cursor += chunk
left -= chunk
return bytes(out)
def _write_guest_bytes(self, uc: Uc, addr: int, data: bytes) -> bool: def _write_guest_bytes(self, uc: Uc, addr: int, data: bytes) -> bool:
if addr == 0: if addr == 0:
return False return False
@@ -743,16 +944,86 @@ class CLeonOSWineNative:
return 1 if self._write_guest_bytes(uc, out_ptr, encoded + b"\x00") else 0 return 1 if self._write_guest_bytes(uc, out_ptr, encoded + b"\x00") else 0
def _exec_path(self, uc: Uc, path_ptr: int) -> int: def _exec_path(self, uc: Uc, path_ptr: int) -> int:
return self._spawn_path_common(uc, path_ptr, 0, 0, return_pid=False) return self._spawn_path_common(
uc,
path_ptr,
0,
0,
return_pid=False,
env_line_override=None,
stdin_fd=FD_INHERIT,
stdout_fd=FD_INHERIT,
stderr_fd=FD_INHERIT,
)
def _exec_pathv(self, uc: Uc, path_ptr: int, argv_ptr: int, env_ptr: int) -> int: def _exec_pathv(self, uc: Uc, path_ptr: int, argv_ptr: int, env_ptr: int) -> int:
return self._spawn_path_common(uc, path_ptr, argv_ptr, env_ptr, return_pid=False) return self._spawn_path_common(
uc,
path_ptr,
argv_ptr,
env_ptr,
return_pid=False,
env_line_override=None,
stdin_fd=FD_INHERIT,
stdout_fd=FD_INHERIT,
stderr_fd=FD_INHERIT,
)
def _exec_pathv_io(self, uc: Uc, path_ptr: int, argv_ptr: int, req_ptr: int) -> int:
req_data: Optional[bytes]
env_ptr: int
stdin_fd: int
stdout_fd: int
stderr_fd: int
env_line: str
if req_ptr == 0:
return u64_neg1()
req_data = self._read_guest_bytes_exact(uc, req_ptr, 32)
if req_data is None or len(req_data) != 32:
return u64_neg1()
env_ptr, stdin_fd, stdout_fd, stderr_fd = struct.unpack("<QQQQ", req_data)
env_line = self._read_guest_cstring(uc, env_ptr, EXEC_ENV_LINE_MAX) if env_ptr != 0 else ""
return self._spawn_path_common(
uc,
path_ptr,
argv_ptr,
0,
return_pid=False,
env_line_override=env_line,
stdin_fd=stdin_fd,
stdout_fd=stdout_fd,
stderr_fd=stderr_fd,
)
def _spawn_path(self, uc: Uc, path_ptr: int) -> int: def _spawn_path(self, uc: Uc, path_ptr: int) -> int:
return self._spawn_path_common(uc, path_ptr, 0, 0, return_pid=True) return self._spawn_path_common(
uc,
path_ptr,
0,
0,
return_pid=True,
env_line_override=None,
stdin_fd=FD_INHERIT,
stdout_fd=FD_INHERIT,
stderr_fd=FD_INHERIT,
)
def _spawn_pathv(self, uc: Uc, path_ptr: int, argv_ptr: int, env_ptr: int) -> int: def _spawn_pathv(self, uc: Uc, path_ptr: int, argv_ptr: int, env_ptr: int) -> int:
return self._spawn_path_common(uc, path_ptr, argv_ptr, env_ptr, return_pid=True) return self._spawn_path_common(
uc,
path_ptr,
argv_ptr,
env_ptr,
return_pid=True,
env_line_override=None,
stdin_fd=FD_INHERIT,
stdout_fd=FD_INHERIT,
stderr_fd=FD_INHERIT,
)
def _spawn_path_common( def _spawn_path_common(
self, self,
@@ -762,17 +1033,30 @@ class CLeonOSWineNative:
env_ptr: int, env_ptr: int,
*, *,
return_pid: bool, return_pid: bool,
env_line_override: Optional[str],
stdin_fd: int,
stdout_fd: int,
stderr_fd: int,
) -> int: ) -> int:
path = self._read_guest_cstring(uc, path_ptr, EXEC_PATH_MAX) path = self._read_guest_cstring(uc, path_ptr, EXEC_PATH_MAX)
guest_path = self._normalize_guest_path(path) guest_path = self._normalize_guest_path(path)
argv_line = self._read_guest_cstring(uc, argv_ptr, EXEC_ARG_LINE_MAX) if argv_ptr != 0 else "" argv_line = self._read_guest_cstring(uc, argv_ptr, EXEC_ARG_LINE_MAX) if argv_ptr != 0 else ""
env_line = self._read_guest_cstring(uc, env_ptr, EXEC_ENV_LINE_MAX) if env_ptr != 0 else "" env_line = (
env_line_override
if env_line_override is not None
else (self._read_guest_cstring(uc, env_ptr, EXEC_ENV_LINE_MAX) if env_ptr != 0 else "")
)
host_path = self._guest_to_host(guest_path, must_exist=True) host_path = self._guest_to_host(guest_path, must_exist=True)
child_stdio = self._build_child_stdio_map(int(stdin_fd), int(stdout_fd), int(stderr_fd))
self.state.exec_requests = u64(self.state.exec_requests + 1) self.state.exec_requests = u64(self.state.exec_requests + 1)
self.state.user_exec_requested = 1 self.state.user_exec_requested = 1
self.state.user_launch_tries = u64(self.state.user_launch_tries + 1) self.state.user_launch_tries = u64(self.state.user_launch_tries + 1)
if child_stdio is None:
self.state.user_launch_fail = u64(self.state.user_launch_fail + 1)
return u64_neg1()
if host_path is None or not host_path.is_file(): if host_path is None or not host_path.is_file():
self.state.user_launch_fail = u64(self.state.user_launch_fail + 1) self.state.user_launch_fail = u64(self.state.user_launch_fail + 1)
return u64_neg1() return u64_neg1()
@@ -802,6 +1086,7 @@ class CLeonOSWineNative:
ppid=parent_pid, ppid=parent_pid,
argv_items=argv_items, argv_items=argv_items,
env_items=env_items, env_items=env_items,
inherited_fds=child_stdio,
) )
child_ret = child.run() child_ret = child.run()
@@ -854,6 +1139,97 @@ class CLeonOSWineNative:
def _proc_fault_rip(self) -> int: def _proc_fault_rip(self) -> int:
return self.state.proc_fault_rip_value(self.state.get_current_pid()) return self.state.proc_fault_rip_value(self.state.get_current_pid())
def _proc_count(self) -> int:
return self.state.proc_count()
def _proc_pid_at(self, uc: Uc, index: int, out_ptr: int) -> int:
if out_ptr == 0:
return 0
pid = self.state.proc_pid_at(int(index))
if pid is None:
return 0
return 1 if self._write_guest_bytes(uc, out_ptr, struct.pack("<Q", u64(pid))) else 0
def _proc_snapshot(self, uc: Uc, pid: int, out_ptr: int, out_size: int) -> int:
if out_ptr == 0 or out_size < (13 * 8 + PROC_PATH_MAX):
return 0
target = int(pid)
state_value = self.state.proc_state_value(target)
if state_value == 0:
return 0
path = self.state.proc_path_value(target)
encoded_path = path.encode("utf-8", errors="replace")
if len(encoded_path) >= PROC_PATH_MAX:
encoded_path = encoded_path[: PROC_PATH_MAX - 1]
path_buf = encoded_path + b"\x00" + (b"\x00" * (PROC_PATH_MAX - len(encoded_path) - 1))
blob = struct.pack(
"<13Q",
u64(target),
u64(self.state.proc_ppid(target)),
u64(state_value),
u64(self.state.proc_started_tick_value(target)),
u64(self.state.proc_exited_tick_value(target)),
u64(self.state.proc_exit_status_value(target)),
u64(self.state.proc_runtime_ticks(target)),
u64(self.state.proc_mem_bytes_value(target)),
u64(self.state.proc_tty_index_value(target)),
u64(self.state.proc_signal(target)),
u64(self.state.proc_fault_vector_value(target)),
u64(self.state.proc_fault_error_value(target)),
u64(self.state.proc_fault_rip_value(target)),
) + path_buf
return 1 if self._write_guest_bytes(uc, out_ptr, blob) else 0
def _proc_kill(self, uc: Uc, pid: int, signal: int) -> int:
target = int(pid)
if target <= 0:
return u64_neg1()
current_state = self.state.proc_state_value(target)
if current_state == 0:
return u64_neg1()
effective_signal = int(signal) & 0xFF
if effective_signal == 0:
effective_signal = SIGTERM
self.state.set_proc_fault(target, effective_signal, 0, 0, 0)
if current_state == PROC_STATE_EXITED:
return 1
if effective_signal == SIGCONT:
if current_state == PROC_STATE_STOPPED:
self.state.set_proc_pending(target)
return 1
if effective_signal == SIGSTOP and current_state in (PROC_STATE_PENDING, PROC_STATE_STOPPED):
self.state.set_proc_stopped(target)
return 1
status = self._encode_signal_status(effective_signal, 0, 0)
if target == self.state.get_current_pid():
self._exit_requested = True
self._exit_status = u64(status)
if effective_signal == SIGSTOP:
self.state.set_proc_stopped(target)
uc.emu_stop()
return 1
if effective_signal == SIGSTOP:
self.state.set_proc_stopped(target)
return 1
self.state.mark_exited(target, status)
return 1
def _wait_pid(self, uc: Uc, pid: int, out_ptr: int) -> int: def _wait_pid(self, uc: Uc, pid: int, out_ptr: int) -> int:
wait_ret, status = self.state.wait_pid(int(pid)) wait_ret, status = self.state.wait_pid(int(pid))
@@ -885,6 +1261,190 @@ class CLeonOSWineNative:
time.sleep(0) time.sleep(0)
return self.state.timer_ticks() return self.state.timer_ticks()
def _fd_open(self, uc: Uc, path_ptr: int, flags: int, mode: int) -> int:
_ = mode
guest_path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr, EXEC_PATH_MAX))
open_flags = int(u64(flags))
lower_path = guest_path.lower()
fd_slot: int
entry: FDEntry
if not guest_path.startswith("/"):
return u64_neg1()
if not self._fd_access_mode_valid(open_flags):
return u64_neg1()
if ((open_flags & O_TRUNC) != 0 or (open_flags & O_APPEND) != 0) and not self._fd_can_write(open_flags):
return u64_neg1()
fd_slot = self._fd_find_free()
if fd_slot < 0:
return u64_neg1()
if lower_path == "/dev/tty":
entry = FDEntry(kind="tty", flags=open_flags, offset=0, tty_index=self._tty_index)
self._fds[fd_slot] = entry
return fd_slot
if lower_path == "/dev/null":
entry = FDEntry(kind="dev_null", flags=open_flags, offset=0, tty_index=self._tty_index)
self._fds[fd_slot] = entry
return fd_slot
if lower_path == "/dev/zero":
entry = FDEntry(kind="dev_zero", flags=open_flags, offset=0, tty_index=self._tty_index)
self._fds[fd_slot] = entry
return fd_slot
if lower_path == "/dev/random":
entry = FDEntry(kind="dev_random", flags=open_flags, offset=0, tty_index=self._tty_index)
self._fds[fd_slot] = entry
return fd_slot
host_path = self._guest_to_host(guest_path, must_exist=False)
if host_path is None:
return u64_neg1()
try:
if not host_path.exists():
if (open_flags & O_CREAT) == 0 or not self._fd_can_write(open_flags):
return u64_neg1()
host_path.parent.mkdir(parents=True, exist_ok=True)
host_path.write_bytes(b"")
if host_path.is_dir():
return u64_neg1()
if (open_flags & O_TRUNC) != 0:
host_path.write_bytes(b"")
offset = int(host_path.stat().st_size) if (open_flags & O_APPEND) != 0 else 0
except Exception:
return u64_neg1()
entry = FDEntry(kind="file", flags=open_flags, offset=offset, path=str(host_path), tty_index=self._tty_index)
self._fds[fd_slot] = entry
return fd_slot
def _fd_read(self, uc: Uc, fd: int, out_ptr: int, size: int) -> int:
req = int(u64(size))
entry = self._fd_lookup(int(fd))
data: bytes
if req == 0:
return 0
if out_ptr == 0:
return u64_neg1()
if entry is None or not self._fd_can_read(entry.flags):
return u64_neg1()
req = min(req, MAX_IO_READ)
if entry.kind == "tty":
out = bytearray()
while len(out) < req:
key = self.state.pop_key()
if key is None:
break
out.append(key & 0xFF)
data = bytes(out)
elif entry.kind == "dev_null":
return 0
elif entry.kind == "dev_zero":
data = b"\x00" * req
elif entry.kind == "dev_random":
data = os.urandom(req)
elif entry.kind == "file":
try:
with open(entry.path, "rb") as fh:
fh.seek(entry.offset)
data = fh.read(req)
except Exception:
return u64_neg1()
else:
return u64_neg1()
if len(data) == 0:
return 0
if not self._write_guest_bytes(uc, int(out_ptr), data):
return u64_neg1()
entry.offset += len(data)
return len(data)
def _fd_write(self, uc: Uc, fd: int, buf_ptr: int, size: int) -> int:
req = int(u64(size))
entry = self._fd_lookup(int(fd))
data: Optional[bytes]
write_pos: int
if req == 0:
return 0
if buf_ptr == 0:
return u64_neg1()
if entry is None or not self._fd_can_write(entry.flags):
return u64_neg1()
req = min(req, MAX_IO_READ)
data = self._read_guest_bytes_exact(uc, int(buf_ptr), req)
if data is None:
return u64_neg1()
if entry.kind == "tty":
self._host_write_bytes(data)
entry.offset += len(data)
return len(data)
if entry.kind in ("dev_null", "dev_zero", "dev_random"):
entry.offset += len(data)
return len(data)
if entry.kind != "file":
return u64_neg1()
try:
host_path = Path(entry.path)
if not host_path.exists():
if (entry.flags & O_CREAT) == 0 or not self._fd_can_write(entry.flags):
return u64_neg1()
host_path.parent.mkdir(parents=True, exist_ok=True)
host_path.write_bytes(b"")
with open(host_path, "r+b") as fh:
write_pos = int(entry.offset)
fh.seek(write_pos)
fh.write(data)
except Exception:
return u64_neg1()
entry.offset += len(data)
return len(data)
def _fd_close(self, fd: int) -> int:
key = int(fd)
if key not in self._fds:
return u64_neg1()
del self._fds[key]
return 0
def _fd_dup(self, fd: int) -> int:
src = self._fd_lookup(int(fd))
if src is None:
return u64_neg1()
slot = self._fd_find_free()
if slot < 0:
return u64_neg1()
self._fds[slot] = self._clone_fd_entry(src)
return slot
@staticmethod @staticmethod
def _truncate_item_text(text: str, max_bytes: int = EXEC_ITEM_MAX) -> str: def _truncate_item_text(text: str, max_bytes: int = EXEC_ITEM_MAX) -> str:
if max_bytes <= 1: if max_bytes <= 1:

View File

@@ -6,7 +6,7 @@ import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Deque, Dict, List, Optional, Tuple from typing import Deque, Dict, List, Optional, Tuple
from .constants import u64 from .constants import PROC_STATE_EXITED, PROC_STATE_PENDING, PROC_STATE_RUNNING, PROC_STATE_STOPPED, u64
@dataclass @dataclass
@@ -37,13 +37,27 @@ class SharedKernelState:
kbd_hotkey_switches: int = 0 kbd_hotkey_switches: int = 0
log_journal_cap: int = 256 log_journal_cap: int = 256
log_journal: Deque[str] = field(default_factory=lambda: collections.deque(maxlen=256)) log_journal: Deque[str] = field(default_factory=lambda: collections.deque(maxlen=256))
fs_write_max: int = 65536 fs_write_max: int = 16 * 1024 * 1024
# syscall stats
stats_lock: threading.Lock = field(default_factory=threading.Lock)
stats_total_calls: int = 0
stats_id_total: Dict[int, int] = field(default_factory=dict)
stats_recent_window_cap: int = 256
stats_recent_ring: Deque[int] = field(default_factory=lambda: collections.deque(maxlen=256))
# process table
proc_lock: threading.Lock = field(default_factory=threading.Lock) proc_lock: threading.Lock = field(default_factory=threading.Lock)
proc_next_pid: int = 1 proc_next_pid: int = 1
proc_current_pid: int = 0 proc_current_pid: int = 0
proc_parents: Dict[int, int] = field(default_factory=dict) proc_parents: Dict[int, int] = field(default_factory=dict)
proc_status: Dict[int, Optional[int]] = field(default_factory=dict) proc_status: Dict[int, Optional[int]] = field(default_factory=dict)
proc_state: Dict[int, int] = field(default_factory=dict)
proc_started_tick: Dict[int, int] = field(default_factory=dict)
proc_exited_tick: Dict[int, int] = field(default_factory=dict)
proc_mem_bytes: Dict[int, int] = field(default_factory=dict)
proc_tty_index: Dict[int, int] = field(default_factory=dict)
proc_path: Dict[int, str] = field(default_factory=dict)
proc_argv: Dict[int, List[str]] = field(default_factory=dict) proc_argv: Dict[int, List[str]] = field(default_factory=dict)
proc_env: Dict[int, List[str]] = field(default_factory=dict) proc_env: Dict[int, List[str]] = field(default_factory=dict)
proc_last_signal: Dict[int, int] = field(default_factory=dict) proc_last_signal: Dict[int, int] = field(default_factory=dict)
@@ -54,6 +68,32 @@ class SharedKernelState:
def timer_ticks(self) -> int: def timer_ticks(self) -> int:
return (time.monotonic_ns() - self.start_ns) // 1_000_000 return (time.monotonic_ns() - self.start_ns) // 1_000_000
def record_syscall(self, sid: int) -> None:
key = int(u64(sid))
with self.stats_lock:
self.stats_total_calls = int(u64(self.stats_total_calls + 1))
self.stats_id_total[key] = int(u64(self.stats_id_total.get(key, 0) + 1))
self.stats_recent_ring.append(key)
def stats_total(self) -> int:
with self.stats_lock:
return int(self.stats_total_calls)
def stats_id_count(self, sid: int) -> int:
key = int(u64(sid))
with self.stats_lock:
return int(self.stats_id_total.get(key, 0))
def stats_recent_window(self) -> int:
with self.stats_lock:
return len(self.stats_recent_ring)
def stats_recent_id_count(self, sid: int) -> int:
key = int(u64(sid))
with self.stats_lock:
return sum(1 for item in self.stats_recent_ring if item == key)
def push_key(self, key: int) -> None: def push_key(self, key: int) -> None:
with self.kbd_lock: with self.kbd_lock:
if len(self.kbd_queue) >= self.kbd_queue_cap: if len(self.kbd_queue) >= self.kbd_queue_cap:
@@ -94,6 +134,8 @@ class SharedKernelState:
return list(self.log_journal)[index_from_oldest] return list(self.log_journal)[index_from_oldest]
def alloc_pid(self, ppid: int) -> int: def alloc_pid(self, ppid: int) -> int:
now = self.timer_ticks()
with self.proc_lock: with self.proc_lock:
pid = int(self.proc_next_pid) pid = int(self.proc_next_pid)
@@ -107,6 +149,12 @@ class SharedKernelState:
self.proc_parents[pid] = int(ppid) self.proc_parents[pid] = int(ppid)
self.proc_status[pid] = None self.proc_status[pid] = None
self.proc_state[pid] = PROC_STATE_PENDING
self.proc_started_tick[pid] = now
self.proc_exited_tick[pid] = 0
self.proc_mem_bytes[pid] = 0
self.proc_tty_index[pid] = int(self.tty_active)
self.proc_path[pid] = ""
self.proc_argv[pid] = [] self.proc_argv[pid] = []
self.proc_env[pid] = [] self.proc_env[pid] = []
self.proc_last_signal[pid] = 0 self.proc_last_signal[pid] = 0
@@ -123,12 +171,51 @@ class SharedKernelState:
with self.proc_lock: with self.proc_lock:
return int(self.proc_current_pid) return int(self.proc_current_pid)
def set_proc_running(self, pid: int, path: Optional[str], tty_index: int) -> None:
if pid <= 0:
return
now = self.timer_ticks()
with self.proc_lock:
if pid not in self.proc_status:
return
self.proc_state[pid] = PROC_STATE_RUNNING
if self.proc_started_tick.get(pid, 0) == 0:
self.proc_started_tick[pid] = now
self.proc_tty_index[pid] = int(tty_index)
if path:
self.proc_path[pid] = str(path)
def mark_exited(self, pid: int, status: int) -> None: def mark_exited(self, pid: int, status: int) -> None:
if pid <= 0: if pid <= 0:
return return
with self.proc_lock: with self.proc_lock:
self.proc_status[int(pid)] = int(u64(status)) self.proc_status[int(pid)] = int(u64(status))
self.proc_state[int(pid)] = PROC_STATE_EXITED
self.proc_exited_tick[int(pid)] = self.timer_ticks()
def set_proc_stopped(self, pid: int) -> None:
if pid <= 0:
return
with self.proc_lock:
if pid not in self.proc_status:
return
if self.proc_state.get(pid, PROC_STATE_PENDING) != PROC_STATE_EXITED:
self.proc_state[pid] = PROC_STATE_STOPPED
def set_proc_pending(self, pid: int) -> None:
if pid <= 0:
return
with self.proc_lock:
if pid not in self.proc_status:
return
if self.proc_state.get(pid, PROC_STATE_PENDING) != PROC_STATE_EXITED:
self.proc_state[pid] = PROC_STATE_PENDING
def wait_pid(self, pid: int) -> Tuple[int, int]: def wait_pid(self, pid: int) -> Tuple[int, int]:
with self.proc_lock: with self.proc_lock:
@@ -151,6 +238,8 @@ class SharedKernelState:
return return
self.proc_argv[pid] = [str(item) for item in argv] self.proc_argv[pid] = [str(item) for item in argv]
self.proc_env[pid] = [str(item) for item in env] self.proc_env[pid] = [str(item) for item in env]
if argv:
self.proc_path[pid] = str(argv[0])
def set_proc_fault(self, pid: int, signal: int, vector: int, error_code: int, rip: int) -> None: def set_proc_fault(self, pid: int, signal: int, vector: int, error_code: int, rip: int) -> None:
if pid <= 0: if pid <= 0:
@@ -164,6 +253,68 @@ class SharedKernelState:
self.proc_fault_error[pid] = int(u64(error_code)) self.proc_fault_error[pid] = int(u64(error_code))
self.proc_fault_rip[pid] = int(u64(rip)) self.proc_fault_rip[pid] = int(u64(rip))
def proc_count(self) -> int:
with self.proc_lock:
return len(self.proc_status)
def proc_pid_at(self, index: int) -> Optional[int]:
if index < 0:
return None
with self.proc_lock:
ordered = sorted(self.proc_status.keys())
if index >= len(ordered):
return None
return int(ordered[index])
def proc_state_value(self, pid: int) -> int:
with self.proc_lock:
return int(self.proc_state.get(int(pid), 0))
def proc_ppid(self, pid: int) -> int:
with self.proc_lock:
return int(self.proc_parents.get(int(pid), 0))
def proc_started_tick_value(self, pid: int) -> int:
with self.proc_lock:
return int(self.proc_started_tick.get(int(pid), 0))
def proc_exited_tick_value(self, pid: int) -> int:
with self.proc_lock:
return int(self.proc_exited_tick.get(int(pid), 0))
def proc_exit_status_value(self, pid: int) -> int:
with self.proc_lock:
value = self.proc_status.get(int(pid))
return int(value) if value is not None else 0
def proc_runtime_ticks(self, pid: int) -> int:
with self.proc_lock:
start = int(self.proc_started_tick.get(int(pid), 0))
state = int(self.proc_state.get(int(pid), 0))
end = int(self.proc_exited_tick.get(int(pid), 0))
if start == 0:
return 0
if state == PROC_STATE_EXITED and end >= start:
return end - start
now = self.timer_ticks()
return 0 if now < start else now - start
def proc_mem_bytes_value(self, pid: int) -> int:
with self.proc_lock:
return int(self.proc_mem_bytes.get(int(pid), 0))
def proc_tty_index_value(self, pid: int) -> int:
with self.proc_lock:
return int(self.proc_tty_index.get(int(pid), 0))
def proc_path_value(self, pid: int) -> str:
with self.proc_lock:
return str(self.proc_path.get(int(pid), ""))
def proc_argc(self, pid: int) -> int: def proc_argc(self, pid: int) -> int:
with self.proc_lock: with self.proc_lock:
return len(self.proc_argv.get(int(pid), [])) return len(self.proc_argv.get(int(pid), []))