@@ -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
6878static 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+
132206static void
133207FinalizeSections (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