Skip to content

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

@kcbanner

Description

@kcbanner

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behavioros-windowsMicrosoft Windows

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions