From 0da4ea8b6510e0d00ccf1eb0c52599532d8c4bb3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 29 Sep 2005 12:03:56 +0000 Subject: * Add tests. --- Makefile.am | 5 +- configure.ac | 4 +- patchelf.c | 410 ---------------------------------------------------- src/Makefile.am | 3 + src/patchelf.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test.c | 9 -- tests/Makefile.am | 27 ++++ tests/bar.c | 7 + tests/foo.c | 9 ++ tests/main.c | 13 ++ tests/plain-fail.sh | 2 + tests/plain-run.sh | 7 + 12 files changed, 481 insertions(+), 425 deletions(-) delete mode 100644 patchelf.c create mode 100644 src/Makefile.am create mode 100644 src/patchelf.c delete mode 100644 test.c create mode 100644 tests/Makefile.am create mode 100644 tests/bar.c create mode 100644 tests/foo.c create mode 100644 tests/main.c create mode 100755 tests/plain-fail.sh create mode 100755 tests/plain-run.sh diff --git a/Makefile.am b/Makefile.am index 5a7c726..a459377 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1 @@ -bin_PROGRAMS = patchelf - -patchelf_SOURCES = patchelf.c - +SUBDIRS = src tests diff --git a/configure.ac b/configure.ac index 13a9b83..6619003 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT(patchelf, "0.1") -AC_CONFIG_SRCDIR(patchelf.c) +AC_CONFIG_SRCDIR(Makefile.am) AM_INIT_AUTOMAKE # Change to `1' to produce a `stable' release (i.e., the `preREVISION' @@ -16,5 +16,5 @@ if test "$STABLE" != "1"; then fi AC_PROG_CC -AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile]) AC_OUTPUT diff --git a/patchelf.c b/patchelf.c deleted file mode 100644 index ae6e7b2..0000000 --- a/patchelf.c +++ /dev/null @@ -1,410 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - - -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) -{ - if (errno) perror(msg); else printf("%s\n", msg); - exit(1); -} - - -static void growFile(off_t newSize) -{ - if (newSize > maxSize) error("maximum file size exceeded"); - if (newSize <= fileSize) return; - if (newSize > fileSize) - memset(contents + fileSize, 0, newSize - fileSize); - fileSize = newSize; -} - - -static void readFile(char * fileName, mode_t * fileMode) -{ - struct stat st; - if (stat(fileName, &st) != 0) error("stat"); - fileSize = st.st_size; - *fileMode = st.st_mode; - maxSize = fileSize + 128 * 1024; - - contents = malloc(fileSize + maxSize); - if (!contents) abort(); - - int fd = open(fileName, O_RDONLY); - if (fd == -1) error("open"); - - if (read(fd, contents, fileSize) != fileSize) error("read"); - - close(fd); -} - - -static void writeFile(char * fileName, mode_t fileMode) -{ - char fileName2[PATH_MAX]; - if (snprintf(fileName2, sizeof(fileName2), - "%s_patchelf_tmp", fileName) >= sizeof(fileName2)) - error("file name too long"); - - int fd = open(fileName2, O_CREAT | O_TRUNC | O_WRONLY, 0700); - if (fd == -1) error("open"); - - if (write(fd, contents, fileSize) != fileSize) error("write"); - - if (close(fd) != 0) error("close"); - - if (chmod(fileName2, fileMode) != 0) error("chmod"); - - if (rename(fileName2, fileName) != 0) error("rename"); -} - - -static unsigned int roundUp(unsigned int n, unsigned int m) -{ - return ((n - 1) / m + 1) * m; -} - - -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 concat(char * dst, char * src) -{ - if (*dst) strcat(dst, ":"); - strcat(dst, src); -} - - -static void shrinkRPath(void) -{ - /* 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; - - /* Non-absolute entries are allowed (e.g., the special - "$ORIGIN" hack). */ - if (*dirName != '/') { - concat(newRPath, 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]; - if (snprintf(libName, sizeof(libName), "%s/%s", dirName, neededLibs[j]) - >= sizeof(libName)) - error("file name too long"); - 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 - concat(newRPath, dirName); - } - - if (strcmp(rpath, newRPath) != 0) { - 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); - - mode_t fileMode; - - readFile(fileName, &fileMode); - - /* Check the ELF header for basic validity. */ - if (fileSize < sizeof(Elf32_Ehdr)) error("missing ELF header"); - - hdr = (Elf32_Ehdr *) contents; - - if (memcmp(hdr->e_ident, ELFMAG, 4) != 0) - error("not an ELF executable"); - - if (contents[EI_CLASS] != ELFCLASS32 || - contents[EI_DATA] != ELFDATA2LSB || - contents[EI_VERSION] != EV_CURRENT) - error("ELF executable is not 32-bit, little-endian, version 1"); - - if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) - error("wrong ELF type"); - - if (hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize > fileSize) - error("missing program headers"); - - if (hdr->e_shoff + hdr->e_shnum * hdr->e_shentsize > fileSize) - error("missing section headers"); - - if (hdr->e_phentsize != sizeof(Elf32_Phdr)) - error("program headers have wrong size"); - - /* Copy the program and section headers. */ - memcpy(phdrs, contents + hdr->e_phoff, hdr->e_phnum * sizeof(Elf32_Phdr)); - memcpy(shdrs, contents + hdr->e_shoff, hdr->e_shnum * sizeof(Elf32_Shdr)); - - /* Find the next free virtual address page so that we can add - segments without messing up other segments. */ - int i; - unsigned int nextFreePage = 0; - for (i = 0; i < hdr->e_phnum; ++i) { - Elf32_Phdr * phdr = phdrs + i; - unsigned int end = roundUp(phdr->p_vaddr + phdr->p_memsz, 4096); - if (end > nextFreePage) nextFreePage = end; - } - - firstPage = 0x08047000; - - /* Do what we're supposed to do. */ - setInterpreter(); - shrinkRPath(); - - 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)); - - /* 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"); - } - - if (changed) - writeFile(fileName, fileMode); - else - fprintf(stderr, "nothing changed in `%s'\n", fileName); -} - - -int main(int argc, char * * argv) -{ - if (argc <= 1) { - fprintf(stderr, "syntax: %s [--interpreter FILENAME] [--shrink-rpath] FILENAME\n", argv[0]); - return 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; -} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..d61eee2 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,3 @@ +bin_PROGRAMS = patchelf + +patchelf_SOURCES = patchelf.c diff --git a/src/patchelf.c b/src/patchelf.c new file mode 100644 index 0000000..ae6e7b2 --- /dev/null +++ b/src/patchelf.c @@ -0,0 +1,410 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +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) +{ + if (errno) perror(msg); else printf("%s\n", msg); + exit(1); +} + + +static void growFile(off_t newSize) +{ + if (newSize > maxSize) error("maximum file size exceeded"); + if (newSize <= fileSize) return; + if (newSize > fileSize) + memset(contents + fileSize, 0, newSize - fileSize); + fileSize = newSize; +} + + +static void readFile(char * fileName, mode_t * fileMode) +{ + struct stat st; + if (stat(fileName, &st) != 0) error("stat"); + fileSize = st.st_size; + *fileMode = st.st_mode; + maxSize = fileSize + 128 * 1024; + + contents = malloc(fileSize + maxSize); + if (!contents) abort(); + + int fd = open(fileName, O_RDONLY); + if (fd == -1) error("open"); + + if (read(fd, contents, fileSize) != fileSize) error("read"); + + close(fd); +} + + +static void writeFile(char * fileName, mode_t fileMode) +{ + char fileName2[PATH_MAX]; + if (snprintf(fileName2, sizeof(fileName2), + "%s_patchelf_tmp", fileName) >= sizeof(fileName2)) + error("file name too long"); + + int fd = open(fileName2, O_CREAT | O_TRUNC | O_WRONLY, 0700); + if (fd == -1) error("open"); + + if (write(fd, contents, fileSize) != fileSize) error("write"); + + if (close(fd) != 0) error("close"); + + if (chmod(fileName2, fileMode) != 0) error("chmod"); + + if (rename(fileName2, fileName) != 0) error("rename"); +} + + +static unsigned int roundUp(unsigned int n, unsigned int m) +{ + return ((n - 1) / m + 1) * m; +} + + +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 concat(char * dst, char * src) +{ + if (*dst) strcat(dst, ":"); + strcat(dst, src); +} + + +static void shrinkRPath(void) +{ + /* 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; + + /* Non-absolute entries are allowed (e.g., the special + "$ORIGIN" hack). */ + if (*dirName != '/') { + concat(newRPath, 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]; + if (snprintf(libName, sizeof(libName), "%s/%s", dirName, neededLibs[j]) + >= sizeof(libName)) + error("file name too long"); + 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 + concat(newRPath, dirName); + } + + if (strcmp(rpath, newRPath) != 0) { + 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); + + mode_t fileMode; + + readFile(fileName, &fileMode); + + /* Check the ELF header for basic validity. */ + if (fileSize < sizeof(Elf32_Ehdr)) error("missing ELF header"); + + hdr = (Elf32_Ehdr *) contents; + + if (memcmp(hdr->e_ident, ELFMAG, 4) != 0) + error("not an ELF executable"); + + if (contents[EI_CLASS] != ELFCLASS32 || + contents[EI_DATA] != ELFDATA2LSB || + contents[EI_VERSION] != EV_CURRENT) + error("ELF executable is not 32-bit, little-endian, version 1"); + + if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) + error("wrong ELF type"); + + if (hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize > fileSize) + error("missing program headers"); + + if (hdr->e_shoff + hdr->e_shnum * hdr->e_shentsize > fileSize) + error("missing section headers"); + + if (hdr->e_phentsize != sizeof(Elf32_Phdr)) + error("program headers have wrong size"); + + /* Copy the program and section headers. */ + memcpy(phdrs, contents + hdr->e_phoff, hdr->e_phnum * sizeof(Elf32_Phdr)); + memcpy(shdrs, contents + hdr->e_shoff, hdr->e_shnum * sizeof(Elf32_Shdr)); + + /* Find the next free virtual address page so that we can add + segments without messing up other segments. */ + int i; + unsigned int nextFreePage = 0; + for (i = 0; i < hdr->e_phnum; ++i) { + Elf32_Phdr * phdr = phdrs + i; + unsigned int end = roundUp(phdr->p_vaddr + phdr->p_memsz, 4096); + if (end > nextFreePage) nextFreePage = end; + } + + firstPage = 0x08047000; + + /* Do what we're supposed to do. */ + setInterpreter(); + shrinkRPath(); + + 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)); + + /* 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"); + } + + if (changed) + writeFile(fileName, fileMode); + else + fprintf(stderr, "nothing changed in `%s'\n", fileName); +} + + +int main(int argc, char * * argv) +{ + if (argc <= 1) { + fprintf(stderr, "syntax: %s [--interpreter FILENAME] [--shrink-rpath] FILENAME\n", argv[0]); + return 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; +} diff --git a/test.c b/test.c deleted file mode 100644 index 62e6d62..0000000 --- a/test.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -char buf[16 * 1024 * 1024]; - -int main(int argc, char * * argv) -{ - printf("Hello World\n"); - return 0; -} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..fc92ab3 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,27 @@ +check_PROGRAMS = main + +TESTS = plain-run.sh $(XFAIL_TESTS) + +XFAIL_TESTS = plain-fail.sh + + +main: main.o libfoo.so + LD_LIBRARY_PATH=. $(CC) -o main main.o -L . -lfoo + +main.o: main.c + $(CC) -fpic -o main.o -c main.c + +libfoo.so: foo.o libbar.so + $(CC) -shared -o libfoo.so foo.o -L . -lbar + +foo.o: foo.c + $(CC) -fpic -o foo.o -c foo.c + +libbar.so: bar.o + $(CC) -shared -o libbar.so bar.o -L . + +bar.o: bar.c + $(CC) -fpic -o bar.o -c bar.c + +clean-local: + $(RM) *.o libfoo.so libbar.so main diff --git a/tests/bar.c b/tests/bar.c new file mode 100644 index 0000000..5012310 --- /dev/null +++ b/tests/bar.c @@ -0,0 +1,7 @@ +#include + +int bar() +{ + printf("This is bar()!\n"); + return 34; +} diff --git a/tests/foo.c b/tests/foo.c new file mode 100644 index 0000000..be71954 --- /dev/null +++ b/tests/foo.c @@ -0,0 +1,9 @@ +#include + +int bar(); + +int foo() +{ + printf("This is foo()!\n"); + return 12 + bar(); +} diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..8d71605 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,13 @@ +#include + +char buf[16 * 1024 * 1024]; + +int foo(); + +int main(int argc, char * * argv) +{ + printf("Hello World\n"); + int x = foo(); + printf("Result is %d\n", x); + return x; +} diff --git a/tests/plain-fail.sh b/tests/plain-fail.sh new file mode 100755 index 0000000..b8021bc --- /dev/null +++ b/tests/plain-fail.sh @@ -0,0 +1,2 @@ +#! /bin/sh +./main diff --git a/tests/plain-run.sh b/tests/plain-run.sh new file mode 100755 index 0000000..e092956 --- /dev/null +++ b/tests/plain-run.sh @@ -0,0 +1,7 @@ +#! /bin/sh +LD_LIBRARY_PATH=. ./main +exitCode=$? +if test "$exitCode" != 46; then + echo "bad exit code!" + exit 1 +fi -- cgit v0.12