3 #include <libelfu/libelfu.h>
6 static int cmpPhdrType(const void *v1, const void *v2)
8 const GElf_Phdr *p1 = (const GElf_Phdr*)v1;
9 const GElf_Phdr *p2 = (const GElf_Phdr*)v2;
11 /* These entries go first */
12 if (p1->p_type == PT_PHDR) {
14 } else if (p2->p_type == PT_PHDR) {
16 } else if (p1->p_type == PT_INTERP) {
18 } else if (p2->p_type == PT_INTERP) {
22 /* These entries go last */
23 if (p1->p_type == PT_GNU_RELRO) {
25 } else if (p2->p_type == PT_GNU_RELRO) {
27 } else if (p1->p_type == PT_GNU_STACK) {
29 } else if (p2->p_type == PT_GNU_STACK) {
31 } else if (p1->p_type == PT_GNU_EH_FRAME) {
33 } else if (p2->p_type == PT_GNU_EH_FRAME) {
35 } else if (p1->p_type == PT_NOTE) {
37 } else if (p2->p_type == PT_NOTE) {
39 } else if (p1->p_type == PT_DYNAMIC) {
41 } else if (p2->p_type == PT_DYNAMIC) {
45 /* Sort the rest by vaddr. */
46 if (p1->p_vaddr < p2->p_vaddr) {
48 } else if (p1->p_vaddr > p2->p_vaddr) {
52 /* Everything else is equal */
58 /* The ELF specification wants PHDR and INTERP headers to come first.
59 * Typical GNU programs also contain DYNAMIC, NOTE, and GNU_* after
61 * Reorder them to comply with the spec. Otherwise the dynamic linker
62 * will calculate the base address incorrectly and crash.
64 * Both Linux and glibc's ld.so are unhelpful here:
65 * Linux calculates the PH table address as
66 * phdrs = (base + e_phoff)
67 * and passes that to ld.so.
68 * ld.so 'recovers' the base address as
69 * base = (phdrs - PT_PHDR.p_vaddr)
71 * Behold those who try to move the PHDR table away from the first
72 * page of memory, where it is stored at the address calculated by
73 * Linux. Things will crash. Badly.
75 * Unfortunately the ELF spec itself states that the PHT is in the
76 * first page of memory. Not much we can do here.
78 void elfu_eReorderPhdrs(Elf *e)
84 assert(!elf_getphdrnum(e, &numPhdr));
86 GElf_Phdr phdrs[numPhdr];
88 for (i = 0; i < numPhdr; i++) {
91 if (gelf_getphdr(e, i, &phdr) != &phdr) {
92 ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
99 qsort(phdrs, numPhdr, sizeof(*phdrs), cmpPhdrType);
101 for (i = 0; i < numPhdr; i++) {
102 assert(gelf_update_phdr(e, i, &phdrs[i]));