Compile this program with -g: typedef int compute_function (int); int nestee (compute_function *computer, int arg, int self_call) { int nested (int nested_arg) { return nested_arg + 23 + self_call; /* Break here */ } if (self_call) arg = nestee (nested, arg + 5, 0); return computer (arg); } int misc (int arg) { return 0; } int main(int argc, char **argv) { nestee (misc, 5, 1); return 0; } .debug_info says: <2><8b>: Abbrev Number: 9 (DW_TAG_subprogram) <8c> DW_AT_name : (indirect string, offset: 0xe6): nested <90> DW_AT_decl_file : 1 <91> DW_AT_decl_line : 5 <92> DW_AT_prototyped : 1 <92> DW_AT_type : <0x47> <96> DW_AT_low_pc : 0x4004b4 <9e> DW_AT_high_pc : 0x4004ca <a6> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa) <a8> DW_AT_static_link : 1 byte block: 50 (DW_OP_reg0 (rax)) <aa> DW_AT_GNU_all_call_sites: 1 <aa> DW_AT_sibling : <0xcb> That is, the static link is $rax. In gdb I set a breakpoint at line 7 and ran the program. Then: (gdb) p/x $rax $8 = 0x7fffffffe400 Now I go up a couple of frames to the relevant (outermost) invocation of nestee: (gdb) p /x $pc $9 = 0x40052c And then from the frame info: 00000080 0000001c 00000084 FDE cie=00000000 pc=0040053c..0040054a LOC CFA rbp ra 000000000040053c rsp+8 u c-8 000000000040053d rsp+16 c-16 c-8 0000000000400540 rbp+16 c-16 c-8 0000000000400549 rsp+8 c-16 c-8 So I think the CFA in this frame is $rsp+8. But in gdb: (gdb) p /x $rsp+8 $10 = 0x7fffffffe3f8 ... which is different from the DW_AT_static_link. nestee does specify that its frame base is the CFA: <1><4e>: Abbrev Number: 6 (DW_TAG_subprogram) <4f> DW_AT_external : 1 <4f> DW_AT_name : (indirect string, offset: 0xed): nestee <53> DW_AT_decl_file : 1 <54> DW_AT_decl_line : 3 <55> DW_AT_prototyped : 1 <55> DW_AT_type : <0x47> <59> DW_AT_low_pc : 0x4004ca <61> DW_AT_high_pc : 0x40053c <69> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa) So, I think this is a GCC bug.
It seems that I read the wrong frame info in my original report. However, the bug still exists. Here is a new and hopefully more correct example showing the bug. I used a relatively recent git master gcc for this. I just followed the directions in the original report. The static link info for 'nested': <aa> DW_AT_static_link : 1 byte block: 50 (DW_OP_reg0 (rax)) In the 'nested' frame this is: (gdb) p/x $rax $1 = 0x7fffffffe370 In the outer 'nestee' frame: (gdb) p $pc $2 = (void (*)()) 0x4004e9 <nestee+99> The frame info as shown by readelf --debug-dump=frames-interp: 00000060 00000024 00000064 FDE cie=00000000 pc=00400486..004004fe LOC CFA rbx rbp ra 0000000000400486 rsp+8 u u c-8 0000000000400487 rsp+16 u c-16 c-8 000000000040048a rbp+16 u c-16 c-8 000000000040048f rbp+16 c-24 c-16 c-8 <--- this row 00000000004004fd rsp+8 c-24 c-16 c-8 So the CFA we want is $rbp + 16 in the outermost 'nestee' frame: (gdb) p/x $rbp + 16 $3 = 0x7fffffffe3b0 ... but this is different from $1.
GCC doesn't set DW_AT_static_link to the frame base of the statically enclosing frame but to the address of the FRAME object of that frame.
I don't see the problem. On both i686 and x86_64 'p self_call' prints 1, which matches the value returned by the function, so debugging seems to be working fine. On i686 the DW_AT_static_link points to $eax because that's where the static chain lives during (part of) the function. As Eric says, the value of the static chain pointer is not the same as the CFA; it points to a struct in the stack frame of the caller. What's the bug?
(In reply to comment #3) > I don't see the problem. On both i686 and x86_64 'p self_call' prints 1, which > matches the value returned by the function, so debugging seems to be working > fine. You can't go by what gdb says here. gdb currently doesn't look at DW_AT_static_link at all. Instead it just does some guessing. This bug came up because I tried to make it work properly. If you try "print arg" you will see gdb get it wrong: (gdb) print arg $1 = 0 I think the correct answer is 5. > On i686 the DW_AT_static_link points to $eax because that's where the static > chain lives during (part of) the function. > > As Eric says, the value of the static chain pointer is not the same as the CFA; > it points to a struct in the stack frame of the caller. > > What's the bug? DWARF defines DW_AT_static_link to be the frame base of the appropriate outer invocation: If a subroutine or entry point is nested, it may have a DW_AT_static_link attribute, whose value is a location description that computes the frame base of the relevant instance of the subroutine that immediately encloses the subroutine or entry point. The idea is you can determine which instance by computing the static link, then unwind the stack and look for the corresponding CFA. The test case here is supposed to confound simplistic approaches to this by having multiple instances of 'nestee' on the stack. Note also that if the nested function doesn't refer to any variables from the outer scope, then GCC seems not to emit DW_AT_static_link at all. I guess this is an optimization; but it is a little unfortunate since it makes debugging less obvious.
Oh, I see what you mean; GCC is using its internal concept of static chain, but DWARF wants something else.
> The idea is you can determine which instance by computing the static link, > then unwind the stack and look for the corresponding CFA. > The test case here is supposed to confound simplistic approaches to this > by having multiple instances of 'nestee' on the stack. Yes, but you can do something useful even with this value of DW_AT_static_link, albeit not exactly what DWARF means. In the GDB released by AdaCore, we do use this value of DW_AT_static_link to deal with up-level references; you can ask Joel for the details. > Note also that if the nested function doesn't refer to any variables from > the outer scope, then GCC seems not to emit DW_AT_static_link at all. > I guess this is an optimization; but it is a little unfortunate since > it makes debugging less obvious. Indeed, that's why in AdaCore's version of the compiler, we always generate DW_AT_static_link at -O0, and we even force the static chain onto the stack in this case (as there are no location lists at -O0).
I think these two meanings of DW_AT_static_link could be compatible if we changed nestee's DW_AT_frame_base to point to the FRAME object, i.e. CFA-24.
> Yes, but you can do something useful even with this value of > DW_AT_static_link, albeit not exactly what DWARF means. Regardless, I think GCC should emit correct DWARF. > Indeed, that's why in AdaCore's version of the compiler, we always > generate DW_AT_static_link at -O0, and we even force the static > chain onto the stack in this case (as there are no location lists at > -O0). I think this would be a nice addition.
> I think this would be a nice addition. OK, I'm attaching the patchlet. I can submit it when stage #1 opens.
Created attachment 29333 [details] patch to tweak the static chain at -O0 * function.c (instantiate_decls): Process the saved static chain. (expand_function_start): If not optimizing, save the static chain onto the stack. * tree-nested.c (convert_all_function_calls): Always create the static chain for nested functions if not optimizing.
> OK, I'm attaching the patchlet. I can submit it when stage #1 opens. I obviously missed one stage #1, but this is now done: http://gcc.gnu.org/ml/gcc-patches/2014-05/msg00573.html
Author: ebotcazou Date: Fri Jun 6 08:13:24 2014 New Revision: 211308 URL: http://gcc.gnu.org/viewcvs?rev=211308&root=gcc&view=rev Log: PR debug/53927 * function.c (instantiate_decls): Process the saved static chain. (expand_function_start): If not optimizing, save the static chain onto the stack. * tree-nested.c (convert_all_function_calls): Always create the static chain for nested functions if not optimizing. Modified: trunk/gcc/ChangeLog trunk/gcc/function.c trunk/gcc/tree-nested.c
(In reply to Tom Tromey from comment #8) > > Yes, but you can do something useful even with this value of > > DW_AT_static_link, albeit not exactly what DWARF means. > > Regardless, I think GCC should emit correct DWARF. I gave it a try: see the attached patch. Jason suggested to change DW_AT_frame_base in order to make it equal to the address of the FRAME object. I was not sure: 1) how to do it: location descriptions for all local variables would need to be updated; 2) whether it's safe to do this: what if optimizers move/duplicate this FRAME object in the stack frame or do similar "disturbing" things? I'm not familiar enough with optimization passes to estimate if it's likely: feedback welcome. :-) I thought: why not make DW_AT_static_link compute the parent frame base address from the current static link argument? Well, when generating DW_AT_static_link for a nested subprogram, we do not know yet the offset between the FRAME object and the frame base address. This is because nested subprograms reach the back-end before their parent. Besides, see point 2: are we only sure that such a constant offset exists? So instead, I patched tree-nested.c in order to append a field at the end of the FRAME object to hold the frame base address (computed with the DWARF_CFA builtin). Then, dwarf2out.c just has to emit a location description for nested subprograms' DW_AT_static_link that fetches this using the static link argument. It's consuming a little stack space, I'm not sure if it's a problem. Once again: feedback welcome! Besides, this does not work yet with Fortran since for this front-end, the DWARF_CFA builtin is not registered. None of builtins.def are registered by the way: how could I register only this one without duplicating code? Is it a good idea anyway? For the record, I bootstrapped and reg-tested this patch on x86_64-linux and preliminary manual testing with a patched GDB[1] and Ada reproducers shows that this approach is working. Thoughts? [1] This patch teaches GDB how to use DW_AT_static_link in order to find the frame corresponding to the lexically enclosing scope. I think I will try to submit it to GDB soon.
Created attachment 34868 [details] patch to generate DWARF-compliant DW_AT_static_link attributes gcc/ * tree-nested.c (finalize_nesting_tree_1): Append a field to hold the frame base address. * dwarf2out.c (gen_subprogram_die): Generate for DW_AT_static_link a location description that computes the value of this field.
(In reply to Pierre-Marie de Rodat from comment #13) > [1] This patch teaches GDB how to use DW_AT_static_link in order to find the > frame corresponding to the lexically enclosing scope. I think I will try to > submit it to GDB soon. For the record, here it is: <https://www.sourceware.org/ml/gdb-patches/2015-03/msg00040.html>.
(In reply to Pierre-Marie de Rodat from comment #15) > (In reply to Pierre-Marie de Rodat from comment #13) > > [1] This patch teaches GDB how to use DW_AT_static_link in order to find the > > frame corresponding to the lexically enclosing scope. I think I will try to > > submit it to GDB soon. > > For the record, here it is: > <https://www.sourceware.org/ml/gdb-patches/2015-03/msg00040.html>. I'm curious if you tried it on the test case in this PR. In the patch you wrote: > Since I'm not sure of how this issue should be solved, I'm nevertheless posting this patch here so this matter can be discussed. In the context of this feature, I think we need a backlink from all symbols to the corresponding embedding block but on the other hand only a few blocks have static link: maybe we could turn this static_link field into a objfile-based hashtable lookup. Thoughts? Yeah, growing these is to be avoided. My patch for this added a method to symbol_computed_ops instead. Unfortunately gitorious is acting weird so you can't see the patch online :-(. But you can fetch from https://gitorious.org/binutils-gdb/gdb.git and look at the branch static-link-fix if you like.
(In reply to Tom Tromey from comment #16) > I'm curious if you tried it on the test case in this PR. I did not, but it looks like it now works as expected. Here are the frame base info for "nestee" and the static link info for "nested": [nestee] <69> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa) [nested] <aa> DW_AT_static_link : 6 byte block: 91 60 6 23 20 6 (DW_OP_fbreg: -32; DW_OP_deref; DW_OP_plus_uconst: 32; DW_OP_deref) ... and the CFA for the two locations we will be interrested in later: LOC CFA 0000000000400496 rsp+8 0000000000400497 rsp+16 000000000040049a rbp+16 # A 00000000004004af rsp+8 LOC CFA 000000000040053b rsp+8 000000000040053c rsp+16 000000000040053f rbp+16 # B 0000000000400564 rsp+8 Now in GDB: $ gdb -n -q -ex 'b pr53927.c:7' -ex r --args ./pr53927 [...] Breakpoint 1, nested (nested_arg=10) at pr53927.c:7 7 return nested_arg + 23 + self_call; /* Break here */ (gdb) bt #0 nested (nested_arg=10) at pr53927.c:7 #1 0x000000000040052b in nestee (computer=0x7fffffffdf24, arg=10) at pr53927.c:13 #2 0x000000000040051d in nestee (computer=0x40052d <misc>, arg=5) at pr53927.c:11 #3 0x000000000040055e in main (argc=1, argv=0x7fffffffe058) at pr53927.c:23 (gdb) p $pc $1 = (void (*)()) 0x4004a4 <nested+14> # This PC corresponds to the "B" line above: CFA = $rbp + 16 (gdb) x/1gx $rbp + 16 - 32 0x7fffffffdea0: 0x00007fffffffdf20 (gdb) x/1gx 0x00007fffffffdf20 + 32 0x7fffffffdf40: 0x00007fffffffdf60 ... so the static link expression gives us the following frame base address: 0x00007fffffffdf60 (gdb) f 2 #2 0x000000000040051d in nestee (computer=0x40052d <misc>, arg=5) at pr53927.c:11 11 arg = nestee (nested, arg + 5, 0); # This PC corresponds to the "A" line above: CFA = $rbp + 16 (gdb) p/x $rbp + 16 $2 = 0x7fffffffdf60 So the static link expression correctly yields the frame 2's CFA. > Yeah, growing these is to be avoided. > My patch for this added a method to symbol_computed_ops instead. > > Unfortunately gitorious is acting weird so you can't see the patch > online :-(. But you can fetch from > https://gitorious.org/binutils-gdb/gdb.git > and look at the branch static-link-fix if you like. Interesting! Having a look at this: thank you! I guess you waited for the GCC issue to be solved before submitting them to GDB?
> Jason suggested to change DW_AT_frame_base in order to make it equal to the > address of the FRAME object. I was not sure: > > 1) how to do it: location descriptions for all local variables would need > to be updated; > 2) whether it's safe to do this: what if optimizers move/duplicate this > FRAME object in the stack frame or do similar "disturbing" things? I'm not > familiar enough with optimization passes to estimate if it's likely: > feedback welcome. :-) > > I thought: why not make DW_AT_static_link compute the parent frame base > address from the current static link argument? Well, when generating > DW_AT_static_link for a nested subprogram, we do not know yet the offset > between the FRAME object and the frame base address. This is because nested > subprograms reach the back-end before their parent. Besides, see point 2: > are we only sure that such a constant offset exists? I think this is worth investigating though because it's conceptually much simpler than adding yet another indirection. And we should concentrate on -O0 (and -Og), we don't really care about what happens with aggressive optimization. I guess the question is: can we arrange to have a constant offset between the frame base and the FRAME object, "constant" meaning valid for every function but possibly target-dependent?
(In reply to Pierre-Marie de Rodat from comment #17) > (In reply to Tom Tromey from comment #16) > > I'm curious if you tried it on the test case in this PR. > > I did not, but it looks like it now works as expected. Great! Thanks very much for trying it. > Interesting! Having a look at this: thank you! I guess you waited for the > GCC issue to be solved before submitting them to GDB? Yeah. There wasn't much point submitting it when it wouldn't work anyhow :} Also see the README.archer file. It explains some changes that are needed. Also I remember thinking that the dwarf "locexpr baton" changes needed some update, but I no longer recall what. Feel free to reuse any parts of that branch (or any other gdb branch of mine on gitorious) that you like. All that code was written while I was at Red Hat and so there are no legal barriers to putting it in. Also, btw, this is https://sourceware.org/bugzilla/show_bug.cgi?id=8300
> Yeah. There wasn't much point submitting it when it wouldn't work anyhow :} > Also see the README.archer file. It explains some changes that are needed. > Also I remember thinking that the dwarf "locexpr baton" changes needed > some update, but I no longer recall what. I see now that I recorded my thoughts in the commit messages. Yay me!
(In reply to Eric Botcazou from comment #18) > I think this is worth investigating though because it's conceptually > much simpler than adding yet another indirection. And we should > concentrate on -O0 (and -Og), we don't really care about what happens > with aggressive optimization. Understood and agreed. Nevertheless... > I guess the question is: can we arrange to have a constant offset > between the frame base and the FRAME object, "constant" meaning valid > for every function but possibly target-dependent? I started to hack into cfgexpand.c and dwarf2out.c, but I realized this is not possible in the general case. Consider the following example: #include <stdlib.h> int nestee (void) { int a __attribute__((aligned(64))) = 1234; void nested (int b) { a = b; } nested (2345); return a; } int call_nestee (int n) { int *v = alloca (sizeof (int) * n); v[0] = nestee (); return v[0]; } int main (void) { call_nestee (1); call_nestee (8); return 0; } With a GCC 5.0 built from fairly recent sources, I get the following CFA information: 00000090 000000000000002c 00000064 FDE cie=00000030 pc=00000000004004ac..00000000004004eb DW_CFA_advance_loc: 5 to 00000000004004b1 DW_CFA_def_cfa: r10 (r10) ofs 0 DW_CFA_advance_loc: 9 to 00000000004004ba DW_CFA_expression: r6 (rbp) (DW_OP_breg6 (rbp): 0) DW_CFA_advance_loc: 5 to 00000000004004bf DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -8; DW_OP_deref) DW_CFA_advance_loc: 38 to 00000000004004e5 And now here is what I get under GDB: $ gdb -n -q -ex 'b nestee' ./dyn_frame Reading symbols from ./dyn_frame...done. Breakpoint 1 at 0x4004c3: file dyn_frame.c, line 6. (gdb) r [...] Breakpoint 1, nestee () at dyn_frame.c:6 6 int a __attribute__((aligned(64))) = 1234; (gdb) p $pc $1 = (void (*)()) 0x4004c3 <nestee+23> (gdb) x/1xg $rbp - 8 0x7fffffffdf28: 0x00007fffffffdf60 (gdb) p/x (char *) 0x00007fffffffdf60 - (char *) &a $2 = 0xa0 ... so for this frame, the CFA and the FRAME object are 0xa0 bytes from each other. Now let's resume to see the next "nestee" frame: (gdb) c Continuing. Breakpoint 1, nestee () at dyn_frame.c:6 6 int a __attribute__((aligned(64))) = 1234; (gdb) p $pc $3 = (void (*)()) 0x4004c3 <nestee+23> (gdb) x/1xg $rbp - 8 0x7fffffffdf28: 0x00007fffffffdf50 (gdb) p/x (char *) 0x00007fffffffdf50 - (char *) &a $4 = 0x90 The offset between the CFA and e FRAME object is now 0x90 bytes. So because of alignment constraints, I think we cannot assume we can have a constant offset (even function-dependent). This offset is dynamic and the only way to compute it is to use the frame's context: here, nestee's saved registers, which we don't have access to in DWARF when computing the static link attribute.
(In reply to Tom Tromey from comment #20) >> Yeah. There wasn't much point submitting it when it wouldn't work anyhow :} >> Also see the README.archer file. It explains some changes that are needed. >> Also I remember thinking that the dwarf "locexpr baton" changes needed >> some update, but I no longer recall what. > > I see now that I recorded my thoughts in the commit messages. > Yay me! Great, thank you for this!
> The offset between the CFA and e FRAME object is now 0x90 bytes. So > because of alignment constraints, I think we cannot assume we can have a > constant offset (even function-dependent). > > This offset is dynamic and the only way to compute it is to use the > frame's context: here, nestee's saved registers, which we don't have > access to in DWARF when computing the static link attribute. OK, thanks for the investigation, the counter-example is quite telling and puts an end to this approach. To rescue some of this work, you could add a line to the comment in the finalize_nesting_tree_1 hunk, explaining that we need to go through the BUILT_IN_DWARF_CFA indirection because the offset of the FRAME object in the stack frame can be dynamic if the FRAME object needs to be more aligned than the stack.
Author: pmderodat Date: Thu Nov 26 14:56:24 2015 New Revision: 230968 URL: https://gcc.gnu.org/viewcvs?rev=230968&root=gcc&view=rev Log: DWARF: fix loc. descr. generation for DW_AT_static_link gcc/ChangeLog: PR debug/53927 * tree-nested.c (finalize_nesting_tree_1): Append a field to hold the frame base address. * dwarf2out.c (gen_subprogram_die): Generate for DW_AT_static_link a location description that computes the value of this field. Modified: trunk/gcc/ChangeLog trunk/gcc/dwarf2out.c trunk/gcc/tree-nested.c
DW_AT_static_link is properly generated after revision 230968.