This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: [PATCH RFA] Misc fixes for GNU/Linux/ARM target
- To: "Kevin Buettner" <kevinb at cygnus dot com>
- Subject: Re: [PATCH RFA] Misc fixes for GNU/Linux/ARM target
- From: "Scott Bambrough" <scottb at netwinder dot org>
- Date: Mon, 4 Sep 2000 22:36:53 -0400
- Cc: <gdb-patches at sourceware dot cygnus dot com>
- References: <1000810072418.ZM20156@ocotillo.lan>
Hi Kevin,
These patches are fine. Sorry to take so long to evaluate them.
Scott
----- Original Message -----
From: Kevin Buettner <kevinb@cygnus.com>
To: Scott Bambrough <scottb@netwinder.org>
Cc: <gdb-patches@sourceware.cygnus.com>
Sent: Thursday, August 10, 2000 3:24 AM
Subject: [PATCH RFA] Misc fixes for GNU/Linux/ARM target
> The patches below add support for backtracing through signal handlers
> and makes the prologue scanning code somewhat less naive about
> optimized code on GNU/Linux/ARM. In addition, there are several small
> fixes so that arm-linux-tdep.c will build without warnings.
>
> These patches fix the following failures on GNU/Linux/ARM:
>
> FAIL: gdb.base/annota1.exp: backtrace @ signal handler
> FAIL: gdb.base/mips_pro.exp: backtrace
> FAIL: gdb.base/selftest.exp: backtrace through signal handler
> FAIL: gdb.base/signals.exp: bt in signals.exp
>
> They do not introduce any new failures.
>
> Okay to commit?
>
> * config/arm/tm-linux.h (arm_linux_sigcontext_register_address,
> arm_linux_in_sigtramp): Declare.
> (IN_SIGTRAMP, SIGCONTEXT_REGISTER_ADDRESS): Define.
> * arm-tdep.c (SIGCONTEXT_REGISTER_ADDRESS): Define to be 0
> if not already defined by tm.h.
> (arm_scan_prologue): Don't assume that the prologue instructions
> will be in a contiguous clump.
> (arm_init_extra_frame_info): Add support for sigtramp frames.
> (arm_pc_is_thumb, arm_pc_is_thumb_dummy): Change type of
> `memaddr' from bfd_vma to CORE_ADDR.
> * arm-linux-tdep.c (gdbcore.h, frame.h): Include.
> (arm_pc_is_thumb): Declare.
> (arm_linux_skip_solib_resolver): Fix printf() statement. [Which
> shouldn't be there anyway.]
> (ARM_LINUX_SIGRETURN_INSTR, ARM_LINUX_RT_SIGRETURN_INSTR): New
> defines.
> (arm_linux_in_sigtramp, arm_linux_sigcontext_register_address):
> New functions.
>
> Index: arm-linux-tdep.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/arm-linux-tdep.c,v
> retrieving revision 1.5
> diff -u -p -r1.5 arm-linux-tdep.c
> --- arm-linux-tdep.c 2000/05/25 18:24:33 1.5
> +++ arm-linux-tdep.c 2000/08/10 07:05:40
> @@ -23,12 +23,18 @@
> #include "value.h"
> #include "gdbtypes.h"
> #include "floatformat.h"
> +#include "gdbcore.h"
> +#include "frame.h"
>
> /* For arm_linux_skip_solib_resolver. */
> #include "symtab.h"
> #include "symfile.h"
> #include "objfiles.h"
>
> +/* FIXME: Put in common header file shared between arm-tdep.c and
> + arm-linux-tdep.c */
> +int arm_pc_is_thumb (CORE_ADDR memaddr);
> +
> #ifdef GET_LONGJMP_TARGET
>
> /* Figure out where the longjmp will land. We expect that we have
> @@ -425,12 +431,93 @@ arm_linux_skip_solib_resolver (CORE_ADDR
>
> /* Plug in functions for other kinds of resolvers here. */
> result = skip_hurd_resolver (pc);
> - printf ("Result = 0x%08x\n");
> + printf ("Result = 0x%08lx\n", result);
> if (result)
> return result;
>
>
> return 0;
> +}
> +
> +/* The constants below were determined by examining the following files
> + in the linux kernel sources:
> +
> + arch/arm/kernel/signal.c
> + - see SWI_SYS_SIGRETURN and SWI_SYS_RT_SIGRETURN
> + include/asm-arm/unistd.h
> + - see __NR_sigreturn, __NR_rt_sigreturn, and __NR_SYSCALL_BASE */
> +
> +#define ARM_LINUX_SIGRETURN_INSTR 0xef900077
> +#define ARM_LINUX_RT_SIGRETURN_INSTR 0xef9000ad
> +
> +/* arm_linux_in_sigtramp determines if PC points at one of the
> + instructions which cause control to return to the Linux kernel upon
> + return from a signal handler. FUNC_NAME is unused. */
> +
> +int
> +arm_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
> +{
> + unsigned long inst;
> +
> + inst = read_memory_integer (pc, 4);
> +
> + return (inst == ARM_LINUX_SIGRETURN_INSTR
> + || inst == ARM_LINUX_RT_SIGRETURN_INSTR);
> +
> +}
> +
> +/* arm_linux_sigcontext_register_address returns the address in the
> + sigcontext of register REGNO given a stack pointer value SP and
> + program counter value PC. The value 0 is returned if PC is not
> + pointing at one of the signal return instructions or if REGNO is
> + not saved in the sigcontext struct. */
> +
> +CORE_ADDR
> +arm_linux_sigcontext_register_address (CORE_ADDR sp, CORE_ADDR pc, int
regno)
> +{
> + unsigned long inst;
> + CORE_ADDR reg_addr = 0;
> +
> + inst = read_memory_integer (pc, 4);
> +
> + if (inst == ARM_LINUX_SIGRETURN_INSTR || inst ==
ARM_LINUX_RT_SIGRETURN_INSTR)
> + {
> + CORE_ADDR sigcontext_addr;
> +
> + /* The sigcontext structure is at different places for the two
> + signal return instructions. For ARM_LINUX_SIGRETURN_INSTR,
> + it starts at the SP value. For ARM_LINUX_RT_SIGRETURN_INSTR,
> + it is at SP+8. For the latter instruction, it may also be
> + the case that the address of this structure may be determined
> + by reading the 4 bytes at SP, but I'm not convinced this is
> + reliable.
> +
> + In any event, these magic constants (0 and 8) may be
> + determined by examining struct sigframe and struct
> + rt_sigframe in arch/arm/kernel/signal.c in the Linux kernel
> + sources. */
> +
> + if (inst == ARM_LINUX_RT_SIGRETURN_INSTR)
> + sigcontext_addr = sp + 8;
> + else /* inst == ARM_LINUX_SIGRETURN_INSTR */
> + sigcontext_addr = sp + 0;
> +
> + /* The layout of the sigcontext structure for ARM GNU/Linux is
> + in include/asm-arm/sigcontext.h in the Linux kernel sources.
> +
> + There are three 4-byte fields which precede the saved r0
> + field. (This accounts for the 12 in the code below.) The
> + sixteen registers (4 bytes per field) follow in order. The
> + PSR value follows the sixteen registers which accounts for
> + the constant 19 below. */
> +
> + if (0 <= regno && regno <= PC_REGNUM)
> + reg_addr = sigcontext_addr + 12 + (4 * regno);
> + else if (regno == PS_REGNUM)
> + reg_addr = sigcontext_addr + 19 * 4;
> + }
> +
> + return reg_addr;
> }
>
> void
> Index: arm-tdep.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/arm-tdep.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 arm-tdep.c
> --- arm-tdep.c 2000/06/08 00:52:56 1.7
> +++ arm-tdep.c 2000/08/10 07:05:43
> @@ -30,6 +30,31 @@
> #include "dis-asm.h" /* For register flavors. */
> #include <ctype.h> /* for isupper () */
>
> +/* Each OS has a different mechanism for accessing the various
> + registers stored in the sigcontext structure.
> +
> + SIGCONTEXT_REGISTER_ADDRESS should be defined to the name (or
> + function pointer) which may be used to determine the addresses
> + of the various saved registers in the sigcontext structure.
> +
> + For the ARM target, there are three parameters to this function.
> + The first is the pc value of the frame under consideration, the
> + second the stack pointer of this frame, and the last is the
> + register number to fetch.
> +
> + If the tm.h file does not define this macro, then it's assumed that
> + no mechanism is needed and we define SIGCONTEXT_REGISTER_ADDRESS to
> + be 0.
> +
> + When it comes time to multi-arching this code, see the identically
> + named machinery in ia64-tdep.c for an example of how it could be
> + done. It should not be necessary to modify the code below where
> + this macro is used. */
> +
> +#ifndef SIGCONTEXT_REGISTER_ADDRESS
> +#define SIGCONTEXT_REGISTER_ADDRESS 0
> +#endif
> +
> extern void _initialize_arm_tdep (void);
>
> /* Number of different reg name sets (options). */
> @@ -226,7 +251,7 @@ static int caller_is_thumb;
> function. */
>
> int
> -arm_pc_is_thumb (bfd_vma memaddr)
> +arm_pc_is_thumb (CORE_ADDR memaddr)
> {
> struct minimal_symbol *sym;
>
> @@ -250,7 +275,7 @@ arm_pc_is_thumb (bfd_vma memaddr)
> dummy being called from a Thumb function. */
>
> int
> -arm_pc_is_thumb_dummy (bfd_vma memaddr)
> +arm_pc_is_thumb_dummy (CORE_ADDR memaddr)
> {
> CORE_ADDR sp = read_sp ();
>
> @@ -725,14 +750,42 @@ arm_scan_prologue (struct frame_info *fi
> the symbol table, peek in the stack frame to find the PC. */
> if (find_pc_partial_function (fi->pc, NULL, &prologue_start,
&prologue_end))
> {
> - /* Assume the prologue is everything between the first instruction
> - in the function and the first source line. */
> - struct symtab_and_line sal = find_pc_line (prologue_start, 0);
> + /* One way to find the end of the prologue (which works well
> + for unoptimized code) is to do the following:
>
> - if (sal.line == 0) /* no line info, use current PC */
> - prologue_end = fi->pc;
> - else if (sal.end < prologue_end) /* next line begins after fn end
*/
> - prologue_end = sal.end; /* (probably means no prologue) */
> + struct symtab_and_line sal = find_pc_line (prologue_start, 0);
> +
> + if (sal.line == 0)
> + prologue_end = fi->pc;
> + else if (sal.end < prologue_end)
> + prologue_end = sal.end;
> +
> + This mechanism is very accurate so long as the optimizer
> + doesn't move any instructions from the function body into the
> + prologue. If this happens, sal.end will be the last
> + instruction in the first hunk of prologue code just before
> + the first instruction that the scheduler has moved from
> + the body to the prologue.
> +
> + In order to make sure that we scan all of the prologue
> + instructions, we use a slightly less accurate mechanism which
> + may scan more than necessary. To help compensate for this
> + lack of accuracy, the prologue scanning loop below contains
> + several clauses which'll cause the loop to terminate early if
> + an implausible prologue instruction is encountered.
> +
> + The expression
> +
> + prologue_start + 64
> +
> + is a suitable endpoint since it accounts for the largest
> + possible prologue plus up to five instructions inserted by
> + the scheduler. */
> +
> + if (prologue_end > prologue_start + 64)
> + {
> + prologue_end = prologue_start + 64; /* See above. */
> + }
> }
> else
> {
> @@ -740,10 +793,7 @@ arm_scan_prologue (struct frame_info *fi
> PC is the address of the stmfd + 8. */
> prologue_start = ADDR_BITS_REMOVE (read_memory_integer (fi->frame,
4))
> - 8;
> - prologue_end = prologue_start + 64; /* This is all the insn's
> - that could be in the prologue,
> - plus room for 5 insn's inserted
> - by the scheduler. */
> + prologue_end = prologue_start + 64; /* See above. */
> }
>
> /* Now search the prologue looking for instructions that set up the
> @@ -833,6 +883,10 @@ arm_scan_prologue (struct frame_info *fi
> fi->fsr.regs[fp_start_reg++] = sp_offset;
> }
> }
> + else if ((insn & 0xf0000000) != 0xe0000000)
> + break; /* Condition not true, exit early */
> + else if ((insn & 0xfe200000) == 0xe8200000) /* ldm? */
> + break; /* Don't scan past a block load */
> else
> /* The optimizer might shove anything into the prologue,
> so we just skip what we don't recognize. */
> @@ -973,6 +1027,40 @@ arm_init_extra_frame_info (int fromleaf,
> }
> else
> #endif
> +
> + /* Determine whether or not we're in a sigtramp frame.
> + Unfortunately, it isn't sufficient to test
> + fi->signal_handler_caller because this value is sometimes set
> + after invoking INIT_EXTRA_FRAME_INFO. So we test *both*
> + fi->signal_handler_caller and IN_SIGTRAMP to determine if we need
> + to use the sigcontext addresses for the saved registers.
> +
> + Note: If an ARM IN_SIGTRAMP method ever needs to compare against
> + the name of the function, the code below will have to be changed
> + to first fetch the name of the function and then pass this name
> + to IN_SIGTRAMP. */
> +
> + if (SIGCONTEXT_REGISTER_ADDRESS
> + && (fi->signal_handler_caller || IN_SIGTRAMP (fi->pc, 0)))
> + {
> + CORE_ADDR sp;
> +
> + if (!fi->next)
> + sp = read_sp();
> + else
> + sp = fi->next->frame - fi->next->frameoffset + fi->next->framesize;
> +
> + for (reg = 0; reg < NUM_REGS; reg++)
> + fi->fsr.regs[reg] = SIGCONTEXT_REGISTER_ADDRESS (sp, fi->pc, reg);
> +
> + /* FIXME: What about thumb mode? */
> + fi->framereg = SP_REGNUM;
> + fi->frame = read_memory_integer (fi->fsr.regs[fi->framereg], 4);
> + fi->framesize = 0;
> + fi->frameoffset = 0;
> +
> + }
> + else
> {
> arm_scan_prologue (fi);
>
> Index: config/arm/tm-linux.h
> ===================================================================
> RCS file: /cvs/src/src/gdb/config/arm/tm-linux.h,v
> retrieving revision 1.5
> diff -u -p -r1.5 tm-linux.h
> --- tm-linux.h 2000/05/25 18:24:33 1.5
> +++ tm-linux.h 2000/08/10 07:05:44
> @@ -135,4 +135,26 @@ extern CORE_ADDR in_svr4_dynsym_resolve_
> #define IN_SOLIB_DYNSYM_RESOLVE_CODE in_svr4_dynsym_resolve_code */
> #endif
>
> +/* When the ARM Linux kernel invokes a signal handler, the return
> + address points at a special instruction which'll trap back into
> + the kernel. These definitions are used to identify this bit of
> + code as a signal trampoline in order to support backtracing
> + through calls to signal handlers. */
> +
> +int arm_linux_in_sigtramp (CORE_ADDR pc, char *name);
> +#define IN_SIGTRAMP(pc, name) arm_linux_in_sigtramp (pc, name)
> +
> +/* Each OS has different mechanisms for accessing the various
> + registers stored in the sigcontext structure. These definitions
> + provide a mechanism by which the generic code in arm-tdep.c can
> + find the addresses at which various registers are saved at in the
> + sigcontext structure. If SIGCONTEXT_REGISTER_ADDRESS is not
> + defined, arm-tdep.c will define it to be 0. (See ia64-tdep.c and
> + ia64-linux-tdep.c to see what a similar mechanism looks like when
> + multi-arched.) */
> +
> +extern CORE_ADDR arm_linux_sigcontext_register_address (CORE_ADDR,
CORE_ADDR,
> + int);
> +#define SIGCONTEXT_REGISTER_ADDRESS arm_linux_sigcontext_register_address
> +
> #endif /* TM_ARMLINUX_H */
>
>