How to compile an assembly file to a raw binary (like DOS .com) format with GNU assembler (as)?

Amir Saniyan picture Amir Saniyan · Dec 12, 2011 · Viewed 10k times · Source

I want to compile this source code in Windows (It just an example):

start:
NOP
NOP

When I compile it with NASM or FASM, output file length is 2 bytes. But when I compile it with GNU assembler (as) the output file length is 292 bytes!

How to compile an assembly file to a raw binary (like DOS .com) format with GNU assembler (as)?


Why I do this?

I want to write my own simple OS, I write my codes with C (without using any C standard libraries even stdio.h or math.h) and convert it to assembly:

gcc -S my_os.c -o my_os.asm -masm=intel

Then, I compile assembly file to a raw binary:

as my_os.asm

Then I rename a.out (output of assembler) to my_os.flp and finally start my OS with VMWare :)

Answer

ld --oformat binary

For quick and dirty tests you can do:

as -o a.o a.S
ld --oformat binary -o a.out a.o
hd a.out

Gives:

00000000  90 90                                             |..|
00000002

Unfortunately this gives a warning:

ld: warning: cannot find entry symbol _start; defaulting to 0000000000400000

which does not make much sense with binary. It could be silenced with:

.section .text
.globl start
start:
nop
nop

and:

ld -e start --oformat binary -o a.out a.o

or simply with:

ld -e 0 --oformat binary -o a.out a.o

which tells ld that the entry point is not _start but the code at address 0.

It is a shame that neither as nor ld can take input / ouptut from stdin / stdout, so no piping.

Proper boot sector

If you are going to to something more serious, the best method is to generate a clean minimal linker script. linker.ld:

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        *(.*)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Here we also place the magic bytes with the linker script.

The linker script is important above all to control the output addresses after relocation. Learn more about relocation at: https://stackoverflow.com/a/30507725/895245

Use it as:

as -o a.o a.S
ld --oformat binary -o a.img -T linker.ld a.o

And then you can boot as:

qemu-system-i386 -hda a.img

Working examples on this repository: https://github.com/cirosantilli/x86-bare-metal-examples/blob/d217b180be4220a0b4a453f31275d38e697a99e0/Makefile

Tested on Binutils 2.24, Ubuntu 14.04.