diff options
Diffstat (limited to 'findtextrel')
-rw-r--r-- | findtextrel/Makefile | 15 | ||||
-rw-r--r-- | findtextrel/findtextrel.1 | 104 | ||||
-rw-r--r-- | findtextrel/findtextrel.c | 417 |
3 files changed, 536 insertions, 0 deletions
diff --git a/findtextrel/Makefile b/findtextrel/Makefile new file mode 100644 index 000000000000..4c316eb0b42a --- /dev/null +++ b/findtextrel/Makefile @@ -0,0 +1,15 @@ +# $Id: Makefile 2069 2011-10-26 15:53:48Z jkoshy $ + +TOP= .. + +PROG= findtextrel +SRCS= findtextrel.c + +WARNS?= 6 + +DPADD= ${LIBELFTC} ${LIBDWARF} ${LIBELF} +LDADD= -lelftc -ldwarf -lelf + +MAN1= findtextrel.1 + +.include "${TOP}/mk/elftoolchain.prog.mk" diff --git a/findtextrel/findtextrel.1 b/findtextrel/findtextrel.1 new file mode 100644 index 000000000000..ee88b8ecba00 --- /dev/null +++ b/findtextrel/findtextrel.1 @@ -0,0 +1,104 @@ +.\" Copyright (c) 2010,2011 Joseph Koshy <jkoshy@users.sourceforge.net> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer +.\" in this position and unchanged. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $Id: findtextrel.1 2069 2011-10-26 15:53:48Z jkoshy $ +.\" +.Dd August 25, 2011 +.Os +.Dt FINDTEXTREL 1 +.Sh NAME +.Nm findtextrel +.Nd locate text relocation entries in position independent ELF executables +.Sh SYNOPSIS +.Nm +.Op Fl V +.Op Fl H +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility displays information about text relocations in ELF objects +containing position independent code. +.Pp +Text relocations are usually undesirable because they require that the +text sections of objects be modified at load time, preventing the +sharing of text sections across multiple processes using a dynamic +shared object. +.Pp +Arguments +.Ar +name ELF executables to be examined. +If no files are specified, the +.Nm +utility will examine the file +.Pa a.out +in the current directory. +.Pp +The +.Nm +utility recognizes the following options: +.Bl -tag -width indent +.It Fl H +Print a brief help message. +.It Fl V +Print a version identifier and exit. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +To list text relocations in an object, use: +.Bd -literal -offset indent +% findtextrel a.out +a.out: ELF object contains text relocation records: +a.out: off: 0x530, func: main, file: a.c, line: 5 +.Ed +.Sh DIAGNOSTICS +The +.Nm +may issue the following diagnostics: +.Bl -diag +.It "ELF object is not a DSO/PIE" +The ELF executable specified by argument +.Ar object +was not a position independent executable. +.It "ELF object does not contain a text relocation" +The ELF executable specified by argument +.Ar object +contained no text relocations. +.El +.Sh SEE ALSO +.Xr addr2line 1 , +.Xr nm 1 , +.Xr readelf 1 +.Sh HISTORY +A +.Nm +utility first appeared in the +.Nm elfutils +toolset from Red Hat, Inc. +.Sh AUTHORS +This implementation of the +.Nm +utility was created by +.An "Kai Wang" Aq kaiwang27@users.sourceforge.net . diff --git a/findtextrel/findtextrel.c b/findtextrel/findtextrel.c new file mode 100644 index 000000000000..a744ca5b0b13 --- /dev/null +++ b/findtextrel/findtextrel.c @@ -0,0 +1,417 @@ +/*- + * Copyright (c) 2010 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <err.h> +#include <dwarf.h> +#include <fcntl.h> +#include <gelf.h> +#include <getopt.h> +#include <libdwarf.h> +#include <libelftc.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "_elftc.h" + +ELFTC_VCSID("$Id: findtextrel.c 2185 2011-11-19 16:07:16Z jkoshy $"); + +static struct option longopts[] = { + {"help", no_argument, NULL, 'H'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +#define USAGE_MESSAGE "\ +Usage: %s [options] [files...]\n\ + Show text relocations present in position independent code.\n\n\ + Options:\n\ + -H Print a help message.\n\ + -V Print a version identifier and exit.\n" + +static void +usage(void) +{ + (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); + exit(1); +} + +static void +version(void) +{ + (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); + exit(0); +} + +static const char * +find_symbol(const char *fn, Elf *e, Elf_Data *d, GElf_Shdr *sh, uintmax_t off) +{ + GElf_Sym sym; + const char *name; + int i, len; + + len = d->d_size / sh->sh_entsize; + for (i = 0; i < len; i++) { + if (gelf_getsym(d, i, &sym) != &sym) { + warnx("%s: gelf_getsym() failed: %s", fn, + elf_errmsg(-1)); + continue; + } + if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) + continue; + if (off >= sym.st_value && off < sym.st_value + sym.st_size) { + name = elf_strptr(e, sh->sh_link, sym.st_name); + if (name == NULL) + warnx("%s: elf_strptr() failed: %s", fn, + elf_errmsg(-1)); + return (name); + } + } + + return (NULL); +} + +static void +report_textrel(const char *fn, Elf *e, Dwarf_Debug dbg, uintmax_t off, + int *textrel) +{ + Dwarf_Die die; + Dwarf_Line *lbuf; + Dwarf_Error de; + Dwarf_Half tag; + Dwarf_Unsigned lopc, hipc, lineno, plineno; + Dwarf_Signed lcount; + Dwarf_Addr lineaddr, plineaddr; + Elf_Scn *scn; + Elf_Data *d; + GElf_Shdr sh; + const char *name; + char *file, *pfile; + int elferr, found, i, ret; + + if (!*textrel) { + printf("%s: ELF object contains text relocation records:\n", + fn); + *textrel = 1; + } + + printf("%s: off: %#jx", fn, off); + + found = 0; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("%s: gelf_getshdr() failed: %s", fn, + elf_errmsg(-1)); + continue; + } + if (sh.sh_type != SHT_DYNSYM && + sh.sh_type != SHT_SYMTAB) + continue; + (void) elf_errno(); + if ((d = elf_getdata(scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("%s: elf_getdata() failed: %s", + fn, elf_errmsg(-1)); + continue; + } + if (d->d_size <= 0) + continue; + if ((name = find_symbol(fn, e, d, &sh, off)) != NULL) { + printf(", func: %s", name); + break; + } + } + elferr = elf_errno(); + if (elferr != 0) + warnx("%s: elf_nextscn() failed: %s", fn, + elf_errmsg(elferr)); + + if (dbg == NULL) + goto done; + + /* + * More verbose output if debugging information is available. + */ + + while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, + &de)) == DW_DLV_OK) { + die = NULL; + while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) { + if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) + goto out; + /* XXX: What about DW_TAG_partial_unit? */ + if (tag == DW_TAG_compile_unit) + break; + } + if (die == NULL) { + /* Could not find DW_TAG_compile_unit DIE. */ + goto out; + } + if (!dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) && + !dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) { + /* + * Check if the address falls into the PC range of + * this CU. + */ + if (off < lopc || off >= hipc) + continue; + } else + continue; + + if (dwarf_srclines(die, &lbuf, &lcount, &de) != DW_DLV_OK) + continue; + + found = 0; + plineaddr = ~0ULL; + plineno = 0; + pfile = NULL; + for (i = 0; i < lcount; i++) { + if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) + continue; + if (dwarf_lineno(lbuf[i], &lineno, &de)) + continue; + if (dwarf_linesrc(lbuf[i], &file, &de)) + continue; + if (off == lineaddr) { + found = 1; + goto out; + } else if (off < lineaddr && off > plineaddr) { + lineno = plineno; + file = pfile; + found = 1; + goto out; + } + plineaddr = lineaddr; + plineno = lineno; + pfile = file; + } + } + +out: + if (found) + printf(", file: %s, line: %ju", file, lineno); + + /* + * Reset internal CU pointer, so we will start from the first CU + * next round. + */ + while (ret != DW_DLV_NO_ENTRY) { + if (ret == DW_DLV_ERROR) + break; + ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, + &de); + } + +done: + putchar('\n'); +} + +static void +examine_reloc(const char *fn, Elf *e, Elf_Data *d, GElf_Shdr *sh, GElf_Phdr *ph, + int phnum, Dwarf_Debug dbg, int *textrel) +{ + GElf_Rel rel; + GElf_Rela rela; + int i, j, len; + + len = d->d_size / sh->sh_entsize; + for (i = 0; i < len; i++) { + if (sh->sh_type == SHT_REL) { + if (gelf_getrel(d, i, &rel) != &rel) { + warnx("%s: gelf_getrel() failed: %s", fn, + elf_errmsg(-1)); + continue; + } + } else { + if (gelf_getrela(d, i, &rela) != &rela) { + warnx("%s: gelf_getrela() failed: %s", fn, + elf_errmsg(-1)); + continue; + } + } + for (j = 0; j < phnum; j++) { + if (sh->sh_type == SHT_REL) { + if (rel.r_offset >= ph[j].p_offset && + rel.r_offset < ph[j].p_offset + + ph[j].p_filesz) + report_textrel(fn, e, dbg, + (uintmax_t) rel.r_offset, textrel); + } else { + if (rela.r_offset >= ph[j].p_offset && + rela.r_offset < ph[j].p_offset + + ph[j].p_filesz) + report_textrel(fn, e, dbg, + (uintmax_t) rela.r_offset, textrel); + } + } + } +} + +static void +find_textrel(const char *fn) +{ + Elf *e; + Elf_Scn *scn; + Elf_Data *d; + GElf_Ehdr eh; + GElf_Phdr *ph; + GElf_Shdr sh; + Dwarf_Debug dbg; + Dwarf_Error de; + int elferr, fd, i, phnum, textrel; + + e = NULL; + ph = NULL; + dbg = NULL; + + if ((fd = open(fn, O_RDONLY)) < 0) { + warn("%s", fn); + return; + } + + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warnx("%s: elf_begin() failed: %s", fn, elf_errmsg(-1)); + goto exit; + } + + if (gelf_getehdr(e, &eh) != &eh) { + warnx("%s: gelf_getehdr() failed: %s", fn, elf_errmsg(-1)); + goto exit; + } + + if (eh.e_type != ET_DYN) { + printf("%s: ELF object is not a DSO/PIE\n", fn); + goto exit; + } + + /* + * Search program header for executable segments. + */ + + if (eh.e_phnum == 0) { + printf("%s: ELF object does not contain program headers\n", + fn); + goto exit; + } + if ((ph = calloc(eh.e_phnum, sizeof(GElf_Phdr))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + phnum = 0; + for (i = 0; (unsigned) i < eh.e_phnum; i++) { + if (gelf_getphdr(e, i, &ph[phnum]) != &ph[phnum]) { + warnx("%s: gelf_getphdr() failed: %s", fn, + elf_errmsg(-1)); + continue; + } + if (ph[phnum].p_flags & PF_X) + phnum++; + } + if (phnum == 0) { + printf("%s: ELF object does not contain any executable " + "segment\n", fn); + goto exit; + } + + /* Check if debugging information is available. */ + if (dwarf_elf_init(e, DW_DLC_READ, NULL, NULL, &dbg, &de)) + dbg = NULL; + + /* + * Search relocation records for possible text relocations. + */ + textrel = 0; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("%s: gelf_getshdr() failed: %s", fn, + elf_errmsg(-1)); + continue; + } + if (sh.sh_type == SHT_REL || sh.sh_type == SHT_RELA) { + (void) elf_errno(); + if ((d = elf_getdata(scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("%s: elf_getdata() failed: %s", + fn, elf_errmsg(-1)); + continue; + } + if (d->d_size <= 0) + continue; + examine_reloc(fn, e, d, &sh, ph, phnum, dbg, &textrel); + } + } + elferr = elf_errno(); + if (elferr != 0) + warnx("%s: elf_nextscn() failed: %s", fn, elf_errmsg(elferr)); + + if (!textrel) + printf("%s: ELF object does not contain a text relocation\n", + fn); + +exit: + if (dbg) + dwarf_finish(dbg, &de); + if (ph) + free(ph); + if (e) + (void) elf_end(e); + close(fd); +} + +int +main(int argc, char **argv) +{ + int i, opt; + + if (elf_version(EV_CURRENT) == EV_NONE) + errx(EXIT_FAILURE, "elf_version(): %s", elf_errmsg(-1)); + + while ((opt = getopt_long(argc, argv, "HV", longopts, NULL)) != -1) { + switch (opt) { + case 'H': + usage(); + case 'V': + version(); + default: + usage(); + } + } + + argv += optind; + argc -= optind; + + if (argc > 0) + for (i = 0; i < argc; i++) + find_textrel(argv[i]); + else + find_textrel("a.out"); + + exit(0); +} |