diff options
Diffstat (limited to 'src/patchelf.cc')
-rw-r--r-- | src/patchelf.cc | 173 |
1 files changed, 170 insertions, 3 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc index d8187bb..27e220d 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -459,10 +459,175 @@ void ElfFile<ElfFileParamNames>::rewriteSections() unsigned int sectionAlignment = sizeof(Elf_Off); + if (!findSection2(".interp")) { + debug("no .interp section; this is a dynamic library\n"); + + + /* For dynamic libraries, we just place the replacement + sections at the end of the file. They're mapped into + memory by a PT_LOAD segment located directly after the last + virtual address page of other segments. */ + Elf_Addr lastPage = 0; + for (unsigned int i = 0; i < phdrs.size(); ++i) { + Elf_Addr thisPage = roundUp(rdi(phdrs[i].p_vaddr) + rdi(phdrs[i].p_memsz), pageSize); + if (thisPage > lastPage) lastPage = thisPage; + } + + debug("last page is 0x%llx\n", (unsigned long long) lastPage); + + + /* Compute the total space needed for the replaced sections + and the program headers. */ + off_t neededSpace = (phdrs.size() + 1) * sizeof(Elf_Phdr); + for (ReplacedSections::iterator i = replacedSections.begin(); + i != replacedSections.end(); ++i) + neededSpace += roundUp(i->second.size(), sectionAlignment); + debug("needed space is %d\n", neededSpace); + + + off_t startOffset = roundUp(fileSize, pageSize); + + growFile(startOffset + neededSpace); + + + /* Add a segment that maps the replaced sections and program + headers into memory.. */ + phdrs.resize(rdi(hdr->e_phnum) + 1); + wri(hdr->e_phnum, rdi(hdr->e_phnum) + 1); + Elf_Phdr & phdr = phdrs[rdi(hdr->e_phnum) - 1]; + wri(phdr.p_type, PT_LOAD); + wri(phdr.p_offset, startOffset); + wri(phdr.p_vaddr, wri(phdr.p_paddr, lastPage)); + wri(phdr.p_filesz, wri(phdr.p_memsz, neededSpace)); + wri(phdr.p_flags, PF_R | PF_W); + wri(phdr.p_align, 4096); + + + /* Write out the replaced sections. */ + off_t curOff = startOffset + phdrs.size() * sizeof(Elf_Phdr); + for (ReplacedSections::iterator i = replacedSections.begin(); + i != replacedSections.end(); ) + { + string sectionName = i->first; + Elf_Shdr & shdr = findSection(sectionName); + debug("rewriting section `%s' from offset %d to offset %d\n", + sectionName.c_str(), rdi(shdr.sh_offset), curOff); + + memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); + memcpy(contents + curOff, (unsigned char *) i->second.c_str(), + i->second.size()); + + /* Update the section header for this section. */ + wri(shdr.sh_offset, curOff); + wri(shdr.sh_addr, lastPage + (curOff - startOffset)); + wri(shdr.sh_size, i->second.size()); + wri(shdr.sh_addralign, sectionAlignment); + + /* If this is the .interp section, then the PT_INTERP segment + must be sync'ed with it. */ + if (sectionName == ".interp") { + for (unsigned int j = 0; j < phdrs.size(); ++j) + if (rdi(phdrs[j].p_type) == PT_INTERP) { + phdrs[j].p_offset = shdr.sh_offset; + phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; + phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; + } + } + + /* If this is the .dynamic section, then the PT_DYNAMIC segment + must be sync'ed with it. */ + if (sectionName == ".dynamic") { + for (unsigned int j = 0; j < phdrs.size(); ++j) + if (rdi(phdrs[j].p_type) == PT_DYNAMIC) { + phdrs[j].p_offset = shdr.sh_offset; + phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; + phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; + } + } + + curOff += roundUp(i->second.size(), sectionAlignment); + + ++i; + replacedSections.erase(sectionName); + } + + assert(replacedSections.empty()); + assert((off_t) curOff == startOffset + neededSpace); + + + /* !!! cut&paste */ + + /* Rewrite the program header table. */ + wri(hdr->e_phoff, startOffset); + + /* 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; + abort(); + // !!! wri(phdrs[0].p_vaddr, wri(phdrs[0].p_paddr, firstPage + rdi(hdr->e_phoff))); + wri(phdrs[0].p_filesz, wri(phdrs[0].p_memsz, phdrs.size() * sizeof(Elf_Phdr))); + } + + sortPhdrs(); + + for (unsigned int i = 0; i < phdrs.size(); ++i) + * ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i) = phdrs[i]; + + + /* Rewrite the section header table. For neatness, keep the + sections sorted. */ + assert(rdi(hdr->e_shnum) == shdrs.size()); + //sortShdrs(); !!! breaks the library + for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) + * ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i) = shdrs[i]; + + + /* Update all those nasty virtual addresses in the .dynamic + section. */ + Elf_Shdr & shdrDynamic = findSection(".dynamic"); + Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + unsigned int d_tag; + for ( ; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++) + if (d_tag == DT_STRTAB) + dyn->d_un.d_ptr = findSection(".dynstr").sh_addr; + else if (d_tag == DT_STRSZ) + dyn->d_un.d_val = findSection(".dynstr").sh_size; + else if (d_tag == DT_SYMTAB) + dyn->d_un.d_ptr = findSection(".dynsym").sh_addr; + else if (d_tag == DT_HASH) + dyn->d_un.d_ptr = findSection(".hash").sh_addr; + else if (d_tag == DT_JMPREL) { + Elf_Shdr * shdr = findSection2(".rel.plt"); + if (!shdr) shdr = findSection2(".rela.plt"); /* 64-bit Linux, x86-64 */ + if (!shdr) shdr = findSection2(".rela.IA_64.pltoff"); /* 64-bit Linux, IA-64 */ + if (!shdr) error("cannot find section corresponding to DT_JMPREL"); + dyn->d_un.d_ptr = shdr->sh_addr; + } + else if (d_tag == DT_REL) { /* !!! hack! */ + Elf_Shdr * shdr = findSection2(".rel.dyn"); + /* no idea if this makes sense, but it was needed for some + program */ + if (!shdr) shdr = findSection2(".rel.got"); + if (!shdr) error("cannot find .rel.dyn or .rel.got"); + dyn->d_un.d_ptr = shdr->sh_addr; + } + /* should probably update DT_RELA */ + else if (d_tag == DT_VERNEED) + dyn->d_un.d_ptr = findSection(".gnu.version_r").sh_addr; + else if (d_tag == DT_VERSYM) + dyn->d_un.d_ptr = findSection(".gnu.version").sh_addr; + + + return; + } + + /* Sort the sections by offset, otherwise we won't correctly find all the sections before the last replaced section. */ sortShdrs(); - + /* What is the index of the last replaced section? */ unsigned int lastReplaced = 0; @@ -622,6 +787,7 @@ void ElfFile<ElfFileParamNames>::rewriteSections() for (unsigned int i = 0; i < phdrs.size(); ++i) * ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i) = phdrs[i]; + /* Rewrite the section header table. For neatness, keep the sections sorted. */ assert(rdi(hdr->e_shnum) == shdrs.size()); @@ -629,6 +795,7 @@ void ElfFile<ElfFileParamNames>::rewriteSections() for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) * ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i) = shdrs[i]; + /* Update all those nasty virtual addresses in the .dynamic section. */ Elf_Shdr & shdrDynamic = findSection(".dynamic"); @@ -808,8 +975,8 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) changed = true; - /* Zero out the previous rpath to prevent retained - dependencies in Nix. */ + /* Zero out the previous rpath to prevent retained dependencies in + Nix. */ unsigned int rpathSize = 0; if (rpath) { rpathSize = strlen(rpath); |