diff options
author | Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> | 2016-02-05 06:03:26 (GMT) |
---|---|---|
committer | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2016-02-22 19:34:09 (GMT) |
commit | 65a4dc6aa982191b11c739c4c9b802c7fa4cbebb (patch) | |
tree | 6d5530eb58db7bf0a4c8d56ba6e8c32d49e9ac61 /src | |
parent | a28fd74cc71e06909765e388edfafc35f2cff8a3 (diff) | |
download | patchelf-65a4dc6aa982191b11c739c4c9b802c7fa4cbebb.zip patchelf-65a4dc6aa982191b11c739c4c9b802c7fa4cbebb.tar.gz patchelf-65a4dc6aa982191b11c739c4c9b802c7fa4cbebb.tar.bz2 |
rewriteHeaders(): Don't assume PT_PHDR is the first program header
Because this assumption doesn't seem to be valid either in theory or
practice: the spec (http://refspecs.linuxbase.org/elf/elf.pdf) only
places these requirements on PT_PHDR:
"This segment type may not occur more than once in a file. Moreover,
it may occur only if the program header table is part of the memory
image of the program. If it is present, it must precede any loadable
segment entry."
And on ARM, binaries generated by GNU GCC / binutils almost never have
PT_PHDR as the first entry, e.g. the coreutils 'ls' has this:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x0169b4 0x0001e9b4 0x0001e9b4 0x00008 0x00008 R 0x4
PHDR 0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x00008154 0x00008154 0x0004f 0x0004f R 0x1
[Requesting program interpreter: /nix/store/whcad4dnkp5pnhbv4p0f9k8srv0kmcjk-glibc-2.21/lib/ld-linux-armhf.so.3]
LOAD 0x000000 0x00008000 0x00008000 0x169c0 0x169c0 R E 0x8000
LOAD 0x0169c0 0x000269c0 0x000269c0 0x003f4 0x01088 RW 0x8000
DYNAMIC 0x0169cc 0x000269cc 0x000269cc 0x000f8 0x000f8 RW 0x4
NOTE 0x0001a4 0x000081a4 0x000081a4 0x00020 0x00020 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
PAX_FLAGS 0x000000 0x00000000 0x00000000 0x00000 0x00000 0x4
This problem has existed for quite a long time on ARM and often results
in patchelf'd programs segfaulting inside the glibc dynamic linker,
which relies on PT_PHDR containing valid contents. This has been worked
around in Nixpkgs in various creative ways, like:
https://github.com/NixOS/nixpkgs/blob/5c20877d40726b6973d222f71fa6e306428c19cf/nixos/modules/system/boot/stage-1.nix#L109
Applying patchelf twice did actually work in practice due to the fact
that patchelf sorts the program headers, causing the first round
of patchelf to rewrite an invalid PT_PHDR to appear first, and then
the second round of patchelf fixing that PT_PHDR.
Diffstat (limited to 'src')
-rw-r--r-- | src/patchelf.cc | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc index 82506b5..136098f 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -799,11 +799,14 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress) /* Rewrite the program header table. */ /* If there is a segment for the program header table, update it. - (According to the ELF spec, it must be the first entry.) */ - if (rdi(phdrs[0].p_type) == PT_PHDR) { - phdrs[0].p_offset = hdr->e_phoff; - wri(phdrs[0].p_vaddr, wri(phdrs[0].p_paddr, phdrAddress)); - wri(phdrs[0].p_filesz, wri(phdrs[0].p_memsz, phdrs.size() * sizeof(Elf_Phdr))); + (According to the ELF spec, there can only be one.) */ + for (unsigned int i = 0; i < phdrs.size(); ++i) { + if (rdi(phdrs[i].p_type) == PT_PHDR) { + phdrs[i].p_offset = hdr->e_phoff; + wri(phdrs[i].p_vaddr, wri(phdrs[i].p_paddr, phdrAddress)); + wri(phdrs[i].p_filesz, wri(phdrs[i].p_memsz, phdrs.size() * sizeof(Elf_Phdr))); + break; + } } sortPhdrs(); |