]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add support for DWARF-5 and experimental two-level line number tables.
authorCary Coutant <ccoutant@google.com>
Tue, 23 Dec 2014 23:01:40 +0000 (15:01 -0800)
committerCary Coutant <ccoutant@google.com>
Tue, 31 Mar 2015 17:51:10 +0000 (10:51 -0700)
2014-12-23  Cary Coutant  <ccoutant@google.com>

gas/
* config/obj-elf.c (elf_pseudo_table): Add .lloc and .subprog
directives.
* dwarf2dbg.h (struct dwarf2_line_info): Add logical field.
(dwarf2_directive_subprog): New function.
(dwarf2_directive_loc): Add is_lloc parameter.
* dwarf2dbg.c: Include "hash.h".
(line_base): New variable.
(line_range): New variable.
(DWARF2_EXPERIMENTAL_LINE_OPCODE_BASE): Rename.
(DWARF2_LINE_BASE): Restore to original value.
(DWARF2_LINE_RANGE): Likewise.
(DWARF5_EXPERIMENTAL_LINE_BASE): New macro.
(DWARF5_EXPERIMENTAL_LINE_RANGE): New macro.
(SPECIAL_LINE): Use line_base, line_range.
(DWARF2_LINE_EXPERIMENTAL_VERSION): New macro.
(DWARF2_EXPERIMENTAL_LINE_OPCODE_BASE): New macro.
(opcode_base): New static variable.
(DWARF2_LINE_BASE, DWARF2_LINE_RANGE): Adjust parameters.
(DWARF2_LINE_MAX_OPS_PER_INSN): New macro.
(SPECIAL_LINE, SPECIAL_ADDR): Use opcode_base.
(struct subprog_entry): New struct.
(subprogs, subprogs_in_use, subprogs_allocated): New variables.
(struct logicals_entry): New struct.
(logicals, logicals_in_use, logicals_allocated, logicals_with_labels):
New variables.
(struct string_table): New struct.
(debug_line_str_table): New variable.
(current): Add logical field.
(dwarf2_gen_line_info): Fill in symbol and segment for recent logicals.
(dwarf2_where): Add logical field.
(make_subprog_entry): New function.
(make_logical): New function.
(dwarf2_directive_subprog): New function.
(dwarf2_directive_loc): Add is_lloc parameter; add support for .lloc
directive.
(out_set_addr_from_logical): New function.
(size_inc_line_addr): Fix signed/unsigned warnings. Use opcode_base,
line_base, line_range.
(emit_inc_line_addr): Likewise.
(emit_logicals): New function.
(process_entries): Add support for two-level line tables.
(add_to_string_table): New function.
(out_string_table): New function.
(out_dwarf5_file_list): New function.
(out_subprog_list): New function.
(out_debug_line): Add support for DWARF-5 line tables and for
experimental two-level line number tables.
(dwarf2_finish): Likewise.

gas/config/obj-elf.c
gas/dwarf2dbg.c
gas/dwarf2dbg.h

index 4d7a8a730113848f885878698a4ed9c9eb0ba35d..d3b3bfbd51df29fb5fa2f26a0fff6490c09cd27b 100644 (file)
@@ -125,8 +125,10 @@ static const pseudo_typeS elf_pseudo_table[] =
   {"8byte", cons, 8},
   /* These are used for dwarf2.  */
   { "file", (void (*) (int)) dwarf2_directive_file, 0 },
-  { "loc",  dwarf2_directive_loc,  0 },
+  { "loc",  dwarf2_directive_loc, 0 },
   { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
+  { "lloc",  dwarf2_directive_loc, 1 },
+  { "subprog",  dwarf2_directive_subprog, 0 },
 
   /* We need to trap the section changing calls to handle .previous.  */
   {"data", obj_elf_data, 0},
index e67c99216686c47a6e3256c807dad56bb4383996..99c18402359536d391ed56b8df34a6e3a5dab88d 100644 (file)
@@ -45,6 +45,8 @@
 #include "dwarf2dbg.h"
 #include <filenames.h>
 
+#include "hash.h"
+
 #ifdef HAVE_DOS_BASED_FILE_SYSTEM
 /* We need to decide which character to use as a directory separator.
    Just because HAVE_DOS_BASED_FILE_SYSTEM is defined, it does not
 #ifndef DWARF2_LINE_VERSION
 #define DWARF2_LINE_VERSION 2
 #endif
+/* If we see .lloc directives, generate an experimental version 6.  */
+#ifndef DWARF2_LINE_EXPERIMENTAL_VERSION
+#define DWARF2_LINE_EXPERIMENTAL_VERSION 0xf006
+#endif
 
 #include "subsegs.h"
 
    "standard_opcode_lengths" table that is emitted below in
    out_debug_line().  */
 #define DWARF2_LINE_OPCODE_BASE                13
+#define DWARF5_EXPERIMENTAL_LINE_OPCODE_BASE  16
+
+static int opcode_base;
+static int line_base;
+static unsigned int line_range;
 
 #ifndef DWARF2_LINE_BASE
   /* Minimum line offset in a special line info. opcode.  This value
 # define DWARF2_LINE_RANGE             14
 #endif
 
+/* For two-level line tables, these values work a bit better.  */
+#define DWARF5_EXPERIMENTAL_LINE_BASE          -3
+#define DWARF5_EXPERIMENTAL_LINE_RANGE         10
+
 #ifndef DWARF2_LINE_MIN_INSN_LENGTH
   /* Define the architecture-dependent minimum instruction length (in
      bytes).  This value should be rather too small than too big.  */
 # define DWARF2_LINE_MIN_INSN_LENGTH   1
 #endif
 
+#ifndef DWARF2_LINE_MAX_OPS_PER_INSN
+# define DWARF2_LINE_MAX_OPS_PER_INSN  1
+#endif
+
 /* Flag that indicates the initial value of the is_stmt_start flag.  */
 #define        DWARF2_LINE_DEFAULT_IS_STMT     1
 
 /* Given a special op, return the line skip amount.  */
 #define SPECIAL_LINE(op) \
-       (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE)
+       (((op) - opcode_base) % line_range + line_base)
 
 /* Given a special op, return the address skip amount (in units of
    DWARF2_LINE_MIN_INSN_LENGTH.  */
-#define SPECIAL_ADDR(op) (((op) - DWARF2_LINE_OPCODE_BASE)/DWARF2_LINE_RANGE)
+#define SPECIAL_ADDR(op) (((op) - opcode_base) / line_range)
 
 /* The maximum address skip amount that can be encoded with a special op.  */
 #define MAX_SPECIAL_ADDR_DELTA         SPECIAL_ADDR(255)
@@ -203,6 +222,44 @@ static char **dirs;
 static unsigned int dirs_in_use;
 static unsigned int dirs_allocated;
 
+/* Experimental DWARF-5 Extension: Table of subprograms.  */
+struct subprog_entry {
+  const char *subpname;
+  unsigned int filenum;
+  unsigned int line;
+};
+
+static struct subprog_entry *subprogs;
+static unsigned int subprogs_in_use;
+static unsigned int subprogs_allocated;
+
+/* Experimental DWARF-5 Extension: Logicals table.  */
+struct logicals_entry {
+  segT seg;
+  symbolS *label;
+  /* A logical row doesn't use every field in this struct, but using it
+     here makes the code for writing the line number program simpler.  */
+  struct dwarf2_line_info loc;
+  unsigned int context;
+  unsigned int subprog;
+};
+
+static struct logicals_entry *logicals;
+static unsigned int logicals_in_use;
+static unsigned int logicals_allocated = 0;
+static unsigned int logicals_with_labels = 0;
+
+/* DWARF-5: .debug_line_str string table.  */
+struct string_table {
+  struct hash_control *hashtab;
+  const char **strings;
+  unsigned int strings_in_use;
+  unsigned int strings_allocated;
+  offsetT next_offset;
+};
+
+static struct string_table debug_line_str_table;
+
 /* TRUE when we've seen a .loc directive recently.  Used to avoid
    doing work when there's nothing to do.  */
 bfd_boolean dwarf2_loc_directive_seen;
@@ -213,9 +270,9 @@ bfd_boolean 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
+  1, 1, 0, 0,  /* filenum, line, column, isa */
+  DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0,  /* flags */
+  0, 0         /* discriminator, logical */
 };
 
 /* The size of an address on the target.  */
@@ -338,6 +395,15 @@ dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc)
   else
     sym = symbol_temp_new (now_seg, ofs, frag_now);
   dwarf2_gen_line_info_1 (sym, loc);
+
+  /* Record the current symbol with all logical rows created since
+     the last emitted instruction.  */
+  while (logicals_with_labels < logicals_in_use)
+    {
+      logicals[logicals_with_labels].label = sym;
+      logicals[logicals_with_labels].seg = now_seg;
+      logicals_with_labels++;
+    }
 }
 
 /* Returns the current source information.  If .file directives have
@@ -357,6 +423,7 @@ dwarf2_where (struct dwarf2_line_info *line)
       line->flags = DWARF2_FLAG_IS_STMT;
       line->isa = current.isa;
       line->discriminator = current.discriminator;
+      line->logical = 0;
     }
   else
     *line = current;
@@ -560,6 +627,68 @@ get_filenum (const char *filename, unsigned int num)
   return i;
 }
 
+/* Make a new entry in the subprograms table.  */
+
+static void
+make_subprog_entry (unsigned int num, char *subpname, int filenum, int line)
+{
+  if (subprogs_allocated == 0)
+    {
+      subprogs_allocated = 4;
+      subprogs = (struct subprog_entry *)
+         xcalloc (subprogs_allocated, sizeof (struct subprog_entry));
+    }
+  if (num > subprogs_allocated)
+    {
+      unsigned int old = subprogs_allocated;
+
+      subprogs_allocated *= 2;
+      if (num > subprogs_allocated)
+        subprogs_allocated = num;
+      subprogs = (struct subprog_entry *)
+         xrealloc (subprogs,
+                   subprogs_allocated * sizeof (struct subprog_entry));
+      memset (subprogs + old, 0,
+             (subprogs_allocated - old) * sizeof (struct subprog_entry));
+    }
+  if (subprogs_in_use < num)
+    subprogs_in_use = num;
+  subprogs[num - 1].subpname = xstrdup (subpname);
+  subprogs[num - 1].filenum = filenum;
+  subprogs[num - 1].line = line;
+}
+
+/* Make a new entry in the logicals table.  */
+
+static void
+make_logical (unsigned int logical, int context, int subprog)
+{
+  if (logicals_allocated == 0)
+    {
+      logicals_allocated = 4;
+      logicals = (struct logicals_entry *)
+         xcalloc (logicals_allocated, sizeof (struct logicals_entry));
+    }
+  if (logical > logicals_allocated)
+    {
+      unsigned int old = logicals_allocated;
+
+      logicals_allocated *= 2;
+      if (logical > logicals_allocated)
+        logicals_allocated = logical;
+      logicals = (struct logicals_entry *)
+         xrealloc (logicals,
+                   logicals_allocated * sizeof (struct logicals_entry));
+      memset (logicals + old, 0,
+             (logicals_allocated - old) * sizeof (struct logicals_entry));
+    }
+  logicals[logical - 1].loc = current;
+  logicals[logical - 1].context = context;
+  logicals[logical - 1].subprog = subprog;
+  if (logical > logicals_in_use)
+    logicals_in_use = logical;
+}
+
 /* Handle two forms of .file directive:
    - Pass .file "source.c" to s_app_file
    - Handle .file 1 "source.c" by adding an entry to the DWARF-2 file table
@@ -608,34 +737,130 @@ dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED)
   return filename;
 }
 
+/* Experimental DWARF-5 extension:
+   Implements the .subprog SUBPNO ["SUBPROG" [FILENO LINENO]] directive.
+   FILENO is the file number, LINENO the line number and the
+   (optional) COLUMN the column of the source code that the following
+   instruction corresponds to.  FILENO can be 0 to indicate that the
+   filename specified by the textually most recent .file directive
+   should be used.  */
 void
-dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
+dwarf2_directive_subprog (int dummy ATTRIBUTE_UNUSED)
 {
-  offsetT filenum, line;
+  offsetT num, filenum, line;
+  char *subpname;
+  int subpname_len;
 
-  /* If we see two .loc directives in a row, force the first one to be
-     output now.  */
-  if (dwarf2_loc_directive_seen)
-    dwarf2_emit_insn (0);
+  num = get_absolute_expression ();
+  subpname = demand_copy_C_string (&subpname_len);
+  if (subpname == NULL)
+    return;
 
+  SKIP_WHITESPACE ();
   filenum = get_absolute_expression ();
   SKIP_WHITESPACE ();
   line = get_absolute_expression ();
+  demand_empty_rest_of_line ();
 
-  if (filenum < 1)
+  if (num < 1)
     {
-      as_bad (_("file number less than one"));
+      as_bad (_("subprogram number less than one"));
       return;
     }
-  if (filenum >= (int) files_in_use || files[filenum].filename == 0)
+
+  /* A .subprog directive implies compiler generated debug information is
+     being supplied.  Turn off gas generated debug info.  */
+  debug_type = DEBUG_NONE;
+
+  if (num < (int) subprogs_in_use && subprogs[num].subpname != NULL)
     {
-      as_bad (_("unassigned file number %ld"), (long) filenum);
+      as_bad (_("subprogram number %ld already allocated"), (long) num);
       return;
     }
 
+  make_subprog_entry (num, subpname, filenum, line);
+}
+
+void
+dwarf2_directive_loc (int is_lloc)
+{
+  offsetT filenum, line;
+  offsetT logical = 0;
+  offsetT context = 0;
+  offsetT subprog = 0;
+  bfd_boolean is_new_logical = FALSE;
+  bfd_boolean is_actual = FALSE;
+  static bfd_boolean saw_loc = FALSE;
+  static bfd_boolean saw_lloc = FALSE;
+  static bfd_boolean saw_both = FALSE;
+
+  if ((is_lloc && saw_loc) || (!is_lloc && saw_lloc))
+    {
+      if (!saw_both)
+        as_bad (_(".loc and .lloc cannot both be used"));
+      saw_both = TRUE;
+      return;
+    }
+
+  if (is_lloc)
+    {
+      saw_lloc = TRUE;
+      logical = get_absolute_expression ();
+      SKIP_WHITESPACE ();
+
+      if (ISDIGIT (*input_line_pointer))
+       is_new_logical = TRUE;
+      else
+       is_actual = TRUE;
+
+      if (logical < 1)
+       {
+         as_bad (_("logical row less than one"));
+         return;
+       }
+      if (is_actual &&
+          ((unsigned int) logical > logicals_in_use
+           || logicals[logical - 1].loc.line == 0))
+       {
+         as_bad (_("unassigned logical row %ld"), (long) logical);
+         return;
+       }
+    }
+  else
+    saw_loc = TRUE;
+
+  /* If we see two .loc directives in a row, force the first one to be
+     output now.  */
+  if (dwarf2_loc_directive_seen)
+    dwarf2_emit_insn (0);
+
+  if (is_lloc && !is_new_logical)
+    {
+      filenum = logicals[logical - 1].loc.filenum;
+      line = logicals[logical - 1].loc.line;
+    }
+  else
+    {
+      filenum = get_absolute_expression ();
+      SKIP_WHITESPACE ();
+      line = get_absolute_expression ();
+
+      if (filenum < 1)
+       {
+         as_bad (_("file number less than one"));
+         return;
+       }
+      if (filenum >= (int) files_in_use || files[filenum].filename == 0)
+       {
+         as_bad (_("unassigned file number %ld"), (long) filenum);
+         return;
+       }
+    }
+
   current.filenum = filenum;
   current.line = line;
   current.discriminator = 0;
+  current.logical = logical;
 
 #ifndef NO_LISTING
   if (listing)
@@ -678,17 +903,17 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
          current.flags |= DWARF2_FLAG_BASIC_BLOCK;
          *input_line_pointer = c;
        }
-      else if (strcmp (p, "prologue_end") == 0)
+      else if (!is_actual && strcmp (p, "prologue_end") == 0)
        {
          current.flags |= DWARF2_FLAG_PROLOGUE_END;
          *input_line_pointer = c;
        }
-      else if (strcmp (p, "epilogue_begin") == 0)
+      else if (!is_actual && strcmp (p, "epilogue_begin") == 0)
        {
          current.flags |= DWARF2_FLAG_EPILOGUE_BEGIN;
          *input_line_pointer = c;
        }
-      else if (strcmp (p, "is_stmt") == 0)
+      else if (!is_actual && strcmp (p, "is_stmt") == 0)
        {
          *input_line_pointer = c;
          value = get_absolute_expression ();
@@ -714,7 +939,7 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
              return;
            }
        }
-      else if (strcmp (p, "discriminator") == 0)
+      else if (!is_actual && strcmp (p, "discriminator") == 0)
        {
          *input_line_pointer = c;
          value = get_absolute_expression ();
@@ -726,6 +951,30 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
              return;
            }
        }
+      else if (!is_actual && strcmp (p, "context") == 0)
+       {
+         *input_line_pointer = c;
+         value = get_absolute_expression ();
+         if (value >= 0)
+           context = value;
+         else
+           {
+             as_bad (_("context less than zero"));
+             return;
+           }
+       }
+      else if (!is_actual && strcmp (p, "subprog") == 0)
+       {
+         *input_line_pointer = c;
+         value = get_absolute_expression ();
+         if (value >= 0)
+           subprog = value;
+         else
+           {
+             as_bad (_("subprog number less than zero"));
+             return;
+           }
+       }
       else
        {
          as_bad (_("unknown .loc sub-directive `%s'"), p);
@@ -739,6 +988,9 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
   dwarf2_loc_directive_seen = TRUE;
   debug_type = DEBUG_NONE;
+
+  if (is_new_logical)
+    make_logical (logical, context, subprog);
 }
 
 void
@@ -869,6 +1121,15 @@ out_set_addr (symbolS *sym)
   emit_expr (&exp, sizeof_address);
 }
 
+/* Set the address from a logicals table entry.  */
+
+static void
+out_set_addr_from_logical (int logical_delta)
+{
+  out_opcode (DW_LNS_set_address_from_logical);
+  out_leb128 (logical_delta);
+}
+
 static void scale_addr_delta (addressT *);
 
 static void
@@ -907,7 +1168,7 @@ size_inc_line_addr (int line_delta, addressT addr_delta)
      to emit the matrix entry.  */
   if (line_delta == INT_MAX)
     {
-      if (addr_delta == MAX_SPECIAL_ADDR_DELTA)
+      if (addr_delta == (unsigned int) MAX_SPECIAL_ADDR_DELTA)
        len = 1;
       else
        len = 1 + sizeof_leb128 (addr_delta, 0);
@@ -915,30 +1176,30 @@ size_inc_line_addr (int line_delta, addressT addr_delta)
     }
 
   /* Bias the line delta by the base.  */
-  tmp = line_delta - DWARF2_LINE_BASE;
+  tmp = line_delta - line_base;
 
   /* If the line increment is out of range of a special opcode, we
      must encode it with DW_LNS_advance_line.  */
-  if (tmp >= DWARF2_LINE_RANGE)
+  if (tmp >= line_range)
     {
       len = 1 + sizeof_leb128 (line_delta, 1);
       line_delta = 0;
-      tmp = 0 - DWARF2_LINE_BASE;
+      tmp = 0 - line_base;
     }
 
   /* Bias the opcode by the special opcode base.  */
-  tmp += DWARF2_LINE_OPCODE_BASE;
+  tmp += opcode_base;
 
   /* Avoid overflow when addr_delta is large.  */
-  if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA)
+  if (addr_delta < (unsigned int) (256 + MAX_SPECIAL_ADDR_DELTA))
     {
       /* Try using a special opcode.  */
-      opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
+      opcode = tmp + addr_delta * line_range;
       if (opcode <= 255)
        return len + 1;
 
       /* Try using DW_LNS_const_add_pc followed by special op.  */
-      opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE;
+      opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * line_range;
       if (opcode <= 255)
        return len + 2;
     }
@@ -971,7 +1232,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
      to emit the matrix entry.  */
   if (line_delta == INT_MAX)
     {
-      if (addr_delta == MAX_SPECIAL_ADDR_DELTA)
+      if (addr_delta == (unsigned int) MAX_SPECIAL_ADDR_DELTA)
        *p++ = DW_LNS_const_add_pc;
       else
        {
@@ -986,17 +1247,17 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
     }
 
   /* Bias the line delta by the base.  */
-  tmp = line_delta - DWARF2_LINE_BASE;
+  tmp = line_delta - line_base;
 
   /* If the line increment is out of range of a special opcode, we
      must encode it with DW_LNS_advance_line.  */
-  if (tmp >= DWARF2_LINE_RANGE)
+  if (tmp >= line_range)
     {
       *p++ = DW_LNS_advance_line;
       p += output_leb128 (p, line_delta, 1);
 
       line_delta = 0;
-      tmp = 0 - DWARF2_LINE_BASE;
+      tmp = 0 - line_base;
       need_copy = 1;
     }
 
@@ -1009,13 +1270,13 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
     }
 
   /* Bias the opcode by the special opcode base.  */
-  tmp += DWARF2_LINE_OPCODE_BASE;
+  tmp += opcode_base;
 
   /* Avoid overflow when addr_delta is large.  */
-  if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA)
+  if (addr_delta < (unsigned int) (256 + MAX_SPECIAL_ADDR_DELTA))
     {
       /* Try using a special opcode.  */
-      opcode = tmp + addr_delta * DWARF2_LINE_RANGE;
+      opcode = tmp + addr_delta * line_range;
       if (opcode <= 255)
        {
          *p++ = opcode;
@@ -1023,7 +1284,7 @@ emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len)
        }
 
       /* Try using DW_LNS_const_add_pc followed by special op.  */
-      opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE;
+      opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * line_range;
       if (opcode <= 255)
        {
          *p++ = DW_LNS_const_add_pc;
@@ -1264,6 +1525,140 @@ dwarf2dbg_convert_frag (fragS *frag)
   frag->fr_offset = 0;
 }
 
+/* Generate .debug_line content for the logicals table rows.  */
+
+static void
+emit_logicals (void)
+{
+  unsigned logical;
+  unsigned filenum = 1;
+  unsigned line = 1;
+  unsigned column = 0;
+  unsigned discriminator;
+  unsigned flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0;
+  unsigned context = 0;
+  unsigned subprog = 0;
+  segT last_seg = NULL;
+  fragS *last_frag = NULL, *frag;
+  addressT last_frag_ofs = 0, frag_ofs;
+  symbolS *last_lab = NULL, *lab;
+
+  for (logical = 1; logical <= logicals_in_use; ++logical)
+    {
+      int line_delta;
+      int context_delta;
+      struct logicals_entry *e = &logicals[logical - 1];
+
+      discriminator = 0;
+
+      if (context != e->context || subprog != e->subprog)
+        {
+         unsigned int caller = context;
+         unsigned int npop = 0;
+
+         // See if a sequence of DW_LNS_pop_context ops will get
+         // to the state we want.
+         while (caller > 0 && caller <= logicals_in_use)
+           {
+             ++npop;
+             if (logicals[caller - 1].subprog == e->subprog)
+               break;
+             caller = logicals[caller - 1].context;
+           }
+         if (caller > 0 && caller <= logicals_in_use && npop < 10)
+           {
+             while (npop-- > 0)
+               out_opcode (DW_LNS_pop_context);
+             filenum = logicals[caller - 1].loc.filenum;
+             line = logicals[caller - 1].loc.line;
+             column = logicals[caller - 1].loc.column;
+             discriminator = logicals[caller - 1].loc.discriminator;
+             flags = logicals[caller - 1].loc.flags;
+             context = logicals[caller - 1].context;
+             subprog = logicals[caller - 1].subprog;
+           }
+         if (context != e->context && e->context == 0)
+           {
+             context = 0;
+             subprog = e->subprog;
+             out_opcode (DW_LNS_set_subprogram);
+             out_uleb128 (subprog);
+           }
+         else if (context != e->context || subprog != e->subprog)
+           {
+             context_delta = e->context - (logical - 1);
+             context = e->context;
+             subprog = e->subprog;
+             out_opcode (DW_LNS_inlined_call);
+             out_leb128 (context_delta);
+             out_uleb128 (subprog);
+           }
+       }
+
+      if (filenum != e->loc.filenum)
+       {
+         filenum = e->loc.filenum;
+         out_opcode (DW_LNS_set_file);
+         out_uleb128 (filenum);
+       }
+
+      if (column != e->loc.column)
+       {
+         column = e->loc.column;
+         out_opcode (DW_LNS_set_column);
+         out_uleb128 (column);
+       }
+
+      if (e->loc.discriminator != discriminator)
+       {
+         out_opcode (DW_LNS_extended_op);
+         out_leb128 (1 + sizeof_leb128 (e->loc.discriminator, 0));
+         out_opcode (DW_LNE_set_discriminator);
+         out_uleb128 (e->loc.discriminator);
+       }
+
+      if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
+       {
+         flags = e->loc.flags;
+         out_opcode (DW_LNS_negate_stmt);
+       }
+
+      if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
+       out_opcode (DW_LNS_set_prologue_end);
+
+      if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
+       out_opcode (DW_LNS_set_epilogue_begin);
+
+      line_delta = e->loc.line - line;
+      if (e->label == NULL)
+       {
+         out_inc_line_addr (line_delta, 0);
+       }
+      else
+       {
+         lab = e->label;
+         frag = symbol_get_frag (lab);
+         frag_ofs = S_GET_VALUE (lab);
+
+         if (last_frag == NULL || e->seg != last_seg)
+           {
+             out_set_addr (lab);
+             out_inc_line_addr (line_delta, 0);
+           }
+         else if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC)
+           out_inc_line_addr (line_delta, frag_ofs - last_frag_ofs);
+         else
+           relax_inc_line_addr (line_delta, lab, last_lab);
+
+         line = e->loc.line;
+         last_seg = e->seg;
+         last_lab = lab;
+         last_frag = frag;
+         last_frag_ofs = frag_ofs;
+       }
+    }
+}
+
 /* Generate .debug_line content for the chain of line number entries
    beginning at E, for segment SEG.  */
 
@@ -1318,27 +1713,30 @@ process_entries (segT seg, struct line_entry *e)
     {
       int line_delta;
 
-      if (filenum != e->loc.filenum)
-       {
-         filenum = e->loc.filenum;
-         out_opcode (DW_LNS_set_file);
-         out_uleb128 (filenum);
-       }
+      if (logicals_in_use == 0)
+        {
+         if (filenum != e->loc.filenum)
+           {
+             filenum = e->loc.filenum;
+             out_opcode (DW_LNS_set_file);
+             out_uleb128 (filenum);
+           }
 
-      if (column != e->loc.column)
-       {
-         column = e->loc.column;
-         out_opcode (DW_LNS_set_column);
-         out_uleb128 (column);
-       }
+         if (column != e->loc.column)
+           {
+             column = e->loc.column;
+             out_opcode (DW_LNS_set_column);
+             out_uleb128 (column);
+           }
 
-      if (e->loc.discriminator != 0)
-       {
-         out_opcode (DW_LNS_extended_op);
-         out_leb128 (1 + sizeof_leb128 (e->loc.discriminator, 0));
-         out_opcode (DW_LNE_set_discriminator);
-         out_uleb128 (e->loc.discriminator);
-       }
+         if (e->loc.discriminator != 0)
+           {
+             out_opcode (DW_LNS_extended_op);
+             out_leb128 (1 + sizeof_leb128 (e->loc.discriminator, 0));
+             out_opcode (DW_LNE_set_discriminator);
+             out_uleb128 (e->loc.discriminator);
+           }
+        }
 
       if (isa != e->loc.isa)
        {
@@ -1347,41 +1745,58 @@ process_entries (segT seg, struct line_entry *e)
          out_uleb128 (isa);
        }
 
-      if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
-       {
-         flags = e->loc.flags;
-         out_opcode (DW_LNS_negate_stmt);
-       }
-
       if (e->loc.flags & DWARF2_FLAG_BASIC_BLOCK)
        out_opcode (DW_LNS_set_basic_block);
 
-      if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
-       out_opcode (DW_LNS_set_prologue_end);
+      if (logicals_in_use == 0)
+        {
+         if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
+           {
+             flags = e->loc.flags;
+             out_opcode (DW_LNS_negate_stmt);
+           }
 
-      if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
-       out_opcode (DW_LNS_set_epilogue_begin);
+         if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
+           out_opcode (DW_LNS_set_prologue_end);
+
+         if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
+           out_opcode (DW_LNS_set_epilogue_begin);
+        }
 
       /* Don't try to optimize away redundant entries; gdb wants two
         entries for a function where the code starts on the same line as
         the {, and there's no way to identify that case here.  Trust gcc
         to optimize appropriately.  */
-      line_delta = e->loc.line - line;
+      if (logicals_in_use == 0)
+       line_delta = e->loc.line - line;
+      else
+       line_delta = e->loc.logical - line;
       lab = e->label;
       frag = symbol_get_frag (lab);
       frag_ofs = S_GET_VALUE (lab);
 
       if (last_frag == NULL)
        {
-         out_set_addr (lab);
-         out_inc_line_addr (line_delta, 0);
+         if (logicals_in_use > 0 && logicals[e->loc.logical - 1].label == lab)
+           {
+             out_set_addr_from_logical (line_delta);
+             out_opcode (DW_LNS_copy);
+           }
+         else
+           {
+             out_set_addr (lab);
+             out_inc_line_addr (line_delta, 0);
+           }
        }
       else if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC)
        out_inc_line_addr (line_delta, frag_ofs - last_frag_ofs);
       else
        relax_inc_line_addr (line_delta, lab, last_lab);
 
-      line = e->loc.line;
+      if (logicals_in_use == 0)
+       line = e->loc.line;
+      else
+       line = e->loc.logical;
       last_lab = lab;
       last_frag = frag;
       last_frag_ofs = frag_ofs;
@@ -1456,6 +1871,160 @@ out_file_list (void)
   out_byte (0);
 }
 
+/* Add a string to the string table.  */
+
+static offsetT
+add_to_string_table (struct string_table *strtab, const char *str)
+{
+  const char *key;
+  offsetT val;
+
+  if (strtab->strings_allocated == 0)
+    {
+      strtab->strings_allocated = 4;
+      strtab->strings = (const char **)
+         xcalloc (strtab->strings_allocated, sizeof(char *));
+      strtab->hashtab = hash_new ();
+    }
+
+  val = (offsetT) hash_find (strtab->hashtab, str);
+  if (val != 0)
+    return val;
+
+  if (strtab->strings_in_use >= strtab->strings_allocated)
+    {
+      unsigned int old = strtab->strings_allocated;
+
+      strtab->strings_allocated *= 2;
+      strtab->strings = (const char **)
+         xrealloc (strtab->strings,
+                   strtab->strings_allocated * sizeof (char *));
+      memset (strtab->strings + old, 0,
+             (strtab->strings_allocated - old) * sizeof (char *));
+    }
+
+  key = xstrdup (str);
+  val = strtab->next_offset;
+  hash_insert (strtab->hashtab, key, (void *) val);
+  strtab->strings[strtab->strings_in_use++] = key;
+  strtab->next_offset += strlen(key) + 1;
+  return val;
+}
+
+/* Output the string table STRTAB to the section STR_SEG.
+   In a debug string table, the first byte is always '\0',
+   and valid indexes begin at 1.  */
+
+static void
+out_string_table (segT str_seg, struct string_table *strtab)
+{
+  unsigned int i;
+  size_t size;
+  char *cp;
+
+  subseg_set (str_seg, 0);
+  out_byte (0);
+  for (i = 0; i < strtab->strings_in_use; i++)
+    {
+      size = strlen (strtab->strings[i]) + 1;
+      cp = frag_more (size);
+      memcpy (cp, strtab->strings[i], size);
+    }
+}
+
+static void
+out_dwarf5_file_list (segT str_seg, int sizeof_offset)
+{
+  const char *dir;
+  offsetT strp;
+  unsigned int i;
+  expressionS exp;
+  unsigned int dir_count = dirs_in_use > 0 ? dirs_in_use - 1 : 0;
+  unsigned int file_count = files_in_use > 0 ? files_in_use - 1 : 0;
+
+  exp.X_op = O_symbol;
+  exp.X_add_symbol = section_symbol (str_seg);
+
+  out_byte (1);                    /* directory_entry_format_count */
+  out_uleb128 (DW_LNCT_path);      /* directory_entry_format[0].content_type */
+  out_uleb128 (DW_FORM_line_strp); /* directory_entry_format[0].form */
+  out_uleb128 (dir_count);         /* directories_count */
+
+  /* Emit directories list.  */
+  for (i = 1; i < dirs_in_use; ++i)
+    {
+      dir = remap_debug_filename (dirs[i]);
+      strp = add_to_string_table (&debug_line_str_table, dir);
+      exp.X_add_number = strp;
+      emit_expr (&exp, sizeof_offset);
+    }
+
+  out_byte (2);                          /* file_name_entry_format_count */
+  out_uleb128 (DW_LNCT_path);            /* file_name_entry_format[0].type */
+  out_uleb128 (DW_FORM_line_strp);       /* file_name_entry_format[0].form */
+  out_uleb128 (DW_LNCT_directory_index); /* file_name_entry_format[0].type */
+  out_uleb128 (DW_FORM_udata);           /* file_name_entry_format[0].form */
+  out_uleb128 (file_count);              /* file_names_count */
+
+  /* Emit file_names list.  */
+  for (i = 1; i < files_in_use; ++i)
+    {
+      const char *fullfilename;
+
+      if (files[i].filename == NULL)
+       {
+         as_bad (_("unassigned file number %ld"), (long) i);
+         /* Prevent a crash later, particularly for file 1.  */
+         files[i].filename = "";
+       }
+
+      fullfilename = DWARF2_FILE_NAME (files[i].filename,
+                                      files[i].dir ? dirs [files [i].dir] : "");
+      strp = add_to_string_table (&debug_line_str_table, fullfilename);
+      exp.X_add_number = strp;
+      emit_expr (&exp, sizeof_offset);
+      out_uleb128 (files[i].dir);      /* directory number */
+    }
+}
+
+static void
+out_subprog_list (segT str_seg, int sizeof_offset)
+{
+  const char *name;
+  offsetT strp;
+  unsigned int i;
+  expressionS exp;
+
+  exp.X_op = O_symbol;
+  exp.X_add_symbol = section_symbol (str_seg);
+
+  out_byte (3);                          /* subprogram_entry_format_count */
+  out_uleb128 (DW_LNCT_subprogram_name); /* subprogram_entry_format[0].type */
+  out_uleb128 (DW_FORM_line_strp);       /* subprogram_entry_format[0].form */
+  out_uleb128 (DW_LNCT_decl_file);       /* subprogram_entry_format[1].type */
+  out_uleb128 (DW_FORM_udata);           /* subprogram_entry_format[1].form */
+  out_uleb128 (DW_LNCT_decl_line);       /* subprogram_entry_format[2].type */
+  out_uleb128 (DW_FORM_udata);           /* subprogram_entry_format[2].form */
+  out_uleb128 (subprogs_in_use);         /* subprograms_count */
+
+  /* Emit subprograms list.  */
+  for (i = 0; i < subprogs_in_use; ++i)
+    {
+      name = subprogs[i].subpname;
+      if (name == NULL)
+       {
+         as_bad (_("unassigned subprogram number %ld"), (long) i);
+         strp = 0;
+       }
+      else
+       strp = add_to_string_table (&debug_line_str_table, name);
+      exp.X_add_number = strp;
+      emit_expr (&exp, sizeof_offset);
+      out_uleb128 (subprogs[i].filenum);
+      out_uleb128 (subprogs[i].line);
+    }
+}
+
 /* Switch to SEC and output a header length field.  Return the size of
    offsets used in SEC.  The caller must set EXPR->X_add_symbol value
    to the end of the section.  */
@@ -1514,19 +2083,35 @@ out_header (asection *sec, expressionS *exp)
 /* Emit the collected .debug_line data.  */
 
 static void
-out_debug_line (segT line_seg)
+out_debug_line (segT line_seg, segT str_seg)
 {
   expressionS exp;
-  symbolS *prologue_start, *prologue_end;
+  symbolS *prologue_start, *prologue_end, *logicals_start, *actuals_start;
   symbolS *line_end;
   struct line_seg *s;
   int sizeof_offset;
+  unsigned int version;
+
+  if (logicals_in_use == 0)
+    {
+      version = DWARF2_LINE_VERSION;
+      opcode_base = DWARF2_LINE_OPCODE_BASE;
+      line_base = DWARF2_LINE_BASE;
+      line_range = DWARF2_LINE_RANGE;
+    }
+  else
+    {
+      version = DWARF2_LINE_EXPERIMENTAL_VERSION;
+      opcode_base = DWARF5_EXPERIMENTAL_LINE_OPCODE_BASE;
+      line_base = DWARF5_EXPERIMENTAL_LINE_BASE;
+      line_range = DWARF5_EXPERIMENTAL_LINE_RANGE;
+    }
 
   sizeof_offset = out_header (line_seg, &exp);
   line_end = exp.X_add_symbol;
 
   /* Version.  */
-  out_two (DWARF2_LINE_VERSION);
+  out_two (version);
 
   /* Length of the prologue following this length.  */
   prologue_start = symbol_temp_make ();
@@ -1540,10 +2125,12 @@ out_debug_line (segT line_seg)
 
   /* Parameters of the state machine.  */
   out_byte (DWARF2_LINE_MIN_INSN_LENGTH);
+  if (version >= 4)
+    out_byte (DWARF2_LINE_MAX_OPS_PER_INSN);
   out_byte (DWARF2_LINE_DEFAULT_IS_STMT);
-  out_byte (DWARF2_LINE_BASE);
-  out_byte (DWARF2_LINE_RANGE);
-  out_byte (DWARF2_LINE_OPCODE_BASE);
+  out_byte (line_base);
+  out_byte (line_range);
+  out_byte (opcode_base);
 
   /* Standard opcode lengths.  */
   out_byte (0);                        /* DW_LNS_copy */
@@ -1558,10 +2145,63 @@ out_debug_line (segT line_seg)
   out_byte (0);                        /* DW_LNS_set_prologue_end */
   out_byte (0);                        /* DW_LNS_set_epilogue_begin */
   out_byte (1);                        /* DW_LNS_set_isa */
+  if (opcode_base == DWARF5_EXPERIMENTAL_LINE_OPCODE_BASE)
+    {
+      out_byte (1);            /* DW_LNS_set_subprogram/DW_LNS_set_address_from_logical */
+      out_byte (2);            /* DW_LNS_inlined_call */
+      out_byte (0);            /* DW_LNS_pop_context */
+    }
 
-  out_file_list ();
-
-  symbol_set_value_now (prologue_end);
+  if (version == DWARF2_LINE_EXPERIMENTAL_VERSION)
+    {
+      /* Fake empty version 4 directory and filename lists, to fool
+         old consumers who don't check the version number.  */
+      out_byte (0);
+      out_byte (0);
+
+      symbol_set_value_now (prologue_end);
+
+      /* Now wrap the remainder of the section inside a fake
+         extended opcode, so old consumers will see just the single
+         extended opcode, and will not try to read anything else.
+         For simplicity, we simply output a very large number for
+         the size of the extended op. */
+      out_opcode (DW_LNS_extended_op);
+      out_byte (255);  /* 3-byte LEB128 for 0x1fffff.  */
+      out_byte (255);
+      out_byte (127);
+      out_byte (127);  /* Fake extended opcode.  */
+
+      /* Logicals table offset.  */
+      logicals_start = symbol_temp_make ();
+      exp.X_add_symbol = logicals_start;
+      emit_expr (&exp, sizeof_offset);
+
+      /* Actuals table offset.  */
+      actuals_start = symbol_temp_make ();
+      exp.X_add_symbol = actuals_start;
+      emit_expr (&exp, sizeof_offset);
+
+      /* Directory and filename lists. */
+      out_dwarf5_file_list (str_seg, sizeof_offset);
+
+      /* Subprogram list. */
+      out_subprog_list (str_seg, sizeof_offset);
+
+      symbol_set_value_now (logicals_start);
+      emit_logicals ();
+      symbol_set_value_now (actuals_start);
+    }
+  else if (version >= 5)
+    {
+      out_dwarf5_file_list (str_seg, sizeof_offset);
+      symbol_set_value_now (prologue_end);
+    }
+  else
+    {
+      out_file_list ();
+      symbol_set_value_now (prologue_end);
+    }
 
   /* For each section, emit a statement program.  */
   for (s = all_segs; s; s = s->next)
@@ -1864,11 +2504,13 @@ dwarf2_finish (void)
   segT line_seg;
   struct line_seg *s;
   segT info_seg;
+  segT str_seg = NULL;
   int emit_other_sections = 0;
   int empty_debug_line = 0;
 
   info_seg = bfd_get_section_by_name (stdoutput, ".debug_info");
-  emit_other_sections = info_seg == NULL || !seg_not_empty_p (info_seg);
+  emit_other_sections = ((info_seg == NULL || !seg_not_empty_p (info_seg))
+                        && logicals_in_use == 0);
 
   line_seg = bfd_get_section_by_name (stdoutput, ".debug_line");
   empty_debug_line = line_seg == NULL || !seg_not_empty_p (line_seg);
@@ -1905,7 +2547,23 @@ dwarf2_finish (void)
        }
     }
 
-  out_debug_line (line_seg);
+  if (logicals_in_use > 0)
+    {
+      str_seg = subseg_new (".debug_line_str", 0);
+      bfd_set_section_flags (stdoutput, str_seg,
+                            (SEC_READONLY | SEC_DEBUGGING
+                             | SEC_MERGE | SEC_STRINGS));
+      str_seg->entsize = 1;
+      debug_line_str_table.strings = NULL;
+      debug_line_str_table.strings_in_use = 0;
+      debug_line_str_table.strings_allocated = 0;
+      debug_line_str_table.next_offset = 1;
+    }
+
+  out_debug_line (line_seg, str_seg);
+
+  if (str_seg != NULL)
+    out_string_table (str_seg, &debug_line_str_table);
 
   /* If this is assembler generated line info, and there is no
      debug_info already, we need .debug_info and .debug_abbrev
index c12de93f887c687641c47637285f78811fa08afb..50d490a417a8005e703880a785d045f2f06e2961 100644 (file)
@@ -35,6 +35,7 @@ struct dwarf2_line_info {
   unsigned int isa;
   unsigned int flags;
   unsigned int discriminator;
+  unsigned int logical;
 };
 
 /* Implements the .file FILENO "FILENAME" directive.  FILENO can be 0
@@ -42,13 +43,29 @@ struct dwarf2_line_info {
    number must be >0.  */
 extern char *dwarf2_directive_file (int dummy);
 
+/* Experimental DWARF-5 extension:
+   Implements the .subprog SUBPNO ["SUBPROG" [FILENO LINENO]] directive.
+   FILENO is the file number, LINENO the line number and the
+   (optional) COLUMN the column of the source code that the following
+   instruction corresponds to.  FILENO can be 0 to indicate that the
+   filename specified by the textually most recent .file directive
+   should be used.  */
+extern void dwarf2_directive_subprog (int dummy);
+
 /* Implements the .loc FILENO LINENO [COLUMN] directive.  FILENO is
    the file number, LINENO the line number and the (optional) COLUMN
    the column of the source code that the following instruction
    corresponds to.  FILENO can be 0 to indicate that the filename
    specified by the textually most recent .file directive should be
    used.  */
-extern void dwarf2_directive_loc (int dummy);
+/* Experimental DWARF-5 extension:
+   If IS_LLOC is true, implements the .lloc LOGICAL [FILENO LINENO [COLUMN]]
+   directive. FILENO is the file number, LINENO the line number and the
+   (optional) COLUMN the column of the source code that the following
+   instruction corresponds to.  FILENO can be 0 to indicate that the
+   filename specified by the textually most recent .file directive
+   should be used.  */
+extern void dwarf2_directive_loc (int is_lloc);
 
 /* Implements the .loc_mark_labels {0,1} directive.  */
 extern void dwarf2_directive_loc_mark_labels (int dummy);