summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2014-04-24 14:27:54 (GMT)
committerEelco Dolstra <eelco.dolstra@logicblox.com>2014-04-24 14:27:54 (GMT)
commitf5194e51dc228e1ec10506118c44a5896ce249d8 (patch)
tree307bc73b7763a0f17995276735251f987a8c5a16
parent21a85cc1c63cf3ef060ece59cdd82455e2884703 (diff)
parentdcc678e7c539378ba3742c402aad3679975d3abf (diff)
downloadpatchelf-f5194e51dc228e1ec10506118c44a5896ce249d8.zip
patchelf-f5194e51dc228e1ec10506118c44a5896ce249d8.tar.gz
patchelf-f5194e51dc228e1ec10506118c44a5896ce249d8.tar.bz2
Merge branch 'cdugz-master'
-rw-r--r--src/patchelf.cc108
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/soname.sh21
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