From b5782a4705cccff527ea6db0a7f19af6a40b610d Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Sun, 3 Feb 2013 20:35:37 +0000 Subject: [PATCH] crunchide(1): support non-custom elf object layout The crunchide utility presumes the last 3 chunks of an ELF object layout are section headers, symbol table, and then string table. However, this is not specified in the ELF standards, and linkers may generate different layouts when doing partial linking (-r). This change is required to build FreeBSD with mclinker or the gold linker. PR: bin/174011 Submitted by: Pete Chou Reviewed by: Cristoph Mallon MFC after: 2 weeks --- usr.sbin/crunch/crunchide/exec_elf32.c | 217 ++++++++++++++++++------- 1 file changed, 157 insertions(+), 60 deletions(-) diff --git a/usr.sbin/crunch/crunchide/exec_elf32.c b/usr.sbin/crunch/crunchide/exec_elf32.c index 9274f8922c82..59ef41f37d30 100644 --- a/usr.sbin/crunch/crunchide/exec_elf32.c +++ b/usr.sbin/crunch/crunchide/exec_elf32.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -82,11 +83,9 @@ __FBSDID("$FreeBSD$"); #define xe32toh(x) ((data == ELFDATA2MSB) ? be32toh(x) : le32toh(x)) #define htoxe32(x) ((data == ELFDATA2MSB) ? htobe32(x) : htole32(x)) -struct listelem { - struct listelem *next; - void *mem; - off_t file; - size_t size; +struct shlayout { + Elf_Shdr *shdr; + void *bufp; }; static ssize_t @@ -235,17 +234,20 @@ int ELFNAMEEND(hide)(int fd, const char *fn) { Elf_Ehdr ehdr; - Elf_Shdr *shdrp = NULL, *symtabshdr, *strtabshdr; + struct shlayout *layoutp = NULL; + Elf_Shdr *shdrp = NULL, *symtabshdr, *strtabshdr, *shstrtabshdr; + Elf_Shdr shdrshdr; Elf_Sym *symtabp = NULL; - char *strtabp = NULL; - Elf_Size nsyms, ewi; + char *shstrtabp = NULL, *strtabp = NULL; + Elf_Size nsyms, ewi; + Elf_Off off; ssize_t shdrsize; - int rv, i, weird; - size_t nstrtab_size, nstrtab_nextoff, fn_size; + int rv, i, weird, l, m, r, strtabidx; + size_t nstrtab_size, nstrtab_nextoff, fn_size, size; char *nstrtabp = NULL; unsigned char data; - Elf_Off maxoff, stroff; const char *weirdreason = NULL; + void *buf; rv = 0; if (xreadatoff(fd, &ehdr, 0, sizeof ehdr, fn) != sizeof ehdr) @@ -260,62 +262,123 @@ ELFNAMEEND(hide)(int fd, const char *fn) shdrsize) goto bad; - symtabshdr = strtabshdr = NULL; + symtabshdr = strtabshdr = shstrtabshdr = NULL; weird = 0; - maxoff = stroff = 0; for (i = 0; i < xe16toh(ehdr.e_shnum); i++) { - if (xewtoh(shdrp[i].sh_offset) > maxoff) - maxoff = xewtoh(shdrp[i].sh_offset); switch (xe32toh(shdrp[i].sh_type)) { case SHT_SYMTAB: - if (symtabshdr != NULL) + if (symtabshdr != NULL) { weird = 1; + weirdreason = "multiple symbol tables"; + } symtabshdr = &shdrp[i]; strtabshdr = &shdrp[xe32toh(shdrp[i].sh_link)]; - - /* Check whether the string table is the last section */ - stroff = xewtoh(shdrp[xe32toh(shdrp[i].sh_link)].sh_offset); - if (!weird && xe32toh(shdrp[i].sh_link) != (xe16toh(ehdr.e_shnum) - 1)) { - weird = 1; - weirdreason = "string table not last section"; - } + break; + case SHT_STRTAB: + if (i == xe16toh(ehdr.e_shstrndx)) + shstrtabshdr = &shdrp[i]; break; } } - if (! weirdreason) - weirdreason = "unsupported"; if (symtabshdr == NULL) goto out; - if (strtabshdr == NULL) + if (strtabshdr == NULL) { weird = 1; - if (!weird && stroff != maxoff) { + weirdreason = "string table does not exist"; + } + if (shstrtabshdr == NULL) { weird = 1; - weirdreason = "string table section not last in file"; - } + weirdreason = "section header string table does not exist"; + } + if (weirdreason == NULL) + weirdreason = "unsupported"; if (weird) { fprintf(stderr, "%s: weird executable (%s)\n", fn, weirdreason); goto bad; } + /* + * sort section layout table by offset + */ + layoutp = xmalloc(sizeof(struct shlayout) * (xe16toh(ehdr.e_shnum) + 1), + fn, "layout table"); + if (layoutp == NULL) + goto bad; + + /* add a pseudo entry to represent the section header table */ + shdrshdr.sh_offset = ehdr.e_shoff; + shdrshdr.sh_size = htoxew(shdrsize); + shdrshdr.sh_addralign = htoxew(ELFSIZE / 8); + layoutp[xe16toh(ehdr.e_shnum)].shdr = &shdrshdr; + + /* insert and sort normal section headers */ + for (i = xe16toh(ehdr.e_shnum) - 1; i >= 0; i--) { + l = i + 1; + r = xe16toh(ehdr.e_shnum); + while (l <= r) { + m = ( l + r) / 2; + if (xewtoh(shdrp[i].sh_offset) > + xewtoh(layoutp[m].shdr->sh_offset)) + l = m + 1; + else + r = m - 1; + } + + if (r != i) { + memmove(&layoutp[i], &layoutp[i + 1], + sizeof(struct shlayout) * (r - i)); + } + + layoutp[r].shdr = &shdrp[i]; + layoutp[r].bufp = NULL; + } + /* * load up everything we need */ - /* symbol table */ - if ((symtabp = xmalloc(xewtoh(symtabshdr->sh_size), fn, "symbol table")) - == NULL) + /* load section string table for debug use */ + if ((shstrtabp = xmalloc(xewtoh(shstrtabshdr->sh_size), fn, + "section string table")) == NULL) goto bad; - if ((size_t)xreadatoff(fd, symtabp, xewtoh(symtabshdr->sh_offset), - xewtoh(symtabshdr->sh_size), fn) != xewtoh(symtabshdr->sh_size)) + if ((size_t)xreadatoff(fd, shstrtabp, xewtoh(shstrtabshdr->sh_offset), + xewtoh(shstrtabshdr->sh_size), fn) != xewtoh(shstrtabshdr->sh_size)) goto bad; - /* string table */ - if ((strtabp = xmalloc(xewtoh(strtabshdr->sh_size), fn, "string table")) - == NULL) - goto bad; - if ((size_t)xreadatoff(fd, strtabp, xewtoh(strtabshdr->sh_offset), - xewtoh(strtabshdr->sh_size), fn) != xewtoh(strtabshdr->sh_size)) - goto bad; + /* we need symtab, strtab, and everything behind strtab */ + strtabidx = INT_MAX; + for (i = 0; i < xe16toh(ehdr.e_shnum) + 1; i++) { + if (layoutp[i].shdr == &shdrshdr) { + /* not load section header again */ + layoutp[i].bufp = shdrp; + continue; + } + if (layoutp[i].shdr == shstrtabshdr) { + /* not load section string table again */ + layoutp[i].bufp = shstrtabp; + continue; + } + + if (layoutp[i].shdr == strtabshdr) + strtabidx = i; + if (layoutp[i].shdr == symtabshdr || i >= strtabidx) { + off = xewtoh(layoutp[i].shdr->sh_offset); + size = xewtoh(layoutp[i].shdr->sh_size); + layoutp[i].bufp = xmalloc(size, fn, + shstrtabp + xewtoh(layoutp[i].shdr->sh_name)); + if (layoutp[i].bufp == NULL) + goto bad; + if ((size_t)xreadatoff(fd, layoutp[i].bufp, off, size, fn) != + size) + goto bad; + + /* set symbol table and string table */ + if (layoutp[i].shdr == symtabshdr) + symtabp = layoutp[i].bufp; + else if (layoutp[i].shdr == strtabshdr) + strtabp = layoutp[i].bufp; + } + } nstrtab_size = 256; nstrtabp = xmalloc(nstrtab_size, fn, "new string table"); @@ -365,28 +428,62 @@ ELFNAMEEND(hide)(int fd, const char *fn) strtabshdr->sh_size = htoxew(nstrtab_nextoff); /* - * write new tables to the file + * update section header table in ascending order of offset */ - if (xwriteatoff(fd, shdrp, xewtoh(ehdr.e_shoff), shdrsize, fn) != - shdrsize) - goto bad; - if ((size_t)xwriteatoff(fd, symtabp, xewtoh(symtabshdr->sh_offset), - xewtoh(symtabshdr->sh_size), fn) != xewtoh(symtabshdr->sh_size)) - goto bad; - /* write new symbol table strings */ - if ((size_t)xwriteatoff(fd, nstrtabp, xewtoh(strtabshdr->sh_offset), - xewtoh(strtabshdr->sh_size), fn) != xewtoh(strtabshdr->sh_size)) - goto bad; + for (i = strtabidx + 1; i < xe16toh(ehdr.e_shnum) + 1; i++) { + Elf_Off off, align; + off = xewtoh(layoutp[i - 1].shdr->sh_offset) + + xewtoh(layoutp[i - 1].shdr->sh_size); + align = xewtoh(layoutp[i].shdr->sh_addralign); + off = (off + (align - 1)) & ~(align - 1); + layoutp[i].shdr->sh_offset = htoxew(off); + } + + /* + * write data to the file in descending order of offset + */ + for (i = xe16toh(ehdr.e_shnum); i >= 0; i--) { + if (layoutp[i].shdr == strtabshdr) { + /* new string table */ + buf = nstrtabp; + } else + buf = layoutp[i].bufp; + + if (layoutp[i].shdr == &shdrshdr || + layoutp[i].shdr == symtabshdr || i >= strtabidx) { + if (buf == NULL) + goto bad; + + /* + * update the offset of section header table in elf + * header if needed. + */ + if (layoutp[i].shdr == &shdrshdr && + ehdr.e_shoff != shdrshdr.sh_offset) { + ehdr.e_shoff = shdrshdr.sh_offset; + off = (ELFSIZE == 32) ? 32 : 44; + size = sizeof(Elf_Off); + if ((size_t)xwriteatoff(fd, &ehdr.e_shoff, off, size, + fn) != size) + goto bad; + } + + off = xewtoh(layoutp[i].shdr->sh_offset); + size = xewtoh(layoutp[i].shdr->sh_size); + if ((size_t)xwriteatoff(fd, buf, off, size, fn) != size) + goto bad; + } + } out: - if (shdrp != NULL) - free(shdrp); - if (symtabp != NULL) - free(symtabp); - if (strtabp != NULL) - free(strtabp); - if (nstrtabp != NULL) - free(nstrtabp); + if (layoutp != NULL) { + for (i = 0; i < xe16toh(ehdr.e_shnum) + 1; i++) { + if (layoutp[i].bufp != NULL) + free(layoutp[i].bufp); + } + free(layoutp); + } + free(nstrtabp); return (rv); bad: