gdb/gdb-bz539590-gnu-ifunc.patch

512 lines
18 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

gdb/
2009-11-24 Jan Kratochvil <jan.kratochvil@redhat.com>
Transparent GNU-IFUNCs support.
* elfread.c (record_minimal_symbol): Apply also for mst_text_gnu_ifunc.
(elf_symtab_read): Set also mst_text_gnu_ifunc.
* gdbtypes.c (init_type): Support TYPE_FLAG_GNU_IFUNC.
(gdbtypes_post_init): Initialize builtin_func_func_ptr.
(objfile_type): Initialize nodebug_text_gnu_ifunc_symbol.
* gdbtypes.h (enum type_flag_value <TYPE_FLAG_GNU_IFUNC>)
(TYPE_GNU_IFUNC, struct main_type <flag_gnu_ifunc>)
(struct builtin_type <builtin_func_func_ptr>)
(struct objfile_type <nodebug_text_gnu_ifunc_symbol>): New.
* infcall.c (find_function_addr <TYPE_GNU_IFUNC (ftype)>): New.
* minsyms.c (lookup_minimal_symbol_text, prim_record_minimal_symbol)
(find_solib_trampoline_target): Support also mst_text_gnu_ifunc.
(in_gnu_ifunc_stub): New.
* parse.c (write_exp_msymbol <mst_text_gnu_ifunc>): New.
* solib-svr4.c (svr4_in_dynsym_resolve_code): Call also
in_gnu_ifunc_stub.
* symmisc.c (dump_msymbols <mst_text_gnu_ifunc>): New.
* symtab.c (search_symbols): Support also mst_text_gnu_ifunc.
* symtab.h (enum minimal_symbol_type <mst_text_gnu_ifunc>)
(in_gnu_ifunc_stub): New.
* linespec.c: Include infcall.h.
(minsym_found <MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc>): New.
gdb/testsuite/
2009-11-24 Jan Kratochvil <jan.kratochvil@redhat.com>
Transparent GNU-IFUNCs support.
* gdb.base/gnu-ifunc-lib.c, gdb.base/gnu-ifunc.c,
gdb.base/gnu-ifunc.exp: New.
Index: gdb-7.0/gdb/elfread.c
===================================================================
--- gdb-7.0.orig/gdb/elfread.c 2009-11-25 10:24:45.000000000 +0100
+++ gdb-7.0/gdb/elfread.c 2009-11-25 10:25:50.000000000 +0100
@@ -168,7 +168,8 @@ record_minimal_symbol (char *name, CORE_
{
struct gdbarch *gdbarch = get_objfile_arch (objfile);
- if (ms_type == mst_text || ms_type == mst_file_text)
+ if (ms_type == mst_text || ms_type == mst_file_text
+ || ms_type == mst_text_gnu_ifunc)
address = gdbarch_smash_text_address (gdbarch, address);
return prim_record_minimal_symbol_and_info
@@ -373,7 +374,10 @@ elf_symtab_read (struct objfile *objfile
{
if (sym->flags & (BSF_GLOBAL | BSF_WEAK))
{
- ms_type = mst_text;
+ if (sym->flags & BSF_GNU_INDIRECT_FUNCTION)
+ ms_type = mst_text_gnu_ifunc;
+ else
+ ms_type = mst_text;
}
else if ((sym->name[0] == '.' && sym->name[1] == 'L')
|| ((sym->flags & BSF_LOCAL)
Index: gdb-7.0/gdb/gdbtypes.c
===================================================================
--- gdb-7.0.orig/gdb/gdbtypes.c 2009-11-25 10:24:47.000000000 +0100
+++ gdb-7.0/gdb/gdbtypes.c 2009-11-25 10:24:56.000000000 +0100
@@ -1904,6 +1904,8 @@ init_type (enum type_code code, int leng
TYPE_NOTTEXT (type) = 1;
if (flags & TYPE_FLAG_FIXED_INSTANCE)
TYPE_FIXED_INSTANCE (type) = 1;
+ if (flags & TYPE_FLAG_GNU_IFUNC)
+ TYPE_GNU_IFUNC (type) = 1;
if (name)
TYPE_NAME (type) = obsavestring (name, strlen (name),
@@ -3762,6 +3764,8 @@ gdbtypes_post_init (struct gdbarch *gdba
= lookup_pointer_type (builtin_type->builtin_void);
builtin_type->builtin_func_ptr
= lookup_pointer_type (lookup_function_type (builtin_type->builtin_void));
+ builtin_type->builtin_func_func_ptr
+ = lookup_pointer_type (lookup_function_type (builtin_type->builtin_func_ptr));
/* This type represents a GDB internal function. */
builtin_type->internal_fn
@@ -3878,6 +3882,11 @@ objfile_type (struct objfile *objfile)
"<text variable, no debug info>", objfile);
TYPE_TARGET_TYPE (objfile_type->nodebug_text_symbol)
= objfile_type->builtin_int;
+ objfile_type->nodebug_text_gnu_ifunc_symbol
+ = init_type (TYPE_CODE_FUNC, 1, TYPE_FLAG_GNU_IFUNC,
+ "<text gnu-ifunc variable, no debug info>", objfile);
+ TYPE_TARGET_TYPE (objfile_type->nodebug_text_gnu_ifunc_symbol)
+ = objfile_type->nodebug_text_symbol;
objfile_type->nodebug_data_symbol
= init_type (TYPE_CODE_INT,
gdbarch_int_bit (gdbarch) / HOST_CHAR_BIT, 0,
Index: gdb-7.0/gdb/gdbtypes.h
===================================================================
--- gdb-7.0.orig/gdb/gdbtypes.h 2009-11-25 10:24:48.000000000 +0100
+++ gdb-7.0/gdb/gdbtypes.h 2009-11-25 10:25:17.000000000 +0100
@@ -187,6 +187,7 @@ enum type_flag_value
TYPE_FLAG_FIXED_INSTANCE = (1 << 15),
TYPE_FLAG_STUB_SUPPORTED = (1 << 16),
TYPE_FLAG_NOTTEXT = (1 << 17),
+ TYPE_FLAG_GNU_IFUNC = (1 << 18),
/* Used for error-checking. */
TYPE_FLAG_MIN = TYPE_FLAG_UNSIGNED
@@ -292,6 +293,12 @@ enum type_instance_flag_value
#define TYPE_NOTTEXT(t) (TYPE_MAIN_TYPE (t)->flag_nottext)
+/* Currently used only for TYPE_CODE_FUNC where specifies the real function
+ address is returned by this function call. TYPE_TARGET_TYPE determines the
+ final returned function type to be presented to user. */
+
+#define TYPE_GNU_IFUNC(t) (TYPE_MAIN_TYPE (t)->flag_gnu_ifunc)
+
/* Type owner. If TYPE_OBJFILE_OWNED is true, the type is owned by
the objfile retrieved as TYPE_OBJFILE. Otherweise, the type is
owned by an architecture; TYPE_OBJFILE is NULL in this case. */
@@ -427,6 +434,7 @@ struct main_type
unsigned int flag_vector : 1;
unsigned int flag_stub_supported : 1;
unsigned int flag_nottext : 1;
+ unsigned int flag_gnu_ifunc : 1;
unsigned int flag_fixed_instance : 1;
unsigned int flag_objfile_owned : 1;
unsigned int flag_discardable : 1;
@@ -1144,6 +1152,10 @@ struct builtin_type
(*) () can server as a generic function pointer. */
struct type *builtin_func_ptr;
+ /* `pointer to function returning pointer to function (returning void)' type.
+ The final void return type is not significant for it. */
+ struct type *builtin_func_func_ptr;
+
/* Special-purpose types. */
@@ -1186,6 +1198,7 @@ struct objfile_type
/* Types used for symbols with no debug information. */
struct type *nodebug_text_symbol;
+ struct type *nodebug_text_gnu_ifunc_symbol;
struct type *nodebug_data_symbol;
struct type *nodebug_unknown_symbol;
struct type *nodebug_tls_symbol;
Index: gdb-7.0/gdb/infcall.c
===================================================================
--- gdb-7.0.orig/gdb/infcall.c 2009-11-25 10:24:45.000000000 +0100
+++ gdb-7.0/gdb/infcall.c 2009-11-25 10:24:56.000000000 +0100
@@ -286,6 +286,27 @@ find_function_addr (struct value *functi
else
error (_("Invalid data type for function to be called."));
+ if (TYPE_GNU_IFUNC (ftype))
+ {
+ struct type *func_func_ptr;
+
+ funaddr += gdbarch_deprecated_function_start_offset (gdbarch);
+
+ /* Cast FUNADDR to drop TYPE_GNU_IFUNC and being able to call gnu-ifunc
+ FUNADDR without causing deadlock by this block of code. */
+
+ func_func_ptr = builtin_type (gdbarch)->builtin_func_func_ptr;
+ function = value_from_pointer (func_func_ptr, funaddr);
+
+ /* gnu-ifuncs have no arguments. */
+ function = call_function_by_hand (function, 0, NULL);
+
+ funaddr = value_as_address (function);
+
+ /* This is `int' as the return type of the final function. */
+ value_type = TYPE_TARGET_TYPE (value_type);
+ }
+
if (retval_type != NULL)
*retval_type = value_type;
return funaddr + gdbarch_deprecated_function_start_offset (gdbarch);
Index: gdb-7.0/gdb/linespec.c
===================================================================
--- gdb-7.0.orig/gdb/linespec.c 2009-11-25 10:24:45.000000000 +0100
+++ gdb-7.0/gdb/linespec.c 2009-11-25 10:24:56.000000000 +0100
@@ -40,6 +40,7 @@
#include "interps.h"
#include "mi/mi-cmds.h"
#include "target.h"
+#include "infcall.h"
/* We share this one with symtab.c, but it is not exported widely. */
@@ -1875,6 +1876,22 @@ minsym_found (int funfirstline, struct m
pc = gdbarch_convert_from_func_ptr_addr (gdbarch,
values.sals[0].pc,
&current_target);
+
+ /* Call gnu-ifunc to resolve breakpoint at its returned function. */
+ if (MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc)
+ {
+ struct type *func_func_ptr;
+ struct value *function;
+
+ func_func_ptr = builtin_type (gdbarch)->builtin_func_func_ptr;
+ function = value_from_pointer (func_func_ptr, pc);
+
+ /* gnu-ifuncs have no arguments. */
+ function = call_function_by_hand (function, 0, NULL);
+
+ pc = value_as_address (function);
+ }
+
if (pc != values.sals[0].pc)
values.sals[0] = find_pc_sect_line (pc, NULL, 0);
Index: gdb-7.0/gdb/minsyms.c
===================================================================
--- gdb-7.0.orig/gdb/minsyms.c 2009-11-25 10:24:47.000000000 +0100
+++ gdb-7.0/gdb/minsyms.c 2009-11-25 10:24:56.000000000 +0100
@@ -331,8 +331,9 @@ lookup_minimal_symbol_text (const char *
msymbol = msymbol->hash_next)
{
if (strcmp (SYMBOL_LINKAGE_NAME (msymbol), name) == 0 &&
- (MSYMBOL_TYPE (msymbol) == mst_text ||
- MSYMBOL_TYPE (msymbol) == mst_file_text))
+ (MSYMBOL_TYPE (msymbol) == mst_text
+ || MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc
+ || MSYMBOL_TYPE (msymbol) == mst_file_text))
{
switch (MSYMBOL_TYPE (msymbol))
{
@@ -699,6 +700,16 @@ lookup_minimal_symbol_by_pc (CORE_ADDR p
{
return lookup_minimal_symbol_by_pc_section (pc, NULL);
}
+
+/* Return non-zero iff PC is in function implementing gnu-ifunc selection. */
+
+int
+in_gnu_ifunc_stub (CORE_ADDR pc)
+{
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc);
+
+ return msymbol && MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc;
+}
/* Return leading symbol character for a BFD. If BFD is NULL,
@@ -738,6 +749,7 @@ prim_record_minimal_symbol (const char *
switch (ms_type)
{
case mst_text:
+ case mst_text_gnu_ifunc:
case mst_file_text:
case mst_solib_trampoline:
section = SECT_OFF_TEXT (objfile);
@@ -1184,7 +1196,8 @@ find_solib_trampoline_target (struct fra
{
ALL_MSYMBOLS (objfile, msymbol)
{
- if (MSYMBOL_TYPE (msymbol) == mst_text
+ if ((MSYMBOL_TYPE (msymbol) == mst_text
+ || MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc)
&& strcmp (SYMBOL_LINKAGE_NAME (msymbol),
SYMBOL_LINKAGE_NAME (tsymbol)) == 0)
return SYMBOL_VALUE_ADDRESS (msymbol);
Index: gdb-7.0/gdb/parse.c
===================================================================
--- gdb-7.0.orig/gdb/parse.c 2009-11-25 10:24:47.000000000 +0100
+++ gdb-7.0/gdb/parse.c 2009-11-25 10:26:16.000000000 +0100
@@ -517,6 +517,11 @@ write_exp_msymbol (struct minimal_symbol
write_exp_elt_type (objfile_type (objfile)->nodebug_text_symbol);
break;
+ case mst_text_gnu_ifunc:
+ write_exp_elt_type (objfile_type (objfile)
+ ->nodebug_text_gnu_ifunc_symbol);
+ break;
+
case mst_data:
case mst_file_data:
case mst_bss:
Index: gdb-7.0/gdb/solib-svr4.c
===================================================================
--- gdb-7.0.orig/gdb/solib-svr4.c 2009-11-25 10:24:49.000000000 +0100
+++ gdb-7.0/gdb/solib-svr4.c 2009-11-25 10:26:41.000000000 +0100
@@ -1242,7 +1242,8 @@ svr4_in_dynsym_resolve_code (CORE_ADDR p
{
return ((pc >= interp_text_sect_low && pc < interp_text_sect_high)
|| (pc >= interp_plt_sect_low && pc < interp_plt_sect_high)
- || in_plt_section (pc, NULL));
+ || in_plt_section (pc, NULL)
+ || in_gnu_ifunc_stub (pc));
}
/* Given an executable's ABFD and target, compute the entry-point
Index: gdb-7.0/gdb/symmisc.c
===================================================================
--- gdb-7.0.orig/gdb/symmisc.c 2009-11-25 10:24:47.000000000 +0100
+++ gdb-7.0/gdb/symmisc.c 2009-11-25 10:24:56.000000000 +0100
@@ -287,6 +287,9 @@ dump_msymbols (struct objfile *objfile,
case mst_text:
ms_type = 'T';
break;
+ case mst_text_gnu_ifunc:
+ ms_type = 'i';
+ break;
case mst_solib_trampoline:
ms_type = 'S';
break;
Index: gdb-7.0/gdb/symtab.c
===================================================================
--- gdb-7.0.orig/gdb/symtab.c 2009-11-25 10:24:47.000000000 +0100
+++ gdb-7.0/gdb/symtab.c 2009-11-25 10:24:56.000000000 +0100
@@ -3155,7 +3155,7 @@ search_symbols (char *regexp, domain_enu
{mst_file_data, mst_solib_trampoline, mst_abs, mst_unknown};
static enum minimal_symbol_type types4[]
=
- {mst_file_bss, mst_text, mst_abs, mst_unknown};
+ {mst_file_bss, mst_text_gnu_ifunc, mst_abs, mst_unknown};
enum minimal_symbol_type ourtype;
enum minimal_symbol_type ourtype2;
enum minimal_symbol_type ourtype3;
Index: gdb-7.0/gdb/symtab.h
===================================================================
--- gdb-7.0.orig/gdb/symtab.h 2009-11-25 10:24:45.000000000 +0100
+++ gdb-7.0/gdb/symtab.h 2009-11-25 10:24:56.000000000 +0100
@@ -275,6 +275,8 @@ enum minimal_symbol_type
{
mst_unknown = 0, /* Unknown type, the default */
mst_text, /* Generally executable instructions */
+ mst_text_gnu_ifunc, /* Executable code returning address
+ of executable code */
mst_data, /* Generally initialized data */
mst_bss, /* Generally uninitialized data */
mst_abs, /* Generally absolute (nonrelocatable) */
@@ -1149,6 +1151,8 @@ extern struct minimal_symbol *lookup_min
extern struct minimal_symbol *lookup_minimal_symbol_by_pc (CORE_ADDR);
+extern int in_gnu_ifunc_stub (CORE_ADDR pc);
+
extern struct minimal_symbol
*lookup_minimal_symbol_by_pc_section (CORE_ADDR, struct obj_section *);
Index: gdb-7.0/gdb/testsuite/gdb.base/gnu-ifunc-lib.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.0/gdb/testsuite/gdb.base/gnu-ifunc-lib.c 2009-11-25 10:24:56.000000000 +0100
@@ -0,0 +1,45 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+
+typedef int (*final_t) (int arg);
+
+static int
+final (int arg)
+{
+ return arg + 1;
+}
+
+static volatile int gnu_ifunc_initialized;
+
+void
+gnu_ifunc_pre (void)
+{
+ assert (!gnu_ifunc_initialized);
+}
+
+final_t gnu_ifuncX (void) asm ("gnu_ifunc");
+asm (".type gnu_ifunc, @gnu_indirect_function");
+
+final_t
+gnu_ifuncX (void)
+{
+ gnu_ifunc_initialized = 1;
+
+ return final;
+}
Index: gdb-7.0/gdb/testsuite/gdb.base/gnu-ifunc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.0/gdb/testsuite/gdb.base/gnu-ifunc.c 2009-11-25 10:24:56.000000000 +0100
@@ -0,0 +1,36 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+
+extern int gnu_ifunc (int arg);
+extern void gnu_ifunc_pre (void);
+
+int
+main (void)
+{
+ int i;
+
+ gnu_ifunc_pre ();
+
+ i = gnu_ifunc (1); /* break-at-call */
+ assert (i == 2);
+
+ gnu_ifunc (2); /* break-at-nextcall */
+
+ return 0; /* break-at-exit */
+}
Index: gdb-7.0/gdb/testsuite/gdb.base/gnu-ifunc.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.0/gdb/testsuite/gdb.base/gnu-ifunc.exp 2009-11-25 10:24:56.000000000 +0100
@@ -0,0 +1,72 @@
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if {[skip_shlib_tests]} {
+ return 0
+}
+
+set testfile "gnu-ifunc"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+set libfile "${testfile}-lib"
+set libsrc ${libfile}.c
+set lib_so ${objdir}/${subdir}/${libfile}.so
+
+set lib_opts [list debug]
+set exec_opts [list debug shlib=$lib_so]
+
+if [get_compiler_info ${binfile}] {
+ return -1
+}
+
+if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc $lib_so $lib_opts] != ""
+ || [gdb_compile ${srcdir}/${subdir}/$srcfile $binfile executable $exec_opts] != ""} {
+ untested "Could not compile either $libsrc or $srcfile."
+ return -1
+}
+
+# Start with a fresh gdb.
+
+clean_restart $testfile
+gdb_load_shlibs ${lib_so}
+
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 1;
+}
+
+gdb_breakpoint [gdb_get_line_number "break-at-nextcall"]
+
+gdb_breakpoint [gdb_get_line_number "break-at-call"]
+gdb_continue_to_breakpoint "break-at-call" ".*break-at-call.*"
+
+# Test GDB will automatically indirect the call.
+
+gdb_test "p gnu_ifunc (3)" " = 4"
+
+# Test GDB will skip the gnu_ifunc resolver on first call.
+
+gdb_test "step" "\r\nfinal .*"
+
+# Test GDB will not break before the final chosen implementation.
+
+gdb_continue_to_breakpoint "break-at-nextcall" ".*break-at-nextcall.*"
+
+gdb_breakpoint "gnu_ifunc"
+
+gdb_continue_to_breakpoint "nextcall gnu_ifunc"
+
+gdb_test "frame" "#0 +final \\(.*" "nextcall gnu_ifunc skipped"