@@ -211,6 +211,57 @@ const char* ElfPath(elfheader_t* head)
211211 return head -> path ;
212212}
213213
214+ static uintptr_t ElfPageStart (uintptr_t addr )
215+ {
216+ return addr & ~(box64_pagesize - 1 );
217+ }
218+
219+ static uintptr_t ElfPageEnd (uintptr_t addr , size_t size )
220+ {
221+ return ALIGN (addr + size );
222+ }
223+
224+ static uint32_t ElfFlagsToProt (uint32_t flags )
225+ {
226+ uint32_t prot = 0 ;
227+ if (flags & PF_R ) prot |= PROT_READ ;
228+ if ((flags & PF_W ) || box64_unittest_mode ) prot |= PROT_WRITE ;
229+ if (flags & PF_X ) prot |= PROT_EXEC ;
230+ return prot ;
231+ }
232+
233+ static int ElfPhdrNeedsCopyLoad64 (elfheader_t * head , int phidx , uintptr_t offs )
234+ {
235+ Elf64_Phdr * e = & head -> PHEntries ._64 [phidx ];
236+ if (box64_pagesize <= 4096 )
237+ return 0 ;
238+
239+ uintptr_t seg_addr = e -> p_paddr + offs ;
240+ uintptr_t seg_addr_end = seg_addr + e -> p_memsz ;
241+ uintptr_t host_start = ElfPageStart (seg_addr );
242+ uintptr_t host_end = ElfPageEnd (seg_addr , e -> p_memsz );
243+
244+ // Partial host-page coverage must be copy-loaded so adjacent PT_LOADs can
245+ // share the host page safely while we defer the final protection fixup.
246+ if (seg_addr != host_start || seg_addr_end != host_end )
247+ return 1 ;
248+
249+ for (size_t i = 0 ; i < head -> numPHEntries ; ++ i ) {
250+ Elf64_Phdr * other = & head -> PHEntries ._64 [i ];
251+ if (i == phidx || other -> p_type != PT_LOAD || !other -> p_flags || !other -> p_memsz )
252+ continue ;
253+
254+ uintptr_t other_addr = other -> p_paddr + offs ;
255+ uintptr_t other_host_start = ElfPageStart (other_addr );
256+ uintptr_t other_host_end = ElfPageEnd (other_addr , other -> p_memsz );
257+
258+ if (other_host_end > host_start && other_host_start < host_end )
259+ return 1 ;
260+ }
261+
262+ return 0 ;
263+ }
264+
214265int AllocLoadElfMemory32 (box64context_t * context , elfheader_t * head , int mainbin )
215266#ifndef BOX32
216267{ return 1 ; }
@@ -300,7 +351,7 @@ int AllocLoadElfMemory(box64context_t* context, elfheader_t* head, int mainbin)
300351 head -> multiblocks [n ].size = e -> p_filesz ;
301352 head -> multiblocks [n ].align = e -> p_align ;
302353 // HACK: Mark all the code pages writable in unittest mode because some tests mix code and (writable) data...
303- uint8_t prot = (( e -> p_flags & PF_R )? PROT_READ : 0 )|((( e -> p_flags & PF_W ) || box64_unittest_mode )? PROT_WRITE : 0 )|(( e -> p_flags & PF_X )? PROT_EXEC : 0 );
354+ uint8_t prot = ElfFlagsToProt ( e -> p_flags );
304355 // check if alignment is correct
305356 uintptr_t balign = head -> multiblocks [n ].align - 1 ;
306357 if (balign < (box64_pagesize - 1 )) balign = box64_pagesize - 1 ;
@@ -316,6 +367,8 @@ int AllocLoadElfMemory(box64context_t* context, elfheader_t* head, int mainbin)
316367 try_mmap = 0 ;
317368 if (e -> p_align < box64_pagesize )
318369 try_mmap = 0 ;
370+ if (ElfPhdrNeedsCopyLoad64 (head , i , offs ))
371+ try_mmap = 0 ;
319372 if (try_mmap ) {
320373 printf_dump (log_level , "Mmaping 0x%lx(0x%lx) bytes @%p with prot %x for Elf \"%s\"\n" , head -> multiblocks [n ].size , head -> multiblocks [n ].asize , (void * )head -> multiblocks [n ].paddr , prot , head -> name );
321374 void * p = InternalMmap (
@@ -426,17 +479,50 @@ int AllocLoadElfMemory(box64context_t* context, elfheader_t* head, int mainbin)
426479 memset (dest + e -> p_filesz , 0 , e -> p_memsz - e -> p_filesz );
427480 }
428481 }
429- // deferred mprotect: apply final protections after all segments are loaded
430- // this avoids the case where mprotect on a shared host page (e.g. 64KB) strips
431- // PROT_WRITE before a later segment that shares the same page has been read into memory
432- for (int j = 0 ; j < n ; j ++ ) {
433- if (!(head -> multiblocks [j ].flags & PF_W )) {
434- uintptr_t start = head -> multiblocks [j ].paddr & ~(box64_pagesize - 1 );
435- uintptr_t end = ALIGN (head -> multiblocks [j ].paddr + head -> multiblocks [j ].asize );
436- for (uintptr_t page = start ; page < end ; page += box64_pagesize ) {
437- uint32_t prot = getProtection (page );
438- if (prot && !(prot & PROT_WRITE ))
439- mprotect ((void * )page , box64_pagesize , prot & ~PROT_CUSTOM );
482+ if (box64_pagesize > 4096 ) {
483+ uintptr_t min_hostpage = UINTPTR_MAX ;
484+ uintptr_t max_hostpage = 0 ;
485+ for ( int j = 0 ;j < n ;j ++ ) {
486+ uintptr_t seg_page_start = ElfPageStart (head -> multiblocks [j ].paddr );
487+ uintptr_t seg_page_end = seg_page_start + head -> multiblocks [j ].asize ;
488+ if (seg_page_start < min_hostpage )
489+ min_hostpage = seg_page_start ;
490+ if (seg_page_end > max_hostpage )
491+ max_hostpage = seg_page_end ;
492+ }
493+ for ( uintptr_t page = min_hostpage ; page < max_hostpage ; page += box64_pagesize ) {
494+ uint32_t final_prot = 0 ;
495+ int covered = 0 ;
496+ for (int j = 0 ; j < n ; ++ j ) {
497+ uintptr_t seg_page_start = ElfPageStart (head -> multiblocks [j ].paddr );
498+ uintptr_t seg_page_end = seg_page_start + head -> multiblocks [j ].asize ;
499+ if (seg_page_end <= page || seg_page_start >= page + box64_pagesize )
500+ continue ;
501+ uint32_t seg_prot = ElfFlagsToProt (head -> multiblocks [j ].flags );
502+ final_prot |= seg_prot ;
503+ covered = 1 ;
504+ }
505+ if (!covered ) continue ;
506+ setProtection_elf (page , box64_pagesize , final_prot );
507+ if (mprotect ((void * )page , box64_pagesize , final_prot & ~PROT_CUSTOM ) != 0 ) {
508+ printf_log (LOG_INFO , "Warning, mprotect failed on ELF host page %p (%s)\n" ,
509+ (void * )page , strerror (errno ));
510+ }
511+ }
512+
513+ } else {
514+ // deferred mprotect: apply final protections after all segments are loaded
515+ // this avoids the case where mprotect on a shared host page (e.g. 64KB) strips
516+ // PROT_WRITE before a later segment that shares the same page has been read into memory
517+ for (int j = 0 ; j < n ; j ++ ) {
518+ if (!(head -> multiblocks [j ].flags & PF_W )) {
519+ uintptr_t start = head -> multiblocks [j ].paddr & ~(box64_pagesize - 1 );
520+ uintptr_t end = ALIGN (head -> multiblocks [j ].paddr + head -> multiblocks [j ].asize );
521+ for (uintptr_t page = start ; page < end ; page += box64_pagesize ) {
522+ uint32_t prot = getProtection (page );
523+ if (prot && !(prot & PROT_WRITE ))
524+ mprotect ((void * )page , box64_pagesize , prot & ~PROT_CUSTOM );
525+ }
440526 }
441527 }
442528 }
0 commit comments