Bug 21555

Summary: "error re-setting breakpoint" on PIE executables
Product: gdb Reporter: Jeff Epler <jepler>
Component: breakpointsAssignee: Yao Qi <qiyao>
Status: RESOLVED FIXED    
Severity: normal CC: qiyao
Priority: P2    
Version: HEAD   
Target Milestone: 8.0.1   
Host: Target:
Build: Last reconfirmed: 2017-07-11 00:00:00

Description Jeff Epler 2017-06-06 15:52:36 UTC
When executables are built in PIE mode, all breakpoints are lost and cannot be reset after gdb decides that the file debugged "has changed".  This would occur in natural use, when the program being debugged is rebuilt within a single gdb session.

[affected session, Debian Jessie, gcc 4.9, position independent executable]
$ echo 'extern int puts(const char *); int main() { puts("hello world"); }' > hello.c
$ gcc -g -fPIE -pie -Wl,-z,relro hello.c
$ gdb a.out
GNU gdb (GDB) 8.0.50.20170606-git
[snip]
(gdb) b main
Breakpoint 1 at 0x794: file hello.c, line 1.
(gdb) r
Starting program: /tmp/a.out 

Breakpoint 1, main () at hello.c:1
1       extern int puts(const char *); int main() { puts("hello world"); }
(gdb) shell touch a.out
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
`/tmp/a.out' has changed; re-reading symbols.
Error in re-setting breakpoint 1: Cannot access memory at address 0x555555554790
Starting program: /tmp/a.out 
hello world
[Inferior 1 (process 12780) exited with code 014]

This has been reported to Debian (see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863620), but it affects the master branch of gdb built without any debian-specific patches.  Until now, it wasn't understood that PIE executables were also part of the equation.

This is slightly different than bug #9553, because of the extra steps (touching the executable) before the problem occurs.

Confirmed affected:
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1          [Debian Jessie]
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git  [Debian Stretch]
GNU gdb (GDB) 8.0.50.20170606-git            [git 9949827bea]

I haven't been able to confirm that there is any gdb version which was not affected.

Tested with gcc versions:
gcc version 4.9.2 (Debian 4.9.2-10)          [Debian Jessie]
gcc version 6.3.0 20170516 (Debian 6.3.0-18) [Debian Stretch]

Note that in debian Stretch, the default is to use PIE, which makes more Debian users likely to encounter this problem.
Comment 1 Yao Qi 2017-07-11 10:57:40 UTC
Mine.
Comment 2 Sourceware Commits 2017-07-25 10:40:19 UTC
The master branch has been updated by Yao Qi <qiyao@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=46a62268b87bd5e8215636b695380bca9a9f3b63

commit 46a62268b87bd5e8215636b695380bca9a9f3b63
Author: Yao Qi <yao.qi@linaro.org>
Date:   Tue Jul 25 11:38:50 2017 +0100

    Catch exceptions thrown from gdbarch_skip_prologue
    
    PR 21555 is caused by the exception during the prologue analysis when re-set
    a breakpoint.
    
    (gdb) bt
     #0  memory_error_message (err=TARGET_XFER_E_IO, gdbarch=0x153db50, memaddr=93824992233232) at ../../binutils-gdb/gdb/corefile.c:192
     #1  0x00000000005718ed in memory_error (err=TARGET_XFER_E_IO, memaddr=memaddr@entry=93824992233232) at ../../binutils-gdb/gdb/corefile.c:220
     #2  0x00000000005719d6 in read_memory_object (object=object@entry=TARGET_OBJECT_CODE_MEMORY, memaddr=93824992233232, memaddr@entry=1, myaddr=myaddr@entry=0x7fffffffd0a0 "P\333S\001", len=len@entry=1) at ../../binutils-gdb/gdb/corefile.c:259
     #3  0x0000000000571c6e in read_code (len=1, myaddr=0x7fffffffd0a0 "P\333S\001", memaddr=<optimized out>) at ../../binutils-gdb/gdb/corefile.c:287
     #4  read_code_unsigned_integer (memaddr=memaddr@entry=93824992233232, len=len@entry=1, byte_order=byte_order@entry=BFD_ENDIAN_LITTLE)                          at ../../binutils-gdb/gdb/corefile.c:362
     #5  0x000000000041d4a0 in amd64_analyze_prologue (gdbarch=gdbarch@entry=0x153db50, pc=pc@entry=93824992233232, current_pc=current_pc@entry=18446744073709551615, cache=cache@entry=0x7fffffffd1e0) at ../../binutils-gdb/gdb/amd64-tdep.c:2310
     #6  0x000000000041e404 in amd64_skip_prologue (gdbarch=0x153db50, start_pc=93824992233232) at ../../binutils-gdb/gdb/amd64-tdep.c:2459
     #7  0x000000000067bfb0 in skip_prologue_sal (sal=sal@entry=0x7fffffffd4e0) at ../../binutils-gdb/gdb/symtab.c:3628
     #8  0x000000000067c4d8 in find_function_start_sal (sym=sym@entry=0x1549960, funfirstline=1) at ../../binutils-gdb/gdb/symtab.c:3501
     #9  0x000000000060999d in symbol_to_sal (result=result@entry=0x7fffffffd5f0, funfirstline=<optimized out>, sym=sym@entry=0x1549960) at ../../binutils-gdb/gdb/linespec.c:3860
    ....
     #16 0x000000000054b733 in location_to_sals (b=b@entry=0x15792d0, location=0x157c230, search_pspace=search_pspace@entry=0x1148120, found=found@entry=0x7fffffffdc64) at ../../binutils-gdb/gdb/breakpoint.c:14211
     #17 0x000000000054c1f5 in breakpoint_re_set_default (b=0x15792d0) at ../../binutils-gdb/gdb/breakpoint.c:14301
     #18 0x00000000005412a9 in breakpoint_re_set_one (bint=bint@entry=0x15792d0) at ../../binutils-gdb/gdb/breakpoint.c:14412
    
    This problem can be fixed by
    
     - either each prologue analyzer doesn't throw exception,
     - or catch the exception thrown from gdbarch_skip_prologue,
    
    I choose the latter because the former needs to fix *every* prologue
    analyzer to not throw exception.
    
    This error can be reproduced by changing reread.exp.  The test reread.exp
    has already test that breakpoint can be reset correctly after the
    executable is re-read.  This patch extends this test by compiling test c
    file with and without -fPIE.
    
    (gdb) run ^M
    The program being debugged has been started already.^M
    Start it from the beginning? (y or n) y^M
    x86_64/gdb/testsuite/outputs/gdb.base/reread/reread' has changed; re-reading symbols.
    Error in re-setting breakpoint 1: Cannot access memory at address 0x555555554790^M
    Error in re-setting breakpoint 2: Cannot access memory at address 0x555555554790^M
    Starting program: /scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/reread/reread ^M
    This is foo^M
    [Inferior 1 (process 27720) exited normally]^M
    (gdb) FAIL: gdb.base/reread.exp: opts= "-fPIE" "ldflags=-pie" : run to foo() second time (the program exited)
    
    This patch doesn't re-indent the code, to keep the patch simple.
    
    gdb:
    
    2017-07-25  Yao Qi  <yao.qi@linaro.org>
    
    	PR gdb/21555
    	* arch-utils.c (gdbarch_skip_prologue_noexcept): New function.
    	* arch-utils.h (gdbarch_skip_prologue_noexcept): Declare.
    	* infrun.c: Include arch-utils.h
    	(handle_step_into_function): Call gdbarch_skip_prologue_noexcept.
    	(handle_step_into_function_backward): Likewise.
    	* symtab.c (skip_prologue_sal): Likewise.
    
    gdb/testsuite:
    
    2017-07-25  Yao Qi  <yao.qi@linaro.org>
    
    	PR gdb/21555
    	* gdb.base/reread.exp: Wrap the whole test with two kinds of
    	compilation flags, with -fPIE and without -fPIE.
Comment 3 Sourceware Commits 2017-07-25 10:55:45 UTC
The gdb-8.0-branch branch has been updated by Yao Qi <qiyao@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=cd33a03d183a268b83ccbcae07f3788308e8d9f4

commit cd33a03d183a268b83ccbcae07f3788308e8d9f4
Author: Yao Qi <yao.qi@linaro.org>
Date:   Tue Jul 25 11:38:50 2017 +0100

    Catch exceptions thrown from gdbarch_skip_prologue
    
    PR 21555 is caused by the exception during the prologue analysis when re-set
    a breakpoint.
    
    (gdb) bt
     #0  memory_error_message (err=TARGET_XFER_E_IO, gdbarch=0x153db50, memaddr=93824992233232) at ../../binutils-gdb/gdb/corefile.c:192
     #1  0x00000000005718ed in memory_error (err=TARGET_XFER_E_IO, memaddr=memaddr@entry=93824992233232) at ../../binutils-gdb/gdb/corefile.c:220
     #2  0x00000000005719d6 in read_memory_object (object=object@entry=TARGET_OBJECT_CODE_MEMORY, memaddr=93824992233232, memaddr@entry=1, myaddr=myaddr@entry=0x7fffffffd0a0 "P\333S\001", len=len@entry=1) at ../../binutils-gdb/gdb/corefile.c:259
     #3  0x0000000000571c6e in read_code (len=1, myaddr=0x7fffffffd0a0 "P\333S\001", memaddr=<optimized out>) at ../../binutils-gdb/gdb/corefile.c:287
     #4  read_code_unsigned_integer (memaddr=memaddr@entry=93824992233232, len=len@entry=1, byte_order=byte_order@entry=BFD_ENDIAN_LITTLE)                          at ../../binutils-gdb/gdb/corefile.c:362
     #5  0x000000000041d4a0 in amd64_analyze_prologue (gdbarch=gdbarch@entry=0x153db50, pc=pc@entry=93824992233232, current_pc=current_pc@entry=18446744073709551615, cache=cache@entry=0x7fffffffd1e0) at ../../binutils-gdb/gdb/amd64-tdep.c:2310
     #6  0x000000000041e404 in amd64_skip_prologue (gdbarch=0x153db50, start_pc=93824992233232) at ../../binutils-gdb/gdb/amd64-tdep.c:2459
     #7  0x000000000067bfb0 in skip_prologue_sal (sal=sal@entry=0x7fffffffd4e0) at ../../binutils-gdb/gdb/symtab.c:3628
     #8  0x000000000067c4d8 in find_function_start_sal (sym=sym@entry=0x1549960, funfirstline=1) at ../../binutils-gdb/gdb/symtab.c:3501
     #9  0x000000000060999d in symbol_to_sal (result=result@entry=0x7fffffffd5f0, funfirstline=<optimized out>, sym=sym@entry=0x1549960) at ../../binutils-gdb/gdb/linespec.c:3860
    ....
     #16 0x000000000054b733 in location_to_sals (b=b@entry=0x15792d0, location=0x157c230, search_pspace=search_pspace@entry=0x1148120, found=found@entry=0x7fffffffdc64) at ../../binutils-gdb/gdb/breakpoint.c:14211
     #17 0x000000000054c1f5 in breakpoint_re_set_default (b=0x15792d0) at ../../binutils-gdb/gdb/breakpoint.c:14301
     #18 0x00000000005412a9 in breakpoint_re_set_one (bint=bint@entry=0x15792d0) at ../../binutils-gdb/gdb/breakpoint.c:14412
    
    This problem can be fixed by
    
     - either each prologue analyzer doesn't throw exception,
     - or catch the exception thrown from gdbarch_skip_prologue,
    
    I choose the latter because the former needs to fix *every* prologue
    analyzer to not throw exception.
    
    This error can be reproduced by changing reread.exp.  The test reread.exp
    has already test that breakpoint can be reset correctly after the
    executable is re-read.  This patch extends this test by compiling test c
    file with and without -fPIE.
    
    (gdb) run ^M
    The program being debugged has been started already.^M
    Start it from the beginning? (y or n) y^M
    x86_64/gdb/testsuite/outputs/gdb.base/reread/reread' has changed; re-reading symbols.
    Error in re-setting breakpoint 1: Cannot access memory at address 0x555555554790^M
    Error in re-setting breakpoint 2: Cannot access memory at address 0x555555554790^M
    Starting program: /scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/reread/reread ^M
    This is foo^M
    [Inferior 1 (process 27720) exited normally]^M
    (gdb) FAIL: gdb.base/reread.exp: opts= "-fPIE" "ldflags=-pie" : run to foo() second time (the program exited)
    
    This patch doesn't re-indent the code, to keep the patch simple.
    
    gdb:
    
    2017-07-25  Yao Qi  <yao.qi@linaro.org>
    
    	PR gdb/21555
    	* arch-utils.c (gdbarch_skip_prologue_noexcept): New function.
    	* arch-utils.h (gdbarch_skip_prologue_noexcept): Declare.
    	* infrun.c: Include arch-utils.h
    	(handle_step_into_function): Call gdbarch_skip_prologue_noexcept.
    	(handle_step_into_function_backward): Likewise.
    	* symtab.c (skip_prologue_sal): Likewise.
    
    gdb/testsuite:
    
    2017-07-25  Yao Qi  <yao.qi@linaro.org>
    
    	PR gdb/21555
    	* gdb.base/reread.exp: Wrap the whole test with two kinds of
    	compilation flags, with -fPIE and without -fPIE.
Comment 4 Yao Qi 2017-07-25 11:01:50 UTC
Patch is pushed in to master and 8.0 branch.  Close it.
Comment 5 Yao Qi 2017-08-24 10:12:02 UTC
Close it again.