8000 Merge pull request #76 from fancycode/binsearch_export_names · fancycode/MemoryModule@55577d9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 55577d9

Browse files
authored
Merge pull request #76 from fancycode/binsearch_export_names
Implement binary search for looking up exports by name.
2 parents 0884dcc + ac78ea1 commit 55577d9

File tree

5 files changed

+195
-15
lines changed

5 files changed

+195
-15
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
*.exe
44
tests/*.dll
55
tests/*.res
6+
7+
tests/SampleExports.cpp
8+
tests/SampleExports.h

MemoryModule.c

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
* Portions created by Joachim Bauch are Copyright (C) 2004-2015
2323
* Joachim Bauch. All Rights Reserved.
2424
*
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.
2531
*/
2632

2733
#include <windows.h>
@@ -56,6 +62,11 @@
5662

5763
#include "MemoryModule.h"
5864

65+
struct ExportNameEntry {
66+
LPCSTR name;
67+
WORD idx;
68+
};
69+
5970
typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
6071
typedef int (WINAPI *ExeEntryProc)(void);
6172

@@ -72,6 +83,7 @@ typedef struct {
7283
CustomLoadLibraryFunc loadLibrary;
7384
CustomGetProcAddressFunc getProcAddress;
7485
CustomFreeLibraryFunc freeLibrary;
86+
struct ExportNameEntry *nameExportsTable;
7587
void *userdata;
7688
ExeEntryProc exeEntry;
7789
DWORD pageSize;
@@ -688,12 +700,27 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size,
688700
return NULL;
689701
}
690702

691-
FARPROC MemoryGetProcAddress(HMEMORYMODULE module, LPCSTR name)
703+
static int _compare(const void *a, const void *b)
704+
{
705+
const struct ExportNameEntry *p1 = (const struct ExportNameEntry*) a;
706+
const struct ExportNameEntry *p2 = (const struct ExportNameEntry*) b;
707+
return _stricmp(p1->name, p2->name);
708+
}
709+
710+
static int _find(const void *a, const void *b)
711+
{
712+
LPCSTR *name = (LPCSTR *) a;
713+
const struct ExportNameEntry *p = (const struct ExportNameEntry*) b;
714+
return _stricmp(*name, p->name);
715+
}
716+
717+
FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name)
692718
{
693-
unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase;
719+
PMEMORYMODULE module = (PMEMORYMODULE)mod;
720+
unsigned char *codeBase = module->codeBase;
694721
DWORD idx = 0;
695722
PIMAGE_EXPORT_DIRECTORY exports;
696-
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)module, IMAGE_DIRECTORY_ENTRY_EXPORT);
723+
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT);
697724
if (directory->Size == 0) {
698725
// no export table found
699726
SetLastError(ERROR_PROC_NOT_FOUND);
@@ -715,25 +742,44 @@ FARPROC MemoryGetProcAddress(HMEMORYMODULE module, LPCSTR name)
715742
}
716743

717744
idx = LOWORD(name) - exports->Base;
745+
} else if (!exports->NumberOfNames) {
746+
SetLastError(ERROR_PROC_NOT_FOUND);
747+
return NULL;
718748
} else {
719-
// search function name in list of exported names
720-
DWORD i;
721-
DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
722-
WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
723-
BOOL found = FALSE;
724-
for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) {
725-
if (_stricmp(name, (const char *) (codeBase + (*nameRef))) == 0) {
726-
idx = *ordinal;
727-
found = TRUE;
728-
break;
749+
const struct ExportNameEntry *found;
750+
751+
// Lazily build name table and sort it by names
752+
if (!module->nameExportsTable) {
753+
DWORD i;
754+
DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
755+
WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
756+
struct ExportNameEntry *entry = (struct ExportNameEntry*) malloc(exports->NumberOfNames * sizeof(struct ExportNameEntry));
757+
module->nameExportsTable = entry;
758+
if (!entry) {
759+
SetLastError(ERROR_OUTOFMEMORY);
760+
return NULL;
729761
}
762+
for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++, entry++) {
763+
entry->name = (const char *) (codeBase + (*nameRef));
764+
entry->idx = *ordinal;
765+
}
766+
qsort(module->nameExportsTable,
767+
exports->NumberOfNames,
768+
sizeof(struct ExportNameEntry), _compare);
730769
}
731770

771+
// search function name in list of exported names with binary search
772+
found = (const struct ExportNameEntry*) bsearch(&name,
773+
module->nameExportsTable,
774+
exports->NumberOfNames,
775+
sizeof(struct ExportNameEntry), _find);
732776
if (!found) {
733777
// exported symbol not found
734778
SetLastError(ERROR_PROC_NOT_FOUND);
735779
return NULL;
736780
}
781+
782+
idx = found->idx;
737783
}
738784

739785
if (idx > exports->NumberOfFunctions) {
@@ -759,6 +805,7 @@ void MemoryFreeLibrary(HMEMORYMODULE mod)
759805
(*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0);
760806
}
761807

808+
free(module->nameExportsTable);
762809
if (module->modules != NULL) {
763810
// free previously opened libraries
764811
int i;

tests/LoadDll.cpp

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "../MemoryModule.h"
1313

14+
typedef int (*addProc)(int);
1415
typedef int (*addNumberProc)(int, int);
1516

1617
// Thanks to Tim Cooper (from http://stackoverflow.com/a/8584708)
@@ -127,6 +128,11 @@ BOOL LoadFromMemory(char *filename)
127128
}
128129

129130
addNumber = (addNumberProc)MemoryGetProcAddress(handle, "addNumbers");
131+
if (!addNumber) {
132+
_tprintf(_T("MemoryGetProcAddress(\"addNumber\") returned NULL\n"));
133+
result = FALSE;
134+
goto exit;
135+
}
130136
_tprintf(_T("From memory: %d\n"), addNumber(1, 2));
131137

132138
// the DLL only exports one function, try to load by ordinal value
@@ -218,15 +224,80 @@ BOOL LoadFromMemory(char *filename)
218224
return result;
219225
}
220226

227+
BOOL LoadExportsFromMemory(char *filename)
228+
{
229+
FILE *fp;
230+
unsigned char *data=NULL;
231+
long size;
232+
size_t read;
233+
HMEMORYMODULE handle = NULL;
234+
int i;
235+
BOOL result = TRUE;
236+
237+
fp = fopen(filename, "rb");
238+
if (fp == NULL)
239+
{
240+
printf("Can't open DLL file \"%s\".", filename);
241+
result = FALSE;
242+
goto exit;
243+
}
244+
245+
fseek(fp, 0, SEEK_END);
246+
size = ftell(fp);
247+
assert(size > 0);
248+
data = (unsigned char *)malloc(size);
249+
assert(data != NULL);
250+
fseek(fp, 0, SEEK_SET);
251+
read = fread(data, 1, size, fp);
252+
assert(read == static_cast<size_t>(size));
253+
fclose(fp);
254+
255+
handle = MemoryLoadLibrary(data, size);
256+
if (handle == NULL)
257+
{
258+
_tprintf(_T("Can't load library from memory.\n"));
259+
result = FALSE;
260+
goto exit;
261+
}
262+
263+
for (i = 1; i <= 100; i++) {
264+
char name[100];
265+
sprintf(name, "add%d", i);
266+
addProc addNumber = (addProc)MemoryGetProcAddress(handle, name);
267+
if (!addNumber) {
268+
_tprintf(_T("MemoryGetProcAddress(\"%s\") returned NULL\n"), name);
269+
result = FALSE;
270+
goto exit;
271+
}
272+
int result = addNumber(1);
273+
if (result != 1 + i) {
274+
_tprintf(_T("(\"%s\") returned %d, expected %d\n"), name, result, 1 + i);
275+
result = FALSE;
276+
goto exit;
277+
}
278+
_tprintf(_T("%s: %d\n"), name, result);
279+
}
280+
exit:
281+
MemoryFreeLibrary(handle);
282+
free(data);
283+
return result;
284+
}
285+
221286
int main(int argc, char* argv[])
222287
{
223288
if (argc < 2) {
224289
fprintf(stderr, "USAGE: %s <filename.dll>\n", argv[0]);
225290
return 1;
226291
}
227292

228-
if (!LoadFromMemory(argv[1])) {
229-
return 2;
293+
if (!strstr((const char *) argv[1], "exports")) {
294+
if (!LoadFromMemory(argv[1])) {
295+
return 2;
296+
}
297+
} else {
298+
if (!LoadExportsFromMemory(argv[1])) {
299+
return 2;
300+
}
230301
}
231302

232303
return 0;

tests/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ TEST_DLLS = \
4747
test-align-800.dll \
4848
test-align-900.dll \
4949
test-relocate.dll \
50+
test-exports.dll
5051

5152
LOADDLL_OBJ = LoadDll.o ../MemoryModule.o
5253
TESTSUITE_OBJ = TestSuite.o ../MemoryModule.o
@@ -72,6 +73,12 @@ test-align-%.dll: $(DLL_OBJ)
7273
test-relocate.dll: $(DLL_OBJ)
7374
$(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -Wl,--image-base -Wl,0x20000000 -o $@ $(DLL_OBJ)
7475

76+
test-exports.dll: SampleExports.o
77+
$(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -o $@ SampleExports.o
78+
79+
SampleExports.cpp: generate-exports.sh
80+
./generate-exports.sh
81+
7582
%.o: %.cpp
7683
$(CXX) $(CFLAGS) $(CFLAGS_DLL) -c $<
7784

tests/generate-exports.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/bin/sh
2+
3+
##
4+
## Generate header file.
5+
##
6+
7+
cat > SampleExports.h << EOF
8+
extern "C" {
9+
10+
#ifdef SAMPLEDLL_EXPORTS
11+
#define SAMPLEDLL_API __declspec(dllexport)
12+
#else
13+
#define SAMPLEDLL_API __declspec(dllimport)
14+
#endif
15+
16+
EOF
17+
18+
for i in `seq 1 100`;
19+
do
20+
cat >> SampleExports.h << EOF
21+
SAMPLEDLL_API int add$i(int a);
22+
EOF
23+
done
24+
25+
cat >> SampleExports.h << EOF
26+
}
27+
EOF
28+
29+
30+
##
31+
## Generate source file.
32+
##
33+
34+
cat > SampleExports.cpp << EOF
35+
#include "SampleExports.h"
36+
37+
extern "C" {
38+
EOF
39+
40+
for i in `seq 1 100 | sort -R`;
41+
do
42+
cat >> SampleExports.cpp << EOF
43+
SAMPLEDLL_API int add$i(int a)
44+
{
45+
return a + $i;
46+
}
47+
EOF
48+
done
49+
50+
cat >> SampleExports.cpp << EOF
51+
}
52+
EOF

0 commit comments

Comments
 (0)
0