1 /* Routines required for instrumenting a program.  */
2 /* Compile this one with gcc.  */
3 /* Copyright (C) 1989-2021 Free Software Foundation, Inc.
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11 
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 Under Section 7 of GPL version 3, you are granted additional
18 permissions described in the GCC Runtime Library Exception, version
19 3.1, as published by the Free Software Foundation.
20 
21 You should have received a copy of the GNU General Public License and
22 a copy of the GCC Runtime Library Exception along with this program;
23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24 <http://www.gnu.org/licenses/>.  */
25 
26 #include "libgcov.h"
27 #include "gcov-io.h"
28 
29 #if defined(inhibit_libc)
30 /* If libc and its header files are not available, provide dummy functions.  */
31 
32 #if defined(L_gcov)
__gcov_init(struct gcov_info * p)33 void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
34 #endif
35 
36 #else /* inhibit_libc */
37 
38 #include <string.h>
39 #if GCOV_LOCKED
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <sys/stat.h>
43 #endif
44 
45 #if HAVE_SYS_MMAN_H
46 #include <sys/mman.h>
47 #endif
48 
49 #ifdef L_gcov
50 
51 /* A utility function for outputting errors.  */
52 static int gcov_error (const char *, ...);
53 
54 #if !IN_GCOV_TOOL
55 static void gcov_error_exit (void);
56 #endif
57 
58 #include "gcov-io.c"
59 
60 #define GCOV_PROF_PREFIX "libgcov profiling error:%s:"
61 
62 struct gcov_fn_buffer
63 {
64   struct gcov_fn_buffer *next;
65   unsigned fn_ix;
66   struct gcov_fn_info info;
67   /* note gcov_fn_info ends in a trailing array.  */
68 };
69 
70 struct gcov_summary_buffer
71 {
72   struct gcov_summary_buffer *next;
73   struct gcov_summary summary;
74 };
75 
76 /* A struct that bundles all the related information about the
77    gcda filename.  */
78 
79 struct gcov_filename
80 {
81   char *filename;  /* filename buffer */
82   int strip; /* leading chars to strip from filename */
83   char *prefix; /* prefix string */
84 };
85 
86 static struct gcov_fn_buffer *
free_fn_data(const struct gcov_info * gi_ptr,struct gcov_fn_buffer * buffer,unsigned limit)87 free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
88               unsigned limit)
89 {
90   struct gcov_fn_buffer *next;
91   unsigned ix, n_ctr = 0;
92 
93   if (!buffer)
94     return 0;
95   next = buffer->next;
96 
97   for (ix = 0; ix != limit; ix++)
98     if (gi_ptr->merge[ix])
99       free (buffer->info.ctrs[n_ctr++].values);
100   free (buffer);
101   return next;
102 }
103 
104 static struct gcov_fn_buffer **
buffer_fn_data(const char * filename,const struct gcov_info * gi_ptr,struct gcov_fn_buffer ** end_ptr,unsigned fn_ix)105 buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
106                 struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
107 {
108   unsigned n_ctrs = 0, ix = 0;
109   struct gcov_fn_buffer *fn_buffer;
110   unsigned len;
111 
112   for (ix = GCOV_COUNTERS; ix--;)
113     if (gi_ptr->merge[ix])
114       n_ctrs++;
115 
116   len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
117   fn_buffer = (struct gcov_fn_buffer *) xmalloc (len);
118 
119   if (!fn_buffer)
120     goto fail;
121 
122   fn_buffer->next = 0;
123   fn_buffer->fn_ix = fn_ix;
124   fn_buffer->info.ident = gcov_read_unsigned ();
125   fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
126   fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
127 
128   for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
129     {
130       gcov_unsigned_t length;
131       gcov_type *values;
132 
133       if (!gi_ptr->merge[ix])
134         continue;
135 
136       if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
137         {
138           len = 0;
139           goto fail;
140         }
141 
142       length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
143       len = length * sizeof (gcov_type);
144       values = (gcov_type *) xmalloc (len);
145       if (!values)
146         goto fail;
147 
148       fn_buffer->info.ctrs[n_ctrs].num = length;
149       fn_buffer->info.ctrs[n_ctrs].values = values;
150 
151       while (length--)
152         *values++ = gcov_read_counter ();
153       n_ctrs++;
154     }
155 
156   *end_ptr = fn_buffer;
157   return &fn_buffer->next;
158 
159 fail:
160   gcov_error (GCOV_PROF_PREFIX "Function %u %s %u \n", filename, fn_ix,
161               len ? "cannot allocate" : "counter mismatch", len ? len : ix);
162 
163   return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
164 }
165 
166 /* Convert VERSION into a string description and return the it.
167    BUFFER is used for storage of the string.  The code should be
168    aligned wit gcov-iov.c.  */
169 
170 static char *
gcov_version_string(char * buffer,char version[4])171 gcov_version_string (char *buffer, char version[4])
172 {
173   if (version[0] < 'A' || version[0] > 'Z'
174       || version[1] < '0' || version[1] > '9'
175       || version[2] < '0' || version[2] > '9')
176     sprintf (buffer, "(unknown)");
177   else
178     {
179       unsigned major = 10 * (version[0] - 'A') + (version[1] - '0');
180       unsigned minor = version[2] - '0';
181       sprintf (buffer, "%u.%u (%s)", major, minor,
182 	       version[3] == '*' ? "release" : "experimental");
183     }
184   return buffer;
185 }
186 
187 /* Check if VERSION of the info block PTR matches libgcov one.
188    Return 1 on success, or zero in case of versions mismatch.
189    If FILENAME is not NULL, its value used for reporting purposes
190    instead of value from the info block.  */
191 
192 static int
gcov_version(struct gcov_info * ptr,gcov_unsigned_t version,const char * filename)193 gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
194               const char *filename)
195 {
196   if (version != GCOV_VERSION)
197     {
198       char v[4], e[4];
199       char version_string[128], expected_string[128];
200 
201       GCOV_UNSIGNED2STRING (v, version);
202       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
203 
204       gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) "
205 		  "got %s (%.4s)\n",
206 		  filename? filename : ptr->filename,
207 		  gcov_version_string (expected_string, e), e,
208 		  gcov_version_string (version_string, v), v);
209       return 0;
210     }
211   return 1;
212 }
213 
214 /* buffer for the fn_data from another program.  */
215 static struct gcov_fn_buffer *fn_buffer;
216 
217 /* Including system dependent components. */
218 #include "libgcov-driver-system.c"
219 
220 /* This function merges counters in GI_PTR to an existing gcda file.
221    Return 0 on success.
222    Return -1 on error. In this case, caller will goto read_fatal.  */
223 
224 static int
merge_one_data(const char * filename,struct gcov_info * gi_ptr,struct gcov_summary * summary)225 merge_one_data (const char *filename,
226 		struct gcov_info *gi_ptr,
227 		struct gcov_summary *summary)
228 {
229   gcov_unsigned_t tag, length;
230   unsigned t_ix;
231   int f_ix = -1;
232   int error = 0;
233   struct gcov_fn_buffer **fn_tail = &fn_buffer;
234 
235   length = gcov_read_unsigned ();
236   if (!gcov_version (gi_ptr, length, filename))
237     return -1;
238 
239   length = gcov_read_unsigned ();
240   if (length != gi_ptr->stamp)
241     {
242       /* Read from a different compilation.  Overwrite the file.  */
243       gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
244 		  "with a different timestamp\n", filename);
245       return 0;
246     }
247 
248   tag = gcov_read_unsigned ();
249   if (tag != GCOV_TAG_OBJECT_SUMMARY)
250     goto read_mismatch;
251   length = gcov_read_unsigned ();
252   gcc_assert (length > 0);
253   gcov_read_summary (summary);
254 
255   tag = gcov_read_unsigned ();
256   /* Merge execution counts for each function.  */
257   for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
258        f_ix++, tag = gcov_read_unsigned ())
259     {
260       const struct gcov_ctr_info *ci_ptr;
261       const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
262 
263       if (tag != GCOV_TAG_FUNCTION)
264         goto read_mismatch;
265 
266       length = gcov_read_unsigned ();
267       if (!length)
268         /* This function did not appear in the other program.
269            We have nothing to merge.  */
270         continue;
271 
272       if (length != GCOV_TAG_FUNCTION_LENGTH)
273         goto read_mismatch;
274 
275       if (!gfi_ptr || gfi_ptr->key != gi_ptr)
276         {
277           /* This function appears in the other program.  We
278              need to buffer the information in order to write
279              it back out -- we'll be inserting data before
280              this point, so cannot simply keep the data in the
281              file.  */
282           fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix);
283           if (!fn_tail)
284             goto read_mismatch;
285           continue;
286         }
287 
288       length = gcov_read_unsigned ();
289       if (length != gfi_ptr->ident)
290         goto read_mismatch;
291 
292       length = gcov_read_unsigned ();
293       if (length != gfi_ptr->lineno_checksum)
294         goto read_mismatch;
295 
296       length = gcov_read_unsigned ();
297       if (length != gfi_ptr->cfg_checksum)
298         goto read_mismatch;
299 
300       ci_ptr = gfi_ptr->ctrs;
301       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
302         {
303           gcov_merge_fn merge = gi_ptr->merge[t_ix];
304 
305           if (!merge)
306             continue;
307 
308 	  tag = gcov_read_unsigned ();
309 	  int read_length = (int)gcov_read_unsigned ();
310 	  length = abs (read_length);
311 	  if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
312 	      || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)
313 		  && t_ix != GCOV_COUNTER_V_TOPN
314 		  && t_ix != GCOV_COUNTER_V_INDIR))
315 	    goto read_mismatch;
316 	  /* Merging with all zero counters does not make sense.  */
317 	  if (read_length > 0)
318 	    (*merge) (ci_ptr->values, ci_ptr->num);
319 	  ci_ptr++;
320 	}
321       if ((error = gcov_is_error ()))
322 	goto read_error;
323     }
324 
325   if (tag)
326     {
327     read_mismatch:;
328       gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n",
329                   filename, f_ix >= 0 ? "function" : "summary",
330                   f_ix < 0 ? -1 - f_ix : f_ix);
331       return -1;
332     }
333   return 0;
334 
335 read_error:
336   gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename,
337               error < 0 ? "Overflow": "Error");
338   return -1;
339 }
340 
341 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
342 
343 /* Store all TOP N counters where each has a dynamic length.  */
344 
345 static void
write_topn_counters(const struct gcov_ctr_info * ci_ptr,unsigned t_ix,gcov_unsigned_t n_counts)346 write_topn_counters (const struct gcov_ctr_info *ci_ptr,
347 		     unsigned t_ix,
348 		     gcov_unsigned_t n_counts)
349 {
350   unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
351   gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
352 
353   /* It can happen in a multi-threaded environment that number of counters is
354      different from the size of the corresponding linked lists.  */
355 #define LIST_SIZE_MIN_LENGTH 4 * 1024
356 
357   static unsigned *list_sizes = NULL;
358   static unsigned list_size_length = 0;
359 
360   if (list_sizes == NULL || counters > list_size_length)
361     {
362       list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
363 #if HAVE_SYS_MMAN_H
364       list_sizes
365 	= (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
366 #endif
367 
368       /* Malloc fallback.  */
369       if (list_sizes == NULL)
370 	list_sizes = (unsigned *)xmalloc (list_size_length * sizeof (unsigned));
371     }
372 
373   memset (list_sizes, 0, counters * sizeof (unsigned));
374   unsigned pair_total = 0;
375 
376   for (unsigned i = 0; i < counters; i++)
377     {
378       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
379       for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
380 	   node != NULL; node = node->next)
381 	{
382 	  ++pair_total;
383 	  ++list_sizes[i];
384 	}
385     }
386 
387   unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
388   gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
389 			 GCOV_TAG_COUNTER_LENGTH (disk_size));
390 
391   for (unsigned i = 0; i < counters; i++)
392     {
393       gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);
394       gcov_write_counter (list_sizes[i]);
395       gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
396 
397       unsigned j = 0;
398       for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
399 	   j < list_sizes[i]; node = node->next, j++)
400 	{
401 	  gcov_write_counter (node->value);
402 	  gcov_write_counter (node->count);
403 	}
404     }
405 }
406 
407 /* Write counters in GI_PTR and the summary in PRG to a gcda file. In
408    the case of appending to an existing file, SUMMARY_POS will be non-zero.
409    We will write the file starting from SUMMAY_POS.  */
410 
411 static void
write_one_data(const struct gcov_info * gi_ptr,const struct gcov_summary * prg_p)412 write_one_data (const struct gcov_info *gi_ptr,
413 		const struct gcov_summary *prg_p)
414 {
415   unsigned f_ix;
416 
417   gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
418   gcov_write_unsigned (gi_ptr->stamp);
419 
420   /* Generate whole program statistics.  */
421   gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
422 
423   /* Write execution counts for each function.  */
424   for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
425     {
426       unsigned buffered = 0;
427       const struct gcov_fn_info *gfi_ptr;
428       const struct gcov_ctr_info *ci_ptr;
429       gcov_unsigned_t length;
430       unsigned t_ix;
431 
432       if (fn_buffer && fn_buffer->fn_ix == f_ix)
433         {
434           /* Buffered data from another program.  */
435           buffered = 1;
436           gfi_ptr = &fn_buffer->info;
437           length = GCOV_TAG_FUNCTION_LENGTH;
438         }
439       else
440         {
441           gfi_ptr = gi_ptr->functions[f_ix];
442           if (gfi_ptr && gfi_ptr->key == gi_ptr)
443             length = GCOV_TAG_FUNCTION_LENGTH;
444           else
445                 length = 0;
446         }
447 
448       gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
449       if (!length)
450         continue;
451 
452       gcov_write_unsigned (gfi_ptr->ident);
453       gcov_write_unsigned (gfi_ptr->lineno_checksum);
454       gcov_write_unsigned (gfi_ptr->cfg_checksum);
455 
456       ci_ptr = gfi_ptr->ctrs;
457       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
458         {
459 	  gcov_position_t n_counts;
460 
461 	  if (!gi_ptr->merge[t_ix])
462 	    continue;
463 
464 	  n_counts = ci_ptr->num;
465 
466 	  if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
467 	    write_topn_counters (ci_ptr, t_ix, n_counts);
468 	  else
469 	    {
470 	      /* Do not stream when all counters are zero.  */
471 	      int all_zeros = 1;
472 	      for (unsigned i = 0; i < n_counts; i++)
473 		if (ci_ptr->values[i] != 0)
474 		  {
475 		    all_zeros = 0;
476 		    break;
477 		  }
478 
479 	      if (all_zeros)
480 		gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
481 				       GCOV_TAG_COUNTER_LENGTH (-n_counts));
482 	      else
483 		{
484 		  gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
485 					 GCOV_TAG_COUNTER_LENGTH (n_counts));
486 		  for (unsigned i = 0; i < n_counts; i++)
487 		    gcov_write_counter (ci_ptr->values[i]);
488 		}
489 	    }
490 
491 	  ci_ptr++;
492 	}
493       if (buffered)
494         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
495     }
496 
497   gcov_write_unsigned (0);
498 }
499 
500 /* Dump the coverage counts for one gcov_info object. We merge with existing
501    counts when possible, to avoid growing the .da files ad infinitum. We use
502    this program's checksum to make sure we only accumulate whole program
503    statistics to the correct summary. An object file might be embedded
504    in two separate programs, and we must keep the two program
505    summaries separate.  */
506 
507 static void
dump_one_gcov(struct gcov_info * gi_ptr,struct gcov_filename * gf,unsigned run_counted ATTRIBUTE_UNUSED,gcov_type run_max ATTRIBUTE_UNUSED)508 dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
509 	       unsigned run_counted ATTRIBUTE_UNUSED,
510 	       gcov_type run_max ATTRIBUTE_UNUSED)
511 {
512   struct gcov_summary summary = {};
513   int error;
514   gcov_unsigned_t tag;
515   fn_buffer = 0;
516 
517   error = gcov_exit_open_gcda_file (gi_ptr, gf);
518   if (error == -1)
519     return;
520 
521   tag = gcov_read_unsigned ();
522   if (tag)
523     {
524       /* Merge data from file.  */
525       if (tag != GCOV_DATA_MAGIC)
526         {
527 	  gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n",
528 		      gf->filename);
529           goto read_fatal;
530         }
531       error = merge_one_data (gf->filename, gi_ptr, &summary);
532       if (error == -1)
533         goto read_fatal;
534     }
535 
536   gcov_rewrite ();
537 
538 #if !IN_GCOV_TOOL
539   if (!run_counted)
540     {
541       summary.runs++;
542       summary.sum_max += run_max;
543     }
544 #else
545   summary = gi_ptr->summary;
546 #endif
547 
548   write_one_data (gi_ptr, &summary);
549   /* fall through */
550 
551 read_fatal:;
552   while (fn_buffer)
553     fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
554 
555   if ((error = gcov_close ()))
556     gcov_error (error  < 0 ?
557 		GCOV_PROF_PREFIX "Overflow writing\n" :
558 		GCOV_PROF_PREFIX "Error writing\n",
559                 gf->filename);
560 }
561 
562 
563 /* Dump all the coverage counts for the program. It first computes program
564    summary and then traverses gcov_list list and dumps the gcov_info
565    objects one by one.  */
566 
567 #if !IN_GCOV_TOOL
568 static
569 #endif
570 void
gcov_do_dump(struct gcov_info * list,int run_counted)571 gcov_do_dump (struct gcov_info *list, int run_counted)
572 {
573   struct gcov_info *gi_ptr;
574   struct gcov_filename gf;
575 
576   /* Compute run_max of this program run.  */
577   gcov_type run_max = 0;
578   for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
579     for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
580       {
581 	const struct gcov_ctr_info *cinfo
582 	  = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS];
583 
584 	for (unsigned i = 0; i < cinfo->num; i++)
585 	  if (run_max < cinfo->values[i])
586 	    run_max = cinfo->values[i];
587       }
588 
589   allocate_filename_struct (&gf);
590 
591   /* Now merge each file.  */
592   for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
593     {
594       dump_one_gcov (gi_ptr, &gf, run_counted, run_max);
595       free (gf.filename);
596     }
597 
598   free (gf.prefix);
599 }
600 
601 #if IN_GCOV_TOOL
602 const char *
603 __attribute__ ((unused))
gcov_get_filename(struct gcov_info * list)604 gcov_get_filename (struct gcov_info *list)
605 {
606   return list->filename;
607 }
608 #endif
609 
610 #if !IN_GCOV_TOOL
611 void
__gcov_dump_one(struct gcov_root * root)612 __gcov_dump_one (struct gcov_root *root)
613 {
614   if (root->dumped)
615     return;
616 
617   gcov_do_dump (root->list, root->run_counted);
618 
619   root->dumped = 1;
620   root->run_counted = 1;
621 }
622 
623 /* Per-dynamic-object gcov state.  */
624 struct gcov_root __gcov_root;
625 
626 /* Exactly one of these will be live in the process image.  */
627 struct gcov_master __gcov_master =
628   {GCOV_VERSION, 0};
629 
630 /* Dynamic pool for gcov_kvp structures.  */
631 struct gcov_kvp *__gcov_kvp_dynamic_pool;
632 
633 /* Index into __gcov_kvp_dynamic_pool array.  */
634 unsigned __gcov_kvp_dynamic_pool_index;
635 
636 /* Size of _gcov_kvp_dynamic_pool array.  */
637 unsigned __gcov_kvp_dynamic_pool_size;
638 
639 void
__gcov_exit(void)640 __gcov_exit (void)
641 {
642   __gcov_dump_one (&__gcov_root);
643   if (__gcov_root.next)
644     __gcov_root.next->prev = __gcov_root.prev;
645   if (__gcov_root.prev)
646     __gcov_root.prev->next = __gcov_root.next;
647   else
648     __gcov_master.root = __gcov_root.next;
649 
650   gcov_error_exit ();
651 }
652 
653 /* Add a new object file onto the bb chain.  Invoked automatically
654   when running an object file's global ctors.  */
655 
656 void
__gcov_init(struct gcov_info * info)657 __gcov_init (struct gcov_info *info)
658 {
659   if (!info->version || !info->n_functions)
660     return;
661   if (gcov_version (info, info->version, 0))
662     {
663       if (!__gcov_root.list)
664 	{
665 	  /* Add to master list and at exit function.  */
666 	  if (gcov_version (NULL, __gcov_master.version, "<master>"))
667 	    {
668 	      __gcov_root.next = __gcov_master.root;
669 	      if (__gcov_master.root)
670 		__gcov_master.root->prev = &__gcov_root;
671 	      __gcov_master.root = &__gcov_root;
672 	    }
673 	}
674 
675       info->next = __gcov_root.list;
676       __gcov_root.list = info;
677     }
678 }
679 #endif /* !IN_GCOV_TOOL */
680 #endif /* L_gcov */
681 #endif /* inhibit_libc */
682