This one it pretty nasty: if we place a breakpoint at an inlined function's low_pc, the arguments aren't even on the stack yet ! Release: 3.3 20030303 (prerelease) Environment: System: Linux ar 2.4.18 #5 Mon Mar 18 09:25:24 ART 2002 i686 unknown Architecture: i686 host: i686-pc-linux-gnu build: i686-pc-linux-gnu target: i686-pc-linux-gnu configured with: ./configure --prefix=/usr/local/gcc20030303/ --enable-languages=c How-To-Repeat: Example (with Red Hat's gcc 3.1 20011127 on ia32): $ cat <<EOF >foo.c int xyz; static inline int foo(int bar) { label: xyz = bar; } void main(int argc) { foo(123); } EOF $ gcc -w -g foo.c # gcc 3.3: gcc -w -g -finline foo.c $ readelf -w a.out ... <2><4a>: Abbrev Number: 4 (DW_TAG_inlined_subroutine) DW_AT_abstract_origin: <68> DW_AT_low_pc : 0x80483a8 134513576 ... $ objdump -d a.out ... 08048398 <main>: ... 80483a6: 29 c4 sub %eax,%esp 80483a8: c7 45 fc 7b 00 00 00 movl $0x7b,0xfffffffc(%ebp) 80483af: 8b 45 fc mov 0xfffffffc(%ebp),%eax 80483b2: a3 34 95 04 08 mov %eax,0x8049534 80483b7: c9 leave ...
Fix: Work-around: don't access arguments in inlined functions ?
Sill happens on the mainline (20030607). I think the problem is the tree inliner is setting the line and file for the passing of the variables to be the same as the inlined functions. It also affects stabs debugging.
I am moving this to middle-end because it a tree-inliner problem. It looks like the initializers' line is set to the begining line of the function that is being inlined. From the comment in tree-inline.c this looks intenational but who knows: /* Expand any inlined calls in the initializers. Do this before we push FN on the stack of functions we are inlining; we want to inline calls to FN that appear in the initializers for the parameters. */
*** Bug 4431 has been marked as a duplicate of this bug. ***
Hmm, this is said to be a tree-inliner bug (hence middle-end instead of debug), but the bug is reported against a GNU CC version that didn't even use the tree-inliner... Maybe it's something else after all?
Tree inlining was added to c on 2001-10-04 so it was reported against a gcc which had tree inlining but any way look at this rtl (which is the *.01.rtl): (note 8 7 9 ("pr10003.c") 11) (note 9 8 10 ("pr10003.c") 4) (note 10 9 11 0x1047a80 NOTE_INSN_BLOCK_BEG) (insn 11 10 12 (set (reg:SI 118) (const_int 123 [0x7b])) -1 (nil) (nil)) (insn 12 11 13 (set (mem/f:SI (reg/f:SI 114 virtual-stack-vars) [0 bar+0 S4 A32]) (reg:SI 118)) -1 (nil) (nil)) (note 13 12 14 0x1047ab0 NOTE_INSN_BLOCK_BEG) (note 14 13 15 NOTE_INSN_DELETED) (note 15 14 16 ("pr10003.c") 6) See the second note is wrong here so it still is a problem with the middle end because the rtl has not loaded 123 yet.
I gave the wrong rtl: (note 8 7 9 ("pr10003.c") 11) (note 9 8 10 ("pr10003.c") 4) (note 10 9 11 0x1047ab0 NOTE_INSN_BLOCK_BEG) (insn 11 10 12 (set (reg/v:SI 120 [ bar ]) (const_int 123 [0x7b])) -1 (nil) (nil)) (note 12 11 13 0x1047ae0 NOTE_INSN_BLOCK_BEG) (note 13 12 14 NOTE_INSN_DELETED) (note 14 13 15 ("pr10003.c") 6) It is still wrong, I think both the tree and the rtl inliners are wrong.
*** Bug 12319 has been marked as a duplicate of this bug. ***
I looked into this, using a little program: extern int a; inline void g() { a = a + 1; } void f() { g(); } Which sports the problem that the low_pc of the inlined g() is including the start of f(): # /usr/src/gcc/gcc-cvs-3.4-objdir/gcc/cc1plus -fpreprocessed troep.i -quiet -dumpbase troep.c -mtune=pentiumpro -auxbase troep -g -O -version -o troep.s # as -V -Qy -o troep.o troep.s # objdump -d troep.o 00000000 <_Z1fv>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: ff 05 00 00 00 00 incl 0x0 9: 5d pop %ebp a: c3 ret The "push %ebp" and "mov %esp,%ebp" are included in the low_pc/high_pc for g(): # readelf --debug-dump troep.o <2><3d>: Abbrev Number: 3 (DW_TAG_inlined_subroutine) DW_AT_abstract_origin: <4b> DW_AT_low_pc : 0 0 DW_AT_high_pc : 0x9 9 <1><4b>: Abbrev Number: 4 (DW_TAG_subprogram) DW_AT_external : 1 DW_AT_name : g The reason that this happens is because in gcc/dwarf2out.c function gen_inlined_subroutine_die the low_pc is generated using ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL, BLOCK_NUMBER (stmt)); add_AT_lbl_id (subr_die, DW_AT_low_pc, label); which translates in that it uses the string ".LBB2" which is supposedly corresponding with the revelant 'stmt' block. The actual place where .LBB2 is emitted then determines the real value of low_pc. Looking at the assembly from the troep.s file we see: _Z1fv: .LFB4: .file 1 "troep.c" .loc 1 5 0 .LBB2: .LBB3: .LBB4: pushl %ebp .LCFI0: movl %esp, %ebp .LCFI1: .loc 1 3 0 incl a .LBE4: .LBE3: .LBE2: .loc 1 7 0 popl %ebp ret In other words, the .LBB2 label indeed includes the 'pushl %ebp' and 'movl %esp, %ebp'. I started to dump various intermitant RTL code and found the first occurence of the 'push %ebp' in the output of -dB : (note 3 1 30 NOTE_INSN_FUNCTION_BEG) ;; Start of basic block 0, registers live: 6 [bp] 7 [sp] 16 [] 20 [frame] (note 30 3 34 0 [bb 0] NOTE_INSN_BASIC_BLOCK) (insn/f 34 30 35 0 troep.c:5 (set (mem:SI (pre_dec:SI (reg/f:SI 7 esp)) [0 S4 A8]) (reg/f:SI 6 ebp)) 32 {*pushsi2} (nil) (nil)) (insn/f 35 34 36 0 troep.c:5 (set (reg/f:SI 6 ebp) (reg/f:SI 7 esp)) 38 {*movsi_1} (nil) (nil)) Note that already here the start of 'basic block 0' is BEFORE these instructions. This seems the only basic block left - in many debug dumps I see no a clear appearance of the three blocks that should be involved according to the final assembly output. The first dump (.01.rtl) these three blocks are still there: ;; Function void f() (note 1 0 2 ("troep.c") 5) (note 2 1 3 NOTE_INSN_DELETED) (note 3 2 4 NOTE_INSN_FUNCTION_BEG) (note 4 3 5 NOTE_INSN_DELETED) (note 5 4 6 0x402a4c30 NOTE_INSN_BLOCK_BEG) (note 6 5 7 NOTE_INSN_DELETED) (note 7 6 8 NOTE_INSN_DELETED) (note 8 7 9 NOTE_INSN_DELETED) (note 9 8 10 ("troep.c") 6) (note 10 9 11 NOTE_INSN_DELETED) (note 11 10 12 NOTE_INSN_DELETED) (note 12 11 13 ("troep.c") 3) (note 13 12 14 0x402a4c80 NOTE_INSN_BLOCK_BEG) (note 14 13 15 0x402a4ca8 NOTE_INSN_BLOCK_BEG) (note 15 14 16 NOTE_INSN_DELETED) (note 16 15 17 NOTE_INSN_DELETED) (note 17 16 18 NOTE_INSN_DELETED) (note 18 17 19 NOTE_INSN_DELETED) (note 19 18 20 NOTE_INSN_DELETED) (insn 20 19 21 (set (reg:SI 59) (mem/f:SI (symbol_ref:SI ("a") [flags 0x40] <var_decl 0x402c62f4 a>) [0 a+0 S4 A32])) -1 (nil) (nil)) [etc, this is the start of a = a + 1] But here it is unclear to me where the "push %ebp" should be. However, because already the next dump in line (troep.c.02.sibling) seems to have gotten rid of two blocks: ;; Function void f() (note 1 0 3 ("troep.c") 5) (note 3 1 9 NOTE_INSN_FUNCTION_BEG) (note 9 3 12 ("troep.c") 6) (note 12 9 30 ("troep.c") 3) (note 30 12 20 0 [bb 0] NOTE_INSN_BASIC_BLOCK) (insn 20 30 21 0 troep.c:3 (set (reg:SI 59) (mem/f:SI (symbol_ref:SI ("a") [flags 0x40] <var_decl 0x402c62f4 a>) [0 a+0 S4 A32])) -1 (nil) (nil)) [etc] and where this '(note 30 ...' no doubt is the inner block and therefore the start of g(), I would conclude that also the block in the last dump is this block. (? or the dumps suck). An important observation is that when we add persistent code to f(), before g(), like this: extern int a, b; inline void g() { a = a + 1; } void f() { b = b + 1; g(); } Then the problem goes away! : _Z1fv: .LFB4: .file 1 "troep.c" .loc 1 5 0 .LBB2: pushl %ebp .LCFI0: movl %esp, %ebp .LCFI1: .loc 1 6 0 incl b .LBB3: .LBB4: .loc 1 3 0 incl a .LBE4: .LBE3: .LBE2: .loc 1 8 0 popl %ebp ret [...] .uleb128 0x3 .long 0x4b .long .LBB3 .long .LBE3 Now the block is .LBB3 till .LBE3 and as you see it does not include too much anymore! Therefore I am inclined to think that the problem lays in the point where the "push %ebp" and "movl %esp, %ebp" are emitted; it seems that this is 'delayed' till there is other code emitted (?) - but as a result is emitted after the start of a block that was marking the start of an inlined function! This of course does not happen in the case when there is code in f() between its start and the call to g(). I am sorry I can't be of further help; I am not famliar with the internals of gcc at all. But I hope that this will help a little in finding the real problem.
Can someone assign this to me please? I intend to fix it - already working on it.
Werner, can you please give some feedback on the following? You write: "if we place a breakpoint at an inlined function's low_pc, the arguments aren't even on the stack yet!". But, this is how it should be - the readelf output you give shows a DW_TAG_inlined_subroutine and the low_pc for that should be set at the very start of the function (include the prologue of the function). After fixing PR 12319, which certainly needs to be fixed before this PR can be fixed (if at all needed), I get the following for your code: 08048314 <main>: 8048314: 55 push %ebp 8048315: 89 e5 mov %esp,%ebp 8048317: 83 ec 08 sub $0x8,%esp 804831a: 83 e4 f0 and $0xfffffff0,%esp 804831d: b8 00 00 00 00 mov $0x0,%eax 8048322: 29 c4 sub %eax,%esp 8048324: c7 45 fc 7b 00 00 00 movl $0x7b,0xfffffffc(%ebp) 804832b: 8b 45 fc mov 0xfffffffc(%ebp),%eax 804832e: a3 04 95 04 08 mov %eax,0x8049504 8048333: c9 leave 8048334: c3 ret <2><a56>: Abbrev Number: 4 (DW_TAG_inlined_subroutine) DW_AT_abstract_origin: <a87> (this is 'foo') DW_AT_low_pc : 0x8048324 134513444 DW_AT_high_pc : 0x8048333 134513459 and that is correct. Further I see: <2><aa3>: Abbrev Number: 11 (DW_TAG_label) DW_AT_name : label DW_AT_decl_file : 1 DW_AT_decl_line : 5 <4><a74>: Abbrev Number: 7 (DW_TAG_label) DW_AT_abstract_origin: <aa3> DW_AT_low_pc : 0x804832b 134513451 And 0x804832b is *after* the prologue, also ok thus. I fail to set a breakpoint on 'label' with gdb though nor do I seem to be able to print 'bar' at any instruction with gdb. But that is a problem of gdb, not of the DWARF2 info generated with gcc. Don't you think that when PR 12319 is fixed, this PR can be closed too?
Is anyone still looking at this? Do things work as they are supposed to on mainline now?
This was fixed on the mainline and 4.0.0.