| /* Read the dynamic section at DYN and fill in INFO with indices DT_*. | 
 |    Copyright (C) 2012-2016 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/>.  */ | 
 |  | 
 | #include <assert.h> | 
 | #include <libc-internal.h> | 
 |  | 
 | #ifndef RESOLVE_MAP | 
 | static | 
 | #else | 
 | auto | 
 | #endif | 
 | inline void __attribute__ ((unused, always_inline)) | 
 | elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) | 
 | { | 
 |   ElfW(Dyn) *dyn = l->l_ld; | 
 |   ElfW(Dyn) **info; | 
 | #if __ELF_NATIVE_CLASS == 32 | 
 |   typedef Elf32_Word d_tag_utype; | 
 | #elif __ELF_NATIVE_CLASS == 64 | 
 |   typedef Elf64_Xword d_tag_utype; | 
 | #endif | 
 |  | 
 | #ifndef RTLD_BOOTSTRAP | 
 |   if (dyn == NULL) | 
 |     return; | 
 | #endif | 
 |  | 
 |   info = l->l_info; | 
 |  | 
 |   while (dyn->d_tag != DT_NULL) | 
 |     { | 
 |       if ((d_tag_utype) dyn->d_tag < DT_NUM) | 
 | 	info[dyn->d_tag] = dyn; | 
 |       else if (dyn->d_tag >= DT_LOPROC && | 
 | 	       dyn->d_tag < DT_LOPROC + DT_THISPROCNUM) | 
 | 	{ | 
 | 	  /* This does not violate the array bounds of l->l_info, but | 
 | 	     gcc 4.6 on sparc somehow does not see this.  */ | 
 | 	  DIAG_PUSH_NEEDS_COMMENT; | 
 | 	  DIAG_IGNORE_NEEDS_COMMENT (4.6, | 
 | 				     "-Warray-bounds"); | 
 | 	  info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn; | 
 | 	  DIAG_POP_NEEDS_COMMENT; | 
 | 	} | 
 |       else if ((d_tag_utype) DT_VERSIONTAGIDX (dyn->d_tag) < DT_VERSIONTAGNUM) | 
 | 	info[VERSYMIDX (dyn->d_tag)] = dyn; | 
 |       else if ((d_tag_utype) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM) | 
 | 	info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM | 
 | 	     + DT_VERSIONTAGNUM] = dyn; | 
 |       else if ((d_tag_utype) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM) | 
 | 	info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM | 
 | 	     + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn; | 
 |       else if ((d_tag_utype) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM) | 
 | 	info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM | 
 | 	     + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn; | 
 |       ++dyn; | 
 |     } | 
 |  | 
 | #define DL_RO_DYN_TEMP_CNT	8 | 
 |  | 
 | #ifndef DL_RO_DYN_SECTION | 
 |   /* Don't adjust .dynamic unnecessarily.  */ | 
 |   if (l->l_addr != 0) | 
 |     { | 
 |       ElfW(Addr) l_addr = l->l_addr; | 
 |       int cnt = 0; | 
 |  | 
 | # define ADJUST_DYN_INFO(tag) \ | 
 |       do								      \ | 
 | 	if (info[tag] != NULL)						      \ | 
 | 	  {								      \ | 
 | 	    if (temp)							      \ | 
 | 	      {								      \ | 
 | 		temp[cnt].d_tag = info[tag]->d_tag;			      \ | 
 | 		temp[cnt].d_un.d_ptr = info[tag]->d_un.d_ptr + l_addr;	      \ | 
 | 		info[tag] = temp + cnt++;				      \ | 
 | 	      }								      \ | 
 | 	    else							      \ | 
 | 	      info[tag]->d_un.d_ptr += l_addr;				      \ | 
 | 	  }								      \ | 
 |       while (0) | 
 |  | 
 |       ADJUST_DYN_INFO (DT_HASH); | 
 |       ADJUST_DYN_INFO (DT_PLTGOT); | 
 |       ADJUST_DYN_INFO (DT_STRTAB); | 
 |       ADJUST_DYN_INFO (DT_SYMTAB); | 
 | # if ! ELF_MACHINE_NO_RELA | 
 |       ADJUST_DYN_INFO (DT_RELA); | 
 | # endif | 
 | # if ! ELF_MACHINE_NO_REL | 
 |       ADJUST_DYN_INFO (DT_REL); | 
 | # endif | 
 |       ADJUST_DYN_INFO (DT_JMPREL); | 
 |       ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM)); | 
 |       ADJUST_DYN_INFO (DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM | 
 | 		       + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM); | 
 | # undef ADJUST_DYN_INFO | 
 |       assert (cnt <= DL_RO_DYN_TEMP_CNT); | 
 |     } | 
 | #endif | 
 |   if (info[DT_PLTREL] != NULL) | 
 |     { | 
 | #if ELF_MACHINE_NO_RELA | 
 |       assert (info[DT_PLTREL]->d_un.d_val == DT_REL); | 
 | #elif ELF_MACHINE_NO_REL | 
 |       assert (info[DT_PLTREL]->d_un.d_val == DT_RELA); | 
 | #else | 
 |       assert (info[DT_PLTREL]->d_un.d_val == DT_REL | 
 | 	      || info[DT_PLTREL]->d_un.d_val == DT_RELA); | 
 | #endif | 
 |     } | 
 | #if ! ELF_MACHINE_NO_RELA | 
 |   if (info[DT_RELA] != NULL) | 
 |     assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela))); | 
 | # endif | 
 | # if ! ELF_MACHINE_NO_REL | 
 |   if (info[DT_REL] != NULL) | 
 |     assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel))); | 
 | #endif | 
 | #ifdef RTLD_BOOTSTRAP | 
 |   /* Only the bind now flags are allowed.  */ | 
 |   assert (info[VERSYMIDX (DT_FLAGS_1)] == NULL | 
 | 	  || (info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val & ~DF_1_NOW) == 0); | 
 |   assert (info[DT_FLAGS] == NULL | 
 | 	  || (info[DT_FLAGS]->d_un.d_val & ~DF_BIND_NOW) == 0); | 
 |   /* Flags must not be set for ld.so.  */ | 
 |   assert (info[DT_RUNPATH] == NULL); | 
 |   assert (info[DT_RPATH] == NULL); | 
 | #else | 
 |   if (info[DT_FLAGS] != NULL) | 
 |     { | 
 |       /* Flags are used.  Translate to the old form where available. | 
 | 	 Since these l_info entries are only tested for NULL pointers it | 
 | 	 is ok if they point to the DT_FLAGS entry.  */ | 
 |       l->l_flags = info[DT_FLAGS]->d_un.d_val; | 
 |  | 
 |       if (l->l_flags & DF_SYMBOLIC) | 
 | 	info[DT_SYMBOLIC] = info[DT_FLAGS]; | 
 |       if (l->l_flags & DF_TEXTREL) | 
 | 	info[DT_TEXTREL] = info[DT_FLAGS]; | 
 |       if (l->l_flags & DF_BIND_NOW) | 
 | 	info[DT_BIND_NOW] = info[DT_FLAGS]; | 
 |     } | 
 |   if (info[VERSYMIDX (DT_FLAGS_1)] != NULL) | 
 |     { | 
 |       l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val; | 
 |  | 
 |       /* Only DT_1_SUPPORTED_MASK bits are supported, and we would like | 
 | 	 to assert this, but we can't. Users have been setting | 
 | 	 unsupported DF_1_* flags for a long time and glibc has ignored | 
 | 	 them. Therefore to avoid breaking existing applications the | 
 | 	 best we can do is add a warning during debugging with the | 
 | 	 intent of notifying the user of the problem.  */ | 
 |       if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0) | 
 | 	  && l->l_flags_1 & ~DT_1_SUPPORTED_MASK) | 
 | 	_dl_debug_printf ("\nWARNING: Unsupported flag value(s) of 0x%x in DT_FLAGS_1.\n", | 
 | 			  l->l_flags_1 & ~DT_1_SUPPORTED_MASK); | 
 |  | 
 |       if (l->l_flags_1 & DF_1_NOW) | 
 | 	info[DT_BIND_NOW] = info[VERSYMIDX (DT_FLAGS_1)]; | 
 |     } | 
 |   if (info[DT_RUNPATH] != NULL) | 
 |     /* If both RUNPATH and RPATH are given, the latter is ignored.  */ | 
 |     info[DT_RPATH] = NULL; | 
 | #endif | 
 | } |