/* Test program for CFI handling. Copyright (C) 2009-2010, 2013, 2015 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 . */ #include #include #include #include ELFUTILS_HEADER(dwfl) #include #include #include #include #include #include #include #include "../libdw/known-dwarf.h" static const char * op_name (unsigned int code) { static const char *const known[] = { #define DWARF_ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME, DWARF_ALL_KNOWN_DW_OP #undef DWARF_ONE_KNOWN_DW_OP }; if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return NULL; } static void print_detail (int result, const Dwarf_Op *ops, size_t nops, Dwarf_Addr bias) { if (result < 0) printf ("indeterminate (%s)\n", dwarf_errmsg (-1)); else if (nops == 0) printf ("%s\n", ops == NULL ? "same_value" : "undefined"); else { printf ("%s expression:", result == 0 ? "location" : "value"); for (size_t i = 0; i < nops; ++i) { printf (" %s", op_name(ops[i].atom)); if (ops[i].number2 == 0) { if (ops[i].atom == DW_OP_addr) printf ("(%#" PRIx64 ")", ops[i].number + bias); else if (ops[i].number != 0) printf ("(%" PRId64 ")", ops[i].number); } else printf ("(%" PRId64 ",%" PRId64 ")", ops[i].number, ops[i].number2); } puts (""); } } struct stuff { Dwarf_Frame *frame; Dwarf_Addr bias; }; static int print_register (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits __attribute__ ((unused)), int type __attribute__ ((unused))) { struct stuff *stuff = arg; printf ("\t%s reg%u (%s%s): ", setname, regno, prefix, regname); Dwarf_Op ops_mem[3]; Dwarf_Op *ops; size_t nops; int result = dwarf_frame_register (stuff->frame, regno, ops_mem, &ops, &nops); print_detail (result, ops, nops, stuff->bias); return DWARF_CB_OK; } static int handle_cfi (Dwfl *dwfl, const char *which, Dwarf_CFI *cfi, GElf_Addr pc, struct stuff *stuff) { if (cfi == NULL) { printf ("handle_cfi no CFI (%s): %s\n", which, dwarf_errmsg (-1)); return -1; } int result = dwarf_cfi_addrframe (cfi, pc - stuff->bias, &stuff->frame); if (result != 0) { printf ("dwarf_cfi_addrframe (%s): %s\n", which, dwarf_errmsg (-1)); return 1; } Dwarf_Addr start = pc; Dwarf_Addr end = pc; bool signalp; int ra_regno = dwarf_frame_info (stuff->frame, &start, &end, &signalp); if (ra_regno >= 0) { start += stuff->bias; end += stuff->bias; } printf ("%s has %#" PRIx64 " => [%#" PRIx64 ", %#" PRIx64 "):\n", which, pc, start, end); if (ra_regno < 0) printf ("\treturn address register unavailable (%s)\n", dwarf_errmsg (0)); else printf ("\treturn address in reg%u%s\n", ra_regno, signalp ? " (signal frame)" : ""); // Point cfa_ops to dummy to match print_detail expectations. // (nops == 0 && cfa_ops != NULL => "undefined") Dwarf_Op dummy; Dwarf_Op *cfa_ops = &dummy; size_t cfa_nops; result = dwarf_frame_cfa (stuff->frame, &cfa_ops, &cfa_nops); printf ("\tCFA "); print_detail (result, cfa_ops, cfa_nops, stuff->bias); (void) dwfl_module_register_names (dwfl_addrmodule (dwfl, pc), &print_register, stuff); return 0; } static int handle_address (GElf_Addr pc, Dwfl *dwfl) { Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc); struct stuff stuff; stuff.frame = NULL; stuff.bias = 0; int res = handle_cfi (dwfl, ".eh_frame", dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff); free (stuff.frame); stuff.frame = NULL; stuff.bias = 0; res &= handle_cfi (dwfl, ".debug_frame", dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff); free (stuff.frame); return res; } int main (int argc, char *argv[]) { int remaining; /* Set locale. */ (void) setlocale (LC_ALL, ""); Dwfl *dwfl = NULL; (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &dwfl); assert (dwfl != NULL); int result = 0; /* 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) result |= 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]) result |= handle_address (addr, dwfl); else result = 1; } while (++remaining < argc); } dwfl_end (dwfl); return result; }