summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/patchelf.cc235
1 files changed, 230 insertions, 5 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 826f78f..dcbfd38 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -144,13 +144,21 @@ public:
string getInterpreter();
+ string getSoname();
+
+ void setSoname(const string & newSoname);
+
void setInterpreter(const string & newInterpreter);
typedef enum { rpPrint, rpShrink, rpSet } RPathOp;
void modifyRPath(RPathOp op, string newRPath);
+ void addNeeded(set<string> libs);
+
void removeNeeded(set<string> libs);
+
+ void replaceNeeded(map<string, string>& libs);
private:
@@ -835,12 +843,21 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
debug("rewriting symbol table section %d\n", i);
for (size_t entry = 0; (entry + 1) * sizeof(Elf_Sym) <= rdi(shdrs[i].sh_size); entry++) {
Elf_Sym * sym = (Elf_Sym *) (contents + rdi(shdrs[i].sh_offset) + entry * sizeof(Elf_Sym));
- if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) {
- string section = sectionsByOldIndex[rdi(sym->st_shndx)];
+ unsigned int shndx = rdi(sym->st_shndx);
+ if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE) {
+ if (shndx >= sectionsByOldIndex.size()) {
+ fprintf(stderr, "warning: entry %d in symbol table refers to a non-existent section, skipping\n", shndx);
+ continue;
+ }
+ string section = sectionsByOldIndex.at(shndx);
assert(!section.empty());
unsigned int newIndex = findSection3(section); // inefficient
- //debug("rewriting symbol %d: index = %d (%s) -> %d\n", entry, rdi(sym->st_shndx), section.c_str(), newIndex);
+ //debug("rewriting symbol %d: index = %d (%s) -> %d\n", entry, shndx, section.c_str(), newIndex);
wri(sym->st_shndx, newIndex);
+ /* Rewrite st_value. FIXME: we should do this for all
+ types, but most don't actually change. */
+ if (ELF32_ST_TYPE(rdi(sym->st_info)) == STT_SECTION)
+ wri(sym->st_value, rdi(shdrs[newIndex].sh_addr));
}
}
}
@@ -862,6 +879,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)
@@ -1079,8 +1179,103 @@ void ElfFile<ElfFileParamNames>::removeNeeded(set<string> libs)
memset(last, 0, sizeof(Elf_Dyn) * (dyn - last));
}
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::replaceNeeded(map<string, string>& libs)
+{
+ if (libs.empty()) return;
+
+ Elf_Shdr & shdrDynamic = findSection(".dynamic");
+ Elf_Shdr & shdrDynStr = findSection(".dynstr");
+ char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset);
+
+ Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset));
+
+ unsigned int dynStrAddedBytes = 0;
+
+ for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) {
+ if (rdi(dyn->d_tag) == DT_NEEDED) {
+ char * name = strTab + rdi(dyn->d_un.d_val);
+ if (libs.find(name) != libs.end()) {
+ const string & replacement = libs[name];
+
+ debug("replacing DT_NEEDED entry `%s' with `%s'\n", name, replacement.c_str());
+
+ // technically, the string referred by d_val could be used otherwise, too (although unlikely)
+ // we'll therefore add a new string
+ debug("resizing .dynstr ...");
+
+ string & newDynStr = replaceSection(".dynstr",
+ rdi(shdrDynStr.sh_size) + replacement.size() + 1 + dynStrAddedBytes);
+ setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + dynStrAddedBytes, replacement + '\0');
+
+ dyn->d_un.d_val = shdrDynStr.sh_size + dynStrAddedBytes;
+
+ dynStrAddedBytes += replacement.size() + 1;
+
+ changed = true;
+ } else {
+ debug("keeping DT_NEEDED entry `%s'\n", name);
+ }
+ }
+ }
+}
+
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::addNeeded(set<string> libs)
+{
+ if (libs.empty()) return;
+
+ Elf_Shdr & shdrDynamic = findSection(".dynamic");
+ Elf_Shdr & shdrDynStr = findSection(".dynstr");
+ char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset);
+
+ Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset));
+
+ /* add all new libs to the dynstr string table */
+ unsigned int length = 0;
+ for (set<string>::iterator it = libs.begin(); it != libs.end(); it++) {
+ length += it->size() + 1;
+ }
+
+ string & newDynStr = replaceSection(".dynstr",
+ rdi(shdrDynStr.sh_size) + length + 1);
+ set<Elf64_Xword> libStrings;
+ unsigned int pos = 0;
+ for (set<string>::iterator it = libs.begin(); it != libs.end(); it++) {
+ setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + pos, *it + '\0');
+ libStrings.insert(rdi(shdrDynStr.sh_size) + pos);
+ pos += it->size() + 1;
+ }
+
+ /* add all new needed entries to the dynamic section */
+ string & newDynamic = replaceSection(".dynamic",
+ rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn) * libs.size());
+
+ 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 the number of new entries. */
+ setSubstr(newDynamic, sizeof(Elf_Dyn) * libs.size(),
+ string(newDynamic, 0, sizeof(Elf_Dyn) * (idx + 1)));
+
+ /* Add the DT_NEEDED entries at the top. */
+ unsigned int i = 0;
+ for (set<Elf64_Xword>::iterator it = libStrings.begin(); it != libStrings.end(); it++, i++) {
+ Elf_Dyn newDyn;
+ wri(newDyn.d_tag, DT_NEEDED);
+ wri(newDyn.d_un.d_val, *it);
+ setSubstr(newDynamic, i * sizeof(Elf_Dyn), string((char *) &newDyn, sizeof(Elf_Dyn)));
+ }
+
+ changed = true;
+}
+
static bool printInterpreter = false;
+static bool printSoname = false;
+static bool setSoname = false;
+static string newSoname;
static string newInterpreter;
static bool shrinkRPath = false;
@@ -1088,7 +1283,8 @@ static bool setRPath = false;
static bool printRPath = false;
static string newRPath;
static set<string> neededLibsToRemove;
-
+static map<string, string> neededLibsToReplace;
+static set<string> neededLibsToAdd;
template<class ElfFile>
static void patchElf2(ElfFile & elfFile, mode_t fileMode)
@@ -1098,6 +1294,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);
@@ -1110,6 +1312,8 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode)
elfFile.modifyRPath(elfFile.rpSet, newRPath);
elfFile.removeNeeded(neededLibsToRemove);
+ elfFile.replaceNeeded(neededLibsToReplace);
+ elfFile.addNeeded(neededLibsToAdd);
if (elfFile.isChanged()){
elfFile.rewriteSections();
@@ -1120,7 +1324,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;
@@ -1157,11 +1361,15 @@ 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\
[--force-rpath]\n\
+ [--add-needed LIBRARY]\n\
[--remove-needed LIBRARY]\n\
+ [--replace-needed LIBRARY NEW_LIBRARY]\n\
[--debug]\n\
[--version]\n\
FILENAME\n", progName.c_str());
@@ -1187,6 +1395,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;
}
@@ -1212,10 +1428,19 @@ int main(int argc, char * * argv)
added. */
forceRPath = true;
}
+ else if (arg == "--add-needed") {
+ if (++i == argc) error("missing argument");
+ neededLibsToAdd.insert(argv[i]);
+ }
else if (arg == "--remove-needed") {
if (++i == argc) error("missing argument");
neededLibsToRemove.insert(argv[i]);
}
+ else if (arg == "--replace-needed") {
+ if (i+2 >= argc) error("missing argument(s)");
+ neededLibsToReplace[ argv[i+1] ] = argv[i+2];
+ i += 2;
+ }
else if (arg == "--debug") {
debugMode = true;
}