/* rebind: Copyright (C) 2001 by Brian Raiter, under the GNU General * Public License. No warranty. See COPYING for details. */ #include #include #include #include #include #include #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #if ELF_CLASS == ELFCLASS32 #define Elf_Ehdr Elf32_Ehdr #define Elf_Phdr Elf32_Phdr #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Dyn Elf32_Dyn #else #define Elf_Ehdr Elf64_Ehdr #define Elf_Phdr Elf64_Phdr #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Dyn Elf64_Dyn #endif #ifndef ELF32_ST_INFO #define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xF)) #endif #ifndef ELF64_ST_INFO #define ELF64_ST_INFO ELF32_ST_INFO /* does not really matter however as they are the same. */ #endif /* The memory-allocation macro. */ #define alloc(p, n) (((p) = realloc(p, n)) \ || (fputs("Out of memory.\n", stderr), \ exit(EXIT_FAILURE), 0)) /* The online help text. */ static char const *yowzitch = "Usage: rebind [-sw] OBJFILE [SYMBOL...]\n\n" " -h Display this help\n" " -v Display version information\n" " -s Change weak symbols into global symbols\n" " -w Change global symbols into weak symbols (the default)\n" "If no symbol names are given on the command line, the program\n" "reads the symbols from standard input.\n"; /* The version text. */ static char const *vourzhon = "rebind, version 0.9: Copyright (C) 2001 Brian Raiter\n"; static unsigned char fromstrength; /* the binding to change from */ static unsigned char tostrength; /* the binding to change to */ static char const *tostrengthtext; /* what to tell the user is going on */ static char **namelist; /* the list of symbol names to seek */ static int namecount; /* the number of symbols in namelist */ static char const *thefilename; /* the current file name */ static FILE *thefile; /* the current file handle */ static Elf_Ehdr ehdr; /* the file's ELF header */ /* Standard qsort/bsearch string comparison function. */ static int qstrcmp(void const *a, void const *b) { return strcmp(*(char const**)a, *(char const**)b); } /* An error-handling function. The given error message is used only * when errno is not set. */ static int err(char const *errmsg) { if (errno) perror(thefilename); else fprintf(stderr, "%s: %s\n", thefilename, errmsg); return FALSE; } /* Reads and returns an area of the input file. */ static void *getarea(int off, int size) { void *buf = NULL; alloc(buf, size); if (fseek(thefile, off, SEEK_SET) || fread(buf, size, 1, thefile) != 1) { free(buf); return NULL; } return buf; } /* namelistfromfile() builds an array of all the non-whitespace strings * in the given file. */ static int namelistfromfile(FILE *fp) { char word[256]; int allocated = 0; int n; while (fscanf(fp, "%255s", word) > 0) { if (namecount >= allocated) { allocated = allocated ? allocated * 2 : 16; alloc(namelist, allocated * sizeof *namelist); } namelist[namecount] = NULL; n = strlen(word) + 1; alloc(namelist[namecount], n); memcpy(namelist[namecount], word, n); ++namecount; } return TRUE; } /* readheader() checks to make sure that this is in fact a proper ELF * object file that we're proposing to munge. */ static int readheader(void) { int endianness; /* * First, load the ELF header and do some sanity-checking. */ errno = 0; if (fread(&ehdr, sizeof ehdr, 1, thefile) != 1) return err("not an ELF file."); 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("not an ELF file."); endianness = 1; *(unsigned char*)&endianness = 0; if (ehdr.e_ident[EI_DATA] != (endianness ? ELFDATA2MSB : ELFDATA2LSB)) { fprintf(stderr, "%s: not %s-endian.\n", thefilename, endianness ? "big" : "little"); return FALSE; } if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) return err("unrecognized ELF header size"); if (!ehdr.e_shoff) return err("no section header table."); if (ehdr.e_shentsize != sizeof(Elf_Shdr)) return err("unrecognized section header size"); return TRUE; } /* changesymbols() finds all symbols in a given symbol table that * appear in the namelist with the specified binding and alters their * binding strength. */ static int changesymbols(Elf_Sym *symtab, char const *strtab, int count) { Elf_Sym *sym; char const *name; int touched; int i; touched = FALSE; for (i = 0, sym = symtab ; i < count ; ++i, ++sym) { if (ELF32_ST_BIND(sym->st_info) != fromstrength) continue; name = strtab + sym->st_name; if (!bsearch(&name, namelist, namecount, sizeof *namelist, qstrcmp)) continue; #if ELF_CLASS == ELFCLASS32 sym->st_info = ELF32_ST_INFO(tostrength, ELF32_ST_TYPE(sym->st_info)); #else sym->st_info = ELF32_ST_INFO(tostrength, ELF64_ST_TYPE(sym->st_info)); #endif printf("%s \"%s\".\n", tostrengthtext, name); touched = TRUE; } return touched; } /* rebind() does most of the grunt work. After checking over the ELF * headers, the function iterates through the sections, looking for * symbol tables containing non-local symbol. When it finds one, it * loads the non-local part of the table and the associated string * table into memory, and calls changesymbols(). If changesymbols() * actually changes anything, the altered symbol table is written back * out to the object file. */ static int rebind(void) { Elf_Shdr *shdrs; Elf_Sym *symtab; char *strtab; unsigned offset, count; int i; if (!readheader()) return FALSE; if (!(shdrs = getarea(ehdr.e_shoff, ehdr.e_shnum * sizeof(Elf_Shdr)))) return err("invalid section header table."); for (i = 0 ; i < ehdr.e_shnum ; ++i) { if (shdrs[i].sh_type != SHT_SYMTAB && shdrs[i].sh_type != SHT_DYNSYM) continue; if (shdrs[i].sh_entsize != sizeof *symtab) { err("symbol table of unrecognized structure ignored."); continue; } offset = shdrs[i].sh_offset + shdrs[i].sh_info * sizeof *symtab; count = shdrs[i].sh_size / sizeof *symtab - shdrs[i].sh_info; if (!count) continue; if (!(symtab = getarea(offset, count * sizeof *symtab))) return err("invalid symbol table"); if (!(strtab = getarea(shdrs[shdrs[i].sh_link].sh_offset, shdrs[shdrs[i].sh_link].sh_size))) return err("invalid associated string table"); if (changesymbols(symtab, strtab, count)) { if (fseek(thefile, offset, SEEK_SET) || fwrite(symtab, sizeof *symtab, count, thefile) != count) return err("unable to write to the object file"); } free(symtab); free(strtab); } free(shdrs); return TRUE; } /* main() interprets the command-line arguments, builds the array of * symbol names, opens the object file, and calls rebind(). */ int main(int argc, char *argv[]) { int n; fromstrength = STB_GLOBAL; tostrength = STB_WEAK; tostrengthtext = "Weakening"; while ((n = getopt(argc, argv, "hsvw")) != EOF) { switch (n) { case 's': fromstrength = STB_WEAK; tostrength = STB_GLOBAL; tostrengthtext = "Strengthening"; break; case 'w': fromstrength = STB_GLOBAL; tostrength = STB_WEAK; tostrengthtext = "Weakening"; break; case 'h': fputs(yowzitch, stdout); return EXIT_SUCCESS; case 'v': fputs(vourzhon, stdout); return EXIT_SUCCESS; default: fputs(yowzitch, stderr); return EXIT_FAILURE; } } if (optind == argc) { fputs(yowzitch, stderr); return EXIT_FAILURE; } thefilename = argv[optind]; if (!(thefile = fopen(thefilename, "rb+"))) { err("unable to open."); return EXIT_FAILURE; } ++optind; if (optind == argc) { if (!namelistfromfile(stdin)) return EXIT_FAILURE; } else { namelist = argv + optind; namecount = argc - optind; } qsort(namelist, namecount, sizeof *namelist, qstrcmp); n = rebind() ? EXIT_SUCCESS : EXIT_FAILURE; fclose(thefile); return n; }