Skip to content

Crash when throwing exceptions in ObjC++ due to __cxa_exception struct mismatch #146

@triplef

Description

@triplef

Throwing exceptions in Objective C++ code currently crashes with some setups. #138 adds tests that expose (some of?) these issues.

According to @davidchisnall:

I've had a look and found a few minor bugs that are easy to fix. Unfortunately, when I fix them I find some more interesting issues that arise from the way that we're now creating the exception by hijacking the C++ runtime library.
I think that the solution that we're going to have to end up with is using that path only to figure our which structure we have and then casting to the correct variant structure and creating it ourselves.

I have created cxa_exception_variants.cpp containing the four different possible layouts of the __cxa_exception struct that are found in libobjc2, libcxxrt, and libunwind:

  • There are two different versions of the __cxa_exception struct: without and with the added "referenceCount" field.
  • However the struct also references _Unwind_Exception, which itself comes in two different variants: without and with an "__aligned__" attribute (and an added "reserved" field on x86).

This leaves us with a total of four different variants of __cxa_exception:

  • __cxa_exception_v1: without referenceCount field, using unaligned _Unwind_Exception
  • __cxa_exception_v2: without referenceCount field, using aligned _Unwind_Exception
  • __cxa_exception_v3: with referenceCount field, using unaligned _Unwind_Exception
  • __cxa_exception_v4: with referenceCount field, using aligned _Unwind_Exception

Depending on the architecture some of these will have the same size. Following are the sizes for the different architectures as printed by the attached tool.

x86

_Unwind_Exception_v1	size: 20, alignment: 4
_Unwind_Exception_v2	size: 32, alignment: 16

__cxa_exception_v1	size: 64, alignment: 4
__cxa_exception_v2	size: 80, alignment: 16
__cxa_exception_v3	size: 68, alignment: 4
__cxa_exception_v4	size: 80, alignment: 16

x86_64

_Unwind_Exception_v1	size: 32, alignment: 8
_Unwind_Exception_v2	size: 32, alignment: 16

__cxa_exception_v1	size: 112, alignment: 8
__cxa_exception_v2	size: 112, alignment: 16
__cxa_exception_v3	size: 120, alignment: 8
__cxa_exception_v4	size: 128, alignment: 16

ARM32

_Unwind_Exception_v1	size: 88, alignment: 4
_Unwind_Exception_v2	size: 88, alignment: 8

__cxa_exception_v1	size: 132, alignment: 4
__cxa_exception_v2	size: 136, alignment: 8
__cxa_exception_v3	size: 136, alignment: 4
__cxa_exception_v4	size: 136, alignment: 8

ARM64

_Unwind_Exception_v1	size: 104, alignment: 8
_Unwind_Exception_v2	size: 104, alignment: 8

__cxa_exception_v1	size: 184, alignment: 8
__cxa_exception_v2	size: 184, alignment: 8
__cxa_exception_v3	size: 192, alignment: 8
__cxa_exception_v4	size: 192, alignment: 8

I’m not 100% sure which of these variants are actually used in the wild, although I think that FreeBSD 12.0 is using v2, and libcxxrt was using v3 and is now using v4 after libcxxrt/libcxxrt#1 was merged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions