Need a way to override calling convention-imposed name mangling for exported symbols
Context: https://github.com/dotnet/runtime/blob/492a5201fdb26eae5288fd6653945a689a601f3a/src/mono/mono/mini/main-core.c#L13-L25
What's happening here is that the functions are exported as stdcall on i386-windows but cdecl everywhere else. The tricky part is that even when exported as stdcall, the functions should not be decorated with the usual _ prefix and @<bytes> suffix.
I'm trying to do this in Zig like so:
const std = @import("std");
const builtin = std.builtin;
const cc: builtin.CallingConvention =
if (builtin.os.tag == .windows and builtin.cpu.arch == .i386) .Stdcall else .C;
fn initialize(
exe_path: [*:0]const u8,
domain_friendly_name: [*:0]const u8,
property_count: c_int,
property_keys: [*]const [*:0]const u8,
property_values: [*]const [*:0]const u8,
host_handle: **c_void,
domain_id: *c_uint
) callconv(cc) c_int {
// ...
}
comptime {
@export(initialize, .{ .name = "coreclr_initialize" });
}
Unfortunately, this doesn't get me the result I would have liked:
$ dumpbin /exports bin/Debug/win-x86/coreclr.dll
Microsoft (R) COFF/PE Dumper Version 14.30.30423.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file coreclr.dll
File Type: DLL
Section contains the following exports for coreclr.dll
00000000 characteristics
0 time date stamp
0.00 version
0 ordinal base
6 number of functions
5 number of names
ordinal hint RVA name
1 0 00002710 _DllMainCRTStartup@12 = __DllMainCRTStartup@12
4 3 00002110 coreclr_initialize@28 = _coreclr_initialize@28
Summary
1000 .buildid
2000 .data
7000 .eh_fram
4000 .rdata
3000 .reloc
47000 .text
1000 .tls
I could probably achieve this with some unholy combination of .def files and a much more complicated build process, but I would prefer if Zig just had a way to say "don't mangle this export's name; I know what I'm doing" as I can in C with #pragma comment(linker, ...).
If it's cdecl or stdcall, I believe it's always supposed to be prefixed with an underscore. The underscore serves as a way to identify that the function uses that calling convention which helps prevent programs from calling functions with the wrong calling convention. In what situation would you want to disable this?
EDIT: actually it looks like this underscore prefix is not a universal standard but toolchain/platform specific...so maybe having a way to override this behavior is justified
If it's cdecl or stdcall, I believe it's always supposed to be prefixed with an underscore. The underscore serves as a way to identify that the function uses that calling convention which helps prevent programs from calling functions with the wrong calling convention.
Note that the same could be said of the @<bytes> suffix.
In what situation would you want to disable this?
See the context link provided at the top of the issue. Ultimately, I have no control over this situation. I'm programming for an interface that already exists, which is "use stdcall on i386-windows and cdecl everywhere else, but don't mangle the name for stdcall".
In an ideal world, the interface would just have been cdecl everywhere and I wouldn't have to deal with this nonsense, but alas.
By the way, your cc calling convention is identical to std.os.windows.WINAPI. This is the standard windows calling convention, stdcall for x86 and cdecl for everythig else.
Not saying we shouldn't allow the programmer to disable mangling, but I wonder if we could improve the default mangling by matching what MSVC does in this case? So if we are targeting windows-i386, then I think the MSVC convention is not to mangle stdcall? That may or may not match what other toolchains on windows does. Would like to see if this could be improved and what the full situation is.
The Mono code linked above is (AFAIK) built with MSVC on Windows, and the linker hackery still seems to be necessary. So I think MSVC still mangles the names.
If memory serves, I think the reason you don't see this sort of mangling in official Win32 APIs (e.g. kernel32.dll) is that (presumably) they use .def files to export the functions, which curiously enough suppresses the mangling - and the linker is happy to resolve an stdcall import to either _<name>@<bytes> or simply <name>.
This also makes it hard to make proxy DLLs without going outside of the Zig build system.