analyzer: fix feasibility false +ve on jumps through function ptrs [PR107582]
[platform/upstream/gcc.git] / gcc / gcov-io.cc
1 /* File format for coverage information
2    Copyright (C) 1996-2022 Free Software Foundation, Inc.
3    Contributed by Bob Manson <manson@cygnus.com>.
4    Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
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 3, or (at your option) any later
11 version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
25 <http://www.gnu.org/licenses/>.  */
26
27 /* Routines declared in gcov-io.h.  This file should be #included by
28    another source file, after having #included gcov-io.h.  */
29
30 static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned);
31
32 /* Indicates the last gcov file access error or that no error occurred
33    so far.  */
34 enum gcov_file_error
35 {
36   GCOV_FILE_COUNTER_OVERFLOW = -1,
37   GCOV_FILE_NO_ERROR = 0,
38   GCOV_FILE_WRITE_ERROR = 1,
39   GCOV_FILE_EOF = 2
40 };
41
42 struct gcov_var
43 {
44   FILE *file;
45   enum gcov_file_error error;
46   int mode;                     /* < 0 writing, > 0 reading.  */
47   int endian;                   /* Swap endianness.  */
48 #ifdef IN_GCOV_TOOL
49   gcov_position_t pos;          /* File position for stdin support.  */
50 #endif
51 } gcov_var;
52
53 #define GCOV_MODE_STDIN 2
54
55 /* Save the current position in the gcov file.  */
56 /* We need to expose this function when compiling for gcov-tool.  */
57 #ifndef IN_GCOV_TOOL
58 static inline
59 #endif
60 gcov_position_t
61 gcov_position (void)
62 {
63 #ifdef IN_GCOV_TOOL
64   if (gcov_var.mode == GCOV_MODE_STDIN)
65     return gcov_var.pos;
66 #endif
67   return ftell (gcov_var.file);
68 }
69
70 /* Return nonzero if the error flag is set.  */
71 /* We need to expose this function when compiling for gcov-tool.  */
72 #ifndef IN_GCOV_TOOL
73 static inline
74 #endif
75 int
76 gcov_is_error (void)
77 {
78   return gcov_var.file ? gcov_var.error : 1;
79 }
80
81 #if IN_LIBGCOV
82 /* Move to beginning of file, initialize for writing, and clear file error
83    status.  */
84
85 GCOV_LINKAGE inline void
86 gcov_rewrite (void)
87 {
88   gcov_var.mode = -1; 
89   gcov_var.error = GCOV_FILE_NO_ERROR;
90   fseek (gcov_var.file, 0L, SEEK_SET);
91 }
92 #endif
93
94 static inline gcov_unsigned_t
95 from_file (gcov_unsigned_t value)
96 {
97 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
98   if (gcov_var.endian)
99     return __builtin_bswap32 (value);
100 #endif
101   return value;
102 }
103
104 /* Open a gcov file. NAME is the name of the file to open and MODE
105    indicates whether a new file should be created, or an existing file
106    opened. If MODE is >= 0 an existing file will be opened, if
107    possible, and if MODE is <= 0, a new file will be created. Use
108    MODE=0 to attempt to reopen an existing file and then fall back on
109    creating a new one.  If MODE > 0, the file will be opened in
110    read-only mode.  Otherwise it will be opened for modification.
111    Return zero on failure, non-zero on success.  */
112
113 GCOV_LINKAGE int
114 gcov_open (const char *name, int mode)
115 {
116 #if GCOV_LOCKED
117   struct flock s_flock;
118   int fd;
119
120   s_flock.l_whence = SEEK_SET;
121   s_flock.l_start = 0;
122   s_flock.l_len = 0; /* Until EOF.  */
123   s_flock.l_pid = getpid ();
124 #elif GCOV_LOCKED_WITH_LOCKING
125   int fd;
126 #endif
127
128   gcov_nonruntime_assert (!gcov_var.file);
129   gcov_var.error = GCOV_FILE_NO_ERROR;
130 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
131   gcov_var.endian = 0;
132 #endif
133 #ifdef IN_GCOV_TOOL
134   gcov_var.pos = 0;
135   if (!name)
136     {
137       gcov_nonruntime_assert (gcov_var.mode > 0);
138       gcov_var.file = stdin;
139       gcov_var.mode = GCOV_MODE_STDIN;
140       return 1;
141     }
142 #endif
143 #if GCOV_LOCKED
144   if (mode > 0)
145     {
146       /* Read-only mode - acquire a read-lock.  */
147       s_flock.l_type = F_RDLCK;
148       /* pass mode (ignored) for compatibility */
149       fd = open (name, O_RDONLY, S_IRUSR | S_IWUSR);
150     }
151   else
152      {
153        /* Write mode - acquire a write-lock.  */
154        s_flock.l_type = F_WRLCK;
155        /* Truncate if force new mode.  */
156        fd = open (name, O_RDWR | O_CREAT | (mode < 0 ? O_TRUNC : 0), 0666);
157     }
158   if (fd < 0)
159     return 0;
160
161   while (fcntl (fd, F_SETLKW, &s_flock) && errno == EINTR)
162     continue;
163
164   gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
165
166   if (!gcov_var.file)
167     {
168       close (fd);
169       return 0;
170     }
171 #elif GCOV_LOCKED_WITH_LOCKING
172   if (mode > 0)
173     {
174       /* pass mode (ignored) for compatibility */
175       fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
176     }
177   else
178      {
179        /* Truncate if force new mode.  */
180        fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
181                   0666);
182     }
183   if (fd < 0)
184     return 0;
185
186   if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
187     {
188       close (fd);
189       return 0;
190     }
191
192   gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
193
194   if (!gcov_var.file)
195     {
196       close (fd);
197       return 0;
198     }
199 #else
200   if (mode >= 0)
201     /* Open an existing file.  */
202     gcov_var.file = fopen (name, (mode > 0) ? "rb" : "r+b");
203
204   if (gcov_var.file)
205     mode = 1;
206   else if (mode <= 0)
207     /* Create a new file.  */
208     gcov_var.file = fopen (name, "w+b");
209
210   if (!gcov_var.file)
211     return 0;
212 #endif
213
214   gcov_var.mode = mode ? mode : 1;
215
216   return 1;
217 }
218
219 /* Close the current gcov file. Flushes data to disk. Returns nonzero
220    on failure or error flag set.  */
221
222 GCOV_LINKAGE int
223 gcov_close (void)
224 {
225 #ifdef IN_GCOV_TOOL
226   if (gcov_var.file == stdin)
227     gcov_var.file = 0;
228   else
229 #endif
230   if (gcov_var.file)
231     {
232       if (fclose (gcov_var.file))
233         gcov_var.error = GCOV_FILE_WRITE_ERROR;
234
235       gcov_var.file = 0;
236     }
237   gcov_var.mode = 0;
238   return gcov_var.error;
239 }
240
241 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
242 /* Check if MAGIC is EXPECTED. Use it to determine endianness of the
243    file. Returns +1 for same endian, -1 for other endian and zero for
244    not EXPECTED.  */
245
246 GCOV_LINKAGE int
247 gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
248 {
249   if (magic == expected)
250     return 1;
251
252   if (__builtin_bswap32 (magic) == expected)
253     {
254       gcov_var.endian = 1;
255       return -1;
256     }
257   return 0;
258 }
259 #endif
260
261 #if !IN_GCOV
262 /* Write DATA of LENGTH characters to coverage file.  */
263
264 GCOV_LINKAGE void
265 gcov_write (const void *data, unsigned length)
266 {
267   gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
268   if (r != 1)
269     gcov_var.error = GCOV_FILE_WRITE_ERROR;
270 }
271
272 /* Write unsigned VALUE to coverage file.  */
273
274 GCOV_LINKAGE void
275 gcov_write_unsigned (gcov_unsigned_t value)
276 {
277   gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
278   if (r != 1)
279     gcov_var.error = GCOV_FILE_WRITE_ERROR;
280 }
281
282 #if !IN_LIBGCOV
283 /* Write STRING to coverage file.  Sets error flag on file
284    error, overflow flag on overflow */
285
286 GCOV_LINKAGE void
287 gcov_write_string (const char *string)
288 {
289   unsigned length = 0;
290
291   if (string)
292     length = strlen (string) + 1;
293
294   gcov_write_unsigned (length);
295   if (length > 0)
296     {
297       gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
298       if (r != 1)
299         gcov_var.error = GCOV_FILE_WRITE_ERROR;
300     }
301 }
302 #endif
303
304 #if !IN_LIBGCOV
305 /* Write FILENAME to coverage file.  Sets error flag on file
306    error, overflow flag on overflow */
307
308 GCOV_LINKAGE void
309 gcov_write_filename (const char *filename)
310 {
311   if (profile_abs_path_flag && filename && filename[0]
312       && !(IS_DIR_SEPARATOR (filename[0])
313 #if HAVE_DOS_BASED_FILE_SYSTEM
314            || filename[1] == ':'
315 #endif
316           ))
317     {
318       char *buf = getcwd (NULL, 0);
319       if (buf != NULL && buf[0])
320         {
321           size_t len = strlen (buf);
322           buf = (char*)xrealloc (buf, len + strlen (filename) + 2);
323           if (!IS_DIR_SEPARATOR (buf[len - 1]))
324             strcat (buf, "/");
325           strcat (buf, filename);
326           gcov_write_string (buf);
327           free (buf);
328           return;
329         }
330     }
331
332   gcov_write_string (filename);
333 }
334
335 /* Move to a given position in a gcov file.  */
336
337 static void
338 gcov_seek (gcov_position_t base)
339 {
340   fseek (gcov_var.file, base, SEEK_SET);
341 }
342
343 /* Write a tag TAG and reserve space for the record length. Return a
344    value to be used for gcov_write_length.  */
345
346 GCOV_LINKAGE gcov_position_t
347 gcov_write_tag (gcov_unsigned_t tag)
348 {
349   gcov_position_t result = gcov_position ();
350   gcov_write_unsigned (tag);
351   gcov_write_unsigned (0);
352
353   return result;
354 }
355
356 /* Write a record length using POSITION, which was returned by
357    gcov_write_tag.  The current file position is the end of the
358    record, and is restored before returning.  Returns nonzero on
359    overflow.  */
360
361 GCOV_LINKAGE void
362 gcov_write_length (gcov_position_t position)
363 {
364   gcov_position_t current_position = gcov_position ();
365   gcov_nonruntime_assert (gcov_var.mode < 0);
366   gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE);
367
368   gcov_seek (position + GCOV_WORD_SIZE);
369   gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
370   gcov_seek (current_position);
371 }
372
373 #else /* IN_LIBGCOV */
374
375 /* Write an object summary structure to the gcov file.  */
376
377 GCOV_LINKAGE void
378 gcov_write_object_summary (const struct gcov_summary *summary)
379 {
380   gcov_write_unsigned (GCOV_TAG_OBJECT_SUMMARY);
381   gcov_write_unsigned (GCOV_TAG_OBJECT_SUMMARY_LENGTH);
382   gcov_write_unsigned (summary->runs);
383   gcov_write_unsigned (summary->sum_max);
384 }
385
386 #endif /* IN_LIBGCOV */
387
388 #endif /*!IN_GCOV */
389
390 /* Return a pointer to read COUNT bytes from the gcov file.  Returns
391    NULL on failure (read past EOF).  */
392
393 static void *
394 gcov_read_bytes (void *buffer, unsigned count)
395 {
396   if (gcov_var.mode <= 0)
397     return NULL;
398
399   unsigned read = fread (buffer, count, 1, gcov_var.file);
400   if (read != 1)
401     {
402       if (feof (gcov_var.file))
403         gcov_var.error = GCOV_FILE_EOF;
404       return NULL;
405     }
406
407 #ifdef IN_GCOV_TOOL
408   gcov_var.pos += count;
409 #endif
410   return buffer;
411 }
412
413 /* Read WORDS gcov_unsigned_t values from gcov file.  */
414
415 static gcov_unsigned_t *
416 gcov_read_words (void *buffer, unsigned words)
417 {
418   return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
419 }
420
421 /* Read unsigned value from a coverage file. Sets error flag on file
422    error, overflow flag on overflow */
423
424 GCOV_LINKAGE gcov_unsigned_t
425 gcov_read_unsigned (void)
426 {
427   gcov_unsigned_t value;
428   gcov_unsigned_t allocated_buffer[1];
429   gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
430
431   if (!buffer)
432     return 0;
433
434   value = from_file (buffer[0]);
435   return value;
436 }
437
438 /* Read counter value from a coverage file. Sets error flag on file
439    error, overflow flag on overflow */
440
441 GCOV_LINKAGE gcov_type
442 gcov_read_counter (void)
443 {
444   gcov_type value;
445   gcov_unsigned_t allocated_buffer[2];
446   gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
447
448   if (!buffer)
449     return 0;
450   value = from_file (buffer[0]);
451   if (sizeof (value) > sizeof (gcov_unsigned_t))
452     value |= ((gcov_type) from_file (buffer[1])) << 32;
453   else if (buffer[1])
454     gcov_var.error = GCOV_FILE_COUNTER_OVERFLOW;
455
456   return value;
457 }
458
459 /* Mangle filename path of BASE and output new allocated pointer with
460    mangled path.  */
461
462 char *
463 mangle_path (char const *base)
464 {
465   /* Convert '/' to '#', convert '..' to '^',
466      convert ':' to '~' on DOS based file system.  */
467   const char *probe;
468   char *buffer = (char *)xmalloc (strlen (base) + 1);
469   char *ptr = buffer;
470
471 #if HAVE_DOS_BASED_FILE_SYSTEM
472   if (base[0] && base[1] == ':')
473     {
474       ptr[0] = base[0];
475       ptr[1] = '~';
476       ptr += 2;
477       base += 2;
478     }
479 #endif
480   for (; *base; base = probe)
481     {
482       size_t len;
483
484       for (probe = base; *probe; probe++)
485         if (*probe == '/')
486           break;
487       len = probe - base;
488       if (len == 2 && base[0] == '.' && base[1] == '.')
489         *ptr++ = '^';
490       else
491         {
492           memcpy (ptr, base, len);
493           ptr += len;
494         }
495       if (*probe)
496         {
497           *ptr++ = '#';
498           probe++;
499         }
500     }
501
502   /* Terminate the string.  */
503   *ptr = '\0';
504
505   return buffer;
506 }
507
508 /* We need to expose the below function when compiling for gcov-tool.  */
509
510 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
511 /* Read string from coverage file.  Allocate the buffer for the string
512    from the heap or die.  Return a pointer to the string, or NULL on
513    empty string.  */
514
515 GCOV_LINKAGE const char *
516 gcov_read_string (void)
517 {
518   unsigned length = gcov_read_unsigned ();
519
520   if (!length)
521     return 0;
522
523   void *buffer = XNEWVEC (char *, length);
524   return (const char *) gcov_read_bytes (buffer, length);
525 }
526 #endif
527
528 GCOV_LINKAGE void
529 gcov_read_summary (struct gcov_summary *summary)
530 {
531   summary->runs = gcov_read_unsigned ();
532   summary->sum_max = gcov_read_unsigned ();
533 }
534
535 /* We need to expose the below function when compiling for gcov-tool.  */
536
537 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
538 /* Reset to a known position.  BASE should have been obtained from
539    gcov_position, LENGTH should be a record length.  */
540
541 GCOV_LINKAGE void
542 gcov_sync (gcov_position_t base, gcov_unsigned_t length)
543 {
544   gcov_nonruntime_assert (gcov_var.mode > 0);
545   base += length;
546 #ifdef IN_GCOV_TOOL
547   if (gcov_var.mode == GCOV_MODE_STDIN)
548     {
549       while (gcov_var.pos < base)
550         {
551           ++gcov_var.pos;
552           (void)fgetc (gcov_var.file);
553         }
554       return;
555     }
556 #endif
557   fseek (gcov_var.file, base, SEEK_SET);
558 }
559 #endif
560
561 #if IN_GCOV > 0
562 /* Return the modification time of the current gcov file.  */
563
564 GCOV_LINKAGE time_t
565 gcov_time (void)
566 {
567   struct stat status;
568
569   if (fstat (fileno (gcov_var.file), &status))
570     return 0;
571   else
572     return status.st_mtime;
573 }
574 #endif /* IN_GCOV */