http://sourceware.org/gdb/wiki/ProjectArcher http://sourceware.org/gdb/wiki/ArcherBranchManagement GIT snapshot: commit 791165df07fd22d381def1318954c389a606f81a archer-jankratochvil-ifunc Index: gdb-7.0.1/gdb/alpha-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/alpha-linux-tdep.c 2009-07-02 19:25:52.000000000 +0200 +++ gdb-7.0.1/gdb/alpha-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -25,6 +25,7 @@ #include "symtab.h" #include "regset.h" #include "regcache.h" +#include "linux-tdep.h" #include "alpha-tdep.h" @@ -235,6 +236,9 @@ alpha_linux_init_abi (struct gdbarch_inf 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. */ Index: gdb-7.0.1/gdb/amd64-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/amd64-linux-tdep.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/amd64-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -1557,6 +1557,9 @@ amd64_linux_init_abi (struct gdbarch_inf 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); } Index: gdb-7.0.1/gdb/arm-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/arm-linux-tdep.c 2009-07-31 01:05:03.000000000 +0200 +++ gdb-7.0.1/gdb/arm-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -873,6 +873,9 @@ arm_linux_init_abi (struct gdbarch_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. */ Index: gdb-7.0.1/gdb/elfread.c =================================================================== --- gdb-7.0.1.orig/gdb/elfread.c 2010-01-21 20:45:21.000000000 +0100 +++ gdb-7.0.1/gdb/elfread.c 2010-01-21 20:46:30.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.1/gdb/frv-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/frv-linux-tdep.c 2009-07-02 19:25:53.000000000 +0200 +++ gdb-7.0.1/gdb/frv-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -31,6 +31,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; @@ -485,7 +486,21 @@ frv_linux_regset_from_core_section (stru return NULL; } - +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; + + resolved = linux_convert_from_func_and_ptr (gdbarch, addr, pc); + if (resolved != pc) + pc = frv_convert_from_func_ptr_addr (gdbarch, resolved, targ); + + return pc; +} + static void frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -493,6 +508,10 @@ frv_linux_init_abi (struct gdbarch_info 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); } static enum gdb_osabi Index: gdb-7.0.1/gdb/frv-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/frv-tdep.c 2009-07-02 19:25:53.000000000 +0200 +++ gdb-7.0.1/gdb/frv-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -1169,7 +1169,7 @@ find_func_descr (struct gdbarch *gdbarch return descr; } -static CORE_ADDR +CORE_ADDR frv_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr, struct target_ops *targ) { Index: gdb-7.0.1/gdb/frv-tdep.h =================================================================== --- gdb-7.0.1.orig/gdb/frv-tdep.h 2009-01-03 06:57:51.000000000 +0100 +++ gdb-7.0.1/gdb/frv-tdep.h 2010-01-21 20:45:32.000000000 +0100 @@ -118,3 +118,6 @@ CORE_ADDR frv_fetch_objfile_link_map (st 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); Index: gdb-7.0.1/gdb/gdbtypes.c =================================================================== --- gdb-7.0.1.orig/gdb/gdbtypes.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/gdbtypes.c 2010-01-21 20:45:32.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 + = 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) "", 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, + "", 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.1/gdb/gdbtypes.h =================================================================== --- gdb-7.0.1.orig/gdb/gdbtypes.h 2010-01-21 20:45:23.000000000 +0100 +++ gdb-7.0.1/gdb/gdbtypes.h 2010-01-21 20:45:32.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; + /* `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. */ @@ -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.1/gdb/hppa-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/hppa-linux-tdep.c 2009-07-02 19:25:54.000000000 +0200 +++ gdb-7.0.1/gdb/hppa-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -31,6 +31,7 @@ #include "regset.h" #include "regcache.h" #include "hppa-tdep.h" +#include "linux-tdep.h" #include "elf/common.h" @@ -512,7 +513,21 @@ hppa_linux_regset_from_core_section (str return NULL; } - + +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; + + resolved = linux_convert_from_func_and_ptr (gdbarch, addr, pc); + if (resolved != pc) + pc = hppa32_convert_from_func_ptr_addr (gdbarch, resolved, targ); + + return pc; +} /* Forward declarations. */ extern initialize_file_ftype _initialize_hppa_linux_tdep; @@ -554,6 +569,10 @@ hppa_linux_init_abi (struct gdbarch_info /* 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); } void Index: gdb-7.0.1/gdb/hppa-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/hppa-tdep.c 2009-09-13 18:28:28.000000000 +0200 +++ gdb-7.0.1/gdb/hppa-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -1247,7 +1247,7 @@ hppa64_return_value (struct gdbarch *gdb } -static CORE_ADDR +CORE_ADDR hppa32_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr, struct target_ops *targ) { Index: gdb-7.0.1/gdb/hppa-tdep.h =================================================================== --- gdb-7.0.1.orig/gdb/hppa-tdep.h 2009-07-02 19:25:54.000000000 +0200 +++ gdb-7.0.1/gdb/hppa-tdep.h 2010-01-21 20:45:32.000000000 +0100 @@ -246,4 +246,8 @@ extern int hppa_in_solib_call_trampoline 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 */ Index: gdb-7.0.1/gdb/i386-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/i386-linux-tdep.c 2009-09-21 08:57:03.000000000 +0200 +++ gdb-7.0.1/gdb/i386-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -798,6 +798,9 @@ i386_linux_init_abi (struct gdbarch_info 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. */ Index: gdb-7.0.1/gdb/ia64-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/ia64-linux-tdep.c 2009-07-02 19:25:55.000000000 +0200 +++ gdb-7.0.1/gdb/ia64-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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 /* 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. */ Index: gdb-7.0.1/gdb/infcall.c =================================================================== --- gdb-7.0.1.orig/gdb/infcall.c 2010-01-21 20:45:21.000000000 +0100 +++ gdb-7.0.1/gdb/infcall.c 2010-01-21 20:45:32.000000000 +0100 @@ -252,9 +252,17 @@ find_function_addr (struct value *functi if (TYPE_CODE (ftype) == TYPE_CODE_FUNC || TYPE_CODE (ftype) == TYPE_CODE_METHOD) { - funaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funaddr, - ¤t_target); - value_type = TYPE_TARGET_TYPE (ftype); + CORE_ADDR funaddr2; + + funaddr2 = gdbarch_convert_from_func_ptr_addr (gdbarch, funaddr, + ¤t_target); + + /* If TYPE_GNU_IFUNC is currently not resolvable keep its type. */ + if (funaddr2 != funaddr || !TYPE_GNU_IFUNC (ftype)) + { + funaddr = funaddr2; + value_type = TYPE_TARGET_TYPE (ftype); + } } } else if (code == TYPE_CODE_INT) Index: gdb-7.0.1/gdb/linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/linux-tdep.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/linux-tdep.c 2010-01-21 20:47:02.000000000 +0100 @@ -20,6 +20,9 @@ #include "defs.h" #include "gdbtypes.h" #include "linux-tdep.h" +#include "value.h" +#include "infcall.h" +#include "target.h" /* This function is suitable for architectures that don't extend/override the standard siginfo structure. */ @@ -134,3 +136,43 @@ linux_get_siginfo_type (struct gdbarch * return siginfo_type; } + +/* Call gnu-ifunc to resolve breakpoint at its returned function. */ + +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 pc; + + msymbol = lookup_minimal_symbol_by_pc (func_ptr); + if (msymbol == NULL) + return pc; + if (MSYMBOL_TYPE (msymbol) != mst_text_gnu_ifunc) + return pc; + + /* Not at the gnu-ifunc entry point? */ + if (SYMBOL_VALUE_ADDRESS (msymbol) != func_ptr) + return pc; + + 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); +} + +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); +} Index: gdb-7.0.1/gdb/linux-tdep.h =================================================================== --- gdb-7.0.1.orig/gdb/linux-tdep.h 2009-02-06 23:59:00.000000000 +0100 +++ gdb-7.0.1/gdb/linux-tdep.h 2010-01-21 20:45:32.000000000 +0100 @@ -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 */ Index: gdb-7.0.1/gdb/m32r-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/m32r-linux-tdep.c 2009-01-03 06:57:52.000000000 +0100 +++ gdb-7.0.1/gdb/m32r-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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 /* 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. */ Index: gdb-7.0.1/gdb/minsyms.c =================================================================== --- gdb-7.0.1.orig/gdb/minsyms.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/minsyms.c 2010-01-21 20:45:32.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.1/gdb/mips-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/mips-linux-tdep.c 2009-07-02 19:25:55.000000000 +0200 +++ gdb-7.0.1/gdb/mips-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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 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. */ Index: gdb-7.0.1/gdb/mn10300-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/mn10300-linux-tdep.c 2009-02-22 02:02:19.000000000 +0100 +++ gdb-7.0.1/gdb/mn10300-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -32,6 +32,7 @@ #include "frame.h" #include "trad-frame.h" #include "tramp-frame.h" +#include "linux-tdep.h" #include @@ -718,6 +719,9 @@ am33_linux_init_osabi (struct gdbarch_in 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. */ Index: gdb-7.0.1/gdb/parse.c =================================================================== --- gdb-7.0.1.orig/gdb/parse.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/parse.c 2010-01-21 20:45:32.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.1/gdb/ppc-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/ppc-linux-tdep.c 2009-09-15 05:30:06.000000000 +0200 +++ gdb-7.0.1/gdb/ppc-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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" @@ -672,8 +673,19 @@ ppc64_linux_convert_from_func_ptr_addr ( res = bfd_get_section_contents (s->bfd, s->the_bfd_section, &buf, addr - s->addr, 8); if (res != 0) - return extract_unsigned_integer (buf, 8, byte_order) - - bfd_section_vma (s->bfd, s->the_bfd_section) + s->addr; + { + CORE_ADDR pc, resolved; + + pc = extract_unsigned_integer (buf, 8, byte_order) + - bfd_section_vma (s->bfd, s->the_bfd_section) + s->addr; + + resolved = linux_convert_from_func_and_ptr (gdbarch, addr, pc); + if (resolved != pc) + pc = ppc64_linux_convert_from_func_ptr_addr (gdbarch, resolved, + targ); + + return pc; + } } return addr; Index: gdb-7.0.1/gdb/solib-svr4.c =================================================================== --- gdb-7.0.1.orig/gdb/solib-svr4.c 2010-01-21 20:45:23.000000000 +0100 +++ gdb-7.0.1/gdb/solib-svr4.c 2010-01-21 20:46:04.000000000 +0100 @@ -1250,7 +1250,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.1/gdb/sparc-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/sparc-linux-tdep.c 2009-07-02 19:25:58.000000000 +0200 +++ gdb-7.0.1/gdb/sparc-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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_i 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. */ Index: gdb-7.0.1/gdb/sparc64-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/sparc64-linux-tdep.c 2009-07-02 19:25:58.000000000 +0200 +++ gdb-7.0.1/gdb/sparc64-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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_i 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); } Index: gdb-7.0.1/gdb/symmisc.c =================================================================== --- gdb-7.0.1.orig/gdb/symmisc.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/symmisc.c 2010-01-21 20:45:32.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.1/gdb/symtab.c =================================================================== --- gdb-7.0.1.orig/gdb/symtab.c 2010-01-21 20:45:22.000000000 +0100 +++ gdb-7.0.1/gdb/symtab.c 2010-01-21 20:45:32.000000000 +0100 @@ -3160,7 +3160,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.1/gdb/symtab.h =================================================================== --- gdb-7.0.1.orig/gdb/symtab.h 2010-01-21 20:45:21.000000000 +0100 +++ gdb-7.0.1/gdb/symtab.h 2010-01-21 20:45:32.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.1/gdb/testsuite/gdb.base/gnu-ifunc-lib.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.0.1/gdb/testsuite/gdb.base/gnu-ifunc-lib.c 2010-01-21 20:45:32.000000000 +0100 @@ -0,0 +1,54 @@ +/* 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 . */ + +#include + +typedef int (*final_t) (int arg); + +static int +init_stub (int arg) +{ + return 0; +} + +static int +final (int arg) +{ + return arg + 1; +} + +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; +} Index: gdb-7.0.1/gdb/testsuite/gdb.base/gnu-ifunc.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.0.1/gdb/testsuite/gdb.base/gnu-ifunc.c 2010-01-21 20:45:32.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 . */ + +#include + +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.1/gdb/testsuite/gdb.base/gnu-ifunc.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.0.1/gdb/testsuite/gdb.base/gnu-ifunc.exp 2010-01-21 20:45:32.000000000 +0100 @@ -0,0 +1,115 @@ +# 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 . + +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} + +# {debug} provides DWARF symbol gnu_ifuncX confusing the ELF symbol +# gnu_ifunc during address->symbol resolution for printing the symbol. +# Still we need it here for "step"ping into the function. +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] != ""} { + 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 format 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 format 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: + +gdb_test "p gnu_ifunc" " = {} 0x\[0-9a-f\]+ " "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> would be an incorrect resolution from DW_AT_MIPS_linkage_name. +# We do not use {debug} build option for this purpose. + +if { [gdb_compile_shlib ${srcdir}/${subdir}/$libsrc $lib_nodebug_so $lib_nodebug_opts] != ""} { + untested "Could not compile either $libsrc." + return -1 +} + +clean_restart $lib_nodebug_so_base + +gdb_test "p gnu_ifunc" " = {} 0x\[0-9a-f\]+ " "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" Index: gdb-7.0.1/gdb/xtensa-linux-tdep.c =================================================================== --- gdb-7.0.1.orig/gdb/xtensa-linux-tdep.c 2009-02-22 02:02:20.000000000 +0100 +++ gdb-7.0.1/gdb/xtensa-linux-tdep.c 2010-01-21 20:45:32.000000000 +0100 @@ -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_in { 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. */ --- gdb-7.0.1/gdb/configure.tgt-orig 2009-08-06 12:28:38.000000000 +0200 +++ gdb-7.0.1/gdb/configure.tgt 2010-01-21 21:28:38.000000000 +0100 @@ -374,7 +374,7 @@ powerpc-*-aix* | rs6000-*-*) ;; powerpc-*-linux* | powerpc64-*-linux*) # Target: PowerPC running Linux - gdb_target_obs="rs6000-tdep.o ppc-linux-tdep.o ppc-sysv-tdep.o \ + gdb_target_obs="rs6000-tdep.o ppc-linux-tdep.o linux-tdep.o ppc-sysv-tdep.o \ solib.o solib-svr4.o solib-spu.o spu-multiarch.o \ corelow.o symfile-mem.o" gdb_sim=../sim/ppc/libsim.a