Override assert handler
Richard (Rikki) Andrew Cattermole
richard at cattermole.co.nz
Mon Aug 19 22:53:53 UTC 2024
On 20/08/2024 9:03 AM, Walter Bright wrote:
> I actually do understand how shared libraries work.
>
> What happens by default when an assert failure happens is the function:
>
> ```
> core.exception._d_assertp(immutable(char*) file, uint line);
> ```
> is called. That forwards the call to:
>
> ```
> core.exception.onAssertError(string file, size_t line);
> ```
> which then forwards the call to:
> ```
> (*_assertHandler)(file,line,null);
> ```
> and core.exception._assertHandler is the pointer to the function.
>
> The default behavior of _assertHandler is:
> ```
> throw staticError!AssertError(file, line);
> ```
>
> Therefore, if you write your own _d_assertp function in the executable,
> it will override the library version in your executable. For code in the
> shared library, the shared library _d_assertp will be called.
Its not quite as simple as that. For Windows yes that's how it'll work
without compiler & linker assistance.
"Symbols so introduced may duplicate symbols already defined by the
program or previous dlopen() operations. To resolve the ambiguities such
a situation might present, the resolution of a symbol reference to
symbol definition is based on a symbol resolution order. Two such
resolution orders are defined: load or dependency ordering. Load order
establishes an ordering among symbol definitions, such that the
definition first loaded (including definitions from the image file and
any dependent objects loaded with it) has priority over objects added
later (via dlopen()). Load ordering is used in relocation processing.
Dependency ordering uses a breadth-first order starting with a given
object, then all of its dependencies, then any dependents of those,
iterating until all dependencies are satisfied. With the exception of
the global symbol object obtained via a dlopen() operation on a file of
0, dependency ordering is used by the dlsym() function. Load ordering is
used in dlsym() operations upon the global symbol object."
https://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
If your ``_d_assertp`` was first seen it would apply to the entire
process if the symbol was exported. If the shared library symbol was
first seen, its the one that gets used no matter what image is being
discussed as long as everything is exported.
Of course, the Posix behavior of setting the assert handler for the
entire process is what you want. Not the Windows one where it is
localized to the binary. After all it doesn't matter who errors out, you
want to act upon it the same way consistently.
Which brings us back to what I already said, the function pointer design
works best and doesn't limit you into a subset of desirable situations.
It also covers the use case where you want to swap it out in a process
that has been running for a month. Not to mention it's fully portable
and won't change behavior based upon platform.
> ------
>
> To understand assert error handling, it's necessary to understand:
>
> ```
> AssertHandler
> assertHandler
> assertHandler (yes, two of them!)
> _assertHandler
> onAssertError
> onAssertErrorMsg
> AssertError
> _d_assertp
> _d_assert_msg
> _d_assert
> ```
>
> which is overly complex.
Agreed, there may be some simplification possible.
More information about the Digitalmars-d
mailing list