[RFC] libelf: segment fault on x86-64 while file's bss offset have a large number
by Hongxu Jia
*Environment
cat /etc/issue
Ubuntu 13.04 \n \l
uname -a
Linux pek-hjia-d1 3.8.0-31-generic #46-Ubuntu SMP Tue Sep 10 20:03:44 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
*Problem
1) Here is the test source code
$ cat >> test.c << EOF
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include "libelf.h"
int main(int argc, char *argv[])
{
int fd;
Elf *e;
if (elf_version(EV_CURRENT) == EV_NONE)
{
printf ("library out of date\n");
exit (1);
}
if ((fd = open("test/xB.linkhuge", O_RDWR)) < 0) {
printf("%s %d failed\n", __FUNCTION__, __LINE__);
exit (1);
}
if ((e = elf_begin(fd, ELF_C_RDWR_MMAP, (Elf *) 0)) == 0) {
printf("failed %s", elf_errmsg (-1));
exit (1);
}
elf_flagelf (e, ELF_C_SET, ELF_F_LAYOUT);
elf_update(e, ELF_C_WRITE);
elf_end(e);
close(fd);
}
EOF
2) Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/e...
to get libelf.a for debug.
Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/e...
to get libelf.h for debug.
3) Compile test.c with libelf.a
$ gcc test.c -o test_case -static -L. -lelf
4) Prepare file whose bss offset have a large number '00200000'
Download the attachment from
https://bugzilla.redhat.com/show_bug.cgi?id=1020842
$ ls test/xB.linkhuge -al
-rwxr-xr-x 1 jiahongxu jiahongxu 1221403 Oct 18 18:55 test/xB.linkhuge
$ readelf -a xB.linkhuge
......
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
skip..
[25] .data PROGBITS 00000000005128a0 001128a0
0000000000010168 0000000000000000 WA 0 0 32
[26] .bss NOBITS 0000000001000000 00200000
0000000000010050 0000000000000000 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 00122a08
0000000000000011 0000000000000001 MS 0 0 1
......
5) Run test_case with strace, there was mmap/munmap error.
$ strace ./test_case
execve("./test_case", ["./test_case"], [/* 59 vars */]) = 0
uname({sys="Linux", node="pek-hjia-d1", ...}) = 0
brk(0) = 0x16a6000
brk(0x16a71c0) = 0x16a71c0
arch_prctl(ARCH_SET_FS, 0x16a6880) = 0
brk(0x16c81c0) = 0x16c81c0
brk(0x16c9000) = 0x16c9000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("test/xB.linkhuge", O_RDWR) = 3
fcntl(3, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0
mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000
fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0
ftruncate(3, 2097152) = 0
msync(0x7ff720fe2000, 1216568, MS_SYNC) = 0
munmap(0x7ff720fe2000, 2097152) = 0
close(3) = 0
exit_group(0) = ?
6) $ ls test/xB.linkhuge -al
-rwxr-xr-x 1 jiahongxu jiahongxu 2097152 Oct 18 19:04 test/xB.linkhuge
*Analysis
1) While ELF_C_RDWR_MMAP was used, elf_begin invoked mmap() to map file
into memory with the size of '1221403'.
...strace log...
mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000
................
2) While 'xB.linkhuge' bss Offset has a large number '00200000', elf_update
caculated file size by __elf64_updatenull_wrlock and the size was
enlarged from '1221403' to '2097152'
3) In this situation, elf_update invoked ftruncate to enlarge the file,
and memory size (elf->maximum_size) also was incorrectly updated.
...strace log...
ftruncate(3, 2097152)
................
4) There was segment fault in elf_end which invoked munmap with the
length is the enlarged file size '2097152', not the length of
mmap '1216568'.
...strace log...
munmap(0x7ff720fe2000, 2097152) = 0
................
*Solution
1) I tried to modify elf_update.c, don't update memory size
(elf->maximum_size) in this situation. It fixed this issue
and everything looks ok, but I am not sure the modification
is necessary.
......
11 diff --git a/libelf/elf_update.c b/libelf/elf_update.c
12 --- a/libelf/elf_update.c
13 +++ b/libelf/elf_update.c
14 @@ -120,7 +120,9 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
15 size = -1;
16 }
17
18 - if (size != -1 && elf->parent == NULL)
19 + /* If the file is enlarged by truncate, we should not update maximum_size to
20 + avoid segment fault while invoking munmap in elf_end */
21 + if (size != -1 && elf->parent == NULL && (size_t) size <= elf->maximum_size)
22 elf->maximum_size = size;
......
2) I also tried to add check before munmap in elf_end by msync with
the length of elf->maximum_size, if msync return error, munmap
should not be invoked, this could avoid segment fault.
--- a/libelf/elf_end.c
+++ b/libelf/elf_end.c
@@ -217,7 +217,10 @@ elf_end (elf)
if ((elf->flags & ELF_F_MALLOCED) != 0)
free (elf->map_address);
else if ((elf->flags & ELF_F_MMAPPED) != 0)
- munmap (elf->map_address, elf->maximum_size);
+ {
+ if (msync (elf->map_address, elf->maximum_size, MS_SYNC) == 0)
+ munmap (elf->map_address, elf->maximum_size);
+ }
}
3) Any suggestion is welcomed.
--
1.8.1.2
9 years, 10 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
9 years, 11 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
9 years, 11 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}
9 years, 11 months
[patch 1/2] Code cleanup: Reindentation
by Jan Kratochvil
Hi Mark,
not sure why it was written this way, I find it easier for the later patch not
to nest too deep.
Thanks,
Jan
libdwfl/
2013-11-01 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Code cleanup.
* core-file.c (dwfl_core_file_report): Reindent block of code by
continue keyword.
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -500,34 +500,35 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
lastmodp = &(*lastmodp)->next;
for (struct r_debug_info_module *module = r_debug_info.module;
module != NULL; module = module->next)
- if (module->elf != NULL)
- {
- Dwfl_Module *mod;
- mod = __libdwfl_report_elf (dwfl, basename (module->name), module->name,
- module->fd, module->elf, module->l_addr,
- true, true);
- if (mod == NULL)
- continue;
- module->elf = NULL;
- module->fd = -1;
- /* Move this module to the end of the list, so that we end
- up with a list in the same order as the link_map chain. */
- if (mod->next != NULL)
- {
- if (*lastmodp != mod)
- {
- lastmodp = &dwfl->modulelist;
- while (*lastmodp != mod)
- lastmodp = &(*lastmodp)->next;
- }
- *lastmodp = mod->next;
- mod->next = NULL;
- while (*lastmodp != NULL)
- lastmodp = &(*lastmodp)->next;
- *lastmodp = mod;
- }
- lastmodp = &mod->next;
- }
+ {
+ if (module->elf == NULL)
+ continue;
+ Dwfl_Module *mod;
+ mod = __libdwfl_report_elf (dwfl, basename (module->name), module->name,
+ module->fd, module->elf, module->l_addr,
+ true, true);
+ if (mod == NULL)
+ continue;
+ module->elf = NULL;
+ module->fd = -1;
+ /* Move this module to the end of the list, so that we end
+ up with a list in the same order as the link_map chain. */
+ if (mod->next != NULL)
+ {
+ if (*lastmodp != mod)
+ {
+ lastmodp = &dwfl->modulelist;
+ while (*lastmodp != mod)
+ lastmodp = &(*lastmodp)->next;
+ }
+ *lastmodp = mod->next;
+ mod->next = NULL;
+ while (*lastmodp != NULL)
+ lastmodp = &(*lastmodp)->next;
+ *lastmodp = mod;
+ }
+ lastmodp = &mod->next;
+ }
clear_r_debug_info (&r_debug_info);
9 years, 11 months
FYI unwinder: tests/ update
by Jan Kratochvil
Hi Mark,
as you requested I have commented more the tests/ part.
It is in: jankratochvil/unwindx86
Thanks,
Jan
diff --git a/tests/backtrace.c b/tests/backtrace.c
index e50399b..36995bc 100644
--- a/tests/backtrace.c
+++ b/tests/backtrace.c
@@ -162,7 +162,7 @@ frame_callback (Dwfl_Frame *state, void *frame_arg)
if (! dwfl_frame_pc (state, &pc, &isactivation))
{
error (0, 0, "%s", dwfl_errmsg (-1));
- return 1;
+ return DWARF_CB_ABORT;
}
Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
@@ -204,8 +204,8 @@ thread_callback (Dwfl_Thread *thread, void *thread_arg)
{
case 0:
break;
- case 1:
- return 1;
+ case DWARF_CB_ABORT:
+ return DWARF_CB_ABORT;
case -1:
error (0, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
/* All platforms do not have yet proper unwind termination. */
@@ -232,17 +232,17 @@ dump (pid_t pid, const char *corefile, callback_t *callback,
struct thread_callback thread_callback_data;
thread_callback_data.callback = callback;
thread_callback_data.callback_data = callback_data;
- int err = 0;
+ bool err = false;
switch (dwfl_getthreads (dwfl, thread_callback, &thread_callback_data))
{
case 0:
break;
- case 1:
- err = 1;
+ case DWARF_CB_ABORT:
+ err = true;
break;
case -1:
error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
- err = 1;
+ err = true;
break;
default:
abort ();
diff --git a/tests/run-backtrace.sh b/tests/run-backtrace.sh
index 3f77da3..8e02403 100755
--- a/tests/run-backtrace.sh
+++ b/tests/run-backtrace.sh
@@ -17,32 +17,23 @@
. $srcdir/test-subr.sh
-if [ -z "$VERBOSE" ]; then
- exec >/dev/null
-else
- set -x
-fi
-
-mytestrun()
-{
- echo "$*"
- testrun "$@"
-}
-
+# Verify one of the backtraced threads contains function 'main'.
check_main()
{
if grep -w main $1; then
return
fi
- cat >&2 $1 $3
echo >&2 $2: no main
false
}
+# Without proper ELF symbols resolution we could get inappropriate weak
+# symbol "gsignal" with the same address as the correct symbol "raise".
+# It was fixed by GIT commit 78dec228b3cfb2f9300cd0b682ebf416c9674c91 .
+# [patch] Improve ELF symbols preference (global > weak)
+# https://lists.fedorahosted.org/pipermail/elfutils-devel/2012-October/0026...
check_gsignal()
{
- # Without proper ELF symbols resolution we could get inappropriate weak
- # symbol "gsignal" with the same address as the correct symbol "raise".
if ! grep -w gsignal $1; then
return
fi
@@ -51,12 +42,15 @@ check_gsignal()
false
}
+# Verify the STDERR output does not contain unexpected errors.
+# In some cases we cannot reliably find out we got behind _start as some
+# operating system do not properly terminate CFI by undefined PC.
+# Ignore it here as it is a bug of OS, not a bug of elfutils.
check_err()
{
if test ! -s $1; then
return
fi
- # In some cases we cannot reliably find out we got behind _start.
if cmp -s <(echo "${abs_builddir}/backtrace: dwfl_thread_getframes: no matching address range") <(uniq <$1); then
return
fi
@@ -65,19 +59,36 @@ check_err()
false
}
+check_all()
+{
+ bt=$1
+ err=$2
+ testname=$3
+ cat $bt $err
+ check_main $bt $testname
+ check_gsignal $bt $testname
+ check_err $err $testname
+}
+
+# Test both 64-bit and 32-bit tracees. Both files will be the same if the
+# current platform does not support different target archs.
for child in backtrace-child{,-biarch}; do
- tempfiles $child{.bt,.err}
+
+ # Backtrace live process.
+ # Do not abort on non-zero exit code due to some warnings of ./backtrace
+ # - see function check_err.
+ tempfiles $child.{bt,err}
(set +ex; testrun ${abs_builddir}/backtrace ${abs_builddir}/$child 1>$child.bt 2>$child.err; true)
- check_main $child.bt $child $child.err
- check_gsignal $child.bt $child
- check_err $child.err $child
+ check_all $child.{bt,err} $child
+
+ # Backtrace core file.
core="core.`ulimit -c unlimited; set +ex; testrun ${abs_builddir}/$child --gencore --run; true`"
- tempfiles $core{,.bt,.err}
+ # Do not abort on non-zero exit code due to some warnings of ./backtrace
+ # - see function check_err.
+ tempfiles $core{,.{bt,err}}
(set +ex; testrun ${abs_builddir}/backtrace ${abs_builddir}/$child $core 1>$core.bt 2>$core.err; true)
- cat $core.{bt,err}
- check_main $core.bt $child-$core $core.err
- check_gsignal $core.bt $child-$core
- check_err $core.err $child-$core
+ check_all $core.{bt,err} $child-$core
+
done
exit 0
9 years, 12 months
[unwinder-portable] [patch 3/3] Older OS compatibility bits
by Jan Kratochvil
Hi Mark,
this patch is for the branch 'portable' to be checked in wotj
'jankratochvil/unwindx86', that is for RHEL-5 systems.
It has been verified it is not needed on RHEL-6.
I guess these three patches can be checked into the 'portable' branch
together.
Thanks,
Jan
libdwfl/
2013-11-01 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Older OS compatibility bits.
* linux-core-attach.c (be64toh, le64toh, be32toh, le32toh): Provide
fallbacks if not defined by system.
tests/
2013-11-01 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Older OS compatibility bits.
* backtrace-child.c (main): Ignore non-zero ERRNO after pthread_create.
* run-backtrace.sh (check_err): Permit also No DWARF information found.
--- a/libdwfl/linux-core-attach.c
+++ b/libdwfl/linux-core-attach.c
@@ -29,6 +29,35 @@
#include "libdwflP.h"
#include <fcntl.h>
#include "system.h"
+#include <endian.h>
+#include <byteswap.h>
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# ifndef be64toh
+# define be64toh(x) bswap_64 (x)
+# endif
+# ifndef le64toh
+# define le64toh(x) (x)
+# endif
+# ifndef be32toh
+# define be32toh(x) bswap_32 (x)
+# endif
+# ifndef le32toh
+# define le32toh(x) (x)
+# endif
+#else
+# ifndef be64toh
+# define be64toh(x) (x)
+# endif
+# ifndef le64toh
+# define le64toh(x) bswap_64 (x)
+# endif
+# ifndef be32toh
+# define be32toh(x) (x)
+# endif
+# ifndef le32toh
+# define le32toh(x) bswap_32 (x)
+# endif
+#endif
#ifndef MIN
# define MIN(a, b) ((a) < (b) ? (a) : (b))
--- a/tests/backtrace-child.c
+++ b/tests/backtrace-child.c
@@ -141,7 +141,7 @@ main (int argc UNUSED, char **argv)
errno = 0;
pthread_t thread;
int i = pthread_create (&thread, NULL, start, NULL);
- assert_perror (errno);
+ errno = 0; // assert_perror (errno); // RHEL-5 may set errno with i == 0
assert (i == 0);
if (ptraceme)
{
--- a/tests/run-backtrace.sh
+++ b/tests/run-backtrace.sh
@@ -60,6 +60,10 @@ check_err()
if cmp -s <(echo "${abs_builddir}/backtrace: dwfl_thread_getframes: no matching address range") <(uniq <$1); then
return
fi
+ if cmp -s <(echo "${abs_builddir}/backtrace: dwfl_thread_getframes: No DWARF information found";
+ echo "${abs_builddir}/backtrace: dwfl_thread_getframes: no matching address range") <(sort -u <$1); then
+ return
+ fi
cat >&2 $1
echo >&2 $2: neither empty nor just out of DWARF
false
9 years, 12 months
[unwinder-portable] [patch 2/3] tests/ Handle missing CFI in .plt
by Jan Kratochvil
Hi Mark,
this patch is for the branch 'portable' to be checked in wotj
'jankratochvil/unwindx86', that is for RHEL-5 systems.
It has been verified it is not needed on RHEL-6.
Jakub Jelinek fixed GNU ld (gold is also fixed now) to provide CFI also for
.plt. But RHEL-5 still does not have it present.
The testsuite was explicitly testing unwinding for .plt. Disable it.
I guess these three patches can be checked into the 'portable' branch
together.
Thanks,
Jan
tests/
2013-11-01 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Handle missing CFI in .plt
(selfdump_callback): Update frame #0 for function raise.
(prepare_thread): Do not single-step into .plt.
--- a/tests/backtrace.c
+++ b/tests/backtrace.c
@@ -297,8 +297,10 @@ selfdump_callback (pid_t tid, unsigned frameno, Dwarf_Addr pc,
switch (frameno)
{
case 0:
- /* .plt has no symbols. */
- assert (symname == NULL);
+ // RHEL-5 has no .plt CFI.
+ assert (symname != NULL && strcmp (symname, "raise") == 0);
+ // /* .plt has no symbols. */
+ // assert (symname == NULL);
break;
case 1:
assert (symname != NULL && strcmp (symname, "sigusr2") == 0);
@@ -346,6 +348,7 @@ prepare_thread (pid_t pid2, Dwarf_Addr plt_start, Dwarf_Addr plt_end,
assert (got == pid2);
assert (WIFSTOPPED (status));
assert (WSTOPSIG (status) == SIGUSR1);
+ return; // RHEL-5 has no .plt CFI.
for (;;)
{
errno = 0;
9 years, 12 months
[unwinder-portable patch 1/3] Handle T-stopped detach for old kernels
by Jan Kratochvil
Hi Mark,
this patch is for the branch 'portable' to be checked in wotj
'jankratochvil/unwindx86', that is for RHEL-5 systems.
It has been verified it is not needed on RHEL-6 (despite RHEL-6 is not yet so
forgiving wrt SIGSTOP like the most recent Linux kernels are).
RHEL-5 kernels were not enough with
ptrace (PTRACE_DETACH, tid, NULL, SIGSTOP);
and they need to explicitly
syscall (__NR_tkill, tid, SIGSTOP);
beforehand.
Moreover for a short time after such PTRACE_DETACH the TID's /proc/PID/status
State may be R (running), so if one tries to PTRACE_ATTACH that time again the
attacher will not notice the process in fact should be T (stopped). Therefore
it detaches the process without __NR_tkill SIGSTOP and the process is no
longer stopped, as it should have been.
For GDB it is handled by:
http://pkgs.fedoraproject.org/cgit/gdb.git/tree/gdb-rhel5-compat.patch
Thanks,
Jan
libdwfl/
2013-11-01 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Handle T-stopped detach for old kernels.
* linux-pid-attach.c (struct pid_arg): New field stopped.
(ptrace_attach): New parameter stoppedp. Set it appropriately.
(pid_set_initial_registers): Pass the new field.
(pid_thread_detach): Handle the case of STOPPED for old kernels.
(__libdwfl_attach_state_for_pid): Initialize STOPPED.
tests/
2013-11-01 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Handle T-stopped detach for old kernels.
* backtrace.c: Include sys/syscall.h.
(linux_proc_pid_is_stopped): New function.
(ptrace_detach_stopped): Handle old kernels.
--- a/libdwfl/linux-pid-attach.c
+++ b/libdwfl/linux-pid-attach.c
@@ -42,6 +42,8 @@ struct pid_arg
DIR *dir;
/* It is 0 if not used. */
pid_t tid_attached;
+ /* TRUE if the process (first of its threads) was State: T (stopped). */
+ bool stopped;
};
static bool
@@ -69,7 +71,7 @@ linux_proc_pid_is_stopped (pid_t pid)
}
static bool
-ptrace_attach (pid_t tid)
+ptrace_attach (pid_t tid, bool *stoppedp)
{
if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
{
@@ -84,6 +86,7 @@ ptrace_attach (pid_t tid)
above. Which would make the waitpid below wait forever. So emulate
it. Since there can only be one SIGSTOP notification pending this is
safe. See also gdb/linux-nat.c linux_nat_post_attach_wait. */
+ *stoppedp = true;
syscall (__NR_tkill, tid, SIGSTOP);
ptrace (PTRACE_CONT, tid, NULL, NULL);
}
@@ -213,7 +216,7 @@ 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))
+ if (! ptrace_attach (tid, &pid_arg->stopped))
return false;
pid_arg->tid_attached = tid;
Dwfl_Process *process = thread->process;
@@ -237,7 +240,18 @@ 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;
- ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ if (! pid_arg->stopped)
+ {
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ return;
+ }
+ syscall (__NR_tkill, tid, SIGSTOP);
+ ptrace (PTRACE_DETACH, tid, NULL, (void *) (intptr_t) SIGSTOP);
+ // Wait till the SIGSTOP settles down.
+ int i;
+ for (i = 0; i < 100000; i++)
+ if (linux_proc_pid_is_stopped (tid))
+ break;
}
static const Dwfl_Thread_Callbacks pid_thread_callbacks =
@@ -271,6 +285,7 @@ __libdwfl_attach_state_for_pid (Dwfl *dwfl, pid_t pid)
}
pid_arg->dir = dir;
pid_arg->tid_attached = 0;
+ pid_arg->stopped = false;
if (! INTUSE(dwfl_attach_state) (dwfl, EM_NONE, pid, &pid_thread_callbacks,
pid_arg))
{
--- a/tests/backtrace.c
+++ b/tests/backtrace.c
@@ -35,6 +35,7 @@
#include <sys/user.h>
#include <fcntl.h>
#include <string.h>
+#include <sys/syscall.h>
#include ELFUTILS_HEADER(dwfl)
static void
@@ -370,13 +371,43 @@ prepare_thread (pid_t pid2, Dwarf_Addr plt_start, Dwarf_Addr plt_end,
#include <unistd.h>
#define tgkill(pid, tid, sig) syscall (__NR_tgkill, (pid), (tid), (sig))
+static bool
+linux_proc_pid_is_stopped (pid_t pid)
+{
+ char buffer[64];
+ FILE *procfile;
+ bool retval, have_state;
+
+ snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid);
+ procfile = fopen (buffer, "r");
+ if (procfile == NULL)
+ return false;
+
+ have_state = false;
+ while (fgets (buffer, sizeof (buffer), procfile) != NULL)
+ if (strncmp (buffer, "State:", 6) == 0)
+ {
+ have_state = true;
+ break;
+ }
+ retval = (have_state && strstr (buffer, "T (stopped)") != NULL);
+ fclose (procfile);
+ return retval;
+}
+
static void
ptrace_detach_stopped (pid_t pid)
{
+ syscall (__NR_tkill, pid, SIGSTOP);
errno = 0;
long l = ptrace (PTRACE_DETACH, pid, NULL, (void *) (intptr_t) SIGSTOP);
assert_perror (errno);
assert (l == 0);
+ // Wait till the SIGSTOP settles down.
+ int i;
+ for (i = 0; i < 100000; i++)
+ if (linux_proc_pid_is_stopped (pid))
+ break;
}
static void
9 years, 12 months
[COMMITTED] Mark new dwfl functions with version ELFUTILS_0.158.
by Mark Wielaard
It took two versions to get the new thread state and unwind dwfl functions
in. Make sure they carry the latest elfutils symbol version in which they
were actually added.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 8 ++++++++
libdw/libdw.map | 21 +++++++++++----------
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index dc0c4c9..33885ac 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,11 @@
+2013-11-26 Mark Wielaard <mjw(a)redhat.com>
+
+ * libdw.map (ELFUTILS_0.156): Move dwfl_attach_state, dwfl_pid,
+ dwfl_thread_dwfl, dwfl_thread_tid, dwfl_frame_thread,
+ dwfl_thread_state_registers, dwfl_thread_state_register_pc,
+ dwfl_getthreads, dwfl_thread_getframes and dwfl_frame_pc to ...
+ (ELFUTILS_0.158): ... here.
+
2013-11-09 Mark Wielaard <mjw(a)redhat.com>
* dwarf_getaranges.c (dwarf_getaranges): Read segment_size and
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 922608a..060c3df 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -259,16 +259,6 @@ ELFUTILS_0.156 {
global:
# Replaced ELFUTILS_0.122 version, which has a wrapper without add_p_vaddr.
dwfl_report_elf;
- dwfl_attach_state;
- dwfl_pid;
- dwfl_thread_dwfl;
- dwfl_thread_tid;
- dwfl_frame_thread;
- dwfl_thread_state_registers;
- dwfl_thread_state_register_pc;
- dwfl_getthreads;
- dwfl_thread_getframes;
- dwfl_frame_pc;
} ELFUTILS_0.149;
ELFUTILS_0.157 {
@@ -282,4 +272,15 @@ ELFUTILS_0.158 {
global:
# Replaced ELFUTILS_0.146 version, which has a wrapper without executable.
dwfl_core_file_report;
+
+ dwfl_attach_state;
+ dwfl_pid;
+ dwfl_thread_dwfl;
+ dwfl_thread_tid;
+ dwfl_frame_thread;
+ dwfl_thread_state_registers;
+ dwfl_thread_state_register_pc;
+ dwfl_getthreads;
+ dwfl_thread_getframes;
+ dwfl_frame_pc;
} ELFUTILS_0.157;
--
1.7.1
10 years