public inbox for libc-ports@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] 2nd round of Mips ifunc support
@ 2013-11-16  0:44 Jack Carter
  2013-11-16  1:11 ` Jack Carter
  2013-11-18 12:29 ` Will Newton
  0 siblings, 2 replies; 4+ messages in thread
From: Jack Carter @ 2013-11-16  0:44 UTC (permalink / raw)
  To: libc-ports; +Cc: rdsandiford

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

Attached is the second pass for Mips ifunc support in glibc.

There is an abi attached as well.

Jack

[-- Attachment #2: ifunc_abi.txt --]
[-- Type: text/plain, Size: 8809 bytes --]

Mips GNU IFUNC ABI

I will use the terms STT_GNU_IFUNC and IFUNC interchangeably below.

***************************************************************
<Generic Definition> Taken from:
http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Function-Attributes.html
***************************************************************
ifunc ("resolver")
    The ifunc attribute is used to mark a function as an indirect 
    function using the STT_GNU_IFUNC symbol type extension to the 
    ELF standard. This allows the resolution of the symbol value 
    to be determined dynamically at load time, and an optimized 
    version of the routine can be selected for the particular 
    processor or other system characteristics determined then. 
    To use this attribute, first define the implementation 
    functions available, and a resolver function that returns a 
    pointer to the selected implementation function. The 
    implementation functions' declarations must match the API of 
    the function being implemented, the resolver's declaration 
    is be a function returning pointer to void function returning 
    void:

              void *my_memcpy (void *dst, const void *src, size_t len)
              {
                ...
              }
              
              static void (*resolve_memcpy (void)) (void)
              {
                return my_memcpy; // we'll just always select this routine
              }

    The exported header file declaring the function the user calls 
    would contain:

              extern void *memcpy (void *, const void *, size_t);

    allowing the user to call this as a regular function, unaware 
    of the implementation. Finally, the indirect function needs 
    to be defined in the same translation unit as the resolver 
    function:

              void *memcpy (void *, const void *, size_t)
                   __attribute__ ((ifunc ("resolve_memcpy")));

    Indirect functions cannot be weak, and require a recent binutils 
    (at least version 2.20.1), and GNU C library (at least version 2.11.1).
     
***************************************************************
<Mips C Example>
***************************************************************
int func1 (void) __attribute__ ((ifunc ("func1_ifunc")));

static int f1_a(void){return 1;}
static int f1_b(void){return 2;}
static int f1_c(void){return 3;}

void *
func1_ifunc (void)
{
  volatile int result;
  struct utsname buf;
  
  if ((uname(&buf)))
    result = 0x30;
  else
    result = 0x3;

  if (result & 0xf0)
    return f1_a;
  else if (result & 0x0f)
    return f1_b;
  else
    return f1_c;
}

***************************************************************
<Resulting Assembly>
***************************************************************
A new symbol for the ifunc routine is created that associates it
to the real ifunc1_func() function.

	.size	func1_ifunc, .-func1_ifunc
	.globl	func1
	.type	func1, @gnu_indirect_function
	func1 = func1_ifunc

***************************************************************
<Resulting Object>
***************************************************************
A new symbol func1 is created with the only difference from 
func1_ifunc being the section type STT_GNU_IFUNC.

Section .symtab
[No] Value Size Type  Bind   Other    Shndx     Name
===============================================================
[22] 0xd8  0xbc FUNC  GLOBAL DEFAULT (1) .text (42) func1_ifunc
[25] 0xd8  0xbc IFUNC GLOBAL DEFAULT (1) .text (69) func1

***************************************************************
<Static Linker Behavior and output>
***************************************************************
There are 3 general linker output variations that are addressed
here:

  1) Static/Non-shared executables that do not use an independent
     dynamic linker at startup time. Instead there is an embedded
     startup routine to resolve IFUNC symbols or the R_MIPS_RELATIVE
     relocation created for them.
     
  2) Executables that use shared objects and thus the dynamic linker.
  
  3) Shared objects

***
*1*
***
Static executable links will for each IFUNC symbol produce the 
following:

  *) Generate an iplt stub which is text and will be part of the .iplt
     section. Currently the stub is the following instruction sequence:

       lui $15, %hi(.got.iplt entry)
       l[wd] $25, %lo(.got.iplt entry)($15)
       jr $25
       nop

     The section attributes for .iplt are:
     
       sh_name:     (.iplt)
       sh_type:     SHT_PROGBITS
       sh_info:     0
       sh_flags:    SHF_ALLOC|SHF_EXECINSTR
       sh_addalign: 4
       sh_entsize:  0
       
     This stub will take the function address in the .igot.plt section
     for this IFUNC symbol and jump to it.
     
  *) Generate igot table (.igot.plt)
  
     This is an array of IFUNC addresses that point to the resolver function
     for this IFUNC. If the target ABI is 32 bit the entries will be 4 bytes
     each and if the ABI is 64 bit the entries will be 8 bytes each. This of
     course will preclude any linking of a combination of n32 and n64, but
     that will have more problems than ifunc.

     The section attributes for .iplt are:
     
       sh_name:     (.igot.plt)
       sh_type:     SHT_PROGBITS
       sh_info:     0
       sh_flags:    SHF_ALLOC|SHF_WRITE *
       sh_addalign: 4/8
       sh_entsize:  4/8
       
       (* Note, in spite of the name this is NOT a GP relative section)

  *) Generate R_MIPS_IRELATIVE relocations for the igot table.
  
     Produce a relocation of type R_MIPS_IRELATIVE (128) pointing at the
     .igot.plt table entry for this IFUNC.
     
     This goes into a .rel.dyn relocation section
     
       sh_name:     (.rel.dyn)
       sh_type:     SHT_REL
       sh_info:     0
       sh_flags:    SHF_ALLOC
       sh_addalign: 4/8
       sh_entsize:  8/24
       
  *) Generate the 2 symbols __rel_iplt_start and __rel_iplt_end which 
     point at the beginning and end of the relocations for the .igot.iplt
     section. That is, first entry and last entry. Not last entry+1.
     
       Type:     STT_NOTYPE
       Binding:  STB_LOCAL
       Other:    STO_DEFAULT
       Size:     0
       Value:    Address of begin or end of IFUNC relocations

At startup, the embedded dynamic linker will find the beginning and
end range of R_MIPS_IRELATIVE relocations through the special symbols
__rel_iplt_start and __rel_iplt_end. 

The relocation entry contains the address of an associated entry in the
.igot.plt table, which initially contains the address of the associated
resolver function. The resolver is called and return value of the resolver
replaces the .igot.plt table entry.

Every call to the ifunc routine goes through the iplt at runtime.

***
*2*
***
Dynamically linked executables with ifunc definitions are for the most
part the same as static executables, with the following exceptions:

  *) No __rel_iplt_start/end symbols. The dynamic linker handles dynamic
     relocations.
  *) Generate STT_GNU_IFUNC symbols in the .dynsym section with the value
     being the iplt entry for this IFUNC and transform them into 
     STT_FUNC symbols. Any references to this IFUNC have to go through 
     the stub. The dynamic linker (ld.so) will be doing the fixup at start-
     up.

***
*3*
***
Links producing dynamic shared objects do not produce iplt stubs, a
.igot.plt table, or R_MIPS_IRELATIVE relocations.

IFUNC resolution occurs during the processing of global GOT entries.
For .dynsym entries of type IFUNC, the load offset for the resolver
function stored in the GOT entry.  The resolver function is called and
the address returned by the resolver function replaces resolver
offset value in the GOT entry.

***************************************************************
<Dynamic Linker Behavior>
***************************************************************

Entries in the GOT within the DT_MIPS_LOCAL_GOTNO should have their
own entries and not share a common local GOT page entry. 

For global entries in the GOT that start at DT_MIPS_GOTSYM, the
entries in the dynamic symbol table are indexed along with the entries
in the GOT table.  If the symbol is of type STT_GNU_IFUNC, the address
of the resolver routine is computed by adding the run-time load
address of the shared object and the offset for the resolver function,
which stored in the GOT entry. The resolver is then called and the
resultant address  replaces the GOT entry.

IFUNC entries cannot be quickstarted/prelinked since the runtime value
is not known until load time.

Any dynamic relocation resolution using R_MIPS_IRELATIVE will take the 
address pointed to by the relocation and assume it is the address of the
resolver, run the resolver and replace the resolver address with the 
value returned by the resolver.




[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: mips_ifunc_glibc.patch --]
[-- Type: text/x-patch; name="mips_ifunc_glibc.patch", Size: 8469 bytes --]

This is the initial MIPS ifunc patch for glibc. This patch should be
applied in conjunction with the binutils patch.

This is targeting o32, n32 and 64 bit. Patches for MicroMips, Mips16,
and possibly vxworks will follow this patch. 

Relocations using R_MIPS_IRELATIVE or referencing a symbol of type
STT_GNU_IFUNC have their affective address produced by executing the resolver
function pointed by either the R_MIPS_IRELATIVE target or the STT_GNU_IFUNC
target. The resultant address is the final function effective address that 
will be used for the duration of the program execution.

There is an attached ABI that describes the runtime fixup in more detail.

***************************************

./ChangeLog
	* elf/elf.h:
	Define for R_MIPS_IRELATIVE and bump R_MIPS_NUM up to 129.

ports/ChangeLog
	* sysdeps/mips/dl-irel.h: New file.
	(elf_ifunc_invoke): Do the indirect reference.
	(elf_irel): Call elf_ifunc_invoke() for acceptable relocations.
	* sysdeps/mips/dl-machine.h: 
	Include new dl-irel.h.
	(ELF_MACHINE_BEFORE_RTLD_RELOC): Add check for STT_GNU_IFUNC.
	(elf_machine_reloc): Add skip_ifunc to parameter.
	Add case for R_MIPS_IRELATIVE.
	(elf_machine_rel): Add skip_ifunc to call to elf_machine_reloc().
	(elf_machine_rela):Add skip_ifunc to call to elf_machine_reloc().
	(RESOLVE_GOTSYM): Add check for STT_GNU_IFUNC.
	(elf_machine_got_rel): Add check for STT_GNU_IFUNC.
	* sysdeps/mips/dl-trampoline.c: 
	(__dl_runtime_resolve): Add check for STT_GNU_IFUNC.
diff --git a/elf/elf.h b/elf/elf.h
index a05ea3b..b3a89db 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1626,8 +1626,9 @@ typedef struct
 #define R_MIPS_GLOB_DAT		51
 #define R_MIPS_COPY		126
 #define R_MIPS_JUMP_SLOT        127
+#define R_MIPS_IRELATIVE        128
 /* Keep this the last entry.  */
-#define R_MIPS_NUM		128
+#define R_MIPS_NUM		129
 
 /* Legal values for p_type field of Elf32_Phdr.  */
 
diff --git a/ports/sysdeps/mips/dl-irel.h b/ports/sysdeps/mips/dl-irel.h
new file mode 100644
index 0000000..1891de0
--- /dev/null
+++ b/ports/sysdeps/mips/dl-irel.h
@@ -0,0 +1,63 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+   MIPS version.
+   Copyright (C) 2009-2013 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sgidefs.h>
+#include <link.h>
+#include <elf.h>
+#include <ldsodefs.h>
+
+#define ELF_MACHINE_IREL	1
+
+static inline ElfW(Addr)
+__attribute ((always_inline))
+elf_ifunc_invoke (ElfW(Addr) addr)
+{
+  /* Print some debugging info if wanted.  */
+  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS, 0))
+      {
+	ElfW(Addr) t_addr =
+	    ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+	GLRO(dl_debug_printf) ("In elf_ifunc_invoke(0x%lx), return(0x%lx)\n",
+				(unsigned long int)addr,
+				(unsigned long int)t_addr);
+      }
+
+  return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+}
+
+/* Allow either R_MIPS_RELATIVE or the nop R_MIPS_NONE */
+static inline void
+__attribute ((always_inline))
+elf_irel (const ElfW(Rel) *reloc)
+{
+  ElfW(Addr) *const reloc_addr = (void *) reloc->r_offset;
+  const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);
+
+  if (__builtin_expect (r_type == R_MIPS_IRELATIVE, 1))
+    *reloc_addr = elf_ifunc_invoke (*reloc_addr);
+  else if (r_type)
+     __libc_fatal ("unexpected reloc type in static binary\n");
+}
+
+#endif /* dl-irel.h */
diff --git a/ports/sysdeps/mips/dl-machine.h b/ports/sysdeps/mips/dl-machine.h
index 722c8a0..671aa1f 100644
--- a/ports/sysdeps/mips/dl-machine.h
+++ b/ports/sysdeps/mips/dl-machine.h
@@ -32,6 +32,7 @@
 #include <sgidefs.h>
 #include <sys/asm.h>
 #include <dl-tls.h>
+#include <dl-irel.h>
 
 /* The offset of gp from GOT might be system-dependent.  It's set by
    ld.  The same value is also */
@@ -204,6 +205,8 @@ do {									\
       else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC			\
 	       && *got != sym->st_value)				\
 	*got += map->l_addr;						\
+      else if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)		\
+	*got = elf_ifunc_invoke(sym->st_value);				\
       else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)		\
 	{								\
 	  if (sym->st_other == 0)					\
@@ -451,7 +454,8 @@ auto inline void
 __attribute__ ((always_inline))
 elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
 		   const ElfW(Sym) *sym, const struct r_found_version *version,
-		   void *reloc_addr, ElfW(Addr) r_addend, int inplace_p)
+		   void *reloc_addr, ElfW(Addr) r_addend, int inplace_p,
+		   int skip_ifunc)
 {
   const unsigned long int r_type = ELFW(R_TYPE) (r_info);
   ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
@@ -656,6 +660,14 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
 	break;
       }
 
+    case R_MIPS_IRELATIVE:
+      /* The resolver routine is the symbol referenced by this relocation.
+	 To get the address of the function to use at runtime, the resolver
+	 routine is called and its return value is the address of the target
+	 functon which is final relocation value. */
+      *addr_field = elf_ifunc_invoke (map->l_addr + *addr_field);
+      break;
+
 #if _MIPS_SIM == _ABI64
     case R_MIPS_64:
       /* For full compliance with the ELF64 ABI, one must precede the
@@ -685,7 +697,8 @@ elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
 		 const ElfW(Sym) *sym, const struct r_found_version *version,
 		 void *const reloc_addr, int skip_ifunc)
 {
-  elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1);
+  elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1, 
+		     skip_ifunc);
 }
 
 auto inline void
@@ -726,7 +739,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
 		  void *const reloc_addr, int skip_ifunc)
 {
   elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr,
-		     reloc->r_addend, 0);
+		     reloc->r_addend, 0, skip_ifunc);
 }
 
 auto inline void
@@ -753,8 +766,15 @@ elf_machine_got_rel (struct link_map *map, int lazy)
       const struct r_found_version *version __attribute__ ((unused))	  \
 	= vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL;	  \
       struct link_map *sym_map;						  \
+      ElfW(Addr) value;							  \
       sym_map = RESOLVE_MAP (&ref, version, reloc);			  \
-      ref ? sym_map->l_addr + ref->st_value : 0;			  \
+      if (ref)								  \
+	{								  \
+	  value = sym_map->l_addr + ref->st_value;			  \
+	  if (ELFW(ST_TYPE) (ref->st_info) == STT_GNU_IFUNC)		  \
+	    value = elf_ifunc_invoke (value);				  \
+	}								  \
+      ref ? value : 0;							  \
     })
 
   if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
@@ -824,6 +844,8 @@ elf_machine_got_rel (struct link_map *map, int lazy)
 	  if (sym->st_other == 0)
 	    *got += map->l_addr;
 	}
+      else if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
+	*got = elf_ifunc_invoke (map->l_addr + *got);
       else
 	*got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
 
diff --git a/ports/sysdeps/mips/dl-trampoline.c b/ports/sysdeps/mips/dl-trampoline.c
index 605e44e..0a510f3 100644
--- a/ports/sysdeps/mips/dl-trampoline.c
+++ b/ports/sysdeps/mips/dl-trampoline.c
@@ -193,6 +193,10 @@ __dl_runtime_resolve (ElfW(Word) sym_index,
       /* Currently value contains the base load address of the object
 	 that defines sym.  Now add in the symbol offset.  */
       value = (sym ? sym_map->l_addr + sym->st_value : 0);
+      if (sym != NULL
+          && __builtin_expect (ELFW(ST_TYPE) (sym->st_info)
+              == STT_GNU_IFUNC, 0))
+        value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
     }
   else
     /* We already found the symbol.  The module (and therefore its load

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

end of thread, other threads:[~2013-11-20 19:57 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-16  0:44 [PATCH] 2nd round of Mips ifunc support Jack Carter
2013-11-16  1:11 ` Jack Carter
2013-11-18 12:29 ` Will Newton
2013-11-20 21:15   ` Jack Carter

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