Модификация стека в Windows, TIB и исключения

Происхождение моего вопроса фактически связано с желанием обеспечить реализацию pthreads в Windows, которая поддерживает пользовательские стеки. В частности, pthread_attr_setstack должен сделать что-то значимое. Мои реальные требования немного более сложны, чем это, но этого достаточно для целей поста.

Не существует общедоступных API-интерфейсов Win для предоставления стека в API-интерфейсе Fiber или Thread. Я искал подлых бэкдоров, обходных путей и взломов, ничего не происходит. На самом деле, я посмотрел, что источник вдохновения winpthread и что игнорирует любой стек, предоставленный pthread_attr_setstack,

Вместо этого я попробовал следующее «решение», чтобы увидеть, будет ли оно работать. Я создаю волокно, используя обычную комбинацию ConvertThreadToFiber, CreateFiberEx а также SwitchToFiber, В CreateFiberEx Я предоставляю минимальный размер стека. Затем в точке входа оптоволокна я выделяю память для стека, соответствующим образом изменив поля TIB: «База стека» и «Предел стека» (см. Здесь: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block), а затем установите ESP на высокий адрес моего стека.

(В реальном мире я бы настроил стек лучше, чем этот, и изменил бы также EIP, чтобы этот шаг больше походил на функцию posix swapcontext, Но ты получил идею).

Если я делаю какие-либо вызовы ОС, когда на этот другой стек, то я в значительной степени облажался (printf например умирает). Однако это не проблема для меня. Я могу гарантировать, что никогда не буду проверять вызовы в моем пользовательском стеке (поэтому я и сказал, что мои реальные требования немного сложнее). Кроме … Мне нужны исключения для работы. И они не делают! В частности, если я попытаюсь сгенерировать и перехватить исключение в моем измененном стеке, я получу подтверждение

Необработанное исключение в 0xXXXXXXXX ….

Итак, мой (расплывчатый) вопрос: есть ли у кого-нибудь понимание того, как исключения и пользовательский стек могут не очень хорошо играть вместе? Я ценю, что это абсолютно не поддерживается и может счастливо, кроме нулевого ответа или «уйти». На самом деле, я в значительной степени решил, что мне нужно другое решение, и, несмотря на компромисс, я, скорее всего, буду его использовать. Однако любопытство одолевает меня, поэтому я хотел бы знать, почему это не работает.

В связи с этим мне было интересно, как Cygwin справился с этим для ucontext. Источник здесь http://szupervigyor.ddsi.hu/source/in/openjdk-6-6b18-1.8.13/cacao-0.99.4/src/vm/jit/i386/cygwin/ucontext.c использования GetThreadContext/SetThreadContext реализовать UContext. Тем не менее, из экспериментов я вижу, что это также терпит неудачу, когда исключение выдается изнутри нового контекста. На самом деле SetThreadContext вызов даже не обновляет блок TIB!

РЕДАКТИРОВАТЬ (основываясь на ответе @avakar)

Следующий код, который очень похож на ваш, демонстрирует ту же ошибку. Разница в том, что я не запускаю второй поток, приостановив его, а затем пытаюсь изменить контекст. Этот код показывает ошибку, которую я описывал, когда в блоке try-catch foo, Возможно, это просто не законно. Примечательно, что в этой ситуации ExceptionList Член TIB является действительным указателем, когда modifyThreadContext называется, тогда как в вашем примере это -1. Редактирование вручную это не помогает.

Как уже упоминалось в моем комментарии к вашему ответу. Это не совсем то, что мне нужно. Я хотел бы переключить контексты из потока, в котором я нахожусь. Тем не менее, документы для SetThreadContext предупредить, чтобы не вызывать это в активном потоке. Поэтому я предполагаю, что если приведенный ниже код не работает, то у меня нет шансов заставить его работать в одном потоке.

namespace
{
HANDLE ghSemaphore = 0;

void foo()
{
try
{
throw 6;
}
catch(...){}

ExitThread(0);
}

void modifyThreadContext(HANDLE thread)
{
typedef NTSTATUS WINAPI NtQueryInformationThread_t(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);

HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
auto NtQueryInformationThread = (NtQueryInformationThread_t *)GetProcAddress(hNtdll, "NtQueryInformationThread");

DWORD stackSize = 1024 * 1024;
void * mystack = VirtualAlloc(0, stackSize, MEM_COMMIT, PAGE_READWRITE);

DWORD threadInfo[7];
NtQueryInformationThread(thread, 0, threadInfo, sizeof threadInfo, 0);

NT_TIB * tib = (NT_TIB *)threadInfo[1];

CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(thread, &ctx);

ctx.Esp = (DWORD)mystack + stackSize - ((DWORD)tib->StackBase - ctx.Esp);
ctx.Eip = (DWORD)&foo;
tib->StackBase = (PVOID)((DWORD)mystack + stackSize);
tib->StackLimit = (PVOID)((DWORD)mystack);
SetThreadContext(thread, &ctx);
}

DWORD CALLBACK threadMain(LPVOID)
{
ReleaseSemaphore(ghSemaphore, 1, NULL);
while (1)
Sleep(10000);
// Never gets here
return 1;
}
} // namespace

int main()
{
ghSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
HANDLE th = CreateThread(0, 0, threadMain, 0, 0, 0);

while (WaitForSingleObject(ghSemaphore, INFINITE) != WAIT_OBJECT_0);

SuspendThread(th);

modifyThreadContext(th);
ResumeThread(th);

while (WaitForSingleObject(th, 10) != WAIT_OBJECT_0);

return 0;
}

2

Решение

Оба исключения и printf работать на меня, и я не понимаю, почему они не должны. Если вы разместите свой код, мы можем попытаться точно определить, что происходит.

#include <windows.h>
#include <stdio.h>

DWORD CALLBACK ThreadProc(LPVOID)
{
try
{
throw 1;
}
catch (int i)
{
printf("%d\n", i);
}
return 0;
}

typedef NTSTATUS WINAPI NtQueryInformationThread_t(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);

int main()
{
HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
auto NtQueryInformationThread = (NtQueryInformationThread_t *)GetProcAddress(hNtdll, "NtQueryInformationThread");

DWORD stackSize = 1024 * 1024;
void * mystack = VirtualAlloc(0, stackSize, MEM_COMMIT, PAGE_READWRITE);

DWORD dwThreadId;
HANDLE hThread = CreateThread(0, 0, &ThreadProc, 0, CREATE_SUSPENDED, &dwThreadId);

DWORD threadInfo[7];
NtQueryInformationThread(hThread, 0, threadInfo, sizeof threadInfo, 0);

NT_TIB * tib = (NT_TIB *)threadInfo[1];

CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(hThread, &ctx);

ctx.Esp = (DWORD)mystack + stackSize - ((DWORD)tib->StackBase - ctx.Esp);
tib->StackBase = (PVOID)((DWORD)mystack + stackSize);
tib->StackLimit = (PVOID)((DWORD)mystack);
SetThreadContext(hThread, &ctx);

ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
}
1

Другие решения


По вопросам рекламы [email protected]