long *video_memory = (long *)0xB8000;
int main() {
// long *video_memory = (long *)0xB8000;
*video_memory = 0x5050505050505050;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么当我链接它时,上面的这个 C 代码会创建一个2Mb 的二进制文件?
如果我注释全局变量并取消注释它的本地变量(文件只有几个字节)。
这就是我链接/[交叉]编译它的方式:
x86_64-elf-gcc -m64 -ffreestanding -nostdlib -mno-red-zone -c kernel.c -o bin/kernel.o
objcopy --remove-section .eh_frame bin/kernel.o
x86_64-elf-ld --oformat binary -Ttext 0x8000 bin/kernel_entry.o bin/kernel.o -o bin/kernel.bin
# kernel.bin is now 2Mb
Run Code Online (Sandbox Code Playgroud)
问题是链接器max-page-size在 x86_64 ABI 中的一个错误导致了 2MB的荒谬,这最终导致了在各个地方的 2MB 对齐要求。通常它会将文本和数据段放在文件的同一部分,目的是将其映射两次,但这只有使用可以表示它的合理二进制格式(如 ELF)才有可能;对于原始二进制文件,它可以实现对齐的唯一方法是具有近 2MB 的间隙。
正确的解决方法是在链接命令行上max-page-size使用-z max-page-size=4096(实际硬件页面大小/内存保护粒度)覆盖荒谬的默认值。如果您使用编译器驱动程序而不是ld直接调用,那就是-Wl,-z,max-page-size=4096. 每当您使用会出现相同问题的强化选项时,现代托管工具链都会为您执行此操作,但显然裸机仍然没有。
您还可以考虑使用可以为内核加载 ELF 的引导加载程序,而不是原始二进制文件。ELF 加载器很容易编写,让你做一些有用的事情,比如通过映射(在裸机级别,只是加载/复制)同一页面两次来避免浪费空间,在图像标题控制的位置有一个入口点,而不是在引导加载程序等中进行硬编码。如果您愿意,您甚至可以通过这种方式使内核位置独立。