From d5784afaf49cd59b9286e766cd9b1cf00cb43553 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Thu, 18 Oct 2012 00:11:30 +0200 Subject: [PATCH] libdwfl/ * argp-std.c: Update Copyright year. (offline_find_elf): New function. (offline_callbacks): Use it for find_elf. (struct parse_opt): New. (parse_opt): New key ARGP_KEY_INIT. In other make hook struct parse_opt pointer from former Dwfl pointer. Delay 'e and OPT_COREFILE processing till ARGP_KEY_SUCCESS. Initialize state->input already from ARGP_KEY_SUCCESS. Modify the cleanup in ARGP_KEY_ERROR. Make the final state->input initialization optional. * dwfl_end.c: Update Copyright year. (dwfl_end): Free executable_for_core. * libdwflP.h: Update Copyright year. (struct Dwfl): New field executable_for_core. tests/ * run-addrname-test.sh: New test for PIE relocation. * testfile70.core.bz2: New file. * testfile70.exec.bz2: New file. * Makefile.am (EXTRA_DIST): Add testfile70.core.bz2 and testfile70.exec.bz2 . Signed-off-by: Jan Kratochvil --- libdwfl/ChangeLog | 16 ++ libdwfl/argp-std.c | 289 ++++++++++++++++++++++++------------- libdwfl/dwfl_end.c | 3 +- libdwfl/libdwflP.h | 4 +- tests/ChangeLog | 8 + tests/Makefile.am | 3 +- tests/run-addrname-test.sh | 10 ++ tests/testfile70.core.bz2 | Bin 0 -> 2739 bytes tests/testfile70.exec.bz2 | Bin 0 -> 2567 bytes 9 files changed, 226 insertions(+), 107 deletions(-) create mode 100644 tests/testfile70.core.bz2 create mode 100644 tests/testfile70.exec.bz2 diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 767334625..bea55cfb7 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,19 @@ +2012-11-29 Jan Kratochvil + + * argp-std.c: Update Copyright year. + (offline_find_elf): New function. + (offline_callbacks): Use it for find_elf. + (struct parse_opt): New. + (parse_opt): New key ARGP_KEY_INIT. In other make hook struct + parse_opt pointer from former Dwfl pointer. Delay 'e and OPT_COREFILE + processing till ARGP_KEY_SUCCESS. Initialize state->input already from + ARGP_KEY_SUCCESS. Modify the cleanup in ARGP_KEY_ERROR. Make the + final state->input initialization optional. + * dwfl_end.c: Update Copyright year. + (dwfl_end): Free executable_for_core. + * libdwflP.h: Update Copyright year. + (struct Dwfl): New field executable_for_core. + 2012-11-20 Jan Kratochvil * dwfl_report_elf.c (__libdwfl_report_elf): Simplify START and BIAS diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c index 2ef4555cc..7584054af 100644 --- a/libdwfl/argp-std.c +++ b/libdwfl/argp-std.c @@ -1,5 +1,5 @@ /* Standard argp argument parsers for tools using libdwfl. - Copyright (C) 2005-2010 Red Hat, Inc. + Copyright (C) 2005-2010, 2012 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -60,6 +60,32 @@ static const struct argp_option options[] = { NULL, 0, NULL, 0, NULL, 0 } }; +/* Wrapper to provide proper FILE_NAME for -e|--executable. */ +static int +offline_find_elf (Dwfl_Module *mod, void **userdata, const char *modname, + Dwarf_Addr base, char **file_name, Elf **elfp) +{ + if (modname != NULL && (strcmp (modname, "[exe]") == 0 + || strcmp (modname, "[pie]") == 0) + && mod->dwfl->executable_for_core) + { + /* When both --core and --executable are given in whatever order + dwfl_core_file_report is called first and this callback will replace + the Dwfl_Module main.name with the recorded --executable file when the + modname is [exe] or [pie] (which then triggers opening and reporting + of the executable). */ + char *e_dup = strdup (mod->dwfl->executable_for_core); + if (e_dup) + { + free (*file_name); + *file_name = e_dup; + return -1; + } + } + return INTUSE(dwfl_build_id_find_elf) (mod, userdata, modname, base, + file_name, elfp); +} + static char *debuginfo_path; static const Dwfl_Callbacks offline_callbacks = @@ -70,7 +96,7 @@ static const Dwfl_Callbacks offline_callbacks = .section_address = INTUSE(dwfl_offline_section_address), /* We use this table for core files too. */ - .find_elf = INTUSE(dwfl_build_id_find_elf), + .find_elf = offline_find_elf, }; static const Dwfl_Callbacks proc_callbacks = @@ -90,6 +116,16 @@ static const Dwfl_Callbacks kernel_callbacks = .section_address = INTUSE(dwfl_linux_kernel_module_section_address), }; +/* Structure held at state->HOOK. */ +struct parse_opt +{ + Dwfl *dwfl; + /* The -e|--executable parameter. */ + const char *e; + /* The --core parameter. */ + const char *core; +}; + static error_t parse_opt (int key, char *arg, struct argp_state *state) { @@ -111,152 +147,142 @@ parse_opt (int key, char *arg, struct argp_state *state) switch (key) { + case ARGP_KEY_INIT: + { + assert (state->hook == NULL); + struct parse_opt *opt = calloc (1, sizeof (*opt)); + if (opt == NULL) + failure (NULL, DWFL_E_ERRNO, "calloc"); + state->hook = opt; + } + break; + case OPT_DEBUGINFO: debuginfo_path = arg; break; case 'e': { - Dwfl *dwfl = state->hook; + struct parse_opt *opt = state->hook; + Dwfl *dwfl = opt->dwfl; if (dwfl == NULL) { dwfl = INTUSE(dwfl_begin) (&offline_callbacks); if (dwfl == NULL) return fail (dwfl, -1, arg); - state->hook = dwfl; + opt->dwfl = dwfl; /* Start at zero so if there is just one -e foo.so, the DSO is shown without address bias. */ dwfl->offline_next_address = 0; } - if (dwfl->callbacks == &offline_callbacks) - { - if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL) - return fail (dwfl, -1, arg); - state->hook = dwfl; - } - else + if (dwfl->callbacks != &offline_callbacks) { toomany: argp_error (state, "%s", _("only one of -e, -p, -k, -K, or --core allowed")); return EINVAL; } + opt->e = arg; } break; case 'p': - if (state->hook == NULL) - { - Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks); - int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg)); - if (result != 0) - return fail (dwfl, result, arg); - state->hook = dwfl; - } - else - goto toomany; + { + struct parse_opt *opt = state->hook; + if (opt->dwfl == NULL) + { + Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks); + int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg)); + if (result != 0) + return fail (dwfl, result, arg); + opt->dwfl = dwfl; + } + else + goto toomany; + } break; case 'M': - if (state->hook == NULL) - { - FILE *f = fopen (arg, "r"); - if (f == NULL) - nofile: - { - int code = errno; - argp_failure (state, EXIT_FAILURE, code, - "cannot open '%s'", arg); - return code; - } - Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks); - int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f); - fclose (f); - if (result != 0) - return fail (dwfl, result, arg); - state->hook = dwfl; - } - else - goto toomany; + { + struct parse_opt *opt = state->hook; + if (opt->dwfl == NULL) + { + FILE *f = fopen (arg, "r"); + if (f == NULL) + nofile: + { + int code = errno; + argp_failure (state, EXIT_FAILURE, code, + "cannot open '%s'", arg); + return code; + } + Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks); + int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f); + fclose (f); + if (result != 0) + return fail (dwfl, result, arg); + opt->dwfl = dwfl; + } + else + goto toomany; + } break; case OPT_COREFILE: { - Dwfl *dwfl = state->hook; + struct parse_opt *opt = state->hook; + Dwfl *dwfl = opt->dwfl; if (dwfl == NULL) - state->hook = dwfl = INTUSE(dwfl_begin) (&offline_callbacks); + opt->dwfl = dwfl = INTUSE(dwfl_begin) (&offline_callbacks); /* Permit -e and --core together. */ else if (dwfl->callbacks != &offline_callbacks) goto toomany; - - int fd = open64 (arg, O_RDONLY); - if (fd < 0) - goto nofile; - - Elf *core; - Dwfl_Error error = __libdw_open_file (&fd, &core, true, false); - if (error != DWFL_E_NOERROR) - { - argp_failure (state, EXIT_FAILURE, 0, - _("cannot read ELF core file: %s"), - INTUSE(dwfl_errmsg) (error)); - return error == DWFL_E_ERRNO ? errno : EIO; - } - - int result = INTUSE(dwfl_core_file_report) (dwfl, core); - if (result < 0) - { - elf_end (core); - close (fd); - return fail (dwfl, result, arg); - } - - /* From now we leak FD and CORE. */ - - if (result == 0) - { - argp_failure (state, EXIT_FAILURE, 0, - _("No modules recognized in core file")); - return ENOENT; - } + opt->core = arg; } break; case 'k': - if (state->hook == NULL) - { - Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks); - int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl); - if (result != 0) - return fail (dwfl, result, _("cannot load kernel symbols")); - result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl); - if (result != 0) - /* Non-fatal to have no modules since we do have the kernel. */ - failure (dwfl, result, _("cannot find kernel modules")); - state->hook = dwfl; - } - else - goto toomany; + { + struct parse_opt *opt = state->hook; + if (opt->dwfl == NULL) + { + Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks); + int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl); + if (result != 0) + return fail (dwfl, result, _("cannot load kernel symbols")); + result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl); + if (result != 0) + /* Non-fatal to have no modules since we do have the kernel. */ + failure (dwfl, result, _("cannot find kernel modules")); + opt->dwfl = dwfl; + } + else + goto toomany; + } break; case 'K': - if (state->hook == NULL) - { - Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks); - int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg, - NULL); - if (result != 0) - return fail (dwfl, result, _("cannot find kernel or modules")); - state->hook = dwfl; - } - else - goto toomany; + { + struct parse_opt *opt = state->hook; + if (opt->dwfl == NULL) + { + Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks); + int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg, + NULL); + if (result != 0) + return fail (dwfl, result, _("cannot find kernel or modules")); + opt->dwfl = dwfl; + } + else + goto toomany; + } break; case ARGP_KEY_SUCCESS: { - Dwfl *dwfl = state->hook; + struct parse_opt *opt = state->hook; + Dwfl *dwfl = opt->dwfl; if (dwfl == NULL) { @@ -265,7 +291,49 @@ parse_opt (int key, char *arg, struct argp_state *state) dwfl = INTUSE(dwfl_begin) (&offline_callbacks); if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL) return fail (dwfl, -1, arg); - state->hook = dwfl; + opt->dwfl = dwfl; + } + + if (opt->core) + { + int fd = open64 (opt->core, O_RDONLY); + if (fd < 0) + goto nofile; + + Elf *core; + Dwfl_Error error = __libdw_open_file (&fd, &core, true, false); + if (error != DWFL_E_NOERROR) + { + argp_failure (state, EXIT_FAILURE, 0, + _("cannot read ELF core file: %s"), + INTUSE(dwfl_errmsg) (error)); + return error == DWFL_E_ERRNO ? errno : EIO; + } + + int result = INTUSE(dwfl_core_file_report) (dwfl, core); + if (result < 0) + { + elf_end (core); + close (fd); + return fail (dwfl, result, opt->core); + } + + /* From now we leak FD and CORE. */ + + if (result == 0) + { + argp_failure (state, EXIT_FAILURE, 0, + _("No modules recognized in core file")); + return ENOENT; + } + + if (opt->e) + dwfl->executable_for_core = strdup (opt->e); + } + else if (opt->e) + { + if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL) + return fail (dwfl, -1, arg); } /* One of the three flavors has done dwfl_begin and some reporting @@ -274,12 +342,22 @@ parse_opt (int key, char *arg, struct argp_state *state) int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL); assert (result == 0); + + /* Update the input all along, so a parent parser can see it. + As we free OPT the update below will be no longer active. */ + *(Dwfl **) state->input = dwfl; + free (opt); + state->hook = NULL; } break; case ARGP_KEY_ERROR: - dwfl_end (state->hook); - state->hook = NULL; + { + struct parse_opt *opt = state->hook; + dwfl_end (opt->dwfl); + free (opt); + state->hook = NULL; + } break; default: @@ -287,7 +365,10 @@ parse_opt (int key, char *arg, struct argp_state *state) } /* Update the input all along, so a parent parser can see it. */ - *(Dwfl **) state->input = state->hook; + struct parse_opt *opt = state->hook; + if (opt) + *(Dwfl **) state->input = opt->dwfl; + return 0; } diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c index fd38e0fea..94fcfc6e9 100644 --- a/libdwfl/dwfl_end.c +++ b/libdwfl/dwfl_end.c @@ -1,5 +1,5 @@ /* Finish a session using libdwfl. - Copyright (C) 2005, 2008 Red Hat, Inc. + Copyright (C) 2005, 2008, 2012 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -46,5 +46,6 @@ dwfl_end (Dwfl *dwfl) __libdwfl_module_free (dead); } + free (dwfl->executable_for_core); free (dwfl); } diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index ca8be2f2a..806ebcd97 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwfl. - Copyright (C) 2005-2011 Red Hat, Inc. + Copyright (C) 2005-2012 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -108,6 +108,8 @@ struct Dwfl GElf_Off lookup_tail_vaddr; GElf_Off lookup_tail_offset; int lookup_tail_ndx; + + char *executable_for_core; /* --executable if --core was specified. */ }; #define OFFLINE_REDZONE 0x10000 diff --git a/tests/ChangeLog b/tests/ChangeLog index edb82b482..5b06b85f4 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +2012-11-29 Jan Kratochvil + + * run-addrname-test.sh: New test for PIE relocation. + * testfile70.core.bz2: New file. + * testfile70.exec.bz2: New file. + * Makefile.am (EXTRA_DIST): Add testfile70.core.bz2 and + testfile70.exec.bz2 . + 2012-10-27 Jan Kratochvil * Makefile.am (EXTRA_DIST): Add testfile64.bz2, testfile65.bz2, diff --git a/tests/Makefile.am b/tests/Makefile.am index f2d2484af..6808bd134 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -180,7 +180,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile60.bz2 testfile61.bz2 \ run-readelf-vmcoreinfo.sh testfile62.bz2 \ run-readelf-mixed-corenote.sh testfile63.bz2 testfile64.bz2 \ - testfile65.bz2 testfile69.core.bz2 testfile69.so.bz2 + testfile65.bz2 testfile69.core.bz2 testfile69.so.bz2 \ + testfile70.core.bz2 testfile70.exec.bz2 if USE_VALGRIND valgrind_cmd="valgrind -q --trace-children=yes --error-exitcode=1" diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh index cc8aa335a..99abf9dbc 100755 --- a/tests/run-addrname-test.sh +++ b/tests/run-addrname-test.sh @@ -306,4 +306,14 @@ libglobal+0x9 ??:0 EOF +testfiles testfile70.exec testfile70.core +testrun_compare ../src/addr2line -S -e testfile70.exec --core=testfile70.core 0x7ff2cfe9b6b5 <<\EOF +main+0x9 +??:0 +EOF +testrun_compare ../src/addr2line -S --core=testfile70.core -e testfile70.exec 0x7ff2cfe9b6b5 <<\EOF +main+0x9 +??:0 +EOF + exit 0 diff --git a/tests/testfile70.core.bz2 b/tests/testfile70.core.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..6c47c6d45b2ba29d6956cedc43d60c48e77e16be GIT binary patch literal 2739 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,KoM%$d1Xdb#tE4 z%wJ(yb%nC)EYr%nN~_0mxkipqL22BErQNf)d&QlRL7VH)1I%L*3{pS@R>(`rtj#4D zq$w0mu>?Tkl|(5LX_^x9w@49a3T?g?-q&nNC%{68ZlID>RNtSK0)#R6r~2f%F0!(c z-S%M4;e3c?b@*L|21ot>ldVQJ8dP)_XowsZcjllJW0`zTWH)nOYM^_7>g>FdAz0|b zjnJnO9lZEXBX_oq9X?+63%dTByNWs8;+PAd;xgR8|1i3eU@&b`YIFFKSoW}RAm91A dQ(%CARyC}#C++`O#!>&p+>uTcBo8B21%Ok5qx1j( literal 0 Hc-jL100001 -- 2.47.2