This is the mail archive of the libc-hacker@sourceware.org mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Handle new PPC64 ABI .opd symbols in dladdr{,1}


On Tue, Aug 30, 2005 at 09:47:06AM +0930, Alan Modra wrote:
> On Mon, Aug 29, 2005 at 05:18:40PM +0200, Jakub Jelinek wrote:
> > Is there any (reliable and cheap) way how to recognize new PPC64 ABI (==
> > dotless PPC64 ABI) .opd symbols from the dynamic linker/libc.so?
> > glibc can't use section header table for that (as it can be stripped
> > off and in any case isn't loaded at runtime).
> 
> Yes.  Any STT_FUNC symbol defined outside of the text segment must be in
> the .opd section.  You could use l->l_ld too.
> 
>   ((ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
>     && l->l_addr + sym->st_value >= (ElfW(Addr)) (l->l_ld)
>     && l->l_addr + sym->st_value < l->l_map_end
>     && sym->st_size != 0)

Ok, here is a patch.  On ppc64 dladdr{,1} will with this patch handle
both the function descriptor area as well as actual function code chunk.
Whether size 16 or 24 should be used for the func descriptor is debatable
though.
binutils has support for .opd entry overlap, but upstream GCC doesn't make
use of this, only Red Hat/Fedora GCCs.  If the .opd entries don't overlap,
then each entry is 24 bytes long.  If they do overlap, then the patch
will DTRT in most cases too (as it prefers higher st_values over smaller
ones), but if there is:
|0 |4 |8 |12|16|24
|foo
|     |bar
+--+--+--+
|FN|TC|  |
+--+--+--+--+--+
      |FN|TC|AU|
      +--+--+--+
then dladdr for addr 8 above will return bar, as it has higher st_value.
The only problem is if bar is not an exported symbol, then dladdr for addr 8
will return foo, not nothing.  I think that's ok, but e.g. LSB libchk
would need to be adjusted to handle this.  On the other side, if 16 is used
instead of 24, dladdr will not find any symbol for the third quad in the
.opd entries, even when they are non-overlapping.

2005-08-30  Jakub Jelinek  <jakub@redhat.com>
	    Alan Modra  <amodra@bigpond.net.au>

	* elf/dl-addr.c (_dl_addr): Use DL_ADDR_SYM_MATCH macro.
	* sysdeps/generic/ldsodefs.h (DL_ADDR_SYM_MATCH): Define.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/ldsodefs.h: New file.

--- libc/elf/dl-addr.c.jj	2005-03-08 13:05:15.000000000 +0100
+++ libc/elf/dl-addr.c	2005-08-30 11:44:35.000000000 +0200
@@ -85,22 +85,15 @@ _dl_addr (const void *address, Dl_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!!  */
       const ElfW(Sym) *matchsym;
       for (matchsym = NULL; (void *) symtab < (void *) symtabend; ++symtab)
-	if (addr >= match->l_addr + symtab->st_value
+	if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
+	     || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
 #if defined 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))
+	    && DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr)
+	    && symtab->st_name < strtabsize)
 	  matchsym = (ElfW(Sym) *) symtab;
 
       if (mapp)
--- libc/sysdeps/generic/ldsodefs.h.jj	2005-06-22 18:34:19.000000000 +0200
+++ libc/sysdeps/generic/ldsodefs.h	2005-08-30 12:38:02.000000000 +0200
@@ -62,7 +62,7 @@ typedef struct link_map *lookup_t;
 # define LOOKUP_VALUE(map) map
 # define LOOKUP_VALUE_ADDRESS(map) ((map) ? (map)->l_addr : 0)
 
-/* on some architectures a pointer to a function is not just a pointer
+/* On some architectures a pointer to a function is not just a pointer
    to the actual code of the function but rather an architecture
    specific descriptor. */
 #ifndef ELF_FUNCTION_PTR_IS_SPECIAL
@@ -73,6 +73,14 @@ typedef struct link_map *lookup_t;
 # define DL_DT_FINI_ADDRESS(map, start) (start)
 #endif
 
+/* On some architectures dladdr can't use st_size of all symbols this way.  */
+#define DL_ADDR_SYM_MATCH(L, SYM, MATCHSYM, ADDR) \
+  ((ADDR) >= (L)->l_addr + (SYM)->st_value				\
+   && (((SYM)->st_size == 0						\
+	&& (ADDR) == (L)->l_addr + (SYM)->st_value)			\
+       || (ADDR) < (L)->l_addr + (SYM)->st_value + (SYM)->st_size)	\
+   && ((MATCHSYM) == NULL || (MATCHSYM)->st_value < (SYM)->st_value))
+
 /* Unmap a loaded object, called by _dl_close (). */
 #ifndef DL_UNMAP_IS_SPECIAL
 # define DL_UNMAP(map) \
--- libc/sysdeps/unix/sysv/linux/powerpc/powerpc64/ldsodefs.h.jj	2005-08-30 11:46:39.000000000 +0200
+++ libc/sysdeps/unix/sysv/linux/powerpc/powerpc64/ldsodefs.h	2005-08-30 13:11:09.000000000 +0200
@@ -0,0 +1,77 @@
+/* Run-time dynamic linker data structures for loaded ELF shared objects.
+   Copyright (C) 2005 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef	_LDSODEFS_H
+
+/* Get the real definitions.  */
+#include_next <ldsodefs.h>
+
+/* Now define our stuff.  */
+
+static __always_inline bool
+_dl_ppc64_is_opd_sym (const struct link_map *l, const ElfW(Sym) *sym)
+{
+  return (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
+	  && l->l_addr + sym->st_value >= (ElfW(Addr)) l->l_ld
+	  && l->l_addr + sym->st_value < l->l_map_end
+	  && sym->st_size != 0);
+}
+
+static __always_inline bool
+_dl_ppc64_addr_sym_match (const struct link_map *l, const ElfW(Sym) *sym,
+			  const ElfW(Sym) *matchsym, ElfW(Addr) addr)
+{
+  ElfW(Addr) value = l->l_addr + sym->st_value;
+  if (_dl_ppc64_is_opd_sym (l, sym))
+    {
+      if (addr < value || addr >= value + 24)
+	{
+	  value = *(ElfW(Addr) *) value;
+	  if (addr < value || addr >= value + sym->st_size)
+	    return false;
+	}
+    }
+  else if (sym->st_size == 0)
+    {
+      if (addr != value)
+	return false;
+    }
+  else if (addr < value || addr >= value + sym->st_size)
+    return false;
+
+  if (matchsym == NULL)
+    return true;
+
+  ElfW(Addr) matchvalue = l->l_addr + matchsym->st_value;
+  if (_dl_ppc64_is_opd_sym (l, matchsym)
+      && (addr < matchvalue || addr > matchvalue + 24))
+    matchvalue = *(ElfW(Addr) *) matchvalue;
+
+  return matchvalue < value;
+}
+
+/* If this is a function symbol defined past the end of our dynamic
+   section, then it must be a function descriptor.  Allow these symbols
+   to match their associated function code range as well as the
+   descriptor addresses.  */
+#undef DL_ADDR_SYM_MATCH
+#define DL_ADDR_SYM_MATCH(L, SYM, MATCHSYM, ADDR) \
+  _dl_ppc64_addr_sym_match (L, SYM, MATCHSYM, ADDR)
+
+#endif /* ldsodefs.h */


	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]