Я пытаюсь понять, как работает отложенная загрузка экспортируемых функций (с c ++ на windows).
Насколько я понимаю, вы можете либо импортировать какой-то '* .h' и вызвать функцию. Компилятор и даже больше компоновщик позаботится обо всем, а также загрузчик windows (который затем исправит точные адреса в соответствии с разметкой памяти во время выполнения). Другой вариант - загрузить адрес экспортируемой функции во время выполнения, преобразовать его в указатель функции и просто запустить его. Поэтому я использовал простой пример для выделения массива символов и распечатки содержимого:
void writeChar(char* char_space, SSIZE_T len)
{
memset(char_space, 0x41, len);
}
int main()
{
// Allocate a buffer
SSIZE_T len = 10;
char* char_space = (char*) VirtualAlloc(NULL, len, MEM_COMMIT, PAGE_READWRITE);
// write to it
writeChar(char_space, len);
// print chars omitted for readability...
return 0;
}
Это работает так, как я ожидаю. Я просто бросил его в консольное приложение windows в VS, и оно скомпилировалось и прекрасно работало.
Поэтому я попытался просто загрузить его во время выполнения. Я скопировал точный заголовок функции из memoryapi.h
и попытался найти его в экспортируемой DLL (kernel32.dll
).
typedef LPVOID (*loadedAlloc) (LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
void writeChar(char* char_space, SSIZE_T len)
{
memset(char_space, 0x41, len);
}
int main()
{
// Load the functions address (assume kernel32.dll is loaded)
loadedAlloc Alloc = (loadedlAlloc)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "VirtualAlloc");
// Allocate a buffer
SSIZE_T len = 10;
char* char_space = (char*) loadedAlloc(NULL, len, MEM_COMMIT, PAGE_READWRITE);
// write to it
writeChar(char_space, len);
// print chars omitted for readability...
return 0;
}
Это ведет себя совершенно иначе. Адрес эффективно загружается, и вызов также извлекает адрес памяти (тот, который выглядит хорошо). Но после его передачи в функцию «writeChar» адрес памяти (по крайней мере, в отладчике) изменяется. Это приведет к некоторому AccessViolation
при следующей записи (memset
-call), и программа завершится.
Так чем отличаются эти вызовы?