簡體   English   中英

Grub錯誤28:當寫高半內核時,所選項目無法適應內存

[英]Grub error 28: selected item cannot fit into memory when writing higher half kernel

我正在嘗試為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=部分!

無論如何,我看了提供一個init區域:

[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,所以這應該加載......但我仍然抱怨所選項目不適合內存。

在grub 0.97源代碼中 ,此錯誤消息來自stage2 / common.c:89

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

此代碼使用了十幾次:

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;

由於您在引導期間有此消息,因此我們將深入了解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;

既然你沒有grub_printf消息,也許你處於這種情況:

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

=>也許你的問題與你的3 GB目標有關。 您可以嘗試使用1GB的內存。 計算機科學已經看到很多文件或mem> 2GB和32位CPU的問題。

你發現它的另一個機會在於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;

您在0x00100000處的起始地址可能會觸發此情況。

經過幾個深夜,很多“這首先是一個愚蠢的想法!” 一般的沮喪,我弄清楚是什么導致了這個問題。

問題是使用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-

是的,你明白了! 這會找到條目並加載就好了。

那么FILEHDRPHDRS是什么意思呢? 根據鏈接器腳本文檔

您可以在程序頭類型之后使用FILEHDR和PHDRS關鍵字來進一步描述段的內容。 FILEHDR關鍵字表示該段應包含ELF文件頭。 PHDRS關鍵字表示該段應包含ELF程序頭本身。

我認為鏈接器在這里有點花哨,並且在物理內存中對齊所需的代碼,但是更早地啟動該段以適應我無意中要求它包含的各種頭文件,而不是意識到它做了什么。 這使程序“不適合”,雖然我認為grub錯誤消息可能是錯誤的。 您可以通過將程序頭表與objdump -x的節表輸出進行比較來查看此操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM