I have a piece of C code that calls a function defined in assembly. By way of example, let's say foo.c contains:
int bar(int x); /* returns 2x */
int main(int argc, char *argv[]) { return bar(7); }
And bar.s contains the implementation of bar() in x86 assembly:
.global bar
bar: movl 4(%esp), %eax
addl %eax, %eax
ret
On Linux I can easily compile and link these sources with GCC as follows:
% gcc -o test foo.c bar.s
% ./test; echo $?
14
On Windows with MinGW this fails with an error of "undefined reference to `bar'". It turns out the cause for this is that on Windows all identifiers of functions with C calling convention are prefixed with an underscore, but since "bar" is defined in assembly, it doesn't get this prefix and linking fails. (So the error message is actually complaining about missing the symbol _bar, not bar.)
To summarize:
% gcc -c foo.c bar.s
% nm foo.o bar.o
foo.o:
00000000 b .bss
00000000 d .data
00000000 t .text
U ___main
U _bar
00000000 T _main
bar.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 T bar
The question now is: how can I resolve this nicely? If I were writing for Windows only, I could just add the underscore to the identifier in bar.s, but then the code breaks on Linux. I have looked at gcc's -fleading-underscore
and -fno-leading-underscore
options but neither appears to do anything (at least on Windows).
The only alternative I see now is passing the assembly file through the C preprocessor and redefining all the declared symbols manually if WIN32 is defined, but that's not very pretty either.
Does anyone have a clean solution for this? Perhaps a compiler option I oversaw? Maybe the GNU assembler supports a way to specific that this particular symbol refers to a function using C calling convention and should be mangled as such? Any other ideas?
One option, though dangerous, is to convince GCC to omit the ABI-required leading underscore.
Another, safer option, is to explicitly tell GCC the name to use.
In your case,
should tell GCC that "
bar
uses asm name ``bar`', even though it's a ccall function".You can use the C preprocessor to preprocess your assembly and use a macro to add the missing underscores on Windows. First, you need to rename your assembly file from bar.s to bar.S (capital 'S'). This tells gcc to use cpp to preprocess the file.
To add the missing underscores, you can define a macro "cdecl" like this:
Then use it like this:
Note that Mac OSX also requires leading underscores, so you can update the first line of the macro like this:
Compilers for the ELF target do not add leading underscores by default. You could add
-fleading-underscore
when compiling to ELF format (under Linux). Use a conditional in the makefile.Reference: http://opencores.org/openrisc,gnu_toolchain (do an on-page search for "leave global names unchanged")
can you declare it twice?
I haven't written assembly in awhile, but does the .global identifier just act sort of like a label?