From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23837 invoked by alias); 12 Jun 2009 16:03:35 -0000 Received: (qmail 23756 invoked by uid 22791); 12 Jun 2009 16:03:34 -0000 X-SWARE-Spam-Status: No, hits=-1.4 required=5.0 tests=AWL,BAYES_00,J_CHICKENPOX_34,J_CHICKENPOX_54,J_CHICKENPOX_75 X-Spam-Check-By: sourceware.org Received: from sunsite.ms.mff.cuni.cz (HELO sunsite.mff.cuni.cz) (195.113.15.26) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 12 Jun 2009 16:03:26 +0000 Received: from sunsite.mff.cuni.cz (localhost [127.0.0.1]) by sunsite.mff.cuni.cz (8.14.3/8.14.3) with ESMTP id n5CG2q6v017967; Fri, 12 Jun 2009 18:02:52 +0200 Received: (from jakub@localhost) by sunsite.mff.cuni.cz (8.14.3/8.14.3/Submit) id n5CG2pHr017962; Fri, 12 Jun 2009 18:02:51 +0200 Date: Fri, 12 Jun 2009 16:03:00 -0000 From: Jakub Jelinek To: Ulrich Drepper , "H.J. Lu" Cc: Glibc hackers , binutils@sources.redhat.com Subject: Preliminary prelink IFUNC support (x86-64 only so far), some binutils IFUNC issues Message-ID: <20090612160251.GJ3101@sunsite.ms.mff.cuni.cz> Reply-To: Jakub Jelinek MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="k+w/mQv8wyuph6w0" Content-Disposition: inline User-Agent: Mutt/1.5.19 (2009-01-05) Mailing-List: contact libc-hacker-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-hacker-owner@sourceware.org X-SW-Source: 2009-06/txt/msg00000.txt.bz2 --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-length: 962 Hi! I've started working on prelink STT_GNU_IFUNC and R_*_IRELATIVE support. With the attached glibc and prelink patches (both against respective trunks) x86-64 prelnk make check passes. But I've discovered two issues, likely on the binutils side. On the attached ifunctest.c gcc -O2 -o ifunctest{,.c} compiles/links, but no R_*_IRELATIVE relocation is emitted and not surprisingly it crashes at runtime. Prelink testsuite contains similar testcase, just using .globl or .globl/.hidden, instead of .local. I'd say .local @gnu_indirect_function symbols should be supported as well. The other issue can be seen with: gcc -O2 -fpic -shared -o ifunc3lib1.{so,c} gcc -O2 -o ifunc3 ./ifunc3lib1.so ./ifunc3lib1 Here, &lib1t3 in the binary resolves to a .plt slot in the binary, while &lib1t3 in the shared library resolves to the actual address the ifunc returned. Not sure what exactly we want to do here, but the function pointers should be the same. Jakub --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=libc6 Content-length: 1285 2009-06-12 Jakub Jelinek * elf/dl-lookup.c (_dl_debug_bindings): When resolving to STT_GNU_IFUNC symbol or in 8 into type_class. --- libc/elf/dl-lookup.c +++ libc/elf/dl-lookup.c @@ -1,5 +1,5 @@ /* Look up a symbol in the loaded objects. - Copyright (C) 1995-2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 1995-2005, 2006, 2007, 2009 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -542,15 +542,20 @@ _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, conflict = 1; } - if (value->s - && (__builtin_expect (ELFW(ST_TYPE) (value->s->st_info) - == STT_TLS, 0))) - type_class = 4; + if (value->s) + { + if (__builtin_expect (ELFW(ST_TYPE) (value->s->st_info) + == STT_TLS, 0)) + type_class = 4; + else if (__builtin_expect (ELFW(ST_TYPE) (value->s->st_info) + == STT_GNU_IFUNC, 0)) + type_class |= 8; + } if (conflict || GLRO(dl_trace_prelink_map) == undef_map || GLRO(dl_trace_prelink_map) == NULL - || type_class == 4) + || type_class >= 4) { _dl_printf ("%s 0x%0*Zx 0x%0*Zx -> 0x%0*Zx 0x%0*Zx ", conflict ? "conflict" : "lookup", --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=prel6 Content-length: 21916 --- src/Makefile.in (revision 168) +++ src/Makefile.in (working copy) @@ -97,8 +97,8 @@ install_sh = @install_sh@ AUTOMAKE_OPTIONS = 1.4 gnu -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -AM_CFLAGS = -Wall +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -Wno-pointer-sign +AM_CFLAGS = -Wall -Wno-pointer-sign AM_CPPFLAGS = -DSBINDIR='"@sbindir@"' INCLUDES = @GELFINCLUDE@ --- src/arch-i386.c (revision 168) +++ src/arch-i386.c (working copy) @@ -75,6 +75,7 @@ i386_adjust_rel (DSO *dso, GElf_Rel *rel { case R_386_RELATIVE: case R_386_JMP_SLOT: + case R_386_IRELATIVE: data = read_ule32 (dso, rel->r_offset); if (data >= start) write_le32 (dso, rel->r_offset, data + adjust); @@ -187,6 +188,7 @@ i386_prelink_rela (struct prelink_info * GElf_Addr value; if (GELF_R_TYPE (rela->r_info) == R_386_RELATIVE + || GELF_R_TYPE (rela->r_info) == R_386_IRELATIVE || GELF_R_TYPE (rela->r_info) == R_386_NONE) /* Fast path: nothing to do. */ return 0; --- src/arch-x86_64.c (revision 168) +++ src/arch-x86_64.c (working copy) @@ -89,6 +89,10 @@ x86_64_adjust_rela (DSO *dso, GElf_Rela rela->r_addend += adjust; } break; + case R_X86_64_IRELATIVE: + if (rela->r_addend >= start) + rela->r_addend += adjust; + /* FALLTHROUGH */ case R_X86_64_JUMP_SLOT: addr = read_ule64 (dso, rela->r_offset); if (addr >= start) @@ -113,7 +117,8 @@ x86_64_prelink_rela (struct prelink_info GElf_Addr value; dso = info->dso; - if (GELF_R_TYPE (rela->r_info) == R_X86_64_NONE) + if (GELF_R_TYPE (rela->r_info) == R_X86_64_NONE + || GELF_R_TYPE (rela->r_info) == R_X86_64_IRELATIVE) return 0; else if (GELF_R_TYPE (rela->r_info) == R_X86_64_RELATIVE) { @@ -248,23 +253,29 @@ x86_64_prelink_conflict_rela (DSO *dso, || GELF_R_TYPE (rela->r_info) == R_X86_64_NONE) /* Fast path: nothing to do. */ return 0; + conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), GELF_R_TYPE (rela->r_info)); if (conflict == NULL) { - if (info->curtls == NULL) - return 0; switch (GELF_R_TYPE (rela->r_info)) { /* Even local DTPMOD and TPOFF relocs need conflicts. */ case R_X86_64_DTPMOD64: case R_X86_64_TPOFF64: + if (info->curtls == NULL || info->dso == dso) + return 0; + break; + /* Similarly IRELATIVE relocations always need conflicts. */ + case R_X86_64_IRELATIVE: break; default: return 0; } value = 0; } + else if (info->dso == dso && !conflict->ifunc) + return 0; else { /* DTPOFF wants to see only real conflicts, not lookups @@ -288,7 +299,10 @@ x86_64_prelink_conflict_rela (DSO *dso, /* FALLTHROUGH */ case R_X86_64_JUMP_SLOT: case R_X86_64_64: + case R_X86_64_IRELATIVE: ret->r_addend = value + rela->r_addend; + if (conflict != NULL && conflict->ifunc) + ret->r_info = GELF_R_INFO (0, R_X86_64_IRELATIVE); break; case R_X86_64_32: value += rela->r_addend; @@ -422,6 +436,7 @@ x86_64_undo_prelink_rela (DSO *dso, GElf { case R_X86_64_NONE: case R_X86_64_RELATIVE: + case R_X86_64_IRELATIVE: break; case R_X86_64_JUMP_SLOT: sec = addr_to_sec (dso, rela->r_offset); --- src/prelink.h (revision 168) +++ src/prelink.h (working copy) @@ -85,6 +85,14 @@ #define R_ARM_TLS_TPOFF32 19 #endif +#ifndef R_386_IRELATIVE +#define R_386_IRELATIVE 42 +#endif + +#ifndef R_X86_64_IRELATIVE +#define R_X86_64_IRELATIVE 37 +#endif + struct prelink_entry; struct prelink_info; struct PLArch; @@ -413,7 +421,8 @@ struct prelink_conflict /* Value it has in conflict.ent. */ GElf_Addr conflictval; int reloc_class; - int used; + unsigned char used; + unsigned char ifunc; }; struct prelink_conflicts --- src/conflict.c (revision 168) +++ src/conflict.c (working copy) @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2003, 2004, 2007 Red Hat, Inc. +/* Copyright (C) 2001, 2002, 2003, 2004, 2007, 2009 Red Hat, Inc. Written by Jakub Jelinek , 2001. This program is free software; you can redistribute it and/or modify @@ -467,15 +467,17 @@ prelink_build_conflicts (struct prelink_ } } - for (i = 1; i < ndeps; ++i) + for (i = 0; i < ndeps; ++i) { + int j, sec, first_conflict, maxidx; + struct prelink_conflict *conflict; + dso = info->dsos[i]; - ent = info->ent->depends[i - 1]; + ent = i ? info->ent->depends[i - 1] : info->ent; /* Verify .gnu.liblist sections of all dependent libraries. */ - if (ent->ndepends > 0) + if (i && ent->ndepends > 0) { - int j; const char *name; int nliblist; Elf32_Lib *liblist; @@ -539,85 +541,78 @@ prelink_build_conflicts (struct prelink_ } } - if (info->conflicts[i].count || info->tls[i].modid) + info->curconflicts = &info->conflicts[i]; + info->curtls = info->tls[i].modid ? info->tls + i : NULL; + first_conflict = info->conflict_rela_size; + sec = addr_to_sec (dso, dso->info[DT_SYMTAB]); + /* DT_SYMTAB should be found and should point to + start of .dynsym section. */ + if (sec == -1 || dso->info[DT_SYMTAB] != dso->shdr[sec].sh_addr) { - int j, sec, first_conflict, maxidx; - struct prelink_conflict *conflict; - - info->curconflicts = &info->conflicts[i]; - info->curtls = info->tls[i].modid ? info->tls + i : NULL; - first_conflict = info->conflict_rela_size; - sec = addr_to_sec (dso, dso->info[DT_SYMTAB]); - /* DT_SYMTAB should be found and should point to - start of .dynsym section. */ - if (sec == -1 - || dso->info[DT_SYMTAB] != dso->shdr[sec].sh_addr) + error (0, 0, "Bad symtab"); + goto error_out; + } + info->symtab_start = dso->shdr[sec].sh_addr - dso->base; + info->symtab_end = info->symtab_start + dso->shdr[sec].sh_size; + for (j = 0; j < dso->ehdr.e_shnum; ++j) + { + if (! (dso->shdr[j].sh_flags & SHF_ALLOC)) + continue; + switch (dso->shdr[j].sh_type) { - error (0, 0, "Bad symtab"); - goto error_out; - } - info->symtab_start = dso->shdr[sec].sh_addr - dso->base; - info->symtab_end = info->symtab_start + dso->shdr[sec].sh_size; - for (j = 0; j < dso->ehdr.e_shnum; ++j) - { - if (! (dso->shdr[j].sh_flags & SHF_ALLOC)) - continue; - switch (dso->shdr[j].sh_type) - { - case SHT_REL: - if (prelink_conflict_rel (dso, j, info)) - goto error_out; - break; - case SHT_RELA: - if (prelink_conflict_rela (dso, j, info)) - goto error_out; - break; - } + case SHT_REL: + if (prelink_conflict_rel (dso, j, info)) + goto error_out; + break; + case SHT_RELA: + if (prelink_conflict_rela (dso, j, info)) + goto error_out; + break; } + } - if (dso->arch->arch_prelink_conflict - && dso->arch->arch_prelink_conflict (dso, info)) - goto error_out; + if (dso->arch->arch_prelink_conflict + && dso->arch->arch_prelink_conflict (dso, info)) + goto error_out; - maxidx = 1; - if (info->curconflicts->hash != &info->curconflicts->first) - maxidx = 251; - for (j = 0; j < maxidx; j++) - for (conflict = info->curconflicts->hash[j]; conflict; - conflict = conflict->next) - if (! conflict->used) - { - error (0, 0, "%s: Conflict %08llx not found in any relocation", - dso->filename, (unsigned long long) conflict->symoff); - ret = 1; - } + maxidx = 1; + if (info->curconflicts->hash != &info->curconflicts->first) + maxidx = 251; + for (j = 0; j < maxidx; j++) + for (conflict = info->curconflicts->hash[j]; conflict; + conflict = conflict->next) + if (! conflict->used && (i || conflict->ifunc)) + { + error (0, 0, "%s: Conflict %08llx not found in any relocation", + dso->filename, (unsigned long long) conflict->symoff); + ret = 1; + } + + /* Record library's position in search scope into R_SYM field. */ + for (j = first_conflict; j < info->conflict_rela_size; ++j) + info->conflict_rela[j].r_info + = GELF_R_INFO (i, GELF_R_TYPE (info->conflict_rela[j].r_info)); + + if (dynamic_info_is_set (dso, DT_TEXTREL) + && info->conflict_rela_size > first_conflict) + { + /* We allow prelinking against non-PIC libraries, as long as + no conflict is against read-only segment. */ + int k; - /* Record library's position in search scope into R_SYM field. */ for (j = first_conflict; j < info->conflict_rela_size; ++j) - info->conflict_rela[j].r_info - = GELF_R_INFO (i, GELF_R_TYPE (info->conflict_rela[j].r_info)); - - if (dynamic_info_is_set (dso, DT_TEXTREL) - && info->conflict_rela_size > first_conflict) - { - /* We allow prelinking against non-PIC libraries, as long as - no conflict is against read-only segment. */ - int k; - - for (j = first_conflict; j < info->conflict_rela_size; ++j) - for (k = 0; k < dso->ehdr.e_phnum; ++k) - if (dso->phdr[k].p_type == PT_LOAD - && (dso->phdr[k].p_flags & PF_W) == 0 - && dso->phdr[k].p_vaddr - <= info->conflict_rela[j].r_offset - && dso->phdr[k].p_vaddr + dso->phdr[k].p_memsz - > info->conflict_rela[j].r_offset) - { - error (0, 0, "%s: Cannot prelink against non-PIC shared library %s", - info->dso->filename, dso->filename); - goto error_out; - } - } + for (k = 0; k < dso->ehdr.e_phnum; ++k) + if (dso->phdr[k].p_type == PT_LOAD + && (dso->phdr[k].p_flags & PF_W) == 0 + && dso->phdr[k].p_vaddr + <= info->conflict_rela[j].r_offset + && dso->phdr[k].p_vaddr + dso->phdr[k].p_memsz + > info->conflict_rela[j].r_offset) + { + error (0, 0, "%s: Cannot prelink against non-PIC shared library %s", + info->dso->filename, dso->filename); + goto error_out; + } } } --- src/get.c (revision 168) +++ src/get.c (working copy) @@ -241,7 +241,7 @@ prelink_record_relocations (struct preli do { unsigned long long symstart, symoff, valstart[3], value[3]; - int reloc_class, len, type = 1; + int reloc_class, len, type = 1, ifunc = 0; char *symname; r = strchr (buffer, '\n'); @@ -276,7 +276,13 @@ prelink_record_relocations (struct preli reloc_class = dso->arch->reloc_class (reloc_class); else { - if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS) + if (reloc_class & 8) + { + reloc_class = ((reloc_class & ~8) + | dso->arch->rtype_class_valid); + ifunc = 1; + } + else if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS) reloc_class |= RTYPE_CLASS_VALID; else reloc_class |= dso->arch->rtype_class_valid; @@ -287,7 +293,8 @@ prelink_record_relocations (struct preli ent = NULL; tls = NULL; if (symstart == deps[0].start - || (reloc_class == RTYPE_CLASS_TLS && info->conflicts)) + || ((reloc_class == RTYPE_CLASS_TLS || ifunc) + && info->conflicts)) { for (i = 0; i < ndeps; i++) if (deps[i].start == valstart[0]) @@ -316,7 +323,7 @@ prelink_record_relocations (struct preli } } - if (symstart == deps[0].start) + if (symstart == deps[0].start && (!ifunc || info->conflicts == NULL)) { /* Only interested in relocations from the current object. */ if (symoff < info->symtab_start || symoff >= info->symtab_end) @@ -326,7 +333,8 @@ prelink_record_relocations (struct preli goto error_out; } - if (ent == info->ent && reloc_class != RTYPE_CLASS_TLS) + if (ent == info->ent + && reloc_class != RTYPE_CLASS_TLS) value[0] = adjust_old_to_new (info->dso, value[0]); s = &info->symbols[(symoff - info->symtab_start) @@ -371,13 +379,14 @@ prelink_record_relocations (struct preli s->next = NULL; } } - else if (reloc_class == RTYPE_CLASS_TLS && info->conflicts) + else if ((reloc_class == RTYPE_CLASS_TLS || ifunc) + && info->conflicts) { struct prelink_conflict *conflict; int symowner; size_t idx; - for (symowner = 1; symowner < ndeps; symowner++) + for (symowner = 0; symowner < ndeps; symowner++) if (deps[symowner].start == symstart) break; if (symowner == ndeps) @@ -395,8 +404,12 @@ prelink_record_relocations (struct preli if (conflict->symoff == symoff && conflict->reloc_class == reloc_class) { - if (conflict->lookup.tls != tls - || conflict->conflict.tls != tls + if ((reloc_class != RTYPE_CLASS_TLS + && (conflict->lookup.ent != ent + || conflict->conflict.ent != ent)) + || (reloc_class == RTYPE_CLASS_TLS + && (conflict->lookup.tls != tls + || conflict->conflict.tls != tls)) || conflict->lookupval != value[0] || conflict->conflictval != value[0]) { @@ -418,13 +431,22 @@ prelink_record_relocations (struct preli conflict->next = info->conflicts[symowner].hash[idx]; conflict->next2 = NULL; info->conflicts[symowner].hash[idx] = conflict; - conflict->lookup.tls = tls; - conflict->conflict.tls = tls; + if (reloc_class != RTYPE_CLASS_TLS) + { + conflict->lookup.ent = ent; + conflict->conflict.ent = ent; + } + else + { + conflict->lookup.tls = tls; + conflict->conflict.tls = tls; + } conflict->lookupval = value[0]; conflict->conflictval = value[0]; conflict->symoff = symoff; conflict->reloc_class = reloc_class; conflict->used = 0; + conflict->ifunc = ifunc; if (++info->conflicts[symowner].count == 16) conflict_hash_init (&info->conflicts[symowner]); } @@ -458,7 +480,13 @@ prelink_record_relocations (struct preli reloc_class = dso->arch->reloc_class (reloc_class); else { - if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS) + if (reloc_class & 8) + { + reloc_class = ((reloc_class & ~8) + | dso->arch->rtype_class_valid); + ifunc = 1; + } + else if ((reloc_class | RTYPE_CLASS_VALID) == RTYPE_CLASS_TLS) reloc_class |= RTYPE_CLASS_VALID; else reloc_class |= dso->arch->rtype_class_valid; @@ -571,6 +599,7 @@ prelink_record_relocations (struct preli conflict->symoff = symoff; conflict->reloc_class = reloc_class; conflict->used = 0; + conflict->ifunc = ifunc; if (++info->conflicts[symowner].count == 16) conflict_hash_init (&info->conflicts[symowner]); } --- src/arch-mips.c (revision 168) +++ src/arch-mips.c (working copy) @@ -550,7 +550,7 @@ mips_prelink_conflict_reloc (DSO *dso, s { GElf_Addr value; struct prelink_conflict *conflict; - struct prelink_tls *tls; + struct prelink_tls *tls = NULL; GElf_Rela *entry; conflict = prelink_conflict (info, GELF_R_SYM (r_info), --- src/dso.c (revision 168) +++ src/dso.c (working copy) @@ -844,6 +844,7 @@ reopen_dso (DSO *dso, struct section_mov } else { + memset (&data, 0, sizeof data); data.d_type = ELF_T_NUM; data1 = NULL; while ((data1 = elf_getdata (dso->scn[j], data1)) --- src/Makefile.am (revision 168) +++ src/Makefile.am (working copy) @@ -2,8 +2,8 @@ AUTOMAKE_OPTIONS = 1.4 gnu -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -AM_CFLAGS = -Wall +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -Wall -Wno-pointer-sign +AM_CFLAGS = -Wall -Wno-pointer-sign AM_CPPFLAGS = -DSBINDIR='"@sbindir@"' INCLUDES = @GELFINCLUDE@ --- testsuite/Makefile.in (revision 168) +++ testsuite/Makefile.in (working copy) @@ -109,6 +109,7 @@ TESTS = movelibs.sh \ tls1.sh tls2.sh tls3.sh tls4.sh tls5.sh tls6.sh tls7.sh \ cxx1.sh cxx2.sh quick1.sh quick2.sh quick3.sh cycle1.sh cycle2.sh \ deps1.sh deps2.sh \ + ifunc1.sh ifunc2.sh \ undosyslibs.sh TESTS_ENVIRONMENT = \ --- testsuite/ifunc1lib2.c (revision 0) +++ testsuite/ifunc1lib2.c (revision 0) @@ -0,0 +1,39 @@ +#include "ifunc.h" + +static int lib2t11 (void) { return 1; } +static int lib2t12 (void) { return 2; } + +IFUNC_LOCAL (lib2t1, lib2t11, lib2t12); + +static int lib2t21 (void) { return 3; } +static int lib2t22 (void) { return 4; } + +IFUNC_GLOBAL (lib2t2, lib2t21, lib2t22); + +static int lib2t31 (void) { return 1; } +static int lib2t32 (void) { return 2; } + +IFUNC_GLOBAL (lib2t3, lib2t31, lib2t32); + +static int lib1t31 (void) { return 1; } +static int lib1t32 (void) { return 2; } + +IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32); + +int (*lib2p1) (void) = lib2t2; + +extern void abort (void); + +int +lib2test (void) +{ + if (lib2t1 () != PICKNO) + abort (); + if (lib2t2 () != PICKNO) + abort (); + if (lib2t3 () != PICKNO) + abort (); + if (lib2p1 () != PICKNO) + abort (); + return 0; +} --- testsuite/ifunc2.sh (revision 0) +++ testsuite/ifunc2.sh (revision 0) @@ -0,0 +1,21 @@ +#!/bin/bash +. `dirname $0`/functions.sh +# First check if __thread is supported by ld.so/gcc/ld/as: +$CCLINK -o ifunctest2 $srcdir/ifunctest.c -Wl,--rpath-link,. > /dev/null 2>&1 || exit 77 +( ./ifunctest2 || { rm -f ifunctest2; exit 77; } ) 2>/dev/null || exit 77 +rm -f ifunc2 ifunc2lib*.so ifunc2.log +rm -f prelink.cache +$CC -shared -O2 -fpic -o ifunc2lib1.so $srcdir/ifunc1lib1.c -DPICKNO=2 +$CC -shared -O2 -fpic -o ifunc2lib2.so $srcdir/ifunc1lib2.c ifunc2lib1.so -DPICKNO=2 +BINS="ifunc2" +LIBS="ifunc2lib1.so ifunc2lib2.so" +$CCLINK -o ifunc2 $srcdir/ifunc1.c -Wl,--rpath-link,. ifunc2lib2.so -DPICKNO=2 +savelibs +echo $PRELINK ${PRELINK_OPTS--vm} ./ifunc2 >> ifunc2.log +$PRELINK ${PRELINK_OPTS--vm} ./ifunc2 >> ifunc2.log 2>&1 || exit 1 +grep -q ^`echo $PRELINK | sed 's/ .*$/: /'` ifunc2.log && exit 2 +LD_LIBRARY_PATH=. ./ifunc2 || exit 3 +readelf -a ./ifunc2 >> ifunc2.log 2>&1 || exit 4 +# So that it is not prelinked again +chmod -x ./ifunc2 +comparelibs >> ifunc2.log 2>&1 || exit 5 --- testsuite/ifunctest.c (revision 0) +++ testsuite/ifunctest.c (revision 0) @@ -0,0 +1,16 @@ +#include "ifunc.h" + +static int foo1 (void) { return 1; } +static int foo2 (void) { return 2; } + +IFUNC_LOCAL (foo, foo1, foo2); + +extern void abort (void); + +int +main (void) +{ + if (foo () != PICKNO) + abort (); + return 0; +} --- testsuite/ifunc1.sh (revision 0) +++ testsuite/ifunc1.sh (revision 0) @@ -0,0 +1,21 @@ +#!/bin/bash +. `dirname $0`/functions.sh +# First check if __thread is supported by ld.so/gcc/ld/as: +$CCLINK -o ifunctest1 $srcdir/ifunctest.c -Wl,--rpath-link,. > /dev/null 2>&1 || exit 77 +( ./ifunctest1 || { rm -f ifunctest1; exit 77; } ) 2>/dev/null || exit 77 +rm -f ifunc1 ifunc1lib*.so ifunc1.log +rm -f prelink.cache +$CC -shared -O2 -fpic -o ifunc1lib1.so $srcdir/ifunc1lib1.c +$CC -shared -O2 -fpic -o ifunc1lib2.so $srcdir/ifunc1lib2.c ifunc1lib1.so +BINS="ifunc1" +LIBS="ifunc1lib1.so ifunc1lib2.so" +$CCLINK -o ifunc1 $srcdir/ifunc1.c -Wl,--rpath-link,. ifunc1lib2.so +savelibs +echo $PRELINK ${PRELINK_OPTS--vm} ./ifunc1 >> ifunc1.log +$PRELINK ${PRELINK_OPTS--vm} ./ifunc1 >> ifunc1.log 2>&1 || exit 1 +grep -q ^`echo $PRELINK | sed 's/ .*$/: /'` ifunc1.log && exit 2 +LD_LIBRARY_PATH=. ./ifunc1 || exit 3 +readelf -a ./ifunc1 >> ifunc1.log 2>&1 || exit 4 +# So that it is not prelinked again +chmod -x ./ifunc1 +comparelibs >> ifunc1.log 2>&1 || exit 5 --- testsuite/ifunc1lib1.c (revision 0) +++ testsuite/ifunc1lib1.c (revision 0) @@ -0,0 +1,32 @@ +#include "ifunc.h" + +static int lib1t11 (void) { return 1; } +static int lib1t12 (void) { return 2; } + +IFUNC_LOCAL (lib1t1, lib1t11, lib1t12); + +static int lib1t21 (void) { return 1; } +static int lib1t22 (void) { return 2; } + +IFUNC_GLOBAL (lib1t2, lib1t21, lib1t22); + +static int lib1t31 (void) { return 3; } +static int lib1t32 (void) { return 4; } + +IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32); + +extern void abort (void); + +int (*lib1p1) (void) = lib1t1; + +int +lib1test (void) +{ + if (lib1t1 () != PICKNO) + abort (); + if (lib1t3 () != PICKNO) + abort (); + if (lib1p1 () != PICKNO) + abort (); + return 0; +} --- testsuite/ifunc.h (revision 0) +++ testsuite/ifunc.h (revision 0) @@ -0,0 +1,23 @@ +#ifndef PICKNO +# define PICKNO 1 +#endif +#if PICKNO == 2 +# define PICK(fn1, fn2) #fn2 +#else +# define PICK(fn1, fn2) #fn1 +#endif +#ifdef __x86_64__ +# define IFUNC_ASM(fn) "\tleaq " fn "(%rip), %rax\n\tretq\n" +#endif +#define IFUNC(name, hidden, fn1, fn2) \ +extern __typeof (fn1) fn1 __attribute__((used)); \ +extern __typeof (fn2) fn2 __attribute__((used)); \ +extern __typeof (fn1) name; \ +asm (".globl " #name "\n" \ + "\t" hidden " " #name "\n" \ + "\t.type " #name ", @gnu_indirect_function\n" \ + #name ":\n" \ + IFUNC_ASM (PICK (fn1, fn2)) \ + "\t.size " #name ", .-" #name "\n") +#define IFUNC_LOCAL(name, fn1, fn2) IFUNC(name, ".hidden", fn1, fn2) +#define IFUNC_GLOBAL(name, fn1, fn2) IFUNC(name, ".globl", fn1, fn2) --- testsuite/ifunc1.c (revision 0) +++ testsuite/ifunc1.c (revision 0) @@ -0,0 +1,29 @@ +#include "ifunc.h" + +static int bint11 (void) { return 1; } +static int bint12 (void) { return 2; } + +IFUNC_LOCAL (bint1, bint11, bint12); + +static int lib2t21 (void) { return 1; } +static int lib2t22 (void) { return 2; } + +IFUNC_GLOBAL (lib2t2, lib2t21, lib2t22); + +extern int lib1t2 (void); +extern int lib1test (void); +extern int lib2test (void); + +extern void abort (void); + +int +main (void) +{ + lib1test (); + lib2test (); + if (bint1 () != PICKNO) + abort (); + if (lib1t2 () != PICKNO) + abort (); + return 0; +} --- testsuite/Makefile.am (revision 168) +++ testsuite/Makefile.am (working copy) @@ -14,6 +14,7 @@ TESTS = movelibs.sh \ tls1.sh tls2.sh tls3.sh tls4.sh tls5.sh tls6.sh tls7.sh \ cxx1.sh cxx2.sh quick1.sh quick2.sh quick3.sh cycle1.sh cycle2.sh \ deps1.sh deps2.sh \ + ifunc1.sh ifunc2.sh \ undosyslibs.sh TESTS_ENVIRONMENT = \ PRELINK="../src/prelink -c ./prelink.conf -C ./prelink.cache --ld-library-path=. --dynamic-linker=`echo ./ld*.so.*[0-9]`" \ --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ifunc.h" Content-length: 748 #ifndef PICKNO # define PICKNO 1 #endif #if PICKNO == 2 # define PICK(fn1, fn2) #fn2 #else # define PICK(fn1, fn2) #fn1 #endif #ifdef __x86_64__ # define IFUNC_ASM(fn) "\tleaq " fn "(%rip), %rax\n\tretq\n" #endif #define IFUNC(name, hidden, fn1, fn2) \ extern __typeof (fn1) fn1 __attribute__((used)); \ extern __typeof (fn2) fn2 __attribute__((used)); \ extern __typeof (fn1) name; \ asm (".globl " #name "\n" \ "\t" hidden " " #name "\n" \ "\t.type " #name ", @gnu_indirect_function\n" \ #name ":\n" \ IFUNC_ASM (PICK (fn1, fn2)) \ "\t.size " #name ", .-" #name "\n") #define IFUNC_LOCAL(name, fn1, fn2) IFUNC(name, ".hidden", fn1, fn2) #define IFUNC_GLOBAL(name, fn1, fn2) IFUNC(name, ".globl", fn1, fn2) --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ifunc3lib1.c" Content-length: 791 #include "ifunc.h" static int lib1t11 (void) { return 1; } static int lib1t12 (void) { return 2; } IFUNC_LOCAL (lib1t1, lib1t11, lib1t12); static int lib1t21 (void) { return 1; } static int lib1t22 (void) { return 2; } IFUNC_GLOBAL (lib1t2, lib1t21, lib1t22); static int lib1t31 (void) { return 3; } static int lib1t32 (void) { return 4; } IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32); extern void abort (void); int (*lib1p1) (void) = lib1t1; int (*lib1p2) (void) = lib1t2; int (*lib1p3) (void) = lib1t3; int lib1test (void) { if (lib1t1 () != PICKNO) abort (); if (lib1t2 () != PICKNO) abort (); if (lib1t3 () != PICKNO) abort (); if (lib1p1 () != PICKNO) abort (); if (lib1p2 () != PICKNO) abort (); if (lib1p3 () != PICKNO) abort (); return 0; } --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ifunc3.c" Content-length: 494 #include "ifunc.h" static int lib1t31 (void) { return 1; } static int lib1t32 (void) { return 2; } IFUNC_GLOBAL (lib1t3, lib1t31, lib1t32); extern int lib1test (void); extern int (*lib1p1) (void); extern int (*lib1p2) (void); extern int (*lib1p3) (void); extern void abort (void); int main (void) { lib1test (); if (lib1p1 () != PICKNO) abort (); if (lib1p2 () != PICKNO) abort (); if (lib1p3 () != PICKNO) abort (); if (lib1p3 != lib1t3) abort (); return 0; } --k+w/mQv8wyuph6w0 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ifunctest.c" Content-length: 914 #ifndef PICKNO # define PICKNO 1 #endif #if PICKNO == 2 # define PICK(fn1, fn2) #fn2 #else # define PICK(fn1, fn2) #fn1 #endif #ifdef __x86_64__ # define IFUNC_ASM(fn) "\tleaq " fn "(%rip), %rax\n\tretq\n" #endif #define IFUNC(name, globl, fn1, fn2) \ extern __typeof (fn1) fn1 __attribute__((used)); \ extern __typeof (fn2) fn2 __attribute__((used)); \ extern __typeof (fn1) name; \ asm (globl " " #name "\n" \ "\t.type " #name ", @gnu_indirect_function\n" \ #name ":\n" \ IFUNC_ASM (PICK (fn1, fn2)) \ "\t.size " #name ", .-" #name "\n") #define IFUNC_LOCAL(name, fn1, fn2) IFUNC(name, ".local", fn1, fn2) #define IFUNC_GLOBAL(name, fn1, fn2) IFUNC(name, ".globl", fn1, fn2) static int foo1 (void) { return 1; } static int foo2 (void) { return 2; } IFUNC_LOCAL (foo, foo1, foo2); extern void abort (void); int main (void) { if (foo () != PICKNO) abort (); return 0; } --k+w/mQv8wyuph6w0--