zig icon indicating copy to clipboard operation
zig copied to clipboard

Need a way to override calling convention-imposed name mangling for exported symbols

Open alexrp opened this issue 4 years ago • 5 comments

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, ...).

alexrp avatar Aug 17 '21 16:08 alexrp

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

marler8997 avatar Aug 17 '21 16:08 marler8997

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.

alexrp avatar Aug 17 '21 17:08 alexrp

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.

marler8997 avatar Aug 17 '21 17:08 marler8997

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>.

alexrp avatar Aug 17 '21 21:08 alexrp

This also makes it hard to make proxy DLLs without going outside of the Zig build system.

trevor403 avatar Mar 06 '23 07:03 trevor403