[PATCH] libdwfl: linux-pid-attach.c don't detach threads already attached.
by Mark Wielaard
While working on using the pid unwinder in ltrace I noticed it didn't
really play well with a process that might already be ptrace attached
to the thread under inspection. This patch solves that by detecting the
thread already is ptrace attached and making sure we don't detach it
ourselves in that case.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdwfl/ChangeLog | 10 ++++++++++
libdwfl/linux-pid-attach.c | 37 +++++++++++++++++++++++++++++--------
2 files changed, 39 insertions(+), 8 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 74d11e0..a215bef 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,13 @@
+2013-12-21 Mark Wielaard <mjw(a)redhat.com>
+
+ * linux-pid-attach.c (struct pid_arg): New field should_detach.
+ (ptrace_attach): New argument should_detachp. Set to false when
+ the process is already ptrace attached to this tid. Set to true
+ otherwise.
+ (pid_set_initial_registers): Call ptrace_attach with extra
+ argument pid_arg->should_detach.
+ (pid_thread_detach): Check pid_arg->should_detach before detaching.
+
2013-12-20 Mark Wielaard <mjw(a)redhat.com>
* dwfl_frame.c (one_arg): New struct.
diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c
index 13e60f8..d9d26d0 100644
--- a/libdwfl/linux-pid-attach.c
+++ b/libdwfl/linux-pid-attach.c
@@ -44,6 +44,8 @@ struct pid_arg
pid_t tid_attached;
/* Valid only if TID_ATTACHED is not zero. */
bool tid_was_stopped;
+ /* If true and TID_ATTACHED is not zero we should detach. */
+ bool should_detach;
};
static bool
@@ -71,13 +73,27 @@ linux_proc_pid_is_stopped (pid_t pid)
}
static bool
-ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
+ptrace_attach (pid_t tid, bool *tid_was_stoppedp, bool *should_detachp)
{
if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
{
+ if (errno == EPERM)
+ {
+ // The process might already be attached and stopped this thread.
+ // Check whether it is and if so, do not stop and detach later.
+ long data;
+ if (ptrace (PTRACE_GETEVENTMSG, tid, NULL, &data) == 0)
+ {
+ *should_detachp = false;
+ return true;
+ }
+ // No luck, we really failed. Make sure we return the right error.
+ errno = EPERM;
+ }
__libdwfl_seterrno (DWFL_E_ERRNO);
return false;
}
+ *should_detachp = true;
*tid_was_stoppedp = linux_proc_pid_is_stopped (tid);
if (*tid_was_stoppedp)
{
@@ -236,7 +252,8 @@ pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
struct pid_arg *pid_arg = thread_arg;
assert (pid_arg->tid_attached == 0);
pid_t tid = INTUSE(dwfl_thread_tid) (thread);
- if (! ptrace_attach (tid, &pid_arg->tid_was_stopped))
+ if (! ptrace_attach (tid, &pid_arg->tid_was_stopped,
+ &pid_arg->should_detach))
return false;
pid_arg->tid_attached = tid;
Dwfl_Process *process = thread->process;
@@ -260,12 +277,16 @@ pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
pid_t tid = INTUSE(dwfl_thread_tid) (thread);
assert (pid_arg->tid_attached == tid);
pid_arg->tid_attached = 0;
- /* This handling is needed only on older Linux kernels such as
- 2.6.32-358.23.2.el6.ppc64. Later kernels such as 3.11.7-200.fc19.x86_64
- remember the T (stopped) state themselves and no longer need to pass
- SIGSTOP during PTRACE_DETACH. */
- ptrace (PTRACE_DETACH, tid, NULL,
- (void *) (intptr_t) (pid_arg->tid_was_stopped ? SIGSTOP : 0));
+ if (pid_arg->should_detach)
+ {
+ /* This handling is needed only on older Linux kernels such as
+ 2.6.32-358.23.2.el6.ppc64. Later kernels such as
+ 3.11.7-200.fc19.x86_64 remember the T (stopped) state
+ themselves and no longer need to pass SIGSTOP during
+ PTRACE_DETACH. */
+ ptrace (PTRACE_DETACH, tid, NULL,
+ (void *) (intptr_t) (pid_arg->tid_was_stopped ? SIGSTOP : 0));
+ }
}
static const Dwfl_Thread_Callbacks pid_thread_callbacks =
--
1.7.1
10 years, 4 months
[PATCH] stack: Simplify argument parsing. Don't use dwfl_standard_argp.
by Mark Wielaard
We were using dwfl_standard_argp but trying to add our own and substract
some options from it. dwfl_standard_argp also handles kernel, modules,
executables without state and process maps that stack doesn't support.
That made argp parsing somewhat ugly and meant our --help and --usage
didn't really match. Just handle the dwfl_standard_argp options we do
want directly ('-p', '--core', '-e' and '--debuginfo-path'). That way
we can also do sanity checking on the options given.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 16 +++++++
src/stack.c | 129 ++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 117 insertions(+), 28 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 4737d69..ea30556 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,21 @@
2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+ * stack.c (OPT_DEBUGINFO): New define.
+ (OPT_COREFILE): Likewise.
+ (pid): New static.
+ (core_fd): Likewise.
+ (core): Likewise.
+ (exec): Likewise.
+ (debuginfo_path): Likewise.
+ (parse_opt): Handle '-p', '--core', '-e' and '--debuginfo-path'.
+ Do argument sanity checking. Setup Dwfl.
+ (main): Add 'p', 'core', 'e' and 'debuginfo-path' to options.
+ Remove argp_child children, simplify argp doc, remove custom
+ usage message and construction of dwfl with dwfl_standard_argp.
+ Use pid directly as tid. close core and core_fd if opened.
+
+2013-12-23 Mark Wielaard <mjw(a)redhat.com>
+
* stack.c (show_build_id): New static boolean.
(print_frames): Print module build-id, load address and pc offset
if show_build_id is true.
diff --git a/src/stack.c b/src/stack.c
index 362cc06..3b0d767 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -36,6 +36,10 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
/* Bug report address. */
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+/* non-printable argp options. */
+#define OPT_DEBUGINFO 0x100
+#define OPT_COREFILE 0x101
+
static bool show_activation = false;
static bool show_module = false;
static bool show_build_id = false;
@@ -57,6 +61,25 @@ struct frames
};
static Dwfl *dwfl = NULL;
+static pid_t pid = 0;
+static int core_fd = -1;
+static Elf *core = NULL;
+static const char *exec = NULL;
+static char *debuginfo_path = NULL;
+
+static const Dwfl_Callbacks proc_callbacks =
+ {
+ .find_elf = dwfl_linux_proc_find_elf,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+ };
+
+static const Dwfl_Callbacks core_callbacks =
+ {
+ .find_elf = dwfl_build_id_find_elf,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+ };
static int
frame_callback (Dwfl_Frame *state, void *arg)
@@ -200,8 +223,28 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
{
switch (key)
{
- case ARGP_KEY_INIT:
- state->child_inputs[0] = state->input;
+ case 'p':
+ pid = atoi (arg);
+ if (pid == 0)
+ argp_error (state, N_("-p PID should be a positive process id."));
+ break;
+
+ case OPT_COREFILE:
+ core_fd = open (arg, O_RDONLY);
+ if (core_fd < 0)
+ error (2, errno, N_("Cannot open core file '%s'."), arg);
+ elf_version (EV_CURRENT);
+ core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
+ if (core == NULL)
+ error (2, 0, "core '%s' elf_begin: %s", arg, elf_errmsg(-1));
+ break;
+
+ case 'e':
+ exec = arg;
+ break;
+
+ case OPT_DEBUGINFO:
+ debuginfo_path = arg;
break;
case 'm':
@@ -237,6 +280,41 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
}
break;
+ case ARGP_KEY_END:
+ if (core == NULL && exec != NULL)
+ argp_error (state,
+ N_("-e EXEC needs a core given by --core."));
+
+ if (pid == 0 && show_one_tid == true)
+ argp_error (state,
+ N_("-1 needs a thread id given by -p."));
+
+ if ((pid == 0 && core == NULL) || (pid != 0 && core != NULL))
+ argp_error (state,
+ N_("One of -p PID or --core COREFILE should be given."));
+
+ if (pid != 0)
+ {
+ dwfl = dwfl_begin (&proc_callbacks);
+ if (dwfl == NULL)
+ error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+ if (dwfl_linux_proc_report (dwfl, pid) != 0)
+ error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+ }
+
+ if (core != NULL)
+ {
+ dwfl = dwfl_begin (&core_callbacks);
+ if (dwfl == NULL)
+ error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+ if (dwfl_core_file_report (dwfl, core, exec) < 0)
+ error (2, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+ }
+
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ break;
+
default:
return ARGP_ERR_UNKNOWN;
}
@@ -256,6 +334,16 @@ main (int argc, char **argv)
const struct argp_option options[] =
{
+ { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
+ { "pid", 'p', "PID", 0,
+ N_("Show stack of process PID"), 0 },
+ { "core", OPT_COREFILE, "COREFILE", 0,
+ N_("Show stack found in COREFILE"), 0 },
+ { "executable", 'e', "EXEC", 0, N_("(optional) EXECUTABLE that produced COREFILE"), 0 },
+ { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
+ N_("Search path for separate debuginfo files"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output selection options:"), 0 },
{ "activation", 'a', NULL, 0,
N_("Additionally show frame activation"), 0 },
{ "module", 'm', NULL, 0,
@@ -269,38 +357,18 @@ main (int argc, char **argv)
{ NULL, '1', NULL, 0,
N_("Show the backtrace of only one thread"), 0 },
{ NULL, 'n', "MAXFRAMES", 0,
- N_("Show at most MAXFRAMES per thread (defaults to 64)"), 0 },
+ N_("Show at most MAXFRAMES per thread (default 64)"), 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
- const struct argp_child children[] =
- {
- { .argp = dwfl_standard_argp () },
- { .argp = NULL },
- };
-
const struct argp argp =
{
.options = options,
.parser = parse_opt,
- .doc = N_("\
-Print a stack for each thread in a process or core file.\n\
-Only real user processes are supported, no kernel or process maps."),
- .children = children
+ .doc = N_("Print a stack for each thread in a process or core file."),
};
- int remaining;
- argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
- assert (dwfl != NULL);
- if (remaining != argc)
- error (2, 0, "eu-stack [-a] [-m] [-b] [-s] [-v] [-1] [-n MAXFRAMES]"
- " [--debuginfo-path=<path>]"
- " {-p <process id>|--core=<file> [--executable=<file>]|--help}");
-
- /* dwfl_linux_proc_report has been already called from dwfl_standard_argp's
- parse_opt function. */
- if (dwfl_report_end (dwfl, NULL, NULL) != 0)
- error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ argp_parse (&argp, argc, argv, 0, NULL, NULL);
struct frames *frames = malloc (sizeof (struct frames)
+ sizeof (struct frame) * maxframes);
@@ -308,13 +376,12 @@ Only real user processes are supported, no kernel or process maps."),
if (show_one_tid)
{
- pid_t tid = dwfl_pid (dwfl);
- switch (dwfl_getthread_frames (dwfl, tid, frame_callback, frames))
+ switch (dwfl_getthread_frames (dwfl, pid, frame_callback, frames))
{
case DWARF_CB_OK:
break;
case -1:
- error (0, 0, "dwfl_getthread_frames (%d): %s", tid,
+ error (0, 0, "dwfl_getthread_frames (%d): %s", pid,
dwfl_errmsg (-1));
break;
default:
@@ -338,5 +405,11 @@ Only real user processes are supported, no kernel or process maps."),
free (frames);
dwfl_end (dwfl);
+ if (core != NULL)
+ elf_end (core);
+
+ if (core_fd != -1)
+ close (core_fd);
+
return 0;
}
--
1.7.1
10 years, 4 months
[PATCH] stack: Add -l, --list-modules. Show module memory map, build-id and files.
by Mark Wielaard
Use to list modules detected for process or core file by stack program
and to see build-ids and which main elf and debug files were recognized
by libdwfl callbacks.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 11 +++++++
src/stack.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 92 insertions(+), 17 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 7911236..cb9c815 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,14 @@
+2013-12-28 Mark Wielaard <mjw(a)redhat.com>
+
+ * stack.c (print_frames): Remove address width code and use...
+ (get_addr_width): ...this new function.
+ (show_modules): New static boolean.
+ (module_callback): New static function.
+ (parse_opt): Handle '-l'.
+ (main): Add 'l' to options. If show_modules then use dwfl_getmodules
+ with module_callback to show all detected modules and possible
+ build_id, elf and dwarf files.
+
2013-12-27 Mark Wielaard <mjw(a)redhat.com>
* stack.c (frames_shown): New static boolean.
diff --git a/src/stack.c b/src/stack.c
index d71d669..156455c 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -23,6 +23,7 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdio_ext.h>
+#include <string.h>
#include <locale.h>
#include <fcntl.h>
#include ELFUTILS_HEADER(dwfl)
@@ -49,6 +50,7 @@ static bool show_quiet = false;
#ifdef USE_DEMANGLE
static bool show_raw = false;
#endif
+static bool show_modules = false;
static unsigned maxframes = 64;
@@ -103,6 +105,70 @@ static bool frames_shown = false;
#define EXIT_USAGE 64
static int
+get_addr_width (Dwfl_Module *mod)
+{
+ // Try to find the address wide if possible.
+ static int width = 0;
+ if (width == 0 && mod)
+ {
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ if (elf)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr)
+ width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
+ }
+ }
+ if (width == 0)
+ width = 16;
+
+ return width;
+}
+
+static int
+module_callback (Dwfl_Module *mod, void **userdata __attribute__((unused)),
+ const char *name, Dwarf_Addr start,
+ void *arg __attribute__((unused)))
+{
+ /* Forces resolving of main elf and debug files. */
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ Dwarf *dwarf = dwfl_module_getdwarf (mod, &bias);
+
+ Dwarf_Addr end;
+ const char *mainfile;
+ const char *debugfile;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, &end, NULL,
+ NULL, &mainfile, &debugfile);
+ assert (strcmp (modname, name) == 0);
+
+ int width = get_addr_width (mod);
+ printf ("0x%0*" PRIx64 "-0x%0*" PRIx64 " %s\n",
+ width, start, width, end, basename (name));
+
+ const unsigned char *id;
+ GElf_Addr id_vaddr;
+ int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+ if (id_len > 0)
+ {
+ printf (" [");
+ do
+ printf ("%02" PRIx8, *id++);
+ while (--id_len > 0);
+ printf ("]\n");
+ }
+
+ if (elf != NULL)
+ printf (" %s\n", mainfile != NULL ? mainfile : "-");
+ if (dwarf != NULL)
+ printf (" %s\n", debugfile != NULL ? debugfile : "-");
+
+ return DWARF_CB_OK;
+}
+
+static int
frame_callback (Dwfl_Frame *state, void *arg)
{
struct frames *frames = (struct frames *) arg;
@@ -137,23 +203,7 @@ print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
if (mod && ! show_quiet)
symname = dwfl_module_addrname (mod, pc_adjusted);
- // Try to find the address wide if possible.
- static int width = 0;
- if (width == 0 && mod)
- {
- Dwarf_Addr bias;
- Elf *elf = dwfl_module_getelf (mod, &bias);
- if (elf)
- {
- GElf_Ehdr ehdr_mem;
- GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
- if (ehdr)
- width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
- }
- }
- if (width == 0)
- width = 16;
-
+ int width = get_addr_width (mod);
printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc);
if (show_activation)
@@ -350,6 +400,10 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
}
break;
+ case 'l':
+ show_modules = true;
+ break;
+
case ARGP_KEY_END:
if (core == NULL && exec != NULL)
argp_error (state,
@@ -443,6 +497,8 @@ main (int argc, char **argv)
N_("Show the backtrace of only one thread"), 0 },
{ NULL, 'n', "MAXFRAMES", 0,
N_("Show at most MAXFRAMES per thread (default 64)"), 0 },
+ { "list-modules", 'l', NULL, 0,
+ N_("Show module memory map with build-id, elf and debug files detected"), 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
@@ -461,6 +517,14 @@ invoked with bad or missing arguments it will exit with return code 64.")
argp_parse (&argp, argc, argv, 0, NULL, NULL);
+ if (show_modules)
+ {
+ printf ("PID %d - %s module memory map\n", dwfl_pid (dwfl),
+ pid != 0 ? "process" : "core");
+ if (dwfl_getmodules (dwfl, module_callback, NULL, 0) != 0)
+ error (EXIT_BAD, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1));
+ }
+
struct frames *frames = malloc (sizeof (struct frames)
+ sizeof (struct frame) * maxframes);
frames->frames = 0;
--
1.8.4.2
10 years, 4 months
[PATCH] libdwfl: dwfl_linux_proc_find_elf should only return regular files.
by Mark Wielaard
When the dwfl_linux_proc_find_elf callback is used together with the
dwfl_linux_proc_report callback that reads /proc/PID/maps files we might
see and try to open special character device files that cannot be normally
read and processed by libelf (and might hang the library on the initial
read from the file). Make sure we only open and return regular files.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdwfl/ChangeLog | 5 +++++
libdwfl/linux-proc-maps.c | 11 +++++++++++
2 files changed, 16 insertions(+)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 6e8fa31..5ddaec8 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,8 @@
+2013-12-28 Mark Wielaard <mjw(a)redhat.com>
+
+ * linux-proc-maps.c (dwfl_linux_proc_find_elf): Don't return special
+ character device files, only regular files.
+
2013-12-24 Mark Wielaard <mjw(a)redhat.com>
* linux-core-attach.c (core_next_thread): Check whether thread_argp
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index 8863cc8..cb081b1 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -29,6 +29,7 @@
#include "libdwflP.h"
#include <inttypes.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdio_ext.h>
@@ -348,6 +349,16 @@ dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
int fd = open64 (module_name, O_RDONLY);
if (fd >= 0)
{
+ /* When this callback is used together with dwfl_linux_proc_report
+ then we might see mappings of special character devices. Make
+ sure we only open and return regular files. */
+ struct stat sb;
+ if (fstat(fd, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG)
+ {
+ close (fd);
+ return -1;
+ }
+
*file_name = strdup (module_name);
if (*file_name == NULL)
{
--
1.8.4.2
10 years, 4 months
[patch 3/4] unwinder: ppc and ppc64
by Jan Kratochvil
Hi Mark,
jankratochvil/ynonx86base-attach-firstreg-ppc
I have left ebl API extensions together with ppc and ppc64 all in one.
Thanks,
Jan
unwinder: ppc and ppc64
backends/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: ppc and ppc64
* Makefile.am (ppc_SRCS, ppc64_SRCS): Add ppc_initreg.c.
* ppc64_init.c (ppc64_init): Initialize also frame_nregs,
core_pc_offset, set_initial_registers_tid and dwarf_to_regno.
* ppc_init.c (ppc64_init): Initialize also frame_nregs,
core_pc_offset, set_initial_registers_tid and dwarf_to_regno.
* ppc_initreg.c: New file.
libdwfl/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: ppc and ppc64
* frame_unwind.c (__libdwfl_frame_reg_get, __libdwfl_frame_reg_set):
Call ebl_dwarf_to_regno.
* linux-core-attach.c (core_set_initial_registers): Keep DESC intact.
Implement ebl_core_pc_offset support.
* linux-pid-attach.c (pid_thread_state_registers_cb): Implement
FIRSTREG -1.
libebl/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: ppc and ppc64
* Makefile.am (gen_SOURCES): Add ebldwarftoregno.c.
* ebl-hooks.h (dwarf_to_regno): New.
* ebldwarftoregno.c: New file.
* eblinitreg.c (ebl_core_pc_offset): New.
* libebl.h (ebl_tid_registers_t): Add FIRSTREG -1 to the comment.
(ebl_core_pc_offset, ebl_dwarf_to_regno): New.
* libeblP.h (struct ebl): New field core_pc_offset.
tests/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (EXTRA_DIST): Add backtrace.ppc.core.bz2,
backtrace.ppc.exec.bz2, backtrace.ppc64.core.bz2 and
backtrace.ppc64.exec.bz2.
* backtrace.ppc.core.bz2: New file.
* backtrace.ppc.exec.bz2: New file.
* backtrace.ppc64.core.bz2: New file.
* backtrace.ppc64.exec.bz2: New file.
* run-backtrace.sh: Add tests for ppc and ppc64.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -88,13 +88,13 @@ am_libebl_sparc_pic_a_OBJECTS = $(sparc_SRCS:.c=.os)
ppc_SRCS = ppc_init.c ppc_symbol.c ppc_retval.c ppc_regs.c \
ppc_corenote.c ppc_auxv.c ppc_attrs.c ppc_syscall.c \
- ppc_cfi.c
+ ppc_cfi.c ppc_initreg.c
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 \
- ppc_cfi.c ppc64_get_symbol.c
+ ppc_cfi.c ppc64_get_symbol.c ppc_initreg.c
libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS)
am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os)
--- a/backends/ppc64_init.c
+++ b/backends/ppc64_init.c
@@ -68,6 +68,11 @@ ppc64_init (elf, machine, eh, ehlen)
HOOK (eh, init_symbols);
HOOK (eh, get_symbol);
HOOK (eh, destr);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. */
+ eh->frame_nregs = (114 - 1) + 32;
+ eh->core_pc_offset = 0x170;
+ HOOK (eh, set_initial_registers_tid);
+ HOOK (eh, dwarf_to_regno);
return MODVERSION;
}
--- a/backends/ppc_init.c
+++ b/backends/ppc_init.c
@@ -65,6 +65,11 @@ ppc_init (elf, machine, eh, ehlen)
HOOK (eh, auxv_info);
HOOK (eh, check_object_attribute);
HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. */
+ eh->frame_nregs = (114 - 1) + 32;
+ eh->core_pc_offset = 0xc8;
+ HOOK (eh, set_initial_registers_tid);
+ HOOK (eh, dwarf_to_regno);
return MODVERSION;
}
--- /dev/null
+++ b/backends/ppc_initreg.c
@@ -0,0 +1,111 @@
+/* Fetch live process registers from TID.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#ifdef __powerpc__
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#endif
+
+#define BACKEND ppc_
+#include "libebl_CPU.h"
+
+bool
+ppc_dwarf_to_regno (Ebl *ebl __attribute__ ((unused)), unsigned *regno)
+{
+ switch (*regno)
+ {
+ case 108:
+ *regno = 65;
+ return true;
+ case 0 ... 107:
+ case 109 ... (114 - 1) -1:
+ return true;
+ case 1200 ... 1231:
+ *regno = *regno - 1200 + (114 - 1);
+ return true;
+ default:
+ return false;
+ }
+ abort ();
+}
+
+__typeof (ppc_dwarf_to_regno)
+ ppc64_dwarf_to_regno
+ __attribute__ ((alias ("ppc_dwarf_to_regno")));
+
+bool
+ppc_set_initial_registers_tid (pid_t tid __attribute__ ((unused)),
+ ebl_tid_registers_t *setfunc __attribute__ ((unused)),
+ void *arg __attribute__ ((unused)))
+{
+#ifndef __powerpc__
+ return false;
+#else /* __powerpc__ */
+ union
+ {
+ struct pt_regs r;
+ long l[sizeof (struct pt_regs) / sizeof (long)];
+ }
+ user_regs;
+ eu_static_assert (sizeof (struct pt_regs) % sizeof (long) == 0);
+ /* PTRACE_GETREGS is EIO on kernel-2.6.18-308.el5.ppc64. */
+ errno = 0;
+ for (unsigned regno = 0; regno < sizeof (user_regs) / sizeof (long);
+ regno++)
+ {
+ user_regs.l[regno] = ptrace (PTRACE_PEEKUSER, tid,
+ (void *) (uintptr_t) (regno
+ * sizeof (long)),
+ NULL);
+ if (errno != 0)
+ return false;
+ }
+ const size_t gprs = sizeof (user_regs.r.gpr) / sizeof (*user_regs.r.gpr);
+ Dwarf_Word dwarf_regs[gprs];
+ for (unsigned gpr = 0; gpr < gprs; gpr++)
+ dwarf_regs[gprs] = user_regs.r.gpr[gpr];
+ if (! setfunc (0, gprs, dwarf_regs, arg))
+ return false;
+ dwarf_regs[0] = user_regs.r.link;
+ if (! setfunc (65, 1, dwarf_regs, arg)) // or 108
+ return false;
+ /* Registers like msr, ctr, xer, dar, dsisr etc. are probably irrelevant
+ for CFI. */
+ dwarf_regs[0] = user_regs.r.nip;
+ return setfunc (-1, 1, dwarf_regs, arg);
+#endif /* __powerpc__ */
+}
+
+__typeof (ppc_set_initial_registers_tid)
+ ppc64_set_initial_registers_tid
+ __attribute__ ((alias ("ppc_set_initial_registers_tid")));
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -52,6 +52,8 @@ internal_function
__libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
{
Ebl *ebl = state->thread->process->ebl;
+ if (! ebl_dwarf_to_regno (ebl, ®no))
+ return false;
if (regno >= ebl_frame_nregs (ebl))
return false;
if ((state->regs_set[regno / sizeof (*state->regs_set) / 8]
@@ -67,6 +69,8 @@ internal_function
__libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno, Dwarf_Addr val)
{
Ebl *ebl = state->thread->process->ebl;
+ if (! ebl_dwarf_to_regno (ebl, ®no))
+ return false;
if (regno >= ebl_frame_nregs (ebl))
return false;
/* For example i386 user_regs_struct has signed fields. */
--- a/libdwfl/linux-core-attach.c
+++ b/libdwfl/linux-core-attach.c
@@ -198,14 +198,13 @@ core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
}
/* core_next_thread already found this TID there. */
assert (tid == INTUSE(dwfl_thread_tid) (thread));
- desc += regs_offset;
for (size_t regloci = 0; regloci < nregloc; regloci++)
{
const Ebl_Register_Location *regloc = reglocs + regloci;
if (regloc->regno >= nregs)
continue;
assert (regloc->bits == 32 || regloc->bits == 64);
- const char *reg_desc = desc + regloc->offset;
+ const char *reg_desc = desc + regs_offset + regloc->offset;
for (unsigned regno = regloc->regno;
regno < MIN (regloc->regno + (regloc->count ?: 1U), nregs);
regno++)
@@ -244,6 +243,30 @@ core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
reg_desc += regloc->pad;
}
}
+ size_t core_pc_offset = ebl_core_pc_offset (core_arg->ebl);
+ if (core_pc_offset)
+ {
+ Dwarf_Word pc;
+ switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
+ {
+ case 32:;
+ uint32_t val32 = *(const uint32_t *) (desc + core_pc_offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ /* Do a host width conversion. */
+ pc = val32;
+ break;
+ case 64:;
+ uint64_t val64 = *(const uint64_t *) (desc + core_pc_offset);
+ val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ pc = val64;
+ break;
+ default:
+ abort ();
+ }
+ INTUSE(dwfl_thread_state_register_pc) (thread, pc);
+ }
return true;
}
--- a/libdwfl/linux-pid-attach.c
+++ b/libdwfl/linux-pid-attach.c
@@ -202,6 +202,17 @@ pid_thread_state_registers_cb (int firstreg, unsigned nregs,
const Dwarf_Word *regs, void *arg)
{
Dwfl_Thread *thread = (Dwfl_Thread *) arg;
+ if (firstreg < 0)
+ {
+ assert (firstreg == -1);
+ assert (nregs > 0);
+ INTUSE(dwfl_thread_state_register_pc) (thread, *regs);
+ firstreg++;
+ nregs--;
+ regs++;
+ }
+ if (! nregs)
+ return true;
return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs);
}
--- 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 eblinitreg.c eblgetsymbol.c
+ eblstother.c eblinitreg.c eblgetsymbol.c ebldwarftoregno.c
libebl_a_SOURCES = $(gen_SOURCES)
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -180,5 +180,9 @@ void EBLHOOK(init_symbols) (Ebl *ebl, size_t syments, int first_global,
const char *EBLHOOK(get_symbol) (Ebl *ebl, size_t ndx, GElf_Sym *symp,
GElf_Word *shndxp, void **filep);
+/* Convert *REGNO as is in DWARF to a lower range suitable for
+ Dwarf_Frame->REGS indexing. */
+bool EBLHOOK(dwarf_to_regno) (Ebl *ebl, unsigned *regno);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
--- /dev/null
+++ b/libebl/ebldwarftoregno.c
@@ -0,0 +1,41 @@
+/* Convert *REGNO as is in DWARF to a lower range.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+bool
+ebl_dwarf_to_regno (Ebl *ebl, unsigned *regno)
+{
+ if (ebl == NULL)
+ return false;
+ return ebl->dwarf_to_regno == NULL ? true : ebl->dwarf_to_regno (ebl, regno);
+}
--- a/libebl/eblinitreg.c
+++ b/libebl/eblinitreg.c
@@ -49,3 +49,9 @@ ebl_frame_nregs (Ebl *ebl)
{
return ebl == NULL ? 0 : ebl->frame_nregs;
}
+
+size_t
+ebl_core_pc_offset (Ebl *ebl)
+{
+ return ebl == NULL ? 0 : ebl->core_pc_offset;
+}
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -383,7 +383,8 @@ extern int ebl_auxv_info (Ebl *ebl, GElf_Xword a_type,
const char **name, const char **format)
__nonnull_attribute__ (1, 3, 4);
-/* Callback type for ebl_set_initial_registers_tid. */
+/* Callback type for ebl_set_initial_registers_tid.
+ Register -1 is mapped to PC (if arch PC has no DWARF number). */
typedef bool (ebl_tid_registers_t) (int firstreg, unsigned nregs,
const Dwarf_Word *regs, void *arg)
__nonnull_attribute__ (3);
@@ -402,6 +403,11 @@ extern bool ebl_set_initial_registers_tid (Ebl *ebl,
extern size_t ebl_frame_nregs (Ebl *ebl)
__nonnull_attribute__ (1);
+/* PC register location stored in core file. It is a number of bytes from
+ descriptor of NT_PRSTATUS core note. It is 0 if unused. */
+extern size_t ebl_core_pc_offset (Ebl *ebl)
+ __nonnull_attribute__ (1);
+
/* Callback type for ebl_init_symbols,
it is forwarded to __libdwfl_module_getsym. */
typedef const char *(ebl_getsym_t) (void *arg, int ndx, GElf_Sym *symp,
@@ -422,6 +428,11 @@ extern const char *ebl_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp,
GElf_Word *shndxp, void **filep)
__nonnull_attribute__ (1, 3);
+/* Convert *REGNO as is in DWARF to a lower range suitable for
+ Dwarf_Frame->REGS indexing. */
+extern bool ebl_dwarf_to_regno (Ebl *ebl, unsigned *regno)
+ __nonnull_attribute__ (1, 2);
+
#ifdef __cplusplus
}
#endif
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -64,6 +64,10 @@ struct ebl
Ebl architecture can unwind iff FRAME_NREGS > 0. */
size_t frame_nregs;
+ /* PC register location stored in core file. It is a number of bytes from
+ descriptor of NT_PRSTATUS core note. It is 0 if unused. */
+ size_t core_pc_offset;
+
/* Internal data. */
void *dlhandle;
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -229,7 +229,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile_parameter_ref.c testfile_parameter_ref.bz2 \
testfile_entry_value.c testfile_entry_value.bz2 \
testfile_implicit_value.c testfile_implicit_value.bz2 \
- testfile66.bz2 run-backtrace.sh
+ testfile66.bz2 run-backtrace.sh \
+ backtrace.ppc.core.bz2 backtrace.ppc.exec.bz2 \
+ backtrace.ppc64.core.bz2 backtrace.ppc64.exec.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
Binary files /dev/null and b/tests/backtrace.ppc.core.bz2 differ
Binary files /dev/null and b/tests/backtrace.ppc.exec.bz2 differ
Binary files /dev/null and b/tests/backtrace.ppc64.core.bz2 differ
Binary files /dev/null and b/tests/backtrace.ppc64.exec.bz2 differ
--- a/tests/run-backtrace.sh
+++ b/tests/run-backtrace.sh
@@ -91,4 +91,12 @@ for child in backtrace-child{,-biarch}; do
done
+for arch in ppc ppc64; do
+ testfiles backtrace.$arch.{exec,core}
+ tempfiles backtrace.$arch.{bt,err}
+ echo ./backtrace ./backtrace.$arch.{exec,core}
+ testrun ${abs_builddir}/backtrace ./backtrace.$arch.{exec,core} 1>backtrace.$arch.bt 2>backtrace.$arch.err || true
+ check_all backtrace.$arch.{bt,err} backtrace.$arch.core
+done
+
exit 0
10 years, 4 months
[PATCH] libdwfl: Fix memory leak in linux-core-attach. Allow reiterating threads.
by Mark Wielaard
core_next_thread would allocate a new thread_arg each time but never free
it. We only need one active thread_arg to keep the state. Free it when
there are no more threads. It was also not possible to start walking all
threads in the core again. Just reset the note offset at the start.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdwfl/ChangeLog | 6 ++++++
libdwfl/linux-core-attach.c | 27 +++++++++++++++++++--------
2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 0e7f80a..b5502cd 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,9 @@
+2013-12-24 Mark Wielaard <mjw(a)redhat.com>
+
+ * linux-core-attach.c (core_next_thread): Check whether thread_argp
+ is NULL. Reset core_arg->thread_note_offset and malloc a thread_arg
+ in that case. Free thread_arg if there are no more threads.
+
2013-12-23 Mark Wielaard <mjw(a)redhat.com>
* dwfl_segment_report_module.c (dwfl_segment_report_module): Free
diff --git a/libdwfl/linux-core-attach.c b/libdwfl/linux-core-attach.c
index f259b2c..cc11467 100644
--- a/libdwfl/linux-core-attach.c
+++ b/libdwfl/linux-core-attach.c
@@ -106,6 +106,23 @@ core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
size_t desc_offset;
Elf_Data *note_data = core_arg->note_data;
size_t offset;
+
+ struct thread_arg *thread_arg;
+ if (*thread_argp == NULL)
+ {
+ core_arg->thread_note_offset = 0;
+ thread_arg = malloc (sizeof (*thread_arg));
+ if (thread_arg == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ thread_arg->core_arg = core_arg;
+ *thread_argp = thread_arg;
+ }
+ else
+ thread_arg = (struct thread_arg *) *thread_argp;
+
while (offset = core_arg->thread_note_offset, offset < note_data->d_size
&& (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
&nhdr, &name_offset,
@@ -138,17 +155,11 @@ core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
? be32toh (val32) : le32toh (val32));
pid_t tid = (int32_t) val32;
eu_static_assert (sizeof val32 <= sizeof tid);
- struct thread_arg *thread_arg = malloc (sizeof (*thread_arg));
- if (thread_arg == NULL)
- {
- __libdwfl_seterrno (DWFL_E_NOMEM);
- return -1;
- }
- thread_arg->core_arg = core_arg;
thread_arg->note_offset = offset;
- *thread_argp = thread_arg;
return tid;
}
+
+ free (thread_arg);
return 0;
}
--
1.7.1
10 years, 4 months
[PATCH] libdwfl: Correct nested asprintf result check in report_kernel_archive.
by Mark Wielaard
Because of wrongly placed parens the result of only one asprintf call
was checked correctly. Causing dwfl_linux_kernel_report_offline to return
ENOMEM. Rewrite nested if unlikely check into separate if statements to
make clear what is actually being checked and what the actual unlikely
condition is.
Reported against systemtap "build-id difficulties with hand-built kernels"
https://sourceware.org/bugzilla/show_bug.cgi?id=16358
Reported-by: Crestez Dan Leonard <lcrestez(a)ixiacom.com>
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdwfl/ChangeLog | 5 +++++
libdwfl/linux-kernel-modules.c | 10 +++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index c3569aa..fa605bd 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,8 @@
+2013-12-20 Mark Wielaard <mjw(a)redhat.com>
+
+ * linux-kernel-modules.c (report_kernel_archive): Correct nested
+ asprintf result check for debug.a.
+
2013-12-18 Mark Wielaard <mjw(a)redhat.com>
* derelocate.c (__libdwfl_find_section_ndx): New internal function.
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
index fe01028..a6d1084 100644
--- a/libdwfl/linux-kernel-modules.c
+++ b/libdwfl/linux-kernel-modules.c
@@ -251,9 +251,13 @@ report_kernel_archive (Dwfl *dwfl, const char **release,
return result;
char *archive;
- if (unlikely ((*release)[0] == '/'
- ? asprintf (&archive, "%s/debug.a", *release)
- : asprintf (&archive, MODULEDIRFMT "/debug.a", *release) < 0))
+ int res;
+ if ((*release)[0] == '/')
+ res = asprintf (&archive, "%s/debug.a", *release);
+ else
+ res = asprintf (&archive, MODULEDIRFMT "/debug.a", *release);
+
+ if (unlikely (res < 0))
return ENOMEM;
int fd = try_kernel_name (dwfl, &archive, false);
--
1.7.1
10 years, 4 months
[PATCH] backends: aarch64 always has _GLOBAL_OFFSET_TABLE_ point to .got[0].
by Mark Wielaard
Like some other architectures aarch64 has a special rule for the
_GLOBAL_OFFSET_TABLE_ symbol. Even if there is a .plt.got section the symbol
value still points to the start of the .got section. This is also what the
dynamic linker expects.
See https://sourceware.org/ml/libc-ports/2013-06/msg00057.html
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
backends/ChangeLog | 5 +++++
backends/aarch64_init.c | 1 +
backends/aarch64_symbol.c | 28 ++++++++++++++++++++++++++++
3 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/backends/ChangeLog b/backends/ChangeLog
index 4535789..b9ec896 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,8 @@
+2013-12-19 Mark Wielaard <mjw(a)redhat.com>
+
+ * aarch64_init.c (aarch64_init): Hook check_special_symbol.
+ * aarch64_symbol.c (aarch64_check_special_symbol): New function.
+
2013-12-18 Mark Wielaard <mjw(a)redhat.com>
* s390_initreg.c (s390_set_initial_registers_tid): Use union
diff --git a/backends/aarch64_init.c b/backends/aarch64_init.c
index 749af2a..d663d40 100644
--- a/backends/aarch64_init.c
+++ b/backends/aarch64_init.c
@@ -56,6 +56,7 @@ aarch64_init (elf, machine, eh, ehlen)
HOOK (eh, core_note);
HOOK (eh, reloc_simple_type);
HOOK (eh, return_value_location);
+ HOOK (eh, check_special_symbol);
return MODVERSION;
}
diff --git a/backends/aarch64_symbol.c b/backends/aarch64_symbol.c
index b0f3377..4e1dbd8 100644
--- a/backends/aarch64_symbol.c
+++ b/backends/aarch64_symbol.c
@@ -32,6 +32,7 @@
#include <elf.h>
#include <stddef.h>
+#include <string.h>
#define BACKEND aarch64_
#include "libebl_CPU.h"
@@ -54,3 +55,30 @@ aarch64_reloc_simple_type (Ebl *ebl __attribute__ ((unused)), int type)
return ELF_T_NUM;
}
}
+
+/* If this is the _GLOBAL_OFFSET_TABLE_ symbol, then it should point to
+ .got[0] even if there is a .got.plt section. */
+bool
+aarch64_check_special_symbol (Elf *elf, GElf_Ehdr *ehdr, const GElf_Sym *sym,
+ const char *name, const GElf_Shdr *destshdr)
+{
+ if (name != NULL
+ && strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+ {
+ const char *sname = elf_strptr (elf, ehdr->e_shstrndx, destshdr->sh_name);
+ if (sname != NULL && strcmp (sname, ".got.plt") == 0)
+ {
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ sname = elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name);
+ if (name != NULL && strcmp (sname, ".got") == 0)
+ return sym->st_value == shdr->sh_addr;
+ }
+ }
+ }
+
+ return false;
+}
--
1.7.1
10 years, 4 months
[patch 4/4] Provide virtual symbols for ppc64 function descriptors
by Jan Kratochvil
Hi Mark,
jankratochvil/xauxfile-filep-scnfindvma-ppc64bidir
obsoletes:
[patch 3/3] ppc64 .opd: Bidirectional provider
https://lists.fedorahosted.org/pipermail/elfutils-devel/2012-December/002...
here is the new variant of bi-directional ppc64 .opd resolver.
Thanks,
Jan
commit e477998865cd170c1ed24d9d0f0f91f45a9e7785
Author: Jan Kratochvil <jan.kratochvil(a)redhat.com>
Date: Wed Nov 6 20:38:05 2013 +0100
Provide virtual symbols for ppc64 function descriptors
backends/
2013-11-06 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Provide virtual symbols for ppc64 function descriptors.
* Makefile.am (ppc64_SRCS): Add ppc64_get_symbol.c.
* ppc64_get_symbol.c: New file.
* ppc64_init.c (ppc64_init): Install init_symbols, get_symbol and
destr.
* dwfl_module_addrsym.c (dwfl_module_addrsym): Adjust FIRST_GLOBAL also
for EBL_FIRST_GLOBAL.
* dwfl_module_getdwarf.c (getsym_helper): New function.
(find_symtab): Call also ebl_init_symbols.
(dwfl_module_getsymtab): Count also EBL_SYMENTS.
* dwfl_module_getsym.c (__libdwfl_module_getsym): Count also
EBL_FIRST_GLOBAL, EBL_SYMENTS. Call also ebl_get_symbol.
* libdwflP.h (DWFL_ERRORS): Add INVALID_INDEX.
(struct Dwfl_Module): Add fields ebl_syments and ebl_first_global.
libebl/
2013-11-06 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Provide virtual symbols for ppc64 function descriptors.
* Makefile.am (gen_SOURCES): Add eblgetsymbol.c.
* ebl-hooks.h (init_symbols, get_symbol): New.
* eblgetsymbol.c: New file.
* libebl.h (ebl_getsym_t): New definition.
(ebl_init_symbols, ebl_get_symbol): New declarations.
* libeblP.h (struct ebl): New field backend.
tests/
2013-11-06 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Provide virtual symbols for ppc64 function descriptors.
* Makefile.am (EXTRA_DIST): Add testfile66.bz2.
* run-addrname-test.sh (testfile66): New test.
* testfile66.bz2: New file.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 557ed87..332ca65 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -92,7 +92,7 @@ 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 \
- ppc_cfi.c
+ ppc_cfi.c ppc64_get_symbol.c
libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS)
am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os)
diff --git a/backends/ppc64_get_symbol.c b/backends/ppc64_get_symbol.c
new file mode 100644
index 0000000..9b1bc87
--- /dev/null
+++ b/backends/ppc64_get_symbol.c
@@ -0,0 +1,178 @@
+/* Provide virtual symbols for ppc64 function descriptors.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <endian.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define BACKEND ppc64_
+#include "libebl_CPU.h"
+
+/* Pointer to the first entry is stored into ebl->backend.
+ It has Dwfl_Module->ebl_syments entries and its memory is followed by
+ strings for the NAME entries. */
+
+struct sym_entry
+{
+ GElf_Sym sym;
+ GElf_Word shndx;
+ void *file;
+ const char *name;
+};
+
+void
+ppc64_init_symbols (Ebl *ebl, size_t syments, int first_global,
+ GElf_Addr main_bias, ebl_getsym_t *getsym, void *arg,
+ size_t *ebl_symentsp, int *ebl_first_globalp)
+{
+ assert (ebl != NULL);
+ assert (ebl->backend == NULL);
+ Elf *elf = ebl->elf;
+ assert (*ebl_symentsp == 0);
+ assert (*ebl_first_globalp == 0);
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ return;
+ GElf_Word opd_shndx = 0;
+ GElf_Shdr opd_shdr_mem, *opd_shdr;
+ Elf_Data *opd_data;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ 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 = elf_ndxscn (scn);
+ break;
+ }
+ if (opd_shndx == 0)
+ return;
+ size_t names_size = 0;
+ size_t ebl_syments = 0;
+ int ebl_first_global = 0;
+ for (size_t symi = 0; symi < syments; symi++)
+ {
+ GElf_Sym sym;
+ GElf_Word shndx;
+ const char *symname = getsym (arg, symi, &sym, &shndx, NULL);
+ if (symname == NULL || shndx != opd_shndx)
+ continue;
+ Elf64_Addr val;
+ if (sym.st_value < opd_shdr->sh_addr + main_bias
+ || sym.st_value > (opd_shdr->sh_addr + main_bias
+ + opd_shdr->sh_size - sizeof (val)))
+ continue;
+ ebl_syments++;
+ if ((ssize_t) symi < first_global)
+ ebl_first_global++;
+ names_size += 1 + strlen (symname) + 1;
+ }
+ if (ebl_syments == 0)
+ return;
+ struct sym_entry *sym_table;
+ sym_table = malloc (ebl_syments * sizeof (*sym_table) + names_size);
+ if (sym_table == NULL)
+ return;
+ struct sym_entry *dest = sym_table;
+ char *names = (void *) &sym_table[ebl_syments];
+ char *names_dest = names;
+ for (size_t symi = 0; symi < syments; symi++)
+ {
+ GElf_Sym sym;
+ GElf_Word shndx;
+ void *file;
+ const char *symname = getsym (arg, symi, &sym, &shndx, &file);
+ if (symname == NULL || shndx != opd_shndx)
+ continue;
+ uint64_t val64;
+ if (sym.st_value < opd_shdr->sh_addr + main_bias
+ || sym.st_value > (opd_shdr->sh_addr + main_bias
+ + opd_shdr->sh_size - sizeof (val64)))
+ continue;
+ val64 = *(const uint64_t *) (opd_data->d_buf + sym.st_value
+ - (opd_shdr->sh_addr + main_bias));
+ val64 = (elf_getident (elf, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ assert ((char *) dest < names);
+ dest->sym = sym;
+ dest->sym.st_value = val64;
+ Elf_Scn *sym_scn = elf_scnfindvma (elf, val64);
+ dest->shndx = sym_scn == NULL ? SHN_ABS : elf_ndxscn (sym_scn);
+ dest->sym.st_shndx = SHN_XINDEX;
+ dest->file = file;
+ dest->name = names_dest;
+ *names_dest++ = '.';
+ names_dest = stpcpy (names_dest, symname) + 1;
+ dest++;
+ }
+ assert ((char *) dest == names);
+ assert (names_dest == names + names_size);
+ *ebl_symentsp = ebl_syments;
+ *ebl_first_globalp = ebl_first_global;
+ ebl->backend = sym_table;
+}
+
+const char *
+ppc64_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp, GElf_Word *shndxp,
+ void **filep)
+{
+ assert (ebl != NULL);
+ if (ebl->backend == NULL)
+ return NULL;
+ struct sym_entry *sym_table = ebl->backend;
+ const struct sym_entry *found = &sym_table[ndx];
+ *symp = found->sym;
+ if (shndxp)
+ *shndxp = found->shndx;
+ if (filep)
+ *filep = found->file;
+ return found->name;
+}
+
+void
+ppc64_destr (Ebl *ebl)
+{
+ if (ebl->backend == NULL)
+ return;
+ struct sym_entry *sym_table = ebl->backend;
+ free (sym_table);
+ ebl->backend = NULL;
+}
diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c
index 1435875..3ed882b 100644
--- a/backends/ppc64_init.c
+++ b/backends/ppc64_init.c
@@ -65,6 +65,9 @@ ppc64_init (elf, machine, eh, ehlen)
HOOK (eh, core_note);
HOOK (eh, auxv_info);
HOOK (eh, abi_cfi);
+ HOOK (eh, init_symbols);
+ HOOK (eh, get_symbol);
+ HOOK (eh, destr);
return MODVERSION;
}
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index 2926726..ebb850a 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -161,16 +161,17 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
}
}
- /* First go through global symbols. mod->first_global and
- mod->aux_first_global are setup by dwfl_module_getsymtab to the
- index of the first global symbol in those symbol tables. Both
- are non-zero when the table exist, except when there is only a
- dynsym table loaded through phdrs, then first_global is zero and
- there will be no auxiliary table. All symbols with local binding
- come first in the symbol table, then all globals. The zeroth,
- null entry, in the auxiliary table is skipped if there is a main
- table. */
- int first_global = mod->first_global + mod->aux_first_global;
+ /* First go through global symbols. mod->first_global,
+ mod->aux_first_global and mod->ebl_first_global are setup by
+ dwfl_module_getsymtab to the index of the first global symbol in
+ those symbol tables. Both are non-zero when the table exist,
+ except when there is only a dynsym table loaded through phdrs, then
+ first_global is zero and there will be no auxiliary table. All
+ symbols with local binding come first in the symbol table, then all
+ globals. The zeroth, null entry, in the auxiliary table is skipped
+ if there is a main table. */
+ int first_global = (mod->first_global + mod->aux_first_global
+ + mod->ebl_first_global);
if (mod->syments > 0 && mod->aux_syments > 0)
first_global--;
search_table (first_global == 0 ? 1 : first_global, syments);
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index 7a65ec3..8f7f72d 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -945,6 +945,15 @@ find_aux_sym (Dwfl_Module *mod __attribute__ ((unused)),
#endif
}
+static const char *
+getsym_helper (void *arg, int ndx, GElf_Sym *sym, GElf_Word *shndxp,
+ void **voidfilep)
+{
+ Dwfl_Module *mod = arg;
+ struct dwfl_file **filep = (struct dwfl_file **) voidfilep;
+ return __libdwfl_module_getsym (mod, ndx, sym, shndxp, filep);
+}
+
/* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */
static void
find_symtab (Dwfl_Module *mod)
@@ -1064,7 +1073,7 @@ find_symtab (Dwfl_Module *mod)
mod->aux_syments = 0;
elf_end (mod->aux_sym.elf);
mod->aux_sym.elf = NULL;
- return;
+ goto aux_done;
}
mod->aux_symstrdata = elf_getdata (elf_getscn (mod->aux_sym.elf,
@@ -1085,7 +1094,15 @@ find_symtab (Dwfl_Module *mod)
mod->aux_symdata = elf_getdata (aux_symscn, NULL);
if (mod->aux_symdata == NULL)
goto aux_cleanup;
+ aux_done:;
}
+
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error == DWFL_E_NOERROR)
+ ebl_init_symbols (mod->ebl, mod->syments + mod->aux_syments,
+ mod->first_global + mod->aux_first_global,
+ mod->main_bias, getsym_helper, mod,
+ &mod->ebl_syments, &mod->ebl_first_global);
}
@@ -1242,7 +1259,7 @@ dwfl_module_getsymtab (Dwfl_Module *mod)
find_symtab (mod);
if (mod->symerr == DWFL_E_NOERROR)
/* We will skip the auxiliary zero entry if there is another one. */
- return (mod->syments + mod->aux_syments
+ return (mod->syments + mod->aux_syments + mod->ebl_syments
- (mod->syments > 0 && mod->aux_syments > 0 ? 1 : 0));
__libdwfl_seterrno (mod->symerr);
diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c
index 0f5dd37..f05880b 100644
--- a/libdwfl/dwfl_module_getsym.c
+++ b/libdwfl/dwfl_module_getsym.c
@@ -55,8 +55,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
Elf_Data *symdata;
Elf_Data *symxndxdata;
Elf_Data *symstrdata;
- if (mod->aux_symdata == NULL
- || ndx < mod->first_global)
+ if (ndx < mod->first_global)
{
/* main symbol table (locals). */
tndx = ndx;
@@ -74,24 +73,63 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
symxndxdata = mod->aux_symxndxdata;
symstrdata = mod->aux_symstrdata;
}
- else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
+ else if (ndx < (mod->first_global + mod->aux_first_global
+ + mod->ebl_first_global - skip_aux_zero))
+ {
+ /* ebl symbol lookup (locals). */
+ symdata = NULL;
+ tndx = ndx - (mod->first_global + mod->aux_first_global - skip_aux_zero);
+ }
+ else if ((size_t) ndx < (mod->syments + mod->aux_first_global
+ + mod->ebl_first_global - skip_aux_zero))
{
/* main symbol table (globals). */
- tndx = ndx - mod->aux_first_global + skip_aux_zero;
+ tndx = ndx - (mod->aux_first_global + mod->ebl_first_global
+ - skip_aux_zero);
file = mod->symfile;
symdata = mod->symdata;
symxndxdata = mod->symxndxdata;
symstrdata = mod->symstrdata;
}
- else
+ else if ((size_t) ndx < (mod->syments + mod->aux_syments
+ + mod->ebl_first_global - skip_aux_zero))
{
/* aux symbol table (globals). */
- tndx = ndx - mod->syments + skip_aux_zero;
+ tndx = ndx - (mod->syments + mod->ebl_first_global - skip_aux_zero);
file = &mod->aux_sym;
symdata = mod->aux_symdata;
symxndxdata = mod->aux_symxndxdata;
symstrdata = mod->aux_symstrdata;
}
+ else if ((size_t) ndx < (mod->syments + mod->aux_syments
+ + mod->ebl_syments - skip_aux_zero))
+ {
+ /* ebl symbol lookup (globals). */
+ symdata = NULL;
+ tndx = ndx - (mod->syments + mod->aux_syments - skip_aux_zero);
+ }
+ else
+ {
+ /* out of range NDX. */
+ __libdwfl_seterrno (DWFL_E_INVALID_INDEX);
+ return NULL;
+ }
+
+ if (symdata == NULL)
+ {
+ void *voidfile;
+ const char *name = ebl_get_symbol (mod->ebl, tndx, sym, &shndx,
+ &voidfile);
+ if (likely (name != NULL))
+ {
+ if (filep)
+ *filep = voidfile;
+ return name;
+ }
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return NULL;
+ }
+
sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);
if (unlikely (sym == NULL))
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index fe30472..f82be55 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -74,7 +74,8 @@
DWFL_ERROR (BADELF, N_("not a valid ELF file")) \
DWFL_ERROR (WEIRD_TYPE, N_("cannot handle DWARF type description")) \
DWFL_ERROR (WRONG_ID_ELF, N_("ELF file does not match build ID")) \
- DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data"))
+ DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data")) \
+ DWFL_ERROR (INVALID_INDEX, N_("invalid section index"))
#define DWFL_ERROR(name, text) DWFL_E_##name,
typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
@@ -155,8 +156,10 @@ struct Dwfl_Module
Elf_Data *aux_symdata; /* Data in the auxiliary ELF symbol table. */
size_t syments; /* sh_size / sh_entsize of that section. */
size_t aux_syments; /* sh_size / sh_entsize of aux_sym section. */
+ size_t ebl_syments; /* Number of symbols from ebl_getsym. */
int first_global; /* Index of first global symbol of table. */
int aux_first_global; /* Index of first global of aux_sym table. */
+ int ebl_first_global; /* Index of first global from ebl_getsym. */
Elf_Data *symstrdata; /* Data for its string table. */
Elf_Data *aux_symstrdata; /* Data for aux_sym string table. */
Elf_Data *symxndxdata; /* Data in the extended section index table. */
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index 4d62fad..db22c32 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 eblgetsymbol.c
libebl_a_SOURCES = $(gen_SOURCES)
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index d3cf3e6..d3c5f71 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -155,5 +155,23 @@ 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);
+/* Initialize virtual backend symbol table for EBL->elf currently containing
+ SYMENTS symbols, FIRST_GLOBAL of them are local, EBL->elf is using
+ MAIN_BIAS. GETSYM is a callback to fetch the existing EBL->elf symbols,
+ ARG is an opaque parameter for GETSYM. Fill in *EBL_SYMENTSP with the total
+ number of virtual symbols found, *EBL_FIRST_GLOBALP of them are local.
+ Both return values have to be initialized to zero by caller. */
+void EBLHOOK(init_symbols) (Ebl *ebl, size_t syments, int first_global,
+ GElf_Addr main_bias,
+ ebl_getsym_t *getsym, void *arg,
+ size_t *ebl_symentsp, int *ebl_first_globalp);
+
+/* Return NDXth virtual backend symbol from MOD, store it to *SYM, its section
+ to *SHNDX and its file pointer to *FILEP. Return its name. NDX must be
+ less than *EBL_SYMENTSP returned by init_symbols above. SHNDXP or FILEP may
+ be NULL. Returned name is valid as long as EBL is valid. */
+const char *EBLHOOK(get_symbol) (Ebl *ebl, size_t ndx, GElf_Sym *symp,
+ GElf_Word *shndxp, void **filep);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
diff --git a/libebl/eblgetsymbol.c b/libebl/eblgetsymbol.c
new file mode 100644
index 0000000..50d714e
--- /dev/null
+++ b/libebl/eblgetsymbol.c
@@ -0,0 +1,58 @@
+/* Provide virtual symbols from backend.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+#include <assert.h>
+
+void
+ebl_init_symbols (Ebl *ebl, size_t syments, int first_global,
+ GElf_Addr main_bias, ebl_getsym_t *getsym, void *arg,
+ size_t *ebl_symentsp, int *ebl_first_globalp)
+{
+ if (ebl == NULL)
+ return;
+ if (ebl->init_symbols == NULL)
+ return;
+ ebl->init_symbols (ebl, syments, first_global, main_bias, getsym, arg,
+ ebl_symentsp, ebl_first_globalp);
+}
+
+const char *
+ebl_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp, GElf_Word *shndxp,
+ void **filep)
+{
+ if (ebl == NULL)
+ return NULL;
+ if (ebl->get_symbol == NULL)
+ return NULL;
+ return ebl->get_symbol (ebl, ndx, symp, shndxp, filep);
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 990167a..821d9ad 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -383,6 +383,25 @@ extern int ebl_auxv_info (Ebl *ebl, GElf_Xword a_type,
const char **name, const char **format)
__nonnull_attribute__ (1, 3, 4);
+/* Callback type for ebl_init_symbols,
+ it is forwarded to __libdwfl_module_getsym. */
+typedef const char *(ebl_getsym_t) (void *arg, int ndx, GElf_Sym *symp,
+ GElf_Word *shndxp, void **filep)
+ __nonnull_attribute__ (3);
+
+extern void ebl_init_symbols (Ebl *ebl, size_t syments, int first_global,
+ Dwarf_Addr symbias, ebl_getsym_t *getsym,
+ void *arg, size_t *ebl_symentsp,
+ int *ebl_first_globalp)
+ __nonnull_attribute__ (1, 5, 7, 8);
+
+/* Return NDXth backend symbol from MOD, store it to *SYM and *SHNDX and return
+ its new name. If NDX is -1 then only initialize MOD's EBLSYMENTS (and
+ associated fields) and return NULL (SYM and SHNDXP are ignored in such
+ case). Returned name is valid as long as EBL is valid. */
+extern const char *ebl_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp,
+ GElf_Word *shndxp, void **filep)
+ __nonnull_attribute__ (1, 3);
#ifdef __cplusplus
}
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index 5ec26a4..c8196bd 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/Makefile.am b/tests/Makefile.am
index d07cb0b..4eaeb43 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -214,7 +214,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile_implicit_pointer.c testfile_implicit_pointer.bz2 \
testfile_parameter_ref.c testfile_parameter_ref.bz2 \
testfile_entry_value.c testfile_entry_value.bz2 \
- testfile_implicit_value.c testfile_implicit_value.bz2
+ testfile_implicit_value.c testfile_implicit_value.bz2 \
+ testfile66.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh
index 8624074..2edc7ca 100755
--- a/tests/run-addrname-test.sh
+++ b/tests/run-addrname-test.sh
@@ -298,6 +298,18 @@ __vdso_time
??:0
EOF
+testfiles testfile66
+testrun_compare ${abs_top_builddir}/src/addr2line -S -e testfile66 func 0x10340 .func 0x250 <<\EOF
+func
+??:0
+func
+??:0
+.func
+??:0
+.func
+??:0
+EOF
+
testfiles testfile69.core testfile69.so
testrun_compare ${abs_top_builddir}/src/addr2line --core=./testfile69.core -S 0x7f0bc6a33535 0x7f0bc6a33546 <<\EOF
libstatic+0x9
diff --git a/tests/testfile66.bz2 b/tests/testfile66.bz2
new file mode 100644
index 0000000..db07f25
Binary files /dev/null and b/tests/testfile66.bz2 differ
10 years, 4 months
[patch 4/4] unwinder: s390 and s390x
by Jan Kratochvil
Hi Mark,
jankratochvil/ynonx86base-attach-firstreg-ppc-s390
I have left ebl API extensions together with s390 and s390x all in one.
Thanks,
Jan
unwinder: s390 and s390x
backends/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: s390 and s390x
* Makefile.am (s390_SRCS): Add s390_initreg.c and s390_unwind.c.
* s390_init.c (s390_init): Initialize frame_nregs, core_pc_offset,
set_initial_registers_tid, normalize_pc and unwind.
* s390_initreg.c: New file.
* s390_unwind.c: New file.
libdwfl/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: s390 and s390x
* dwfl_frame_pc.c (dwfl_frame_pc): Call ebl_normalize_pc.
* frame_unwind.c (new_unwound): New function from ...
(handle_cfi): ... here. Call it.
Call also ebl_dwarf_to_regno.
(setfunc, getfunc, readfunc): New functions.
(__libdwfl_frame_unwind): Call ebl_unwind with those functions.
libebl/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: s390 and s390x
* Makefile.am (gen_SOURCES): Add eblnormalizepc.c and eblunwind.c.
* ebl-hooks.h (normalize_pc, unwind): New.
* eblnormalizepc.c: New file.
* eblunwind.c: New file.
* libebl.h (ebl_normalize_pc): New declaration.
(ebl_tid_registers_get_t, ebl_pid_memory_read_t): New definitions.
(ebl_unwind): New declaration.
tests/
2013-11-10 Jan Kratochvil <jan.kratochvil(a)redhat.com>
unwinder: s390 and s390x
* Makefile.am (EXTRA_DIST): Add backtrace.s390.core.bz2,
backtrace.s390.exec.bz2, backtrace.s390x.core.bz2 and
backtrace.s390x.exec.bz2.
* backtrace.s390.core.bz2: New file.
* backtrace.s390.exec.bz2: New file.
* backtrace.s390x.core.bz2: New file.
* backtrace.s390x.exec.bz2: New file.
* run-backtrace.sh: Test also s390 and s390x.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -99,7 +99,8 @@ 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_corenote.c s390x_corenote.c s390_cfi.c
+ s390_corenote.c s390x_corenote.c s390_cfi.c s390_initreg.c \
+ s390_unwind.c
libebl_s390_pic_a_SOURCES = $(s390_SRCS)
am_libebl_s390_pic_a_OBJECTS = $(s390_SRCS:.c=.os)
--- a/backends/s390_init.c
+++ b/backends/s390_init.c
@@ -62,6 +62,16 @@ s390_init (elf, machine, eh, ehlen)
else
HOOK (eh, core_note);
HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS 34.
+ But from the gcc/config/s390/s390.h "Register usage." comment it looks as
+ if #32 (Argument pointer) and #33 (Condition code) are not used for
+ unwinding. */
+ eh->frame_nregs = 32;
+ eh->core_pc_offset = eh->class == ELFCLASS64 ? 0x78 : 0x4c;
+ HOOK (eh, set_initial_registers_tid);
+ if (eh->class == ELFCLASS32)
+ HOOK (eh, normalize_pc);
+ HOOK (eh, unwind);
/* Only the 64-bit format uses the incorrect hash table entry size. */
if (eh->class == ELFCLASS64)
--- /dev/null
+++ b/backends/s390_initreg.c
@@ -0,0 +1,92 @@
+/* Fetch live process registers from TID.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#ifdef __s390__
+# include <sys/user.h>
+# include <asm/ptrace.h>
+# include <sys/ptrace.h>
+#endif
+
+#define BACKEND s390_
+#include "libebl_CPU.h"
+
+bool
+s390_set_initial_registers_tid (pid_t tid __attribute__ ((unused)),
+ ebl_tid_registers_t *setfunc __attribute__ ((unused)),
+ void *arg __attribute__ ((unused)))
+{
+#ifndef __s390__
+ return false;
+#else /* __s390__ */
+ struct user user_regs;
+ ptrace_area parea;
+ parea.process_addr = (uintptr_t) &user_regs;
+ parea.kernel_addr = 0;
+ parea.len = sizeof (user_regs);
+ if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea, NULL) != 0)
+ return false;
+ /* If we run as s390x we get the 64-bit registers of tid.
+ But -m31 executable seems to use only the 32-bit parts of its
+ registers so we ignore the upper half. */
+ Dwarf_Word dwarf_regs[16];
+ for (unsigned u = 0; u < 16; u++)
+ dwarf_regs[u] = user_regs.regs.gprs[u]);
+ return setfunc (0, 16, dwarf_regs, arg);
+ /* Avoid conversion double -> integer. */
+ eu_static_assert (sizeof user_regs.regs.fp_regs.fprs[0]
+ == sizeof state->regs[0]);
+ for (unsigned u = 0; u < 16; u++)
+ dwarf_regs[u] = *((const __typeof (*state->regs) *)
+ &user_regs.regs.fp_regs.fprs[u]));
+ if (! setfunc (0, 16, dwarf_regs, arg))
+ return false;
+ for (unsigned u = 0; u < 16; u++)
+ dwarf_frame_state_reg_set (state, 16 + u,
+ *((const __typeof (*state->regs) *)
+ &user_regs.regs.fp_regs.fprs[u]));
+ if (! setfunc (16, 16, dwarf_regs, arg))
+ return false;
+ dwarf_regs[0] = user_regs.regs.psw.addr;
+ return setfunc (-1, 1, dwarf_regs, arg))
+ return false;
+#endif /* __s390__ */
+}
+
+void
+s390_normalize_pc (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr *pc)
+{
+ assert (ebl->class == ELFCLASS32);
+
+ /* Clear S390 bit 31. */
+ *pc &= (1U << 31) - 1;
+}
--- /dev/null
+++ b/backends/s390_unwind.c
@@ -0,0 +1,133 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define BACKEND s390_
+#include "libebl_CPU.h"
+
+bool
+s390_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc,
+ ebl_tid_registers_get_t *getfunc, ebl_pid_memory_read_t *readfunc,
+ void *arg, bool *signal_framep)
+{
+ /* Caller already assumed caller adjustment but S390 instructions are 4 bytes
+ long. Undo it. */
+ if ((pc & 0x3) != 0x3)
+ return false;
+ pc++;
+ /* We can assume big-endian read here. */
+ Dwarf_Word instr;
+ if (! readfunc (pc, &instr, arg))
+ return false;
+ /* Fetch only the very first two bytes. */
+ instr = (instr >> (ebl->class == ELFCLASS64 ? 48 : 16)) & 0xffff;
+ /* See GDB s390_sigtramp_frame_sniffer. */
+ /* Check for 'svc'. */
+ if (((instr >> 8) & 0xff) != 0x0a)
+ return false;
+ /* Check for 'sigreturn' or 'rt_sigreturn'. */
+ if ((instr & 0xff) != 119 && (instr & 0xff) != 173)
+ return false;
+ /* See GDB s390_sigtramp_frame_unwind_cache. */
+# define S390_SP_REGNUM (0 + 15) /* S390_R15_REGNUM */
+ Dwarf_Word this_sp;
+ if (! getfunc (S390_SP_REGNUM, 1, &this_sp, arg))
+ return false;
+ unsigned word_size = ebl->class == ELFCLASS64 ? 8 : 4;
+ Dwarf_Addr next_cfa = this_sp + 16 * word_size + 32;
+ /* "New-style RT frame" is not supported,
+ assuming "Old-style RT frame and all non-RT frames". */
+ Dwarf_Word sigreg_ptr;
+ if (! readfunc (next_cfa + 8, &sigreg_ptr, arg))
+ return false;
+ /* Skip PSW mask. */
+ sigreg_ptr += word_size;
+ /* Read PSW address. */
+ Dwarf_Word val;
+ if (! readfunc (sigreg_ptr, &val, arg))
+ return false;
+ if (! setfunc (-1, 1, &val, arg))
+ return false;
+ sigreg_ptr += word_size;
+ /* Then the GPRs. */
+ Dwarf_Word gprs[16];
+ for (int i = 0; i < 16; i++)
+ {
+ if (! readfunc (sigreg_ptr, &gprs[i], arg))
+ return false;
+ sigreg_ptr += word_size;
+ }
+ /* Then the ACRs. Skip them, they are not used in CFI. */
+ for (int i = 0; i < 16; i++)
+ sigreg_ptr += 4;
+ /* The floating-point control word. */
+ sigreg_ptr += 8;
+ /* And finally the FPRs. */
+ Dwarf_Word fprs[16];
+ for (int i = 0; i < 16; i++)
+ {
+ if (! readfunc (sigreg_ptr, &val, arg))
+ return false;
+ if (ebl->class == ELFCLASS32)
+ {
+ Dwarf_Addr val_low;
+ if (! readfunc (sigreg_ptr + 4, &val_low, arg))
+ return false;
+ val = (val << 32) | val_low;
+ }
+ fprs[i] = val;
+ sigreg_ptr += 8;
+ }
+ /* If we have them, the GPR upper halves are appended at the end. */
+ if (ebl->class == ELFCLASS32)
+ {
+ unsigned sigreg_high_off = 4;
+ sigreg_ptr += sigreg_high_off;
+ for (int i = 0; i < 16; i++)
+ {
+ if (! readfunc (sigreg_ptr, &val, arg))
+ return false;
+ Dwarf_Word val_low = gprs[i];
+ val = (val << 32) | val_low;
+ gprs[i] = val;
+ sigreg_ptr += 4;
+ }
+ }
+ if (! setfunc (0, 16, gprs, arg))
+ return false;
+ if (! setfunc (16, 16, fprs, arg))
+ return false;
+ *signal_framep = true;
+ return true;
+}
--- a/libdwfl/dwfl_frame_pc.c
+++ b/libdwfl/dwfl_frame_pc.c
@@ -37,6 +37,7 @@ dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation)
{
assert (state->pc_state == DWFL_FRAME_STATE_PC_SET);
*pc = state->pc;
+ ebl_normalize_pc (state->thread->process->ebl, pc);
if (isactivation)
{
/* Bottom frame? */
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -494,6 +494,26 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
return true;
}
+static void
+new_unwound (Dwfl_Frame *state)
+{
+ assert (state->unwound == NULL);
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ size_t nregs = ebl_frame_nregs (ebl);
+ assert (nregs > 0);
+ Dwfl_Frame *unwound;
+ unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
+ state->unwound = unwound;
+ unwound->thread = thread;
+ unwound->unwound = NULL;
+ unwound->signal_frame = false;
+ unwound->initial_frame = false;
+ unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
+}
+
/* The logic is to call __libdwfl_seterrno for any CFI bytecode interpretation
error so one can easily catch the problem with a debugger. Still there are
archs with invalid CFI for some registers where the registers are never used
@@ -508,20 +528,14 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
__libdwfl_seterrno (DWFL_E_LIBDW);
return;
}
+ new_unwound (state);
+ Dwfl_Frame *unwound = state->unwound;
+ unwound->signal_frame = frame->fde->cie->signal_frame;
Dwfl_Thread *thread = state->thread;
Dwfl_Process *process = thread->process;
Ebl *ebl = process->ebl;
size_t nregs = ebl_frame_nregs (ebl);
assert (nregs > 0);
- Dwfl_Frame *unwound;
- unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
- state->unwound = unwound;
- unwound->thread = thread;
- unwound->unwound = NULL;
- unwound->signal_frame = frame->fde->cie->signal_frame;
- unwound->initial_frame = false;
- unwound->pc_state = DWFL_FRAME_STATE_ERROR;
- memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
for (unsigned regno = 0; regno < nregs; regno++)
{
Dwarf_Op reg_ops_mem[3], *reg_ops;
@@ -539,7 +553,7 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
{
/* REGNO is undefined. */
unsigned ra = frame->fde->cie->return_address_register;
- if (regno == ra)
+ if (ebl_dwarf_to_regno (ebl, &ra) && regno == ra)
unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
continue;
}
@@ -582,6 +596,58 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
}
}
+static bool
+setfunc (int firstreg, unsigned nregs, const Dwarf_Word *regs, void *arg)
+{
+ Dwfl_Frame *state = arg;
+ Dwfl_Frame *unwound = state->unwound;
+ if (firstreg < 0)
+ {
+ assert (firstreg == -1);
+ assert (nregs > 0);
+ assert (unwound->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
+ unwound->pc = *regs;
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ firstreg++;
+ nregs--;
+ regs++;
+ }
+ while (nregs--)
+ if (! __libdwfl_frame_reg_set (unwound, firstreg++, *regs++))
+ return false;
+ return true;
+}
+
+static bool
+getfunc (int firstreg, unsigned nregs, Dwarf_Word *regs, void *arg)
+{
+ Dwfl_Frame *state = arg;
+ if (firstreg < 0)
+ {
+ assert (firstreg == -1);
+ assert (nregs > 0);
+ assert (state->pc_state == DWFL_FRAME_STATE_PC_SET);
+ *regs = state->pc;
+ firstreg++;
+ nregs--;
+ regs++;
+ }
+ while (nregs--)
+ if (! __libdwfl_frame_reg_get (state, firstreg++, regs++))
+ return false;
+ return true;
+}
+
+static bool
+readfunc (Dwarf_Addr addr, Dwarf_Word *datap, void *arg)
+{
+ Dwfl_Frame *state = arg;
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ return process->callbacks->memory_read (process->dwfl, addr, datap,
+ process->callbacks_arg);
+}
+
void
internal_function
__libdwfl_frame_unwind (Dwfl_Frame *state)
@@ -618,4 +684,20 @@ __libdwfl_frame_unwind (Dwfl_Frame *state)
return;
}
}
+ assert (state->unwound == NULL);
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ new_unwound (state);
+ state->unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ // &Dwfl_Frame.signal_frame cannot be passed as it is a bitfield.
+ bool signal_frame = false;
+ if (! ebl_unwind (ebl, pc, setfunc, getfunc, readfunc, state, &signal_frame))
+ {
+ state->unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ // __libdwfl_seterrno has been called above.
+ return;
+ }
+ assert (state->unwound->pc_state == DWFL_FRAME_STATE_PC_SET);
+ state->unwound->signal_frame = signal_frame;
}
--- a/libebl/Makefile.am
+++ b/libebl/Makefile.am
@@ -54,7 +54,8 @@ 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 eblinitreg.c eblgetsymbol.c ebldwarftoregno.c
+ eblstother.c eblinitreg.c eblgetsymbol.c ebldwarftoregno.c \
+ eblnormalizepc.c eblunwind.c
libebl_a_SOURCES = $(gen_SOURCES)
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -184,5 +184,21 @@ const char *EBLHOOK(get_symbol) (Ebl *ebl, size_t ndx, GElf_Sym *symp,
Dwarf_Frame->REGS indexing. */
bool EBLHOOK(dwarf_to_regno) (Ebl *ebl, unsigned *regno);
+/* Optionally modify *PC as fetched from inferior data into valid PC
+ instruction pointer. */
+void EBLHOOK(normalize_pc) (Ebl *ebl, Dwarf_Addr *pc);
+
+/* Get previous frame state for an existing frame state. PC is for the
+ existing frame. SETFUNC sets register in the previous frame. GETFUNC gets
+ register from the existing frame. Note that GETFUNC vs. SETFUNC act on
+ a disjunct set of registers. READFUNC reads memory. ARG has to be passed
+ for SETFUNC, GETFUNC and READFUNC. *SIGNAL_FRAMEP is initialized to false,
+ it can be set to true if existing frame is a signal frame. SIGNAL_FRAMEP is
+ never NULL. */
+bool EBLHOOK(unwind) (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc,
+ ebl_tid_registers_get_t *getfunc,
+ ebl_pid_memory_read_t *readfunc, void *arg,
+ bool *signal_framep);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
--- /dev/null
+++ b/libebl/eblnormalizepc.c
@@ -0,0 +1,40 @@
+/* Modify PC as fetched from inferior data into valid PC.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+void
+ebl_normalize_pc (Ebl *ebl, Dwarf_Addr *pc)
+{
+ if (ebl != NULL && ebl->normalize_pc != NULL)
+ ebl->normalize_pc (ebl, pc);
+}
--- /dev/null
+++ b/libebl/eblunwind.c
@@ -0,0 +1,43 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+bool
+ebl_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc,
+ ebl_tid_registers_get_t *getfunc, ebl_pid_memory_read_t *readfunc,
+ void *arg, bool *signal_framep)
+{
+ if (ebl == NULL || ebl->unwind == NULL)
+ return false;
+ return ebl->unwind (ebl, pc, setfunc, getfunc, readfunc, arg, signal_framep);
+}
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -433,6 +433,33 @@ extern const char *ebl_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp,
extern bool ebl_dwarf_to_regno (Ebl *ebl, unsigned *regno)
__nonnull_attribute__ (1, 2);
+/* Modify PC as fetched from inferior data into valid PC. */
+extern void ebl_normalize_pc (Ebl *ebl, Dwarf_Addr *pc)
+ __nonnull_attribute__ (1, 2);
+
+/* Callback type for FIXME.
+ Register -1 is mapped to PC (if arch PC has no DWARF number). */
+typedef bool (ebl_tid_registers_get_t) (int firstreg, unsigned nregs,
+ Dwarf_Word *regs, void *arg)
+ __nonnull_attribute__ (3);
+
+typedef bool (ebl_pid_memory_read_t) (Dwarf_Addr addr, Dwarf_Word *data,
+ void *arg)
+ __nonnull_attribute__ (3);
+
+/* Get previous frame state for an existing frame state. PC is for the
+ existing frame. SETFUNC sets register in the previous frame. GETFUNC gets
+ register from the existing frame. Note that GETFUNC vs. SETFUNC act on
+ a disjunct set of registers. READFUNC reads memory. ARG has to be passed
+ for SETFUNC, GETFUNC and READFUNC. *SIGNAL_FRAMEP is initialized to false,
+ it can be set to true if existing frame is a signal frame. SIGNAL_FRAMEP is
+ never NULL. */
+extern bool ebl_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc,
+ ebl_tid_registers_get_t *getfunc,
+ ebl_pid_memory_read_t *readfunc, void *arg,
+ bool *signal_framep)
+ __nonnull_attribute__ (1, 3, 4, 5, 7);
+
#ifdef __cplusplus
}
#endif
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -231,7 +231,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile_implicit_value.c testfile_implicit_value.bz2 \
testfile66.bz2 run-backtrace.sh \
backtrace.ppc.core.bz2 backtrace.ppc.exec.bz2 \
- backtrace.ppc64.core.bz2 backtrace.ppc64.exec.bz2
+ backtrace.ppc64.core.bz2 backtrace.ppc64.exec.bz2 \
+ backtrace.s390.core.bz2 backtrace.s390.exec.bz2 \
+ backtrace.s390x.core.bz2 backtrace.s390x.exec.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
Binary files /dev/null and b/tests/backtrace.s390.core.bz2 differ
Binary files /dev/null and b/tests/backtrace.s390.exec.bz2 differ
Binary files /dev/null and b/tests/backtrace.s390x.core.bz2 differ
Binary files /dev/null and b/tests/backtrace.s390x.exec.bz2 differ
--- a/tests/run-backtrace.sh
+++ b/tests/run-backtrace.sh
@@ -91,7 +91,7 @@ for child in backtrace-child{,-biarch}; do
done
-for arch in ppc ppc64; do
+for arch in ppc ppc64 s390 s390x; do
testfiles backtrace.$arch.{exec,core}
tempfiles backtrace.$arch.{bt,err}
echo ./backtrace ./backtrace.$arch.{exec,core}
10 years, 4 months