Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix linking issue due to missing crt flags on windows msvc targets compiled by clang (not clang-cl). #811

Closed
wants to merge 1 commit into from

Conversation

jschwe
Copy link

@jschwe jschwe commented May 27, 2023

The msvc runtime library needs to be added for all targets compiled for the windows-msvc abi, regardless of the compiler. There are three options to compile code for windows msvc:

  • with the official msvc toolchain
  • with clang-cl, which has commandline syntax compatible with the msvc toolchain
  • with clang, which uses the GNU commandline syntax.

In all three cases the -MT or -MD flag must be added. Without this commit, the option is only added in the first two cases.

The msvc runtime library needs to be added for all targets compiled
for the windows-msvc abi, regardless of the compiler.
There are three options to compile code for windows msvc:
- with the official msvc toolchain
- with clang-cl, which has commandline syntax compatible with the
  msvc toolchain
- with clang, which uses the GNU commandline syntax.

In all three cases the -MT or -MD flag must be added.

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
@dot-asm
Copy link
Contributor

dot-asm commented Jul 23, 2023

In all three cases the -MT or -MD flag must be added.

This is incorrect. The flags in question have completely different meaning to clang, different from clang-cl and cl. Passing them to clang won't achieve the suggested goal. The current version is correct.

But what is the problem? Linking with C run-time is handled by rustc, so one doesn't actually have to pass anything to clang if you compile C. C++ on the other hand would be problematic, and there is no reliable solution for it. Just use clang-cl.

@jschwe
Copy link
Author

jschwe commented Jul 29, 2023

But what is the problem?

The background here is corrosion which integrates cargo into CMake. Corrosion sets the CC_target_triple environment variables so that cc-rs uses the same compiler as CMake. I believe this is necessary to avoid potential ABI differences.

Some corrosion users prefer to use clang with the GNU cli syntax targeting the msvc ABI, but this causes unresolved symbol issues . Now I am not familiar with Windows so at this point I'm not really sure what the best way forward would be.

C++ on the other hand would be problematic, and there is no reliable solution for it.

Corrosion is a CMake module, so yes C++ is involved. If the code on the CMake side is built with clang or clang++ for msvc, could we set CC to the clang-cl in the same directory as clang, or are there any potential issues lurking here?

@dot-asm
Copy link
Contributor

dot-asm commented Jul 29, 2023

unresolved symbol issues

For reference, all of the referred symbols are actually deprecated by Microsoft and users are advised to use their counterparts prefixed with _, e.g. _strdup instead of strdup. But don't ask me why they did it, as I don't know :-) But in either case, consider the following snippet:

#include <string.h>
int main() { strdup("foobar"); }

If you compile it with -c and examine the symbol table in the object file, you'll see that it will attempt to link with strdup. But is there strdup in the run-time library? No. So what's going on? If you compile the .obj file with cl or clang-cl and examine linker directives (with dumpbin /directives), you'll notice two library references, one to the run-time and one to "oldnames". That's where the strdup symbol is. You can convince yourself by triggering the linking error by omitting the "oldnames" with [clang-]cl a.c /link /nodefaultlib:oldnames.

Moving on to clang. If you compile an .o file with clang [--target=x86_64-windows-msvc] it won't have any linker directives. Ever. You can pass -MD all you like to it, it won't add any linker directives. [But do what a "unix-y" -MD does, generate a .d file with object's source code dependency list.] At the same time you can note that if you compile the above snipped with clang, it will link successfully. How come? Add -v flag to the command line and you'll see that clang passes oldnames.lib to link.exe explicitly.

This is the answer to the mystery about "missing" strdup, no oldnames at link time when you compile your object files with clang [not to be confused with clang-cl]. How come the suggested modification apparently helped you, I don't know. It has to be circumstantial. Because it can be easily demonstrated that passing -MD or -MT to clang.exe doesn't do anything that would be meaningful at link stage. [BTW, passing -MT is likely to trigger an error, immediately or later on, as it's supposed to be followed by an additional argument.]

@dot-asm
Copy link
Contributor

dot-asm commented Jul 29, 2023

yes C++ is involved.

Just in case for reference. The referred linking errors are not related to C++ in any way. But the problem would be similar. If C++ code made references to C++ run-time, then the success at the linking stage would be dependent on linker directives being left by the compiler in object files. And clang.exe just doesn't do that...

@jschwe jschwe closed this Jul 29, 2023
@jschwe jschwe deleted the fix_crt branch July 29, 2023 14:02
@dot-asm
Copy link
Contributor

dot-asm commented Jul 29, 2023

Given the context something like this should do it.

index 91536c3..a0f2090 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1649,6 +1649,9 @@ impl Build {
                     // depending on the origin clang can default to a mismatchig
                     // run-time.
                     cmd.push_cc_arg(format!("--target={}", target).into());
+                    if target.contains("msvc") {
+                        self.print(&format_args!("cargo:rustc-link-lib=oldnames"));
+                    }
                 }
 
                 if cmd.family == ToolFamily::Clang && target.contains("android") {

Just in case, I for one would advocate rather in favour of not supporting non-cl compilers in msvc target context:-)

dot-asm added a commit to dot-asm/cc-rs that referenced this pull request Sep 20, 2023
dot-asm added a commit to dot-asm/cc-rs that referenced this pull request Sep 24, 2023
Fixes problem discussed in rust-lang#811.

[Also deduplicate clang --target argument on Windows, because it's
passed later on in the "// Target flags" section.]
dot-asm added a commit to dot-asm/cc-rs that referenced this pull request Sep 27, 2023
Fixes problem discussed in rust-lang#811.

[Also deduplicate clang --target argument on Windows, because it's
passed later on in the "// Target flags" section.]
dot-asm added a commit to dot-asm/cc-rs that referenced this pull request Nov 3, 2023
Fixes problem discussed in rust-lang#811.

[Also deduplicate clang --target argument on Windows, because it's
passed later on in the "// Target flags" section.]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants