自制内核链接器全局变量和内联字符串无法访问

2024-02-15

我遵循了网上的一些教程并创建了自己的内核。它在带有 QEMU 的 GRUB 上成功启动。但我遇到了中描述的问题这个问题 https://stackoverflow.com/questions/11305075/simple-c-kernel-char-pointers-arent-working,我无法解决它。我可以描述该解决方法,但我还需要使用全局变量,这将使工作变得更容易,但我不明白应该在链接器中更改什么才能正确使用全局变量和内联字符串。

main.c

struct grub_signature {
    unsigned int magic;
    unsigned int flags;
    unsigned int checksum;
};

#define GRUB_MAGIC 0x1BADB002
#define GRUB_FLAGS 0x0
#define GRUB_CHECKSUM (-1 * (GRUB_MAGIC + GRUB_FLAGS))

struct grub_signature gs __attribute__ ((section (".grub_sig"))) =
    { GRUB_MAGIC, GRUB_FLAGS, GRUB_CHECKSUM };


void putc(unsigned int pos, char c){
    char* video = (char*)0xB8000;
    video[2 * pos ] = c;
    video[2 * pos + 1] = 0x3F;
}

void puts(char* str){
    int i = 0;
    while(*str){        
        putc(i++, *(str++));
    }
}

void main (void)
{
    char txt[] = "MyOS";
    puts("where is this text"); // does not work, puts(txt) works.
    while(1){};
}

生成文件:

CC = gcc
LD = ld

CFLAGS = -Wall -nostdlib -ffreestanding -m32 -g
LDFLAGS = -T linker.ld -nostdlib -n -melf_i386

SRC = main.c
OBJ = ${SRC:.c=.o}

all: kernel

.c.o:
    @echo CC $<
    @${CC} -c ${CFLAGS} $<

kernel: ${OBJ} linker.ld
    @echo CC -c -o $@
    @${LD} ${LDFLAGS} -o kernel ${OBJ}

clean:
    @echo cleaning
    @rm -f ${OBJ} kernel

.PHONY: all

链接器.ld

OUTPUT_FORMAT("elf32-i386")
ENTRY(main)
SECTIONS
{
    .grub_sig 0xC0100000 : AT(0x100000)
    {
        *(.grub_sig)
    }
    .text :
    {
        *(.text)
    }
    .data :
    {
        *(.data)void main (void)
    }
    .bss :
    {
        *(.bss)
    }
    /DISCARD/ :
    {
        *(.comment)
        *(.eh_frame)
    }
}

什么有效:

void main (void)
{
char txt[] = "MyOS";
puts(txt);
while(1) {}
}

什么不起作用:

1)

char txt[] = "MyOS";
void main (void)
{
    puts(txt);
    while(1) {}
}

2)

void main (void)
{
    puts("MyOS");
    while(1) {}
}

汇编输出:(外部链接,因为有点长)http://hastebin.com/gidebefuga.pl http://hastebin.com/gidebefuga.pl


如果你看objdump -h输出,您将看到虚拟地址和线性地址与任何部分都不匹配。如果你看objdump -d输出,您会看到地址都在 0xC0100000 范围内。

但是,您没有在地址信息中提供任何地址信息。多重引导头结构 http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#OS-image-format;您只需提供最少三个字段。相反,引导加载程序将选择一个好的地址(x86 上的 1M,即 0x00100000,对于虚拟地址和线性地址),并在那里加载代码。

人们可能会认为这种差异应该导致内核根本无法运行,但碰巧的是上面生成的代码main.c除了只读常量之外,不将地址用于任何其他用途。特别是,GCC 生成使用相对地址(相对于 x86 上下一条指令的地址的有符号偏移量)的跳转和调用,因此代码仍然可以运行。

有两种解决方案,第一种很简单。

x86 上的大多数引导加载程序都会在允许的最小虚拟和线性地址 1M (= 0x00100000 = 1048576) 处加载映像。因此,如果您告诉链接器脚本使用从 0x00100000 开始的虚拟地址和线性地址,即

  .grub_sig 0x00100000 : AT(0x100000)
  {
      *(.grub_sig)
  }

你的内核将会正常工作。我已经验证这可以解决您遇到的问题,删除额外的void main(void)当然,来自您的链接描述文件。具体来说,我构建了一个33MB的虚拟磁盘,其中包含一个ext2分区,在其上安装了grub2(使用1.99-21ubuntu3.10)和上述内核,并在qemu-kvm 1.0(1.0+noroms-0ubuntu14)下成功运行该映像.11)。

第二个选项是设置多重引导标志中的位 16,并提供必要的五个附加字来告诉引导加载程序代码预期驻留在何处。然而,0xC0100000 不会工作——至少 grub2 会崩溃并重新启动——而像 0x00200000 这样的东西却可以正常工作。这是因为多重引导实际上被设计为使用虚拟==线性地址,并且最高地址处可能已经存在其他内容(类似于为什么避免低于1M的地址)。

请注意,引导加载程序不为您提供堆栈,因此代码的工作原理有点令人惊讶。

我个人建议您使用简单的汇编文件来构造签名,并保留一些堆栈空间。例如,start.asm简化自here http://www.cs.vu.nl/~herbertb/misc/writingkernels.txt,

BITS 32
EXTERN main
GLOBAL start

SECTION .grub_sig
signature:
    MAGIC equ 0x1BADB002
    FLAGS equ 0
    dd MAGIC, FLAGS, -(MAGIC+FLAGS)

SECTION .text
start:
    mov esp, _sys_stack     ; End of stack area
    call main
    jmp $                   ; Infinite loop

SECTION .bss
    resb 16384              ; reserve 16384 bytes for stack
_sys_stack:                 ; end of stack

编译使用

nasm -f elf start.asm -o start.o

并修改您的链接器脚本以使用start代替main作为入口点,

ENTRY(start)

从你的系统中删除多重启动的东西main.c,然后编译并链接到kernel使用例如

gcc -Wall -nostdlib -ffreestanding -fno-stack-protector -O3 -fomit-frame-pointer -m32 -c main.c -o main.o
ld -T linker.ld -nostdlib -n -melf_i386 start.o main.o -o kernel

这样您就可以开始开发自己的内核了。

问题?评论?

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

自制内核链接器全局变量和内联字符串无法访问 的相关文章

随机推荐

  • 如何在 Java 中获取声音文件的总时间?

    如何在 Java 中获取声音文件的总时间 UPDATE 看起来这段代码确实有效 长音频文件长度 audioFile length recordedTimeInSec audioFileLength frameSize frameRate 我
  • 当我运行单元测试时,是什么导致卡西尼号加载[重复]

    这个问题在这里已经有答案了 我有一个解决方案的测试项目 其中涉及 MVC Web 应用程序和几个类库 我使用模拟对象和 System Web Abstractions 来避免对 ASP NET 内部对象的依赖 但是当我开始我的测试项目卡西尼
  • 如何为子图设置相同的比例

    我想轻松地在视觉上比较子图 为此 我想为所有子图设置相同的比例 我的代码运行良好 并且我能够绘制子图 但具有自己的比例 我想保持 x 轴上的比例 如果您想要两个具有相同 x 轴的子图 您可以使用sharex keyword https ma
  • 如何在 Google 云平台 (GCP) 中跨服务 (API)、资源类型和项目列出、查找或搜索 iam 政策?

    在 Google Cloud Platform GCP 中 您只能通过调用 getIamPolicy gcloud 中的 get iam policy 来获取特定资源的 IAM 策略 有没有办法跨资源 服务或项目列出 搜索 列出 搜索或查找
  • 在外部 for 中使用 parfor 时出现两循环错误

    我需要并行化代码以节省时间 我有一个循环内部另一个循环 我想并行化外部循环 因为我认为这样代码更快 我的代码如下所示 A rand 1000 1 B rand 1000 1 Biggers zeros 1000 1000 parfor i
  • 拦截所有对 std::streambuf 的写入

    我正在创建一个继承的类std streambuf 异步写入 UART 我需要能够在课堂上讲出std ostream持有streambuf向其中写入字符 这样我就可以启用 写就绪 中断并实际写入数据 我的印象是我只需要覆盖xsputn 但这似
  • 我可以使用 Heroku PG Backup 插件安排备份吗?

    我一直在使用PG 备份插件 https devcenter heroku com articles pgbackups最近一切都工作正常 但是今天早上备份过程在上午 10 00 触发 早上在我的应用程序中生成一些块和超时 有没有办法指定使用
  • 获取 JavaScript 数组中的方法列表

    var c
  • Android SQLite 内存泄漏

    我知道这是一篇很长的文章 请不要介意 Leak found E Database 4549 java lang IllegalStateException mPrograms size 1 E Database 4549 at androi
  • preg_match_all 查找所有 URL 但排除电子邮件

    我搜索了很多 Stackoverflow 正则表达式帖子 但找不到我的答案 我使用以下命令查找给定的所有 URL text string pattern http https ftp ftps a zA Z0 9 a zA Z0 9 2 4
  • Java服务器套接字不重用地址

    我在 Linux 中使用服务器套接字 需要在 time wait TCP 状态到期之前关闭它并重新打开 我在绑定之前设置了服务器套接字的重用地址选项 但它仍然抛出 BindException 我也尝试过这个http meteatamel w
  • 选择 Jquery 日期选择器后重新加载 JQGrid

    我在我的一个 ASP NET 项目中使用 JQGrid 其中网格显示可排序 可过滤的项目列表 一切正常 我遇到的唯一问题是 其中一列是日期字段 因此我使用日期选择器 使用以下选项 来过滤文本框 name Due index Due widt
  • 使用 Ajax 的 Select2 未发送请求

    我正在使用 Select2 版本 4 0 0 并尝试从 PHP 脚本加载远程 JSON 该脚本返回我需要的已格式化数据 问题是黑暗势力正在做一些事情 因为我就是无法发送请求 没有错误 但是没有发送请求 它只是像魔鬼一样安静得让我快哭了 我使
  • linkedin 连接 API 出现错误 401 - iPhone

    您好 我正在使用 linkedin iphone api 通过电子邮件发送连接邀请 请求 XML
  • 在Python中,如何使用urllib查看网站是404还是200?

    如何通过urllib获取header的code getcode 方法 在 python2 6 中添加 返回随响应一起发送的 HTTP 状态代码 如果 URL 不是 HTTP URL 则返回 None gt gt gt a urllib ur
  • SQL 代理作业上次运行状态

    我有 N 个作业 每个作业有 M 个步骤 可以随时更改 添加 删除一些步骤 作业有不同的运行时间表 我想要一个查询来获取每个作业的上次运行状态 作业 ID 作业名称 执行此场景的正确方法是 Job outcome 这是我用来检查作业状态的查
  • Flutter :- 用相机拍照

    我是 Flutter 新手 编写了以下代码来在图像上显示捕获的图像 但是 相机预览不会显示在我的手机上 并且圆形指示器不断旋转 我无法查看相机 import dart io import package camera camera dart
  • ASP.NET 实体框架 NotSupportedException

    我在应用程序的数据层中使用 LINQ to Entities 但在调用 results ToList 时遇到 NotSupportedException 问题 这是导致异常的函数 public List
  • 使用 Facebook Graph API 发布嵌入视频链接

    当使用 Facebook Web 界面手动将视频链接 来自 YouTube Vimeo 等 附加到帖子时 Facebook 会自动将该链接识别为视频 并允许生成的状态消息内嵌播放视频 视频在墙或新闻源中显示为嵌入式播放器 以下是手动发布后嵌
  • 自制内核链接器全局变量和内联字符串无法访问

    我遵循了网上的一些教程并创建了自己的内核 它在带有 QEMU 的 GRUB 上成功启动 但我遇到了中描述的问题这个问题 https stackoverflow com questions 11305075 simple c kernel c