about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile2
-rw-r--r--elf/dl-open.c24
-rw-r--r--elf/dl-profstub.c43
-rw-r--r--elf/dl-support.c1
-rw-r--r--elf/dlfcn.h30
-rw-r--r--elf/ldsodefs.h19
-rw-r--r--elf/rtld.c1
7 files changed, 113 insertions, 7 deletions
diff --git a/elf/Makefile b/elf/Makefile
index b2ea241808..dedc3d5074 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -22,7 +22,7 @@ subdir		:= elf
 
 headers		= elf.h bits/elfclass.h bits/dlfcn.h link.h dlfcn.h
 routines	= $(dl-routines) dl-open dl-close dl-symbol dl-support \
-		  dl-addr enbl-secure
+		  dl-addr enbl-secure dl-profstub
 
 # The core dynamic linking functions are in libc for the static and
 # profiled libraries.
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 4c4c8abdc5..2b9590913f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -101,9 +101,27 @@ _dl_open (const char *file, int mode)
 	     magic ward.  */
 	  asm ("" : "=r" (reloc) : "0" (reloc));
 
-	  (*reloc) (l, _dl_object_relocation_scope (l),
-		    ((mode & RTLD_BINDING_MASK) == RTLD_LAZY
-		     || _dl_profile != NULL), _dl_profile != NULL);
+#ifdef PIC
+	  if (_dl_profile != NULL)
+	    {
+	      /* If this here is the shared object which we want to profile
+		 make sure the profile is started.  We can find out whether
+	         this is necessary or not by observing the `_dl_profile_map'
+	         variable.  If was NULL but is not NULL afterwars we must
+		 start the profiling.  */
+	      struct link_map *old_profile_map = _dl_profile_map;
+
+	      (*reloc) (l, _dl_object_relocation_scope (l), 1, 1);
+
+	      if (old_profile_map == NULL && _dl_profile_map != NULL)
+		/* We must prepare the profiling.  */
+		_dl_start_profile (_dl_profile_map, _dl_profile_output);
+	    }
+	  else
+#endif
+	    (*reloc) (l, _dl_object_relocation_scope (l),
+		      (mode & RTLD_BINDING_MASK) == RTLD_LAZY, 0);
+
 	  *_dl_global_scope_end = NULL;
 	}
 
diff --git a/elf/dl-profstub.c b/elf/dl-profstub.c
new file mode 100644
index 0000000000..9740c6adc4
--- /dev/null
+++ b/elf/dl-profstub.c
@@ -0,0 +1,43 @@
+/* Helper definitions for profiling of shared libraries.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <dlfcn.h>
+#include <elf.h>
+#include <elf/ldsodefs.h>
+
+/* This is the map for the shared object we profile.  It is defined here
+   only because we test for this value being NULL or not.  */
+struct link_map *_dl_profile_map;
+
+
+void
+_dl_mcount_wrapper (ElfW(Addr) selfpc)
+{
+  _dl_mcount ((ElfW(Addr)) __builtin_return_address (0), selfpc);
+}
+
+
+void
+_dl_mcount_wrapper_check (void *selfpc)
+{
+  if (_dl_profile_map != NULL)
+    _dl_mcount ((ElfW(Addr)) __builtin_return_address (0),
+		(ElfW(Addr)) selfpc);
+}
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 85f656c2fd..73c90c2f42 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -53,7 +53,6 @@ struct r_search_path *_dl_search_paths;
 
 /* We never do profiling.  */
 const char *_dl_profile;
-struct link_map *_dl_profile_map;
 
 /* Names of shared object for which the RPATHs should be ignored.  */
 const char *_dl_inhibit_rpath;
diff --git a/elf/dlfcn.h b/elf/dlfcn.h
index 825b4843c1..e0b17d392a 100644
--- a/elf/dlfcn.h
+++ b/elf/dlfcn.h
@@ -1,5 +1,5 @@
 /* User functions for run-time dynamic loading.
-   Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997, 1998 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
@@ -70,6 +70,34 @@ typedef struct
   } Dl_info;
 extern int dladdr __P ((const void *__address, Dl_info *__info));
 
+#ifdef __USE_GNU
+/* To support profiling of shared objects it is a good idea to call
+   the function found using `dlsym' using the following macro since
+   these calls do not use the PLT.  But this would mean the dynamic
+   loader has no chance to find out when the function is called.  The
+   macro applies the necessary magic so that profiling is possible.
+   Rewrite
+	foo = (*fctp) (arg1, arg2);
+   into
+        foo = DL_CALL_FCT (fctp, (arg1, arg2));
+*/
+# if __GNUC__ >= 2
+/* Do not ever use this variable directly, it is internal!  */
+extern struct link_map *_dl_profile_map;
+
+#  define DL_CALL_FCT(fctp, args) \
+  (__extension__ ({ if (_dl_profile_map != NULL)			      \
+		      _dl_mcount_wrapper_check (fctp);			      \
+		    (*fctp) args; })
+# else
+/* This feature is not available without GCC.  */
+#  define DL_CALL_FCT(fctp, args) (*fctp) args
+# endif
+
+/* This function calls the profiling functions.  */
+extern void _dl_mcount_wrapper_check __P ((void *__selfpc));
+#endif
+
 __END_DECLS
 
 #endif	/* dlfcn.h */
diff --git a/elf/ldsodefs.h b/elf/ldsodefs.h
index 45c2a5ec64..6814f25556 100644
--- a/elf/ldsodefs.h
+++ b/elf/ldsodefs.h
@@ -127,6 +127,8 @@ extern int _dl_zerofd;
 extern const char *_dl_profile;
 /* Map of shared object to be profiled.  */
 extern struct link_map *_dl_profile_map;
+/* Filename of the output file.  */
+extern const char *_dl_profile_output;
 
 /* If nonzero the appropriate debug information is printed.  */
 extern int _dl_debug_libs;
@@ -409,6 +411,11 @@ extern void _dl_start_profile (struct link_map *map, const char *output_dir)
 /* The actual functions used to keep book on the calls.  */
 extern void _dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc);
 
+/* This function is simply a wrapper around the _dl_mcount function
+   which does not require a FROMPC parameter since this is the
+   calling function.  */
+extern void _dl_mcount_wrapper (ElfW(Addr) selfpc);
+
 
 /* Show the members of the auxiliary array passed up from the kernel.  */
 extern void _dl_show_auxv (void) internal_function;
@@ -424,6 +431,18 @@ extern const struct r_strlenpair *_dl_important_hwcaps (const char *platform,
 							size_t *max_capstrlen)
      internal_function;
 
+
+/* When we do profiling we have the problem that uses of `dlopen'ed
+   objects don't use the PLT but instead use a pointer to the function.
+   We still want to have profiling data and in these cases we must do
+   the work of calling `_dl_mcount' ourself.  The following macros
+   helps do it.  */
+#define _CALL_DL_FCT(fctp, args) \
+  ({ if (_dl_profile_map != NULL)					      \
+       _dl_mcount_wrapper ((ElfW(Addr)) fctp);				      \
+     (*fctp) args;							      \
+  })
+
 __END_DECLS
 
 #endif /* ldsodefs.h */
diff --git a/elf/rtld.c b/elf/rtld.c
index 028467f6f8..5bd4b7cd97 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -73,7 +73,6 @@ unsigned long _dl_hwcap;
 struct r_search_path *_dl_search_paths;
 const char *_dl_profile;
 const char *_dl_profile_output;
-struct link_map *_dl_profile_map;
 int _dl_debug_libs;
 int _dl_debug_impcalls;
 int _dl_debug_bindings;