ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.46k stars 2.52k forks source link

Data symbols marked with `__declspec(dllexport)` do not link correctly to `extern var` when using the MSVC ABI #21749

Open kcbanner opened 5 hours ago

kcbanner commented 5 hours ago

Zig Version

0.14.0-dev.1951+857383689

Steps to Reproduce and Observed Behavior

extern fn can succesfully link with function symbols marked with __declspec(dllexport), it matches the behaviour as if __declspec(dllimport) had been used. This works on both -gnu and -msvc.

However, only with the -msvc ABI, extern var cannot link with data symbols marked with __declspec(dllexport).

Given this source and build script:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const foo_shared = b.addSharedLibrary(.{
        .name = "foo_shared",
        .target = target,
        .optimize = optimize,
    });

    foo_shared.defineCMacro("FOO_API", "__declspec(dllexport)");
    foo_shared.addCSourceFile(.{ .file = b.path("src/foo.c"), .flags = &.{} });
    foo_shared.linkLibC();
    b.installArtifact(foo_shared);

    const foo_cpp_shared = b.addSharedLibrary(.{
        .name = "foo_cpp_shared",
        .target = target,
        .optimize = optimize,
    });

    foo_cpp_shared.defineCMacro("FOO_API", "__declspec(dllexport)");
    foo_cpp_shared.addCSourceFile(.{ .file = b.path("src/foo.cpp"), .flags = &.{} });
    if (target.result.abi == .msvc)
        foo_cpp_shared.linkLibC()
    else
        foo_cpp_shared.linkLibCpp();
    b.installArtifact(foo_cpp_shared);

    const exe = b.addExecutable(.{
        .name = "dllimport",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    exe.linkLibrary(foo_shared);
    exe.linkLibrary(foo_cpp_shared);
    b.installArtifact(exe);
}

foo.h:

#ifndef FOO_API
#define FOO_API
#endif

FOO_API int foo();
FOO_API int foo_data;
FOO_API void* (*foo_function_pointer)(int bar);

#if _MSC_VER
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    return TRUE;
}
#endif

foo.c

#include "foo.h"

int foo() {
    return 7;
};
int foo_data = 8;
void* (*foo_function_pointer)(int bar) = 0;

foo.hpp:

#ifndef FOO_API
#define FOO_API
#endif

#ifdef __cplusplus
extern "C" {
#endif

FOO_API int foo_cpp();
FOO_API extern int foo_cpp_data;
FOO_API extern void* (*foo_cpp_function_pointer)(int bar);

#if _MSC_VER
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    return TRUE;
}
#endif

#ifdef __cplusplus
} /* extern "C" */
#endif

foo.cpp

#include "foo.hpp"

int foo_cpp() {
    return 9;
};
int foo_cpp_data = 10;
void* (*foo_cpp_function_pointer)(int bar) = nullptr;

Produces this output with -gnu:

> zig build -Dtarget=x86_64-windows-gnu && zig-out\bin\dllimport.exe
foo() says: 7
foo_data is: u32@100000008
foo_function_pointer is: null
foo_cpp() says: 9
foo_cpp_data is: u32@10000000a
foo_cpp_function_pointer is: null

But fails to link with -msvc:

>zig build -Dtarget=x86_64-windows-msvc && zig-out\bin\dllimport.exe
install
└─ install dllimport
   └─ zig build-exe dllimport Debug x86_64-windows-msvc 4 errors
error: lld-link: undefined symbol: foo_data
    note: referenced by C:\cygwin64\home\kcbanner\temp\dllimport\src\main.zig:14
    note:               c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\d0aa5c5dc0e14955cec128eb46501ab6\dllimport.exe.obj:(main.main)
error: lld-link: undefined symbol: foo_function_pointer
    note: referenced by C:\cygwin64\home\kcbanner\temp\dllimport\src\main.zig:15
    note:               c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\d0aa5c5dc0e14955cec128eb46501ab6\dllimport.exe.obj:(main.main)
error: lld-link: undefined symbol: foo_cpp_data
    note: referenced by C:\cygwin64\home\kcbanner\temp\dllimport\src\main.zig:18
    note:               c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\d0aa5c5dc0e14955cec128eb46501ab6\dllimport.exe.obj:(main.main)
error: lld-link: undefined symbol: foo_cpp_function_pointer
    note: referenced by C:\cygwin64\home\kcbanner\temp\dllimport\src\main.zig:19
    note:               c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\d0aa5c5dc0e14955cec128eb46501ab6\dllimport.exe.obj:(main.main)
error: the following command failed with 4 compilation errors:
C:\cygwin64\home\kcbanner\kit\zig\build-stage3-release\bin\zig.exe build-exe c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\89dd1bf28160503a9c8636fb7182d3a2\foo_shared.lib c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\a55b9a2d906f932dd72ab4d979d22921\foo_cpp_shared.lib -ODebug -target x86_64-windows-msvc -mcpu baseline -I C:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\b4fda69f43e846a2309c0948ef9aabbc -I C:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\b4fda69f43e846a2309c0948ef9aabbc -Mroot=C:\cygwin64\home\kcbanner\temp\dllimport\src\main.zig -lc --cache-dir c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache --global-cache-dir e:\dev\zig-cache --name dllimport --zig-lib-dir C:\cygwin64\home\kcbanner\kit\zig\build-stage3-release\lib\zig\ --listen=-
Build Summary: 6/9 steps succeeded; 1 failed

Note that only the data symbols are undefined. The functions link as expected.

Expected Behavior

Expected successful linking and matching output to -gnu.

kcbanner commented 5 hours ago

dumpbin /exports output (-gnu):

Dump of file c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\4c14a708863f64bba4014500ee3c00ae\foo_shared.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  foo
                  foo_data
                  foo_function_pointer

  Summary

          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
           F .idata$6

Dump of file c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\e9f58199a99136ed4cd21b4b31e8cc3f\foo_cpp_shared.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  foo_cpp
                  foo_cpp_data
                  foo_cpp_function_pointer

  Summary

          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
          13 .idata$6

dumpbin /exports output (-msvc):

Dump of file c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\89dd1bf28160503a9c8636fb7182d3a2\foo_shared.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  foo
                  foo_data
                  foo_function_pointer

  Summary

          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
           F .idata$6

Dump of file c:\cygwin64\home\kcbanner\temp\dllimport\.zig-cache\o\a55b9a2d906f932dd72ab4d979d22921\foo_cpp_shared.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  foo_cpp
                  foo_cpp_data
                  foo_cpp_function_pointer

  Summary

          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
          13 .idata$6

The symbols are exported from the respective libs, so this seems to be an issue on the import side.