Skip to content

[BUG]: GCC/Clang exceptions are exported without any ABI change mitigation #5359

Closed

Description

@feltech

Required prerequisites

What version (or hash if on master) of pybind11 are you using?

2.9, 2.10 (code seems similar on current master)

Problem description

pybind11 enforces hidden visibility for all symbols, apart from exceptions (including error_already_set), where the PYBIND11_EXPORT_EXCEPTION macro is used to grant them default visibility . This is presumably to allow pybind11-specific exceptions to be caught across DSO boundaries, which is useful.

However, if the DSOs use different versions of pybind11 internally, then e.g. catching error_already_set, even as a std::exception, and calling .what() on it can give garbled text (at best).

We experienced this exact scenario with a library built against pybind11 2.10 and an application built against 2.9. Between these versions, the ABI of error_already_set changed dramatically.

I would suggest an inline namespace (using the PYBIND11_VERSION_MAJOR and PYBIND11_VERSION_MINOR macros to construct a suitable name). This namespace would wrap all exported types (i.e. exceptions), or perhaps just all of pybind11 (which has the advantage that there is then technically no need to enforce hidden visibility for other symbols). E.g. the full symbolic namespace could be pybind11::v2_13 but code could still refer to it as pybind11 (because of the inline).

Reproducible example code

Tricky to put together a code example. I will explain in words what happens in our product.

You need a full example project with two DSOs. In the order they are loaded, the first uses pybind11 2.9 and the second uses 2.10. 

The first DSO calls through to the second, which calls out to a Python function. 

The Python function then `raise`s. 

The first DSO catches the resulting C++ exception as a `std::exception` and calls `.what()` on it to verify the expected exception message. It will be garbled.

You can see on stepping through a GCC 11 build using GDB, the 2.10 class is used to construct the exception, but it gets the 2.9 vtable pointer.

Is this a regression? Put the last known working version here if it is.

Not a regression

Activity

  1. feltech commented on May 9, 2025

    @feltech
    Author

    This looks like a dupe of #4105 - on scanning briefly through the comments, PYBIND11_EXPORT_EXCEPTION pops up a lot.

    I suspect its (mostly, see below) fixed by #4298 - available since v2.10.1. But need to double-check.

    Unfortunately that fix won't solve the problem if one of the DSOs involved is stuck on an older pybind11 version.

  2. henryiii commented on Jul 25, 2026

    @henryiii
    Collaborator

    🤖 AI text below 🤖

    Since #4298 (released in v2.10.1), PYBIND11_EXPORT_EXCEPTION is empty except on libc++. GCC and libstdc++ exception types therefore no longer get forced default visibility. To be clear, this does not help a process in which one DSO stays pinned to a pre-2.10.1 pybind11.

    If you can still reproduce this with current master (v3), please comment and we can reopen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageNew bug, unverified

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions