版本说明: Linux 6.12.32架构说明: x86_64这个文章其实是因为看 arttnba3 大佬的文章的时候看见了一个泄漏地址小trick也就是 physmem_base + 0x9d000 存放着secondary_startup_64指针,从而泄漏 kernel base.但是搜索了很多文章好像都只是提及了一下这个小trick但是没说明原因。然后在询问了 Tplus 大佬后,知道了这个板块是realmode相关的,所以从头开始进行了分析。最终结合自己的理解推测了一下这个trick的原因,当然受限于本人的水平,可能这个推断并不正确。(如果有问题请各位大佬帮忙指正一下)想要快速看这个问题的答案可以直接跳转到最后的总结,因为本文很多过程都是针对于Linux启动过程中的内存变化,可能略显啰嗦(可能有点点偏离主题)。参考文章:Real Address Mode (实模式):在此模式下地址访问的是真实地内存地址所在位置。在 realmode 模式下,数据地址是根据 ds:x 和 es:x (ES是一个“额外”的数据段寄存器。当需要同时访问两个不同的数据段时,DS用于一个,ES就用于另一个。)来进行访问的比如 ds 为 0x7c0 ,si为0则指令地址是根据 cs:x 寄存器进行寻址的Protected Address Mode (保护模式):采用虚拟内存,页等机制对内存进行保护。电源接通时,CPU的寄存器值为:CS : 0xfffff000 即此时代码执行的地址是0xfffffff0 (BIOS程序所在的ROM区域)BIOS 的固件程序会将硬盘启动区 512 B的数据原封不动复制给 0x7c00 这个位置,并且跳转到 0x7c00最后两字节也就是检查第一扇区的最后两字节 boot Signature 和 Magic Number 分别是否为0x55 和 0xAA (01010101 10101010这种交叉数据最容易检查传输是否错误)或许你会疑惑为什么是 0x7c00 ,这里可以看一下这个文章:e1cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8X3S2T1N6i4S2A6j5h3!0X3k6h3W2Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3k6r3g2@1j5h3W2D9M7#2)9J5c8U0p5K6y4o6f1^5y4K6V1H3z5b7`.`.当 BIOS 执行完毕,就会将 bootloader 复制给 0x7c00 处并且 BIOS 会将控制权给 bootloader 在早期版本下 bootloader 一般是 Linux 的 boot/bootsect.s 中定义的在后面一般 bootloader 单独存在的软件对于 x86/x86-64 架构:对于 ARM 架构:一般来说 GRUB 是最常见的 bootloader,其实 bootloader 干的事情基本一致,所以我们主要分析 GRUB下文主要是引用的这个文章感兴趣可以直接看原文:064K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5K9h3&6I4K9i4g2Q4x3X3g2Y4K9i4c8T1L8$3!0C8M7#2)9J5k6h3W2G2i4K6u0r3L8r3W2F1N6i4S2Q4x3X3c8A6L8Y4y4A6k6r3g2K6i4K6u0V1j5$3&6Q4x3V1k6U0L8$3&6@1k6h3&6@1i4K6u0r3b7X3!0G2N6r3W2F1k6#2)9J5c8X3I4A6L8Y4g2^5i4K6u0V1j5X3!0G2N6s2y4@1M7X3q4H3i4K6u0V1x3g2)9J5k6h3S2@1L8h3H3`.grub_main 初始化控制台,计算模块基地址,设置 root 设备,读取 grub 配置文件,加载模块。最后,将 GRUB 置于 normal 模式,在这个模式中,grub_normal_execute (from grub-core/normal/main.c) 将被调用以完成最后的准备工作,然后显示一个菜单列出所用可用的操作系统。(这个界面我们就比较熟悉了对吧,比如进入网吧我们就会看见这个界面)根据 arch/x86/boot/header.S 定义,可以知道 setup 一般在 0x90000,code32_start一般在 0x100000总结当 bootloader 完成工作后,我们的 kernel setup代码(realmode)就被加载进入了内存中了然后 bootloader 会将执行权限交给 kernel (也就是 arch/x86/boot/header.S _start 函数开始执行)vmlinuz是压缩后的kernel,于是可以将它分为两部分setup.bin 程序的入口为__start执行 arch/x86/boot/header.S __start代码在 _start 函数开始之前,还有很多的代码,一般这些代码就是 kenerl 自带的 bootloader (现在已经不再使用,只是会输出一些错误信息)Start 后紧跟着是一个 jmp 指令(0xeb) 这里相当于是跳转到 start_of_setup这里算得上 Linux 真正执行的第一个代码主要做的事情就是:初始化段寄存器、设置堆栈、代码段规范化、验证设置签名、清空BSS段然后就是跳转到 main 函数(arch/x86/boot/main.c)在初始化后各个段寄存器的值应该是start_of_setup 设置了一些寄存器初始化后,就跳转到了 main 函数 (arch/x86/boot/main.c)主要是完成一些初始化操作后,进入 Protect Mode在正式切换到 protected mode 之前的内存准备工作,主要是将内核的 struct setup_header hdr 信息拷贝到 boot_params 中 hdr 去引导加载程序(如GRUB)与Linux内核之间传递启动参数的关键数据结构。boot_params 的三次传递结合参数protected_mode_jump(boot_params.hdr.code32_start,(u32)&boot_params + (ds() << 4)); 一起分析传参规范(传统寄存器传参):参数1 → %eax参数2 → %edx 参数3 → %ebx参数4 → %ecx参数5 → %esi参数6 → %ediPage Map Level 4Page Directory Pointer Table核心功能主要是完成了内核的解压,随后跳转到解压后的内核从 secondary_startup_64 开始执行回到最初我们的问题.那么就得探究一下realmode trampoline在内存中的加载在 setup_arch(command_line) 阶段为我们的切换程序留内存运行空间为后续启动过程中的代码预留好空间在系统内存低端(1MB以下)为实模式蹦床代码预留内存
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-289014.htm
[原创]Linux启动流程初探(V:6.12.32)
225 浏览
2 回复
大佬 带带俺
牛!!!,大佬再搞个Windows 的