From 02d61ee510a4fab8b2860505e3a8fb3b32779670 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Wed, 10 Oct 2012 20:42:30 +0200 Subject: libdwfl/ linux-proc-maps.c: Include system.h. (PROCEXEFMT, get_pid_class): New. (grovel_auxv): Detect 32-bit vs. 64-bit auxv, possibly call get_pid_class. Signed-off-by: Jan Kratochvil --- libdwfl/ChangeLog | 7 +++ libdwfl/linux-proc-maps.c | 129 ++++++++++++++++++++++++++++++---------------- 2 files changed, 93 insertions(+), 43 deletions(-) diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index bdd9440f..824daf19 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,10 @@ +2012-10-27 Jan Kratochvil + + * linux-proc-maps.c: Include system.h. + (PROCEXEFMT, get_pid_class): New. + (grovel_auxv): Detect 32-bit vs. 64-bit auxv, possibly call + get_pid_class. + 2012-10-17 Jan Kratochvil * dwfl_module_getdwarf.c (mod_verify_build_id): New function with code diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c index 4fbe90d1..feed6a11 100644 --- a/libdwfl/linux-proc-maps.c +++ b/libdwfl/linux-proc-maps.c @@ -39,13 +39,42 @@ #include #include #include +#include "system.h" #define PROCMAPSFMT "/proc/%d/maps" #define PROCMEMFMT "/proc/%d/mem" #define PROCAUXVFMT "/proc/%d/auxv" +#define PROCEXEFMT "/proc/%d/exe" +/* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable. Return + ELFCLASSNONE for an error. */ + +static unsigned char +get_pid_class (pid_t pid) +{ + char *fname; + if (asprintf (&fname, PROCEXEFMT, pid) < 0) + return ELFCLASSNONE; + + int fd = open64 (fname, O_RDONLY); + free (fname); + if (fd < 0) + return ELFCLASSNONE; + + unsigned char buf[EI_CLASS + 1]; + ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0); + close (fd); + if (nread != sizeof (buf) || buf[EI_MAG0] != ELFMAG0 + || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2 + || buf[EI_MAG3] != ELFMAG3 + || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32)) + return ELFCLASSNONE; + + return buf[EI_CLASS]; +} + /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */ static int @@ -60,61 +89,75 @@ grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr) if (fd < 0) return errno == ENOENT ? 0 : errno; + GElf_Addr sysinfo_ehdr64 = 0, sysinfo_ehdr32 = 0; + GElf_Addr segment_align64 = dwfl->segment_align; + GElf_Addr segment_align32 = dwfl->segment_align; + off_t offset = 0; ssize_t nread; + union + { + Elf64_auxv_t a64[64]; + Elf32_auxv_t a32[128]; + } d; do { - union - { - char buffer[sizeof (long int) * 2 * 64]; - Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)]; - Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)]; - } d; - nread = read (fd, &d, sizeof d); - if (nread > 0) + eu_static_assert (sizeof d.a64 == sizeof d.a32); + nread = pread_retry (fd, d.a64, sizeof d.a64, offset); + if (nread < 0) + return errno; + for (unsigned a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++) { - switch (sizeof (long int)) - { - case 4: - for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i) - if (d.a32[i].a_type == AT_SYSINFO_EHDR) - { - *sysinfo_ehdr = d.a32[i].a_un.a_val; - if (dwfl->segment_align > 1) - { - nread = 0; - break; - } - } - else if (d.a32[i].a_type == AT_PAGESZ - && dwfl->segment_align <= 1) - dwfl->segment_align = d.a32[i].a_un.a_val; + const Elf32_auxv_t *a32 = d.a32 + a32i; + switch (a32->a_type) + { + case AT_SYSINFO_EHDR: + sysinfo_ehdr32 = a32->a_un.a_val; break; - case 8: - for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i) - if (d.a64[i].a_type == AT_SYSINFO_EHDR) - { - *sysinfo_ehdr = d.a64[i].a_un.a_val; - if (dwfl->segment_align > 1) - { - nread = 0; - break; - } - } - else if (d.a64[i].a_type == AT_PAGESZ - && dwfl->segment_align <= 1) - dwfl->segment_align = d.a64[i].a_un.a_val; + case AT_PAGESZ: + segment_align32 = a32->a_un.a_val; break; - default: - abort (); + } + } + for (unsigned a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++) + { + const Elf64_auxv_t *a64 = d.a64 + a64i; + switch (a64->a_type) + { + case AT_SYSINFO_EHDR: + sysinfo_ehdr64 = a64->a_un.a_val; break; - } + case AT_PAGESZ: + segment_align64 = a64->a_un.a_val; + break; + } } + offset += nread; } - while (nread > 0); + while (nread == sizeof d.a64); close (fd); - return nread < 0 ? errno : 0; + bool valid64 = sysinfo_ehdr64 || segment_align64 != dwfl->segment_align; + bool valid32 = sysinfo_ehdr32 || segment_align32 != dwfl->segment_align; + + if (! valid64 && ! valid32) + return 0; + + unsigned char pid_class = ELFCLASSNONE; + if (valid64 && valid32) + pid_class = get_pid_class (pid); + + if (pid_class == ELFCLASS64 || (valid64 && ! valid32)) + { + *sysinfo_ehdr = sysinfo_ehdr64; + dwfl->segment_align = segment_align64; + } + if (pid_class == ELFCLASS32 || (! valid64 && valid32)) + { + *sysinfo_ehdr = sysinfo_ehdr32; + dwfl->segment_align = segment_align32; + } + return 0; } static int -- cgit v1.2.1 From 660c3ba7790b30ec411798642ef5730df3db61d7 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Thu, 18 Oct 2012 00:11:30 +0200 Subject: libdwfl/ * argp-std.c (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. * libdwfl.h (dwfl_standard_argp): Extend the comment for USERDATA. 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 | 11 ++ libdwfl/argp-std.c | 289 +++++++++++++++++++++++++++++---------------- libdwfl/libdwfl.h | 3 +- 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 8 files changed, 219 insertions(+), 105 deletions(-) create mode 100644 tests/testfile70.core.bz2 create mode 100644 tests/testfile70.exec.bz2 diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index bdd9440f..5f95b7c9 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,14 @@ +2012-10-27 Jan Kratochvil + + * argp-std.c (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. + * libdwfl.h (dwfl_standard_argp): Extend the comment for USERDATA. + 2012-10-17 Jan Kratochvil * dwfl_module_getdwarf.c (mod_verify_build_id): New function with code diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c index 2ef4555c..f0c530f2 100644 --- a/libdwfl/argp-std.c +++ b/libdwfl/argp-std.c @@ -60,6 +60,27 @@ 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) + && *userdata) + { + char *e_dup = strdup (*userdata); + 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 +91,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 +111,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 +142,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 +286,56 @@ 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) + for (Dwfl_Module *mod = dwfl->modulelist; mod; mod = mod->next) + { + if (mod->name == NULL + || (strcmp (mod->name, "[exe]") != 0 + && strcmp (mod->name, "[pie]") != 0)) + continue; + mod->userdata = (void *) 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 +344,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 +367,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/libdwfl.h b/libdwfl/libdwfl.h index 000d582f..8498c0cd 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -364,7 +364,8 @@ extern int dwfl_linux_proc_find_elf (Dwfl_Module *mod, void **userdata, const char *module_name, Dwarf_Addr base, char **file_name, Elf **); -/* Standard argument parsing for using a standard callback set. */ +/* Standard argument parsing for using a standard callback set. + Field Dwfl_Module->USERDATA is reserved for "[exe]" or "[pie]" modules. */ struct argp; extern const struct argp *dwfl_standard_argp (void) __attribute__ ((const)); diff --git a/tests/ChangeLog b/tests/ChangeLog index edb82b48..1f0eb34b 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +2012-10-27 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 f2d2484a..6808bd13 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 cc8aa335..99abf9db 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 00000000..6c47c6d4 Binary files /dev/null and b/tests/testfile70.core.bz2 differ diff --git a/tests/testfile70.exec.bz2 b/tests/testfile70.exec.bz2 new file mode 100644 index 00000000..f1b969af Binary files /dev/null and b/tests/testfile70.exec.bz2 differ -- cgit v1.2.1 From 18ba6d08cbefef833b757bdcfefbc4b7900c5678 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Wed, 10 Oct 2012 09:48:10 +0200 Subject: backends/ * Makefile.am (INCLUDES): Add libdwfl. (ppc64_SRCS): Add ppc64_get_func_pc.c. * ppc64_get_func_pc.c: New file. * ppc64_init.c (ppc64_init): Install get_func_pc and destr. libdwfl/ * dwfl_module_addrsym.c (dwfl_module_addrsym) (get_section): New function from ... (dwfl_module_addrsym) (same_section): ... here. Call it. (dwfl_module_addrsym) (found_sym): New function from ... (dwfl_module_addrsym) (search_table): ... here. Call it. Try second time with ebl_get_func_pc. libebl/ * Makefile.am (gen_SOURCES): Add eblgetfuncpc.c. * ebl-hooks.h (get_func_pc): New entry. * eblgetfuncpc.c: New file. * libebl.h (struct Dwfl_Module): New declaration. (ebl_get_func_pc): New declaration. * libeblP.h (struct ebl): New field backend. tests/ * run-addrname-test.sh: New testcase for ppc64 function descriptors. * testfile66.bz2: New file. * Makefile.am (EXTRA_DIST): Add testfile66.bz2. Signed-off-by: Jan Kratochvil --- backends/ChangeLog | 7 ++ backends/Makefile.am | 5 +- backends/ppc64_get_func_pc.c | 195 ++++++++++++++++++++++++++++++++++++++++++ backends/ppc64_init.c | 2 + libdwfl/ChangeLog | 9 ++ libdwfl/dwfl_module_addrsym.c | 195 +++++++++++++++++++++++------------------- libebl/ChangeLog | 9 ++ libebl/Makefile.am | 2 +- libebl/ebl-hooks.h | 6 ++ libebl/eblgetfuncpc.c | 46 ++++++++++ libebl/libebl.h | 6 ++ libebl/libeblP.h | 3 + tests/ChangeLog | 6 ++ tests/Makefile.am | 2 +- tests/run-addrname-test.sh | 8 ++ tests/testfile66.bz2 | Bin 0 -> 569 bytes 16 files changed, 408 insertions(+), 93 deletions(-) create mode 100644 backends/ppc64_get_func_pc.c create mode 100644 libebl/eblgetfuncpc.c create mode 100755 tests/testfile66.bz2 diff --git a/backends/ChangeLog b/backends/ChangeLog index cca71130..a090167a 100644 --- a/backends/ChangeLog +++ b/backends/ChangeLog @@ -1,3 +1,10 @@ +2012-10-27 Jan Kratochvil + + * Makefile.am (INCLUDES): Add libdwfl. + (ppc64_SRCS): Add ppc64_get_func_pc.c. + * ppc64_get_func_pc.c: New file. + * ppc64_init.c (ppc64_init): Install get_func_pc and destr. + 2012-10-12 Jan Kratochvil * linux-core-note.c (prstatus_items): Rename groups of sigpend and diff --git a/backends/Makefile.am b/backends/Makefile.am index 982ff2a7..fa85593f 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -29,7 +29,7 @@ ## not, see . include $(top_srcdir)/config/eu.am INCLUDES += -I$(top_srcdir)/libebl -I$(top_srcdir)/libasm \ - -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw + -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw -I$(top_srcdir)/libdwfl modules = i386 sh x86_64 ia64 alpha arm sparc ppc ppc64 s390 tilegx @@ -90,7 +90,8 @@ libebl_ppc_pic_a_SOURCES = $(ppc_SRCS) am_libebl_ppc_pic_a_OBJECTS = $(ppc_SRCS:.c=.os) ppc64_SRCS = ppc64_init.c ppc64_symbol.c ppc64_retval.c \ - ppc64_corenote.c ppc_regs.c ppc_auxv.c ppc_attrs.c ppc_syscall.c + ppc64_corenote.c ppc_regs.c ppc_auxv.c ppc_attrs.c ppc_syscall.c \ + ppc64_get_func_pc.c libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS) am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os) diff --git a/backends/ppc64_get_func_pc.c b/backends/ppc64_get_func_pc.c new file mode 100644 index 00000000..5038b00b --- /dev/null +++ b/backends/ppc64_get_func_pc.c @@ -0,0 +1,195 @@ +/* Convert function descriptor SYM to the function PC value in-place. + Copyright (C) 2012 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 either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "libdwfl.h" +#include +#include +#include + +#define BACKEND ppc64_ +#include "libebl_CPU.h" + +struct pc_entry +{ + /* sym_from must be the very first element for the use in bsearch below. */ + GElf_Sym sym_from; + Elf64_Addr st_value_to; + const char *name_to; +}; + +struct pc_table +{ + size_t nelem; + struct pc_entry a[]; + /* Here follow strings allocated for pc_entry->name_to. */ +}; + +static int +compar (const void *a_voidp, const void *b_voidp) +{ + const struct pc_entry *a = a_voidp; + const struct pc_entry *b = b_voidp; + + return memcmp (&a->sym_from, &b->sym_from, sizeof (a->sym_from)); +} + +static void +init (Ebl *ebl, Dwfl_Module *mod) +{ + int syments = dwfl_module_getsymtab (mod); + assert (syments >= 0); + size_t funcs = 0; + size_t names_size = 0; + Elf *elf = ebl->elf; + if (elf == NULL) + return; + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + return; + GElf_Word shndx, opd_shndx = 0; + /* Needless initialization for old GCCs. */ + Elf_Data *opd_data = NULL; + /* Needless initialization for old GCCs. */ + GElf_Shdr opd_shdr_mem, *opd_shdr = NULL; + Dwarf_Addr symbias; + dwfl_module_info (mod, NULL, NULL, NULL, NULL, &symbias, NULL, NULL); + for (int symi = 1; symi < syments; symi++) + { + GElf_Sym sym; + const char *symname = dwfl_module_getsym (mod, symi, &sym, &shndx); + if (symname == NULL || GELF_ST_TYPE (sym.st_info) != STT_FUNC) + continue; + if (sym.st_shndx != SHN_XINDEX) + shndx = sym.st_shndx; + /* Zero is invalid value but it could crash this code. */ + if (shndx == 0) + continue; + if (opd_shndx == 0) + { + Elf_Scn *scn = elf_getscn (elf, shndx); + if (scn == NULL) + continue; + opd_shdr = gelf_getshdr (scn, &opd_shdr_mem); + if (opd_shdr == NULL) + continue; + if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, opd_shdr->sh_name), + ".opd") != 0) + continue; + opd_data = elf_getdata (scn, NULL); + /* SHT_NOBITS will produce NULL D_BUF. */ + if (opd_data == NULL || opd_data->d_buf == NULL) + return; + assert (opd_data->d_size == opd_shdr->sh_size); + opd_shndx = shndx; + } + if (shndx != opd_shndx) + continue; + Elf64_Addr val; + if (sym.st_value < opd_shdr->sh_addr + symbias + || sym.st_value > (opd_shdr->sh_addr + symbias + + opd_shdr->sh_size - sizeof (val))) + continue; + funcs++; + names_size += 1 + strlen (symname) + 1; + } + struct pc_table *pc_table; + pc_table = malloc (sizeof (*pc_table) + funcs * sizeof (*pc_table->a) + + names_size); + if (pc_table == NULL) + return; + ebl->backend = pc_table; + pc_table->nelem = 0; + if (funcs == 0) + return; + struct pc_entry *dest = pc_table->a; + char *names = (void *) (pc_table->a + funcs), *names_dest = names; + for (int symi = 1; symi < syments; symi++) + { + GElf_Sym sym; + const char *symname = dwfl_module_getsym (mod, symi, &sym, &shndx); + if (symname == NULL || GELF_ST_TYPE (sym.st_info) != STT_FUNC) + continue; + if (sym.st_shndx != SHN_XINDEX) + shndx = sym.st_shndx; + if (shndx != opd_shndx) + continue; + uint64_t val64; + if (sym.st_value < opd_shdr->sh_addr + symbias + || sym.st_value > (opd_shdr->sh_addr + symbias + + opd_shdr->sh_size - sizeof (val64))) + continue; + val64 = *(const uint64_t *) (opd_data->d_buf + sym.st_value + - (opd_shdr->sh_addr + symbias)); + val64 = (elf_getident (elf, NULL)[EI_DATA] == ELFDATA2MSB + ? be64toh (val64) : le64toh (val64)); + assert (dest < pc_table->a + funcs); + dest->sym_from = sym; + dest->st_value_to = val64 + symbias; + dest->name_to = names_dest; + *names_dest++ = '.'; + names_dest = stpcpy (names_dest, symname) + 1; + dest++; + pc_table->nelem++; + } + assert (pc_table->nelem == funcs); + assert (dest == pc_table->a + pc_table->nelem); + assert (names_dest == names + names_size); + qsort (pc_table->a, pc_table->nelem, sizeof (*pc_table->a), compar); +} + +const char * +ppc64_get_func_pc (Ebl *ebl, Dwfl_Module *mod, GElf_Sym *sym) +{ + if (ebl->backend == NULL) + init (ebl, mod); + if (ebl->backend == NULL) + return NULL; + const struct pc_table *pc_table = ebl->backend; + const struct pc_entry *found; + found = bsearch (sym, pc_table->a, pc_table->nelem, sizeof (*pc_table->a), + compar); + if (found == NULL) + return NULL; + sym->st_value = found->st_value_to; + return found->name_to; +} + +void +ppc64_destr (Ebl *ebl) +{ + if (ebl->backend == NULL) + return; + struct pc_table *pc_table = ebl->backend; + free (pc_table); + ebl->backend = NULL; +} diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c index 90d4f2ba..da7d02ce 100644 --- a/backends/ppc64_init.c +++ b/backends/ppc64_init.c @@ -64,6 +64,8 @@ ppc64_init (elf, machine, eh, ehlen) HOOK (eh, syscall_abi); HOOK (eh, core_note); HOOK (eh, auxv_info); + HOOK (eh, get_func_pc); + HOOK (eh, destr); return MODVERSION; } diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index bdd9440f..a72ca686 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,12 @@ +2012-10-27 Jan Kratochvil + + * dwfl_module_addrsym.c (dwfl_module_addrsym) (get_section): New + function from ... + (dwfl_module_addrsym) (same_section): ... here. Call it. + (dwfl_module_addrsym) (found_sym): New function from ... + (dwfl_module_addrsym) (search_table): ... here. Call it. Try second + time with ebl_get_func_pc. + 2012-10-17 Jan Kratochvil * dwfl_module_getdwarf.c (mod_verify_build_id): New function with code diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c index fdc95fc0..f2c00d59 100644 --- a/libdwfl/dwfl_module_addrsym.c +++ b/libdwfl/dwfl_module_addrsym.c @@ -39,6 +39,23 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, if (syments < 0) return NULL; + /* Return section where FIND_ADDR lies. Return SHN_ABS otherwise. */ + inline GElf_Word get_section (GElf_Addr find_addr) + { + GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, find_addr); + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (likely (shdr != NULL) + && mod_addr >= shdr->sh_addr + && mod_addr < shdr->sh_addr + shdr->sh_size) + return elf_ndxscn (scn); + } + return SHN_ABS; + } + /* Return true iff we consider ADDR to lie in the same section as SYM. */ GElf_Word addr_shndx = SHN_UNDEF; inline bool same_section (const GElf_Sym *sym, GElf_Word shndx) @@ -49,23 +66,7 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, /* Figure out what section ADDR lies in. */ if (addr_shndx == SHN_UNDEF) - { - GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, addr); - Elf_Scn *scn = NULL; - addr_shndx = SHN_ABS; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) - { - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (likely (shdr != NULL) - && mod_addr >= shdr->sh_addr - && mod_addr < shdr->sh_addr + shdr->sh_size) - { - addr_shndx = elf_ndxscn (scn); - break; - } - } - } + addr_shndx = get_section (addr); return shndx == addr_shndx; } @@ -83,91 +84,107 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, /* Keep track of the lowest address a relevant sizeless symbol could have. */ GElf_Addr min_label = 0; - /* Look through the symbol table for a matching symbol. */ - inline void search_table (int start, int end) + /* Consider one symbol SYM. */ + inline void found_sym (const GElf_Sym *sym, GElf_Word shndx, const char *name) { - for (int i = start; i < end; ++i) + if (name != NULL && name[0] != '\0' + && sym->st_shndx != SHN_UNDEF + && sym->st_value <= addr + && GELF_ST_TYPE (sym->st_info) != STT_SECTION + && GELF_ST_TYPE (sym->st_info) != STT_FILE + && GELF_ST_TYPE (sym->st_info) != STT_TLS) { - GElf_Sym sym; - GElf_Word shndx; - const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx); - if (name != NULL && name[0] != '\0' - && sym.st_shndx != SHN_UNDEF - && sym.st_value <= addr - && GELF_ST_TYPE (sym.st_info) != STT_SECTION - && GELF_ST_TYPE (sym.st_info) != STT_FILE - && GELF_ST_TYPE (sym.st_info) != STT_TLS) - { - /* Even if we don't choose this symbol, its existence excludes - any sizeless symbol (assembly label) that is below its upper - bound. */ - if (sym.st_value + sym.st_size > min_label) - min_label = sym.st_value + sym.st_size; + /* Even if we don't choose this symbol, its existence excludes + any sizeless symbol (assembly label) that is below its upper + bound. */ + if (sym->st_value + sym->st_size > min_label) + min_label = sym->st_value + sym->st_size; - if (sym.st_size == 0 || addr - sym.st_value < sym.st_size) + if (sym->st_size == 0 || addr - sym->st_value < sym->st_size) + { + /* Return GELF_ST_BIND as higher-is-better integer. */ + inline int binding_value (const GElf_Sym *symp) { - /* Return GELF_ST_BIND as higher-is-better integer. */ - inline int binding_value (const GElf_Sym *symp) - { - switch (GELF_ST_BIND (symp->st_info)) - { - case STB_GLOBAL: - return 3; - case STB_WEAK: - return 2; - case STB_LOCAL: - return 1; - default: - return 0; - } - } - /* This symbol is a better candidate than the current one - if it's closer to ADDR or is global when it was local. */ - if (closest_name == NULL - || closest_sym->st_value < sym.st_value - || binding_value (closest_sym) < binding_value (&sym)) - { - if (sym.st_size != 0) - { - *closest_sym = sym; - closest_shndx = shndx; - closest_name = name; - } - else if (closest_name == NULL - && sym.st_value >= min_label - && same_section (&sym, shndx)) - { - /* Handwritten assembly symbols sometimes have no - st_size. If no symbol with proper size includes - the address, we'll use the closest one that is in - the same section as ADDR. */ - sizeless_sym = sym; - sizeless_shndx = shndx; - sizeless_name = name; - } - } - /* When the beginning of its range is no closer, - the end of its range might be. Otherwise follow - GELF_ST_BIND preference. If all are equal prefer - the first symbol found. */ - else if (sym.st_size != 0 - && closest_sym->st_value == sym.st_value - && ((closest_sym->st_size > sym.st_size - && (binding_value (closest_sym) - <= binding_value (&sym))) - || (closest_sym->st_size >= sym.st_size - && (binding_value (closest_sym) - < binding_value (&sym))))) + switch (GELF_ST_BIND (symp->st_info)) + { + case STB_GLOBAL: + return 3; + case STB_WEAK: + return 2; + case STB_LOCAL: + return 1; + default: + return 0; + } + } + /* This symbol is a better candidate than the current one + if it's closer to ADDR or is global when it was local. */ + if (closest_name == NULL + || closest_sym->st_value < sym->st_value + || binding_value (closest_sym) < binding_value (sym)) + { + if (sym->st_size != 0) { - *closest_sym = sym; + *closest_sym = *sym; closest_shndx = shndx; closest_name = name; } + else if (closest_name == NULL + && sym->st_value >= min_label + && same_section (sym, shndx)) + { + /* Handwritten assembly symbols sometimes have no + st_size. If no symbol with proper size includes + the address, we'll use the closest one that is in + the same section as ADDR. */ + sizeless_sym = *sym; + sizeless_shndx = shndx; + sizeless_name = name; + } + } + /* When the beginning of its range is no closer, + the end of its range might be. Otherwise follow + GELF_ST_BIND preference. If all are equal prefer + the first symbol found. */ + else if (sym->st_size != 0 + && closest_sym->st_value == sym->st_value + && ((closest_sym->st_size > sym->st_size + && (binding_value (closest_sym) + <= binding_value (sym))) + || (closest_sym->st_size >= sym->st_size + && (binding_value (closest_sym) + < binding_value (sym))))) + { + *closest_sym = *sym; + closest_shndx = shndx; + closest_name = name; } } } } + /* Look through the symbol table for a matching symbol. */ + inline void search_table (int start, int end) + { + for (int i = start; i < end; ++i) + { + GElf_Sym sym; + GElf_Word shndx; + const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx); + found_sym (&sym, shndx, name); + if (name == NULL || GELF_ST_TYPE (sym.st_info) != STT_FUNC) + continue; + Dwfl_Error error = __libdwfl_module_getebl (mod); + if (error != DWFL_E_NOERROR) + continue; + name = ebl_get_func_pc (mod->ebl, mod, &sym); + if (name == NULL) + continue; + shndx = get_section (sym.st_value); + found_sym (&sym, shndx, name); + } + } + /* First go through global symbols. mod->first_global is setup by dwfl_module_getsymtab to the index of the first global symbol in the module's symbol table, or -1 when unknown. All symbols with diff --git a/libebl/ChangeLog b/libebl/ChangeLog index e881ce72..029f0d73 100644 --- a/libebl/ChangeLog +++ b/libebl/ChangeLog @@ -1,3 +1,12 @@ +2012-10-27 Jan Kratochvil + + * Makefile.am (gen_SOURCES): Add eblgetfuncpc.c. + * ebl-hooks.h (get_func_pc): New entry. + * eblgetfuncpc.c: New file. + * libebl.h (struct Dwfl_Module): New declaration. + (ebl_get_func_pc): New declaration. + * libeblP.h (struct ebl): New field backend. + 2012-10-12 Jan Kratochvil * ebl-hooks.h (abi_cfi): Extend its comment for return value. diff --git a/libebl/Makefile.am b/libebl/Makefile.am index 65e6b5b4..1ab08e7d 100644 --- a/libebl/Makefile.am +++ b/libebl/Makefile.am @@ -54,7 +54,7 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \ eblreginfo.c eblnonerelocp.c eblrelativerelocp.c \ eblsysvhashentrysize.c eblauxvinfo.c eblcheckobjattr.c \ ebl_check_special_section.c ebl_syscall_abi.c eblabicfi.c \ - eblstother.c + eblstother.c eblgetfuncpc.c libebl_a_SOURCES = $(gen_SOURCES) diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index d3cf3e62..b39131d5 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -155,5 +155,11 @@ int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end, Function returns 0 on success and -1 on error. */ int EBLHOOK(abi_cfi) (Ebl *ebl, Dwarf_CIE *abi_info); +/* *SYM must be STT_FUNC. Then if it describes a function descriptor (PPC64) + convert in-place its data and return a possibly different new name for it. + The name is valid as long as EBL is valid. */ +const char *EBLHOOK(get_func_pc) (Ebl *ebl, struct Dwfl_Module *mod, + GElf_Sym *sym); + /* Destructor for ELF backend handle. */ void EBLHOOK(destr) (struct ebl *); diff --git a/libebl/eblgetfuncpc.c b/libebl/eblgetfuncpc.c new file mode 100644 index 00000000..57ef1597 --- /dev/null +++ b/libebl/eblgetfuncpc.c @@ -0,0 +1,46 @@ +/* Convert function descriptor SYM to the function PC value in-place. + Copyright (C) 2012 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 either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +const char * +ebl_get_func_pc (Ebl *ebl, struct Dwfl_Module *mod, GElf_Sym *sym) +{ + if (ebl == NULL) + return NULL; + assert (sym != NULL); + assert (GELF_ST_TYPE (sym->st_info) == STT_FUNC); + if (ebl->get_func_pc == NULL) + return NULL; + return ebl->get_func_pc (ebl, mod, sym); +} diff --git a/libebl/libebl.h b/libebl/libebl.h index cae31c9b..6c507697 100644 --- a/libebl/libebl.h +++ b/libebl/libebl.h @@ -378,6 +378,12 @@ extern int ebl_auxv_info (Ebl *ebl, GElf_Xword a_type, const char **name, const char **format) __nonnull_attribute__ (1, 3, 4); +/* Convert function descriptor SYM to the function PC value in-place. */ +struct Dwfl_Module; +extern const char *ebl_get_func_pc (Ebl *ebl, struct Dwfl_Module *mod, + GElf_Sym *sym) + __nonnull_attribute__ (1, 2, 3); + #ifdef __cplusplus } diff --git a/libebl/libeblP.h b/libebl/libeblP.h index 5ec26a4b..c8196bd1 100644 --- a/libebl/libeblP.h +++ b/libebl/libeblP.h @@ -62,6 +62,9 @@ struct ebl /* Internal data. */ void *dlhandle; + + /* Data specific to the backend. */ + void *backend; }; diff --git a/tests/ChangeLog b/tests/ChangeLog index edb82b48..2100cc7c 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2012-10-27 Jan Kratochvil + + * run-addrname-test.sh: New testcase for ppc64 function descriptors. + * testfile66.bz2: New file. + * Makefile.am (EXTRA_DIST): Add testfile66.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 f2d2484a..975ef5c7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -180,7 +180,7 @@ 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 testfile66.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 cc8aa335..78c70f05 100755 --- a/tests/run-addrname-test.sh +++ b/tests/run-addrname-test.sh @@ -298,6 +298,14 @@ __vdso_time ??:0 EOF +testfiles testfile66 +testrun_compare ../src/addr2line -S -e testfile66 0x10340 0x250 <<\EOF +func +??:0 +.func +??:0 +EOF + testfiles testfile69.core testfile69.so testrun_compare ../src/addr2line --core=./testfile69.core -S 0x7f0bc6a33535 0x7f0bc6a33546 <<\EOF libstatic+0x9 diff --git a/tests/testfile66.bz2 b/tests/testfile66.bz2 new file mode 100755 index 00000000..db07f254 Binary files /dev/null and b/tests/testfile66.bz2 differ -- cgit v1.2.1 From 08d93c3f5c0aa5d6169b03ec213ac70b43aab513 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Wed, 10 Oct 2012 19:32:05 +0200 Subject: backends/ * Makefile.am (s390_SRCS): Add s390_corenote.c and s390x_corenote.c. * linux-core-note.c (ALIGN_PR_REG): New definitions. (struct EBLHOOK(prstatus)): Change field pr_reg to anonymous union with ALIGN_PR_REG. (EXTRA_ITEMS): New macro. * s390_corenote.c: New file. * s390_init.c (s390x_core_note): New declaration. (s390_init): Install s390x_core_note and s390_core_note. * s390x_corenote.c: New file. src/ * readelf.c (handle_core_items): Limit special repeated items handling to single-item formats '\n', 'b' and 'B', assert OFFSET 0 there. tests/ * run-readelf-mixed-corenote.sh: New testcase for readelf -n of s390 and s390x core notes. * testfile67.bz2: New file. * testfile68.bz2: New file. * Makefile.am (EXTRA_DIST): Add testfile67.bz2 and testfile68.bz2 . Signed-off-by: Jan Kratochvil --- backends/ChangeLog | 13 +++ backends/Makefile.am | 3 +- backends/arm_corenote.c | 2 +- backends/linux-core-note.c | 23 ++++- backends/s390_corenote.c | 189 ++++++++++++++++++++++++++++++++++++ backends/s390_init.c | 6 ++ backends/s390x_corenote.c | 2 + src/ChangeLog | 5 + src/readelf.c | 34 ++++--- tests/ChangeLog | 8 ++ tests/Makefile.am | 3 +- tests/run-readelf-mixed-corenote.sh | 133 +++++++++++++++++++++++++ tests/testfile67.bz2 | Bin 0 -> 424 bytes tests/testfile68.bz2 | Bin 0 -> 399 bytes 14 files changed, 401 insertions(+), 20 deletions(-) create mode 100644 backends/s390_corenote.c create mode 100644 backends/s390x_corenote.c create mode 100644 tests/testfile67.bz2 create mode 100644 tests/testfile68.bz2 diff --git a/backends/ChangeLog b/backends/ChangeLog index cca71130..388e22e1 100644 --- a/backends/ChangeLog +++ b/backends/ChangeLog @@ -1,3 +1,16 @@ +2012-10-14 Jan Kratochvil + Roland McGrath + + * Makefile.am (s390_SRCS): Add s390_corenote.c and s390x_corenote.c. + * linux-core-note.c (ALIGN_PR_REG): New definitions. + (struct EBLHOOK(prstatus)): Change field pr_reg to anonymous union with + ALIGN_PR_REG. + (EXTRA_ITEMS): New macro. + * s390_corenote.c: New file. + * s390_init.c (s390x_core_note): New declaration. + (s390_init): Install s390x_core_note and s390_core_note. + * s390x_corenote.c: New file. + 2012-10-12 Jan Kratochvil * linux-core-note.c (prstatus_items): Rename groups of sigpend and diff --git a/backends/Makefile.am b/backends/Makefile.am index 982ff2a7..283bbfc3 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -94,7 +94,8 @@ ppc64_SRCS = ppc64_init.c ppc64_symbol.c ppc64_retval.c \ libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS) am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os) -s390_SRCS = s390_init.c s390_symbol.c s390_regs.c s390_retval.c +s390_SRCS = s390_init.c s390_symbol.c s390_regs.c s390_retval.c \ + s390_corenote.c s390x_corenote.c libebl_s390_pic_a_SOURCES = $(s390_SRCS) am_libebl_s390_pic_a_OBJECTS = $(s390_SRCS:.c=.os) diff --git a/backends/arm_corenote.c b/backends/arm_corenote.c index d5279442..c5d8d887 100644 --- a/backends/arm_corenote.c +++ b/backends/arm_corenote.c @@ -84,7 +84,7 @@ static const Ebl_Core_Item vfp_items[] = { .name = "fpscr", .group = "register", .offset = 0, - .count = 0, .type = ELF_T_WORD, .format = 'x', + .type = ELF_T_WORD, .format = 'x', }, }; diff --git a/backends/linux-core-note.c b/backends/linux-core-note.c index b09154f2..4f1642df 100644 --- a/backends/linux-core-note.c +++ b/backends/linux-core-note.c @@ -42,6 +42,9 @@ #define INT int32_t #define ALIGN_INT 4 #define TYPE_INT ELF_T_SWORD +#ifndef ALIGN_PR_REG +# define ALIGN_PR_REG ALIGN_ULONG +#endif #define FIELD(type, name) type name __attribute__ ((aligned (ALIGN_##type))) @@ -81,7 +84,14 @@ struct EBLHOOK(prstatus) struct EBLHOOK(timeval) pr_stime; struct EBLHOOK(timeval) pr_cutime; struct EBLHOOK(timeval) pr_cstime; - FIELD (ULONG, pr_reg[PRSTATUS_REGS_SIZE / sizeof (ULONG)]); + union + { + FIELD (ULONG, pr_reg[PRSTATUS_REGS_SIZE / sizeof (ULONG)]); + } +#ifdef ALIGN_PR_REG + __attribute__ ((aligned (ALIGN_PR_REG))) +#endif + ; FIELD (INT, pr_fpvalid); }; @@ -265,6 +275,17 @@ EBLHOOK(core_note) (nhdr, name, regs_offset, nregloc, reglocs, nitems, items) *items = extra_items; \ return 1; +#define EXTRA_ITEMS(type, size, extra_items) \ + case type: \ + if (nhdr->n_descsz != size) \ + return 0; \ + *regs_offset = 0; \ + *nregloc = 0; \ + *reglocs = NULL; \ + *nitems = sizeof extra_items / sizeof extra_items[0]; \ + *items = extra_items; \ + return 1; + #ifdef FPREGSET_SIZE EXTRA_REGSET (NT_FPREGSET, FPREGSET_SIZE, fpregset_regs) #endif diff --git a/backends/s390_corenote.c b/backends/s390_corenote.c new file mode 100644 index 00000000..17cf8192 --- /dev/null +++ b/backends/s390_corenote.c @@ -0,0 +1,189 @@ +/* S390 specific core note handling. + Copyright (C) 2012 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 either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#ifndef BITS +# define BITS 32 +# define BACKEND s390_ +#else +# define BITS 64 +# define BACKEND s390x_ +#endif +#include "libebl_CPU.h" + +static const Ebl_Register_Location prstatus_regs[] = + { +#define GR(at, n, dwreg, b) \ + { .offset = at * BITS/8, .regno = dwreg, .count = n, .bits = b } + + GR ( 0, 1, 64, BITS), /* pswm */ + GR ( 1, 1, 65, BITS), /* pswa */ + GR ( 2, 16, 0, BITS), /* r0-r15 */ + GR (18, 16, 48, 32), /* ar0-ar15 */ + +#undef GR + }; + + /* orig_r2 is at offset (BITS == 32 ? 34 * 4 : 26 * 8). */ +#define PRSTATUS_REGS_SIZE (BITS / 8 * (BITS == 32 ? 35 : 27)) + +static const Ebl_Register_Location fpregset_regs[] = + { +#define FPR(at, n, dwreg) \ + { .offset = at * 64/8, .regno = dwreg, .count = n, .bits = 64 } + + /* fpc is at offset 0, see fpregset_items, it has no assigned DWARF regno. + Bytes at offsets 4 to 7 are unused. */ + FPR (1 + 0, 1, 16), /* f0 */ + FPR (1 + 1, 1, 20), /* f1 */ + FPR (1 + 2, 1, 17), /* f2 */ + FPR (1 + 3, 1, 21), /* f3 */ + FPR (1 + 4, 1, 18), /* f4 */ + FPR (1 + 5, 1, 22), /* f5 */ + FPR (1 + 6, 1, 19), /* f6 */ + FPR (1 + 7, 1, 23), /* f7 */ + FPR (1 + 8, 1, 24), /* f8 */ + FPR (1 + 9, 1, 28), /* f9 */ + FPR (1 + 10, 1, 25), /* f10 */ + FPR (1 + 11, 1, 29), /* f11 */ + FPR (1 + 12, 1, 26), /* f12 */ + FPR (1 + 13, 1, 30), /* f13 */ + FPR (1 + 14, 1, 27), /* f14 */ + FPR (1 + 15, 1, 31), /* f15 */ + +#undef FPR + }; + +static const Ebl_Core_Item fpregset_items[] = + { + { + .name = "fpc", .group = "register", .offset = 0, .type = ELF_T_WORD, + .format = 'x', + }, + }; + +/* Do not set FPREGSET_SIZE so that we can supply fpregset_items. */ +#define EXTRA_NOTES_FPREGSET \ + EXTRA_REGSET_ITEMS (NT_FPREGSET, 17 * 8, fpregset_regs, fpregset_items) + +#if BITS == 32 +# define ULONG uint32_t +# define ALIGN_ULONG 4 +# define TYPE_ULONG ELF_T_WORD +# define TYPE_LONG ELF_T_SWORD +# define UID_T uint16_t +# define GID_T uint16_t +# define ALIGN_UID_T 2 +# define ALIGN_GID_T 2 +# define TYPE_UID_T ELF_T_HALF +# define TYPE_GID_T ELF_T_HALF +#else +# define ULONG uint64_t +# define ALIGN_ULONG 8 +# define TYPE_ULONG ELF_T_XWORD +# define TYPE_LONG ELF_T_SXWORD +# define UID_T uint32_t +# define GID_T uint32_t +# define ALIGN_UID_T 4 +# define ALIGN_GID_T 4 +# define TYPE_UID_T ELF_T_WORD +# define TYPE_GID_T ELF_T_WORD +#endif +#define PID_T int32_t +#define ALIGN_PID_T 4 +#define TYPE_PID_T ELF_T_SWORD +/* s390 psw_compat_t has alignment 8 bytes where it is inherited from. */ +#define ALIGN_PR_REG 8 + +#define PRSTATUS_REGSET_ITEMS \ + { \ + .name = "orig_r2", .type = TYPE_LONG, .format = 'd', \ + .offset = offsetof (struct EBLHOOK(prstatus), \ + pr_reg[BITS == 32 ? 34 : 26]), \ + .group = "register" \ + } + +#if BITS == 32 + +static const Ebl_Core_Item high_regs_items[] = + { +#define HR(n) \ + { \ + .name = "high_r" #n , .group = "register", .offset = (n) * 4, \ + .type = ELF_T_WORD, .format = 'x', \ + } + + /* Upper halves of r0-r15 are stored here. + FIXME: They are currently not combined with the r0-r15 lower halves. */ + HR (0), HR (1), HR (2), HR (3), HR (4), HR (5), HR (6), HR (7), + HR (8), HR (9), HR (10), HR (11), HR (12), HR (13), HR (14), HR (15) + +#undef HR + }; + +#define EXTRA_NOTES_HIGH_GPRS \ + EXTRA_ITEMS (NT_S390_HIGH_GPRS, 16 * 4, high_regs_items) + +#else /* BITS == 64 */ + +#define EXTRA_NOTES_HIGH_GPRS + +#endif /* BITS == 64 */ + +static const Ebl_Core_Item last_break_items[] = + { + { + .name = "last_break", .group = "system", .offset = BITS == 32 ? 4 : 0, + .type = BITS == 32 ? ELF_T_WORD : ELF_T_XWORD, .format = 'x', + }, + }; + +static const Ebl_Core_Item system_call_items[] = + { + { + .name = "system_call", .group = "system", .offset = 0, .type = ELF_T_WORD, + .format = 'd', + }, + }; + +#define EXTRA_NOTES \ + EXTRA_NOTES_FPREGSET \ + EXTRA_NOTES_HIGH_GPRS \ + EXTRA_ITEMS (NT_S390_LAST_BREAK, 8, last_break_items) \ + EXTRA_ITEMS (NT_S390_SYSTEM_CALL, 4, system_call_items) + +#include "linux-core-note.c" diff --git a/backends/s390_init.c b/backends/s390_init.c index 528e3576..91fe4b8d 100644 --- a/backends/s390_init.c +++ b/backends/s390_init.c @@ -37,6 +37,8 @@ /* This defines the common reloc hooks based on arm_reloc.def. */ #include "common-reloc.c" +extern __typeof (s390_core_note) s390x_core_note; + const char * s390_init (elf, machine, eh, ehlen) @@ -55,6 +57,10 @@ s390_init (elf, machine, eh, ehlen) HOOK (eh, reloc_simple_type); HOOK (eh, register_info); HOOK (eh, return_value_location); + if (eh->class == ELFCLASS64) + eh->core_note = s390x_core_note; + else + HOOK (eh, core_note); /* Only the 64-bit format uses the incorrect hash table entry size. */ if (eh->class == ELFCLASS64) diff --git a/backends/s390x_corenote.c b/backends/s390x_corenote.c new file mode 100644 index 00000000..427bf7de --- /dev/null +++ b/backends/s390x_corenote.c @@ -0,0 +1,2 @@ +#define BITS 64 +#include "s390_corenote.c" diff --git a/src/ChangeLog b/src/ChangeLog index 9d6fbe3c..362cb81f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2012-10-14 Jan Kratochvil + + * readelf.c (handle_core_items): Limit special repeated items handling + to single-item formats '\n', 'b' and 'B', assert OFFSET 0 there. + 2012-10-12 Jan Kratochvil * readelf.c (ITEM_WRAP_COLUMN, REGISTER_WRAP_COLUMN): Merge to ... diff --git a/src/readelf.c b/src/readelf.c index 1801c1c5..fa2d75cc 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -7680,6 +7680,24 @@ handle_core_items (Elf *core, const void *desc, size_t descsz, { if (nitems == 0) return 0; + unsigned int colno = 0; + + /* FORMAT '\n' makes sense to be present only as a single item as it + processes all the data of a note. FORMATs 'b' and 'B' have a special case + if present as a single item but they can be also processed with other + items below. */ + if (nitems == 1 && (items[0].format == '\n' || items[0].format == 'b' + || items[0].format == 'B')) + { + assert (items[0].offset == 0); + size_t size = descsz; + colno = handle_core_item (core, items, desc, colno, &size); + /* If SIZE is not zero here there is some remaining data. But we do not + know how to process it anyway. */ + return colno; + } + for (size_t i = 0; i < nitems; ++i) + assert (items[i].format != '\n'); /* Sort to collect the groups together. */ const Ebl_Core_Item *sorted_items[nitems]; @@ -7698,23 +7716,7 @@ handle_core_items (Elf *core, const void *desc, size_t descsz, qsort (groups, ngroups, sizeof groups[0], &compare_core_item_groups); /* Write out all the groups. */ - unsigned int colno = 0; - const void *last = desc; - if (nitems == 1) - { - size_t size = descsz; - /* If this note contains registers as well as items, don't pass - &size to express that we don't wish to repeat. */ - colno = handle_core_item (core, sorted_items[0], desc, colno, - size != 0 ? &size : NULL); - - if (size == 0) - return colno; - desc += descsz - size; - descsz = size; - } - do { for (size_t i = 0; i < ngroups; ++i) diff --git a/tests/ChangeLog b/tests/ChangeLog index edb82b48..9a213340 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +2012-10-27 Jan Kratochvil + + * run-readelf-mixed-corenote.sh: New testcase for readelf -n of s390 + and s390x core notes. + * testfile67.bz2: New file. + * testfile68.bz2: New file. + * Makefile.am (EXTRA_DIST): Add testfile67.bz2 and testfile68.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 f2d2484a..f6e0d672 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 \ + testfile67.bz2 testfile68.bz2 if USE_VALGRIND valgrind_cmd="valgrind -q --trace-children=yes --error-exitcode=1" diff --git a/tests/run-readelf-mixed-corenote.sh b/tests/run-readelf-mixed-corenote.sh index f9b96c83..3c4ec514 100755 --- a/tests/run-readelf-mixed-corenote.sh +++ b/tests/run-readelf-mixed-corenote.sh @@ -84,4 +84,137 @@ Note segment of 892 bytes at offset 0x274: d30: 0x0000000000000000 d31: 0x0000000000000000 EOF +testfiles testfile67 +testrun_compare ../src/readelf -n testfile67 <<\EOF + +Note segment of 1044 bytes at offset 0xe8: + Owner Data size Type + CORE 336 PRSTATUS + info.si_signo: 4, info.si_code: 0, info.si_errno: 0, cursig: 4 + sigpend: <> + sighold: <> + pid: 805, ppid: 804, pgrp: 804, sid: 699 + utime: 0.000042, stime: 0.000103, cutime: 0.000000, cstime: 0.000000 + orig_r2: 2571552016, fpvalid: 1 + pswm: 0x0705c00180000000 pswa: 0x00000000800000d6 + r0: 4393751543808 r1: 4398002544388 + r2: 11 r3: 2571578208 + r4: 2571702016 r5: 4398003235624 + r6: 2571580768 r7: 2571702016 + r8: 2571578208 r9: 2571552016 + r10: 2571552016 r11: 0 + r12: 4398003499008 r13: 2148274656 + r14: 0 r15: 4398040761216 + a0: 0x000003ff a1: 0xfd54a6f0 a2: 0x00000000 a3: 0x00000000 + a4: 0x00000000 a5: 0x00000000 a6: 0x00000000 a7: 0x00000000 + a8: 0x00000000 a9: 0x00000000 a10: 0x00000000 a11: 0x00000000 + a12: 0x00000000 a13: 0x00000000 a14: 0x00000000 a15: 0x00000000 + CORE 136 PRPSINFO + state: 0, sname: R, zomb: 0, nice: 0, flag: 0x0000000000400400 + uid: 0, gid: 0, pid: 805, ppid: 804, pgrp: 804, sid: 699 + fname: 1, psargs: ./1 + CORE 304 AUXV + SYSINFO_EHDR: 0 + HWCAP: 0x37f + PAGESZ: 4096 + CLKTCK: 100 + PHDR: 0x80000040 + PHENT: 56 + PHNUM: 2 + BASE: 0 + FLAGS: 0 + ENTRY: 0x800000d4 + UID: 0 + EUID: 0 + GID: 0 + EGID: 0 + SECURE: 0 + RANDOM: 0x3ffffa8463c + EXECFN: 0x3ffffa85ff4 + PLATFORM: 0x3ffffa8464c + NULL + CORE 136 FPREGSET + fpc: 0x00000000 + f0: 0x0000000000000040 f1: 0x4b00000000000000 + f2: 0x0000000000000041 f3: 0x3ad50b5555555600 + f4: 0x0000000000000000 f5: 0x0000000000000000 + f6: 0x0000000000000000 f7: 0x0000000000000000 + f8: 0x0000000000000000 f9: 0x0000000000000000 + f10: 0x0000000000000000 f11: 0x0000000000000000 + f12: 0x0000000000000000 f13: 0x0000000000000000 + f14: 0x0000000000000000 f15: 0x0000000000000000 + LINUX 8 S390_LAST_BREAK + last_break: 0x000003fffd75ccbe + LINUX 4 S390_SYSTEM_CALL + system_call: 0 +EOF + +testfiles testfile68 +testrun_compare ../src/readelf -n testfile68 <<\EOF + +Note segment of 852 bytes at offset 0x94: + Owner Data size Type + CORE 224 PRSTATUS + info.si_signo: 4, info.si_code: 0, info.si_errno: 0, cursig: 4 + sigpend: <> + sighold: <> + pid: 839, ppid: 838, pgrp: 838, sid: 699 + utime: 0.000043, stime: 0.000102, cutime: 0.000000, cstime: 0.000000 + orig_r2: -1723388288, fpvalid: 1 + pswm: 0x070dc000 pswa: 0x8040009a + r0: 0 r1: -43966716 r2: 11 r3: -1723238816 + r4: -1723265280 r5: -43275480 r6: -1723245280 r7: -1723265280 + r8: -1723238816 r9: -1723388288 r10: -1723388288 r11: 0 + r12: -43012096 r13: -2146692640 r14: 0 r15: 2139883440 + a0: 0x000003ff a1: 0xfd54a6f0 a2: 0x00000000 a3: 0x00000000 + a4: 0x00000000 a5: 0x00000000 a6: 0x00000000 a7: 0x00000000 + a8: 0x00000000 a9: 0x00000000 a10: 0x00000000 a11: 0x00000000 + a12: 0x00000000 a13: 0x00000000 a14: 0x00000000 a15: 0x00000000 + CORE 124 PRPSINFO + state: 0, sname: R, zomb: 0, nice: 0, flag: 0x00400400 + uid: 0, gid: 0, pid: 839, ppid: 838, pgrp: 838, sid: 699 + fname: 2, psargs: ./2 + CORE 152 AUXV + SYSINFO_EHDR: 0 + HWCAP: 0x37f + PAGESZ: 4096 + CLKTCK: 100 + PHDR: 0x400034 + PHENT: 32 + PHNUM: 2 + BASE: 0 + FLAGS: 0 + ENTRY: 0x400098 + UID: 0 + EUID: 0 + GID: 0 + EGID: 0 + SECURE: 0 + RANDOM: 0x7f8c090c + EXECFN: 0x7f8c1ff4 + PLATFORM: 0x7f8c091c + NULL + CORE 136 FPREGSET + fpc: 0x00000000 + f0: 0x0000000000000040 f1: 0x4b00000000000000 + f2: 0x0000000000000041 f3: 0x3ad50b5555555600 + f4: 0x0000000000000000 f5: 0x0000000000000000 + f6: 0x0000000000000000 f7: 0x0000000000000000 + f8: 0x0000000000000000 f9: 0x0000000000000000 + f10: 0x0000000000000000 f11: 0x0000000000000000 + f12: 0x0000000000000000 f13: 0x0000000000000000 + f14: 0x0000000000000000 f15: 0x0000000000000000 + LINUX 8 S390_LAST_BREAK + last_break: 0xfd75ccbe + LINUX 4 S390_SYSTEM_CALL + system_call: 0 + LINUX 64 S390_HIGH_GPRS + high_r0: 0x000003ff, high_r1: 0x000003ff, high_r2: 0x00000000 + high_r3: 0x00000000, high_r4: 0x00000000, high_r5: 0x000003ff + high_r6: 0x00000000, high_r7: 0x00000000, high_r8: 0x00000000 + high_r9: 0x00000000, high_r10: 0x00000000, high_r11: 0x00000000 + high_r12: 0x000003ff, high_r13: 0x00000000, high_r14: 0x00000000 + high_r15: 0x00000000 +EOF + exit 0 diff --git a/tests/testfile67.bz2 b/tests/testfile67.bz2 new file mode 100644 index 00000000..bb64745d Binary files /dev/null and b/tests/testfile67.bz2 differ diff --git a/tests/testfile68.bz2 b/tests/testfile68.bz2 new file mode 100644 index 00000000..3fe67921 Binary files /dev/null and b/tests/testfile68.bz2 differ -- cgit v1.2.1