* 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).
This commit is contained in:
Eelco Dolstra
2008-05-28 09:19:49 +00:00
parent 704c22c6a8
commit 15ba1450c3

View File

@@ -459,10 +459,175 @@ void ElfFile<ElfFileParamNames>::rewriteSections()
unsigned int sectionAlignment = sizeof(Elf_Off); 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 /* Sort the sections by offset, otherwise we won't correctly find
all the sections before the last replaced section. */ all the sections before the last replaced section. */
sortShdrs(); sortShdrs();
/* What is the index of the last replaced section? */ /* What is the index of the last replaced section? */
unsigned int lastReplaced = 0; unsigned int lastReplaced = 0;
@@ -622,6 +787,7 @@ void ElfFile<ElfFileParamNames>::rewriteSections()
for (unsigned int i = 0; i < phdrs.size(); ++i) for (unsigned int i = 0; i < phdrs.size(); ++i)
* ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i) = phdrs[i]; * ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i) = phdrs[i];
/* Rewrite the section header table. For neatness, keep the /* Rewrite the section header table. For neatness, keep the
sections sorted. */ sections sorted. */
assert(rdi(hdr->e_shnum) == shdrs.size()); 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) for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i)
* ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i) = shdrs[i]; * ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i) = shdrs[i];
/* Update all those nasty virtual addresses in the .dynamic /* Update all those nasty virtual addresses in the .dynamic
section. */ section. */
Elf_Shdr & shdrDynamic = findSection(".dynamic"); Elf_Shdr & shdrDynamic = findSection(".dynamic");
@@ -808,8 +975,8 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
changed = true; changed = true;
/* Zero out the previous rpath to prevent retained /* Zero out the previous rpath to prevent retained dependencies in
dependencies in Nix. */ Nix. */
unsigned int rpathSize = 0; unsigned int rpathSize = 0;
if (rpath) { if (rpath) {
rpathSize = strlen(rpath); rpathSize = strlen(rpath);