]> 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 c10b1e56370e6a45ef93aef3bfd318295ff83593..1e3ef254985de17f1ecd214e8bef8957f918985e 100644 (file)
@@ -1,5 +1,5 @@
 /* Machine-dependent ELF dynamic relocation inline functions.  i386 version.
-   Copyright (C) 1995-2014 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
@@ -25,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))
@@ -34,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))
@@ -59,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.  */
 
@@ -99,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)
     {
@@ -125,7 +98,9 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
         end in this function.  */
       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))
@@ -136,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;
@@ -182,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\
@@ -207,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@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\
@@ -238,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
@@ -261,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)
 {
@@ -336,14 +329,35 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
       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)
        {
@@ -456,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;
@@ -501,12 +515,12 @@ 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))
@@ -601,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;
@@ -663,7 +677,8 @@ elf_machine_lazy_rel (struct link_map *map,
   /* Check for unexpected PLT reloc type.  */
   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