错误13:使用字符串文字在grub中启动简单内核时,可执行文件无效或不受支持

我写了一个简单的内核,试图将两个字符写入帧缓冲区。

如果我在内核中定义一个字符串文字,我在启动时会得到以下输出:

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, , , shtab=0x102168, entry=0x1001f0] 

装载机

我在汇编中编写了加载程序:

 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 } 

外部标题

有两个外部标头。 一个用于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) <>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。 我正在使用gcc交叉编译器为i386-elf我使用本指南编译( http://wiki.osdev.org/GCC_Cross-Compiler )。

 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,因此多引导头部位于所需部分。 如果你有一个字符串文字,你会得到一个新的.rodata部分,而且,由于另一个不明原因,它在你的.text:之前排序.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:之后删除多余的冒号。