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

от автора

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

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

Первое, что вы заметите, если будете изучать работу исключений С++ по той серии — это то, что код, приведённый там, падает с 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 переменных.


ссылка на оригинал статьи https://habr.com/ru/post/700458/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *