Date: Mon, 4 Mar 2024 16:43:40 +0000
On Mon, Mar 4, 2024 at 8:43 AM Antony Polukhin wrote:
>
> On Wed, Feb 28, 2024, 19:09 Frederick Virchanza Gotham wrote:
>>
>> I got this working, and so now when the C# program catches the
>> exception from C++, it's able to ask the DLL to see the exception log
>> to see what was thrown (along with a stack trace). When it comes to
>> the GNU g++ compiler, a quick web search shows me that people have
>> been able to do the same thing by providing an implementation of
>> '__cxa_throw'.
>
>
> Could you please share the source code? I've recently implemented
> something like your trick in Boost.Stacktrace for POSIX, and now I'm
> in search for a working Windows solution.
The guys here on the mailing list aren't really interested in this for
now, so please reply to my own email -- I want to see how it looks
when it's done. On the mailing list here for the time being, I'm just
going to focus on getting "typeid_except" accepted.
The following code is tested and working on the 64-Bit versions of the
following compilers: Microsoft VC++, GNU g++, LLVM clang++, Intel ICX.
It won't work on the 32-Bit version of the Microsoft compiler but it
would only be a half-hour of coding to get that working (it will
definitely work). Here is a GodBolt link:
https://godbolt.org/z/jEzrnrba4
And here's all the code copy-pasted:
#include <atomic> // atomic
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <typeinfo> // type_info
#ifdef _MSC_VER
# include <Windows.h> // LoadLibraryA, GetProcAddress
#else
# include <dlfcn.h> // dlsym, RTLD_NEXT
#endif
namespace std {
using throw_handler = void (*)(void const *, type_info const &);
// cannot mark as noexcept?
static atomic<throw_handler> detail_current_throw_handler{nullptr};
extern throw_handler get_throw(void) noexcept
{
return detail_current_throw_handler.load();
}
extern throw_handler set_throw(throw_handler const f) noexcept
{
return atomic_exchange(&detail_current_throw_handler, f);
}
}
namespace {
#ifdef _MSC_VER
using FuncPtr = void (*__stdcall)(void*,_s__ThrowInfo const*);
#else
using FuncPtr = void (*)(void*,void*,void (*)(void*));
#endif
FuncPtr GetOriginalThrower(void) noexcept
{
#ifdef _MSC_VER
auto const h = ::LoadLibraryA("vcruntime140.dll");
if ( nullptr == h ) return nullptr;
return (FuncPtr) ::GetProcAddress(h, "_CxxThrowException");
#else
return (FuncPtr) ::dlsym(RTLD_NEXT, "__cxa_throw");
#endif
}
FuncPtr const original_thrower = GetOriginalThrower();
}
std::type_info const *GetTypeInfoFromSecondArgument(void const *const arg)
{
if ( nullptr == arg ) return &typeid(void);
#ifdef _MSC_VER
auto const pThrowInfo = static_cast<__int32 const*>(arg);
auto const base = static_cast<__int32 const*>(static_cast<void
const*>(GetModuleHandle(nullptr)));
ptrdiff_t offset;
offset = pThrowInfo[3];
auto const pCatchArray = base + offset/4;
offset = pCatchArray[1];
auto const pCatchType = base + offset/4;
offset = pCatchType[1];
return static_cast<std::type_info const*>(static_cast<void
const*>(base + offset/4));
#else
return static_cast<std::type_info const *>(arg);
#endif
}
extern "C" {
#ifdef _MSC_VER
[[noreturn]]void _CxxThrowException(void *const p1, _s__ThrowInfo
const *const p2)
#else
[[noreturn]]void __cxa_throw(void *const p1, void *const p2, void
(* const pf)(void *))
#endif
{
static thread_local bool already_entered = false;
if ( already_entered ) std::terminate();
auto const handler = std::detail_current_throw_handler.load();
if ( handler )
{
already_entered = true;
handler(p1, *GetTypeInfoFromSecondArgument(p2));
already_entered = false;
}
#ifdef _MSC_VER
original_thrower(p1,p2);
#else
original_thrower(p1,p2,pf);
#endif
std::terminate();
}
}
// ====================================
// Now here comes the test code
// ====================================
#include <cstdio> // puts, printf
#include <stdexcept> // runtime_error
#include <typeinfo> // typeid, type_info
void MyLogger(void const *const p, std::type_info const &ti) noexcept
{
// Note that this function must be thread-safe
// and must never throw an exception (not even
// an exception that's caught and doesn't
// propagate)
char const *str = "<no info available>";
if ( typeid(std::runtime_error) == ti )
{
str = static_cast<std::runtime_error const*>(p)->what();
}
std::printf("Exception logged : '%s'\n",str);
}
int main(void)
{
std::puts("First line in main");
std::set_throw(MyLogger);
try
{
throw std::runtime_error("monkey");
}
catch(...)
{
std::puts("Exception caught");
}
std::puts("Last line in main");
}
>
> On Wed, Feb 28, 2024, 19:09 Frederick Virchanza Gotham wrote:
>>
>> I got this working, and so now when the C# program catches the
>> exception from C++, it's able to ask the DLL to see the exception log
>> to see what was thrown (along with a stack trace). When it comes to
>> the GNU g++ compiler, a quick web search shows me that people have
>> been able to do the same thing by providing an implementation of
>> '__cxa_throw'.
>
>
> Could you please share the source code? I've recently implemented
> something like your trick in Boost.Stacktrace for POSIX, and now I'm
> in search for a working Windows solution.
The guys here on the mailing list aren't really interested in this for
now, so please reply to my own email -- I want to see how it looks
when it's done. On the mailing list here for the time being, I'm just
going to focus on getting "typeid_except" accepted.
The following code is tested and working on the 64-Bit versions of the
following compilers: Microsoft VC++, GNU g++, LLVM clang++, Intel ICX.
It won't work on the 32-Bit version of the Microsoft compiler but it
would only be a half-hour of coding to get that working (it will
definitely work). Here is a GodBolt link:
https://godbolt.org/z/jEzrnrba4
And here's all the code copy-pasted:
#include <atomic> // atomic
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <typeinfo> // type_info
#ifdef _MSC_VER
# include <Windows.h> // LoadLibraryA, GetProcAddress
#else
# include <dlfcn.h> // dlsym, RTLD_NEXT
#endif
namespace std {
using throw_handler = void (*)(void const *, type_info const &);
// cannot mark as noexcept?
static atomic<throw_handler> detail_current_throw_handler{nullptr};
extern throw_handler get_throw(void) noexcept
{
return detail_current_throw_handler.load();
}
extern throw_handler set_throw(throw_handler const f) noexcept
{
return atomic_exchange(&detail_current_throw_handler, f);
}
}
namespace {
#ifdef _MSC_VER
using FuncPtr = void (*__stdcall)(void*,_s__ThrowInfo const*);
#else
using FuncPtr = void (*)(void*,void*,void (*)(void*));
#endif
FuncPtr GetOriginalThrower(void) noexcept
{
#ifdef _MSC_VER
auto const h = ::LoadLibraryA("vcruntime140.dll");
if ( nullptr == h ) return nullptr;
return (FuncPtr) ::GetProcAddress(h, "_CxxThrowException");
#else
return (FuncPtr) ::dlsym(RTLD_NEXT, "__cxa_throw");
#endif
}
FuncPtr const original_thrower = GetOriginalThrower();
}
std::type_info const *GetTypeInfoFromSecondArgument(void const *const arg)
{
if ( nullptr == arg ) return &typeid(void);
#ifdef _MSC_VER
auto const pThrowInfo = static_cast<__int32 const*>(arg);
auto const base = static_cast<__int32 const*>(static_cast<void
const*>(GetModuleHandle(nullptr)));
ptrdiff_t offset;
offset = pThrowInfo[3];
auto const pCatchArray = base + offset/4;
offset = pCatchArray[1];
auto const pCatchType = base + offset/4;
offset = pCatchType[1];
return static_cast<std::type_info const*>(static_cast<void
const*>(base + offset/4));
#else
return static_cast<std::type_info const *>(arg);
#endif
}
extern "C" {
#ifdef _MSC_VER
[[noreturn]]void _CxxThrowException(void *const p1, _s__ThrowInfo
const *const p2)
#else
[[noreturn]]void __cxa_throw(void *const p1, void *const p2, void
(* const pf)(void *))
#endif
{
static thread_local bool already_entered = false;
if ( already_entered ) std::terminate();
auto const handler = std::detail_current_throw_handler.load();
if ( handler )
{
already_entered = true;
handler(p1, *GetTypeInfoFromSecondArgument(p2));
already_entered = false;
}
#ifdef _MSC_VER
original_thrower(p1,p2);
#else
original_thrower(p1,p2,pf);
#endif
std::terminate();
}
}
// ====================================
// Now here comes the test code
// ====================================
#include <cstdio> // puts, printf
#include <stdexcept> // runtime_error
#include <typeinfo> // typeid, type_info
void MyLogger(void const *const p, std::type_info const &ti) noexcept
{
// Note that this function must be thread-safe
// and must never throw an exception (not even
// an exception that's caught and doesn't
// propagate)
char const *str = "<no info available>";
if ( typeid(std::runtime_error) == ti )
{
str = static_cast<std::runtime_error const*>(p)->what();
}
std::printf("Exception logged : '%s'\n",str);
}
int main(void)
{
std::puts("First line in main");
std::set_throw(MyLogger);
try
{
throw std::runtime_error("monkey");
}
catch(...)
{
std::puts("Exception caught");
}
std::puts("Last line in main");
}
Received on 2024-03-04 16:43:53