This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 6/6] DWARF Two Level Line Tables: initial support


Hi.

This patch adds support for Two Level Line Tables described here:

http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables
http://www.dwarfstd.org/ShowIssue.php?issue=140906.1

This patch includes a board file to make testing easy.
Here's how I ran the tests:
Install google-4.9.x gcc and users/ccoutant/two-level-line-tables
binutils and put them in $PATH.
You need to configure binutils with --enable-gold=default --enable-plugins
[I'm not sure you need --enable-plugins, but gold defaults plugins
to off and bfd-ld defaults it to on, and I was getting problems without it.
I didn't dig into why.]

Then: make check --target_board=fission-tll

I also plan to test without fission before committing,
but this is good enough for now.

2015-03-12  Doug Evans  <dje@google.com>

	include/
	* dwarf2.h (dwarf_line_number_ops): New enum values
	DW_LNS_set_address_from_logical, DW_LNS_set_subprogram,
	DW_LNS_inlined_call, DW_LNS_pop_context.
	(dwarf_line_number_content_type): New enum values
	DW_LNCT_subprogram_name, DW_LNCT_decl_file, DW_LNCT_decl_line.

	gdb/
	* NEWS: Mention Two Level Line Tables support.
	* dwarf2read.c (DW_VERSION_EXPERIMENTAL_TLL): New macro.
	(line_header) <actuals_program_start, actuals_program_end>: New
	members.
	(dwarf5_line_header_entry) <path_or_name>: Renamed from path.
	All uses updated.
	(dwarf5_line_header_entry) <decl_file, decl_line>: New members.
	(read_dwarf5_line_header_entry): Handle DW_LNCT_subprogram_name,
	DW_LNCT_decl_file, DW_LNCT_decl_line.
	(read_dwarf6_subprograms): New function.
	(read_experimental_tll_header_and_tables): New function.
	(dwarf_decode_line_header): Handle DW_VERSION_EXPERIMENTAL_TLL.
	(logical_line): New typdef.
	(DEF_VEC_O logical_line): Define vector.
	(lnp_state_machine) <context, subprogram>: New members.
	(lnp_reader_state) <two_level_p, build_logicals_p>: New members.
	(lnp_reader_state) <logical_line_table>: New member.
	(dwarf_append_logical_line): New function.
	(dwarf_record_line): Handle Two Level Line Tables.
	(init_lnp_state_machine): Update.
	(dwarf_decode_lines_1): New result type to int.  All callers updated.
	Add Two Level Line Tables support.
	(dwarf_decode_lines): Add Two Level Line Tables support.

	testsuite/
	* boards/fission-tll.exp: New file.

diff --git a/gdb/NEWS b/gdb/NEWS
index c448aff..37ac6d3 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -7,6 +7,9 @@
 
 GDB now supports DWARF 5 line tables and the new .debug_line_str section.
 
+GDB now supports the Two Level Line Tables extension proposed here:
+http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables
+
 * The "info source" command now displays the producer string if it was
   present in the debug info.  This typically includes the compiler version
   and may include things like its command line arguments.
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index c82bbcb..861e43e 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -76,6 +76,11 @@
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
 
+/* DWARF version with Two Level Line Tables (TLLs).
+   http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables
+   http://www.dwarfstd.org/ShowIssue.php?issue=140906.1  */
+#define DW_VERSION_EXPERIMENTAL_TLL 0xf006
+
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
    This is in contrast to the low level DIE reading of dwarf2_die_debug.  */
@@ -1092,6 +1097,13 @@ struct line_header
   /* The start and end of the statement program following this
      header.  These point into dwarf2_per_objfile->line_buffer.  */
   const gdb_byte *statement_program_start, *statement_program_end;
+
+  /* The start and end of the actuals statement program following this
+     header.  These point into dwarf2_per_objfile->line_buffer.
+     Only used by Two Level Line Tables.
+     They are NULL if the actuals program is not present.
+     http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables */
+  const gdb_byte *actuals_program_start, *actuals_program_end;
 };
 
 /* When we construct a partial symbol table entry we only
@@ -17360,14 +17372,18 @@ struct dwarf5_line_header_format
 
 struct dwarf5_line_header_entry
 {
-  /* DW_LNCT_path.  */
-  const char *path;
+  /* DW_LNCT_path or DW_LNCT_subprogram_name.  */
+  const char *path_or_name;
   /* DW_LNCT_directory_index.  */
   unsigned int dir_index;
   /* DW_LNCT_timestamp.  */
   unsigned int timestamp;
   /* DW_LNCT_size.  */
   unsigned int size;
+  /* DW_LNCT_decl_file.  */
+  unsigned int decl_file;
+  /* DW_LNCT_decl_line.  */
+  unsigned int decl_line;
 };
 
 /* Subroutine of read_dwarf5_line_header_paths to simplify it.
@@ -17393,6 +17409,7 @@ read_dwarf5_line_header_entry (struct line_header *lh,
   switch (type)
     {
     case DW_LNCT_path:
+    case DW_LNCT_subprogram_name:
       switch (form)
 	{
 	case DW_FORM_string:
@@ -17413,6 +17430,8 @@ read_dwarf5_line_header_entry (struct line_header *lh,
     case DW_LNCT_directory_index:
     case DW_LNCT_timestamp:
     case DW_LNCT_size:
+    case DW_LNCT_decl_file:
+    case DW_LNCT_decl_line:
       line_ptr = read_unsigned_integer (section, line_ptr, form, context,
 					&value);
       if (line_ptr == NULL)
@@ -17443,7 +17462,10 @@ read_dwarf5_line_header_entry (struct line_header *lh,
   switch (type)
     {
     case DW_LNCT_path:
-      entry->path = string;
+      entry->path_or_name = string;
+      break;
+    case DW_LNCT_subprogram_name:
+      entry->path_or_name = string;
       break;
     case DW_LNCT_directory_index:
       entry->dir_index = value;
@@ -17454,6 +17476,12 @@ read_dwarf5_line_header_entry (struct line_header *lh,
     case DW_LNCT_size:
       entry->size = value;
       break;
+    case DW_LNCT_decl_file:
+      entry->decl_file = value;
+      break;
+    case DW_LNCT_decl_line:
+      entry->decl_line = value;
+      break;
     default:
       gdb_assert_not_reached ("bad type handling");
     }
@@ -17578,12 +17606,12 @@ read_dwarf5_line_header_paths (struct line_header *lh,
 
   for (i = 0; i < dir_count; ++i)
     {
-      add_include_dir (lh, directories[i].path);
+      add_include_dir (lh, directories[i].path_or_name);
     }
 
   for (i = 0; i < file_count; ++i)
     {
-      add_file_name (lh, files[i].path, files[i].dir_index,
+      add_file_name (lh, files[i].path_or_name, files[i].dir_index,
 		     files[i].timestamp, files[i].size);
     }
 
@@ -17592,6 +17620,98 @@ read_dwarf5_line_header_paths (struct line_header *lh,
   return line_ptr;
 }
 
+/* Subroutine of dwarf_decode_line_header to simplify it.
+   Read dwarf6 two level line table entries in the header.
+   The result is a pointer to the end of the read data, or NULL
+   if an error was found (complaint has already been issued).  */
+
+static const gdb_byte *
+read_dwarf6_subprograms (struct line_header *lh,
+			 struct dwarf2_section_info *section,
+			 const gdb_byte *line_ptr)
+{
+  bfd *abfd = get_section_bfd_owner (section);
+  unsigned int subprog_count;
+  struct dwarf5_line_header_entry *subprograms;
+  struct cleanup *cleanups;
+
+  cleanups = make_cleanup (null_cleanup, NULL);
+
+  line_ptr = read_dwarf5_line_header_subtable (lh, section, line_ptr,
+					       &subprograms, &subprog_count);
+  if (line_ptr == NULL)
+    {
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  /* TODO(dje): We don't do anything with subprograms.  */
+  xfree (subprograms);
+
+  gdb_assert (line_ptr != NULL);
+  do_cleanups (cleanups);
+  return line_ptr;
+}
+
+/* Read the header and directory/file/subprogram tables part of experimental
+   TLL support.
+   The result is a pointer to the logicals line number program, which
+   is actually embedded in the faked line number program of the outer
+   version 2 shell.  */
+
+static const gdb_byte *
+read_experimental_tll_header_and_tables (struct line_header *lh,
+					 struct dwarf2_section_info *section,
+					 const gdb_byte *line_ptr,
+					 const gdb_byte *end_of_header_length,
+					 const gdb_byte *program_end)
+{
+  bfd *abfd = get_section_bfd_owner (section);
+  unsigned int logicals_offset, actuals_offset;
+
+  /* Skip the fake directory and filename table.  */
+  if (*line_ptr != 0)
+    complaint (&symfile_complaints, _("fake directory table not empty"));
+  ++line_ptr;
+  if (*line_ptr != 0)
+    complaint (&symfile_complaints, _("fake file table not empty"));
+  ++line_ptr;
+
+  /* Skip the fake extended opcode that wraps the rest of the section.  */
+  line_ptr += 5;
+
+  logicals_offset = read_offset_1 (abfd, line_ptr, lh->offset_size);
+  line_ptr += lh->offset_size;
+  actuals_offset = read_offset_1 (abfd, line_ptr, lh->offset_size);
+  line_ptr += lh->offset_size;
+
+  lh->statement_program_start = end_of_header_length + logicals_offset;
+  if (actuals_offset != 0)
+    {
+      lh->statement_program_end = end_of_header_length + actuals_offset;
+      lh->actuals_program_start = lh->statement_program_end;
+      lh->actuals_program_end = program_end;
+    }
+  else
+    lh->statement_program_end = program_end;
+
+  if (line_ptr >= program_end)
+    {
+      dwarf2_section_buffer_overflow_complaint (section);
+      return NULL;
+    }
+
+  line_ptr = read_dwarf5_line_header_paths (lh, section, line_ptr);
+  if (line_ptr == NULL)
+    return NULL;
+
+  line_ptr = read_dwarf6_subprograms (lh, section, line_ptr);
+  if (line_ptr == NULL)
+    return NULL;
+
+  return line_ptr;
+}
+
 /* Read the statement program header starting at OFFSET in
    .debug_line, or .debug_line.dwo.  Return a pointer
    to a struct line_header, allocated using xmalloc.
@@ -17610,7 +17730,7 @@ dwarf_decode_line_header (unsigned int offset, struct dwarf2_cu *cu)
 {
   struct cleanup *back_to;
   struct line_header *lh;
-  const gdb_byte *line_ptr, *end_ptr, *program_end;
+  const gdb_byte *line_ptr, *end_ptr, *program_end, *end_of_header_length;
   unsigned int bytes_read, offset_size;
   int i;
   const char *cur_dir, *cur_file;
@@ -17667,7 +17787,7 @@ dwarf_decode_line_header (unsigned int offset, struct dwarf2_cu *cu)
   program_end = line_ptr + lh->total_length;
   lh->version = read_2_bytes (abfd, line_ptr);
   line_ptr += 2;
-  if (lh->version > 5)
+  if (lh->version > 5 && lh->version != DW_VERSION_EXPERIMENTAL_TLL)
     {
       /* This is a version we don't understand.  The format could have
 	 changed in ways we don't handle properly so just punt.  */
@@ -17675,7 +17795,7 @@ dwarf_decode_line_header (unsigned int offset, struct dwarf2_cu *cu)
 		 _("unsupported version in .debug_line section"));
       return NULL;
     }
-  if (lh->version >= 5)
+  if (lh->version >= 5 && lh->version != DW_VERSION_EXPERIMENTAL_TLL)
     {
       lh->address_size = read_1_byte (abfd, line_ptr);
       line_ptr += 1;
@@ -17684,6 +17804,7 @@ dwarf_decode_line_header (unsigned int offset, struct dwarf2_cu *cu)
     }
   lh->prologue_length = read_offset_1 (abfd, line_ptr, offset_size);
   line_ptr += offset_size;
+  end_of_header_length = line_ptr;
   lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
   line_ptr += 1;
   if (lh->version >= 4)
@@ -17746,8 +17867,28 @@ dwarf_decode_line_header (unsigned int offset, struct dwarf2_cu *cu)
       return NULL;
     }
 
-  lh->statement_program_start = line_ptr;
-  lh->statement_program_end = program_end;
+  /* In order to not hang existing gdb's the experimental version of TLL
+     support uses a gross hack.  The extension is embedded in a line header
+     that looks like a version 4 header, and in the line number program that
+     is a single entry that masks out both the real programs (logicals and
+     actuals), plus the version field is different.  */
+  if (lh->version == DW_VERSION_EXPERIMENTAL_TLL)
+    {
+      line_ptr
+	= read_experimental_tll_header_and_tables (lh, section, line_ptr,
+						   end_of_header_length,
+						   program_end);
+      if (line_ptr == NULL)
+	{
+	  do_cleanups (back_to);
+	  return NULL;
+	}
+    }
+  else
+    {
+      lh->statement_program_start = line_ptr;
+      lh->statement_program_end = program_end;
+    }
 
   if (line_ptr > program_end)
     {
@@ -17846,6 +17987,23 @@ psymtab_include_file_name (const struct line_header *lh, int file_index,
   return include_name;
 }
 
+/* An entry in the "logicals" linetable of the Two Level Line Tables
+   extension to dwarf5.  */
+
+typedef struct
+{
+  unsigned int op_index;
+  unsigned int file;
+  unsigned int line;
+  CORE_ADDR address;
+  unsigned int discriminator;
+  int is_stmt;
+  unsigned int context;
+  unsigned int subprogram;
+} logical_line;
+
+DEF_VEC_O (logical_line);
+
 /* Function to record a line number.  */
 
 typedef void (record_line_ftype) (struct subfile *subfile, int line,
@@ -17864,6 +18022,12 @@ typedef struct
   int is_stmt;
   unsigned int discriminator;
 
+  /* Two Level Line Tables.
+     http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables
+     http://www.dwarfstd.org/ShowIssue.php?issue=140906.1  */
+  unsigned int context;
+  unsigned int subprogram;
+
   /* Additional bits of state we need to track.  */
 
   /* The last file that we called dwarf2_start_subfile for.
@@ -17897,8 +18061,65 @@ typedef struct
      Otherwise we're building partial symtabs and are just interested in
      finding include files mentioned by the line number program.  */
   int record_lines_p;
+
+  /* Non-zero if we're reading Two Level Line Tables (TLLs).  */
+  int two_level_p;
+
+  /* Non-zero if we're building the logical line table.
+     Otherwise we're building the actual line table.
+     The distinction comes from the TLL feature.
+     An "actual" line is what gets recorded in GDB's own tables.
+     A logical line is used to help compute actual line entries.  */
+  int build_logicals_p;
+
+  /* The logical line table.  */
+  VEC (logical_line) *logical_line_table;
 } lnp_reader_state;
 
+/* Append a line to the logical line table.
+   Note: There are no heuristics involved in building this table.
+   The finite state machine must be followed to the letter because entries
+   in the actuals table will refer to entries in the logicals table assuming
+   the state machine has been precisely followed.
+   Assuming no bugs of course :-).  */
+
+static void
+dwarf_append_logical_line (lnp_reader_state *reader,
+			   const lnp_state_machine *state)
+{
+  logical_line entry;
+
+  if (dwarf2_line_debug)
+    {
+      unsigned int logical_line_nr
+	= VEC_length (logical_line, reader->logical_line_table) + 1;
+      const char *file_name = "???";
+
+      if (state->file != 0
+	  && state->file - 1 < reader->line_header->num_file_names)
+	file_name = reader->line_header->file_names[state->file - 1].name;
+
+      fprintf_unfiltered (gdb_stdlog,
+			  "Adding logical line %u: file %u(%s), line %u,"
+			  " address %s, is_stmt %u, discrim %u\n",
+			  logical_line_nr, state->file, lbasename (file_name),
+			  state->line,
+			  paddress (reader->gdbarch, state->address),
+			  state->is_stmt, state->discriminator);
+    }
+
+  entry.op_index = state->op_index;
+  entry.file = state->file;
+  entry.line = state->line;
+  entry.address = state->address;
+  entry.is_stmt = state->is_stmt;
+  entry.discriminator = state->discriminator;
+  entry.context = state->context;
+  entry.subprogram = state->subprogram;
+
+  VEC_safe_push (logical_line, reader->logical_line_table, &entry);
+}
+
 /* Ignore this record_line request.  */
 
 static void
@@ -18005,53 +18226,113 @@ static void
 dwarf_record_line (lnp_reader_state *reader, lnp_state_machine *state,
 		   int end_sequence)
 {
-  const struct line_header *lh = reader->line_header;
-  unsigned int file, line, discriminator;
-  int is_stmt;
-
-  file = state->file;
-  line = state->line;
-  is_stmt = state->is_stmt;
-  discriminator = state->discriminator;
-
-  if (dwarf2_line_debug)
+  if (reader->build_logicals_p)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "Processing actual line %u: file %u,"
-			  " address %s, is_stmt %u, discrim %u\n",
-			  line, file,
-			  paddress (reader->gdbarch, state->address),
-			  is_stmt, discriminator);
+      /* If there's a problem with a value here we leave it
+	 to processing the actuals table to flag it.  */
+      dwarf_append_logical_line (reader, state);
     }
-
-  if (file == 0 || file - 1 >= lh->num_file_names)
-    dwarf2_debug_line_missing_file_complaint ();
-  /* For now we ignore lines not starting on an instruction boundary.
-     But not when processing end_sequence for compatibility with the
-     previous version of the code.  */
-  else if (state->op_index == 0 || end_sequence)
+  else
     {
-      lh->file_names[file - 1].included_p = 1;
-      if (reader->record_lines_p && is_stmt)
+      const struct line_header *lh = reader->line_header;
+      unsigned int file, line, discriminator;
+      int is_stmt;
+
+      /* If we're reading the actuals table, a lot of the info
+	 comes from the logicals table, so fetch it.  */
+      if (reader->two_level_p)
 	{
-	  if (state->last_subfile != current_subfile)
+	  const logical_line *table
+	    = VEC_address (logical_line, reader->logical_line_table);
+
+	  if (state->line == 0
+	      || (state->line - 1
+		  >= VEC_length (logical_line, reader->logical_line_table)))
 	    {
-	      dwarf_finish_line (reader->gdbarch, state->last_subfile,
-				 state->address, state->record_line);
+	      complaint (&symfile_complaints,
+			 _("logical line number is zero"
+			   " or too large"));
+	      return;
 	    }
 
-	  if (!end_sequence)
+	  /* The "line number" when processing the actual line table is
+	     an origin-1 index into the logical line table.  */
+	  file = table[state->line - 1].file;
+	  line = table[state->line - 1].line;
+	  is_stmt = table[state->line - 1].is_stmt;
+	  discriminator = table[state->line - 1].discriminator;
+	}
+      else
+	{
+	  file = state->file;
+	  line = state->line;
+	  is_stmt = state->is_stmt;
+	  discriminator = state->discriminator;
+	}
+
+      if (dwarf2_line_debug)
+	{
+	  fprintf_unfiltered (gdb_stdlog,
+			      "Processing actual line %u: file %u,"
+			      " address %s, is_stmt %u, discrim %u\n",
+			      line, file,
+			      paddress (reader->gdbarch, state->address),
+			      is_stmt, discriminator);
+	}
+
+      if (file == 0 || file - 1 >= lh->num_file_names)
+	dwarf2_debug_line_missing_file_complaint ();
+      /* For now we ignore lines not starting on an instruction boundary.
+	 But not when processing end_sequence for compatibility with the
+	 previous version of the code.  */
+      else if (state->op_index == 0 || end_sequence)
+	{
+	  lh->file_names[file - 1].included_p = 1;
+	  if (reader->record_lines_p && is_stmt)
 	    {
-	      if (dwarf_record_line_p (line, state->last_line,
-				       state->line_has_non_zero_discriminator,
-				       state->last_subfile))
+	      /* If we're reading the actuals line number program, then we
+		 have to create subfiles here as DW_LNS_set_file isn't
+		 used.  */
+	      if (reader->two_level_p
+		  /* Only do this if we need to, start_subfile does a
+		     linear search for existing files.  */
+		  && file != state->last_file)
+		{
+		  struct file_entry *fe;
+		  const char *dir = NULL;
+
+		  fe = &lh->file_names[file - 1];
+		  if (fe->dir_index)
+		    dir = lh->include_dirs[fe->dir_index - 1];
+		  /* dwarf2_start_subfile sets current_subfile, so save this
+		     now.  */
+		  state->last_subfile = current_subfile;
+		  state->line_has_non_zero_discriminator = discriminator != 0;
+		  dwarf2_start_subfile (fe->name, dir);
+		  /* This records what's in current_subfile in a way we can
+		     compare with the file in the state machine.  */
+		  state->last_file = file;
+		}
+
+	      if (state->last_subfile != current_subfile)
 		{
-		  dwarf_record_line_1 (reader->gdbarch, current_subfile,
-				       line, state->address,
-				       state->record_line);
+		  dwarf_finish_line (reader->gdbarch, state->last_subfile,
+				     state->address, state->record_line);
+		}
+
+	      if (!end_sequence)
+		{
+		  if (dwarf_record_line_p (line, state->last_line,
+					   state->line_has_non_zero_discriminator,
+					   state->last_subfile))
+		    {
+		      dwarf_record_line_1 (reader->gdbarch, current_subfile,
+					   line, state->address,
+					   state->record_line);
+		    }
+		  state->last_subfile = current_subfile;
+		  state->last_line = line;
 		}
-	      state->last_subfile = current_subfile;
-	      state->last_line = line;
 	    }
 	}
     }
@@ -18087,6 +18368,8 @@ init_lnp_state_machine (lnp_state_machine *state,
   state->address = gdbarch_adjust_dwarf2_line (reader->gdbarch, 0, 0);
   state->is_stmt = reader->line_header->default_is_stmt;
   state->discriminator = 0;
+  state->context = 0;
+  state->subprogram = 0;
 }
 
 /* Check address and if invalid nop-out the rest of the lines in this
@@ -18122,11 +18405,23 @@ check_line_address (struct dwarf2_cu *cu, lnp_state_machine *state,
 /* Subroutine of dwarf_decode_lines to simplify it.
    Process the line number information in LH.
    If DECODE_FOR_PST_P is non-zero, all we do is process the line number
-   program in order to set included_p for every referenced header.  */
+   program in order to set included_p for every referenced header.
 
-static void
+   Two Level Line Tables extension: If LOGICALS_PTR is non-NULL then we are
+   processing TLLs.  If PASS is 1 then we are reading the logicals table,
+   and on return the result is stored in *LOGICALS_PTR.
+   If PASS is 2 then we are reading the actuals table, and *LOGICALS_PTR is
+   the used as input.
+   If LOGICALS_PTR is NULL (dwarf5 and earlier), then PASS must be 1.
+   If DECODE_FOR_PST_P is non-zero, then LOGICALS_PTR must be NULL and
+   PASS must be 1.
+
+   The result is zero for success, -1 for failure.  */
+
+static int
 dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
-		      const int decode_for_pst_p, CORE_ADDR lowpc)
+		      const int decode_for_pst_p, CORE_ADDR lowpc,
+		      VEC (logical_line) **logicals_ptr, int pass)
 {
   const gdb_byte *line_ptr, *extended_end;
   const gdb_byte *line_end;
@@ -18136,20 +18431,78 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
   struct objfile *objfile = cu->objfile;
   bfd *abfd = objfile->obfd;
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
+  int two_level_p = logicals_ptr != NULL;
+  /* Non-zero if we're building the logical line table.  */
+  int build_logicals_p = two_level_p && pass == 1;
+  /* Non-zero if we're reading the actuals table, otherwise we're reading
+     the logicals table.  */
+  int reading_actuals_p = pass == 2;
   /* Non-zero if we're recording line info (as opposed to building partial
      symtabs or building the logical line table).  */
-  int record_lines_p = !decode_for_pst_p;
+  int record_lines_p = (!decode_for_pst_p
+			&& (!two_level_p || (two_level_p && pass == 2)));
+  struct cleanup *cleanups, *logicals_cleanup;
   /* A collection of things we need to pass to dwarf_record_line.  */
   lnp_reader_state reader_state;
 
+  cleanups = make_cleanup (null_cleanup, NULL);
+
+  /* Reading the line number program state machine involves a lot of bit-fiddly
+     code and we'd rather have only one copy of it.  OTOH it is used for
+     multiple purposes.  These asserts exist, besides to validate the
+     arguments, to add clarity to how this function can be used.  */
+
+  /* If we're not using TLLs there's only one pass.  */
+  if (!two_level_p)
+    gdb_assert (pass == 1);
+  /* Both could be zero (building partial symtabs), but at most one can be
+     non-zero.  */
+  gdb_assert (!(build_logicals_p && record_lines_p));
+  /* If we're building partial symtabs, we only need the file information.  */
+  if (decode_for_pst_p)
+    gdb_assert (logicals_ptr == NULL && pass == 1);
+  /* If we're reading the actuals table then we should be recording lines.  */
+  if (reading_actuals_p)
+    gdb_assert (record_lines_p);
+
   baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
 
-  line_ptr = lh->statement_program_start;
-  line_end = lh->statement_program_end;
+  if (reading_actuals_p)
+    {
+      line_ptr = lh->actuals_program_start;
+      line_end = lh->actuals_program_end;
+    }
+  else
+    {
+      line_ptr = lh->statement_program_start;
+      line_end = lh->statement_program_end;
+    }
 
   reader_state.gdbarch = gdbarch;
   reader_state.line_header = lh;
   reader_state.record_lines_p = record_lines_p;
+  reader_state.two_level_p = two_level_p;
+  reader_state.build_logicals_p = build_logicals_p;
+  reader_state.logical_line_table = NULL;
+
+  logicals_cleanup = NULL;
+  if (two_level_p)
+    {
+      if (pass == 1)
+	{
+	  *logicals_ptr = NULL;
+	  logicals_cleanup = make_cleanup (VEC_cleanup (logical_line),
+					   &reader_state.logical_line_table);
+	}
+      else
+	reader_state.logical_line_table = *logicals_ptr;
+    }
+
+  /* Note: If we're building the logical line table then we need to protect it
+     with a cleanup, but we also need to discard that cleanup at the end.
+     Therefore be careful with adding new cleanups after this point: they
+     can't just be simply added and forgotten because they will be discarded
+     along with logicals_cleanup.  */
 
   /* Read the statement sequences until there's nothing left.  */
   while (line_ptr < line_end)
@@ -18161,6 +18514,23 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
       /* Reset the state machine at the start of each sequence.  */
       init_lnp_state_machine (&state_machine, &reader_state);
 
+      /* Notes on Two Level Line Tables.
+	 http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables
+
+	 When only a single line number table is used, the context/subprogram
+	 registers are not used, and the logicals table "degrades gracefully
+	 to the single table of dwarf4".
+
+	 When both a logical line table and an actual line table are
+	 used, the basic_block, end_sequence, and isa registers are
+	 not used in the logical line table.  The file, column,
+	 is_stmt, prologue_end, epilogue_begin, discriminator,
+	 context, and subprogram registers are not used in the actual
+	 line table.  In the actual line table, the line register
+	 represents a row in the logicals table rather than an actual
+	 line number.  Rows in the logical line table are numbered
+	 starting at 1.  */
+
       if (record_lines_p && lh->num_file_names >= state_machine.file)
 	{
           /* Start a subfile for the current file of the state machine.  */
@@ -18272,7 +18642,8 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
 		default:
 		  complaint (&symfile_complaints,
 			     _("mangled .debug_line section"));
-		  return;
+		  do_cleanups (cleanups);
+		  return -1;
 		}
 	      /* Make sure that we parsed the extended op correctly.  If e.g.
 		 we expected a different address size than the producer used,
@@ -18281,7 +18652,8 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
 		{
 		  complaint (&symfile_complaints,
 			     _("mangled .debug_line section"));
-		  return;
+		  do_cleanups (cleanups);
+		  return -1;
 		}
 	      break;
 	    case DW_LNS_copy:
@@ -18384,6 +18756,107 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
 		line_ptr += 2;
 	      }
 	      break;
+	    case DW_LNS_set_subprogram: /* Logicals table only.  */
+	      /* case DW_LNS_set_address_from_logical: - Actuals table only,
+		 same value as DW_LNS_set_subprogram.  */
+	      if (reading_actuals_p)
+		{
+		  /* DW_LNS_set_address_from_logical */
+		  int line_adj = read_signed_leb128 (abfd, line_ptr,
+						     &bytes_read);
+
+		  line_ptr += bytes_read;
+		  state_machine.line = (int) state_machine.line + line_adj;
+		  if (state_machine.line == 0
+		      || (state_machine.line - 1
+			  >= VEC_length (logical_line,
+					 reader_state.logical_line_table)))
+		    {
+		      complaint (&symfile_complaints,
+				 _("logical line number is zero"
+				   " or too large"));
+		    }
+		  else
+		    {
+		      const logical_line *table
+			= VEC_address (logical_line,
+				       reader_state.logical_line_table);
+		      CORE_ADDR address
+			= table[state_machine.line - 1].address;
+
+		      check_line_address (cu, &state_machine, line_ptr,
+					  lowpc, address);
+		      state_machine.address = address;
+		      state_machine.op_index
+			= table[state_machine.line - 1].op_index;
+		    }
+		}
+	      else /* DW_LNS_set_subprogram */
+		{
+		  state_machine.subprogram
+		    = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		  line_ptr += bytes_read;
+		  state_machine.context = 0;
+		}
+	      break;
+	    case DW_LNS_inlined_call: /* Logicals table only.  */
+	      {
+		int context_adj = read_signed_leb128 (abfd, line_ptr,
+						      &bytes_read);
+
+		line_ptr += bytes_read;
+		if (build_logicals_p)
+		  {
+		    state_machine.context
+		      = ((int) VEC_length (logical_line,
+					   reader_state.logical_line_table)
+			 + context_adj);
+		    state_machine.subprogram
+		      = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+		  }
+		else
+		  {
+		    complaint (&symfile_complaints,
+			       _("DW_LNS_inlined_call seen outside of"
+				 " logicals table"));
+		  }
+	      }
+	      break;
+	    case DW_LNS_pop_context: /* Logicals table only.  */
+	      if (build_logicals_p)
+		{
+		  unsigned int logical = state_machine.context;
+		  const logical_line *table
+		    = VEC_address (logical_line,
+				   reader_state.logical_line_table);
+
+		  if (logical > 0
+		      && (logical - 1
+			  < VEC_length (logical_line,
+					reader_state.logical_line_table)))
+		    {
+		      state_machine.file = table[logical - 1].file;
+		      state_machine.line = table[logical - 1].line;
+		      state_machine.discriminator
+			= table[logical - 1].discriminator;
+		      state_machine.is_stmt = table[logical - 1].is_stmt;
+		      state_machine.context = table[logical - 1].context;
+		      state_machine.subprogram = table[logical - 1].subprogram;
+		    }
+		  else
+		    {
+		      complaint (&symfile_complaints,
+				 _("invalid context to pop from"));
+		    }
+		}
+	      else
+		{
+		  complaint (&symfile_complaints,
+			     _("DW_LNS_pop_context seen outside of"
+			       " logicals table"));
+		}
+	      break;
 	    default:
 	      {
 		/* Unknown standard opcode, ignore it.  */
@@ -18398,13 +18871,28 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
 	    }
 	}
 
-      if (!end_sequence)
-	dwarf2_debug_line_missing_end_sequence_complaint ();
+      if (!build_logicals_p)
+	{
+	  if (!end_sequence)
+	    dwarf2_debug_line_missing_end_sequence_complaint ();
+
+	  /* We got a DW_LNE_end_sequence (or we ran off the end of the buffer,
+	     in which case we still finish recording the last line).  */
+	  dwarf_record_line (&reader_state, &state_machine, 1);
+	}
+    }
 
-      /* We got a DW_LNE_end_sequence (or we ran off the end of the buffer,
-	 in which case we still finish recording the last line).  */
-      dwarf_record_line (&reader_state, &state_machine, 1);
+  /* Pass back the logicals table to the caller if we just built it.  */
+  if (two_level_p && pass == 1)
+    {
+      *logicals_ptr = reader_state.logical_line_table;
+      discard_cleanups (logicals_cleanup);
     }
+  else
+    gdb_assert (logicals_cleanup == NULL);
+
+  do_cleanups (cleanups);
+  return 0;
 }
 
 /* Decode the Line Number Program (LNP) for the given line_header
@@ -18444,7 +18932,35 @@ dwarf_decode_lines (struct line_header *lh, const char *comp_dir,
   const int decode_for_pst_p = (pst != NULL);
 
   if (decode_mapping)
-    dwarf_decode_lines_1 (lh, cu, decode_for_pst_p, lowpc);
+    {
+      /* If an "actuals" line number program is present, we need to process the
+	 line number information in two passes: the first pass builds the
+	 "logicals" line table, and then the second pass builds the "actuals"
+	 table (the one we use).  If we're building partial symtabs, then we
+	 just want file information and therefore just need to read the
+	 logicals table.  */
+      if (!decode_for_pst_p && lh->actuals_program_start != NULL)
+	{
+	  VEC (logical_line) *logical_line_table = NULL;
+	  struct cleanup *cleanups = NULL;
+
+	  if (dwarf_decode_lines_1 (lh, cu, decode_for_pst_p, lowpc,
+				    &logical_line_table, 1) == 0)
+	    {
+	      cleanups = make_cleanup (VEC_cleanup (logical_line),
+				       &logical_line_table);
+	      dwarf_decode_lines_1 (lh, cu, decode_for_pst_p, lowpc,
+				    &logical_line_table, 2);
+	    }
+	  if (cleanups != NULL)
+	    do_cleanups (cleanups);
+	}
+      else
+	dwarf_decode_lines_1 (lh, cu, decode_for_pst_p, lowpc, NULL, 1);
+    }
+
+  /* We ignore a failure when building the real or "actuals" line table,
+     as we can still build the symtabs.  */
 
   if (decode_for_pst_p)
     {
diff --git a/gdb/testsuite/boards/fission-tll.exp b/gdb/testsuite/boards/fission-tll.exp
new file mode 100644
index 0000000..3aae884
--- /dev/null
+++ b/gdb/testsuite/boards/fission-tll.exp
@@ -0,0 +1,37 @@
+# Copyright 2012-2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is a dejagnu "board file" and is used to run the testsuite
+# with Fission support.  http://gcc.gnu.org/wiki/DebugFission
+#
+# Example usage:
+# bash$ make check RUNTESTFLAGS='--target_board=fission'
+
+# This is copied from baseboards/unix.exp.
+# At the moment this only supports things that unix.exp supports.
+load_generic_config "unix"
+process_multilib_options ""
+set_board_info compiler "[find_gcc]"
+
+# This requires a relatively recent version of gcc (>4.7) and gold
+# for the linker.
+# Note: -ggnu-pubnames is required for Gold to build .gdb_index, and while
+# -gsplit-dwarf implies -ggnu-pubnames for gcc, it doesn't for clang.
+set_board_info debug_flags "-gdwarf-4 -gsplit-dwarf -ggnu-pubnames -fdebug-types-section -Wl,--gdb-index -ftwo-level-line-tables"
+
+# This is needed otherwise dejagnu tries to rsh to host "fission".  Blech.
+# Double blech: set_board_info only sets the value if not already set.
+unset_board_info isremote
+set_board_info isremote 0
diff --git a/include/dwarf2.h b/include/dwarf2.h
index 52e39b2..048aa56 100644
--- a/include/dwarf2.h
+++ b/include/dwarf2.h
@@ -222,7 +222,13 @@ enum dwarf_line_number_ops
     /* DWARF 3.  */
     DW_LNS_set_prologue_end = 10,
     DW_LNS_set_epilogue_begin = 11,
-    DW_LNS_set_isa = 12
+    DW_LNS_set_isa = 12,
+    /* Experimental DWARF 5 extensions.
+       See http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables.  */
+    DW_LNS_set_address_from_logical = 13, /* Actuals table only.  */
+    DW_LNS_set_subprogram = 13,           /* Logicals table only.  */
+    DW_LNS_inlined_call = 14,             /* Logicals table only.  */
+    DW_LNS_pop_context = 15               /* Logicals table only.  */
   };
 
 /* Line number extended opcodes.  */
@@ -277,6 +283,11 @@ enum dwarf_line_number_content_type
     DW_LNCT_timestamp = 3,
     DW_LNCT_size = 4,
     DW_LNCT_MD5 = 5,
+    /* Experimental DWARF 5 extensions.
+       See http://wiki.dwarfstd.org/index.php?title=TwoLevelLineTables.  */
+    DW_LNCT_subprogram_name = 6,
+    DW_LNCT_decl_file = 7,
+    DW_LNCT_decl_line = 8,
     DW_LNCT_lo_user = 0x2000,
     DW_LNCT_hi_user = 0x3fff
   };


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]