diff options
Diffstat (limited to 'src/tests/addrcfi.c')
-rw-r--r-- | src/tests/addrcfi.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/src/tests/addrcfi.c b/src/tests/addrcfi.c new file mode 100644 index 00000000..8a2d126c --- /dev/null +++ b/src/tests/addrcfi.c @@ -0,0 +1,226 @@ +/* Test program for CFI handling. + Copyright (C) 2009-2010, 2013 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 ELFUTILS_HEADER(dwfl) +#include <dwarf.h> +#include <argp.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include "../libdw/known-dwarf.h" + +static const char * +op_name (unsigned int code) +{ + static const char *const known[] = + { +#define ONE_KNOWN_DW_OP_DESC(NAME, CODE, DESC) ONE_KNOWN_DW_OP (NAME, CODE) +#define ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME, + ALL_KNOWN_DW_OP +#undef ONE_KNOWN_DW_OP +#undef ONE_KNOWN_DW_OP_DESC + }; + + 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[2]; + 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; + return (handle_cfi (dwfl, ".eh_frame", + dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff) + & handle_cfi (dwfl, ".debug_frame", + dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff)); +} + +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; +} |