/* shdrtab.c: Reading the section header table. * Copyright (C) 1999,2011 by Brian Raiter * License GPLv2+: GNU GPL version 2 or later. * This is free software; you are free to change and redistribute it. * There is NO WARRANTY, to the extent permitted by law. */ #include #include #include #include #include "gen.h" #include "readelf.h" #include "names.h" #include "pieces.h" #include "outbase.h" #include "outitems.h" #include "shdrtab.h" /* An extra value is added to the section name enum to represent the * total number of sections. */ #define LAST_ENUM "COUNT" /* Each entry in the enum is identified by a name and an index value. */ struct enumid { char *name; int ndx; }; /* The list of enum identifiers. */ static struct enumid *sectionenums = NULL; static int sectionenumcount = 0; /* Creates the enum identifier for a section based on its name. * (Sections entries without names do not get included in the enum.) */ static void addenumname(int ndx, char const *name) { char const *src; char *dest; int i; dest = allocate(16 + strlen(name)); for (src = name ; *src ; ++src) if (isalnum(*src)) break; if (*src) { memcpy(dest, "SHN_", 4); for (i = 0 ; src[i] ; ++i) dest[4 + i] = isalnum(src[i]) ? toupper(src[i]) : '_'; dest[4 + i] = '\0'; } else { sprintf(dest, "SHN_%d", ndx); } sectionenums = reallocate(sectionenums, (sectionenumcount + 1) * sizeof *sectionenums); sectionenums[sectionenumcount].name = dest; sectionenums[sectionenumcount].ndx = ndx; ++sectionenumcount; } /* Ensures that there are no duplicated enum identifiers, and that no * enum name matches one of the predefined SHN_* identifiers. */ static void enumfinalize(void) { int i, j; for (i = 0 ; i < sectionenumcount ; ++i) { if (lookupname(sectionenums[i].name, NULL)) goto conflict; if (!strcmp(sectionenums[i].name, "SHN_" LAST_ENUM)) goto conflict; for (j = 0 ; j < i ; ++j) { if (!strcmp(sectionenums[i].name, sectionenums[j].name)) goto conflict; } continue; conflict: sprintf(sectionenums[i].name + strlen(sectionenums[i].name), "_%d", sectionenums[i].ndx); } } /* Extracts a string from a string table, ensuring that it is safely * NUL-terminated. */ static char const *getstringptr(char const *strtab, long strsize, long offset) { long i; if (!strtab || strsize <= 0 || offset < 0 || offset > strsize) return NULL; for (i = offset ; i < strsize ; ++i) if (strtab[i] == '\0') return strtab + offset; warn("string in string table at offset %ld is unterminated.", offset); return NULL; } /* Extract the section header table as an array of Elf64_Shdr. If the * section header table is not already in this form, then a translated * copy is made. */ static Elf64_Shdr const *copytable(char const *ptr, int count, int entsize) { Elf64_Shdr *shdrs; Elf32_Shdr sh32; int i; if (iself64() && entsize == sizeof *shdrs) return (Elf64_Shdr const*)ptr; shdrs = allocate(count * sizeof *shdrs); for (i = 0 ; i < count ; ++i) { if (iself64()) { if (entsize >= (int)(sizeof *shdrs)) { memcpy(shdrs + i, ptr + i * entsize, sizeof *shdrs); } else { memcpy(shdrs + i, ptr + i * entsize, entsize); memset((char*)(shdrs + i) + entsize, 0, sizeof *shdrs - entsize); } } else { if (entsize >= (int)(sizeof sh32)) { memcpy(&sh32, ptr + i * entsize, sizeof sh32); } else { memcpy(&sh32, ptr + i * entsize, entsize); memset((char*)&sh32 + entsize, 0, sizeof sh32 - entsize); } shdrs[i].sh_name = sh32.sh_name; shdrs[i].sh_type = sh32.sh_type; shdrs[i].sh_flags = sh32.sh_flags; shdrs[i].sh_addr = sh32.sh_addr; shdrs[i].sh_offset = sh32.sh_offset; shdrs[i].sh_size = sh32.sh_size; shdrs[i].sh_link = sh32.sh_link; shdrs[i].sh_info = sh32.sh_info; shdrs[i].sh_addralign = sh32.sh_addralign; shdrs[i].sh_entsize = sh32.sh_entsize; } } return shdrs; } /* Uses a section's name and other information in the section header * table to guess the type of its contents. */ static int selectshtype(Elf64_Shdr const *shdr, char const *name) { if (!strcmp(name, ".got")) return P_ADDRS; if (!strncmp(name, ".got.", 5)) return P_ADDRS; if (!strncmp(name, ".ctors", 6)) return P_ADDRS; if (!strncmp(name, ".dtors", 6)) return P_ADDRS; if (shdr->sh_flags & SHF_STRINGS) return P_STRINGS; if (shdr->sh_flags & SHF_EXECINSTR) return P_BYTES; switch (shdr->sh_entsize) { case 8: return P_XWORDS; case 4: return P_WORDS; case 2: return P_HALVES; case 1: return P_BYTES; } return P_SECTION; } /* Consider an entry in the section header table and use its * information to identify one piece of the ELF iamge. A section's * size and type is typically the best indicator this program has as * to its contents. The name in the section header string table is * used to assign an identifier for the piece's identifier, as well as * the section's enum. */ static void dividesection(Elf64_Shdr const *shdrs, int shndx, char const *strtab, long strsize) { char name[64]; char const *str; int type, n; if (shdrs[shndx].sh_type == SHT_NULL) return; switch (shdrs[shndx].sh_type) { case SHT_NOBITS: type = 0; break; case SHT_PROGBITS: type = P_SECTION; break; case SHT_STRTAB: type = P_STRINGS; break; case SHT_SYMTAB: type = P_SYMTAB; break; case SHT_DYNSYM: type = P_SYMTAB; break; case SHT_HASH: type = P_HASH; break; case SHT_DYNAMIC: type = P_DYNAMIC; break; case SHT_REL: type = P_REL; break; case SHT_RELA: type = P_RELA; break; case SHT_NOTE: type = P_NOTE; break; case SHT_INIT_ARRAY: type = P_ADDRS; break; case SHT_FINI_ARRAY: type = P_ADDRS; break; case SHT_PREINIT_ARRAY: type = P_ADDRS; break; #ifdef SHT_GNU_HASH case SHT_GNU_HASH: type = P_GNUHASH; break; #endif #ifdef SHT_GNU_verdef case SHT_GNU_verdef: type = P_WORDS; break; case SHT_GNU_verneed: type = P_WORDS; break; case SHT_GNU_versym: type = P_HALVES; break; #endif case SHT_SUNW_move: type = P_MOVE; break; case SHT_SUNW_syminfo: type = P_SYMINFO; break; default: type = P_SECTION; break; } *name = '\0'; str = getstringptr(strtab, strsize, shdrs[shndx].sh_name); if (str) { addenumname(shndx, str); if (type == P_SECTION) type = selectshtype(shdrs + shndx, str); while (*str && !isalpha(*str)) ++str; sprintf(name, "%-.*s", (int)sizeof name - 1, str); for (n = 0 ; name[n] ; ++n) if (!isalnum(name[n])) name[n] = '_'; } if (type) { n = recordpiece(shdrs[shndx].sh_offset, shdrs[shndx].sh_size, type, name, shdrs[shndx].sh_addralign); if (n < 0) return; setpieceshndx(n, shndx); if (shdrs[shndx].sh_link) { shndx = shdrs[shndx].sh_link; switch (type) { case P_SYMTAB: setpiecestrtable(n, shdrs[shndx].sh_offset, shdrs[shndx].sh_size); break; case P_REL: case P_RELA: setpiecemisctable(n, shdrs[shndx].sh_offset, shdrs[shndx].sh_size); shndx = shdrs[shndx].sh_link; setpiecestrtable(n, shdrs[shndx].sh_offset, shdrs[shndx].sh_size); break; } } } } /* Reads the given section of the ELF image as a section header table, * and uses it break the file into separate pieces. Warnings are * displayed if the table appears to be malformed or invalid. The * section header string table, if present, is loaded as well. */ void dividesections(long offset, int count, int entsize, int shstrndx) { void const *ptr; Elf64_Shdr const *shdrs; char const *strtab = NULL; long size, strsize; int i, n; if (!offset || !count) return; if (!entsize) { warn("ignoring zero value for shentsize."); entsize = sizeof_elf(Shdr); } else if (entsize != sizeof_elf(Shdr)) { warn("section table entry size: %d instead of %d.", entsize, sizeof_elf(Shdr)); } size = count * entsize; ptr = getptrto(offset, &size); if (size != count * entsize) { if (size == 0) { warn("invalid section header table offset: %ld.", offset); return; } warn("section table extends %ld bytes past EOF.", count * entsize - size); } count = size / entsize; if (!count) return; shdrs = copytable(ptr, count, entsize); n = recordpiece(offset, size, P_SHDRTAB, "shdrs", sizeof_elf(Addr)); if (shstrndx) { if (shstrndx >= count) { warn("invalid section header string table index: %d.", shstrndx); } else { strsize = shdrs[shstrndx].sh_size; strtab = getptrto(shdrs[shstrndx].sh_offset, &strsize); if (strsize <= 0) { strtab = NULL; warn("invalid section header string table offset: %ld.", shdrs[shstrndx].sh_offset); } else { setpiecestrtable(n, shdrs[shstrndx].sh_offset, strsize); } } } for (i = 0 ; i < count ; ++i) dividesection(shdrs, i, strtab, strsize); if (sectionenumcount) { enumfinalize(); addenumname(count, LAST_ENUM); } } /* Returns the identifier name for a given section index. If no such * name exists, a decimal literal is returned instead. */ char const *getsectionid(int ndx) { static char buf[16]; char const *name; int i; if (sectionenums) for (i = 0 ; i < sectionenumcount ; ++i) if (sectionenums[i].ndx == ndx) return sectionenums[i].name; name = findname(ndx, "SHN_"); if (name) return name; sprintf(buf, "%d", ndx); return buf; } /* Outputs the section index identifiers as an enum definition. */ void outputenum(void) { int prev = -1; int i; if (sectionenumcount == 0) return; out("enum sections"); beginblock(TRUE); for (i = 0 ; i < sectionenumcount ; ++i) { if (sectionenums[i].ndx == prev + 1) out(sectionenums[i].name); else outf("%s = %d", sectionenums[i].name, sectionenums[i].ndx); prev = sectionenums[i].ndx; } endblock(); out("\n"); }