From 9064e6222331bce3b8a3978e79fc287c85070cb2 Mon Sep 17 00:00:00 2001 From: norly Date: Mon, 24 Jun 2013 03:34:19 +0100 Subject: [PATCH] Reorder PHDRs according to ELF spec --- include/libelfu/elfops.h | 1 + src/libelfu/elfops/reorderphdrs.c | 104 ++++++++++++++++++ src/libelfu/modelops/toFile.c | 2 + tests/reference/putsmain32-cloned | Bin 6104 -> 6104 bytes .../putsmain32-with-puts-alternative32 | Bin 18696 -> 18696 bytes 5 files changed, 107 insertions(+) create mode 100644 src/libelfu/elfops/reorderphdrs.c diff --git a/include/libelfu/elfops.h b/include/libelfu/elfops.h index 48ef380..4131f41 100644 --- a/include/libelfu/elfops.h +++ b/include/libelfu/elfops.h @@ -8,6 +8,7 @@ int elfu_eCheck(Elf *e); +void elfu_eReorderPhdrs(Elf *e); #endif diff --git a/src/libelfu/elfops/reorderphdrs.c b/src/libelfu/elfops/reorderphdrs.c new file mode 100644 index 0000000..c2c92fd --- /dev/null +++ b/src/libelfu/elfops/reorderphdrs.c @@ -0,0 +1,104 @@ +#include +#include +#include + + +static int cmpPhdrType(const void *v1, const void *v2) +{ + const GElf_Phdr *p1 = (const GElf_Phdr*)v1; + const GElf_Phdr *p2 = (const GElf_Phdr*)v2; + + /* These entries go first */ + if (p1->p_type == PT_PHDR) { + return -1; + } else if (p2->p_type == PT_PHDR) { + return 1; + } else if (p1->p_type == PT_INTERP) { + return -1; + } else if (p2->p_type == PT_INTERP) { + return 1; + } + + /* These entries go last */ + if (p1->p_type == PT_GNU_RELRO) { + return 1; + } else if (p2->p_type == PT_GNU_RELRO) { + return -1; + } else if (p1->p_type == PT_GNU_STACK) { + return 1; + } else if (p2->p_type == PT_GNU_STACK) { + return -1; + } else if (p1->p_type == PT_GNU_EH_FRAME) { + return 1; + } else if (p2->p_type == PT_GNU_EH_FRAME) { + return -1; + } else if (p1->p_type == PT_NOTE) { + return 1; + } else if (p2->p_type == PT_NOTE) { + return -1; + } else if (p1->p_type == PT_DYNAMIC) { + return 1; + } else if (p2->p_type == PT_DYNAMIC) { + return -1; + } + + /* Sort the rest by vaddr. */ + if (p1->p_vaddr < p2->p_vaddr) { + return -1; + } else if (p1->p_vaddr > p2->p_vaddr) { + return 1; + } + + /* Everything else is equal */ + return 0; +} + + + +/* The ELF specification wants PHDR and INTERP headers to come first. + * Typical GNU programs also contain DYNAMIC, NOTE, and GNU_* after + * the LOAD segments. + * Reorder them to comply with the spec. Otherwise the dynamic linker + * will calculate the base address incorrectly and crash. + * + * Both Linux and glibc's ld.so are unhelpful here: + * Linux calculates the PH table address as + * phdrs = (base + e_phoff) + * and passes that to ld.so. + * ld.so 'recovers' the base address as + * base = (phdrs - PT_PHDR.p_vaddr) + * + * Behold those who try to move the PHDR table away from the first + * page of memory, where it is stored at the address calculated by + * Linux. Things will crash. Badly. + * + * Unfortunately the ELF spec itself states that the PHT is in the + * first page of memory. Not much we can do here. + */ +void elfu_eReorderPhdrs(Elf *e) +{ + size_t i, numPhdr; + + assert(e); + + assert(!elf_getphdrnum(e, &numPhdr)); + + GElf_Phdr phdrs[numPhdr]; + + for (i = 0; i < numPhdr; i++) { + GElf_Phdr phdr; + + if (gelf_getphdr(e, i, &phdr) != &phdr) { + ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + return; + } + + phdrs[i] = phdr; + } + + qsort(phdrs, numPhdr, sizeof(*phdrs), cmpPhdrType); + + for (i = 0; i < numPhdr; i++) { + assert(gelf_update_phdr(e, i, &phdrs[i])); + } +} diff --git a/src/libelfu/modelops/toFile.c b/src/libelfu/modelops/toFile.c index b42bc08..7b9e1f4 100644 --- a/src/libelfu/modelops/toFile.c +++ b/src/libelfu/modelops/toFile.c @@ -110,6 +110,8 @@ void elfu_mToElf(ElfuElf *me, Elf *e) elfu_mPhdrForall(me, modelToPhdr, &i, e); + elfu_eReorderPhdrs(e); + /* Done */ elf_flagelf(e, ELF_C_SET, ELF_F_DIRTY); diff --git a/tests/reference/putsmain32-cloned b/tests/reference/putsmain32-cloned index 973ea9c01bdb771776c0a2dfcddf8cc804f79e49..58328b4f0cc4bbe596d38b57e00fbf0e4d754c32 100755 GIT binary patch delta 23 fcmcbie?xzQ$;1$ci6#mYuQ*Jc;;>nS@rW1zdOrz? delta 23 fcmcbie?xzQ$;1+ci6#ycuQ*Jc;;>nS@rW1zd*2DE diff --git a/tests/reference/putsmain32-with-puts-alternative32 b/tests/reference/putsmain32-with-puts-alternative32 index c820ef55851e56e8e9e9dc17e7cc2fcbed99103d..953ddad817253b60089f9767dd21e825a416099d 100755 GIT binary patch delta 25 hcmeB}#Mm*3ae~Rj5Qm8-3KOq5Oq}AdS%mS6I{<*m3K9SS delta 25 hcmeB}#Mm*3ae~Rj5`~E-4im39Oq}AdS%mS6I{<=d3Nioy -- 2.30.2