From 00980d845f8f2ec3ed4ad161a1e5b97704be1929 Mon Sep 17 00:00:00 2001 From: Stefan Liebler Date: Fri, 7 Oct 2016 09:56:46 +0200 Subject: Use gcc attribute ifunc in libc_ifunc macro instead of inline assembly due to false debuginfo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current s390 ifunc resolver for vector optimized functions and the common libc_ifunc macro in include/libc-symbols.h uses something like that to generate ifunc'ed functions: extern void *__resolve___strlen(unsigned long int dl_hwcap) asm (strlen); asm (".type strlen, %gnu_indirect_function"); This leads to false debug information: objdump --dwarf=info libc.so: ... <1><1e6424>: Abbrev Number: 43 (DW_TAG_subprogram) <1e6425> DW_AT_external : 1 <1e6425> DW_AT_name : (indirect string, offset: 0x1146e): __resolve___strlen <1e6429> DW_AT_decl_file : 1 <1e642a> DW_AT_decl_line : 23 <1e642b> DW_AT_linkage_name: (indirect string, offset: 0x1147a): strlen <1e642f> DW_AT_prototyped : 1 <1e642f> DW_AT_type : <0x1e4ccd> <1e6433> DW_AT_low_pc : 0x998e0 <1e643b> DW_AT_high_pc : 0x16 <1e6443> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa) <1e6445> DW_AT_GNU_all_call_sites: 1 <1e6445> DW_AT_sibling : <0x1e6459> <2><1e6449>: Abbrev Number: 44 (DW_TAG_formal_parameter) <1e644a> DW_AT_name : (indirect string, offset: 0x1845): dl_hwcap <1e644e> DW_AT_decl_file : 1 <1e644f> DW_AT_decl_line : 23 <1e6450> DW_AT_type : <0x1e4c8d> <1e6454> DW_AT_location : 0x122115 (location list) ... The debuginfo for the ifunc-resolver function contains the DW_AT_linkage_name field, which names the real function name "strlen". If you perform an inferior function call to strlen in lldb, then it fails due to something like that: "error: no matching function for call to 'strlen' candidate function not viable: no known conversion from 'const char [6]' to 'unsigned long' for 1st argument" The unsigned long is the dl_hwcap argument of the resolver function. The strlen function itself has no debufinfo. The s390 ifunc resolver for memset & co uses something like that: asm (".globl FUNC" ".type FUNC, @gnu_indirect_function" ".set FUNC, __resolve_FUNC"); This way the debuginfo for the ifunc-resolver function does not conain the DW_AT_linkage_name field and the real function has no debuginfo, too. Using this strategy for the vector optimized functions leads to some troubles for functions like strnlen. Here we have __strnlen and a weak alias strnlen. The __strnlen function is the ifunc function, which is realized with the asm- statement above. The weak_alias-macro can't be used here due to undefined symbol: gcc ../sysdeps/s390/multiarch/strnlen.c -c ... In file included from :0:0: ../sysdeps/s390/multiarch/strnlen.c:28:24: error: ‘strnlen’ aliased to undefined symbol ‘__strnlen’ weak_alias (__strnlen, strnlen) ^ ./../include/libc-symbols.h:111:26: note: in definition of macro ‘_weak_alias’ extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); ^ ../sysdeps/s390/multiarch/strnlen.c:28:1: note: in expansion of macro ‘weak_alias’ weak_alias (__strnlen, strnlen) ^ make[2]: *** [build/string/strnlen.o] Error 1 As the __strnlen function is defined with asm-statements the function name __strnlen isn't known by gcc. But the weak alias can also be done with an asm statement to resolve this issue: __asm__ (".weak strnlen\n\t" ".set strnlen,__strnlen\n"); In order to use the weak_alias macro, gcc needs to know the ifunc function. The minimum gcc to build glibc is currently 4.7, which supports attribute((ifunc)). See https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html. It is only supported if gcc is configured with --enable-gnu-indirect-function or gcc supports it by default for at least intel and s390x architecture. This patch uses the old behaviour if gcc support is not available. Usage of attribute ifunc is something like that: __typeof (FUNC) FUNC __attribute__ ((ifunc ("__resolve_FUNC"))); Then gcc produces the same .globl, .type, .set assembler instructions like above. And the debuginfo does not contain the DW_AT_linkage_name field and there is no debuginfo for the real function, too. But in order to get it work, there is also some extra work to do. Currently, the glibc internal symbol on s390x e.g. __GI___strnlen is not the ifunc symbol, but the fallback __strnlen_c symbol. Thus I have to omit the libc_hidden_def macro in strnlen.c (here is the ifunc function __strnlen) because it is already handled in strnlen-c.c (here is __strnlen_c). Due to libc_hidden_proto (__strnlen) in string.h, compiling fails: gcc ../sysdeps/s390/multiarch/strnlen.c -c ... In file included from :0:0: ../sysdeps/s390/multiarch/strnlen.c:53:24: error: ‘strnlen’ aliased to undefined symbol ‘__strnlen’ weak_alias (__strnlen, strnlen) ^ ./../include/libc-symbols.h:111:26: note: in definition of macro ‘_weak_alias’ extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); ^ ../sysdeps/s390/multiarch/strnlen.c:53:1: note: in expansion of macro ‘weak_alias’ weak_alias (__strnlen, strnlen) ^ make[2]: *** [build/string/strnlen.os] Error 1 I have to redirect the prototypes for __strnlen in string.h and create a copy of the prototype for using as ifunc function: __typeof (__redirect___strnlen) __strnlen __attribute__ ((ifunc ("__resolve_strnlen"))); weak_alias (__strnlen, strnlen) This way there is no trouble with the internal __GI_* symbols. Glibc builds fine with this construct and the debuginfo is "correct". For functions without a __GI_* symbol like memccpy this redirection is not needed. This patch adjusts the common libc_ifunc and libm_ifunc macro to use gcc attribute ifunc. Due to this change, the macro users where the __GI_* symbol does not target the ifunc symbol have to be prepared with the redirection construct. Furthermore a configure check to test gcc support is added. If it is not supported, the old behaviour is used. This patch also prepares the libc_ifunc macro to be useable in s390-ifunc-macro. The s390 ifunc-resolver-functions do have an hwcaps parameter and not all resolvers need the same initialization code. The next patch in this series changes the s390 ifunc macros to use this common one. ChangeLog: * include/libc-symbols.h (__ifunc_resolver): New macro is used by __ifunc* macros. (__ifunc): New macro uses gcc attribute ifunc or inline assembly depending on HAVE_GCC_IFUNC. (libc_ifunc, libm_ifunc): Use __ifunc as base macro. (libc_ifunc_redirected, libc_ifunc_hidden, libm_ifunc_init): New macro. * sysdeps/powerpc/powerpc32/power4/fpu/multiarch/s_finite.c: Redirect ifunced function in header for using as type for ifunc function. * sysdeps/powerpc/powerpc32/power4/fpu/multiarch/s_finitef.c: Likewise. * sysdeps/powerpc/powerpc32/power4/fpu/multiarch/s_isinf.c: Likewise. * sysdeps/powerpc/powerpc32/power4/fpu/multiarch/s_isinff.c: Likewise. * sysdeps/powerpc/powerpc32/power4/fpu/multiarch/s_isnan.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/memcmp.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/memcpy.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/memmove.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/mempcpy.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/memset.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/rawmemchr.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/strchr.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/strlen.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/strncmp.c: Likewise. * sysdeps/powerpc/powerpc32/power4/multiarch/strnlen.c: Likewise. * sysdeps/powerpc/powerpc64/fpu/multiarch/s_finite.c: Likewise. * sysdeps/powerpc/powerpc64/fpu/multiarch/s_finitef.c: Likewise. * sysdeps/powerpc/powerpc64/fpu/multiarch/s_isinf.c: Likewise. * sysdeps/powerpc/powerpc64/fpu/multiarch/s_isinff.c: Likewise. * sysdeps/powerpc/powerpc64/fpu/multiarch/s_isnan.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/memcmp.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/mempcpy.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/rawmemchr.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/stpncpy.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strcat.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strchr.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strcmp.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strcpy.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strncmp.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strncpy.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strnlen.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strrchr.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/strstr.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/wcschr.c: Likewise. * sysdeps/powerpc/powerpc32/power4/fpu/multiarch/s_isnanf.c: Add libc_hidden_def() and use libc_ifunc_hidden() macro instead of libc_ifunc() macro. * sysdeps/powerpc/powerpc64/fpu/multiarch/s_isnanf.c: Likewise. * sysdeps/powerpc/powerpc64/multiarch/stpcpy.c: Likewise. --- include/libc-symbols.h | 142 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/libc-symbols.h b/include/libc-symbols.h index e362d42..6ae309a 100644 --- a/include/libc-symbols.h +++ b/include/libc-symbols.h @@ -737,27 +737,137 @@ for linking") # define compat_data_section .section ".data.compat", "aw"; #endif -/* Marker used for indirection function symbols. */ -#define libc_ifunc(name, expr) \ - extern void *name##_ifunc (void) __asm__ (#name); \ - void *name##_ifunc (void) \ +/* Helper / base macros for indirect function symbols. */ +#define __ifunc_resolver(type_name, name, expr, arg, init, classifier) \ + classifier void *name##_ifunc (arg) \ { \ - INIT_ARCH (); \ - __typeof (name) *res = expr; \ + init (); \ + __typeof (type_name) *res = expr; \ return res; \ - } \ - __asm__ (".type " #name ", %gnu_indirect_function"); + } + +#ifdef HAVE_GCC_IFUNC +# define __ifunc(type_name, name, expr, arg, init) \ + extern __typeof (type_name) name __attribute__ \ + ((ifunc (#name "_ifunc"))); \ + __ifunc_resolver (type_name, name, expr, arg, init, static) + +# define __ifunc_hidden(type_name, name, expr, arg, init) \ + __ifunc (type_name, name, expr, arg, init) +#else +/* Gcc does not support __attribute__ ((ifunc (...))). Use the old behaviour + as fallback. But keep in mind that the debug information for the ifunc + resolver functions is not correct. It contains the ifunc'ed function as + DW_AT_linkage_name. E.g. lldb uses this field and an inferior function + call of the ifunc'ed function will fail due to "no matching function for + call to ..." because the ifunc'ed function and the resolver function have + different signatures. (Gcc support is disabled at least on a ppc64le + Ubuntu 14.04 system.) */ + +# define __ifunc(type_name, name, expr, arg, init) \ + extern __typeof (type_name) name; \ + void *name##_ifunc (arg) __asm__ (#name); \ + __ifunc_resolver (type_name, name, expr, arg, init,) \ + __asm__ (".type " #name ", %gnu_indirect_function"); + +# define __ifunc_hidden(type_name, name, expr, arg, init) \ + extern __typeof (type_name) __libc_##name; \ + __ifunc (type_name, __libc_##name, expr, arg, init) \ + strong_alias (__libc_##name, name); +#endif /* !HAVE_GCC_IFUNC */ + +/* The following macros are used for indirect function symbols in libc.so. + First of all, you need to have the function prototyped somewhere, + say in foo.h: + + int foo (int __bar); + + If you have an implementation for foo which e.g. uses a special hardware + feature which isn't available on all machines where this libc.so will be + used but decideable if available at runtime e.g. via hwcaps, you can provide + two or multiple implementations of foo: + + int __foo_default (int __bar) + { + return __bar; + } + + int __foo_special (int __bar) + { + return __bar; + } + + If your function foo has no libc_hidden_proto (foo) defined for PLT + bypassing, you can use: + + #define INIT_ARCH() unsigned long int hwcap = __GLRO(dl_hwcap); + + libc_ifunc (foo, (hwcap & HWCAP_SPECIAL) ? __foo_special : __foo_default); + + This will define a resolver function for foo which returns __foo_special or + __foo_default depending on your specified expression. Please note that you + have to define a macro function INIT_ARCH before using libc_ifunc macro as + it is called by the resolver function before evaluating the specified + expression. In this example it is used to prepare the hwcap variable. + The resolver function is assigned to an ifunc'ed symbol foo. Calls to foo + from inside or outside of libc.so will be indirected by a PLT call. + + If your function foo has a libc_hidden_proto (foo) defined for PLT bypassing + and calls to foo within libc.so should always go to one specific + implementation of foo e.g. __foo_default then you have to add: + + __hidden_ver1 (__foo_default, __GI_foo, __foo_default); + + or a tweaked definition of libc_hidden_def macro after the __foo_default + function definition. Calls to foo within libc.so will always go directly to + __foo_default. Calls to foo from outside libc.so will be indirected by a + PLT call to ifunc'ed symbol foo which you have to define in a separate + compile unit: + + #define foo __redirect_foo + #include + #undef foo + + extern __typeof (__redirect_foo) __foo_default attribute_hidden; + extern __typeof (__redirect_foo) __foo_special attribute_hidden; + + libc_ifunc_redirected (__redirect_foo, foo, + (hwcap & HWCAP_SPECIAL) + ? __foo_special + : __foo_default); + + This will define the ifunc'ed symbol foo like above. The redirection of foo + in header file is needed to omit an additional defintion of __GI_foo which + would end in a linker error while linking libc.so. You have to specify + __redirect_foo as first parameter which is used within libc_ifunc_redirected + macro in conjunction with typeof to define the ifunc'ed symbol foo. + + If your function foo has a libc_hidden_proto (foo) defined and calls to foo + within or from outside libc.so should go via ifunc'ed symbol, then you have + to use: + + libc_ifunc_hidden (foo, foo, + (hwcap & HWCAP_SPECIAL) + ? __foo_special + : __foo_default); + libc_hidden_def (foo) + + The first parameter foo of libc_ifunc_hidden macro is used in the same way + as for libc_ifunc_redirected macro. */ + +#define libc_ifunc(name, expr) __ifunc (name, name, expr, void, INIT_ARCH) + +#define libc_ifunc_redirected(redirected_name, name, expr) \ + __ifunc (redirected_name, name, expr, void, INIT_ARCH) + +#define libc_ifunc_hidden(redirected_name, name, expr) \ + __ifunc_hidden (redirected_name, name, expr, void, INIT_ARCH) /* The body of the function is supposed to use __get_cpu_features which will, if necessary, initialize the data first. */ -#define libm_ifunc(name, expr) \ - extern void *name##_ifunc (void) __asm__ (#name); \ - void *name##_ifunc (void) \ - { \ - __typeof (name) *res = expr; \ - return res; \ - } \ - __asm__ (".type " #name ", %gnu_indirect_function"); +#define libm_ifunc_init() +#define libm_ifunc(name, expr) \ + __ifunc (name, name, expr, void, libm_ifunc_init) #ifdef HAVE_ASM_SET_DIRECTIVE # define libc_ifunc_hidden_def1(local, name) \ -- cgit v1.1