diff options
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | src/patchelf.cc | 214 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rwxr-xr-x | tests/soname.sh | 11 |
4 files changed, 118 insertions, 119 deletions
diff --git a/configure.ac b/configure.ac index 8ca9ad4..bfd3b6e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([patchelf], m4_esyscmd([echo -n $(cat ./version)])) +AC_INIT([patchelf], m4_esyscmd([printf $(cat ./version)])) AC_CONFIG_SRCDIR([src/patchelf.cc]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([-Wall -Werror dist-bzip2 foreign color-tests parallel-tests]) @@ -6,13 +6,7 @@ AM_INIT_AUTOMAKE([-Wall -Werror dist-bzip2 foreign color-tests parallel-tests]) AM_PROG_CC_C_O AC_PROG_CXX -AC_CHECK_HEADERS([attr/libattr.h attr/error_context.h]) -AC_SEARCH_LIBS(attr_copy_file, attr) -AC_CHECK_FUNCS([attr_copy_file]) - -AC_CHECK_HEADERS([sys/acl.h acl/libacl.h]) -AC_SEARCH_LIBS(perm_copy_file, acl) -AC_CHECK_FUNCS([perm_copy_file]) +AC_CHECK_FUNCS([sysconf]) AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile patchelf.spec]) AC_OUTPUT diff --git a/src/patchelf.cc b/src/patchelf.cc index 0ef0695..8771776 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -17,27 +17,11 @@ #include <fcntl.h> #include <limits.h> -#ifdef HAVE_ATTR_LIBATTR_H -# include <attr/libattr.h> -#endif -#ifdef HAVE_SYS_ACL_H -# include <sys/acl.h> -#endif -#ifdef HAVE_ACL_LIBACL_H -# include <acl/libacl.h> -#endif - #include "elf.h" using namespace std; -#ifdef MIPSEL -/* The lemote fuloong 2f kernel defconfig sets a page size of 16KB */ -const unsigned int pageSize = 4096*4; -#else -const unsigned int pageSize = 4096; -#endif static bool debugMode = false; @@ -55,6 +39,16 @@ unsigned char * contents = 0; #define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym +static unsigned int getPageSize(){ +#if (defined HAVE_SYSCONF) + // if present, use sysconf to get kernel page size + return sysconf(_SC_PAGESIZE); +#else + return 4096; +#endif +} + + template<ElfFileParams> class ElfFile { @@ -154,9 +148,9 @@ public: string getInterpreter(); - string getSoname(); + typedef enum { printSoname, replaceSoname } sonameMode; - void setSoname(const string & newSoname); + void modifySoname(sonameMode op, const string & newSoname); void setInterpreter(const string & newInterpreter); @@ -378,28 +372,17 @@ void ElfFile<ElfFileParamNames>::sortShdrs() } -static void writeFile(string fileName, mode_t fileMode) +static void writeFile(string fileName) { - string fileName2 = fileName + "_patchelf_tmp"; - - int fd = open(fileName2.c_str(), - O_CREAT | O_TRUNC | O_WRONLY, 0700); - if (fd == -1) error("open"); - - if (write(fd, contents, fileSize) != fileSize) error("write"); + int fd = open(fileName.c_str(), O_TRUNC | O_WRONLY); + if (fd == -1) + error("open"); - if (close(fd) != 0) error("close"); - -#if defined(HAVE_ATTR_COPY_FILE) - if (attr_copy_file(fileName.c_str(), fileName2.c_str(), 0, 0) != 0) error("attr_copy_file"); -#endif -#if defined(HAVE_PERM_COPY_FILE) - if (perm_copy_file(fileName.c_str(), fileName2.c_str(), 0) != 0) error("perm_copy_file"); -#else - if (chmod(fileName2.c_str(), fileMode) != 0) error("chmod"); -#endif + if (write(fd, contents, fileSize) != fileSize) + error("write"); - if (rename(fileName2.c_str(), fileName.c_str()) != 0) error("rename"); + if (close(fd) != 0) + error("close"); } @@ -415,9 +398,9 @@ void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, Elf_Addr sta /* Move the entire contents of the file `extraPages' pages further. */ unsigned int oldSize = fileSize; - unsigned int shift = extraPages * pageSize; - growFile(fileSize + extraPages * pageSize); - memmove(contents + extraPages * pageSize, contents, oldSize); + unsigned int shift = extraPages * getPageSize(); + growFile(fileSize + extraPages * getPageSize()); + memmove(contents + extraPages * getPageSize(), contents, oldSize); memset(contents + sizeof(Elf_Ehdr), 0, shift - sizeof(Elf_Ehdr)); /* Adjust the ELF header. */ @@ -434,8 +417,8 @@ void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, Elf_Addr sta if (rdi(phdrs[i].p_align) != 0 && (rdi(phdrs[i].p_vaddr) - rdi(phdrs[i].p_offset)) % rdi(phdrs[i].p_align) != 0) { debug("changing alignment of program header %d from %d to %d\n", i, - rdi(phdrs[i].p_align), pageSize); - wri(phdrs[i].p_align, pageSize); + rdi(phdrs[i].p_align), getPageSize()); + wri(phdrs[i].p_align, getPageSize()); } } @@ -449,7 +432,7 @@ void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, Elf_Addr sta wri(phdr.p_vaddr, wri(phdr.p_paddr, startPage)); wri(phdr.p_filesz, wri(phdr.p_memsz, shift)); wri(phdr.p_flags, PF_R | PF_W); - wri(phdr.p_align, pageSize); + wri(phdr.p_align, getPageSize()); } @@ -578,7 +561,7 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() page of other segments. */ Elf_Addr startPage = 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); + Elf_Addr thisPage = roundUp(rdi(phdrs[i].p_vaddr) + rdi(phdrs[i].p_memsz), getPageSize()); if (thisPage > startPage) startPage = thisPage; } @@ -594,7 +577,7 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() debug("needed space is %d\n", neededSpace); - size_t startOffset = roundUp(fileSize, pageSize); + size_t startOffset = roundUp(fileSize, getPageSize()); growFile(startOffset + neededSpace); @@ -618,7 +601,7 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() size_t hole = startPage - startOffset; /* Print a warning, because the hole could be very big. */ fprintf(stderr, "warning: working around a Linux kernel bug by creating a hole of %zu bytes in ā%sā\n", hole, fileName.c_str()); - assert(hole % pageSize == 0); + assert(hole % getPageSize() == 0); /* !!! We could create an actual hole in the file here, but it's probably not worth the effort. */ growFile(fileSize + hole); @@ -638,7 +621,7 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() wri(phdr.p_vaddr, wri(phdr.p_paddr, startPage)); wri(phdr.p_filesz, wri(phdr.p_memsz, neededSpace)); wri(phdr.p_flags, PF_R | PF_W); - wri(phdr.p_align, pageSize); + wri(phdr.p_align, getPageSize()); /* Write out the replaced sections. */ @@ -709,7 +692,7 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable() debug("first reserved offset/addr is 0x%x/0x%llx\n", startOffset, (unsigned long long) startAddr); - assert(startAddr % pageSize == startOffset % pageSize); + assert(startAddr % getPageSize() == startOffset % getPageSize()); Elf_Addr firstPage = startAddr - startOffset; debug("first page is 0x%llx\n", (unsigned long long) firstPage); @@ -738,13 +721,13 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable() neededSpace += sizeof(Elf_Phdr); debug("needed space is %d\n", neededSpace); - unsigned int neededPages = roundUp(neededSpace - startOffset, pageSize) / pageSize; + unsigned int neededPages = roundUp(neededSpace - startOffset, getPageSize()) / getPageSize(); debug("needed pages is %d\n", neededPages); - if (neededPages * pageSize > firstPage) + if (neededPages * getPageSize() > firstPage) error("virtual address space underrun!"); - firstPage -= neededPages * pageSize; - startOffset += neededPages * pageSize; + firstPage -= neededPages * getPageSize(); + startOffset += neededPages * getPageSize(); shiftFile(neededPages, firstPage); } @@ -899,8 +882,13 @@ string ElfFile<ElfFileParamNames>::getInterpreter() } template<ElfFileParams> -string ElfFile<ElfFileParamNames>::getSoname() +void ElfFile<ElfFileParamNames>::modifySoname(sonameMode op, const string & newSoname) { + if (rdi(hdr->e_type) != ET_DYN) { + debug("this is not a dynamic library\n"); + return; + } + Elf_Shdr & shdrDynamic = findSection(".dynamic"); Elf_Shdr & shdrDynStr = findSection(".dynstr"); char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); @@ -909,76 +897,82 @@ string ElfFile<ElfFileParamNames>::getSoname() Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); Elf_Addr strTabAddr = 0; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) - if (rdi(dyn->d_tag) == DT_STRTAB) strTabAddr = rdi(dyn->d_un.d_ptr); + if (rdi(dyn->d_tag) == DT_STRTAB) + strTabAddr = rdi(dyn->d_un.d_ptr); if (!strTabAddr) error("strange: no string table"); /* We assume that the virtual address in the DT_STRTAB entry of the dynamic section corresponds to the .dynstr section. */ assert(strTabAddr == rdi(shdrDynStr.sh_addr)); - Elf_Dyn * dynSoname = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + /* Walk through the dynamic section, look for the DT_SONAME entry. */ + static vector<string> neededLibs; + dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + Elf_Dyn * dynSoname = 0; char * soname = 0; - for ( ; rdi(dynSoname->d_tag) != DT_NULL; dynSoname++) { - if (rdi(dynSoname->d_tag) == DT_SONAME) { - soname = strTab + rdi(dynSoname->d_un.d_val); - break; + for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { + if (rdi(dyn->d_tag) == DT_SONAME) { + dynSoname = dyn; + soname = strTab + rdi(dyn->d_un.d_val); + } else if (rdi(dyn->d_tag) == DT_INIT) + neededLibs.push_back(string(strTab + rdi(dyn->d_un.d_val))); + } + + if (op == printSoname) { + if (soname) { + if (string(soname ? soname : "") == "") + debug("DT_SONAME is empty\n"); + else + printf("%s\n", soname); + } else { + debug("no DT_SONAME found\n"); } + return; } - if (rdi(dynSoname->d_tag) == DT_NULL) - error("Specified ELF file does not contain any DT_SONAME entry in .dynamic section!"); + if (string(soname ? soname : "") == newSoname) { + debug("current and proposed new SONAMEs are equal keeping DT_SONAME entry\n"); + return; + } - return soname; -} + /* Zero out the previous SONAME */ + unsigned int sonameSize = 0; + if (soname) { + sonameSize = strlen(soname); + memset(soname, 'X', sonameSize); + } -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::setSoname(const string & newSoname) -{ - Elf_Shdr & shdrDynamic = findSection(".dynamic"); - Elf_Shdr & shdrDynStr = findSection(".dynstr"); - char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); + debug("new SONAME is `%s'\n", newSoname.c_str()); - /* Find the DT_STRTAB entry in the dynamic section. */ - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - Elf_Addr strTabAddr = 0; - for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) - if (rdi(dyn->d_tag) == DT_STRTAB) strTabAddr = rdi(dyn->d_un.d_ptr); - if (!strTabAddr) error("strange: no string table"); + /* Grow the .dynstr section to make room for the new SONAME. */ + debug("SONAME is too long, resizing...\n"); - /* We assume that the virtual address in the DT_STRTAB entry - of the dynamic section corresponds to the .dynstr section. */ - assert(strTabAddr == rdi(shdrDynStr.sh_addr)); + string & newDynStr = replaceSection(".dynstr", rdi(shdrDynStr.sh_size) + newSoname.size() + 1); + setSubstr(newDynStr, rdi(shdrDynStr.sh_size), newSoname + '\0'); - Elf_Dyn * dynSoname = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - char * soname = 0; - for ( ; rdi(dynSoname->d_tag) != DT_NULL; dynSoname++) { - if (rdi(dynSoname->d_tag) == DT_SONAME) { - soname = strTab + rdi(dynSoname->d_un.d_val); - break; - } - } - if (rdi(dynSoname->d_tag) == DT_NULL) - error("Specified ELF file does not contain any DT_SONAME entry in .dynamic section!"); + /* Update the DT_SONAME entry. */ + if (dynSoname) { + dynSoname->d_un.d_val = shdrDynStr.sh_size; + } else { + /* There is no DT_SONAME entry in the .dynamic section, so we + have to grow the .dynamic section. */ + string & newDynamic = replaceSection(".dynamic", rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn)); - if (newSoname.size() <= strlen(soname)) { - debug("old soname: `%s', new soname: `%s'\n", soname, newSoname.c_str()); - strcpy(soname, newSoname.c_str()); - changed = true; - } - else { - /* Grow the .dynstr section to make room for the new DT_SONAME */ - debug("new soname is too long, resizing .dynstr section...\n"); - - string & newDynStr = replaceSection(".dynstr", - rdi(shdrDynStr.sh_size) + newSoname.size() + 1); - setSubstr(newDynStr, rdi(shdrDynStr.sh_size), newSoname + '\0'); - /* Update the DT_SONAME entry, if any */ - if (dynSoname) { - debug("old soname: `%s', new soname: `%s'\n", soname, newSoname.c_str()); - dynSoname->d_un.d_val = shdrDynStr.sh_size; - changed = true; - } + unsigned int idx = 0; + for (; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++); + debug("DT_NULL index is %d\n", idx); + + /* Shift all entries down by one. */ + setSubstr(newDynamic, sizeof(Elf_Dyn), string(newDynamic, 0, sizeof(Elf_Dyn) * (idx + 1))); + + /* Add the DT_SONAME entry at the top. */ + Elf_Dyn newDyn; + wri(newDyn.d_tag, DT_SONAME); + newDyn.d_un.d_val = shdrDynStr.sh_size; + setSubstr(newDynamic, 0, string((char *)&newDyn, sizeof(Elf_Dyn))); } + + changed = true; } template<ElfFileParams> @@ -1375,10 +1369,10 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) printf("%s\n", elfFile.getInterpreter().c_str()); if (printSoname) - printf("%s\n", elfFile.getSoname().c_str()); + elfFile.modifySoname(elfFile.printSoname, ""); if (setSoname) - elfFile.setSoname(newSoname); + elfFile.modifySoname(elfFile.replaceSoname, newSoname); if (newInterpreter != "") elfFile.setInterpreter(newInterpreter); @@ -1402,7 +1396,7 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) if (elfFile.isChanged()){ elfFile.rewriteSections(); - writeFile(fileName, fileMode); + writeFile(fileName); } } @@ -1412,6 +1406,8 @@ static void patchElf() if (!printInterpreter && !printRPath && !printSoname) debug("patching ELF file `%s'\n", fileName.c_str()); + debug("Kernel page size is %u bytes\n", getPageSize()); + mode_t fileMode; readFile(fileName, &fileMode); diff --git a/tests/Makefile.am b/tests/Makefile.am index 190212c..284eb80 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -94,7 +94,7 @@ libbar_scoped_so_SOURCES = bar.c libbar_scoped_so_LDFLAGS = $(LDFLAGS_sharedlib) libsimple_so_SOURCES = simple.c -libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-soname,libsimple.so.1.0 +libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib) no_rpath_SOURCES = no-rpath.c # no -fpic for no-rpath.o diff --git a/tests/soname.sh b/tests/soname.sh index f81e8ed..4a8fb96 100755 --- a/tests/soname.sh +++ b/tests/soname.sh @@ -6,13 +6,22 @@ mkdir -p ${SCRATCH} cp libsimple.so ${SCRATCH}/ -# print and set DT_SONAME +# set an initial DT_SONAME entry +../src/patchelf --set-soname libsimple.so.1.0 ${SCRATCH}/libsimple.so +newSoname=$(../src/patchelf --print-soname ${SCRATCH}/libsimple.so) +if test "$newSoname" != libsimple.so.1.0; then + echo "failed --set-soname test. Expected newSoname: libsimple.so.1.0, got: $newSoname" + exit 1 +fi + +# print DT_SONAME soname=$(../src/patchelf --print-soname ${SCRATCH}/libsimple.so) if test "$soname" != libsimple.so.1.0; then echo "failed --print-soname test. Expected soname: libsimple.so.1.0, got: $soname" exit 1 fi +# replace DT_SONAME entry ../src/patchelf --set-soname libsimple.so.1.1 ${SCRATCH}/libsimple.so newSoname=$(../src/patchelf --print-soname ${SCRATCH}/libsimple.so) if test "$newSoname" != libsimple.so.1.1; then |