Виключення в UEFI додатку

Будь-якому програмісту, який знайомий з UEFI, відомо, що вбудованого механізму обробки виключень там немає. Мова йде про try/except блоках, які є розширенням Microsoft C/C + + компіляторів. Буває дуже корисно мати такий механізм і в повному обсязі користуватися тими перевагами, які він дає. Тому у даній статті мова піде саме про вирішення цієї проблеми. Також до статті додається повна реалізація механізму з його демонстрацією на базі UEFI програми. Порушені тільки 64х бітні процесори фірми Intel, і в обговоренні маються на увазі тільки вони. Реалізація механізму знаходиться в папці exceptions сховища git за адресою: https://github.com/anatolymik/machineries.git.

Спочатку трохи поговоримо про те, як взагалі обробляються виключення. Перш за все, виняток це особлива ситуація. Як правило, виключення виникає з-за того, що процесор не може виконати конкретну інструкцію. Коли така подія має місце, процесор викликає обробник винятку, точка входу якого знаходиться в ІDT таблиці. Винятків у процесора безліч, тому відповідно до його типом викликається відповідний обробник.


Як видно з малюнка вище, обробників винятків лише 256. Причому перші 32 зарезервовані під виключення. Інші використовуються для обробки переривань, про яких тут ми говорити не будемо. Як вже було згадано, згідно з типом виключення викликається відповідний обробник. Наприклад, при діленні числа на 0, викликається обробник винятку 0, або якщо процесор зустріне в потоці незнайому йому інструкцію, то буде викликаний обробник винятку 6. Більш докладно про це можна дізнатися в «Intel 64 and IA-32 Architectures Software developer's Manual». Ми тільки що торкнулися апаратні засоби процесора, без яких обробка апаратних помилок неможлива, але це не все.

Говорячи про try/except блоках, слід згадати, що коли компілятор зустрічає їх, то він генерує метаінформацію, яка описує розташування кожного блоку в виконуваному файлі. Ця інформація розташований у відокремленому місці, кажучи точніше, в .pdata секції PE образу.


Як видно з малюнка, код, дані і разносортная інформація про виконуваному файлі (наприклад, як у нашому випадку описатели try/except блоків) розміщуються в єдиному образі. Всі ці дані розподілені по так званим секціях, у відповідності з типом самих даних. Повертаючись до обговорення .pdata секції, слід згадати, що для кожного try/except блоку, крім його розташування, зберігається також і адресу обробника. Як правило, це код, укладений в блоки except і finally. Детальніше про PE форматі файлу можна довідатися в «Microsoft Portable Executable and Common Object File Format Specification». Ми тільки що торкнулися програмні засоби, необхідні для функціонування розглянутого механізму, тепер подивимося на все це ще раз комплексно.

Незважаючи на те, що try/except блоки обробляються компілятором в процесі генерації коду, і взагалі вони є механізмом самої мови, їх функціонування вимагає підтримки з боку операційної системи. Причому, слід зазначити, що частина обробки виконується операційною системою, а частина виконується самим виконуваним файлом. Також існує деяка частина, яка виконується самим виконуваним файлом, але не генерується компілятором, і замість цього її потрібно реалізувати. Наприклад, зарезервоване ім'я __c_specific_handler є ім'ям функції, яка відповідає за обробку виключення. В разі відсутності її реалізації, компонувальник не зможе створити виконуваний файл. У засобах розробки для Windows реалізації цих функцій включені в бібліотеки, які компонуються з додатком.


Як показано на малюнку вище, в момент виникнення виключення процесор викликає відповідний обробник з IDT таблиці. Обробники винятків встановлює операційна система під час ініціалізації, тобто в момент виникнення винятку управління отримує сама операційна система. Обробники всіх винятків дуже схожі, вони зберігають всю необхідну інформацію для обробки та інформацію, унікальну для конкретного типу виключення. Потім всі ці обробники викликають функцію пошуку і виклику обробника. Функція сканує .pdata секцію з метою знайти обробник, відповідний конкретної ділянки коду, в якому виникло виключення. І якщо обробник знайдений, то операційна система передає йому управління, в іншому випадку програма завершується з помилкою. Ми тільки що розглянули процес виникнення та обробки виключень, тому можна перейти до обговорення того, що необхідно зробити в UEFI додатку для того, щоб винятку повноцінно функціонували. Також, слід зазначити, що наведене опис є дуже спрощеним.

Оскільки в UEFI немає нічого, що мало б хоч якесь відношення до винятків, то очевидно, що все вищеописане необхідно реалізувати. Далі в процесі перерахування з метою полегшення вивчення вихідних кодів з прикладеною до статті реалізації, ми будемо посилатися на її функції, файли і папки. В першу чергу, необхідно реалізувати обробника виключень. Вони розташовуються в exccpu.asm файл з папки exc. Також необхідно встановити їх адреси в ІDT, це виконує функція CpuInitializeExceptionHandlers, розташована в exccpu.cpp файл з папки exc. Майже всі з цих обробників викликають CpuDispatchException функцію з файлу exccpu.cpp, яка фактично і є відправною точкою в пошуку і виклику обробника виключення. Функції DispatchException і UnwindStack, розташовані у файлі excdsptch.cpp з папки exc, відповідають за пошук та виклик обробників винятків. Щоб сканувати .pdata секцію, необхідна реалізація функцій обробки PE образу. Ці функції використовуються раніше згаданими функціями пошуку і виклику обробників винятків. Реалізація функцій сканування PE образу згруповані в окрему папку — pe. І, нарешті, необхідні реалізації зарезервованих функцій __C_specific_handler і _local_unwind. Обидві функції реалізовані в excchandler.cpp файл з папки exc. Точкою входу в додаток є функція EfiMain, розташована у файлі efi.cpp з кореня проекту. Слід зазначити, що установка адреси обробників в ІDT спрощено у демонстраційних цілях. Якщо говорити про повноцінної реалізації, то вам потрібно буде ізольована ІDT.

Мабуть, на цьому все. Варто тільки додати, що опис цього механізму багаторазово висвітлювалося на просторах Інтернету в самих різних деталях. І дану статтю унікальною робить швидше додається реалізація і демонстраційний приклад. Хоча, спочатку коли виникла потреба в реалізації даного механізму для проектів, що працюють поза середовища Windows, раніше згаданих описів виявилося недостатньо. У тому числі доводилося реверсить Windows, а опис UNWIND_INFO версії 2 не вдалося знайти взагалі. Тому спочатку стаття замислювалася в іншому форматі, у дуже докладному, після прочитання якої не залишилося б жодних питань. На практиці, її написання виродилося в написання документації, а не статті, читати яку може виявитися нудним заняттям, оскільки з самого початку зрозуміло, яка проблема вирішується. Тому був написаний саме такий, простий і легкий варіант, без всяких деталей. А докладний варіант планується публікувати по частинах.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.