/* Locate source files and line information for given addresses Copyright (C) 2005 Red Hat, Inc. Written by Ulrich Drepper , 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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_("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); static struct argp_child argp_children[2]; /* [0] is set in main. */ /* Data structure to communicate with argp functions. */ static const struct argp argp = { options, parse_opt, args_doc, doc, argp_children, NULL, NULL }; /* Handle ADDR. */ static void handle_address (GElf_Addr addr, Dwfl *dwfl); /* 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. This includes opening the modules. */ argp_children[0].argp = dwfl_standard_argp (); Dwfl *dwfl = NULL; (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl); assert (dwfl != NULL); /* 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, dwfl); else result = 1; } free (buf); } else { do { char *endp; uintmax_t addr = strtoumax (argv[remaining], &endp, 0); if (endp != argv[remaining]) handle_address (addr, dwfl); 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 __attribute__ ((unused)), struct argp_state *state) { switch (key) { case ARGP_KEY_INIT: state->child_inputs[0] = state->input; break; case 'b': case 'C': case OPT_DEMANGLER: /* Ignored for compatibility. */ break; case 's': only_basenames = true; break; case 'f': show_functions = true; break; default: return ARGP_ERR_UNKNOWN; } return 0; } static bool print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) { Dwarf_Addr bias = 0; Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias); Dwarf_Die *scopes; int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes); if (nscopes <= 0) return false; for (int i = 0; i < nscopes; ++i) switch (dwarf_tag (&scopes[i])) { case DW_TAG_subprogram: { const char *name = dwarf_diename (&scopes[i]); if (name == NULL) return false; puts (name); return true; } case DW_TAG_inlined_subroutine: { const char *name = dwarf_diename (&scopes[i]); if (name == NULL) return false; printf ("%s inlined", name); Dwarf_Files *files; if (dwarf_getsrcfiles (cudie, &files, NULL) == 0) { Dwarf_Attribute attr_mem; Dwarf_Word val; if (dwarf_formudata (dwarf_attr (&scopes[i], DW_AT_call_file, &attr_mem), &val) == 0) { const char *file = dwarf_filesrc (files, val, NULL, NULL); int lineno = 0, colno = 0; if (dwarf_formudata (dwarf_attr (&scopes[i], DW_AT_call_line, &attr_mem), &val) == 0) lineno = val; if (dwarf_formudata (dwarf_attr (&scopes[i], DW_AT_call_column, &attr_mem), &val) == 0) colno = val; if (lineno == 0) { if (file != NULL) printf (" from %s", file); } else if (colno == 0) printf (" at %s:%u", file, lineno); else printf (" at %s:%u:%u", file, lineno, colno); } } printf (" in "); continue; } } return false; } static void handle_address (GElf_Addr addr, Dwfl *dwfl) { Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr); if (show_functions) { /* First determine the function name. Use the DWARF information if possible. */ if (! print_dwarf_function (mod, addr)) puts (dwfl_module_addrname (mod, addr) ?: "??"); } Dwfl_Line *line = dwfl_module_getsrc (mod, addr); const char *src; int lineno, linecol; if (line != NULL && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol, NULL, NULL)) != NULL) { if (only_basenames) src = basename (src); if (linecol != 0) printf ("%s:%d:%d\n", src, lineno, linecol); else printf ("%s:%d\n", src, lineno); } else puts ("??:0"); }