feat(build, kernel): 内核链接直接产出带符号 kernel.elf,再由 objcopy 二次产出 kernel.bin

按方案调整:
- VKernel/project.vpj:linker 去掉 --oformat binary / --strip-all,output 改为 kernel.elf。
- build.py:用 objcopy -O binary --set-section-flags .bss=alloc,load,contents 由 kernel.elf
  二次产出 kernel.bin,再拷到 disk/。同源产出 ⇒ 两者布局逐字节一致,日志里的 rip 可直接用
  objdump / nm / readelf 落到函数名;原来那段依赖 phase2cmd.bat 的改写字样已整体删除。
- --set-section-flags .bss=...contents 把 .bss 一并写进裸镜像(ELF 里 .bss 默认 NOBITS),
  于是 bootloader 按“文件大小”预留即可覆盖 .bss(boot.c 里另有 256KB 余量,双保险)。
- linker.ld:移除此前对 lld 无效的 LONG(0) 写法,.bss 恢复为干净定义。

注意:需完整重编(不能只 --onlyrun)才会生成新的 kernel.elf / kernel.bin。
This commit is contained in:
TermiNexus committed 2026-10-14 15:22:05 +08:00
1 parent 1ebb744418
commit 8b0d24e0d5
3 files changed
+14 -53

No files matched your search

-7
View File
@@ -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 = .;
}
+2 -5
View File
@@ -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"
+12 -41
View File
@@ -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 <path>" 或 "--output=<path>"。任一没匹配上就放弃(绝不去执行原样命令,否则会把
# 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():