]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/i386/dl-machine.h
x86: Check IFUNC definition in unrelocated executable [BZ #20019]
[thirdparty/glibc.git] / sysdeps / i386 / dl-machine.h
index 9469a2b5d3c365ad8414915c1a77ee458f991db2..1e3ef254985de17f1ecd214e8bef8957f918985e 100644 (file)
@@ -1,5 +1,5 @@
 /* Machine-dependent ELF dynamic relocation inline functions.  i386 version.
-   Copyright (C) 1995-2005, 2006, 2009, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1995-2018 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
@@ -13,9 +13,8 @@
    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, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifndef dl_machine_h
 #define dl_machine_h
@@ -26,6 +25,7 @@
 #include <sysdep.h>
 #include <tls.h>
 #include <dl-tlsdesc.h>
+#include <cpu-features.c>
 
 /* Return nonzero iff ELF header is compatible with the running host.  */
 static inline int __attribute__ ((unused))
@@ -35,8 +35,6 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
 }
 
 
-#ifdef PI_STATIC_AND_HIDDEN
-
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT, a special entry that is never relocated.  */
 static inline Elf32_Addr __attribute__ ((unused, const))
@@ -60,37 +58,6 @@ elf_machine_load_address (void)
   return (Elf32_Addr) &bygotoff - elf_machine_dynamic ();
 }
 
-#else  /* Without .hidden support, we can't compile the code above.  */
-
-/* Return the link-time address of _DYNAMIC.  Conveniently, this is the
-   first element of the GOT.  This must be inlined in a function which
-   uses global data.  */
-static inline Elf32_Addr __attribute__ ((unused))
-elf_machine_dynamic (void)
-{
-  register Elf32_Addr *got asm ("%ebx");
-  return *got;
-}
-
-
-/* Return the run-time load address of the shared object.  */
-static inline Elf32_Addr __attribute__ ((unused))
-elf_machine_load_address (void)
-{
-  /* It doesn't matter what variable this is, the reference never makes
-     it to assembly.  We need a dummy reference to some global variable
-     via the GOT to make sure the compiler initialized %ebx in time.  */
-  extern int _dl_argc;
-  Elf32_Addr addr;
-  asm ("leal _dl_start@GOTOFF(%%ebx), %0\n"
-       "subl _dl_start@GOT(%%ebx), %0"
-       : "=r" (addr) : "m" (_dl_argc) : "cc");
-  return addr;
-}
-
-#endif
-
-
 /* Set up the loaded object described by L so its unrelocated PLT
    entries will jump to the on-demand fixup code in dl-runtime.c.  */
 
@@ -100,6 +67,11 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
   Elf32_Addr *got;
   extern void _dl_runtime_resolve (Elf32_Word) attribute_hidden;
   extern void _dl_runtime_profile (Elf32_Word) attribute_hidden;
+  extern void _dl_runtime_resolve_shstk (Elf32_Word) attribute_hidden;
+  extern void _dl_runtime_profile_shstk (Elf32_Word) attribute_hidden;
+  /* Check if SHSTK is enabled by kernel.  */
+  bool shstk_enabled
+    = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0;
 
   if (l->l_info[DT_JMPREL] && lazy)
     {
@@ -124,9 +96,11 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
         to intercept the calls to collect information.  In this case we
         don't store the address in the GOT so that all future calls also
         end in this function.  */
-      if (__builtin_expect (profile, 0))
+      if (__glibc_unlikely (profile))
        {
-         got[2] = (Elf32_Addr) &_dl_runtime_profile;
+         got[2] = (shstk_enabled
+                   ? (Elf32_Addr) &_dl_runtime_profile_shstk
+                   : (Elf32_Addr) &_dl_runtime_profile);
 
          if (GLRO(dl_profile) != NULL
              && _dl_name_match_p (GLRO(dl_profile), l))
@@ -137,7 +111,9 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
       else
        /* This function will get called to fix up the GOT entry indicated by
           the offset on the stack, and then jump to the resolved address.  */
-       got[2] = (Elf32_Addr) &_dl_runtime_resolve;
+       got[2] = (shstk_enabled
+                 ? (Elf32_Addr) &_dl_runtime_resolve_shstk
+                 : (Elf32_Addr) &_dl_runtime_resolve);
     }
 
   return lazy;
@@ -145,18 +121,16 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
 
 #ifdef IN_DL_RUNTIME
 
-# if !defined PROF && !__BOUNDED_POINTERS__
+# ifndef PROF
 /* We add a declaration of this function here so that in dl-runtime.c
    the ELF_MACHINE_RUNTIME_TRAMPOLINE macro really can pass the parameters
    in registers.
 
    We cannot use this scheme for profiling because the _mcount call
    destroys the passed register information.  */
-/* GKM FIXME: Fix trampoline to pass bounds so we can do
-   without the `__unbounded' qualifier.  */
 #define ARCH_FIXUP_ATTRIBUTE __attribute__ ((regparm (3), stdcall, unused))
 
-extern ElfW(Addr) _dl_fixup (struct link_map *__unbounded l,
+extern ElfW(Addr) _dl_fixup (struct link_map *l,
                             ElfW(Word) reloc_offset)
      ARCH_FIXUP_ATTRIBUTE;
 extern ElfW(Addr) _dl_profile_fixup (struct link_map *l,
@@ -185,9 +159,11 @@ extern ElfW(Addr) _dl_profile_fixup (struct link_map *l,
 .globl _start\n\
 .globl _dl_start_user\n\
 _start:\n\
-       # Note that _dl_start gets the parameter in %eax.\n\
        movl %esp, %eax\n\
+        subl $12, %esp\n\
+        pushl %eax\n\
        call _dl_start\n\
+        addl $16, %esp\n\
 _dl_start_user:\n\
        # Save the user entry point address in %edi.\n\
        movl %eax, %edi\n\
@@ -210,25 +186,28 @@ _dl_start_user:\n\
        # switch stacks if it moves these contents over.\n\
 " RTLD_START_SPECIAL_INIT "\n\
        # Load the parameters again.\n\
-       # (eax, edx, ecx, *--esp) = (_dl_loaded, argc, argv, envp)\n\
+       # (eax, edx, ecx, esi) = (_dl_loaded, argc, argv, envp)\n\
        movl _rtld_local@GOTOFF(%ebx), %eax\n\
        leal 8(%esp,%edx,4), %esi\n\
        leal 4(%esp), %ecx\n\
        movl %esp, %ebp\n\
        # Make sure _dl_init is run with 16 byte aligned stack.\n\
        andl $-16, %esp\n\
-       pushl %eax\n\
-       pushl %eax\n\
+        subl $12, %esp\n\
        pushl %ebp\n\
+        # Arguments for _dl_init.\n\
        pushl %esi\n\
+       pushl %ecx\n\
+       pushl %edx\n\
+       pushl %eax\n\
        # Clear %ebp, so that even constructors have terminated backchain.\n\
        xorl %ebp, %ebp\n\
        # Call the function to run the initializers.\n\
-       call _dl_init_internal@PLT\n\
+       call _dl_init\n\
        # Pass our finalizer function to the user in %edx, as per ELF ABI.\n\
        leal _dl_fini@GOTOFF(%ebx), %edx\n\
        # Restore %esp _start expects.\n\
-       movl (%esp), %esp\n\
+       movl 16(%esp), %esp\n\
        # Jump to the user's entry point.\n\
        jmp *%edi\n\
        .previous\n\
@@ -241,14 +220,18 @@ _dl_start_user:\n\
 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
    TLS variable, so undefined references should not be allowed to
    define the value.
-   ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
-   of the main executable's symbols, as for a COPY reloc.  */
+   ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one
+   of the main executable's symbols, as for a COPY reloc.
+   ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA iff TYPE describes relocation may
+   against protected data whose address be external due to copy relocation.
+ */
 # define elf_machine_type_class(type) \
   ((((type) == R_386_JMP_SLOT || (type) == R_386_TLS_DTPMOD32                \
      || (type) == R_386_TLS_DTPOFF32 || (type) == R_386_TLS_TPOFF32          \
      || (type) == R_386_TLS_TPOFF || (type) == R_386_TLS_DESC)               \
     * ELF_RTYPE_CLASS_PLT)                                                   \
-   | (((type) == R_386_COPY) * ELF_RTYPE_CLASS_COPY))
+   | (((type) == R_386_COPY) * ELF_RTYPE_CLASS_COPY)                         \
+   | (((type) == R_386_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA))
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_386_JMP_SLOT
@@ -264,13 +247,20 @@ _dl_start_user:\n\
 static inline void __attribute__ ((unused))
 dl_platform_init (void)
 {
+#if IS_IN (rtld)
+  /* init_cpu_features has been called early from __libc_start_main in
+     static executable.  */
+  init_cpu_features (&GLRO(dl_x86_cpu_features));
+#else
   if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
     /* Avoid an empty string which would disturb us.  */
     GLRO(dl_platform) = NULL;
+#endif
 }
 
 static inline Elf32_Addr
 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
+                      const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
                       const Elf32_Rel *reloc,
                       Elf32_Addr *reloc_addr, Elf32_Addr value)
 {
@@ -295,6 +285,7 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc,
 /* The i386 never uses Elf32_Rela relocations for the dynamic linker.
    Prelinked libraries may use Elf32_Rela though.  */
 #define ELF_MACHINE_NO_RELA defined RTLD_BOOTSTRAP
+#define ELF_MACHINE_NO_REL 0
 
 #ifdef RESOLVE_MAP
 
@@ -311,7 +302,7 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
 
 # if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
-  if (__builtin_expect (r_type == R_386_RELATIVE, 0))
+  if (__glibc_unlikely (r_type == R_386_RELATIVE))
     {
 #  if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
       /* This is defined in rtld.c, but nowhere in the static libc.a;
@@ -328,25 +319,54 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
        *reloc_addr += map->l_addr;
     }
 #  ifndef RTLD_BOOTSTRAP
-  else if (__builtin_expect (r_type == R_386_NONE, 0))
+  else if (__glibc_unlikely (r_type == R_386_NONE))
     return;
 #  endif
   else
 # endif        /* !RTLD_BOOTSTRAP and have no -z combreloc */
     {
+# ifndef RTLD_BOOTSTRAP
       const Elf32_Sym *const refsym = sym;
+# endif
       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
-      Elf32_Addr value = sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value;
+      Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
 
       if (sym != NULL
-         && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC,
-                              0)
-         && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1)
-         && __builtin_expect (!skip_ifunc, 1))
-       value = ((Elf32_Addr (*) (void)) value) ();
+         && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
+         && __glibc_likely (sym->st_shndx != SHN_UNDEF)
+         && __glibc_likely (!skip_ifunc))
+       {
+# ifndef RTLD_BOOTSTRAP
+         if (sym_map != map
+             && !sym_map->l_relocated)
+           {
+             const char *strtab
+               = (const char *) D_PTR (map, l_info[DT_STRTAB]);
+             if (sym_map->l_type == lt_executable)
+               _dl_fatal_printf ("\
+%s: IFUNC symbol '%s' referenced in '%s' is defined in the executable \
+and creates an unsatisfiable circular dependency.\n",
+                                 RTLD_PROGNAME, strtab + refsym->st_name,
+                                 map->l_name);
+             else
+               _dl_error_printf ("\
+%s: Relink `%s' with `%s' for IFUNC symbol `%s'\n",
+                                 RTLD_PROGNAME, map->l_name,
+                                 sym_map->l_name,
+                                 strtab + refsym->st_name);
+           }
+# endif
+         value = ((Elf32_Addr (*) (void)) value) ();
+       }
 
       switch (r_type)
        {
+# ifndef RTLD_BOOTSTRAP
+       case R_386_SIZE32:
+         /* Set to symbol size plus addend.  */
+         *reloc_addr += sym->st_size;
+         break;
+# endif
        case R_386_GLOB_DAT:
        case R_386_JMP_SLOT:
          *reloc_addr = value;
@@ -450,8 +470,8 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
            /* This can happen in trace mode if an object could not be
               found.  */
            break;
-         if (__builtin_expect (sym->st_size > refsym->st_size, 0)
-             || (__builtin_expect (sym->st_size < refsym->st_size, 0)
+         if (__glibc_unlikely (sym->st_size > refsym->st_size)
+             || (__glibc_unlikely(sym->st_size < refsym->st_size)
                  && GLRO(dl_verbose)))
            {
              const char *strtab;
@@ -459,8 +479,7 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
              strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
              _dl_error_printf ("\
 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
-                               rtld_progname ?: "<program name unknown>",
-                               strtab + refsym->st_name);
+                               RTLD_PROGNAME, strtab + refsym->st_name);
            }
          memcpy (reloc_addr_arg, (void *) value,
                  MIN (sym->st_size, refsym->st_size));
@@ -496,16 +515,19 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
       const Elf32_Sym *const refsym = sym;
 #  endif
       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
-      Elf32_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value;
+      Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
 
       if (sym != NULL
-         && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1)
-         && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
-         && __builtin_expect (!skip_ifunc, 1))
+         && __glibc_likely (sym->st_shndx != SHN_UNDEF)
+         && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
+         && __glibc_likely (!skip_ifunc))
        value = ((Elf32_Addr (*) (void)) value) ();
 
       switch (ELF32_R_TYPE (reloc->r_info))
        {
+       case R_386_SIZE32:
+         /* Set to symbol size plus addend.  */
+         value = sym->st_size;
        case R_386_GLOB_DAT:
        case R_386_JMP_SLOT:
        case R_386_32:
@@ -593,8 +615,8 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
            /* This can happen in trace mode if an object could not be
               found.  */
            break;
-         if (__builtin_expect (sym->st_size > refsym->st_size, 0)
-             || (__builtin_expect (sym->st_size < refsym->st_size, 0)
+         if (__glibc_unlikely (sym->st_size > refsym->st_size)
+             || (__glibc_unlikely (sym->st_size < refsym->st_size)
                  && GLRO(dl_verbose)))
            {
              const char *strtab;
@@ -602,8 +624,7 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
              strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
              _dl_error_printf ("\
 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
-                               rtld_progname ?: "<program name unknown>",
-                               strtab + refsym->st_name);
+                               RTLD_PROGNAME, strtab + refsym->st_name);
            }
          memcpy (reloc_addr_arg, (void *) value,
                  MIN (sym->st_size, refsym->st_size));
@@ -654,15 +675,16 @@ elf_machine_lazy_rel (struct link_map *map,
   Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
   /* Check for unexpected PLT reloc type.  */
-  if (__builtin_expect (r_type == R_386_JMP_SLOT, 1))
+  if (__glibc_likely (r_type == R_386_JMP_SLOT))
     {
-      if (__builtin_expect (map->l_mach.plt, 0) == 0)
+      /* Prelink has been deprecated.  */
+      if (__glibc_likely (map->l_mach.plt == 0))
        *reloc_addr += l_addr;
       else
        *reloc_addr = (map->l_mach.plt
                       + (((Elf32_Addr) reloc_addr) - map->l_mach.gotplt) * 4);
     }
-  else if (__builtin_expect (r_type == R_386_TLS_DESC, 1))
+  else if (__glibc_likely (r_type == R_386_TLS_DESC))
     {
       struct tlsdesc volatile * __attribute__((__unused__)) td =
        (struct tlsdesc volatile *)reloc_addr;
@@ -709,10 +731,10 @@ elf_machine_lazy_rel (struct link_map *map,
 # endif
        }
     }
-  else if (__builtin_expect (r_type == R_386_IRELATIVE, 0))
+  else if (__glibc_unlikely (r_type == R_386_IRELATIVE))
     {
       Elf32_Addr value = map->l_addr + *reloc_addr;
-      if (__builtin_expect (!skip_ifunc, 1))
+      if (__glibc_likely (!skip_ifunc))
        value = ((Elf32_Addr (*) (void)) value) ();
       *reloc_addr = value;
     }
@@ -730,9 +752,9 @@ elf_machine_lazy_rela (struct link_map *map,
 {
   Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
-  if (__builtin_expect (r_type == R_386_JMP_SLOT, 1))
+  if (__glibc_likely (r_type == R_386_JMP_SLOT))
     ;
-  else if (__builtin_expect (r_type == R_386_TLS_DESC, 1))
+  else if (__glibc_likely (r_type == R_386_TLS_DESC))
     {
       struct tlsdesc volatile * __attribute__((__unused__)) td =
        (struct tlsdesc volatile *)reloc_addr;
@@ -740,10 +762,10 @@ elf_machine_lazy_rela (struct link_map *map,
       td->arg = (void*)reloc;
       td->entry = _dl_tlsdesc_resolve_rela;
     }
-  else if (__builtin_expect (r_type == R_386_IRELATIVE, 0))
+  else if (__glibc_unlikely (r_type == R_386_IRELATIVE))
     {
       Elf32_Addr value = map->l_addr + reloc->r_addend;
-      if (__builtin_expect (!skip_ifunc, 1))
+      if (__glibc_likely (!skip_ifunc))
        value = ((Elf32_Addr (*) (void)) value) ();
       *reloc_addr = value;
     }