Bridging Delphi and C: Practical Linking of C Object Files (Part 1)The main content of this article is as follows:
-
Object File Compatibility Testing
-
Limitations of Older Delphi Versions
-
Library Files
-
Calling Conventions
In general, C language generates object files, which are then linked into executable programs. On 32-bit Windows systems, these files typically use the .obj extension, and sometimes .o. However, these extensions may correspond to incompatible formats. For example, Microsoft’s C++ compiler and its compatible compilers generate a slightly modified COFF format; while C++Builder’s Win32 compiler (bcc32 and bcc32c) generates a 32-bit OMF format, which is the format required by early Delphi compilers.
Starting from Delphi XE2, the Delphi compiler can also directly use 32-bit COFF format object files generated by many other 32-bit C/C++ compilers for Windows.
Object File Compatibility Testing
To verify which types of object files are supported by modern Delphi versions, we wrote a simple program to generate multiple .c and .cpp test files. These files have different names and internal function names, but their functionality is identical. For example, the file cbc.c (cb stands for C++Builder, c stands for C language) contains the following function:
#include "cbc.h"
int cb_add_c(int a, int b){ return a + b;}
Next, I wrote a batch script to compile these source files using three different compilers: Embarcadero’s bcc32c.exe or bcc64.exe, different versions of Microsoft’s cl.exe, and MinGW’s gcc.exe. The script explicitly specifies the language type (C or C++) and the target platform bitness (32-bit or 64-bit). The generated object file names also reflect these differences. For example, the file name generated by compiling gccpp.cpp with 64-bit gcc is gccpp64.o, which contains the function <span>int gc_add_cpp(int a, int b)</span>.
The specific compilation commands are as follows:
bcc32c -c cbc.c -o cbc32.obj
bcc32c -c cbcpp.cpp -o cbcpp32.obj
bcc64 -c cbc.c -o cbc64.obj
bcc64 -c cbcpp.cpp -o cbcpp64.o
set clpath="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\bin\Hostx64\"
%clpath%x86\cl.exe /Fovcc32.obj /Tc vcc.c /c
%clpath%x86\cl.exe /Fovccpp32.obj /Tp vccpp.cpp /c
%clpath%x64\cl.exe /Fovcc64.obj /Tc vcc.c /c
%clpath%x64\cl.exe /Fovccpp64.obj /Tp vccpp.cpp /c
gcc -m32 -c gcc.c -o gcc32.o
gcc -m32 -c gccpp.cpp -o gccpp32.o
gcc -m64 -c gcc.c -o gcc64.o
gcc -m64 -c gccpp.cpp -o gccpp64.o
Subsequently, I wrote a small console test program in Delphi, compiled in both WIN32 and WIN64 modes, to attempt to link all the generated object files and call their functions.
The test results indicate that: Delphi 10.3 Rio can successfully link and call functions from all the above object files, except for the C++ file compiled by Embarcadero’s own bcc32c.exe (i.e., cbcpp32.obj). Even using the following command to convert it from OMF32 format to COFF32 format, the issue persists:
objconv -fpe cbcpp32.obj cbcpp32-coff.obj
Limitations of Older Delphi Versions
Early versions of Delphi only supported OMF format object files. Therefore, if you only have COFF format files, you must use existing format conversion tools to convert them; alternatively, if you have the source code, you can recompile using C++Builder or the free command-line compiler provided by Embarcadero.
One of the more reliable conversion tools currently available is objconv, developed by Agner Fog. Additionally, it is said that the COFF2OMF converter provided by DigitalMars also works properly.
It is important to note: the COFF2OMF tool included with C++Builder or RAD Studio is only for converting import libraries, and cannot handle complete code object files. This is because it only recognizes a small subset of record types in complete object files and is ineffective for complex object files containing actual code and data.
Library Files
C and C++ typically also use library files (with the .lib extension). These files are essentially collections of multiple object files. Many C compilers come with a “library management tool” (librarian) that can be used to extract, insert, replace, or list the object files contained in the library.
In Delphi, it is not possible to directly link .lib files. However, we can use the TDUMP tool included with Delphi and C++Builder to view the internal contents of library files. Additionally, the free C++ compiler and C++Builder provided by Embarcadero also include a library management tool called TLIB, which can be used to manipulate individual object files within the library—such as extracting the required object module and linking it separately into a Delphi project.
Note: The object file format used on the 64-bit Windows (Win64) platform is different from that of 32-bit.
Calling Conventions
On the Win64 platform, there is only one mainstream calling convention (strictly speaking, there are two, but the other is extremely low-level and not of concern for ordinary applications), so there is no need to explicitly declare the calling convention.
However, if your code needs to be compatible with both Win32 and Win64, you can still retain declarations of calling conventions such as <span>stdcall</span>, <span>register</span>, or <span>cdecl</span> in the source code—the 64-bit compiler will automatically ignore them and will not produce errors or warnings.