编写一个简单的Bootloader HelloWorld - 错误函数打印字符串

2024-02-27

我尝试创建一个简单的引导加载程序来打印“hello world”。

当我调用一个仅打印“hello world”的函数时,我可以做到这一点,但是当我调用一个函数来打印特定字符串时,什么也没有发生。

为此,我使用两个文件。第一个是 boot.ld,第二个是 boot.cpp(它也可以在 C 语言中与 boot.c 一起使用)。

首先,我从终端创建软盘:

dd if=/dev/zero of=floppy.img bs=512 计数=2880

其次,我编译代码(boot.cpp 和 boot.ld):

gcc -c -g -Os -m64 -ffreestand -Wall -Werror boot.cpp -o boot.o

ld -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o

objcopy -O 二进制 boot.elf boot.bin

最后,我将 boot.bin 添加到 floppy.img 中:

dd if=boot.bin of=floppy.img

现在我们只需要从 VirtualBox 的存储面板中添加软盘并启动我们的虚拟机。

源代码

from: http://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part 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 $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'e' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'o' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $' ' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'w' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'o' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'r' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'d' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");
}

Output:

被窃听的源代码

boot.cpp(或boot.c)

void cout(const char* str);

void main()
{
    cout("hello world");
}

void cout(const char* str)
{
    while(*str)
    {
        __asm__ __volatile__ ("int $0x10" : : "a"(0x0e00 | *str), "b"(0x0007));
        ++str;
    }
}

Output:

为什么输出为空?

我的功能出了什么问题?

我忘记了什么?

感谢您的帮助。


在我的交叉编译器上(i686-elf-gcc (GCC) 4.9.2),后面的代码生成以下(反)汇编:

    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    $0x7,%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     $0xe,%ah
  16:   cd 10                   int    $0x10
  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 https://en.wikipedia.org/wiki/Kernel_(operating_system)。在这种(常见)情况下,请阅读毫无疑问的神圣仪式,让您开始进入 OSDev http://wiki.osdev.org/Bare_Bones。事实上,你的第一个例子的工作只是运气,任何最小的改变都可能会破坏一切,甚至导致神秘的可怕的三重错误,内核恐慌本身的噩梦 http://wiki.osdev.org/Triple_Fault.

无论如何,直接写入 VGA DMA 内存比使用 BIOS 调用更好(您需要访问保护模式 http://wiki.osdev.org/Protected_mode首先,并且设置 VGA 硬件和模式 http://wiki.osdev.org/VGA_Hardware(GRUB 会为您执行此操作,但您正在创建引导加载程序,不是吗?)):

void PrintString(const char *str) {
    uint16_t *vga = (uint16_t*)0xB8000;

    for(; *str != '\0'; str++, vga++)
        *vga = ((uint16_t)0x07 << 8) | *str; // Light grey on a black background, nice!
}

BTW,你可能会发现OSDev 社区、wiki 和论坛 http://osdev.org很有用。并且,如评论中所示,您应该使用.code16 for 实模式 http://wiki.osdev.org/Real_mode代码,并且您链接的文章已经显示其年龄。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写一个简单的Bootloader HelloWorld - 错误函数打印字符串 的相关文章

  • 为什么 VB.NET 和 C# 中针对值检查 null 存在差异?

    In VB NET http en wikipedia org wiki Visual Basic NET有时候是这样的 Dim x As System Nullable Of Decimal Nothing Dim y As System
  • 将图像文件从网址复制到本地文件夹?

    我有该图像的网址 例如 http testsite com web abc jpg http testsite com web abc jpg 我想将该 URL 复制到 c images 中的本地文件夹中 而且当我将该文件复制到文件夹中时
  • 获取 std::variant 当前持有的 typeid(如 boost::variant type())

    我已经从 boost variant 迁移到 std variant 但遇到了障碍 我在 boost type 中使用了一个很好的函数 它可以让你获取当前持有的 typeid 看https www boost org doc libs 1
  • 使用API​​隐藏程序标题栏

    它可以使用 c 和 windows api 删除窗口控制台标题栏 如果是的话如何 请 这个简单的应用程序隐藏并显示其所在控制台的标题栏 它会立即将控制台标题更改为 guid 以查找窗口句柄 然后 它使用 ToggleTitleBar 使用找
  • C# 中四舍五入到偶数

    我没有看到 Math Round 的预期结果 return Math Round 99 96535789 2 MidpointRounding ToEven returning 99 97 据我了解 MidpointRounding ToE
  • 静态类变量与外部变量相同,只是具有类作用域吗?

    在我看来 静态类变量与外部变量相同 因为你只需要declare它在static int x extern int x语句 并在其他地方实际定义它 通常在 cpp 文件中 静态类变量 h file class Foo static int x
  • 无法从 Web api POST 读取正文数据

    我正在尝试从新的 Asp Net Web Api 中的请求中提取一些数据 我有一个像这样的处理程序设置 public class MyTestHandler DelegatingHandler protected override Syst
  • 如何使用 wpf webbrowser 将数据发布到 Web 服务器

    我想从数据库获取数据并使用它来让用户登录到网站 我有一个包含 Web 浏览器控件的 wpf 页面 我有这样的代码 用于将用户登录到用 php 编写的网站
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • 我可以仅在少数情况下关闭模拟吗

    我有一个始终使用模拟的应用程序 但是 当用户以管理员身份登录时 一些操作需要他们写入服务器本身 现在 如果这些用户在实际服务器上没有权限 有些用户没有 则不会让他们写入 我想做的是关闭几个命令的模拟 有没有办法做这样的事情 using Ho
  • C# datagridview 列转入数组

    我正在用 C 构建一个程序 并在其中包含一个 datagridview 组件 datagridview 有固定数量的列 2 我想将其保存到两个单独的数组中 但行数确实发生了变化 我怎么能这样做呢 假设一个名为 dataGridView1 的
  • 操纵 setter 以避免 null

    通常我们有 public string code get set 如果最终有人将代码设置为 null 我需要避免空引用异常 我尝试这个想法 有什么帮助吗 public string code get set if code null cod
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • 如何知道寄存器是否是“通用寄存器”?

    我试图了解寄存器必须具备什么标准才能被称为 通用寄存器 我相信通用寄存器是一个可以用于任何用途的寄存器 用于计算 将数据移入 移出等 并且是一个没有特殊用途的寄存器 现在我读到了ESP寄存器是通用寄存器 我猜是ESP寄存器可以用于任何事情
  • .NET JIT 编译的代码缓存在哪里?

    NET 程序首先被编译为 MSIL 代码 当它被执行时 JIT编译器会将其编译为本机机器代码 我想知道 这些JIT编译的机器代码存储在哪里 它只存储在进程的地址空间中吗 但由于程序的第二次启动比第一次快得多 我认为即使在执行完成后 该本机代
  • 从 C 线程调用 Python 代码

    我对从 C 或 C 线程调用 Python 代码时如何确保线程安全感到非常困惑 The Python 文档 http docs python org c api init html non python created threads似乎是
  • TPL 数据流块下游如何获取源生成的数据?

    我正在使用 TPL Dataflow 处理图像 我收到处理请求 从流中读取图像 应用多次转换 然后将生成的图像写入另一个流 Request gt Stream gt Image gt Image gt Stream 为此 我使用块 Buff
  • #pragma pack(16) 和 #pragma pack(8) 的效果总是相同吗?

    我正在尝试使用来对齐数据成员 pragma pack n http msdn microsoft com en us library 2e70t5y1 28v vs 100 29 aspx 以下面为例 include
  • C++ [Windows] 可执行文件所在文件夹的路径[重复]

    这个问题在这里已经有答案了 我需要访问一些文件fstream在我的 Windows 上的 C 应用程序中 这些文件都位于我的exe文件所在文件夹的子文件夹中 获取当前可执行文件的文件夹路径的最简单且更重要的 最安全的方法是什么 Use 获取
  • 新的 .NET 6 控制台模板中的 C# 函数重载不起作用

    我在尝试重载该函数时遇到错误Print object in the 新的 NET 6 C 控制台应用程序模板 https learn microsoft com en us dotnet core tutorials top level t

随机推荐