diff options
author | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2014-02-04 18:44:14 +0100 |
---|---|---|
committer | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2014-02-04 18:44:14 +0100 |
commit | 591a12a1d4c8843343eb999145d8bcc1efedf408 (patch) | |
tree | 3ee9bd92c9be0f9d47afdc936fc4f1995f21920a /gdb/ppc-linux-tdep.c | |
parent | cc0e89c519912e0e4e75a2fc0d836f715cdc6806 (diff) | |
download | gdb-591a12a1d4c8843343eb999145d8bcc1efedf408.zip gdb-591a12a1d4c8843343eb999145d8bcc1efedf408.tar.gz gdb-591a12a1d4c8843343eb999145d8bcc1efedf408.tar.bz2 |
PowerPC64 ELFv2 ABI: skip global entry point code
This patch handles another aspect of the ELFv2 ABI, which unfortunately
requires common code changes.
In ELFv2, functions may provide both a global and a local entry point.
The global entry point (where the function symbol points to) is intended
to be used for function-pointer or cross-module (PLT) calls, and requires
r12 to be set up to the entry point address itself. The local entry
point (which is found at a fixed offset after the global entry point,
as defined by bits in the symbol table entries' st_other field), instead
expects r2 to be set up to the current TOC.
Now, when setting a breakpoint on a function by name, you really want
that breakpoint to trigger either way, no matter whether the function
is called via its local or global entry point. Since the global entry
point will always fall through into the local entry point, the way to
achieve that is to simply set the breakpoint at the local entry point.
One way to do that would be to have prologue parsing skip the code
sequence that makes up the global entry point. Unfortunately, this
does not work reliably, since -for optimized code- GDB these days
will not actuall invoke the prologue parsing code but instead just
set the breakpoint at the symbol address and rely on DWARF being
correct at any point throughout the function ...
Unfortunately, I don't really see any way to express the notion of
local entry points with the current set of gdbarch callbacks.
Thus this patch adds a new callback, skip_entrypoint, that is
somewhat analogous to skip_prologue, but is called every time
GDB needs to determine a function start address, even in those
cases where GDB decides to not call skip_prologue.
As a side effect, the skip_entrypoint implementation on ppc64
does not need to perform any instruction parsing; it can simply
rely on the local entry point flags in the symbol table entry.
With this implemented, two test cases would still fail to set
the breakpoint correctly, but that's because they use the construct:
gdb_test "break *hello"
Now, using "*hello" explicitly instructs GDB to set the breakpoint
at the numerical value of "hello" treated as function pointer, so
it will by definition only hit the global entry point.
I think this behaviour is unavoidable, but acceptable -- most people
do not use this construct, and if they do, they get what they
asked for ...
In one of those two test cases, use of this construct is really
not appropriate. I think this was added way back when as a means
to work around prologue skipping problems on some platforms. These
days that shouldn't really be necessary any more ...
For the other (step-bt), we really want to make sure backtracing
works on the very first instruction of the routine. To enable that
test also on powerpc64le-linux, we can modify the code to call the
test function via function pointer (which makes it use the global
entry point in the ELFv2 ABI).
gdb/ChangeLog:
* gdbarch.sh (skip_entrypoint): New callback.
* gdbarch.c, gdbarch.h: Regenerate.
* symtab.c (skip_prologue_sal): Call gdbarch_skip_entrypoint.
* infrun.c (fill_in_stop_func): Likewise.
* ppc-linux-tdep.c: Include "elf/ppc64.h".
(ppc_elfv2_elf_make_msymbol_special): New function.
(ppc_elfv2_skip_entrypoint): Likewise.
(ppc_linux_init_abi): Install them for ELFv2.
gdb/testsuite/ChangeLog:
* gdb.base/sigbpt.exp: Do not use "*" when setting breakpoint
on a function.
* gdb.base/step-bt.c: Call hello via function pointer to make
sure its first instruction is executed on powerpc64le-linux.
Diffstat (limited to 'gdb/ppc-linux-tdep.c')
-rw-r--r-- | gdb/ppc-linux-tdep.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c index 25008c0..6d3b2d8 100644 --- a/gdb/ppc-linux-tdep.c +++ b/gdb/ppc-linux-tdep.c @@ -44,6 +44,7 @@ #include "observer.h" #include "auxv.h" #include "elf/common.h" +#include "elf/ppc64.h" #include "exceptions.h" #include "arch-utils.h" #include "spu-tdep.h" @@ -876,6 +877,55 @@ ppc_linux_core_read_description (struct gdbarch *gdbarch, } } + +/* Implementation of `gdbarch_elf_make_msymbol_special', as defined in + gdbarch.h. This implementation is used for the ELFv2 ABI only. */ + +static void +ppc_elfv2_elf_make_msymbol_special (asymbol *sym, struct minimal_symbol *msym) +{ + elf_symbol_type *elf_sym = (elf_symbol_type *)sym; + + /* If the symbol is marked as having a local entry point, set a target + flag in the msymbol. We currently only support local entry point + offsets of 8 bytes, which is the only entry point offset ever used + by current compilers. If/when other offsets are ever used, we will + have to use additional target flag bits to store them. */ + switch (PPC64_LOCAL_ENTRY_OFFSET (elf_sym->internal_elf_sym.st_other)) + { + default: + break; + case 8: + MSYMBOL_TARGET_FLAG_1 (msym) = 1; + break; + } +} + +/* Implementation of `gdbarch_skip_entrypoint', as defined in + gdbarch.h. This implementation is used for the ELFv2 ABI only. */ + +static CORE_ADDR +ppc_elfv2_skip_entrypoint (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + struct bound_minimal_symbol fun; + int local_entry_offset = 0; + + fun = lookup_minimal_symbol_by_pc (pc); + if (fun.minsym == NULL) + return pc; + + /* See ppc_elfv2_elf_make_msymbol_special for how local entry point + offset values are encoded. */ + if (MSYMBOL_TARGET_FLAG_1 (fun.minsym)) + local_entry_offset = 8; + + if (SYMBOL_VALUE_ADDRESS (fun.minsym) <= pc + && pc < SYMBOL_VALUE_ADDRESS (fun.minsym) + local_entry_offset) + return SYMBOL_VALUE_ADDRESS (fun.minsym) + local_entry_offset; + + return pc; +} + /* Implementation of `gdbarch_stap_is_single_operand', as defined in gdbarch.h. */ @@ -1349,6 +1399,13 @@ ppc_linux_init_abi (struct gdbarch_info info, set_gdbarch_elf_make_msymbol_special (gdbarch, ppc64_elf_make_msymbol_special); } + else + { + set_gdbarch_elf_make_msymbol_special + (gdbarch, ppc_elfv2_elf_make_msymbol_special); + + set_gdbarch_skip_entrypoint (gdbarch, ppc_elfv2_skip_entrypoint); + } /* Shared library handling. */ set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code); |