Search code examples
gccassemblygnugnu-assembleratt

Including header file in assembly file


I am trying to include a header file containing a macro into my main assembly file, but the compilation fails.

Below is my main.S file

#include "common.h"
BEGIN
    mov $0x0E40, %ax
    int $0x10
    hlt

Below is my common.h file :

.macro BEGIN
    LOCAL after_locals
    .code16
    cli
    ljmp $0, $1f
    1:
    xor %ax, %ax
    /* We must zero %ds for any data access. */
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs
    mov %ax, %bp
    /* Automatically disables interrupts until the end of the next instruction. */
    mov %ax, %ss
    /* We should set SP because BIOS calls may depend on that. TODO confirm. */
    mov %bp, %sp
    /* Store the initial dl to load stage 2 later on. */
    mov %dl, initial_dl
    jmp after_locals
    initial_dl: .byte 0
after_locals:
.endm

Both files are in same directory. When I do the compilation :

$ as --32 -o main.o main.S
main.S: Assembler messages:
main.S:2: Error: no such instruction: `begin'

What am I missing? I did a little research and got this answer in SO, but its not helpful. Please help.


Solution

  • $ as --32 -o main.o main.S

    as is just an assembler, it translates assembly source to object code. It does not run the C preprocessor which is supposed to expand #include.

    (# is the comment character in GAS syntax for x86 so the line is treated as a comment if it's seen by the assembler instead of replaced by CPP)

    What you can do:

    1. Use gcc to assemble, with appropriate file suffix (.S or .sx), it will run the C preprocessor before running the assembler.
      • Add -v to see what commands gcc is invoking.
      • If your source has a different suffix, you can -x assembler-with-cpp source.asm.
      • If you want to see the intermediate result after preprocessing, add -save-temps. This will write a .s file with the preprocessed source.
      • If you want to pass down a command line option to as, you can for example -Wa,--32. However, it is better to use options which the compiler driver understands like -m32 or -m16 in the present case. The driver knows about such options, for example it will also cater for appropriate options when linking, provided you are linking with gcc -m32 ... as noted below.
    2. Use a .include assembler directive which is handled by the assembler itself, not the C preprocessor.

    Note: In case 1. adding include search paths by means of -I path might not work as expected: The compiler driver (gcc in this case) will add -I path only to the assembler's command line if it knows that it's the GNU assembler. You can tell this when the compiler is configured by configure flag --with-gnu-as.

    Note: Similar applies to linking. You probably do not want to call the linker (ld by hand) unless you're making a static executable or flat binary; use gcc or g++ instead if you're making a normal executable to run on the host system. It will add many options needed for linking like multilib paths, search paths, etc. which you do not want to fiddle by hand.

    (int $0x10 is a 16-bit BIOS call, though, which won't work under a modern mainstream OS, only DOS or a legacy BIOS bootloader.)