Skip to content

Commit

Permalink
Add objc_setUncaughtExceptionHandler() API
Browse files Browse the repository at this point in the history
  • Loading branch information
triplef committed Aug 24, 2023
1 parent 0aea4b2 commit 3b6b960
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 13 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ set(libobjc_HDRS
objc/objc-arc.h
objc/objc-auto.h
objc/objc-class.h
objc/objc-exception.h
objc/objc-runtime.h
objc/objc-visibility.h
objc/objc.h
Expand Down
2 changes: 1 addition & 1 deletion Test/UnexpectedException.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ int main(void)
SetUnhandledExceptionFilter(&_UnhandledExceptionFilter);
#endif

_objc_unexpected_exception = _UncaughtExceptionHandler;
objc_setUncaughtExceptionHandler(_UncaughtExceptionHandler);
@throw exceptionObj;
assert(0 && "should not be reached!");

Expand Down
6 changes: 6 additions & 0 deletions eh_personality.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "dwarf_eh.h"
#include "objc/runtime.h"
#include "objc/hooks.h"
#include "objc/objc-exception.h"
#include "class.h"
#include "objcxx_eh.h"

Expand Down Expand Up @@ -760,3 +761,8 @@ void objc_exception_rethrow(struct _Unwind_Exception *e)
_Unwind_Resume_or_Rethrow(e);
abort();
}

objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler)
{
return __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST);
}
27 changes: 15 additions & 12 deletions eh_win32_msvc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <vector>

#include "objc/runtime.h"
#include "objc/hooks.h"
#include "objc/objc-exception.h"
#include "visibility.h"

#include <windows.h>
Expand Down Expand Up @@ -57,6 +57,7 @@ struct _MSVC_ThrowInfo
};

static LPTOP_LEVEL_EXCEPTION_FILTER originalUnhandledExceptionFilter = nullptr;
void (*_objc_unexpected_exception)(id exception);
LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* exceptionInfo);

#if defined(_WIN64)
Expand Down Expand Up @@ -197,17 +198,6 @@ OBJC_PUBLIC extern "C" void objc_exception_throw(id object)
exception.ExceptionInformation[2] = reinterpret_cast<ULONG_PTR>(&ti);
exception.ExceptionInformation[3] = reinterpret_cast<ULONG_PTR>(&x);

// Set unhandled exception filter to support _objc_unexpected_exception hook.
// Unfortunately there doesn't seem to be a better place to call this, as
// installing it at construction time means that it will get overwritten by
// the handler installed by the VC runtime. This way it works, but as a side
// effect throwing exceptions will overwrite any previously installed handler,
// so we save it and call it as part of our handler.
LPTOP_LEVEL_EXCEPTION_FILTER previousExceptionFilter = SetUnhandledExceptionFilter(&_objc_unhandled_exception_filter);
if (previousExceptionFilter != &_objc_unhandled_exception_filter) {
originalUnhandledExceptionFilter = previousExceptionFilter;
}

#ifdef _WIN64
RtlRaiseException(&exception);
#else
Expand Down Expand Up @@ -282,3 +272,16 @@ LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* excepti
// Since this is the last one, it is not likely to find any more.
return EXCEPTION_CONTINUE_SEARCH;
}

OBJC_PUBLIC extern "C" objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler)
{
objc_uncaught_exception_handler previousHandler = __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST);

// set unhandled exception filter to support hook
LPTOP_LEVEL_EXCEPTION_FILTER previousExceptionFilter = SetUnhandledExceptionFilter(&_objc_unhandled_exception_filter);
if (previousExceptionFilter != &_objc_unhandled_exception_filter) {
originalUnhandledExceptionFilter = previousExceptionFilter;
}

return previousHandler;
}
5 changes: 5 additions & 0 deletions objc/hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@ OBJC_PUBLIC extern struct objc_slot *(*__objc_msg_forward3)(id, SEL) OBJC_DEPREC
* handles the forwarding.
*/
OBJC_PUBLIC extern IMP (*__objc_msg_forward2)(id, SEL);

#ifndef _WIN32
/**
* Hook defined for handling unhandled exceptions. If the unwind library
* reaches the end of the stack without finding a handler then this hook is
* called.
* Deprecated. Use objc_setUncaughtExceptionHandler() instead.
*/
OBJC_HOOK void (*_objc_unexpected_exception)(id exception);
#endif

/**
* Hook defined to return the class to be used for boxing a foreign exception
* type. The class must implement:
Expand Down
33 changes: 33 additions & 0 deletions objc/objc-exception.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__)
#pragma clang system_header
#endif
#include "objc-visibility.h"

#ifndef __OBJC_EXCEPTION_INCLUDED__
#define __OBJC_EXCEPTION_INCLUDED__

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*objc_uncaught_exception_handler)(id exception);

/**
* Throw a runtime exception. Inserted by the compiler in place of @throw.
*/
OBJC_PUBLIC
void objc_exception_throw(id object);

/**
* Installs handler for uncaught Objective-C exceptions. If the unwind library
* reaches the end of the stack without finding a handler then the handler is
* called. Returns the previous handler.
*/
OBJC_PUBLIC
objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler);

#ifdef __cplusplus
}
#endif

#endif // __OBJC_EXCEPTION_INCLUDED__

0 comments on commit 3b6b960

Please sign in to comment.