Есть ли лучший способ реализовать это управление памятью? - PullRequest
2 голосов
/ 02 декабря 2011

Это продолжение моего предыдущего вопроса об исключениях .

У меня есть устаревший код, который я пытаюсь сохранить. У него есть пользовательский компонент управления памятью, который мне трудно понять.

Мое понимание системы следующее:

Вызывающая функция запрашивает некоторую память, которая будет выделена для нее, обеспечивая начальный объем необходимой памяти (needed) и максимальный объем (max). Звонит:

base = VirtualAlloc(0, max, MEM_RESERVE, PAGE_NOACCESS);

Что я понимаю, резервирует память, но не предоставляет доступ. Другими словами, если я попытаюсь записать в зарезервированный сегмент, я получу нарушение прав доступа.

Затем он звонит:

VirtualAlloc(base, needed, MEM_COMMIT, PAGE_READWRITE);

Что делает доступным needed объем памяти, начиная с base.

Слепая часть возникает при попытке определить, когда нужно сделать доступным больше памяти. Насколько я понимаю, система пытается перехватить исключения нарушения прав доступа, когда они возникают, и вызвать адрес VirtualAlloc, чтобы сделать доступной память.

Это делается путем объявления следующего метода:

unsigned long __cdecl
exceptionCatch(struct _EXCEPTION_RECORD* er, void*, struct _CONTEXT* cr, void*)
{
    if( er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
      && ExtendBuffer( (void*)er->ExceptionInformation[1] ) )
        return ExceptionContinueExecution;

    return ExceptionContinueSearch;
}

Затем он регистрирует это как обработчик исключений для вершины стека (я думаю), используя этот особенно ужасный кусок кода:

void __cdecl SetHandler(bExceptionRegistration& v)
{
    __asm
    {
        mov     eax, 8[ebp]     ; get exception register record to install
        mov     ecx, fs:[0]     ; get current head of chain

        cmp     ecx, eax        ; should we be at head?
        jb      search
        mov     [eax], ecx      ; save current head
        mov     fs:[0], eax     ; install new record at head
        jmp     short ret1
search:
        cmp     [ecx], eax      ; at proper location yet?
        ja      link
        mov     ecx, [ecx]      ; get next link
        jmp     search
link:
        mov     edx, [ecx]
        mov     [eax], edx      ; point to next
        mov     [ecx], eax
ret1:
    }
}

Этот метод вызывается путем создания экземпляра определенного класса в области видимости метода. Похоже, что он применяет обработчик только к текущему контексту стека; Например, исключения, вызванные в вызываемых функциях, не обрабатываются текущим методом, если исключение не распространяется на текущий метод.

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

Полагаю, мои основные вопросы:

  1. Есть ли какая-то конкретная причина, почему это не должно работать? Редактировать: Основываясь на моих собственных тестах и ​​комментариях, я думаю, что ассемблерный код - это проблемная область.
  2. Что более важно, есть ли лучший способ сделать то, что я думаю, код пытается сделать?

Я не думаю, что что-то вроде set_unexpected возможно, так как управление памятью применяется только к этой конкретной библиотеке, а клиентское приложение может (и в нашем случае имеет) собственный обработчик неожиданных исключений.

Edit:

Установка и отключение обработчика на стек выполняется объявлением класса bExceptionRegistration со следующим конструктором класса и деструктором:

bExceptionRegistration :: bExceptionRegistration() : function(exceptionCatch)
{
    SetHandler(*this);
}

bExceptionRegistration :: ~bExceptionRegistration()
{
    UnsetHandler(*this);
}

Итак, чтобы на самом деле установить обработчик для конкретной области стека, вам потребуется:

void someFunction()
{
    bExceptionRegistration er;
    // do some stuff here
}

Редактировать: Я предполагаю, что, вероятно, наиболее подходящим решением для всего этого является замена объявлений bExceptionRegistration в коде на блоки __try, __except. Однако я надеялся избежать этого, так как это происходит во многих местах.

1 Ответ

2 голосов
/ 02 декабря 2011

Я не уверен на 100% в этом, не увидев больше кода.Он не регистрирует обработчик исключений в верхней части стека, но использует прием для вставки обработки исключений, где определена структура EXCEPTION_REGISTRATION.Так, например (возможно, в вашем случае это реализовано немного по-другому):

void function3(EXCEPTION_REGISTRATION& handler)
{
    SetHandler(handler);
    //Do other stuff
}
void function2(EXCEPTION_REGISTRATION& handler)
{
    __try
    {
        //Do something
        function3(handler);
    }
    __except(expression)
    {
        //...
    }
}

void function()
{
    EXCEPTION_REGISTRATION handler;
    //..Init handler
    function2(handler)
}

Когда вы вызываете SetHandler, он вставит обработку исключений, как это было в области действия функции.Таким образом, в этом случае в момент вызова SetHandler это будет выглядеть так, как будто в функции есть блок __try __except.

Поэтому, если есть исключение внутри функции 3, сначала будет вызван обработчик в функции, и еслиОбработчик этого не обрабатывает, будет вызываться обработчик, установленный SetHandler.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...