summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/dl-load.c97
-rw-r--r--elf/dl-minimal.c2
-rw-r--r--elf/dl-misc.c2
-rw-r--r--elf/dl-profile.c251
4 files changed, 254 insertions, 98 deletions
diff --git a/elf/dl-load.c b/elf/dl-load.c
index f7c2c53f7e..f0af29292a 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -107,27 +107,51 @@ local_strdup (const char *s)
   return (char *) memcpy (new, s, len);
 }
 
-
-/* Implement cache for search path lookup.  */
-#if 0
-/* This is how generated should look like.  I'll remove this once I'm
-   sure everything works correctly.  */
-static struct r_search_path_elem rtld_search_dir1 =
-  { "/lib/", 5, unknown, 0, unknown, NULL };
-static struct r_search_path_elem rtld_search_dir2 =
-  { "/usr/lib/", 9, unknown, 0, unknown, &r ld_search_dir1 };
-
-static struct r_search_path_elem *rtld_search_dirs[] =
+/* Add `name' to the list of names for a particular shared object.
+   `name' is expected to have been allocated with malloc and will
+   be freed if the shared object already has this name.
+   Returns false if the object already had this name.  */
+static int
+add_name_to_object (struct link_map *l, char *name)
 {
-  &rtld_search_dir1,
-  &rtld_search_dir2,
-  NULL
-};
+  struct libname_list *lnp, *lastp;
+  struct libname_list *newname;
 
-static struct r_search_path_elem *all_dirs = &rtld_search_dir2;
-#else
-# include "rtldtbl.h"
-#endif
+  if (name == NULL)
+  {
+    /* No more memory.  */
+    _dl_signal_error (ENOMEM, NULL, _("could not allocate name string"));
+    return 0;
+  }
+
+  lastp = NULL;
+  for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next)
+    if (strcmp (name, lnp->name) == 0)
+      {
+	free (name);
+	return 0;
+      }
+
+  newname = malloc (sizeof *newname);
+  if (newname == NULL)
+  {
+    /* No more memory.  */
+    _dl_signal_error (ENOMEM, name, _("cannot allocate name record"));
+    free(name);
+    return 0;
+  }
+  /* The object should have a libname set from _dl_new_object.  */
+  assert (lastp != NULL);
+
+  newname->name = name;
+  newname->next = NULL;
+  lastp->next = newname;
+  return 1;
+}
+
+
+/* Implement cache for search path lookup.  */
+#include "rtldtbl.h"
 
 static size_t max_dirnamelen;
 
@@ -423,9 +447,10 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 	    l->l_next->l_prev = l->l_prev;
 	  free (l);
 	}
-      free (name);	/* XXX Can this be correct? --drepper */
       free (realname);
       _dl_signal_error (code, name, msg);
+      free (name);         /* Hmmm.  Can this leak memory?  Better
+			      than a segfault, anyway.  */
     }
 
   inline caddr_t map_segment (ElfW(Addr) mapstart, size_t len,
@@ -434,7 +459,7 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
       caddr_t mapat = __mmap ((caddr_t) mapstart, len, prot,
 			      fixed|MAP_COPY|MAP_FILE,
 			      fd, offset);
-      if (mapat == (caddr_t) -1)
+      if (mapat == MAP_FAILED)
 	lose (errno, "failed to map segment from shared object");
       return mapat;
     }
@@ -451,7 +476,7 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 	  mapping_size &= ~(_dl_pagesize - 1);
 	  result = __mmap (file_mapping, mapping_size, PROT_READ,
 			   MAP_COPY|MAP_FILE, fd, 0);
-	  if (result == (void *) -1)
+	  if (result == MAP_FAILED)
 	    lose (errno, "cannot map file data");
 	  file_mapping = result;
 	}
@@ -467,7 +492,6 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
   for (l = _dl_loaded; l; l = l->l_next)
     if (! strcmp (realname, l->l_name))
       {
-	struct libname_list *lnp, *lastp;
 	/* The object is already loaded.
 	   Just bump its reference count and return it.  */
 	__close (fd);
@@ -475,26 +499,7 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 	/* If the name is not in the list of names for this object add
 	   it.  */
 	free (realname);
-	lastp = NULL;
-	for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next)
-	  if (strcmp (name, lnp->name) == 0)
-	    {
-	      free (name);
-	      break;
-	    }
-	if (lnp == NULL)
-	  {
-	    struct libname_list *newname = malloc (sizeof *newname);
-	    if (newname == NULL)
-	      /* No more memory.  */
-	      lose (ENOMEM, "cannot allocate name record");
-	    /* The object should have a libname set.  */
-	    assert (lastp != NULL);
-
-	    newname->name = name;
-	    newname->next = NULL;
-	    lastp->next = newname;
-	  }
+	add_name_to_object (l, name);
 	++l->l_opencount;
 	return l;
       }
@@ -701,7 +706,7 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 		mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
 				c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
 				ANONFD, 0);
-		if (mapat == (caddr_t) -1)
+		if (mapat == MAP_FAILED)
 		  lose (errno, "cannot map zero-fill pages");
 	      }
 	  }
@@ -876,6 +881,10 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
       {
 	/* The object is already loaded.
 	   Just bump its reference count and return it.  */
+	const char *soname = (const char *) (l->l_addr +
+					     l->l_info[DT_STRTAB]->d_un.d_ptr +
+					     l->l_info[DT_SONAME]->d_un.d_val);
+	add_name_to_object (l, local_strdup (soname));
 	++l->l_opencount;
 	return l;
       }
diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index f5b256808a..486e5bc608 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -66,7 +66,7 @@ malloc (size_t n)
       assert (n <= _dl_pagesize);
       page = __mmap (0, _dl_pagesize, PROT_READ|PROT_WRITE,
 		     MAP_ANON|MAP_PRIVATE, _dl_zerofd, 0);
-      assert (page != (caddr_t) -1);
+      assert (page != MAP_FAILED);
       if (page != alloc_end)
 	alloc_ptr = page;
       alloc_end = page + _dl_pagesize;
diff --git a/elf/dl-misc.c b/elf/dl-misc.c
index d5b1464a6b..0894a7216b 100644
--- a/elf/dl-misc.c
+++ b/elf/dl-misc.c
@@ -62,7 +62,7 @@ _dl_sysdep_read_whole_file (const char *file, size_t *sizep, int prot)
                        | MAP_FILE
 #endif
                        , fd, 0);
-      if (result == (void *) -1)
+      if (result == MAP_FAILED)
         result = NULL;
       else
         *sizep = st.st_size;
diff --git a/elf/dl-profile.c b/elf/dl-profile.c
index 805624909b..c8028a7c5a 100644
--- a/elf/dl-profile.c
+++ b/elf/dl-profile.c
@@ -50,29 +50,129 @@
 
    This approach is very different from the normal profiling.  We have
    to use the profiling data in exactly the way they are expected to
-   be written to disk.  */
+   be written to disk.  But the normal format used by gprof is not usable
+   to do this.  It is optimized for size.  It writes the tags as single
+   bytes but this means that the following 32/64 bit values are
+   unaligned.
+
+   Therefore we use a new format.  This will look like this
+
+					0  1  2  3	<- byte is 32 bit word
+	0000				g  m  o  n
+	0004				*version*	<- GMON_SHOBJ_VERSION
+	0008				00 00 00 00
+	000c				00 00 00 00
+	0010				00 00 00 00
+
+	0014				*tag*		<- GMON_TAG_TIME_HIST
+	0018				?? ?? ?? ??
+					?? ?? ?? ??	<- 32/64 bit LowPC
+	0018+A				?? ?? ?? ??
+					?? ?? ?? ??	<- 32/64 bit HighPC
+	0018+2*A			*histsize*
+	001c+2*A			*profrate*
+	0020+2*A			s  e  c  o
+	0024+2*A			n  d  s  \0
+	0028+2*A			\0 \0 \0 \0
+	002c+2*A			\0 \0 \0
+	002f+2*A			s
+
+	0030+2*A			?? ?? ?? ??	<- Count data
+	...				...
+	0030+2*A+K			?? ?? ?? ??
+
+	0030+2*A+K			*tag*		<- GMON_TAG_CG_ARC
+	0034+2*A+K			*lastused*
+	0038+2*A+K			?? ?? ?? ??
+					?? ?? ?? ??	<- FromPC#1
+	0038+3*A+K			?? ?? ?? ??
+					?? ?? ?? ??	<- ToPC#1
+	0038+4*A+K			?? ?? ?? ??	<- Count#1
+	...				...		   ...
+	0038+(2*(CN-1)+2)*A+(CN-1)*4+K	?? ?? ?? ??
+					?? ?? ?? ??	<- FromPC#CGN
+	0038+(2*(CN-1)+3)*A+(CN-1)*4+K	?? ?? ?? ??
+					?? ?? ?? ??	<- ToPC#CGN
+	0038+(2*CN+2)*A+(CN-1)*4+K	?? ?? ?? ??	<- Count#CGN
+
+   We put (for now? no basic block information in the file since this would
+   introduce rase conditions among all the processes who want to write them.
+
+   `K' is the number of count entries which is computed as
+
+ 		textsize / HISTFRACTION
+
+   `CG' in the above table is the number of call graph arcs.  Normally,
+   the table is sparse and the profiling code writes out only the those
+   entries which are really used in the program run.  But since we must
+   not extend this table (the profiling file) we'll keep them all here.
+   So CN can be executed in advance as
+
+		MINARCS <= textsize*(ARCDENSITY/100) <= MAXARCS
+
+   Now the remaining question is: how to build the data structures we can
+   work with from this data.  We need the from set and must associate the
+   froms with all the associated tos.  We will do this by constructing this
+   data structures at the program start.  To do this we'll simply visit all
+   entries in the call graph table and add it to the appropriate list.  */
 
 extern char *_strerror_internal __P ((int, char *buf, size_t));
 
 extern int __profile_frequency __P ((void));
 
-
-static struct gmonparam param;
-
 /* We define a special type to address the elements of the arc table.
    This is basically the `gmon_cg_arc_record' format but it includes
    the room for the tag and it uses real types.  */
 struct here_cg_arc_record
   {
-    char tag;
-    uintptr_t from_pc __attribute__ ((packed));
-    uintptr_t self_pc __attribute__ ((packed));
-    uint32_t count __attribute__ ((packed));
-  };
+    uintptr_t from_pc;
+    uintptr_t self_pc;
+    uint32_t count;
+  } __attribute__ ((packed));
 
 static struct here_cg_arc_record *data;
 
+/* This is the number of entry which have been incorporated in the toset.  */
+static uint32_t narcs;
+/* This is a pointer to the object representing the number of entries
+   currently in the mmaped file.  At no point of time this has to be the
+   same as NARCS.  If it is equal all entries from the file are in our
+   lists.  */
+static uint32_t *narcsp;
+
+/* Description of the currently profiled object.  */
+static long int state;
 
+static volatile uint16_t *kcount;
+static size_t kcountsize;
+
+struct here_tostruct
+  {
+    struct here_cg_arc_record volatile *here;
+    uint16_t link;
+  };
+
+static uint16_t *froms;
+static size_t fromssize;
+
+static struct here_tostruct *tos;
+static size_t tossize;
+static size_t tolimit;
+static size_t toidx;
+
+static uintptr_t lowpc;
+static uintptr_t highpc;
+static size_t textsize;
+static unsigned int hashfraction;
+static unsigned int log_hashfraction;
+
+/* This is the information about the mmaped memory.  */
+static struct gmon_hdr *addr;
+static off_t expected_size;
+
+
+/* Set up profiling data to profile object desribed by MAP.  The output
+   file is found (or created) in OUTPUT_DIR.  */
 void
 _dl_start_profile (struct link_map *map, const char *output_dir)
 {
@@ -82,11 +182,10 @@ _dl_start_profile (struct link_map *map, const char *output_dir)
   const ElfW(Phdr) *ph;
   ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
   ElfW(Addr) mapend = 0;
-  off_t expected_size;
   struct gmon_hdr gmon_hdr;
   struct gmon_hist_hdr hist_hdr;
-  struct gmon_hdr *addr;
   char *hist;
+  size_t idx;
 
   /* Compute the size of the sections which contain program code.  */
   for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
@@ -104,40 +203,41 @@ _dl_start_profile (struct link_map *map, const char *output_dir)
 
   /* Now we can compute the size of the profiling data.  This is done
      with the same formulars as in `monstartup' (see gmon.c).  */
-  param.state = GMON_PROF_OFF;
-  param.lowpc = mapstart + map->l_addr;
-  param.highpc = mapend + map->l_addr;
-  param.textsize = mapend - mapstart;
-  param.kcountsize = param.textsize / HISTFRACTION;
-  param.hashfraction = HASHFRACTION;
-  param.log_hashfraction = -1;
+  state = GMON_PROF_OFF;
+  lowpc = ROUNDDOWN (mapstart + map->l_addr,
+		     HISTFRACTION * sizeof(HISTCOUNTER));
+  highpc = ROUNDUP (mapend + map->l_addr,
+		    HISTFRACTION * sizeof(HISTCOUNTER));
+  textsize = highpc - lowpc;
+  kcountsize = textsize / HISTFRACTION;
+  hashfraction = HASHFRACTION;
   if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
     /* If HASHFRACTION is a power of two, mcount can use shifting
        instead of integer division.  Precompute shift amount.  */
-    param.log_hashfraction = ffs (param.hashfraction
-				  * sizeof (*param.froms)) - 1;
-  param.fromssize = param.textsize / HASHFRACTION;
-  param.tolimit = param.textsize * ARCDENSITY / 100;
-  if (param.tolimit < MINARCS)
-    param.tolimit = MINARCS;
-  if (param.tolimit > MAXARCS)
-    param.tolimit = MAXARCS;
-  param.tossize = param.tolimit * sizeof (struct tostruct);
+    log_hashfraction = __builtin_ffs (hashfraction * sizeof (*froms)) - 1;
+  else
+    log_hashfraction = -1;
+  fromssize = textsize / HASHFRACTION;
+  tolimit = textsize * ARCDENSITY / 100;
+  if (tolimit < MINARCS)
+    tolimit = MINARCS;
+  if (tolimit > MAXARCS)
+    tolimit = MAXARCS;
+  tossize = tolimit * sizeof (struct here_tostruct);
 
   expected_size = (sizeof (struct gmon_hdr)
-		   + 1 + sizeof (struct gmon_hist_hdr)
-		   + ((1 + sizeof (struct gmon_cg_arc_record))
-		      * (param.fromssize / sizeof (*param.froms))));
+		   + 4 + sizeof (struct gmon_hist_hdr) + kcountsize
+		   + 4 + 4 + tossize * sizeof (struct here_cg_arc_record));
 
   /* Create the gmon_hdr we expect or write.  */
   memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
   memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
-  *(int32_t *) gmon_hdr.version = GMON_VERSION;
+  *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION;
 
   /* Create the hist_hdr we expect or write.  */
   *(char **) hist_hdr.low_pc = (char *) mapstart;
   *(char **) hist_hdr.high_pc = (char *) mapend;
-  *(int32_t *) hist_hdr.hist_size = param.kcountsize / sizeof (HISTCOUNTER);
+  *(int32_t *) hist_hdr.hist_size = kcountsize / sizeof (HISTCOUNTER);
   *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
   strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
   hist_hdr.dimen_abbrev = 's';
@@ -193,15 +293,19 @@ _dl_start_profile (struct link_map *map, const char *output_dir)
     {
       __close (fd);
     wrong_format:
+
+      if (addr != NULL)
+	__munmap ((void *) addr, expected_size);
+
       _dl_sysdep_error (filename,
 			": file is no correct profile data file for `",
 			_dl_profile, "'\n", NULL);
       return;
     }
 
-  addr = (void *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE,
-			  MAP_SHARED|MAP_FILE, fd, 0);
-  if (addr == (void *) -1)
+  addr = (struct gmon_hdr *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE,
+				     MAP_SHARED|MAP_FILE, fd, 0);
+  if (addr == (struct gmon_hdr *) MAP_FAILED)
     {
       char buf[400];
       int errnum = errno;
@@ -217,54 +321,97 @@ _dl_start_profile (struct link_map *map, const char *output_dir)
 
   /* Pointer to data after the header.  */
   hist = (char *) (addr + 1);
+  kcount = (uint16_t *) ((char *) hist + sizeof (uint32_t)
+			 + sizeof (struct gmon_hist_hdr));
 
   /* Compute pointer to array of the arc information.  */
-  data = (struct here_cg_arc_record *) (hist + 1
-					+ sizeof (struct gmon_hist_hdr));
+  data = (struct here_cg_arc_record *) ((char *) kcount + kcountsize
+					+ 2 * sizeof (uint32_t));
+  narcsp = (uint32_t *) (hist + sizeof (uint32_t)
+			 + sizeof (struct gmon_hist_hdr) + sizeof (uint32_t));
 
   if (st.st_size == 0)
     {
       /* Create the signature.  */
-      size_t cnt;
-
       memcpy (addr, &gmon_hdr, sizeof (struct gmon_hdr));
 
-      *hist = GMON_TAG_TIME_HIST;
-      memcpy (hist + 1, &hist_hdr, sizeof (struct gmon_hist_hdr));
+      *(uint32_t *) hist = GMON_TAG_TIME_HIST;
+      memcpy (hist + sizeof (uint32_t), &hist_hdr,
+	      sizeof (struct gmon_hist_hdr));
 
-      for (cnt = 0; cnt < param.fromssize / sizeof (*param.froms); ++cnt)
-	data[cnt].tag = GMON_TAG_CG_ARC;
+      *(uint32_t *) (hist + sizeof (uint32_t) + sizeof (struct gmon_hist_hdr)
+		     + kcountsize) = GMON_TAG_CG_ARC;
     }
   else
     {
       /* Test the signature in the file.  */
       if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
-	  || *hist != GMON_TAG_TIME_HIST
-	  || memcmp (hist + 1, &hist_hdr, sizeof (struct gmon_hist_hdr)) != 0)
+	  || *(uint32_t *) hist != GMON_TAG_TIME_HIST
+	  || memcmp (hist + sizeof (uint32_t), &hist_hdr,
+		     sizeof (struct gmon_hist_hdr)) != 0
+	  || (*(uint32_t *) (hist + sizeof (uint32_t)
+			    + sizeof (struct gmon_hist_hdr) + kcountsize)
+	      != GMON_TAG_CG_ARC))
 	goto wrong_format;
     }
 
+  /* Allocate memory for the froms data and the pointer to the tos records.  */
+  froms = (uint16_t *) calloc (fromssize + tossize, 1);
+  if (froms == NULL)
+    {
+      __munmap ((void *) addr, expected_size);
+      _dl_sysdep_fatal ("Out of memory while initializing profiler", NULL);
+      /* NOTREACHED */
+    }
+
+  tos = (struct here_tostruct *) ((char *) froms + fromssize);
+  toidx = 0;
+
+  /* Now we have to process all the arc count entries.  BTW: it is
+     not critical whether the *NARCSP value changes meanwhile.  Before
+     we enter a new entry in to toset we will check that everything is
+     available in TOS.  This happens in _dl_mcount.
+
+     Loading the entries in reverse order should help to get the most
+     frequently used entries at the front of the list.  */
+  for (idx = narcs = *narcsp; idx > 0; )
+    {
+      size_t from_index;
+      size_t newtoidx;
+      --idx;
+      from_index = ((data[idx].from_pc - lowpc)
+		    / (hashfraction * sizeof (*froms)));
+      newtoidx = toidx++;
+      tos[newtoidx].here = &data[idx];
+      tos[newtoidx].link = froms[from_index];
+      froms[from_index] = newtoidx;
+    }
+
   /* Turn on profiling.  */
-  param.state = GMON_PROF_ON;
+  state = GMON_PROF_ON;
 }
 
 
 void
 _dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc)
 {
-  if (param.state != GMON_PROF_ON)
+  if (state != GMON_PROF_ON)
     return;
-  param.state = GMON_PROF_BUSY;
+  state = GMON_PROF_BUSY;
 
   /* Compute relative addresses.  The shared object can be loaded at
      any address.  The value of frompc could be anything.  We cannot
      restrict it in any way, just set to a fixed value (0) in case it
      is outside the allowed range.  These calls show up as calls from
      <external> in the gprof output.  */
-  frompc -= param.lowpc;
-  if (frompc >= param.textsize)
+  frompc -= lowpc;
+  if (frompc >= textsize)
     frompc = 0;
-  selfpc -= param.lowpc;
+  selfpc -= lowpc;
+  if (selfpc >= textsize)
+    goto done;
+
 
-  param.state = GMON_PROF_ON;
+ done:
+  state = GMON_PROF_ON;
 }