编译+;将自定义操作系统与Cygwin gcc链接:无法识别的仿真模式:elf_i386

编译+;将自定义操作系统与Cygwin gcc链接:无法识别的仿真模式:elf_i386,gcc,assembly,x86,cygwin,osdev,Gcc,Assembly,X86,Cygwin,Osdev,我有一个用汇编编写的引导加载程序(boot.s)和一个用c编写的内核(kernel.c) 我还有一些其他文件,如:linker.ld和grub.cfg,但我不知道如何使用它们 我的问题是: 如果我跑步: gcc -g -m32 -c -ffreestanding -o kernel.o kernel.c -lgcc ld -melf_i386 -Tlinker.ld -nostdlib --nmagic -o kernel.elf kernel.o objcopy -O binary kerne

我有一个用汇编编写的引导加载程序(
boot.s
)和一个用c编写的内核(
kernel.c

我还有一些其他文件,如:
linker.ld
grub.cfg
,但我不知道如何使用它们

我的问题是: 如果我跑步:

gcc -g -m32 -c -ffreestanding -o kernel.o kernel.c -lgcc
ld -melf_i386 -Tlinker.ld -nostdlib --nmagic -o kernel.elf kernel.o
objcopy -O binary kernel.elf kernel.bin
我得到错误:
ld:无法识别的仿真模式:elf_i386

注:Im使用Windows 10 Pro 32位,并且还安装了VirtualBox(如果这有帮助的话),用于使用cygwin的gcc Im

kernel.c 使用交叉编译器工具链 我强烈建议您构建一个生成ELF对象的C交叉编译器和工具链。这使您脱离了主机编译器和链接器的细微差别。GCC和LD中的默认Cygwin与通用ELF编译器和链接器有许多不同之处。其中包括为Cygwin构建交叉编译器的信息。我个人还没有在Cygwin上构建交叉编译器,因此无法说明这些指令是否适用于该环境

Cygwin生成Windows PE32(32位)和PE32+(64位)对象。这就是为什么
-melf_i386
不起作用的原因。构建ELF交叉编译器将允许您使用
-melf_i386
。在本例中,您需要一个ELF交叉编译器,因为多引导加载程序需要一个Cywgin的GCC和LD无法生成的ELF可执行文件

如果您使用的是64位Windows 10,那么您就可以在Windows Linux子系统(WSL)下实现这一点,因为Ubuntu的GCC和LD将默认生成ELF可执行文件


如果您不想使用交叉编译器 尽管推动您使用交叉编译器是正确的方法,但是有一种方法可以使它与Cygwin一起工作

Cygwin GCC(与其他32位Windows编译器一样)将为具有全局可见范围的非静态函数预先添加一个
。这意味着您的
kmain
实际上是
\u kmain
。修改
boot.s
以执行
call\u-kmain
而不是
call-kmain
。这适用于从程序集调用的任何C函数。要在C代码中访问的程序集文件中提供的任何函数也必须添加一个
\uu
下划线

Windows程序的一大区别是节名可能有点不同rodata可以是
.rdata*
。可能有许多以
rdata
开头的部分。您必须在链接器脚本中对此进行说明:

ENTRY(_start)

SECTIONS
{
    . = 1M;

    .text BLOCK(4K) : ALIGN(4K)
    {
        *(.multiboot)
        *(.text*)
    }

    .rodata BLOCK(4K) : ALIGN(4K)
    {
        *(.rodata)
        *(.rdata*)    /* IMPORTANT - Windows uses rdata */
    }

    .data BLOCK(4K) : ALIGN(4K)
    {
        *(.data)
    }

    .bss BLOCK(4K) : ALIGN(4K)
    {
        *(COMMON)
        *(.bss)
        *(.bootstrap_stack)
    }
}
如果您没有正确处理
rdata
部分,它们可能会放在您的多引导头之前,这可能会导致GRUB等多引导加载程序看不到它,因此这会产生很大的不同。因此,这一变化非常重要

您用于构建可由多引导兼容引导加载程序(或QEMU的
-kernel
选项)使用的文件的命令不正确。由于LD无法输出ELF文件,因此需要使用OBJCOPY将PE32可执行文件转换为32位ELF可执行文件。您的OBJCOPY命令做了错误的事情。您可以将其转换为二进制文件。不幸的是,多重引导头的编写方式不起作用

用于汇编和链接代码以及生成可由多引导加载程序使用的最终
kernel.elf
文件的命令如下所示:

gcc -g -m32 -c -ffreestanding -o kernel.o kernel.c
gcc -g -m32 -c -ffreestanding -o boot.o boot.s
ld -mi386pe -Tlinker.ld -nostdlib --nmagic -o kernel.pe kernel.o boot.o
objcopy -O elf32-i386 kernel.pe kernel.elf

使用Grub和内核制作可引导的ISO/CD 这个过程有点棘手,因为Cygwin没有grub遗留包。要制作带有Grub的可引导ISO/CD映像,需要获取文件
stage2\u eltorito
。您可以从下载一份副本

您必须运行Cygwin安装程序并安装软件包
genisoimage

在前面的部分中,我们构建了一个名为
kernel.elf
的文件。现在我们有了构建ISO/CD映像所需的组件

从构建
kernel.elf
的目录中,我们需要创建一系列子目录。这可以通过以下方式实现:

mkdir -p iso/boot/grub
您需要将
stage2_eltorito
文件复制到
iso/boot/grub
目录中。您还需要在
iso/boot/grub
中创建文件
menu.lst

iso/boot/grub/menu.lst

default 0
timeout 0

title MyOS
# kernel ELF file.
kernel /boot/kernel.elf
上述过程只需执行一次。这足以用Grub和我们的内核创建一个基本的可引导ISO

现在,构建内核、将文件复制到
iso
目录并生成iso/CD映像的过程可以如下完成:

gcc -g -m32 -c -ffreestanding -o kernel.o kernel.c
gcc -g -m32 -c -ffreestanding -o boot.o boot.s
ld -mi386pe -Tlinker.ld -nostdlib --nmagic -o kernel.pe kernel.o boot.o
objcopy -O elf32-i386 kernel.pe kernel.elf
cp kernel.elf iso/boot
genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot \
    -boot-load-size 4 -boot-info-table -o myos.iso iso
genisoimage
命令创建一个名为
myos.ISO
的ISO/CD。您可以通过将
genisoimage
命令行上的
myos.iso
替换为您喜欢的名称来更改名称

myos.iso
应该可以作为一个简单的CD映像从大多数硬件、虚拟机和模拟器启动。当与内核一起运行时,它应该显示如下内容:

上图是我在QEMU中用命令引导ISO/CD时看到的:

qemu-system-i386 -cdrom myos.iso

如果您也在VirtualBox中运行它,您应该会看到类似的情况。

什么体系结构?x86?手臂?如果是x86,它是使用传统BIOS还是32位或64位EFI?我不知道,请稍等,我将发布我的代码…相关:以及用于制作链接C和asm@PeterCordes:咯咯地笑,我使用Windows,但不是用于操作系统开发(当然,如果您在Linux的Windows子系统中这样做也没关系),但确实可以在Cygwin中实现,但它与直接支持ELF的常规编译器/链接器不同。我只知道如何在Cygwin上这样做,因为我必须帮助别人。至少这是使用通用elf交叉编译器更可取的另一个原因。您可以避开宿主环境的细微差别。Cygwin的32位LD仅支持i386pe(win32 PE格式)。Cygwin上的Objcopy支持将i386pe转换为
default 0
timeout 0

title MyOS
# kernel ELF file.
kernel /boot/kernel.elf
gcc -g -m32 -c -ffreestanding -o kernel.o kernel.c
gcc -g -m32 -c -ffreestanding -o boot.o boot.s
ld -mi386pe -Tlinker.ld -nostdlib --nmagic -o kernel.pe kernel.o boot.o
objcopy -O elf32-i386 kernel.pe kernel.elf
cp kernel.elf iso/boot
genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot \
    -boot-load-size 4 -boot-info-table -o myos.iso iso
qemu-system-i386 -cdrom myos.iso