[英]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認為內核可以適應內存? 我努力了:
這些都沒有奏效。 我覺得我錯過了一些非常明顯的東西,但卻無法理解它是什么。
更新:查看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-
是的,你明白了! 這會找到條目並加載就好了。
那么FILEHDR
和PHDRS
是什么意思呢? 根據鏈接器腳本文檔 :
您可以在程序頭類型之后使用FILEHDR和PHDRS關鍵字來進一步描述段的內容。 FILEHDR關鍵字表示該段應包含ELF文件頭。 PHDRS關鍵字表示該段應包含ELF程序頭本身。
我認為鏈接器在這里有點花哨,並且在物理內存中對齊所需的代碼,但是更早地啟動該段以適應我無意中要求它包含的各種頭文件,而不是意識到它做了什么。 這使程序“不適合”,雖然我認為grub錯誤消息可能是錯誤的。 您可以通過將程序頭表與objdump -x
的節表輸出進行比較來查看此操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.