https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84550
--- Comment #11 from Kevin Buettner <kevinb at redhat dot com> --- I've simplified Jakub's example slightly: --- vau2.c --- struct A { int a; }; struct B { struct A *b; }; struct C { struct B *c; }; __attribute__((noipa)) bool foo (struct A *p) { return false; } __attribute__((noipa)) int baz (int x) { return 0; } __attribute__((noipa)) void qux (struct C *p) { struct A *a; bool b; int c; if (!p->c) __builtin_abort (); a = p->c->b; b = (a->a == 4) && (foo (a)); c = baz (0); baz (b); } int main () { struct A a = { 4 }; struct B b = { &a }; struct C c = { &b }; qux (&c); return 0; } --- end vau2.c --- When I compile this via "g++ -O2 -g vau2.c -o vau2", and load it into gdb, it exhibits the same behavior shown by Jakub. I.e. the following sequence... b qux run s s s ... does not stop in foo as expected. The program instead exits. It turns out that qux consists of two disjoint pieces which, for some reason, are separated by a lot of other code, e.g. main, _start, foo, baz, and a lot of other stuff too. Here's what it looks like: 0x400460 <qux(C*)>: callq 0x400430 <abort@plt> 0x400465: nopw %cs:0x0(%rax,%rax,1) 0x40046f: nop 0x400470 <main()>: sub $0x28,%rsp 0x400474 <main()+4>: lea 0xc(%rsp),%rax 0x400479 <main()+9>: lea 0x18(%rsp),%rdi 0x40047e <main()+14>: movl $0x4,0xc(%rsp) 0x400486 <main()+22>: mov %rax,0x10(%rsp) 0x40048b <main()+27>: lea 0x10(%rsp),%rax 0x400490 <main()+32>: mov %rax,0x18(%rsp) 0x400495 <main()+37>: callq 0x4005b0 <_Z3quxP1C> 0x40049a <main()+42>: xor %eax,%eax 0x40049c <main()+44>: add $0x28,%rsp 0x4004a0 <main()+48>: retq 0x4004a1: nopw %cs:0x0(%rax,%rax,1) 0x4004ab: nopl 0x0(%rax,%rax,1) 0x4004b0 <_start>: xor %ebp,%ebp 0x4004b2 <_start+2>: mov %rdx,%r9 0x4004b5 <_start+5>: pop %rsi 0x4004b6 <_start+6>: mov %rsp,%rdx 0x4004b9 <_start+9>: and $0xfffffffffffffff0,%rsp 0x4004bd <_start+13>: push %rax 0x4004be <_start+14>: push %rsp 0x4004bf <_start+15>: mov $0x400660,%r8 0x4004c6 <_start+22>: mov $0x4005f0,%rcx 0x4004cd <_start+29>: mov $0x400470,%rdi 0x4004d4 <_start+36>: callq 0x400440 <__libc_start_main@plt> 0x4004d9 <_start+41>: hlt ... 0x400590 <foo(A*)>: xor %eax,%eax 0x400592 <foo(A*)+2>: retq 0x400593: nopl (%rax) 0x400596: nopw %cs:0x0(%rax,%rax,1) 0x4005a0 <baz(int)>: xor %eax,%eax 0x4005a2 <baz(int)+2>: retq 0x4005a3: nopl (%rax) 0x4005a6: nopw %cs:0x0(%rax,%rax,1) 0x4005b0 <_Z3quxP1C>: push %rbx 0x4005b1 <_Z3quxP1C+1>: mov (%rdi),%rax 0x4005b4 <_Z3quxP1C+4>: test %rax,%rax 0x4005b7 <_Z3quxP1C+7>: je 0x400460 <qux(C*)> 0x4005bd <_Z3quxP1C+13>: mov (%rax),%rdi 0x4005c0 <_Z3quxP1C+16>: xor %ebx,%ebx 0x4005c2 <_Z3quxP1C+18>: cmpl $0x4,(%rdi) 0x4005c5 <_Z3quxP1C+21>: je 0x4005d8 <_Z3quxP1C+40> 0x4005c7 <_Z3quxP1C+23>: xor %edi,%edi 0x4005c9 <_Z3quxP1C+25>: callq 0x4005a0 <baz(int)> 0x4005ce <_Z3quxP1C+30>: mov %ebx,%edi 0x4005d0 <_Z3quxP1C+32>: pop %rbx 0x4005d1 <_Z3quxP1C+33>: jmp 0x4005a0 <baz(int)> 0x4005d3 <_Z3quxP1C+35>: nopl 0x0(%rax,%rax,1) 0x4005d8 <_Z3quxP1C+40>: callq 0x400590 <foo(A*)> 0x4005dd <_Z3quxP1C+45>: xor %edi,%edi 0x4005df <_Z3quxP1C+47>: movzbl %al,%ebx 0x4005e2 <_Z3quxP1C+50>: callq 0x4005a0 <baz(int)> 0x4005e7 <_Z3quxP1C+55>: mov %ebx,%edi 0x4005e9 <_Z3quxP1C+57>: pop %rbx 0x4005ea <_Z3quxP1C+58>: jmp 0x4005a0 <baz(int)> 0x4005ec: nopl 0x0(%rax) Within GDB, this is where things go wrong: top-gdb> bt 4 #0 find_pc_partial_function_gnu_ifunc (pc=4195728, name=0x7fffffffdd28, address=0x7fffffffdd18, endaddr=0x7fffffffdd20, is_gnu_ifunc_p=0x0) at /ironwood1/sourceware-git/mesquite-native-thread_handle_to_thread_info/bld/../../binutils-gdb/gdb/blockframe.c:213 #1 0x0000000000553281 in find_pc_partial_function (pc=4195728, name=0x7fffffffdd28, address=0x7fffffffdd18, endaddr=0x7fffffffdd20) at /ironwood1/sourceware-git/mesquite-native-thread_handle_to_thread_info/bld/../../binutils-gdb/gdb/blockframe.c:323 #2 0x00000000006b6aec in fill_in_stop_func (gdbarch=0x1249170, ecs=0x7fffffffdcd0) at /ironwood1/sourceware-git/mesquite-native-thread_handle_to_thread_info/bld/../../binutils-gdb/gdb/infrun.c:4303 #3 0x00000000006baf13 in process_event_stop_test (ecs=0x7fffffffdcd0) at /ironwood1/sourceware-git/mesquite-native-thread_handle_to_thread_info/bld/../../binutils-gdb/gdb/infrun.c:6494 That pc value is actually the first address for foo(), which is what we want: top-gdb> p/x pc $22 = 0x400590 (Refer to my disassembly above to verify this.) This code, which is in find_pc_partial_function_gnu_ifunc(), incorrectly identifies this address, 0x400590, as belonging to qux: if (mapped_pc >= cache_pc_function_low && mapped_pc < cache_pc_function_high && section == cache_pc_function_section) goto return_cached_value; top-gdb> p/x cache_pc_function_low $30 = 0x400460 top-gdb> p/x cache_pc_function_high $31 = 0x4005ec Note that these high and low addresses include not only qux, but foo, baz, main, and _start. This is due to the fact that qux consists of two separate parts separated by a lot of other code. So far as I can tell, prologue analysis is not at fault here. I'm now trying to figure out how this might be fixed.