summaryrefslogtreecommitdiffstats
path: root/src/patchelf.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/patchelf.cc')
-rw-r--r--src/patchelf.cc304
1 files changed, 188 insertions, 116 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 4a9aad7..b80f0a7 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,13 +148,13 @@ 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);
- typedef enum { rpPrint, rpShrink, rpSet } RPathOp;
+ typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp;
void modifyRPath(RPathOp op, string newRPath);
@@ -170,6 +164,8 @@ public:
void replaceNeeded(map<string, string>& libs);
+ void noDefaultLib();
+
private:
/* Convert an integer in big or little endian representation (as
@@ -223,7 +219,7 @@ static void debug(const char * format, ...)
}
-static void error(string msg)
+__attribute__((noreturn)) static void error(string msg)
{
if (errno) perror(msg.c_str()); else fprintf(stderr, "%s\n", msg.c_str());
exit(1);
@@ -246,7 +242,7 @@ static void readFile(string fileName, mode_t * fileMode)
if (stat(fileName.c_str(), &st) != 0) error("stat");
fileSize = st.st_size;
*fileMode = st.st_mode;
- maxSize = fileSize + 8 * 1024 * 1024;
+ maxSize = fileSize + 32 * 1024 * 1024;
contents = (unsigned char *) malloc(fileSize + maxSize);
if (!contents) abort();
@@ -376,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 (write(fd, contents, fileSize) != fileSize)
+ error("write");
-#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 (rename(fileName2.c_str(), fileName.c_str()) != 0) error("rename");
+ if (close(fd) != 0)
+ error("close");
}
@@ -413,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. */
@@ -432,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());
}
}
@@ -447,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());
}
@@ -576,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;
}
@@ -592,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);
@@ -616,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);
@@ -636,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. */
@@ -707,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);
@@ -747,13 +732,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);
}
@@ -908,8 +893,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);
@@ -918,77 +908,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;
}
- else {
- 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>
@@ -1113,6 +1108,29 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
}
}
+ if (op == rpRemove) {
+ if (!rpath) {
+ debug("no RPATH to delete\n");
+ return;
+ }
+
+ Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset));
+ Elf_Dyn * last = dyn;
+ for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) {
+ if (rdi(dyn->d_tag) == DT_RPATH) {
+ debug("removing DT_RPATH entry\n");
+ changed = true;
+ } else if (rdi(dyn->d_tag) == DT_RUNPATH) {
+ debug("removing DT_RUNPATH entry\n");
+ changed = true;
+ } else {
+ *last++ = *dyn;
+ }
+ }
+ memset(last, 0, sizeof(Elf_Dyn) * (dyn - last));
+ return;
+ }
+
if (string(rpath ? rpath : "") == newRPath) return;
@@ -1255,10 +1273,7 @@ void ElfFile<ElfFileParamNames>::addNeeded(set<string> libs)
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++) {
@@ -1300,6 +1315,46 @@ void ElfFile<ElfFileParamNames>::addNeeded(set<string> libs)
}
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::noDefaultLib()
+{
+ Elf_Shdr & shdrDynamic = findSection(".dynamic");
+
+ Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset));
+ Elf_Dyn * dynFlags1 = 0;
+ for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) {
+ if (rdi(dyn->d_tag) == DT_FLAGS_1) {
+ dynFlags1 = dyn;
+ break;
+ }
+ }
+ if (dynFlags1) {
+ if (dynFlags1->d_un.d_val & DF_1_NODEFLIB)
+ return;
+ dynFlags1->d_un.d_val |= DF_1_NODEFLIB;
+ } else {
+ string & newDynamic = replaceSection(".dynamic",
+ rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn));
+
+ 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_FLAGS_1 entry at the top. */
+ Elf_Dyn newDyn;
+ wri(newDyn.d_tag, DT_FLAGS_1);
+ newDyn.d_un.d_val = DF_1_NODEFLIB;
+ setSubstr(newDynamic, 0, string((char *) &newDyn, sizeof(Elf_Dyn)));
+ }
+
+ changed = true;
+}
+
+
static bool printInterpreter = false;
static bool printSoname = false;
static bool setSoname = false;
@@ -1307,12 +1362,14 @@ static string newSoname;
static string newInterpreter;
static bool shrinkRPath = false;
+static bool removeRPath = false;
static bool setRPath = false;
static bool printRPath = false;
static string newRPath;
static set<string> neededLibsToRemove;
static map<string, string> neededLibsToReplace;
static set<string> neededLibsToAdd;
+static bool noDefaultLib = false;
template<class ElfFile>
static void patchElf2(ElfFile & elfFile, mode_t fileMode)
@@ -1323,10 +1380,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);
@@ -1336,6 +1393,8 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode)
if (shrinkRPath)
elfFile.modifyRPath(elfFile.rpShrink, "");
+ else if (removeRPath)
+ elfFile.modifyRPath(elfFile.rpRemove, "");
else if (setRPath)
elfFile.modifyRPath(elfFile.rpSet, newRPath);
@@ -1343,9 +1402,12 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode)
elfFile.replaceNeeded(neededLibsToReplace);
elfFile.addNeeded(neededLibsToAdd);
+ if (noDefaultLib)
+ elfFile.noDefaultLib();
+
if (elfFile.isChanged()){
elfFile.rewriteSections();
- writeFile(fileName, fileMode);
+ writeFile(fileName);
}
}
@@ -1355,6 +1417,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);
@@ -1392,12 +1456,14 @@ void showHelp(const string & progName)
[--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\
+ [--remove-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\
+ [--no-default-lib]\n\
[--debug]\n\
[--version]\n\
FILENAME\n", progName.c_str());
@@ -1431,6 +1497,9 @@ int main(int argc, char * * argv)
setSoname = true;
newSoname = argv[i];
}
+ else if (arg == "--remove-rpath") {
+ removeRPath = true;
+ }
else if (arg == "--shrink-rpath") {
shrinkRPath = true;
}
@@ -1472,6 +1541,9 @@ int main(int argc, char * * argv)
else if (arg == "--debug") {
debugMode = true;
}
+ else if (arg == "--no-default-lib") {
+ noDefaultLib = true;
+ }
else if (arg == "--help" || arg == "-h" ) {
showHelp(argv[0]);
return 0;