如何控制grub从磁盘加载到内存中的内容量?

Posted

技术标签:

【中文标题】如何控制grub从磁盘加载到内存中的内容量?【英文标题】:How to control the amount of contents loaded into memory from disk by grub? 【发布时间】:2020-12-08 05:51:56 【问题描述】:

我正在开发一个由 grub 引导的玩具操作系统。

但是,内核映像中的某些部分(尤其是 objcopy 到原始内核映像的部分)在启动时并未加载到物理内存中。 (即跳转到内核精灵定义的ENTRY后)

这里有一些问题。

什么决定了 grub 从磁盘加载到内存中的内容的大小?可以配置吗?

或者大小是固定的,我应该手动从磁盘读取其余部分吗?

################ 更新################

There are 21 section headers, starting at offset 0x279bfc:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        01000000 001000 009a79 00  AX  0   0 16
  [ 2] .rodata           PROGBITS        0100a000 00b000 0012b0 00   A  0   0 4096
  [ 3] .eh_frame         PROGBITS        0100b2b0 00c2b0 0041b0 00   A  0   0  4
  [ 4] .data             PROGBITS        02000000 011000 002400 00  WA  0   0 4096
  [ 5] .bss              NOBITS          02002400 013400 108430 00  WA  0   0 1024
  [ 6] .percpu_data      PROGBITS        0210a880 11b880 002880 00  WA  0   0 4096
  [ 7] .comment          PROGBITS        00000000 11e100 000011 01  MS  0   0  1
  [ 8] .debug_aranges    PROGBITS        00000000 11e118 000620 00      0   0  8
  [ 9] .debug_info       PROGBITS        00000000 11e738 011352 00      0   0  1
  [10] .debug_abbrev     PROGBITS        00000000 12fa8a 004750 00      0   0  1
  [11] .debug_line       PROGBITS        00000000 1341da 00577b 00      0   0  1
  [12] .debug_str        PROGBITS        00000000 139955 00403c 01  MS  0   0  1
  [13] .debug_loc        PROGBITS        00000000 13d991 008966 00      0   0  1
  [14] .debug_ranges     PROGBITS        00000000 1462f7 000840 00      0   0  1
  [15] .part1            PROGBITS        0c000000 147000 096ba8 00   A  0   0  1
  [16] .part2            PROGBITS        0c100000 1de000 096b58 00   A  0   0  1
  [17] .srtos_conf       PROGBITS        0c1a0000 275000 00064f 00   A  0   0  1
  [18] .symtab           SYMTAB          00000000 275650 002780 10     19 157  4
  [19] .strtab           STRTAB          00000000 277dd0 001d64 00      0   0  1
  [20] .shstrtab         STRTAB          00000000 279b34 0000c7 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

Elf file type is EXEC (Executable file)
Entry point 0x1000038
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x01000000 0x01000000 0x0f460 0x0f460 R E 0x1000
  LOAD           0x011000 0x02000000 0x02000000 0x10d100 0x10d100 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata .eh_frame 
   01     .data .bss .percpu_data 

特别是,我想让 GRUB 加载 .part1.part2.srtos_conf 部分。

我猜它们没有加载,因为它们不是程序头的一部分。

如何将这些部分添加为程序标题?

目前,这些都使用objcopy --add-section XXX 合并到内核中,并且部分标志是alloc,readonly,load,contents

我必须使用哪个选项将这些部分添加到程序头?

【问题讨论】:

你在使用多重引导吗?你的内核和 ELF 是可执行的吗? @MichaelPetch 是的,我意识到我可以配置 multiboot2 标头来决定 load_addr、load_end_addr 和 bss_end_addr。但是,如果我想加载超出 bss_end_addr 的非零内容,我该怎么办?手动将内容从磁盘加载到内存是唯一的方法吗? @shpark 如果您使用的是 ELF,则不需要这些 _addr 选项。 GRUB 应该尊重二进制文件中的程序头以获取该信息。您能否详细说明“尤其是原始内核映像的 objcopy ”是什么意思?我也很好奇readelf -lS <yourkernel.elf> 的输出是什么,因为它准确地显示了你指示加载器在内存中放置的内容。 @ChrisSmeele 哦,我不知道_addr 不推荐。我添加了更多细节,包括readelf 输出。感谢您的帮助! 【参考方案1】:

向 ELF 添加新部分的 objcopy 方法似乎太有限了。 https://reverseengineering.stackexchange.com/a/14780 表示不可能 以这种方式添加程序头条目。

我建议使用 objcopy 为 .part1 等创建目标文件, 然后在构建 kernel.elf 时将这些指定为链接器的附加输入。 objcopy 将放置文件 .data 部分中的内容,并将创建符号 _binary_x_start /_binary_x_end / _binary_x_size 为方便起见。

举个例子:

从任何类型的文件中创建一个目标文件(替换您的目标三元组):

i686-elf-objcopy --input binary -B i386 -O elf32-i386 x x.o

将 x.o 添加到链接器输入会扩展 .data 输出部分以包含 文件内容,自然会导致它被加载器加载。

如果需要,您可以使用链接描述文件控制文件的加载位置。 (带有正确参数的 objcopy 也可以为您执行此操作,但链接描述文件 通常是一种更清洁的方法)。

例如,要将文件专门放置在 0x200000,您可以这样做:

/* ... */

.data : 
    *(EXCLUDE_FILE(x.o) .data)


. = 0x0200000;
.foo : 
    x.o (.data)


/* ... */

当然,您也可以重命名目标文件中的部分以避免 EXCLUDE_FILE这里。


您可能想要查看的另一个选项是使用多重引导模块。将文件作为模块使用可能是一种更简单的方法,但您无法控制它们的加载位置/加载方式。 它还让你更多地参与到多重引导中(我喜欢我的引导加载程序简单且可替换 ;-)。

【讨论】:

谢谢!我会采用这种方法。

以上是关于如何控制grub从磁盘加载到内存中的内容量?的主要内容,如果未能解决你的问题,请参考以下文章

VMware虚拟机预留内存分别与HA接入控制磁盘使用容量的关系

CPU,内存,磁盘,指令以及它们之间的关系

Linux中的inode到底是啥

如何扩大电脑磁盘容量

《程序是怎样跑起来的》第五章有感

GRUB 阶段 1.5 的代码驻留在磁盘的啥位置,加载的地址是啥?