Как стать автором
Обновить

Продолжение серии статей про работу исключений в С++ «под капотом»

Время на прочтение3 мин
Количество просмотров3.6K

Когда я работал над своей курсовой про то, как передавать С++ исключения из ядра ОС пользовательским программам, я изучал, как работают исключения в С++ и в этом мне очень помогла эта серия статей с Хабра. Теперь я хочу дополнить некоторые моменты, которые недостают в этой серии статей.

Запуск кода автора изначальной серии статей

Первое, что вы заметите, если будете изучать работу исключений С++ по той серии - это то, что код, приведённый там, падает с Segmentation Fault, если вы используете 64-ёх битный процессор.

Дело в том, что в 64-ёх битном режиме компилятор g++ пишет указатель на структуру std::type_info в совершенно диком виде. Чтобы его раскодировать, нужен такой код:

void* read_int_tbltype(const int* ptr, uint8_t type_encoding) {
    if (type_encoding == 3) {
          return (void *) *ptr; // случай кодировки здорового человека
          // так было в 32-ух битном gcc и в clang
    } else if (type_encoding == 0x9b) { // 64-ёх битный режим курильщика (gcc)
        uintptr_t result = *ptr;
        if (result == 0) {
            return nullptr;
        }
        result += (uintptr_t) ptr;
        result = *((uintptr_t*)result);
        return (void *)result;
    } else {
        exit(0);
    }
}

Дополнения к функциям C++ ABI, чтобы сделать реализацию более корректной

В репозитории автора изначальной серии статей функции __cxa_begin_catch и __cxa_end_catch не делают ничего, кроме отладочного вывода.

В реальности у этих двух функций две задачи: во-первых,__cxa_begin_catch должна получить по указателю структуру _Unwind_Exception и вернуть указатель на выброшенное исключение, чтобы код catch блока мог получить к нему доступ - иначе читать его он не сможет. Во-вторых, эти две функции должны обеспечить вызов деструктора выброшенного исключения и освобождение памяти, выделенной для исключения и информацию об исключении. Для этого первая функция должна положить это исключение на стек исключений, а вторая - вызвать его деструктор, если надо, а потом осводобить память. Стек исключений нужен на случай, если одновременно в обработке будут два исключения. Он должен быть thread_local. Итоговый код получается такой:

  // for freeing exceptions
  thread_local __cxa_exception* last_exception;

  void* __cxa_begin_catch(void* raw_unwind_exception) throw() {
        auto unwind_exception = (_Unwind_Exception *)raw_unwind_exception;
        __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception + 1) - 1;
        exception_header->nextException = last_exception;
        last_exception = exception_header;
        return exception_header + 1;
    }


    void __cxa_end_catch() {
        // we need do destroy the last exception
        __cxa_exception* exception_header = last_exception;
        last_exception = exception_header->nextException;
        if (exception_header->exceptionDestructor) {
            (*exception_header->exceptionDestructor)(exception_header + 1);
        }
        free(exception_header->exceptionTypeName);
        __cxa_free_exception((char *)(exception_header + 1));
    }

Ещё есть функция __cxa_get_exception_ptr она вызывается в том случае, если исключение ловится по ссылке, поэтому код из репозитория автора оригинальной статьи компилируется без неё. Эта функция в основном аналогична функции __cxa_begin_catch и возвращает указатель на оригинальное исключение.

  void* __cxa_get_exception_ptr(_Unwind_Exception* unwind_exception) {
        __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception + 1) - 1;
        return exception_header + 1;
    }

Кажется, это всё, что я дополнил к реализации из репозитория автора оригинальной статьи. Мой код можно посмотреть здесь. Единственное (кажется), что там не реализовано - это то, что catch блок может поймать не только то исключение, которое указано у него, но и те, что от него унаследованы.

Если интересно, в следующий раз я могу написать про реализацию поддержки thread_local переменных.

Теги:
Хабы:
Всего голосов 3: ↑2 и ↓1+3
Комментарии3

Публикации

Истории

Работа

QT разработчик
4 вакансии
Программист C++
106 вакансий

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань