summaryrefslogtreecommitdiff
path: root/src/addr2line.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2005-07-26 05:00:05 +0000
committerUlrich Drepper <drepper@redhat.com>2005-07-26 05:00:05 +0000
commitb08d5a8fb42f4586d756068065186b5af7e48dad (patch)
tree9f05f86be7877ed461b4dc05f53b29ea4fc0d2a1 /src/addr2line.c
downloadelfutils-b08d5a8fb42f4586d756068065186b5af7e48dad.tar.gz
Adjust for monotone.
Diffstat (limited to 'src/addr2line.c')
-rw-r--r--src/addr2line.c372
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");
+}