内容由用户产出,本次仅代为提交、未做任何语义修改;含 static 修饰符前置迁移等(新增 static 前缀 16 行)。提交消息为中性描述——如需更精确的说明请 amend。
823 lines
32 KiB
Plaintext
823 lines
32 KiB
Plaintext
import drivers.serial.uart.serial as serial
|
||
import drivers.fs.fat32.fat32 as fat32
|
||
import drivers.fs.fat32.fat32_types as fat32_types
|
||
import drivers.core.cpu.cpu as cpu
|
||
import mm.mm as mm
|
||
import paging.paging as paging
|
||
import sched.process as proc
|
||
import sched.sched as sched
|
||
import intr.gdt as gdt
|
||
import viperlib
|
||
import string
|
||
import asm
|
||
|
||
|
||
# Do NOT import KERN_STACK_SIZE/USER_STACK_SIZE from process - CDefine cross-module import is broken
|
||
# Redefine them here to ensure correct values
|
||
define KERN_STACK_SIZE = 8192
|
||
define USER_STACK_SIZE = 65536
|
||
|
||
define ELFMAG0 = 0x7F
|
||
define ELFMAG1 = 0x45
|
||
define ELFMAG2 = 0x4C
|
||
define ELFMAG3 = 0x46
|
||
define ELFCLASS64 = 2
|
||
define ELFDATA2LSB = 1
|
||
define ET_EXEC = 2
|
||
define ET_DYN = 3
|
||
define EM_X86_64 = 62
|
||
define PT_LOAD = 1
|
||
define PT_DYNAMIC = 2
|
||
define PF_X = 1
|
||
define PF_W = 2
|
||
define PF_R = 4
|
||
define DT_NULL = 0
|
||
define DT_HASH = 4
|
||
define DT_STRTAB = 5
|
||
define DT_SYMTAB = 6
|
||
define DT_STRSZ = 10
|
||
define DT_SYMENT = 11
|
||
define DT_RELA = 7
|
||
define DT_RELASZ = 8
|
||
define DT_RELAENT = 9
|
||
define DT_PLTRELSZ = 2
|
||
define DT_JMPREL = 23
|
||
define R_X86_64_64 = 1
|
||
define R_X86_64_GLOB_DAT = 6
|
||
define R_X86_64_JUMP_SLOT = 7
|
||
define R_X86_64_RELATIVE = 8
|
||
define STB_GLOBAL = 1
|
||
define STB_WEAK = 2
|
||
|
||
@__attribute__(packed)
|
||
class Elf64_Ehdr:
|
||
e_ident: [16]uint8
|
||
e_type: uint16
|
||
e_machine: uint16
|
||
e_version: uint32
|
||
e_entry: uint64
|
||
e_phoff: uint64
|
||
e_shoff: uint64
|
||
e_flags: uint32
|
||
e_ehsize: uint16
|
||
e_phentsize: uint16
|
||
e_phnum: uint16
|
||
e_shentsize: uint16
|
||
e_shnum: uint16
|
||
e_shstrndx: uint16
|
||
|
||
@__attribute__(packed)
|
||
class Elf64_Phdr:
|
||
p_type: uint32
|
||
p_flags: uint32
|
||
p_offset: uint64
|
||
p_vaddr: uint64
|
||
p_paddr: uint64
|
||
p_filesz: uint64
|
||
p_memsz: uint64
|
||
p_align: uint64
|
||
|
||
define MAX_PHDRS = 16
|
||
|
||
@__attribute__(packed)
|
||
class Elf64_Shdr:
|
||
sh_name: uint32
|
||
sh_type: uint32
|
||
sh_flags: uint64
|
||
sh_addr: uint64
|
||
sh_offset: uint64
|
||
sh_size: uint64
|
||
sh_link: uint32
|
||
sh_info: uint32
|
||
sh_addralign: uint64
|
||
sh_entsize: uint64
|
||
|
||
@__attribute__(packed)
|
||
class Elf64_Sym:
|
||
st_name: uint32
|
||
st_info: uint8
|
||
st_other: uint8
|
||
st_shndx: uint16
|
||
st_value: uint64
|
||
st_size: uint64
|
||
|
||
@__attribute__(packed)
|
||
class Elf64_Rela:
|
||
r_offset: uint64
|
||
r_info: uint64
|
||
r_addend: int64
|
||
|
||
@__attribute__(packed)
|
||
class Elf64_Dyn:
|
||
d_tag: int64
|
||
d_val: uint64
|
||
|
||
define MAX_SHDRS = 64
|
||
define MAX_DYNS = 64
|
||
define MAX_RELAS = 256
|
||
|
||
def load_elf(path: const str, do_map: int = 1) -> rawptr:
|
||
fp: fat32_types.fileobj* = fat32.open(path, fat32_types.FA_READ)
|
||
if fp is None:
|
||
err: uint32 = fat32.last_error()
|
||
eb: [64]char
|
||
viperlib.snprintf(&eb, 64, "[elf] failed to open file err=%d\n", err)
|
||
serial.puts(eb)
|
||
return None
|
||
|
||
ehdr_buf: [64]uint8
|
||
string.memset(&ehdr_buf, 0, 64)
|
||
read_len: uint32 = fat32.read(fp, &ehdr_buf, 64)
|
||
# [DIAG] read_len 实际值 + call 后 RAX 快照:区分"call 返回错"(rl=0 rax=0)
|
||
# vs "store 后 RAX 被覆盖"(rl=0 rax=64)
|
||
_rax_v2: uint64 = 0
|
||
# 读 RAX 未声明输入:至少须声明 CLOBBER_RAX,让编译器知道本块不保留
|
||
# RAX(此前只声明 ~{memory},编译器可假设 RAX 存活 → 读到的值不保证是
|
||
# 紧邻 call 的返回值)。N16。(本块为 [DIAG] 探针,问题定位后建议删除。)
|
||
__asm__(f"""mov {asmout(_rax_v2, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
||
_dbrl: [96]char
|
||
viperlib.snprintf(&_dbrl, 96, "[elf] rl=%lu rax=0x%lx\n", read_len, _rax_v2)
|
||
serial.puts(&_dbrl)
|
||
if read_len < 64:
|
||
serial.puts("[elf] failed to read ehdr\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
ehdr: Elf64_Ehdr* = &ehdr_buf
|
||
|
||
if ehdr.e_ident[0] != ELFMAG0 or ehdr.e_ident[1] != ELFMAG1 or ehdr.e_ident[2] != ELFMAG2 or ehdr.e_ident[3] != ELFMAG3:
|
||
serial.puts("[elf] bad magic\n")
|
||
# [DIAG] 一次性定位 bad magic:dump ehdr_buf 前 16 字节 + read_len,
|
||
# 区分全 0(read 未写)/ 目录数据(sector 错)/ 垃圾(seek 或缓冲错)。
|
||
_dbg_mg: [120]char
|
||
viperlib.snprintf(&_dbg_mg, 120,
|
||
"[elf] mg=%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x rl=%lu fp=%lu/%lu/%lu/%lu\n",
|
||
ehdr_buf[0], ehdr_buf[1], ehdr_buf[2], ehdr_buf[3],
|
||
ehdr_buf[4], ehdr_buf[5], ehdr_buf[6], ehdr_buf[7],
|
||
ehdr_buf[8], ehdr_buf[9], ehdr_buf[10], ehdr_buf[11],
|
||
ehdr_buf[12], ehdr_buf[13], ehdr_buf[14], ehdr_buf[15],
|
||
read_len, fp.first_cluster, fp.current_cluster, fp.file_pos, fp.file_size)
|
||
serial.puts(&_dbg_mg)
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
if ehdr.e_ident[4] != ELFCLASS64:
|
||
serial.puts("[elf] not ELF64\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
if ehdr.e_ident[5] != ELFDATA2LSB:
|
||
serial.puts("[elf] not little-endian\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
if ehdr.e_type != ET_EXEC and ehdr.e_type != ET_DYN:
|
||
serial.puts("[elf] not ET_EXEC or ET_DYN\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
if ehdr.e_machine != EM_X86_64:
|
||
serial.puts("[elf] not x86_64\n")
|
||
# [DIAG] e_machine 错:dump ehdr 前 24 字节(read_len=64 正确但内容错)
|
||
_dbg_em: [128]char
|
||
viperlib.snprintf(&_dbg_em, 128,
|
||
"[elf] ehdr=%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x rl=%lu\n",
|
||
ehdr_buf[0], ehdr_buf[1], ehdr_buf[2], ehdr_buf[3],
|
||
ehdr_buf[4], ehdr_buf[5], ehdr_buf[6], ehdr_buf[7],
|
||
ehdr_buf[8], ehdr_buf[9], ehdr_buf[10], ehdr_buf[11],
|
||
ehdr_buf[12], ehdr_buf[13], ehdr_buf[14], ehdr_buf[15],
|
||
ehdr_buf[16], ehdr_buf[17], ehdr_buf[18], ehdr_buf[19], read_len)
|
||
serial.puts(&_dbg_em)
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
entry_point: uint64 = ehdr.e_entry
|
||
phoff: uint64 = ehdr.e_phoff
|
||
phentsize: uint16 = ehdr.e_phentsize
|
||
phnum: uint16 = ehdr.e_phnum
|
||
|
||
if phentsize == 0 or phnum == 0 or phnum > MAX_PHDRS:
|
||
serial.puts("[elf] invalid phdr count\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
cr: [80]char
|
||
viperlib.snprintf(&cr, 80, "[elf] entry=0x%lx phoff=%lu phentsize=%u phnum=%u\n", entry_point, phoff, phentsize, phnum)
|
||
serial.puts(cr)
|
||
|
||
phdr_bufs: [MAX_PHDRS]Elf64_Phdr
|
||
string.memset(&phdr_bufs, 0, MAX_PHDRS * 56)
|
||
|
||
for pi in range(phnum):
|
||
phdr_off: uint64 = phoff + uint64(pi) * uint64(phentsize)
|
||
fat32.seek(fp, uint32(phdr_off))
|
||
fat32.read(fp, &phdr_bufs[pi], 56)
|
||
|
||
found_load: int = 0
|
||
lowest_vaddr: uint64 = 0
|
||
highest_vaddr_end: uint64 = 0
|
||
for pi in range(phnum):
|
||
if phdr_bufs[pi].p_type == PT_LOAD:
|
||
seg_end: uint64 = phdr_bufs[pi].p_vaddr + phdr_bufs[pi].p_memsz
|
||
if found_load == 0:
|
||
lowest_vaddr = phdr_bufs[pi].p_vaddr
|
||
highest_vaddr_end = seg_end
|
||
found_load = 1
|
||
else:
|
||
if phdr_bufs[pi].p_vaddr < lowest_vaddr:
|
||
lowest_vaddr = phdr_bufs[pi].p_vaddr
|
||
if seg_end > highest_vaddr_end:
|
||
highest_vaddr_end = seg_end
|
||
|
||
if found_load == 0:
|
||
serial.puts("[elf] no LOAD segments\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
total_memsz: uint64 = highest_vaddr_end - lowest_vaddr
|
||
total_pages: uint64 = (total_memsz + 0xFFF) // 0x1000
|
||
|
||
cr2: [80]char
|
||
viperlib.snprintf(&cr2, 80, "[elf] vaddr 0x%lx-0x%lx pages=%lu\n", lowest_vaddr, highest_vaddr_end, total_pages)
|
||
serial.puts(cr2)
|
||
|
||
raw_alloc: rawptr = mm.malloc(total_pages * 0x1000 + 0x1000)
|
||
_db_ra: [80]char
|
||
viperlib.snprintf(&_db_ra, 80, "[elf] malloc ret raw_alloc=0x%lx\n", uint64(raw_alloc))
|
||
serial.puts(_db_ra)
|
||
if not raw_alloc:
|
||
serial.puts("[elf] malloc failed\n")
|
||
fat32.close(fp)
|
||
return None
|
||
|
||
phys_base_aligned: uint64 = (uint64(raw_alloc) + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
phys_base: rawptr = rawptr(phys_base_aligned)
|
||
|
||
string.memset(phys_base, 0, total_pages * 0x1000)
|
||
|
||
for pi in range(phnum):
|
||
if phdr_bufs[pi].p_type == PT_LOAD:
|
||
vaddr: uint64 = phdr_bufs[pi].p_vaddr
|
||
file_off: uint64 = phdr_bufs[pi].p_offset
|
||
filesz: uint64 = phdr_bufs[pi].p_filesz
|
||
memsz: uint64 = phdr_bufs[pi].p_memsz
|
||
dest_off: uint64 = vaddr - lowest_vaddr
|
||
dest: rawptr = rawptr(uint64(phys_base) + dest_off)
|
||
|
||
if filesz > 0:
|
||
fat32.seek(fp, uint32(file_off))
|
||
fat32.read(fp, dest, uint32(filesz))
|
||
|
||
if memsz > filesz:
|
||
bss_start: rawptr = rawptr(uint64(dest) + filesz)
|
||
bss_len: uint64 = memsz - filesz
|
||
string.memset(bss_start, 0, bss_len)
|
||
|
||
fat32.close(fp)
|
||
|
||
virt_page_start: uint64 = lowest_vaddr & ~uint64(0xFFF)
|
||
phys_page_start: uint64 = uint64(phys_base) & ~uint64(0xFFF)
|
||
map_end: uint64 = (highest_vaddr_end + 0xFFF) & ~uint64(0xFFF)
|
||
num_map_pages: uint64 = (map_end - virt_page_start) // 0x1000
|
||
|
||
# Store mapping info for spawn_elf (used when do_map=0)
|
||
global _last_elf_phys_page_start, _last_elf_virt_page_start, _last_elf_num_map_pages
|
||
_last_elf_phys_page_start = phys_page_start
|
||
_last_elf_virt_page_start = virt_page_start
|
||
_last_elf_num_map_pages = num_map_pages
|
||
|
||
if do_map != 0:
|
||
for mi in range(num_map_pages):
|
||
virt_addr: uint64 = virt_page_start + mi * 0x1000
|
||
phys_addr: uint64 = phys_page_start + mi * 0x1000
|
||
page_flags: uint64 = paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER
|
||
paging.MapPage(virt_addr, phys_addr, page_flags)
|
||
|
||
entry_addr: rawptr = rawptr(entry_point)
|
||
|
||
return entry_addr
|
||
|
||
def run_elf(path: const str) -> int:
|
||
entry: rawptr = load_elf(path)
|
||
if entry is None:
|
||
serial.puts("[elf] load failed\n")
|
||
return -1
|
||
serial.puts("[elf] jumping to entry...\n")
|
||
# 被调方是任意 ELF 入口(外部代码):`call` 破坏全部 caller-saved 寄存器,
|
||
# 除 rax/rcx/rdx/r8~r11 外必须声明 rsi/rdi(N20)。
|
||
__asm__(f"""call {asminp(entry, ASM_DESCR.REG_ANY)}""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
||
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
|
||
ASM_DESCR.CLOBBER_RSI, ASM_DESCR.CLOBBER_RDI,
|
||
ASM_DESCR.CLOBBER_R8, ASM_DESCR.CLOBBER_R9,
|
||
ASM_DESCR.CLOBBER_R10, ASM_DESCR.CLOBBER_R11])
|
||
serial.puts("[elf] returned from entry\n")
|
||
return 0
|
||
|
||
define MAX_SPAWNED = 8
|
||
_elf_entries: [MAX_SPAWNED]rawptr
|
||
|
||
# User stack virtual address: top of 48-bit user space, 64KB below
|
||
define USER_STACK_VTOP = 0x7FFFFFE00000
|
||
|
||
# Globals for ELF mapping info (set by load_elf when do_map=0, used by spawn_elf
|
||
# to map ELF pages in the cloned PML4 instead of kernel PML4).
|
||
# ROOT CAUSE: previously load_elf mapped ELF vaddr (e.g. 0x400000) into the
|
||
# kernel PML4, overwriting the identity mapping of buddy system memory_block
|
||
# headers at 0x400000. This corrupted buddy metadata -> malloc failures,
|
||
# pid=0, and #GP in split_block. Fix: load_elf does NOT map; spawn_elf maps
|
||
# in the cloned PML4 after clone_for_process.
|
||
_last_elf_phys_page_start: uint64 = 0
|
||
_last_elf_virt_page_start: uint64 = 0
|
||
_last_elf_num_map_pages: uint64 = 0
|
||
|
||
def _elf_entry_wrapper(arg: rawptr) -> int:
|
||
pid: int = int(uint64(arg))
|
||
_db1: [64]char
|
||
viperlib.snprintf(&_db1, 64, "[elf] wrapper pid=%d\n", pid)
|
||
serial.puts(_db1)
|
||
# Disable interrupts around switch_pid + MapPage + TSS setup: @pml4 is a global
|
||
# that switch_pid modifies. A timer interrupt mid-section would make the scheduler
|
||
# call switch_pid for another thread, corrupting @pml4 for our MapPage calls.
|
||
asm.cli()
|
||
proc.switch_pid(pid)
|
||
cur_p: proc.Process* = proc.ProcessManager.current()
|
||
entry_fn: rawptr = cur_p.entry
|
||
# [DIAG] 定位用户进程首个指令 PF:打印 entry 实际值(PF cr2=ELF 物理区最后一页,
|
||
# 疑似 entry 被写成 phys 地址而非虚拟 0x400000)。
|
||
_dbw: [160]char
|
||
viperlib.snprintf(&_dbw, 160, "[wrap] pid=%d cur=0x%lx ent=0x%lx ebase=0x%lx ks=0x%lx us=0x%lx\n",
|
||
pid, uint64(cur_p), uint64(entry_fn), cur_p.elf_base, cur_p.kern_stack, cur_p.user_stack)
|
||
serial.puts(&_dbw)
|
||
if entry_fn is not None:
|
||
user_stack_vtop: uint64 = USER_STACK_VTOP
|
||
us_sz: uint64 = USER_STACK_SIZE
|
||
user_stack_vbottom: uint64 = USER_STACK_VTOP - us_sz
|
||
# Physical pages of the user stack (allocated by mm.malloc, identity-mapped)
|
||
us_size: uint64 = USER_STACK_SIZE
|
||
phys_bottom: uint64 = (cur_p.user_stack - us_size) & ~uint64(0xFFF)
|
||
# Map each page of the user stack at the virtual address with PTE_USER
|
||
num_stack_pages: uint64 = 16 # USER_STACK_SIZE(65536) / PAGE_SIZE(4096) = 16
|
||
si: uint64
|
||
for si in range(num_stack_pages):
|
||
vaddr: uint64 = (user_stack_vbottom & ~uint64(0xFFF)) + si * uint64(0x1000)
|
||
paddr: uint64 = phys_bottom + si * uint64(0x1000)
|
||
paging.MapPage(vaddr, paddr, paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER)
|
||
# Set TSS RSP0 so interrupts from Ring 3 use kernel stack
|
||
gdt.set_tss_rsp0(cur_p.kern_stack)
|
||
# Set GS kernel_rsp0 for syscall entry from Ring 3
|
||
cpu.set_kernel_rsp0(cur_p.kern_stack)
|
||
# Keep interrupts disabled until iretq: drop_to_user_mode's iretq
|
||
# will set RFLAGS.IF from the saved frame (0x202) for user mode.
|
||
# This prevents timer interrupt preemption between TSS setup and iretq.
|
||
# Drop to Ring 3 user mode via iretq
|
||
proc.drop_to_user_mode(entry_fn, user_stack_vtop, cur_p.kern_stack)
|
||
cur: proc.Process* = proc.ProcessManager.current()
|
||
cur.exit()
|
||
return 0
|
||
|
||
def spawn_elf(path: const str) -> int:
|
||
fat32.lock()
|
||
name_start: int = 0
|
||
si: int = 0
|
||
while True:
|
||
ch: char = path[si]
|
||
if ch == 0: break
|
||
if ch == 47: name_start = si + 1
|
||
si += 1
|
||
|
||
lowest_vaddr: uint64 = 0
|
||
highest_vaddr_end: uint64 = 0
|
||
fp_scan: fat32_types.fileobj* = fat32.open(path, fat32_types.FA_READ)
|
||
if fp_scan is None:
|
||
serr: uint32 = fat32.last_error()
|
||
seb: [64]char
|
||
viperlib.snprintf(&seb, 64, "[elf] spawn scan open failed err=%d\n", serr)
|
||
serial.puts(&seb)
|
||
fat32.unlock()
|
||
return -1
|
||
if fp_scan is not None:
|
||
ehdr_buf_s: [64]uint8
|
||
string.memset(&ehdr_buf_s, 0, 64)
|
||
fat32.read(fp_scan, &ehdr_buf_s, 64)
|
||
ehdr_s: Elf64_Ehdr* = &ehdr_buf_s
|
||
if ehdr_s.e_type == ET_EXEC or ehdr_s.e_type == ET_DYN:
|
||
phoff_s: uint64 = ehdr_s.e_phoff
|
||
phentsize_s: uint16 = ehdr_s.e_phentsize
|
||
phnum_s: uint16 = ehdr_s.e_phnum
|
||
if phentsize_s > 0 and phnum_s > 0 and phnum_s <= MAX_PHDRS:
|
||
phdr_bufs_s: [MAX_PHDRS]Elf64_Phdr
|
||
string.memset(&phdr_bufs_s, 0, MAX_PHDRS * 56)
|
||
found_load_s: int = 0
|
||
for pi_s in range(phnum_s):
|
||
fat32.seek(fp_scan, uint32(phoff_s + uint64(pi_s) * uint64(phentsize_s)))
|
||
fat32.read(fp_scan, &phdr_bufs_s[pi_s], 56)
|
||
if phdr_bufs_s[pi_s].p_type == PT_LOAD:
|
||
seg_end_s: uint64 = phdr_bufs_s[pi_s].p_vaddr + phdr_bufs_s[pi_s].p_memsz
|
||
if found_load_s == 0:
|
||
lowest_vaddr = phdr_bufs_s[pi_s].p_vaddr
|
||
highest_vaddr_end = seg_end_s
|
||
found_load_s = 1
|
||
else:
|
||
if phdr_bufs_s[pi_s].p_vaddr < lowest_vaddr:
|
||
lowest_vaddr = phdr_bufs_s[pi_s].p_vaddr
|
||
if seg_end_s > highest_vaddr_end:
|
||
highest_vaddr_end = seg_end_s
|
||
fat32.close(fp_scan)
|
||
|
||
isolate_va: uint64 = lowest_vaddr
|
||
if isolate_va == 0:
|
||
isolate_va = 0x400000
|
||
|
||
# Load ELF to physical memory WITHOUT mapping in kernel PML4.
|
||
# do_map=0: load_elf stores mapping info in globals for spawn_elf to use.
|
||
# ROOT CAUSE FIX: previously load_elf mapped ELF vaddr (e.g. 0x400000) into
|
||
# kernel PML4, overwriting identity mapping of buddy memory_block headers.
|
||
entry: rawptr = load_elf(path, 0)
|
||
if entry is None:
|
||
serial.puts("[elf] spawn: load failed\n")
|
||
fat32.unlock()
|
||
return -1
|
||
|
||
# Clone kernel PML4 (identity mapping intact, no ELF mapping yet)
|
||
proc_pml4: uint64 = paging.clone_for_process(isolate_va)
|
||
slog1: [80]char
|
||
viperlib.snprintf(&slog1, 80, "[elf] spawn: clone pml4=0x%lx va=0x%lx\n", proc_pml4, isolate_va)
|
||
serial.puts(&slog1)
|
||
if proc_pml4 == 0:
|
||
serial.puts("[elf] spawn: clone_for_process failed\n")
|
||
fat32.unlock()
|
||
return -1
|
||
|
||
# Map ELF pages in the CLONED PML4 (not kernel's).
|
||
# This prevents overwriting kernel identity mapping at 0x400000 which
|
||
# would corrupt buddy system memory_block headers.
|
||
# Must disable interrupts: @pml4 global is used by MapPage and a timer
|
||
# interrupt mid-section would call switch_pid, corrupting @pml4.
|
||
asm.cli()
|
||
_kernel_cr3: uint64 = 0
|
||
__asm__(f"""mov {asmout(_kernel_cr3, ASM_DESCR.OUTPUT_REG)}, cr3""", op=[ASM_DESCR.CLOBBER_MEMORY])
|
||
__asm__(f"mov cr3, {asminp(proc_pml4, ASM_DESCR.REG_ANY)}", op=[ASM_DESCR.CLOBBER_MEMORY])
|
||
paging.set_active_pml4(proc_pml4)
|
||
mi: uint64
|
||
for mi in range(_last_elf_num_map_pages):
|
||
_va: uint64 = _last_elf_virt_page_start + mi * 0x1000
|
||
_pa: uint64 = _last_elf_phys_page_start + mi * 0x1000
|
||
paging.MapPage(_va, _pa, paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER)
|
||
__asm__(f"mov cr3, {asminp(_kernel_cr3, ASM_DESCR.REG_ANY)}", op=[ASM_DESCR.CLOBBER_MEMORY])
|
||
paging.set_active_pml4(_kernel_cr3)
|
||
asm.sti()
|
||
|
||
# path[name_start:] 切片表达式 translate_subscript 不支持 Slice(返回 None)→
|
||
# 作为实参丢 name、entry 错位成第一参数(TPV 已修,此处指针算术双保险)
|
||
nm_ptr: const str = str(uint64(path) + uint64(name_start))
|
||
p: proc.Process* = proc.ProcessManager.create_process(nm_ptr, entry)
|
||
if p is None:
|
||
serial.puts("[elf] spawn: create_process failed\n")
|
||
fat32.unlock()
|
||
return -2
|
||
p.elf_base = uint64(entry)
|
||
p.pml4_root = proc_pml4
|
||
if p.pid >= 0 and p.pid < MAX_SPAWNED:
|
||
_elf_entries[p.pid] = entry
|
||
|
||
th: sched.Thread* = sched.Scheduler.create_thread(&_elf_entry_wrapper, rawptr(uint64(p.pid)), p.pid)
|
||
if th is None:
|
||
serial.puts("[elf] spawn: create_thread failed\n")
|
||
p.exit()
|
||
fat32.unlock()
|
||
return -3
|
||
tid: int = th.tid
|
||
p.addThread(tid)
|
||
slog: [80]char
|
||
viperlib.snprintf(&slog, 80, "[elf] spawn: pid=%d tid=%d entry=0x%lx pml4=0x%lx\n", p.pid, tid, uint64(entry), p.pml4_root)
|
||
serial.puts(&slog)
|
||
# [DIAG] 确认 return p.pid 的实际值(main 收到 pid=0 而此处打印 3)
|
||
_dbw2: [64]char
|
||
viperlib.snprintf(&_dbw2, 64, "[elf] spawn ret pid=%d\n", p.pid)
|
||
serial.puts(&_dbw2)
|
||
fat32.unlock()
|
||
return p.pid
|
||
|
||
def _resolve_symbol(name: const str) -> rawptr:
|
||
nm: int = 0
|
||
while name[nm] != 0:
|
||
nm += 1
|
||
cmp_name: const str = name
|
||
cmp_len: int = nm
|
||
if nm > 9:
|
||
sha1_len2: int = 0
|
||
for si2 in range(nm):
|
||
ch2: char = name[si2]
|
||
if ch2 == 46:
|
||
if sha1_len2 >= 8:
|
||
cmp_name = str(uint64(name) + si2 + 1)
|
||
cmp_len = nm - si2 - 1
|
||
break
|
||
if ch2 == 0:
|
||
break
|
||
if (ch2 >= 48 and ch2 <= 57) or (ch2 >= 97 and ch2 <= 102):
|
||
sha1_len2 += 1
|
||
else:
|
||
break
|
||
if cmp_len == 11:
|
||
match: int = 1
|
||
for i in range(cmp_len):
|
||
if cmp_name[i] != "serial_puts"[i]:
|
||
match = 0
|
||
if match:
|
||
return &serial.puts
|
||
return None
|
||
|
||
_so_strtab: rawptr
|
||
_so_symtab: rawptr
|
||
_so_syment: uint64
|
||
_so_sym_count: int
|
||
|
||
def load_so(path: const str) -> rawptr:
|
||
fat32.lock()
|
||
fp: fat32_types.fileobj* = fat32.open(path, fat32_types.FA_READ)
|
||
if fp is None:
|
||
serial.puts("[so] failed to open file\n")
|
||
fat32.unlock()
|
||
return None
|
||
|
||
ehdr_buf: [64]uint8
|
||
string.memset(&ehdr_buf, 0, 64)
|
||
read_len: uint32 = fat32.read(fp, &ehdr_buf, 64)
|
||
if read_len < 64:
|
||
serial.puts("[so] failed to read ehdr\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
|
||
ehdr: Elf64_Ehdr* = &ehdr_buf
|
||
if ehdr.e_ident[0] != ELFMAG0 or ehdr.e_ident[1] != ELFMAG1 or ehdr.e_ident[2] != ELFMAG2 or ehdr.e_ident[3] != ELFMAG3:
|
||
serial.puts("[so] bad magic\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
if ehdr.e_ident[4] != ELFCLASS64:
|
||
serial.puts("[so] not ELF64\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
if ehdr.e_type == ET_DYN:
|
||
pass
|
||
elif ehdr.e_type == ET_EXEC:
|
||
pass
|
||
else:
|
||
serial.puts("[so] not ET_DYN or ET_EXEC\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
|
||
phoff: uint64 = ehdr.e_phoff
|
||
phentsize: uint16 = ehdr.e_phentsize
|
||
phnum: uint16 = ehdr.e_phnum
|
||
|
||
# 程序头数量越界保护(phdr_bufs 仅 MAX_PHDRS 项):此前缺此校验,
|
||
# 异常/恶意 e_phnum > MAX_PHDRS 会越界写栈(load_elf 已有同款校验)。
|
||
if phnum > MAX_PHDRS:
|
||
serial.puts("[so] phnum exceeds MAX_PHDRS\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
|
||
phdr_bufs: [MAX_PHDRS]Elf64_Phdr
|
||
string.memset(&phdr_bufs, 0, MAX_PHDRS * 56)
|
||
for pi in range(phnum):
|
||
phdr_off: uint64 = phoff + uint64(pi) * uint64(phentsize)
|
||
fat32.seek(fp, uint32(phdr_off))
|
||
fat32.read(fp, &phdr_bufs[pi], 56)
|
||
|
||
found_load: int = 0
|
||
lowest_vaddr: uint64 = 0
|
||
highest_vaddr_end: uint64 = 0
|
||
for pi in range(phnum):
|
||
if phdr_bufs[pi].p_type == PT_LOAD:
|
||
seg_end: uint64 = phdr_bufs[pi].p_vaddr + phdr_bufs[pi].p_memsz
|
||
if found_load == 0:
|
||
lowest_vaddr = phdr_bufs[pi].p_vaddr
|
||
highest_vaddr_end = seg_end
|
||
found_load = 1
|
||
else:
|
||
if phdr_bufs[pi].p_vaddr < lowest_vaddr:
|
||
lowest_vaddr = phdr_bufs[pi].p_vaddr
|
||
if seg_end > highest_vaddr_end:
|
||
highest_vaddr_end = seg_end
|
||
|
||
if found_load == 0:
|
||
serial.puts("[so] no LOAD segments\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
|
||
total_memsz: uint64 = highest_vaddr_end - lowest_vaddr
|
||
total_pages: uint64 = (total_memsz + 0xFFF) // 0x1000
|
||
|
||
so_load_addr: uint64 = 0x600000
|
||
raw_alloc: rawptr = mm.malloc(total_pages * 0x1000 + 0x1000)
|
||
if not raw_alloc:
|
||
serial.puts("[so] malloc failed\n")
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return None
|
||
|
||
phys_base_aligned: uint64 = (uint64(raw_alloc) + 0xFFF) & ~uint64(0xFFF)
|
||
phys_base: rawptr = rawptr(phys_base_aligned)
|
||
string.memset(phys_base, 0, total_pages * 0x1000)
|
||
|
||
for pi in range(phnum):
|
||
if phdr_bufs[pi].p_type == PT_LOAD:
|
||
vaddr: uint64 = phdr_bufs[pi].p_vaddr
|
||
file_off: uint64 = phdr_bufs[pi].p_offset
|
||
filesz: uint64 = phdr_bufs[pi].p_filesz
|
||
memsz: uint64 = phdr_bufs[pi].p_memsz
|
||
dest_off: uint64 = vaddr - lowest_vaddr
|
||
dest: rawptr = rawptr(uint64(phys_base) + dest_off)
|
||
if filesz > 0:
|
||
fat32.seek(fp, uint32(file_off))
|
||
fat32.read(fp, dest, uint32(filesz))
|
||
if memsz > filesz:
|
||
bss_start: rawptr = rawptr(uint64(dest) + filesz)
|
||
bss_len: uint64 = memsz - filesz
|
||
string.memset(bss_start, 0, bss_len)
|
||
|
||
virt_page_start: uint64 = (so_load_addr + lowest_vaddr) & ~uint64(0xFFF)
|
||
phys_page_start: uint64 = uint64(phys_base) & ~uint64(0xFFF)
|
||
map_end: uint64 = (so_load_addr + highest_vaddr_end + 0xFFF) & ~uint64(0xFFF)
|
||
num_map_pages: uint64 = (map_end - virt_page_start) // 0x1000
|
||
|
||
for mi in range(num_map_pages):
|
||
virt_addr: uint64 = virt_page_start + mi * 0x1000
|
||
phys_addr: uint64 = phys_page_start + mi * 0x1000
|
||
paging.MapPage(virt_addr, phys_addr, paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER)
|
||
|
||
base_vaddr: uint64 = so_load_addr + lowest_vaddr
|
||
|
||
dyn_buf: [MAX_DYNS]Elf64_Dyn
|
||
string.memset(&dyn_buf, 0, MAX_DYNS * 16)
|
||
dyn_count: int = 0
|
||
for pi in range(phnum):
|
||
if phdr_bufs[pi].p_type == PT_DYNAMIC:
|
||
fat32.seek(fp, uint32(phdr_bufs[pi].p_offset))
|
||
# p_filesz 无上界,直接读会越界写 dyn_buf(MAX_DYNS*16 字节)→ 夹取
|
||
filesz_d: uint32 = uint32(phdr_bufs[pi].p_filesz)
|
||
max_dyn_bytes: uint32 = uint32(MAX_DYNS * 16)
|
||
if filesz_d > max_dyn_bytes:
|
||
filesz_d = max_dyn_bytes
|
||
dyn_read: uint32 = fat32.read(fp, &dyn_buf, filesz_d)
|
||
dyn_count = int(dyn_read) // 16
|
||
|
||
strtab_vaddr: uint64 = 0
|
||
symtab_vaddr: uint64 = 0
|
||
hash_vaddr: uint64 = 0
|
||
syment: uint64 = 0
|
||
rela_vaddr: uint64 = 0
|
||
relasz: uint64 = 0
|
||
jmprel_vaddr: uint64 = 0
|
||
pltrelsz: uint64 = 0
|
||
for di in range(dyn_count):
|
||
if dyn_buf[di].d_tag == DT_NULL: break
|
||
if dyn_buf[di].d_tag == DT_STRTAB: strtab_vaddr = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_SYMTAB: symtab_vaddr = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_HASH: hash_vaddr = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_SYMENT: syment = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_RELA: rela_vaddr = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_RELASZ: relasz = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_JMPREL: jmprel_vaddr = dyn_buf[di].d_val
|
||
elif dyn_buf[di].d_tag == DT_PLTRELSZ: pltrelsz = dyn_buf[di].d_val
|
||
|
||
strtab_ptr: rawptr = rawptr(strtab_vaddr + so_load_addr)
|
||
symtab_ptr: rawptr = rawptr(symtab_vaddr + so_load_addr)
|
||
|
||
if rela_vaddr != 0 and relasz > 0:
|
||
rela_count: uint64 = relasz // 24
|
||
for ri in range(rela_count):
|
||
rela: Elf64_Rela* = Elf64_Rela*(uint64(rela_vaddr) + so_load_addr + ri * 24)
|
||
r_type: uint32 = uint32(rela.r_info & 0xFFFFFFFF)
|
||
r_sym: uint32 = uint32(rela.r_info >> 32)
|
||
target_addr: rawptr = rawptr(rela.r_offset + so_load_addr)
|
||
if r_type == R_X86_64_RELATIVE:
|
||
val: uint64 = uint64(rela.r_addend) + so_load_addr
|
||
string.memcpy(target_addr, &val, 8)
|
||
elif r_type == R_X86_64_64 or r_type == R_X86_64_GLOB_DAT or r_type == R_X86_64_JUMP_SLOT:
|
||
sym: Elf64_Sym* = Elf64_Sym*(uint64(symtab_ptr) + uint64(r_sym) * syment)
|
||
sym_name: const str = str(uint64(strtab_ptr) + sym.st_name)
|
||
resolved: rawptr = _resolve_symbol(sym_name)
|
||
if resolved is not None:
|
||
val2: uint64 = uint64(resolved) + uint64(rela.r_addend)
|
||
string.memcpy(target_addr, &val2, 8)
|
||
elif sym.st_value != 0:
|
||
val3: uint64 = sym.st_value + so_load_addr + uint64(rela.r_addend)
|
||
string.memcpy(target_addr, &val3, 8)
|
||
|
||
if jmprel_vaddr != 0 and pltrelsz > 0:
|
||
jmprel_count: uint64 = pltrelsz // 24
|
||
for ji in range(jmprel_count):
|
||
rela2: Elf64_Rela* = Elf64_Rela*(jmprel_vaddr + so_load_addr + ji * 24)
|
||
r_type2: uint32 = uint32(rela2.r_info & 0xFFFFFFFF)
|
||
r_sym2: uint32 = uint32(rela2.r_info >> 32)
|
||
target2: rawptr = rawptr(rela2.r_offset + so_load_addr)
|
||
if r_type2 == R_X86_64_JUMP_SLOT or r_type2 == R_X86_64_GLOB_DAT:
|
||
sym2: Elf64_Sym* = Elf64_Sym*(uint64(symtab_ptr) + uint64(r_sym2) * syment)
|
||
sym_name2: const str = str(uint64(strtab_ptr) + sym2.st_name)
|
||
resolved2: rawptr = _resolve_symbol(sym_name2)
|
||
if resolved2 is not None:
|
||
val4: uint64 = uint64(resolved2) + uint64(rela2.r_addend)
|
||
string.memcpy(target2, &val4, 8)
|
||
elif sym2.st_value != 0:
|
||
val5: uint64 = sym2.st_value + so_load_addr + uint64(rela2.r_addend)
|
||
string.memcpy(target2, &val5, 8)
|
||
else:
|
||
cr3: [80]char
|
||
viperlib.snprintf(&cr3, 80, "[so] WARN: unresolved PLT sym offset=%lu\n", sym2.st_name)
|
||
serial.puts(&cr3)
|
||
|
||
fat32.close(fp)
|
||
|
||
global _so_strtab, _so_symtab, _so_syment, _so_sym_count
|
||
|
||
_so_strtab = strtab_ptr
|
||
_so_symtab = symtab_ptr
|
||
_so_syment = syment
|
||
_so_sym_count = 0
|
||
if hash_vaddr != 0:
|
||
hash_ptr: rawptr = rawptr(hash_vaddr + so_load_addr)
|
||
# 宽度只由指针的元素类型决定:nchain 是 4 字节字段,必须给出 u32*
|
||
# (i8* 现在恒读 1 字节)。地址口径不变(hash_ptr + 4 字节)。
|
||
nchain: uint32 = *(uint32*(uint64(hash_ptr) + 4))
|
||
_so_sym_count = int(nchain)
|
||
elif syment > 0 and strtab_vaddr > symtab_vaddr and symtab_vaddr != 0:
|
||
sym_tab_size: uint64 = strtab_vaddr - symtab_vaddr
|
||
_so_sym_count = int(sym_tab_size // syment)
|
||
|
||
cr: [80]char
|
||
viperlib.snprintf(&cr, 80, "[so] loaded at 0x%lx\n", base_vaddr)
|
||
serial.puts(cr)
|
||
|
||
fat32.unlock()
|
||
return rawptr(base_vaddr)
|
||
|
||
def get_so_symbol(so_base: rawptr, name: const str, so_path: const str) -> rawptr:
|
||
if _so_symtab is None or _so_strtab is None or _so_syment == 0:
|
||
return None
|
||
if name is None:
|
||
return None
|
||
for symi in range(_so_sym_count):
|
||
sb2: Elf64_Sym* = Elf64_Sym*(uint64(_so_symtab) + uint64(symi) * _so_syment)
|
||
if sb2.st_value != 0 and sb2.st_shndx != 0:
|
||
sym_name_off2: uint32 = sb2.st_name
|
||
cmp_off2: uint32 = sym_name_off2
|
||
match2: int = 0
|
||
ni2: int = 0
|
||
while True:
|
||
sc3: char = *(char*(uint64(_so_strtab) + cmp_off2 + ni2))
|
||
nc3: char = *(name + ni2)
|
||
if sc3 != nc3:
|
||
break
|
||
if sc3 == 0:
|
||
match2 = 1
|
||
break
|
||
ni2 += 1
|
||
if match2 == 0:
|
||
dot_pos3: uint32 = sym_name_off2
|
||
is_sha1_2: int = 0
|
||
sha1_len_2: uint32 = 0
|
||
while True:
|
||
dc4: char = *(char*(uint64(_so_strtab) + dot_pos3))
|
||
if dc4 == 46:
|
||
if sha1_len_2 >= 8:
|
||
is_sha1_2 = 1
|
||
break
|
||
if dc4 == 0:
|
||
break
|
||
if (dc4 >= 48 and dc4 <= 57) or (dc4 >= 97 and dc4 <= 102):
|
||
sha1_len_2 += 1
|
||
else:
|
||
break
|
||
dot_pos3 += 1
|
||
if is_sha1_2:
|
||
cmp_off2 = dot_pos3 + 1
|
||
match2 = 1
|
||
ni2 = 0
|
||
while True:
|
||
sc4: char = *(char*(uint64(_so_strtab) + cmp_off2 + ni2))
|
||
nc4: char = *(name + ni2)
|
||
if sc4 != nc4:
|
||
match2 = 0
|
||
break
|
||
if sc4 == 0:
|
||
break
|
||
ni2 += 1
|
||
if match2:
|
||
result_addr: uint64 = uint64(so_base) + sb2.st_value
|
||
dbg2: [80]char
|
||
viperlib.snprintf(&dbg2, 80, "[so] found '%s' at 0x%lx\n", rawptr(uint64(_so_strtab) + sym_name_off2), result_addr)
|
||
serial.puts(&dbg2)
|
||
return rawptr(result_addr)
|
||
serial.puts("[so] symbol not found\n")
|
||
return None
|