758 lines
30 KiB
Python
758 lines
30 KiB
Python
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
|
|
# 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[t.CUInt8T, 16]
|
|
e_type: t.CUInt16T
|
|
e_machine: t.CUInt16T
|
|
e_version: t.CUInt32T
|
|
e_entry: t.CUInt64T
|
|
e_phoff: t.CUInt64T
|
|
e_shoff: t.CUInt64T
|
|
e_flags: t.CUInt32T
|
|
e_ehsize: t.CUInt16T
|
|
e_phentsize: t.CUInt16T
|
|
e_phnum: t.CUInt16T
|
|
e_shentsize: t.CUInt16T
|
|
e_shnum: t.CUInt16T
|
|
e_shstrndx: t.CUInt16T
|
|
|
|
@c.Attribute(t.attr.packed)
|
|
class Elf64_Phdr:
|
|
p_type: t.CUInt32T
|
|
p_flags: t.CUInt32T
|
|
p_offset: t.CUInt64T
|
|
p_vaddr: t.CUInt64T
|
|
p_paddr: t.CUInt64T
|
|
p_filesz: t.CUInt64T
|
|
p_memsz: t.CUInt64T
|
|
p_align: t.CUInt64T
|
|
|
|
MAX_PHDRS: t.CDefine = 16
|
|
|
|
@c.Attribute(t.attr.packed)
|
|
class Elf64_Shdr:
|
|
sh_name: t.CUInt32T
|
|
sh_type: t.CUInt32T
|
|
sh_flags: t.CUInt64T
|
|
sh_addr: t.CUInt64T
|
|
sh_offset: t.CUInt64T
|
|
sh_size: t.CUInt64T
|
|
sh_link: t.CUInt32T
|
|
sh_info: t.CUInt32T
|
|
sh_addralign: t.CUInt64T
|
|
sh_entsize: t.CUInt64T
|
|
|
|
@c.Attribute(t.attr.packed)
|
|
class Elf64_Sym:
|
|
st_name: t.CUInt32T
|
|
st_info: t.CUInt8T
|
|
st_other: t.CUInt8T
|
|
st_shndx: t.CUInt16T
|
|
st_value: t.CUInt64T
|
|
st_size: t.CUInt64T
|
|
|
|
@c.Attribute(t.attr.packed)
|
|
class Elf64_Rela:
|
|
r_offset: t.CUInt64T
|
|
r_info: t.CUInt64T
|
|
r_addend: t.CInt64T
|
|
|
|
@c.Attribute(t.attr.packed)
|
|
class Elf64_Dyn:
|
|
d_tag: t.CInt64T
|
|
d_val: t.CUInt64T
|
|
|
|
MAX_SHDRS: t.CDefine = 64
|
|
MAX_DYNS: t.CDefine = 64
|
|
MAX_RELAS: t.CDefine = 256
|
|
|
|
def load_elf(path: t.CConst | str, do_map: t.CInt = 1) -> t.CVoid | t.CPtr:
|
|
fp: fat32_types.fileobj | t.CPtr = fat32.open(path, fat32_types.FA_READ)
|
|
if fp is None:
|
|
err: t.CUInt32T = 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[t.CUInt8T, 64]
|
|
string.memset(c.Addr(ehdr_buf), 0, 64)
|
|
read_len: t.CUInt32T = fat32.read(fp, c.Addr(ehdr_buf), 64)
|
|
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")
|
|
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")
|
|
fat32.close(fp)
|
|
return None
|
|
|
|
entry_point: t.CUInt64T = ehdr.e_entry
|
|
phoff: t.CUInt64T = ehdr.e_phoff
|
|
phentsize: t.CUInt16T = ehdr.e_phentsize
|
|
phnum: t.CUInt16T = 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: t.CUInt64T = phoff + t.CUInt64T(pi) * t.CUInt64T(phentsize)
|
|
fat32.seek(fp, t.CUInt32T(phdr_off))
|
|
fat32.read(fp, c.Addr(phdr_bufs[pi]), 56)
|
|
|
|
found_load: t.CInt = 0
|
|
lowest_vaddr: t.CUInt64T = 0
|
|
highest_vaddr_end: t.CUInt64T = 0
|
|
for pi in range(phnum):
|
|
if phdr_bufs[pi].p_type == PT_LOAD:
|
|
seg_end: t.CUInt64T = 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: t.CUInt64T = highest_vaddr_end - lowest_vaddr
|
|
total_pages: t.CUInt64T = (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.CVoid | 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", t.CUInt64T(raw_alloc))
|
|
serial.puts(_db_ra)
|
|
if not raw_alloc:
|
|
serial.puts("[elf] malloc failed\n")
|
|
fat32.close(fp)
|
|
return None
|
|
|
|
phys_base_aligned: t.CUInt64T = (t.CUInt64T(raw_alloc) + t.CUInt64T(0xFFF)) & ~t.CUInt64T(0xFFF)
|
|
phys_base: t.CVoid | 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: t.CUInt64T = phdr_bufs[pi].p_vaddr
|
|
file_off: t.CUInt64T = phdr_bufs[pi].p_offset
|
|
filesz: t.CUInt64T = phdr_bufs[pi].p_filesz
|
|
memsz: t.CUInt64T = phdr_bufs[pi].p_memsz
|
|
dest_off: t.CUInt64T = vaddr - lowest_vaddr
|
|
dest: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(phys_base) + dest_off, t.CPtr)
|
|
|
|
if filesz > 0:
|
|
fat32.seek(fp, t.CUInt32T(file_off))
|
|
fat32.read(fp, dest, t.CUInt32T(filesz))
|
|
|
|
if memsz > filesz:
|
|
bss_start: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(dest) + filesz, t.CPtr)
|
|
bss_len: t.CUInt64T = memsz - filesz
|
|
string.memset(bss_start, 0, bss_len)
|
|
|
|
fat32.close(fp)
|
|
|
|
virt_page_start: t.CUInt64T = lowest_vaddr & ~t.CUInt64T(0xFFF)
|
|
phys_page_start: t.CUInt64T = t.CUInt64T(phys_base) & ~t.CUInt64T(0xFFF)
|
|
map_end: t.CUInt64T = (highest_vaddr_end + 0xFFF) & ~t.CUInt64T(0xFFF)
|
|
num_map_pages: t.CUInt64T = (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: t.CUInt64T = virt_page_start + mi * 0x1000
|
|
phys_addr: t.CUInt64T = phys_page_start + mi * 0x1000
|
|
page_flags: t.CUInt64T = paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER
|
|
paging.MapPage(virt_addr, phys_addr, page_flags)
|
|
|
|
entry_addr: t.CVoid | t.CPtr = t.CVoid(entry_point, t.CPtr)
|
|
|
|
return entry_addr
|
|
|
|
def run_elf(path: t.CConst | str) -> t.CInt:
|
|
entry: t.CVoid | t.CPtr = load_elf(path)
|
|
if entry is None:
|
|
serial.puts("[elf] load failed\n")
|
|
return -1
|
|
serial.puts("[elf] jumping to entry...\n")
|
|
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_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.CVoid | 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: t.CUInt64T = 0
|
|
_last_elf_virt_page_start: t.CUInt64T = 0
|
|
_last_elf_num_map_pages: t.CUInt64T = 0
|
|
|
|
def _elf_entry_wrapper(arg: t.CVoid | t.CPtr) -> t.CInt:
|
|
pid: t.CInt = t.CInt(t.CUInt64T(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.CVoid | t.CPtr = cur_p.entry
|
|
if entry_fn is not None:
|
|
user_stack_vtop: t.CUInt64T = USER_STACK_VTOP
|
|
us_sz: t.CUInt64T = USER_STACK_SIZE
|
|
user_stack_vbottom: t.CUInt64T = USER_STACK_VTOP - us_sz
|
|
# Physical pages of the user stack (allocated by mm.malloc, identity-mapped)
|
|
us_size: t.CUInt64T = USER_STACK_SIZE
|
|
phys_bottom: t.CUInt64T = (cur_p.user_stack - us_size) & ~t.CUInt64T(0xFFF)
|
|
# Map each page of the user stack at the virtual address with PTE_USER
|
|
num_stack_pages: t.CUInt64T = 16 # USER_STACK_SIZE(65536) / PAGE_SIZE(4096) = 16
|
|
si: t.CUInt64T
|
|
for si in range(num_stack_pages):
|
|
vaddr: t.CUInt64T = (user_stack_vbottom & ~t.CUInt64T(0xFFF)) + si * t.CUInt64T(0x1000)
|
|
paddr: t.CUInt64T = phys_bottom + si * t.CUInt64T(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) -> t.CInt:
|
|
fat32.lock()
|
|
name_start: t.CInt = 0
|
|
si: t.CInt = 0
|
|
while True:
|
|
ch: t.CChar = path[si]
|
|
if ch == 0: break
|
|
if ch == 47:
|
|
name_start = si + 1
|
|
si += 1
|
|
|
|
lowest_vaddr: t.CUInt64T = 0
|
|
highest_vaddr_end: t.CUInt64T = 0
|
|
fp_scan: fat32_types.fileobj | t.CPtr = fat32.open(path, fat32_types.FA_READ)
|
|
if fp_scan is None:
|
|
serr: t.CUInt32T = 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[t.CUInt8T, 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: t.CUInt64T = ehdr_s.e_phoff
|
|
phentsize_s: t.CUInt16T = ehdr_s.e_phentsize
|
|
phnum_s: t.CUInt16T = 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: t.CInt = 0
|
|
for pi_s in range(phnum_s):
|
|
fat32.seek(fp_scan, t.CUInt32T(phoff_s + t.CUInt64T(pi_s) * t.CUInt64T(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: t.CUInt64T = 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: t.CUInt64T = 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.CVoid | 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: t.CUInt64T = 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: t.CUInt64T = 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: t.CUInt64T
|
|
for mi in range(_last_elf_num_map_pages):
|
|
_va: t.CUInt64T = _last_elf_virt_page_start + mi * 0x1000
|
|
_pa: t.CUInt64T = _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()
|
|
|
|
p: proc.Process | t.CPtr = proc.ProcessManager.create_process(path[name_start:], entry)
|
|
if p is None:
|
|
serial.puts("[elf] spawn: create_process failed\n")
|
|
fat32.unlock()
|
|
return -2
|
|
p.elf_base = t.CUInt64T(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(_elf_entry_wrapper, t.CVoid(t.CUInt64T(p.pid), t.CPtr), p.pid)
|
|
if th is None:
|
|
serial.puts("[elf] spawn: create_thread failed\n")
|
|
p.exit()
|
|
fat32.unlock()
|
|
return -3
|
|
tid: t.CInt = 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, t.CUInt64T(entry), p.pml4_root)
|
|
serial.puts(slog)
|
|
fat32.unlock()
|
|
return p.pid
|
|
|
|
def _resolve_symbol(name: t.CConst | t.CChar | t.CPtr) -> t.CVoid | t.CPtr:
|
|
nm: t.CInt = 0
|
|
while c.Deref(name + nm) != 0:
|
|
nm += 1
|
|
cmp_name: t.CConst | t.CChar | t.CPtr = name
|
|
cmp_len: t.CInt = nm
|
|
if nm > 9:
|
|
sha1_len2: t.CInt = 0
|
|
for si2 in range(nm):
|
|
ch2: t.CChar = c.Deref(name + si2)
|
|
if ch2 == 46:
|
|
if sha1_len2 >= 8:
|
|
cmp_name = t.CConst(t.CUInt64T(name) + si2 + 1, t.CPtr)
|
|
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: t.CInt = 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.CVoid | t.CPtr
|
|
_so_symtab: t.CVoid | t.CPtr
|
|
_so_syment: t.CUInt64T
|
|
_so_sym_count: t.CInt
|
|
|
|
def load_so(path: t.CConst | str) -> t.CVoid | 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[t.CUInt8T, 64]
|
|
string.memset(c.Addr(ehdr_buf), 0, 64)
|
|
read_len: t.CUInt32T = 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: t.CUInt64T = ehdr.e_phoff
|
|
phentsize: t.CUInt16T = ehdr.e_phentsize
|
|
phnum: t.CUInt16T = ehdr.e_phnum
|
|
|
|
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: t.CUInt64T = phoff + t.CUInt64T(pi) * t.CUInt64T(phentsize)
|
|
fat32.seek(fp, t.CUInt32T(phdr_off))
|
|
fat32.read(fp, c.Addr(phdr_bufs[pi]), 56)
|
|
|
|
found_load: t.CInt = 0
|
|
lowest_vaddr: t.CUInt64T = 0
|
|
highest_vaddr_end: t.CUInt64T = 0
|
|
for pi in range(phnum):
|
|
if phdr_bufs[pi].p_type == PT_LOAD:
|
|
seg_end: t.CUInt64T = 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: t.CUInt64T = highest_vaddr_end - lowest_vaddr
|
|
total_pages: t.CUInt64T = (total_memsz + 0xFFF) // 0x1000
|
|
|
|
so_load_addr: t.CUInt64T = 0x600000
|
|
raw_alloc: t.CVoid | 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: t.CUInt64T = (t.CUInt64T(raw_alloc) + 0xFFF) & ~t.CUInt64T(0xFFF)
|
|
phys_base: t.CVoid | 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: t.CUInt64T = phdr_bufs[pi].p_vaddr
|
|
file_off: t.CUInt64T = phdr_bufs[pi].p_offset
|
|
filesz: t.CUInt64T = phdr_bufs[pi].p_filesz
|
|
memsz: t.CUInt64T = phdr_bufs[pi].p_memsz
|
|
dest_off: t.CUInt64T = vaddr - lowest_vaddr
|
|
dest: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(phys_base) + dest_off, t.CPtr)
|
|
if filesz > 0:
|
|
fat32.seek(fp, t.CUInt32T(file_off))
|
|
fat32.read(fp, dest, t.CUInt32T(filesz))
|
|
if memsz > filesz:
|
|
bss_start: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(dest) + filesz, t.CPtr)
|
|
bss_len: t.CUInt64T = memsz - filesz
|
|
string.memset(bss_start, 0, bss_len)
|
|
|
|
virt_page_start: t.CUInt64T = (so_load_addr + lowest_vaddr) & ~t.CUInt64T(0xFFF)
|
|
phys_page_start: t.CUInt64T = t.CUInt64T(phys_base) & ~t.CUInt64T(0xFFF)
|
|
map_end: t.CUInt64T = (so_load_addr + highest_vaddr_end + 0xFFF) & ~t.CUInt64T(0xFFF)
|
|
num_map_pages: t.CUInt64T = (map_end - virt_page_start) // 0x1000
|
|
|
|
for mi in range(num_map_pages):
|
|
virt_addr: t.CUInt64T = virt_page_start + mi * 0x1000
|
|
phys_addr: t.CUInt64T = phys_page_start + mi * 0x1000
|
|
paging.MapPage(virt_addr, phys_addr, paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER)
|
|
|
|
base_vaddr: t.CUInt64T = 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: t.CInt = 0
|
|
for pi in range(phnum):
|
|
if phdr_bufs[pi].p_type == PT_DYNAMIC:
|
|
fat32.seek(fp, t.CUInt32T(phdr_bufs[pi].p_offset))
|
|
dyn_read: t.CUInt32T = fat32.read(fp, c.Addr(dyn_buf), t.CUInt32T(phdr_bufs[pi].p_filesz))
|
|
dyn_count = t.CInt(dyn_read) // 16
|
|
|
|
strtab_vaddr: t.CUInt64T = 0
|
|
symtab_vaddr: t.CUInt64T = 0
|
|
hash_vaddr: t.CUInt64T = 0
|
|
syment: t.CUInt64T = 0
|
|
rela_vaddr: t.CUInt64T = 0
|
|
relasz: t.CUInt64T = 0
|
|
jmprel_vaddr: t.CUInt64T = 0
|
|
pltrelsz: t.CUInt64T = 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.CVoid | t.CPtr = t.CVoid(strtab_vaddr + so_load_addr, t.CPtr)
|
|
symtab_ptr: t.CVoid | t.CPtr = t.CVoid(symtab_vaddr + so_load_addr, t.CPtr)
|
|
|
|
if rela_vaddr != 0 and relasz > 0:
|
|
rela_count: t.CUInt64T = relasz // 24
|
|
for ri in range(rela_count):
|
|
rela: Elf64_Rela | t.CPtr = t.CVoid(t.CUInt64T(rela_vaddr) + so_load_addr + ri * 24, t.CPtr)
|
|
r_type: t.CUInt32T = t.CUInt32T(rela.r_info & 0xFFFFFFFF)
|
|
r_sym: t.CUInt32T = t.CUInt32T(rela.r_info >> 32)
|
|
target_addr: t.CVoid | t.CPtr = t.CVoid(rela.r_offset + so_load_addr, t.CPtr)
|
|
if r_type == R_X86_64_RELATIVE:
|
|
val: t.CUInt64T = t.CUInt64T(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(t.CUInt64T(symtab_ptr) + t.CUInt64T(r_sym) * syment, t.CPtr)
|
|
sym_name: t.CConst | t.CChar | t.CPtr = t.CConst(t.CUInt64T(strtab_ptr) + sym.st_name, t.CPtr)
|
|
resolved: t.CVoid | t.CPtr = _resolve_symbol(sym_name)
|
|
if resolved is not None:
|
|
val2: t.CUInt64T = t.CUInt64T(resolved) + t.CUInt64T(rela.r_addend)
|
|
string.memcpy(target_addr, c.Addr(val2), 8)
|
|
elif sym.st_value != 0:
|
|
val3: t.CUInt64T = sym.st_value + so_load_addr + t.CUInt64T(rela.r_addend)
|
|
string.memcpy(target_addr, c.Addr(val3), 8)
|
|
|
|
if jmprel_vaddr != 0 and pltrelsz > 0:
|
|
jmprel_count: t.CUInt64T = 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: t.CUInt32T = t.CUInt32T(rela2.r_info & 0xFFFFFFFF)
|
|
r_sym2: t.CUInt32T = t.CUInt32T(rela2.r_info >> 32)
|
|
target2: t.CVoid | 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(t.CUInt64T(symtab_ptr) + t.CUInt64T(r_sym2) * syment, t.CPtr)
|
|
sym_name2: t.CConst | t.CChar | t.CPtr = t.CConst(t.CUInt64T(strtab_ptr) + sym2.st_name, t.CPtr)
|
|
resolved2: t.CVoid | t.CPtr = _resolve_symbol(sym_name2)
|
|
if resolved2 is not None:
|
|
val4: t.CUInt64T = t.CUInt64T(resolved2) + t.CUInt64T(rela2.r_addend)
|
|
string.memcpy(target2, c.Addr(val4), 8)
|
|
elif sym2.st_value != 0:
|
|
val5: t.CUInt64T = sym2.st_value + so_load_addr + t.CUInt64T(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)
|
|
|
|
_so_strtab = strtab_ptr
|
|
_so_symtab = symtab_ptr
|
|
_so_syment = syment
|
|
_so_sym_count = 0
|
|
if hash_vaddr != 0:
|
|
hash_ptr: t.CVoid | t.CPtr = t.CVoid(hash_vaddr + so_load_addr, t.CPtr)
|
|
nchain: t.CUInt32T = c.Deref(t.CVoid(t.CUInt64T(hash_ptr) + 4, t.CPtr))
|
|
_so_sym_count = t.CInt(nchain)
|
|
elif syment > 0 and strtab_vaddr > symtab_vaddr and symtab_vaddr != 0:
|
|
sym_tab_size: t.CUInt64T = strtab_vaddr - symtab_vaddr
|
|
_so_sym_count = t.CInt(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.CVoid | t.CPtr, name: t.CConst | t.CChar | t.CPtr, so_path: t.CConst | t.CChar | t.CPtr) -> t.CVoid | 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(t.CUInt64T(_so_symtab) + t.CUInt64T(symi) * _so_syment, t.CPtr)
|
|
if sb2.st_value != 0 and sb2.st_shndx != 0:
|
|
sym_name_off2: t.CUInt32T = sb2.st_name
|
|
cmp_off2: t.CUInt32T = sym_name_off2
|
|
match2: t.CInt = 0
|
|
ni2: t.CInt = 0
|
|
while True:
|
|
sc3: t.CChar = c.Deref(t.CVoid(t.CUInt64T(_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: t.CUInt32T = sym_name_off2
|
|
is_sha1_2: t.CInt = 0
|
|
sha1_len_2: t.CUInt32T = 0
|
|
while True:
|
|
dc4: t.CChar = c.Deref(t.CVoid(t.CUInt64T(_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(t.CUInt64T(_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: t.CUInt64T = t.CUInt64T(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(t.CUInt64T(_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
|