The C++Builder linker will warn you when you mix object files linked with classic and clang together, helping you avoid linker or runtime errors.
Today in 2020, we recommend using the modern Clang compilers, especially with the work we’ve been doing for Win64 (eg entirely new debugger) and the RTL improvements to make upgrading from classic easy. However, many customers upgrade in steps: first from classic Win32 to Clang Win32, and then adding Win64 after. In fact, we often directly recommend this as an upgrade strategy, to move to Clang first and Win64 second, in two steps.
However, there’s a common problem when moving from the classic to clang compilers. Object files (.obj and .lib libraries) generated by the classic and Clang Win32 compilers are not binary compatible with each other. (Both classic and clang can link against Delphi, but classic and clang cannot link against each other.) If you try to build an application where some parts are built with Clang and some with classic all mixed together, you can expect anything from linker errors to runtime instability. Yet, it’s easy to accidentally do, both when upgrading or even just when adding a new library or EXE to your project.
In 10.3.3, and this was documented in the online help but not blogged about at the time, we added a new linker warning to help you detect these situations.
Learn more about one of the main features of C++ Builder 11.1 which is the Clangd compiler in this article.
Before showing the solution, let’s have a quick look at what can happen if you have this error in your builds.
Table of Contents
Problems When Mismatching Compilers
In 10.3.3 and newer, you will get a linker warning – see below – ahead of any of the following. But if you’re using an older version, before we added the warning diagnosing the issue, you might see only the following errors. These appear when linking the app.
Link-time errors
When an app built with the Clang toolchain is linked against a Classic-built library, you might see the following errors:
1 2 3 |
[ilink32 Error] Error: Unresolved external '__InitExceptBlockLDTC' [ilink32 Error] Error: Unresolved external '_ThrowExceptionLDTC' Error: Unresolved external '_CatchCleanup()' |
When an app built with the Classic toolchain is linked against a Clang-built library, you might see the following errors:
1 2 3 4 5 6 7 8 9 |
Error: Unresolved external '___seh_personality_v0' Error: Unresolved external '__Unwind_SjLj_Register' Error: Unresolved external 'std::_Xbad_alloc()' Error: Unresolved external '___cxa_begin_catch' Error: Unresolved external '___cxa_rethrow' Error: Unresolved external '___cxa_end_catch' Error: Unresolved external '__Unwind_SjLj_Unregister' Error: Unresolved external '__Unwind_Resume' Error: Unresolved external '___cpp_terminate' |
Runtime errors
In some cases you might not see any linker errors at all, and you will run your app. Most likely, at some point your app will crash and it will be hard to figure out why, because the code looks fine. In reality, it will be due to two different compilers doing things in different ways – different memory layout, alignment, runtime library expectations, exception handling, all sort of possible differences.
It’s not a good situation to have either odd linker errors, or odd runtime behaviour. So we added a warning to check for this situation.
Diagnosing: a New Linker Warning
RAD Studio / C++Builder 10.3.3 and newer warns you about mixing clang and classic when linking. (If linker errors like the above occur, this warning is emitted before them, so you will see the warning before any errors due to the problem.)
The message says ‘Warning: Compiler mismatch.’ and then tells you what it expected and what it found:
1 |
Warning: Compiler mismatch. Module 'Z:RAD STUDIO PROJECTSDEMOS AND WEBINARSMIXING CLASSIC AND CLANGWIN32DEBUGMYSTATICLIB.LIB|Unit1' built with bcc32 compiler; expected the bcc32c compiler because of 'Z:RAD STUDIO PROJECTSDEMOS AND WEBINARSMIXING CLASSIC AND CLANGWIN32DEBUGVCLAPP.OBJ' |
So how does the linker check for mixed objects, and how does it know what to expect?
In the above error, I have a VCL application linking in a static library. The VCL app is built with Clang. The library is built with classic. The clang app links in the library with:
1 |
#pragma comment(lib, "MyStaticLib") |
⸺ see documentation on linking with #pragma comment vs #link.
It’s quite easy to make the mistake of mismatching compilers. As you can see in the screenshot to the right, you can’t easily tell which Win32 compiler is being used. This is something we could improve, but right now it lists the target platform, not the compiler.
Our compiler now emits into each object a record (a small bit of data) saying what compiler it was built with (eg, Clang Win32.) When linking, the linker is given a set of object files and libraries, in order on the command line. It looks at the first object it links with, and marks down that it expects all further objects to be built with the same compiler as that first one. For each object it links in (and given a library, which is just a set of objects, for each object in the library) it checks if it was built with the compiler it’s expecting.
If not, it emits the above warning, which you can also see in the image on the right. You can see that it lists the erroneous object and what it was built with, and it also explains why it was looking for a different compiler. This is intended to help you diagnose your build setup.
TLDR
Quick summary: mixing object files built with two compilers into one binary is a bad idea. The linker will give you a warning if you do, and tell you what is built with what so you can fix it. This fixes linker problems, and fixes app stability problems!
Further Reading
Packaging Components Or Libraries Written In Classic and Clang C++ Win32 Compilers
This new docwiki page discusses the linker warning, but also has a bit more information. It addresses how to use clang or classic binaries if you’re an app developer and building apps with multiple parts (perhaps linking against an open source C++ library you’re also building), and also has another section for if you’re a library developer (eg writing components or libraries and distributing them to your customers.)
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition
So how do I use the office components since they only seem to be provided by Embarcadero in non clang form?
I’d love to use the clang compiler. But:
– compile speed is 4-10 times slower
– code guard does not work
– editing has endless hiccups where code insight freezes the IDE for seconds at a time
– debugging is difficult because variables do not show values
– class browsing does not work
Now the problem is that if I have a single file that requires C11, it forces me to switch over to the non-functional clang compiler for the whole project. I can live with the reduction in compile speed and the lack of code guard, but
not being able to effectively debug coupled with no code browsing makes it hard.
Hi DS,
You’re right, CodeGuard is a feature only for classic. We’re looking at what we can enable for clang in future. In terms of compile speed, we don’t see it as slow as you do, except for very rare cases. If you have source code that repros that, please send it through! We’ve also been working on parallel building to resolve speed issues, and TwineCompile is free in GetIt right now.
For debugging, 10.4 has a new Win64 debugger for clang that is significantly better.
Finally, browsing and code insight: 10.4.2 brings improvements to code completion – please test it out when it arrives soon.
Thank you for the reply.
A small project:
Classic compiler: 7.8 seconds
CLang compiler: 42.2 seconds
Using TwineCompile the time reduced to 32.5 seconds, but I did not like the way the compiler output was directed to the TwineCompile window, which does not retain it’s size.
By removing TwineCompile and then optimizing the precompiled headers I got the CLang time down to 17 seconds, which is only about double the classic compile time.
For another small project classic was 8.5 seconds, clang was 35.5 seconds and by optimizing the precompiled headers I reduced the compile time using clang to 14.6 seconds. But the time to run for a single small change was dramatically reduced.
On a larger project the build times were 23 seconds vs 184 seconds, and I could not reduce the clang time by optimizing the headers. In this case I probably need to move the invariant units to libraries.
So by paying attention to the use of pre-compiled headers I can at make the compile time for clang closer to 2x than 5x greater than classic.
That’s good to know! Thanks for the info.
Hello,
my clang compiled c++ Builder application requires a clang compiled “vcle.lib”, but this library exists only for classic compiler (c++ Builder 11). What shall I do?
Hello. vcle is a library that bridges between C++ and Delphi. Sometimes it reimplements Delphi classes too (like strings.) As such it’s not really a VCL-only library: effectively it was for the classic compiler when Win32 was the only platform, but these days we have more platforms and also FMX. Plus the bridging and class reimplementation are a RTL issue.
So in Clang, the library is renamed and is called rtle.lib.
The odd thing is why your project tries to link to it. The library is auto-linked based from the headers, which means it should never need to be manually listed as a library to link in. If it is listed in your project options, the easiest thing is to remove that and see if linking then works correctly.
I wrote a blog post expanding on my comment, with much more info. Hope it helps! https://blogs.embarcadero.com/what-are-vcle-lib-and-rtle-lib/