public inbox for libc-hacker@sourceware.org
 help / color / mirror / Atom feed
* Preliminary prelink IFUNC support (x86-64 only so far), some binutils IFUNC issues
@ 2009-06-12 16:03 Jakub Jelinek
  2009-06-13  6:01 ` Ulrich Drepper
  0 siblings, 1 reply; 2+ messages in thread
From: Jakub Jelinek @ 2009-06-12 16:03 UTC (permalink / raw)
  To: Ulrich Drepper, H.J. Lu; +Cc: Glibc hackers, binutils

[-- Attachment #1: Type: text/plain, Size: 962 bytes --]

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

[-- Attachment #2: libc6 --]
[-- Type: text/plain, Size: 1285 bytes --]

2009-06-12  Jakub Jelinek  <jakub@redhat.com>

	* 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",

[-- Attachment #3: prel6 --]
[-- Type: text/plain, Size: 21916 bytes --]

--- 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 <jakub@redhat.com>, 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]`" \

[-- Attachment #4: ifunc.h --]
[-- Type: text/plain, Size: 748 bytes --]

#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)

[-- Attachment #5: ifunc3lib1.c --]
[-- Type: text/plain, Size: 791 bytes --]

#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;
}

[-- Attachment #6: ifunc3.c --]
[-- Type: text/plain, Size: 494 bytes --]

#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;
}

[-- Attachment #7: ifunctest.c --]
[-- Type: text/plain, Size: 914 bytes --]

#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;
}

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2009-06-13  6:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-06-12 16:03 Preliminary prelink IFUNC support (x86-64 only so far), some binutils IFUNC issues Jakub Jelinek
2009-06-13  6:01 ` Ulrich Drepper

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).