Skip to content

Commit 0e2fde2

Browse files
committed
Scan every code range for ppc64 plt call stubs and accept static-chain loads ##bin
The stub scan only walked .text, so it missed the __gmon_start__ stub bfd puts in .init and found nothing at all on section-less files. Walk every executable section, falling back to the PT_LOAD PF_X segments when there are none, with each range clamped to the file and a shared 32MB budget so corrupt section headers cannot inflate load time. The matcher now takes the optional ld rX, d+16(base) env load that --plt-static-chain emits, before or after the toc restore, and stubs at the tail of a range match through zero padding. Adds section-less, static-chain and bogus-shdr libz fixtures.
1 parent 512069a commit 0e2fde2

3 files changed

Lines changed: 144 additions & 44 deletions

File tree

libr/bin/format/elf/plt.c

Lines changed: 98 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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

test/db/anal/plt-local

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ fs symbols
162162
f~plt.~?
163163
EOF
164164
EXPECT=<<EOF
165-
49
165+
50
166166
EOF
167167
RUN
168168

test/db/formats/elf/plt

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ is~plt.~?
9696
is~plt.crc32_z
9797
EOF2
9898
EXPECT=<<EOF2
99-
49
99+
50
100100
29 0x00003ac0 0x00003ac0 GLOBAL FUNC 28 plt.crc32_z
101101
EOF2
102102
RUN
@@ -108,7 +108,50 @@ is~plt.~?
108108
is~plt.feof
109109
EOF2
110110
EXPECT=<<EOF2
111-
112
111+
113
112112
3 0x00003100 0x00003100 GLOBAL FUNC 24 plt.feof
113113
EOF2
114114
RUN
115+
116+
NAME=ppc64 ELFv1: section-less binaries fall back to the executable segments
117+
FILE=bins/elf/ppc64v1-libz-noshdr.so
118+
CMDS=<<EOF
119+
iS~.text~?
120+
is~plt.~?
121+
is~plt.crc32_z
122+
EOF
123+
EXPECT=<<EOF
124+
0
125+
48
126+
29 0x00003ac0 0x00003ac0 GLOBAL FUNC 28 plt.crc32_z
127+
EOF
128+
RUN
129+
130+
NAME=ppc64 ELFv1: --plt-static-chain env loads are accepted in both orders
131+
FILE=bins/elf/ppc64v1-libz-static-chain.so
132+
CMDS=<<EOF
133+
is~plt.~?
134+
is~plt.crc32_z
135+
o bins/elf/ppc64v1-libz-static-chain-shared.so
136+
is~plt.~?
137+
is~plt.crc32_z
138+
EOF
139+
EXPECT=<<EOF
140+
50
141+
29 0x00003ac0 0x00003ac0 GLOBAL FUNC 32 plt.crc32_z
142+
50
143+
29 0x00003ac0 0x00003ac0 GLOBAL FUNC 32 plt.crc32_z
144+
EOF
145+
RUN
146+
147+
NAME=ppc64 ELFv1: a bogus .text size is clamped to the file, .init still scanned
148+
FILE=bins/elf/ppc64v1-libz-badshdr.so
149+
CMDS=<<EOF
150+
is~plt.~?
151+
is~plt.__gmon_start__
152+
EOF
153+
EXPECT=<<EOF
154+
1
155+
12 0x00003a00 0x00003a00 WEAK FUNC 28 plt.__gmon_start__
156+
EOF
157+
RUN

0 commit comments

Comments
 (0)