Subject | RE: [Firebird-Architect] XSQLDA/XSQLVAR issues |
---|---|
Author | Samofatov, Nickolay |
Post date | 2005-02-01T23:17:26Z |
All,
I cannot afford writing detailed explanation often, but I can do it
once.
Jim says:
point.
Jim replies:
exception in practice.
Now, let me explain something about exception mechanisms.
Imagine the following call stack for running program in our case:
UserFunction()
FB_API_Function()
Exception_Callback_function()
Stack for could look like this:
[...return address to caller of UserFunction...] ----------- Controlled
by code generated by API user compiler
[...parameters for UserFunction...]
[...local variables...]
[...stack frame size for UserFunction...]
[...return address to UserFunction...] ----------- Controlled by code
generated by Firebird compiler
[...parameters for FB_API_Function...]
[...local variables...]
[...stack frame size for UserFunction...]
[...return address to FB_API_Function...] ----------- Controlled by code
generated by API user compiler
[...parameters for Exception_Callback_function...]
[...local variables...]
[...stack frame size for Exception_Callback_function...]
Note that there is a number of ways to store stack frame size. One
compiler may chose to store frame size in bytes, another may chose to
save pointer to function return address, etc.
On some platforms stack frame layout is fixed (VMS is one of them to the
best of my knowledge), but on most it is not and in many cases stack
frames may be omitted (for performance reasons) complicating stack
walking process. In such cases stack walking requires access to special
global table generated by the compiler known as "location lists".
There is no portable way of stack walking.
When C++ or Pascal exception is thrown by Exception_Callback_function
the following things are happening:
1. reference to current exception object is placed to magical location
known to everybody
2. for each function which require stack unwind actions (such as calling
destructors) compiler generates unwind helper function. Pointers to such
functions are stored in special global table.
3. _throw_ code walks the stack up using stack frame size data for each
frame and examines the global unwind helpers table. If it finds the
unwind handler associated with given return address it calls it.
4. if unwind handler says that it didn't finish handling exception stack
walking continues
In our example, throw code in Exception_Callback_function is going to
walk through and unwind stack for FB_API_Function.
This generally doesn't work if compilers used to generate code for
Exception_Callback_function and FB_API_Function are different.
In mild cases, for example when FB_API_Function is compiled with VC++ on
Win32, but Exception_Callback_function is compiled by Delphi and raises
Delphi exception the program doesn't crash right away.
Only C++ semantics is getting broken. Destructors for objects declared
on stack of FB_API_Function are not called. Vlad has a nice sample for
this behavior.
In more severe cases, when stack frames layouts are different crash is
imminent.
As additional research exercise you may try to write exception callback
using interpreted language, for example Visual Basic 6. While it allows
calling routines from DLL writing callbacks is problematic.
Writing exception callbacks is prohibited by MSDN documentation, but if
you are brave you may try. :-)
Portable way of passing exceptions through API is to use dynamic
thread-specific exception buffers. This method is used by Windows API.
It is also used by COM. It is used by many POSIX implementations. It
works ok.
I cannot afford writing detailed explanation often, but I can do it
once.
Jim says:
> >>If we build in a function to register a callback with a knowncalling
> >>sequence, we can the callback in your call to throw the actualI reply:
> >>exception. The API implementation code doesn't care about the
> >>mechanisms of the exception, just that a responsible adult does
> >>whatever is required to make sure that it never returns from the
> >>exception callback.
> >Exception handling is not magical thing and compiler needs to know a_all_ functions between throw point and catch handler. This is important
> >lot to throw, pass and catch exception properly.
> > Any throw code needs to know how to unwind all stack frames for
point.
Jim replies:
> There is no disagreement that exceptions can't be thrownJim says that exception callback should "never return", i.e. throw
> across compilation boundaries. A call back, however, doesn't
> suffer from this problem, and has the added advantage of
> making the object lifetime of the exception object clear --
> the duration of the callback.
exception in practice.
Now, let me explain something about exception mechanisms.
Imagine the following call stack for running program in our case:
UserFunction()
FB_API_Function()
Exception_Callback_function()
Stack for could look like this:
[...return address to caller of UserFunction...] ----------- Controlled
by code generated by API user compiler
[...parameters for UserFunction...]
[...local variables...]
[...stack frame size for UserFunction...]
[...return address to UserFunction...] ----------- Controlled by code
generated by Firebird compiler
[...parameters for FB_API_Function...]
[...local variables...]
[...stack frame size for UserFunction...]
[...return address to FB_API_Function...] ----------- Controlled by code
generated by API user compiler
[...parameters for Exception_Callback_function...]
[...local variables...]
[...stack frame size for Exception_Callback_function...]
Note that there is a number of ways to store stack frame size. One
compiler may chose to store frame size in bytes, another may chose to
save pointer to function return address, etc.
On some platforms stack frame layout is fixed (VMS is one of them to the
best of my knowledge), but on most it is not and in many cases stack
frames may be omitted (for performance reasons) complicating stack
walking process. In such cases stack walking requires access to special
global table generated by the compiler known as "location lists".
There is no portable way of stack walking.
When C++ or Pascal exception is thrown by Exception_Callback_function
the following things are happening:
1. reference to current exception object is placed to magical location
known to everybody
2. for each function which require stack unwind actions (such as calling
destructors) compiler generates unwind helper function. Pointers to such
functions are stored in special global table.
3. _throw_ code walks the stack up using stack frame size data for each
frame and examines the global unwind helpers table. If it finds the
unwind handler associated with given return address it calls it.
4. if unwind handler says that it didn't finish handling exception stack
walking continues
In our example, throw code in Exception_Callback_function is going to
walk through and unwind stack for FB_API_Function.
This generally doesn't work if compilers used to generate code for
Exception_Callback_function and FB_API_Function are different.
In mild cases, for example when FB_API_Function is compiled with VC++ on
Win32, but Exception_Callback_function is compiled by Delphi and raises
Delphi exception the program doesn't crash right away.
Only C++ semantics is getting broken. Destructors for objects declared
on stack of FB_API_Function are not called. Vlad has a nice sample for
this behavior.
In more severe cases, when stack frames layouts are different crash is
imminent.
As additional research exercise you may try to write exception callback
using interpreted language, for example Visual Basic 6. While it allows
calling routines from DLL writing callbacks is problematic.
Writing exception callbacks is prohibited by MSDN documentation, but if
you are brave you may try. :-)
Portable way of passing exceptions through API is to use dynamic
thread-specific exception buffers. This method is used by Windows API.
It is also used by COM. It is used by many POSIX implementations. It
works ok.
> Jim StarkeyNickolay