VC ++ exception handling differs on x86 and x64 for IBPP / Firebird client

I am hacking IBPP on Visual Studio 2015 / VC ++. IBPP is a C ++ wrapper for the Firebird / Interbase API. IBPP, C ++ Client Interface for Firebird Server

Part of this package is a small test suite, you can download it here: ibpp-2-5-3-1-src.zip

To start with a test suite, you will find a simple batch file to compile it under

x: ... \ PPIR-2-5-3-1-Src \ Tests \ VS2005 \ protozoa-build.bat

It compiles fine using native x86 and x64 vC ++ 2015 kits.

Before compiling, you need to edit lines 84 to 86

x: ... \ PPIR-2-5-3-1-Src \ Tests \ tests.cpp

const char* DbName = "x:/ibpptest/test.fdb"; // FDB extension (GDB is hacked by Windows Me/XP "System Restore") const char* BkName = "x:/ibpptest/test.fbk"; const std::string ServerName = ""; //"localhost"; // Change to "" for local protocol / embedded 

Remember to create the x:\ibpptest\ .

In addition, you need to download fblient files that are not available on their own, but as part of the entire server archive. Get both of these files: the 32-bit embedded version as well as the 64-bit embedded version .

To simplify, create two directories besides x:\...\ibpp-2-5-3-1-src\tests\vs2005\ :

 x:\...\ibpp-2-5-3-1-src\tests\vs2015x86\ x:\...\ibpp-2-5-3-1-src\tests\vs2015x84\ 

and copy x:\...\ibpp-2-5-3-1-src\tests\vs2005\simplest-build.bat into them. Now copy the fbclient files (32 bits in x86, 64 bits in x64) in these directories:

 intl/* udf/* fbembed.dll firebird.msg ib_util.dll icudt30.dll icuin30.dll icuuc30.dll msvcp80.dll msvcr80.dll 

Now you can compile and run the tests.exe file. The x86 binary generates some errors in test 6, which is normal because you are using the built-in version of fblient files. The x64 binary will end on the Windows program crash screen. This happens in Test3 when the test suite throws an exception:

 try { #if defined(IBPP_WINDOWS) && defined(_DEBUG) OutputDebugString(_("An exception will now get logged in the debugger: this is expected.\n")); #endif st1->ExecuteImmediate( "CREATE SYNTAX ERROR(X, Y) AS " "SELECT ERRONEOUS FROM MUSTFAIL M" ); } catch(IBPP::SQLException& e) { //~ std::cout<< e.what(); if (e.EngineCode() != 335544569) { _Success = false; printf(_("The error code returned by the engine during a\n" "voluntary statement syntax error is unexpected.\n")); } } 

In x86 binary, this exception was detected as expected, but in x64 binary it will not. Does anyone know how to achieve similar exception handling in binary x64?

Thanks in advance for your help!

+7
c ++ exception 64bit firebird
source share
2 answers

After a lot of research, I found the reason. I was mistaken in believing that the exception handling in x86 and x64 is different. The IBPP library contains an error (from 10 years old!) That only occurs in x64 / windows szenario when the firebird library reports an error (complex) for the caller.

So what happens:

The test program calls the IBPP library. The IBPP library calls the firebird API / library. The firebird library reports the results of calls in the [20] longs array, they call it "ISC_STATUS vector". The IBPP library checks these results, and if an error occurs, it throws an exception. The testing program catches such exceptions and tells them about it.

So far so good. But the error is that IBPP defines ISC_STATUS as an array of [20] longs as firebird, which it also ran before v2.0, which only supports x86 windows. Starting with v2.1, firebird supports x64 windows and defines ISC_STATUS as an intptr_t array, which leads to a long debt on Windows x64 LLP64 compilers and long LP64 linux compilers - both have a width of 64 bits. In ILP32 windows and linux x86, intptr_t compilers are 32 bits wide. IBPP does not close the gap for Firebird and stays on its ISC_STATUS definition longer - which leads to a 32-bit data type on Windows x64 systems on LLP64 (but up to 64 bits on Linux, since gcc on linux uses the LP64 system, so the error only occurs in windows )

So, the Firebird x64 API reports up to 20 state integers to an array "by reference parameter" [20] out of 64 bit integers. The FBPP library passes an array [20] of 32 bit integers. When the Firebird API stores values ​​in the second half of the array, it overwrites the caller’s memory. In this case, the following bytes after the ISC_STATUS vector are occupied by the C ++ string object. Both status arrays and string are part of the IBS (interbase status) class. Many of the IBPP functions often instantiate a local object of this class to control the results of the Firebird API and the error description string. When functions exit the framework, it clears such local objects and tries to free the line, but its metadata in memory has been overwritten by the firebird API.

Thus, flushing the local IBS object leads to unknown software exceptions that overload the exceptions created by the IBPP infrastructure. And this exception does not get into the test program, but through windows / dr. watson.

I set the ISC_STATUS definition from "long" to "intptr_t" and everything works as expected.

Thank you all for your hints.

+1
source share

Caution: I used Visual Studio 2017 to run the simpleest-build.bat file.

From the evidence below, you can see that branching or other compilation occurs between the 32-bit and 64-bit versions of the Firebird service, which causes this problem.

Decision. The EngineCode () member function does not exist in the 64-bit version. You should use the function () function exception as shown in the commented line inside the catch3 () block of catch. If you want to use EngineCode information, you will have to parse it from the what () line, since it contains all the information that was originally provided as separate data elements in the IBPP :: SQLExceptionImpl class for the 32-bit version.

 try { #if defined(IBPP_WINDOWS) && defined(_DEBUG) OutputDebugString(_("An exception will now get logged in the debugger: this is expected.\n")); #endif st1->ExecuteImmediate( "CREATE SYNTAX ERROR(X, Y) AS " "SELECT ERRONEOUS FROM MUSTFAIL M" ); } catch(IBPP::SQLException& e) { //~ std::cout<< e.what(); printf(e.what()); //if (e.EngineCode() != 335544569) //{ // _Success = false; // printf(_("The error code returned by the engine during a\n" // "voluntary statement syntax error is unexpected.\n")); //} } 

Call result ().

 *** IBPP::SQLException *** Context: Statement::ExecuteImmediate( CREATE SYNTAX ERROR(X, Y) AS SELECT ERRONEOUS FROM MUSTFAIL M ) Message: isc_dsql_execute_immediate failed SQL Message : -104 can't format message 13:896 -- message file C:\WINDOWS\SYSTEM32\firebird.msg not found Engine Code : 335544569 Engine Message : Dynamic SQL Error SQL error code = -104 Token unknown - line 1, column 8 SYNTAX 

Puzzle: statement.cpp shows using IBPP :: SQLExceptionImpl and more ... using ExceptionImpl, so I should assume that this source code is a 32-bit branch. If it is assumed that this is a common branch for 32 and 64 bits, then I do not see how it can work.

There are two header files that define exception classes. I believe that _ibbp.h was used to compile a 32-bit client, and ibbp.h was used for a 64-bit client. I came to these conclusions by modifying the catch clause in Test3 () to find out what possible exceptions might occur .... ExceptionImpl classes will only compile with 32-bit client dlls and they won't compile with 64-bit dlls.

From _ibpp.h (32-bit fbclient dlls recognize these classes. 64-bit fbclient dlls do not.)

 /////////////////////////////////////////////////////////////////////////////// // // Implementation of the "hidden" classes associated with their public // counterparts. Their private data and methods can freely change without // breaking the compatibility of the DLL. If they receive new public methods, // and those methods are reflected in the public class, then the compatibility // is broken. // /////////////////////////////////////////////////////////////////////////////// // // Hidden implementation of Exception classes. // /* std::exception | IBPP::Exception / \ / \ IBPP::LogicException ExceptionBase IBPP::SQLException | \ / | \ / | LogicExceptionImpl | SQLExceptionImpl | | IBPP::WrongType | \ | IBPP::WrongTypeImpl */ 

From ibpp.h (both sets of fbclient dll recognize these classes)

 /* IBPP never return any error codes. It throws exceptions. * On database engine reported errors, an IBPP::SQLException is thrown. * In all other cases, IBPP throws IBPP::LogicException. * Also note that the runtime and the language might also throw exceptions * while executing some IBPP methods. A failing new operator will throw * std::bad_alloc, IBPP does nothing to alter the standard behaviour. * * std::exception * | * IBPP::Exception * / \ * IBPP::LogicException IBPP::SQLException * | * IBPP::WrongType */ 

In all tests, test.cpp, the only attribute for IBPP :: SQLException is in Test3 (). Every other catch uses IBPP :: Exception.

Thus, this problem would only appear in Test3 () when compiling for 64 bits, but I think that it would occur whenever IBPP :: SQLException is used in a 64-bit implementation.

+1
source share

All Articles