Files
ViperOS/VKernel/Kernel/execrunner/elf.py
2026-07-19 12:38:20 +08:00

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