Skip to content

Odd detail::buffer<char> related dynamic linking issue on Windows (clang-cl) #4576

@FatihBAKIR

Description

@FatihBAKIR

I'm running into an issue with my clang-cl based DLL build:

LINK: command "lld-link /nologo test/CMakeFiles/printf-test.dir/printf-test.cc.obj /out:bin/printf-test.exe /implib:test/printf-test.lib /pdb:bin/printf-test.pdb /version:0.0 [...] c++.lib ws2_32.lib bcrypt.lib userenv.lib msvcrt.lib msvcprt.lib /machine:x64 /INCREMENTAL:NO /subsystem:console test/test-main.lib fmt.lib test/gtest/gtest.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST:EMBED,ID=1" failed (exit code 1) with the following output:
lld-link: error: undefined symbol: __declspec(dllimport) public: void __cdecl fmt::v12::detail::buffer<char>::try_resize(unsigned __int64)
>>> referenced by test/CMakeFiles/printf-test.dir/printf-test.cc.obj:(int __cdecl fmt::v12::detail::format_float<double>(double, int, struct fmt::v12::format_specs const &, bool, class fmt::v12::detail::buffer<char> &))
>>> referenced by test/CMakeFiles/printf-test.dir/printf-test.cc.obj:(int __cdecl fmt::v12::detail::format_float<double>(double, int, struct fmt::v12::format_specs const &, bool, class fmt::v12::detail::buffer<char> &))
>>> referenced by test/CMakeFiles/printf-test.dir/printf-test.cc.obj:(int __cdecl fmt::v12::detail::format_float<double>(double, int, struct fmt::v12::format_specs const &, bool, class fmt::v12::detail::buffer<char> &))
>>> referenced 9 more times

Which I finally tracked down to this definition in os.h:

class FMT_API ostream : private detail::buffer<char> {

Since ostream has FMT_API in it, MSVC ABI considers the inline functions in detail::buffer<char> to be dllimport when used in printf-test.cc, and even if the definition is available in the header, the compiler expects to find the function to be defined in the dll and just emits a relocation. However fmt.dll does not have this symbol.

Just including fmt/os.h in format.cc fixes the error (which can be behind a #ifdef FMT_OS), or adding an explicit template instantiation:

template FMT_API void buffer<char>::try_resize(size_t);

I see a similar PR was merged previously to make ostream not inherit from detail::buffer<char> and instead use it as a member, which would also solve this problem. However, looks like that change was somehow reverted as of latest master and the 12.0 release has the inheritance again.

I acknowledge it's odd that I'm running into this with clang-cl and the shared builds in CI pass with MSVC. It is possible this is a clang-cl bug, or difference in behavior with MSVC. Even if clang-cl is differing in behavior from MSVC, would we still want to incorporate one of the fixes?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions