summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--patchelf.c335
2 files changed, 256 insertions, 89 deletions
diff --git a/Makefile b/Makefile
index b5811ff..0af7d7a 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,11 @@ patchelf: patchelf.c
test: test.c
gcc -Wall -o test test.c
-runtest: test patchelf
- readelf -a test > dump1
- ./patchelf test
+TEST = svn
+
+runtest: $(TEST) patchelf
+ readelf -a $(TEST) > dump1
+# ./patchelf --interpreter /nix/store/42de22963bca8f234ad54b01118215df-glibc-2.3.2/lib/ld-linux.so.2 \
+# --shrink-rpath $(TEST)
+ ./patchelf --shrink-rpath $(TEST)
readelf -a new.exe > dump2
diff --git a/patchelf.c b/patchelf.c
index 696d00b..da9cf8b 100644
--- a/patchelf.c
+++ b/patchelf.c
@@ -8,18 +8,34 @@
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
#include <elf.h>
+static char * fileName = 0;
+static char * newInterpreter = 0;
+static int doShrinkRPath = 0;
+
+
#define MAX_PHEADERS 128
#define MAX_SHEADERS 128
+#define MAX_NEEDED 1024
static off_t fileSize, maxSize;
static unsigned char * contents = 0;
+static Elf32_Ehdr * hdr;
static Elf32_Phdr phdrs[MAX_PHEADERS];
static Elf32_Shdr shdrs[MAX_SHEADERS];
+static unsigned int nrNeededLibs = 0;
+static char * neededLibs[MAX_NEEDED];
+static int neededLibFound[MAX_NEEDED];
+static unsigned int freeOffset;
+static unsigned int firstPage;
+
+static int changed = 0;
+static int rewriteHeaders = 0;
static void error(char * msg)
@@ -75,21 +91,207 @@ static unsigned int roundUp(unsigned int n, unsigned int m)
}
-//char newInterpreter[] = "/lib/ld-linux.so.2";
-char newInterpreter[] = "/nix/store/42de22963bca8f234ad54b01118215df-glibc-2.3.2/lib/ld-linux.so.2";
-//char newInterpreter[] = "/nix/store/2ccde1632ef69ebdb5f21cd2222d19f2-glibc-2.3.3/lib/ld-linux.so.2";
+static void shiftFile(void)
+{
+ /* Move the entire contents of the file one page further. */
+ unsigned int oldSize = fileSize;
+ growFile(fileSize + 4096);
+ memmove(contents + 4096, contents, oldSize);
+ memset(contents + sizeof(Elf32_Ehdr), 0, 4096 - sizeof(Elf32_Ehdr));
+
+ /* Update the ELF header. */
+ hdr->e_shoff += 4096;
+
+ /* Update the offsets in the section headers. */
+ int i;
+ for (i = 0; i < hdr->e_shnum; ++i) {
+ shdrs[i].sh_offset += 4096;
+ }
+
+ /* Update the offsets in the program headers. */
+ for (i = 0; i < hdr->e_phnum; ++i) {
+ phdrs[i].p_offset += 4096;
+ }
+
+ /* Add a segment that maps the new program/section headers and
+ PT_INTERP segment into memory. Otherwise glibc will choke. Add
+ this after the PT_PHDR segment but before all other PT_LOAD
+ segments. */
+ for (i = hdr->e_phnum; i > 1; --i)
+ phdrs[i] = phdrs[i - 1];
+ hdr->e_phnum++;
+ Elf32_Phdr * phdr = phdrs + 1;
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = 0;
+ phdr->p_vaddr = phdr->p_paddr = firstPage;
+ phdr->p_filesz = phdr->p_memsz = 4096;
+ phdr->p_flags = PF_R;
+ phdr->p_align = 4096;
+
+ freeOffset = sizeof(Elf32_Ehdr);
+
+ rewriteHeaders = 1;
+}
+
+
+static void setInterpreter(void)
+{
+ /* Find the PT_INTERP segment and replace it by a new one that
+ contains the new interpreter name. */
+ if (newInterpreter && hdr->e_type == ET_EXEC) {
+ shiftFile();
+ int i;
+ for (i = 0; i < hdr->e_phnum; ++i) {
+ Elf32_Phdr * phdr = phdrs + i;
+ if (phdr->p_type == PT_INTERP) {
+ fprintf(stderr, "changing interpreter from `%s' to `%s'\n",
+ (char *) (contents + phdr->p_offset), newInterpreter);
+ unsigned int interpOffset = freeOffset;
+ unsigned int interpSize = strlen(newInterpreter) + 1;
+ freeOffset += roundUp(interpSize, 4);
+ phdr->p_offset = interpOffset;
+ growFile(phdr->p_offset + interpSize);
+ phdr->p_vaddr = phdr->p_paddr = firstPage + interpOffset % 4096;
+ phdr->p_filesz = phdr->p_memsz = interpSize;
+ strncpy(contents + interpOffset,
+ newInterpreter, interpSize);
+ changed = 1;
+ break;
+ }
+ }
+ }
+}
-static void patchElf(char * fileName)
+static void shrinkRPath(void)
{
- fprintf(stderr, "patching %s\n", fileName);
+ /* Shrink the RPATH. */
+ if (doShrinkRPath) {
+
+ /* Find the .dynamic section. */
+ int i, dynSec = 0;
+ for (i = 0; i < hdr->e_shnum; ++i)
+ if (shdrs[i].sh_type == SHT_DYNAMIC) dynSec = i;
+
+ if (!dynSec) {
+ fprintf(stderr, "no dynamic section, so no RPATH to shrink\n");
+ return;
+ }
+
+ /* Find the DT_STRTAB entry in the dynamic section. */
+ Elf32_Dyn * dyn = (Elf32_Dyn *) (contents + shdrs[dynSec].sh_offset);
+ Elf32_Addr strTabAddr = 0;
+ for ( ; dyn->d_tag != DT_NULL; dyn++)
+ if (dyn->d_tag == DT_STRTAB) strTabAddr = dyn->d_un.d_ptr;
+
+ if (!strTabAddr) {
+ fprintf(stderr, "strange: no string table\n");
+ return;
+ }
+
+ /* Nasty: map the virtual address for the string table back to
+ a offset in the file. */
+ char * strTab = 0;
+ for (i = 0; i < hdr->e_phnum; ++i)
+ if (phdrs[i].p_vaddr <= strTabAddr &&
+ strTabAddr < phdrs[i].p_vaddr + phdrs[i].p_filesz)
+ {
+ strTab = contents +
+ strTabAddr - phdrs[i].p_vaddr + phdrs[i].p_offset;
+ }
+
+ if (!strTab) error("could not reverse map DT_STRTAB");
+
+ /* Walk through the dynamic section, look for the RPATH
+ entry. */
+ dyn = (Elf32_Dyn *) (contents + shdrs[dynSec].sh_offset);
+ Elf32_Dyn * rpathEntry = 0;
+ char * rpath = 0;
+ for ( ; dyn->d_tag != DT_NULL; dyn++) {
+ if (dyn->d_tag == DT_RPATH) {
+ rpathEntry = dyn;
+ rpath = strTab + dyn->d_un.d_val;
+ }
+ else if (dyn->d_tag == DT_NEEDED) {
+ if (nrNeededLibs == MAX_NEEDED)
+ error("too many needed libraries");
+ neededLibs[nrNeededLibs++] = strTab + dyn->d_un.d_val;
+ }
+ }
+
+ if (!rpath) {
+ fprintf(stderr, "no RPATH to shrink\n");
+ return;
+ }
+
+ /* For each directory in the RPATH, check if it contains any
+ needed library. */
+ for (i = 0; i < nrNeededLibs; ++i)
+ neededLibFound[i] = 0;
+
+ char * newRPath = malloc(strlen(rpath) + 1);
+ *newRPath = 0;
+
+ char * pos = rpath;
+ while (*pos) {
+ char * end = strchr(pos, ':');
+ if (!end) end = strchr(pos, 0);
+
+ /* Get the name of the directory. */
+ char dirName[PATH_MAX];
+ if (end - pos >= PATH_MAX) error("library name too long");
+ strncpy(dirName, pos, end - pos);
+ dirName[end - pos] = 0;
+ if (*end == ':') ++end;
+ pos = end;
+ if (*dirName != '/') continue;
+
+ /* For each library that we haven't found yet, see if it
+ exists in this directory. */
+ int j;
+ int libFound = 0;
+ for (j = 0; j < nrNeededLibs; ++j)
+ if (!neededLibFound[j]) {
+ char libName[PATH_MAX];
+ snprintf(libName, sizeof(libName), "%s/%s", dirName, neededLibs[j]);
+ struct stat st;
+ if (stat(libName, &st) == 0) {
+ neededLibFound[j] = 1;
+ libFound = 1;
+ }
+ }
+
+ if (!libFound)
+ fprintf(stderr, "removing directory `%s' from RPATH\n", dirName);
+ else {
+ if (*newRPath) strcat(newRPath, ":");
+ strcat(newRPath, dirName);
+ }
+
+ }
+
+ if (strcmp(rpath, newRPath) != -1) {
+ assert(strlen(newRPath) <= strlen(rpath));
+ /* Zero out the previous rpath to prevent retained
+ dependencies in Nix. */
+ memset(rpath, 0, strlen(rpath));
+ strcpy(rpath, newRPath);
+ changed = 1;
+ }
+ }
+}
+
+
+static void patchElf(void)
+{
+ fprintf(stderr, "patching ELF file `%s'\n", fileName);
readFile(fileName);
/* Check the ELF header for basic validity. */
if (fileSize < sizeof(Elf32_Ehdr)) error("missing ELF header");
- Elf32_Ehdr * hdr = (Elf32_Ehdr *) contents;
+ hdr = (Elf32_Ehdr *) contents;
if (memcmp(hdr->e_ident, ELFMAG, 4) != 0)
error("not an ELF executable");
@@ -102,9 +304,6 @@ static void patchElf(char * fileName)
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)
error("wrong ELF type");
- fprintf(stderr, "%d ph entries, %d sh entries\n",
- hdr->e_phnum, hdr->e_shnum);
-
if (hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize > fileSize)
error("missing program headers");
@@ -128,96 +327,60 @@ static void patchElf(char * fileName)
if (end > nextFreePage) nextFreePage = end;
}
- fprintf(stderr, "next free page is %x\n", nextFreePage);
+ firstPage = 0x08047000;
- unsigned int firstPage = 0x08047000;
-
- /* Move the entire contents of the file one page further. */
- unsigned int oldSize = fileSize;
- growFile(fileSize + 4096);
- memmove(contents + 4096, contents, oldSize);
- memset(contents + sizeof(Elf32_Ehdr), 0, 4096 - sizeof(Elf32_Ehdr));
+ /* Do what we're supposed to do. */
+ setInterpreter();
+ shrinkRPath();
- /* Update the ELF header. */
- hdr->e_shoff += 4096;
+ if (rewriteHeaders) {
+ /* Rewrite the program header table. */
+ hdr->e_phoff = freeOffset;
+ freeOffset += hdr->e_phnum * sizeof(Elf32_Phdr);
+ assert(phdrs[0].p_type == PT_PHDR);
+ phdrs[0].p_offset = hdr->e_phoff;
+ phdrs[0].p_vaddr = phdrs[0].p_paddr = firstPage + hdr->e_phoff % 4096;
+ phdrs[0].p_filesz = phdrs[0].p_memsz = hdr->e_phnum * sizeof(Elf32_Phdr);
+ memcpy(contents + hdr->e_phoff, phdrs, hdr->e_phnum * sizeof(Elf32_Phdr));
- /* Update the offsets in the section headers. */
- for (i = 0; i < hdr->e_shnum; ++i) {
- shdrs[i].sh_offset += 4096;
- }
-
- /* Update the offsets in the program headers. */
- for (i = 0; i < hdr->e_phnum; ++i) {
- phdrs[i].p_offset += 4096;
- }
+ /* Rewrite the section header table. */
+ hdr->e_shoff = freeOffset;
+ freeOffset += hdr->e_shnum * sizeof(Elf32_Shdr);
+ memcpy(contents + hdr->e_shoff, shdrs, hdr->e_shnum * sizeof(Elf32_Shdr));
- /* Add a segment that maps the new program/section headers and
- PT_INTERP segment into memory. Otherwise glibc will choke. Add
- this after the PT_PHDR segment but before all other PT_LOAD
- segments. */
- for (i = hdr->e_phnum; i > 1; --i)
- phdrs[i] = phdrs[i - 1];
- hdr->e_phnum++;
- Elf32_Phdr * phdr = phdrs + 1;
- phdr->p_type = PT_LOAD;
- phdr->p_offset = 0;
- phdr->p_vaddr = phdr->p_paddr = firstPage;
- phdr->p_filesz = phdr->p_memsz = 4096;
- phdr->p_flags = PF_R;
- phdr->p_align = 4096;
-
- unsigned int freeOffset = sizeof(Elf32_Ehdr);
-
- /* Find the PT_INTERP segment and replace it by a new one that
- contains the new interpreter name. */
- unsigned int interpOffset = 0, interpSize = 0, interpAddr = 0;
- for (i = 0; i < hdr->e_phnum; ++i) {
- Elf32_Phdr * phdr = phdrs + i;
- fprintf(stderr, "segment type %d at %x\n", phdr->p_type, phdr->p_offset);
- if (phdr->p_type == PT_INTERP) {
- fprintf(stderr, "found interpreter (%s)\n",
- (char *) (contents + phdr->p_offset));
- interpOffset = freeOffset;
- interpSize = strlen(newInterpreter) + 1;
- freeOffset += roundUp(interpSize, 4);
- interpAddr = firstPage + interpOffset % 4096;
- phdr->p_offset = interpOffset;
- growFile(phdr->p_offset + interpSize);
- phdr->p_vaddr = phdr->p_paddr = interpAddr;
- phdr->p_filesz = phdr->p_memsz = interpSize;
- strncpy(contents + interpOffset,
- newInterpreter, interpSize);
- }
+ if (freeOffset > 4096) error("ran out of space in page 0");
}
-
- /* Rewrite the program header table. */
- hdr->e_phoff = freeOffset;
- freeOffset += hdr->e_phnum * sizeof(Elf32_Phdr);
- assert(phdrs[0].p_type == PT_PHDR);
- phdrs[0].p_offset = hdr->e_phoff;
- phdrs[0].p_vaddr = phdrs[0].p_paddr = firstPage + hdr->e_phoff % 4096;
- phdrs[0].p_filesz = phdrs[0].p_memsz = hdr->e_phnum * sizeof(Elf32_Phdr);
- memcpy(contents + hdr->e_phoff, phdrs, hdr->e_phnum * sizeof(Elf32_Phdr));
-
- /* Rewrite the section header table. */
- hdr->e_shoff = freeOffset;
- freeOffset += hdr->e_shnum * sizeof(Elf32_Shdr);
- memcpy(contents + hdr->e_shoff, shdrs, hdr->e_shnum * sizeof(Elf32_Shdr));
-
- if (freeOffset > 4096) error("ran out of space in page 0");
-
- writeFile("./new.exe");
+
+ if (changed)
+ writeFile("./new.exe");
+ else
+ fprintf(stderr, "nothing changed in `%s'\n", fileName);
}
int main(int argc, char * * argv)
{
- if (argc != 2) {
- fprintf(stderr, "syntax: %s FILENAME\n", argv[0]);
+ if (argc <= 1) {
+ fprintf(stderr, "syntax: %s [--interpreter FILENAME] [--shrink-rpath] FILENAME\n", argv[0]);
return 1;
}
- patchElf(argv[1]);
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--interpreter") == 0) {
+ if (++i == argc) error("missing argument");
+ newInterpreter = argv[i];
+ }
+ else if (strcmp(argv[i], "--shrink-rpath") == 0) {
+ doShrinkRPath = 1;
+ }
+ else break;
+ }
+
+ if (i == argc) error("missing filename");
+ fileName = argv[i];
+
+ patchElf();
return 0;
}