Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 4 Jan 2012 16:43:29 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r229503 - in stable/9/libexec/rtld-elf: . amd64 arm i386 ia64 mips powerpc powerpc64 sparc64
Message-ID:  <201201041643.q04GhTvb087251@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Wed Jan  4 16:43:29 2012
New Revision: 229503
URL: http://svn.freebsd.org/changeset/base/229503

Log:
  MFC r228435:
  Add support for STT_GNU_IFUNC and R_MACHINE_IRELATIVE GNU extensions to
  rtld on 386 and amd64.
  
  MFC r228503:
  Postpone the resolution for irelative/ifunc right before initializers
  are called, and drop bind lock around calls to dispatcher.  Use
  initlist to iterate over the objects instead of the ->next, due to
  drop of the bind lock in iteration.
  
  For i386/reloc.c:reloc_iresolve(), fix calculation of the dispatch
  function address for dso, by taking into account possible non-zero
  relocbase.
  
  MFC r228635 (by nwhitehorn):
  Fix RTLD on PowerPC after r228435. Changing the order of init_pltgot()
  caused the icache to be invalidated at the wrong time, resulting in
  an icache full of nonsense in the PLT section.

Modified:
  stable/9/libexec/rtld-elf/amd64/reloc.c
  stable/9/libexec/rtld-elf/arm/reloc.c
  stable/9/libexec/rtld-elf/i386/reloc.c
  stable/9/libexec/rtld-elf/ia64/reloc.c
  stable/9/libexec/rtld-elf/mips/reloc.c
  stable/9/libexec/rtld-elf/powerpc/reloc.c
  stable/9/libexec/rtld-elf/powerpc64/reloc.c
  stable/9/libexec/rtld-elf/rtld.c
  stable/9/libexec/rtld-elf/rtld.h
  stable/9/libexec/rtld-elf/sparc64/reloc.c
Directory Properties:
  stable/9/libexec/rtld-elf/   (props changed)

Modified: stable/9/libexec/rtld-elf/amd64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/amd64/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/amd64/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -344,11 +344,22 @@ reloc_plt(Obj_Entry *obj)
     for (rela = obj->pltrela;  rela < relalim;  rela++) {
 	Elf_Addr *where;
 
-	assert(ELF_R_TYPE(rela->r_info) == R_X86_64_JMP_SLOT);
-
-	/* Relocate the GOT slot pointing into the PLT. */
-	where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
-	*where += (Elf_Addr)obj->relocbase;
+	switch(ELF_R_TYPE(rela->r_info)) {
+	case R_X86_64_JMP_SLOT:
+	  /* Relocate the GOT slot pointing into the PLT. */
+	  where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+	  *where += (Elf_Addr)obj->relocbase;
+	  break;
+
+	case R_X86_64_IRELATIVE:
+	  obj->irelative = true;
+	  break;
+
+	default:
+	  _rtld_error("Unknown relocation type %x in PLT",
+	    (unsigned int)ELF_R_TYPE(rela->r_info));
+	  return (-1);
+	}
     }
     return 0;
 }
@@ -368,19 +379,98 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
 	const Elf_Sym *def;
 	const Obj_Entry *defobj;
 
-	assert(ELF_R_TYPE(rela->r_info) == R_X86_64_JMP_SLOT);
-	where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
-	def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
-	    lockstate);
-	if (def == NULL)
-	    return -1;
-	target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
-	reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+	switch (ELF_R_TYPE(rela->r_info)) {
+	case R_X86_64_JMP_SLOT:
+	  where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+	  def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
+	      lockstate);
+	  if (def == NULL)
+	      return (-1);
+	  if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+	      obj->gnu_ifunc = true;
+	      continue;
+	  }
+	  target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
+	  reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+	  break;
+
+	case R_X86_64_IRELATIVE:
+	  break;
+
+	default:
+	  _rtld_error("Unknown relocation type %x in PLT",
+	    (unsigned int)ELF_R_TYPE(rela->r_info));
+	  return (-1);
+	}
     }
     obj->jmpslots_done = true;
     return 0;
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+    const Elf_Rela *relalim;
+    const Elf_Rela *rela;
+
+    if (!obj->irelative)
+	return (0);
+    relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+    for (rela = obj->pltrela;  rela < relalim;  rela++) {
+	Elf_Addr *where, target, *ptr;
+
+	switch (ELF_R_TYPE(rela->r_info)) {
+	case R_X86_64_JMP_SLOT:
+	  break;
+
+	case R_X86_64_IRELATIVE:
+	  ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+	  where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+	  lock_release(rtld_bind_lock, lockstate);
+	  target = ((Elf_Addr (*)(void))ptr)();
+	  wlock_acquire(rtld_bind_lock, lockstate);
+	  *where = target;
+	  break;
+	}
+    }
+    obj->irelative = false;
+    return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, RtldLockState *lockstate)
+{
+    const Elf_Rela *relalim;
+    const Elf_Rela *rela;
+
+    if (!obj->gnu_ifunc)
+	return (0);
+    relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+    for (rela = obj->pltrela;  rela < relalim;  rela++) {
+	Elf_Addr *where, target;
+	const Elf_Sym *def;
+	const Obj_Entry *defobj;
+
+	switch (ELF_R_TYPE(rela->r_info)) {
+	case R_X86_64_JMP_SLOT:
+	  where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+	  def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
+	      lockstate);
+	  if (def == NULL)
+	      return (-1);
+	  if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+	      continue;
+	  lock_release(rtld_bind_lock, lockstate);
+	  target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+	  wlock_acquire(rtld_bind_lock, lockstate);
+	  reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+	  break;
+	}
+    }
+    obj->gnu_ifunc = false;
+    return (0);
+}
+
 void
 allocate_initial_tls(Obj_Entry *objs)
 {

Modified: stable/9/libexec/rtld-elf/arm/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/arm/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/arm/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -337,6 +337,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
 	return (0);
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
 Elf_Addr
 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
     		const Obj_Entry *obj, const Elf_Rel *rel)

Modified: stable/9/libexec/rtld-elf/i386/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/i386/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/i386/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -298,13 +298,24 @@ reloc_plt(Obj_Entry *obj)
 
     rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
     for (rel = obj->pltrel;  rel < rellim;  rel++) {
-	Elf_Addr *where;
+	Elf_Addr *where/*, val*/;
 
-	assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
-
-	/* Relocate the GOT slot pointing into the PLT. */
-	where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
-	*where += (Elf_Addr)obj->relocbase;
+	switch (ELF_R_TYPE(rel->r_info)) {
+	case R_386_JMP_SLOT:
+	  /* Relocate the GOT slot pointing into the PLT. */
+	  where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+	  *where += (Elf_Addr)obj->relocbase;
+	  break;
+
+	case R_386_IRELATIVE:
+	  obj->irelative = true;
+	  break;
+
+	default:
+	  _rtld_error("Unknown relocation type %x in PLT",
+	    ELF_R_TYPE(rel->r_info));
+	  return (-1);
+	}
     }
     return 0;
 }
@@ -324,19 +335,95 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
 	const Elf_Sym *def;
 	const Obj_Entry *defobj;
 
-	assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
-	where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
-	def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
-	    lockstate);
-	if (def == NULL)
-	    return -1;
-	target = (Elf_Addr)(defobj->relocbase + def->st_value);
-	reloc_jmpslot(where, target, defobj, obj, rel);
+	switch (ELF_R_TYPE(rel->r_info)) {
+	case R_386_JMP_SLOT:
+	  where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+	  def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+	      lockstate);
+	  if (def == NULL)
+	      return (-1);
+	  if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+	      obj->gnu_ifunc = true;
+	      continue;
+	  }
+	  target = (Elf_Addr)(defobj->relocbase + def->st_value);
+	  reloc_jmpslot(where, target, defobj, obj, rel);
+	  break;
+
+	case R_386_IRELATIVE:
+	  break;
+
+	default:
+	  _rtld_error("Unknown relocation type %x in PLT",
+	    ELF_R_TYPE(rel->r_info));
+	  return (-1);
+	}
     }
+
     obj->jmpslots_done = true;
     return 0;
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+    const Elf_Rel *rellim;
+    const Elf_Rel *rel;
+    Elf_Addr *where, target;
+
+    if (!obj->irelative)
+	return (0);
+    rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+    for (rel = obj->pltrel;  rel < rellim;  rel++) {
+	switch (ELF_R_TYPE(rel->r_info)) {
+	case R_386_IRELATIVE:
+	  where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+	  lock_release(rtld_bind_lock, lockstate);
+	  target = ((Elf_Addr (*)(void))(obj->relocbase + *where))();
+	  wlock_acquire(rtld_bind_lock, lockstate);
+	  *where = target;
+	  break;
+	}
+    }
+    obj->irelative = false;
+    return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, RtldLockState *lockstate)
+{
+    const Elf_Rel *rellim;
+    const Elf_Rel *rel;
+
+    if (!obj->gnu_ifunc)
+	return (0);
+    rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+    for (rel = obj->pltrel;  rel < rellim;  rel++) {
+	Elf_Addr *where, target;
+	const Elf_Sym *def;
+	const Obj_Entry *defobj;
+
+	switch (ELF_R_TYPE(rel->r_info)) {
+	case R_386_JMP_SLOT:
+	  where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+	  def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+	      lockstate);
+	  if (def == NULL)
+	      return (-1);
+	  if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+	      continue;
+	  lock_release(rtld_bind_lock, lockstate);
+	  target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+	  wlock_acquire(rtld_bind_lock, lockstate);
+	  reloc_jmpslot(where, target, defobj, obj, rel);
+	  break;
+	}
+    }
+
+    obj->gnu_ifunc = false;
+    return (0);
+}
+
 void
 allocate_initial_tls(Obj_Entry *objs)
 {

Modified: stable/9/libexec/rtld-elf/ia64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/ia64/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/ia64/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -435,6 +435,22 @@ reloc_plt(Obj_Entry *obj)
 	return 0;
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
 /* Relocate the jump slots in an object. */
 int
 reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate)

Modified: stable/9/libexec/rtld-elf/mips/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/mips/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/mips/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -498,6 +498,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
 	return (0);
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
 Elf_Addr
 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
     		const Obj_Entry *obj, const Elf_Rel *rel)

Modified: stable/9/libexec/rtld-elf/powerpc/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/powerpc/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/powerpc/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -370,7 +370,7 @@ reloc_plt_object(Obj_Entry *obj, const E
 		
 
 	/*
-	 * The icache will be sync'd in init_pltgot, which is called
+	 * The icache will be sync'd in reloc_plt, which is called
 	 * after all the slots have been updated
 	 */
 
@@ -386,6 +386,7 @@ reloc_plt(Obj_Entry *obj)
 {
 	const Elf_Rela *relalim;
 	const Elf_Rela *rela;
+	int N = obj->pltrelasize / sizeof(Elf_Rela);
 
 	if (obj->pltrelasize != 0) {
 
@@ -400,6 +401,13 @@ reloc_plt(Obj_Entry *obj)
 		}
 	}
 
+	/*
+	 * Sync the icache for the byte range represented by the
+	 * trampoline routines and call slots.
+	 */
+	if (obj->pltgot != NULL)
+		__syncicache(obj->pltgot, JMPTAB_BASE(N)*4);
+
 	return (0);
 }
 
@@ -508,6 +516,21 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr
 	return (target);
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
 
 /*
  * Setup the plt glue routines.
@@ -584,10 +607,9 @@ init_pltgot(Obj_Entry *obj)
 	pltresolve[4] |= _ppc_la(obj);
 
 	/*
-	 * Sync the icache for the byte range represented by the
-	 * trampoline routines and call slots.
+	 * The icache will be sync'd in reloc_plt, which is called
+	 * after all the slots have been updated
 	 */
-	__syncicache(obj->pltgot, JMPTAB_BASE(N)*4);
 }
 
 void

Modified: stable/9/libexec/rtld-elf/powerpc64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/powerpc64/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/powerpc64/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -459,6 +459,22 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr
 	return (target);
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
 void
 init_pltgot(Obj_Entry *obj)
 {

Modified: stable/9/libexec/rtld-elf/rtld.c
==============================================================================
--- stable/9/libexec/rtld-elf/rtld.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/rtld.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -116,6 +116,8 @@ static void objlist_push_tail(Objlist *,
 static void objlist_remove(Objlist *, Obj_Entry *);
 static void *path_enumerate(const char *, path_enum_proc, void *);
 static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, RtldLockState *);
+static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now,
+    RtldLockState *lockstate);
 static int rtld_dirname(const char *, char *);
 static int rtld_dirname_abs(const char *, char *);
 static void rtld_exit(void);
@@ -513,6 +515,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
       ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld, NULL) == -1)
 	die();
 
+    if (resolve_objects_ifunc(obj_main,
+      ld_bind_now != NULL && *ld_bind_now != '\0', NULL) == -1)
+	die();
+
     dbg("doing copy relocations");
     if (do_copy_relocations(obj_main) == -1)
 	die();
@@ -561,6 +567,17 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
     return (func_ptr_type) obj_main->entry;
 }
 
+void *
+rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
+{
+	void *ptr;
+	Elf_Addr target;
+
+	ptr = (void *)make_function_pointer(def, obj);
+	target = ((Elf_Addr (*)(void))ptr)();
+	return ((void *)target);
+}
+
 Elf_Addr
 _rtld_bind(Obj_Entry *obj, Elf_Size reloff)
 {
@@ -584,8 +601,10 @@ _rtld_bind(Obj_Entry *obj, Elf_Size relo
 	&lockstate);
     if (def == NULL)
 	die();
-
-    target = (Elf_Addr)(defobj->relocbase + def->st_value);
+    if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+	target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+    else
+	target = (Elf_Addr)(defobj->relocbase + def->st_value);
 
     dbg("\"%s\" in \"%s\" ==> %p in \"%s\"",
       defobj->strtab + def->st_name, basename(obj->path),
@@ -1944,6 +1963,10 @@ relocate_objects(Obj_Entry *first, bool 
 	    }
 	}
 
+
+	/* Set the special PLT or GOT entries. */
+	init_pltgot(obj);
+
 	/* Process the PLT relocations. */
 	if (reloc_plt(obj) == -1)
 	    return -1;
@@ -1952,7 +1975,6 @@ relocate_objects(Obj_Entry *first, bool 
 	    if (reloc_jmpslots(obj, lockstate) == -1)
 		return -1;
 
-
 	/*
 	 * Set up the magic number and version in the Obj_Entry.  These
 	 * were checked in the crt1.o from the original ElfKit, so we
@@ -1960,12 +1982,55 @@ relocate_objects(Obj_Entry *first, bool 
 	 */
 	obj->magic = RTLD_MAGIC;
 	obj->version = RTLD_VERSION;
-
-	/* Set the special PLT or GOT entries. */
-	init_pltgot(obj);
     }
 
-    return 0;
+    return (0);
+}
+
+/*
+ * The handling of R_MACHINE_IRELATIVE relocations and jumpslots
+ * referencing STT_GNU_IFUNC symbols is postponed till the other
+ * relocations are done.  The indirect functions specified as
+ * ifunc are allowed to call other symbols, so we need to have
+ * objects relocated before asking for resolution from indirects.
+ *
+ * The R_MACHINE_IRELATIVE slots are resolved in greedy fashion,
+ * instead of the usual lazy handling of PLT slots.  It is
+ * consistent with how GNU does it.
+ */
+static int
+resolve_object_ifunc(Obj_Entry *obj, bool bind_now, RtldLockState *lockstate)
+{
+	if (obj->irelative && reloc_iresolve(obj, lockstate) == -1)
+		return (-1);
+	if ((obj->bind_now || bind_now) && obj->gnu_ifunc &&
+	    reloc_gnu_ifunc(obj, lockstate) == -1)
+		return (-1);
+	return (0);
+}
+
+static int
+resolve_objects_ifunc(Obj_Entry *first, bool bind_now, RtldLockState *lockstate)
+{
+	Obj_Entry *obj;
+
+	for (obj = first;  obj != NULL;  obj = obj->next) {
+		if (resolve_object_ifunc(obj, bind_now, lockstate) == -1)
+			return (-1);
+	}
+	return (0);
+}
+
+static int
+initlist_objects_ifunc(Objlist *list, bool bind_now, RtldLockState *lockstate)
+{
+	Objlist_Entry *elm;
+
+	STAILQ_FOREACH(elm, list, link) {
+		if (resolve_object_ifunc(elm->obj, bind_now, lockstate) == -1)
+			return (-1);
+	}
+	return (0);
 }
 
 /*
@@ -2170,6 +2235,16 @@ dlopen(const char *name, int mode)
       mode & (RTLD_MODEMASK | RTLD_GLOBAL)));
 }
 
+static void
+dlopen_cleanup(Obj_Entry *obj)
+{
+
+	obj->dl_refcount--;
+	unref_dag(obj);
+	if (obj->refcount == 0)
+		unload_object(obj);
+}
+
 static Obj_Entry *
 dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
 {
@@ -2208,10 +2283,7 @@ dlopen_object(const char *name, Obj_Entr
 		goto trace;
 	    if (result == -1 || (relocate_objects(obj, (mode & RTLD_MODEMASK)
 	      == RTLD_NOW, &obj_rtld, &lockstate)) == -1) {
-		obj->dl_refcount--;
-		unref_dag(obj);
-		if (obj->refcount == 0)
-		    unload_object(obj);
+		dlopen_cleanup(obj);
 		obj = NULL;
 	    } else {
 		/* Make list of init functions to call. */
@@ -2245,6 +2317,14 @@ dlopen_object(const char *name, Obj_Entr
 
     map_stacks_exec(&lockstate);
 
+    if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == RTLD_NOW,
+      &lockstate) == -1) {
+	objlist_clear(&initlist);
+	dlopen_cleanup(obj);
+	lock_release(rtld_bind_lock, &lockstate);
+	return (NULL);
+    }
+
     /* Call the init functions. */
     objlist_call_init(&initlist, &lockstate);
     objlist_clear(&initlist);
@@ -2376,9 +2456,11 @@ do_dlsym(void *handle, const char *name,
 	 * the relocated value of the symbol.
 	 */
 	if (ELF_ST_TYPE(def->st_info) == STT_FUNC)
-	    return make_function_pointer(def, defobj);
+	    return (make_function_pointer(def, defobj));
+	else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+	    return (rtld_resolve_ifunc(defobj, def));
 	else
-	    return defobj->relocbase + def->st_value;
+	    return (defobj->relocbase + def->st_value);
     }
 
     _rtld_error("Undefined symbol \"%s\"", name);
@@ -2822,6 +2904,8 @@ get_program_var_addr(const char *name, R
     if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC)
 	return ((const void **)make_function_pointer(req.sym_out,
 	  req.defobj_out));
+    else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC)
+	return ((const void **)rtld_resolve_ifunc(req.defobj_out, req.sym_out));
     else
 	return ((const void **)(req.defobj_out->relocbase +
 	  req.sym_out->st_value));
@@ -3088,6 +3172,7 @@ symlook_obj1(SymLook *req, const Obj_Ent
 	case STT_FUNC:
 	case STT_NOTYPE:
 	case STT_OBJECT:
+	case STT_GNU_IFUNC:
 	    if (symp->st_value == 0)
 		continue;
 		/* fallthrough */

Modified: stable/9/libexec/rtld-elf/rtld.h
==============================================================================
--- stable/9/libexec/rtld-elf/rtld.h	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/rtld.h	Wed Jan  4 16:43:29 2012	(r229503)
@@ -230,6 +230,8 @@ typedef struct Struct_Obj_Entry {
     bool on_fini_list: 1;	/* Object is already on fini list. */
     bool dag_inited : 1;	/* Object has its DAG initialized. */
     bool filtees_loaded : 1;	/* Filtees loaded */
+    bool irelative : 1;		/* Object has R_MACHDEP_IRELATIVE relocs */
+    bool gnu_ifunc : 1;		/* Object has references to STT_GNU_IFUNC */
 
     struct link_map linkmap;	/* For GDB and dlinfo() */
     Objlist dldags;		/* Object belongs to these dlopened DAGs (%) */
@@ -317,6 +319,7 @@ void lockdflt_init(void);
 void obj_free(Obj_Entry *);
 Obj_Entry *obj_new(void);
 void _rtld_bind_start(void);
+void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def);
 void symlook_init(SymLook *, const char *);
 int symlook_obj(SymLook *, const Obj_Entry *);
 void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset);
@@ -334,6 +337,8 @@ int do_copy_relocations(Obj_Entry *);
 int reloc_non_plt(Obj_Entry *, Obj_Entry *, struct Struct_RtldLockState *);
 int reloc_plt(Obj_Entry *);
 int reloc_jmpslots(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_gnu_ifunc(Obj_Entry *, struct Struct_RtldLockState *);
 void allocate_initial_tls(Obj_Entry *);
 
 #endif /* } */

Modified: stable/9/libexec/rtld-elf/sparc64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/sparc64/reloc.c	Wed Jan  4 16:43:08 2012	(r229502)
+++ stable/9/libexec/rtld-elf/sparc64/reloc.c	Wed Jan  4 16:43:29 2012	(r229503)
@@ -550,6 +550,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
 	return (0);
 }
 
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+	/* XXX not implemented */
+	return (0);
+}
+
 Elf_Addr
 reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *obj,
     const Obj_Entry *refobj, const Elf_Rel *rel)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201041643.q04GhTvb087251>