| /* Test program for libdwfl basic module tracking, relocation. |
| Copyright (C) 2005, 2007 Red Hat, Inc. |
| This file is part of elfutils. |
| |
| This file is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| elfutils is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include <config.h> |
| #include <assert.h> |
| #include <inttypes.h> |
| #include <sys/types.h> |
| #include <stdio.h> |
| #include <stdio_ext.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <error.h> |
| #include <locale.h> |
| #include <argp.h> |
| #include ELFUTILS_HEADER(dwfl) |
| #include <dwarf.h> |
| |
| static bool show_inlines; |
| |
| struct info |
| { |
| Dwarf_Die *cudie; |
| Dwarf_Addr dwbias; |
| }; |
| |
| static int |
| print_instance (Dwarf_Die *instance, void *arg) |
| { |
| const struct info *info = arg; |
| |
| printf (" inlined"); |
| |
| Dwarf_Files *files; |
| if (dwarf_getsrcfiles (info->cudie, &files, NULL) == 0) |
| { |
| Dwarf_Attribute attr_mem; |
| Dwarf_Word val; |
| if (dwarf_formudata (dwarf_attr (instance, 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 (instance, DW_AT_call_line, |
| &attr_mem), &val) == 0) |
| lineno = val; |
| if (dwarf_formudata (dwarf_attr (instance, 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); |
| } |
| } |
| |
| Dwarf_Addr lo = -1, hi = -1, entry = -1; |
| if (dwarf_lowpc (instance, &lo) == 0) |
| lo += info->dwbias; |
| else |
| printf (" (lowpc => %s)", dwarf_errmsg (-1)); |
| if (dwarf_highpc (instance, &hi) == 0) |
| hi += info->dwbias; |
| else |
| printf (" (highpc => %s)", dwarf_errmsg (-1)); |
| |
| Dwarf_Attribute attr_mem; |
| Dwarf_Attribute *attr = dwarf_attr (instance, DW_AT_entry_pc, &attr_mem); |
| if (attr != NULL) |
| { |
| if (dwarf_formaddr (attr, &entry) == 0) |
| entry += info->dwbias; |
| else |
| printf (" (entrypc => %s)", dwarf_errmsg (-1)); |
| } |
| |
| if (lo != (Dwarf_Addr) -1 || hi != (Dwarf_Addr) -1) |
| printf (" %#" PRIx64 "..%#" PRIx64, lo, hi); |
| if (entry != (Dwarf_Addr) -1) |
| printf (" => %#" PRIx64 "\n", entry); |
| else |
| puts (""); |
| |
| return DWARF_CB_OK; |
| } |
| |
| static void |
| print_inline (Dwarf_Die *func, void *arg) |
| { |
| if (dwarf_func_inline_instances (func, &print_instance, arg) != 0) |
| printf (" error finding instances: %s\n", dwarf_errmsg (-1)); |
| } |
| |
| static int |
| print_func (Dwarf_Die *func, void *arg) |
| { |
| const struct info *info = arg; |
| |
| const char *file = dwarf_decl_file (func); |
| int line = -1; |
| dwarf_decl_line (func, &line); |
| const char *fct = dwarf_diename (func); |
| |
| printf (" %s:%d: %s:", file, line, fct); |
| |
| if (dwarf_func_inline (func)) |
| { |
| puts (" inline function"); |
| if (show_inlines) |
| print_inline (func, arg); |
| } |
| else |
| { |
| Dwarf_Addr lo = -1, hi = -1, entry = -1; |
| if (dwarf_lowpc (func, &lo) == 0) |
| lo += info->dwbias; |
| else |
| printf (" (lowpc => %s)", dwarf_errmsg (-1)); |
| if (dwarf_highpc (func, &hi) == 0) |
| hi += info->dwbias; |
| else |
| printf (" (highpc => %s)", dwarf_errmsg (-1)); |
| if (dwarf_entrypc (func, &entry) == 0) |
| entry += info->dwbias; |
| else |
| printf (" (entrypc => %s)", dwarf_errmsg (-1)); |
| |
| if (lo != (Dwarf_Addr) -1 || hi != (Dwarf_Addr) -1 |
| || entry != (Dwarf_Addr) -1) |
| printf (" %#" PRIx64 "..%#" PRIx64 " => %#" PRIx64 "\n", |
| lo, hi, entry); |
| else |
| puts (""); |
| } |
| |
| return DWARF_CB_OK; |
| } |
| |
| static int |
| list_module (Dwfl_Module *mod __attribute__ ((unused)), |
| void **userdata __attribute__ ((unused)), |
| const char *name, Dwarf_Addr base, |
| void *arg __attribute__ ((unused))) |
| { |
| Dwarf_Addr start; |
| Dwarf_Addr end; |
| const char *file; |
| const char *debug; |
| if (dwfl_module_info (mod, NULL, &start, &end, |
| NULL, NULL, &file, &debug) != name |
| || start != base) |
| abort (); |
| printf ("module: %30s %08" PRIx64 "..%08" PRIx64 " %s %s\n", |
| name, start, end, file, debug); |
| return DWARF_CB_OK; |
| } |
| |
| static int |
| print_module (Dwfl_Module *mod __attribute__ ((unused)), |
| void **userdata __attribute__ ((unused)), |
| const char *name, Dwarf_Addr base, |
| Dwarf *dw, Dwarf_Addr bias, |
| void *arg) |
| { |
| printf ("module: %30s %08" PRIx64 " %s %" PRIx64 " (%s)\n", |
| name, base, dw == NULL ? "no" : "DWARF", bias, dwfl_errmsg (-1)); |
| |
| if (dw != NULL && *(const bool *) arg) |
| { |
| Dwarf_Off off = 0; |
| size_t cuhl; |
| Dwarf_Off noff; |
| |
| while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0) |
| { |
| Dwarf_Die die_mem; |
| struct info info = { dwarf_offdie (dw, off + cuhl, &die_mem), bias }; |
| (void) dwarf_getfuncs (info.cudie, print_func, &info, 0); |
| |
| off = noff; |
| } |
| } |
| |
| return DWARF_CB_OK; |
| } |
| |
| static bool show_functions; |
| |
| /* gettext helper macro. */ |
| #undef N_ |
| #define N_(Str) Str |
| |
| static const struct argp_option options[] = |
| { |
| { "functions", 'f', NULL, 0, N_("Additionally show function names"), 0 }, |
| { "inlines", 'i', NULL, 0, N_("Show instances of inlined functions"), 0 }, |
| { NULL, 0, NULL, 0, NULL, 0 } |
| }; |
| |
| static error_t |
| parse_opt (int key, char *arg __attribute__ ((unused)), |
| struct argp_state *state __attribute__ ((unused))) |
| { |
| switch (key) |
| { |
| case ARGP_KEY_INIT: |
| state->child_inputs[0] = state->input; |
| break; |
| |
| case 'f': |
| show_functions = true; |
| break; |
| |
| case 'i': |
| show_inlines = show_functions = true; |
| break; |
| |
| default: |
| return ARGP_ERR_UNKNOWN; |
| } |
| return 0; |
| } |
| |
| int |
| main (int argc, char **argv) |
| { |
| /* We use no threads here which can interfere with handling a stream. */ |
| (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); |
| |
| /* Set locale. */ |
| (void) setlocale (LC_ALL, ""); |
| |
| Dwfl *dwfl = NULL; |
| const struct argp_child argp_children[] = |
| { |
| { .argp = dwfl_standard_argp () }, |
| { .argp = NULL } |
| }; |
| const struct argp argp = |
| { |
| options, parse_opt, NULL, NULL, argp_children, NULL, NULL |
| }; |
| (void) argp_parse (&argp, argc, argv, 0, NULL, &dwfl); |
| assert (dwfl != NULL); |
| |
| ptrdiff_t p = 0; |
| do |
| p = dwfl_getmodules (dwfl, &list_module, NULL, p); |
| while (p > 0); |
| if (p < 0) |
| error (2, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1)); |
| |
| do |
| p = dwfl_getdwarf (dwfl, &print_module, &show_functions, p); |
| while (p > 0); |
| if (p < 0) |
| error (2, 0, "dwfl_getdwarf: %s", dwfl_errmsg (-1)); |
| |
| p = 0; |
| do |
| p = dwfl_getmodules (dwfl, &list_module, NULL, p); |
| while (p > 0); |
| if (p < 0) |
| error (2, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1)); |
| |
| dwfl_end (dwfl); |
| |
| return 0; |
| } |