· 5 years ago · Jul 21, 2020, 07:28 PM
1/*
2 * Memory DLL loading code
3 * Version 0.0.4
4 *
5 * Copyright (c) 2004-2015 by Joachim Bauch / mail@joachim-bauch.de
6 * http://www.joachim-bauch.de
7 *
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 2.0 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
12 *
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
16 * License.
17 *
18 * The Original Code is MemoryModule.c
19 *
20 * The Initial Developer of the Original Code is Joachim Bauch.
21 *
22 * Portions created by Joachim Bauch are Copyright (C) 2004-2015
23 * Joachim Bauch. All Rights Reserved.
24 *
25 *
26 * THeller: Added binary search in MemoryGetProcAddress function
27 * (#define USE_BINARY_SEARCH to enable it). This gives a very large
28 * speedup for libraries that exports lots of functions.
29 *
30 * These portions are Copyright (C) 2013 Thomas Heller.
31 */
32
33#include <windows.h>
34#include <winnt.h>
35#include <stddef.h>
36#include <tchar.h>
37#ifdef DEBUG_OUTPUT
38#include <stdio.h>
39#endif
40
41#if _MSC_VER
42// Disable warning about data -> function pointer conversion
43#pragma warning(disable:4055)
44 // C4244: conversion from 'uintptr_t' to 'DWORD', possible loss of data.
45#pragma warning(error: 4244)
46// C4267: conversion from 'size_t' to 'int', possible loss of data.
47#pragma warning(error: 4267)
48
49#define inline __inline
50#endif
51
52#ifndef IMAGE_SIZEOF_BASE_RELOCATION
53// Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
54#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
55#endif
56
57#ifdef _WIN64
58#define HOST_MACHINE IMAGE_FILE_MACHINE_AMD64
59#else
60#define HOST_MACHINE IMAGE_FILE_MACHINE_I386
61#endif
62
63#ifndef __MEMORY_MODULE_HEADER
64#define __MEMORY_MODULE_HEADER
65
66#include <windows.h>
67
68typedef void *HMEMORYMODULE;
69
70typedef void *HMEMORYRSRC;
71
72typedef void *HCUSTOMMODULE;
73
74#ifdef __cplusplus
75extern "C" {
76#endif
77
78typedef LPVOID (*CustomAllocFunc)(LPVOID, SIZE_T, DWORD, DWORD, void*);
79typedef BOOL (*CustomFreeFunc)(LPVOID, SIZE_T, DWORD, void*);
80typedef HCUSTOMMODULE (*CustomLoadLibraryFunc)(LPCSTR, void *);
81typedef FARPROC (*CustomGetProcAddressFunc)(HCUSTOMMODULE, LPCSTR, void *);
82typedef void (*CustomFreeLibraryFunc)(HCUSTOMMODULE, void *);
83
84/**
85 * Load EXE/DLL from memory location with the given size.
86 *
87 * All dependencies are resolved using default LoadLibrary/GetProcAddress
88 * calls through the Windows API.
89 */
90HMEMORYMODULE MemoryLoadLibrary(const void *, size_t);
91
92/**
93 * Load EXE/DLL from memory location with the given size using custom dependency
94 * resolvers.
95 *
96 * Dependencies will be resolved using passed callback methods.
97 */
98HMEMORYMODULE MemoryLoadLibraryEx(const void *, size_t,
99 CustomAllocFunc,
100 CustomFreeFunc,
101 CustomLoadLibraryFunc,
102 CustomGetProcAddressFunc,
103 CustomFreeLibraryFunc,
104 void *);
105
106/**
107 * Get address of exported method. Supports loading both by name and by
108 * ordinal value.
109 */
110FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR);
111
112/**
113 * Free previously loaded EXE/DLL.
114 */
115void MemoryFreeLibrary(HMEMORYMODULE);
116
117/**
118 * Execute entry point (EXE only). The entry point can only be executed
119 * if the EXE has been loaded to the correct base address or it could
120 * be relocated (i.e. relocation information have not been stripped by
121 * the linker).
122 *
123 * Important: calling this function will not return, i.e. once the loaded
124 * EXE finished running, the process will terminate.
125 *
126 * Returns a negative value if the entry point could not be executed.
127 */
128int MemoryCallEntryPoint(HMEMORYMODULE);
129
130/**
131 * Find the location of a resource with the specified type and name.
132 */
133HMEMORYRSRC MemoryFindResource(HMEMORYMODULE, LPCTSTR, LPCTSTR);
134
135/**
136 * Find the location of a resource with the specified type, name and language.
137 */
138HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE, LPCTSTR, LPCTSTR, WORD);
139
140/**
141 * Get the size of the resource in bytes.
142 */
143DWORD MemorySizeofResource(HMEMORYMODULE, HMEMORYRSRC);
144
145/**
146 * Get a pointer to the contents of the resource.
147 */
148LPVOID MemoryLoadResource(HMEMORYMODULE, HMEMORYRSRC);
149
150/**
151 * Load a string resource.
152 */
153int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int);
154
155/**
156 * Load a string resource with a given language.
157 */
158int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD);
159
160/**
161* Default implementation of CustomAllocFunc that calls VirtualAlloc
162* internally to allocate memory for a library
163*
164* This is the default as used by MemoryLoadLibrary.
165*/
166LPVOID MemoryDefaultAlloc(LPVOID, SIZE_T, DWORD, DWORD, void *);
167
168/**
169* Default implementation of CustomFreeFunc that calls VirtualFree
170* internally to free the memory used by a library
171*
172* This is the default as used by MemoryLoadLibrary.
173*/
174BOOL MemoryDefaultFree(LPVOID, SIZE_T, DWORD, void *);
175
176/**
177 * Default implementation of CustomLoadLibraryFunc that calls LoadLibraryA
178 * internally to load an additional libary.
179 *
180 * This is the default as used by MemoryLoadLibrary.
181 */
182HCUSTOMMODULE MemoryDefaultLoadLibrary(LPCSTR, void *);
183
184/**
185 * Default implementation of CustomGetProcAddressFunc that calls GetProcAddress
186 * internally to get the address of an exported function.
187 *
188 * This is the default as used by MemoryLoadLibrary.
189 */
190FARPROC MemoryDefaultGetProcAddress(HCUSTOMMODULE, LPCSTR, void *);
191
192/**
193 * Default implementation of CustomFreeLibraryFunc that calls FreeLibrary
194 * internally to release an additional libary.
195 *
196 * This is the default as used by MemoryLoadLibrary.
197 */
198void MemoryDefaultFreeLibrary(HCUSTOMMODULE, void *);
199
200#ifdef __cplusplus
201}
202#endif
203
204#endif // __MEMORY_MODULE_HEADER
205
206struct ExportNameEntry {
207 LPCSTR name;
208 WORD idx;
209};
210
211typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
212typedef int (WINAPI *ExeEntryProc)(void);
213
214#ifdef _WIN64
215typedef struct POINTER_LIST {
216 struct POINTER_LIST *next;
217 void *address;
218} POINTER_LIST;
219#endif
220
221typedef struct {
222 PIMAGE_NT_HEADERS headers;
223 unsigned char *codeBase;
224 HCUSTOMMODULE *modules;
225 int numModules;
226 BOOL initialized;
227 BOOL isDLL;
228 BOOL isRelocated;
229 CustomAllocFunc alloc;
230 CustomFreeFunc free;
231 CustomLoadLibraryFunc loadLibrary;
232 CustomGetProcAddressFunc getProcAddress;
233 CustomFreeLibraryFunc freeLibrary;
234 struct ExportNameEntry *nameExportsTable;
235 void *userdata;
236 ExeEntryProc exeEntry;
237 DWORD pageSize;
238#ifdef _WIN64
239 POINTER_LIST *blockedMemory;
240#endif
241} MEMORYMODULE, *PMEMORYMODULE;
242
243typedef struct {
244 LPVOID address;
245 LPVOID alignedAddress;
246 SIZE_T size;
247 DWORD characteristics;
248 BOOL last;
249} SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
250
251#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
252
253static inline uintptr_t
254AlignValueDown(uintptr_t value, uintptr_t alignment) {
255 return value & ~(alignment - 1);
256}
257
258static inline LPVOID
259AlignAddressDown(LPVOID address, uintptr_t alignment) {
260 return (LPVOID) AlignValueDown((uintptr_t) address, alignment);
261}
262
263static inline size_t
264AlignValueUp(size_t value, size_t alignment) {
265 return (value + alignment - 1) & ~(alignment - 1);
266}
267
268static inline void*
269OffsetPointer(void* data, ptrdiff_t offset) {
270 return (void*) ((uintptr_t) data + offset);
271}
272
273static inline void
274OutputLastError(const char *msg)
275{
276#ifndef DEBUG_OUTPUT
277 UNREFERENCED_PARAMETER(msg);
278#else
279 LPVOID tmp;
280 char *tmpmsg;
281 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
282 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
283 tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3);
284 sprintf(tmpmsg, "%s: %s", msg, tmp);
285 OutputDebugString(tmpmsg);
286 LocalFree(tmpmsg);
287 LocalFree(tmp);
288#endif
289}
290
291#ifdef _WIN64
292static void
293FreePointerList(POINTER_LIST *head, CustomFreeFunc freeMemory, void *userdata)
294{
295 POINTER_LIST *node = head;
296 while (node) {
297 POINTER_LIST *next;
298 freeMemory(node->address, 0, MEM_RELEASE, userdata);
299 next = node->next;
300 free(node);
301 node = next;
302 }
303}
304#endif
305
306static BOOL
307CheckSize(size_t size, size_t expected) {
308 if (size < expected) {
309 SetLastError(ERROR_INVALID_DATA);
310 return FALSE;
311 }
312
313 return TRUE;
314}
315
316static BOOL
317CopySections(const unsigned char *data, size_t size, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module)
318{
319 int i, section_size;
320 unsigned char *codeBase = module->codeBase;
321 unsigned char *dest;
322 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
323 for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
324 if (section->SizeOfRawData == 0) {
325 // section doesn't contain data in the dll itself, but may define
326 // uninitialized data
327 section_size = old_headers->OptionalHeader.SectionAlignment;
328 if (section_size > 0) {
329 dest = (unsigned char *)module->alloc(codeBase + section->VirtualAddress,
330 section_size,
331 MEM_COMMIT,
332 PAGE_READWRITE,
333 module->userdata);
334 if (dest == NULL) {
335 return FALSE;
336 }
337
338 // Always use position from file to support alignments smaller
339 // than page size (allocation above will align to page size).
340 dest = codeBase + section->VirtualAddress;
341 // NOTE: On 64bit systems we truncate to 32bit here but expand
342 // again later when "PhysicalAddress" is used.
343 section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff);
344 memset(dest, 0, section_size);
345 }
346
347 // section is empty
348 continue;
349 }
350
351 if (!CheckSize(size, section->PointerToRawData + section->SizeOfRawData)) {
352 return FALSE;
353 }
354
355 // commit memory block and copy data from dll
356 dest = (unsigned char *)module->alloc(codeBase + section->VirtualAddress,
357 section->SizeOfRawData,
358 MEM_COMMIT,
359 PAGE_READWRITE,
360 module->userdata);
361 if (dest == NULL) {
362 return FALSE;
363 }
364
365 // Always use position from file to support alignments smaller
366 // than page size (allocation above will align to page size).
367 dest = codeBase + section->VirtualAddress;
368 memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
369 // NOTE: On 64bit systems we truncate to 32bit here but expand
370 // again later when "PhysicalAddress" is used.
371 section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff);
372 }
373
374 return TRUE;
375}
376
377// Protection flags for memory pages (Executable, Readable, Writeable)
378static int ProtectionFlags[2][2][2] = {
379 {
380 // not executable
381 {PAGE_NOACCESS, PAGE_WRITECOPY},
382 {PAGE_READONLY, PAGE_READWRITE},
383 }, {
384 // executable
385 {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY},
386 {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE},
387 },
388};
389
390static SIZE_T
391GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) {
392 DWORD size = section->SizeOfRawData;
393 if (size == 0) {
394 if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
395 size = module->headers->OptionalHeader.SizeOfInitializedData;
396 } else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
397 size = module->headers->OptionalHeader.SizeOfUninitializedData;
398 }
399 }
400 return (SIZE_T) size;
401}
402
403static BOOL
404FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) {
405 DWORD protect, oldProtect;
406 BOOL executable;
407 BOOL readable;
408 BOOL writeable;
409
410 if (sectionData->size == 0) {
411 return TRUE;
412 }
413
414 if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
415 // section is not needed any more and can safely be freed
416 if (sectionData->address == sectionData->alignedAddress &&
417 (sectionData->last ||
418 module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
419 (sectionData->size % module->pageSize) == 0)
420 ) {
421 // Only allowed to decommit whole pages
422 module->free(sectionData->address, sectionData->size, MEM_DECOMMIT, module->userdata);
423 }
424 return TRUE;
425 }
426
427 // determine protection flags based on characteristics
428 executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
429 readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0;
430 writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0;
431 protect = ProtectionFlags[executable][readable][writeable];
432 if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
433 protect |= PAGE_NOCACHE;
434 }
435
436 // change memory access flags
437 if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) {
438 OutputLastError("Error protecting memory page");
439 return FALSE;
440 }
441
442 return TRUE;
443}
444
445static BOOL
446FinalizeSections(PMEMORYMODULE module)
447{
448 int i;
449 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
450#ifdef _WIN64
451 // "PhysicalAddress" might have been truncated to 32bit above, expand to
452 // 64bits again.
453 uintptr_t imageOffset = ((uintptr_t) module->headers->OptionalHeader.ImageBase & 0xffffffff00000000);
454#else
455 static const uintptr_t imageOffset = 0;
456#endif
457 SECTIONFINALIZEDATA sectionData;
458 sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
459 sectionData.alignedAddress = AlignAddressDown(sectionData.address, module->pageSize);
460 sectionData.size = GetRealSectionSize(module, section);
461 sectionData.characteristics = section->Characteristics;
462 sectionData.last = FALSE;
463 section++;
464
465 // loop through all sections and change access flags
466 for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
467 LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
468 LPVOID alignedAddress = AlignAddressDown(sectionAddress, module->pageSize);
469 SIZE_T sectionSize = GetRealSectionSize(module, section);
470 // Combine access flags of all sections that share a page
471 // TODO(fancycode): We currently share flags of a trailing large section
472 // with the page of a first small section. This should be optimized.
473 if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) {
474 // Section shares page with previous
475 if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) {
476 sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
477 } else {
478 sectionData.characteristics |= section->Characteristics;
479 }
480 sectionData.size = (((uintptr_t)sectionAddress) + ((uintptr_t) sectionSize)) - (uintptr_t) sectionData.address;
481 continue;
482 }
483
484 if (!FinalizeSection(module, §ionData)) {
485 return FALSE;
486 }
487 sectionData.address = sectionAddress;
488 sectionData.alignedAddress = alignedAddress;
489 sectionData.size = sectionSize;
490 sectionData.characteristics = section->Characteristics;
491 }
492 sectionData.last = TRUE;
493 if (!FinalizeSection(module, §ionData)) {
494 return FALSE;
495 }
496 return TRUE;
497}
498
499static BOOL
500ExecuteTLS(PMEMORYMODULE module)
501{
502 unsigned char *codeBase = module->codeBase;
503 PIMAGE_TLS_DIRECTORY tls;
504 PIMAGE_TLS_CALLBACK* callback;
505
506 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS);
507 if (directory->VirtualAddress == 0) {
508 return TRUE;
509 }
510
511 tls = (PIMAGE_TLS_DIRECTORY) (codeBase + directory->VirtualAddress);
512 callback = (PIMAGE_TLS_CALLBACK *) tls->AddressOfCallBacks;
513 if (callback) {
514 while (*callback) {
515 (*callback)((LPVOID) codeBase, DLL_PROCESS_ATTACH, NULL);
516 callback++;
517 }
518 }
519 return TRUE;
520}
521
522static BOOL
523PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta)
524{
525 unsigned char *codeBase = module->codeBase;
526 PIMAGE_BASE_RELOCATION relocation;
527
528 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
529 if (directory->Size == 0) {
530 return (delta == 0);
531 }
532
533 relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress);
534 for (; relocation->VirtualAddress > 0; ) {
535 DWORD i;
536 unsigned char *dest = codeBase + relocation->VirtualAddress;
537 unsigned short *relInfo = (unsigned short*) OffsetPointer(relocation, IMAGE_SIZEOF_BASE_RELOCATION);
538 for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) {
539 // the upper 4 bits define the type of relocation
540 int type = *relInfo >> 12;
541 // the lower 12 bits define the offset
542 int offset = *relInfo & 0xfff;
543
544 switch (type)
545 {
546 case IMAGE_REL_BASED_ABSOLUTE:
547 // skip relocation
548 break;
549
550 case IMAGE_REL_BASED_HIGHLOW:
551 // change complete 32 bit address
552 {
553 DWORD *patchAddrHL = (DWORD *) (dest + offset);
554 *patchAddrHL += (DWORD) delta;
555 }
556 break;
557
558#ifdef _WIN64
559 case IMAGE_REL_BASED_DIR64:
560 {
561 ULONGLONG *patchAddr64 = (ULONGLONG *) (dest + offset);
562 *patchAddr64 += (ULONGLONG) delta;
563 }
564 break;
565#endif
566
567 default:
568 //printf("Unknown relocation: %d\n", type);
569 break;
570 }
571 }
572
573 // advance to next relocation block
574 relocation = (PIMAGE_BASE_RELOCATION) OffsetPointer(relocation, relocation->SizeOfBlock);
575 }
576 return TRUE;
577}
578
579static BOOL
580BuildImportTable(PMEMORYMODULE module)
581{
582 unsigned char *codeBase = module->codeBase;
583 PIMAGE_IMPORT_DESCRIPTOR importDesc;
584 BOOL result = TRUE;
585
586 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
587 if (directory->Size == 0) {
588 return TRUE;
589 }
590
591 importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress);
592 for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) {
593 uintptr_t *thunkRef;
594 FARPROC *funcRef;
595 HCUSTOMMODULE *tmp;
596 HCUSTOMMODULE handle = module->loadLibrary((LPCSTR) (codeBase + importDesc->Name), module->userdata);
597 if (handle == NULL) {
598 SetLastError(ERROR_MOD_NOT_FOUND);
599 result = FALSE;
600 break;
601 }
602
603 tmp = (HCUSTOMMODULE *) realloc(module->modules, (module->numModules+1)*(sizeof(HCUSTOMMODULE)));
604 if (tmp == NULL) {
605 module->freeLibrary(handle, module->userdata);
606 SetLastError(ERROR_OUTOFMEMORY);
607 result = FALSE;
608 break;
609 }
610 module->modules = tmp;
611
612 module->modules[module->numModules++] = handle;
613 if (importDesc->OriginalFirstThunk) {
614 thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk);
615 funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
616 } else {
617 // no hint table
618 thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk);
619 funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
620 }
621 for (; *thunkRef; thunkRef++, funcRef++) {
622 if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
623 *funcRef = module->getProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef), module->userdata);
624 } else {
625 PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef));
626 *funcRef = module->getProcAddress(handle, (LPCSTR)&thunkData->Name, module->userdata);
627 }
628 if (*funcRef == 0) {
629 result = FALSE;
630 break;
631 }
632 }
633
634 if (!result) {
635 module->freeLibrary(handle, module->userdata);
636 SetLastError(ERROR_PROC_NOT_FOUND);
637 break;
638 }
639 }
640
641 return result;
642}
643
644LPVOID MemoryDefaultAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect, void* userdata)
645{
646 UNREFERENCED_PARAMETER(userdata);
647 return VirtualAlloc(address, size, allocationType, protect);
648}
649
650BOOL MemoryDefaultFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType, void* userdata)
651{
652 UNREFERENCED_PARAMETER(userdata);
653 return VirtualFree(lpAddress, dwSize, dwFreeType);
654}
655
656HCUSTOMMODULE MemoryDefaultLoadLibrary(LPCSTR filename, void *userdata)
657{
658 HMODULE result;
659 UNREFERENCED_PARAMETER(userdata);
660 result = LoadLibraryA(filename);
661 if (result == NULL) {
662 return NULL;
663 }
664
665 return (HCUSTOMMODULE) result;
666}
667
668FARPROC MemoryDefaultGetProcAddress(HCUSTOMMODULE module, LPCSTR name, void *userdata)
669{
670 UNREFERENCED_PARAMETER(userdata);
671 return (FARPROC) GetProcAddress((HMODULE) module, name);
672}
673
674void MemoryDefaultFreeLibrary(HCUSTOMMODULE module, void *userdata)
675{
676 UNREFERENCED_PARAMETER(userdata);
677 FreeLibrary((HMODULE) module);
678}
679
680HMEMORYMODULE MemoryLoadLibrary(const void *data, size_t size)
681{
682 return MemoryLoadLibraryEx(data, size, MemoryDefaultAlloc, MemoryDefaultFree, MemoryDefaultLoadLibrary, MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, NULL);
683}
684
685HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size,
686 CustomAllocFunc allocMemory,
687 CustomFreeFunc freeMemory,
688 CustomLoadLibraryFunc loadLibrary,
689 CustomGetProcAddressFunc getProcAddress,
690 CustomFreeLibraryFunc freeLibrary,
691 void *userdata)
692{
693 PMEMORYMODULE result = NULL;
694 PIMAGE_DOS_HEADER dos_header;
695 PIMAGE_NT_HEADERS old_header;
696 unsigned char *code, *headers;
697 ptrdiff_t locationDelta;
698 SYSTEM_INFO sysInfo;
699 PIMAGE_SECTION_HEADER section;
700 DWORD i;
701 size_t optionalSectionSize;
702 size_t lastSectionEnd = 0;
703 size_t alignedImageSize;
704#ifdef _WIN64
705 POINTER_LIST *blockedMemory = NULL;
706#endif
707
708 if (!CheckSize(size, sizeof(IMAGE_DOS_HEADER))) {
709 return NULL;
710 }
711 dos_header = (PIMAGE_DOS_HEADER)data;
712 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
713 SetLastError(ERROR_BAD_EXE_FORMAT);
714 return NULL;
715 }
716
717 if (!CheckSize(size, dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS))) {
718 return NULL;
719 }
720 old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew];
721 if (old_header->Signature != IMAGE_NT_SIGNATURE) {
722 SetLastError(ERROR_BAD_EXE_FORMAT);
723 return NULL;
724 }
725
726 if (old_header->FileHeader.Machine != HOST_MACHINE) {
727 SetLastError(ERROR_BAD_EXE_FORMAT);
728 return NULL;
729 }
730
731 if (old_header->OptionalHeader.SectionAlignment & 1) {
732 // Only support section alignments that are a multiple of 2
733 SetLastError(ERROR_BAD_EXE_FORMAT);
734 return NULL;
735 }
736
737 section = IMAGE_FIRST_SECTION(old_header);
738 optionalSectionSize = old_header->OptionalHeader.SectionAlignment;
739 for (i=0; i<old_header->FileHeader.NumberOfSections; i++, section++) {
740 size_t endOfSection;
741 if (section->SizeOfRawData == 0) {
742 // Section without data in the DLL
743 endOfSection = section->VirtualAddress + optionalSectionSize;
744 } else {
745 endOfSection = section->VirtualAddress + section->SizeOfRawData;
746 }
747
748 if (endOfSection > lastSectionEnd) {
749 lastSectionEnd = endOfSection;
750 }
751 }
752
753 GetNativeSystemInfo(&sysInfo);
754 alignedImageSize = AlignValueUp(old_header->OptionalHeader.SizeOfImage, sysInfo.dwPageSize);
755 if (alignedImageSize != AlignValueUp(lastSectionEnd, sysInfo.dwPageSize)) {
756 SetLastError(ERROR_BAD_EXE_FORMAT);
757 return NULL;
758 }
759
760 // reserve memory for image of library
761 // XXX: is it correct to commit the complete memory region at once?
762 // calling DllEntry raises an exception if we don't...
763 code = (unsigned char *)allocMemory((LPVOID)(old_header->OptionalHeader.ImageBase),
764 alignedImageSize,
765 MEM_RESERVE | MEM_COMMIT,
766 PAGE_READWRITE,
767 userdata);
768
769 if (code == NULL) {
770 // try to allocate memory at arbitrary position
771 code = (unsigned char *)allocMemory(NULL,
772 alignedImageSize,
773 MEM_RESERVE | MEM_COMMIT,
774 PAGE_READWRITE,
775 userdata);
776 if (code == NULL) {
777 SetLastError(ERROR_OUTOFMEMORY);
778 return NULL;
779 }
780 }
781
782#ifdef _WIN64
783 // Memory block may not span 4 GB boundaries.
784 while ((((uintptr_t) code) >> 32) < (((uintptr_t) (code + alignedImageSize)) >> 32)) {
785 POINTER_LIST *node = (POINTER_LIST*) malloc(sizeof(POINTER_LIST));
786 if (!node) {
787 freeMemory(code, 0, MEM_RELEASE, userdata);
788 FreePointerList(blockedMemory, freeMemory, userdata);
789 SetLastError(ERROR_OUTOFMEMORY);
790 return NULL;
791 }
792
793 node->next = blockedMemory;
794 node->address = code;
795 blockedMemory = node;
796
797 code = (unsigned char *)allocMemory(NULL,
798 alignedImageSize,
799 MEM_RESERVE | MEM_COMMIT,
800 PAGE_READWRITE,
801 userdata);
802 if (code == NULL) {
803 FreePointerList(blockedMemory, freeMemory, userdata);
804 SetLastError(ERROR_OUTOFMEMORY);
805 return NULL;
806 }
807 }
808#endif
809
810 result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE));
811 if (result == NULL) {
812 freeMemory(code, 0, MEM_RELEASE, userdata);
813#ifdef _WIN64
814 FreePointerList(blockedMemory, freeMemory, userdata);
815#endif
816 SetLastError(ERROR_OUTOFMEMORY);
817 return NULL;
818 }
819
820 result->codeBase = code;
821 result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0;
822 result->alloc = allocMemory;
823 result->free = freeMemory;
824 result->loadLibrary = loadLibrary;
825 result->getProcAddress = getProcAddress;
826 result->freeLibrary = freeLibrary;
827 result->userdata = userdata;
828 result->pageSize = sysInfo.dwPageSize;
829#ifdef _WIN64
830 result->blockedMemory = blockedMemory;
831#endif
832
833 if (!CheckSize(size, old_header->OptionalHeader.SizeOfHeaders)) {
834 goto error;
835 }
836
837 // commit memory for headers
838 headers = (unsigned char *)allocMemory(code,
839 old_header->OptionalHeader.SizeOfHeaders,
840 MEM_COMMIT,
841 PAGE_READWRITE,
842 userdata);
843
844 // copy PE header to code
845 memcpy(headers, dos_header, old_header->OptionalHeader.SizeOfHeaders);
846 result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew];
847
848 // update position
849 result->headers->OptionalHeader.ImageBase = (uintptr_t)code;
850
851 // copy sections from DLL file block to new memory location
852 if (!CopySections((const unsigned char *) data, size, old_header, result)) {
853 goto error;
854 }
855
856 // adjust base address of imported data
857 locationDelta = (ptrdiff_t)(result->headers->OptionalHeader.ImageBase - old_header->OptionalHeader.ImageBase);
858 if (locationDelta != 0) {
859 result->isRelocated = PerformBaseRelocation(result, locationDelta);
860 } else {
861 result->isRelocated = TRUE;
862 }
863
864 // load required dlls and adjust function table of imports
865 if (!BuildImportTable(result)) {
866 goto error;
867 }
868
869 // mark memory pages depending on section headers and release
870 // sections that are marked as "discardable"
871 if (!FinalizeSections(result)) {
872 goto error;
873 }
874
875 // TLS callbacks are executed BEFORE the main loading
876 if (!ExecuteTLS(result)) {
877 goto error;
878 }
879
880 // get entry point of loaded library
881 if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) {
882 if (result->isDLL) {
883 DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
884 // notify library about attaching to process
885 BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);
886 if (!successfull) {
887 SetLastError(ERROR_DLL_INIT_FAILED);
888 goto error;
889 }
890 result->initialized = TRUE;
891 } else {
892 result->exeEntry = (ExeEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
893 }
894 } else {
895 result->exeEntry = NULL;
896 }
897
898 return (HMEMORYMODULE)result;
899
900error:
901 // cleanup
902 MemoryFreeLibrary(result);
903 return NULL;
904}
905
906static int _compare(const void *a, const void *b)
907{
908 const struct ExportNameEntry *p1 = (const struct ExportNameEntry*) a;
909 const struct ExportNameEntry *p2 = (const struct ExportNameEntry*) b;
910 return strcmp(p1->name, p2->name);
911}
912
913static int _find(const void *a, const void *b)
914{
915 LPCSTR *name = (LPCSTR *) a;
916 const struct ExportNameEntry *p = (const struct ExportNameEntry*) b;
917 return strcmp(*name, p->name);
918}
919
920FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name)
921{
922 PMEMORYMODULE module = (PMEMORYMODULE)mod;
923 unsigned char *codeBase = module->codeBase;
924 DWORD idx = 0;
925 PIMAGE_EXPORT_DIRECTORY exports;
926 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT);
927 if (directory->Size == 0) {
928 // no export table found
929 SetLastError(ERROR_PROC_NOT_FOUND);
930 return NULL;
931 }
932
933 exports = (PIMAGE_EXPORT_DIRECTORY) (codeBase + directory->VirtualAddress);
934 if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) {
935 // DLL doesn't export anything
936 SetLastError(ERROR_PROC_NOT_FOUND);
937 return NULL;
938 }
939
940 if (HIWORD(name) == 0) {
941 // load function by ordinal value
942 if (LOWORD(name) < exports->Base) {
943 SetLastError(ERROR_PROC_NOT_FOUND);
944 return NULL;
945 }
946
947 idx = LOWORD(name) - exports->Base;
948 } else if (!exports->NumberOfNames) {
949 SetLastError(ERROR_PROC_NOT_FOUND);
950 return NULL;
951 } else {
952 const struct ExportNameEntry *found;
953
954 // Lazily build name table and sort it by names
955 if (!module->nameExportsTable) {
956 DWORD i;
957 DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
958 WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
959 struct ExportNameEntry *entry = (struct ExportNameEntry*) malloc(exports->NumberOfNames * sizeof(struct ExportNameEntry));
960 module->nameExportsTable = entry;
961 if (!entry) {
962 SetLastError(ERROR_OUTOFMEMORY);
963 return NULL;
964 }
965 for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++, entry++) {
966 entry->name = (const char *) (codeBase + (*nameRef));
967 entry->idx = *ordinal;
968 }
969 qsort(module->nameExportsTable,
970 exports->NumberOfNames,
971 sizeof(struct ExportNameEntry), _compare);
972 }
973
974 // search function name in list of exported names with binary search
975 found = (const struct ExportNameEntry*) bsearch(&name,
976 module->nameExportsTable,
977 exports->NumberOfNames,
978 sizeof(struct ExportNameEntry), _find);
979 if (!found) {
980 // exported symbol not found
981 SetLastError(ERROR_PROC_NOT_FOUND);
982 return NULL;
983 }
984
985 idx = found->idx;
986 }
987
988 if (idx > exports->NumberOfFunctions) {
989 // name <-> ordinal number don't match
990 SetLastError(ERROR_PROC_NOT_FOUND);
991 return NULL;
992 }
993
994 // AddressOfFunctions contains the RVAs to the "real" functions
995 return (FARPROC)(LPVOID)(codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4))));
996}
997
998void MemoryFreeLibrary(HMEMORYMODULE mod)
999{
1000 PMEMORYMODULE module = (PMEMORYMODULE)mod;
1001
1002 if (module == NULL) {
1003 return;
1004 }
1005 if (module->initialized) {
1006 // notify library about detaching from process
1007 DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(module->codeBase + module->headers->OptionalHeader.AddressOfEntryPoint);
1008 (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0);
1009 }
1010
1011 free(module->nameExportsTable);
1012 if (module->modules != NULL) {
1013 // free previously opened libraries
1014 int i;
1015 for (i=0; i<module->numModules; i++) {
1016 if (module->modules[i] != NULL) {
1017 module->freeLibrary(module->modules[i], module->userdata);
1018 }
1019 }
1020
1021 free(module->modules);
1022 }
1023
1024 if (module->codeBase != NULL) {
1025 // release memory of library
1026 module->free(module->codeBase, 0, MEM_RELEASE, module->userdata);
1027 }
1028
1029#ifdef _WIN64
1030 FreePointerList(module->blockedMemory, module->free, module->userdata);
1031#endif
1032 HeapFree(GetProcessHeap(), 0, module);
1033}
1034
1035int MemoryCallEntryPoint(HMEMORYMODULE mod)
1036{
1037 PMEMORYMODULE module = (PMEMORYMODULE)mod;
1038
1039 if (module == NULL || module->isDLL || module->exeEntry == NULL || !module->isRelocated) {
1040 return -1;
1041 }
1042
1043 return module->exeEntry();
1044}
1045
1046#define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
1047
1048HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type)
1049{
1050 return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE);
1051}
1052
1053static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(
1054 void *root,
1055 PIMAGE_RESOURCE_DIRECTORY resources,
1056 LPCTSTR key)
1057{
1058 PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1);
1059 PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL;
1060 DWORD start;
1061 DWORD end;
1062 DWORD middle;
1063
1064 if (!IS_INTRESOURCE(key) && key[0] == TEXT('#')) {
1065 // special case: resource id given as string
1066 TCHAR *endpos = NULL;
1067 long int tmpkey = (WORD) _tcstol((TCHAR *) &key[1], &endpos, 10);
1068 if (tmpkey <= 0xffff && lstrlen(endpos) == 0) {
1069 key = MAKEINTRESOURCE(tmpkey);
1070 }
1071 }
1072
1073 // entries are stored as ordered list of named entries,
1074 // followed by an ordered list of id entries - we can do
1075 // a binary search to find faster...
1076 if (IS_INTRESOURCE(key)) {
1077 WORD check = (WORD) (uintptr_t) key;
1078 start = resources->NumberOfNamedEntries;
1079 end = start + resources->NumberOfIdEntries;
1080
1081 while (end > start) {
1082 WORD entryName;
1083 middle = (start + end) >> 1;
1084 entryName = (WORD) entries[middle].Name;
1085 if (check < entryName) {
1086 end = (end != middle ? middle : middle-1);
1087 } else if (check > entryName) {
1088 start = (start != middle ? middle : middle+1);
1089 } else {
1090 result = &entries[middle];
1091 break;
1092 }
1093 }
1094 } else {
1095 LPCWSTR searchKey;
1096 size_t searchKeyLen = _tcslen(key);
1097#if defined(UNICODE)
1098 searchKey = key;
1099#else
1100 // Resource names are always stored using 16bit characters, need to
1101 // convert string we search for.
1102#define MAX_LOCAL_KEY_LENGTH 2048
1103 // In most cases resource names are short, so optimize for that by
1104 // using a pre-allocated array.
1105 wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1];
1106 LPWSTR _searchKey;
1107 if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
1108 size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t);
1109 _searchKey = (LPWSTR) malloc(_searchKeySize);
1110 if (_searchKey == NULL) {
1111 SetLastError(ERROR_OUTOFMEMORY);
1112 return NULL;
1113 }
1114 } else {
1115 _searchKey = &_searchKeySpace[0];
1116 }
1117
1118 mbstowcs(_searchKey, key, searchKeyLen);
1119 _searchKey[searchKeyLen] = 0;
1120 searchKey = _searchKey;
1121#endif
1122 start = 0;
1123 end = resources->NumberOfNamedEntries;
1124 while (end > start) {
1125 int cmp;
1126 PIMAGE_RESOURCE_DIR_STRING_U resourceString;
1127 middle = (start + end) >> 1;
1128 resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(root, entries[middle].Name & 0x7FFFFFFF);
1129 cmp = _wcsnicmp(searchKey, resourceString->NameString, resourceString->Length);
1130 if (cmp == 0) {
1131 // Handle partial match
1132 if (searchKeyLen > resourceString->Length) {
1133 cmp = 1;
1134 } else if (searchKeyLen < resourceString->Length) {
1135 cmp = -1;
1136 }
1137 }
1138 if (cmp < 0) {
1139 end = (middle != end ? middle : middle-1);
1140 } else if (cmp > 0) {
1141 start = (middle != start ? middle : middle+1);
1142 } else {
1143 result = &entries[middle];
1144 break;
1145 }
1146 }
1147#if !defined(UNICODE)
1148 if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
1149 free(_searchKey);
1150 }
1151#undef MAX_LOCAL_KEY_LENGTH
1152#endif
1153 }
1154
1155 return result;
1156}
1157
1158HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language)
1159{
1160 unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
1161 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE);
1162 PIMAGE_RESOURCE_DIRECTORY rootResources;
1163 PIMAGE_RESOURCE_DIRECTORY nameResources;
1164 PIMAGE_RESOURCE_DIRECTORY typeResources;
1165 PIMAGE_RESOURCE_DIRECTORY_ENTRY foundType;
1166 PIMAGE_RESOURCE_DIRECTORY_ENTRY foundName;
1167 PIMAGE_RESOURCE_DIRECTORY_ENTRY foundLanguage;
1168 if (directory->Size == 0) {
1169 // no resource table found
1170 SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
1171 return NULL;
1172 }
1173
1174 if (language == DEFAULT_LANGUAGE) {
1175 // use language from current thread
1176 language = LANGIDFROMLCID(GetThreadLocale());
1177 }
1178
1179 // resources are stored as three-level tree
1180 // - first node is the type
1181 // - second node is the name
1182 // - third node is the language
1183 rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress);
1184 foundType = _MemorySearchResourceEntry(rootResources, rootResources, type);
1185 if (foundType == NULL) {
1186 SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND);
1187 return NULL;
1188 }
1189
1190 typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff));
1191 foundName = _MemorySearchResourceEntry(rootResources, typeResources, name);
1192 if (foundName == NULL) {
1193 SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
1194 return NULL;
1195 }
1196
1197 nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff));
1198 foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR) (uintptr_t) language);
1199 if (foundLanguage == NULL) {
1200 // requested language not found, use first available
1201 if (nameResources->NumberOfIdEntries == 0) {
1202 SetLastError(ERROR_RESOURCE_LANG_NOT_FOUND);
1203 return NULL;
1204 }
1205
1206 foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (nameResources + 1);
1207 }
1208
1209 return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff));
1210}
1211
1212DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource)
1213{
1214 PIMAGE_RESOURCE_DATA_ENTRY entry;
1215 UNREFERENCED_PARAMETER(module);
1216 entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
1217 if (entry == NULL) {
1218 return 0;
1219 }
1220
1221 return entry->Size;
1222}
1223
1224LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource)
1225{
1226 unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
1227 PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
1228 if (entry == NULL) {
1229 return NULL;
1230 }
1231
1232 return codeBase + entry->OffsetToData;
1233}
1234
1235int
1236MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize)
1237{
1238 return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE);
1239}
1240
1241int
1242MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language)
1243{
1244 HMEMORYRSRC resource;
1245 PIMAGE_RESOURCE_DIR_STRING_U data;
1246 DWORD size;
1247 if (maxsize == 0) {
1248 return 0;
1249 }
1250
1251 resource = MemoryFindResourceEx(module, MAKEINTRESOURCE((id >> 4) + 1), RT_STRING, language);
1252 if (resource == NULL) {
1253 buffer[0] = 0;
1254 return 0;
1255 }
1256
1257 data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource);
1258 id = id & 0x0f;
1259 while (id--) {
1260 data = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(data, (data->Length + 1) * sizeof(WCHAR));
1261 }
1262 if (data->Length == 0) {
1263 SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
1264 buffer[0] = 0;
1265 return 0;
1266 }
1267
1268 size = data->Length;
1269 if (size >= (DWORD) maxsize) {
1270 size = maxsize;
1271 } else {
1272 buffer[size] = 0;
1273 }
1274#if defined(UNICODE)
1275 wcsncpy(buffer, data->NameString, size);
1276#else
1277 wcstombs(buffer, data->NameString, size);
1278#endif
1279 return size;
1280}
1281
1282#ifdef TESTSUITE
1283#include <stdio.h>
1284
1285#ifndef PRIxPTR
1286#ifdef _WIN64
1287#define PRIxPTR "I64x"
1288#else
1289#define PRIxPTR "x"
1290#endif
1291#endif
1292
1293static const uintptr_t AlignValueDownTests[][3] = {
1294 {16, 16, 16},
1295 {17, 16, 16},
1296 {32, 16, 32},
1297 {33, 16, 32},
1298#ifdef _WIN64
1299 {0x12345678abcd1000, 0x1000, 0x12345678abcd1000},
1300 {0x12345678abcd101f, 0x1000, 0x12345678abcd1000},
1301#endif
1302 {0, 0, 0},
1303};
1304
1305static const uintptr_t AlignValueUpTests[][3] = {
1306 {16, 16, 16},
1307 {17, 16, 32},
1308 {32, 16, 32},
1309 {33, 16, 48},
1310#ifdef _WIN64
1311 {0x12345678abcd1000, 0x1000, 0x12345678abcd1000},
1312 {0x12345678abcd101f, 0x1000, 0x12345678abcd2000},
1313#endif
1314 {0, 0, 0},
1315};
1316
1317BOOL MemoryModuleTestsuite() {
1318 BOOL success = TRUE;
1319 size_t idx;
1320 for (idx = 0; AlignValueDownTests[idx][0]; ++idx) {
1321 const uintptr_t* tests = AlignValueDownTests[idx];
1322 uintptr_t value = AlignValueDown(tests[0], tests[1]);
1323 if (value != tests[2]) {
1324 printf("AlignValueDown failed for 0x%" PRIxPTR "/0x%" PRIxPTR ": expected 0x%" PRIxPTR ", got 0x%" PRIxPTR "\n",
1325 tests[0], tests[1], tests[2], value);
1326 success = FALSE;
1327 }
1328 }
1329 for (idx = 0; AlignValueDownTests[idx][0]; ++idx) {
1330 const uintptr_t* tests = AlignValueUpTests[idx];
1331 uintptr_t value = AlignValueUp(tests[0], tests[1]);
1332 if (value != tests[2]) {
1333 printf("AlignValueUp failed for 0x%" PRIxPTR "/0x%" PRIxPTR ": expected 0x%" PRIxPTR ", got 0x%" PRIxPTR "\n",
1334 tests[0], tests[1], tests[2], value);
1335 success = FALSE;
1336 }
1337 }
1338 if (success) {
1339 printf("OK\n");
1340 }
1341 return success;
1342}
1343#endif