diff --git a/src/stub/util/sstrip/README.1ST b/src/stub/util/sstrip/README.1ST index 8663bbf1..3b6466a6 100644 --- a/src/stub/util/sstrip/README.1ST +++ b/src/stub/util/sstrip/README.1ST @@ -5,8 +5,8 @@ The main purpose of these programs is to be illustrative and educational -- to help fellow programmers understand the ELF file format and something of how it works under the Linux platform. For the most part, these programs have limited real-world utility. (Although I -myself have found these programs quite useful while writing the -others.) +myself have found some of these programs quite useful while writing +the others.) Each program is independent. There is no shared code between them, and in fact they all take slightly different approaches to handling ELF @@ -18,6 +18,10 @@ sstrip/ sstrip is a small utility that removes everything from an ELF file that is not part of the file's memory image. +rebind/ + rebind is another small utility that alters the binding of selected + exported symbols in an ELF object file. + elfls/ elfls is a utility that displays an ELF file's program and/or section header tables, which serve as a kind of global roadmap to @@ -38,12 +42,12 @@ tiny/ See the README in each directory for more details. The ELF standard is necessary reading if you wish to fully understand -how these programs work. You can download a copy as a Postscript +how all of the programs work. You can download a copy as a Postscript document from ftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz. Alternately, you can obtain a flat-text transcription of this document from http://www.muppetlabs.com/~breadbox/software/ELF.txt. - All these programs are Copyright (C) 1999 by Brian Raiter. + These programs are Copyright (C) 1999-2001 by Brian Raiter. These programs are all free software; you can redistribute and/or modify them under the terms of the GNU General Public License as @@ -67,22 +71,3 @@ Share and enjoy. Brian Raiter breadbox@muppetlabs.com -July, 1999 - -________________ - - -This is a minor update of the original release. Shortly after -installing a 2.2 Linux kernel, I discovered that changes to the system -header files, plus a new warning in gcc 2.95.2, caused several of the -programs to generate numerous compiler warnings and errors. The errors -were caused by system headers re-defining a macro (benignly), and were -suppressed by rearranging the inclusion of some header files. The -warnings were due to my taking advantage of the standard feature of -omitting trailing initializers in the definition of a structure, and -were suppressed by omitting the -W gcc option in the Makefiles, -leaving just -Wall. My apologies to any Linux 2.2 users who were -bitten by this. (Hopefully Linux 2.4 isn't in the process of breaking -this version as I write.) - -August, 2000 diff --git a/src/stub/util/sstrip/sstrip.c b/src/stub/util/sstrip/sstrip.c index 68fdd3c5..a23b2e99 100644 --- a/src/stub/util/sstrip/sstrip.c +++ b/src/stub/util/sstrip/sstrip.c @@ -1,191 +1,264 @@ -/* sstrip, version 1.0: Copyright (C) 1999 by Brian Raiter, under the - * GNU General Public License. No warranty. See COPYING for details. +/* sstrip: Copyright (C) 1999-2001 by Brian Raiter, under the GNU + * General Public License. No warranty. See COPYING for details. */ -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif -/* The memory-allocation macro. +#if ELF_CLASS == ELFCLASS32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Phdr Elf32_Phdr +#else +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Phdr Elf64_Phdr +#endif + +/* The name of the program. */ -#define alloc(p, n) (((p) = realloc(p, n)) \ - || (fputs("Out of memory.\n", stderr), \ - exit(EXIT_FAILURE), 0)) +static char const *progname; -static char const *thefilename; /* the current file name */ -static FILE *thefile; /* the current file handle */ +/* The name of the current file. + */ +static char const *filename; -static Elf32_Ehdr elfhdr; /* original ELF header */ -static Elf32_Phdr *phdrs = NULL; /* original program header tbl */ -static unsigned long phdrsize; /* size of program header tbl */ -static unsigned long newsize; /* size of the new file */ -/* An error-handling function. The given error message is used only - * when errno is not set. +/* A simple error-handling function. FALSE is always returned for the + * convenience of the caller. */ static int err(char const *errmsg) { - if (errno) - perror(thefilename); - else - fprintf(stderr, "%s: %s\n", thefilename, errmsg); + fprintf(stderr, "%s: %s: %s\n", progname, filename, errmsg); return FALSE; } -/* readheaders() reads the ELF header and the program header table, - * and checks to make sure that this is in fact a file that we should - * be munging. +/* A macro for I/O errors: The given error message is used only when + * errno is not set. */ -static int readheaders(void) +#define ferr(msg) (err(errno ? strerror(errno) : (msg))) + +/* readelfheader() reads the ELF header into our global variable, and + * checks to make sure that this is in fact a file that we should be + * munging. + */ +static int readelfheader(int fd, Elf_Ehdr *ehdr) { - int bigend; - errno = 0; - if (fread(&elfhdr, sizeof elfhdr, 1, thefile) != 1) - return err("not an ELF file."); - if (elfhdr.e_ident[EI_MAG0] != ELFMAG0 - || elfhdr.e_ident[EI_MAG1] != ELFMAG1 - || elfhdr.e_ident[EI_MAG2] != ELFMAG2 - || elfhdr.e_ident[EI_MAG3] != ELFMAG3) - return err("not an ELF file."); + if (read(fd, ehdr, sizeof *ehdr) != sizeof *ehdr) + return ferr("missing or incomplete ELF header."); - bigend = TRUE; - *(char*)&bigend = 0; - if (elfhdr.e_ident[EI_DATA] != (bigend ? ELFDATA2MSB : ELFDATA2LSB)) { - fprintf(stderr, "%s: not %s-endian.\n", - thefilename, bigend ? "big" : "little"); - return FALSE; - } - if (elfhdr.e_ehsize != sizeof(Elf32_Ehdr)) { - fprintf(stderr, "%s: unrecognized ELF header size " - "(size = %u instead of %u).\n", - thefilename, elfhdr.e_ehsize, sizeof(Elf32_Ehdr)); - return FALSE; - } - if (!elfhdr.e_phoff) - return err("no program header table."); - if (elfhdr.e_phentsize != sizeof(Elf32_Phdr)) { - fprintf(stderr, "%s: unrecognized program header size " - "(size = %u instead of %u).\n", - thefilename, elfhdr.e_phentsize, sizeof(Elf32_Ehdr)); - return FALSE; - } + /* Check the ELF signature. + */ + if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0 && + ehdr->e_ident[EI_MAG1] == ELFMAG1 && + ehdr->e_ident[EI_MAG2] == ELFMAG2 && + ehdr->e_ident[EI_MAG3] == ELFMAG3)) + return err("missing ELF signature."); - phdrsize = elfhdr.e_phnum * elfhdr.e_phentsize; - alloc(phdrs, phdrsize); - errno = 0; - if (fread(phdrs, phdrsize, 1, thefile) != 1) - return err("invalid program header table."); + /* Compare the file's class and endianness with the program's. + */ + if (ehdr->e_ident[EI_DATA] != ELF_DATA) + return err("ELF file has different endianness."); + if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) + return err("ELF file has different word size."); + + /* Check the target architecture. + */ + if (ehdr->e_machine != ELF_ARCH) + return err("ELF file created for different architecture."); + + /* Verify the sizes of the ELF header and the program segment + * header table entries. + */ + if (ehdr->e_ehsize != sizeof(Elf_Ehdr)) + return err("unrecognized ELF header size."); + if (ehdr->e_phentsize != sizeof(Elf_Phdr)) + return err("unrecognized program segment header size."); + + /* Finally, check the file type. + */ + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + return err("not an executable or shared-object library."); return TRUE; } -/* getloadsize() determines the offset of the last byte of the file - * that is actually loaded into memory. Anything after this point can - * be safely discarded. +/* readphdrtable() loads the program segment header table into memory. */ -static int getloadsize(void) +static int readphdrtable(int fd, Elf_Ehdr const *ehdr, Elf_Phdr **phdrs) { - Elf32_Phdr *phdr; - unsigned long n; + size_t size; + + if (!ehdr->e_phoff || !ehdr->e_phnum) + return err("ELF file has no program header table."); + + size = ehdr->e_phnum * sizeof **phdrs; + if (!(*phdrs = malloc(size))) + return err("Out of memory!"); + + errno = 0; + if (read(fd, *phdrs, size) != (ssize_t)size) + return ferr("missing or incomplete program segment header table."); + + return TRUE; +} + +/* getmemorysize() determines the offset of the last byte of the file + * that is referenced by an entry in the program segment header table. + * (Anything in the file after that point is not used when the program + * is executing, and thus can be safely discarded.) + */ +static int getmemorysize(Elf_Ehdr const *ehdr, Elf_Phdr const *phdrs, + unsigned long *newsize) +{ + Elf32_Phdr const *phdr; + unsigned long size, n; int i; - newsize = elfhdr.e_phoff + phdrsize; - phdr = phdrs; - for (i = 0 ; i < elfhdr.e_phnum ; ++i) { - if (phdr->p_type == PT_NULL || phdr->p_type == PT_NOTE) - continue; - n = phdr->p_offset + phdr->p_filesz; - if (n > newsize) - newsize = n; - phdr = (Elf32_Phdr*)((char*)phdr + elfhdr.e_phentsize); + /* Start by setting the size to include the ELF header and the + * complete program segment header table. + */ + size = ehdr->e_phoff + ehdr->e_phnum * sizeof *phdrs; + if (size < sizeof *ehdr) + size = sizeof *ehdr; + + /* Then keep extending the size to include whatever data the + * program segment header table references. + */ + for (i = 0, phdr = phdrs ; i < ehdr->e_phnum ; ++i, ++phdr) { + if (phdr->p_type != PT_NULL) { + n = phdr->p_offset + phdr->p_filesz; + if (n > size) + size = n; + } } - for (i = 0 ; i < elfhdr.e_phnum ; ++i) - if (phdr->p_filesz > 0 && phdr->p_offset >= newsize) - memset(phdr, 0, elfhdr.e_phentsize); - + *newsize = size; return TRUE; } /* truncatezeros() examines the bytes at the end of the file's - * size-to-be, and reduces the size to exclude trailing zero bytes. + * size-to-be, and reduces the size to exclude any trailing zero + * bytes. */ -static int truncatezeros(void) +static int truncatezeros(int fd, unsigned long *newsize) { - char contents[1024]; - unsigned long n; + unsigned char contents[1024]; + unsigned long size, n; + size = *newsize; do { n = sizeof contents; - if (n > newsize) - n = newsize; - if (fseek(thefile, newsize - n, SEEK_SET) - || fread(contents, n, 1, thefile) != 1) - return err("cannot read file contents"); + if (n > size) + n = size; + if (lseek(fd, size - n, SEEK_SET) == (off_t)-1) + return ferr("cannot seek in file."); + if (read(fd, contents, n) != (ssize_t)n) + return ferr("cannot read file contents"); while (n && !contents[--n]) - --newsize; - } while (newsize && !n); + --size; + } while (size && !n); + /* Sanity check. + */ + if (!size) + return err("ELF file is completely blank!"); + + *newsize = size; return TRUE; } /* modifyheaders() removes references to the section header table if - * it was removed, and reduces program header table entries that + * it was stripped, and reduces program header table entries that * included truncated bytes at the end of the file. */ -static int modifyheaders(void) +static int modifyheaders(Elf_Ehdr *ehdr, Elf_Phdr *phdrs, + unsigned long newsize) { Elf32_Phdr *phdr; int i; - if (elfhdr.e_shoff >= newsize) { - elfhdr.e_shoff = 0; - elfhdr.e_shnum = 0; - elfhdr.e_shentsize = 0; - elfhdr.e_shstrndx = 0; + /* If the section header table is gone, then remove all references + * to it in the ELF header. + */ + if (ehdr->e_shoff >= newsize) { + ehdr->e_shoff = 0; + ehdr->e_shnum = 0; + ehdr->e_shentsize = 0; + ehdr->e_shstrndx = 0; } - phdr = phdrs; - for (i = 0 ; i < elfhdr.e_phnum ; ++i) { - if (phdr->p_offset + phdr->p_filesz > newsize) { - if (phdr->p_offset >= newsize) - phdr->p_filesz = 0; - else - phdr->p_filesz = newsize - phdr->p_offset; + /* The program adjusts the file size of any segment that was + * truncated. The case of a segment being completely stripped out + * is handled separately. + */ + for (i = 0, phdr = phdrs ; i < ehdr->e_phnum ; ++i, ++phdr) { + if (phdr->p_offset >= newsize) { + phdr->p_offset = newsize; + phdr->p_filesz = 0; + } else if (phdr->p_offset + phdr->p_filesz > newsize) { + phdr->p_filesz = newsize - phdr->p_offset; } - phdr = (Elf32_Phdr*)((char*)phdr + elfhdr.e_phentsize); } return TRUE; } -/* savestripped() writes the new headers back to the original file - * and sets the new file size. +/* commitchanges() writes the new headers back to the original file + * and sets the file to its new size. */ -static int savestripped(void) +static int commitchanges(int fd, Elf_Ehdr const *ehdr, Elf_Phdr *phdrs, + unsigned long newsize) { - rewind(thefile); + size_t n; + /* Save the changes to the ELF header, if any. + */ + if (lseek(fd, 0, SEEK_SET)) + return ferr("could not rewind file"); errno = 0; - if (fwrite(&elfhdr, sizeof elfhdr, 1, thefile) != 1 - || fwrite(phdrs, phdrsize, 1, thefile) != 1 - || ftruncate(fileno(thefile), newsize)) { - err("could not write contents"); - fprintf(stderr, "WARNING: %s may be corrupted!\n", thefilename); - return FALSE; + if (write(fd, ehdr, sizeof *ehdr) != sizeof *ehdr) + return err("could not modify file"); + + /* Save the changes to the program segment header table, if any. + */ + if (lseek(fd, ehdr->e_phoff, SEEK_SET) == (off_t)-1) { + err("could not seek in file."); + goto warning; + } + n = ehdr->e_phnum * sizeof *phdrs; + if (write(fd, phdrs, n) != (ssize_t)n) { + err("could not write to file"); + goto warning; + } + + /* Eleventh-hour sanity check: don't truncate before the end of + * the program segment header table. + */ + if (newsize < ehdr->e_phoff + n) + newsize = ehdr->e_phoff + n; + + /* Chop off the end of the file. + */ + if (ftruncate(fd, newsize)) { + err("could not resize file"); + goto warning; } return TRUE; + + warning: + return err("ELF file may have been corrupted!"); } /* main() loops over the cmdline arguments, leaving all the real work @@ -193,31 +266,48 @@ static int savestripped(void) */ int main(int argc, char *argv[]) { + int fd; + Elf_Ehdr ehdr; + Elf_Phdr *phdrs; + unsigned long newsize; char **arg; - int ret = 0; + int failures = 0; - if (argc < 2 || !strcmp(argv[1], "-h")) { - printf("sstrip, version 2.0: Copyright (C) 1999 Brian Raiter\n" - "Usage: sstrip FILE...\n"); - return 0; + if (argc < 2 || argv[1][0] == '-') { + printf("Usage: sstrip FILE...\n" + "sstrip discards all nonessential bytes from an executable.\n\n" + "Version 2.0 Copyright (C) 2000,2001 Brian Raiter.\n" + "This program is free software, licensed under the GNU\n" + "General Public License. There is absolutely no warranty.\n"); + return EXIT_SUCCESS; } - for (arg = argv + 1 ; (thefilename = *arg) != NULL ; ++arg) { - if (!(thefile = fopen(thefilename, "rb+"))) { - err("unable to open."); - ++ret; + progname = argv[0]; + + for (arg = argv + 1 ; *arg != NULL ; ++arg) { + filename = *arg; + + fd = open(*arg, O_RDWR); + if (fd < 0) { + ferr("can't open"); + ++failures; continue; } - if (!readheaders() || !getloadsize() || !truncatezeros() - || !modifyheaders() || !savestripped()) - ++ret; - fclose(thefile); + + if (!(readelfheader(fd, &ehdr) && + readphdrtable(fd, &ehdr, &phdrs) && + getmemorysize(&ehdr, phdrs, &newsize) && + truncatezeros(fd, &newsize) && + modifyheaders(&ehdr, phdrs, newsize) && + commitchanges(fd, &ehdr, phdrs, newsize))) + ++failures; + + close(fd); } - return ret; + return failures ? EXIT_FAILURE : EXIT_SUCCESS; } - /* vi:ts=8:et:nowrap */