Back to index

glibc  2.9
dl-machine.h
Go to the documentation of this file.
00001 /* Machine-dependent ELF dynamic relocation inline functions.  x86-64 version.
00002    Copyright (C) 2001-2005, 2006 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Andreas Jaeger <aj@suse.de>.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #ifndef dl_machine_h
00022 #define dl_machine_h
00023 
00024 #define ELF_MACHINE_NAME "x86_64"
00025 
00026 #include <sys/param.h>
00027 #include <sysdep.h>
00028 #include <tls.h>
00029 #include <dl-tlsdesc.h>
00030 
00031 /* Return nonzero iff ELF header is compatible with the running host.  */
00032 static inline int __attribute__ ((unused))
00033 elf_machine_matches_host (const Elf64_Ehdr *ehdr)
00034 {
00035   return ehdr->e_machine == EM_X86_64;
00036 }
00037 
00038 
00039 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
00040    first element of the GOT.  This must be inlined in a function which
00041    uses global data.  */
00042 static inline Elf64_Addr __attribute__ ((unused))
00043 elf_machine_dynamic (void)
00044 {
00045   Elf64_Addr addr;
00046 
00047   /* This works because we have our GOT address available in the small PIC
00048      model.  */
00049   addr = (Elf64_Addr) &_DYNAMIC;
00050 
00051   return addr;
00052 }
00053 
00054 
00055 /* Return the run-time load address of the shared object.  */
00056 static inline Elf64_Addr __attribute__ ((unused))
00057 elf_machine_load_address (void)
00058 {
00059   Elf64_Addr addr;
00060 
00061   /* The easy way is just the same as on x86:
00062        leaq _dl_start, %0
00063        leaq _dl_start(%%rip), %1
00064        subq %0, %1
00065      but this does not work with binutils since we then have
00066      a R_X86_64_32S relocation in a shared lib.
00067 
00068      Instead we store the address of _dl_start in the data section
00069      and compare it with the current value that we can get via
00070      an RIP relative addressing mode.  Note that this is the address
00071      of _dl_start before any relocation performed at runtime.  In case
00072      the binary is prelinked the resulting "address" is actually a
00073      load offset which is zero if the binary was loaded at the address
00074      it is prelinked for.  */
00075 
00076   asm ("leaq _dl_start(%%rip), %0\n\t"
00077        "subq 1f(%%rip), %0\n\t"
00078        ".section\t.data.rel.ro\n"
00079        "1:\t.quad _dl_start\n\t"
00080        ".previous\n\t"
00081        : "=r" (addr) : : "cc");
00082 
00083   return addr;
00084 }
00085 
00086 /* Set up the loaded object described by L so its unrelocated PLT
00087    entries will jump to the on-demand fixup code in dl-runtime.c.  */
00088 
00089 static inline int __attribute__ ((unused, always_inline))
00090 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
00091 {
00092   Elf64_Addr *got;
00093   extern void _dl_runtime_resolve (Elf64_Word) attribute_hidden;
00094   extern void _dl_runtime_profile (Elf64_Word) attribute_hidden;
00095 
00096   if (l->l_info[DT_JMPREL] && lazy)
00097     {
00098       /* The GOT entries for functions in the PLT have not yet been filled
00099         in.  Their initial contents will arrange when called to push an
00100         offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1],
00101         and then jump to _GLOBAL_OFFSET_TABLE[2].  */
00102       got = (Elf64_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
00103       /* If a library is prelinked but we have to relocate anyway,
00104         we have to be able to undo the prelinking of .got.plt.
00105         The prelinker saved us here address of .plt + 0x16.  */
00106       if (got[1])
00107        {
00108          l->l_mach.plt = got[1] + l->l_addr;
00109          l->l_mach.gotplt = (Elf64_Addr) &got[3];
00110        }
00111       got[1] = (Elf64_Addr) l;     /* Identify this shared object.  */
00112 
00113       /* The got[2] entry contains the address of a function which gets
00114         called to get the address of a so far unresolved function and
00115         jump to it.  The profiling extension of the dynamic linker allows
00116         to intercept the calls to collect information.  In this case we
00117         don't store the address in the GOT so that all future calls also
00118         end in this function.  */
00119       if (__builtin_expect (profile, 0))
00120        {
00121          got[2] = (Elf64_Addr) &_dl_runtime_profile;
00122 
00123          if (GLRO(dl_profile) != NULL
00124              && _dl_name_match_p (GLRO(dl_profile), l))
00125            /* This is the object we are looking for.  Say that we really
00126               want profiling and the timers are started.  */
00127            GL(dl_profile_map) = l;
00128        }
00129       else
00130        /* This function will get called to fix up the GOT entry indicated by
00131           the offset on the stack, and then jump to the resolved address.  */
00132        got[2] = (Elf64_Addr) &_dl_runtime_resolve;
00133     }
00134 
00135   if (l->l_info[ADDRIDX (DT_TLSDESC_GOT)] && lazy)
00136     *(Elf64_Addr*)(D_PTR (l, l_info[ADDRIDX (DT_TLSDESC_GOT)]) + l->l_addr)
00137       = (Elf64_Addr) &_dl_tlsdesc_resolve_rela;
00138 
00139   return lazy;
00140 }
00141 
00142 /* Initial entry point code for the dynamic linker.
00143    The C function `_dl_start' is the real entry point;
00144    its return value is the user program's entry point.  */
00145 #define RTLD_START asm ("\n\
00146 .text\n\
00147        .align 16\n\
00148 .globl _start\n\
00149 .globl _dl_start_user\n\
00150 _start:\n\
00151        movq %rsp, %rdi\n\
00152        call _dl_start\n\
00153 _dl_start_user:\n\
00154        # Save the user entry point address in %r12.\n\
00155        movq %rax, %r12\n\
00156        # See if we were run as a command with the executable file\n\
00157        # name as an extra leading argument.\n\
00158        movl _dl_skip_args(%rip), %eax\n\
00159        # Pop the original argument count.\n\
00160        popq %rdx\n\
00161        # Adjust the stack pointer to skip _dl_skip_args words.\n\
00162        leaq (%rsp,%rax,8), %rsp\n\
00163        # Subtract _dl_skip_args from argc.\n\
00164        subl %eax, %edx\n\
00165        # Push argc back on the stack.\n\
00166        pushq %rdx\n\
00167        # Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env)\n\
00168        # argc -> rsi\n\
00169        movq %rdx, %rsi\n\
00170        # Save %rsp value in %r13.\n\
00171        movq %rsp, %r13\n\
00172        # And align stack for the _dl_init_internal call. \n\
00173        andq $-16, %rsp\n\
00174        # _dl_loaded -> rdi\n\
00175        movq _rtld_local(%rip), %rdi\n\
00176        # env -> rcx\n\
00177        leaq 16(%r13,%rdx,8), %rcx\n\
00178        # argv -> rdx\n\
00179        leaq 8(%r13), %rdx\n\
00180        # Clear %rbp to mark outermost frame obviously even for constructors.\n\
00181        xorl %ebp, %ebp\n\
00182        # Call the function to run the initializers.\n\
00183        call _dl_init_internal@PLT\n\
00184        # Pass our finalizer function to the user in %rdx, as per ELF ABI.\n\
00185        leaq _dl_fini(%rip), %rdx\n\
00186        # And make sure %rsp points to argc stored on the stack.\n\
00187        movq %r13, %rsp\n\
00188        # Jump to the user's entry point.\n\
00189        jmp *%r12\n\
00190 .previous\n\
00191 ");
00192 
00193 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
00194    TLS variable, so undefined references should not be allowed to
00195    define the value.
00196    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
00197    of the main executable's symbols, as for a COPY reloc.  */
00198 #if !defined RTLD_BOOTSTRAP || USE___THREAD
00199 # define elf_machine_type_class(type)                                       \
00200   ((((type) == R_X86_64_JUMP_SLOT                                    \
00201      || (type) == R_X86_64_DTPMOD64                                         \
00202      || (type) == R_X86_64_DTPOFF64                                         \
00203      || (type) == R_X86_64_TPOFF64                                   \
00204      || (type) == R_X86_64_TLSDESC)                                         \
00205     * ELF_RTYPE_CLASS_PLT)                                           \
00206    | (((type) == R_X86_64_COPY) * ELF_RTYPE_CLASS_COPY))
00207 #else
00208 # define elf_machine_type_class(type) \
00209   ((((type) == R_X86_64_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT) \
00210    | (((type) == R_X86_64_COPY) * ELF_RTYPE_CLASS_COPY))
00211 #endif
00212 
00213 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
00214 #define ELF_MACHINE_JMP_SLOT       R_X86_64_JUMP_SLOT
00215 
00216 /* The x86-64 never uses Elf64_Rel relocations.  */
00217 #define ELF_MACHINE_NO_REL 1
00218 
00219 /* We define an initialization functions.  This is called very early in
00220    _dl_sysdep_start.  */
00221 #define DL_PLATFORM_INIT dl_platform_init ()
00222 
00223 static inline void __attribute__ ((unused))
00224 dl_platform_init (void)
00225 {
00226   if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
00227     /* Avoid an empty string which would disturb us.  */
00228     GLRO(dl_platform) = NULL;
00229 }
00230 
00231 static inline Elf64_Addr
00232 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
00233                      const Elf64_Rela *reloc,
00234                      Elf64_Addr *reloc_addr, Elf64_Addr value)
00235 {
00236   return *reloc_addr = value;
00237 }
00238 
00239 /* Return the final value of a plt relocation.  On x86-64 the
00240    JUMP_SLOT relocation ignores the addend. */
00241 static inline Elf64_Addr
00242 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
00243                      Elf64_Addr value)
00244 {
00245   return value;
00246 }
00247 
00248 
00249 /* Names of the architecture-specific auditing callback functions.  */
00250 #define ARCH_LA_PLTENTER x86_64_gnu_pltenter
00251 #define ARCH_LA_PLTEXIT x86_64_gnu_pltexit
00252 
00253 #endif /* !dl_machine_h */
00254 
00255 #ifdef RESOLVE_MAP
00256 
00257 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
00258    MAP is the object containing the reloc.  */
00259 
00260 auto inline void
00261 __attribute__ ((always_inline))
00262 elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
00263                 const Elf64_Sym *sym, const struct r_found_version *version,
00264                 void *const reloc_addr_arg)
00265 {
00266   Elf64_Addr *const reloc_addr = reloc_addr_arg;
00267   const unsigned long int r_type = ELF64_R_TYPE (reloc->r_info);
00268 
00269 #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
00270   if (__builtin_expect (r_type == R_X86_64_RELATIVE, 0))
00271     {
00272 # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
00273       /* This is defined in rtld.c, but nowhere in the static libc.a;
00274         make the reference weak so static programs can still link.
00275         This declaration cannot be done when compiling rtld.c
00276         (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
00277         common defn for _dl_rtld_map, which is incompatible with a
00278         weak decl in the same file.  */
00279 #  ifndef SHARED
00280       weak_extern (GL(dl_rtld_map));
00281 #  endif
00282       if (map != &GL(dl_rtld_map)) /* Already done in rtld itself.  */
00283 # endif
00284        *reloc_addr = map->l_addr + reloc->r_addend;
00285     }
00286   else
00287 #endif
00288   if (__builtin_expect (r_type == R_X86_64_NONE, 0))
00289     return;
00290   else
00291     {
00292 #ifndef RTLD_BOOTSTRAP
00293       const Elf64_Sym *const refsym = sym;
00294 #endif
00295       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
00296       Elf64_Addr value = (sym == NULL ? 0
00297                        : (Elf64_Addr) sym_map->l_addr + sym->st_value);
00298 
00299 #if defined RTLD_BOOTSTRAP && !USE___THREAD
00300       assert (r_type == R_X86_64_GLOB_DAT || r_type == R_X86_64_JUMP_SLOT);
00301       *reloc_addr = value + reloc->r_addend;
00302 #else
00303       switch (r_type)
00304        {
00305        case R_X86_64_GLOB_DAT:
00306        case R_X86_64_JUMP_SLOT:
00307          *reloc_addr = value + reloc->r_addend;
00308          break;
00309 
00310 #ifndef RESOLVE_CONFLICT_FIND_MAP
00311        case R_X86_64_DTPMOD64:
00312 # ifdef RTLD_BOOTSTRAP
00313          /* During startup the dynamic linker is always the module
00314             with index 1.
00315             XXX If this relocation is necessary move before RESOLVE
00316             call.  */
00317          *reloc_addr = 1;
00318 # else
00319          /* Get the information from the link map returned by the
00320             resolve function.  */
00321          if (sym_map != NULL)
00322            *reloc_addr = sym_map->l_tls_modid;
00323 # endif
00324          break;
00325        case R_X86_64_DTPOFF64:
00326 # ifndef RTLD_BOOTSTRAP
00327          /* During relocation all TLS symbols are defined and used.
00328             Therefore the offset is already correct.  */
00329          if (sym != NULL)
00330            *reloc_addr = sym->st_value + reloc->r_addend;
00331 # endif
00332          break;
00333        case R_X86_64_TLSDESC:
00334          {
00335            struct tlsdesc volatile *td =
00336              (struct tlsdesc volatile *)reloc_addr;
00337 
00338 # ifndef RTLD_BOOTSTRAP
00339            if (! sym)
00340              {
00341               td->arg = (void*)reloc->r_addend;
00342               td->entry = _dl_tlsdesc_undefweak;
00343              }
00344            else
00345 # endif
00346              {
00347 # ifndef RTLD_BOOTSTRAP
00348 #  ifndef SHARED
00349               CHECK_STATIC_TLS (map, sym_map);
00350 #  else
00351               if (!TRY_STATIC_TLS (map, sym_map))
00352                 {
00353                   td->arg = _dl_make_tlsdesc_dynamic
00354                     (sym_map, sym->st_value + reloc->r_addend);
00355                   td->entry = _dl_tlsdesc_dynamic;
00356                 }
00357               else
00358 #  endif
00359 # endif
00360                 {
00361                   td->arg = (void*)(sym->st_value - sym_map->l_tls_offset
00362                                   + reloc->r_addend);
00363                   td->entry = _dl_tlsdesc_return;
00364                 }
00365              }
00366            break;
00367          }
00368        case R_X86_64_TPOFF64:
00369          /* The offset is negative, forward from the thread pointer.  */
00370 # ifndef RTLD_BOOTSTRAP
00371          if (sym != NULL)
00372 # endif
00373            {
00374 # ifndef RTLD_BOOTSTRAP
00375              CHECK_STATIC_TLS (map, sym_map);
00376 # endif
00377              /* We know the offset of the object the symbol is contained in.
00378                It is a negative value which will be added to the
00379                thread pointer.  */
00380              *reloc_addr = (sym->st_value + reloc->r_addend
00381                           - sym_map->l_tls_offset);
00382            }
00383          break;
00384 #endif
00385 
00386 #ifndef RTLD_BOOTSTRAP
00387        case R_X86_64_64:
00388          *reloc_addr = value + reloc->r_addend;
00389          break;
00390        case R_X86_64_32:
00391          *(unsigned int *) reloc_addr = value + reloc->r_addend;
00392          if (value + reloc->r_addend > UINT_MAX)
00393            {
00394              const char *strtab;
00395 
00396              strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
00397 
00398              _dl_error_printf ("\
00399 %s: Symbol `%s' causes overflow in R_X86_64_32 relocation\n",
00400                             rtld_progname ?: "<program name unknown>",
00401                             strtab + refsym->st_name);
00402            }
00403          break;
00404 # ifndef RESOLVE_CONFLICT_FIND_MAP
00405          /* Not needed for dl-conflict.c.  */
00406        case R_X86_64_PC32:
00407          *(unsigned int *) reloc_addr = value + reloc->r_addend
00408            - (Elf64_Addr) reloc_addr;
00409          if (value + reloc->r_addend - (Elf64_Addr) reloc_addr
00410              != (int)(value + reloc->r_addend - (Elf64_Addr) reloc_addr))
00411            {
00412              const char *strtab;
00413 
00414              strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
00415 
00416              _dl_error_printf ("\
00417 %s: Symbol `%s' causes overflow in R_X86_64_PC32 relocation\n",
00418                             rtld_progname ?: "<program name unknown>",
00419                             strtab + refsym->st_name);
00420            }
00421          break;
00422        case R_X86_64_COPY:
00423          if (sym == NULL)
00424            /* This can happen in trace mode if an object could not be
00425               found.  */
00426            break;
00427          if (__builtin_expect (sym->st_size > refsym->st_size, 0)
00428              || (__builtin_expect (sym->st_size < refsym->st_size, 0)
00429                 && GLRO(dl_verbose)))
00430            {
00431              const char *strtab;
00432 
00433              strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
00434              _dl_error_printf ("\
00435 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
00436                             rtld_progname ?: "<program name unknown>",
00437                             strtab + refsym->st_name);
00438            }
00439          memcpy (reloc_addr_arg, (void *) value,
00440                 MIN (sym->st_size, refsym->st_size));
00441          break;
00442 # endif
00443        default:
00444          _dl_reloc_bad_type (map, r_type, 0);
00445          break;
00446 #endif
00447        }
00448 #endif
00449     }
00450 }
00451 
00452 auto inline void
00453 __attribute ((always_inline))
00454 elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc,
00455                         void *const reloc_addr_arg)
00456 {
00457   Elf64_Addr *const reloc_addr = reloc_addr_arg;
00458   assert (ELF64_R_TYPE (reloc->r_info) == R_X86_64_RELATIVE);
00459   *reloc_addr = l_addr + reloc->r_addend;
00460 }
00461 
00462 auto inline void
00463 __attribute ((always_inline))
00464 elf_machine_lazy_rel (struct link_map *map,
00465                     Elf64_Addr l_addr, const Elf64_Rela *reloc)
00466 {
00467   Elf64_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
00468   const unsigned long int r_type = ELF64_R_TYPE (reloc->r_info);
00469 
00470   /* Check for unexpected PLT reloc type.  */
00471   if (__builtin_expect (r_type == R_X86_64_JUMP_SLOT, 1))
00472     {
00473       if (__builtin_expect (map->l_mach.plt, 0) == 0)
00474        *reloc_addr += l_addr;
00475       else
00476        *reloc_addr =
00477          map->l_mach.plt
00478          + (((Elf64_Addr) reloc_addr) - map->l_mach.gotplt) * 2;
00479     }
00480   else if (__builtin_expect (r_type == R_X86_64_TLSDESC, 1))
00481     {
00482       struct tlsdesc volatile * __attribute__((__unused__)) td =
00483        (struct tlsdesc volatile *)reloc_addr;
00484 
00485       td->arg = (void*)reloc;
00486       td->entry = (void*)(D_PTR (map, l_info[ADDRIDX (DT_TLSDESC_PLT)])
00487                        + map->l_addr);
00488     }
00489   else
00490     _dl_reloc_bad_type (map, r_type, 1);
00491 }
00492 
00493 #endif /* RESOLVE_MAP */