Coverage Report

Created: 2026-03-10 08:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/binutils/fuzz_addr2line.h
Line
Count
Source
1
/* addr2line.c -- convert addresses to line number and function name
2
   Copyright (C) 1997-2026 Free Software Foundation, Inc.
3
   Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
4
5
   This file is part of GNU Binutils.
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3, or (at your option)
10
   any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program; if not, write to the Free Software
19
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
22
23
/* Derived from objdump.c and nm.c by Ulrich.Lauther@mchp.siemens.de
24
25
   Usage:
26
   addr2line [options] addr addr ...
27
   or
28
   addr2line [options]
29
30
   both forms write results to stdout, the second form reads addresses
31
   to be converted from stdin.  */
32
33
#include "sysdep.h"
34
#include "bfd.h"
35
#include "getopt.h"
36
#include "libiberty.h"
37
#include "demangle.h"
38
#include "bucomm.h"
39
#include "elf-bfd.h"
40
#include "safe-ctype.h"
41
42
static bool unwind_inlines; /* -i, unwind inlined functions. */
43
static bool with_addresses; /* -a, show addresses.  */
44
static bool with_functions; /* -f, show function names.  */
45
static bool do_demangle;  /* -C, demangle names.  */
46
static bool pretty_print; /* -p, print on one line.  */
47
static bool base_names;   /* -s, strip directory names.  */
48
49
/* Flags passed to the name demangler.  */
50
static int demangle_flags = DMGL_PARAMS | DMGL_ANSI;
51
52
static int naddr;   /* Number of addresses to process.  */
53
static char **addr;   /* Hex addresses to process.  */
54
55
static long symcount;
56
static asymbol **syms;    /* Symbol table.  */
57
58
static struct option long_options[] =
59
{
60
  {"addresses", no_argument, NULL, 'a'},
61
  {"basenames", no_argument, NULL, 's'},
62
  {"demangle", optional_argument, NULL, 'C'},
63
  {"exe", required_argument, NULL, 'e'},
64
  {"functions", no_argument, NULL, 'f'},
65
  {"inlines", no_argument, NULL, 'i'},
66
  {"pretty-print", no_argument, NULL, 'p'},
67
  {"recurse-limit", no_argument, NULL, 'R'},
68
  {"recursion-limit", no_argument, NULL, 'R'},  
69
  {"no-recurse-limit", no_argument, NULL, 'r'},
70
  {"no-recursion-limit", no_argument, NULL, 'r'},  
71
  {"section", required_argument, NULL, 'j'},
72
  {"target", required_argument, NULL, 'b'},
73
  {"help", no_argument, NULL, 'H'},
74
  {"version", no_argument, NULL, 'V'},
75
  {0, no_argument, 0, 0}
76
};
77
78
static void usage (FILE *, int);
79
static void slurp_symtab (bfd *);
80
static void find_address_in_section (bfd *, asection *, void *);
81
static void find_offset_in_section (bfd *, asection *);
82
static void translate_addresses (bfd *, asection *);
83

84
/* Print a usage message to STREAM and exit with STATUS.  */
85
86
static void
87
usage (FILE *stream, int status)
88
0
{
89
0
  fprintf (stream, _("Usage: %s [option(s)] [addr(s)]\n"), program_name);
90
0
  fprintf (stream, _(" Convert addresses into line number/file name pairs.\n"));
91
0
  fprintf (stream, _(" If no addresses are specified on the command line, they will be read from stdin\n"));
92
0
  fprintf (stream, _(" The options are:\n\
93
0
  @<file>                Read options from <file>\n\
94
0
  -a --addresses         Show addresses\n\
95
0
  -b --target=<bfdname>  Set the binary file format\n\
96
0
  -e --exe=<executable>  Set the input file name (default is a.out)\n\
97
0
  -i --inlines           Unwind inlined functions\n\
98
0
  -j --section=<name>    Read section-relative offsets instead of addresses\n\
99
0
  -p --pretty-print      Make the output easier to read for humans\n\
100
0
  -s --basenames         Strip directory names\n\
101
0
  -f --functions         Show function names\n\
102
0
  -C --demangle[=style]  Demangle function names\n\
103
0
  -R --recurse-limit     Enable a limit on recursion whilst demangling.  [Default]\n\
104
0
  -r --no-recurse-limit  Disable a limit on recursion whilst demangling\n\
105
0
  -h --help              Display this information\n\
106
0
  -v --version           Display the program's version\n\
107
0
\n"));
108
109
0
  list_supported_targets (program_name, stream);
110
0
  if (REPORT_BUGS_TO[0] && status == 0)
111
0
    fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
112
0
  exit (status);
113
0
}
114

115
/* Read in the symbol table.  */
116
117
static void
118
slurp_symtab (bfd *abfd)
119
8.48k
{
120
8.48k
  long storage;
121
8.48k
  bool dynamic = false;
122
123
8.48k
  if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0)
124
1.28k
    return;
125
126
7.20k
  storage = bfd_get_symtab_upper_bound (abfd);
127
7.20k
  if (storage == 0)
128
5
    {
129
5
      storage = bfd_get_dynamic_symtab_upper_bound (abfd);
130
5
      dynamic = true;
131
5
    }
132
7.20k
  if (storage < 0)
133
5.23k
    {
134
5.23k
      bfd_nonfatal (bfd_get_filename (abfd));
135
5.23k
      return;
136
5.23k
    }
137
138
1.96k
  syms = (asymbol **) xmalloc (storage);
139
1.96k
  if (dynamic)
140
0
    symcount = bfd_canonicalize_dynamic_symtab (abfd, syms);
141
1.96k
  else
142
1.96k
    symcount = bfd_canonicalize_symtab (abfd, syms);
143
1.96k
  if (symcount < 0)
144
555
    bfd_nonfatal (bfd_get_filename (abfd));
145
146
  /* If there are no symbols left after canonicalization and
147
     we have not tried the dynamic symbols then give them a go.  */
148
1.96k
  if (symcount == 0
149
105
      && ! dynamic
150
105
      && (storage = bfd_get_dynamic_symtab_upper_bound (abfd)) > 0)
151
49
    {
152
49
      free (syms);
153
49
      syms = xmalloc (storage);
154
49
      symcount = bfd_canonicalize_dynamic_symtab (abfd, syms);
155
49
    }
156
157
  /* PR 17512: file: 2a1d3b5b.
158
     Do not pretend that we have some symbols when we don't.  */
159
1.96k
  if (symcount <= 0)
160
640
    {
161
640
      free (syms);
162
640
      syms = NULL;
163
640
    }
164
1.96k
}
165

166
/* These global variables are used to pass information between
167
   translate_addresses and find_address_in_section.  */
168
169
static bfd_vma pc;
170
static const char *filename;
171
static const char *functionname;
172
static unsigned int line;
173
static unsigned int discriminator;
174
static bool found;
175
176
/* Look for an address in a section.  This is called via
177
   bfd_map_over_sections.  */
178
179
static void
180
find_address_in_section (bfd *abfd, asection *section,
181
       void *data ATTRIBUTE_UNUSED)
182
932k
{
183
932k
  bfd_vma vma;
184
932k
  bfd_size_type size;
185
186
932k
  if (found)
187
228k
    return;
188
189
704k
  if ((bfd_section_flags (section) & SEC_ALLOC) == 0)
190
308k
    return;
191
192
396k
  vma = bfd_section_vma (section);
193
396k
  if (pc < vma)
194
139k
    return;
195
196
256k
  size = bfd_section_size (section);
197
256k
  if (pc >= vma + size)
198
194k
    return;
199
200
61.3k
  found = bfd_find_nearest_line_discriminator (abfd, section, syms, pc - vma,
201
61.3k
                                               &filename, &functionname,
202
61.3k
                                               &line, &discriminator);
203
61.3k
}
204
205
/* Look for an offset in a section.  This is directly called.  */
206
207
static void
208
find_offset_in_section (bfd *abfd, asection *section)
209
0
{
210
0
  bfd_size_type size;
211
212
0
  if (found)
213
0
    return;
214
215
0
  if ((bfd_section_flags (section) & SEC_ALLOC) == 0)
216
0
    return;
217
218
0
  size = bfd_section_size (section);
219
0
  if (pc >= size)
220
0
    return;
221
222
0
  found = bfd_find_nearest_line_discriminator (abfd, section, syms, pc,
223
0
                                               &filename, &functionname,
224
0
                                               &line, &discriminator);
225
0
}
226
227
/* Lookup a symbol with offset in symbol table.  */
228
229
static bfd_vma
230
lookup_symbol (bfd *abfd, char *sym, size_t offset)
231
0
{
232
0
  long i;
233
234
0
  for (i = 0; i < symcount; i++)
235
0
    {
236
0
      if (!strcmp (syms[i]->name, sym))
237
0
  return syms[i]->value + offset + bfd_asymbol_section (syms[i])->vma;
238
0
    }
239
  /* Try again mangled */
240
0
  for (i = 0; i < symcount; i++)
241
0
    {
242
0
      char *d = bfd_demangle (abfd, syms[i]->name, demangle_flags);
243
0
      bool match = d && !strcmp (d, sym);
244
0
      free (d);
245
246
0
      if (match)
247
0
  return syms[i]->value + offset + bfd_asymbol_section (syms[i])->vma;
248
0
    }
249
0
  return 0;
250
0
}
251
252
/* Split an symbol+offset expression. adr is modified.  */
253
254
static bool
255
is_symbol (char *adr, char **symp, size_t *offset)
256
42.4k
{
257
42.4k
  char *end;
258
259
42.4k
  while (ISSPACE (*adr))
260
0
    adr++;
261
42.4k
  if (ISDIGIT (*adr) || *adr == 0)
262
25.4k
    return false;
263
  /* Could be either symbol or hex number. Check if it has +.  */
264
16.9k
  if (TOUPPER(*adr) >= 'A' && TOUPPER(*adr) <= 'F' && !strchr (adr, '+'))
265
16.9k
    return false;
266
267
0
  *symp = adr;
268
0
  while (*adr && !ISSPACE (*adr) && *adr != '+')
269
0
    adr++;
270
0
  end = adr;
271
0
  while (ISSPACE (*adr))
272
0
    adr++;
273
0
  *offset = 0;
274
0
  if (*adr == '+')
275
0
    {
276
0
      adr++;
277
0
      *offset = strtoul(adr, NULL, 0);
278
0
    }
279
0
  *end = 0;
280
0
  return true;
281
16.9k
}
282
283
/* Read hexadecimal or symbolic with offset addresses from stdin, translate into
284
   file_name:line_number and optionally function name.  */
285
286
static void
287
translate_addresses (bfd *abfd, asection *section)
288
8.48k
{
289
8.48k
  int read_stdin = (naddr == 0);
290
8.48k
  char *adr;
291
8.48k
  char addr_hex[100];
292
8.48k
  char *symp;
293
8.48k
  size_t offset;
294
295
8.48k
  for (;;)
296
50.9k
    {
297
50.9k
      if (read_stdin)
298
0
  {
299
0
    if (fgets (addr_hex, sizeof addr_hex, stdin) == NULL)
300
0
      break;
301
0
    adr = addr_hex;
302
0
  }
303
50.9k
      else
304
50.9k
  {
305
50.9k
    if (naddr <= 0)
306
8.48k
      break;
307
42.4k
    --naddr;
308
42.4k
    adr = *addr++;
309
42.4k
  }
310
311
42.4k
      if (is_symbol (adr, &symp, &offset))
312
0
        pc = lookup_symbol (abfd, symp, offset);
313
42.4k
      else
314
42.4k
        pc = bfd_scan_vma (adr, NULL, 16);
315
42.4k
      if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
316
12.7k
  {
317
12.7k
    elf_backend_data *bed = get_elf_backend_data (abfd);
318
12.7k
    bfd_vma sign = (bfd_vma) 1 << (bed->s->arch_size - 1);
319
320
12.7k
    pc &= (sign << 1) - 1;
321
12.7k
    if (bed->sign_extend_vma)
322
3.89k
      pc = (pc ^ sign) - sign;
323
12.7k
  }
324
325
42.4k
      if (with_addresses)
326
0
        {
327
0
          printf ("0x");
328
0
          bfd_printf_vma (abfd, pc);
329
330
0
          if (pretty_print)
331
0
            printf (": ");
332
0
          else
333
0
            printf ("\n");
334
0
        }
335
336
42.4k
      found = false;
337
42.4k
      if (section)
338
0
  find_offset_in_section (abfd, section);
339
42.4k
      else
340
42.4k
  bfd_map_over_sections (abfd, find_address_in_section, NULL);
341
342
42.4k
      if (! found)
343
34.7k
  {
344
34.7k
    if (with_functions)
345
0
      {
346
0
        if (pretty_print)
347
0
    printf ("?? ");
348
0
        else
349
0
    printf ("??\n");
350
0
      }
351
34.7k
    printf ("??:0\n");
352
34.7k
  }
353
7.65k
      else
354
7.65k
  {
355
7.65k
    while (1)
356
7.65k
            {
357
7.65k
              if (with_functions)
358
0
                {
359
0
                  const char *name;
360
0
                  char *alloc = NULL;
361
362
0
                  name = functionname;
363
0
                  if (name == NULL || *name == '\0')
364
0
                    name = "??";
365
0
                  else if (do_demangle)
366
0
                    {
367
0
                      alloc = bfd_demangle (abfd, name, demangle_flags);
368
0
                      if (alloc != NULL)
369
0
                        name = alloc;
370
0
                    }
371
372
0
                  printf ("%s", name);
373
0
                  if (pretty_print)
374
        /* Note for translators:  This printf is used to join the
375
           function name just printed above to the line number/
376
           file name pair that is about to be printed below.  Eg:
377
378
             foo at 123:bar.c  */
379
0
                    printf (_(" at "));
380
0
                  else
381
0
                    printf ("\n");
382
383
0
      free (alloc);
384
0
                }
385
386
7.65k
              if (base_names && filename != NULL)
387
0
                {
388
0
                  const char *h;
389
390
0
                  h = strrchr (filename, '/');
391
0
                  if (h != NULL)
392
0
                    filename = h + 1;
393
0
                }
394
395
7.65k
              printf ("%s:", filename ? filename : "??");
396
7.65k
        if (line != 0)
397
1.04k
                {
398
1.04k
                  if (discriminator != 0)
399
0
                    printf ("%u (discriminator %u)\n", line, discriminator);
400
1.04k
                  else
401
1.04k
                    printf ("%u\n", line);
402
1.04k
                }
403
6.60k
        else
404
6.60k
    printf ("?\n");
405
7.65k
              if (!unwind_inlines)
406
7.65k
                found = false;
407
0
              else
408
0
                found = bfd_find_inliner_info (abfd, &filename, &functionname,
409
7.65k
                 &line);
410
7.65k
              if (! found)
411
7.65k
                break;
412
0
              if (pretty_print)
413
    /* Note for translators: This printf is used to join the
414
       line number/file name pair that has just been printed with
415
       the line number/file name pair that is going to be printed
416
       by the next iteration of the while loop.  Eg:
417
418
         123:bar.c (inlined by) 456:main.c  */
419
0
                printf (_(" (inlined by) "));
420
0
            }
421
7.65k
  }
422
423
      /* fflush() is essential for using this command as a server
424
         child process that reads addresses from a pipe and responds
425
         with line number information, processing one address at a
426
         time.  */
427
42.4k
      fflush (stdout);
428
42.4k
    }
429
8.48k
}
430
431
/* Process a file.  Returns an exit value for main().  */
432
433
static int
434
process_file (const char *file_name, const char *section_name,
435
        const char *target)
436
11.0k
{
437
11.0k
  bfd *abfd;
438
11.0k
  asection *section;
439
11.0k
  char **matching;
440
441
11.0k
  if (get_file_size (file_name) < 1)
442
0
    return 1;
443
444
11.0k
  abfd = bfd_openr (file_name, target);
445
11.0k
  if (abfd == NULL)
446
0
    bfd_fatal (file_name);
447
448
  /* Decompress sections.  */
449
11.0k
  abfd->flags |= BFD_DECOMPRESS;
450
451
11.0k
  if (bfd_check_format (abfd, bfd_archive))
452
403
    {
453
403
      non_fatal (_("%s: cannot get addresses from archive"), file_name);
454
403
      bfd_close (abfd);
455
403
      return 1;
456
403
    }
457
458
10.6k
  if (! bfd_check_format_matches (abfd, bfd_object, &matching))
459
2.15k
    {
460
2.15k
      bfd_nonfatal (bfd_get_filename (abfd));
461
2.15k
      if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
462
498
  list_matching_formats (matching);
463
2.15k
      bfd_close (abfd);
464
2.15k
      return 1;
465
2.15k
    }
466
467
8.48k
  if (section_name != NULL)
468
0
    {
469
0
      section = bfd_get_section_by_name (abfd, section_name);
470
0
      if (section == NULL)
471
0
  {
472
0
    non_fatal (_("%s: cannot find section %s"), file_name, section_name);
473
0
    bfd_close (abfd);
474
0
    return 1;
475
0
  }
476
0
    }
477
8.48k
  else
478
8.48k
    section = NULL;
479
480
8.48k
  slurp_symtab (abfd);
481
482
8.48k
  translate_addresses (abfd, section);
483
484
8.48k
  free (syms);
485
8.48k
  syms = NULL;
486
487
8.48k
  bfd_close (abfd);
488
489
8.48k
  return 0;
490
8.48k
}
491

492
int
493
old_main32 (int argc, char **argv);
494
int old_main32 (int argc, char **argv)
495
0
{
496
0
  const char *file_name;
497
0
  const char *section_name;
498
0
  char *target;
499
0
  int c;
500
501
0
#ifdef HAVE_LC_MESSAGES
502
0
  setlocale (LC_MESSAGES, "");
503
0
#endif
504
0
  setlocale (LC_CTYPE, "");
505
0
  bindtextdomain (PACKAGE, LOCALEDIR);
506
0
  textdomain (PACKAGE);
507
508
0
  program_name = *argv;
509
0
  xmalloc_set_program_name (program_name);
510
0
  bfd_set_error_program_name (program_name);
511
512
0
  expandargv (&argc, &argv);
513
514
0
  if (bfd_init () != BFD_INIT_MAGIC)
515
0
    fatal (_("fatal error: libbfd ABI mismatch"));
516
0
  set_default_bfd_target ();
517
518
0
  file_name = NULL;
519
0
  section_name = NULL;
520
0
  target = NULL;
521
0
  while ((c = getopt_long (argc, argv, "ab:Ce:rRsfHhij:pVv", long_options, (int *) 0))
522
0
   != EOF)
523
0
    {
524
0
      switch (c)
525
0
  {
526
0
  case 0:
527
0
    break;   /* We've been given a long option.  */
528
0
  case 'a':
529
0
    with_addresses = true;
530
0
    break;
531
0
  case 'b':
532
0
    target = optarg;
533
0
    break;
534
0
  case 'C':
535
0
    do_demangle = true;
536
0
    if (optarg != NULL)
537
0
      {
538
0
        enum demangling_styles style;
539
540
0
        style = cplus_demangle_name_to_style (optarg);
541
0
        if (style == unknown_demangling)
542
0
    fatal (_("unknown demangling style `%s'"),
543
0
           optarg);
544
545
0
        cplus_demangle_set_style (style);
546
0
      }
547
0
    break;
548
0
  case 'r':
549
0
    demangle_flags |= DMGL_NO_RECURSE_LIMIT;
550
0
    break;
551
0
  case 'R':
552
0
    demangle_flags &= ~ DMGL_NO_RECURSE_LIMIT;
553
0
    break;
554
0
  case 'e':
555
0
    file_name = optarg;
556
0
    break;
557
0
  case 's':
558
0
    base_names = true;
559
0
    break;
560
0
  case 'f':
561
0
    with_functions = true;
562
0
    break;
563
0
        case 'p':
564
0
          pretty_print = true;
565
0
          break;
566
0
  case 'v':
567
0
  case 'V':
568
0
    print_version ("addr2line");
569
0
    break;
570
0
  case 'h':
571
0
  case 'H':
572
0
    usage (stdout, 0);
573
0
    break;
574
0
  case 'i':
575
0
    unwind_inlines = true;
576
0
    break;
577
0
  case 'j':
578
0
    section_name = optarg;
579
0
    break;
580
0
  default:
581
0
    usage (stderr, 1);
582
0
    break;
583
0
  }
584
0
    }
585
586
0
  if (file_name == NULL)
587
0
    file_name = "a.out";
588
589
0
  addr = argv + optind;
590
0
  naddr = argc - optind;
591
592
0
  return process_file (file_name, section_name, target);
593
0
}