链接时,ld无法找到条目符号main

时间:2017-06-25 07:36:09

标签: gcc ld inline-assembly bootloader osdev

我正在使用 this article 在内联汇编中用C编写一个简单的hello world bootloader。没什么好看的,没有内核加载和其他高级主题。只是一个简单的“你好世界”的消息。

以下是我的文件:

boot.c

ENTRY(main);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00)
    {
        *(.text);
    }
    .sig : AT(0x7DFE)
    {
        SHORT(0xaa55);
    }
}

boot.ld

Click

然后我运行了以下命令:(与第一篇文章不同;改编自another StackOverflow article,因为第一篇文章中的命令对我不起作用)

  

gcc -std = c99 -c -g -Os -march = i686 -m32 -ffreestanding -Wall -Werror boot.c -o boot.o

     

ld -static -T boot.ld -m elf_i386 -nostdlib --nmagic -o boot.elf boot.o

第一行编译成功,但在执行第二行时出错:

  

ld:警告:找不到输入符号main;默认为0000000000007c00

     

boot.o:boot.c :(。text + 0x2):对'main'的未定义引用

     

boot.o:在函数'main'中:

     

C :( ...)/ boot.c:16:未定义引用'__main'

     

C :( ...)/ boot.c:16 :(。text.startup + 0xe):重定位被截断以适合:DISP16对未定义的符号'__main'

怎么了?我使用Windows 10 x64和Dev-C ++附带的gcc编译器。

1 个答案:

答案 0 :(得分:2)

我建议使用i686-elf交叉编译器,而不是使用本机Windows编译器和工具链。我认为您的部分问题是与Windows i386pe格式相关的特殊情况。

.sig部分可能根本没有被写入,因为未知部分可能没有标记可分配数据。结果是签名没有写入最终的二进制文件。虚拟内存地址(VMA)也可能未在boot.ld中设置,因此它可能无法将引导签名提前到512字节扇区的最后2个字节。对于Windows格式,只读数据将放在以.rdata开头的部分中。您需要确保在数据部分之后和引导签名之前包含这些内容。如果不这样做,将默认链接器脚本将未处理的输入节放在引导签名之外的末尾。

假设您已按照关于额外下划线的评论中提到的更改,您的文件可能会以这种方式工作:

boot.ld

ENTRY(__main);

SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00)
    {
        *(.text);
    }
    .data :
    {
        *(.data);
        *(.rdata*);
    }
    .sig 0x7DFE : AT(0x7DFE) SUBALIGN(0)
    {
        SHORT(0xaa55);
    }
}

编译/链接并将.sig部分调整为常规只读分配数据部分的命令如下所示:

gcc.exe -std=c99 -c -g -Os -march=i686 -m32 -ffreestanding -Wall -Werror boot.c -o boot.o
ld.exe -mi386pe -static -T boot.ld -nostdlib --nmagic -o boot.elf boot.o

# This adjusts the .sig section attributes and updates boot.elf 
objcopy --set-section-flags .sig=alloc,contents,load,data,readonly boot.elf boot.elf
# Convert to binary
objcopy -O binary boot.elf boot.bin

其他观察

使用__asm__(".code16\n");不会为引导加载程序生成可用的代码。您将需要使用实验性伪16位代码生成,该代码生成强制汇编程序修改指令以与32位代码兼容,但编码为可在16位实模式下使用。您可以使用每个 C / C ++ 文件顶部的__asm__(".code16gcc\n");来执行此操作。

本教程有一些不好的建议。执行 JMP 到main的全局级基本汇编语句可能会重定位到引导加载程序开头以外的某个位置(某些优化级别可能会导致这种情况)。启动代码未将 ES DS CS 设置为0x0000,也未设置 SS:SP 堆栈段和指针。这可能会导致问题。

如果尝试从真实硬件上的USB驱动器运行,您可能会发现需要引导参数块。我写的Stackoverflow Answer讨论了这个问题以及在真实硬件/ USB /笔记本电脑问题

下的可能解决方法

注意:GCC当前生成的唯一有用代码是32位代码,可以在16位实模式下运行。这意味着您不能指望此代码在386之前的处理器上运行,如80186/80286/8086等。

我的一般建议是不要使用 GCC 创建引导加载程序,除非您知道自己在做什么并了解所涉及的所有细微差别。在汇编中写它可能是一个更好的主意。

如果您想要生成真正的16位代码的 C / C ++ 编译器,您可能希望查看OpenWatcom