OSDN Git Service

2010-04-20 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / gcov-io.c
index b595217..6d371cd 100644 (file)
@@ -1,6 +1,6 @@
 /* File format for coverage information
 /* File format for coverage information
-   Copyright (C) 1996, 1997, 1998, 2000, 2002,
-   2003  Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005, 2007,
+   2008  Free Software Foundation, Inc.
    Contributed by Bob Manson <manson@cygnus.com>.
    Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
 
    Contributed by Bob Manson <manson@cygnus.com>.
    Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
 
@@ -8,7 +8,7 @@ This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -17,113 +17,150 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 /* Routines declared in gcov-io.h.  This file should be #included by
    another source file, after having #included gcov-io.h.  */
 
 
 /* Routines declared in gcov-io.h.  This file should be #included by
    another source file, after having #included gcov-io.h.  */
 
+#if !IN_GCOV
+static void gcov_write_block (unsigned);
+static gcov_unsigned_t *gcov_write_words (unsigned);
+#endif
+static const gcov_unsigned_t *gcov_read_words (unsigned);
+#if !IN_LIBGCOV
+static void gcov_allocate (unsigned);
+#endif
+
+static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
+{
+#if !IN_LIBGCOV
+  if (gcov_var.endian)
+    {
+      value = (value >> 16) | (value << 16);
+      value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
+    }
+#endif
+  return value;
+}
+
 /* Open a gcov file. NAME is the name of the file to open and MODE
    indicates whether a new file should be created, or an existing file
 /* Open a gcov file. NAME is the name of the file to open and MODE
    indicates whether a new file should be created, or an existing file
-   opened for modification. If MODE is >= 0 an existing file will be
-   opened, if possible, and if MODE is <= 0, a new file will be
-   created. Use MODE=0 to attempt to reopen an existing file and then
-   fall back on creating a new one.  Return zero on failure, >0 on
-   opening an existing file and <0 on creating a new one.  */
+   opened. If MODE is >= 0 an existing file will be opened, if
+   possible, and if MODE is <= 0, a new file will be created. Use
+   MODE=0 to attempt to reopen an existing file and then fall back on
+   creating a new one.  If MODE < 0, the file will be opened in
+   read-only mode.  Otherwise it will be opened for modification.
+   Return zero on failure, >0 on opening an existing file and <0 on
+   creating a new one.  */
 
 GCOV_LINKAGE int
 
 GCOV_LINKAGE int
+#if IN_LIBGCOV
+gcov_open (const char *name)
+#else
 gcov_open (const char *name, int mode)
 gcov_open (const char *name, int mode)
+#endif
 {
 {
-  int result = 1;
-  size_t alloc = 1024;
-#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
+#if IN_LIBGCOV
+  const int mode = 0;
+#endif
+#if GCOV_LOCKED
   struct flock s_flock;
   struct flock s_flock;
+  int fd;
 
 
-  s_flock.l_type = F_WRLCK;
   s_flock.l_whence = SEEK_SET;
   s_flock.l_start = 0;
   s_flock.l_len = 0; /* Until EOF.  */
   s_flock.l_pid = getpid ();
 #endif
   s_flock.l_whence = SEEK_SET;
   s_flock.l_start = 0;
   s_flock.l_len = 0; /* Until EOF.  */
   s_flock.l_pid = getpid ();
 #endif
-  
-  if (gcov_var.file)
-    abort ();
-  gcov_var.position = gcov_var.length = 0;
-  gcov_var.error = gcov_var.modified = 0;
-  if (mode >= 0)
-    gcov_var.file = fopen (name, "r+b");
-  if (!gcov_var.file && mode <= 0)
+
+  gcc_assert (!gcov_var.file);
+  gcov_var.start = 0;
+  gcov_var.offset = gcov_var.length = 0;
+  gcov_var.overread = -1u;
+  gcov_var.error = 0;
+#if !IN_LIBGCOV
+  gcov_var.endian = 0;
+#endif
+#if GCOV_LOCKED
+  if (mode > 0)
     {
     {
-      result = -1;
-      gcov_var.file = fopen (name, "w+b");
+      /* Read-only mode - acquire a read-lock.  */
+      s_flock.l_type = F_RDLCK;
+      fd = open (name, O_RDONLY);
     }
     }
-  if (!gcov_var.file)
+  else
+    {
+      /* Write mode - acquire a write-lock.  */
+      s_flock.l_type = F_WRLCK;
+      fd = open (name, O_RDWR | O_CREAT, 0666);
+    }
+  if (fd < 0)
     return 0;
 
     return 0;
 
-#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
-  while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock)
-        && errno == EINTR)
+  while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
     continue;
     continue;
-#endif
 
 
-  if (result >= 0)
+  gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
+
+  if (!gcov_var.file)
     {
     {
-      if (fseek (gcov_var.file, 0, SEEK_END))
-       {
-         fclose (gcov_var.file);
-         gcov_var.file = 0;
-         return 0;
-       }
-      gcov_var.length = ftell (gcov_var.file);
-      fseek (gcov_var.file, 0, SEEK_SET);
-      alloc += gcov_var.length;
+      close (fd);
+      return 0;
     }
     }
-  if (alloc > gcov_var.alloc)
+
+  if (mode > 0)
+    gcov_var.mode = 1;
+  else if (mode == 0)
     {
     {
-      if (gcov_var.buffer)
-       free (gcov_var.buffer);
-      gcov_var.alloc = alloc;
-#if IN_LIBGCOV
-      gcov_var.buffer = malloc (gcov_var.alloc);
-      if (!gcov_var.buffer)
+      struct stat st;
+
+      if (fstat (fd, &st) < 0)
        {
          fclose (gcov_var.file);
          gcov_var.file = 0;
        {
          fclose (gcov_var.file);
          gcov_var.file = 0;
-         gcov_var.length = 0;
-         gcov_var.alloc = 0;
          return 0;
        }
          return 0;
        }
-#else
-      gcov_var.buffer = xmalloc (gcov_var.alloc);
-#endif
+      if (st.st_size != 0)
+       gcov_var.mode = 1;
+      else
+       gcov_var.mode = mode * 2 + 1;
     }
     }
-  if (result >= 0
-      && fread (gcov_var.buffer, gcov_var.length, 1, gcov_var.file) != 1)
+  else
+    gcov_var.mode = mode * 2 + 1;
+#else
+  if (mode >= 0)
+    gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
+
+  if (gcov_var.file)
+    gcov_var.mode = 1;
+  else if (mode <= 0)
     {
     {
-      fclose (gcov_var.file);
-      gcov_var.file = 0;
-      gcov_var.length = 0;
-      return 0;
+      gcov_var.file = fopen (name, "w+b");
+      if (gcov_var.file)
+       gcov_var.mode = mode * 2 + 1;
     }
     }
-  return result;
+  if (!gcov_var.file)
+    return 0;
+#endif
+
+  setbuf (gcov_var.file, (char *)0);
+
+  return 1;
 }
 
 /* Close the current gcov file. Flushes data to disk. Returns nonzero
    on failure or error flag set.  */
 
 GCOV_LINKAGE int
 }
 
 /* Close the current gcov file. Flushes data to disk. Returns nonzero
    on failure or error flag set.  */
 
 GCOV_LINKAGE int
-gcov_close ()
+gcov_close (void)
 {
 {
-  int result = 0;
-  
   if (gcov_var.file)
     {
   if (gcov_var.file)
     {
-      if (gcov_var.modified
-         && (fseek (gcov_var.file, 0, SEEK_SET)
-             || fwrite (gcov_var.buffer, gcov_var.length,
-                        1, gcov_var.file) != 1))
-       result = 1;
+#if !IN_GCOV
+      if (gcov_var.offset && gcov_var.mode < 0)
+       gcov_write_block (gcov_var.offset);
+#endif
       fclose (gcov_var.file);
       gcov_var.file = 0;
       gcov_var.length = 0;
       fclose (gcov_var.file);
       gcov_var.file = 0;
       gcov_var.length = 0;
@@ -133,47 +170,85 @@ gcov_close ()
   gcov_var.alloc = 0;
   gcov_var.buffer = 0;
 #endif
   gcov_var.alloc = 0;
   gcov_var.buffer = 0;
 #endif
-  return result ? 1 : gcov_var.error;
+  gcov_var.mode = 0;
+  return gcov_var.error;
+}
+
+#if !IN_LIBGCOV
+/* Check if MAGIC is EXPECTED. Use it to determine endianness of the
+   file. Returns +1 for same endian, -1 for other endian and zero for
+   not EXPECTED.  */
+
+GCOV_LINKAGE int
+gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
+{
+  if (magic == expected)
+    return 1;
+  magic = (magic >> 16) | (magic << 16);
+  magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
+  if (magic == expected)
+    {
+      gcov_var.endian = 1;
+      return -1;
+    }
+  return 0;
+}
+#endif
+
+#if !IN_LIBGCOV
+static void
+gcov_allocate (unsigned length)
+{
+  size_t new_size = gcov_var.alloc;
+
+  if (!new_size)
+    new_size = GCOV_BLOCK_SIZE;
+  new_size += length;
+  new_size *= 2;
+
+  gcov_var.alloc = new_size;
+  gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
 }
 }
+#endif
 
 #if !IN_GCOV
 
 #if !IN_GCOV
+/* Write out the current block, if needs be.  */
+
+static void
+gcov_write_block (unsigned size)
+{
+  if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
+    gcov_var.error = 1;
+  gcov_var.start += size;
+  gcov_var.offset -= size;
+}
+
 /* Allocate space to write BYTES bytes to the gcov file. Return a
    pointer to those bytes, or NULL on failure.  */
 
 /* Allocate space to write BYTES bytes to the gcov file. Return a
    pointer to those bytes, or NULL on failure.  */
 
-GCOV_LINKAGE unsigned char *
-gcov_write_bytes (unsigned bytes)
+static gcov_unsigned_t *
+gcov_write_words (unsigned words)
 {
 {
-  char unsigned *result;
-
-  if (gcov_var.position + bytes > gcov_var.alloc)
-    {
-      size_t new_size = (gcov_var.alloc + bytes) * 3 / 2;
+  gcov_unsigned_t *result;
 
 
-      if (!gcov_var.buffer)
-       return 0;
+  gcc_assert (gcov_var.mode < 0);
 #if IN_LIBGCOV
 #if IN_LIBGCOV
-      result = realloc (gcov_var.buffer, new_size);
-      if (!result)
+  if (gcov_var.offset >= GCOV_BLOCK_SIZE)
+    {
+      gcov_write_block (GCOV_BLOCK_SIZE);
+      if (gcov_var.offset)
        {
        {
-         free (gcov_var.buffer);
-         gcov_var.buffer = 0;
-         gcov_var.alloc = 0;
-         gcov_var.position = gcov_var.length = 0;
-         gcov_var.error = 1;
-         return 0;
+         gcc_assert (gcov_var.offset == 1);
+         memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
        }
        }
+    }
 #else
 #else
-      result = xrealloc (gcov_var.buffer, new_size);
+  if (gcov_var.offset + words > gcov_var.alloc)
+    gcov_allocate (gcov_var.offset + words);
 #endif
 #endif
-      gcov_var.alloc = new_size;
-      gcov_var.buffer = result;
-    }
-  
-  result = &gcov_var.buffer[gcov_var.position];
-  gcov_var.position += bytes;
-  gcov_var.modified = 1;
-  if (gcov_var.position > gcov_var.length)
-    gcov_var.length = gcov_var.position;
+  result = &gcov_var.buffer[gcov_var.offset];
+  gcov_var.offset += words;
+
   return result;
 }
 
   return result;
 }
 
@@ -181,22 +256,11 @@ gcov_write_bytes (unsigned bytes)
    appropriately.  */
 
 GCOV_LINKAGE void
    appropriately.  */
 
 GCOV_LINKAGE void
-gcov_write_unsigned (unsigned value)
+gcov_write_unsigned (gcov_unsigned_t value)
 {
 {
-  unsigned char *buffer = gcov_write_bytes (4);
-  unsigned ix;
+  gcov_unsigned_t *buffer = gcov_write_words (1);
 
 
-  if (!buffer)
-    return;
-  for (ix = 4; ix--; )
-    {
-      buffer[ix] = value;
-      value >>= 8;
-    }
-  if (sizeof (value) > 4 && value)
-    gcov_var.error = -1;
-
-  return;
+  buffer[0] = value;
 }
 
 /* Write counter VALUE to coverage file.  Sets error flag
 }
 
 /* Write counter VALUE to coverage file.  Sets error flag
@@ -206,22 +270,17 @@ gcov_write_unsigned (unsigned value)
 GCOV_LINKAGE void
 gcov_write_counter (gcov_type value)
 {
 GCOV_LINKAGE void
 gcov_write_counter (gcov_type value)
 {
-  unsigned char *buffer = gcov_write_bytes (8);
-  unsigned ix;
+  gcov_unsigned_t *buffer = gcov_write_words (2);
 
 
-  if (!buffer)
-    return;
-  for (ix = 8; ix--; )
-    {
-      buffer[ix] = value;
-      value >>= 8;
-    }
-  if ((sizeof (value) > 8 && value) || value < 0)
-    gcov_var.error = -1;
-  return;
+  buffer[0] = (gcov_unsigned_t) value;
+  if (sizeof (value) > sizeof (gcov_unsigned_t))
+    buffer[1] = (gcov_unsigned_t) (value >> 32);
+  else
+    buffer[1] = 0;
 }
 #endif /* IN_LIBGCOV */
 
 }
 #endif /* IN_LIBGCOV */
 
+#if !IN_LIBGCOV
 /* Write STRING to coverage file.  Sets error flag on file
    error, overflow flag on overflow */
 
 /* Write STRING to coverage file.  Sets error flag on file
    error, overflow flag on overflow */
 
@@ -229,50 +288,36 @@ GCOV_LINKAGE void
 gcov_write_string (const char *string)
 {
   unsigned length = 0;
 gcov_write_string (const char *string)
 {
   unsigned length = 0;
-  unsigned pad = 0;
-  unsigned rem = 0;
-  unsigned char *buffer;
+  unsigned alloc = 0;
+  gcov_unsigned_t *buffer;
 
   if (string)
     {
       length = strlen (string);
 
   if (string)
     {
       length = strlen (string);
-      rem = 4 - (length & 3);
-    }
-  
-  buffer = gcov_write_bytes (4 + length + rem);
-  if (buffer)
-    {
-      unsigned ix;
-      unsigned value = length;
-      
-      for (ix = 4; ix--; )
-       {
-         buffer[ix] = value;
-         value >>= 8;
-       }
-      memcpy (buffer + 4, string, length);
-      memcpy (buffer + 4 + length, &pad, rem);
+      alloc = (length + 4) >> 2;
     }
     }
+
+  buffer = gcov_write_words (1 + alloc);
+
+  buffer[0] = alloc;
+  buffer[alloc] = 0;
+  memcpy (&buffer[1], string, length);
 }
 }
+#endif
 
 
+#if !IN_LIBGCOV
 /* Write a tag TAG and reserve space for the record length. Return a
    value to be used for gcov_write_length.  */
 
 /* Write a tag TAG and reserve space for the record length. Return a
    value to be used for gcov_write_length.  */
 
-GCOV_LINKAGE unsigned long
-gcov_write_tag (unsigned tag)
+GCOV_LINKAGE gcov_position_t
+gcov_write_tag (gcov_unsigned_t tag)
 {
 {
-  unsigned long result = gcov_var.position;
-  unsigned char *buffer = gcov_write_bytes (8);
-  unsigned ix;
+  gcov_position_t result = gcov_var.start + gcov_var.offset;
+  gcov_unsigned_t *buffer = gcov_write_words (2);
+
+  buffer[0] = tag;
+  buffer[1] = 0;
 
 
-  if (!buffer)
-    return 0;
-  for (ix = 4; ix--; )
-    {
-      buffer[ix] = tag;
-      tag >>= 8;
-    }
-  memset (buffer + 4, 0, 4);
   return result;
 }
 
   return result;
 }
 
@@ -282,36 +327,48 @@ gcov_write_tag (unsigned tag)
    overflow.  */
 
 GCOV_LINKAGE void
    overflow.  */
 
 GCOV_LINKAGE void
-gcov_write_length (unsigned long position)
+gcov_write_length (gcov_position_t position)
 {
 {
-  if (position)
-    {
-      unsigned length = gcov_var.position - position - 8;
-      unsigned char *buffer = &gcov_var.buffer[position + 4];
-      unsigned ix;
-      
-      for (ix = 4; ix--; )
-       {
-         buffer[ix] = length;
-         length >>= 8;
-       }
-    }
+  unsigned offset;
+  gcov_unsigned_t length;
+  gcov_unsigned_t *buffer;
+
+  gcc_assert (gcov_var.mode < 0);
+  gcc_assert (position + 2 <= gcov_var.start + gcov_var.offset);
+  gcc_assert (position >= gcov_var.start);
+  offset = position - gcov_var.start;
+  length = gcov_var.offset - offset - 2;
+  buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
+  buffer[1] = length;
+  if (gcov_var.offset >= GCOV_BLOCK_SIZE)
+    gcov_write_block (gcov_var.offset);
 }
 
 }
 
-#if IN_LIBGCOV
-/* Write a summary structure to the gcov file.  Return non-zero on
+#else /* IN_LIBGCOV */
+
+/* Write a tag TAG and length LENGTH.  */
+
+GCOV_LINKAGE void
+gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
+{
+  gcov_unsigned_t *buffer = gcov_write_words (2);
+
+  buffer[0] = tag;
+  buffer[1] = length;
+}
+
+/* Write a summary structure to the gcov file.  Return nonzero on
    overflow.  */
 
 GCOV_LINKAGE void
    overflow.  */
 
 GCOV_LINKAGE void
-gcov_write_summary (unsigned tag, const struct gcov_summary *summary)
+gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
 {
   unsigned ix;
   const struct gcov_ctr_summary *csum;
 {
   unsigned ix;
   const struct gcov_ctr_summary *csum;
-  unsigned long base;
 
 
-  base = gcov_write_tag (tag);
+  gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
   gcov_write_unsigned (summary->checksum);
   gcov_write_unsigned (summary->checksum);
-  for (csum = summary->ctrs, ix = GCOV_COUNTERS; ix--; csum++)
+  for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
     {
       gcov_write_unsigned (csum->num);
       gcov_write_unsigned (csum->runs);
     {
       gcov_write_unsigned (csum->num);
       gcov_write_unsigned (csum->runs);
@@ -319,51 +376,70 @@ gcov_write_summary (unsigned tag, const struct gcov_summary *summary)
       gcov_write_counter (csum->run_max);
       gcov_write_counter (csum->sum_max);
     }
       gcov_write_counter (csum->run_max);
       gcov_write_counter (csum->sum_max);
     }
-  gcov_write_length (base);
 }
 #endif /* IN_LIBGCOV */
 
 #endif /*!IN_GCOV */
 
 /* Return a pointer to read BYTES bytes from the gcov file. Returns
 }
 #endif /* IN_LIBGCOV */
 
 #endif /*!IN_GCOV */
 
 /* Return a pointer to read BYTES bytes from the gcov file. Returns
-   NULL on failure (read past EOF). */
+   NULL on failure (read past EOF).  */
 
 
-GCOV_LINKAGE const unsigned char *
-gcov_read_bytes (unsigned bytes)
+static const gcov_unsigned_t *
+gcov_read_words (unsigned words)
 {
 {
-  const unsigned char *result;
-  
-  if (gcov_var.position + bytes > gcov_var.length)
+  const gcov_unsigned_t *result;
+  unsigned excess = gcov_var.length - gcov_var.offset;
+
+  gcc_assert (gcov_var.mode > 0);
+  if (excess < words)
     {
     {
-      gcov_var.error = 1;
-      return 0;
+      gcov_var.start += gcov_var.offset;
+#if IN_LIBGCOV
+      if (excess)
+       {
+         gcc_assert (excess == 1);
+         memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
+       }
+#else
+      memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4);
+#endif
+      gcov_var.offset = 0;
+      gcov_var.length = excess;
+#if IN_LIBGCOV
+      gcc_assert (!gcov_var.length || gcov_var.length == 1);
+      excess = GCOV_BLOCK_SIZE;
+#else
+      if (gcov_var.length + words > gcov_var.alloc)
+       gcov_allocate (gcov_var.length + words);
+      excess = gcov_var.alloc - gcov_var.length;
+#endif
+      excess = fread (gcov_var.buffer + gcov_var.length,
+                     1, excess << 2, gcov_var.file) >> 2;
+      gcov_var.length += excess;
+      if (gcov_var.length < words)
+       {
+         gcov_var.overread += words - gcov_var.length;
+         gcov_var.length = 0;
+         return 0;
+       }
     }
     }
-  
-  result = &gcov_var.buffer[gcov_var.position];
-  gcov_var.position += bytes;
+  result = &gcov_var.buffer[gcov_var.offset];
+  gcov_var.offset += words;
   return result;
 }
 
 /* Read unsigned value from a coverage file. Sets error flag on file
    error, overflow flag on overflow */
 
   return result;
 }
 
 /* Read unsigned value from a coverage file. Sets error flag on file
    error, overflow flag on overflow */
 
-GCOV_LINKAGE unsigned
-gcov_read_unsigned ()
+GCOV_LINKAGE gcov_unsigned_t
+gcov_read_unsigned (void)
 {
 {
-  unsigned value = 0;
-  unsigned ix;
-  const unsigned char *buffer = gcov_read_bytes (4);
+  gcov_unsigned_t value;
+  const gcov_unsigned_t *buffer = gcov_read_words (1);
 
   if (!buffer)
     return 0;
 
   if (!buffer)
     return 0;
-  for (ix = sizeof (value); ix < 4; ix++)
-    if (buffer[ix])
-      gcov_var.error = -1;
-  for (ix = 0; ix != 4; ix++)
-    {
-      value <<= 8;
-      value |= buffer[ix];
-    }
+  value = from_file (buffer[0]);
   return value;
 }
 
   return value;
 }
 
@@ -371,24 +447,19 @@ gcov_read_unsigned ()
    error, overflow flag on overflow */
 
 GCOV_LINKAGE gcov_type
    error, overflow flag on overflow */
 
 GCOV_LINKAGE gcov_type
-gcov_read_counter ()
+gcov_read_counter (void)
 {
 {
-  gcov_type value = 0;
-  unsigned ix;
-  const unsigned char *buffer = gcov_read_bytes (8);
+  gcov_type value;
+  const gcov_unsigned_t *buffer = gcov_read_words (2);
 
   if (!buffer)
     return 0;
 
   if (!buffer)
     return 0;
-  for (ix = sizeof (value); ix < 8; ix++)
-    if (buffer[ix])
-      gcov_var.error = -1;
-  for (ix = 0; ix != 8; ix++)
-    {
-      value <<= 8;
-      value |= buffer[ix];
-    }
-  if (value < 0)
+  value = from_file (buffer[0]);
+  if (sizeof (value) > sizeof (gcov_unsigned_t))
+    value |= ((gcov_type) from_file (buffer[1])) << 32;
+  else if (buffer[1])
     gcov_var.error = -1;
     gcov_var.error = -1;
+
   return value;
 }
 
   return value;
 }
 
@@ -396,26 +467,27 @@ gcov_read_counter ()
    buffer, or NULL on empty string. You must copy the string before
    calling another gcov function.  */
 
    buffer, or NULL on empty string. You must copy the string before
    calling another gcov function.  */
 
+#if !IN_LIBGCOV
 GCOV_LINKAGE const char *
 GCOV_LINKAGE const char *
-gcov_read_string ()
+gcov_read_string (void)
 {
   unsigned length = gcov_read_unsigned ();
 {
   unsigned length = gcov_read_unsigned ();
-  
+
   if (!length)
     return 0;
 
   if (!length)
     return 0;
 
-  length += 4 - (length & 3);
-  return (const char *) gcov_read_bytes (length);
+  return (const char *) gcov_read_words (length);
 }
 }
+#endif
 
 GCOV_LINKAGE void
 gcov_read_summary (struct gcov_summary *summary)
 {
   unsigned ix;
   struct gcov_ctr_summary *csum;
 
 GCOV_LINKAGE void
 gcov_read_summary (struct gcov_summary *summary)
 {
   unsigned ix;
   struct gcov_ctr_summary *csum;
-  
+
   summary->checksum = gcov_read_unsigned ();
   summary->checksum = gcov_read_unsigned ();
-  for (csum = summary->ctrs, ix = GCOV_COUNTERS; ix--; csum++)
+  for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++)
     {
       csum->num = gcov_read_unsigned ();
       csum->runs = gcov_read_unsigned ();
     {
       csum->num = gcov_read_unsigned ();
       csum->runs = gcov_read_unsigned ();
@@ -425,14 +497,48 @@ gcov_read_summary (struct gcov_summary *summary)
     }
 }
 
     }
 }
 
+#if !IN_LIBGCOV
+/* Reset to a known position.  BASE should have been obtained from
+   gcov_position, LENGTH should be a record length.  */
+
+GCOV_LINKAGE void
+gcov_sync (gcov_position_t base, gcov_unsigned_t length)
+{
+  gcc_assert (gcov_var.mode > 0);
+  base += length;
+  if (base - gcov_var.start <= gcov_var.length)
+    gcov_var.offset = base - gcov_var.start;
+  else
+    {
+      gcov_var.offset = gcov_var.length = 0;
+      fseek (gcov_var.file, base << 2, SEEK_SET);
+      gcov_var.start = ftell (gcov_var.file) >> 2;
+    }
+}
+#endif
+
+#if IN_LIBGCOV
+/* Move to a given position in a gcov file.  */
+
+GCOV_LINKAGE void
+gcov_seek (gcov_position_t base)
+{
+  gcc_assert (gcov_var.mode < 0);
+  if (gcov_var.offset)
+    gcov_write_block (gcov_var.offset);
+  fseek (gcov_var.file, base << 2, SEEK_SET);
+  gcov_var.start = ftell (gcov_var.file) >> 2;
+}
+#endif
+
 #if IN_GCOV > 0
 /* Return the modification time of the current gcov file.  */
 
 GCOV_LINKAGE time_t
 #if IN_GCOV > 0
 /* Return the modification time of the current gcov file.  */
 
 GCOV_LINKAGE time_t
-gcov_time ()
+gcov_time (void)
 {
   struct stat status;
 {
   struct stat status;
-  
+
   if (fstat (fileno (gcov_var.file), &status))
     return 0;
   else
   if (fstat (fileno (gcov_var.file), &status))
     return 0;
   else