cassemblylinkerlow-levelretro-computing

Linker Inserts Unnecessary Opcode Padding


I've recently come across a minor issue when linking multiple object files for a Motorola 68000 based system (SEGA Mega Drive). The problem is, when an input section for one object file ends and the next one begins, the linker fills memory addresses with zeros so that the next object file begins aligned on a four byte boundary. The text below is a memory map output by the linker. As you can see, the .text output section contains three object files. The first two (main.o, swap.o), were written in C compiled and assembled using m68k-elf-gcc. The third one (swap_asm.o) was hand written in 68000 assembly and assembled using the vasm. The function at the beginning of swap.o would normally start at address 0x0000001E. But, the linker is *fill*ing the beginning of the swap.o file with two bytes, specifically 0x0000. So, swap.o starts at 0x00000020. But, swap_asm.o is not getting aligned and begins at a non-four-byte-aligned address, 0x00000036. Is there a way to make the linker not add any padding and just start the swap.o right away? I understand there are a few work arounds like filling the space with a NOP, but I was wondering if there is a way to just not do a *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

Solution

  • So I found my answer. When the assembler detects long (32-bits) data is being dealt with in an assembly file, it automatically aligns the input section along a 4 byte boundary. You can actually override this using SUBALIGN in a linker script. Here's my linker script aligning input sections along a 2 byte boundary.

    
    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
    }
    
    

    New linker map:

    .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