From patchwork Wed Sep 2 23:49:47 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joel Brobecker X-Patchwork-Id: 8558 Received: (qmail 61902 invoked by alias); 2 Sep 2015 23:50:49 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 61890 invoked by uid 89); 2 Sep 2015 23:50:48 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.1 required=5.0 tests=AWL, BAYES_40, KAM_LAZY_DOMAIN_SECURITY, KAM_STOCKGEN, RCVD_IN_DNSWL_LOW autolearn=no version=3.3.2 X-HELO: rock.gnat.com Received: from rock.gnat.com (HELO rock.gnat.com) (205.232.38.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-SHA encrypted) ESMTPS; Wed, 02 Sep 2015 23:50:46 +0000 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 1956D28F25; Wed, 2 Sep 2015 19:50:44 -0400 (EDT) Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id WskRoU7pLcgF; Wed, 2 Sep 2015 19:50:44 -0400 (EDT) Received: from joel.gnat.com (localhost.localdomain [127.0.0.1]) by rock.gnat.com (Postfix) with ESMTP id 5E37528EB7; Wed, 2 Sep 2015 19:50:14 -0400 (EDT) Received: by joel.gnat.com (Postfix, from userid 1000) id EA7E9472C4; Wed, 2 Sep 2015 16:49:47 -0700 (PDT) Date: Wed, 2 Sep 2015 16:49:47 -0700 From: Joel Brobecker To: Pierre-Marie de Rodat Cc: Doug Evans , Kevin Buettner , gdb-patches@sourceware.org Subject: Re: [PATCH] Add proper handling for non-local references in nested functions Message-ID: <20150902234947.GA575@adacore.com> References: <550C1170.9070208@adacore.com> <55685B60.3000004@redhat.com> <55775EB0.4080701@adacore.com> <55AF5F7E.5000600@adacore.com> <20150722173957.7ed51f18@pinnacle.lan> <55B0C583.6050601@adacore.com> <55BB538B.7090104@adacore.com> <55D1E2B5.4000200@adacore.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <55D1E2B5.4000200@adacore.com> User-Agent: Mutt/1.5.23 (2014-03-12) Hi Pierre-Marie, > >From f8cb12e93bc4b317bf03dd31fc158cc05fc60367 Mon Sep 17 00:00:00 2001 > From: Pierre-Marie de Rodat > Date: Thu, 5 Feb 2015 17:00:06 +0100 > Subject: [PATCH] DWARF: handle non-local references in nested functions > > GDB's current behavior when dealing with non-local references in the > context of nested fuctions is approximative: > > - code using valops.c:value_of_variable read the first available stack > frame that holds the corresponding variable (whereas there can be > multiple candidates for this); > > - code directly relying on read_var_value will instead read non-local > variables in frames where they are not even defined. > > This change adds the necessary context to symbol reads (to get the block > they belong to) and to blocks (the static link property, if any) so that > GDB can make the proper decisions when dealing with non-local varibale > references. > > gdb/ChangeLog: > > * ada-lang.c (ada_read_var_value): Add a var_block argument > and pass it to default_read_var_value. > * block.c (block_static_link): New accessor. > * block.h (block_static_link): Declare it. > * buildsym.c (finish_block_internal): Add a static_link > [...] This patch is causing a crash on some platforms, as explained by the revision log of the attached patch. gdb/ChangeLog: * dwarf2loc.c (locexpr_get_frame_base): Renames block_op_get_frame_base. (dwarf2_block_frame_base_locexpr_funcs): Replace reference to block_op_get_frame_base by reference to locexpr_get_frame_base. (loclist_get_frame_base): New function, near identical copy of locexpr_get_frame_base. (dwarf2_block_frame_base_loclist_funcs): Replace reference to block_op_get_frame_base by reference to loclist_get_frame_base. Tested on x86_64-darwin (AdaCore testsuite), and x86_64-linux (official testsuite). As also mentioned in the revision log of the patch, we can probably do some refactoring, or perhaps a different API in the vector that just extracts the needed data (per_cu, at the moment) from symbol's SYMBOL_LOCATION_BATON. Then, instead of calling the method to get the function frame_base, you have a function that gets it by using that different symbol_block_ops function. But, I'd suggest fixing the issue the obvious way first, and doing the cleanup that as a follow up. I wanted to help with the cleanup, but I am not going to be available for a while, so here goes... Thanks! From 684ce5200125d14c5a31fe28b251966ced472b19 Mon Sep 17 00:00:00 2001 From: Joel Brobecker Date: Tue, 1 Sep 2015 22:20:32 +0200 Subject: [PATCH] crash printing non-local variable from nested subprogram We have noticed that GDB would sometimes crash trying to print from a nested function the value of a variable declared in an enclosing scope. This appears to be target dependent, although that correlation might only be fortuitious. We noticed the issue on x86_64-darwin, x86-vxworks6 and x86-solaris. The investigation was done on Darwin. This is a new feature that was introduced by: commit 63e43d3aedb8b1112899c2d0ad74cbbee687e5d6 Date: Thu Feb 5 17:00:06 2015 +0100 DWARF: handle non-local references in nested functions We can reproduce the problem with one of the testcases that was added with the patch (gdb.base/nested-subp1.exp), where we have... 18 int 19 foo (int i1) 20 { 21 int 22 nested (int i2) 23 { [...] 27 return i1 * i2; /* STOP */ 28 } ... After building the example program, and running until line 27, try printing the value of "i1": % gdb gdb.base/nested-subp1 (gdb) break foo.c:27 (gdb) run Breakpoint 1, nested (i2=2) at /[...]/nested-subp1.c:27 27 return i1 * i2; /* STOP */ (gdb) p i1 [1] 73090 segmentation fault ../gdb -q gdb.base/nested-subp1 Ooops! What happens is that, because the reference is non-local, we are trying to follow the function's static link, which does... /* If we don't know how to compute FRAME's base address, don't give up: maybe the frame we are looking for is upper in the stace frame. */ if (framefunc != NULL && SYMBOL_BLOCK_OPS (framefunc)->get_frame_base != NULL && (SYMBOL_BLOCK_OPS (framefunc)->get_frame_base (framefunc, frame) == upper_frame_base)) ... or, in other words, calls the get_frame_base "method" of framefunc's struct symbol_block_ops data. This resolves to the block_op_get_frame_base function. Looking at the function's implementation, we see: struct dwarf2_locexpr_baton *dlbaton; [...] dlbaton = SYMBOL_LOCATION_BATON (framefunc); [...] result = dwarf2_evaluate_loc_desc (type, frame, start, length, dlbaton->per_cu); ^^^^^^^^^^^^^^^ Printing dlbaton->per_cu gives a value that seems fairly bogus for a memory address (0x60). Because of it, dwarf2_evaluate_loc_desc then crashes trying to dereference it. What's different on Darwin compared to Linux is that the function's frame base is encoded using the following form: .byte 0x40 # uleb128 0x40; (DW_AT_frame_base) .byte 0x6 # uleb128 0x6; (DW_FORM_data4) ... and so dwarf2_symbol_mark_computed ends up creating a SYMBOL_LOCATION_BATON as a struct dwarf2_loclist_baton: if (attr_form_is_section_offset (attr) /* .debug_loc{,.dwo} may not exist at all, or the offset may be outside the section. If so, fall through to the complaint in the other branch. */ && DW_UNSND (attr) < dwarf2_section_size (objfile, section)) { struct dwarf2_loclist_baton *baton; [...] SYMBOL_LOCATION_BATON (sym) = baton; However, if you look more closely at block_op_get_frame_base's implementation, you'll notice that the function extracts the symbol's SYMBOL_LOCATION_BATON as a dwarf2_locexpr_baton (a DWARF _expression_ rather than a _location list_). That's why we end up decoding the DLBATON improperly, and thus pass a random dlbaton->per_cu when calling dwarf2_evaluate_loc_desc. This works on x86_64-linux, because we indeed have the frame base described using a different form: .uleb128 0x40 # (DW_AT_frame_base) .uleb128 0x18 # (DW_FORM_exprloc) This patch fixes the issue by doing what we do for most (if not all) other such methods: providing one implementation each for loc-list, and loc-expr. Both implementations are nearly identical, so perhaps we might later want to improve this. But this patch first tries to fix the crash first, leaving the design issue for later. gdb/ChangeLog: * dwarf2loc.c (locexpr_get_frame_base): Renames block_op_get_frame_base. (dwarf2_block_frame_base_locexpr_funcs): Replace reference to block_op_get_frame_base by reference to locexpr_get_frame_base. (loclist_get_frame_base): New function, near identical copy of locexpr_get_frame_base. (dwarf2_block_frame_base_loclist_funcs): Replace reference to block_op_get_frame_base by reference to loclist_get_frame_base. Tested on x86_64-darwin (AdaCore testsuite), and x86_64-linux (official testsuite). --- gdb/dwarf2loc.c | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c index 91cb99a..dd8dd0b 100644 --- a/gdb/dwarf2loc.c +++ b/gdb/dwarf2loc.c @@ -381,10 +381,11 @@ locexpr_find_frame_base_location (struct symbol *framefunc, CORE_ADDR pc, *start = symbaton->data; } -/* Implement the struct symbol_block_ops::get_frame_base method. */ +/* Implement the struct symbol_block_ops::get_frame_base method for + LOC_BLOCK functions using a DWARF expression as its DW_AT_frame_base. */ static CORE_ADDR -block_op_get_frame_base (struct symbol *framefunc, struct frame_info *frame) +locexpr_get_frame_base (struct symbol *framefunc, struct frame_info *frame) { struct gdbarch *gdbarch; struct type *type; @@ -421,7 +422,7 @@ block_op_get_frame_base (struct symbol *framefunc, struct frame_info *frame) const struct symbol_block_ops dwarf2_block_frame_base_locexpr_funcs = { locexpr_find_frame_base_location, - block_op_get_frame_base + locexpr_get_frame_base }; /* Implement find_frame_base_location method for LOC_BLOCK functions using @@ -436,13 +437,48 @@ loclist_find_frame_base_location (struct symbol *framefunc, CORE_ADDR pc, *start = dwarf2_find_location_expression (symbaton, length, pc); } +/* Implement the struct symbol_block_ops::get_frame_base method for + LOC_BLOCK functions using a DWARF location list as its DW_AT_frame_base. */ + +static CORE_ADDR +loclist_get_frame_base (struct symbol *framefunc, struct frame_info *frame) +{ + struct gdbarch *gdbarch; + struct type *type; + struct dwarf2_loclist_baton *dlbaton; + const gdb_byte *start; + size_t length; + struct value *result; + + /* If this method is called, then FRAMEFUNC is supposed to be a DWARF block. + Thus, it's supposed to provide the find_frame_base_location method as + well. */ + gdb_assert (SYMBOL_BLOCK_OPS (framefunc)->find_frame_base_location != NULL); + + gdbarch = get_frame_arch (frame); + type = builtin_type (gdbarch)->builtin_data_ptr; + dlbaton = SYMBOL_LOCATION_BATON (framefunc); + + SYMBOL_BLOCK_OPS (framefunc)->find_frame_base_location + (framefunc, get_frame_pc (frame), &start, &length); + result = dwarf2_evaluate_loc_desc (type, frame, start, length, + dlbaton->per_cu); + + /* The DW_AT_frame_base attribute contains a location description which + computes the base address itself. However, the call to + dwarf2_evaluate_loc_desc returns a value representing a variable at + that address. The frame base address is thus this variable's + address. */ + return value_address (result); +} + /* Vector for inferior functions as represented by LOC_BLOCK, if the inferior function uses DWARF location list for its DW_AT_frame_base. */ const struct symbol_block_ops dwarf2_block_frame_base_loclist_funcs = { loclist_find_frame_base_location, - block_op_get_frame_base + loclist_get_frame_base }; /* See dwarf2loc.h. */ -- 2.1.4