* [patch] STT_GNU_IFUNC support
@ 2010-02-14 20:35 Jan Kratochvil
2010-02-14 21:43 ` Mark Kettenis
2010-02-15 18:40 ` Daniel Jacobowitz
0 siblings, 2 replies; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-14 20:35 UTC (permalink / raw)
To: gdb-patches
Hi,
recent systems (at least Fedora 12; Nov 2009) using STT_GNU_IFUNC have
regressed FSF GDB (incl. HEAD):
(gdb) p strcmp ("a", "a")
$1 = 162645472
(gdb) disass strcmp
Dump of assembler code for function strcmp:
0x0000003009a7d870 <+0>: cmpl $0x0,0x2fa9a9(%rip) # 0x3009d78220 <__cpu_features>
0x0000003009a7d877 <+7>: jne 0x3009a7d87e <strcmp+14>
0x0000003009a7d879 <+9>: callq 0x3009a1ee60 <__init_cpu_features>
...
7016: 0000003009a7d870 60 IFUNC GLOBAL DEFAULT 12 strcmp
1242: 0000003009b1c5e0 3737 FUNC LOCAL DEFAULT 12 __strcmp_sse42
1243: 0000003009a7d8b0 5177 FUNC LOCAL DEFAULT 12 __strcmp_sse2
5114: 0000003009b23800 4697 FUNC LOCAL DEFAULT 12 __strcmp_ssse3
Some performance-critical functions are now just returning pointer to the real
function to execute; it gets transparently resolved by ld.so.
------------------------------------------------------------------------------
STT_GNU_IFUNC
This symbol type is the same as STT_FUNC except that it always
points to a function or piece of executable code which takes no
arguments and returns a function pointer. If an STT_GNU_IFUNC
symbol is referred to by a relocation, then evaluation of that
relocation is delayed until load-time. The value used in the
relocation is the function pointer returned by an invocation
of the STT_GNU_IFUNC symbol.
The purpose of this symbol type is to allow the run-time to
select between multiple versions of the implementation of a
specific function. The selection made in general will take the
currently available hardware into account and select the most
appropriate version.
STT_GNU_IFUNC is defined in OS-specific range:
#define STT_LOOS 10 /* OS-specific semantics */
#define STT_GNU_IFUNC 10 /* Symbol is an indirect code object */
#define STT_HIOS 12 /* OS-specific semantics */
STT_GNU_IFUNC symbols can be in shared object, dynamic executable and
static executable. DT_GNU_XXX tags are used to mark the indirect
relocation table for relocations against locally defined STT_GNU_IFUNC
symbols.
------------------------------------------------------------------------------
This implementation resolves ifunc in an uncached way by an inferior call any
time the symbol resolution is needed. It does not try to pick out the jump
address value from ".got.plt" as filled there by ld.so. (GDB stepping into
library functions also does not try to do so.)
As I find gnu-ifunc functions to be mostly the same kind of indirection as the
ppc64-and-others function descriptors I have hooked it at the same GDB
infrastructure. This makes it a bit problematic to find out the real gnu-ifunc
resolver address but regular users should not be interested in it and one can
query it using "info addr" (the same already applies to function descriptors).
# With running (stopped) inferior to be able to call the gnu-ifunc resolvers.
(gdb) p strcmp
$1 = {<text variable, no debug info>} 0x3009b1c5e0 <__strcmp_sse42>
(gdb) info addr strcmp
Symbol "strcmp" is at 0x3009a7d870 in a file compiled without debugging.
# Without started inferior thus unable to call the gnu-ifunc resolvers.
$ ./gdb -nx /lib64/libc.so.6
(gdb) p strcmp
$1 = {<text gnu-ifunc variable, no debug info>} 0x3009a7d870 <strcmp>
Due to the function descriptors similarity I had to hook it into the OSABI
framework which does not support any inheritance. Single OSABI is chosen.
gnu-ifunc is only OS-dependent (GNU) and not arch dependent, though.
Therefore gnu-ifunc had to be hooked into all the OS-ARCH combinations where
the GNU OS gets installed as OSABI (=> *-linux-tdep.c).
As other GNU tools already support STT_GNU_IFUNC in stable releases it would be
nice for gdb-7.1 but I understand it is probably too late:
binutils supports IFUNC since (first of the check-ins):
commit 493c5fa01eebba9c31de96d60fcd1fbfea604f81
Author: Nick Clifton <nickc@redhat.com>
Date: Thu Apr 30 15:47:13 2009 +0000
released version 2.19 - Oct 2009
glibc has used it since (first of the check-ins):
commit 425ce2edb9d11cc1ff650fac16dfbc450241896a
Author: Ulrich Drepper <drepper@redhat.com>
Date: Fri Mar 13 23:53:18 2009 +0000
released version 2.10 - May 2009
No regressions on {x86_64,x86_64-m32,i686}-fedora12-linux-gnu. Testcase
PASSing tested on {x86_64,x86_64-m32,i686}-fedora12-linux-gnu and
{ppc64-m64,ppc64-m32,ppc}-fedora13pre-linux-gnu. On older systems the new
testcase will fail with:
Running ./gdb.base/gnu-ifunc.exp ...
gdb compile failed, /tmp/ccuTiefA.s: Assembler messages:
/tmp/ccuTiefA.s:63: Error: unrecognized symbol type "gnu_indirect_function"
(=> an "untested" testcase)
Thanks,
Jan
gdb/
2010-02-14 Jan Kratochvil <jan.kratochvil@redhat.com>
Transparent STT_GNU_IFUNC support.
* elfread.c (record_minimal_symbol): Apply also for mst_text_gnu_ifunc.
(elf_symtab_read) <sym->flags & BSF_GNU_INDIRECT_FUNCTION>: New.
* gdbtypes.c (init_type): Support TYPE_FLAG_GNU_IFUNC.
(gdbtypes_post_init): Initialize builtin_func_func.
(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>
(struct objfile_type) <nodebug_text_gnu_ifunc_symbol>: New.
* linux-tdep.c: Include value.h and infcall.h.
(linux_convert_from_func_and_ptr, linux_convert_from_func_ptr_addr):
New.
* linux-tdep.h (linux_convert_from_func_and_ptr)
(linux_convert_from_func_ptr_addr): 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.
* objc-lang.c (find_methods): Call gdbarch_convert_from_func_ptr_addr
later. New comment why.
* parse.c (write_exp_msymbol): New variable ifunc_msym. Support also
mst_text_gnu_ifunc as the resolved entry point type. Support also
mst_text_gnu_ifunc for the write_exp_elt_type call.
* 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.
* alpha-linux-tdep.c: Include linux-tdep.h.
(alpha_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* amd64-linux-tdep.c (amd64_linux_init_abi): Install
linux_convert_from_func_ptr_addr.
* arm-linux-tdep.c (arm_linux_init_abi): Install
linux_convert_from_func_ptr_addr.
* configure.tgt (alpha*-*-linux*, am33_2.0*-*-linux*, ia64-*-linux*)
(m32r*-*-linux*, microblaze*-linux-*, mips*-*-linux*, sh*-*-linux*)
(sparc-*-linux*, sparc64-*-linux*, xtensa*-*-linux*): Add linux-tdep.o
to gdb_target_obs.
* frv-linux-tdep.c: Include linux-tdep.h.
(frv_linux_convert_from_func_ptr_addr): New.
(frv_linux_init_abi): Install it or linux_convert_from_func_ptr_addr.
* frv-tdep.c (frv_convert_from_func_ptr_addr): Remove static qualifier.
New comment.
* frv-tdep.h (frv_convert_from_func_ptr_addr): New declaration.
* hppa-linux-tdep.c: Include linux-tdep.h.
(hppa32_linux_convert_from_func_ptr_addr): New.
(hppa_linux_init_abi): Install it or linux_convert_from_func_ptr_addr.
* hppa-tdep.c (hppa32_convert_from_func_ptr_addr): Remove static
qualifier. New comment.
* hppa-tdep.h (hppa32_convert_from_func_ptr_addr): New declaration.
* i386-linux-tdep.c (i386_linux_init_abi): Install
linux_convert_from_func_ptr_addr.
* ia64-linux-tdep.c: Include linux-tdep.h.
(ia64_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* m32r-linux-tdep.c: Include linux-tdep.h.
(m32r_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* microblaze-linux-tdep.c: Include linux-tdep.h.
(microblaze_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* mips-linux-tdep.c: Include linux-tdep.h.
(mips_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* mn10300-linux-tdep.c: Include linux-tdep.h.
(am33_linux_init_osabi): Install linux_convert_from_func_ptr_addr.
* ppc-linux-tdep.c: Include linux-tdep.h.
(ppc64_linux_convert_from_func_ptr_addr): Rename to ...
(convert_from_func_ptr_addr): ... this name.
(ppc64_linux_convert_from_func_ptr_addr): New wrapper of it.
(ppc_linux_init_abi) <tdep->wordsize == 4>: Install
linux_convert_from_func_ptr_addr.
* sh-linux-tdep.c: Include linux-tdep.h.
(sh_linux_init_abi): Include linux_convert_from_func_ptr_addr.
* sparc-linux-tdep.c: Include linux-tdep.h.
(sparc32_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* sparc64-linux-tdep.c: Include linux-tdep.h.
(sparc64_linux_init_abi): Install linux_convert_from_func_ptr_addr.
* xtensa-linux-tdep.c: Include linux-tdep.h.
(xtensa_linux_init_abi): Install linux_convert_from_func_ptr_addr.
gdb/testsuite/
2010-02-14 Jan Kratochvil <jan.kratochvil@redhat.com>
Transparent STT_GNU_IFUNC support.
* gdb.base/gnu-ifunc-lib.c, gdb.base/gnu-ifunc.c,
gdb.base/gnu-ifunc.exp: New.
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -179,7 +179,8 @@ record_minimal_symbol (const char *name, int name_len, int copy_name,
{
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_full (name, name_len, copy_name, address,
@@ -388,7 +389,10 @@ elf_symtab_read (struct objfile *objfile, int type,
{
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)
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1791,6 +1791,8 @@ init_type (enum type_code code, int length, int flags,
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),
@@ -3475,6 +3477,8 @@ gdbtypes_post_init (struct gdbarch *gdbarch)
= 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
+ = lookup_function_type (builtin_type->builtin_func_ptr);
/* This type represents a GDB internal function. */
builtin_type->internal_fn
@@ -3588,6 +3592,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,
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -171,6 +171,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
@@ -271,6 +272,12 @@ enum type_instance_flag_value
#define TYPE_NOTTEXT(t) (TYPE_MAIN_TYPE (t)->flag_nottext)
+/* Used only for TYPE_CODE_FUNC where it 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. */
@@ -390,6 +397,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;
/* True if this type was declared with "class" rather than
@@ -1139,6 +1147,10 @@ struct builtin_type
(*) () can server as a generic function pointer. */
struct type *builtin_func_ptr;
+ /* `function returning pointer to function (returning void)' type.
+ The final void return type is not significant for it. */
+ struct type *builtin_func_func;
+
/* Special-purpose types. */
@@ -1179,6 +1191,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;
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -23,6 +23,8 @@
#include "auxv.h"
#include "target.h"
#include "elf/common.h"
+#include "value.h"
+#include "infcall.h"
/* This function is suitable for architectures that don't
extend/override the standard siginfo structure. */
@@ -152,3 +154,71 @@ linux_has_shared_address_space (void)
return target_is_uclinux;
}
+
+/* Call gnu-ifunc (STT_GNU_IFUNC - a function returning addresss of a real
+ function to call) to resolve breakpoint at its returned function. FUNC_PTR
+ is the function pointer (possibly function descriptor), PC is the function
+ entry. Use linux_convert_from_func_ptr_addr if no function descriptors need
+ to be resolved on the platform. FUNC_PTR is equal to PC on platforms
+ without function descriptors. Function returns FUNC_PTR if no gnu-ifunc has
+ been found, otherwise it returns address of the gnu-ifunc-resolved function
+ to call. Returned address is always the type of address possibly being
+ a function descriptor. */
+
+CORE_ADDR
+linux_convert_from_func_and_ptr (struct gdbarch *gdbarch, CORE_ADDR func_ptr,
+ CORE_ADDR pc)
+{
+ struct type *func_func_type = builtin_type (gdbarch)->builtin_func_func;
+ struct minimal_symbol *msymbol;
+ struct value *function, *address;
+
+ if (!target_has_execution)
+ return func_ptr;
+
+ if (func_ptr != pc)
+ {
+ /* Verify FUNC_PTR is a function descriptor. */
+
+ msymbol = lookup_minimal_symbol_by_pc (func_ptr);
+ if (msymbol == NULL)
+ return func_ptr;
+ if (MSYMBOL_TYPE (msymbol) != mst_data)
+ return func_ptr;
+ if (SYMBOL_VALUE_ADDRESS (msymbol) != func_ptr)
+ return func_ptr;
+ }
+
+ /* Verify PC is a function entry point. */
+
+ msymbol = lookup_minimal_symbol_by_pc (pc);
+ if (msymbol == NULL)
+ return func_ptr;
+ if (MSYMBOL_TYPE (msymbol) != mst_text_gnu_ifunc)
+ return func_ptr;
+ if (SYMBOL_VALUE_ADDRESS (msymbol) != pc)
+ return func_ptr;
+
+ function = allocate_value (func_func_type);
+ set_value_address (function, pc);
+
+ /* gnu-ifuncs have no arguments. FUNCTION is the code instruction address
+ while ADDRESS is a function descriptor. */
+ address = call_function_by_hand (function, 0, NULL);
+
+ return value_as_address (address);
+}
+
+/* Call gnu-ifunc (STT_GNU_IFUNC - a function returning addresss of a real
+ function to call) to resolve breakpoint at its returned function. Use
+ linux_convert_from_func_and_ptr if the platform requires also a function
+ descriptor resolution. ADDR is address of the possibly-gnu-ifunc function.
+ Function returns ADDR if no gnu-ifunc has been found, otherwise it returns
+ address of the resolved function to call. */
+
+CORE_ADDR
+linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ return linux_convert_from_func_and_ptr (gdbarch, addr, addr);
+}
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -22,4 +22,11 @@
struct type *linux_get_siginfo_type (struct gdbarch *);
+CORE_ADDR linux_convert_from_func_and_ptr (struct gdbarch *gdbarch,
+ CORE_ADDR func_ptr, CORE_ADDR pc);
+
+CORE_ADDR linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ);
+
#endif /* linux-tdep.h */
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -331,8 +331,9 @@ lookup_minimal_symbol_text (const char *name, struct objfile *objf)
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))
{
@@ -694,6 +695,16 @@ lookup_minimal_symbol_by_pc (CORE_ADDR pc)
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;
+}
+
/* Find the minimal symbol named NAME, and return both the minsym
struct and its objfile. This only checks the linkage name. Sets
*OBJFILE_P and returns the minimal symbol, if it is found. If it
@@ -763,6 +774,7 @@ prim_record_minimal_symbol (const char *name, CORE_ADDR address,
switch (ms_type)
{
case mst_text:
+ case mst_text_gnu_ifunc:
case mst_file_text:
case mst_solib_trampoline:
section = SECT_OFF_TEXT (objfile);
@@ -1228,7 +1240,8 @@ find_solib_trampoline_target (struct frame_info *frame, CORE_ADDR pc)
{
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);
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -1178,16 +1178,6 @@ find_methods (struct symtab *symtab, char type,
QUIT;
- /* The minimal symbol might point to a function descriptor;
- resolve it to the actual code address instead. */
- pc = gdbarch_convert_from_func_ptr_addr (gdbarch, pc,
- ¤t_target);
-
- if (symtab)
- if (pc < BLOCK_START (block) || pc >= BLOCK_END (block))
- /* Not in the specified symtab. */
- continue;
-
symname = SYMBOL_NATURAL_NAME (msymbol);
if (symname == NULL)
continue;
@@ -1223,6 +1213,17 @@ find_methods (struct symtab *symtab, char type,
((nselector == NULL) || (strcmp (selector, nselector) != 0)))
continue;
+ /* The minimal symbol might point to a function descriptor;
+ resolve it to the actual code address instead. Delay the call as
+ its resolution may be expensive. */
+ pc = gdbarch_convert_from_func_ptr_addr (gdbarch, pc,
+ ¤t_target);
+
+ if (symtab)
+ if (pc < BLOCK_START (block) || pc >= BLOCK_END (block))
+ /* Not in the specified symtab. */
+ continue;
+
sym = find_pc_function (pc);
if (sym != NULL)
{
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -489,9 +489,21 @@ write_exp_msymbol (struct minimal_symbol *msymbol)
pc = gdbarch_convert_from_func_ptr_addr (gdbarch, addr, ¤t_target);
if (pc != addr)
{
+ struct minimal_symbol *ifunc_msym = lookup_minimal_symbol_by_pc (pc);
+
/* In this case, assume we have a code symbol instead of
a data symbol. */
- type = mst_text;
+
+ if (ifunc_msym != NULL && MSYMBOL_TYPE (ifunc_msym) == mst_text_gnu_ifunc
+ && SYMBOL_VALUE_ADDRESS (ifunc_msym) == pc)
+ {
+ /* A function descriptor has been resolved but PC is still in the
+ gnu-ifunc resolver body (such as because inferior does not run to
+ be able to call it). */
+ type = mst_text_gnu_ifunc;
+ }
+ else
+ type = mst_text;
section = NULL;
addr = pc;
}
@@ -523,6 +535,11 @@ write_exp_msymbol (struct minimal_symbol *msymbol)
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:
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -1235,7 +1235,8 @@ svr4_in_dynsym_resolve_code (CORE_ADDR pc)
&& pc < info->interp_text_sect_high)
|| (pc >= info->interp_plt_sect_low
&& pc < info->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
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -294,6 +294,9 @@ dump_msymbols (struct objfile *objfile, struct ui_file *outfile)
case mst_text:
ms_type = 'T';
break;
+ case mst_text_gnu_ifunc:
+ ms_type = 'i';
+ break;
case mst_solib_trampoline:
ms_type = 'S';
break;
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3201,7 +3201,7 @@ search_symbols (char *regexp, domain_enum kind, int nfiles, char *files[],
{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;
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -280,6 +280,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) */
@@ -1159,6 +1161,8 @@ extern struct minimal_symbol *lookup_minimal_symbol_by_pc_name
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_and_objfile (const char *,
struct objfile **);
--- a/gdb/alpha-linux-tdep.c
+++ b/gdb/alpha-linux-tdep.c
@@ -26,6 +26,7 @@
#include "symtab.h"
#include "regset.h"
#include "regcache.h"
+#include "linux-tdep.h"
#include "alpha-tdep.h"
@@ -236,6 +237,9 @@ alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_gdbarch_regset_from_core_section
(gdbarch, alpha_linux_regset_from_core_section);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1481,6 +1481,9 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
amd64_linux_record_tdep.arg6 = AMD64_R9_REGNUM;
tdep->i386_syscall_record = amd64_linux_syscall_record;
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
\f
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -918,6 +918,9 @@ arm_linux_init_abi (struct gdbarch_info info,
set_gdbarch_displaced_step_free_closure (gdbarch,
simple_displaced_step_free_closure);
set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -40,7 +40,7 @@ alpha*-*-osf*)
alpha*-*-linux*)
# Target: Little-endian Alpha running Linux
gdb_target_obs="alpha-tdep.o alpha-mdebug-tdep.o alpha-linux-tdep.o \
- solib.o solib-svr4.o"
+ solib.o solib-svr4.o linux-tdep.o"
;;
alpha*-*-freebsd* | alpha*-*-kfreebsd*-gnu)
# Target: FreeBSD/alpha
@@ -67,7 +67,7 @@ alpha*-*-*)
am33_2.0*-*-linux*)
# Target: Matsushita mn10300 (AM33) running Linux
gdb_target_obs="mn10300-tdep.o mn10300-linux-tdep.o corelow.o \
- solib.o solib-svr4.o"
+ solib.o solib-svr4.o linux-tdep.o"
;;
arm*-wince-pe | arm*-*-mingw32ce*)
@@ -233,7 +233,7 @@ i[34567]86-*-*)
ia64-*-linux*)
# Target: Intel IA-64 running GNU/Linux
- gdb_target_obs="ia64-tdep.o ia64-linux-tdep.o \
+ gdb_target_obs="ia64-tdep.o ia64-linux-tdep.o linux-tdep.o \
solib.o solib-svr4.o symfile-mem.o"
build_gdbserver=yes
;;
@@ -263,7 +263,8 @@ m32c-*-*)
m32r*-*-linux*)
# Target: Renesas M32R running GNU/Linux
gdb_target_obs="m32r-tdep.o m32r-linux-tdep.o remote-m32r-sdi.o \
- glibc-tdep.o solib.o solib-svr4.o symfile-mem.o"
+ glibc-tdep.o solib.o solib-svr4.o symfile-mem.o \
+ linux-tdep.o"
gdb_sim=../sim/m32r/libsim.a
build_gdbserver=yes
;;
@@ -317,7 +318,7 @@ microblaze*-linux-*)
# Target: Xilinx MicroBlaze running Linux
gdb_target_obs="microblaze-tdep.o microblaze-linux-tdep.o microblaze-rom.o \
monitor.o dsrec.o solib.o solib-svr4.o corelow.o \
- symfile-mem.o"
+ symfile-mem.o linux-tdep.o"
gdb_sim=../sim/microblaze/libsim.a
;;
microblaze*-xilinx-*)
@@ -337,7 +338,8 @@ mips*-sgi-irix6*)
mips*-*-linux*)
# Target: Linux/MIPS
gdb_target_obs="mips-tdep.o mips-linux-tdep.o glibc-tdep.o \
- corelow.o solib.o solib-svr4.o symfile-mem.o"
+ corelow.o solib.o solib-svr4.o symfile-mem.o \
+ linux-tdep.o"
gdb_sim=../sim/mips/libsim.a
build_gdbserver=yes
;;
@@ -427,7 +429,7 @@ sh*-*-linux*)
# Target: GNU/Linux Super-H
gdb_target_obs="sh-tdep.o sh64-tdep.o sh-linux-tdep.o monitor.o \
dsrec.o solib.o solib-svr4.o symfile-mem.o \
- glibc-tdep.o corelow.o"
+ glibc-tdep.o corelow.o linux-tdep.o"
gdb_sim=../sim/sh/libsim.a
build_gdbserver=yes
;;
@@ -455,7 +457,8 @@ sh*)
sparc-*-linux*)
# Target: GNU/Linux SPARC
gdb_target_obs="sparc-tdep.o sparc-sol2-tdep.o sol2-tdep.o \
- sparc-linux-tdep.o solib.o solib-svr4.o symfile-mem.o"
+ sparc-linux-tdep.o solib.o solib-svr4.o symfile-mem.o \
+ linux-tdep.o"
if test "x$enable_64_bit_bfd" = "xyes"; then
# Target: GNU/Linux UltraSPARC
gdb_target_obs="sparc64-tdep.o sparc64-sol2-tdep.o \
@@ -466,7 +469,7 @@ sparc64-*-linux*)
# Target: GNU/Linux UltraSPARC
gdb_target_obs="sparc64-tdep.o sparc64-sol2-tdep.o sol2-tdep.o \
sparc64-linux-tdep.o sparc-tdep.o sparc-sol2-tdep.o \
- sparc-linux-tdep.o solib.o solib-svr4.o"
+ sparc-linux-tdep.o solib.o solib-svr4.o linux-tdep.o"
build_gdbserver=yes
;;
sparc*-*-freebsd* | sparc*-*-kfreebsd*-gnu)
@@ -601,7 +604,8 @@ x86_64-*-openbsd*)
xtensa*-*-linux*) gdb_target=linux
# Target: GNU/Linux Xtensa
gdb_target_obs="xtensa-tdep.o xtensa-config.o xtensa-linux-tdep.o \
- solib.o solib-svr4.o corelow.o symfile-mem.o"
+ solib.o solib-svr4.o corelow.o symfile-mem.o \
+ linux-tdep.o"
build_gdbserver=yes
;;
xtensa*)
--- a/gdb/frv-linux-tdep.c
+++ b/gdb/frv-linux-tdep.c
@@ -32,6 +32,7 @@
#include "frame-unwind.h"
#include "regset.h"
#include "gdb_string.h"
+#include "linux-tdep.h"
/* Define the size (in bytes) of an FR-V instruction. */
static const int frv_instr_size = 4;
@@ -486,7 +487,23 @@ frv_linux_regset_from_core_section (struct gdbarch *gdbarch,
return NULL;
}
-\f
+/* Resolve both gnu-ifunc function addresses and function descriptors. */
+
+static CORE_ADDR
+frv_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ CORE_ADDR pc = frv_convert_from_func_ptr_addr (gdbarch, addr, targ);
+ CORE_ADDR resolved_addr;
+
+ resolved_addr = linux_convert_from_func_and_ptr (gdbarch, addr, pc);
+ if (resolved_addr != addr)
+ pc = frv_convert_from_func_ptr_addr (gdbarch, resolved_addr, targ);
+
+ return pc;
+}
+
static void
frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -494,6 +511,13 @@ frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
frame_unwind_append_unwinder (gdbarch, &frv_linux_sigtramp_frame_unwind);
set_gdbarch_regset_from_core_section (gdbarch,
frv_linux_regset_from_core_section);
+
+ if (frv_abi (gdbarch) == FRV_ABI_FDPIC)
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ frv_linux_convert_from_func_ptr_addr);
+ else
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
static enum gdb_osabi
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1169,7 +1169,10 @@ find_func_descr (struct gdbarch *gdbarch, CORE_ADDR entry_point)
return descr;
}
-static CORE_ADDR
+/* Resolve function descriptors. Return ADDR if no function descriptor was
+ found. */
+
+CORE_ADDR
frv_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr,
struct target_ops *targ)
{
--- a/gdb/frv-tdep.h
+++ b/gdb/frv-tdep.h
@@ -118,3 +118,6 @@ CORE_ADDR frv_fetch_objfile_link_map (struct objfile *objfile);
struct target_so_ops;
extern struct target_so_ops frv_so_ops;
+CORE_ADDR frv_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ);
--- a/gdb/hppa-linux-tdep.c
+++ b/gdb/hppa-linux-tdep.c
@@ -32,6 +32,7 @@
#include "regset.h"
#include "regcache.h"
#include "hppa-tdep.h"
+#include "linux-tdep.h"
#include "elf/common.h"
@@ -513,7 +514,23 @@ hppa_linux_regset_from_core_section (struct gdbarch *gdbarch,
return NULL;
}
-\f
+
+/* Resolve both gnu-ifunc function addresses and function descriptors. */
+
+static CORE_ADDR
+hppa32_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ CORE_ADDR pc = hppa32_convert_from_func_ptr_addr (gdbarch, addr, targ);
+ CORE_ADDR resolved_addr;
+
+ resolved_addr = linux_convert_from_func_and_ptr (gdbarch, addr, pc);
+ if (resolved_addr != addr)
+ pc = hppa32_convert_from_func_ptr_addr (gdbarch, resolved_addr, targ);
+
+ return pc;
+}
/* Forward declarations. */
extern initialize_file_ftype _initialize_hppa_linux_tdep;
@@ -555,6 +572,13 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+
+ if (tdep->bytes_per_address == 4)
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ hppa32_linux_convert_from_func_ptr_addr);
+ else
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
void
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -1245,9 +1245,11 @@ hppa64_return_value (struct gdbarch *gdbarch, struct type *func_type,
return RETURN_VALUE_REGISTER_CONVENTION;
}
-\f
-static CORE_ADDR
+/* Resolve function descriptors. Return ADDR if no function descriptor was
+ found. */
+
+CORE_ADDR
hppa32_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr,
struct target_ops *targ)
{
--- a/gdb/hppa-tdep.h
+++ b/gdb/hppa-tdep.h
@@ -246,4 +246,8 @@ extern int hppa_in_solib_call_trampoline (struct gdbarch *gdbarch,
CORE_ADDR pc, char *name);
extern CORE_ADDR hppa_skip_trampoline_code (struct frame_info *, CORE_ADDR pc);
+extern CORE_ADDR hppa32_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ);
+
#endif /* hppa-tdep.h */
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -798,6 +798,9 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
i386_linux_get_syscall_number);
set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/ia64-linux-tdep.c
+++ b/gdb/ia64-linux-tdep.c
@@ -26,6 +26,7 @@
#include "osabi.h"
#include "solib-svr4.h"
#include "symtab.h"
+#include "linux-tdep.h"
/* The sigtramp code is in a non-readable (executable-only) region
of memory called the ``gate page''. The addresses in question
@@ -139,6 +140,9 @@ ia64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/m32r-linux-tdep.c
+++ b/gdb/m32r-linux-tdep.c
@@ -30,6 +30,7 @@
#include "gdb_string.h"
#include "glibc-tdep.h"
+#include "linux-tdep.h"
#include "solib-svr4.h"
#include "symtab.h"
@@ -422,6 +423,9 @@ m32r_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/microblaze-linux-tdep.c
+++ b/gdb/microblaze-linux-tdep.c
@@ -35,6 +35,7 @@
#include "trad-frame.h"
#include "frame-unwind.h"
#include "tramp-frame.h"
+#include "linux-tdep.h"
static int
@@ -133,6 +134,9 @@ microblaze_linux_init_abi (struct gdbarch_info info,
/* Trampolines. */
tramp_frame_prepend_unwinder (gdbarch,
µblaze_linux_sighandler_tramp_frame);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
void
--- a/gdb/mips-linux-tdep.c
+++ b/gdb/mips-linux-tdep.c
@@ -38,6 +38,7 @@
#include "target-descriptions.h"
#include "mips-linux-tdep.h"
#include "glibc-tdep.h"
+#include "linux-tdep.h"
static struct target_so_ops mips_svr4_so_ops;
@@ -1225,6 +1226,9 @@ mips_linux_init_abi (struct gdbarch_info info,
tdesc_numbered_register (feature, tdesc_data, MIPS_RESTART_REGNUM,
"restart");
}
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/mn10300-linux-tdep.c
+++ b/gdb/mn10300-linux-tdep.c
@@ -32,6 +32,7 @@
#include "frame.h"
#include "trad-frame.h"
#include "tramp-frame.h"
+#include "linux-tdep.h"
#include <stdlib.h>
@@ -718,6 +719,9 @@ am33_linux_init_osabi (struct gdbarch_info gdbinfo, struct gdbarch *gdbarch)
tramp_frame_prepend_unwinder (gdbarch, &am33_linux_sigframe);
tramp_frame_prepend_unwinder (gdbarch, &am33_linux_rt_sigframe);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -48,6 +48,7 @@
#include "arch-utils.h"
#include "spu-tdep.h"
#include "xml-syscall.h"
+#include "linux-tdep.h"
#include "features/rs6000/powerpc-32l.c"
#include "features/rs6000/powerpc-altivec32l.c"
@@ -608,11 +609,7 @@ ppc64_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
return target? target : pc;
}
-
-/* Support for convert_from_func_ptr_addr (ARCH, ADDR, TARG) on PPC64
- GNU/Linux.
-
- Usually a function pointer's representation is simply the address
+/* Usually a function pointer's representation is simply the address
of the function. On GNU/Linux on the PowerPC however, a function
pointer may be a pointer to a function descriptor.
@@ -636,9 +633,8 @@ ppc64_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
random addresses such as occur when there is no symbol table. */
static CORE_ADDR
-ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
- CORE_ADDR addr,
- struct target_ops *targ)
+convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr,
+ struct target_ops *targ)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
struct target_section *s = target_section_by_addr (targ, addr);
@@ -679,6 +675,27 @@ ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
return addr;
}
+/* Support for convert_from_func_ptr_addr (ARCH, ADDR, TARG) on PPC64
+ GNU/Linux.
+
+ Call convert_from_func_ptr_addr for resolving PPC64 function descriptors but
+ support also gnu-ifunc functions on the GNU/Linux platform. */
+
+static CORE_ADDR
+ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ CORE_ADDR pc = convert_from_func_ptr_addr (gdbarch, addr, targ);
+ CORE_ADDR resolved_addr;
+
+ resolved_addr = linux_convert_from_func_and_ptr (gdbarch, addr, pc);
+ if (resolved_addr != addr)
+ pc = convert_from_func_ptr_addr (gdbarch, resolved_addr, targ);
+
+ return pc;
+}
+
/* Wrappers to handle Linux-only registers. */
static void
@@ -1478,6 +1495,9 @@ ppc_linux_init_abi (struct gdbarch_info info,
if (tdep->wordsize == 4)
{
+ set_gdbarch_convert_from_func_ptr_addr
+ (gdbarch, linux_convert_from_func_ptr_addr);
+
/* Until November 2001, gcc did not comply with the 32 bit SysV
R4 ABI requirement that structures less than or equal to 8
bytes should be returned in registers. Instead GCC was using
--- a/gdb/sh-linux-tdep.c
+++ b/gdb/sh-linux-tdep.c
@@ -25,6 +25,7 @@
#include "glibc-tdep.h"
#include "sh-tdep.h"
+#include "linux-tdep.h"
#define REGSx16(base) \
{(base), 0}, \
@@ -89,6 +90,9 @@ sh_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->core_gregmap = (struct sh_corefile_regmap *)gregs_table;
tdep->core_fpregmap = (struct sh_corefile_regmap *)fpregs_table;
}
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/sparc-linux-tdep.c
+++ b/gdb/sparc-linux-tdep.c
@@ -32,6 +32,7 @@
#include "symtab.h"
#include "trad-frame.h"
#include "tramp-frame.h"
+#include "linux-tdep.h"
#include "sparc-tdep.h"
@@ -279,6 +280,9 @@ sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
dwarf2_append_unwinders (gdbarch);
set_gdbarch_write_pc (gdbarch, sparc_linux_write_pc);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- a/gdb/sparc64-linux-tdep.c
+++ b/gdb/sparc64-linux-tdep.c
@@ -31,6 +31,7 @@
#include "symtab.h"
#include "trad-frame.h"
#include "tramp-frame.h"
+#include "linux-tdep.h"
#include "sparc64-tdep.h"
@@ -244,6 +245,9 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->step_trap = sparc64_linux_step_trap;
set_gdbarch_write_pc (gdbarch, sparc64_linux_write_pc);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
\f
--- a/gdb/xtensa-linux-tdep.c
+++ b/gdb/xtensa-linux-tdep.c
@@ -22,6 +22,7 @@
#include "solib-svr4.h"
#include "symtab.h"
+#include "linux-tdep.h"
/* OS specific initialization of gdbarch. */
@@ -30,6 +31,9 @@ xtensa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_ilp32_fetch_link_map_offsets);
+
+ set_gdbarch_convert_from_func_ptr_addr (gdbarch,
+ linux_convert_from_func_ptr_addr);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gnu-ifunc-lib.c
@@ -0,0 +1,58 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009, 2010 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
+init_stub (int arg)
+{
+ return 0;
+}
+
+static int
+final (int arg)
+{
+ return arg + 1;
+}
+
+/* Make differentiation of how the gnu_ifunc call resolves before and after
+ calling gnu_ifunc_pre. This ensures the resolved function address is not
+ being cached anywhere for the debugging purposes. */
+
+static volatile int gnu_ifunc_initialized;
+
+void
+gnu_ifunc_pre (void)
+{
+ assert (!gnu_ifunc_initialized);
+
+ gnu_ifunc_initialized = 1;
+}
+
+final_t gnu_ifuncX (void) asm ("gnu_ifunc");
+asm (".type gnu_ifunc, @gnu_indirect_function");
+
+final_t
+gnu_ifuncX (void)
+{
+ if (!gnu_ifunc_initialized)
+ return init_stub;
+ else
+ return final;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gnu-ifunc.c
@@ -0,0 +1,36 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009, 2010 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 */
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gnu-ifunc.exp
@@ -0,0 +1,132 @@
+# Copyright (C) 2009, 2010 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_nodebug_so_base ${libfile}-nodebug.so
+set lib_nodebug_so ${objdir}/${subdir}/${lib_nodebug_so_base}
+
+# We need DWARF for the "final" function as we "step" into the function and GDB
+# would step-over the "final" function if there would be no line number debug
+# information (DWARF) available.
+#
+# We must not have DWARF for the "gnu_ifunc" function as DWARF has no way to
+# express the gnu-ifunc type and it would be considered as a regular function
+# due to DWARF by GDB. In ELF gnu-ifunc is expressed by the STT_GNU_IFUNC type.
+#
+# As both functions need to be in the same shared library file and
+# gdb_compile_shlib has no way to specify source-specific compilation options
+# we hide the DWARF debug information for "gnu_ifunc" by defining it as
+# C "gnu_ifuncX" function with asm alias "gnu_ifunc".
+#
+# Still as "gnu_ifunc" gets the wrong "gnu_ifuncX" DWARF debug information it
+# confuses the ELF symbol address->symbol resoluted for printing the symbol
+# value. Therefore use lib_nodebug_opts later to check the address->symbol
+# resolution without DWARF present there.
+#
+# Additionally GCC would generate incorrect DW_AT_MIPS_linkage_name <*gnu_ifunc>
+# for the "gnu_ifunc" DWARF symbol, therefore with disabled DWARF it is no
+# longer a problem.
+
+set lib_opts [list debug]
+set lib_nodebug_opts [list]
+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] != ""
+ || [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc $lib_nodebug_so $lib_nodebug_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;
+}
+
+# The "if" condition is artifical to test regression of a former patch.
+gdb_breakpoint "[gdb_get_line_number "break-at-nextcall"] if i && gnu_ifunc (i) != 42"
+
+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.
+
+# Also test a former patch regression:
+# Continuing.
+# Error in testing breakpoint condition:
+# Attempt to take address of value not located in memory.
+#
+# Breakpoint 2, main () at ./gdb.base/gnu-ifunc.c:33
+
+gdb_test "continue" "Continuing.\r\n\r\nBreakpoint .* (at|in) .*break-at-nextcall.*" \
+ "continue to break-at-nextcall"
+
+gdb_breakpoint "gnu_ifunc"
+
+gdb_continue_to_breakpoint "nextcall gnu_ifunc"
+
+gdb_test "frame" "#0 +(0x\[0-9a-f\]+ in +)?final \\(.*" "nextcall gnu_ifunc skipped"
+
+
+# Compare the two different addresses. "info addr" is the only way to query the
+# gnu_ifunc resolver address without automatically getting from GDB the already
+# resolved function address.
+
+gdb_test "p gnu_ifunc" " = {<text variable, no debug info>} 0x\[0-9a-f\]+ <final>" "p gnu_ifunc executing"
+gdb_test "info sym gnu_ifunc" "final in section .*" "info sym gnu_ifunc executing"
+
+set test "info addr gnu_ifunc"
+gdb_test_multiple $test $test {
+ -re "Symbol \"gnu_ifunc\" is at (0x\[0-9a-f\]+) in .*$gdb_prompt $" {
+ pass $test
+ }
+}
+gdb_test "info sym $expect_out(1,string)" "gnu_ifunc in section .*" "info sym <gnu_ifunc-address>"
+
+
+# Check the lib_nodebug_opts variant.
+
+clean_restart $lib_nodebug_so_base
+
+gdb_test "p gnu_ifunc" " = {<text gnu-ifunc variable, no debug info>} 0x\[0-9a-f\]+ <\.?gnu_ifunc>" "p gnu_ifunc not executing without debug"
+gdb_test "info sym gnu_ifunc" "gnu_ifunc in section .*" "info sym gnu_ifunc not executing without debug"
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-14 20:35 [patch] STT_GNU_IFUNC support Jan Kratochvil
@ 2010-02-14 21:43 ` Mark Kettenis
2010-02-14 21:59 ` Jan Kratochvil
2010-02-15 18:40 ` Daniel Jacobowitz
1 sibling, 1 reply; 13+ messages in thread
From: Mark Kettenis @ 2010-02-14 21:43 UTC (permalink / raw)
To: jan.kratochvil; +Cc: gdb-patches
> Date: Sun, 14 Feb 2010 21:35:12 +0100
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
>
> STT_GNU_IFUNC is defined in OS-specific range:
>
> #define STT_LOOS 10 /* OS-specific semantics */
> #define STT_GNU_IFUNC 10 /* Symbol is an indirect code object */
> #define STT_HIOS 12 /* OS-specific semantics */
>
> ...
>
> Due to the function descriptors similarity I had to hook it into the OSABI
> framework which does not support any inheritance. Single OSABI is chosen.
> gnu-ifunc is only OS-dependent (GNU) and not arch dependent, though.
> Therefore gnu-ifunc had to be hooked into all the OS-ARCH combinations where
> the GNU OS gets installed as OSABI (=> *-linux-tdep.c).
While the symbol may be in the OS-specific range it is actually more
toolchain dependent than OS-dependent. Unless I'm missing something,
all OS'es that use binutils as their toolchain will support
STT_GNU_IFUNC. In particular, I expect the various BSD's to
eventually support this too, and I found a Google groups discussion
that said that while Sun^H^H^Oracle has no immediate plans to support
this extension, they may eventually do and will reserve the slot
occupied by STT_GNU_IFUNC in the STT_LOOS-STT_HIOS range.
This has happened before with GNU extensions. OpenBSD/sh supports
some GNU-specific ELF relocations and I personally added support for
PT_GNU_EH_FRAME to OpenBSD to make DWARF-based exception handling work
on several OpenBSD platforms.
So I think you should move the gnu-ifunc support code out of
linux-tdep.c into a more generic file. And perhaps we should consider
enabling support for it on all supported ELF platforms. Given that
Sun is aware of STT_GNU_IFUNC, I don't think there is a serious risk
of another OS vendor using the value occupied by STT_GNU_IFUNC for a
different purpose.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-14 21:43 ` Mark Kettenis
@ 2010-02-14 21:59 ` Jan Kratochvil
0 siblings, 0 replies; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-14 21:59 UTC (permalink / raw)
To: Mark Kettenis; +Cc: gdb-patches
On Sun, 14 Feb 2010 22:42:30 +0100, Mark Kettenis wrote:
> While the symbol may be in the OS-specific range it is actually more
> toolchain dependent than OS-dependent. Unless I'm missing something,
> all OS'es that use binutils as their toolchain will support
> STT_GNU_IFUNC.
It is true GDB already does not interpret STT_GNU_IFUNC but it just follows
BSF_GNU_INDIRECT_FUNCTION. It is more the bfd/'s task to decide when the
STT_GNU_IFUNC numeric value is valid at the specific platform.
> And perhaps we should consider enabling support for it on all supported ELF
> platforms.
OK, that will make this patch also simple. I will send it later.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-14 20:35 [patch] STT_GNU_IFUNC support Jan Kratochvil
2010-02-14 21:43 ` Mark Kettenis
@ 2010-02-15 18:40 ` Daniel Jacobowitz
2010-02-15 18:49 ` Jan Kratochvil
2010-02-17 12:34 ` Pedro Alves
1 sibling, 2 replies; 13+ messages in thread
From: Daniel Jacobowitz @ 2010-02-15 18:40 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
On Sun, Feb 14, 2010 at 09:35:12PM +0100, Jan Kratochvil wrote:
> This implementation resolves ifunc in an uncached way by an inferior call any
> time the symbol resolution is needed. It does not try to pick out the jump
> address value from ".got.plt" as filled there by ld.so. (GDB stepping into
> library functions also does not try to do so.)
Do you mean that "print strcmp" or "break strcmp" is now going to do
an inferior call? That doesn't seem like a good idea to me. I would
like for some other maintainers to comment though.
Inferior calls are very slow, and they can go wrong (pending signals,
misbehaving programs, etc). I believe we should make an effort to
minimize them.
For user messages, maybe we should call these "indirect functions";
that's what ifunc is short for.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-15 18:40 ` Daniel Jacobowitz
@ 2010-02-15 18:49 ` Jan Kratochvil
2010-02-17 12:34 ` Pedro Alves
1 sibling, 0 replies; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-15 18:49 UTC (permalink / raw)
To: gdb-patches
On Mon, 15 Feb 2010 19:40:50 +0100, Daniel Jacobowitz wrote:
> On Sun, Feb 14, 2010 at 09:35:12PM +0100, Jan Kratochvil wrote:
> > This implementation resolves ifunc in an uncached way by an inferior call any
> > time the symbol resolution is needed. It does not try to pick out the jump
> > address value from ".got.plt" as filled there by ld.so. (GDB stepping into
> > library functions also does not try to do so.)
>
> Do you mean that "print strcmp" or "break strcmp" is now going to do
> an inferior call?
Yes.
> Inferior calls are very slow, and they can go wrong (pending signals,
> misbehaving programs, etc). I believe we should make an effort to
> minimize them.
Caching may not help as during first "print strcmp" it may be called neither
by the inferior nor by gdb yet. Making the "print strcmp" output dependent on
whether "strcmp" has been already called looks too non-deterministic to me.
Displaying the indirect function resolver on "print strcmp" looks to me as
needlessly complex for normal application developers.
> For user messages, maybe we should call these "indirect functions";
> that's what ifunc is short for.
OK.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-15 18:40 ` Daniel Jacobowitz
2010-02-15 18:49 ` Jan Kratochvil
@ 2010-02-17 12:34 ` Pedro Alves
2010-02-17 14:19 ` Jan Kratochvil
1 sibling, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2010-02-17 12:34 UTC (permalink / raw)
To: gdb-patches; +Cc: Daniel Jacobowitz, Jan Kratochvil
On Monday 15 February 2010 18:40:50, Daniel Jacobowitz wrote:
> On Sun, Feb 14, 2010 at 09:35:12PM +0100, Jan Kratochvil wrote:
> > This implementation resolves ifunc in an uncached way by an inferior call any
> > time the symbol resolution is needed. It does not try to pick out the jump
> > address value from ".got.plt" as filled there by ld.so. (GDB stepping into
> > library functions also does not try to do so.)
>
> Do you mean that "print strcmp" or "break strcmp" is now going to do
> an inferior call? That doesn't seem like a good idea to me. I would
> like for some other maintainers to comment though.
>
> Inferior calls are very slow, and they can go wrong (pending signals,
> misbehaving programs, etc). I believe we should make an effort to
> minimize them.
Yeah, agreed, we should avoid them the best we can.
[ Not to mention that the scheduler-locking setting also applies to
them, meaning, in a multi-threaded environment, without more
care, these behind the scenes infcalls resume more than
you'd want (all-threads), which can be surprising, and make other
threads easily hit events while handling the infcall. Something
that IWBN to fix. ]
--
Pedro Alves
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 12:34 ` Pedro Alves
@ 2010-02-17 14:19 ` Jan Kratochvil
2010-02-17 14:46 ` Daniel Jacobowitz
2010-02-17 14:52 ` Pedro Alves
0 siblings, 2 replies; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-17 14:19 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Daniel Jacobowitz
On Wed, 17 Feb 2010 13:34:15 +0100, Pedro Alves wrote:
> On Monday 15 February 2010 18:40:50, Daniel Jacobowitz wrote:
> > Do you mean that "print strcmp" or "break strcmp" is now going to do
> > an inferior call? That doesn't seem like a good idea to me. I would
> > like for some other maintainers to comment though.
> >
> > Inferior calls are very slow, and they can go wrong (pending signals,
> > misbehaving programs, etc). I believe we should make an effort to
> > minimize them.
>
> Yeah, agreed, we should avoid them the best we can.
Possibilities known to me:
(A) Call ifunc-resolver any time it is needed.
= currently implemented.
(B) Pick out the resolver result from .got.plt - if it is already there;
otherwise (A).
(C) Print just the bare ifunc-resolver address for "p strcmp".
+(CACHE) = + possibility: Cache the pointer in GDB.
"Regular users" just print "strcmp (...)" and do not print "strcmp" which
possibly makes (C) a viable option.
When an inferior call of "strcmp (...)" is being made I do not find a problem
doing also the ifunc-resolver call that time, do you?
I would choose (A) + (CACHE) myself. I did not find (CACHE) to be such
a concern to implement it. Inferior calls may be slow on embedded targets?
> [ Not to mention that the scheduler-locking setting also applies to
> them, meaning, in a multi-threaded environment, without more
> care, these behind the scenes infcalls resume more than
> you'd want (all-threads), which can be surprising, and make other
> threads easily hit events while handling the infcall. Something
> that IWBN to fix. ]
If you are concerned about other threads running you should already use at
least "set scheduler-locking step". It should be default anyway.
What about making this GNU-IFUNC inferior call scheduling follow the "step"
policy? Maybe the whole inferior calls should follow the "step" policy?
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 14:19 ` Jan Kratochvil
@ 2010-02-17 14:46 ` Daniel Jacobowitz
2010-02-17 17:45 ` Jan Kratochvil
2010-02-17 14:52 ` Pedro Alves
1 sibling, 1 reply; 13+ messages in thread
From: Daniel Jacobowitz @ 2010-02-17 14:46 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: Pedro Alves, gdb-patches
On Wed, Feb 17, 2010 at 03:19:12PM +0100, Jan Kratochvil wrote:
> (C) Print just the bare ifunc-resolver address for "p strcmp".
>
> +(CACHE) = + possibility: Cache the pointer in GDB.
>
> "Regular users" just print "strcmp (...)" and do not print "strcmp" which
> possibly makes (C) a viable option.
I don't know. What about "disassemble strcmp" - I think it should
disassemble the same thing that will show up in the assembly as "call
0x$hex <strcmp>". But maybe automatically disassembling strcmp_sse is
more useful.
Hmm. I guess that leaves "break strcmp" in a weird place since strcmp
will only be called once. Maybe a breakpoint on an indirect function
should also set a breakpoint on the target of the indirect function?
This is slightly awkward to implement because there's no debug hook
after the indirect function returns; we'd have to do that
automatically to set the second breakpoint, or risk missing calls.
I'm open to suggestions.
> I would choose (A) + (CACHE) myself. I did not find (CACHE) to be such
> a concern to implement it. Inferior calls may be slow on embedded targets?
Inferior calls are hundreds of times slower than symbol lookups. GDB
can do a lot of symbol lookups behind the scenes, for instance "info
func" will do a lookup_symbol for every function. Since native x86_64
Linux inferior calls are only hundreds of times slower, you may not
notice this as much. On a slower target, or a remote target, it may
be thousands of times slower.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 14:19 ` Jan Kratochvil
2010-02-17 14:46 ` Daniel Jacobowitz
@ 2010-02-17 14:52 ` Pedro Alves
2010-02-17 17:33 ` Jan Kratochvil
1 sibling, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2010-02-17 14:52 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches, Daniel Jacobowitz
On Wednesday 17 February 2010 14:19:12, Jan Kratochvil wrote:
> On Wed, 17 Feb 2010 13:34:15 +0100, Pedro Alves wrote:
> > On Monday 15 February 2010 18:40:50, Daniel Jacobowitz wrote:
> > > Do you mean that "print strcmp" or "break strcmp" is now going to do
> > > an inferior call? That doesn't seem like a good idea to me. I would
> > > like for some other maintainers to comment though.
> > >
> > > Inferior calls are very slow, and they can go wrong (pending signals,
> > > misbehaving programs, etc). I believe we should make an effort to
> > > minimize them.
> >
> > Yeah, agreed, we should avoid them the best we can.
>
> Possibilities known to me:
>
> (A) Call ifunc-resolver any time it is needed.
> = currently implemented.
> (B) Pick out the resolver result from .got.plt - if it is already there;
> otherwise (A).
> (C) Print just the bare ifunc-resolver address for "p strcmp".
>
> "Regular users" just print "strcmp (...)" and do not print "strcmp" which
> possibly makes (C) a viable option.
> +(CACHE) = + possibility: Cache the pointer in GDB.
Yes. If possible, I'd pick C, maybe B -> C. The thing that
ends up calling the resolver or strcmp_optimized_for_foo really
_is_ strcmp, no? Isn't that what objdump/nm, etc. would show
as well for address of strcmp? And B -> C is all that you'll be
able to do when debugging a core file.
>
> When an inferior call of "strcmp (...)" is being made I do not find a problem
> doing also the ifunc-resolver call that time, do you?
> I would choose (A) + (CACHE) myself. I did not find (CACHE) to be such
> a concern to implement it. Inferior calls may be slow on embedded targets?
Yes, they can be slow, but, the fact that random bad things can happen
that when you set the inferior running behind the scenes, is what
troublesme the most. It's really a last resort.
> > [ Not to mention that the scheduler-locking setting also applies to
> > them, meaning, in a multi-threaded environment, without more
> > care, these behind the scenes infcalls resume more than
> > you'd want (all-threads), which can be surprising, and make other
> > threads easily hit events while handling the infcall. Something
> > that IWBN to fix. ]
>
> If you are concerned about other threads running you should already use at
> least "set scheduler-locking step".
Say I'm hard headed and I want scheduler locking off. I'd still be
surprised when my threads get resumed when I do "b foo".
> It should be default anyway.
That's another story.
>
> What about making this GNU-IFUNC inferior call scheduling follow the "step"
> policy? Maybe the whole inferior calls should follow the "step" policy?
Or "on", should be the same. But that's not enough, fully fixing
means removing breakpoints as well: e.g., what if the user has
a breakpoint on (the ifunc-resolver of) "strcmp", or one of its
callees? -- "p strcmp" or "b strcmp" can stop working mysteriously
then, no? Also, if ifunc-resolving takes locks internally, then
we _can't_ make that inferior call sched-locked --- we may
deadlock the inferior, and the infcall never ends. Does
it (takes locks internally), or ever will?
--
Pedro Alves
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 14:52 ` Pedro Alves
@ 2010-02-17 17:33 ` Jan Kratochvil
2010-02-17 17:55 ` Pedro Alves
0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-17 17:33 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Daniel Jacobowitz
On Wed, 17 Feb 2010 15:52:36 +0100, Pedro Alves wrote:
> On Wednesday 17 February 2010 14:19:12, Jan Kratochvil wrote:
> > (A) Call ifunc-resolver any time it is needed.
> > = currently implemented.
> > (B) Pick out the resolver result from .got.plt - if it is already there;
> > otherwise (A).
> > (C) Print just the bare ifunc-resolver address for "p strcmp".
> >
> > "Regular users" just print "strcmp (...)" and do not print "strcmp" which
> > possibly makes (C) a viable option.
>
> > +(CACHE) = + possibility: Cache the pointer in GDB.
> The thing that ends up calling the resolver or strcmp_optimized_for_foo
> really _is_ strcmp, no? Isn't that what objdump/nm, etc. would show as well
> for address of strcmp?
Yes, the gnu-ifunc resolver is called "strcmp":
nm:
000000303a87d8b0 t __strcmp_sse2
000000303a91c5e0 t __strcmp_sse42
000000303a923800 t __strcmp_ssse3
000000303a87d870 i strcmp
objdump -d:
000000000007d870 <strcmp>:
7d870: 83 3d a9 a9 2f 00 00 cmpl $0x0,0x2fa9a9(%rip) # 378220 <__cpu_features>
7d877: 75 05 jne 7d87e <strcmp+0xe>
7d879: e8 e2 15 fa ff callq 1ee60 <__init_cpu_features>
I was considering gdb as a more high-level tool than nm/objdump.
Anyway I find it now OK to resolve ifunc just for "strcmp()" (and not for
"strcmp").
> Yes. If possible, I'd pick C, maybe B -> C.
(B): The .got.plt picker could be based on OSABI-dependent check of .rela.plt
type (such as is R_X86_64_JUMP_SLOT).
Do you consider it still worth it if the uncached gnu-ifunc resolver call
would be made only for real "strcmp()" inferior calls?
------------------------------------------------------------------------------
With the (C) "unresolving" option these items have became offtopic:
> And B -> C is all that you'll be > able to do when debugging a core file.
True.
Program terminated with signal 11, Segmentation fault.
(gdb) p strcmp
$1 = {<text gnu-ifunc variable, no debug info>} 0x3009a7d870 <strcmp>
> > What about making this GNU-IFUNC inferior call scheduling follow the "step"
> > policy? Maybe the whole inferior calls should follow the "step" policy?
>
> Or "on", should be the same.
Not so. I find "step" to be the reasonable default (and it has been so for
a long time before me in RHEL/Fedora) and I find GNU-IFUNC resolving with
locked scheduler also as a reasonable default. I do not find
"scheduler-locking on" as a reasonable GDB default.
> But that's not enough, fully fixing means removing breakpoints as well:
> e.g., what if the user has a breakpoint on (the ifunc-resolver of) "strcmp",
> or one of its callees? -- "p strcmp" or "b strcmp" can stop working
> mysteriously then, no?
It will stop at that breakpoint, I would find it valid.
> Also, if ifunc-resolving takes locks internally, then we _can't_ make that
> inferior call sched-locked --- we may deadlock the inferior, and the infcall
> never ends. Does it (takes locks internally), or ever will?
gnu-ifunc is a general framework, it would be probably OK for a specific
application to take a lock during its gnu-ifunc resolver.
Quick scan of current ifunc resolvers in glibc does not show any locks.
Filed glibc/11292 about it, there seems to be a race.
Still such lock can be non-nesting and already taken by the current thread.
"scheduler-locking off" cannot always help.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 14:46 ` Daniel Jacobowitz
@ 2010-02-17 17:45 ` Jan Kratochvil
0 siblings, 0 replies; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-17 17:45 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Pedro Alves, gdb-patches
On Wed, 17 Feb 2010 15:46:15 +0100, Daniel Jacobowitz wrote:
> On Wed, Feb 17, 2010 at 03:19:12PM +0100, Jan Kratochvil wrote:
> > (C) Print just the bare ifunc-resolver address for "p strcmp".
> >
> > +(CACHE) = + possibility: Cache the pointer in GDB.
> >
> > "Regular users" just print "strcmp (...)" and do not print "strcmp" which
> > possibly makes (C) a viable option.
>
> I don't know. What about "disassemble strcmp" - I think it should
> disassemble the same thing that will show up in the assembly as "call
> 0x$hex <strcmp>". But maybe automatically disassembling strcmp_sse is
> more useful.
According to the reply to Pedro Alves
http://sourceware.org/ml/gdb-patches/2010-02/msg00428.html
I will follow now that "strcmp" means the unresolved gnu-ifunc resolver.
(and "disassemble strcmp" disassembles just the gnu-ifunc resolver)
> Hmm. I guess that leaves "break strcmp" in a weird place since strcmp
> will only be called once. Maybe a breakpoint on an indirect function
> should also set a breakpoint on the target of the indirect function?
> This is slightly awkward to implement because there's no debug hook
> after the indirect function returns; we'd have to do that
> automatically to set the second breakpoint, or risk missing calls.
OK, nice catch.
GDB automatically calls "strcmp" from glibc (which is the resolver), not the
'glibc@plt' (which acts as a regular function hiding gnu-ifunc at all)..
Probably to break at the "strcmp" resolver and when it is hit the first time
then GDB can easily move the breakpoint to the resolved "strcmp_sse" function
by internally executing GDB "finish" call and using the returned address.
It would contradict that GDB should deal only with the gnu-ifunc resolver for
"strcmp", though.
> Inferior calls are hundreds of times slower than symbol lookups. GDB
> can do a lot of symbol lookups behind the scenes, for instance "info
> func" will do a lookup_symbol for every function. Since native x86_64
> Linux inferior calls are only hundreds of times slower, you may not
> notice this as much. On a slower target, or a remote target, it may
> be thousands of times slower.
What if GDB prefers to use "strcmp@plt" instead of "strcmp" when GDB finds
"strcmp" is a gnu-ifunc symbol. It will save the new GDB framework to deal
with the .got.plt addresses.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 17:33 ` Jan Kratochvil
@ 2010-02-17 17:55 ` Pedro Alves
2010-02-17 18:12 ` Jan Kratochvil
0 siblings, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2010-02-17 17:55 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches, Daniel Jacobowitz
On Wednesday 17 February 2010 17:33:38, Jan Kratochvil wrote:
> > > What about making this GNU-IFUNC inferior call scheduling follow the "step"
> > > policy? Maybe the whole inferior calls should follow the "step" policy?
> >
> > Or "on", should be the same.
>
> Not so. I find "step" to be the reasonable default (and it has been so for
> a long time before me in RHEL/Fedora) and I find GNU-IFUNC resolving with
> locked scheduler also as a reasonable default. I do not find
> "scheduler-locking on" as a reasonable GDB default.
>
Oh, of course not. I was answering the first question,
about the specific infcall to resolve "strcmp" when the
user did "p strcmp" or "b strcmp". I assume you meant to
sched-lock that call, which would be the same as "on",
but maybe I misunderstood what you meant. A reply to
the second question would be similar to
the "That's another story." reply. ;-)
--
Pedro Alves
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] STT_GNU_IFUNC support
2010-02-17 17:55 ` Pedro Alves
@ 2010-02-17 18:12 ` Jan Kratochvil
0 siblings, 0 replies; 13+ messages in thread
From: Jan Kratochvil @ 2010-02-17 18:12 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Daniel Jacobowitz
On Wed, 17 Feb 2010 18:55:10 +0100, Pedro Alves wrote:
> On Wednesday 17 February 2010 17:33:38, Jan Kratochvil wrote:
> > > > What about making this GNU-IFUNC inferior call scheduling follow the "step"
> > > > policy? Maybe the whole inferior calls should follow the "step" policy?
> > >
> > > Or "on", should be the same.
> >
> > Not so. I find "step" to be the reasonable default (and it has been so for
> > a long time before me in RHEL/Fedora) and I find GNU-IFUNC resolving with
> > locked scheduler also as a reasonable default. I do not find
> > "scheduler-locking on" as a reasonable GDB default.
>
> Oh, of course not. I was answering the first question,
> about the specific infcall to resolve "strcmp" when the
> user did "p strcmp" or "b strcmp". I assume you meant to
> sched-lock that call, which would be the same as "on",
> but maybe I misunderstood what you meant.
We agree on the significant part. The other part is offtopic for IFUNC.
Thanks,
Jan
------------------------------------------------------------------------------
> A reply to
> the second question would be similar to
> the "That's another story." reply. ;-)
The "That's another story." part:
To make it whole more clear I would suggesting creating:
set scheduler-locking-continue (on|off)
set scheduler-locking-step (on|off)
mapping current settings -> the new scheme as:
set scheduler-locking on -> s-l-continue=on s-l-step=on
set scheduler-locking step -> s-l-continue=off s-l-step=on
set scheduler-locking off -> s-l-continue=off s-l-step=off
and introducing for infcall/gnu-ifunc new settings:
set scheduler-locking-infcall (on|off)
set scheduler-locking-gnu-ifunc (on|off)
Currently s-l-infcall behaves the same as s-l-continue.
IMO s-l-infcall should behave more the same as s-l-step.
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2010-02-17 18:12 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-02-14 20:35 [patch] STT_GNU_IFUNC support Jan Kratochvil
2010-02-14 21:43 ` Mark Kettenis
2010-02-14 21:59 ` Jan Kratochvil
2010-02-15 18:40 ` Daniel Jacobowitz
2010-02-15 18:49 ` Jan Kratochvil
2010-02-17 12:34 ` Pedro Alves
2010-02-17 14:19 ` Jan Kratochvil
2010-02-17 14:46 ` Daniel Jacobowitz
2010-02-17 17:45 ` Jan Kratochvil
2010-02-17 14:52 ` Pedro Alves
2010-02-17 17:33 ` Jan Kratochvil
2010-02-17 17:55 ` Pedro Alves
2010-02-17 18:12 ` Jan Kratochvil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox