如何告诉 GNU 汇编程序在使用未定义标签时发出警告?

How to tell the GNU assembler to warn when using undefined labels?

有没有办法告诉 GNU 汇编器在编译时警告使用未定义的标签?

假设我打错了字:

jmp MyLabell

MyLabel:

我不使用链接器。我使用 as 生成目标文件,然后我 objcopy.o 文件转换为原始二进制文件。我这样做是因为我打算在只能执行原始二进制文件(实模式引导加载程序)的环境中 运行 它。我以为如果我在汇编源代码时使用了未定义的标签,汇编程序会警告我

它会编译得很好,但由于 MyLabell 从未被定义,它总是会转换为地址 0,让程序员毫无头绪和遗忘。 是否可以告诉 as 不要忽视此类问题? 如果不能,是否有理由不这样做?据我所知(如果我错了请纠正我),NASM 确实关心我只使用 定义的标签。

我使用的 GNU 汇编程序版本:GNU 汇编程序 (GNU Binutils) 2.29.1

我已经花了一整夜调试我的代码,只是为了发现在重命名标签后,我并没有更改对它的所有引用。

GNU汇编程序生成一个目标文件。这样的文件不能直接作为二进制文件使用,必须linked第一,就算没有别的就link吧。 linking 进程解析重定位并修复二进制文件的加载地址,这是汇编程序无法自行完成的两项任务。当在 linking 阶段留下未定义的符号时,linker 抱怨并中止 linking 过程。您可以使用此行为来检查拼写错误的符号,这是 GNU 汇编器本身不提供的东西,因为它假定每个未定义的符号在 linking.

期间由另一个目标文件提供。

这本质上是一个XY problem

为了回答最初的问题,GNU 汇编程序 as 假定它在当前文件中找不到的任何标签都在另一个文件中,该文件将由 linker 在 link 时间。它放置一个虚拟值作为跳转目标,由 linker 解析。


问题归结为这样一个事实,即您从未发现标签是否未定义,因为您没有通过 linker 运行 生成可执行文件。将原始对象文件转换为平面二进制文件可能无法按预期工作。要解决此问题:

  • 使用 GNU assembler 生成目标文件。
  • 使用linker LD设置原点
  • 使用 OBJCOPY 将最终的 linked 可执行文件转换为二进制文件。

如果您正在创建实模式引导加载程序,请不要在 GNU 汇编程序中使用 .org 指令。它不会做你期望的事情。这与 NASM 在直接生成原始二进制文件时使用的 org 指令不同。你可以使用类似的东西:

as --32 boot.s -o boot.o
ld -melf_i386 -nostdlib -Ttext=0x7c00 boot.o -o boot.elf
objcopy -O binary boot.elf boot.bin

使用示例中的 LD 命令,您可以将任意数量的 .o 文件指定到 link 中,如果您愿意,也可以只指定一个目标文件。


作为上一节的附录,我更喜欢将 linker 脚本与 LD 一起使用。对于引导加载程序,我使用 linker 将引导签名放在适当的位置(它可以从您的程序集文件中删除),并将原点设置为 0x7c00。这是一个非常简单的方法,假设您的引导加载程序仅使用 .text.data 甚至 .rodata 部分:

文件link.ld:

OUTPUT_FORMAT("elf32-i386");
ENTRY(start);
SECTIONS
{
    . = 0x7C00;
    .text : {
        *(.text);
    }
    .data : {
        *(.data);
        *(.rodata);
    }

    /* Boot signature */
    .sig : AT(0x7DFE) {
        SHORT(0xaa55);
    }

    /* Discard common unwanted/unneeded sections */
    /DISCARD/ : {
        *(.comment);
        *(.note.gnu.build-id);
    }
}

然后是 assemble 和 link 文件。在这种情况下,我们指定 -Tlink.ld 来使用上面的 linker 脚本,我们不再需要使用 -Ttext=0x7c00:

as --32 boot.s -o boot.o
ld -melf_i386 -nostdlib -Tlink.ld boot.o -o boot.elf
objcopy -O binary boot.elf boot.bin

正确使用 linker 生成可执行文件应该会产生类似这样的错误,如果找不到像 MyLabel1 这样的标签:

boot.o:(.text+0x1): undefined reference to `MyLabell'


原点和实模式代码

GNU 链接器不理解 real mode 20-bit segment:offset addressing。引导加载程序将加载到物理地址 0x07c00,但有不止一种方法可以寻址该位置。在实模式下 segment:offset 寻址段和偏移量结合起来定义物理地址。计算是segment * 16 + offset。您在 linker 脚本或 .Ttext= 选项中选择的原点必须与您加载到段寄存器(尤其是 DS)的段结合为 0x07c00 .如果将段设置为 0x0000,则所需的偏移量为 0x7c00,因为 0x0000 * 16 + 0x7c00 = 0x07c00。使用 0x7c0 的段,您需要 0x0000 的偏移量作为 0x7c0 * 16 + 0x0000 = 0x07c00.

我的 linker 脚本 link.ld 假定您用 0x0000 加载了 DS 段寄存器。您用于段的值是 0x7c0 因此您需要更改 link.ld 以使用 . = 0x0000; 而不是 . = 0x7C00;.

如果您使用 . = 0x0000 作为原点,那么您还需要通过从中减去 0x7c00 来调整引导加载程序的位置。 .sig : AT(0x7DFE) { 行必须更改为 .sig : AT(0x1FE) {。如果您不使用 linker 脚本并在 运行ning LD 时指定原点,那么它将具有将更改为 -Ttext=0x0000.