The early C compilers decorated the name of the functions with an _
to avoid name clashing when linking against the already developed and huge assembly libraries of the times.
Credits for this information go to this excellent old answer.
Today this is not needed anymore but the tradition is still sticking around, mostly for backward compatibility, even though some systems are getting rid of it.
This is not an OS issue, OSes are completely orthogonal to programming languages, name decoration is not something defined by the OS ABI, it is a matter of the compiler/linker designers; though standards have been created to reduce the incompatibilities and an ABI may suggest their use.
In order to fully understand how you can mitigate your problem it is worth noting that while the OS API are language agnostic, a C program rarely invoke them directly, more likely it uses the C run-time.
The C run-time is usually statically linked and it expects names to be decorated according to the scheme of the compiler used to create it.
So if you need to use the C run-time you have to stick with the same name decoration as your system components are using.
This last point rules out the -fno-leading-underscore
option as it will generate a linker error on the relevant platforms.
It is better to work on the assembly files, since you have the freedom to define and imports names exactly as typed. Furthermore usually the assembly code is limited.
If you are using NASM1 there is a nice trick you can use, it's called Macro indirection and it allow you to append a symbol, define at command line, to a name.
Consider:
BITS 32
mov eax, %[p]data
_data db 0
data db 0
If you compile this file twice, the first time as nasm -Dp=_ ...
and the second as nasm -Dp= ...
, by inspecting the immediate value in the generated opcode for mov eax, %[p]data
you can check that in the first case it has been translated as mov eax, _data
and in the second as mov eax, data
.
Assuming you access external symbols by declaring them as EXTERN symn
(precise syntax is irrelevant here), you can define a macro PEXTERN
that works like the directive EXTERN
but import the symbol with or without a leading underscore based on the value of the macro p
(you can change this name) and define an alias for it so that its imported name is the same regardless.
BITS 32
%macro PEXTERN 1
EXTERN %[p]%1
%ifnidn %1, %[p]%1
%define %1 %[p]%1
%endif
%endmacro
PEXTERN foo
PEXTERN bar
mov eax, foo
call bar
Running nasm -Dp= -e ...
and nasm -Dp=_ -e ...
produces the listings
extern foo extern _foo
extern bar extern _bar
mov eax, foo mov eax, _foo
call bar call _bar
You'll need to update the building scripts/Makefiles, off the top of my head you can use two methods:
Detect the OS type and properly define the symbol p
.
With Makefiles this may be easier.
Try compiling a test program.
Write a minimal C program that import/export a function and a minimal assembly file that export/import that function.
Define the symbol as _
and try to assemble + compile (redirecting everything into /dev/null).
If it fails redefine the symbol as empty.
Note that besides names, individual OSes may need specific assembly flags, so a universal building script maybe more involved but not necessarily unmanageable.
You'll end up needing something like Cygwin for Windows.
1 If not, check if you can port the idea into your assembler.