This bug has been reported on PR breakpoints/24541, but it is possible
to reproduce it easily by running:
make check-gdb TESTS=gdb.base/stap-probe.exp RUNTESTFLAGS='--target_board unix/-m32'
The underlying cause is kind of complex, and involves decisions made
by GCC and the sys/sdt.h header file about how to represent a probe
argument that lives in a register in 32-bit programs. I'll use
Andrew's example on the bug to illustrate the problem.
libstdc++ has a probe named "throw" with two arguments. On i386, the
probe is:
stapsdt 0x00000028 NT_STAPSDT (SystemTap probe descriptors)
Provider: libstdcxx
Name: throw
Location: 0x00072c96, Base: 0x00133d64, Semaphore: 0x00000000
Arguments: 4@%si 4@%di
I.e., the first argument is an unsigned 32-bit value (represented by
the "4@") that lives on %si, and the second argument is an unsigned
32-bit value that lives on %di. Note the discrepancy between the
argument size reported by the probe (32-bit) and the register size
being used to store the value (16-bit).
However, if you take a look at the disassemble of a program that uses
this probe, you will see:
00072c80 <__cxa_throw@@CXXABI_1.3>:
72c80: 57 push %edi
72c81: 56 push %esi
72c82: 53 push %ebx
72c83: 8b 74 24 10 mov 0x10(%esp),%esi
72c87: e8 74 bf ff ff call 6ec00 <__cxa_finalize@plt+0x980>
72c8c: 81 c3 74 e3 10 00 add $0x10e374,%ebx
72c92: 8b 7c 24 14 mov 0x14(%esp),%edi
72c96: 90 nop <----------------- PROBE IS HERE
72c97: e8 d4 a2 ff ff call 6cf70 <__cxa_get_globals@plt>
72c9c: 83 40 04 01 addl $0x1,0x4(%eax)
72ca0: 83 ec 04 sub $0x4,%esp
72ca3: ff 74 24 1c pushl 0x1c(%esp)
72ca7: 57 push %edi
72ca8: 56 push %esi
72ca9: e8 62 a3 ff ff call 6d010 <__cxa_init_primary_exception@plt>
72cae: 8d 70 40 lea 0x40(%eax),%esi
72cb1: c7 00 01 00 00 00 movl $0x1,(%eax)
72cb7: 89 34 24 mov %esi,(%esp)
72cba: e8 61 96 ff ff call 6c320 <_Unwind_RaiseException@plt>
72cbf: 89 34 24 mov %esi,(%esp)
72cc2: e8 c9 84 ff ff call 6b190 <__cxa_begin_catch@plt>
72cc7: e8 d4 b3 ff ff call 6e0a0 <_ZSt9terminatev@plt>
72ccc: 66 90 xchg %ax,%ax
72cce: 66 90 xchg %ax,%ax
Note how the program is actually using %edi, and not %di, to store the
second argument. This is the problem here.
GDB will basically read the probe argument, then read the contents of
%di, and then cast this value to uint32_t, which causes the wrong
value to be obtained. In the gdb.base/stap-probe.exp case, this makes
GDB read the wrong memory location, and not be able to display a test
string. In Andrew's example, this causes GDB to actually stop at a
"catch throw" when it should actually have *not* stopped.
After some discussion with Frank Eigler and Jakub Jelinek, it was
decided that this bug should be fixed on the client side (i.e., the
program that actually reads the probes), and this is why I'm proposing
this patch.
The idea is simple: we will have a gdbarch method, which, for now, is
only used by i386. The generic code that deals with register operands
on gdb/stap-probe.c will call this method if it exists, passing the
current parse information, the register name and its number.
The i386 method will then verify if the register size is greater or
equal than the size reported by the stap probe (the "4@" part). If it
is, we're fine. Otherwise, it will check if we're dealing with any of
the "extendable" registers (like ax, bx, si, di, sp, etc.). If we
are, it will change the register name to include the "e" prefix.
I have tested the patch here in many scenarios, and it fixes Andrew's
bug and also the regressions I mentioned before, on
gdb.base/stap-probe.exp. No regressions where found on other tests.
Comments?
gdb/ChangeLog:
2019-06-27 Sergio Durigan Junior <sergiodj@redhat.com>
PR breakpoints/24541
* gdbarch.c: Regenerate.
* gdbarch.h: Regenerate.
* gdbarch.sh: Add 'stap_adjust_register'.
* i386-tdep.c: Include '<unordered_set>'.
(i386_stap_adjust_register): New function.
(i386_elf_init_abi): Register 'i386_stap_adjust_register'.
* stap-probe.c (stap_parse_register_operand): Call
'gdbarch_stap_adjust_register'.
|
||
|---|---|---|
| bfd | ||
| binutils | ||
| config | ||
| contrib | ||
| cpu | ||
| elfcpp | ||
| etc | ||
| gas | ||
| gdb | ||
| gnulib | ||
| gold | ||
| gprof | ||
| include | ||
| intl | ||
| ld | ||
| libctf | ||
| libdecnumber | ||
| libiberty | ||
| opcodes | ||
| readline | ||
| sim | ||
| texinfo | ||
| zlib | ||
| .cvsignore | ||
| .gitattributes | ||
| .gitignore | ||
| ar-lib | ||
| ChangeLog | ||
| compile | ||
| config-ml.in | ||
| config.guess | ||
| config.rpath | ||
| config.sub | ||
| configure | ||
| configure.ac | ||
| COPYING | ||
| COPYING3 | ||
| COPYING3.LIB | ||
| COPYING.LIB | ||
| COPYING.LIBGLOSS | ||
| COPYING.NEWLIB | ||
| depcomp | ||
| djunpack.bat | ||
| install-sh | ||
| libtool.m4 | ||
| lt~obsolete.m4 | ||
| ltgcc.m4 | ||
| ltmain.sh | ||
| ltoptions.m4 | ||
| ltsugar.m4 | ||
| ltversion.m4 | ||
| MAINTAINERS | ||
| Makefile.def | ||
| Makefile.in | ||
| Makefile.tpl | ||
| makefile.vms | ||
| missing | ||
| mkdep | ||
| mkinstalldirs | ||
| move-if-change | ||
| multilib.am | ||
| README | ||
| README-maintainer-mode | ||
| setup.com | ||
| src-release.sh | ||
| symlink-tree | ||
| test-driver | ||
| ylwrap | ||
README for GNU development tools This directory contains various GNU compilers, assemblers, linkers, debuggers, etc., plus their support routines, definitions, and documentation. If you are receiving this as part of a GDB release, see the file gdb/README. If with a binutils release, see binutils/README; if with a libg++ release, see libg++/README, etc. That'll give you info about this package -- supported targets, how to use it, how to report bugs, etc. It is now possible to automatically configure and build a variety of tools with one command. To build all of the tools contained herein, run the ``configure'' script here, e.g.: ./configure make To install them (by default in /usr/local/bin, /usr/local/lib, etc), then do: make install (If the configure script can't determine your type of computer, give it the name as an argument, for instance ``./configure sun4''. You can use the script ``config.sub'' to test whether a name is recognized; if it is, config.sub translates it to a triplet specifying CPU, vendor, and OS.) If you have more than one compiler on your system, it is often best to explicitly set CC in the environment before running configure, and to also set CC when running make. For example (assuming sh/bash/ksh): CC=gcc ./configure make A similar example using csh: setenv CC gcc ./configure make Much of the code and documentation enclosed is copyright by the Free Software Foundation, Inc. See the file COPYING or COPYING.LIB in the various directories, for a description of the GNU General Public License terms under which you can copy the files. REPORTING BUGS: Again, see gdb/README, binutils/README, etc., for info on where and how to report problems.