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.
Before showing the solution, let’s have a quick look at what can happen if you have this error in your builds.
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.
When an app built with the Clang toolchain is linked against a Classic-built library, you might see the following errors:
[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:
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'
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:
Warning: Compiler mismatch. Module 'Z:\RAD STUDIO PROJECTS\DEMOS AND WEBINARS\MIXING CLASSIC AND CLANG\WIN32\DEBUG\MYSTATICLIB.LIB|Unit1' built with bcc32 compiler; expected the bcc32c compiler because of 'Z:\RAD STUDIO PROJECTS\DEMOS AND WEBINARS\MIXING CLASSIC AND CLANG\WIN32\DEBUG\VCLAPP.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:
#pragma comment(lib, "MyStaticLib")
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.
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!
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.)