@@ -456,7 +456,11 @@ ut64 Elf_(ppc64_get_plt_stub_for_slot)(ELFOBJ *eo, ut64 slot_vaddr) {
456456// cmpldi r2, 0; bnectr+; b <glink stub>
457457// the trailing branch into a known glink stub names the plt slot it serves
458458#define PPC64_STD_R2_40R1 0xf8410028
459- #define PPC64_TEXT_STUB_SIZE 28
459+ #define PPC64_TEXT_STUB_MIN 24
460+ // longest form: std, addis, ld, mtctr, ld env, ld r2, cmpldi, bnectr, b
461+ #define PPC64_TEXT_STUB_MAX 36
462+ // bytes of executable ranges worth scanning for stubs before giving up
463+ #define PPC64_TEXT_SCAN_MAX 0x2000000
460464
461465// reloc index + 1 of the slot a plt_call stub serves, 0 when it is not one;
462466// after the std comes an optional addis r11, r2, ha, then ld rX, d(base);
@@ -485,13 +489,25 @@ static ut64 ppc64v1_text_stub_reloc(ELFOBJ *eo, const ut8 *w, ut64 vaddr,
485489 || (op & 0xfc1fffff ) != 0x7c0903a6 || ((op >> 21 ) & 0x1f ) != reg ) { // mtctr rX
486490 return 0 ;
487491 }
492+ // --plt-static-chain also loads the descriptor env word, ld rX, d+16(base),
493+ // before the toc restore or after it, depending on whether it clobbers base
488494 op = r_read_ble32 (w + 4 * ++ i , eo -> endian );
495+ bool env = false;
496+ if ((op & 0xfc1f0003 ) == (0xe8000000 | (base << 16 )) && ((op >> 21 ) & 0x1f ) != base
497+ && ((op >> 21 ) & 0x1f ) != 2 && (st32 )(st16 )(op & 0xfffc ) == lo + 16 ) {
498+ env = true;
499+ op = r_read_ble32 (w + 4 * ++ i , eo -> endian );
500+ }
489501 // the code address and the toc it runs with come from one descriptor
490502 if ((op & 0xffff0003 ) != (0xe8400000 | (base << 16 )) // ld r2, d+8(base)
491503 || (st32 )(st16 )(op & 0xfffc ) != lo + 8 ) {
492504 return 0 ;
493505 }
494506 op = r_read_ble32 (w + 4 * ++ i , eo -> endian );
507+ if (!env && (op & 0xfc1f0003 ) == (0xe8000000 | (base << 16 )) && ((op >> 21 ) & 0x1f ) != 2
508+ && (st32 )(st16 )(op & 0xfffc ) == lo + 16 ) {
509+ op = r_read_ble32 (w + 4 * ++ i , eo -> endian );
510+ }
495511 if (op == 0x4e800420 ) { // bctr: an eager stub, only the toc names the slot
496512 * size = 4 * (i + 1 );
497513 return toc ? ht_uu_find (rel_by_slot , toc + ha + lo , NULL ): 0 ;
@@ -529,54 +545,41 @@ static const char *ppc64v1_stub_target(ELFOBJ *eo, RBinElfReloc *rel, const char
529545 return NULL ;
530546}
531547
532- void Elf_ (plt_ppc64v1_load_text_stubs )(ELFOBJ * eo ) {
533- if (Elf_ (plt_ppc64_abi ) (eo ) != 1 ) {
534- return ;
535- }
536- Elf_ (load_symbols_vec ) (eo );
537- RBinElfSection * text = Elf_ (plt_section_by_name ) (eo , ".text" );
538- if (!text || text -> size < PPC64_TEXT_STUB_SIZE || text -> size > 0x2000000 ) {
548+ typedef struct {
549+ HtUU * rel_by_glink ;
550+ HtUU * rel_by_slot ;
551+ ut64 toc ;
552+ ut64 budget ;
553+ } PPC64StubScan ;
554+
555+ // scan one executable file range for plt_call stubs, clamped to the file and
556+ // to the remaining scan budget so corrupt headers cannot make this expensive
557+ static void ppc64v1_scan_stubs (ELFOBJ * eo , PPC64StubScan * sc , ut64 paddr , ut64 vaddr , ut64 size ) {
558+ const ut64 fsz = r_buf_size (eo -> b );
559+ if (paddr >= fsz || !sc -> budget ) {
539560 return ;
540561 }
541- // each glink stub or plt slot identifies its reloc, and that its target
542- RBinElfSection * got = Elf_ (plt_section_by_name ) (eo , ".got" );
543- const ut64 toc = got ? got -> rva + 0x8000 : 0 ;
544- HtUU * rel_by_glink = ht_uu_new0 ();
545- HtUU * rel_by_slot = ht_uu_new0 ();
546- if (!rel_by_glink || !rel_by_slot ) {
547- ht_uu_free (rel_by_glink );
548- ht_uu_free (rel_by_slot );
562+ size = R_MIN (size , fsz - paddr );
563+ size = R_MIN (size , sc -> budget );
564+ if (size < PPC64_TEXT_STUB_MIN ) {
549565 return ;
550566 }
551- RBinElfReloc * rel ;
552- ut64 nrel = 0 ;
553- bool any = false;
554- R_VEC_FOREACH (& eo -> g_relocs , rel ) {
555- nrel ++ ;
556- if (rel -> type == R_PPC64_JMP_SLOT && rel -> sym > 0 ) {
557- const ut64 glink = Elf_ (ppc64_get_plt_stub_for_slot ) (eo , rel -> rva );
558- any |= ht_uu_insert (rel_by_slot , rel -> rva , nrel );
559- if (glink != UT64_MAX ) {
560- ht_uu_insert (rel_by_glink , glink , nrel );
561- }
562- }
563- }
564- ut8 * buf = any ? malloc (text -> size ): NULL ;
565- if (!buf || r_buf_read_at (eo -> b , text -> offset , buf , text -> size ) != (st64 )text -> size ) {
567+ sc -> budget -= size ;
568+ // zero padding lets the matcher read a full stub window at the range end
569+ ut8 * buf = calloc (size + PPC64_TEXT_STUB_MAX , 1 );
570+ if (!buf || r_buf_read_at (eo -> b , paddr , buf , size ) != (st64 )size ) {
566571 free (buf );
567- ht_uu_free (rel_by_glink );
568- ht_uu_free (rel_by_slot );
569572 return ;
570573 }
571574 ut64 i ;
572- for (i = 0 ; i + 32 <= text -> size ; i += 4 ) {
575+ for (i = 0 ; i + PPC64_TEXT_STUB_MIN <= size ; i += 4 ) {
573576 if (r_read_ble32 (buf + i , eo -> endian ) != PPC64_STD_R2_40R1 ) {
574577 continue ;
575578 }
576- const ut64 vaddr = text -> rva + i ;
577579 ut32 stub_size = 0 ;
578- const ut64 relnum = ppc64v1_text_stub_reloc (eo , buf + i , vaddr ,
579- rel_by_glink , rel_by_slot , toc , & stub_size );
580+ const ut64 relnum = ppc64v1_text_stub_reloc (eo , buf + i , vaddr + i ,
581+ sc -> rel_by_glink , sc -> rel_by_slot , sc -> toc , & stub_size );
582+ RBinElfReloc * rel ;
580583 if (!relnum || !(rel = RVecRBinElfReloc_at (& eo -> g_relocs , relnum - 1 ))) {
581584 continue ;
582585 }
@@ -592,13 +595,67 @@ void Elf_(plt_ppc64v1_load_text_stubs)(ELFOBJ *eo) {
592595 sym .type = R_BIN_TYPE_FUNC_STR ;
593596 sym .attr .size = stub_size ;
594597 sym .ordinal = rel -> sym ;
595- sym .vaddr = vaddr ;
596- sym .paddr = text -> offset + i ;
598+ sym .vaddr = vaddr + i ;
599+ sym .paddr = paddr + i ;
597600 RVecRBinSymbol_push_back (& eo -> plt_symbols_cache , & sym );
598601 }
599602 free (buf );
600- ht_uu_free (rel_by_glink );
601- ht_uu_free (rel_by_slot );
603+ }
604+
605+ void Elf_ (plt_ppc64v1_load_text_stubs )(ELFOBJ * eo ) {
606+ if (Elf_ (plt_ppc64_abi ) (eo ) != 1 ) {
607+ return ;
608+ }
609+ Elf_ (load_symbols_vec ) (eo );
610+ // each glink stub or plt slot identifies its reloc, and that its target
611+ RBinElfSection * got = Elf_ (plt_section_by_name ) (eo , ".got" );
612+ PPC64StubScan sc = {
613+ .rel_by_glink = ht_uu_new0 (),
614+ .rel_by_slot = ht_uu_new0 (),
615+ .toc = got ? got -> rva + 0x8000 : 0 ,
616+ .budget = PPC64_TEXT_SCAN_MAX ,
617+ };
618+ if (!sc .rel_by_glink || !sc .rel_by_slot ) {
619+ goto beach ;
620+ }
621+ RBinElfReloc * rel ;
622+ ut64 nrel = 0 ;
623+ bool any = false;
624+ R_VEC_FOREACH (& eo -> g_relocs , rel ) {
625+ nrel ++ ;
626+ if (rel -> type == R_PPC64_JMP_SLOT && rel -> sym > 0 ) {
627+ const ut64 glink = Elf_ (ppc64_get_plt_stub_for_slot ) (eo , rel -> rva );
628+ any |= ht_uu_insert (sc .rel_by_slot , rel -> rva , nrel );
629+ if (glink != UT64_MAX ) {
630+ ht_uu_insert (sc .rel_by_glink , glink , nrel );
631+ }
632+ }
633+ }
634+ if (!any ) {
635+ goto beach ;
636+ }
637+ // bfd emits stubs into every code section (.init holds __gmon_start__),
638+ // and without section headers the executable segments are all we have
639+ bool scanned = false;
640+ RBinElfSection * s ;
641+ R_VEC_FOREACH (& eo -> g_sections , s ) {
642+ if (s -> type == SHT_PROGBITS && (s -> flags & SHF_EXECINSTR )) {
643+ ppc64v1_scan_stubs (eo , & sc , s -> offset , s -> rva , s -> size );
644+ scanned = true;
645+ }
646+ }
647+ if (!scanned && eo -> phdr ) {
648+ int i ;
649+ for (i = 0 ; i < eo -> ehdr .e_phnum ; i ++ ) {
650+ const Elf_ (Phdr ) * p = & eo -> phdr [i ];
651+ if (p -> p_type == PT_LOAD && (p -> p_flags & PF_X )) {
652+ ppc64v1_scan_stubs (eo , & sc , p -> p_offset , p -> p_vaddr , p -> p_filesz );
653+ }
654+ }
655+ }
656+ beach :
657+ ht_uu_free (sc .rel_by_glink );
658+ ht_uu_free (sc .rel_by_slot );
602659}
603660#endif
604661
0 commit comments