diff options
-rw-r--r-- | src/patchelf.cc | 108 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rwxr-xr-x | tests/soname.sh | 21 |
3 files changed, 132 insertions, 3 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc index 2c6bec7..0aad3e3 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -144,6 +144,10 @@ public: string getInterpreter(); + string getSoname(); + + void setSoname(const string & newSoname); + void setInterpreter(const string & newInterpreter); typedef enum { rpPrint, rpShrink, rpSet } RPathOp; @@ -871,6 +875,89 @@ string ElfFile<ElfFileParamNames>::getInterpreter() return string((char *) contents + rdi(shdr.sh_offset), rdi(shdr.sh_size)); } +template<ElfFileParams> +string ElfFile<ElfFileParamNames>::getSoname() +{ + Elf_Shdr & shdrDynamic = findSection(".dynamic"); + Elf_Shdr & shdrDynStr = findSection(".dynstr"); + char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); + + /* 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"); + + /* 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)); + 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!"); + } + else { + return soname; + } +} + +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); + + /* 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"); + + /* 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)); + 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!"); + + 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; + } + } +} template<ElfFileParams> void ElfFile<ElfFileParamNames>::setInterpreter(const string & newInterpreter) @@ -1090,6 +1177,9 @@ void ElfFile<ElfFileParamNames>::removeNeeded(set<string> libs) static bool printInterpreter = false; +static bool printSoname = false; +static bool setSoname = false; +static string newSoname; static string newInterpreter; static bool shrinkRPath = false; @@ -1107,6 +1197,12 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) if (printInterpreter) printf("%s\n", elfFile.getInterpreter().c_str()); + if (printSoname) + printf("%s\n", elfFile.getSoname().c_str()); + + if (setSoname) + elfFile.setSoname(newSoname); + if (newInterpreter != "") elfFile.setInterpreter(newInterpreter); @@ -1129,7 +1225,7 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) static void patchElf() { - if (!printInterpreter && !printRPath) + if (!printInterpreter && !printRPath && !printSoname) debug("patching ELF file `%s'\n", fileName.c_str()); mode_t fileMode; @@ -1166,6 +1262,8 @@ void showHelp(const string & progName) fprintf(stderr, "syntax: %s\n\ [--set-interpreter FILENAME]\n\ [--print-interpreter]\n\ + [--print-soname]\t\tPrints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n\ + [--set-soname SONAME]\t\tSets 'DT_SONAME' entry to SONAME. Raises an error if DT_SONAME doesn't exist\n\ [--set-rpath RPATH]\n\ [--shrink-rpath]\n\ [--print-rpath]\n\ @@ -1196,6 +1294,14 @@ int main(int argc, char * * argv) else if (arg == "--print-interpreter") { printInterpreter = true; } + else if (arg == "--print-soname") { + printSoname = true; + } + else if (arg == "--set-soname") { + if (++i == argc) error("missing argument"); + setSoname = true; + newSoname = argv[i]; + } else if (arg == "--shrink-rpath") { shrinkRPath = true; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 9a38df6..708b17e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,7 @@ check_PROGRAMS = simple main main-scoped big-dynstr TESTS = plain-fail.sh plain-run.sh shrink-rpath.sh set-interpreter-short.sh \ set-interpreter-long.sh set-rpath.sh no-rpath.sh big-dynstr.sh \ - set-rpath-library.sh + set-rpath-library.sh soname.sh EXTRA_DIST = no-rpath $(TESTS) TESTS_ENVIRONMENT = PATCHELF_DEBUG=1 @@ -44,7 +44,7 @@ big_dynstr_LDFLAGS = $(LDFLAGS_local) # - without libtool, only archives (static libraries) can be built by automake # - with libtool, it is difficult to control options # - with libtool, it is not possible to compile convenience *dynamic* libraries :-( -check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so +check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libfoo_so_SOURCES = foo.c libfoo_so_LDADD = -lbar $(AM_LDADD) @@ -62,3 +62,5 @@ libbar_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-rpath,`pwd`/no-such-path 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 diff --git a/tests/soname.sh b/tests/soname.sh new file mode 100755 index 0000000..f81e8ed --- /dev/null +++ b/tests/soname.sh @@ -0,0 +1,21 @@ +#! /bin/sh -e +SCRATCH=scratch/$(basename $0 .sh) + +rm -rf ${SCRATCH} +mkdir -p ${SCRATCH} + +cp libsimple.so ${SCRATCH}/ + +# print and set 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 + +../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 + echo "failed --set-soname test. Expected newSoname: libsimple.so.1.1, got: $newSoname" + exit 1 +fi |