1 /* This file is part of centaur.
3 * centaur is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License 2 as
5 * published by the Free Software Foundation.
7 * centaur is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with centaur. If not, see <http://www.gnu.org/licenses/>.
18 #include <libelfu/libelfu.h>
21 static int cmpPhdrType(const void *v1, const void *v2)
23 const GElf_Phdr *p1 = (const GElf_Phdr*)v1;
24 const GElf_Phdr *p2 = (const GElf_Phdr*)v2;
26 /* These entries go first */
27 if (p1->p_type == PT_PHDR) {
29 } else if (p2->p_type == PT_PHDR) {
31 } else if (p1->p_type == PT_INTERP) {
33 } else if (p2->p_type == PT_INTERP) {
37 /* These entries go last */
38 if (p1->p_type == PT_GNU_RELRO) {
40 } else if (p2->p_type == PT_GNU_RELRO) {
42 } else if (p1->p_type == PT_GNU_STACK) {
44 } else if (p2->p_type == PT_GNU_STACK) {
46 } else if (p1->p_type == PT_GNU_EH_FRAME) {
48 } else if (p2->p_type == PT_GNU_EH_FRAME) {
50 } else if (p1->p_type == PT_NOTE) {
52 } else if (p2->p_type == PT_NOTE) {
54 } else if (p1->p_type == PT_DYNAMIC) {
56 } else if (p2->p_type == PT_DYNAMIC) {
60 /* Sort the rest by vaddr. */
61 if (p1->p_vaddr < p2->p_vaddr) {
63 } else if (p1->p_vaddr > p2->p_vaddr) {
67 /* Everything else is equal */
73 /* The ELF specification wants PHDR and INTERP headers to come first.
74 * Typical GNU programs also contain DYNAMIC, NOTE, and GNU_* after
76 * Reorder them to comply with the spec. Otherwise the dynamic linker
77 * will calculate the base address incorrectly and crash.
79 * Both Linux and glibc's ld.so are unhelpful here:
80 * Linux calculates the PH table address as
81 * phdrs = (base + e_phoff)
82 * and passes that to ld.so.
83 * ld.so 'recovers' the base address as
84 * base = (phdrs - PT_PHDR.p_vaddr)
86 * Behold those who try to move the PHDR table away from the first
87 * page of memory, where it is stored at the address calculated by
88 * Linux. Things will crash. Badly.
90 * Unfortunately the ELF spec itself states that the PHT is in the
91 * first page of memory. Not much we can do here.
93 void elfu_eReorderPhdrs(Elf *e)
99 assert(!elf_getphdrnum(e, &numPhdr));
101 GElf_Phdr phdrs[numPhdr];
103 for (i = 0; i < numPhdr; i++) {
106 if (gelf_getphdr(e, i, &phdr) != &phdr) {
107 ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
114 qsort(phdrs, numPhdr, sizeof(*phdrs), cmpPhdrType);
116 for (i = 0; i < numPhdr; i++) {
117 assert(gelf_update_phdr(e, i, &phdrs[i]));