错误 13:在带有字符串文字的 grub 中引导简单内核时出现无效或不受支持的可执行文件
Error 13: Invalid or unsupported executable while booting simple kernel in grub with string literal
我编写了一个简单的内核,试图将两个字符写入帧缓冲区。
如果我在内核中定义一个字符串字面量,我在启动时会得到以下输出:
Booting 'os'
kernel /boot/kernel.elf
Error 13: Invalid or unsupported executable format
Press any key to continue...
否则,如果我定义两个字符,我会得到以下结果(注意输出开头的 'ab'):
abBooting 'os'
kernel /boot/kernel.elf
[Multiboot-elf, <0x100000:0x201:0x0>, <0x101000:0x0:0x1000>, shtab=0x102168,
entry=0x1001f0]
装载机
我用汇编写了loader:
global loader ; the entry symbol for ELF
MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant
FLAGS equ 0x0 ; multiboot flags
CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum
; (magic number + checksum + flags should equal 0)
KERNEL_STACK_SIZE equ 4096 ; size of stack in bytes
section .text: ; start of the text (code) section
align 4 ; the code must be 4 byte aligned
dd MAGIC_NUMBER ; write the magic number to the machine code,
dd FLAGS ; the flags,
dd CHECKSUM ; and the checksum
loader: ; the loader label (defined as entry point in linker script)
mov eax, 0xCAFEBABE ; place the number 0xCAFEBABE in the register eax
mov esp, kernel_stack + KERNEL_STACK_SIZE ; point esp to the start of the
; stack (end of memory area)
extern run
call run
.loop:
jmp .loop ; loop forever
section .bss
align 4 ; align at 4 bytes
kernel_stack: ; label points to beginning of memory
resb KERNEL_STACK_SIZE ; reserve stack for the kernel
内核是用c写的
#include "io.h"
#include "fb.h"
void run()
{
// try writing message to port
char* c = (char *) 10000;
c[0] = 'a';
c[1] = 'b';
fb_write(c, 2); // this does not cause the error
// fb_write("ab",2); // this line would cause the error
}
外部headers
有两个外部headers。一个用于 IO 端口,称为 io.h,另一个用于写入帧缓冲区,称为 fb.h
这是 io.h 和实现 io.s
io.h:
#ifndef INCLUDE_IO_H
#define INCLUDE_IO_H
/** outb:
* Sends the given data to the given I/O port. Defined in io.s
*
* @param port The I/O port to send the data to
* @param data The data to send to the I/O port
*/
void outb(unsigned short port, unsigned char data);
#endif /* INCLUDE_IO_H */
io.s:
global outb ; make the label outb visible outside this file
; outb - send a byte to an I/O port
; stack: [esp + 8] the data byte
; [esp + 4] the I/O port
; [esp ] return address
outb:
mov al, [esp + 8]
mov dx, [esp + 4]
out dx, al
ret
fb.h
#include "io.h"
// FRAME BUFFER ================================
// Text colors
#define FB_BLACK 0
#define FB_BLUE 1
#define FB_GREEN 2
#define FB_CYAN 3
#define FB_RED 4
#define FB_MAGENTA 5
#define FB_BROWN 6
#define FB_LT_GREY 7
#define FB_DARK_GREY 8
#define FB_LT_BLUE 9
#define FB_LT_GREEN 10
#define FB_LT_CYAN 11
#define FB_LT_RED 12
#define FB_LT_MAGENTA 13
#define FB_LT_BROWN 14
#define FB_WHITE 15
// IO PORTS
#define FB_COMMAND_PORT 0x3D4
#define FB_DATA_PORT 0x3D5
// IO PORT COMMANDS
#define FB_HIGH_BYTE_COMMAND 14 // move cursor command low
#define FB_LOW_BYTE_COMMAND 15 // move cursor command high
/** fb_write_cell:
* used to write a character to a cell in the framebuffer
*
* param i which cell to write to
* param c the ascii char to write
* param fg foreground color
* param bf background color
*/
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg);
/** fb_move_cursor:
* used to move the cursor within the frame buffer
*
* param pos position within frame buffer to move cursor to
*/
void fb_move_cursor(unsigned short pos);
/** fb_write:
* write some text to the cursor
*
* param buf pointer to character string
* param len length of string to write
*/
int fb_write(char *buf, unsigned int len);
fb.c
#include "fb.h"
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg)
{
char *fb = (char *) 0x000B8000;
fb[i*2] = c;
fb[i*2 + 1] = ((fg & 0x0F) << 4) | (bg & 0x0F);
}
void fb_move_cursor(unsigned short pos) {
outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND);
outb(FB_DATA_PORT, ((pos>>8) & 0x00FF));
outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND);
outb(FB_DATA_PORT, pos & 0x00FF);
}
int fb_write(char *buf, unsigned int len) {
unsigned int i = 0;
for(i = 0; i < len; i++) {
fb_write_cell(i, buf[i], FB_BLACK, FB_WHITE);
}
return 0;
}
建设中
我有一个名为 link.ld 的链接描述文件和一个 Makefile。我正在使用我使用本指南 (http://wiki.osdev.org/GCC_Cross-Compiler) 编译的 i386-elf 的 gcc 交叉编译器。
ENTRY(loader) /* the name of the entry label */
SECTIONS {
. = 0x00100000; /* the code should be loaded at 1 MB */
.text ALIGN (0x1000) : /* align at 4 KB */
{
*(.text) /* all text sections from all files */
}
.rodata ALIGN (0x1000) : /* align at 4 KB */
{
*(.rodata*) /* all read-only data sections from all files */
}
.data ALIGN (0x1000) : /* align at 4 KB */
{
*(.data) /* all data sections from all files */
}
.bss ALIGN (0x1000) : /* align at 4 KB */
{
sbss = .;
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
ebss = .;
}
}
这是我的 makefile
OBJECTS = io.o fb.o loader.o kmain.o
#CC = gcc
CC = /home/albertlockett/opt/cross/bin/i386-elf-gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
%.o: %.s
$(AS) $(ASFLAGS) $< -o $@
clean:
rm -rf *.o kernel.elf os.iso
运行它
makefile 从名为 iso 的目录的内容构建一个 iso。该文件夹包含我在此处获得的预配置版本的 grub (https://github.com/littleosbook/littleosbook/blob/master/files/stage2_eltorito) 和 grub
的 menu.lst 文件
menu.lst:
default=0
timeout=0
title os
kernel /boot/kernel.elf
iso 目录的内容:
iso
`-- boot
|-- grub
| |-- menu.lst
| `-- stage2_eltorito
`-- kernel.elf
iso 映像在 bochs 中启动。这是我的 bochsrc.txt 文件
megs: 32
display_library: term
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=1000000
com1: enabled=1, mode=file, dev=com1.out
有谁知道为什么当我尝试启动 iso 时内核文件中的字符串文字会产生错误?
您在 section .text:
末尾有一个额外的冒号,因此创建了一个名为 .text:
的新部分。由于某些我无法通过快速浏览文档发现的模糊原因,即使该部分未在您的链接描述文件中列出,它也会被发送到输出。当你在 C 代码中没有文字数据时,你很幸运它仍然在图像的前 8kiB 之内,因此 multiboot header 在所需的部分。如果你确实有一个字符串文字,你会得到一个新的部分 .rodata
并且,由于另一个模糊的原因,它被排序在你的 .text:
之前但在标准 .text
之后。示例:
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000001 00100000 00100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rodata 00000005 00101000 00101000 00002000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .text: 00000018 00101008 00101008 00002008 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .bss 0000100a 00102000 00102000 00003000 2**2
ALLOC
大家可以看到已经不在图片的前8kiB以内了,grub会很难过的。
TL;DR: 删除 section .text:
.
后多余的冒号
我编写了一个简单的内核,试图将两个字符写入帧缓冲区。
如果我在内核中定义一个字符串字面量,我在启动时会得到以下输出:
Booting 'os'
kernel /boot/kernel.elf
Error 13: Invalid or unsupported executable format
Press any key to continue...
否则,如果我定义两个字符,我会得到以下结果(注意输出开头的 'ab'):
abBooting 'os'
kernel /boot/kernel.elf
[Multiboot-elf, <0x100000:0x201:0x0>, <0x101000:0x0:0x1000>, shtab=0x102168,
entry=0x1001f0]
装载机
我用汇编写了loader:
global loader ; the entry symbol for ELF
MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant
FLAGS equ 0x0 ; multiboot flags
CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum
; (magic number + checksum + flags should equal 0)
KERNEL_STACK_SIZE equ 4096 ; size of stack in bytes
section .text: ; start of the text (code) section
align 4 ; the code must be 4 byte aligned
dd MAGIC_NUMBER ; write the magic number to the machine code,
dd FLAGS ; the flags,
dd CHECKSUM ; and the checksum
loader: ; the loader label (defined as entry point in linker script)
mov eax, 0xCAFEBABE ; place the number 0xCAFEBABE in the register eax
mov esp, kernel_stack + KERNEL_STACK_SIZE ; point esp to the start of the
; stack (end of memory area)
extern run
call run
.loop:
jmp .loop ; loop forever
section .bss
align 4 ; align at 4 bytes
kernel_stack: ; label points to beginning of memory
resb KERNEL_STACK_SIZE ; reserve stack for the kernel
内核是用c写的
#include "io.h"
#include "fb.h"
void run()
{
// try writing message to port
char* c = (char *) 10000;
c[0] = 'a';
c[1] = 'b';
fb_write(c, 2); // this does not cause the error
// fb_write("ab",2); // this line would cause the error
}
外部headers
有两个外部headers。一个用于 IO 端口,称为 io.h,另一个用于写入帧缓冲区,称为 fb.h
这是 io.h 和实现 io.s
io.h:
#ifndef INCLUDE_IO_H
#define INCLUDE_IO_H
/** outb:
* Sends the given data to the given I/O port. Defined in io.s
*
* @param port The I/O port to send the data to
* @param data The data to send to the I/O port
*/
void outb(unsigned short port, unsigned char data);
#endif /* INCLUDE_IO_H */
io.s:
global outb ; make the label outb visible outside this file
; outb - send a byte to an I/O port
; stack: [esp + 8] the data byte
; [esp + 4] the I/O port
; [esp ] return address
outb:
mov al, [esp + 8]
mov dx, [esp + 4]
out dx, al
ret
fb.h
#include "io.h"
// FRAME BUFFER ================================
// Text colors
#define FB_BLACK 0
#define FB_BLUE 1
#define FB_GREEN 2
#define FB_CYAN 3
#define FB_RED 4
#define FB_MAGENTA 5
#define FB_BROWN 6
#define FB_LT_GREY 7
#define FB_DARK_GREY 8
#define FB_LT_BLUE 9
#define FB_LT_GREEN 10
#define FB_LT_CYAN 11
#define FB_LT_RED 12
#define FB_LT_MAGENTA 13
#define FB_LT_BROWN 14
#define FB_WHITE 15
// IO PORTS
#define FB_COMMAND_PORT 0x3D4
#define FB_DATA_PORT 0x3D5
// IO PORT COMMANDS
#define FB_HIGH_BYTE_COMMAND 14 // move cursor command low
#define FB_LOW_BYTE_COMMAND 15 // move cursor command high
/** fb_write_cell:
* used to write a character to a cell in the framebuffer
*
* param i which cell to write to
* param c the ascii char to write
* param fg foreground color
* param bf background color
*/
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg);
/** fb_move_cursor:
* used to move the cursor within the frame buffer
*
* param pos position within frame buffer to move cursor to
*/
void fb_move_cursor(unsigned short pos);
/** fb_write:
* write some text to the cursor
*
* param buf pointer to character string
* param len length of string to write
*/
int fb_write(char *buf, unsigned int len);
fb.c
#include "fb.h"
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg)
{
char *fb = (char *) 0x000B8000;
fb[i*2] = c;
fb[i*2 + 1] = ((fg & 0x0F) << 4) | (bg & 0x0F);
}
void fb_move_cursor(unsigned short pos) {
outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND);
outb(FB_DATA_PORT, ((pos>>8) & 0x00FF));
outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND);
outb(FB_DATA_PORT, pos & 0x00FF);
}
int fb_write(char *buf, unsigned int len) {
unsigned int i = 0;
for(i = 0; i < len; i++) {
fb_write_cell(i, buf[i], FB_BLACK, FB_WHITE);
}
return 0;
}
建设中
我有一个名为 link.ld 的链接描述文件和一个 Makefile。我正在使用我使用本指南 (http://wiki.osdev.org/GCC_Cross-Compiler) 编译的 i386-elf 的 gcc 交叉编译器。
ENTRY(loader) /* the name of the entry label */
SECTIONS {
. = 0x00100000; /* the code should be loaded at 1 MB */
.text ALIGN (0x1000) : /* align at 4 KB */
{
*(.text) /* all text sections from all files */
}
.rodata ALIGN (0x1000) : /* align at 4 KB */
{
*(.rodata*) /* all read-only data sections from all files */
}
.data ALIGN (0x1000) : /* align at 4 KB */
{
*(.data) /* all data sections from all files */
}
.bss ALIGN (0x1000) : /* align at 4 KB */
{
sbss = .;
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
ebss = .;
}
}
这是我的 makefile
OBJECTS = io.o fb.o loader.o kmain.o
#CC = gcc
CC = /home/albertlockett/opt/cross/bin/i386-elf-gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
%.o: %.s
$(AS) $(ASFLAGS) $< -o $@
clean:
rm -rf *.o kernel.elf os.iso
运行它
makefile 从名为 iso 的目录的内容构建一个 iso。该文件夹包含我在此处获得的预配置版本的 grub (https://github.com/littleosbook/littleosbook/blob/master/files/stage2_eltorito) 和 grub
的 menu.lst 文件menu.lst:
default=0
timeout=0
title os
kernel /boot/kernel.elf
iso 目录的内容:
iso
`-- boot
|-- grub
| |-- menu.lst
| `-- stage2_eltorito
`-- kernel.elf
iso 映像在 bochs 中启动。这是我的 bochsrc.txt 文件
megs: 32
display_library: term
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=1000000
com1: enabled=1, mode=file, dev=com1.out
有谁知道为什么当我尝试启动 iso 时内核文件中的字符串文字会产生错误?
您在 section .text:
末尾有一个额外的冒号,因此创建了一个名为 .text:
的新部分。由于某些我无法通过快速浏览文档发现的模糊原因,即使该部分未在您的链接描述文件中列出,它也会被发送到输出。当你在 C 代码中没有文字数据时,你很幸运它仍然在图像的前 8kiB 之内,因此 multiboot header 在所需的部分。如果你确实有一个字符串文字,你会得到一个新的部分 .rodata
并且,由于另一个模糊的原因,它被排序在你的 .text:
之前但在标准 .text
之后。示例:
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000001 00100000 00100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rodata 00000005 00101000 00101000 00002000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .text: 00000018 00101008 00101008 00002008 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .bss 0000100a 00102000 00102000 00003000 2**2
ALLOC
大家可以看到已经不在图片的前8kiB以内了,grub会很难过的。
TL;DR: 删除 section .text:
.