diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c
index 454e224c95fdc87fab954974050a9966535a94d3..7bda4aedfc89332c26a84ffac58b94678db685f3 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O2 -ldl" } */
+/* { dg-options "-O2 -ldl -fprofile-correction" } */
 
 #define _GNU_SOURCE
 #include <stdio.h>
diff --git a/gcc/testsuite/gcc.dg/tree-prof/pr97461.c b/gcc/testsuite/gcc.dg/tree-prof/pr97461.c
index 213fac9af042203eb5509efdcc740f53cab62c45..f684be4d80f4b0d3207c0e80867f513e05d9ff37 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/pr97461.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/pr97461.c
@@ -1,5 +1,5 @@
 /* PR gcov-profile/97461 */
-/* { dg-options "-O2 -ldl" } */
+/* { dg-options "-O2 -ldl -fprofile-correction" } */
 
 #define _GNU_SOURCE
 
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index 914623501329420efaea9e0f475601a66c1c90e1..a1338b6e5255997e817a552718b02b6ebedc3a95 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -42,6 +42,10 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
 #include <sys/stat.h>
 #endif
 
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
 #ifdef L_gcov
 
 /* A utility function for outputting errors.  */
@@ -334,30 +338,65 @@ read_error:
   return -1;
 }
 
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
 /* Store all TOP N counters where each has a dynamic length.  */
 
 static void
-write_top_counters (const struct gcov_ctr_info *ci_ptr,
-		    unsigned t_ix,
-		    gcov_unsigned_t n_counts)
+write_topn_counters (const struct gcov_ctr_info *ci_ptr,
+		     unsigned t_ix,
+		     gcov_unsigned_t n_counts)
 {
   unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
   gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
+
+  /* It can happen in a multi-threaded environment that number of counters is
+     different from the size of the corresponding linked lists.  */
+#define LIST_SIZE_MIN_LENGTH 4 * 1024
+
+  static unsigned *list_sizes = NULL;
+  static unsigned list_size_length = 0;
+
+  if (list_sizes == NULL || counters > list_size_length)
+    {
+      list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
+#if HAVE_SYS_MMAN_H
+      list_sizes
+	= (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
+#endif
+
+      /* Malloc fallback.  */
+      if (list_sizes == NULL)
+	list_sizes = (unsigned *)xmalloc (list_size_length * sizeof (unsigned));
+    }
+
+  memset (list_sizes, 0, counters * sizeof (unsigned));
   unsigned pair_total = 0;
+
   for (unsigned i = 0; i < counters; i++)
-    pair_total += ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
+    {
+      gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
+      for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
+	   node != NULL; node = node->next)
+	{
+	  ++pair_total;
+	  ++list_sizes[i];
+	}
+    }
+
   unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
   gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
 			 GCOV_TAG_COUNTER_LENGTH (disk_size));
 
   for (unsigned i = 0; i < counters; i++)
     {
-      gcov_type pair_count = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
       gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);
-      gcov_write_counter (pair_count);
+      gcov_write_counter (list_sizes[i]);
       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
+
+      unsigned j = 0;
       for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
-	   node != NULL; node = node->next)
+	   j < list_sizes[i]; node = node->next, j++)
 	{
 	  gcov_write_counter (node->value);
 	  gcov_write_counter (node->count);
@@ -425,7 +464,7 @@ write_one_data (const struct gcov_info *gi_ptr,
 	  n_counts = ci_ptr->num;
 
 	  if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
-	    write_top_counters (ci_ptr, t_ix, n_counts);
+	    write_topn_counters (ci_ptr, t_ix, n_counts);
 	  else
 	    {
 	      /* Do not stream when all counters are zero.  */
diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h
index acdb7cd150071a9251db0805f07040bc17b9f602..2780cc098c86dd709a5b02cb07a5a7ba168411b6 100644
--- a/libgcc/libgcov.h
+++ b/libgcc/libgcov.h
@@ -409,6 +409,19 @@ gcov_counter_add (gcov_type *counter, gcov_type value,
     *counter += value;
 }
 
+#if HAVE_SYS_MMAN_H
+
+/* Allocate LENGTH with mmap function.  */
+
+static inline void *
+malloc_mmap (size_t length)
+{
+  return mmap (NULL, length, PROT_READ | PROT_WRITE,
+	       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+}
+
+#endif
+
 /* Allocate gcov_kvp from statically pre-allocated pool,
    or use heap otherwise.  */
 
@@ -424,9 +437,7 @@ allocate_gcov_kvp (void)
   if (__gcov_kvp_dynamic_pool == NULL
       || __gcov_kvp_dynamic_pool_index >= __gcov_kvp_dynamic_pool_size)
     {
-      void *ptr = mmap (NULL, MMAP_CHUNK_SIZE,
-			PROT_READ | PROT_WRITE,
-			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+      void *ptr = malloc_mmap (MMAP_CHUNK_SIZE);
       if (ptr != MAP_FAILED)
 	{
 	  __gcov_kvp_dynamic_pool = ptr;