diff --git a/Apps/Gargantua/project.vpj b/Apps/Gargantua/project.vpj index 767d2c5..93e6143 100644 --- a/Apps/Gargantua/project.vpj +++ b/Apps/Gargantua/project.vpj @@ -1,30 +1,30 @@ -{ - "name": "Gargantua", - "version": "1.0.0", - "source_dir": "./", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", "elf_x86_64", - "-T", "linker.ld", - "--oformat", "elf64-x86-64", - "--strip-all" - ], - "output": "gargantua.elf" - }, - "includes": ["../..", "../../../../includes"], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } -} +{ + "name": "Gargantua", + "version": "1.0.0", + "source_dir": "./", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", "elf_x86_64", + "-T", "linker.ld", + "--oformat", "elf64-x86-64", + "--strip-all" + ], + "output": "gargantua.elf" + }, + "includes": ["../..", "../../../../includes"], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } +} diff --git a/Apps/HelloWorld/project.vpj b/Apps/HelloWorld/project.vpj index 4514794..56ad8b9 100644 --- a/Apps/HelloWorld/project.vpj +++ b/Apps/HelloWorld/project.vpj @@ -1,30 +1,30 @@ -{ - "name": "HelloWorld", - "version": "1.0.0", - "source_dir": "./", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", "elf_x86_64", - "-T", "linker.ld", - "--oformat", "elf64-x86-64", - "--strip-all" - ], - "output": "helloworld.elf" - }, - "includes": ["../..", "../../../../includes"], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } -} +{ + "name": "HelloWorld", + "version": "1.0.0", + "source_dir": "./", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", "elf_x86_64", + "-T", "linker.ld", + "--oformat", "elf64-x86-64", + "--strip-all" + ], + "output": "helloworld.elf" + }, + "includes": ["../..", "../../../../includes"], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } +} diff --git a/Apps/Scene3D/project.vpj b/Apps/Scene3D/project.vpj index be315b6..84d20a4 100644 --- a/Apps/Scene3D/project.vpj +++ b/Apps/Scene3D/project.vpj @@ -1,30 +1,30 @@ -{ - "name": "Scene3D", - "version": "1.0.0", - "source_dir": "./", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", "elf_x86_64", - "-T", "linker.ld", - "--oformat", "elf64-x86-64", - "--strip-all" - ], - "output": "scene3d.elf" - }, - "includes": ["../..", "../../../../includes"], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } -} +{ + "name": "Scene3D", + "version": "1.0.0", + "source_dir": "./", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", "elf_x86_64", + "-T", "linker.ld", + "--oformat", "elf64-x86-64", + "--strip-all" + ], + "output": "scene3d.elf" + }, + "includes": ["../..", "../../../../includes"], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } +} diff --git a/Apps/Terminal/project.vpj b/Apps/Terminal/project.vpj index 113bf2d..a887a1b 100644 --- a/Apps/Terminal/project.vpj +++ b/Apps/Terminal/project.vpj @@ -1,30 +1,30 @@ -{ - "name": "Terminal", - "version": "1.0.0", - "source_dir": "./", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", "elf_x86_64", - "-T", "linker.ld", - "--oformat", "elf64-x86-64", - "--strip-all" - ], - "output": "terminal.elf" - }, - "includes": ["../..", "../../../../includes"], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } -} +{ + "name": "Terminal", + "version": "1.0.0", + "source_dir": "./", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", "elf_x86_64", + "-T", "linker.ld", + "--oformat", "elf64-x86-64", + "--strip-all" + ], + "output": "terminal.elf" + }, + "includes": ["../..", "../../../../includes"], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } +} diff --git a/Libs/SerialLogger/project.vpj b/Libs/SerialLogger/project.vpj index e5cd696..76d3d5c 100644 --- a/Libs/SerialLogger/project.vpj +++ b/Libs/SerialLogger/project.vpj @@ -1,33 +1,33 @@ -{ - "name": "SerialLogger", - "version": "1.0.0", - "source_dir": "./", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", "elf_x86_64", - "-T", "linker.ld", - "--oformat", "elf64-x86-64", - "-shared", - "-z", "notext", - "--no-relax", - "--strip-all" - ], - "output": "slog.so" - }, - "includes": ["../..", "../../../../includes"], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } -} +{ + "name": "SerialLogger", + "version": "1.0.0", + "source_dir": "./", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", "elf_x86_64", + "-T", "linker.ld", + "--oformat", "elf64-x86-64", + "-shared", + "-z", "notext", + "--no-relax", + "--strip-all" + ], + "output": "slog.so" + }, + "includes": ["../..", "../../../../includes"], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } +} diff --git a/Libs/VPUI64/project.vpj b/Libs/VPUI64/project.vpj index 37a6b5c..0065c35 100644 --- a/Libs/VPUI64/project.vpj +++ b/Libs/VPUI64/project.vpj @@ -1,33 +1,33 @@ -{ - "name": "VPUI64", - "version": "1.0.0", - "source_dir": "./", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", "elf_x86_64", - "-T", "linker.ld", - "--oformat", "elf64-x86-64", - "-shared", - "-z", "notext", - "--no-relax", - "--strip-all" - ], - "output": "vpui64.so" - }, - "includes": ["../..", "../../../../includes"], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } -} +{ + "name": "VPUI64", + "version": "1.0.0", + "source_dir": "./", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", "elf_x86_64", + "-T", "linker.ld", + "--oformat", "elf64-x86-64", + "-shared", + "-z", "notext", + "--no-relax", + "--strip-all" + ], + "output": "vpui64.so" + }, + "includes": ["../..", "../../../../includes"], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } +} diff --git a/VKernel/Kernel/execrunner/elf.vp b/VKernel/Kernel/execrunner/elf.vp index a9054a1..fe0d0f2 100644 --- a/VKernel/Kernel/execrunner/elf.vp +++ b/VKernel/Kernel/execrunner/elf.vp @@ -1,822 +1,822 @@ -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: - Matched: int = 1 - for i in range(cmp_len): - if cmp_name[i] != "serial_puts"[i]: - Matched = 0 - if Matched != 0: - 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 +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 错位成第一参数(VPC 已修,此处指针算术双保险) + 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: + Matched: int = 1 + for i in range(cmp_len): + if cmp_name[i] != "serial_puts"[i]: + Matched = 0 + if Matched != 0: + 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 diff --git a/VKernel/Kernel/paging/paging.vp b/VKernel/Kernel/paging/paging.vp index 3225c94..f459192 100644 --- a/VKernel/Kernel/paging/paging.vp +++ b/VKernel/Kernel/paging/paging.vp @@ -1,476 +1,476 @@ -import drivers.serial.uart.serial as serial -import viperlib - - -define PTE_PRESENT = (1 << 0) -define PTE_WRITABLE = (1 << 1) -define PTE_USER = (1 << 2) -define PTE_PWT = (1 << 3) -define PTE_PCD = (1 << 4) -define PTE_ACCESSED = (1 << 5) -define PTE_DIRTY = (1 << 6) -define PTE_PS = (1 << 7) -define PTE_GLOBAL = (1 << 8) - -class _p: - entries: [512]uint64 - -define PT_POOL_PAGES = 512 -pt_pool_raw: rawptr = None -pt_pool_start: uint64 = 0 -pt_pool_used: uint64 = 0 - -AllocatedPages: uint64 = 0 -pml4: _p* = None -pdp: _p* -pd: _p* -pt: _p* - -def init(MemmapAddr: uint64, MemmapSize: uint64): - global pml4 - pml4 = GetCurrentPml4() - # bootloader/UEFI 的 CR0.WP 通常已是 1(原代码清 WP 正因此)。页表页 - # (pml4/pdp/pd/pt,0xf8xxxx)默认只读,而内核运行时 MapPage/clone 需 - # 修改页表。顺序:先清 WP(允许写只读页表页)→ MakePTWritable 放开 - # 全部页表页 → 再置 WP=1(此后写只读页即 #PF 暴露越界写源)。 - __asm__(f""" - mov rax, cr0 - and rax, ~0x10000 - mov cr0, rax - """) - MakePTWritable() - __asm__(f""" - mov rax, cr0 - or rax, 0x10000 - mov cr0, rax - """) - -def init_pool(buf: rawptr, buf_size: uint64): - global pt_pool_raw, pt_pool_start, pt_pool_used - pt_pool_raw = buf - if buf is None: return - raw_addr: uint64 = uint64(buf) - aligned_addr: uint64 = (raw_addr + uint64(0xFFF)) & ~uint64(0xFFF) - pt_pool_start = aligned_addr - pt_pool_used = 0 - -def AllocPage() -> rawptr: - global AllocatedPages, pt_pool_used, pml4, pdp, pd, pt - if pt_pool_raw is None: return None - if pt_pool_used >= uint64(PT_POOL_PAGES): return None - page: rawptr = rawptr(pt_pool_start + pt_pool_used * uint64(4096)) - pt_pool_used = pt_pool_used + uint64(1) - # rep stosb 向 [rdi] 写 4096 字节:必须声明 CLOBBER_MEMORY,否则优化器 - # 无法得知本块改写了内存,可能把其前后的内存读写重排/消除(N8)。 - __asm__(f"""mov al, 0 - mov ecx, 4096 - mov rdi, {asminp(page, ASM_DESCR.REG_ANY)} - rep stosb""", - op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_AL, - ASM_DESCR.CLOBBER_ECX, ASM_DESCR.CLOBBER_RDI]) - AllocatedPages = AllocatedPages + uint64(1) - return page - -def FreePage(page: rawptr): - global AllocatedPages, pml4, pdp, pd, pt - if page is None: return - if AllocatedPages > 0: AllocatedPages = AllocatedPages - uint64(1) - -def MapPage(VirtAddr: uint64, PhysAddr: uint64, flags: uint64): - global pml4, pdp, pd, pt - if VirtAddr == 0 or PhysAddr == 0: return - VirtAddr = VirtAddr & ~uint64(0xFFF) - PhysAddr = PhysAddr & ~uint64(0xFFF) - Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF - PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF - PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF - PtIndex: uint64 = (VirtAddr >> 12) & 0x1FF - - if not pml4: - pml4 = AllocPage() - if not pml4: return - - if not (pml4.entries[Pml4Index] & PTE_PRESENT): - pdp = AllocPage() - if not pdp: return - pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) - else: - if (flags & PTE_USER) and not (pml4.entries[Pml4Index] & PTE_USER): - pml4.entries[Pml4Index] = pml4.entries[Pml4Index] | PTE_USER - pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF)) - - if not (pdp.entries[PdpIndex] & PTE_PRESENT): - pd = AllocPage() - if not pd: return - pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) - else: - if (flags & PTE_USER) and not (pdp.entries[PdpIndex] & PTE_USER): - pdp.entries[PdpIndex] = pdp.entries[PdpIndex] | PTE_USER - pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF)) - - if pd.entries[PdIndex] & 1: - if pd.entries[PdIndex] & 0x80: - big_phys: uint64 = pd.entries[PdIndex] & ~uint64(0x1FFFFF) - inherit_flags: uint64 = pd.entries[PdIndex] & (PTE_PCD | PTE_PWT) - pt = AllocPage() - if not pt: return - for split_i in range(512): - pt.entries[split_i] = (big_phys + uint64(split_i) * 0x1000) | PTE_PRESENT | PTE_WRITABLE | inherit_flags - pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) - else: - # PD entry exists as a regular PT (e.g. cloned identity mapping). - # Must propagate PTE_USER to the PD entry so user-mode access is - # allowed at all paging levels, otherwise #PF err=0x5. - if (flags & PTE_USER) and not (pd.entries[PdIndex] & PTE_USER): - pd.entries[PdIndex] = pd.entries[PdIndex] | PTE_USER - else: - pt = AllocPage() - if not pt: return - pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) - pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF)) - - pt.entries[PtIndex] = PhysAddr | flags | PTE_PRESENT - __asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]") - -def MapLargePage(VirtAddr: uint64, PhysAddr: uint64, flags: uint64): - global pml4, pdp, pd, pt - VirtAddr = VirtAddr & ~uint64(0x1FFFFF) - PhysAddr = PhysAddr & ~uint64(0x1FFFFF) - Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF - PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF - PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF - - if not pml4: - pml4 = AllocPage() - if not pml4: return - - if not pml4.entries[Pml4Index] & PTE_PRESENT: - pdp = AllocPage() - if not pdp: return - pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE - pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF)) - - if not (pdp.entries[PdpIndex] & PTE_PRESENT): - pd = AllocPage() - if not pd: return - pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE - pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF)) - - pd.entries[PdIndex] = PhysAddr | flags | PTE_PRESENT | PTE_PS - __asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]") - -def UnMapPage(VirtAddr: uint64): - global pml4, pdp, pd, pt - if VirtAddr == 0: return - if not pml4: return - Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF - PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF - PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF - PtIndex: uint64 = (VirtAddr >> 12) & 0x1FF - - if not pml4.entries[Pml4Index] & PTE_PRESENT: return - - pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF)) - if not pdp.entries[PdpIndex] & PTE_PRESENT: return - - pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF)) - - if not pd.entries[PdIndex] & PTE_PRESENT: return - - if pd.entries[PdIndex] & PTE_PS: - pd.entries[PdIndex] = 0 - else: - pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF)) - pt.entries[PtIndex] = 0 - - __asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]") - -def EnablePaging(): - global pml4, pdp, pd, pt - __asm__(f"mov cr3, {asminp(uint64(pml4), ASM_DESCR.REG_ANY)}") - -def _set_page_writable(phys_addr: uint64): - """把物理页 phys_addr(identity 映射,虚拟=物理)的 PTE 置 W 位 - - 页表页(bootloader 建立的 pml4/pdp/pd/pt 位于 0xf8xxxx)默认只读, - 而内核运行时 MapPage/clone 需修改页表 → 必须先把这些页表页自身设为可写, - 否则 CR0.WP=1 下写页表触发 #PF(slog.so 映射 0x600000 新建页表项时 - cr2=0xf801000 err=0x3 根因)。 - """ - global pml4 - if not pml4: return - va: uint64 = phys_addr & ~uint64(0xFFF) - pml4_idx: uint64 = (va >> 39) & 0x1FF - if not (pml4.entries[pml4_idx] & PTE_PRESENT): return - pdp_s: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) - pdp_idx: uint64 = (va >> 30) & 0x1FF - if not (pdp_s.entries[pdp_idx] & PTE_PRESENT): return - pd_s: _p* = _p*(pdp_s.entries[pdp_idx] & ~uint64(0xFFF)) - pd_idx: uint64 = (va >> 21) & 0x1FF - if not (pd_s.entries[pd_idx] & PTE_PRESENT): return - if pd_s.entries[pd_idx] & PTE_PS: - # 2MB 大页:W 位在 PD 条目自身(无下层 PT),直接置位 - pd_s.entries[pd_idx] = pd_s.entries[pd_idx] | uint64(PTE_WRITABLE) - __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") - return - pt_s: _p* = _p*(pd_s.entries[pd_idx] & ~uint64(0xFFF)) - pt_idx: uint64 = (va >> 12) & 0x1FF - pt_s.entries[pt_idx] = pt_s.entries[pt_idx] | uint64(PTE_WRITABLE) - __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") - -def MakePTWritable(): - """把当前页表层次所有页表页(pml4 自身 + pdp/pd/pt)设为可写 - - 内核运行时需修改页表(MapPage/clone_for_process),而 bootloader 建立的 - 页表页映射为只读——CR0.WP=1 下必须先放开页表页自身,否则写页表即 #PF。 - """ - global pml4 - if not pml4: return - _set_page_writable(uint64(pml4)) - for pi in range(512): - if not (pml4.entries[pi] & PTE_PRESENT): continue - pdp_m: _p* = _p*(pml4.entries[pi] & ~uint64(0xFFF)) - _set_page_writable(uint64(pdp_m)) - pj: uint64 - for pj in range(512): - if not (pdp_m.entries[pj] & PTE_PRESENT): continue - pd_m: _p* = _p*(pdp_m.entries[pj] & ~uint64(0xFFF)) - _set_page_writable(uint64(pd_m)) - pk: uint64 - for pk in range(512): - if not (pd_m.entries[pk] & PTE_PRESENT): continue - if pd_m.entries[pk] & PTE_PS: continue - pt_m: _p* = _p*(pd_m.entries[pk] & ~uint64(0xFFF)) - _set_page_writable(uint64(pt_m)) - -def MakeRangeRO(vaddr_start: uint64, vaddr_end: uint64): - """把 [vaddr_start, vaddr_end) 范围内所有页的 PTE 清 W 位(只读) - - 定位越界写源:内核态写只读页在 CR0.WP=1 下触发 #PF(int=14), - isrCommonhandler 打印 cr2/rip → 写源立即暴露,不再静默破坏 .rodata。 - """ - global pml4 - if not pml4: return - va: uint64 = vaddr_start & ~uint64(0xFFF) - # ro_end 下取整到页:不保护含 ro_end 的未对齐尾页。链接脚本输出段未页对齐, - # .data 紧跟 .rodata(__rodata_end 未对齐时 .data 起始落在 rodata 尾页内), - # 整页置只读会把 .data 全局(如 devfs @DevFSObject=0x141630)误保护 → - # devfs_init 写全局 #PF(cr2=0x141630 rip=0x101aaa mov [r15],rax 根因)。 - # 尾页含少量 rodata 尾部(对齐填充),保持可写可容忍。 - ro_end_align: uint64 = vaddr_end & ~uint64(0xFFF) - while va < ro_end_align: - pml4_idx: uint64 = (va >> 39) & 0x1FF - if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue - pdp_r: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) - pdp_idx: uint64 = (va >> 30) & 0x1FF - if not (pdp_r.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue - pd_r: _p* = _p*(pdp_r.entries[pdp_idx] & ~uint64(0xFFF)) - pd_idx: uint64 = (va >> 21) & 0x1FF - if not (pd_r.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue - if pd_r.entries[pd_idx] & PTE_PS: - # 2MB 大页:直接跳过会让 .rodata 保持可写——越界写被静默吞掉 - # (perf 格式串 win=%lu/work=%lu 被逐步破坏的根因)。拆分为 512 - # 个 4KB 页(复用 MapPage 的分裂逻辑),再走下方常规路径清 W 位。 - # WP=1 后内核态写只读页即 #PF → isrCommonhandler 打印 cr2/rip 暴露写源。 - big_phys: uint64 = pd_r.entries[pd_idx] & ~uint64(0x1FFFFF) - inherit_flags: uint64 = pd_r.entries[pd_idx] & (PTE_PCD | PTE_PWT | PTE_USER | uint64(0x8000000000000000)) - pt_split: _p* = AllocPage() - if not pt_split: - va += 0x1000 - continue - for split_i in range(512): - pt_split.entries[split_i] = (big_phys + uint64(split_i) * uint64(0x1000)) | PTE_PRESENT | PTE_WRITABLE | inherit_flags - pd_r.entries[pd_idx] = (uint64(pt_split) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (pd_r.entries[pd_idx] & PTE_USER) - __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") - # 分裂后落入下方 4KB 常规路径,继续清当前页 W 位 - pt_r: _p* = _p*(pd_r.entries[pd_idx] & ~uint64(0xFFF)) - pt_idx: uint64 = (va >> 12) & 0x1FF - pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~uint64(PTE_WRITABLE) - __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") - va += 0x1000 - -def ProtectKernelRO(): - """把内核镜像 .rodata 段(字符串常量/vtable 等)映射为只读 - - 地址经 __asm__ 内联引用链接器符号 __rodata_start/__rodata_end - (linker.ld 定义,TPV 不声明 extern——避免生成定义与链接器符号冲突)。 - """ - global pml4 - if not pml4: return - # 页表页可写已在 paging.init(WP=0 阶段)由 MakePTWritable 完成, - # 此处只保护 .rodata 内容页(WP=1 后写只读页即 #PF 暴露写源)。 - ro_start: uint64 = 0 - ro_end: uint64 = 0 - __asm__(f"""lea rax, [rip + __rodata_start] - mov {asmout(ro_start, ASM_DESCR.OUTPUT_REG)}, rax - lea rax, [rip + __rodata_end] - mov {asmout(ro_end, ASM_DESCR.OUTPUT_REG)}, rax""", - op=[ASM_DESCR.CLOBBER_RAX]) - MakeRangeRO(ro_start, ro_end) - # [PG] 打印 .rodata 起始页映射:确认只读真正生效。 - # ps=1 说明仍在 2MB 大页(分裂失败)→ .rodata 保持可写 → 越界写继续被静默吞掉。 - DumpMapping(ro_start, "ro_s") - -def DumpMapping(va: uint64, tag: const str): - """[PG] 打印指定 VA 的 pml4/pdp/pd/pt 条目,定位大页(PS)与权限位 - - 内核区域若为 UEFI 2MB 大页,MakeRangeRO 无法只读 .rodata(越界写静默破坏 - 格式串);1GB 页同理(PS 位在 pdpe)。W=1 且 PS=1 → 该页可写且是大页。 - """ - global pml4 - if not pml4: return - pml4_i: uint64 = (va >> 39) & 0x1FF - pdp_i: uint64 = (va >> 30) & 0x1FF - pd_i: uint64 = (va >> 21) & 0x1FF - pt_i: uint64 = (va >> 12) & 0x1FF - pml4_e: uint64 = pml4.entries[pml4_i] - pdp_e: uint64 = 0 - pd_e: uint64 = 0 - pt_e: uint64 = 0 - if pml4_e & PTE_PRESENT: - pdp_r: _p* = _p*(pml4_e & ~uint64(0xFFF)) - pdp_e = pdp_r.entries[pdp_i] - if pdp_e & PTE_PRESENT: - if pdp_e & PTE_PS: - pd_e = pdp_e - else: - pd_r: _p* = _p*(pdp_e & ~uint64(0xFFF)) - pd_e = pd_r.entries[pd_i] - if (pd_e & PTE_PRESENT) and not (pd_e & PTE_PS): - pt_r: _p* = _p*(pd_e & ~uint64(0xFFF)) - pt_e = pt_r.entries[pt_i] - buf_d: [200]char - viperlib.snprintf(&buf_d, 200, "[PG] %s va=0x%lx pdpe=0x%lx pde=0x%lx pte=0x%lx (ps=%d W=%d)\n", - tag, va, pdp_e, pd_e, pt_e, - 1 if (pd_e & PTE_PS) else 0, - 1 if (pt_e & PTE_WRITABLE) or (pd_e & PTE_WRITABLE) or (pdp_e & PTE_WRITABLE) else 0) - serial.puts(&buf_d) - -def GetCurrentPml4() -> _p*: - global pml4, pdp, pd, pt - cr3: uint64 - __asm__(f"mov {asmout(cr3, ASM_DESCR.OUTPUT_REG)}, cr3") - return _p*(cr3 & ~uint64(0xFFF)) - -def create_process_page_table() -> uint64: - global pml4 - if not pml4: return 0 - new_pml4: _p* = AllocPage() - if not new_pml4: return 0 - i: uint64 - for i in range(512): - entry: uint64 = pml4.entries[i] - if entry & PTE_PRESENT: - new_pml4.entries[i] = entry - return uint64(new_pml4) - -def clone_for_process(isolate_vaddr: uint64) -> uint64: - global pml4 - _dbg: [120]char - viperlib.snprintf(&_dbg, 120, "[clone] pml4=0x%lx pool_used=%lu iso_va=0x%lx\n", uint64(pml4), pt_pool_used, isolate_vaddr) - serial.puts(_dbg) - if not pml4: return 0 - new_pml4_page: _p* = AllocPage() - if not new_pml4_page: - # [DIAG] 定位 AllocPage 失败:pool_used 打印 0 但分配失败 → BSS 全局 - # (pt_pool_raw/pt_pool_used)疑似被内存越界写覆盖。 - _dbgf: [120]char - viperlib.snprintf(&_dbgf, 120, "[clone] AllocPage FAIL raw=0x%lx used=%lu PP=%d\n", - uint64(pt_pool_raw), pt_pool_used, PT_POOL_PAGES) - serial.puts(&_dbgf) - serial.puts("[clone] AllocPage pml4 failed\n") - return 0 - _dbg2: [120]char - viperlib.snprintf(&_dbg2, 120, "[clone] new_pml4=0x%lx\n", uint64(new_pml4_page)) - serial.puts(_dbg2) - i: uint64 - for i in range(512): - new_pml4_page.entries[i] = pml4.entries[i] - pml4_idx: uint64 = (isolate_vaddr >> 39) & 0x1FF - _dbg3: [120]char - viperlib.snprintf(&_dbg3, 120, "[clone] pml4_idx=%lu entry0=0x%lx\n", pml4_idx, pml4.entries[pml4_idx]) - serial.puts(_dbg3) - if not (pml4.entries[pml4_idx] & PTE_PRESENT): return uint64(new_pml4_page) - kernel_pdp: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) - new_pdp_page: _p* = AllocPage() - if not new_pdp_page: return uint64(new_pml4_page) - for i in range(512): - new_pdp_page.entries[i] = kernel_pdp.entries[i] - new_pml4_page.entries[pml4_idx] = (uint64(new_pdp_page) & ~uint64(0xFFF)) | (pml4.entries[pml4_idx] & uint64(0xFFF)) - pdp_idx: uint64 = (isolate_vaddr >> 30) & 0x1FF - if not (kernel_pdp.entries[pdp_idx] & PTE_PRESENT): return uint64(new_pml4_page) - kernel_pd: _p* = _p*(kernel_pdp.entries[pdp_idx] & ~uint64(0xFFF)) - new_pd_page: _p* = AllocPage() - if not new_pd_page: return uint64(new_pml4_page) - for i in range(512): - new_pd_page.entries[i] = kernel_pd.entries[i] - new_pdp_page.entries[pdp_idx] = (uint64(new_pd_page) & ~uint64(0xFFF)) | (kernel_pdp.entries[pdp_idx] & uint64(0xFFF)) - pd_idx: uint64 = (isolate_vaddr >> 21) & 0x1FF - if not (kernel_pd.entries[pd_idx] & PTE_PRESENT): return uint64(new_pml4_page) - if kernel_pd.entries[pd_idx] & PTE_PS: return uint64(new_pml4_page) - kernel_pt: _p* = _p*(kernel_pd.entries[pd_idx] & ~uint64(0xFFF)) - new_pt_page: _p* = AllocPage() - if not new_pt_page: return uint64(new_pml4_page) - for i in range(512): - new_pt_page.entries[i] = kernel_pt.entries[i] - new_pd_page.entries[pd_idx] = (uint64(new_pt_page) & ~uint64(0xFFF)) | (kernel_pd.entries[pd_idx] & uint64(0xFFF)) - return uint64(new_pml4_page) - -def save_pt_range(vaddr_start: uint64, vaddr_end: uint64, save_buf: uint64*, save_count: uint32*) -> int: - global pml4 - if not pml4: return -1 - *save_count = uint32(0) - va: uint64 = vaddr_start & ~uint64(0xFFF) - while va < vaddr_end: - pml4_idx: uint64 = (va >> 39) & 0x1FF - if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue - pdp_: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) - pdp_idx: uint64 = (va >> 30) & 0x1FF - if not (pdp_.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue - pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF)) - pd_idx: uint64 = (va >> 21) & 0x1FF - if not (pd_.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue - if pd_.entries[pd_idx] & PTE_PS: va += 0x200000; continue - pt_: _p* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF)) - pt_idx: uint64 = (va >> 12) & 0x1FF - saved: uint32 = *save_count - # [待复核·指针缩放] save_buf 是 `uint64*`;若该联合解析为 Ptr(u64),则下面的 - # `* 16` 与 `+ 8` 都会再乘 8(GEP 按元素缩放)⇒ 记录步长 128 字节、字段偏移 64 字节, - # 与本处"16 字节一条记录(va 在 +0、pte 在 +8)"的设计不符。若本意是字节偏移, - # 需改用整数加法推进地址(或显式 rawptr/字节指针)——迁移到 rawptr[T] 时一并复核, - # 见 Debugger/rawptr-migration-and-fastfail.md。结论待定(需一次 IR 取证 GEP 的元素类型)。 - entry_ptr: uint64* = save_buf + uint64(saved) * 16 - *(entry_ptr + 0) = va - *(entry_ptr + 8) = pt_.entries[pt_idx] - *save_count = uint32(saved + 1) - va += 0x1000 - return 0 - -def restore_pt_range(save_buf: uint64*, save_count: uint32): - global pml4 - if not pml4: return - i: uint32 = 0 - while i < save_count: - # [待复核·指针缩放] 同 save_pt_range:`* 16` / `+ 8` 若按 Ptr(u64) 缩放会变成 128/64 字节 - # (该函数必须与 save_pt_range 的写入布局严格一致,两者要一起复核/一起改)。 - entry_ptr: uint64* = save_buf + uint64(i) * 16 - va: uint64 = *(entry_ptr + 0) - orig_pte: uint64 = *(entry_ptr + 8) - pml4_idx: uint64 = (va >> 39) & 0x1FF - if not (pml4.entries[pml4_idx] & PTE_PRESENT): i += 1; continue - pdp_: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) - pdp_idx: uint64 = (va >> 30) & 0x1FF - if not (pdp_.entries[pdp_idx] & PTE_PRESENT): i += 1; continue - pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF)) - pd_idx: uint64 = (va >> 21) & 0x1FF - if not (pd_.entries[pd_idx] & PTE_PRESENT): i += 1; continue - if pd_.entries[pd_idx] & PTE_PS: i += 1; continue - pt_: _p* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF)) - pt_idx: uint64 = (va >> 12) & 0x1FF - pt_.entries[pt_idx] = orig_pte - __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]", op=[ASM_DESCR.CLOBBER_MEMORY]) - i += 1 - -def set_active_pml4(cr3_val: uint64): - global pml4 - pml4 = _p*(cr3_val & ~uint64(0xFFF)) +import drivers.serial.uart.serial as serial +import viperlib + + +define PTE_PRESENT = (1 << 0) +define PTE_WRITABLE = (1 << 1) +define PTE_USER = (1 << 2) +define PTE_PWT = (1 << 3) +define PTE_PCD = (1 << 4) +define PTE_ACCESSED = (1 << 5) +define PTE_DIRTY = (1 << 6) +define PTE_PS = (1 << 7) +define PTE_GLOBAL = (1 << 8) + +class _p: + entries: [512]uint64 + +define PT_POOL_PAGES = 512 +pt_pool_raw: rawptr = None +pt_pool_start: uint64 = 0 +pt_pool_used: uint64 = 0 + +AllocatedPages: uint64 = 0 +pml4: _p* = None +pdp: _p* +pd: _p* +pt: _p* + +def init(MemmapAddr: uint64, MemmapSize: uint64): + global pml4 + pml4 = GetCurrentPml4() + # bootloader/UEFI 的 CR0.WP 通常已是 1(原代码清 WP 正因此)。页表页 + # (pml4/pdp/pd/pt,0xf8xxxx)默认只读,而内核运行时 MapPage/clone 需 + # 修改页表。顺序:先清 WP(允许写只读页表页)→ MakePTWritable 放开 + # 全部页表页 → 再置 WP=1(此后写只读页即 #PF 暴露越界写源)。 + __asm__(f""" + mov rax, cr0 + and rax, ~0x10000 + mov cr0, rax + """) + MakePTWritable() + __asm__(f""" + mov rax, cr0 + or rax, 0x10000 + mov cr0, rax + """) + +def init_pool(buf: rawptr, buf_size: uint64): + global pt_pool_raw, pt_pool_start, pt_pool_used + pt_pool_raw = buf + if buf is None: return + raw_addr: uint64 = uint64(buf) + aligned_addr: uint64 = (raw_addr + uint64(0xFFF)) & ~uint64(0xFFF) + pt_pool_start = aligned_addr + pt_pool_used = 0 + +def AllocPage() -> rawptr: + global AllocatedPages, pt_pool_used, pml4, pdp, pd, pt + if pt_pool_raw is None: return None + if pt_pool_used >= uint64(PT_POOL_PAGES): return None + page: rawptr = rawptr(pt_pool_start + pt_pool_used * uint64(4096)) + pt_pool_used = pt_pool_used + uint64(1) + # rep stosb 向 [rdi] 写 4096 字节:必须声明 CLOBBER_MEMORY,否则优化器 + # 无法得知本块改写了内存,可能把其前后的内存读写重排/消除(N8)。 + __asm__(f"""mov al, 0 + mov ecx, 4096 + mov rdi, {asminp(page, ASM_DESCR.REG_ANY)} + rep stosb""", + op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_AL, + ASM_DESCR.CLOBBER_ECX, ASM_DESCR.CLOBBER_RDI]) + AllocatedPages = AllocatedPages + uint64(1) + return page + +def FreePage(page: rawptr): + global AllocatedPages, pml4, pdp, pd, pt + if page is None: return + if AllocatedPages > 0: AllocatedPages = AllocatedPages - uint64(1) + +def MapPage(VirtAddr: uint64, PhysAddr: uint64, flags: uint64): + global pml4, pdp, pd, pt + if VirtAddr == 0 or PhysAddr == 0: return + VirtAddr = VirtAddr & ~uint64(0xFFF) + PhysAddr = PhysAddr & ~uint64(0xFFF) + Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF + PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF + PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF + PtIndex: uint64 = (VirtAddr >> 12) & 0x1FF + + if not pml4: + pml4 = AllocPage() + if not pml4: return + + if not (pml4.entries[Pml4Index] & PTE_PRESENT): + pdp = AllocPage() + if not pdp: return + pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) + else: + if (flags & PTE_USER) and not (pml4.entries[Pml4Index] & PTE_USER): + pml4.entries[Pml4Index] = pml4.entries[Pml4Index] | PTE_USER + pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF)) + + if not (pdp.entries[PdpIndex] & PTE_PRESENT): + pd = AllocPage() + if not pd: return + pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) + else: + if (flags & PTE_USER) and not (pdp.entries[PdpIndex] & PTE_USER): + pdp.entries[PdpIndex] = pdp.entries[PdpIndex] | PTE_USER + pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF)) + + if pd.entries[PdIndex] & 1: + if pd.entries[PdIndex] & 0x80: + big_phys: uint64 = pd.entries[PdIndex] & ~uint64(0x1FFFFF) + inherit_flags: uint64 = pd.entries[PdIndex] & (PTE_PCD | PTE_PWT) + pt = AllocPage() + if not pt: return + for split_i in range(512): + pt.entries[split_i] = (big_phys + uint64(split_i) * 0x1000) | PTE_PRESENT | PTE_WRITABLE | inherit_flags + pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) + else: + # PD entry exists as a regular PT (e.g. cloned identity mapping). + # Must propagate PTE_USER to the PD entry so user-mode access is + # allowed at all paging levels, otherwise #PF err=0x5. + if (flags & PTE_USER) and not (pd.entries[PdIndex] & PTE_USER): + pd.entries[PdIndex] = pd.entries[PdIndex] | PTE_USER + else: + pt = AllocPage() + if not pt: return + pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER) + pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF)) + + pt.entries[PtIndex] = PhysAddr | flags | PTE_PRESENT + __asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]") + +def MapLargePage(VirtAddr: uint64, PhysAddr: uint64, flags: uint64): + global pml4, pdp, pd, pt + VirtAddr = VirtAddr & ~uint64(0x1FFFFF) + PhysAddr = PhysAddr & ~uint64(0x1FFFFF) + Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF + PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF + PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF + + if not pml4: + pml4 = AllocPage() + if not pml4: return + + if not pml4.entries[Pml4Index] & PTE_PRESENT: + pdp = AllocPage() + if not pdp: return + pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE + pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF)) + + if not (pdp.entries[PdpIndex] & PTE_PRESENT): + pd = AllocPage() + if not pd: return + pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE + pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF)) + + pd.entries[PdIndex] = PhysAddr | flags | PTE_PRESENT | PTE_PS + __asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]") + +def UnMapPage(VirtAddr: uint64): + global pml4, pdp, pd, pt + if VirtAddr == 0: return + if not pml4: return + Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF + PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF + PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF + PtIndex: uint64 = (VirtAddr >> 12) & 0x1FF + + if not pml4.entries[Pml4Index] & PTE_PRESENT: return + + pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF)) + if not pdp.entries[PdpIndex] & PTE_PRESENT: return + + pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF)) + + if not pd.entries[PdIndex] & PTE_PRESENT: return + + if pd.entries[PdIndex] & PTE_PS: + pd.entries[PdIndex] = 0 + else: + pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF)) + pt.entries[PtIndex] = 0 + + __asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]") + +def EnablePaging(): + global pml4, pdp, pd, pt + __asm__(f"mov cr3, {asminp(uint64(pml4), ASM_DESCR.REG_ANY)}") + +def _set_page_writable(phys_addr: uint64): + """把物理页 phys_addr(identity 映射,虚拟=物理)的 PTE 置 W 位 + + 页表页(bootloader 建立的 pml4/pdp/pd/pt 位于 0xf8xxxx)默认只读, + 而内核运行时 MapPage/clone 需修改页表 → 必须先把这些页表页自身设为可写, + 否则 CR0.WP=1 下写页表触发 #PF(slog.so 映射 0x600000 新建页表项时 + cr2=0xf801000 err=0x3 根因)。 + """ + global pml4 + if not pml4: return + va: uint64 = phys_addr & ~uint64(0xFFF) + pml4_idx: uint64 = (va >> 39) & 0x1FF + if not (pml4.entries[pml4_idx] & PTE_PRESENT): return + pdp_s: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) + pdp_idx: uint64 = (va >> 30) & 0x1FF + if not (pdp_s.entries[pdp_idx] & PTE_PRESENT): return + pd_s: _p* = _p*(pdp_s.entries[pdp_idx] & ~uint64(0xFFF)) + pd_idx: uint64 = (va >> 21) & 0x1FF + if not (pd_s.entries[pd_idx] & PTE_PRESENT): return + if pd_s.entries[pd_idx] & PTE_PS: + # 2MB 大页:W 位在 PD 条目自身(无下层 PT),直接置位 + pd_s.entries[pd_idx] = pd_s.entries[pd_idx] | uint64(PTE_WRITABLE) + __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") + return + pt_s: _p* = _p*(pd_s.entries[pd_idx] & ~uint64(0xFFF)) + pt_idx: uint64 = (va >> 12) & 0x1FF + pt_s.entries[pt_idx] = pt_s.entries[pt_idx] | uint64(PTE_WRITABLE) + __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") + +def MakePTWritable(): + """把当前页表层次所有页表页(pml4 自身 + pdp/pd/pt)设为可写 + + 内核运行时需修改页表(MapPage/clone_for_process),而 bootloader 建立的 + 页表页映射为只读——CR0.WP=1 下必须先放开页表页自身,否则写页表即 #PF。 + """ + global pml4 + if not pml4: return + _set_page_writable(uint64(pml4)) + for pi in range(512): + if not (pml4.entries[pi] & PTE_PRESENT): continue + pdp_m: _p* = _p*(pml4.entries[pi] & ~uint64(0xFFF)) + _set_page_writable(uint64(pdp_m)) + pj: uint64 + for pj in range(512): + if not (pdp_m.entries[pj] & PTE_PRESENT): continue + pd_m: _p* = _p*(pdp_m.entries[pj] & ~uint64(0xFFF)) + _set_page_writable(uint64(pd_m)) + pk: uint64 + for pk in range(512): + if not (pd_m.entries[pk] & PTE_PRESENT): continue + if pd_m.entries[pk] & PTE_PS: continue + pt_m: _p* = _p*(pd_m.entries[pk] & ~uint64(0xFFF)) + _set_page_writable(uint64(pt_m)) + +def MakeRangeRO(vaddr_start: uint64, vaddr_end: uint64): + """把 [vaddr_start, vaddr_end) 范围内所有页的 PTE 清 W 位(只读) + + 定位越界写源:内核态写只读页在 CR0.WP=1 下触发 #PF(int=14), + isrCommonhandler 打印 cr2/rip → 写源立即暴露,不再静默破坏 .rodata。 + """ + global pml4 + if not pml4: return + va: uint64 = vaddr_start & ~uint64(0xFFF) + # ro_end 下取整到页:不保护含 ro_end 的未对齐尾页。链接脚本输出段未页对齐, + # .data 紧跟 .rodata(__rodata_end 未对齐时 .data 起始落在 rodata 尾页内), + # 整页置只读会把 .data 全局(如 devfs @DevFSObject=0x141630)误保护 → + # devfs_init 写全局 #PF(cr2=0x141630 rip=0x101aaa mov [r15],rax 根因)。 + # 尾页含少量 rodata 尾部(对齐填充),保持可写可容忍。 + ro_end_align: uint64 = vaddr_end & ~uint64(0xFFF) + while va < ro_end_align: + pml4_idx: uint64 = (va >> 39) & 0x1FF + if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue + pdp_r: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) + pdp_idx: uint64 = (va >> 30) & 0x1FF + if not (pdp_r.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue + pd_r: _p* = _p*(pdp_r.entries[pdp_idx] & ~uint64(0xFFF)) + pd_idx: uint64 = (va >> 21) & 0x1FF + if not (pd_r.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue + if pd_r.entries[pd_idx] & PTE_PS: + # 2MB 大页:直接跳过会让 .rodata 保持可写——越界写被静默吞掉 + # (perf 格式串 win=%lu/work=%lu 被逐步破坏的根因)。拆分为 512 + # 个 4KB 页(复用 MapPage 的分裂逻辑),再走下方常规路径清 W 位。 + # WP=1 后内核态写只读页即 #PF → isrCommonhandler 打印 cr2/rip 暴露写源。 + big_phys: uint64 = pd_r.entries[pd_idx] & ~uint64(0x1FFFFF) + inherit_flags: uint64 = pd_r.entries[pd_idx] & (PTE_PCD | PTE_PWT | PTE_USER | uint64(0x8000000000000000)) + pt_split: _p* = AllocPage() + if not pt_split: + va += 0x1000 + continue + for split_i in range(512): + pt_split.entries[split_i] = (big_phys + uint64(split_i) * uint64(0x1000)) | PTE_PRESENT | PTE_WRITABLE | inherit_flags + pd_r.entries[pd_idx] = (uint64(pt_split) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (pd_r.entries[pd_idx] & PTE_USER) + __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") + # 分裂后落入下方 4KB 常规路径,继续清当前页 W 位 + pt_r: _p* = _p*(pd_r.entries[pd_idx] & ~uint64(0xFFF)) + pt_idx: uint64 = (va >> 12) & 0x1FF + pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~uint64(PTE_WRITABLE) + __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]") + va += 0x1000 + +def ProtectKernelRO(): + """把内核镜像 .rodata 段(字符串常量/vtable 等)映射为只读 + + 地址经 __asm__ 内联引用链接器符号 __rodata_start/__rodata_end + (linker.ld 定义,VPC 不声明 extern——避免生成定义与链接器符号冲突)。 + """ + global pml4 + if not pml4: return + # 页表页可写已在 paging.init(WP=0 阶段)由 MakePTWritable 完成, + # 此处只保护 .rodata 内容页(WP=1 后写只读页即 #PF 暴露写源)。 + ro_start: uint64 = 0 + ro_end: uint64 = 0 + __asm__(f"""lea rax, [rip + __rodata_start] + mov {asmout(ro_start, ASM_DESCR.OUTPUT_REG)}, rax + lea rax, [rip + __rodata_end] + mov {asmout(ro_end, ASM_DESCR.OUTPUT_REG)}, rax""", + op=[ASM_DESCR.CLOBBER_RAX]) + MakeRangeRO(ro_start, ro_end) + # [PG] 打印 .rodata 起始页映射:确认只读真正生效。 + # ps=1 说明仍在 2MB 大页(分裂失败)→ .rodata 保持可写 → 越界写继续被静默吞掉。 + DumpMapping(ro_start, "ro_s") + +def DumpMapping(va: uint64, tag: const str): + """[PG] 打印指定 VA 的 pml4/pdp/pd/pt 条目,定位大页(PS)与权限位 + + 内核区域若为 UEFI 2MB 大页,MakeRangeRO 无法只读 .rodata(越界写静默破坏 + 格式串);1GB 页同理(PS 位在 pdpe)。W=1 且 PS=1 → 该页可写且是大页。 + """ + global pml4 + if not pml4: return + pml4_i: uint64 = (va >> 39) & 0x1FF + pdp_i: uint64 = (va >> 30) & 0x1FF + pd_i: uint64 = (va >> 21) & 0x1FF + pt_i: uint64 = (va >> 12) & 0x1FF + pml4_e: uint64 = pml4.entries[pml4_i] + pdp_e: uint64 = 0 + pd_e: uint64 = 0 + pt_e: uint64 = 0 + if pml4_e & PTE_PRESENT: + pdp_r: _p* = _p*(pml4_e & ~uint64(0xFFF)) + pdp_e = pdp_r.entries[pdp_i] + if pdp_e & PTE_PRESENT: + if pdp_e & PTE_PS: + pd_e = pdp_e + else: + pd_r: _p* = _p*(pdp_e & ~uint64(0xFFF)) + pd_e = pd_r.entries[pd_i] + if (pd_e & PTE_PRESENT) and not (pd_e & PTE_PS): + pt_r: _p* = _p*(pd_e & ~uint64(0xFFF)) + pt_e = pt_r.entries[pt_i] + buf_d: [200]char + viperlib.snprintf(&buf_d, 200, "[PG] %s va=0x%lx pdpe=0x%lx pde=0x%lx pte=0x%lx (ps=%d W=%d)\n", + tag, va, pdp_e, pd_e, pt_e, + 1 if (pd_e & PTE_PS) else 0, + 1 if (pt_e & PTE_WRITABLE) or (pd_e & PTE_WRITABLE) or (pdp_e & PTE_WRITABLE) else 0) + serial.puts(&buf_d) + +def GetCurrentPml4() -> _p*: + global pml4, pdp, pd, pt + cr3: uint64 + __asm__(f"mov {asmout(cr3, ASM_DESCR.OUTPUT_REG)}, cr3") + return _p*(cr3 & ~uint64(0xFFF)) + +def create_process_page_table() -> uint64: + global pml4 + if not pml4: return 0 + new_pml4: _p* = AllocPage() + if not new_pml4: return 0 + i: uint64 + for i in range(512): + entry: uint64 = pml4.entries[i] + if entry & PTE_PRESENT: + new_pml4.entries[i] = entry + return uint64(new_pml4) + +def clone_for_process(isolate_vaddr: uint64) -> uint64: + global pml4 + _dbg: [120]char + viperlib.snprintf(&_dbg, 120, "[clone] pml4=0x%lx pool_used=%lu iso_va=0x%lx\n", uint64(pml4), pt_pool_used, isolate_vaddr) + serial.puts(_dbg) + if not pml4: return 0 + new_pml4_page: _p* = AllocPage() + if not new_pml4_page: + # [DIAG] 定位 AllocPage 失败:pool_used 打印 0 但分配失败 → BSS 全局 + # (pt_pool_raw/pt_pool_used)疑似被内存越界写覆盖。 + _dbgf: [120]char + viperlib.snprintf(&_dbgf, 120, "[clone] AllocPage FAIL raw=0x%lx used=%lu PP=%d\n", + uint64(pt_pool_raw), pt_pool_used, PT_POOL_PAGES) + serial.puts(&_dbgf) + serial.puts("[clone] AllocPage pml4 failed\n") + return 0 + _dbg2: [120]char + viperlib.snprintf(&_dbg2, 120, "[clone] new_pml4=0x%lx\n", uint64(new_pml4_page)) + serial.puts(_dbg2) + i: uint64 + for i in range(512): + new_pml4_page.entries[i] = pml4.entries[i] + pml4_idx: uint64 = (isolate_vaddr >> 39) & 0x1FF + _dbg3: [120]char + viperlib.snprintf(&_dbg3, 120, "[clone] pml4_idx=%lu entry0=0x%lx\n", pml4_idx, pml4.entries[pml4_idx]) + serial.puts(_dbg3) + if not (pml4.entries[pml4_idx] & PTE_PRESENT): return uint64(new_pml4_page) + kernel_pdp: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) + new_pdp_page: _p* = AllocPage() + if not new_pdp_page: return uint64(new_pml4_page) + for i in range(512): + new_pdp_page.entries[i] = kernel_pdp.entries[i] + new_pml4_page.entries[pml4_idx] = (uint64(new_pdp_page) & ~uint64(0xFFF)) | (pml4.entries[pml4_idx] & uint64(0xFFF)) + pdp_idx: uint64 = (isolate_vaddr >> 30) & 0x1FF + if not (kernel_pdp.entries[pdp_idx] & PTE_PRESENT): return uint64(new_pml4_page) + kernel_pd: _p* = _p*(kernel_pdp.entries[pdp_idx] & ~uint64(0xFFF)) + new_pd_page: _p* = AllocPage() + if not new_pd_page: return uint64(new_pml4_page) + for i in range(512): + new_pd_page.entries[i] = kernel_pd.entries[i] + new_pdp_page.entries[pdp_idx] = (uint64(new_pd_page) & ~uint64(0xFFF)) | (kernel_pdp.entries[pdp_idx] & uint64(0xFFF)) + pd_idx: uint64 = (isolate_vaddr >> 21) & 0x1FF + if not (kernel_pd.entries[pd_idx] & PTE_PRESENT): return uint64(new_pml4_page) + if kernel_pd.entries[pd_idx] & PTE_PS: return uint64(new_pml4_page) + kernel_pt: _p* = _p*(kernel_pd.entries[pd_idx] & ~uint64(0xFFF)) + new_pt_page: _p* = AllocPage() + if not new_pt_page: return uint64(new_pml4_page) + for i in range(512): + new_pt_page.entries[i] = kernel_pt.entries[i] + new_pd_page.entries[pd_idx] = (uint64(new_pt_page) & ~uint64(0xFFF)) | (kernel_pd.entries[pd_idx] & uint64(0xFFF)) + return uint64(new_pml4_page) + +def save_pt_range(vaddr_start: uint64, vaddr_end: uint64, save_buf: uint64*, save_count: uint32*) -> int: + global pml4 + if not pml4: return -1 + *save_count = uint32(0) + va: uint64 = vaddr_start & ~uint64(0xFFF) + while va < vaddr_end: + pml4_idx: uint64 = (va >> 39) & 0x1FF + if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue + pdp_: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) + pdp_idx: uint64 = (va >> 30) & 0x1FF + if not (pdp_.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue + pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF)) + pd_idx: uint64 = (va >> 21) & 0x1FF + if not (pd_.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue + if pd_.entries[pd_idx] & PTE_PS: va += 0x200000; continue + pt_: _p* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF)) + pt_idx: uint64 = (va >> 12) & 0x1FF + saved: uint32 = *save_count + # [待复核·指针缩放] save_buf 是 `uint64*`;若该联合解析为 Ptr(u64),则下面的 + # `* 16` 与 `+ 8` 都会再乘 8(GEP 按元素缩放)⇒ 记录步长 128 字节、字段偏移 64 字节, + # 与本处"16 字节一条记录(va 在 +0、pte 在 +8)"的设计不符。若本意是字节偏移, + # 需改用整数加法推进地址(或显式 rawptr/字节指针)——迁移到 rawptr[T] 时一并复核, + # 见 Debugger/rawptr-migration-and-fastfail.md。结论待定(需一次 IR 取证 GEP 的元素类型)。 + entry_ptr: uint64* = save_buf + uint64(saved) * 16 + *(entry_ptr + 0) = va + *(entry_ptr + 8) = pt_.entries[pt_idx] + *save_count = uint32(saved + 1) + va += 0x1000 + return 0 + +def restore_pt_range(save_buf: uint64*, save_count: uint32): + global pml4 + if not pml4: return + i: uint32 = 0 + while i < save_count: + # [待复核·指针缩放] 同 save_pt_range:`* 16` / `+ 8` 若按 Ptr(u64) 缩放会变成 128/64 字节 + # (该函数必须与 save_pt_range 的写入布局严格一致,两者要一起复核/一起改)。 + entry_ptr: uint64* = save_buf + uint64(i) * 16 + va: uint64 = *(entry_ptr + 0) + orig_pte: uint64 = *(entry_ptr + 8) + pml4_idx: uint64 = (va >> 39) & 0x1FF + if not (pml4.entries[pml4_idx] & PTE_PRESENT): i += 1; continue + pdp_: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF)) + pdp_idx: uint64 = (va >> 30) & 0x1FF + if not (pdp_.entries[pdp_idx] & PTE_PRESENT): i += 1; continue + pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF)) + pd_idx: uint64 = (va >> 21) & 0x1FF + if not (pd_.entries[pd_idx] & PTE_PRESENT): i += 1; continue + if pd_.entries[pd_idx] & PTE_PS: i += 1; continue + pt_: _p* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF)) + pt_idx: uint64 = (va >> 12) & 0x1FF + pt_.entries[pt_idx] = orig_pte + __asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]", op=[ASM_DESCR.CLOBBER_MEMORY]) + i += 1 + +def set_active_pml4(cr3_val: uint64): + global pml4 + pml4 = _p*(cr3_val & ~uint64(0xFFF)) diff --git a/VKernel/project.vpj b/VKernel/project.vpj index e74db62..58c537b 100644 --- a/VKernel/project.vpj +++ b/VKernel/project.vpj @@ -1,40 +1,40 @@ -{ - "name": "Viper OS Kernel", - "version": "2.3.7", - "source_dir": "./Kernel", - "build_dir": "./.tpv_build", - "compiler": { - "cmd": "llc", - "flags": [ - "-filetype=obj", - "-mtriple=x86_64-none-elf", - "-relocation-model=static", - "-O2" - ] - }, - "linker": { - "cmd": "ld.lld.exe", - "flags": [ - "-m", - "elf_x86_64", - "-T", - "linker.ld", - "--oformat", - "binary", - "--strip-all" - ], - "output": "kernel.bin" - }, - "includes": [ - "../../../includes" - ], - "target": { - "triple": "x86_64-none-elf", - "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - }, - "options": { - "slice_level": 3, - "target": "llvm", - "strict_mode": true - } +{ + "name": "Viper OS Kernel", + "version": "2.3.7", + "source_dir": "./Kernel", + "build_dir": "./__vpcbuild__", + "compiler": { + "cmd": "llc", + "flags": [ + "-filetype=obj", + "-mtriple=x86_64-none-elf", + "-relocation-model=static", + "-O2" + ] + }, + "linker": { + "cmd": "ld.lld.exe", + "flags": [ + "-m", + "elf_x86_64", + "-T", + "linker.ld", + "--oformat", + "binary", + "--strip-all" + ], + "output": "kernel.bin" + }, + "includes": [ + "../../../includes" + ], + "target": { + "triple": "x86_64-none-elf", + "datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "target": "llvm", + "strict_mode": true + } } \ No newline at end of file diff --git a/build.py b/build.py index e2bfd46..b084a7c 100644 --- a/build.py +++ b/build.py @@ -12,13 +12,13 @@ noapp = '--noapp' in sys.argv # 编译内核 -cmd = r'..\..\TransPyV\output\TransPyV.exe --project ./VKernel/project.vpj --clear-cache --rebuild-includes' +cmd = r'..\..\ViperC\output\viperc.exe --project ./VKernel/project.vpj --clear-cache --rebuild-includes' if clean: cmd += ' --clean' os.system(cmd) -shutil.copy("VKernel/.tpv_build/output/kernel.bin", "./disk/sys/kernel/x86_64/kernel.bin") +shutil.copy("VKernel/__vpcbuild__/output/kernel.bin", "./disk/sys/kernel/x86_64/kernel.bin") # --noapp 优先级高于 --kernel-only:只编译内核,不编译所有库和应用 @@ -26,14 +26,14 @@ if noapp: print("[build] --noapp: 跳过所有库与应用编译,复用已有缓存文件") # 检查必要文件是否存在 required_libs = [ - ("Libs/SerialLogger/.tpv_build/output/slog.so", "./disk/libs/slog.so"), - ("Libs/VPUI64/.tpv_build/output/vpui64.so", "./disk/libs/vpui64.so"), + ("Libs/SerialLogger/__vpcbuild__/output/slog.so", "./disk/libs/slog.so"), + ("Libs/VPUI64/__vpcbuild__/output/vpui64.so", "./disk/libs/vpui64.so"), ] required_apps = [ - ("Apps/HelloWorld/.tpv_build/output/helloworld.elf", "./disk/apps/hello.elf"), - ("Apps/Terminal/.tpv_build/output/terminal.elf", "./disk/apps/terminal.elf"), - ("Apps/Gargantua/.tpv_build/output/gargantua.elf", "./disk/apps/gargantua.elf"), - ("Apps/Scene3D/.tpv_build/output/scene3d.elf", "./disk/apps/scene3d.elf"), + ("Apps/HelloWorld/__vpcbuild__/output/helloworld.elf", "./disk/apps/hello.elf"), + ("Apps/Terminal/__vpcbuild__/output/terminal.elf", "./disk/apps/terminal.elf"), + ("Apps/Gargantua/__vpcbuild__/output/gargantua.elf", "./disk/apps/gargantua.elf"), + ("Apps/Scene3D/__vpcbuild__/output/scene3d.elf", "./disk/apps/scene3d.elf"), ] os.makedirs("./disk/apps", exist_ok=True) os.makedirs("./disk/libs", exist_ok=True) @@ -49,48 +49,48 @@ elif kernel_only: print("[build] 警告: hello.elf 不存在,请先不带 --kernel-only 完整构建一次") else: # 完整编译所有库+应用 - so_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Libs/SerialLogger/project.vpj --clear-cache' + so_cmd = r'..\..\ViperC\output\viperc.exe --project Libs/SerialLogger/project.vpj --clear-cache' if clean: so_cmd += ' --clean' os.system(so_cmd) - vpui_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Libs/VPUI64/project.vpj --clear-cache' + vpui_cmd = r'..\..\ViperC\output\viperc.exe --project Libs/VPUI64/project.vpj --clear-cache' if clean: vpui_cmd += ' --clean' os.system(vpui_cmd) - app_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/HelloWorld/project.vpj --clear-cache' + app_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/HelloWorld/project.vpj --clear-cache' if clean: app_cmd += ' --clean' os.system(app_cmd) os.makedirs("./disk/apps", exist_ok=True) - shutil.copy("Apps/HelloWorld/.tpv_build/output/helloworld.elf", "./disk/apps/hello.elf") + shutil.copy("Apps/HelloWorld/__vpcbuild__/output/helloworld.elf", "./disk/apps/hello.elf") - term_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/Terminal/project.vpj --clear-cache' + term_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Terminal/project.vpj --clear-cache' if clean: term_cmd += ' --clean' os.system(term_cmd) - shutil.copy("Apps/Terminal/.tpv_build/output/terminal.elf", "./disk/apps/terminal.elf") + shutil.copy("Apps/Terminal/__vpcbuild__/output/terminal.elf", "./disk/apps/terminal.elf") - garg_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/Gargantua/project.vpj --clear-cache' + garg_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Gargantua/project.vpj --clear-cache' if clean: garg_cmd += ' --clean' os.system(garg_cmd) - shutil.copy("Apps/Gargantua/.tpv_build/output/gargantua.elf", "./disk/apps/gargantua.elf") + shutil.copy("Apps/Gargantua/__vpcbuild__/output/gargantua.elf", "./disk/apps/gargantua.elf") - scene3d_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/Scene3D/project.vpj --clear-cache' + scene3d_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Scene3D/project.vpj --clear-cache' if clean: scene3d_cmd += ' --clean' os.system(scene3d_cmd) - shutil.copy("Apps/Scene3D/.tpv_build/output/scene3d.elf", "./disk/apps/scene3d.elf") + shutil.copy("Apps/Scene3D/__vpcbuild__/output/scene3d.elf", "./disk/apps/scene3d.elf") os.makedirs("./disk/libs", exist_ok=True) - so_path = os.path.abspath("Libs/SerialLogger/.tpv_build/output/slog.so") - vpui_path = os.path.abspath("Libs/VPUI64/.tpv_build/output/vpui64.so") + so_path = os.path.abspath("Libs/SerialLogger/__vpcbuild__/output/slog.so") + vpui_path = os.path.abspath("Libs/VPUI64/__vpcbuild__/output/vpui64.so") if os.path.exists(so_path): shutil.copy(so_path, "./disk/libs/slog.so") if os.path.exists(vpui_path):