Как включить / SAFESEH с ассемблерным кодом / SEH-производительностью - PullRequest
0 голосов
/ 16 апреля 2020

Я разработал небольшую программу, которая проверяет производительность 32-битной Windows обработки структурированных исключений. Чтобы минимизировать издержки по сравнению с остальными, я написал код, генерирующий фильтрацию исключения в сборке.

Это код C ++:

#include <Windows.h>
#include <iostream>

using namespace std;

bool __fastcall getPointerFaultSafe( void *volatile *from, void **to );

int main()
{
    auto getThreadTimes = []( LONGLONG &kt, LONGLONG &ut )
    {
        union
        {
            FILETIME ft;
            LONGLONG ll;
        } creationTime, exitTime, kernelTime, userTime;
        GetThreadTimes( GetCurrentThread(), &creationTime.ft, &exitTime.ft, &kernelTime.ft, &userTime.ft );
        kt = kernelTime.ll;
        ut = userTime.ll;
    };
    LONGLONG ktStart, utStart;
    getThreadTimes( ktStart, utStart );
    size_t const  COUNT = 100'000;
    void         *pv;
    for( size_t c = COUNT; c; --c )
        getPointerFaultSafe( nullptr, &pv );
    LONGLONG ktEnd, utEnd;
    getThreadTimes( ktEnd, utEnd );
    double ktNsPerException = (ktEnd - ktStart) * 100.0 / COUNT,
           utNsPerException = (utEnd - utStart) * 100.0 / COUNT;
    cout << "kernel-time per exception: " << ktNsPerException << "ns" << endl;
    cout << "user-time per exception:   " << utNsPerException << "ns" << endl;
    return 0;
}

Это сборка- код:

.686P
PUBLIC ?getPointerFaultSafe@@YI_NPCRAXPAPAX@Z
PUBLIC sehHandler
.SAFESEH sehHandler
sehHandler PROTO

_DATA SEGMENT
byebyeOffset dd 0
_DATA ENDS

exc_ctx_eax = 0b0h
exc_ctx_eip = 0b8h

_TEXT    SEGMENT

?getPointerFaultSafe@@YI_NPCRAXPAPAX@Z PROC
ASSUME ds:_DATA
    push    OFFSET sehHandler
    push    dword ptr fs:0
    mov     dword ptr fs:0, esp
    mov     byebyeOffset, OFFSET byebye - OFFSET mightfail
    mov     al, 1
mightfail:
    mov     ecx, dword ptr [ecx]
    mov     dword ptr [edx], ecx
byebye:
    mov     edx, dword ptr [esp]
    mov     dword ptr fs:0, edx
    add     esp, 8
    ret     0
?getPointerFaultSafe@@YI_NPCRAXPAPAX@Z ENDP

sehHandler PROC
    mov     eax, dword ptr [esp + 12]
    mov     dword ptr [eax + exc_ctx_eax], 0
    mov     edx, byebyeOffset
    add     [eax + exc_ctx_eip], edx
    mov     eax, 0
    ret     0
sehHandler ENDP


_TEXT    ENDS
END
  1. Как получить asm-модуль моей программы / SAFESEH-совместимый?
  2. Почему эта программа потребляет так много пользовательского ЦП-времени? Библиотечный код, вызываемый операционной системой после начала обработки исключения, должен сохранять только все регистры в структуре CONTEXT, заполнять структуру EXCEPTION_RECORD, вызывать самый верхний фильтр исключений, который - в данном случае - сдвигает выполнение еще на две инструкции, и при возврате он в моем случае восстановит все регистры для продолжения выполнения в соответствии с тем, что я возвратил в EAX. Это не должно быть так много времени, что почти треть процессорного времени будет потрачено в пользовательском пространстве. Это около 2,3 мс, т.е. когда мой старый Ryzen 1800X работает на одном ядре с 4 ГГц, около 5 200 тактов.
  3. Я использую переменную byebyeOffset в своем коде для переноса расстояния между небезопасными инструкция, которая может сгенерировать нарушение доступа и код безопасности впоследствии. Я инициализирую эту переменную перед небезопасной инструкцией. Но было бы неплохо иметь это смещение статически как немедленное в точке, где я добавляю его в EIP в функции фильтра исключений sehHandler; но смещения ограничены getPointerFaultSafe. Конечно, сохранение смещения и выборка его из переменной занимают незначительную часть общего времени вычислений, но было бы лучше иметь чистое решение.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...