Это продолжение моего предыдущего вопроса об исключениях .
У меня есть устаревший код, который я пытаюсь сохранить. У него есть пользовательский компонент управления памятью, который мне трудно понять.
Мое понимание системы следующее:
Вызывающая функция запрашивает некоторую память, которая будет выделена для нее, обеспечивая начальный объем необходимой памяти (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
, и выполнение не появляется для ее ввода.
Полагаю, мои основные вопросы:
- Есть ли какая-то конкретная причина, почему это не должно работать? Редактировать: Основываясь на моих собственных тестах и комментариях, я думаю, что ассемблерный код - это проблемная область.
- Что более важно, есть ли лучший способ сделать то, что я думаю, код пытается сделать?
Я не думаю, что что-то вроде 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
. Однако я надеялся избежать этого, так как это происходит во многих местах.