about summary refs log tree commit diff
path: root/elf/dl-addr.c
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2007-07-12 18:26:36 +0000
committerJakub Jelinek <jakub@redhat.com>2007-07-12 18:26:36 +0000
commit0ecb606cb6cf65de1d9fc8a919bceb4be476c602 (patch)
tree2ea1f8305970753e4a657acb2ccc15ca3eec8e2c /elf/dl-addr.c
parent7d58530341304d403a6626d7f7a1913165fe2f32 (diff)
downloadglibc-0ecb606cb6cf65de1d9fc8a919bceb4be476c602.tar.gz
glibc-0ecb606cb6cf65de1d9fc8a919bceb4be476c602.tar.xz
glibc-0ecb606cb6cf65de1d9fc8a919bceb4be476c602.zip
2.5-18.1
Diffstat (limited to 'elf/dl-addr.c')
-rw-r--r--elf/dl-addr.c193
1 files changed, 114 insertions, 79 deletions
diff --git a/elf/dl-addr.c b/elf/dl-addr.c
index 685cab9be8..7dbf716cfa 100644
--- a/elf/dl-addr.c
+++ b/elf/dl-addr.c
@@ -1,5 +1,5 @@
 /* Locate the shared object symbol nearest a given address.
-   Copyright (C) 1996-2003, 2004   Free Software Foundation, Inc.
+   Copyright (C) 1996-2004, 2005, 2006, 2007 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
@@ -22,61 +22,62 @@
 #include <ldsodefs.h>
 
 
-int
-internal_function
-_dl_addr (const void *address, Dl_info *info,
-	  struct link_map **mapp, const ElfW(Sym) **symbolp)
+static void
+__attribute ((always_inline))
+determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
+		struct link_map **mapp, const ElfW(Sym) **symbolp)
 {
-  const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address);
-  struct link_map *match;
-  const ElfW(Sym) *symtab, *matchsym, *symtabend;
-  const char *strtab;
-  ElfW(Word) strtabsize;
+  /* Now we know what object the address lies in.  */
+  info->dli_fname = match->l_name;
+  info->dli_fbase = (void *) match->l_map_start;
 
-  /* Protect against concurrent loads and unloads.  */
-  __rtld_lock_lock_recursive (GL(dl_load_lock));
+  /* If this is the main program the information is incomplete.  */
+  if (__builtin_expect (match->l_name[0], 'a') == '\0'
+      && match->l_type == lt_executable)
+    info->dli_fname = _dl_argv[0];
 
-  /* Find the highest-addressed object that ADDRESS is not below.  */
-  match = NULL;
-  for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
-    for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l; l = l->l_next)
-      if (addr >= l->l_map_start && addr < l->l_map_end)
+  const ElfW(Sym) *symtab
+    = (const ElfW(Sym) *) D_PTR (match, l_info[DT_SYMTAB]);
+  const char *strtab = (const char *) D_PTR (match, l_info[DT_STRTAB]);
+
+  ElfW(Word) strtabsize = match->l_info[DT_STRSZ]->d_un.d_val;
+
+  const ElfW(Sym) *matchsym = NULL;
+  if (match->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM
+		    + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] != NULL)
+    {
+      /* We look at all symbol table entries referenced by the hash
+	 table.  */
+      for (Elf_Symndx bucket = 0; bucket < match->l_nbuckets; ++bucket)
 	{
-	  /* We know ADDRESS lies within L if in any shared object.
-	     Make sure it isn't past the end of L's segments.  */
-	  size_t n = l->l_phnum;
-	  if (n > 0)
+	  Elf32_Word symndx = match->l_gnu_buckets[bucket];
+	  if (symndx != 0)
 	    {
+	      const Elf32_Word *hasharr = &match->l_gnu_chain_zero[symndx];
+
 	      do
-		--n;
-	      while (l->l_phdr[n].p_type != PT_LOAD);
-	      if (addr >= (l->l_addr +
-			   l->l_phdr[n].p_vaddr + l->l_phdr[n].p_memsz))
-		/* Off the end of the highest-addressed shared object.  */
-		continue;
+		{
+		  /* The hash table never references local symbols so
+		     we can omit that test here.  */
+		  if ((symtab[symndx].st_shndx != SHN_UNDEF
+		       || symtab[symndx].st_value != 0)
+#ifdef USE_TLS
+		      && ELFW(ST_TYPE) (symtab[symndx].st_info) != STT_TLS
+#endif
+		      && DL_ADDR_SYM_MATCH (match, &symtab[symndx],
+					    matchsym, addr)
+		      && symtab[symndx].st_name < strtabsize)
+		    matchsym = (ElfW(Sym) *) &symtab[symndx];
+
+		  ++symndx;
+		}
+	      while ((*hasharr++ & 1u) == 0);
 	    }
-
-	  match = l;
-	  break;
 	}
-
-  int result = 0;
-  if (match != NULL)
+    }
+  else
     {
-      /* Now we know what object the address lies in.  */
-      info->dli_fname = match->l_name;
-      info->dli_fbase = (void *) match->l_map_start;
-
-      /* If this is the main program the information is incomplete.  */
-      if (__builtin_expect (match->l_name[0], 'a') == '\0'
-	  && match->l_type == lt_executable)
-	info->dli_fname = _dl_argv[0];
-
-      symtab = (const void *) D_PTR (match, l_info[DT_SYMTAB]);
-      strtab = (const void *) D_PTR (match, l_info[DT_STRTAB]);
-
-      strtabsize = match->l_info[DT_STRSZ]->d_un.d_val;
-
+      const ElfW(Sym) *symtabend;
       if (match->l_info[DT_HASH] != NULL)
 	symtabend = (symtab
 		     + ((Elf_Symndx *) D_PTR (match, l_info[DT_HASH]))[1]);
@@ -87,49 +88,83 @@ _dl_addr (const void *address, Dl_info *info,
 	   the string table which generally follows the symbol table.  */
 	symtabend = (const ElfW(Sym) *) strtab;
 
-      /* We assume that the string table follows the symbol table,
-	 because there is no way in ELF to know the size of the
-	 dynamic symbol table!!  */
-      for (matchsym = NULL; (void *) symtab < (void *) symtabend; ++symtab)
-	if (addr >= match->l_addr + symtab->st_value
-#if defined USE_TLS
+      for (; (void *) symtab < (void *) symtabend; ++symtab)
+	if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
+	     || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
+#ifdef USE_TLS
 	    && ELFW(ST_TYPE) (symtab->st_info) != STT_TLS
 #endif
-	    && ((symtab->st_size == 0
-		 && addr == match->l_addr + symtab->st_value)
-		|| addr < match->l_addr + symtab->st_value + symtab->st_size)
-	    && symtab->st_name < strtabsize
-	    && (matchsym == NULL || matchsym->st_value < symtab->st_value)
-	    && (ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
-		|| ELFW(ST_BIND) (symtab->st_info) == STB_WEAK))
+	    && (symtab->st_shndx != SHN_UNDEF
+		|| symtab->st_value != 0)
+	    && DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr)
+	    && symtab->st_name < strtabsize)
 	  matchsym = (ElfW(Sym) *) symtab;
+    }
 
-      if (mapp)
-	*mapp = match;
-      if (symbolp)
-	*symbolp = matchsym;
+  if (mapp)
+    *mapp = match;
+  if (symbolp)
+    *symbolp = matchsym;
 
-      if (matchsym)
-	{
-	  /* We found a symbol close by.  Fill in its name and exact
-	     address.  */
-	  lookup_t matchl = LOOKUP_VALUE (match);
+  if (matchsym)
+    {
+      /* We found a symbol close by.  Fill in its name and exact
+	 address.  */
+      lookup_t matchl = LOOKUP_VALUE (match);
 
-	  info->dli_sname = strtab + matchsym->st_name;
-	  info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym);
-	}
-      else
+      info->dli_sname = strtab + matchsym->st_name;
+      info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym);
+    }
+  else
+    {
+      /* No symbol matches.  We return only the containing object.  */
+      info->dli_sname = NULL;
+      info->dli_saddr = NULL;
+    }
+}
+
+
+int
+internal_function
+_dl_addr (const void *address, Dl_info *info,
+	  struct link_map **mapp, const ElfW(Sym) **symbolp)
+{
+  const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address);
+  int result = 0;
+
+  /* Protect against concurrent loads and unloads.  */
+  __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+  /* Find the highest-addressed object that ADDRESS is not below.  */
+  for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+    for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l; l = l->l_next)
+      if (addr >= l->l_map_start && addr < l->l_map_end
+	  && (l->l_contiguous || _dl_addr_inside_object (l, addr)))
 	{
-	  /* No symbol matches.  We return only the containing object.  */
-	  info->dli_sname = NULL;
-	  info->dli_saddr = NULL;
+	  determine_info (addr, l, info, mapp, symbolp);
+	  result = 1;
+	  goto out;
 	}
 
-      result = 1;
-    }
-
+ out:
   __rtld_lock_unlock_recursive (GL(dl_load_lock));
 
   return result;
 }
 libc_hidden_def (_dl_addr)
+
+/* Return non-zero if ADDR lies within one of L's segments.  */
+int
+internal_function
+_dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr)
+{
+  int n = l->l_phnum;
+  const ElfW(Addr) reladdr = addr - l->l_addr;
+
+  while (--n >= 0)
+    if (l->l_phdr[n].p_type == PT_LOAD
+	&& reladdr - l->l_phdr[n].p_vaddr >= 0
+	&& reladdr - l->l_phdr[n].p_vaddr < l->l_phdr[n].p_memsz)
+      return 1;
+  return 0;
+}