Skip to content

Commit aa6b93b

Browse files
committed
Support section alignments that are smaller than the memory page size (fixes #20/#21).
1 parent 927bd2a commit aa6b93b

File tree

1 file changed

+113
-33
lines changed

1 file changed

+113
-33
lines changed

MemoryModule.c

+113-33
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,19 @@ typedef struct {
6060
CustomFreeLibraryFunc freeLibrary;
6161
void *userdata;
6262
ExeEntryProc exeEntry;
63+
DWORD pageSize;
6364
} MEMORYMODULE, *PMEMORYMODULE;
6465

66+
typedef struct {
67+
LPVOID address;
68+
LPVOID alignedAddress;
69+
DWORD size;
70+
DWORD characteristics;
71+
BOOL last;
72+
} SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
73+
6574
#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
75+
#define ALIGN_DOWN(address, alignment) (LPVOID)((uintptr_t)(address) & ~((alignment) - 1))
6676

6777
#ifdef DEBUG_OUTPUT
6878
static void
@@ -98,6 +108,9 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO
98108
MEM_COMMIT,
99109
PAGE_READWRITE);
100110

111+
// Always use position from file to support alignments smaller
112+
// than page size.
113+
dest = codeBase + section->VirtualAddress;
101114
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
102115
memset(dest, 0, size);
103116
}
@@ -111,6 +124,10 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO
111124
section->SizeOfRawData,
112125
MEM_COMMIT,
113126
PAGE_READWRITE);
127+
128+
// Always use position from file to support alignments smaller
129+
// than page size.
130+
dest = codeBase + section->VirtualAddress;
114131
memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
115132
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
116133
}
@@ -129,6 +146,63 @@ static int ProtectionFlags[2][2][2] = {
129146
},
130147
};
131148

149+
static DWORD
150+
GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) {
151+
DWORD size = section->SizeOfRawData;
152+
if (size == 0) {
153+
if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
154+
size = module->headers->OptionalHeader.SizeOfInitializedData;
155+
} else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
156+
size = module->headers->OptionalHeader.SizeOfUninitializedData;
157+
}
158+
}
159+
return size;
160+
}
161+
162+
static BOOL
163+
FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) {
164+
DWORD protect, oldProtect;
165+
int executable;
166+
int readable;
167+
int writeable;
168+
169+
if (sectionData->size == 0) {
170+
return TRUE;
171+
}
172+
173+
if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
174+
// section is not needed any more and can safely be freed
175+
if (sectionData->address == sectionData->alignedAddress &&
176+
(sectionData->last ||
177+
module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
178+
(sectionData->size % module->pageSize) == 0)
179+
) {
180+
// Only allowed to decommit whole pages
181+
VirtualFree(sectionData->address, sectionData->size, MEM_DECOMMIT);
182+
}
183+
return TRUE;
184+
}
185+
186+
// determine protection flags based on characteristics
187+
executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
188+
readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0;
189+
writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0;
190+
protect = ProtectionFlags[executable][readable][writeable];
191+
if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
192+
protect |= PAGE_NOCACHE;
193+
}
194+
195+
// change memory access flags
196+
if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) {
197+
#ifdef DEBUG_OUTPUT
198+
OutputLastError("Error protecting memory page")
199+
#endif
200+
return FALSE;
201+
}
202+
203+
return TRUE;
204+
}
205+
132206
static void
133207
FinalizeSections(PMEMORYMODULE module)
134208
{
@@ -139,45 +213,41 @@ FinalizeSections(PMEMORYMODULE module)
139213
#else
140214
#define imageOffset 0
141215
#endif
216+
SECTIONFINALIZEDATA sectionData;
217+
sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
218+
sectionData.alignedAddress = ALIGN_DOWN(sectionData.address, module->pageSize);
219+
sectionData.size = GetRealSectionSize(module, section);
220+
sectionData.characteristics = section->Characteristics;
221+
sectionData.last = FALSE;
222+
section++;
142223

143224
// loop through all sections and change access flags
144-
for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
145-
DWORD protect, oldProtect, size;
146-
int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
147-
int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
148-
int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
149-
150-
if (section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
151-
// section is not needed any more and can safely be freed
152-
VirtualFree((LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset), section->SizeOfRawData, MEM_DECOMMIT);
153-
continue;
154-
}
155-
156-
// determine protection flags based on characteristics
157-
protect = ProtectionFlags[executable][readable][writeable];
158-
if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
159-
protect |= PAGE_NOCACHE;
160-
}
161-
162-
// determine size of region
163-
size = section->SizeOfRawData;
164-
if (size == 0) {
165-
if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
166-
size = module->headers->OptionalHeader.SizeOfInitializedData;
167-
} else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
168-
size = module->headers->OptionalHeader.SizeOfUninitializedData;
225+
for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
226+
LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
227+
LPVOID alignedAddress = ALIGN_DOWN(sectionAddress, module->pageSize);
228+
DWORD sectionSize = GetRealSectionSize(module, section);
229+
// Combine access flags of all sections that share a page
230+
// TODO(fancycode): We currently share flags of a trailing large section
231+
// with the page of a first small section. This should be optimized.
232+
if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) {
233+
// Section shares page with previous
234+
if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) {
235+
sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
236+
} else {
237+
sectionData.characteristics |= section->Characteristics;
169238
}
239+
sectionData.size = (((uintptr_t)sectionAddress) + sectionSize) - (uintptr_t) sectionData.address;
240+
continue;
170241
}
171242

172-
if (size > 0) {
173-
// change memory access flags
174-
if (VirtualProtect((LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset), size, protect, &oldProtect) == 0)
175-
#ifdef DEBUG_OUTPUT
176-
OutputLastError("Error protecting memory page")
177-
#endif
178-
;
179-
}
243+
FinalizeSection(module, &sectionData);
244+
sectionData.address = sectionAddress;
245+
sectionData.alignedAddress = alignedAddress;
246+
sectionData.size = sectionSize;
247+
sectionData.characteristics = section->Characteristics;
180248
}
249+
sectionData.last = TRUE;
250+
FinalizeSection(module, &sectionData);
181251
#ifndef _WIN64
182252
#undef imageOffset
183253
#endif
@@ -358,6 +428,7 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
358428
unsigned char *code, *headers;
359429
SIZE_T locationDelta;
360430
BOOL successfull;
431+
SYSTEM_INFO sysInfo;
361432

362433
dos_header = (PIMAGE_DOS_HEADER)data;
363434
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
@@ -380,6 +451,12 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
380451
return NULL;
381452
}
382453

454+
if (old_header->OptionalHeader.SectionAlignment & 1) {
455+
// Only support section alignments that are a multiple of 2
456+
SetLastError(ERROR_BAD_EXE_FORMAT);
457+
return NULL;
458+
}
459+
383460
// reserve memory for image of library
384461
// XXX: is it correct to commit the complete memory region at once?
385462
// calling DllEntry raises an exception if we don't...
@@ -417,6 +494,9 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
417494
result->freeLibrary = freeLibrary;
418495
result->userdata = userdata;
419496

497+
GetNativeSystemInfo(&sysInfo);
498+
result->pageSize = sysInfo.dwPageSize;
499+
420500
// commit memory for headers
421501
headers = (unsigned char *)VirtualAlloc(code,
422502
old_header->OptionalHeader.SizeOfHeaders,

0 commit comments

Comments
 (0)