链接器插入不必要的操作码填充

Linker Inserts Unnecessary Opcode Padding

我最近在为基于 Motorola 68000 的系统 (SEGA Mega Drive) 链接多个目标文件时遇到了一个小问题。问题是,当一个目标文件的输入部分结束而下一个目标文件开始时,链接器会用零填充内存地址,以便下一个目标文件开始对齐四字节边界。下面的文本是链接器输出的内存映射。如您所见,.text output 部分包含三个目标文件。前两个(main.o、swap.o)是用 C 编写的,使用 m68k-elf-gcc 编译和汇编。第三个(swap_asm.o)是用68000汇编手写的,使用vasm组装而成。 swap.o 开头的函数通常从地址 0x0000001E 开始。但是,链接器在 swap.o 文件的开头*填充了两个字节,特别是 0x0000。所以,swap.o 从 0x00000020 开始。但是,swap_asm.o 没有对齐并且从非四字节对齐地址 0x00000036 开始。有没有办法让链接器不添加任何填充并立即启动 swap.o ?我知道有一些解决方法,比如用 NOP 填充 space,但我想知道是否有办法不做 *fill*?

.text           0x00000000       0x4c
 main.o(.text)
 .text          0x00000000       0x1e main.o
                0x00000000                main
 swap.o(.text)
 *fill*         0x0000001e        0x2 
 .text          0x00000020       0x16 swap.o
                0x00000020                swap
 swap_asm.o(.text)
 .text          0x00000036       0x16 swap_asm.o
                0x00000036                swap_asm

68000 处理器要求指令对齐(此要求也适用于数据)。尽管有 CPU 要求(不可跳过),链接器还使用脚本,其中的段需要对齐(通常是为了满足此 cpu 要求)

虽然链接器脚本是可以调整的,但更改对齐方式可能会使链接器生成不正确的代码(由于上段所述),但无论如何,您可以尝试测试一下.

Motorola 68000(以及更多 MegaDrive 的 16 位版本)在奇数地址上请求 16 位传输时触发总线错误陷阱。如果是 32 位,也会发生同样的情况(但这种情况也会发生在 68030 上,我认为 68040 已经处理了这使得多个总线访问,就像英特尔处理器一样)

所以我找到了答案。当汇编程序检测到汇编文件中正在处理长(32 位)数据时,它会自动将输入部分沿 4 字节边界对齐。实际上,您可以在链接描述文件中使用 SUBALIGN 覆盖它。这是我的链接描述文件沿 2 字节边界对齐输入部分。


MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x00400000
}

SECTIONS
{
  .text  : SUBALIGN(0x2) {
        *(.header)
        *(.boot)
        obj/main.o(.text)
        *(.text)
        *(.isr)
        *(.vdp)
  } > rom
  .data : { *(.data) } > rom
  .bss : { *(.bss) } > rom
}

新链接映射:

.text           0x00000000       0x4a
 main.o(.text)
 .text          0x00000000       0x1e main.o
                0x00000000                main
 swap.o(.text) 
 .text          0x0000001e       0x14 swap.o
                0x0000001e                swap
 swap_asm.o(.text)
 .text          0x00000034       0x16 swap_asm.o
                0x00000034                swap_asm