Files
ViperOS/VKernel/Kernel/execrunner/elf.vp
T
TermiNexus 6a54373c26 fix(vkernel): 补 ISR/调度 call 的 rsi/rdi clobber、rep stosb 内存 clobber,修 mm 别名笔误
N8  paging.vp:AllocPage 的 rep stosb 写 4096B 补 CLOBBER_MEMORY,避免优化器
    重排/消除其前后的内存读写
N9  coroutine.vp / sched.vp:_coro_entry/_thread_entry 的 call rax 补
    CLOBBER_RSI/CLOBBER_RDI(RSP 由 sub rsp,8 / add rsp,8 配对抵消,无需声明)
N15 process.vp:mm_mm.malloc/free → mm.malloc/free(该文件只有 import mm.mm as mm)
N16 elf.vp:读 RAX 的 c.Asm 补 CLOBBER_RAX([DIAG] 探针)
N20 elf.vp / idt.vp(12 处):外部 ELF 入口 / ISR handler 的 call 补
    CLOBBER_RSI/CLOBBER_RDI(被调方破坏全部 caller-saved 寄存器)
2026-09-11 17:40:21 +08:00

824 lines
34 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 t, c
# Do NOT import KERN_STACK_SIZE/USER_STACK_SIZE from process - CDefine cross-module import is broken
# Redefine them here to ensure correct values
KERN_STACK_SIZE: t.CDefine = 8192
USER_STACK_SIZE: t.CDefine = 65536
import viperlib
import string
import asm
import t, c
ELFMAG0: t.CDefine = 0x7F
ELFMAG1: t.CDefine = 0x45
ELFMAG2: t.CDefine = 0x4C
ELFMAG3: t.CDefine = 0x46
ELFCLASS64: t.CDefine = 2
ELFDATA2LSB: t.CDefine = 1
ET_EXEC: t.CDefine = 2
ET_DYN: t.CDefine = 3
EM_X86_64: t.CDefine = 62
PT_LOAD: t.CDefine = 1
PT_DYNAMIC: t.CDefine = 2
PF_X: t.CDefine = 1
PF_W: t.CDefine = 2
PF_R: t.CDefine = 4
DT_NULL: t.CDefine = 0
DT_HASH: t.CDefine = 4
DT_STRTAB: t.CDefine = 5
DT_SYMTAB: t.CDefine = 6
DT_STRSZ: t.CDefine = 10
DT_SYMENT: t.CDefine = 11
DT_RELA: t.CDefine = 7
DT_RELASZ: t.CDefine = 8
DT_RELAENT: t.CDefine = 9
DT_PLTRELSZ: t.CDefine = 2
DT_JMPREL: t.CDefine = 23
R_X86_64_64: t.CDefine = 1
R_X86_64_GLOB_DAT: t.CDefine = 6
R_X86_64_JUMP_SLOT: t.CDefine = 7
R_X86_64_RELATIVE: t.CDefine = 8
STB_GLOBAL: t.CDefine = 1
STB_WEAK: t.CDefine = 2
@c.Attribute(t.attr.packed)
class Elf64_Ehdr:
e_ident: t.CArray[uint8, 16]
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
@c.Attribute(t.attr.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
MAX_PHDRS: t.CDefine = 16
@c.Attribute(t.attr.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
@c.Attribute(t.attr.packed)
class Elf64_Sym:
st_name: uint32
st_info: uint8
st_other: uint8
st_shndx: uint16
st_value: uint64
st_size: uint64
@c.Attribute(t.attr.packed)
class Elf64_Rela:
r_offset: uint64
r_info: uint64
r_addend: int64
@c.Attribute(t.attr.packed)
class Elf64_Dyn:
d_tag: int64
d_val: uint64
MAX_SHDRS: t.CDefine = 64
MAX_DYNS: t.CDefine = 64
MAX_RELAS: t.CDefine = 256
def load_elf(path: t.CConst | str, do_map: int = 1) -> t.CPtr:
fp: fat32_types.fileobj | t.CPtr = fat32.open(path, fat32_types.FA_READ)
if fp is None:
err: uint32 = fat32.last_error()
eb: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(eb), 64, "[elf] failed to open file err=%d\n", err)
serial.puts(eb)
return None
ehdr_buf: t.CArray[uint8, 64]
string.memset(c.Addr(ehdr_buf), 0, 64)
read_len: uint32 = fat32.read(fp, c.Addr(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] 探针,问题定位后建议删除。)
c.Asm(f"""mov {c.AsmOut(_rax_v2, t.ASM_DESCR.OUTPUT_REG)}, rax""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX])
_dbrl: t.CArray[t.CChar, 96]
viperlib.snprintf(c.Addr(_dbrl), 96, "[elf] rl=%lu rax=0x%lx\n", read_len, _rax_v2)
serial.puts(c.Addr(_dbrl))
if read_len < 64:
serial.puts("[elf] failed to read ehdr\n")
fat32.close(fp)
return None
ehdr: Elf64_Ehdr | t.CPtr = c.Addr(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: t.CArray[t.CChar, 120]
viperlib.snprintf(c.Addr(_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(c.Addr(_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: t.CArray[t.CChar, 128]
viperlib.snprintf(c.Addr(_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(c.Addr(_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: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(cr), 80, "[elf] entry=0x%lx phoff=%lu phentsize=%u phnum=%u\n", entry_point, phoff, phentsize, phnum)
serial.puts(cr)
phdr_bufs: t.CArray[Elf64_Phdr, MAX_PHDRS]
string.memset(c.Addr(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, c.Addr(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: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(cr2), 80, "[elf] vaddr 0x%lx-0x%lx pages=%lu\n", lowest_vaddr, highest_vaddr_end, total_pages)
serial.puts(cr2)
raw_alloc: t.CPtr = mm.malloc(total_pages * 0x1000 + 0x1000)
_db_ra: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(_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: t.CPtr = t.CVoid(phys_base_aligned, t.CPtr)
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: t.CPtr = t.CVoid(uint64(phys_base) + dest_off, t.CPtr)
if filesz > 0:
fat32.seek(fp, uint32(file_off))
fat32.read(fp, dest, uint32(filesz))
if memsz > filesz:
bss_start: t.CPtr = t.CVoid(uint64(dest) + filesz, t.CPtr)
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: t.CPtr = t.CVoid(entry_point, t.CPtr)
return entry_addr
def run_elf(path: t.CConst | str) -> int:
entry: t.CPtr = 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)。
c.Asm(f"""call {c.AsmInp(entry, t.ASM_DESCR.REG_ANY)}""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX,
t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX,
t.ASM_DESCR.CLOBBER_RSI, t.ASM_DESCR.CLOBBER_RDI,
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
serial.puts("[elf] returned from entry\n")
return 0
MAX_SPAWNED: t.CDefine = 8
_elf_entries: t.CArray[t.CPtr, MAX_SPAWNED]
# User stack virtual address: top of 48-bit user space, 64KB below
USER_STACK_VTOP: t.CDefine = 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: t.CPtr) -> int:
pid: int = int(uint64(arg))
_db1: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(_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 | t.CPtr = proc.ProcessManager.current()
entry_fn: t.CPtr = cur_p.entry
# [DIAG] 定位用户进程首个指令 PF:打印 entry 实际值(PF cr2=ELF 物理区最后一页,
# 疑似 entry 被写成 phys 地址而非虚拟 0x400000)。
_dbw: t.CArray[t.CChar, 160]
viperlib.snprintf(c.Addr(_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(c.Addr(_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 | t.CPtr = proc.ProcessManager.current()
cur.exit()
return 0
def spawn_elf(path: t.CConst | str) -> int:
fat32.lock()
name_start: int = 0
si: int = 0
while True:
ch: t.CChar = 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 | t.CPtr = fat32.open(path, fat32_types.FA_READ)
if fp_scan is None:
serr: uint32 = fat32.last_error()
seb: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(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: t.CArray[uint8, 64]
string.memset(c.Addr(ehdr_buf_s), 0, 64)
fat32.read(fp_scan, c.Addr(ehdr_buf_s), 64)
ehdr_s: Elf64_Ehdr | t.CPtr = c.Addr(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: t.CArray[Elf64_Phdr, MAX_PHDRS]
string.memset(c.Addr(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, c.Addr(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: t.CPtr = 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: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(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
c.Asm(f"""mov {c.AsmOut(_kernel_cr3, t.ASM_DESCR.OUTPUT_REG)}, cr3""", op=[t.ASM_DESCR.CLOBBER_MEMORY])
c.Asm(f"mov cr3, {c.AsmInp(proc_pml4, t.ASM_DESCR.REG_ANY)}", op=[t.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)
c.Asm(f"mov cr3, {c.AsmInp(_kernel_cr3, t.ASM_DESCR.REG_ANY)}", op=[t.ASM_DESCR.CLOBBER_MEMORY])
paging.set_active_pml4(_kernel_cr3)
asm.sti()
# path[name_start:] 切片表达式 translate_subscript 不支持 Slice(返回 None)→
# 作为实参丢 name、entry 错位成第一参数(TPV 已修,此处指针算术双保险)
nm_ptr: t.CConst | t.CChar | t.CPtr = t.CConst(uint64(path) + uint64(name_start), t.CPtr)
p: proc.Process | t.CPtr = 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 | t.CPtr = sched.Scheduler.create_thread(t.CPtr(_elf_entry_wrapper), t.CPtr(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: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(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: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(_dbw2), 64, "[elf] spawn ret pid=%d\n", p.pid)
serial.puts(c.Addr(_dbw2))
fat32.unlock()
return p.pid
def _resolve_symbol(name: t.CConst | t.CChar | t.CPtr) -> t.CPtr:
nm: int = 0
while c.Deref(name + nm) != 0:
nm += 1
cmp_name: t.CConst | t.CChar | t.CPtr = name
cmp_len: int = nm
if nm > 9:
sha1_len2: int = 0
for si2 in range(nm):
ch2: t.CChar = c.Deref(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 c.Deref(cmp_name + i) != c.Deref("serial_puts" + i):
match = 0
if match:
return t.CVoid(c.Addr(serial.puts), t.CPtr)
return None
_so_strtab: t.CPtr
_so_symtab: t.CPtr
_so_syment: uint64
_so_sym_count: int
def load_so(path: t.CConst | str) -> t.CPtr:
fat32.lock()
fp: fat32_types.fileobj | t.CPtr = 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: t.CArray[uint8, 64]
string.memset(c.Addr(ehdr_buf), 0, 64)
read_len: uint32 = fat32.read(fp, c.Addr(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 | t.CPtr = c.Addr(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: t.CArray[Elf64_Phdr, MAX_PHDRS]
string.memset(c.Addr(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, c.Addr(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: t.CPtr = 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: t.CPtr = t.CVoid(phys_base_aligned, t.CPtr)
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: t.CPtr = t.CVoid(uint64(phys_base) + dest_off, t.CPtr)
if filesz > 0:
fat32.seek(fp, uint32(file_off))
fat32.read(fp, dest, uint32(filesz))
if memsz > filesz:
bss_start: t.CPtr = t.CVoid(uint64(dest) + filesz, t.CPtr)
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: t.CArray[Elf64_Dyn, MAX_DYNS]
string.memset(c.Addr(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, c.Addr(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: t.CPtr = t.CVoid(strtab_vaddr + so_load_addr, t.CPtr)
symtab_ptr: t.CPtr = t.CVoid(symtab_vaddr + so_load_addr, t.CPtr)
if rela_vaddr != 0 and relasz > 0:
rela_count: uint64 = relasz // 24
for ri in range(rela_count):
rela: Elf64_Rela | t.CPtr = t.CVoid(uint64(rela_vaddr) + so_load_addr + ri * 24, t.CPtr)
r_type: uint32 = uint32(rela.r_info & 0xFFFFFFFF)
r_sym: uint32 = uint32(rela.r_info >> 32)
target_addr: t.CPtr = t.CVoid(rela.r_offset + so_load_addr, t.CPtr)
if r_type == R_X86_64_RELATIVE:
val: uint64 = uint64(rela.r_addend) + so_load_addr
string.memcpy(target_addr, c.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 | t.CPtr = t.CVoid(uint64(symtab_ptr) + uint64(r_sym) * syment, t.CPtr)
sym_name: t.CConst | t.CChar | t.CPtr = t.CConst(uint64(strtab_ptr) + sym.st_name, t.CPtr)
resolved: t.CPtr = _resolve_symbol(sym_name)
if resolved is not None:
val2: uint64 = uint64(resolved) + uint64(rela.r_addend)
string.memcpy(target_addr, c.Addr(val2), 8)
elif sym.st_value != 0:
val3: uint64 = sym.st_value + so_load_addr + uint64(rela.r_addend)
string.memcpy(target_addr, c.Addr(val3), 8)
if jmprel_vaddr != 0 and pltrelsz > 0:
jmprel_count: uint64 = pltrelsz // 24
for ji in range(jmprel_count):
rela2: Elf64_Rela | t.CPtr = t.CVoid(jmprel_vaddr + so_load_addr + ji * 24, t.CPtr)
r_type2: uint32 = uint32(rela2.r_info & 0xFFFFFFFF)
r_sym2: uint32 = uint32(rela2.r_info >> 32)
target2: t.CPtr = t.CVoid(rela2.r_offset + so_load_addr, t.CPtr)
if r_type2 == R_X86_64_JUMP_SLOT or r_type2 == R_X86_64_GLOB_DAT:
sym2: Elf64_Sym | t.CPtr = t.CVoid(uint64(symtab_ptr) + uint64(r_sym2) * syment, t.CPtr)
sym_name2: t.CConst | t.CChar | t.CPtr = t.CConst(uint64(strtab_ptr) + sym2.st_name, t.CPtr)
resolved2: t.CPtr = _resolve_symbol(sym_name2)
if resolved2 is not None:
val4: uint64 = uint64(resolved2) + uint64(rela2.r_addend)
string.memcpy(target2, c.Addr(val4), 8)
elif sym2.st_value != 0:
val5: uint64 = sym2.st_value + so_load_addr + uint64(rela2.r_addend)
string.memcpy(target2, c.Addr(val5), 8)
else:
cr3: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(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: t.CPtr = t.CVoid(hash_vaddr + so_load_addr, t.CPtr)
nchain: uint32 = c.Deref(t.CVoid(uint64(hash_ptr) + 4, t.CPtr))
_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: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(cr), 80, "[so] loaded at 0x%lx\n", base_vaddr)
serial.puts(cr)
fat32.unlock()
return t.CVoid(base_vaddr, t.CPtr)
def get_so_symbol(so_base: t.CPtr, name: t.CConst | t.CChar | t.CPtr, so_path: t.CConst | t.CChar | t.CPtr) -> t.CPtr:
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 | t.CPtr = t.CVoid(uint64(_so_symtab) + uint64(symi) * _so_syment, t.CPtr)
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: t.CChar = c.Deref(t.CVoid(uint64(_so_strtab) + cmp_off2 + ni2, t.CPtr))
nc3: t.CChar = c.Deref(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: t.CChar = c.Deref(t.CVoid(uint64(_so_strtab) + dot_pos3, t.CPtr))
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: t.CChar = c.Deref(t.CVoid(uint64(_so_strtab) + cmp_off2 + ni2, t.CPtr))
nc4: t.CChar = c.Deref(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: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(dbg2), 80, "[so] found '%s' at 0x%lx\n", t.CVoid(uint64(_so_strtab) + sym_name_off2, t.CPtr), result_addr)
serial.puts(dbg2)
return t.CVoid(result_addr, t.CPtr)
serial.puts("[so] symbol not found\n")
return None