From 15ba1450c3e9878e6e9aa8dad28a64f39b51bc6c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 28 May 2008 09:19:49 +0000 Subject: * A first attempt at --set-rpath support for libraries. This is very useful for the NVIDIA libGL.so, since setting an RPATH on libGL.so to include its dependencies (like libXext and libGLcore) makes it unnecessary to set an RPATH on the executables that use it. This is especially important for executables that use RUNPATH (like Wine) since there the paths in RUNPATH are ignored when resolving the dependencies of dependencies (i.e. RUNPATH is scoped). --- src/patchelf.cc | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file 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::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::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::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::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); -- cgit v0.12