Patchwork libbacktrace patch committed: Handle missing line number entry

login
register
mail settings
Submitter Ian Taylor
Date Jan. 17, 2013, 1:20 a.m.
Message ID <mcrd2x4ppup.fsf@google.com>
Download mbox | patch
Permalink /patch/213117/
State New
Headers show

Comments

Ian Taylor - Jan. 17, 2013, 1:20 a.m.
It turns out that it is possible to construct debug info in which a
compilation unit has a low_pc and a high_pc, and a line number table,
but the line number table does not cover PC values from low_pc up to
some value.  For example, this will happen in an assembler file if you
use a .cfi_startproc with no preceding .loc, and then provide some
instructions, and then use a .loc later in the function before the
.cfi_endproc.

In this admittedly obscure case the backtrace library was returning an
error "inconsistent DWARF line number info."  Since the case can occur
in real files, this is inappropriate.  This patch fixes libbacktrace to
return the main compilation file, if available, with no associated
function or line number.

I have not added a test case because I don't know how to create one
without using assembler code.  I did write a test case myself and
verified that it fixed the problem on my system.

Bootstrapped and ran libbacktrace testsuite on
x86_64-unknown-linux-gnu.  Committed to mainline.

Ian


2013-01-16  Ian Lance Taylor  <iant@google.com>

	* dwarf.c (struct unit): Add filename and abs_filename fields.
	(build_address_map): Set new fields when reading unit.
	(dwarf_lookup_pc): If we don't find an entry in the line table,
	just return the main file name.

Patch

Index: dwarf.c
===================================================================
--- dwarf.c	(revision 195256)
+++ dwarf.c	(working copy)
@@ -283,8 +283,12 @@  struct unit
   int addrsize;
   /* Offset into line number information.  */
   off_t lineoff;
+  /* Primary source file.  */
+  const char *filename;
   /* Compilation command working directory.  */
   const char *comp_dir;
+  /* Absolute file name, only set if needed.  */
+  const char *abs_filename;
   /* The abbreviations for this unit.  */
   struct abbrevs abbrevs;
 
@@ -1288,6 +1292,7 @@  build_address_map (struct backtrace_stat
       int have_ranges;
       uint64_t lineoff;
       int have_lineoff;
+      const char *filename;
       const char *comp_dir;
 
       if (info.reported_underflow)
@@ -1346,6 +1351,7 @@  build_address_map (struct backtrace_stat
       have_ranges = 0;
       lineoff = 0;
       have_lineoff = 0;
+      filename = NULL;
       comp_dir = NULL;
       for (i = 0; i < abbrev->num_attrs; ++i)
 	{
@@ -1394,6 +1400,10 @@  build_address_map (struct backtrace_stat
 		  have_lineoff = 1;
 		}
 	      break;
+	    case DW_AT_name:
+	      if (val.encoding == ATTR_VAL_STRING)
+		filename = val.u.string;
+	      break;
 	    case DW_AT_comp_dir:
 	      if (val.encoding == ATTR_VAL_STRING)
 		comp_dir = val.u.string;
@@ -1421,7 +1431,9 @@  build_address_map (struct backtrace_stat
 	  u->version = version;
 	  u->is_dwarf64 = is_dwarf64;
 	  u->addrsize = addrsize;
+	  u->filename = filename;
 	  u->comp_dir = comp_dir;
+	  u->abs_filename = NULL;
 	  u->lineoff = lineoff;
 	  u->abbrevs = abbrevs;
 	  memset (&abbrevs, 0, sizeof abbrevs);
@@ -2701,8 +2713,45 @@  dwarf_lookup_pc (struct backtrace_state
 				sizeof (struct line), line_search);
   if (ln == NULL)
     {
-      error_callback (data, "inconsistent DWARF line number info", 0);
-      return 0;
+      /* The PC is between the low_pc and high_pc attributes of the
+	 compilation unit, but no entry in the line table covers it.
+	 This implies that the start of the compilation unit has no
+	 line number information.  */
+
+      if (entry->u->abs_filename == NULL)
+	{
+	  const char *filename;
+
+	  filename = entry->u->filename;
+	  if (filename != NULL
+	      && !IS_ABSOLUTE_PATH (filename)
+	      && entry->u->comp_dir != NULL)
+	    {
+	      size_t filename_len;
+	      const char *dir;
+	      size_t dir_len;
+	      char *s;
+
+	      filename_len = strlen (filename);
+	      dir = entry->u->comp_dir;
+	      dir_len = strlen (dir);
+	      s = (char *) backtrace_alloc (state, dir_len + filename_len + 2,
+					    error_callback, data);
+	      if (s == NULL)
+		{
+		  *found = 0;
+		  return 0;
+		}
+	      memcpy (s, dir, dir_len);
+	      /* FIXME: Should use backslash if DOS file system.  */
+	      s[dir_len] = '/';
+	      memcpy (s + dir_len + 1, filename, filename_len + 1);
+	      filename = s;
+	    }
+	  entry->u->abs_filename = filename;
+	}
+
+      return callback (data, pc, entry->u->abs_filename, 0, NULL);
     }
 
   /* Search for function name within this unit.  */