3

我正在尝试为 i386 32 位编写一个非常基本的内核。我一直在关注 JamesM 的内核教程。我的内核代码本身目前做的很少 - 我正在努力让 grub 正确加载我选择使用的高半格式。

这个答案中描述了这样做的建议,我正在粗略地使用较高的一半裸骨。我的测试环境是 bochs,带有 grub 软盘(0.97/legacy)。

为了让您了解我在做什么,我有一个非常简单的几乎什么都不做的“主要”例程:

[section .text]
align 4

; setting up entry point for linker
start equ (start_vma - 0xBFF00000)

; entry point
start_vma:
    push ebx
    mov  eax, 0xCCCCCCCC
    hlt

这种技术给了我:

bjdump -x kernel | grep "start"
start address 0x00100000
c0000000 g       .text   00000000 start_vma
00100000 g       .text   00000000 start

然后我所做的是使用链接器脚本将 LMA(物理地址加载点)设置为 1MB,但将 VMA 设置为 3GB,如下所示:

OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
KERNEL_MBOOT = 0x400;
KERNEL_VMA        = 0xC0000000;
KERNEL_LMA_OFFSET = 0xBFF00000;

PHDRS
{
    headers PT_PHDR PHDRS ;
    mboot PT_LOAD FILEHDR ;
    text PT_LOAD FILEHDR ;
    data PT_LOAD ;
}

SECTIONS
{
  . = KERNEL_MBOOT;

  .mboot : AT(KERNEL_VMA - KERNEL_LMA_OFFSET)
  {
     *(.mboot)
  } : mboot

  . = KERNEL_VMA;

  .text : AT(ADDR(.text)+ADDR(.mboot) - KERNEL_LMA_OFFSET)
  {
    code = .; _code = .; __code = .;*/
    *(.text)
    *(.rodata*)
  } : text

  .data ALIGN (0x1000) : AT(ADDR(.data) - KERNEL_LMA_OFFSET)
  {
     data = .; _data = .; __data = .;
     *(.data)
     *(.rodata)
  } : data

  .bss : AT(ADDR(.bss) - KERNEL_LMA_OFFSET)
  {
    bss = .; _bss = .; __bss = .;
    *(.bss)
    *(COMMON)
    ebss = .;
  } : data

  end = .; _end = .; __end = .;
}

或者至少我相信。解释。当然,这就是我的对象中的内容:

$ objdump -h kernel.bin
file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .mboot        0000000c  00000400  00100000  00000400  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000002e  c0000000  00100400  00001000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .data         00000001  c0001000  00101000  00002000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  3 .bss          00004000  c0001020  00101020  00002001  2**5
                  ALLOC

.mboot部分仅包含多重引导标头。事实上,运行 mbchk 给出:

kernel: The Multiboot header is found at the offset 1024.
kernel: Page alignment is turned on.
kernel: Memory information is turned on.
kernel: Address fields is turned off.
kernel: All checks passed.

所以总而言之,这看起来像是一个有效的,应该是引导绝对什么都不做的精灵内核。

但是,它实际上并没有启动。相反,grub 会这样抱怨:

错误 28:所选项目无法放入内存

我在 x64 fedora 上使用 gcc 4.7.2 构建,具有:

CFLAGS=-nostdlib -fno-builtin -fno-stack-protector -ffreestanding -m32 
LDFLAGS=-melf_i386 -Tlink.ld

link.ld我上面包含的脚本在哪里。

我的问题是,我在上面做错了什么让 grub 相信内核可以放入内存?我努力了:

  • 更改 VMA_OFFSET 字段以使 VMA 与 LMA 匹配,即以 1MB 物理和虚拟加载所有内容没有区别,即我得到相同的错误。
  • 将 LMA 降至 1MB 以下。导致 grub 错误 7:无法按预期加载低于 1MB。
  • 狂饮茶和咖啡。

这些事情都没有奏效。我觉得我错过了一些非常明显的东西,但无法弄清楚它是什么。


更新:查看 grub multiboot elf 行,这就是我所看到的:

 [Multiboot-elf, <0xffc00:0x40c:0x0>

至关重要的是,我相信我应该看到类似于这个问题中的那条线,即它应该有entry=一部分!

无论如何,我看着提供一个初始化区域:

[section .init]
; setting up entry point for linker

; entry point
start:
    push ebx
    mov  eax, 0xCCCCCCCC
    ; jmp  later
    hlt  

我这样链接:

.init : AT(ADDR(.mboot) + (KERNEL_VMA - KERNEL_LMA_OFFSET))
{
    *(.init) 
} : init

给予:

 Idx Name          Size      VMA       LMA       File off  Algn
 1   .init         0000000c  00100000  00100400  00001000  2**0

其 VMA/LMA 约为 1M,因此应该加载...但我仍然收到有关所选项目不适合内存的投诉。

4

2 回答 2

2

In grub 0.97 source code, this error message came from stage2/common.c:89

[ERR_WONT_FIT] = "Selected item cannot fit into memory",

This code is used a dozen of times:

stage2/char_io.c:1215:    errnum = ERR_WONT_FIT;
stage2/boot.c:276:    errnum = ERR_WONT_FIT;
stage2/boot.c:280:  errnum = ERR_WONT_FIT;
stage2/shared.h:540:  ERR_WONT_FIT,
stage2/builtins.c:1822:     errnum = ERR_WONT_FIT;
stage2/builtins.c:2386:      errnum = ERR_WONT_FIT;
stage2/builtins.c:2498:      errnum = ERR_WONT_FIT;
stage2/builtins.c:2594:   errnum = ERR_WONT_FIT;
stage2/builtins.c:2932:   errnum = ERR_WONT_FIT;
stage2/builtins.c:3711:   errnum = ERR_WONT_FIT;
stage2/builtins.c:3744:   errnum = ERR_WONT_FIT;

Since you have this message during boot, let's take a deeper look at boot.c

 if (! big_linux
      && text_len > linux_data_real_addr - (char *) LINUX_ZIMAGE_ADDR)
    {
      grub_printf (" linux 'zImage' kernel too big, try 'make bzImage'\n");
      errnum = ERR_WONT_FIT;
    }
 else if (linux_data_real_addr + LINUX_SETUP_MOVE_SIZE
               > RAW_ADDR ((char *) (mbi.mem_lower << 10)))
        errnum = ERR_WONT_FIT;

Since you have not the grub_printf message, maybe you are in this conditions:

linux_data_real_addr + LINUX_SETUP_MOVE_SIZE > RAW_ADDR ((char *) (mbi.mem_lower << 10))

=> Maybe your problem is linked to your 3 GB objectives. You may try to make it work with 1GB of mem. Computer science has seen many problems for file or mem > 2GB with 32 Bits CPU.

Your other chance of finding it lies in stage2/char_io.c:1215

  if ((addr < RAW_ADDR (0x1000))
      || (addr < RAW_ADDR (0x100000)
          && RAW_ADDR (mbi.mem_lower * 1024) < (addr + len))
      || (addr >= RAW_ADDR (0x100000)
          && RAW_ADDR (mbi.mem_upper * 1024) < ((addr - 0x100000) + len)))
    errnum = ERR_WONT_FIT;

You start address at 0x00100000 could trigger this condition.

于 2013-06-29T19:37:21.523 回答
1

经过几个深夜,很多“这首先是一个愚蠢的想法!” 和普遍的挫败感,我弄清楚是什么导致了这个问题。

问题在于 的使用PHDRS,您可以通过使用objdump -x kernel并查看第一个表来查看。损坏的可执行文件如下所示:

Program Header:
PHDR off    0x00000034 vaddr 0x00001034 paddr 0x00000000 align 2**2
     filesz 0x00000080 memsz 0x00000080 flags r--
LOAD off    0x00000000 vaddr 0x00000000 paddr 0x000ffc00 align 2**12
     filesz 0x0000040c memsz 0x0000040c flags r--
LOAD off    0x00000000 vaddr 0xbffff000 paddr 0x000ff400 align 2**12
     filesz 0x0000102e memsz 0x0000102e flags r-x
LOAD off    0x00002000 vaddr 0xc0001000 paddr 0x00101000 align 2**12
     filesz 0x00000001 memsz 0x00004020 flags rw-

你能看到这个吗?

 [Multiboot-elf, <0xffc00:0x40c:0x0>

此表中的第一个加载条目是 grub 在此处打印的值。不出所料,paddr 表示物理地址,它低于1MB哪个是错误的(grub 不会加载任何低于1MB、句号、3GB 高半部分的东西)。

这是什么原因造成的?好吧,我改变了我的PHDRS样子:

 PHDRS
 {
-    headers PT_PHDR PHDRS ;
-    mboot PT_LOAD FILEHDR ;
-    text PT_LOAD FILEHDR ;
+    headers PT_PHDR ;
+    mboot PT_LOAD ;
+    text PT_LOAD ;
     data PT_LOAD ;
 }

突然间:

Program Header:
PHDR off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
     filesz 0x00000000 memsz 0x00000000 flags ---
LOAD off    0x00000400 vaddr 0x00000400 paddr 0x00100000 align 2**12
     filesz 0x0000000c memsz 0x0000000c flags r--
LOAD off    0x00001000 vaddr 0xc0000000 paddr 0x00100400 align 2**12
     filesz 0x0000002e memsz 0x0000002e flags r-x
LOAD off    0x00002000 vaddr 0xc0001000 paddr 0x00101000 align 2**12
     filesz 0x00000001 memsz 0x00004020 flags rw-

是的,你明白了!这样就可以找到条目并很好地加载。

FILEHDR那么是什么PHDRS意思呢?根据链接器脚本文档

您可以在程序头类型之后使用 FILEHDR 和 PHDRS 关键字来进一步描述段的内容。FILEHDR 关键字表示该段应包含 ELF 文件头。PHDRS 关键字意味着该段应包括 ELF 程序头本身。

我认为链接器在这里有点花哨,并在物理内存中将代码对齐,但更早地启动了该段以适应我无意中要求它包含的各种标头,但没有意识到它做了什么。这使得程序“不适合”,尽管我认为 grub 错误消息可能是错误的。您可以通过将程序头表与从objdump -x.

于 2013-06-30T11:03:29.673 回答