编写一个简单的 Bootloader HelloWorld - 错误函数打印字符串
Write a simple Bootloader HelloWorld - Error function print string
我尝试创建一个打印 "hello world".
的简单引导加载程序
当我调用一个只打印 "hello world" 的函数时我可以做到这一点,但是当我调用一个函数来打印特定字符串时,什么也没有发生。
为此,我使用了两个文件。第一个是 boot.ld,第二个是 boot.cpp(它也可以在 C 中使用 boot.c)。
首先,我从我的终端创建软盘:
dd if=/dev/zero of=floppy.img bs=512 count=2880
其次,我编译代码(boot.cpp和boot.ld):
gcc -c -g -Os -m64 -ffreestanding -Wall -Werror boot.cpp -o boot.o
ld -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin
最后,我将 boot.bin 添加到 floppy.img:
dd if=boot.bin of=floppy.img
现在我们只需要从 VirtualBox 的存储面板添加软盘并启动我们的虚拟机。
源码
来自:http://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part
boot.ld
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
boot.cpp(或boot.c)
void cout();
void main()
{
cout();
}
void cout()
{
__asm__ __volatile__("movb $'h' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'e' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'l' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'l' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'o' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $' ' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'w' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'o' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'r' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'l' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'d' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
}
输出:
有漏洞的源代码
boot.cpp(或boot.c)
void cout(const char* str);
void main()
{
cout("hello world");
}
void cout(const char* str)
{
while(*str)
{
__asm__ __volatile__ ("int [=12=]x10" : : "a"(0x0e00 | *str), "b"(0x0007));
++str;
}
}
输出:
为什么输出为空?
我的函数有什么问题?
我忘记了什么?
感谢您的帮助。
在我的交叉编译器 (i686-elf-gcc (GCC) 4.9.2
) 上,后面的代码生成以下 (dis) 程序集:
boot.o: file format elf32-i386
Disassembly of section .text:
00000000 <cout>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 53 push %ebx
4: bb 07 00 00 00 mov [=10=]x7,%ebx
9: 8b 55 08 mov 0x8(%ebp),%edx
c: 0f be 02 movsbl (%edx),%eax
f: 84 c0 test %al,%al
11: 74 08 je 1b <cout+0x1b>
13: 80 cc 0e or [=10=]xe,%ah
16: cd 10 int [=10=]x10
18: 42 inc %edx
19: eb f1 jmp c <cout+0xc>
1b: 5b pop %ebx
1c: 5d pop %ebp
1d: c3 ret
我很想知道您是否将 GCC(非 16 位兼容编译器)与 16 位内容(BIOS 中断)一起使用。如果您要编写 16 位代码,请使用完整的汇编代码! GCC 只会把它搞砸,因为它生成的 32 位代码将 运行 在 16 位模式下 。如果你想直接上C/C++,那么你要写的很可能不是bootloader,而是一个kernel. In such a (common) case, read the unquestionable sacred ritual to initiate you into OSDev. The fact that your first example works is just luck, and any minimal change may break everything, even leading to the mythical horrifying triple fault, nightmares of kernel panics themselves.
无论如何,你最好直接写入 VGA DMA 内存而不是使用 BIOS 调用(你需要进入 protected mode first, and setup the VGA hardware and modes
(GRUB 会为您完成此操作,但您正在创建引导加载程序,不是吗?)):
void PrintString(const char *str) {
uint16_t *vga = (uint16_t*)0xB8000;
for(; *str != '[=11=]'; str++, vga++)
*vga = ((uint16_t)0x07 << 8) | *str; // Light grey on a black background, nice!
}
顺便说一句,您可能会找到 the OSDev community, wiki, andforums very useful. And, as shown in the comments, you should be using .code16
for real mode 代码,并且您链接的文章已经显示了它的年龄。
感谢@MichaelPetch 的回答。
源代码:
boot.ld
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
boot.cpp
也在这里:http://pastebin.com/6NV3UMjE
asm(".code16gcc");
__asm__("jmpl [=11=]x0000, $main\n");
void cout(const char* str);
void main()
{
__asm__ __volatile__ ("xor %ax, %ax\n");
__asm__ __volatile__ ("mov %ax, %ds\n");
cout("Hello World");
__asm__ __volatile__("cli\n");
__asm__ __volatile__("hlt\n");
}
void cout(const char* str)
{
while(*str)
{
__asm__ __volatile__("int [=11=]x10" : : "a"(0x0e00 | *str), "b"(0x0007));
++str;
}
}
编译:
gcc -c -g -O0 -m32 -ffreestanding -Wall -Werror boot.cpp -o boot.o
ld -melf_i386 -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin
dd if=boot.bin of=floppy.img conv=notrunc
输出:
我尝试创建一个打印 "hello world".
的简单引导加载程序当我调用一个只打印 "hello world" 的函数时我可以做到这一点,但是当我调用一个函数来打印特定字符串时,什么也没有发生。
为此,我使用了两个文件。第一个是 boot.ld,第二个是 boot.cpp(它也可以在 C 中使用 boot.c)。
首先,我从我的终端创建软盘:
dd if=/dev/zero of=floppy.img bs=512 count=2880
其次,我编译代码(boot.cpp和boot.ld):
gcc -c -g -Os -m64 -ffreestanding -Wall -Werror boot.cpp -o boot.o
ld -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin
最后,我将 boot.bin 添加到 floppy.img:
dd if=boot.bin of=floppy.img
现在我们只需要从 VirtualBox 的存储面板添加软盘并启动我们的虚拟机。
源码
来自:http://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part
boot.ld
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
boot.cpp(或boot.c)
void cout();
void main()
{
cout();
}
void cout()
{
__asm__ __volatile__("movb $'h' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'e' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'l' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'l' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'o' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $' ' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'w' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'o' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'r' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'l' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
__asm__ __volatile__("movb $'d' , %al\n");
__asm__ __volatile__("movb [=11=]x0e, %ah\n");
__asm__ __volatile__("int [=11=]x10\n");
}
输出:
有漏洞的源代码
boot.cpp(或boot.c)
void cout(const char* str);
void main()
{
cout("hello world");
}
void cout(const char* str)
{
while(*str)
{
__asm__ __volatile__ ("int [=12=]x10" : : "a"(0x0e00 | *str), "b"(0x0007));
++str;
}
}
输出:
为什么输出为空?
我的函数有什么问题?
我忘记了什么?
感谢您的帮助。
在我的交叉编译器 (i686-elf-gcc (GCC) 4.9.2
) 上,后面的代码生成以下 (dis) 程序集:
boot.o: file format elf32-i386
Disassembly of section .text:
00000000 <cout>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 53 push %ebx
4: bb 07 00 00 00 mov [=10=]x7,%ebx
9: 8b 55 08 mov 0x8(%ebp),%edx
c: 0f be 02 movsbl (%edx),%eax
f: 84 c0 test %al,%al
11: 74 08 je 1b <cout+0x1b>
13: 80 cc 0e or [=10=]xe,%ah
16: cd 10 int [=10=]x10
18: 42 inc %edx
19: eb f1 jmp c <cout+0xc>
1b: 5b pop %ebx
1c: 5d pop %ebp
1d: c3 ret
我很想知道您是否将 GCC(非 16 位兼容编译器)与 16 位内容(BIOS 中断)一起使用。如果您要编写 16 位代码,请使用完整的汇编代码! GCC 只会把它搞砸,因为它生成的 32 位代码将 运行 在 16 位模式下 。如果你想直接上C/C++,那么你要写的很可能不是bootloader,而是一个kernel. In such a (common) case, read the unquestionable sacred ritual to initiate you into OSDev. The fact that your first example works is just luck, and any minimal change may break everything, even leading to the mythical horrifying triple fault, nightmares of kernel panics themselves.
无论如何,你最好直接写入 VGA DMA 内存而不是使用 BIOS 调用(你需要进入 protected mode first, and setup the VGA hardware and modes (GRUB 会为您完成此操作,但您正在创建引导加载程序,不是吗?)):
void PrintString(const char *str) {
uint16_t *vga = (uint16_t*)0xB8000;
for(; *str != '[=11=]'; str++, vga++)
*vga = ((uint16_t)0x07 << 8) | *str; // Light grey on a black background, nice!
}
顺便说一句,您可能会找到 the OSDev community, wiki, andforums very useful. And, as shown in the comments, you should be using .code16
for real mode 代码,并且您链接的文章已经显示了它的年龄。
感谢@MichaelPetch 的回答。
源代码:
boot.ld
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
boot.cpp
也在这里:http://pastebin.com/6NV3UMjE
asm(".code16gcc");
__asm__("jmpl [=11=]x0000, $main\n");
void cout(const char* str);
void main()
{
__asm__ __volatile__ ("xor %ax, %ax\n");
__asm__ __volatile__ ("mov %ax, %ds\n");
cout("Hello World");
__asm__ __volatile__("cli\n");
__asm__ __volatile__("hlt\n");
}
void cout(const char* str)
{
while(*str)
{
__asm__ __volatile__("int [=11=]x10" : : "a"(0x0e00 | *str), "b"(0x0007));
++str;
}
}
编译:
gcc -c -g -O0 -m32 -ffreestanding -Wall -Werror boot.cpp -o boot.o
ld -melf_i386 -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin
dd if=boot.bin of=floppy.img conv=notrunc
输出: