Linking windows debug DLL with allocation in debug causes issues

So this is kind of a follow-up to my last issue with making a "minimal" rust library that links against a C/C++ library: https://users.rust-lang.org/t/workspace-link-static-c-c-library-release-vs-debug/ but that specific issue was solved. On to the next issue encountered!

TL;DR; Building in debug fails with an error about debug library linking because C++ is allocating now, building in release works fine.

Repository still the same one as last time, now on a branch for the issue: Github Repository

I changed my C++ file to include this:

#include <string>
size_t print_int_val(int value)
{
#ifdef _DEBUG
    std::string profile = "DEBUG";
#else
    std::string profile = "RELEASE";
#endif
    printf("Profile is: %s\n", profile.c_str());
    auto num_printed = printf("Value is: %d\n", value);
    return num_printed;
}

No changes to Rust. That's the only difference between the branch and master. Release builds and runs. The linker error is as follows:

  = note: liblink_to_c-ee0db79a9f7fbfea.rlib(RustTestStatic.obj) : warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/OPT:REF' specification
          LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library
          liblink_to_c-ee0db79a9f7fbfea.rlib(RustTestStatic.obj) : error LNK2019: unresolved external symbol __imp__invalid_parameter referenced in function "void * __cdecl std::_Allocate_manually_vector_aligned<struct std::_Default_allocate_traits>(unsigned __int64)" (??$_Allocate_manually_vector_aligned@U_Default_allocate_traits@std@@@std@@YAPEAX_K@Z) 
          liblink_to_c-ee0db79a9f7fbfea.rlib(RustTestStatic.obj) : error LNK2019: unresolved external symbol __imp__CrtDbgReport referenced in function "void * __cdecl std::_Allocate_manually_vector_aligned<struct std::_Default_allocate_traits>(unsigned __int64)" (??$_Allocate_manually_vector_aligned@U_Default_allocate_traits@std@@@std@@YAPEAX_K@Z)      
          C:\Users\Kevin\Source\Rust\workspace_test_public\target\debug\deps\console_test.exe : fatal error LNK1120: 2 unresolved externals

My guess so far is that the debug library for the allocator doesn't link nicely like the release one does, but not sure what the right linking arguments are. I also added more allocations (a vector push/popping, etc) to see if Release was just optimizing away the string allocation, but that doesn't seem to be true.

Any ideas here for the right way to link? this PR came up when googling, but even if that's the right solution, I don't know how to make that work with what I'm doing. I also tried to do , modifiers = "-bundle" for debug as well, but that didn't help either.

You need to build your C/C++ code with /MD (or /MT) to use the non-debug msvcrt library, since rust will always link against the non-debug version. See this issue.

1 Like

That's a "bleeping" disaster IMO. On Windows you cannot link a library that links against the debug runtime? So if you want to debug a Rust/C++ application, you're just SOL? How else are you supposed to do serious development of a heterogeneous application? Is this just a big "F U" to Windows developers? Or what? How else am I supposed to read this?

What about the Pull Request linked there? What can be done to take that idea and run with it again?

Yes this is a little strong. But I'm also extremely pissed off. Prove me wrong.

So if you want to debug a Rust/C++ application, you're just SOL?

Well, the other way around (linking a Rust staticlib into C/C++ binary) does allow you to use the debug version of msvcrt. But yeah, ideally Rust would expose this as a target feature, just how it's done for the dynamic vs. static version of msvcrt.

What about the Pull Request linked there?

AFAIK, the author of that PR (and this one in the Rust repo stopped developing for windows, so they closed their PR. From the comment history in the linked issue and PRs it looks to me like this PR could be picked up again - it just needs someone motivated to pursue fixing the issue. I guess there just aren't too many Rust developers working on Windows with MSVC.

Sorry for coming off really strong in that last reply. I appreciate you responding nicely and measured. Thinking on it further, I'm going to try w/o the debug libraries, even if that disables some things, as you can usually rely on the fact that if something's wrong, it's not in the standard library (or if you go through life expecting it's the library and not you, you have other problems). So it's (probably) not crippling, but it's still going to make C/C++ veterans look at Rust askance.

Right now though, this just feels like an XKCD #619 situation, except with Windows. This is literally one of the biggest platforms period and Rust is treating it like a 2nd-class citizen. That's a problem. If nobody on the steering committee (or whatever) recognizes this as a major problem, that doesn't bode well for the future. Yes I see the way the wind is blowing with regards to development (VS Code over VS), but something tells me that even if the dev environment goes away in favor of CMake, the msvc compiler is what will endure (over MinGW or related), and this problem will endure. So this isn't something that can or should be ignored long term.

I wish I was that person in all honesty, but I'm far too inexperienced here to take up such a role. It's a bit of a chicken-and-egg problem, where I need to have Rust "ready" to convince my employer to take it up, which means I would get more regular experience, which would mean maybe I'd be able to do such a role, but I can't get such experience without it being ready. If I had a lot more spare time to just dedicate? Maybe, but that's not where I'm at in life either.

Either way, @jschwe, thanks for contributing here. I'll reply later if/when I find out if I can just reconfigure my debug build to still use the release runtime, and if that works. I'm very much of a "clone it and it'd better work" type of person, so I don't like a repository/project where you have to do workarounds. I want the example I'm creating to "just work".

Thinking on it further, I'm going to try w/o the debug libraries, even if that disables some things,

(I'm not developing for Windows myself, so take everything I say with a grain of salt).
I think in general this may not be as large of an issue as it may seem at first, since usually you would test your C++ libraries with a C++ testing framework first (which supports msvcrtd). I'm not sure if the debug version of msvcrt provides any major benefit for the rust code itself, so if you are reasonably confident in the quality of your C++ library, then I think it may only be a minor inconvenience

If nobody on the steering committee (or whatever) recognizes this as a major problem

There is not really a steering committee in that sense. However, Microsoft is actually a foundation member and does also contribute to the Rust eco-system. If it was a major issue, I would expect some Microsoft engineers to already have driven a fix forward.

Yes I see the way the wind is blowing with regards to development (VS Code over VS), but something tells me that even if the dev environment goes away in favor of CMake

FYI, we are currently talking about the MSVC ABI. Which IDE you use is kind of orthogonal (except that VS probably only supports compiling for the MSVC ABI). CMake by the way also supports:
a) Using Visual Studio as a Code Generator (targetting MSVC ABI)
b) Using Ninja as a Code Generator in combination with clang to target the MSVC ABI (and be MSVC commandline compatible).

Just in case you are interested, I'm maintaining a CMake integration for cargo called Corrosion and I wrote some documentation on this issue and how to solve it in CMake.
Basically in CMake you can do set_target_properties(your_cpp_library_target PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL").

Update: I got it to work without changing to the release library, and without changing Rust either. What I had to do was add "msvcrtd.lib" to the "Additional Dependencies" in the Librarian section of the project, but only in debug mode. This is all pushed up to the repository to be seen there, but here's the section of the project file affected:

    <Lib>
      <AdditionalDependencies>msvcrtd.lib</AdditionalDependencies>
    </Lib>

And this completely fixes it. It's still on the branch cpp_allocation_issue, not master (yet), but I encourage anybody following this to clone, build the projects (debug and release) in VS 2019, and then build the rust workspace in both debug and release. (I'm working on having cargo invoke msbuild in the future, so it'll be clone, "cargo build" and it just work)

When I tried to just change the library to the non-debug version, I got linker errors because the C++ vector is calling the debug version of the default allocator. So I'd need to undef the _DEBUG symbol, and other things to try and get this to work (didn't even try). I had almost resigned myself to release-only C++ modules (which I'm not a fan of doing, I'd prefer to be able to mixed-mode debug), but then remembered that additional library includes exists, and decided "what the heck, I'll add it there."

And that worked. I can even debug! Straight from rust, steps through into the C++ no problem, and debugging there too. I can set a breakpoint and it catches no problem! Some of the variables aren't being picked up right, but that's a debugger problem, not this.

Now what this actually means for the final binary... I think it's statically linking the debug CRT, and dynamically loading the runtime one? I'd really like somebody who's more of an expert in linking (I very much am not) to give an opinion on this, consequences, etc.

2 Likes

Hi, I'm a Windows contributor and reviewer for the Rust std, The lack of support for the debug CRT is definitely an issue. I agree with that 100%. It's something I really want to see improved.

However, it's not without some nuances. For example. did you known distributing the debug CRT is not allowed? Maybe that's not an issue for you but it is something the rust lang project has to be mindful of as normally it's fine to distribute rust debug libraries.

2 Likes

Hey @chrisd - Yes I in fact did know that distributing the debug runtime was against the license agreement. So when shipping, you'd better be in Release mode, but when developing it seems advantageous to be able to link with debug easily. As you can read above, I found a workaround, and I'm not distributing static libraries (using them sure, but they're not in the repository), but after my example is more done, I definitely need to hit some of the docs/tutorials with Pull Requests to let others know how to do it too.

Assuming what I'm doing isn't horrible. As I said above, I'd love for somebody with more linker experience to say why what I chose above (for debug mode only) is bad, and/or what effects it will have.