[RFC][PATCH] MIPS ifunc for glibc

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

[RFC][PATCH] MIPS ifunc for glibc

Jack Carter

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

Note: This patch is for initial code review and has no test cases included.
They will be forth coming with submission for commit.

This is targeting o32. Patches for 64 bit, n32, MicroMips, Mips16,
and possibly vxworks will follow this patch. I have also not
yet implemented ifunc with local, hidden, or protected
symbol status.

Relocations using R_MIPS_IRELATIVE or referencting a symbol of type
STT_GNU_IFUNC have their affective address produced by executing the
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.

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

./ChangeLog

2013-08-16  Jack Carter  <[hidden email]>

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

ports/ChangeLog

2013-08-16  Jack Carter  <[hidden email]>

        * 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 f372271..78c347b 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1625,8 +1625,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..10acca8
--- /dev/null
+++ b/ports/sysdeps/mips/dl-irel.h
@@ -0,0 +1,62 @@
+/* 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%x), return(0x%x)\n",
+ addr,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 Elf32_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 dae938f..9293eb3 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 */
@@ -190,6 +191,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) \
@@ -437,7 +440,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;
@@ -642,6 +646,21 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
  break;
       }
 
+    case R_MIPS_IRELATIVE:
+      {
+ ElfW(Addr) value;
+
+ /* The address for the got entry storing the address for the */
+ /* ifunc routine is in this relocation. To get the address of */
+ /* the function to use on this machine the ifunc routine is run */
+ /* and its return value is the address which is then put back */
+ /* into the got entry. */
+ value = map->l_addr + *addr_field;
+ value = ((ElfW(Addr) (*) (void)) value) ();
+ *addr_field = value;
+ break;
+      }
+
 #if _MIPS_SIM == _ABI64
     case R_MIPS_64:
       /* For full compliance with the ELF64 ABI, one must precede the
@@ -671,7 +690,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
@@ -712,7 +732,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
@@ -739,8 +759,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 = ((ElfW(Addr) (*) (void)) value) ();  \
+ }  \
+      ref ? value : 0;  \
     })
 
   if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
@@ -810,6 +837,13 @@ 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)
+ {
+  ElfW(Addr) value;
+  value = map->l_addr + *got;
+  value = ((ElfW(Addr) (*) (void)) value) ();
+  *got = value;
+ }
       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..661dfc4 100644
--- a/ports/sysdeps/mips/dl-trampoline.c
+++ b/ports/sysdeps/mips/dl-trampoline.c
@@ -193,6 +193,9 @@ __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

Reply | Threaded
Open this post in threaded view
|

Re: [RFC][PATCH] MIPS ifunc for glibc

Joseph Myers
On Sat, 17 Aug 2013, Jack Carter wrote:

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

Do you have an ABI document specifying all the relevant semantics, for all
three ABIs?

Why aren't you consistently passing the HWCAP value to resolver functions
(it seems some places pass it but others don't)?  If you don't pass it
from the start, it could be problematic to change the ABI later to start
passing it, as applications' IFUNC resolvers wouldn't know if they are
being run with a glibc version that passes the HWCAP value to them or not.  
The ABI document should presumably specify what arguments get passed to
the resolver function....

> +    case R_MIPS_IRELATIVE:
> +      {
> + ElfW(Addr) value;
> +
> + /* The address for the got entry storing the address for the */
> + /* ifunc routine is in this relocation. To get the address of */
> + /* the function to use on this machine the ifunc routine is run */
> + /* and its return value is the address which is then put back */
> + /* into the got entry. */

Ordinary multi-line comment, please.

--
Joseph S. Myers
[hidden email]
Reply | Threaded
Open this post in threaded view
|

RE: [RFC][PATCH] MIPS ifunc for glibc

Jack Carter
Joseph,

Good comments. See inline.

Thanks for looking at this!

Jack
________________________________________
From: Joseph Myers [[hidden email]]
Sent: Sunday, August 18, 2013 1:38 PM
To: Jack Carter
Cc: [hidden email]
Subject: Re: [RFC][PATCH] MIPS ifunc for glibc

On Sat, 17 Aug 2013, Jack Carter wrote:

> > This is the initial MIPS ifunc patch for glibc. This patch should be
> > applied in conjunction with the binutils patch.
>
> Do you have an ABI document specifying all the relevant semantics, for all
> three ABIs?

All three abis, o32, n32, 64 should behave the same. I have only tested o32 and thus this patch just vouches for that abi. The others were to follow in subsequent patches. This is what I had in the accompanying binutils patch:

 All ifunc functions are represented in .iplt section stubs and
in the .igot.plt section table. Each igot.plt entry has an
R_MIPS_IRELOCATE relocation record against it with the initial
igot entry having link time address of the ifunc routine and
after the relocation action, the final runtime target routine
address.

The only change to the traditional MIPS GOT was to use the .iplt stub
address for the defined ifunc function instead of the function address.
This should allow seamless multigot support

***************************************
If an iplt is needed:

        Generate iplt stubs (.iplt)
        Generate igot table (.igot.plt)
        Generate IRELATIVE relocations for igot

Iplts are needed for a.outs only.

Non-shared a.outs:

        Always goes through the iplt with IRELATIVE relocations against
        the igot.

Shared a.outs (both fPIC and not):

        All a.out references are direct with got entries containing ifunc
        addresses that have IRELATIVE relocations against them. The dynamic
        linker will catch the IRELATIVE relocations and do the fixup based on
        the GOT contents
       
        Dso references are through the iplt with IRELATIVE relocations
        against the igot and the dynsym entries being the iplt address
        and the symbol type changed STT_FUNC. The dynamic linker will update
        the igot table in the a.out in the same manner as it does the GOT.

Dsos:

        No iplt, igot or IRELATIVE relocations.
       
        The GOT and .dynsym will have the ifunc values and the dynsym type
        will remain STT_GNU_IFUNC. The dynamic linker will detect that
        the symbol is an STT_GNU_IFUNC and do the fixup based on the defining
        dynsym address.

In all cases the contents of the igot is the ifunc address with an
R_MIPS_IRELATIVE relocation against it.

In all cases the contents of the got for the ifunc functions are the ifunc
addresses.

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

Is this enough? If not, could you point me to an example I could model after?

> Why aren't you consistently passing the HWCAP value to resolver functions
> (it seems some places pass it but others don't)?  If you don't pass it
> from the start, it could be problematic to change the ABI later to start
> passing it, as applications' IFUNC resolvers wouldn't know if they are
> being run with a glibc version that passes the HWCAP value to them or not.
> The ABI document should presumably specify what arguments get passed to
> the resolver function....

I am not sure what you mean by passing the HWCAP value to the resolver. The resolver contains logic that determines the HWCAP and from that picks the actual function that is most appropriate for the hardware being run on, returning the address to said function.

Does the rough ABI I gave above answer your question?

If I missed any conditions, could you be specific?

> > +    case R_MIPS_IRELATIVE:
> > +      {
> > +     ElfW(Addr) value;
> > +
> > +     /* The address for the got entry storing the address for the */
> > +     /* ifunc routine is in this relocation. To get the address of */
> > +     /* the function to use on this machine the ifunc routine is run */
> > +     /* and its return value is the address which is then put back */
> > +     /* into the got entry. */

> Ordinary multi-line comment, please.

This was just missed. Thanks for catching it.

--
Joseph S. Myers
[hidden email]

Reply | Threaded
Open this post in threaded view
|

RE: [RFC][PATCH] MIPS ifunc for glibc

Joseph Myers
On Tue, 20 Aug 2013, Jack Carter wrote:

> Is this enough? If not, could you point me to an example I could model after?

What you have doesn't really look like an ABI document at all; it looks
like an implementation sketch.  I don't know what you mean by "a.outs" at
all, let alone "Non-shared a.outs" and "Shared a.outs".  Please use the
appropriate terminology from the gABI and existing psABI documents.  For
example, if you mean ET_EXEC (which of course excludes PIEs, which are
ET_DYN objects that can also be executed directory), then say so.  If you
mean a particular subset of ET_EXEC objects, be clear about what subset
(again, in standard terms).  If you mean some ET_EXEC and some ET_DYN
objects, again, make that clear.

The specification should ideally be sufficient for someone to write an
interoperable static or dynamic linker without needing to refer to an
existing implementation.

As an example, you could always look at the existing MIPS o32 ABI document
(from SCO, last updated 1996).  Like many such documents, it is of limited
current value for reliably describing any ABI in current use.  But it
indicates the sort of level of detail in such specifications, and thinking
in terms of what text should be inserted where in the document is a good
starting point for thinking about how to specify an ABI extension.  Or see
the non-PIC ABI specification
<http://gcc.gnu.org/ml/gcc/2008-07/txt00000.txt>.

Among other things, the specification needs to state the values of any new
relocations....  (Your text mentions both R_MIPS_IRELOCATE and
R_MIPS_IRELATIVE but never gives them numeric values.)

> > Why aren't you consistently passing the HWCAP value to resolver functions
> > (it seems some places pass it but others don't)?  If you don't pass it
> > from the start, it could be problematic to change the ABI later to start
> > passing it, as applications' IFUNC resolvers wouldn't know if they are
> > being run with a glibc version that passes the HWCAP value to them or not.
> > The ABI document should presumably specify what arguments get passed to
> > the resolver function....
>
> I am not sure what you mean by passing the HWCAP value to the resolver.
> The resolver contains logic that determines the HWCAP and from that
> picks the actual function that is most appropriate for the hardware
> being run on, returning the address to said function.

In elf_ifunc_invoke, you do

> +  return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));

but in other places you do e.g.

> + value = ((ElfW(Addr) (*) (void)) value) ();

and there is no apparent reason for the difference, or any comments
explaining why it's OK not to pass the argument in some cases - how you
can know that the resolver functions don't need the argument when called
in those cases.

> Does the rough ABI I gave above answer your question?

No.  It doesn't seem to say anything about what the type of the resolver
function is and the significance of its arguments / return value.  
(Regarding the return value, I guess it should also be explicit how
addresses of MIPS16 / microMIPS functions are returned.)

--
Joseph S. Myers
[hidden email]