user6416335
user6416335

Reputation:

Include external .asm file into C++ code

It is possible to include source code as follows:

extern "C" 
{
    #include "src.c"
}

#include "src.cpp"

Is it possible to do like this for .asm (assembly) file?

Upvotes: 1

Views: 8959

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 364180

On thing you could do in GNU C is asm(".include \"src.s\"); at global scope to have the assembler do the including using a GAS .include assembler directive.

This would not preprocess the asm source so you might as well just assemble it separately, and (I think) it wouldn't use the usual -I / -i include path.


Other than that, using the C preprocessor:

TL:DR No with most compilers, not unless you munge the asm source file into something that can go inside an asm statement at the global scope. Then sure, but why bother; building the asm separately is generally much better.


GNU C inline-asm syntax allows you to put arbitrary assembly at the global scope to be assembled along with the compiler-generated asm. Use asm("instructions and assembler directives ..."); outside any C / C++ function.

But to make that work when you #include a .S file (GAS syntax asm source), you'd need to get the pre-processed contents of the .S inside double quotes in the C++ source. This is not possible: C/C++, can you #include a file into a string literal?


If you could write something like this

int foo () { return 0; }

asm ("#include \"src.S\" ");   // doesn't work

in a way that would actually include the contents of src.S at that point, it would work. If you literally use this, the compiler's asm output will be:

        .file   "example.cpp"
        .text
.Ltext0:
#APP
        #include "src.S" 
#NO_APP

# omitted some .directives
foo:
    xorl    %eax, %eax
    ret

# is the comment character in GNU as syntax for x86, so #include "src.S" in the input to the assembler is simply treated as a comment!

gcc foo.S runs the C pre-processor on foo.S (to produce foo.s) before feeding it to the assembler. But this doesn't happen on compiler-generated asm output; only the C/C++ source is fed through CPP before being compiled.


You could write an asm source file that wrapped every line in double quotes, exactly like what you'd write inside the body of an asm statement.

For example, bar.inline-asm could be

".p2align 4                     \n\t"
".globl bar                     \n\t"
".type  bar, @function          \n\t"
"bar:                           \n\t"
"     lea  (%rdi,%rsi,2), %eax  \n\t"
"     ret                       \n\t"
".size   bar, .-bar             \n\t"

(Note that this is GNU C Basic ASM, so we don't need to double the % characters.)

With this src.c, we can test it:

// extern "C"   // if this is in C++.
int bar(int a, int b);

asm(
#include "bar.inline-asm"
);


int main(void) {
    return bar(2, 4);
}

I tested this, and it works:

peter@volta:~/src/SO$ gcc -Wall -O2 include-asm.c
peter@volta:~/src/SO$ ./a.out; echo $?
10
# correct result for 2 + 4*2

$ disas a.out     # alias for objdump -drwC -Mintel

...
0000000000000530 <main>:
 530:   be 04 00 00 00          mov    esi,0x4
 535:   bf 02 00 00 00          mov    edi,0x2
 53a:   e9 11 01 00 00          jmp    650 <bar>     # optimized tail-call to bar()
 53f:   90                      nop

...
0000000000000650 <bar>:
 650:   8d 04 77                lea    eax,[rdi+rsi*2]
 653:   c3                      ret    
 654:   66 2e 0f 1f 84 00 00 00 00 00   nop    WORD PTR cs:[rax+rax*1+0x0]
 65e:   66 90                   xchg   ax,ax

Obviously in this case there's zero advantage to doing this vs. simply gcc -O2 foo.c asm.S to build separate object files which are linked together at the end.

It doesn't make name-mangling any easier, i.e. it doesn't simplify the _bar (Windows) vs. bar (Linux) situation. (See Agner Fog's calling conventions PDF, as well as his asm optimization guides if you're using x86 asm for anything.)

Upvotes: 4

Related Questions