diff --git a/VKernel/linker.ld b/VKernel/linker.ld index 296fdaa..d994a3a 100644 --- a/VKernel/linker.ld +++ b/VKernel/linker.ld @@ -26,13 +26,6 @@ SECTIONS { *(.bss) *(.bss.*) *(COMMON) - /* 强制本输出段为 PROGBITS 并占用文件空间:.bss 默认是 NOBITS(不占文件), - 于是 kernel.bin 的 FileSize 不包含 .bss。bootloader 只按文件大小预留物理页 - (PagesNeeded = KernelSize/4096),.bss 尾部(ticks/_perf_* 等全局量)便留在 - 未预留的 EfiConventionalMemory 里,被 mm 的堆 arena 覆盖 → 破坏内核全局量、 - 并反过来破坏堆元数据。加一条数据语句(LONG)即让整段 .bss 写入 kernel.bin。 */ - . = ALIGN(16); - LONG(0); __bss_end = .; } diff --git a/VKernel/project.vpj b/VKernel/project.vpj index 58c537b..46519f9 100644 --- a/VKernel/project.vpj +++ b/VKernel/project.vpj @@ -18,12 +18,9 @@ "-m", "elf_x86_64", "-T", - "linker.ld", - "--oformat", - "binary", - "--strip-all" + "linker.ld" ], - "output": "kernel.bin" + "output": "kernel.elf" }, "includes": [ "../../../includes" diff --git a/build.py b/build.py index 0ac1f58..63f4c15 100644 --- a/build.py +++ b/build.py @@ -1,6 +1,5 @@ import shutil import os -import re import sys import subprocess @@ -16,52 +15,24 @@ only_run = '--onlyrun' in sys.argv def BuildKernel(): - """编译内核 + 产出带符号 kernel.elf(仅供分析)""" + """编译内核:链接器一次产出带符号 kernel.elf,再由 objcopy 二次处理成裸镜像 kernel.bin""" cmd = r'..\..\ViperC\output\viperc.exe --project ./VKernel/project.vpj --clear-cache --rebuild-includes' if clean: cmd += ' --clean' os.system(cmd) - shutil.copy("VKernel/__vpcbuild__/output/kernel.bin", "./disk/sys/kernel/x86_64/kernel.bin") - - # 额外产出带符号的 kernel.elf(仅供分析:objdump / nm / readelf 定位崩溃 rip)。 - # kernel.bin 是 --oformat binary --strip-all 的裸镜像、无符号表,不便把日志里的 - # rip 映射回函数。要点:**对象顺序决定段内布局**,自行 glob 一组 .obj 去链会得到 - # 与 kernel.bin 不同的地址(实测前 247KB 里 230KB 不同),没法对上日志。 - # 因此用 viperc 自己的 --phase2command:它把 llc/链接命令按“精确顺序”写进 .bat, - # 我们只把该链接行里的 --oformat binary/--strip-all 去掉、输出改为 kernel.elf 再执行。 + # project.vpj 的 linker 已改为直接产出 ELF(保留符号表,去掉 --oformat binary/--strip-all), + # kernel.bin 由同一份 ELF 二次处理得到 —— 两者布局逐字节一致,日志里的 rip 可直接用 + # objdump / nm / readelf 落到函数名,不必再猜地址(此前自行 glob .obj 去链会得到不同布局)。 + # --set-section-flags .bss=alloc,load,contents:把 .bss 一并写进裸镜像(ELF 里 .bss 默认 + # NOBITS、不占文件空间),这样 bootloader 按“文件大小”预留就能覆盖 .bss。 kernel_elf = "VKernel/__vpcbuild__/output/kernel.elf" - p2c_bat = "VKernel/__vpcbuild__/phase2cmd.bat" - os.system(r'..\..\ViperC\output\viperc.exe --project ./VKernel/project.vpj --phase2command ' + p2c_bat) - link_line = None - if os.path.exists(p2c_bat): - with open(p2c_bat, "r", encoding="utf-8", errors="replace") as f: - for line in f: - low = line.lower() - if "ld.lld" in low and (".bin" in low or "output" in low): - link_line = line.rstrip("\r\n") - if link_line is not None: - # 稳健改写:--oformat 可能是 "--oformat binary" 或 "--oformat=binary";输出可能是 - # "-o " 或 "--output="。任一没匹配上就放弃(绝不去执行原样命令,否则会把 - # kernel.bin 覆盖成非预期产物)。 - elf_line = re.sub(r"--oformat[ =]binary", "", link_line) - elf_line = re.sub(r"--strip-all", "", elf_line) - elf_line = re.sub(r"(-o\s+)\S+", r"\1" + kernel_elf, elf_line) - elf_line = re.sub(r"(--output=)\S+", r"\1" + kernel_elf, elf_line) - if elf_line == link_line or kernel_elf not in elf_line: - print("[build] 警告: 未能改写链接命令,跳过 kernel.elf(避免误覆盖 kernel.bin)") - else: - # .bat 里的对象路径可能已过期(例如 --rebuild-includes 之后 includes.binary_mlir - # 下的目标文件名变化),逐个校验;缺任何一个就跳过,避免刷一屏 ld.lld 报错。 - missing = [t for t in elf_line.split() if t.endswith(".obj") and not os.path.exists(t)] - if missing: - print(f"[build] 跳过 kernel.elf:{len(missing)} 个对象缺失(.bat 路径已过期),例:{missing[0]}") - else: - print("[build] linking kernel.elf for analysis ...") - if os.system(elf_line) != 0: - print("[build] 警告: kernel.elf 链接失败(不影响 kernel.bin)") - else: - print("[build] 警告: 未从 phase2command 取到链接命令,跳过 kernel.elf") + kernel_bin = "VKernel/__vpcbuild__/output/kernel.bin" + rc = os.system(f'llvm-objcopy.exe -O binary --set-section-flags .bss=alloc,load,contents "{kernel_elf}" "{kernel_bin}"') + if rc != 0: + print("[build] 警告: objcopy 生成 kernel.bin 失败") + + shutil.copy(kernel_bin, "./disk/sys/kernel/x86_64/kernel.bin") def BuildApps():