OSDN Git Service

* doc/install.texi: Document that dejagnu 1.4.4 is required.
[pf3gnuchains/gcc-fork.git] / gcc / libgcov.c
1 /* Routines required for instrumenting a program.  */
2 /* Compile this one with gcc.  */
3 /* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4    2000, 2001, 2002, 2003, 2004  Free Software Foundation, Inc.
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 In addition to the permissions in the GNU General Public License, the
14 Free Software Foundation gives you unlimited permission to link the
15 compiled version of this file into combinations with other programs,
16 and to distribute those combinations without any restriction coming
17 from the use of this file.  (The General Public License restrictions
18 do apply in other respects; for example, they cover modification of
19 the file, and distribution when not linked into a combine
20 executable.)
21
22 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
23 WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with GCC; see the file COPYING.  If not, write to the Free
29 Software Foundation, 59 Temple Place - Suite 330, Boston, MA
30 02111-1307, USA.  */
31
32 /* It is incorrect to include config.h here, because this file is being
33    compiled for the target, and hence definitions concerning only the host
34    do not apply.  */
35
36 #include "tconfig.h"
37 #include "tsystem.h"
38 #include "coretypes.h"
39 #include "tm.h"
40
41 #if defined(inhibit_libc)
42 #define IN_LIBGCOV (-1)
43 #else
44 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
45 #include <stdio.h>
46 #define IN_LIBGCOV 1
47 #if defined(L_gcov)
48 #define GCOV_LINKAGE /* nothing */
49 #endif
50 #endif
51 #include "gcov-io.h"
52
53 #if defined(inhibit_libc)
54 /* If libc and its header files are not available, provide dummy functions.  */
55
56 #ifdef L_gcov
57 void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
58 void __gcov_flush (void) {}
59 #endif
60
61 #ifdef L_gcov_merge_add
62 void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
63                        unsigned n_counters __attribute__ ((unused))) {}
64 #endif
65
66 #ifdef L_gcov_merge_single
67 void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
68                           unsigned n_counters __attribute__ ((unused))) {}
69 #endif
70
71 #ifdef L_gcov_merge_delta
72 void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
73                          unsigned n_counters __attribute__ ((unused))) {}
74 #endif
75
76 #else
77
78 #include <string.h>
79 #if GCOV_LOCKED
80 #include <fcntl.h>
81 #include <errno.h>
82 #include <sys/stat.h>
83 #endif
84
85 #ifdef L_gcov
86 #include "gcov-io.c"
87
88 /* Chain of per-object gcov structures.  */
89 static struct gcov_info *gcov_list;
90
91 /* A program checksum allows us to distinguish program data for an
92    object file included in multiple programs.  */
93 static gcov_unsigned_t gcov_crc32;
94
95 static int
96 gcov_version (struct gcov_info *ptr, gcov_unsigned_t version)
97 {
98   if (version != GCOV_VERSION)
99     {
100       char v[4], e[4];
101
102       GCOV_UNSIGNED2STRING (v, version);
103       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
104       
105       fprintf (stderr,
106                "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
107                ptr->filename, e, v);
108       return 0;
109     }
110   return 1;
111 }
112
113 /* Dump the coverage counts. We merge with existing counts when
114    possible, to avoid growing the .da files ad infinitum. We use this
115    program's checksum to make sure we only accumulate whole program
116    statistics to the correct summary. An object file might be embedded
117    in two separate programs, and we must keep the two program
118    summaries separate.  */
119
120 static void
121 gcov_exit (void)
122 {
123   struct gcov_info *gi_ptr;
124   struct gcov_summary this_program;
125   struct gcov_summary all;
126   struct gcov_ctr_summary *cs_ptr;
127   const struct gcov_ctr_info *ci_ptr;
128   unsigned t_ix;
129   gcov_unsigned_t c_num;
130
131   memset (&all, 0, sizeof (all));
132   /* Find the totals for this execution.  */
133   memset (&this_program, 0, sizeof (this_program));
134   for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
135     {
136       ci_ptr = gi_ptr->counts;
137       for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
138         {
139           if (!((1 << t_ix) & gi_ptr->ctr_mask))
140             continue;
141
142           cs_ptr = &this_program.ctrs[t_ix];
143           cs_ptr->num += ci_ptr->num;
144           for (c_num = 0; c_num < ci_ptr->num; c_num++)
145             {
146               cs_ptr->sum_all += ci_ptr->values[c_num];
147               if (cs_ptr->run_max < ci_ptr->values[c_num])
148                 cs_ptr->run_max = ci_ptr->values[c_num];
149             }
150           ci_ptr++;
151         }
152     }
153
154   /* Now merge each file.  */
155   for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
156     {
157       struct gcov_summary this_object;
158       struct gcov_summary object, program;
159       gcov_type *values[GCOV_COUNTERS];
160       const struct gcov_fn_info *fi_ptr;
161       unsigned fi_stride;
162       unsigned c_ix, f_ix, n_counts;
163       struct gcov_ctr_summary *cs_obj, *cs_tobj, *cs_prg, *cs_tprg, *cs_all;
164       int error = 0;
165       gcov_unsigned_t tag, length;
166       gcov_position_t summary_pos = 0;
167
168       memset (&this_object, 0, sizeof (this_object));
169       memset (&object, 0, sizeof (object));
170       
171       /* Totals for this object file.  */
172       ci_ptr = gi_ptr->counts;
173       for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
174         {
175           if (!((1 << t_ix) & gi_ptr->ctr_mask))
176             continue;
177
178           cs_ptr = &this_object.ctrs[t_ix];
179           cs_ptr->num += ci_ptr->num;
180           for (c_num = 0; c_num < ci_ptr->num; c_num++)
181             {
182               cs_ptr->sum_all += ci_ptr->values[c_num];
183               if (cs_ptr->run_max < ci_ptr->values[c_num])
184                 cs_ptr->run_max = ci_ptr->values[c_num];
185             }
186
187           ci_ptr++;
188         }
189
190       c_ix = 0;
191       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
192         if ((1 << t_ix) & gi_ptr->ctr_mask)
193           {
194             values[c_ix] = gi_ptr->counts[c_ix].values;
195             c_ix++;
196           }
197
198       /* Calculate the function_info stride. This depends on the
199          number of counter types being measured.  */
200       fi_stride = sizeof (struct gcov_fn_info) + c_ix * sizeof (unsigned);
201       if (__alignof__ (struct gcov_fn_info) > sizeof (unsigned))
202         {
203           fi_stride += __alignof__ (struct gcov_fn_info) - 1;
204           fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
205         }
206       
207       if (!gcov_open (gi_ptr->filename))
208         {
209           fprintf (stderr, "profiling:%s:Cannot open\n", gi_ptr->filename);
210           continue;
211         }
212
213       tag = gcov_read_unsigned ();
214       if (tag)
215         {
216           /* Merge data from file.  */
217           if (tag != GCOV_DATA_MAGIC)
218             {
219               fprintf (stderr, "profiling:%s:Not a gcov data file\n",
220                        gi_ptr->filename);
221             read_fatal:;
222               gcov_close ();
223               continue;
224             }
225           length = gcov_read_unsigned ();
226           if (!gcov_version (gi_ptr, length))
227             goto read_fatal;
228
229           length = gcov_read_unsigned ();
230           if (length != gi_ptr->stamp)
231             {
232               /* Read from a different compilation. Overwrite the
233                  file.  */
234               gcov_truncate ();
235               goto rewrite;
236             }
237           
238           /* Merge execution counts for each function.  */
239           for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
240             {
241               fi_ptr = (const struct gcov_fn_info *)
242                       ((const char *) gi_ptr->functions + f_ix * fi_stride);
243               tag = gcov_read_unsigned ();
244               length = gcov_read_unsigned ();
245
246               /* Check function.  */
247               if (tag != GCOV_TAG_FUNCTION
248                   || length != GCOV_TAG_FUNCTION_LENGTH
249                   || gcov_read_unsigned () != fi_ptr->ident
250                   || gcov_read_unsigned () != fi_ptr->checksum)
251                 {
252                 read_mismatch:;
253                   fprintf (stderr, "profiling:%s:Merge mismatch for %s\n",
254                            gi_ptr->filename,
255                            f_ix + 1 ? "function" : "summaries");
256                   goto read_fatal;
257                 }
258
259               c_ix = 0;
260               for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
261                 {
262                   gcov_merge_fn merge;
263
264                   if (!((1 << t_ix) & gi_ptr->ctr_mask))
265                     continue;
266                   
267                   n_counts = fi_ptr->n_ctrs[c_ix];
268                   merge = gi_ptr->counts[c_ix].merge;
269                     
270                   tag = gcov_read_unsigned ();
271                   length = gcov_read_unsigned ();
272                   if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
273                       || length != GCOV_TAG_COUNTER_LENGTH (n_counts))
274                     goto read_mismatch;
275                   (*merge) (values[c_ix], n_counts);
276                   values[c_ix] += n_counts;
277                   c_ix++;
278                 }
279               if ((error = gcov_is_error ()))
280                 goto read_error;
281             }
282
283           f_ix = ~0u;
284           /* Check program & object summary */
285           while (1)
286             {
287               gcov_position_t base = gcov_position ();
288               int is_program;
289               
290               tag = gcov_read_unsigned ();
291               if (!tag)
292                 break;
293               length = gcov_read_unsigned ();
294               is_program = tag == GCOV_TAG_PROGRAM_SUMMARY;
295               if (length != GCOV_TAG_SUMMARY_LENGTH
296                   || (!is_program && tag != GCOV_TAG_OBJECT_SUMMARY))
297                 goto read_mismatch;
298               gcov_read_summary (is_program ? &program : &object);
299               if ((error = gcov_is_error ()))
300                 goto read_error;
301               if (is_program && program.checksum == gcov_crc32)
302                 {
303                   summary_pos = base;
304                   goto rewrite;
305                 }
306             }
307         }
308       
309       if (!gcov_is_eof ())
310         {
311         read_error:;
312           fprintf (stderr, error < 0 ? "profiling:%s:Overflow merging\n"
313                    : "profiling:%s:Error merging\n", gi_ptr->filename);
314           goto read_fatal;
315         }
316     rewrite:;
317       gcov_rewrite ();
318       if (!summary_pos)
319         memset (&program, 0, sizeof (program));
320
321       /* Merge the summaries.  */
322       f_ix = ~0u;
323       for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
324         {
325           cs_obj = &object.ctrs[t_ix];
326           cs_tobj = &this_object.ctrs[t_ix];
327           cs_prg = &program.ctrs[t_ix];
328           cs_tprg = &this_program.ctrs[t_ix];
329           cs_all = &all.ctrs[t_ix];
330
331           if ((1 << t_ix) & gi_ptr->ctr_mask)
332             {
333               if (!cs_obj->runs++)
334                 cs_obj->num = cs_tobj->num;
335               else if (cs_obj->num != cs_tobj->num)
336                 goto read_mismatch;
337               cs_obj->sum_all += cs_tobj->sum_all;
338               if (cs_obj->run_max < cs_tobj->run_max)
339                 cs_obj->run_max = cs_tobj->run_max;
340               cs_obj->sum_max += cs_tobj->run_max;
341               
342               if (!cs_prg->runs++)
343                 cs_prg->num = cs_tprg->num;
344               else if (cs_prg->num != cs_tprg->num)
345                 goto read_mismatch;
346               cs_prg->sum_all += cs_tprg->sum_all;
347               if (cs_prg->run_max < cs_tprg->run_max)
348                 cs_prg->run_max = cs_tprg->run_max;
349               cs_prg->sum_max += cs_tprg->run_max;
350             }
351           else if (cs_obj->num || cs_prg->num)
352             goto read_mismatch;
353           
354           if (!cs_all->runs && cs_prg->runs)
355             memcpy (cs_all, cs_prg, sizeof (*cs_all));
356           else if (!all.checksum
357                    && (!GCOV_LOCKED || cs_all->runs == cs_prg->runs)
358                    && memcmp (cs_all, cs_prg, sizeof (*cs_all)))
359             {
360               fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s",
361                        gi_ptr->filename, GCOV_LOCKED
362                        ? "" : " or concurrent update without locking support");
363               all.checksum = ~0u;
364             }
365         }
366       
367       c_ix = 0;
368       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
369         if ((1 << t_ix) & gi_ptr->ctr_mask)
370           {
371             values[c_ix] = gi_ptr->counts[c_ix].values;
372             c_ix++;
373           }
374
375       program.checksum = gcov_crc32;
376       
377       /* Write out the data.  */
378       gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
379       gcov_write_unsigned (gi_ptr->stamp);
380       
381       /* Write execution counts for each function.  */
382       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
383         {
384           fi_ptr = (const struct gcov_fn_info *)
385                   ((const char *) gi_ptr->functions + f_ix * fi_stride);
386
387           /* Announce function.  */
388           gcov_write_tag_length (GCOV_TAG_FUNCTION, GCOV_TAG_FUNCTION_LENGTH);
389           gcov_write_unsigned (fi_ptr->ident);
390           gcov_write_unsigned (fi_ptr->checksum);
391
392           c_ix = 0;
393           for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
394             {
395               gcov_type *c_ptr;
396
397               if (!((1 << t_ix) & gi_ptr->ctr_mask))
398                 continue;
399
400               n_counts = fi_ptr->n_ctrs[c_ix];
401                     
402               gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
403                                      GCOV_TAG_COUNTER_LENGTH (n_counts));
404               c_ptr = values[c_ix];
405               while (n_counts--)
406                 gcov_write_counter (*c_ptr++);
407
408               values[c_ix] = c_ptr;
409               c_ix++;
410             }
411         }
412
413       /* Object file summary.  */
414       gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object);
415
416       /* Generate whole program statistics.  */
417       gcov_seek (summary_pos);
418       gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &program);
419       if ((error = gcov_close ()))
420           fprintf (stderr, error  < 0 ?
421                    "profiling:%s:Overflow writing\n" :
422                    "profiling:%s:Error writing\n",
423                    gi_ptr->filename);
424     }
425 }
426
427 /* Add a new object file onto the bb chain.  Invoked automatically
428    when running an object file's global ctors.  */
429
430 void
431 __gcov_init (struct gcov_info *info)
432 {
433   if (!info->version)
434     return;
435   if (gcov_version (info, info->version))
436     {
437       const char *ptr = info->filename;
438       gcov_unsigned_t crc32 = gcov_crc32;
439   
440       do
441         {
442           unsigned ix;
443           gcov_unsigned_t value = *ptr << 24;
444
445           for (ix = 8; ix--; value <<= 1)
446             {
447               gcov_unsigned_t feedback;
448
449               feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
450               crc32 <<= 1;
451               crc32 ^= feedback;
452             }
453         }
454       while (*ptr++);
455       
456       gcov_crc32 = crc32;
457       
458       if (!gcov_list)
459         atexit (gcov_exit);
460       
461       info->next = gcov_list;
462       gcov_list = info;
463     }
464   info->version = 0;
465 }
466
467 /* Called before fork or exec - write out profile information gathered so
468    far and reset it to zero.  This avoids duplication or loss of the
469    profile information gathered so far.  */
470
471 void
472 __gcov_flush (void)
473 {
474   const struct gcov_info *gi_ptr;
475
476   gcov_exit ();
477   for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
478     {
479       unsigned t_ix;
480       const struct gcov_ctr_info *ci_ptr;
481       
482       for (t_ix = 0, ci_ptr = gi_ptr->counts; t_ix != GCOV_COUNTERS; t_ix++)
483         if ((1 << t_ix) & gi_ptr->ctr_mask)
484           {
485             memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
486             ci_ptr++;
487           }
488     }
489 }
490
491 #endif /* L_gcov */
492
493 #ifdef L_gcov_merge_add
494 /* The profile merging function that just adds the counters.  It is given
495    an array COUNTERS of N_COUNTERS old counters and it reads the same number
496    of counters from the gcov file.  */
497 void
498 __gcov_merge_add (gcov_type *counters, unsigned n_counters)
499 {
500   for (; n_counters; counters++, n_counters--)
501     *counters += gcov_read_counter ();
502 }
503 #endif /* L_gcov_merge_add */
504
505 #ifdef L_gcov_merge_single
506 /* The profile merging function for choosing the most common value.
507    It is given an array COUNTERS of N_COUNTERS old counters and it
508    reads the same number of counters from the gcov file.  The counters
509    are split into 3-tuples where the members of the tuple have
510    meanings:
511    
512    -- the stored candidate on the most common value of the measured entity
513    -- counter
514    -- total number of evaluations of the value  */
515 void
516 __gcov_merge_single (gcov_type *counters, unsigned n_counters)
517 {
518   unsigned i, n_measures;
519   gcov_type value, counter, all;
520
521   GCOV_CHECK (!(n_counters % 3));
522   n_measures = n_counters / 3;
523   for (i = 0; i < n_measures; i++, counters += 3)
524     {
525       value = gcov_read_counter ();
526       counter = gcov_read_counter ();
527       all = gcov_read_counter ();
528
529       if (counters[0] == value)
530         counters[1] += counter;
531       else if (counter > counters[1])
532         {
533           counters[0] = value;
534           counters[1] = counter - counters[1];
535         }
536       else
537         counters[1] -= counter;
538       counters[2] += all;
539     }
540 }
541 #endif /* L_gcov_merge_single */
542
543 #ifdef L_gcov_merge_delta
544 /* The profile merging function for choosing the most common
545    difference between two consecutive evaluations of the value.  It is
546    given an array COUNTERS of N_COUNTERS old counters and it reads the
547    same number of counters from the gcov file.  The counters are split
548    into 4-tuples where the members of the tuple have meanings:
549    
550    -- the last value of the measured entity
551    -- the stored candidate on the most common difference
552    -- counter
553    -- total number of evaluations of the value  */
554 void
555 __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
556 {
557   unsigned i, n_measures;
558   gcov_type last, value, counter, all;
559
560   GCOV_CHECK (!(n_counters % 4));
561   n_measures = n_counters / 4;
562   for (i = 0; i < n_measures; i++, counters += 4)
563     {
564       last = gcov_read_counter ();
565       value = gcov_read_counter ();
566       counter = gcov_read_counter ();
567       all = gcov_read_counter ();
568
569       if (counters[1] == value)
570         counters[2] += counter;
571       else if (counter > counters[2])
572         {
573           counters[1] = value;
574           counters[2] = counter - counters[2];
575         }
576       else
577         counters[2] -= counter;
578       counters[3] += all;
579     }
580 }
581 #endif /* L_gcov_merge_delta */
582
583 #ifdef L_gcov_fork
584 /* A wrapper for the fork function.  Flushes the accumulated profiling data, so
585    that they are not counted twice.  */
586
587 pid_t
588 __gcov_fork (void)
589 {
590   __gcov_flush ();
591   return fork ();
592 }
593 #endif
594
595 #ifdef L_gcov_execl
596 /* A wrapper for the execl function.  Flushes the accumulated profiling data, so
597    that they are not lost.  */
598
599 int
600 __gcov_execl (const char *path, const char *arg, ...)
601 {
602   va_list ap, aq;
603   unsigned i, length;
604   char **args;
605
606   __gcov_flush ();
607
608   va_start (ap, arg);
609   va_copy (aq, ap);
610
611   length = 2;
612   while (va_arg (ap, char *))
613     length++;
614   va_end (ap);
615
616   args = alloca (length * sizeof (void *));
617   args[0] = (char *) arg;
618   for (i = 1; i < length; i++)
619     args[i] = va_arg (aq, char *);
620   va_end (aq);
621
622   return execv (path, args);
623 }
624 #endif
625
626 #ifdef L_gcov_execlp
627 /* A wrapper for the execlp function.  Flushes the accumulated profiling data, so
628    that they are not lost.  */
629
630 int
631 __gcov_execlp (const char *path, const char *arg, ...)
632 {
633   va_list ap, aq;
634   unsigned i, length;
635   char **args;
636
637   __gcov_flush ();
638
639   va_start (ap, arg);
640   va_copy (aq, ap);
641
642   length = 2;
643   while (va_arg (ap, char *))
644     length++;
645   va_end (ap);
646
647   args = alloca (length * sizeof (void *));
648   args[0] = (char *) arg;
649   for (i = 1; i < length; i++)
650     args[i] = va_arg (aq, char *);
651   va_end (aq);
652
653   return execvp (path, args);
654 }
655 #endif
656
657 #ifdef L_gcov_execle
658 /* A wrapper for the execle function.  Flushes the accumulated profiling data, so
659    that they are not lost.  */
660
661 int
662 __gcov_execle (const char *path, const char *arg, ...)
663 {
664   va_list ap, aq;
665   unsigned i, length;
666   char **args;
667   char **envp;
668
669   __gcov_flush ();
670
671   va_start (ap, arg);
672   va_copy (aq, ap);
673
674   length = 2;
675   while (va_arg (ap, char *))
676     length++;
677   va_end (ap);
678
679   args = alloca (length * sizeof (void *));
680   args[0] = (char *) arg;
681   for (i = 1; i < length; i++)
682     args[i] = va_arg (aq, char *);
683   envp = va_arg (aq, char **);
684   va_end (aq);
685
686   return execve (path, args, envp);
687 }
688 #endif
689
690 #ifdef L_gcov_execv
691 /* A wrapper for the execv function.  Flushes the accumulated profiling data, so
692    that they are not lost.  */
693
694 int
695 __gcov_execv (const char *path, char *const argv[])
696 {
697   __gcov_flush ();
698   return execv (path, argv);
699 }
700 #endif
701
702 #ifdef L_gcov_execvp
703 /* A wrapper for the execvp function.  Flushes the accumulated profiling data, so
704    that they are not lost.  */
705
706 int
707 __gcov_execvp (const char *path, char *const argv[])
708 {
709   __gcov_flush ();
710   return execvp (path, argv);
711 }
712 #endif
713
714 #ifdef L_gcov_execve
715 /* A wrapper for the execve function.  Flushes the accumulated profiling data, so
716    that they are not lost.  */
717
718 int
719 __gcov_execve (const char *path, char *const argv[], char *const envp[])
720 {
721   __gcov_flush ();
722   return execve (path, argv, envp);
723 }
724 #endif
725 #endif /* inhibit_libc */