Compiling InterBase with the Borland C++ Builder Compiler
The linker included with C++Builder (1, 3, and 4) does not work correctly with gds32.lib
Dialects will determine how InterBase functions in relation to some of the new and existing features. In particular, dialects control how InterBase interprets:
"Decoration" is the name applied to changes made to external function names to indicate whether they use the C or the Pascal calling sequence. One difference is that in C, the caller cleans up the stack. In Pascal, the routine called cleans up the stack. Microsoft and Borland 'decorate' exported functions differently.
The build process adds an alias table to the import library to redirect the calls which take a variable number of arguments to the correct function internally. [ed. Note I think that the Pascal calling sequence can not handle variable length argument lists.] This alias table was created using assembly code (sort of) and was added as part of the library. Earlier versions of the Borland linker (TLINK32) had no problems with using the alias table and all was well.
The current version of the Borland linker (ILINK32) does not support aliases. Since aliases are not supported, CBuilder 1.0 and 3.0 can not use any function that takes a variable number of arguments (isc_start_transaction, isc_event_block, isc_expand_dpb). To work around this issue, customers would need to create a definition file which remaps the functions much as the alias table did.
After addressing this issue, we decided that we no longer have a real reason not to use the Borland compiler to build the product. As of this past Tuesday, we had a production kit built with CBuilder 4.0. On Wednesday, the DEV kit was born. This process wasn't too difficult, but did cause headaches along the way. The issues found were
The Borland compiler is very picky and by default stops compiling after 250 warnings are reached (needless to say, this didn't take long). It did however, surface some problems in the source code. For example, the Borland compiler rejected this although it works with all others:
STATUS *foo foo=(STATUS) blee();
Other areas where this shows up were in formatting error messages, passing function pointers, and incorrect casting. These changes will all make their way into the latest kit regardless since they do removing warnings and address issues.
Some files produced were smaller than their MS counterparts, but in many cases, the files were a bit larger. The files that became larger did so because of issues with the source code. For example, in order to use gds__log, 5 other .obj files needed to be included to resolve all external functions.
The linker required three different switches based on the type of target. Microsoft only required two.
Because the makefiles were changed, all targets had version information included in them. This was required partially because of the way the parameters needed to be passed to the Borland linker. These same changes could and probably should be made regardless of compiler.
All of the .BIND files needed to be changed to remove unrecognized directives (they were also renamed to .DEF files which wasn't really necessary).
Overall, there were 65 source files (including makefiles and bind files) that needed to be modified.
Lastly, just to mention the obvious, since we were compiling using the Microsoft compiler, each makefile needed to change to work with the Borland compiler/linker syntax.
While converting the build process to use the Borland compiler, I encountered the following bugs which will be submitted to the CBuilder group once I get small test cases.
windows.h included a bad conditional define to use nameless unions when not compiling a C++ program. I needed to hack around this in our own source files. After talking to someone in the CBuilder group, this was confirmed as a bug (and a source of a few gray). The Tools
As part of this process, I wanted to see how the debugging tools were. At first glance, not too bad but very different from the Microsoft environment and somewhat confusing at first since there were many windows (much like Workshop on Solaris). The watch and inspection windows were handy. There was still the same problem with scoping and structures of the same name (this is really our own problem). Overall, the tools seemed usable at a first glance.
Now that the product was built with Borland CBuilder (and I located the BCRTL which didn't seem to be documented), it was time to try some vector runs against the kit. I was unable to run tests on my machine because of issues with the file drop_gdb. It seemed to crash each time it was run.
I didn't have the time to investigate fully, but this file was built with MSVC and an earlier version of the product (5.5). This seemed to indicate that either we will have compatibility issues between the .EXE files or that something wasn't built correctly. Using the tools from the Borland build, there were no such problems (I tried the same thing that drop_gdb does ... I didn't work it to death).
So, I ran the tests from Solaris using NT as a server. The tests looked ok with the following exceptions:
I did not analyze the test to determine exactly why something failed. I only took a quick glance at the results and failures.
Tests for External Relations had fits since the user name and password used by the tests was not recognized by the server (which is odd seeing as the server had all the correct information in the security database).
All tests that relied on UDFs failed as I did not have UDF information available for the server.
Some errors came back with a ^M attached to them.
While running the tests, I was also accessing the server locally via building the product. The file empbuild.exe that is used to build the employee database ended up hanging. In addition, the tests also hung (the test currently executing was EXT_REL_0_4_E). The server was idle and the client app (empbuild) was taking about 90% of the processor time.
All other failures seemed to be caused by databases that could not be restored because of username/password issues or because of output differences that are caused by the way information is stored in the databases. For example, unordered queries can return information in a different order when run against databases on different platforms.
Once the kit was built, there was still the issue of creating a Microsoft compatible import library. First some background.
When linking applications, different linkers have different ways of finding functions. This is the basic reason why we need different import libraries for different compilers.
When the functions are exported, they are 'decorated' to help the linker find the correct information. Function decorations differ between compilers. Microsoft decorated functions look like:
for a __stdcall function _isc_attach_database@24 for a __cdecl function _isc_start_transaction
Borland decorated functions look like:
for a __stdcall function isc_attach_database for a __cdecl function _isc_start_transaction
When compiling with the Microsoft compiler and creating a Borland import library, there was no additional information that was needed and couldn't be worked around by using aliases. This is not the same when using a Borland compiler and creating a Microsoft import library.
The Microsoft linker looks at stdcall functions to determine the size of the parameters being passed in (this number is the @24 part of the example above). This information can not be determined by the Microsoft tools unless the object files (or DLL) is created using a Microsoft compiler.
This leaves one question: How can we create a Microsoft compatible import library if we build with Borland CBuilder?
We can't do this directly from the objects created by the Borland compiler. This was confirmed by many hours of testing, trips to CBuilder development and calls to Microsoft.
- After the build has completed, recompile the client library using the Microsoft compiler and make the import library from that. This would introduce a second compiler into the mix and possibly other issues. We would still ship only those binaries created with the Borland compiler.
- Don't ship a Microsoft import library with the product.
- When talking to Microsoft I was told that when they have customers who need to link in a Borland DLL file, they either need to dynamically load the entrypoints or they need to
- Compile the application
- Look at the linker output with respect to unresolved externals
- Use the information from the unresolved externals to create a definition file
- Use the definition file to create an import library which they can then use
We could do #3 for the customer. This would be a manual process that would need to be repeated each time a new entrypoint is added to the API.
If we use the Borland compiler we would need to ensure that all internal tools work correctly. So far, tcs is the only app that seems to work and that may be because it was compiled with Borland C. Both drop_gdb and marion crash. This could be a sign of things to come for our customers.
We would also need to come to a conclusion on what to do about the Microsoft import library.
We would need to update the install program to deliver a different C runtime library