From patchwork Fri Mar 9 21:16:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 26269 Received: (qmail 79510 invoked by alias); 9 Mar 2018 21:24:53 -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 78676 invoked by uid 89); 9 Mar 2018 21:24:35 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-23.0 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_STOCKGEN, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy= X-HELO: mx1.redhat.com Received: from mx3-rdu2.redhat.com (HELO mx1.redhat.com) (66.187.233.73) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 09 Mar 2018 21:24:32 +0000 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5EA904026779 for ; Fri, 9 Mar 2018 21:16:16 +0000 (UTC) Received: from localhost.localdomain (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id EBEA42026DFD for ; Fri, 9 Mar 2018 21:16:15 +0000 (UTC) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 04/11] Calling ifunc functions when target has no debug info but resolver has Date: Fri, 9 Mar 2018 21:16:05 +0000 Message-Id: <20180309211612.12941-5-palves@redhat.com> In-Reply-To: <20180309211612.12941-1-palves@redhat.com> References: <20180309211612.12941-1-palves@redhat.com> After the previous patch, on Fedora 27 (glibc 2.26), if you try calling strlen in the inferior, you now get: (top-gdb) p strlen ("hello") '__strlen_avx2' has unknown return type; cast the call to its declared return type This is correct, because __strlen_avx2 is written in assembly. We can improve on this though -- if the final ifunc resolved/target function has no debug info, but the ifunc _resolver_ does have debug info, we can try extracting the final function's type from the type that the resolver returns. E.g.,: typedef size_t (*strlen_t) (const char*); size_t my_strlen (const char *) { /* some implementation */ } strlen_t strlen_resolver (unsigned long hwcap) { return my_strlen; } extern size_t strlen (const char *s); __typeof (strlen) strlen __attribute__ ((ifunc ("strlen_resolver"))); In the strlen example above, the resolver returns strlen_t, which is a typedef for pointer to a function that returns size_t. "strlen_t" is the type of both the user-visible "strlen", and of the the target function that implements it. This patch teaches GDB to extract that type. This is done for actual inferior function calls (in infcall.c), and for ptype (in eval_call). By the time we get to either of these places, we've already lost the original symbol/minsym, and only have values and types to work with. Hence the changes to c-exp.y and evaluate_var_msym_value, to ensure that we propagate the ifunc minsymbol's info. gdb/ChangeLog: yyyy-mm-dd Pedro Alves * blockframe.c (gnu_ifunc_target_type): New function. * eval.c (evaluate_var_msym_value): For GNU ifunc types, always return a value with a memory address. (eval_call): For calls to GNU ifunc functions, try to find the type of the target function from the type that the resolver returns. * gdbtypes.c (objfile_type): Don't install a return type for ifunc symbols. * infcall.c (find_function_return_type): Rename to ... (find_function_type): ... this, and return the function's type instead of the function's return type. (find_function_addr): Add 'function_type' parameter. For calls to GNU ifunc functions, try to find the type of the target function from the type that the resolver returns, and return it via FUNCTION_TYPE. (call_function_by_hand_dummy): Adjust to use the function type returned by find_function_addr. (find_function_addr): Add 'function_type' parameter and move description here. * symtab.h (find_gnu_ifunc_target_type): New declaration. --- gdb/blockframe.c | 34 +++++++++++++++++++++++++++++++++ gdb/eval.c | 25 ++++++++++++++---------- gdb/gdbtypes.c | 4 ---- gdb/infcall.c | 58 +++++++++++++++++++++++++++++++++++++------------------- gdb/infcall.h | 9 ++++++++- gdb/symtab.h | 6 ++++++ 6 files changed, 101 insertions(+), 35 deletions(-) diff --git a/gdb/blockframe.c b/gdb/blockframe.c index 9be8871f756..db02b35742d 100644 --- a/gdb/blockframe.c +++ b/gdb/blockframe.c @@ -323,6 +323,40 @@ find_pc_partial_function (CORE_ADDR pc, const char **name, CORE_ADDR *address, return find_pc_partial_function_gnu_ifunc (pc, name, address, endaddr, NULL); } +/* See symtab.h. */ + +struct type * +find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr) +{ + /* See if we can figure out the function's return type from the type + that the resolver returns. */ + symbol *sym = find_pc_function (resolver_funaddr); + if (sym != NULL + && SYMBOL_CLASS (sym) == LOC_BLOCK + && BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) == resolver_funaddr) + { + struct type *resolver_type = SYMBOL_TYPE (sym); + if (TYPE_CODE (resolver_type) == TYPE_CODE_FUNC) + { + /* Get the return type of the resolver. */ + struct type *resolver_ret_type + = check_typedef (TYPE_TARGET_TYPE (resolver_type)); + + /* If we found a pointer to function, then the resolved type + is the type of the pointed-to function. */ + if (TYPE_CODE (resolver_ret_type) == TYPE_CODE_PTR) + { + struct type *resolved_type + = TYPE_TARGET_TYPE (resolver_ret_type); + if (TYPE_CODE (check_typedef (resolved_type)) == TYPE_CODE_FUNC) + return resolved_type; + } + } + } + + return NULL; +} + /* Return the innermost stack frame that is executing inside of BLOCK and is at least as old as the selected frame. Return NULL if there is no such frame. If BLOCK is NULL, just return NULL. */ diff --git a/gdb/eval.c b/gdb/eval.c index a50299cbfdb..79f4d866e98 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -742,17 +742,13 @@ value * evaluate_var_msym_value (enum noside noside, struct objfile *objfile, minimal_symbol *msymbol) { - if (noside == EVAL_AVOID_SIDE_EFFECTS) - { - type *the_type = find_minsym_type_and_address (msymbol, objfile, NULL); - return value_zero (the_type, not_lval); - } + CORE_ADDR address; + type *the_type = find_minsym_type_and_address (msymbol, objfile, &address); + + if (noside == EVAL_AVOID_SIDE_EFFECTS && !TYPE_GNU_IFUNC (the_type)) + return value_zero (the_type, not_lval); else - { - CORE_ADDR address; - type *the_type = find_minsym_type_and_address (msymbol, objfile, &address); - return value_at_lazy (the_type, address); - } + return value_at_lazy (the_type, address); } /* Helper for returning a value when handling EVAL_SKIP. */ @@ -805,6 +801,15 @@ eval_call (expression *exp, enum noside noside, else if (TYPE_CODE (ftype) == TYPE_CODE_FUNC || TYPE_CODE (ftype) == TYPE_CODE_METHOD) { + if (TYPE_GNU_IFUNC (ftype)) + { + CORE_ADDR address = value_address (argvec[0]); + type *resolved_type = find_gnu_ifunc_target_type (address); + + if (resolved_type != NULL) + ftype = resolved_type; + } + type *return_type = TYPE_TARGET_TYPE (ftype); if (return_type == NULL) diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index b3a037971e0..2efd1264ff8 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -5443,10 +5443,6 @@ objfile_type (struct objfile *objfile) objfile_type->nodebug_text_gnu_ifunc_symbol = init_type (objfile, TYPE_CODE_FUNC, TARGET_CHAR_BIT, ""); - /* Ifunc resolvers return a function address. */ - TYPE_TARGET_TYPE (objfile_type->nodebug_text_gnu_ifunc_symbol) - = init_integer_type (objfile, gdbarch_addr_bit (gdbarch), 1, - "__IFUNC_RESOLVER_RET"); TYPE_GNU_IFUNC (objfile_type->nodebug_text_gnu_ifunc_symbol) = 1; objfile_type->nodebug_got_plt_symbol = init_pointer_type (objfile, gdbarch_addr_bit (gdbarch), diff --git a/gdb/infcall.c b/gdb/infcall.c index b7f4a176db5..c2710dd5519 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -229,26 +229,26 @@ value_arg_coerce (struct gdbarch *gdbarch, struct value *arg, return value_cast (type, arg); } -/* Return the return type of a function with its first instruction exactly at +/* Return the type of a function with its first instruction exactly at the PC address. Return NULL otherwise. */ static struct type * -find_function_return_type (CORE_ADDR pc) +find_function_type (CORE_ADDR pc) { struct symbol *sym = find_pc_function (pc); - if (sym != NULL && BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) == pc - && SYMBOL_TYPE (sym) != NULL) - return TYPE_TARGET_TYPE (SYMBOL_TYPE (sym)); + if (sym != NULL && BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) == pc) + return SYMBOL_TYPE (sym); return NULL; } -/* Determine a function's address and its return type from its value. - Calls error() if the function is not valid for calling. */ +/* See infcall.h. */ CORE_ADDR -find_function_addr (struct value *function, struct type **retval_type) +find_function_addr (struct value *function, + struct type **retval_type, + struct type **function_type) { struct type *ftype = check_typedef (value_type (function)); struct gdbarch *gdbarch = get_type_arch (ftype); @@ -275,17 +275,33 @@ find_function_addr (struct value *function, struct type **retval_type) if (TYPE_CODE (ftype) == TYPE_CODE_FUNC || TYPE_CODE (ftype) == TYPE_CODE_METHOD) { - value_type = TYPE_TARGET_TYPE (ftype); - if (TYPE_GNU_IFUNC (ftype)) { - funaddr = gnu_ifunc_resolve_addr (gdbarch, funaddr); + CORE_ADDR resolver_addr = funaddr; - /* Skip querying the function symbol if no RETVAL_TYPE has been - asked for. */ - if (retval_type) - value_type = find_function_return_type (funaddr); + /* Resolve the ifunc. Note this may call the resolver + function in the inferior. */ + funaddr = gnu_ifunc_resolve_addr (gdbarch, resolver_addr); + + /* Skip querying the function symbol if no RETVAL_TYPE or + FUNCTION_TYPE have been asked for. */ + if (retval_type != NULL || function_type != NULL) + { + type *target_ftype = find_function_type (funaddr); + /* If we don't have debug info for the target function, + see if we can instead extract the target function's + type from the type that the resolver returns. */ + if (target_ftype == NULL) + target_ftype = find_gnu_ifunc_target_type (resolver_addr); + if (target_ftype != NULL) + { + value_type = TYPE_TARGET_TYPE (check_typedef (target_ftype)); + ftype = target_ftype; + } + } } + else + value_type = TYPE_TARGET_TYPE (ftype); } else if (TYPE_CODE (ftype) == TYPE_CODE_INT) { @@ -320,6 +336,8 @@ find_function_addr (struct value *function, struct type **retval_type) if (retval_type != NULL) *retval_type = value_type; + if (function_type != NULL) + *function_type = ftype; return funaddr + gdbarch_deprecated_function_start_offset (gdbarch); } @@ -730,7 +748,6 @@ call_function_by_hand_dummy (struct value *function, struct infcall_suspend_state *caller_state; CORE_ADDR funaddr; CORE_ADDR real_pc; - struct type *ftype = check_typedef (value_type (function)); CORE_ADDR bp_addr; struct frame_id dummy_id; struct frame_info *frame; @@ -741,9 +758,6 @@ call_function_by_hand_dummy (struct value *function, char name_buf[RAW_FUNCTION_ADDRESS_SIZE]; int stack_temporaries = thread_stack_temporaries_enabled_p (inferior_ptid); - if (TYPE_CODE (ftype) == TYPE_CODE_PTR) - ftype = check_typedef (TYPE_TARGET_TYPE (ftype)); - if (!target_has_execution) noprocess (); @@ -867,7 +881,11 @@ call_function_by_hand_dummy (struct value *function, } } - funaddr = find_function_addr (function, &values_type); + struct type *ftype = check_typedef (value_type (function)); + if (TYPE_CODE (ftype) == TYPE_CODE_PTR) + ftype = check_typedef (TYPE_TARGET_TYPE (ftype)); + + funaddr = find_function_addr (function, &values_type, &ftype); if (values_type == NULL) values_type = default_return_type; if (values_type == NULL) diff --git a/gdb/infcall.h b/gdb/infcall.h index a3861fb1bf3..bea1494b50d 100644 --- a/gdb/infcall.h +++ b/gdb/infcall.h @@ -25,8 +25,15 @@ struct value; struct type; +/* Determine a function's address and its return type from its value. + If the function is a GNU ifunc, then return the address of the + target function, and set *FUNCTION_TYPE to the target function's + type, and *RETVAL_TYPE to the target function's return type.. + Calls error() if the function is not valid for calling. */ + extern CORE_ADDR find_function_addr (struct value *function, - struct type **retval_type); + struct type **retval_type, + struct type **function_type = NULL); /* Perform a function call in the inferior. diff --git a/gdb/symtab.h b/gdb/symtab.h index f9d52e76979..22b52019ee3 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -1675,6 +1675,12 @@ extern int find_pc_partial_function_gnu_ifunc (CORE_ADDR pc, const char **name, extern int find_pc_partial_function (CORE_ADDR, const char **, CORE_ADDR *, CORE_ADDR *); +/* See if we can figure out the function's actual type from the type + that the resolver returns. RESOLVER_FUNADDR is the address of the + ifunc resolver. */ + +extern struct type *find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr); + extern void clear_pc_function_cache (void); /* Expand symtab containing PC, SECTION if not already expanded. */