From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23113 invoked by alias); 2 Dec 2003 04:26:51 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 23046 invoked from network); 2 Dec 2003 04:26:48 -0000 Received: from unknown (HELO takamaka.act-europe.fr) (142.179.108.108) by sources.redhat.com with SMTP; 2 Dec 2003 04:26:48 -0000 Received: by takamaka.act-europe.fr (Postfix, from userid 507) id AFAA647D5B; Mon, 1 Dec 2003 20:26:46 -0800 (PST) Date: Tue, 02 Dec 2003 04:26:00 -0000 From: Joel Brobecker To: gdb-patches@sources.redhat.com Subject: [RFA] OSF/1 - "next" over prologueless function call Message-ID: <20031202042646.GW1186@gnat.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="X1bOJ3K7DJ5YkBrT" Content-Disposition: inline User-Agent: Mutt/1.4i X-SW-Source: 2003-12/txt/msg00037.txt.bz2 --X1bOJ3K7DJ5YkBrT Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-length: 4614 Hello, We have recently noticed this failure. To demonstrate it, you'll need to use the GNAT compiler because the sources that caused the problem are in the GNAT runtime. Use the following little Ada program: with Ada.Text_IO; use Ada.Text_IO; procedure Foo is begin Put_Line ("Hello World."); Put_Line ("Me again."); end Foo; Compile it using the following command: % gnatmake -g foo The following GDB transcript demonstrates the problem: (gdb) b foo.adb:5 Breakpoint 1 at 0x120015a18: file foo.adb, line 5. (gdb) run Starting program: /[...]/foo Breakpoint 1, _ada_foo () at foo.adb:5 5 Put_Line ("Hello World."); (gdb) n 0x0000000120023fe8 in ada__text_io__put_line__2 () at a-textio.adb:6 6 a-textio.adb: No such file or directory. in a-textio.adb The expected behavior was for GDB to stop at line 6 of foo.adb, not inside Put_Line: (gdb) n Hello World. 6 Put_Line ("Me again."); Here is what happens. First, after having hit the breakpoint at line 5, GDB sees the following code for line 5: #.stabn 68,0,5,$LM2 lda $1,$LC0 lda $2,$LC1 bis $31,$1,$16 bis $31,$2,$17 jsr $26,ada__text_io__put_line__2 ldgp $29,0($26) (for the curious, LC0 and LC1 are the string "Hello World." and its bounds). That's the assembly code as generated by GCC. However, if you look at the actual assembly code, as produced after the link, you will see that the OSF/1 linker has done a little optimization (which I find is done quite often). Here is how the jsr instruction has been modified: bsr ra,0x120023fe8 The jump is actually made to the third instruction of Put_Line. So when GDB starts doing the "next" operation, it eventually receives a sigtrap. Normally, GDB should detect that we are inside a function call and therefore should step_over_function(). But it fails to recognize this situation because of the conjunction of the following two factors: 1. The stop_pc is not at the "official" Put_Line function start address 2. The function does not have a prologue So the following test fails in infrun.c:handle_inferior_event() fails, and GDB incorrectly thinks that we have landed at the next line of code: if (((stop_pc == ecs->stop_func_start /* Quick test */ || in_prologue (stop_pc, ecs->stop_func_start)) && !IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, ecs->stop_func_name)) || IN_SOLIB_CALL_TRAMPOLINE (stop_pc, ecs->stop_func_name) || ecs->stop_func_name == 0) Just FYI, the first instructions of Put_Line are: : ldah gp,8191(t12) : lda gp,-1088(gp) : ldq t8,-28008(gp) : nop : mov a0,t0 : mov a1,a2 : ldq a0,0(t8) : mov t0,a1 : ldq t12,-30040(gp) : jmp zero,(t12),0x120023d50 If it wasn't for the linker optimization, the check for "top_pc == ecs->stop_func_start" would probably have kicked in, and all would have been fine. So I suggest we refine this test to use a new gdbarch function which, by default, would be the exact equivalent of this equality check. But I then provide an OSF/1-specific version of this function that checks that we stopped either at the first instruction of the function, or right after the couple of instructions forming the ldgp macro. 2003-12-01 J. Brobecker * gdbarch.sh (at_function_start): New gdbarch function. * gdbarch.h: Regenerate. * gdbarch.c: Regenerate. * arch-utils.c (default_at_function_start): New function. * arch-utils.h (default_at_function_start): Add prototype. * infrun.c (handle_inferior_event): Use new gdbarch function at_function_start to properly detect function calls during STEP_OVER_ALL. * alpha-osf1-tdep.c (alpha_osf1_at_function_start): New function. (alpha_osf1_init_abi): Set the gdbarch at_function_call function. Tested on alpha-osf1 and x86-linux. No regression. OK to apply? Thanks, -- Joel --X1bOJ3K7DJ5YkBrT Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="next.diff" Content-length: 5699 Index: gdbarch.sh =================================================================== RCS file: /cvs/src/src/gdb/gdbarch.sh,v retrieving revision 1.288 diff -u -p -r1.288 gdbarch.sh --- gdbarch.sh 14 Nov 2003 21:22:42 -0000 1.288 +++ gdbarch.sh 2 Dec 2003 03:52:38 -0000 @@ -771,6 +771,24 @@ F::FETCH_POINTER_ARGUMENT:CORE_ADDR:fetc # Return the appropriate register set for a core file section with # name SECT_NAME and size SECT_SIZE. M:::const struct regset *:regset_from_core_section:const char *sect_name, size_t sect_size:sect_name, sect_size + +# Return nonzero if PC points to the first "effective" instruction of +# the function starting at FUNC_START. +# +# The default implementation which should be appropriate for most +# targets is to return nonzero if PC is equal to FUNC_START. However, +# it can happen that the address of a subroutine call instruction +# is changed to jump slightly after the begining of the function, +# thus making the "effective" function start address a bit different +# from the start address read from the symbol table or the debugging +# information. +# +# For instance, on alpha-tru64, the first two instructions of a function +# are often setting up the GP. The linker sometimes determines that +# these instructions are not needed for the proper execution of the +# program and therefore virtually optimizes them out by changing the +# jump address to branch 2 instructions later. +f:2:AT_FUNCTION_START:int:at_function_start:CORE_ADDR pc, CORE_ADDR func_start:pc, func_start::0:default_at_function_start::0 EOF } Index: arch-utils.c =================================================================== RCS file: /cvs/src/src/gdb/arch-utils.c,v retrieving revision 1.108 diff -u -p -r1.108 arch-utils.c --- arch-utils.c 14 Nov 2003 21:22:42 -0000 1.108 +++ arch-utils.c 2 Dec 2003 03:52:39 -0000 @@ -370,6 +370,12 @@ default_stabs_argument_has_addr (struct return 0; } +int +default_at_function_start (CORE_ADDR pc, CORE_ADDR func_start) +{ + return pc == func_start; +} + /* Functions to manipulate the endianness of the target. */ Index: arch-utils.h =================================================================== RCS file: /cvs/src/src/gdb/arch-utils.h,v retrieving revision 1.65 diff -u -p -r1.65 arch-utils.h --- arch-utils.h 23 Nov 2003 21:32:42 -0000 1.65 +++ arch-utils.h 2 Dec 2003 03:52:39 -0000 @@ -142,6 +142,8 @@ extern void legacy_value_to_register (st extern int default_stabs_argument_has_addr (struct gdbarch *gdbarch, struct type *type); +extern int default_at_function_start (CORE_ADDR pc, CORE_ADDR func_start); + /* For compatibility with older architectures, returns (LEGACY_SIM_REGNO_IGNORE) when the register doesn't have a valid name. */ Index: infrun.c =================================================================== RCS file: /cvs/src/src/gdb/infrun.c,v retrieving revision 1.122 diff -u -p -r1.122 infrun.c --- infrun.c 25 Nov 2003 16:01:36 -0000 1.122 +++ infrun.c 2 Dec 2003 03:52:41 -0000 @@ -2472,7 +2472,7 @@ process_event_stop_test: return; } - if (((stop_pc == ecs->stop_func_start /* Quick test */ + if (((AT_FUNCTION_START (stop_pc, ecs->stop_func_start) || in_prologue (stop_pc, ecs->stop_func_start)) && !IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, ecs->stop_func_name)) || IN_SOLIB_CALL_TRAMPOLINE (stop_pc, ecs->stop_func_name) Index: alpha-osf1-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/alpha-osf1-tdep.c,v retrieving revision 1.16 diff -u -p -r1.16 alpha-osf1-tdep.c --- alpha-osf1-tdep.c 27 Aug 2003 19:02:03 -0000 1.16 +++ alpha-osf1-tdep.c 2 Dec 2003 03:52:41 -0000 @@ -45,6 +45,47 @@ alpha_osf1_sigcontext_addr (struct frame return (read_memory_integer (get_frame_base (frame), 8)); } +static int +alpha_osf1_at_function_start (CORE_ADDR pc, CORE_ADDR func_start) +{ + /* A function prologue typically starts with 2 instructions setting + up the GP. But the linker sometimes changes the jump address of + subroutine calls because it has determined that these 2 instructions + do not make any difference in the execution of the program. So it + has virtually optimized them out by changing the jump address + inside the function call to branch just after these 2 instructions. + + As a consequence, the "effective" start address of a given function + can be either the first instruction of this function, or the third + instruction if the first two instructions are setting up the GP. + + It is important to recognize these two cases during a "next" in + order to properly detect function calls that land into a prologueless + function which first two instructions have been shunted by the linker. */ + + if (pc == func_start) + return 1; + + if (pc == func_start + 8) + { + unsigned int inst; + + /* ldah $gp,n($t12) */ + inst = alpha_read_insn (func_start); + if ((inst & 0xffff0000) != 0x27bb0000) + return 0; + + /* lda $gp,n($gp) */ + inst = alpha_read_insn (func_start + 4); + if ((inst & 0xffff0000) != 0x23bd0000) + return 0; + + return 1; + } + + return 0; +} + static void alpha_osf1_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) @@ -59,6 +100,7 @@ alpha_osf1_init_abi (struct gdbarch_info on multi-processor machines. We need to use software single stepping instead. */ set_gdbarch_software_single_step (gdbarch, alpha_software_single_step); + set_gdbarch_at_function_start (gdbarch, alpha_osf1_at_function_start); tdep->sigcontext_addr = alpha_osf1_sigcontext_addr; --X1bOJ3K7DJ5YkBrT--