@@ -365,6 +365,83 @@ size_t emitLoaderMap(uint8_t *data, intptr_t addr, size_t len, off_t offset,
365365 return size;
366366}
367367
368+ /*
369+ * Get the offset for an address.
370+ */
371+ static off_t addrToOffset (const Binary *B, intptr_t lo, intptr_t hi)
372+ {
373+ const Elf64_Phdr *phdrs =
374+ (const Elf64_Phdr *)(B->patched .bytes + B->elf .ehdr ->e_phoff );
375+ size_t phnum = (size_t )B->elf .ehdr ->e_phnum ;
376+ for (unsigned i = 0 ; i < phnum; i++)
377+ {
378+ const Elf64_Phdr *phdr = phdrs + i;
379+ switch (phdr->p_type )
380+ {
381+ case PT_LOAD : case PT_GNU_RELRO :
382+ break ;
383+ default :
384+ continue ;
385+ }
386+ intptr_t base = (intptr_t )phdr->p_vaddr ;
387+ size_t size = (size_t )phdr->p_filesz ;
388+ off_t offset = (off_t )phdr->p_offset ;
389+ if (lo >= base && hi < base + (ssize_t )size)
390+ return offset + (lo - base);
391+ }
392+ return -1 ;
393+ }
394+
395+ /*
396+ * Replace an init/fini function.
397+ */
398+ intptr_t replaceInitFini (Binary *B, Elf64_Dyn *dyn_fn,
399+ const Elf64_Dyn *dyn_fn_array, size_t dyn_fn_arraysz,
400+ const Elf64_Dyn *dyn_rela, size_t dyn_relasz, intptr_t entry)
401+ {
402+ uint8_t *data = B->patched .bytes ;
403+ if (dyn_fn != nullptr )
404+ {
405+ intptr_t old = (intptr_t )dyn_fn->d_un .d_ptr ;
406+ dyn_fn->d_un .d_ptr = (Elf64_Addr)entry;
407+ return old;
408+ }
409+ else if (dyn_fn_array != nullptr && dyn_fn_arraysz > 0 )
410+ {
411+ intptr_t ptr = (intptr_t )dyn_fn_array->d_un .d_ptr ;
412+ intptr_t offset = addrToOffset (B, ptr, ptr);
413+ if (offset < 0 )
414+ return INTPTR_MIN ;
415+ Elf64_Addr *fns = (Elf64_Addr *)(data + offset);
416+ intptr_t old = (intptr_t )fns[0 ];
417+ fns[0 ] = (Elf64_Addr)entry;
418+ if (dyn_rela != nullptr && dyn_relasz > 0 )
419+ {
420+ // fns[0] may be subject to a relocation:
421+ intptr_t rela_ptr = (intptr_t )dyn_rela->d_un .d_ptr ;
422+ intptr_t rela_offset = addrToOffset (B, rela_ptr,
423+ rela_ptr + dyn_relasz);
424+ if (rela_offset < 0 )
425+ return old;
426+ size_t rela_size = dyn_relasz / sizeof (Elf64_Rela);
427+ Elf64_Rela *rela = (Elf64_Rela *)(B->patched .bytes + rela_offset);
428+ for (size_t i = 0 ; i < rela_size; i++)
429+ {
430+ if (ELF64_R_TYPE (rela[i].r_info ) != R_X86_64_RELATIVE )
431+ continue ;
432+ if ((intptr_t )rela[i].r_offset == ptr)
433+ {
434+ old = rela[i].r_addend ;
435+ rela[i].r_addend = (Elf64_Addr)entry;
436+ break ;
437+ }
438+ }
439+ }
440+ return old;
441+ }
442+ return INTPTR_MIN ;
443+ }
444+
368445/*
369446 * Emit the (modified) ELF binary.
370447 */
@@ -575,7 +652,10 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
575652
576653 // Step (5): Modify the entry/fini addresses.
577654 Elf64_Phdr *phdr = B->elf .phdr_dynamic ;
578- Elf64_Dyn *dyn_init = nullptr , *dyn_fini = nullptr ;
655+ Elf64_Dyn *dyn_init = nullptr , *dyn_init_array = nullptr ,
656+ *dyn_fini = nullptr , *dyn_fini_array = nullptr ,
657+ *dyn_rela = nullptr ;
658+ size_t dyn_init_arraysz = 0 , dyn_fini_arraysz = 0 , dyn_relasz = 0 ;
579659 if (phdr != nullptr )
580660 {
581661 config_elf->dynamic = (intptr_t )phdr->p_vaddr ;
@@ -588,11 +668,21 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
588668 switch (dynamic[i].d_tag )
589669 {
590670 case DT_INIT :
591- dyn_init = dynamic + i;
592- break ;
671+ dyn_init = dynamic + i; break ;
672+ case DT_INIT_ARRAY :
673+ dyn_init_array = dynamic + i; break ;
674+ case DT_INIT_ARRAYSZ :
675+ dyn_init_arraysz = (size_t )dynamic[i].d_un .d_val ; break ;
593676 case DT_FINI :
594- dyn_fini = dynamic + i;
595- break ;
677+ dyn_fini = dynamic + i; break ;
678+ case DT_FINI_ARRAY :
679+ dyn_fini_array = dynamic + i; break ;
680+ case DT_FINI_ARRAYSZ :
681+ dyn_fini_arraysz = (size_t )dynamic[i].d_un .d_val ; break ;
682+ case DT_RELA :
683+ dyn_rela = dynamic + i; break ;
684+ case DT_RELASZ :
685+ dyn_relasz = (size_t )dynamic[i].d_un .d_val ; break ;
596686 default :
597687 break ;
598688 }
@@ -609,22 +699,22 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
609699 break ;
610700 }
611701 case MODE_ELF_DSO :
612- if ( dyn_init == nullptr )
613- error ( " failed to replace DT_INIT entry; no DT_INIT entry "
614- " was found " );
615- config-> entry = ( intptr_t )dyn_init-> d_un . d_ptr ;
616- dyn_init-> d_un . d_ptr = (Elf64_Addr) entry;
702+ config-> entry = replaceInitFini (B, dyn_init, dyn_init_array,
703+ dyn_init_arraysz, dyn_rela, dyn_relasz, entry);
704+ if (config-> entry == INTPTR_MIN )
705+ error ( " failed to replace entry point; no DT_INIT or "
706+ " DT_INIT_ARRAY entry found " ) ;
617707 break ;
618708 default :
619709 error (" invalid mode" );
620710 }
621711 if (fini != 0x0 )
622712 {
623- if ( dyn_fini == nullptr )
624- error ( " failed to replace DT_FINI entry; no DT_FINI entry was "
625- " found " );
626- config-> fini = ( intptr_t )dyn_fini-> d_un . d_ptr ;
627- dyn_fini-> d_un . d_ptr = (Elf64_Addr)fini ;
713+ config-> fini = replaceInitFini (B, dyn_fini, dyn_fini_array,
714+ dyn_fini_arraysz, dyn_rela, dyn_relasz, fini);
715+ if (config-> fini == INTPTR_MIN )
716+ error ( " failed to replace finalization point; no DT_FINI or "
717+ " DT_FINI_ARRAY entry found " ) ;
628718 }
629719
630720 // Step (6): Modify the PHDR to load the loader.
0 commit comments