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 <i...@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.
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. */