diff options
author | Ulrich Drepper <drepper@redhat.com> | 2005-07-26 05:00:05 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2005-07-26 05:00:05 +0000 |
commit | b08d5a8fb42f4586d756068065186b5af7e48dad (patch) | |
tree | 9f05f86be7877ed461b4dc05f53b29ea4fc0d2a1 /src/addr2line.c | |
download | elfutils-b08d5a8fb42f4586d756068065186b5af7e48dad.tar.gz |
Adjust for monotone.
Diffstat (limited to 'src/addr2line.c')
-rw-r--r-- | src/addr2line.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/src/addr2line.c b/src/addr2line.c new file mode 100644 index 00000000..60fcdf63 --- /dev/null +++ b/src/addr2line.c @@ -0,0 +1,372 @@ +/* Locate source files and line information for given addresses + Copyright (C) 2005 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <inttypes.h> +#include <libdw.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the parameters which have no short form. */ +#define OPT_DEMANGLER 0x100 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Input Selection:"), 0 }, + { "exe", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 }, + + { NULL, 0, NULL, 0, N_("Output Selection:"), 0 }, + { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 }, + { "functions", 'f', NULL, 0, N_("Additional show function names"), 0 }, + + { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, + /* Unsupported options. */ + { "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 }, + { "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 }, + { "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +Locate source files and line information for ADDRs (in a.out by default)."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[ADDR...]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, NULL, NULL +}; + + +/* Handle ADDR. */ +static void handle_address (GElf_Addr addr, Elf *elf, Dwarf *dw); + + +/* Name of the executable. */ +static const char *executable = "a.out"; + +/* True if only base names of files should be shown. */ +static bool only_basenames; + +/* True if function names should be shown. */ +static bool show_functions; + + +int +main (int argc, char *argv[]) +{ + int remaining; + int result = 0; + + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE); + + /* Parse and process arguments. */ + (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + elf_version (EV_CURRENT); + + /* Open the file. */ + int fd = open64 (executable, O_RDONLY); + if (fd == -1) + error (1, errno, gettext ("cannot open '%s'"), executable); + + /* Create the ELF descriptor. */ + Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + { + close (fd); + error (1, 0, gettext ("cannot create ELF descriptor: %s"), + elf_errmsg (-1)); + } + + /* Try to get a DWARF descriptor. If it fails, we try to locate the + debuginfo file. */ + Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL); + int fd2 = -1; + Elf *elf2 = NULL; + if (dw == NULL) + { + char *canon = canonicalize_file_name (executable); + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + + if (canon != NULL && ehdr != NULL) + { + const char *debuginfo_dir; + if (ehdr->e_ident[EI_CLASS] == ELFCLASS32 + || ehdr->e_machine == EM_IA_64 || ehdr->e_machine == EM_ALPHA) + debuginfo_dir = "/usr/lib/debug"; + else + debuginfo_dir = "/usr/lib64/debug"; + + char *difname = alloca (strlen (debuginfo_dir) + strlen (canon) + 1); + strcpy (stpcpy (difname, debuginfo_dir), canon); + fd2 = open64 (difname, O_RDONLY); + if (fd2 != -1) + dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL); + } + + free (canon); + } + + /* Now handle the addresses. In case none are given on the command + line, read from stdin. */ + if (remaining == argc) + { + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + + char *buf = NULL; + size_t len = 0; + while (!feof_unlocked (stdin)) + { + if (getline (&buf, &len, stdin) < 0) + break; + + char *endp; + uintmax_t addr = strtoumax (buf, &endp, 0); + if (endp != buf) + handle_address (addr, elf2 ?: elf, dw); + else + result = 1; + } + + free (buf); + } + else + { + do + { + char *endp; + uintmax_t addr = strtoumax (argv[remaining], &endp, 0); + if (endp != argv[remaining]) + handle_address (addr, elf2 ?: elf, dw); + else + result = 1; + } + while (++remaining < argc); + } + + return result; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "addr2line (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'b': + case 'C': + case OPT_DEMANGLER: + /* Ignored for compatibility. */ + break; + + case 'e': + executable = arg; + break; + + case 's': + only_basenames = true; + break; + + case 'f': + show_functions = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +struct func_arg +{ + GElf_Addr addr; + const char *name; +}; + + +static int +match_func (Dwarf_Func *func, void *arg) +{ + struct func_arg *func_arg = (struct func_arg *) arg; + Dwarf_Addr addr; + + if (dwarf_func_lowpc (func, &addr) == 0 && addr <= func_arg->addr + && dwarf_func_highpc (func, &addr) == 0 && func_arg->addr < addr) + { + func_arg->name = dwarf_func_name (func); + return DWARF_CB_ABORT; + } + + return DWARF_CB_OK; +} + + +static const char * +elf_getname (GElf_Addr addr, Elf *elf) +{ + /* The DWARF information is not available. Use the ELF + symbol table. */ + Elf_Scn *scn = NULL; + Elf_Scn *dynscn = NULL; + + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL) + { + if (shdr->sh_type == SHT_SYMTAB) + break; + if (shdr->sh_type == SHT_DYNSYM) + dynscn = scn; + } + } + if (scn == NULL) + scn = dynscn; + + if (scn != NULL) + { + /* Look through the symbol table for a matching symbol. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr != NULL); + + Elf_Data *data = elf_getdata (scn, NULL); + if (data != NULL) + for (int cnt = 1; cnt < (int) (shdr->sh_size / shdr->sh_entsize); + ++cnt) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (data, cnt, &sym_mem); + if (sym != NULL + && sym->st_value <= addr + && addr < sym->st_value + sym->st_size) + return elf_strptr (elf, shdr->sh_link, sym->st_name); + } + } + + return NULL; +} + + +static void +handle_address (GElf_Addr addr, Elf *elf, Dwarf *dw) +{ + Dwarf_Die die_mem; + Dwarf_Die *die = dwarf_addrdie (dw, addr, &die_mem); + + if (show_functions) + { + /* First determine the function name. Use the DWARF information if + possible. */ + struct func_arg arg; + arg.addr = addr; + arg.name = NULL; + + if (dwarf_getfuncs (die, match_func, &arg, 0) <= 0) + arg.name = elf_getname (addr, elf); + + puts (arg.name ?: "??"); + } + + + Dwarf_Line *line; + const char *src; + if ((line = dwarf_getsrc_die (die, addr)) != NULL + && (src = dwarf_linesrc (line, NULL, NULL)) != NULL) + { + if (only_basenames) + src = basename (src); + + int lineno; + if (dwarf_lineno (line, &lineno) != -1) + { + int linecol; + if (dwarf_linecol (line, &linecol) != -1 && linecol != 0) + printf ("%s:%d:%d\n", src, lineno, linecol); + else + printf ("%s:%d\n", src, lineno); + } + else + printf ("%s:0\n", src); + } + else + puts ("??:0"); +} |