LoongArch: gas: Add LSX and LASX instructions test
[binutils-gdb.git] / gas / dwarf2dbg.c
index e927c5059b8af36cecb30cea37214e915f72c4cc..331264b1f6f76f7c8e4794d7cc539aba141a2ea2 100644 (file)
@@ -1,5 +1,5 @@
 /* dwarf2dbg.c - DWARF2 debug support
-   Copyright (C) 1999-2020 Free Software Foundation, Inc.
+   Copyright (C) 1999-2023 Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
    This file is part of GAS, the GNU Assembler.
        .file FILENO "file.c"
        .loc  FILENO LINENO [COLUMN] [basic_block] [prologue_end] \
              [epilogue_begin] [is_stmt VALUE] [isa VALUE] \
-             [discriminator VALUE]
+             [discriminator VALUE] [view VALUE]
 */
 
 #include "as.h"
 #include "safe-ctype.h"
-
-#ifdef HAVE_LIMITS_H
 #include <limits.h>
-#else
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#ifndef INT_MAX
-#define INT_MAX (int) (((unsigned) (-1)) >> 1)
-#endif
-#endif
-
 #include "dwarf2dbg.h"
 #include <filenames.h>
 
@@ -97,9 +86,9 @@
 #define DWARF2_ARANGES_VERSION 2
 #endif
 
-/* This implementation outputs version 3 .debug_line information.  */
+/* The .debug_line version is the same as the .debug_info version.  */
 #ifndef DWARF2_LINE_VERSION
-#define DWARF2_LINE_VERSION (dwarf_level > 3 ? dwarf_level : 3)
+#define DWARF2_LINE_VERSION DWARF2_VERSION
 #endif
 
 /* The .debug_rnglists has only been in DWARF version 5. */
    Note: If you want to change this, you'll have to update the
    "standard_opcode_lengths" table that is emitted below in
    out_debug_line().  */
-#define DWARF2_LINE_OPCODE_BASE                13
+#define DWARF2_LINE_OPCODE_BASE                (DWARF2_LINE_VERSION == 2 ? 10 : 13)
 
 #ifndef DWARF2_LINE_BASE
   /* Minimum line offset in a special line info. opcode.  This value
 #define TC_PARSE_CONS_RETURN_NONE BFD_RELOC_NONE
 #endif
 
+#define GAS_ABBREV_COMP_UNIT 1
+#define GAS_ABBREV_SUBPROG   2
+#define GAS_ABBREV_NO_TYPE   3
+
 struct line_entry
 {
   struct line_entry *next;
@@ -218,34 +211,28 @@ struct file_entry
 static struct file_entry *files;
 static unsigned int files_in_use;
 static unsigned int files_allocated;
-static unsigned int num_of_auto_assigned;
 
 /* Table of directories used by .debug_line.  */
-static char **       dirs = NULL;
-static unsigned int  dirs_in_use = 0;
-static unsigned int  dirs_allocated = 0;
+static char **       dirs;
+static unsigned int  dirs_in_use;
+static unsigned int  dirs_allocated;
 
 /* TRUE when we've seen a .loc directive recently.  Used to avoid
    doing work when there's nothing to do.  Will be reset by
    dwarf2_consume_line_info.  */
-bfd_boolean dwarf2_loc_directive_seen;
+bool dwarf2_loc_directive_seen;
 
 /* TRUE when we've seen any .loc directive at any time during parsing.
    Indicates the user wants us to generate a .debug_line section.
    Used in dwarf2_finish as sanity check.  */
-static bfd_boolean dwarf2_any_loc_directive_seen;
+static bool dwarf2_any_loc_directive_seen;
 
 /* TRUE when we're supposed to set the basic block mark whenever a
    label is seen.  */
-bfd_boolean dwarf2_loc_mark_labels;
+bool dwarf2_loc_mark_labels;
 
 /* Current location as indicated by the most recent .loc directive.  */
-static struct dwarf2_line_info current =
-{
-  1, 1, 0, 0,
-  DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0,
-  0, NULL
-};
+static struct dwarf2_line_info current;
 
 /* This symbol is used to recognize view number forced resets in loc
    lists.  */
@@ -279,7 +266,7 @@ generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
 /* Find or create (if CREATE_P) an entry for SEG+SUBSEG in ALL_SEGS.  */
 
 static struct line_subseg *
-get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p)
+get_line_subseg (segT seg, subsegT subseg, bool create_p)
 {
   struct line_seg *s = seg_info (seg)->dwarf2_line_seg;
   struct line_subseg **pss, *lss;
@@ -353,7 +340,7 @@ set_or_check_view (struct line_entry *e, struct line_entry *p,
   /* First, compute !(E->label > P->label), to tell whether or not
      we're to reset the view number.  If we can't resolve it to a
      constant, keep it symbolic.  */
-  if (!p || (e->loc.view == force_reset_view && force_reset_view))
+  if (!p || (e->loc.u.view == force_reset_view && force_reset_view))
     {
       viewx.X_op = O_constant;
       viewx.X_add_number = 0;
@@ -378,9 +365,9 @@ set_or_check_view (struct line_entry *e, struct line_entry *p,
        }
     }
 
-  if (S_IS_DEFINED (e->loc.view) && symbol_constant_p (e->loc.view))
+  if (S_IS_DEFINED (e->loc.u.view) && symbol_constant_p (e->loc.u.view))
     {
-      expressionS *value = symbol_get_value_expression (e->loc.view);
+      expressionS *value = symbol_get_value_expression (e->loc.u.view);
       /* We can't compare the view numbers at this point, because in
         VIEWX we've only determined whether we're to reset it so
         far.  */
@@ -414,18 +401,27 @@ set_or_check_view (struct line_entry *e, struct line_entry *p,
   if (viewx.X_op != O_constant || viewx.X_add_number)
     {
       expressionS incv;
+      expressionS *p_view;
 
-      if (!p->loc.view)
-       {
-         p->loc.view = symbol_temp_make ();
-         gas_assert (!S_IS_DEFINED (p->loc.view));
-       }
+      if (!p->loc.u.view)
+       p->loc.u.view = symbol_temp_make ();
 
       memset (&incv, 0, sizeof (incv));
       incv.X_unsigned = 1;
       incv.X_op = O_symbol;
-      incv.X_add_symbol = p->loc.view;
+      incv.X_add_symbol = p->loc.u.view;
       incv.X_add_number = 1;
+      p_view = symbol_get_value_expression (p->loc.u.view);
+      if (p_view->X_op == O_constant || p_view->X_op == O_symbol)
+       {
+         /* If we can, constant fold increments so that a chain of
+            expressions v + 1 + 1 ... + 1 is not created.
+            resolve_expression isn't ideal for this purpose.  The
+            base v might not be resolvable until later.  */
+         incv.X_op = p_view->X_op;
+         incv.X_add_symbol = p_view->X_add_symbol;
+         incv.X_add_number = p_view->X_add_number + 1;
+       }
 
       if (viewx.X_op == O_constant)
        {
@@ -441,16 +437,16 @@ set_or_check_view (struct line_entry *e, struct line_entry *p,
        }
     }
 
-  if (!S_IS_DEFINED (e->loc.view))
+  if (!S_IS_DEFINED (e->loc.u.view))
     {
-      symbol_set_value_expression (e->loc.view, &viewx);
-      S_SET_SEGMENT (e->loc.view, expr_section);
-      symbol_set_frag (e->loc.view, &zero_address_frag);
+      symbol_set_value_expression (e->loc.u.view, &viewx);
+      S_SET_SEGMENT (e->loc.u.view, expr_section);
+      symbol_set_frag (e->loc.u.view, &zero_address_frag);
     }
 
   /* Define and attempt to simplify any earlier views needed to
      compute E's.  */
-  if (h && p && p->loc.view && !S_IS_DEFINED (p->loc.view))
+  if (h && p && p->loc.u.view && !S_IS_DEFINED (p->loc.u.view))
     {
       struct line_entry *h2;
       /* Reverse the list to avoid quadratic behavior going backwards
@@ -470,7 +466,9 @@ set_or_check_view (struct line_entry *e, struct line_entry *p,
            break;
          set_or_check_view (r, r->next, NULL);
        }
-      while (r->next && r->next->loc.view && !S_IS_DEFINED (r->next->loc.view)
+      while (r->next
+            && r->next->loc.u.view
+            && !S_IS_DEFINED (r->next->loc.u.view)
             && (r = r->next));
 
       /* Unreverse the list, so that we can go forward again.  */
@@ -486,14 +484,14 @@ set_or_check_view (struct line_entry *e, struct line_entry *p,
             view of the previous subsegment.  */
          if (r == h)
            continue;
-         gas_assert (S_IS_DEFINED (r->loc.view));
-         resolve_expression (symbol_get_value_expression (r->loc.view));
+         gas_assert (S_IS_DEFINED (r->loc.u.view));
+         resolve_expression (symbol_get_value_expression (r->loc.u.view));
        }
       while (r != p && (r = r->next));
 
       /* Now that we've defined and computed all earlier views that might
         be needed to compute E's, attempt to simplify it.  */
-      resolve_expression (symbol_get_value_expression (e->loc.view));
+      resolve_expression (symbol_get_value_expression (e->loc.u.view));
     }
 }
 
@@ -514,8 +512,9 @@ dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc)
   if ((now_seg->flags & need_flags) != need_flags)
     {
       /* FIXME: Add code to suppress multiple warnings ?  */
-      as_warn ("dwarf line number information for %s ignored",
-              segment_name (now_seg));
+      if (debug_type != DEBUG_DWARF2)
+       as_warn ("dwarf line number information for %s ignored",
+                segment_name (now_seg));
       return;
     }
 
@@ -524,14 +523,12 @@ dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc)
   e->label = label;
   e->loc = *loc;
 
-  lss = get_line_subseg (now_seg, now_subseg, TRUE);
+  lss = get_line_subseg (now_seg, now_subseg, true);
 
   /* Subseg heads are chained to previous subsegs in
      dwarf2_finish.  */
-  if (loc->view && lss->head)
-    set_or_check_view (e,
-                      (struct line_entry *)lss->ptail,
-                      lss->head);
+  if (loc->filenum != -1u && loc->u.view && lss->head)
+    set_or_check_view (e, (struct line_entry *) lss->ptail, lss->head);
 
   *lss->ptail = e;
   lss->ptail = &e->next;
@@ -539,38 +536,55 @@ dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc)
 
 /* Record an entry for LOC occurring at OFS within the current fragment.  */
 
+static unsigned int dw2_line;
+static const char *dw2_filename;
+static int label_num;
+
 void
 dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc)
 {
-  static unsigned int line = -1;
-  static unsigned int filenum = -1;
-
   symbolS *sym;
 
   /* Early out for as-yet incomplete location information.  */
   if (loc->line == 0)
     return;
-  if (loc->filenum == 0 && DWARF2_LINE_VERSION < 5)
-    return;
+  if (loc->filenum == 0)
+    {
+      if (dwarf_level < 5)
+       dwarf_level = 5;
+      if (DWARF2_LINE_VERSION < 5)
+       return;
+    }
 
   /* Don't emit sequences of line symbols for the same line when the
      symbols apply to assembler code.  It is necessary to emit
      duplicate line symbols when a compiler asks for them, because GDB
      uses them to determine the end of the prologue.  */
-  if (debug_type == DEBUG_DWARF2
-      && line == loc->line && filenum == loc->filenum)
-    return;
+  if (debug_type == DEBUG_DWARF2)
+    {
+      if (dw2_line == loc->line)
+       {
+         if (dw2_filename == loc->u.filename)
+           return;
+         if (filename_cmp (dw2_filename, loc->u.filename) == 0)
+           {
+             dw2_filename = loc->u.filename;
+             return;
+           }
+       }
 
-  line = loc->line;
-  filenum = loc->filenum;
+      dw2_line = loc->line;
+      dw2_filename = loc->u.filename;
+    }
 
   if (linkrelax)
     {
-      char name[120];
+      char name[32];
 
       /* Use a non-fake name for the line number location,
         so that it can be referred to by relocations.  */
-      sprintf (name, ".Loc.%u.%u", line, filenum);
+      sprintf (name, ".Loc.%u", label_num);
+      label_num++;
       sym = symbol_new (name, now_seg, frag_now, ofs);
     }
   else
@@ -596,9 +610,10 @@ get_basename (const char * pathname)
 }
 
 static unsigned int
-get_directory_table_entry (const char *  dirname,
-                          size_t        dirlen,
-                          bfd_boolean   can_use_zero)
+get_directory_table_entry (const char *dirname,
+                          const char *file0_dirname,
+                          size_t dirlen,
+                          bool can_use_zero)
 {
   unsigned int d;
 
@@ -625,7 +640,23 @@ get_directory_table_entry (const char *  dirname,
   if (can_use_zero)
     {
       if (dirs == NULL || dirs[0] == NULL)
-       d = 0;
+       {
+         const char * pwd = file0_dirname ? file0_dirname : getpwd ();
+
+         if (dwarf_level >= 5 && filename_cmp (dirname, pwd) != 0)
+           {
+             /* In DWARF-5 the 0 entry in the directory table is
+                expected to be the same as the DW_AT_comp_dir (which
+                is set to the current build directory).  Since we are
+                about to create a directory entry that is not the
+                same, allocate the current directory first.  */
+             (void) get_directory_table_entry (pwd, file0_dirname,
+                                               strlen (pwd), true);
+             d = 1;
+           }
+         else
+           d = 0;
+       }
     }
   else if (d == 0)
     d = 1;
@@ -633,8 +664,8 @@ get_directory_table_entry (const char *  dirname,
   if (d >= dirs_allocated)
     {
       unsigned int old = dirs_allocated;
-
-      dirs_allocated = d + 32;
+#define DIR_TABLE_INCREMENT 32
+      dirs_allocated = d + DIR_TABLE_INCREMENT;
       dirs = XRESIZEVEC (char *, dirs, dirs_allocated);
       memset (dirs + old, 0, (dirs_allocated - old) * sizeof (char *));
     }
@@ -646,23 +677,26 @@ get_directory_table_entry (const char *  dirname,
   return d;  
 }
 
-static bfd_boolean
-assign_file_to_slot (unsigned long i, const char *file, unsigned int dir)
+static bool
+assign_file_to_slot (unsigned int i, const char *file, unsigned int dir)
 {
   if (i >= files_allocated)
     {
-      unsigned int old = files_allocated;
+      unsigned int want = i + 32;
 
-      files_allocated = i + 32;
       /* Catch wraparound.  */
-      if (files_allocated <= old)
+      if (want < files_allocated
+         || want < i
+         || want > UINT_MAX / sizeof (struct file_entry))
        {
-         as_bad (_("file number %lu is too big"), (unsigned long) i);
-         return FALSE;
+         as_bad (_("file number %u is too big"), i);
+         return false;
        }
 
-      files = XRESIZEVEC (struct file_entry, files, files_allocated);
-      memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry));
+      files = XRESIZEVEC (struct file_entry, files, want);
+      memset (files + files_allocated, 0,
+             (want - files_allocated) * sizeof (struct file_entry));
+      files_allocated = want;
     }
 
   files[i].filename = file;
@@ -672,7 +706,7 @@ assign_file_to_slot (unsigned long i, const char *file, unsigned int dir)
   if (files_in_use < i + 1)
     files_in_use = i + 1;
 
-  return TRUE;
+  return true;
 }
 
 /* Get a .debug_line file number for PATHNAME.  If there is a
@@ -681,10 +715,12 @@ assign_file_to_slot (unsigned long i, const char *file, unsigned int dir)
    Returns the slot number allocated to that filename or -1
    if there was a problem.  */
 
+static int last_used;
+static int last_used_dir_len;
+
 static signed int
 allocate_filenum (const char * pathname)
 {
-  static signed int last_used = -1, last_used_dir_len = 0;
   const char *file;
   size_t dir_len;
   unsigned int i, dir;
@@ -705,9 +741,9 @@ allocate_filenum (const char * pathname)
        }
       else
        {
-         if (filename_ncmp (pathname, dirname, last_used_dir_len) == 0
-             && IS_DIR_SEPARATOR (pathname [last_used_dir_len])
-             && filename_cmp (pathname + last_used_dir_len + 1,
+         if (filename_ncmp (pathname, dirname, last_used_dir_len - 1) == 0
+             && IS_DIR_SEPARATOR (pathname [last_used_dir_len - 1])
+             && filename_cmp (pathname + last_used_dir_len,
                               files[last_used].filename) == 0)
            return last_used;
        }
@@ -716,7 +752,7 @@ allocate_filenum (const char * pathname)
   file = get_basename (pathname);
   dir_len = file - pathname;
 
-  dir = get_directory_table_entry (pathname, dir_len, FALSE);
+  dir = get_directory_table_entry (pathname, NULL, dir_len, false);
 
   /* Do not use slot-0.  That is specifically reserved for use by
      the '.file 0 "name"' directive.  */
@@ -733,29 +769,87 @@ allocate_filenum (const char * pathname)
   if (!assign_file_to_slot (i, file, dir))
     return -1;
 
-  num_of_auto_assigned++;
-
   last_used = i;
   last_used_dir_len = dir_len;
 
   return i;
 }
 
+/* Run through the list of line entries starting at E, allocating
+   file entries for gas generated debug.  */
+
+static void
+do_allocate_filenum (struct line_entry *e)
+{
+  do
+    {
+      if (e->loc.filenum == -1u)
+       {
+         e->loc.filenum = allocate_filenum (e->loc.u.filename);
+         e->loc.u.view = NULL;
+       }
+      e = e->next;
+    }
+  while (e);
+}
+
+/* Remove any generated line entries.  These don't live comfortably
+   with compiler generated line info.  If THELOT then remove
+   everything, freeing all list entries we have created.  */
+
+static void
+purge_generated_debug (bool thelot)
+{
+  struct line_seg *s, *nexts;
+
+  for (s = all_segs; s; s = nexts)
+    {
+      struct line_subseg *lss, *nextlss;
+
+      for (lss = s->head; lss; lss = nextlss)
+       {
+         struct line_entry *e, *next;
+
+         for (e = lss->head; e; e = next)
+           {
+             if (!thelot)
+               know (e->loc.filenum == -1u);
+             next = e->next;
+             free (e);
+           }
+
+         lss->head = NULL;
+         lss->ptail = &lss->head;
+         lss->pmove_tail = &lss->head;
+         nextlss = lss->next;
+         if (thelot)
+           free (lss);
+       }
+      nexts = s->next;
+      if (thelot)
+       {
+         seg_info (s->seg)->dwarf2_line_seg = NULL;
+         free (s);
+       }
+    }
+}
+
 /* Allocate slot NUM in the .debug_line file table to FILENAME.
    If DIRNAME is not NULL or there is a directory component to FILENAME
    then this will be stored in the directory table, if not already present.
    if WITH_MD5 is TRUE then there is a md5 value in generic_bignum.
    Returns TRUE if allocation succeeded, FALSE otherwise.  */
 
-static bfd_boolean
-allocate_filename_to_slot (const char *  dirname,
-                          const char *  filename,
-                          unsigned int  num,
-                          bfd_boolean   with_md5)
+static bool
+allocate_filename_to_slot (const char *dirname,
+                          const char *filename,
+                          unsigned int num,
+                          bool with_md5)
 {
   const char *file;
   size_t dirlen;
   unsigned int i, d;
+  const char *file0_dirname;
 
   /* Short circuit the common case of adding the same pathname
      as last time.  */
@@ -763,7 +857,7 @@ allocate_filename_to_slot (const char *  dirname,
     {
       const char * dir = NULL;
 
-      if (dirs)
+      if (dirs != NULL)
        dir = dirs[files[num].dir];
 
       if (with_md5
@@ -781,9 +875,17 @@ allocate_filename_to_slot (const char *  dirname,
          /* If the filenames match, but the directory table entry was
             empty, then fill it with the provided directory name.  */
          if (dir == NULL)
-           dirs[files[num].dir] = xmemdup0 (dirname, strlen (dirname));
+           {
+             if (dirs == NULL)
+               {
+                 dirs_allocated = files[num].dir + DIR_TABLE_INCREMENT;
+                 dirs = XCNEWVEC (char *, dirs_allocated);
+               }
+             
+             dirs[files[num].dir] = xmemdup0 (dirname, strlen (dirname));
+           }
            
-         return TRUE;
+         return true;
        }
       else if (dir != NULL) 
        {
@@ -791,18 +893,26 @@ allocate_filename_to_slot (const char *  dirname,
          if (filename_ncmp (filename, dir, dirlen) == 0
              && IS_DIR_SEPARATOR (filename [dirlen])
              && filename_cmp (filename + dirlen + 1, files[num].filename) == 0)
-           return TRUE;
+           return true;
        }
       else /* dir == NULL  */
        {
          file = get_basename (filename);
          if (filename_cmp (file, files[num].filename) == 0)
            {
+             /* The filenames match, but the directory table entry is empty.
+                Fill it with the provided directory name.  */
              if (file > filename)
-               /* The filenames match, but the directory table entry is empty.
-                  Fill it with the provided directory name.  */
-               dirs[files[num].dir] = xmemdup0 (filename, file - filename);
-             return TRUE;
+               {
+                 if (dirs == NULL)
+                   {
+                     dirs_allocated = files[num].dir + DIR_TABLE_INCREMENT;
+                     dirs = XCNEWVEC (char *, dirs_allocated);
+                   }
+
+                 dirs[files[num].dir] = xmemdup0 (filename, file - filename);
+               }
+             return true;
            }
        }
 
@@ -815,26 +925,47 @@ allocate_filename_to_slot (const char *  dirname,
              dirname == NULL ? "" : dirname,
              dirname == NULL ? "" : "/",
              filename);
-      return FALSE;
+      return false;
     }
 
-  if (dirname == NULL)
+  /* For file .0, the directory name is the current directory and the file
+     may be in another directory contained in the file name.  */
+  if (num == 0)
     {
-      dirname = filename;
+      file0_dirname = dirname;
+
       file = get_basename (filename);
-      dirlen = file - filename;
+
+      if (dirname && file == filename)
+       dirlen = strlen (dirname);
+      else
+       {
+         dirname = filename;
+         dirlen = file - filename;
+       }
     }
   else
     {
-      dirlen = strlen (dirname);
-      file = filename;
+      file0_dirname = NULL;
+
+      if (dirname == NULL)
+       {
+         dirname = filename;
+         file = get_basename (filename);
+         dirlen = file - filename;
+       }
+      else
+       {
+         dirlen = strlen (dirname);
+         file = filename;
+       }
     }
-  
-  d = get_directory_table_entry (dirname, dirlen, num == 0);
+
+  d = get_directory_table_entry (dirname, file0_dirname, dirlen, num == 0);
   i = num;
 
   if (! assign_file_to_slot (i, file, d))
-    return FALSE;
+    return false;
 
   if (with_md5)
     {
@@ -886,7 +1017,7 @@ allocate_filename_to_slot (const char *  dirname,
   else
     memset (files[i].md5, 0, NUM_MD5_BYTES);
 
-  return TRUE;
+  return true;
 }
 
 /* Returns the current source information.  If .file directives have
@@ -899,17 +1030,12 @@ dwarf2_where (struct dwarf2_line_info *line)
 {
   if (debug_type == DEBUG_DWARF2)
     {
-      const char *filename;
-
-      memset (line, 0, sizeof (*line));
-      filename = as_where (&line->line);
-      line->filenum = allocate_filenum (filename);
-      /* FIXME: We should check the return value from allocate_filenum.  */
+      line->u.filename = as_where (&line->line);
+      line->filenum = -1u;
       line->column = 0;
       line->flags = DWARF2_FLAG_IS_STMT;
       line->isa = current.isa;
       line->discriminator = current.discriminator;
-      line->view = NULL;
     }
   else
     *line = current;
@@ -959,7 +1085,7 @@ dwarf2_move_insn (int delta)
   if (delta == 0)
     return;
 
-  lss = get_line_subseg (now_seg, now_subseg, FALSE);
+  lss = get_line_subseg (now_seg, now_subseg, false);
   if (!lss)
     return;
 
@@ -982,13 +1108,13 @@ dwarf2_consume_line_info (void)
 {
   /* Unless we generate DWARF2 debugging information for each
      assembler line, we only emit one line symbol for one LOC.  */
-  dwarf2_loc_directive_seen = FALSE;
+  dwarf2_loc_directive_seen = false;
 
   current.flags &= ~(DWARF2_FLAG_BASIC_BLOCK
                     | DWARF2_FLAG_PROLOGUE_END
                     | DWARF2_FLAG_EPILOGUE_BEGIN);
   current.discriminator = 0;
-  current.view = NULL;
+  current.u.view = NULL;
 }
 
 /* Called for each (preferably code) label.  If dwarf2_loc_mark_labels
@@ -1017,7 +1143,7 @@ dwarf2_emit_label (symbolS *label)
 }
 
 /* Handle two forms of .file directive:
-   - Pass .file "source.c" to s_app_file
+   - Pass .file "source.c" to s_file
    - Handle .file 1 "source.c" by adding an entry to the DWARF-2 file table
 
    If an entry is added to the file table, return a pointer to the filename.  */
@@ -1025,28 +1151,32 @@ dwarf2_emit_label (symbolS *label)
 char *
 dwarf2_directive_filename (void)
 {
-  bfd_boolean with_md5 = FALSE;
+  bool with_md5 = false;
   valueT num;
   char *filename;
   const char * dirname = NULL;
   int filename_len;
-  unsigned int i;
 
   /* Continue to accept a bare string and pass it off.  */
   SKIP_WHITESPACE ();
   if (*input_line_pointer == '"')
     {
-      s_app_file (0);
+      s_file (0);
       return NULL;
     }
 
   num = get_absolute_expression ();
 
-  if ((offsetT) num < 1 && DWARF2_LINE_VERSION < 5)
+  if ((offsetT) num < 1)
     {
-      as_bad (_("file number less than one"));
-      ignore_rest_of_line ();
-      return NULL;
+      if (num == 0 && dwarf_level < 5)
+       dwarf_level = 5;
+      if ((offsetT) num < 0 || DWARF2_LINE_VERSION < 5)
+       {
+         as_bad (_("file number less than one"));
+         ignore_rest_of_line ();
+         return NULL;
+       }
     }
 
   /* FIXME: Should we allow ".file <N>\n" as an expression meaning
@@ -1067,10 +1197,12 @@ dwarf2_directive_filename (void)
        {
          dirname = filename;
          filename = demand_copy_C_string (&filename_len);
+         if (filename == NULL)
+           return NULL;
          SKIP_WHITESPACE ();
        }
 
-      if (strncmp (input_line_pointer, "md5", 3) == 0)
+      if (startswith (input_line_pointer, "md5"))
        {
          input_line_pointer += 3;
          SKIP_WHITESPACE ();
@@ -1080,7 +1212,7 @@ dwarf2_directive_filename (void)
          if (exp.X_op != O_big)
            as_bad (_("md5 value too small or not a constant"));
          else
-           with_md5 = TRUE;
+           with_md5 = true;
        }
     }
 
@@ -1088,6 +1220,8 @@ dwarf2_directive_filename (void)
 
   /* A .file directive implies compiler generated debug information is
      being supplied.  Turn off gas generated debug info.  */
+  if (debug_type == DEBUG_DWARF2)
+    purge_generated_debug (false);
   debug_type = DEBUG_NONE;
 
   if (num != (unsigned int) num
@@ -1097,18 +1231,6 @@ dwarf2_directive_filename (void)
       return NULL;
     }
 
-  if (num_of_auto_assigned)
-    {
-      /* Clear slots auto-assigned before the first .file <NUMBER>
-        directive was seen.  */
-      if (files_in_use != (num_of_auto_assigned + 1))
-       abort ();
-      for (i = 1; i < files_in_use; i++)
-       files[i].filename = NULL;
-      files_in_use = 0;
-      num_of_auto_assigned = 0;
-    }
-
   if (! allocate_filename_to_slot (dirname, filename, (unsigned int) num,
                                   with_md5))
     return NULL;
@@ -1141,19 +1263,26 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
 
   if (filenum < 1)
     {
-      if (filenum != 0 || DWARF2_LINE_VERSION < 5)
+      if (filenum == 0 && dwarf_level < 5)
+       dwarf_level = 5;
+      if (filenum < 0 || DWARF2_LINE_VERSION < 5)
        {
          as_bad (_("file number less than one"));
          return;
        }
     }
 
-  if (filenum >= (int) files_in_use || files[filenum].filename == NULL)
+  if ((valueT) filenum >= files_in_use || files[filenum].filename == NULL)
     {
       as_bad (_("unassigned file number %ld"), (long) filenum);
       return;
     }
 
+  /* debug_type will be turned off by dwarf2_directive_filename, and
+     if we don't have a dwarf style .file then files_in_use will be
+     zero and the above error will trigger.  */
+  gas_assert (debug_type == DEBUG_NONE);
+
   current.filenum = filenum;
   current.line = line;
   current.discriminator = 0;
@@ -1201,11 +1330,15 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
        }
       else if (strcmp (p, "prologue_end") == 0)
        {
+         if (dwarf_level < 3)
+           dwarf_level = 3;
          current.flags |= DWARF2_FLAG_PROLOGUE_END;
          *input_line_pointer = c;
        }
       else if (strcmp (p, "epilogue_begin") == 0)
        {
+         if (dwarf_level < 3)
+           dwarf_level = 3;
          current.flags |= DWARF2_FLAG_EPILOGUE_BEGIN;
          *input_line_pointer = c;
        }
@@ -1225,6 +1358,8 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
        }
       else if (strcmp (p, "isa") == 0)
        {
+         if (dwarf_level < 3)
+           dwarf_level = 3;
          (void) restore_line_pointer (c);
          value = get_absolute_expression ();
          if (value >= 0)
@@ -1257,7 +1392,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
          if (ISDIGIT (*input_line_pointer)
              || *input_line_pointer == '-')
            {
-             bfd_boolean force_reset = *input_line_pointer == '-';
+             bool force_reset = *input_line_pointer == '-';
 
              value = get_absolute_expression ();
              if (value != 0)
@@ -1282,13 +1417,15 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
              if (!name)
                return;
              sym = symbol_find_or_make (name);
+             free (name);
              if (S_IS_DEFINED (sym) || symbol_equated_p (sym))
                {
                  if (S_IS_VOLATILE (sym))
                    sym = symbol_clone (sym, 1);
                  else if (!S_CAN_BE_REDEFINED (sym))
                    {
-                     as_bad (_("symbol `%s' is already defined"), name);
+                     as_bad (_("symbol `%s' is already defined"),
+                             S_GET_NAME (sym));
                      return;
                    }
                }
@@ -1296,7 +1433,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
              S_SET_VALUE (sym, 0);
              symbol_set_frag (sym, &zero_address_frag);
            }
-         current.view = sym;
+         current.u.view = sym;
        }
       else
        {
@@ -1309,11 +1446,10 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
     }
 
   demand_empty_rest_of_line ();
-  dwarf2_any_loc_directive_seen = dwarf2_loc_directive_seen = TRUE;
-  debug_type = DEBUG_NONE;
+  dwarf2_any_loc_directive_seen = dwarf2_loc_directive_seen = true;
 
   /* If we were given a view id, emit the row right away.  */
-  if (current.view)
+  if (current.u.view)
     dwarf2_emit_insn (0);
 }
 
@@ -1446,15 +1582,15 @@ out_set_addr (symbolS *sym)
   emit_expr (&exp, sizeof_address);
 }
 
-static void scale_addr_delta (addressT *);
-
 static void
-scale_addr_delta (addressT *addr_delta)
+scale_addr_delta (int line_delta, addressT *addr_delta)
 {
   static int printed_this = 0;
   if (DWARF2_LINE_MIN_INSN_LENGTH > 1)
     {
-      if (*addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0  && !printed_this)
+      /* Don't error on non-instruction bytes at end of section.  */
+      if (line_delta != INT_MAX
+         && *addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0  && !printed_this)
        {
          as_bad("unaligned opcodes detected in executable segment");
          printed_this = 1;
@@ -1477,7 +1613,7 @@ size_inc_line_addr (int line_delta, addressT addr_delta)
   int len = 0;
 
   /* Scale the address delta by the minimum instruction length.  */
-  scale_addr_delta (&addr_delta);
+  scale_addr_delta (line_delta, &addr_delta);
 
   /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence.
      We cannot use special opcodes here, since we want the end_sequence
@@ -1507,7 +1643,7 @@ size_inc_line_addr (int line_delta, addressT addr_delta)
   tmp += DWARF2_LINE_OPCODE_BASE;
 
   /* Avoid overflow when addr_delta is large.  */
-  if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA)
+  if (addr_delta < 256U + MAX_SPECIAL_ADDR_DELTA)
     {
       /* Try using a special opcode.  */
       opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
@@ -1541,7 +1677,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
   gas_assert ((offsetT) addr_delta >= 0);
 
   /* Scale the address delta by the minimum instruction length.  */
-  scale_addr_delta (&addr_delta);
+  scale_addr_delta (line_delta, &addr_delta);
 
   /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence.
      We cannot use special opcodes here, since we want the end_sequence
@@ -1589,7 +1725,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
   tmp += DWARF2_LINE_OPCODE_BASE;
 
   /* Avoid overflow when addr_delta is large.  */
-  if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA)
+  if (addr_delta < 256U + MAX_SPECIAL_ADDR_DELTA)
     {
       /* Try using a special opcode.  */
       opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
@@ -1857,7 +1993,6 @@ process_entries (segT seg, struct line_entry *e)
   fragS *last_frag = NULL, *frag;
   addressT last_frag_ofs = 0, frag_ofs;
   symbolS *last_lab = NULL, *lab;
-  struct line_entry *next;
 
   if (flag_dwarf_sections)
     {
@@ -1880,13 +2015,13 @@ process_entries (segT seg, struct line_entry *e)
       if (strcmp (sec_name, ".text") != 0)
        {
          name = concat (".debug_line", sec_name, (char *) NULL);
-         subseg_set (subseg_get (name, FALSE), 0);
+         subseg_set (subseg_get (name, false), 0);
        }
       else
        /* Don't create a .debug_line.text section -
           that is redundant.  Instead just switch back to the
           normal .debug_line section.  */
-       subseg_set (subseg_get (".debug_line", FALSE), 0);
+       subseg_set (subseg_get (".debug_line", false), 0);
     }
 
   do
@@ -1947,7 +2082,7 @@ process_entries (segT seg, struct line_entry *e)
       frag_ofs = S_GET_VALUE (lab);
 
       if (last_frag == NULL
-         || (e->loc.view == force_reset_view && force_reset_view
+         || (e->loc.u.view == force_reset_view && force_reset_view
              /* If we're going to reset the view, but we know we're
                 advancing the PC, we don't have to force with
                 set_address.  We know we do when we're at the same
@@ -1973,9 +2108,7 @@ process_entries (segT seg, struct line_entry *e)
       last_frag = frag;
       last_frag_ofs = frag_ofs;
 
-      next = e->next;
-      free (e);
-      e = next;
+      e = e->next;
     }
   while (e);
 
@@ -2019,14 +2152,14 @@ static void
 out_dir_and_file_list (segT line_seg, int sizeof_offset)
 {
   size_t size;
-  const char *dir;
+  char *dir;
   char *cp;
-  unsigned int i;
-  bfd_boolean emit_md5 = FALSE;
-  bfd_boolean emit_timestamps = TRUE;
-  bfd_boolean emit_filesize = TRUE;
+  unsigned int i, j;
+  bool emit_md5 = false;
+  bool emit_timestamps = true;
+  bool emit_filesize = true;
   segT line_str_seg = NULL;
-  symbolS *line_strp;
+  symbolS *line_strp, *file0_strp = NULL;
 
   /* Output the Directory Table.  */
   if (DWARF2_LINE_VERSION >= 5)
@@ -2059,17 +2192,15 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
       line_str_seg->entsize = 1;
 
       /* DWARF5 uses slot zero, but that is only set explicitly
-        using a .file 0 directive.  If that isn't used, but dir
-        one is used, then use that as main file directory.
-        Otherwise use pwd as main file directory.  */
-      if (dirs_in_use > 0 && dirs != NULL && dirs[0] != NULL)
+        using a .file 0 directive.  Otherwise use pwd as main file
+        directory.  */
+      if (dirs_in_use > 0 && dirs[0] != NULL)
        dir = remap_debug_filename (dirs[0]);
-      else if (dirs_in_use > 1 && dirs != NULL && dirs[1] != NULL)
-       dir = remap_debug_filename (dirs[1]);
       else
        dir = remap_debug_filename (getpwd ());
 
       line_strp = add_line_strp (line_str_seg, dir);
+      free (dir);
       subseg_set (line_seg, 0);
       TC_DWARF2_EMIT_OFFSET (line_strp, sizeof_offset);
     }
@@ -2088,6 +2219,7 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
          subseg_set (line_seg, 0);
          TC_DWARF2_EMIT_OFFSET (line_strp, sizeof_offset);
        }
+      free (dir);
     }
 
   if (DWARF2_LINE_VERSION < 5)
@@ -2101,13 +2233,13 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
 
       if (((unsigned long) DWARF2_FILE_TIME_NAME ("", "")) == -1UL)
        {
-         emit_timestamps = FALSE;
+         emit_timestamps = false;
          -- columns;
        }
 
       if (DWARF2_FILE_SIZE_NAME ("", "") == -1)
        {
-         emit_filesize = FALSE;
+         emit_filesize = false;
          -- columns;
        }
 
@@ -2116,7 +2248,7 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
          break;
       if (i < files_in_use)
        {
-         emit_md5 = TRUE;
+         emit_md5 = true;
          ++ columns;
        }
       
@@ -2163,19 +2295,24 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
 
       if (files[i].filename == NULL)
        {
-         /* Prevent a crash later, particularly for file 1.  DWARF5
-            uses slot zero, but that is only set explicitly using a
-            .file 0 directive.  If that isn't used, but file 1 is,
-            then use that as main file name.  */
-         if (DWARF2_LINE_VERSION >= 5 && i == 0 && files_in_use >= 1)
-             files[0].filename = files[1].filename;
-         else
-           files[i].filename = "";
          if (DWARF2_LINE_VERSION < 5 || i != 0)
            {
              as_bad (_("unassigned file number %ld"), (long) i);
              continue;
            }
+         /* DWARF5 uses slot zero, but that is only set explicitly using
+            a .file 0 directive.  If that isn't used, but file 1 is, then
+            use that as main file name.  */
+         if (files_in_use > 1 && files[1].filename != NULL)
+           {
+             files[0].filename = files[1].filename;
+             files[0].dir = files[1].dir;
+             if (emit_md5)
+               for (j = 0; j < NUM_MD5_BYTES; ++j)
+                 files[0].md5[j] = files[1].md5[j];
+           }
+         else
+           files[0].filename = "";
        }
 
       fullfilename = DWARF2_FILE_NAME (files[i].filename,
@@ -2188,9 +2325,17 @@ out_dir_and_file_list (segT line_seg, int sizeof_offset)
        }
       else
        {
-         line_strp = add_line_strp (line_str_seg, fullfilename);
+         if (!file0_strp)
+           line_strp = add_line_strp (line_str_seg, fullfilename);
+         else
+           line_strp = file0_strp;
          subseg_set (line_seg, 0);
          TC_DWARF2_EMIT_OFFSET (line_strp, sizeof_offset);
+         if (i == 0 && files_in_use > 1
+             && files[0].filename == files[1].filename)
+           file0_strp = line_strp;
+         else
+           file0_strp = NULL;
        }
 
       /* Directory number.  */
@@ -2342,12 +2487,17 @@ out_debug_line (segT line_seg)
   out_byte (0);                        /* DW_LNS_set_basic_block */
   out_byte (0);                        /* DW_LNS_const_add_pc */
   out_byte (1);                        /* DW_LNS_fixed_advance_pc */
-  out_byte (0);                        /* DW_LNS_set_prologue_end */
-  out_byte (0);                        /* DW_LNS_set_epilogue_begin */
-  out_byte (1);                        /* DW_LNS_set_isa */
-  /* We have emitted 12 opcode lengths, so make that this
-     matches up to the opcode base value we have been using.  */
-  gas_assert (DWARF2_LINE_OPCODE_BASE == 13);
+  if (DWARF2_LINE_VERSION >= 3)
+    {
+      out_byte (0);                    /* DW_LNS_set_prologue_end */
+      out_byte (0);                    /* DW_LNS_set_epilogue_begin */
+      out_byte (1);                    /* DW_LNS_set_isa */
+      /* We have emitted 12 opcode lengths, so make that this
+        matches up to the opcode base value we have been using.  */
+      gas_assert (DWARF2_LINE_OPCODE_BASE == 13);
+    }
+  else
+    gas_assert (DWARF2_LINE_OPCODE_BASE == 10);
 
   out_dir_and_file_list (line_seg, sizeof_offset);
 
@@ -2357,7 +2507,7 @@ out_debug_line (segT line_seg)
   for (s = all_segs; s; s = s->next)
     /* Paranoia - this check should have already have
        been handled in dwarf2_gen_line_info_1().  */
-    if (SEG_NORMAL (s->seg))
+    if (s->head->head && SEG_NORMAL (s->seg))
       process_entries (s->seg, s->head->head);
 
   if (flag_dwarf_sections)
@@ -2368,7 +2518,7 @@ out_debug_line (segT line_seg)
        This section contains the line_end symbol which is used to
        compute the size of the linked .debug_line section, as seen
        in the DWARF Line Number header.  */
-    subseg_set (subseg_get (".debug_line_end", FALSE), 0);
+    subseg_set (subseg_get (".debug_line_end", false), 0);
 
   symbol_set_value_now (line_end);
 }
@@ -2553,14 +2703,55 @@ out_debug_aranges (segT aranges_seg, segT info_seg)
 static void
 out_debug_abbrev (segT abbrev_seg,
                  segT info_seg ATTRIBUTE_UNUSED,
-                 segT line_seg ATTRIBUTE_UNUSED)
+                 segT line_seg ATTRIBUTE_UNUSED,
+                 unsigned char *func_formP)
 {
   int secoff_form;
+  bool have_efunc = false, have_lfunc = false;
+
+  /* Check the symbol table for function symbols which also have their size
+     specified.  */
+  if (symbol_rootP)
+    {
+      symbolS *symp;
+
+      for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+       {
+         /* A warning construct is a warning symbol followed by the
+            symbol warned about.  Skip this and the following symbol.  */
+         if (symbol_get_bfdsym (symp)->flags & BSF_WARNING)
+           {
+             symp = symbol_next (symp);
+             if (!symp)
+               break;
+             continue;
+           }
+
+         if (!S_IS_DEFINED (symp) || !S_IS_FUNCTION (symp))
+           continue;
+
+#if defined (OBJ_ELF) /* || defined (OBJ_MAYBE_ELF) */
+         if (S_GET_SIZE (symp) == 0)
+           {
+             if (!IS_ELF || symbol_get_obj (symp)->size == NULL)
+               continue;
+           }
+#else
+         continue;
+#endif
+
+         if (S_IS_EXTERNAL (symp))
+           have_efunc = true;
+         else
+           have_lfunc = true;
+       }
+    }
+
   subseg_set (abbrev_seg, 0);
 
-  out_uleb128 (1);
+  out_uleb128 (GAS_ABBREV_COMP_UNIT);
   out_uleb128 (DW_TAG_compile_unit);
-  out_byte (DW_CHILDREN_no);
+  out_byte (have_efunc || have_lfunc ? DW_CHILDREN_yes : DW_CHILDREN_no);
   if (DWARF2_VERSION < 4)
     {
       if (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit)
@@ -2587,6 +2778,45 @@ out_debug_abbrev (segT abbrev_seg,
   out_abbrev (DW_AT_language, DW_FORM_data2);
   out_abbrev (0, 0);
 
+  if (have_efunc || have_lfunc)
+    {
+      out_uleb128 (GAS_ABBREV_SUBPROG);
+      out_uleb128 (DW_TAG_subprogram);
+      out_byte (DW_CHILDREN_no);
+      out_abbrev (DW_AT_name, DW_FORM_strp);
+      if (have_efunc)
+       {
+         if (have_lfunc || DWARF2_VERSION < 4)
+           *func_formP = DW_FORM_flag;
+         else
+           *func_formP = DW_FORM_flag_present;
+         out_abbrev (DW_AT_external, *func_formP);
+       }
+      else
+       /* Any non-zero value other than DW_FORM_flag will do.  */
+       *func_formP = DW_FORM_block;
+
+      /* PR 29517: Provide a return type for the function.  */
+      if (DWARF2_VERSION > 2)
+       out_abbrev (DW_AT_type, DW_FORM_ref_udata);
+
+      out_abbrev (DW_AT_low_pc, DW_FORM_addr);
+      out_abbrev (DW_AT_high_pc,
+                 DWARF2_VERSION < 4 ? DW_FORM_addr : DW_FORM_udata);
+      out_abbrev (0, 0);
+
+      if (DWARF2_VERSION > 2)
+       {
+         /* PR 29517: We do not actually know the return type of these
+            functions, so provide an abbrev that uses DWARF's unspecified
+            type.  */
+         out_uleb128 (GAS_ABBREV_NO_TYPE);
+         out_uleb128 (DW_TAG_unspecified_type);
+         out_byte (DW_CHILDREN_no);
+         out_abbrev (0, 0);
+       }
+    }
+
   /* Terminate the abbreviations for this compilation unit.  */
   out_byte (0);
 }
@@ -2594,9 +2824,10 @@ out_debug_abbrev (segT abbrev_seg,
 /* Emit a description of this compilation unit for .debug_info.  */
 
 static void
-out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg,
+out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT str_seg,
                symbolS *ranges_sym, symbolS *name_sym,
-               symbolS *comp_dir_sym, symbolS *producer_sym)
+               symbolS *comp_dir_sym, symbolS *producer_sym,
+               unsigned char func_form)
 {
   expressionS exp;
   symbolS *info_end;
@@ -2630,7 +2861,7 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg,
     }
 
   /* DW_TAG_compile_unit DIE abbrev */
-  out_uleb128 (1);
+  out_uleb128 (GAS_ABBREV_COMP_UNIT);
 
   /* DW_AT_stmt_list */
   TC_DWARF2_EMIT_OFFSET (section_symbol (line_seg),
@@ -2678,6 +2909,107 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg,
      dwarf2 draft has no standard code for assembler.  */
   out_two (DW_LANG_Mips_Assembler);
 
+  if (func_form)
+    {
+      symbolS *symp;
+      symbolS *no_type_tag;
+
+      if (DWARF2_VERSION > 2)
+       no_type_tag = symbol_make (".Ldebug_no_type_tag");
+      else
+       no_type_tag = NULL;
+
+      for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+       {
+         const char *name;
+         size_t len;
+         expressionS size = { .X_op = O_constant };
+
+         /* Skip warning constructs (see above).  */
+         if (symbol_get_bfdsym (symp)->flags & BSF_WARNING)
+           {
+             symp = symbol_next (symp);
+             if (!symp)
+               break;
+             continue;
+           }
+
+         if (!S_IS_DEFINED (symp) || !S_IS_FUNCTION (symp))
+           continue;
+
+#if defined (OBJ_ELF) /* || defined (OBJ_MAYBE_ELF) */
+         size.X_add_number = S_GET_SIZE (symp);
+         if (size.X_add_number == 0 && IS_ELF
+             && symbol_get_obj (symp)->size != NULL)
+           {
+             size.X_op = O_add;
+             size.X_op_symbol = make_expr_symbol (symbol_get_obj (symp)->size);
+           }
+#endif
+         if (size.X_op == O_constant && size.X_add_number == 0)
+           continue;
+
+         subseg_set (str_seg, 0);
+         name_sym = symbol_temp_new_now_octets ();
+         name = S_GET_NAME (symp);
+         len = strlen (name) + 1;
+         memcpy (frag_more (len), name, len);
+
+         subseg_set (info_seg, 0);
+
+         /* DW_TAG_subprogram DIE abbrev */
+         out_uleb128 (GAS_ABBREV_SUBPROG);
+
+         /* DW_AT_name */
+         TC_DWARF2_EMIT_OFFSET (name_sym, sizeof_offset);
+
+         /* DW_AT_external.  */
+         if (func_form == DW_FORM_flag)
+           out_byte (S_IS_EXTERNAL (symp));
+
+         /* PR 29517: Let consumers know that we do not have
+            return type information for this function.  */
+         if (DWARF2_VERSION > 2)
+           {
+             exp.X_op = O_symbol;
+             exp.X_add_symbol = no_type_tag;
+             exp.X_add_number = 0;
+             emit_leb128_expr (&exp, 0);
+           }
+
+         /* DW_AT_low_pc */
+         exp.X_op = O_symbol;
+         exp.X_add_symbol = symp;
+         exp.X_add_number = 0;
+         emit_expr (&exp, sizeof_address);
+
+         /* DW_AT_high_pc */
+         if (DWARF2_VERSION < 4)
+           {
+             if (size.X_op == O_constant)
+               size.X_op = O_symbol;
+             size.X_add_symbol = symp;
+             emit_expr (&size, sizeof_address);
+           }
+         else if (size.X_op == O_constant)
+           out_uleb128 (size.X_add_number);
+         else
+           emit_leb128_expr (symbol_get_value_expression (size.X_op_symbol), 0);
+       }
+
+      if (DWARF2_VERSION > 2)
+       {
+         /* PR 29517: Generate a DIE for the unspecified type abbrev.
+            We do it here because it cannot be part of the top level DIE.   */
+         subseg_set (info_seg, 0);
+         symbol_set_value_now (no_type_tag);
+         out_uleb128 (GAS_ABBREV_NO_TYPE);
+       }
+
+      /* End of children.  */
+      out_leb128 (0);
+    }
+
   symbol_set_value_now (info_end);
 }
 
@@ -2688,8 +3020,6 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
               symbolS **producer_sym)
 {
   char producer[128];
-  const char *comp_dir;
-  const char *dirname;
   char *p;
   int len;
   int first_file = DWARF2_LINE_VERSION > 4 ? 0 : 1;
@@ -2705,7 +3035,7 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
     abort ();
   if (files[first_file].dir)
     {
-      dirname = remap_debug_filename (dirs[files[first_file].dir]);
+      char *dirname = remap_debug_filename (dirs[files[first_file].dir]);
       len = strlen (dirname);
 #ifdef TE_VMS
       /* Already has trailing slash.  */
@@ -2716,6 +3046,7 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
       memcpy (p, dirname, len);
       INSERT_DIR_SEPARATOR (p, len);
 #endif
+      free (dirname);
     }
   len = strlen (files[first_file].filename) + 1;
   p = frag_more (len);
@@ -2723,10 +3054,11 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
 
   /* DW_AT_comp_dir */
   *comp_dir_sym = symbol_temp_new_now_octets ();
-  comp_dir = remap_debug_filename (getpwd ());
+  char *comp_dir = remap_debug_filename (getpwd ());
   len = strlen (comp_dir) + 1;
   p = frag_more (len);
   memcpy (p, comp_dir, len);
+  free (comp_dir);
 
   /* DW_AT_producer */
   *producer_sym = symbol_temp_new_now_octets ();
@@ -2739,7 +3071,30 @@ out_debug_str (segT str_seg, symbolS **name_sym, symbolS **comp_dir_sym,
 void
 dwarf2_init (void)
 {
+  all_segs = NULL;
   last_seg_ptr = &all_segs;
+  files = NULL;
+  files_in_use = 0;
+  files_allocated = 0;
+  dirs = NULL;
+  dirs_in_use = 0;
+  dirs_allocated = 0;
+  dwarf2_loc_directive_seen = false;
+  dwarf2_any_loc_directive_seen = false;
+  dwarf2_loc_mark_labels = false;
+  current.filenum = 1;
+  current.line = 1;
+  current.column = 0;
+  current.isa = 0;
+  current.flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0;
+  current.discriminator = 0;
+  current.u.view = NULL;
+  force_reset_view = NULL;
+  view_assert_failed = NULL;
+  dw2_line = -1;
+  dw2_filename = NULL;
+  label_num = 0;
+  last_used = -1;
 
   /* Select the default CIE version to produce here.  The global
      starts with a value of -1 and will be modified to a valid value
@@ -2753,6 +3108,16 @@ dwarf2_init (void)
     flag_dwarf_cie_version = 1;
 }
 
+static void
+dwarf2_cleanup (void)
+{
+  purge_generated_debug (true);
+  free (files);
+  for (unsigned int i = 0; i < dirs_in_use; i++)
+    free (dirs[i]);
+  free (dirs);
+}
+
 /* Finish the dwarf2 debug sections.  We emit .debug.line if there
    were any .file/.loc directives, or --gdwarf2 was given, and if the
    file has a non-empty .debug_info section and an empty .debug_line
@@ -2789,7 +3154,10 @@ dwarf2_finish (void)
     /* If there is no line information and no non-empty .debug_info
        section, or if there is both a non-empty .debug_info and a non-empty
        .debug_line, then we do nothing.  */
-    return;
+    {
+      dwarf2_cleanup ();
+      return;
+    }
 
   /* Calculate the size of an address for the target machine.  */
   sizeof_address = DWARF2_ADDR_SIZE (stdoutput);
@@ -2802,6 +3170,15 @@ dwarf2_finish (void)
                             SEC_READONLY | SEC_DEBUGGING | SEC_OCTETS);
     }
 
+  for (s = all_segs; s; s = s->next)
+    {
+      struct line_subseg *lss;
+
+      for (lss = s->head; lss; lss = lss->next)
+       if (lss->head)
+         do_allocate_filenum (lss->head);
+    }
+
   /* For each subsection, chain the debug entries together.  */
   for (s = all_segs; s; s = s->next)
     {
@@ -2810,18 +3187,19 @@ dwarf2_finish (void)
 
       /* Reset the initial view of the first subsection of the
         section.  */
-      if (lss->head && lss->head->loc.view)
+      if (lss->head && lss->head->loc.u.view)
        set_or_check_view (lss->head, NULL, NULL);
 
       while ((lss = lss->next) != NULL)
        {
          /* Link the first view of subsequent subsections to the
             previous view.  */
-         if (lss->head && lss->head->loc.view)
+         if (lss->head && lss->head->loc.u.view)
            set_or_check_view (lss->head,
                               !s->head ? NULL : (struct line_entry *)ptail,
                               s->head ? s->head->head : NULL);
          *ptail = lss->head;
+         lss->head = NULL;
          ptail = lss->ptail;
        }
     }
@@ -2838,6 +3216,7 @@ dwarf2_finish (void)
       segT aranges_seg;
       segT str_seg;
       symbolS *name_sym, *comp_dir_sym, *producer_sym, *ranges_sym;
+      unsigned char func_form = 0;
 
       gas_assert (all_segs);
 
@@ -2883,11 +3262,13 @@ dwarf2_finish (void)
        }
 
       out_debug_aranges (aranges_seg, info_seg);
-      out_debug_abbrev (abbrev_seg, info_seg, line_seg);
+      out_debug_abbrev (abbrev_seg, info_seg, line_seg, &func_form);
       out_debug_str (str_seg, &name_sym, &comp_dir_sym, &producer_sym);
-      out_debug_info (info_seg, abbrev_seg, line_seg, ranges_sym,
-                     name_sym, comp_dir_sym, producer_sym);
+      out_debug_info (info_seg, abbrev_seg, line_seg, str_seg,
+                     ranges_sym, name_sym, comp_dir_sym, producer_sym,
+                     func_form);
     }
+  dwarf2_cleanup ();
 }
 
 /* Perform any deferred checks pertaining to debug information.  */