This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC 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]

GCC stack backtraces


I've spent the last couple of days working on a stack backtrace library.

It uses the GCC unwind interface to collect a stack trace, and parses
DWARF debug info to get file/line/function information.  (Of course it's
silly to write yet another DWARF reader, but I didn't find an existing
reader that seemed wholly suitable.)  The code currently only works for
ELF/DWARF, but it's designed to support other object file and debug
formats should anybody care to write them.  Since its use in GCC would
be purely for GCC developers, it's not essential that it be fully
portable.


I picked a random PR that causes an ICE.  Here is the final output of
g++ -c foo.cc:

foo.cc:6:6: internal compiler error: in cp_lexer_new_from_tokens, at cp/parser.c:638
0xed6e77 diagnostic_report_diagnostic(diagnostic_context*, diagnostic_info*)
	../../trunk/gcc/diagnostic.c:700
0xed7c2f internal_error(char const*, ...)
	../../trunk/gcc/diagnostic.c:1003
0xed6703 fancy_abort(char const*, int, char const*)
	../../trunk/gcc/diagnostic.c:1057
0x5bc3ee cp_lexer_new_from_tokens
	../../trunk/gcc/cp/parser.c:638
0x5bc3ee cp_parser_push_lexer_for_tokens
	../../trunk/gcc/cp/parser.c:3293
0x5c5510 cp_parser_late_parsing_for_member
	../../trunk/gcc/cp/parser.c:21741
0x5c5510 cp_parser_class_specifier_1
	../../trunk/gcc/cp/parser.c:18210
0x5c5510 cp_parser_class_specifier
	../../trunk/gcc/cp/parser.c:18234
0x5c5510 cp_parser_type_specifier
	../../trunk/gcc/cp/parser.c:13393
0x5dd56d cp_parser_decl_specifier_seq
	../../trunk/gcc/cp/parser.c:10734
0x5e00d7 cp_parser_single_declaration
	../../trunk/gcc/cp/parser.c:21344
0x5e1211 cp_parser_template_declaration_after_export
	../../trunk/gcc/cp/parser.c:21231
0x5ea9f9 cp_parser_declaration
	../../trunk/gcc/cp/parser.c:10186
0x5e9507 cp_parser_declaration_seq_opt
	../../trunk/gcc/cp/parser.c:10108
0x5eae92 cp_parser_translation_unit
	../../trunk/gcc/cp/parser.c:3760
0x5eae92 c_parse_file()
	../../trunk/gcc/cp/parser.c:27585
0x6ed0c4 c_common_parse_file()
	../../trunk/gcc/c-family/c-opts.c:1137
0xa2017b compile_file
	../../trunk/gcc/toplev.c:546
0xa21d89 do_compile
	../../trunk/gcc/toplev.c:1863
0xa21d89 toplev_main(int, char**)
	../../trunk/gcc/toplev.c:1939
0x7fb0a50d8c4c ???
	???:0
0x4c6db8 ???
	???:0
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://gcc.gnu.org/bugs.html> for instructions.


So, big deal.  You can get a stack backtrace from gdb anyhow.

For a more interesting use I hacked ggc-common.c to collect detailed
statistics when used with -fmem-report.  This gives us data similar to,
and better than, that you get when you configure GCC with
--enable-gather-detailed-mem-stats.  However, with this approach you do
not need to recompile GCC.  Running this modified GCC with -fmem-report
-O2 -g combine.ii took 36 seconds.  The full report is too large to
attach here, but here are the last few entries of the ggc-common.c.
Note that I spent exactly 0 seconds making the report pretty.

0xbf1151 ../../trunk/gcc/tree.c:7607 build_function_type(tree_node*, tree_node*)
0x50f1c0 ../../trunk/gcc/cp/decl.c:9346 grokdeclarator(cp_declarator const*, cp_decl_specifier_seq const*, decl_context, int, tree_node**)
0x514a75 ../../trunk/gcc/cp/decl.c:4366 start_decl(cp_declarator const*, cp_decl_specifier_seq*, int, tree_node*, tree_node*, tree_node**)
0x5dbeca ../../trunk/gcc/cp/parser.c:15720 cp_parser_init_declarator
0x5de4ba ../../trunk/gcc/cp/parser.c:10445 cp_parser_simple_declaration
0x5e6337 ../../trunk/gcc/cp/parser.c:10326 cp_parser_block_declaration
0x5ea8fb ../../trunk/gcc/cp/parser.c:10222 cp_parser_declaration
0x5e9507 ../../trunk/gcc/cp/parser.c:10108 cp_parser_declaration_seq_opt
0x5eae92 ../../trunk/gcc/cp/parser.c:3760 cp_parser_translation_unit
0x5eae92 ../../trunk/gcc/cp/parser.c:27585 c_parse_file()
0x6ed0c4 ../../trunk/gcc/c-family/c-opts.c:1137 c_common_parse_file()
0xa2017b ../../trunk/gcc/toplev.c:546 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
    611352: 1.0%          0: 0.0%     181776: 0.6%          0: 0.0%       4721
0xbf342f ../../trunk/gcc/tree.c:1224 build_int_cst_wide(tree_node*, unsigned long, long)
0xbf37a5 ../../trunk/gcc/tree.c:1065 double_int_to_tree(tree_node*, double_int)
0xbf37ea ../../trunk/gcc/tree.c:1042 build_int_cst(tree_node*, long)
0xa19fee ../../trunk/gcc/stor-layout.c:2545 set_min_and_max_values_for_integral_type(tree_node*, int, bool)
0x502788 ../../trunk/gcc/cp/decl.c:12345 finish_enum_value_list(tree_node*)
0x5d53d4 ../../trunk/gcc/cp/parser.c:14609 cp_parser_enum_specifier
0x5c4c1d ../../trunk/gcc/cp/parser.c:13365 cp_parser_type_specifier
0x5dd56d ../../trunk/gcc/cp/parser.c:10734 cp_parser_decl_specifier_seq
0x5de309 ../../trunk/gcc/cp/parser.c:10377 cp_parser_simple_declaration
0x5e6337 ../../trunk/gcc/cp/parser.c:10326 cp_parser_block_declaration
0x5ea8fb ../../trunk/gcc/cp/parser.c:10222 cp_parser_declaration
0x5e9507 ../../trunk/gcc/cp/parser.c:10108 cp_parser_declaration_seq_opt
0x5eae92 ../../trunk/gcc/cp/parser.c:3760 cp_parser_translation_unit
0x5eae92 ../../trunk/gcc/cp/parser.c:27585 c_parse_file()
0x6ed0c4 ../../trunk/gcc/c-family/c-opts.c:1137 c_common_parse_file()
0xa2017b ../../trunk/gcc/toplev.c:546 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
         0: 0.0%          0: 0.0%     837344: 2.8%     276192:16.2%        137
0xc147e7 ../../trunk/gcc/var-tracking.c:8568 emit_note_insn_var_location
0xf18f08 ../../trunk/libiberty/hashtab.c:784 htab_traverse
0xc22ea7 ../../trunk/gcc/var-tracking.c:8756 emit_notes_for_changes
0xc23054 ../../trunk/gcc/var-tracking.c:8870 emit_notes_for_differences
0xc23054 ../../trunk/gcc/var-tracking.c:9245 vt_emit_notes
0xc23b49 ../../trunk/gcc/var-tracking.c:10048 variable_tracking_main_1
0xc23b49 ../../trunk/gcc/var-tracking.c:10062 variable_tracking_main()
0x97de5e ../../trunk/gcc/passes.c:2157 execute_one_pass(opt_pass*)
0x97e214 ../../trunk/gcc/passes.c:2212 execute_pass_list(opt_pass*)
0x97e226 ../../trunk/gcc/passes.c:2213 execute_pass_list(opt_pass*)
0x97e226 ../../trunk/gcc/passes.c:2213 execute_pass_list(opt_pass*)
0x7782d7 ../../trunk/gcc/cgraphunit.c:1609 expand_function
0x77a179 ../../trunk/gcc/cgraphunit.c:1714 expand_all_functions
0x77a179 ../../trunk/gcc/cgraphunit.c:2012 compile()
0x77a774 ../../trunk/gcc/cgraphunit.c:2089 finalize_compilation_unit()
0x5aa0ba ../../trunk/gcc/cp/decl2.c:4024 cp_write_global_declarations()
0xa201c4 ../../trunk/gcc/toplev.c:560 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
    872704: 1.5%          0: 0.0%          0: 0.0%          0: 0.0%      13636
0xbf03e8 ../../trunk/gcc/tree.c:4093 build_decl_stat(unsigned int, tree_code, tree_node*, tree_node*)
0x5bb085 ../../trunk/gcc/cp/lex.c:532 build_lang_decl_loc(unsigned int, tree_code, tree_node*, tree_node*)
0x516729 ../../trunk/gcc/cp/decl.c:7175 grokfndecl
0x5129fd ../../trunk/gcc/cp/decl.c:10394 grokdeclarator(cp_declarator const*, cp_decl_specifier_seq const*, decl_context, int, tree_node**)
0x514a75 ../../trunk/gcc/cp/decl.c:4366 start_decl(cp_declarator const*, cp_decl_specifier_seq*, int, tree_node*, tree_node*, tree_node**)
0x5dbeca ../../trunk/gcc/cp/parser.c:15720 cp_parser_init_declarator
0x5de4ba ../../trunk/gcc/cp/parser.c:10445 cp_parser_simple_declaration
0x5e6337 ../../trunk/gcc/cp/parser.c:10326 cp_parser_block_declaration
0x5ea8fb ../../trunk/gcc/cp/parser.c:10222 cp_parser_declaration
0x5e9507 ../../trunk/gcc/cp/parser.c:10108 cp_parser_declaration_seq_opt
0x5eae92 ../../trunk/gcc/cp/parser.c:3760 cp_parser_translation_unit
0x5eae92 ../../trunk/gcc/cp/parser.c:27585 c_parse_file()
0x6ed0c4 ../../trunk/gcc/c-family/c-opts.c:1137 c_common_parse_file()
0xa2017b ../../trunk/gcc/toplev.c:546 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
         0: 0.0%        864: 0.0%    1348128: 4.5%     149888: 8.8%       4684
0xb86a21 ../../trunk/gcc/tree-ssanames.c:140 make_ssa_name_fn(function*, tree_node*, gimple_statement_d*)
0xa7a3c1 ../../trunk/gcc/tree-flow-inline.h:1144 make_ssa_name
0xa7a3c1 ../../trunk/gcc/tree-into-ssa.c:1396 rewrite_stmt
0xa7a3c1 ../../trunk/gcc/tree-into-ssa.c:1471 rewrite_enter_block
0xe275fa ../../trunk/gcc/domwalk.c:188 walk_dominator_tree(dom_walk_data*, basic_block_def*)
0xa7bc70 ../../trunk/gcc/tree-into-ssa.c:2265 rewrite_blocks
0xa7bf97 ../../trunk/gcc/tree-into-ssa.c:2432 rewrite_into_ssa
0x97de5e ../../trunk/gcc/passes.c:2157 execute_one_pass(opt_pass*)
0x97e214 ../../trunk/gcc/passes.c:2212 execute_pass_list(opt_pass*)
0x97d37c ../../trunk/gcc/passes.c:1702 do_per_function_toporder(void (*)(void*), void*)
0x97e6ab ../../trunk/gcc/passes.c:2526 execute_ipa_pass_list(opt_pass*)
0x779fe1 ../../trunk/gcc/cgraphunit.c:1838 ipa_passes
0x779fe1 ../../trunk/gcc/cgraphunit.c:1961 compile()
0x77a774 ../../trunk/gcc/cgraphunit.c:2089 finalize_compilation_unit()
0x5aa0ba ../../trunk/gcc/cp/decl2.c:4024 cp_write_global_declarations()
0xa201c4 ../../trunk/gcc/toplev.c:560 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
   1005760: 1.7%          0: 0.0%     359680: 1.2%          0: 0.0%      17068
0xa1ca7d ../../trunk/gcc/stringpool.c:75 alloc_node
0xef2dc7 ../../trunk/libcpp/symtab.c:158 ht_lookup_with_hash(ht*, unsigned char const*, unsigned long, unsigned int, ht_lookup_option)
0xee88de ../../trunk/libcpp/lex.c:1232 lex_identifier
0xeeadcf ../../trunk/libcpp/lex.c:2205 _cpp_lex_direct
0xeeb96b ../../trunk/libcpp/lex.c:1974 _cpp_lex_token
0xeefce7 ../../trunk/libcpp/macro.c:2357 cpp_get_token_1
0x6e6622 ../../trunk/gcc/c-family/c-lex.c:299 c_lex_with_flags(tree_node**, unsigned int*, unsigned char*, int)
0x5bf66f ../../trunk/gcc/cp/parser.c:719 cp_lexer_get_preprocessor_token
0x5ead40 ../../trunk/gcc/cp/parser.c:598 cp_lexer_new_main
0x5ead40 ../../trunk/gcc/cp/parser.c:3216 cp_parser_new
0x5ead40 ../../trunk/gcc/cp/parser.c:27582 c_parse_file()
0x6ed0c4 ../../trunk/gcc/c-family/c-opts.c:1137 c_common_parse_file()
0xa2017b ../../trunk/gcc/toplev.c:546 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
     16016: 0.0%          0: 0.0%    1564728: 5.2%          0: 0.0%      17963
0xbf03e8 ../../trunk/gcc/tree.c:4093 build_decl_stat(unsigned int, tree_code, tree_node*, tree_node*)
0x59de96 ../../trunk/gcc/cp/decl2.c:181 cp_build_parm_decl(tree_node*, tree_node*)
0x5133de ../../trunk/gcc/cp/decl.c:10054 grokdeclarator(cp_declarator const*, cp_decl_specifier_seq const*, decl_context, int, tree_node**)
0x5defbc ../../trunk/gcc/cp/parser.c:17116 cp_parser_parameter_declaration_list
0x5df444 ../../trunk/gcc/cp/parser.c:17030 cp_parser_parameter_declaration_clause
0x5d6aee ../../trunk/gcc/cp/parser.c:16090 cp_parser_direct_declarator
0x5d6aee ../../trunk/gcc/cp/parser.c:15961 cp_parser_declarator
0x5dbb67 ../../trunk/gcc/cp/parser.c:15537 cp_parser_init_declarator
0x5de4ba ../../trunk/gcc/cp/parser.c:10445 cp_parser_simple_declaration
0x5e6337 ../../trunk/gcc/cp/parser.c:10326 cp_parser_block_declaration
0x5ea8fb ../../trunk/gcc/cp/parser.c:10222 cp_parser_declaration
0x5e9507 ../../trunk/gcc/cp/parser.c:10108 cp_parser_declaration_seq_opt
0x5eae92 ../../trunk/gcc/cp/parser.c:3760 cp_parser_translation_unit
0x5eae92 ../../trunk/gcc/cp/parser.c:27585 c_parse_file()
0x6ed0c4 ../../trunk/gcc/c-family/c-opts.c:1137 c_common_parse_file()
0xa2017b ../../trunk/gcc/toplev.c:546 compile_file
0xa21d89 ../../trunk/gcc/toplev.c:1863 do_compile
0xa21d89 ../../trunk/gcc/toplev.c:1939 toplev_main(int, char**)
0x7f4044f36c4c ???:0 ???
0x4c6db8 ???:0 ???
     32640: 0.1%          0: 0.0%    1555328: 5.2%          0: 0.0%      12406
Total                                              59553548         38936868         29879567          1701879          1921781
    Garbage            Freed             Leak         Overhead            Times



As you can see, this gives us the opportunity for some fairly detailed
analysis of where we are allocating memory, without requiring the
compiler to be recompiled, and without taking an inordinate amount of
time.


I expect to use this code not just for GCC proper, but also for libgo
(currently libgo uses Go code to parse DWARF, but that is not very
satisfactory as that code is only available if it has been imported into
the program).  So I put it under a BSD license, although that is open
for discussion.  Also in case it finds more uses elsewhere I wrote it in
reasonably portable C rather than C++.


I've attached two patches.  The first is the patch for the libbacktrace
library, plus the changes to the top level configure.ac and
Makefile.def.  The second is the changes to GCC proper that I used to
generate the above dumps.  The second patch is just quick and dirty work
to see if the library could work well.


Does this seem like something we could usefully add to GCC?  Does
anybody see any big problems with it?

The library is not quite ready to commit, but it's quite close.  It just
needs a bit more testing in uncommon build environments, i.e., anything
other than x86_64 GNU/Linux, to make sure that it at least builds and
does not crash even when it can't get a backtrace.

Ian


Index: libbacktrace/unknown.c
===================================================================
--- libbacktrace/unknown.c	(revision 0)
+++ libbacktrace/unknown.c	(revision 0)
@@ -0,0 +1,64 @@
+/* unknown.c -- used when backtrace configury does not know file format.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* A trivial routine that always fails to find fileline data.  */
+
+static int
+unknown_fileline (void *fileline_data ATTRIBUTE_UNUSED,
+		  uintptr_t pc, backtrace_callback callback,
+		  backtrace_error_callback ATTRIBUTE_UNUSED,
+		  void *data)
+
+{
+  return callback (data, pc, NULL, 0, NULL);
+}
+
+/* Initialize the backtrace data when we don't know how to read the
+   debug info.  */
+
+int
+backtrace_initialize (int descriptor ATTRIBUTE_UNUSED,
+		      backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+		      void *data ATTRIBUTE_UNUSED, fileline *fileline_fn,
+		      void **fileline_data)
+{
+  *fileline_fn = unknown_fileline;
+  *fileline_data = NULL;
+  return 1;
+}
Index: libbacktrace/posix.c
===================================================================
--- libbacktrace/posix.c	(revision 0)
+++ libbacktrace/posix.c	(revision 0)
@@ -0,0 +1,117 @@
+/* posix.c -- POSIX file I/O routines for the backtrace library.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 1
+#endif
+
+/* Open a file for reading.  */
+
+int
+backtrace_open (const char *filename, backtrace_error_callback error_callback,
+		void *data)
+{
+  int descriptor;
+
+  descriptor = open (filename, O_RDONLY | O_CLOEXEC);
+  if (descriptor < 0)
+    {
+      error_callback (data, filename, errno);
+      return -1;
+    }
+  if (O_CLOEXEC == 0)
+    {
+      /* It doesn't matter if this fails for some reason.  */
+      fcntl (descriptor, F_SETFD, FD_CLOEXEC);
+    }
+  return descriptor;
+}
+
+/* Read SIZE bytes from DESCRIPTOR at OFFSET.  */
+
+int
+backtrace_read (int descriptor, off_t offset, void *buffer,
+		size_t size, backtrace_error_callback error_callback,
+		void *data)
+{
+  ssize_t got;
+
+  if (lseek (descriptor, offset, SEEK_SET) < 0)
+    {
+      error_callback (data, "lseek", errno);
+      return 0;
+    }
+
+  got = read (descriptor, buffer, size);
+  if (got < 0)
+    {
+      error_callback (data, "read", errno);
+      return 0;
+    }
+
+  if ((size_t) got < size)
+    {
+      error_callback (data, "file too short", 0);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Close DESCRIPTOR.  */
+
+int
+backtrace_close (int descriptor, backtrace_error_callback error_callback,
+		 void *data)
+{
+  if (close (descriptor) < 0)
+    {
+      error_callback (data, "close", errno);
+      return 0;
+    }
+  return 1;
+}
Index: libbacktrace/dwarf.c
===================================================================
--- libbacktrace/dwarf.c	(revision 0)
+++ libbacktrace/dwarf.c	(revision 0)
@@ -0,0 +1,2746 @@
+/* dwarf.c -- Get file/line information from DWARF for backtraces.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "dwarf2.h"
+#include "filenames.h"
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* A buffer to read DWARF info.  */
+
+struct dwarf_buf
+{
+  /* Buffer name for error messages.  */
+  const char *name;
+  /* Start of the buffer.  */
+  const unsigned char *start;
+  /* Next byte to read.  */
+  const unsigned char *buf;
+  /* The number of bytes remaining.  */
+  size_t left;
+  /* Whether the data is big-endian.  */
+  int is_bigendian;
+  /* Error callback routine.  */
+  backtrace_error_callback error_callback;
+  /* Data for error_callback.  */
+  void *data;
+  /* Non-zero if we've reported an underflow error.  */
+  int reported_underflow;
+};
+
+/* A single attribute in a DWARF abbreviation.  */
+
+struct attr
+{
+  /* The attribute name.  */
+  enum dwarf_attribute name;
+  /* The attribute form.  */
+  enum dwarf_form form;
+};
+
+/* A single DWARF abbreviation.  */
+
+struct abbrev
+{
+  /* The entry tag.  */
+  enum dwarf_tag tag;
+  /* Non-zero if this abbrev has child entries.  */
+  int has_children;
+  /* The number of attributes.  */
+  size_t num_attrs;
+  /* The attributes.  */
+  struct attr *attrs;
+};
+
+/* A list mapping abbrev code to abbrev entries.  */
+
+struct abbrev_list
+{
+  /* The next entry in the list.  */
+  struct abbrev_list *next;
+  /* The abbrev code--the number used to refer to the abbrev.  */
+  unsigned int code;
+  /* The abbrev info.  */
+  struct abbrev abbrev;
+};
+
+/* The DWARF abbreviations for a compilation unit.  This structure
+   only exists while reading the compilation unit.  Most DWARF readers
+   seem to a hash table to map abbrev ID's to abbrev entries.
+   However, we primarily care about GCC, and GCC simply issues ID's in
+   numerical order starting at 1.  So we just keep a vector, with a
+   linked list for odd cases.  */
+
+struct abbrevs
+{
+  /* The number of abbrevs in the vector.  */
+  size_t num_abbrevs;
+  /* The vector of abbrevs.  This is indexed by the abbrev code - 1,
+     so that element 0 is abbrev code 1.  */
+  struct abbrev *abbrevs;
+  /* A list of abbrev whose codes do not fit neatly into the
+     vector.  */
+  struct abbrev_list *list;
+};
+
+/* The different kinds of attribute values.  */
+
+enum attr_val_encoding
+{
+  /* A unsigned integer.  */
+  ATTR_VAL_UINT,
+  /* A sigd integer.  */
+  ATTR_VAL_SINT,
+  /* A string.  */
+  ATTR_VAL_STRING,
+  /* An offset to other data in the containing unit.  */
+  ATTR_VAL_REF_UNIT,
+  /* An offset to other data within the .dwarf_info section.  */
+  ATTR_VAL_REF_INFO,
+  /* An offset to data in some other section.  */
+  ATTR_VAL_REF_SECTION,
+  /* A type signature.  */
+  ATTR_VAL_REF_TYPE,
+  /* A block of data (not represented).  */
+  ATTR_VAL_BLOCK,
+  /* An expression (not represented).  */
+  ATTR_VAL_EXPR,
+};
+
+/* An attribute value.  */
+
+struct attr_val
+{
+  /* How the value is stored in the field u.  */
+  enum attr_val_encoding encoding;
+  union
+  {
+    /* ATTR_VAL_UINT, ATTR_VAL_REF*.  */
+    uint64_t uint;
+    /* ATTR_VAL_SINT.  */
+    int64_t sint;
+    /* ATTR_VAL_STRING.  */
+    const char *string;
+    /* ATTR_VAL_BLOCK not stored.  */
+  } u;
+};
+
+/* The line number program header.  */
+
+struct line_header
+{
+  /* The version of the line number information.  */
+  int version;
+  /* The minimum instruction length.  */
+  unsigned int min_insn_len;
+  /* The maximum number of ops per instruction.  */
+  unsigned int max_ops_per_insn;
+  /* The line base for special opcodes.  */
+  int line_base;
+  /* The line range for special opcodes.  */
+  unsigned int line_range;
+  /* The opcode base--the first special opcode.  */
+  unsigned int opcode_base;
+  /* Opcode lengths, indexed by opcode - 1.  */
+  const unsigned char *opcode_lengths;
+  /* The number of directory entries.  */
+  size_t dirs_count;
+  /* The directory entries.  */
+  const char **dirs;
+  /* The number of filenames.  */
+  size_t filenames_count;
+  /* The filenames.  */
+  char **filenames;
+};
+
+/* Map a single PC value to a file/line.  We will keep a vector of
+   these sorted by PC value.  Each file/line will be correct from the
+   PC up to the PC of the next entry if there is one.  We allocate one
+   extra entry at the end so that we can use bsearch.  */
+
+struct line
+{
+  /* PC.  */
+  uintptr_t pc;
+  /* File name.  Many entries in the array are expected to point to
+     the same file name.  */
+  const char *filename;
+  /* Line number.  */
+  int lineno;
+};
+
+/* A vector of line number information.  This is used while reading
+   the line numbers.  */
+
+struct line_vector
+{
+  /* The line number mappings.  */
+  struct line *lines;
+  /* Number of valid mappings.  */
+  size_t count;
+  /* Number allocated.  */
+  size_t alc;
+};
+
+/* A function described in the debug info.  */
+
+struct function
+{
+  /* The name of the function.  */
+  const char *name;
+  /* If this is an inlined function, the filename of the call
+     site.  */
+  const char *caller_filename;
+  /* If this is an inlined function, the line number of the call
+     site.  */
+  int caller_lineno;
+  /* Map PC ranges to inlined functions.  */
+  struct function_addrs *function_addrs;
+  size_t function_addrs_count;
+};
+
+/* An address range for a function.  This maps a PC value to a
+   specific function.  */
+
+struct function_addrs
+{
+  /* Range is LOW <= PC < HIGH.  */
+  uint64_t low;
+  uint64_t high;
+  /* Function for this address range.  */
+  struct function *function;
+};
+
+/* A growable vector of function address ranges.  */
+
+struct function_vector
+{
+  /* Address ranges.  */
+  struct function_addrs *addrs;
+  /* Number of address ranges present.  */
+  size_t count;
+  /* Number of address ranges allocated.  */
+  size_t alc;
+};
+
+/* A DWARF compilation unit.  This only holds the information we need
+   to map a PC to a file and line.  */
+
+struct unit
+{
+  /* The first entry for this compilation unit.  */
+  const unsigned char *unit_data;
+  /* The length of the data for this compilation unit.  */
+  size_t unit_data_len;
+  /* The offset of UNIT_DATA from the start of the information for
+     this compilation unit.  */
+  size_t unit_data_offset;
+  /* DWARF version.  */
+  int version;
+  /* Whether unit is DWARF64.  */
+  int is_dwarf64;
+  /* Address size.  */
+  int addrsize;
+  /* Offset into line number information.  */
+  off_t lineoff;
+  /* Compilation command working directory.  */
+  char *comp_dir;
+  /* The abbreviations for this unit.  */
+  struct abbrevs *abbrevs;
+  /* PC to line number mapping.  */
+  struct line *lines;
+  /* Number of entries in lines.  This will be set to -1 if there is
+     some problem reading the line number information.  */
+  size_t lines_count;
+  /* PC ranges to function.  */
+  struct function_addrs *function_addrs;
+  size_t function_addrs_count;
+};
+
+/* An address range for a compilation unit.  This maps a PC value to a
+   specific compilation unit.  Note that we invert the representation
+   in DWARF: instead of listing the units and attaching a list of
+   ranges, we list the ranges and have each one point to the unit.
+   This lets us do a binary search to find the unit.  */
+
+struct unit_addrs
+{
+  /* Range is LOW <= PC < HIGH.  */
+  uint64_t low;
+  uint64_t high;
+  /* Compilation unit for this address range.  */
+  struct unit *u;
+};
+
+/* A growable vector of compilation unit address ranges.  */
+
+struct unit_addrs_vector
+{
+  /* Address ranges.  */
+  struct unit_addrs *addrs;
+  /* Number of address ranges present.  */
+  size_t count;
+  /* Number of address ranges allocated.  */
+  size_t alc;
+};
+
+/* The information we need to map a PC to a file and line.  */
+
+struct dwarf_data
+{
+  /* A sorted list of address ranges.  */
+  struct unit_addrs *addrs;
+  /* Number of address ranges in list.  */
+  size_t addrs_count;
+  /* The unparsed .debug_info section.  */
+  unsigned char *dwarf_info;
+  size_t dwarf_info_size;
+  /* The unparsed .debug_line section.  */
+  unsigned char *dwarf_line;
+  size_t dwarf_line_size;
+  /* The unparsed .debug_ranges section.  */
+  unsigned char *dwarf_ranges;
+  size_t dwarf_ranges_size;
+  /* The unparsed .debug_str section.  */
+  unsigned char *dwarf_str;
+  size_t dwarf_str_size;
+  /* Whether the data is big-endian or not.  */
+  int is_bigendian;
+};
+
+/* Report an error for a DWARF buffer.  */
+
+static void
+dwarf_buf_error (struct dwarf_buf *buf, const char *msg)
+{
+  char b[200];
+
+  snprintf (b, sizeof b, "%s in %s at %d",
+	    msg, buf->name, (int) (buf->buf - buf->start));
+  buf->error_callback (buf->data, b, 0);
+}
+
+/* Require at least COUNT bytes in BUF.  Return 1 if all is well, 0 on
+   error.  */
+
+static int
+require (struct dwarf_buf *buf, size_t count)
+{
+  if (buf->left >= count)
+    return 1;
+
+  if (!buf->reported_underflow)
+    {
+      dwarf_buf_error (buf, "DWARF underflow");
+      buf->reported_underflow = 1;
+    }
+
+  return 0;
+}
+
+/* Advance COUNT bytes in BUF.  Return 1 if all is well, 0 on
+   error.  */
+
+static int
+advance (struct dwarf_buf *buf, size_t count)
+{
+  if (!require (buf, count))
+    return 0;
+  buf->buf += count;
+  buf->left -= count;
+  return 1;
+}
+
+/* Read one byte from BUF and advance 1 byte.  */
+
+static unsigned char
+read_byte (struct dwarf_buf *buf)
+{
+  const unsigned char *p = buf->buf;
+
+  if (!advance (buf, 1))
+    return 0;
+  return p[0];
+}
+
+/* Read a signed char from BUF and advance 1 byte.  */
+
+static signed char
+read_sbyte (struct dwarf_buf *buf)
+{
+  const unsigned char *p = buf->buf;
+
+  if (!advance (buf, 1))
+    return 0;
+  return (*p ^ 0x80) - 0x80;
+}
+
+/* Read a uint16 from BUF and advance 2 bytes.  */
+
+static uint16_t
+read_uint16 (struct dwarf_buf *buf)
+{
+  const unsigned char *p = buf->buf;
+
+  if (!advance (buf, 2))
+    return 0;
+  if (buf->is_bigendian)
+    return ((uint16_t) p[0] << 8) | (uint16_t) p[1];
+  else
+    return ((uint16_t) p[1] << 8) | (uint16_t) p[0];
+}
+
+/* Read a uint32 from BUF and advance 4 bytes.  */
+
+static uint32_t
+read_uint32 (struct dwarf_buf *buf)
+{
+  const unsigned char *p = buf->buf;
+
+  if (!advance (buf, 4))
+    return 0;
+  if (buf->is_bigendian)
+    return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16)
+	    | ((uint32_t) p[2] << 8) | (uint32_t) p[3]);
+  else
+    return (((uint32_t) p[3] << 24) | ((uint32_t) p[2] << 16)
+	    | ((uint32_t) p[1] << 8) | (uint32_t) p[0]);
+}
+
+/* Read a uint64 from BUF and advance 8 bytes.  */
+
+static uint64_t
+read_uint64 (struct dwarf_buf *buf)
+{
+  const unsigned char *p = buf->buf;
+
+  if (!advance (buf, 8))
+    return 0;
+  if (buf->is_bigendian)
+    return (((uint64_t) p[0] << 56) | ((uint64_t) p[1] << 48)
+	    | ((uint64_t) p[2] << 40) | ((uint64_t) p[3] << 32)
+	    | ((uint64_t) p[4] << 24) | ((uint64_t) p[5] << 16)
+	    | ((uint64_t) p[6] << 8) | (uint64_t) p[7]);
+  else
+    return (((uint64_t) p[7] << 56) | ((uint64_t) p[6] << 48)
+	    | ((uint64_t) p[5] << 40) | ((uint64_t) p[4] << 32)
+	    | ((uint64_t) p[3] << 24) | ((uint64_t) p[2] << 16)
+	    | ((uint64_t) p[1] << 8) | (uint64_t) p[0]);
+}
+
+/* Read an offset from BUF and advance the appropriate number of
+   bytes.  */
+
+static uint64_t
+read_offset (struct dwarf_buf *buf, int is_dwarf64)
+{
+  if (is_dwarf64)
+    return read_uint64 (buf);
+  else
+    return read_uint32 (buf);
+}
+
+/* Read an address from BUF and advance the appropriate number of
+   bytes.  */
+
+static uint64_t
+read_address (struct dwarf_buf *buf, int addrsize)
+{
+  switch (addrsize)
+    {
+    case 1:
+      return read_byte (buf);
+    case 2:
+      return read_uint16 (buf);
+    case 4:
+      return read_uint32 (buf);
+    case 8:
+      return read_uint64 (buf);
+    default:
+      dwarf_buf_error (buf, "unrecognized address size");
+      return 0;
+    }
+}
+
+/* Return whether a value is the highest possible address, given the
+   address size.  */
+
+static int
+is_highest_address (uint64_t address, int addrsize)
+{
+  switch (addrsize)
+    {
+    case 1:
+      return address == (unsigned char) -1;
+    case 2:
+      return address == (uint16_t) -1;
+    case 4:
+      return address == (uint32_t) -1;
+    case 8:
+      return address == (uint64_t) -1;
+    default:
+      return 0;
+    }
+}
+
+/* Read an unsigned LEB128 number.  */
+
+static uint64_t
+read_uleb128 (struct dwarf_buf *buf)
+{
+  uint64_t ret;
+  unsigned int shift;
+  unsigned char b;
+
+  ret = 0;
+  shift = 0;
+  do
+    {
+      const unsigned char *p;
+
+      p = buf->buf;
+      if (!advance (buf, 1))
+	return 0;
+      b = *p;
+      ret |= ((uint64_t) (b & 0x7f)) << shift;
+      shift += 7;
+    }
+  while ((b & 0x80) != 0);
+
+  if (shift > 64)
+    dwarf_buf_error (buf, "LEB128 overflows uint64_5");
+
+  return ret;
+}
+
+/* Read a signed LEB128 number.  */
+
+static int64_t
+read_sleb128 (struct dwarf_buf *buf)
+{
+  uint64_t val;
+  unsigned int shift;
+  unsigned char b;
+
+  val = 0;
+  shift = 0;
+  do
+    {
+      const unsigned char *p;
+
+      p = buf->buf;
+      if (!advance (buf, 1))
+	return 0;
+      b = *p;
+      val |= ((uint64_t) (b & 0x7f)) << shift;
+      shift += 7;
+    }
+  while ((b & 0x80) != 0);
+
+  if (shift > 64)
+    dwarf_buf_error (buf, "signed LEB128 overflows uint64_t");
+
+  if ((b & 0x40) != 0)
+    val |= ((uint64_t) -1) << shift;
+
+  return (int64_t) val;
+}
+
+/* Return the length of an LEB128 number.  */
+
+static size_t
+leb128_len (const unsigned char *p)
+{
+  size_t ret;
+
+  ret = 1;
+  while ((*p & 0x80) != 0)
+    {
+      ++p;
+      ++ret;
+    }
+  return ret;
+}
+
+/* Free an abbreviations vector.  */
+
+static void
+free_abbrevs_vector (struct abbrev *abbrevs, size_t num_abbrevs)
+{
+  size_t i;
+
+  for (i = 0; i < num_abbrevs; ++i)
+    free (abbrevs[i].attrs);
+  free (abbrevs);
+}
+
+/* Free an abbreviations list.  */
+
+static void
+free_abbrev_list (struct abbrev_list *list)
+{
+  while (list != NULL)
+    {
+      struct abbrev_list *next;
+
+      next = list->next;
+      free (list->abbrev.attrs);
+      free (list);
+      list = next;
+    }
+}
+
+/* Free an abbreviations structure.  */
+
+static void
+free_abbrevs (struct abbrevs *abbrevs)
+{
+  if (abbrevs != NULL)
+    {
+      free_abbrevs_vector (abbrevs->abbrevs, abbrevs->num_abbrevs);
+      free_abbrev_list (abbrevs->list);
+    }
+}
+
+/* Read an attribute value.  Returns 1 on success, 0 on failure.  If
+   the value can be represented as a uint64_t, sets *VAL and sets
+   *IS_VALID to 1.  We don't try to store the value of other attribute
+   forms, because we don't care about them.  */
+
+static int
+read_attribute (enum dwarf_form form, struct dwarf_buf *buf,
+		int is_dwarf64, int version, int addrsize,
+		const unsigned char *dwarf_str, size_t dwarf_str_size,
+		struct attr_val *val)
+{
+  switch (form)
+    {
+    case DW_FORM_addr:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_address (buf, addrsize);
+      return 1;
+    case DW_FORM_block2:
+      val->encoding = ATTR_VAL_BLOCK;
+      return advance (buf, read_uint16 (buf));
+    case DW_FORM_block4:
+      val->encoding = ATTR_VAL_BLOCK;
+      return advance (buf, read_uint32 (buf));
+    case DW_FORM_data2:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_uint16 (buf);
+      return 1;
+    case DW_FORM_data4:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_uint32 (buf);
+      return 1;
+    case DW_FORM_data8:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_uint64 (buf);
+      return 1;
+    case DW_FORM_string:
+      val->encoding = ATTR_VAL_STRING;
+      val->u.string = (const char *) buf->buf;
+      return advance (buf, strnlen ((const char *) buf->buf, buf->left) + 1);
+    case DW_FORM_block:
+      val->encoding = ATTR_VAL_BLOCK;
+      return advance (buf, read_uleb128 (buf));
+    case DW_FORM_block1:
+      val->encoding = ATTR_VAL_BLOCK;
+      return advance (buf, read_byte (buf));
+    case DW_FORM_data1:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_byte (buf);
+      return 1;
+    case DW_FORM_flag:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_byte (buf);
+      return 1;
+    case DW_FORM_sdata:
+      val->encoding = ATTR_VAL_SINT;
+      val->u.sint = read_sleb128 (buf);
+      return 1;
+    case DW_FORM_strp:
+      {
+	uint64_t offset;
+
+	offset = read_offset (buf, is_dwarf64);
+	if (offset >= dwarf_str_size)
+	  {
+	    dwarf_buf_error (buf, "DW_FORM_strp out of range");
+	    return 0;
+	  }
+	val->encoding = ATTR_VAL_STRING;
+	val->u.string = (const char *) dwarf_str + offset;
+	return 1;
+      }
+    case DW_FORM_udata:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = read_uleb128 (buf);
+      return 1;
+    case DW_FORM_ref_addr:
+      val->encoding = ATTR_VAL_REF_INFO;
+      if (version == 2)
+	val->u.uint = read_address (buf, addrsize);
+      else
+	val->u.uint = read_offset (buf, is_dwarf64);
+      return 1;
+    case DW_FORM_ref1:
+      val->encoding = ATTR_VAL_REF_UNIT;
+      val->u.uint = read_byte (buf);
+      return 1;
+    case DW_FORM_ref2:
+      val->encoding = ATTR_VAL_REF_UNIT;
+      val->u.uint = read_uint16 (buf);
+      return 1;
+    case DW_FORM_ref4:
+      val->encoding = ATTR_VAL_REF_UNIT;
+      val->u.uint = read_uint32 (buf);
+      return 1;
+    case DW_FORM_ref8:
+      val->encoding = ATTR_VAL_REF_UNIT;
+      val->u.uint = read_uint64 (buf);
+      return 1;
+    case DW_FORM_ref_udata:
+      val->encoding = ATTR_VAL_REF_UNIT;
+      val->u.uint = read_uleb128 (buf);
+      return 1;
+    case DW_FORM_indirect:
+      {
+	uint64_t form;
+
+	form = read_uleb128 (buf);
+	return read_attribute ((enum dwarf_form) form, buf, is_dwarf64,
+			       version, addrsize, dwarf_str, dwarf_str_size,
+			       val);
+      }
+    case DW_FORM_sec_offset:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_offset (buf, is_dwarf64);
+      return 1;
+    case DW_FORM_exprloc:
+      val->encoding = ATTR_VAL_EXPR;
+      return advance (buf, read_uleb128 (buf));
+    case DW_FORM_flag_present:
+      val->encoding = ATTR_VAL_UINT;
+      val->u.uint = 1;
+      return 1;
+    case DW_FORM_ref_sig8:
+      val->encoding = ATTR_VAL_REF_TYPE;
+      val->u.uint = read_uint64 (buf);
+      return 1;
+    case DW_FORM_GNU_addr_index:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_uleb128 (buf);
+      return 1;
+    case DW_FORM_GNU_str_index:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_uleb128 (buf);
+      return 1;
+    case DW_FORM_GNU_ref_alt:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_offset (buf, is_dwarf64);
+      return 1;
+    case DW_FORM_GNU_strp_alt:
+      val->encoding = ATTR_VAL_REF_SECTION;
+      val->u.uint = read_offset (buf, is_dwarf64);
+      return 1;
+    default:
+      dwarf_buf_error (buf, "unrecognized DWARF form");
+      return 0;
+    }
+}
+
+/* Compare function_addrs for qsort.  When ranges are nested, make the
+   smallest one sort last.  */
+
+static int
+function_addrs_compare (const void *v1, const void *v2)
+{
+  const struct function_addrs *a1 = (const struct function_addrs *) v1;
+  const struct function_addrs *a2 = (const struct function_addrs *) v2;
+
+  if (a1->low < a2->low)
+    return -1;
+  if (a1->low > a2->low)
+    return 1;
+  if (a1->high < a2->high)
+    return 1;
+  if (a1->high > a2->high)
+    return -1;
+  return strcmp (a1->function->name, a2->function->name);
+}
+
+/* Compare a PC against a function_addrs for bsearch.  Note that if
+   there are multiple ranges containing PC, which one will be returned
+   is unpredictable.  We compensate for that in dwarf_fileline.  */
+
+static int
+function_addrs_search (const void *vkey, const void *ventry)
+{
+  const uintptr_t *key = (const uintptr_t *) vkey;
+  const struct function_addrs *entry = (const struct function_addrs *) ventry;
+  uintptr_t pc;
+
+  pc = *key;
+  if (pc < entry->low)
+    return -1;
+  else if (pc >= entry->high)
+    return 1;
+  else
+    return 0;
+}
+
+/* Add a new compilation unit address range to a vector.  Returns 1 on
+   success, 0 on failure.  */
+
+static int
+add_unit_addr (struct unit_addrs addrs,
+	       backtrace_error_callback error_callback, void *data,
+	       struct unit_addrs_vector *vec)
+{
+  if (vec->count > 0)
+    {
+      if ((addrs.low == vec->addrs[vec->count - 1].high
+	   || addrs.low == vec->addrs[vec->count - 1].high + 1)
+	  && addrs.u == vec->addrs[vec->count - 1].u)
+	{
+	  if (addrs.high > vec->addrs[vec->count - 1].high)
+	    vec->addrs[vec->count - 1].high = addrs.high;
+	  return 1;
+	}
+    }
+
+  if (vec->count >= vec->alc)
+    {
+      size_t alc;
+
+      alc = vec->alc;
+      if (alc == 0)
+	alc = 32;
+      else if (alc >= 2048)
+	alc += 2048;
+      else
+	alc *= 2;
+      vec->addrs = ((struct unit_addrs *)
+		    realloc (vec->addrs, alc * sizeof (struct unit_addrs)));
+      if (vec->addrs == NULL)
+	{
+	  error_callback (data, "realloc", errno);
+	  return 0;
+	}
+      vec->alc = alc;
+    }
+
+  vec->addrs[vec->count] = addrs;
+  ++vec->count;
+  return 1;
+}
+
+/* Free a unit address vector.  */
+
+static void
+free_unit_addrs_vector (struct unit_addrs_vector *vec)
+{
+  size_t i;
+
+  for (i = 0; i < vec->count; ++i)
+    {
+      free (vec->addrs[i].u->comp_dir);
+      free_abbrevs (vec->addrs[i].u->abbrevs);
+      free (vec->addrs[i].u);
+    }
+}
+
+/* Compare unit_addrs for qsort.  When ranges are nested, make the
+   smallest one sort last.  */
+
+static int
+unit_addrs_compare (const void *v1, const void *v2)
+{
+  const struct unit_addrs *a1 = (const struct unit_addrs *) v1;
+  const struct unit_addrs *a2 = (const struct unit_addrs *) v2;
+
+  if (a1->low < a2->low)
+    return -1;
+  if (a1->low > a2->low)
+    return 1;
+  if (a1->high < a2->high)
+    return 1;
+  if (a1->high > a2->high)
+    return -1;
+  if (a1->u->lineoff < a2->u->lineoff)
+    return -1;
+  if (a1->u->lineoff > a2->u->lineoff)
+    return 1;
+  return 0;
+}
+
+/* Compare a PC against a unit_addrs for bsearch.  Note that if there
+   are multiple ranges containing PC, which one will be returned is
+   unpredictable.  We compensate for that in dwarf_fileline.  */
+
+static int
+unit_addrs_search (const void *vkey, const void *ventry)
+{
+  const uintptr_t *key = (const uintptr_t *) vkey;
+  const struct unit_addrs *entry = (const struct unit_addrs *) ventry;
+  uintptr_t pc;
+
+  pc = *key;
+  if (pc < entry->low)
+    return -1;
+  else if (pc >= entry->high)
+    return 1;
+  else
+    return 0;
+}
+
+/* Sort the line vector by PC.  We want a stable sort here.  We know
+   that the pointers are into the same array, so it is safe to compare
+   them directly.  */
+
+static int
+line_compare (const void *v1, const void *v2)
+{
+  const struct line *ln1 = (const struct line *) v1;
+  const struct line *ln2 = (const struct line *) v2;
+
+  if (ln1->pc < ln2->pc)
+    return -1;
+  else if (ln1->pc > ln2->pc)
+    return 1;
+  else if (ln1 < ln2)
+    return -1;
+  else if (ln1 > ln2)
+    return 1;
+  else
+    return 0;
+}
+
+/* Find a PC in a line vector.  We always allocate an extra entry at
+   the end of the lines vector, so that this routine can safely look
+   at the next entry.  Note that when there are multiple mappings for
+   the same PC value, this will return the last one.  */
+
+static int
+line_search (const void *vkey, const void *ventry)
+{
+  const uintptr_t *key = (const uintptr_t *) vkey;
+  const struct line *entry = (const struct line *) ventry;
+  uintptr_t pc;
+
+  pc = *key;
+  if (pc < entry->pc)
+    return -1;
+  else if (pc >= (entry + 1)->pc)
+    return 1;
+  else
+    return 0;
+}
+
+/* Read the abbreviation table for a compilation unit.  */
+
+static struct abbrevs *
+read_abbrevs (uint64_t abbrev_offset, const unsigned char *dwarf_abbrev,
+	      size_t dwarf_abbrev_size, int is_bigendian,
+	      backtrace_error_callback error_callback, void *data)
+{
+  struct dwarf_buf abbrev_buf;
+  size_t num_abbrevs;
+  size_t alc_abbrevs;
+  struct abbrev *abbrevs;
+  struct abbrev_list *list;
+  struct abbrevs *ret;
+
+  if (abbrev_offset >= dwarf_abbrev_size)
+    {
+      error_callback (data, "abbrev offset out of range", 0);
+      return NULL;
+    }
+
+  abbrev_buf.name = ".debug_abbrev";
+  abbrev_buf.start = dwarf_abbrev;
+  abbrev_buf.buf = dwarf_abbrev + abbrev_offset;
+  abbrev_buf.left = dwarf_abbrev_size - abbrev_offset;
+  abbrev_buf.is_bigendian = is_bigendian;
+  abbrev_buf.error_callback = error_callback;
+  abbrev_buf.data = data;
+  abbrev_buf.reported_underflow = 0;
+
+  num_abbrevs = 0;
+  alc_abbrevs = 0;
+  abbrevs = NULL;
+  list = NULL;
+
+  while (1)
+    {
+      uint64_t code;
+      struct abbrev a;
+      size_t num_attrs;
+      size_t alc_attrs;
+      struct attr *attrs;
+
+      if (abbrev_buf.reported_underflow)
+	goto fail;
+
+      code = read_uleb128 (&abbrev_buf);
+      if (code == 0)
+	break;
+
+      a.tag = (enum dwarf_tag) read_uleb128 (&abbrev_buf);
+      a.has_children = read_byte (&abbrev_buf);
+
+      num_attrs = 0;
+      alc_attrs = 0;
+      attrs = NULL;
+
+      while (1)
+	{
+	  uint64_t name;
+	  uint64_t form;
+
+	  name = read_uleb128 (&abbrev_buf);
+	  form = read_uleb128 (&abbrev_buf);
+	  if (name == 0)
+	    break;
+
+	  if (num_attrs >= alc_attrs)
+	    {
+	      if (alc_attrs == 0)
+		alc_attrs = 8;
+	      else if (alc_attrs >= 64)
+		alc_attrs += 64;
+	      else
+		alc_attrs *= 2;
+	      attrs = ((struct attr *)
+		       realloc (attrs, alc_attrs * sizeof *attrs));
+	      if (attrs == NULL)
+		{
+		  error_callback (data, "realloc", errno);
+		  goto fail;
+		}
+	    }
+
+	  attrs[num_attrs].name = (enum dwarf_attribute) name;
+	  attrs[num_attrs].form = (enum dwarf_form) form;
+	  ++num_attrs;
+	}
+
+      a.num_attrs = num_attrs;
+      a.attrs = attrs;
+
+      if (code < num_abbrevs + 16)
+	{
+	  if (code >= alc_abbrevs)
+	    {
+	      size_t n;
+	      size_t i;
+
+	      if (alc_abbrevs == 0)
+		n = 16;
+	      else if (alc_abbrevs >= 64)
+		n += 64;
+	      else
+		n *= 2;
+	      if (n <= code)
+		n = code + 1;
+	      abbrevs = ((struct abbrev *)
+			 realloc (abbrevs, n * sizeof *abbrevs));
+	      if (abbrevs == NULL)
+		{
+		  error_callback (data, "realloc", errno);
+		  goto fail;
+		}
+
+	      memset (abbrevs + alc_abbrevs, 0,
+		      (n - alc_abbrevs) * sizeof *abbrevs);
+
+	      for (i = alc_abbrevs; i < n; ++i)
+		abbrevs[i].tag = (enum dwarf_tag) -1;
+
+	      alc_abbrevs = n;
+	    }
+
+	  abbrevs[code] = a;
+	  num_abbrevs = code + 1;
+	}
+      else
+	{
+	  struct abbrev_list *n;
+
+	  n = (struct abbrev_list *) malloc (sizeof *n);
+	  if (n == NULL)
+	    {
+	      error_callback (data, "malloc", errno);
+	      goto fail;
+	    }
+	  n->next = list;
+	  n->code = code;
+	  n->abbrev = a;
+	  list = n;
+	}
+    }
+
+  ret = (struct abbrevs *) malloc (sizeof *ret);
+  if (ret == NULL)
+    {
+      error_callback (data, "malloc", errno);
+      goto fail;
+    }
+
+  ret->num_abbrevs = num_abbrevs;
+  ret->abbrevs = abbrevs;
+  ret->list = list;
+
+  return ret;
+
+ fail:
+  free_abbrevs_vector (abbrevs, num_abbrevs);
+  free_abbrev_list (list);
+  return NULL;
+}
+
+/* Return the abbrev information for an abbrev code.  */
+
+static const struct abbrev *
+lookup_abbrev (struct abbrevs *abbrevs, uint64_t code,
+	       backtrace_error_callback error_callback, void *data)
+{
+  if (code < abbrevs->num_abbrevs)
+    {
+      struct abbrev *ret;
+
+      ret = &abbrevs->abbrevs[code];
+      if (ret->tag != (enum dwarf_tag) -1)
+	return ret;
+    }
+  else
+    {
+      struct abbrev_list *p;
+
+      for (p = abbrevs->list; p != NULL; p = p->next)
+	{
+	  if (p->code == code)
+	    return &p->abbrev;
+	}
+    }
+
+  error_callback (data, "invalid abbreviation code", 0);
+  return NULL;
+}
+
+/* Add non-contiguous address ranges for a compilation unit.  Returns
+   1 on success, 0 on failure.  */
+
+static int
+add_unit_ranges (struct unit *u, uint64_t ranges, uint64_t lowpc,
+		 int is_bigendian, const unsigned char *dwarf_ranges,
+		 size_t dwarf_ranges_size,
+		 backtrace_error_callback error_callback, void *data,
+		 struct unit_addrs_vector *addrs)
+{
+  struct dwarf_buf ranges_buf;
+  uint64_t base;
+
+  if (ranges >= dwarf_ranges_size)
+    {
+      error_callback (data, "ranges offset out of range", 0);
+      return 0;
+    }
+
+  ranges_buf.name = ".debug_ranges";
+  ranges_buf.start = dwarf_ranges;
+  ranges_buf.buf = dwarf_ranges + ranges;
+  ranges_buf.left = dwarf_ranges_size - ranges;
+  ranges_buf.is_bigendian = is_bigendian;
+  ranges_buf.error_callback = error_callback;
+  ranges_buf.data = data;
+  ranges_buf.reported_underflow = 0;
+
+  base = lowpc;
+  while (1)
+    {
+      uint64_t low;
+      uint64_t high;
+
+      if (ranges_buf.reported_underflow)
+	return 0;
+
+      low = read_address (&ranges_buf, u->addrsize);
+      high = read_address (&ranges_buf, u->addrsize);
+
+      if (low == 0 && high == 0)
+	break;
+
+      if (is_highest_address (low, u->addrsize))
+	base = high;
+      else
+	{
+	  struct unit_addrs a;
+
+	  a.low = low + base;
+	  a.high = high + base;
+	  a.u = u;
+	  if (!add_unit_addr (a, error_callback, data, addrs))
+	    return 0;
+	}
+    }
+
+  if (ranges_buf.reported_underflow)
+    return 0;
+
+  return 1;
+}
+
+/* Build a mapping from address ranges to the compilation units where
+   the line number information for that range can be found.  Returns 1
+   on success, 0 on failure.  */
+
+static int
+build_address_map (const unsigned char *dwarf_info, size_t dwarf_info_size,
+		   const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size,
+		   const unsigned char *dwarf_ranges, size_t dwarf_ranges_size,
+		   const unsigned char *dwarf_str, size_t dwarf_str_size,
+		   int is_bigendian, backtrace_error_callback error_callback,
+		   void *data, struct unit_addrs_vector *addrs)
+{
+  struct dwarf_buf info;
+  struct abbrevs* abbrevs;
+
+  addrs->addrs = NULL;
+  addrs->count = 0;
+  addrs->alc = 0;
+
+  /* Read through the .debug_info section.  FIXME: Should we use the
+     .debug_aranges section?  gdb and addr2line don't use it, but I'm
+     not sure why.  */
+
+  info.name = ".debug_info";
+  info.start = dwarf_info;
+  info.buf = dwarf_info;
+  info.left = dwarf_info_size;
+  info.is_bigendian = is_bigendian;
+  info.error_callback = error_callback;
+  info.data = data;
+  info.reported_underflow = 0;
+
+  abbrevs = NULL;
+  while (info.left > 0)
+    {
+      const unsigned char *unit_data_start;
+      uint64_t len;
+      int is_dwarf64;
+      struct dwarf_buf unit_buf;
+      int version;
+      uint64_t abbrev_offset;
+      const struct abbrev *abbrev;
+      int addrsize;
+      const unsigned char *unit_data;
+      size_t unit_data_len;
+      size_t unit_data_offset;
+      uint64_t code;
+      size_t i;
+      uint64_t lowpc;
+      int have_lowpc;
+      uint64_t highpc;
+      int have_highpc;
+      uint64_t ranges;
+      int have_ranges;
+      uint64_t lineoff;
+      int have_lineoff;
+      const char *comp_dir;
+
+      if (info.reported_underflow)
+	goto fail;
+
+      unit_data_start = info.buf;
+
+      is_dwarf64 = 0;
+      len = read_uint32 (&info);
+      if (len == 0xffffffff)
+	{
+	  len = read_uint64 (&info);
+	  is_dwarf64 = 1;
+	}
+
+      unit_buf = info;
+      unit_buf.start = info.buf;
+      unit_buf.left = len;
+
+      if (!advance (&info, len))
+	goto fail;
+
+      version = read_uint16 (&unit_buf);
+      if (version < 2 || version > 4)
+	{
+	  dwarf_buf_error (&unit_buf, "unrecognized DWARF version");
+	  goto fail;
+	}
+
+      abbrev_offset = read_offset (&unit_buf, is_dwarf64);
+      abbrevs = read_abbrevs (abbrev_offset, dwarf_abbrev, dwarf_abbrev_size,
+			      is_bigendian, error_callback, data);
+      if (abbrevs == NULL)
+	goto fail;
+
+      addrsize = read_byte (&unit_buf);
+
+      unit_data = unit_buf.buf;
+      unit_data_len = unit_buf.left;
+      unit_data_offset = unit_buf.buf - unit_data_start;
+
+      /* We only look at the first attribute in the compilation unit.
+	 In practice this will be a DW_TAG_compile_unit which will
+	 tell us the PC range and where to find the line number
+	 information.  */
+
+      code = read_uleb128 (&unit_buf);
+      abbrev = lookup_abbrev (abbrevs, code, error_callback, data);
+      if (abbrev == NULL)
+	goto fail;
+
+      lowpc = 0;
+      have_lowpc = 0;
+      highpc = 0;
+      have_highpc = 0;
+      ranges = 0;
+      have_ranges = 0;
+      lineoff = 0;
+      have_lineoff = 0;
+      comp_dir = NULL;
+      for (i = 0; i < abbrev->num_attrs; ++i)
+	{
+	  struct attr_val val;
+
+	  if (!read_attribute (abbrev->attrs[i].form, &unit_buf, is_dwarf64,
+			       version, addrsize, dwarf_str, dwarf_str_size,
+			       &val))
+	    goto fail;
+
+	  switch (abbrev->attrs[i].name)
+	    {
+	    case DW_AT_low_pc:
+	      if (val.encoding == ATTR_VAL_UINT)
+		{
+		  lowpc = val.u.uint;
+		  have_lowpc = 1;
+		}
+	      break;
+	    case DW_AT_high_pc:
+	      if (val.encoding == ATTR_VAL_UINT)
+		{
+		  highpc = val.u.uint;
+		  have_highpc = 1;
+		}
+	      break;
+	    case DW_AT_ranges:
+	      if (val.encoding == ATTR_VAL_UINT
+		  || val.encoding == ATTR_VAL_REF_SECTION)
+		{
+		  ranges = val.u.uint;
+		  have_ranges = 1;
+		}
+	      break;
+	    case DW_AT_stmt_list:
+	      if (val.encoding == ATTR_VAL_UINT
+		  || val.encoding == ATTR_VAL_REF_SECTION)
+		{
+		  lineoff = val.u.uint;
+		  have_lineoff = 1;
+		}
+	      break;
+	    case DW_AT_comp_dir:
+	      if (val.encoding == ATTR_VAL_STRING)
+		comp_dir = val.u.string;
+	      break;
+	    default:
+	      break;
+	    }
+	}
+
+      if (unit_buf.reported_underflow)
+	goto fail;
+
+      if (((have_lowpc && have_highpc) || have_ranges) && have_lineoff)
+	{
+	  struct unit *u;
+	  struct unit_addrs a;
+
+	  u = (struct unit *) malloc (sizeof *u);
+	  if (u == NULL)
+	    {
+	      error_callback (data, "malloc", errno);
+	      goto fail;
+	    }
+	  u->unit_data = unit_data;
+	  u->unit_data_len = unit_data_len;
+	  u->unit_data_offset = unit_data_offset;
+	  u->version = version;
+	  u->is_dwarf64 = is_dwarf64;
+	  u->addrsize = addrsize;
+	  if (comp_dir == NULL)
+	    u->comp_dir = NULL;
+	  else
+	    {
+	      u->comp_dir = strdup (comp_dir);
+	      if (u->comp_dir == NULL)
+		{
+		  error_callback (data, "strdup", errno);
+		  free (u);
+		  goto fail;
+		}
+	    }
+	  u->lineoff = lineoff;
+	  u->abbrevs = abbrevs;
+	  abbrevs = NULL;
+
+	  /* The actual line number mappings will be read as
+	     needed.  */
+	  u->lines = NULL;
+	  u->lines_count = 0;
+
+	  if (have_ranges)
+	    {
+	      if (!add_unit_ranges (u, ranges, lowpc, is_bigendian,
+				    dwarf_ranges, dwarf_ranges_size,
+				    error_callback, data, addrs))
+		{
+		  free_abbrevs (u->abbrevs);
+		  free (u->comp_dir);
+		  free (u);
+		  goto fail;
+		}
+	    }
+	  else
+	    {
+	      a.low = lowpc;
+	      a.high = highpc;
+	      a.u = u;
+
+	      if (!add_unit_addr (a, error_callback, data, addrs))
+		{
+		  free_abbrevs (u->abbrevs);
+		  free (u->comp_dir);
+		  free (u);
+		  goto fail;
+		}
+	    }
+	}
+      else
+	{
+	  free_abbrevs (abbrevs);
+	  abbrevs = NULL;
+	}
+    }
+  if (info.reported_underflow)
+    goto fail;
+
+  return 1;
+
+ fail:
+  free_abbrevs (abbrevs);
+  free_unit_addrs_vector (addrs);
+  return 0;
+}
+
+/* Add a new mapping to the vector of line mappings that we are
+   building.  Returns 1 on success, 0 on failure.  */
+
+static int
+add_line (uintptr_t pc, const char *filename, int lineno,
+	  backtrace_error_callback error_callback, void *data,
+	  struct line_vector *vec)
+{
+  struct line *ln;
+
+  /* If we are adding the same mapping, ignore it.  This can happen
+     when using discriminators.  */
+  if (vec->count > 0
+      && pc == vec->lines[vec->count - 1].pc
+      && filename == vec->lines[vec->count - 1].filename
+      && lineno == vec->lines[vec->count - 1].lineno)
+    return 1;
+
+  if (vec->count >= vec->alc)
+    {
+      size_t alc;
+
+      alc = vec->alc;
+      if (alc == 0)
+	alc = 64;
+      else if (alc >= 1024)
+	alc += 1024;
+      else
+	alc *= 2;
+      vec->lines = ((struct line *)
+		    realloc (vec->lines, alc * sizeof (struct line)));
+      if (vec->lines == NULL)
+	{
+	  error_callback (data, "realloc", errno);
+	  return 0;
+	}
+      vec->alc = alc;
+    }
+
+  ln = &vec->lines[vec->count];
+  ln->pc = pc;
+  ln->filename = filename;
+  ln->lineno = lineno;
+
+  ++vec->count;
+
+  return 1;
+}
+
+/* Free the line header information.  If FREE_FILENAMES is true we
+   free the file names themselves, otherwise we leave them, as there
+   may be line structures pointing to them.  */
+
+static void
+free_line_header (struct line_header *hdr, int free_filenames)
+{
+  free (hdr->dirs);
+  if (free_filenames && hdr->filenames != NULL)
+    {
+      size_t i;
+
+      for (i = 0; i < hdr->filenames_count; ++i)
+	free (hdr->filenames[i]);
+    }
+  free (hdr->filenames);
+}
+
+/* Read the line header.  Return 1 on success, 0 on failure.  */
+
+static int
+read_line_header (struct unit *u, int is_dwarf64, struct dwarf_buf *line_buf,
+		  struct line_header *hdr)
+{
+  uint64_t hdrlen;
+  struct dwarf_buf hdr_buf;
+  const unsigned char *p;
+  const unsigned char *pend;
+  size_t i;
+
+  hdr->version = read_uint16 (line_buf);
+  if (hdr->version < 2 || hdr->version > 4)
+    {
+      dwarf_buf_error (line_buf, "unsupported line number version");
+      return 0;
+    }
+
+  hdrlen = read_offset (line_buf, is_dwarf64);
+
+  hdr_buf = *line_buf;
+  hdr_buf.left = hdrlen;
+
+  if (!advance (line_buf, hdrlen))
+    return 0;
+  
+  hdr->min_insn_len = read_byte (&hdr_buf);
+  if (hdr->version < 4)
+    hdr->max_ops_per_insn = 1;
+  else
+    hdr->max_ops_per_insn = read_byte (&hdr_buf);
+
+  /* We don't care about default_is_stmt.  */
+  read_byte (&hdr_buf);
+  
+  hdr->line_base = read_sbyte (&hdr_buf);
+  hdr->line_range = read_byte (&hdr_buf);
+
+  hdr->opcode_base = read_byte (&hdr_buf);
+  hdr->opcode_lengths = hdr_buf.buf;
+  if (!advance (&hdr_buf, hdr->opcode_base - 1))
+    return 0;
+
+  /* Count the number of directory entries.  */
+  hdr->dirs_count = 0;
+  p = hdr_buf.buf;
+  pend = p + hdr_buf.left;
+  while (p < pend && *p != '\0')
+    {
+      p += strnlen((const char *) p, pend - p) + 1;
+      ++hdr->dirs_count;
+    }
+
+  hdr->dirs = (const char **) calloc (hdr->dirs_count, sizeof (const char *));
+  if (hdr->dirs == NULL)
+    {
+      line_buf->error_callback (line_buf->data, "malloc", errno);
+      return 0;
+    }
+  i = 0;
+  while (*hdr_buf.buf != '\0')
+    {
+      if (hdr_buf.reported_underflow)
+	return 0;
+
+      hdr->dirs[i] = (const char *) hdr_buf.buf;
+      ++i;
+      if (!advance (&hdr_buf,
+		    strnlen ((const char *) hdr_buf.buf, hdr_buf.left) + 1))
+	return 0;
+    }
+  if (!advance (&hdr_buf, 1))
+    return 0;
+
+  /* Count the number of file entries.  */
+  hdr->filenames_count = 0;
+  p = hdr_buf.buf;
+  pend = p + hdr_buf.left;
+  while (p < pend && *p != '\0')
+    {
+      p += strnlen ((const char *) p, pend - p) + 1;
+      p += leb128_len (p);
+      p += leb128_len (p);
+      p += leb128_len (p);
+      ++hdr->filenames_count;
+    }
+
+  hdr->filenames = (char **) calloc (hdr->filenames_count, sizeof (char *));
+  if (hdr->filenames == NULL)
+    {
+      line_buf->error_callback (line_buf->data, "malloc", errno);
+      return 0;
+    }
+  memset (hdr->filenames, 0, hdr->filenames_count * sizeof (char *));
+  i = 0;
+  while (*hdr_buf.buf != '\0')
+    {
+      const char *filename;
+      uint64_t dir_index;
+
+      if (hdr_buf.reported_underflow)
+	return 0;
+
+      filename = (const char *) hdr_buf.buf;
+      if (!advance (&hdr_buf,
+		    strnlen ((const char *) hdr_buf.buf, hdr_buf.left) + 1))
+	return 0;
+      dir_index = read_uleb128 (&hdr_buf);
+      if (IS_ABSOLUTE_PATH (filename))
+	{
+	  hdr->filenames[i] = strdup (filename);
+	  if (hdr->filenames[i] == NULL)
+	    {
+	      line_buf->error_callback (line_buf->data, "strdup", errno);
+	      return 0;
+	    }
+	}
+      else
+	{
+	  const char *dir;
+	  size_t dir_len;
+	  size_t filename_len;
+
+	  if (dir_index == 0)
+	    dir = u->comp_dir;
+	  else if (dir_index - 1 < hdr->dirs_count)
+	    dir = hdr->dirs[dir_index - 1];
+	  else
+	    {
+	      dwarf_buf_error (line_buf,
+			       ("invalid directory index in "
+				"line number program header"));
+	      return 0;
+	    }
+	  dir_len = strlen (dir);
+	  filename_len = strlen (filename);
+	  hdr->filenames[i] = (char *) malloc (dir_len + filename_len + 2);
+	  if (hdr->filenames[i] == NULL)
+	    {
+	      line_buf->error_callback (line_buf->data, "malloc", errno);
+	      return 0;
+	    }
+	  memcpy (hdr->filenames[i], dir, dir_len);
+	  /* FIXME: If we are on a DOS-based file system, and the
+	     directory or the file name use backslashes, then we
+	     should use a backslash here.  */
+	  hdr->filenames[i][dir_len] = '/';
+	  memcpy (hdr->filenames[i] + dir_len + 1, filename, filename_len + 1);
+	}
+
+      /* Ignore the modification time and size.  */
+      read_uleb128 (&hdr_buf);
+      read_uleb128 (&hdr_buf);
+
+      ++i;
+    }
+
+  if (hdr_buf.reported_underflow)
+    return 0;
+
+  return 1;
+}
+
+/* Read the line program, adding line mappings to VEC.  Return 1 on
+   success, 0 on failure.  */
+
+static int
+read_line_program (struct unit *u, const struct line_header *hdr,
+		   struct dwarf_buf *line_buf, struct line_vector *vec)
+{
+  uint64_t address;
+  unsigned int op_index;
+  const char *reset_filename;
+  const char *filename;
+  int lineno;
+
+  address = 0;
+  op_index = 0;
+  if (hdr->filenames_count > 0)
+    reset_filename = hdr->filenames[0];
+  else
+    reset_filename = "";
+  filename = reset_filename;
+  lineno = 1;
+  while (line_buf->left > 0)
+    {
+      unsigned int op;
+
+      op = read_byte (line_buf);
+      if (op >= hdr->opcode_base)
+	{
+	  unsigned int advance;
+
+	  /* Special opcode.  */
+	  op -= hdr->opcode_base;
+	  advance = op / hdr->line_range;
+	  address += (hdr->min_insn_len * (op_index + advance)
+		      / hdr->max_ops_per_insn);
+	  op_index = (op_index + advance) % hdr->max_ops_per_insn;
+	  lineno += hdr->line_base + (int) (op % hdr->line_range);
+	  add_line (address, filename, lineno, line_buf->error_callback,
+		    line_buf->data, vec);
+	}
+      else if (op == DW_LNS_extended_op)
+	{
+	  uint64_t len;
+
+	  len = read_uleb128 (line_buf);
+	  op = read_byte (line_buf);
+	  switch (op)
+	    {
+	    case DW_LNE_end_sequence:
+	      /* FIXME: Should we mark the high PC here?  It seems
+		 that we already have that information from the
+		 compilation unit.  */
+	      address = 0;
+	      op_index = 0;
+	      filename = reset_filename;
+	      lineno = 1;
+	      break;
+	    case DW_LNE_set_address:
+	      address = read_address (line_buf, u->addrsize);
+	      break;
+	    case DW_LNE_define_file:
+	      {
+		const char *f;
+		unsigned int dir_index;
+
+		f = (const char *) line_buf->buf;
+		if (!advance (line_buf, strnlen (f, line_buf->left) + 1))
+		  return 0;
+		dir_index = read_uleb128 (line_buf);
+		/* Ignore that time and length.  */
+		read_uleb128 (line_buf);
+		read_uleb128 (line_buf);
+		if (IS_ABSOLUTE_PATH (f))
+		  filename = f;
+		else
+		  {
+		    const char *dir;
+		    size_t dir_len;
+		    size_t f_len;
+		    char *p;
+
+		    if (dir_index == 0)
+		      dir = u->comp_dir;
+		    else if (dir_index - 1 < hdr->dirs_count)
+		      dir = hdr->dirs[dir_index - 1];
+		    else
+		      {
+			dwarf_buf_error (line_buf,
+					 ("invalid directory index "
+					  "in line number program"));
+			return 0;
+		      }
+		    dir_len = strlen (dir);
+		    f_len = strlen (f);
+		    p = (char *) malloc (dir_len + f_len + 2);
+		    if (p == NULL)
+		      {
+			line_buf->error_callback (line_buf->data, "malloc", 0);
+			return 0;
+		      }
+		    memcpy (p, dir, dir_len);
+		    /* FIXME: If we are on a DOS-based file system,
+		       and the directory or the file name use
+		       backslashes, then we should use a backslash
+		       here.  */
+		    p[dir_len] = '/';
+		    memcpy (p + dir_len + 1, f, f_len + 1);
+		    filename = p;
+		  }
+	      }
+	      break;
+	    case DW_LNE_set_discriminator:
+	      /* We don't care about discriminators.  */
+	      read_uleb128 (line_buf);
+	      break;
+	    default:
+	      if (!advance (line_buf, len - 1))
+		return 0;
+	      break;
+	    }
+	}
+      else
+	{
+	  switch (op)
+	    {
+	    case DW_LNS_copy:
+	      add_line (address, filename, lineno, line_buf->error_callback,
+			line_buf->data, vec);
+	      break;
+	    case DW_LNS_advance_pc:
+	      {
+		uint64_t advance;
+
+		advance = read_uleb128 (line_buf);
+		address += (hdr->min_insn_len * (op_index + advance)
+			    / hdr->max_ops_per_insn);
+		op_index = (op_index + advance) % hdr->max_ops_per_insn;
+	      }
+	      break;
+	    case DW_LNS_advance_line:
+	      lineno += (int) read_sleb128 (line_buf);
+	      break;
+	    case DW_LNS_set_file:
+	      {
+		uint64_t fileno;
+
+		fileno = read_uleb128 (line_buf);
+		if (fileno == 0)
+		  filename = "";
+		else
+		  {
+		    if (fileno - 1 >= hdr->filenames_count)
+		      {
+			dwarf_buf_error (line_buf,
+					 ("invalid file number in "
+					  "line number program"));
+			return 0;
+		      }
+		    filename = hdr->filenames[fileno - 1];
+		  }
+	      }
+	      break;
+	    case DW_LNS_set_column:
+	      read_uleb128 (line_buf);
+	      break;
+	    case DW_LNS_negate_stmt:
+	      break;
+	    case DW_LNS_set_basic_block:
+	      break;
+	    case DW_LNS_const_add_pc:
+	      {
+		unsigned int advance;
+
+		op = 255 - hdr->opcode_base;
+		advance = op / hdr->line_range;
+		address += (hdr->min_insn_len * (op_index + advance)
+			    / hdr->max_ops_per_insn);
+		op_index = (op_index + advance) % hdr->max_ops_per_insn;
+	      }
+	      break;
+	    case DW_LNS_fixed_advance_pc:
+	      address += read_uint16 (line_buf);
+	      op_index = 0;
+	      break;
+	    case DW_LNS_set_prologue_end:
+	      break;
+	    case DW_LNS_set_epilogue_begin:
+	      break;
+	    case DW_LNS_set_isa:
+	      read_uleb128 (line_buf);
+	      break;
+	    default:
+	      {
+		unsigned int i;
+
+		for (i = hdr->opcode_lengths[op - 1]; i > 0; --i)
+		  read_uleb128 (line_buf);
+	      }
+	      break;
+	    }
+	}
+    }
+
+  return 1;
+}
+
+/* Read the line number information for a compilation unit.  This is
+   called with the backtrace lock held.  This sets U->lines and
+   U->lines_count.  Returns 1 on success, 0 on failure.  */
+
+static int
+read_line_info (struct dwarf_data *ddata,
+		backtrace_error_callback error_callback, void *data,
+		struct unit *u, struct line_header *hdr)
+{
+  struct line_vector vec;
+  struct dwarf_buf line_buf;
+  uint64_t len;
+  int is_dwarf64;
+
+  vec.lines = NULL;
+  vec.count = 0;
+  vec.alc = 0;
+  memset (hdr, 0, sizeof *hdr);
+
+  if (u->lineoff != (off_t) (size_t) u->lineoff
+      || (size_t) u->lineoff >= ddata->dwarf_line_size)
+    {
+      error_callback (data, "unit line offset out of range", 0);
+      goto fail;
+    }
+
+  line_buf.name = ".debug_line";
+  line_buf.start = ddata->dwarf_line;
+  line_buf.buf = ddata->dwarf_line + u->lineoff;
+  line_buf.left = ddata->dwarf_line_size - u->lineoff;
+  line_buf.is_bigendian = ddata->is_bigendian;
+  line_buf.error_callback = error_callback;
+  line_buf.data = data;
+  line_buf.reported_underflow = 0;
+
+  is_dwarf64 = 0;
+  len = read_uint32 (&line_buf);
+  if (len == 0xffffffff)
+    {
+      len = read_uint64 (&line_buf);
+      is_dwarf64 = 1;
+    }
+  line_buf.left = len;
+
+  if (!read_line_header (u, is_dwarf64, &line_buf, hdr))
+    goto fail;
+
+  if (!read_line_program (u, hdr, &line_buf, &vec))
+    goto fail;
+
+  if (line_buf.reported_underflow)
+    goto fail;
+
+  if (vec.count == 0)
+    {
+      /* This is not a failure in the sense of a generating an error,
+	 but it is a failure in that sense that we have no useful
+	 information.  */
+      goto fail;
+    }
+
+  /* Allocate one extra entry at the end, while discarding any data we
+     may have allocated but do not need.  */
+  vec.lines = ((struct line *)
+	       realloc (vec.lines, (vec.count + 1) * sizeof (struct line)));
+  if (vec.lines == NULL)
+    {
+      error_callback (data, "realloc", errno);
+      goto fail;
+    }
+  vec.lines[vec.count].pc = (uintptr_t) -1;
+  vec.lines[vec.count].filename = NULL;
+  vec.lines[vec.count].lineno = 0;
+
+  qsort (vec.lines, vec.count, sizeof (struct line), line_compare);
+
+  u->lines = vec.lines;
+  u->lines_count = vec.count;
+
+  return 1;
+
+ fail:
+  free (vec.lines);
+  free_line_header (hdr, 1);
+  u->lines = NULL;
+  u->lines_count = (size_t) -1;
+  return 0;
+}
+
+/* Read the name of a function from a DIE referenced by a
+   DW_AT_abstract_origin or DW_AT_specification tag.  OFFSET is within
+   the same compilation unit.  */
+
+static const char *
+read_referenced_name (struct dwarf_data *ddata, struct unit *u,
+		      uint64_t offset, backtrace_error_callback error_callback,
+		      void *data)
+{
+  struct dwarf_buf unit_buf;
+  uint64_t code;
+  const struct abbrev *abbrev;
+  const char *ret;
+  size_t i;
+
+  /* OFFSET is from the start of the data for this compilation unit.
+     U->unit_data is the data, but it starts U->unit_data_offset bytes
+     from the beginning.  */
+
+  if (offset < u->unit_data_offset
+      || offset - u->unit_data_offset >= u->unit_data_len)
+    {
+      error_callback (data,
+		      "abstract origin or specification out of range",
+		      0);
+      return NULL;
+    }
+
+  offset -= u->unit_data_offset;
+
+  unit_buf.name = ".debug_info";
+  unit_buf.start = ddata->dwarf_info;
+  unit_buf.buf = u->unit_data + offset;
+  unit_buf.left = u->unit_data_len - offset;
+  unit_buf.is_bigendian = ddata->is_bigendian;
+  unit_buf.error_callback = error_callback;
+  unit_buf.data = data;
+  unit_buf.reported_underflow = 0;
+
+  code = read_uleb128 (&unit_buf);
+  if (code == 0)
+    {
+      dwarf_buf_error (&unit_buf, "invalid abstract origin or specification");
+      return NULL;
+    }
+
+  abbrev = lookup_abbrev (u->abbrevs, code, error_callback, data);
+  if (abbrev == NULL)
+    return NULL;
+
+  ret = NULL;
+  for (i = 0; i < abbrev->num_attrs; ++i)
+    {
+      struct attr_val val;
+
+      if (!read_attribute (abbrev->attrs[i].form, &unit_buf,
+			   u->is_dwarf64, u->version, u->addrsize,
+			   ddata->dwarf_str, ddata->dwarf_str_size,
+			   &val))
+	return NULL;
+
+      switch (abbrev->attrs[i].name)
+	{
+	case DW_AT_name:
+	  /* We prefer the linkage name if get one.  */
+	  if (val.encoding == ATTR_VAL_STRING)
+	    ret = val.u.string;
+	  break;
+
+	case DW_AT_linkage_name:
+	case DW_AT_MIPS_linkage_name:
+	  if (val.encoding == ATTR_VAL_STRING)
+	    return val.u.string;
+	  break;
+
+	case DW_AT_specification:
+	  if (abbrev->attrs[i].form == DW_FORM_ref_addr
+	      || abbrev->attrs[i].form == DW_FORM_ref_sig8)
+	    {
+	      /* This refers to a specification defined in some other
+		 compilation unit.  We can handle this case if we
+		 must, but it's harder.  */
+	      break;
+	    }
+	  if (val.encoding == ATTR_VAL_UINT
+	      || val.encoding == ATTR_VAL_REF_UNIT)
+	    {
+	      const char *name;
+
+	      name = read_referenced_name (ddata, u, val.u.uint,
+					   error_callback, data);
+	      if (name != NULL)
+		ret = name;
+	    }
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  return ret;
+}
+
+/* Add a single range to U that maps to function.  Returns 1 on
+   success, 0 on error.  */
+
+static int
+add_function_range (struct function *function, uint64_t lowpc, uint64_t highpc,
+		    backtrace_error_callback error_callback,
+		    void *data, struct function_vector *vec)
+{
+  struct function_addrs *p;
+
+  if (vec->count > 0)
+    {
+      if ((lowpc == vec->addrs[vec->count - 1].high
+	   || lowpc == vec->addrs[vec->count - 1].high + 1)
+	  && function == vec->addrs[vec->count - 1].function)
+	{
+	  if (highpc > vec->addrs[vec->count - 1].high)
+	    vec->addrs[vec->count - 1].high = highpc;
+	  return 1;
+	}
+    }
+
+  if (vec->count >= vec->alc)
+    {
+      size_t alc;
+
+      alc = vec->alc;
+      if (alc == 0)
+	alc = 16;
+      else if (alc >= 256)
+	alc += 256;
+      else
+	alc *= 2;
+      vec->addrs = ((struct function_addrs *)
+		    realloc (vec->addrs,
+			     alc * sizeof (struct function_addrs)));
+      if (vec->addrs == NULL)
+	{
+	  error_callback (data, "realloc", errno);
+	  return 0;
+	}
+      vec->alc = alc;
+    }
+
+  p = &vec->addrs[vec->count];
+  p->low = lowpc;
+  p->high = highpc;
+  p->function = function;
+  ++vec->count;
+  return 1;
+}
+
+/* Add PC ranges to U that map to FUNCTION.  Returns 1 on success, 0
+   on error.  */
+
+static int
+add_function_ranges (struct dwarf_data *ddata, struct unit *u,
+		     struct function *function, uint64_t ranges,
+		     uint64_t lowpc, backtrace_error_callback error_callback,
+		     void *data, struct function_vector *vec)
+{
+  struct dwarf_buf ranges_buf;
+  uint64_t base;
+
+  if (ranges >= ddata->dwarf_ranges_size)
+    {
+      error_callback (data, "function ranges offset out of range", 0);
+      return 0;
+    }
+
+  ranges_buf.name = ".debug_ranges";
+  ranges_buf.start = ddata->dwarf_ranges;
+  ranges_buf.buf = ddata->dwarf_ranges + ranges;
+  ranges_buf.left = ddata->dwarf_ranges_size - ranges;
+  ranges_buf.is_bigendian = ddata->is_bigendian;
+  ranges_buf.error_callback = error_callback;
+  ranges_buf.data = data;
+  ranges_buf.reported_underflow = 0;
+
+  base = lowpc;
+  while (1)
+    {
+      uint64_t low;
+      uint64_t high;
+
+      if (ranges_buf.reported_underflow)
+	return 0;
+
+      low = read_address (&ranges_buf, u->addrsize);
+      high = read_address (&ranges_buf, u->addrsize);
+
+      if (low == 0 && high == 0)
+	break;
+
+      if (is_highest_address (low, u->addrsize))
+	base = high;
+      else
+	{
+	  if (!add_function_range (function, low + base, high + base,
+				   error_callback, data, vec))
+	    return 0;
+	}
+    }
+
+  if (ranges_buf.reported_underflow)
+    return 0;
+
+  return 1;
+}
+
+/* Read one entry plus all its children.  Add function addresses to
+   VEC.  Returns 1 on success, 0 on error.  */
+
+static int
+read_function_entry (struct dwarf_data *ddata, struct unit *u,
+		     struct dwarf_buf *unit_buf,
+		     const struct line_header *lhdr,
+		     backtrace_error_callback error_callback, void *data,
+		     struct function_vector *vec)
+{
+  while (unit_buf->left > 0)
+    {
+      uint64_t code;
+      const struct abbrev *abbrev;
+      int is_function;
+      struct function *function;
+      size_t i;
+      uint64_t lowpc;
+      int have_lowpc;
+      uint64_t highpc;
+      int have_highpc;
+      uint64_t ranges;
+      int have_ranges;
+
+      code = read_uleb128 (unit_buf);
+      if (code == 0)
+	return 1;
+
+      abbrev = lookup_abbrev (u->abbrevs, code, error_callback, data);
+      if (abbrev == NULL)
+	return 0;
+
+      is_function = (abbrev->tag == DW_TAG_subprogram
+		     || abbrev->tag == DW_TAG_entry_point
+		     || abbrev->tag == DW_TAG_inlined_subroutine);
+
+      function = NULL;
+      if (is_function)
+	{
+	  function = (struct function *) calloc (1, sizeof *function);
+	  if (function == NULL)
+	    {
+	      error_callback (data, "malloc", errno);
+	      return 0;
+	    }
+	}
+
+      lowpc = 0;
+      have_lowpc = 0;
+      highpc = 0;
+      have_highpc = 0;
+      ranges = 0;
+      have_ranges = 0;
+      for (i = 0; i < abbrev->num_attrs; ++i)
+	{
+	  struct attr_val val;
+
+	  if (!read_attribute (abbrev->attrs[i].form, unit_buf,
+			       u->is_dwarf64, u->version, u->addrsize,
+			       ddata->dwarf_str, ddata->dwarf_str_size,
+			       &val))
+	    return 0;
+
+	  if (is_function)
+	    {
+	      switch (abbrev->attrs[i].name)
+		{
+		case DW_AT_call_file:
+		  if (val.encoding == ATTR_VAL_UINT)
+		    {
+		      if (val.u.uint == 0)
+			function->caller_filename = "";
+		      else
+			{
+			  if (val.u.uint >= lhdr->filenames_count)
+			    {
+			      dwarf_buf_error (unit_buf,
+					       ("invalid file number in "
+						"DW_AT_call_file attribute"));
+			      return 0;
+			    }
+			  function->caller_filename =
+			    lhdr->filenames[val.u.uint - 1];
+			}
+		    }
+		  break;
+
+		case DW_AT_call_line:
+		  if (val.encoding == ATTR_VAL_UINT)
+		    function->caller_lineno = val.u.uint;
+		  break;
+
+		case DW_AT_abstract_origin:
+		case DW_AT_specification:
+		  if (abbrev->attrs[i].form == DW_FORM_ref_addr
+		      || abbrev->attrs[i].form == DW_FORM_ref_sig8)
+		    {
+		      /* This refers to an abstract origin defined in
+			 some other compilation unit.  We can handle
+			 this case if we must, but it's harder.  */
+		      break;
+		    }
+		  if (val.encoding == ATTR_VAL_UINT
+		      || val.encoding == ATTR_VAL_REF_UNIT)
+		    {
+		      const char *name;
+
+		      name = read_referenced_name (ddata, u, val.u.uint,
+						   error_callback, data);
+		      if (name != NULL)
+			function->name = name;
+		    }
+		  break;
+
+		case DW_AT_name:
+		  if (val.encoding == ATTR_VAL_STRING)
+		    {
+		      /* Don't override a name we found in some other
+			 way, as it will normally be more
+			 useful--e.g., this name is normally not
+			 mangled.  */
+		      if (function->name == NULL)
+			function->name = val.u.string;
+		    }
+		  break;
+
+		case DW_AT_linkage_name:
+		case DW_AT_MIPS_linkage_name:
+		  if (val.encoding == ATTR_VAL_STRING)
+		    function->name = val.u.string;
+		  break;
+
+		case DW_AT_low_pc:
+		  if (val.encoding == ATTR_VAL_UINT)
+		    {
+		      lowpc = val.u.uint;
+		      have_lowpc = 1;
+		    }
+		  break;
+
+		case DW_AT_high_pc:
+		  if (val.encoding == ATTR_VAL_UINT)
+		    {
+		      highpc = val.u.uint;
+		      have_highpc = 1;
+		    }
+		  break;
+
+		case DW_AT_ranges:
+		  if (val.encoding == ATTR_VAL_UINT
+		      || val.encoding == ATTR_VAL_REF_SECTION)
+		    {
+		      ranges = val.u.uint;
+		      have_ranges = 1;
+		    }
+		  break;
+
+		default:
+		  break;
+		}
+	    }
+	}
+
+      /* If we couldn't find a name for the function, we have no use
+	 for it.  */
+      if (is_function && function->name == NULL)
+	{
+	  free (function);
+	  is_function = 0;
+	}
+
+      if (is_function)
+	{
+	  if (have_ranges)
+	    {
+	      if (!add_function_ranges (ddata, u, function, ranges, lowpc,
+					error_callback, data, vec))
+		return 0;
+	    }
+	  else if (have_lowpc && have_highpc)
+	    {
+	      if (!add_function_range (function, lowpc, highpc, error_callback,
+				       data, vec))
+		return 0;
+	    }
+	  else
+	    {
+	      free (function);
+	      is_function = 0;
+	    }
+	}
+
+      if (abbrev->has_children)
+	{
+	  if (!is_function)
+	    {
+	      if (!read_function_entry (ddata, u, unit_buf, lhdr,
+					error_callback, data, vec))
+		return 0;
+	    }
+	  else
+	    {
+	      struct function_vector fvec;
+
+	      /* Gather any information for inlined functions in
+		 FVEC.  */
+
+	      fvec.addrs = NULL;
+	      fvec.count = 0;
+	      fvec.alc = 0;
+
+	      if (!read_function_entry (ddata, u, unit_buf, lhdr,
+					error_callback, data, &fvec))
+		return 0;
+
+	      if (fvec.count > 0)
+		{
+		  qsort (fvec.addrs, fvec.count,
+			 sizeof (struct function_addrs),
+			 function_addrs_compare);
+
+		  function->function_addrs = fvec.addrs;
+		  function->function_addrs_count = fvec.count;
+		}
+	    }
+	}
+    }
+
+  return 1;
+}
+
+/* Read function name information for a compilation unit.  We look
+   through the whole unit looking for function tags.  This adds
+   entries to U->function_addrs.  */
+
+static void
+read_function_info (struct dwarf_data *ddata,
+		    const struct line_header *lhdr,
+		    backtrace_error_callback error_callback, void *data,
+		    struct unit *u)
+{
+  struct dwarf_buf unit_buf;
+  struct function_vector vec;
+
+  unit_buf.name = ".debug_info";
+  unit_buf.start = ddata->dwarf_info;
+  unit_buf.buf = u->unit_data;
+  unit_buf.left = u->unit_data_len;
+  unit_buf.is_bigendian = ddata->is_bigendian;
+  unit_buf.error_callback = error_callback;
+  unit_buf.data = data;
+  unit_buf.reported_underflow = 0;
+
+  vec.addrs = NULL;
+  vec.count = 0;
+  vec.alc = 0;
+
+  while (unit_buf.left > 0)
+    {
+      if (!read_function_entry (ddata, u, &unit_buf, lhdr, error_callback,
+				data, &vec))
+	return;
+    }
+
+  if (vec.count == 0)
+    return;
+
+  qsort (vec.addrs, vec.count, sizeof (struct function_addrs),
+	 function_addrs_compare);
+
+  u->function_addrs = vec.addrs;
+  u->function_addrs_count = vec.count;
+}
+
+/* See if PC is inlined in FUNCTION.  If it is, print out the inlined
+   information, and update FILENAME and LINENO for the caller.
+   Returns whatever CALLBACK returns, or 0 to keep going.  */
+
+static int
+report_inlined_functions (uintptr_t pc, struct function *function,
+			  backtrace_callback callback, void *data,
+			  const char **filename, int *lineno)
+{
+  struct function_addrs *function_addrs;
+  struct function *inlined;
+  int ret;
+
+  if (function->function_addrs_count == 0)
+    return 0;
+
+  function_addrs = ((struct function_addrs *)
+		    bsearch (&pc, function->function_addrs,
+			     function->function_addrs_count,
+			     sizeof (struct function_addrs),
+			     function_addrs_search));
+  if (function_addrs == NULL)
+    return 0;
+
+  while (((size_t) (function_addrs - function->function_addrs + 1)
+	  < function->function_addrs_count)
+	 && pc >= (function_addrs + 1)->low
+	 && pc < (function_addrs + 1)->high)
+    ++function_addrs;
+
+  /* We found an inlined call.  */
+
+  inlined = function_addrs->function;
+
+  /* Report any calls inlined into this one.  */
+  ret = report_inlined_functions (pc, inlined, callback, data,
+				  filename, lineno);
+  if (ret != 0)
+    return ret;
+
+  /* Report this inlined call.  */
+  ret = callback (data, pc, *filename, *lineno, inlined->name);
+  if (ret != 0)
+    return ret;
+
+  /* Our caller will report the caller of the inlined function; tell
+     it the appropriate filename and line number.  */
+  *filename = inlined->caller_filename;
+  *lineno = inlined->caller_lineno;
+
+  return 0;
+}
+
+/* Return the file/line information for a PC using the DWARF mapping
+   we built earlier.  */
+
+static int
+dwarf_fileline (void *fileline_data, uintptr_t pc, backtrace_callback callback,
+		backtrace_error_callback error_callback, void *data)
+{
+  struct dwarf_data *ddata = (struct dwarf_data *) fileline_data;
+  struct unit_addrs *entry;
+  int new_data;
+  struct line *ln;
+  struct function_addrs *function_addrs;
+  struct function *function;
+  const char *filename;
+  int lineno;
+  int ret;
+
+  /* Find an address range that includes PC.  */
+  entry = bsearch (&pc, ddata->addrs, ddata->addrs_count,
+		   sizeof (struct unit_addrs), unit_addrs_search);
+
+  if (entry == NULL)
+    return callback (data, pc, NULL, 0, NULL);
+
+  /* If there are multiple ranges that contain PC, use the last one,
+     in order to produce predictable results.  If we assume that all
+     ranges are properly nested, then the last range will be the
+     smallest one.  */
+  while (pc >= (entry + 1)->low && pc < (entry + 1)->high)
+    ++entry;
+
+  /* We parse the line number information as we need it, so in case we
+     are multi-threaded we need to lock.  */
+  if (!backtrace_lock (error_callback, data))
+    return 0;
+
+  /* Skip units with no useful line number information.  Note that we
+     can only look at the lines_count field of an arbitrary unit while
+     holding the lock.  */
+
+  while (entry->u->lines_count == (size_t) -1
+	 && (size_t) (entry - ddata->addrs) < ddata->addrs_count
+	 && (entry + 1)->low <= pc
+	 && (entry + 1)->high > pc)
+    ++entry;
+
+  new_data = 0;
+  if (entry->u->lines == NULL && entry->u->lines_count != (size_t) -1)
+    {
+      struct line_header lhdr;
+
+      if (read_line_info (ddata, error_callback, data, entry->u, &lhdr))
+	{
+	  read_function_info (ddata, &lhdr, error_callback, data, entry->u);
+	  free_line_header (&lhdr, 0);
+	}
+      new_data = 1;
+    }
+
+  if (!backtrace_unlock (error_callback, data))
+    return 0;
+
+  /* Now that we've unlocked we can't look at an arbitrary unit, but
+     we can safely look at entry->u.  */
+
+  if (entry->u->lines_count == (size_t) -1)
+    {
+      /* If reading the line number information failed in some way,
+	 try again to see if there is a better compilation unit for
+	 this PC.  */
+      if (new_data)
+	dwarf_fileline (ddata, pc, callback, error_callback, data);
+      return callback (data, pc, NULL, 0, NULL);
+    }
+
+  /* Search for PC within this unit.  */
+
+  ln = (struct line *) bsearch (&pc, entry->u->lines, entry->u->lines_count,
+				sizeof (struct line), line_search);
+  if (ln == NULL)
+    {
+      error_callback (data, "inconsistent DWARF line number info", 0);
+      return 0;
+    }
+
+  /* Search for function name within this unit.  */
+
+  if (entry->u->function_addrs_count == 0)
+    return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+  function_addrs = ((struct function_addrs *)
+		    bsearch (&pc, entry->u->function_addrs,
+			     entry->u->function_addrs_count,
+			     sizeof (struct function_addrs),
+			     function_addrs_search));
+  if (function_addrs == NULL)
+    return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+  /* If there are multiple function ranges that contain PC, use the
+     last one, in order to produce predictable results.  */
+
+  while (((size_t) (function_addrs - entry->u->function_addrs + 1)
+	  < entry->u->function_addrs_count)
+	 && pc >= (function_addrs + 1)->low
+	 && pc < (function_addrs + 1)->high)
+    ++function_addrs;
+
+  function = function_addrs->function;
+
+  filename = ln->filename;
+  lineno = ln->lineno;
+
+  ret = report_inlined_functions (pc, function, callback, data,
+				  &filename, &lineno);
+  if (ret != 0)
+    return ret;
+
+  return callback (data, pc, filename, lineno, function->name);
+}
+
+/* Build our data structures from the .debug_info and .debug_line
+   sections.  Set *FILELINE_FN and *FILELINE_DATA.  Return 1 on
+   success, 0 on failure.  */
+
+int
+backtrace_dwarf_initialize (unsigned char *dwarf_info, size_t dwarf_info_size,
+			    unsigned char *dwarf_line, size_t dwarf_line_size,
+			    unsigned char *dwarf_abbrev,
+			    size_t dwarf_abbrev_size,
+			    unsigned char *dwarf_ranges,
+			    size_t dwarf_ranges_size,
+			    unsigned char *dwarf_str, size_t dwarf_str_size,
+			    int is_bigendian,
+			    backtrace_error_callback error_callback,
+			    void *data, fileline *fileline_fn,
+			    void **fileline_data)
+{
+  struct unit_addrs_vector addrs;
+  struct dwarf_data *fdata;
+
+  if (!build_address_map (dwarf_info, dwarf_info_size, dwarf_abbrev,
+			  dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size,
+			  dwarf_str, dwarf_str_size, is_bigendian,
+			  error_callback, data, &addrs))
+    goto fail;
+
+  /* All the abbrevs we care about are now stored with the units.  */
+  free (dwarf_abbrev);
+  dwarf_abbrev = NULL;
+
+  /* Release any memory we allocated but do not need.  */
+  addrs.addrs = ((struct unit_addrs *)
+		 realloc (addrs.addrs,
+			  addrs.count * sizeof (struct unit_addrs)));
+  if (addrs.addrs == NULL)
+    {
+      error_callback (data, "realloc", errno);
+      goto fail;
+    }
+
+  qsort (addrs.addrs, addrs.count, sizeof (struct unit_addrs),
+	 unit_addrs_compare);
+
+  fdata = malloc (sizeof (struct dwarf_data));
+  if (fdata == NULL)
+    {
+      error_callback (data, "malloc", errno);
+      goto fail;
+    }
+
+  fdata->addrs = addrs.addrs;
+  fdata->addrs_count = addrs.count;
+  fdata->dwarf_info = dwarf_info;
+  fdata->dwarf_info_size = dwarf_info_size;
+  fdata->dwarf_line = dwarf_line;
+  fdata->dwarf_line_size = dwarf_line_size;
+  fdata->dwarf_ranges = dwarf_ranges;
+  fdata->dwarf_ranges_size = dwarf_ranges_size;
+  fdata->dwarf_str = dwarf_str;
+  fdata->dwarf_str_size = dwarf_str_size;
+  fdata->is_bigendian = is_bigendian;
+
+  *fileline_fn = dwarf_fileline;
+  *fileline_data = fdata;
+
+  return 1;
+
+ fail:
+  free (addrs.addrs);
+  free (dwarf_info);
+  free (dwarf_line);
+  free (dwarf_ranges);
+  free (dwarf_abbrev);
+  free (dwarf_str);
+  return 0;
+}
Index: libbacktrace/nothread.c
===================================================================
--- libbacktrace/nothread.c	(revision 0)
+++ libbacktrace/nothread.c	(revision 0)
@@ -0,0 +1,58 @@
+/* nothread.c -- Non-threading routines for the backtrace library.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The backtrace lock and unlock routines when not using threads.  */
+
+/* Acquire the lock.  */
+
+int
+backtrace_lock (backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+		void *data ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
+
+/* Release the lock.  */
+
+int
+backtrace_unlock (backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+		  void *data ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
Index: libbacktrace/elf.c
===================================================================
--- libbacktrace/elf.c	(revision 0)
+++ libbacktrace/elf.c	(revision 0)
@@ -0,0 +1,399 @@
+/* elf.c -- Get debug data from an ELF file for backtraces.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The configure script must tell us whether we are 32-bit or 64-bit
+   ELF.  We could make this code test and support either possibility,
+   but there is no point.  This code only works for the currently
+   running executable, which means that we know the ELF mode at
+   configure mode.  */
+
+#if BACKTRACE_ELF_SIZE != 32 && BACKTRACE_ELF_SIZE != 64
+#error "Unknown BACKTRACE_ELF_SIZE"
+#endif
+
+/* Basic types.  */
+
+typedef uint16_t Elf_Half;
+typedef uint32_t Elf_Word;
+typedef int32_t  Elf_Sword;
+
+#if BACKTRACE_ELF_SIZE == 32
+
+typedef uint32_t Elf_Addr;
+typedef uint32_t Elf_Off;
+
+typedef uint32_t Elf_WXword;
+
+#else
+
+typedef uint64_t Elf_Addr;
+typedef uint64_t Elf_Off;
+typedef uint64_t Elf_Xword;
+typedef int64_t  Elf_Sxword;
+
+typedef uint64_t Elf_WXword;
+
+#endif
+
+/* Data structures and associated constants.  */
+
+#define EI_NIDENT 16
+
+typedef struct {
+  unsigned char	e_ident[EI_NIDENT];	/* ELF "magic number" */
+  Elf_Half	e_type;			/* Identifies object file type */
+  Elf_Half	e_machine;		/* Specifies required architecture */
+  Elf_Word	e_version;		/* Identifies object file version */
+  Elf_Addr	e_entry;		/* Entry point virtual address */
+  Elf_Off	e_phoff;		/* Program header table file offset */
+  Elf_Off	e_shoff;		/* Section header table file offset */
+  Elf_Word	e_flags;		/* Processor-specific flags */
+  Elf_Half	e_ehsize;		/* ELF header size in bytes */
+  Elf_Half	e_phentsize;		/* Program header table entry size */
+  Elf_Half	e_phnum;		/* Program header table entry count */
+  Elf_Half	e_shentsize;		/* Section header table entry size */
+  Elf_Half	e_shnum;		/* Section header table entry count */
+  Elf_Half	e_shstrndx;		/* Section header string table index */
+} Elf_Ehdr;
+
+#define EI_MAG0 0
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+
+#define ELFMAG0 0x7f
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EV_CURRENT 1
+
+typedef struct {
+  Elf_Word	sh_name;		/* Section name, index in string tbl */
+  Elf_Word	sh_type;		/* Type of section */
+  Elf_WXword	sh_flags;		/* Miscellaneous section attributes */
+  Elf_Addr	sh_addr;		/* Section virtual addr at execution */
+  Elf_Off	sh_offset;		/* Section file offset */
+  Elf_WXword	sh_size;		/* Size of section in bytes */
+  Elf_Word	sh_link;		/* Index of another section */
+  Elf_Word	sh_info;		/* Additional section information */
+  Elf_WXword	sh_addralign;		/* Section alignment */
+  Elf_WXword	sh_entsize;		/* Entry size if section holds table */
+} Elf_Shdr;
+
+#define SHN_LORESERVE	0xFF00		/* Begin range of reserved indices */
+#define SHN_XINDEX	0xFFFF		/* Section index is held elsewhere */
+
+/* Read the contents of a section into a buffer allocated in
+   memory.  */
+
+static unsigned char *
+read_section (int descriptor, const Elf_Shdr *shdr,
+	      backtrace_error_callback error_callback, void *data)
+{
+  unsigned char *ret;
+
+  ret = (unsigned char *) malloc (shdr->sh_size);
+  if (ret == NULL)
+    {
+      error_callback (data, "malloc", errno);
+      return NULL;
+    }
+
+  if (!backtrace_read (descriptor, shdr->sh_offset, ret, shdr->sh_size,
+		       error_callback, data))
+    {
+      free (ret);
+      return NULL;
+    }
+
+  return ret;
+}
+
+/* Initialize the backtrace data we need from an ELF executable.  At
+   the ELF level, all we need to do is find the debug info
+   sections.  */
+
+int
+backtrace_initialize (int descriptor, backtrace_error_callback error_callback,
+		      void *data, fileline *fileline_fn, void **fileline_data)
+{
+  Elf_Ehdr ehdr;
+  off_t shoff;
+  unsigned int shnum;
+  unsigned int shstrndx;
+  Elf_Shdr *shdrs;
+  Elf_Shdr *shstrhdr;
+  size_t shstr_size;
+  off_t shstr_off;
+  char *names;
+  unsigned int i;
+  unsigned char *dwarf_info;
+  size_t dwarf_info_size;
+  unsigned char *dwarf_line;
+  size_t dwarf_line_size;
+  unsigned char *dwarf_abbrev;
+  size_t dwarf_abbrev_size;
+  unsigned char *dwarf_ranges;
+  size_t dwarf_ranges_size;
+  unsigned char *dwarf_str;
+  size_t dwarf_str_size;
+
+  names = NULL;
+  shdrs = NULL;
+  dwarf_info = NULL;
+  dwarf_line = NULL;
+  dwarf_abbrev = NULL;
+  dwarf_ranges = NULL;
+  dwarf_str = NULL;
+
+  if (!backtrace_read (descriptor, 0, &ehdr, sizeof ehdr,
+		       error_callback, data))
+    return 0;
+
+  if (ehdr.e_ident[EI_MAG0] != ELFMAG0
+      || ehdr.e_ident[EI_MAG1] != ELFMAG1
+      || ehdr.e_ident[EI_MAG2] != ELFMAG2
+      || ehdr.e_ident[EI_MAG3] != ELFMAG3)
+    {
+      error_callback (data, "executable file is not ELF", 0);
+      return 0;
+    }
+  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "executable file is unrecognized ELF version", 0);
+      return 0;
+    }
+
+#if BACKTRACE_ELF_SIZE == 32
+#define BACKTRACE_ELFCLASS ELFCLASS32
+#else
+#define BACKTRACE_ELFCLASS ELFCLASS64
+#endif
+
+  if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+    {
+      error_callback (data, "executable file is unexpected ELF class", 0);
+      return 0;
+    }
+
+  if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB
+      && ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
+    {
+      error_callback (data, "executable file has unknown endianness", 0);
+      return 0;
+    }
+
+  shoff = ehdr.e_shoff;
+  shnum = ehdr.e_shnum;
+  shstrndx = ehdr.e_shstrndx;
+
+  if ((shnum == 0 || shstrndx == SHN_XINDEX)
+      && shoff != 0)
+    {
+      Elf_Shdr shdr;
+
+      if (!backtrace_read (descriptor, shoff, &shdr, sizeof shdr,
+			   error_callback, data))
+	return 0;
+
+      if (shnum == 0)
+	shnum = shdr.sh_size;
+
+      if (shstrndx == SHN_XINDEX)
+	{
+	  shstrndx = shdr.sh_link;
+
+	  /* Versions of the GNU binutils between 2.12 and 2.18 did
+	     not handle objects with more than SHN_LORESERVE sections
+	     correctly.  All large section indexes were offset by
+	     0x100.  There is more information at
+	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
+	     Fortunately these object files are easy to detect, as the
+	     GNU binutils always put the section header string table
+	     near the end of the list of sections.  Thus if the
+	     section header string table index is larger than the
+	     number of sections, then we know we have to subtract
+	     0x100 to get the real section index.  */
+	  if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100)
+	    shstrndx -= 0x100;
+	}
+    }
+
+  /* To translate PC to file/line when using DWARF, we need to find
+     the .debug_info and .debug_line sections.  */
+
+  /* Read the section headers, skipping the first one.  */
+
+  shdrs = (Elf_Shdr *) malloc ((shnum - 1) * sizeof (Elf_Shdr));
+  if (shdrs == NULL)
+    {
+      error_callback (data, "malloc", errno);
+      goto fail;
+    }
+
+  if (!backtrace_read (descriptor, shoff + sizeof (Elf_Shdr), shdrs,
+		       (shnum - 1) * sizeof (Elf_Shdr), error_callback, data))
+    goto fail;
+
+  /* Read the section names.  */
+
+  shstrhdr = &shdrs[shstrndx - 1];
+  shstr_size = shstrhdr->sh_size;
+  shstr_off = shstrhdr->sh_offset;
+
+  names = (char *) malloc (shstr_size);
+  if (names == NULL)
+    {
+      error_callback (data, "malloc", errno);
+      goto fail;
+    }
+  
+  if (!backtrace_read (descriptor, shstr_off, names, shstr_size,
+		       error_callback, data))
+    goto fail;
+
+  dwarf_info_size = 0;
+  dwarf_line_size = 0;
+  dwarf_abbrev_size = 0;
+  dwarf_ranges_size = 0;
+  dwarf_str_size = 0;
+  for (i = 1; i < shnum; ++i)
+    {
+      Elf_Shdr *shdr;
+      unsigned int sh_name;
+      const char *name;
+
+      shdr = &shdrs[i - 1];
+      sh_name = shdr->sh_name;
+      if (sh_name >= shstr_size)
+	{
+	  error_callback (data, "ELF section name out of range", 0);
+	  goto fail;
+	}
+
+      name = names + sh_name;
+
+      if (strcmp (name, ".debug_info") == 0)
+	{
+	  dwarf_info = read_section (descriptor, shdr, error_callback, data);
+	  if (dwarf_info == NULL)
+	    goto fail;
+	  dwarf_info_size = shdr->sh_size;
+	}
+      else if (strcmp (name, ".debug_line") == 0)
+	{
+	  dwarf_line = read_section (descriptor, shdr, error_callback, data);
+	  if (dwarf_line == NULL)
+	    goto fail;
+	  dwarf_line_size = shdr->sh_size;
+	}
+      else if (strcmp (name, ".debug_abbrev") == 0)
+	{
+	  dwarf_abbrev = read_section (descriptor, shdr, error_callback, data);
+	  if (dwarf_abbrev == NULL)
+	    goto fail;
+	  dwarf_abbrev_size = shdr->sh_size;
+	}
+      else if (strcmp (name, ".debug_ranges") == 0)
+	{
+	  dwarf_ranges = read_section (descriptor, shdr, error_callback, data);
+	  if (dwarf_ranges == NULL)
+	    goto fail;
+	  dwarf_ranges_size = shdr->sh_size;
+	}
+      else if (strcmp (name, ".debug_str") == 0)
+	{
+	  dwarf_str = read_section (descriptor, shdr, error_callback, data);
+	  if (dwarf_str == NULL)
+	    goto fail;
+	  dwarf_str_size = shdr->sh_size;
+	}
+
+      /* FIXME: Need to handle compressed debug sections.  */
+    }
+
+  free (names);
+  names = NULL;
+  free (shdrs);
+  shdrs = NULL;
+
+  /* We've read all we need from the executable.  */
+  if (!backtrace_close (descriptor, error_callback, data))
+    goto fail;
+
+  /* Note that we don't necessarily need dwarf_ranges.  */
+  if (dwarf_info == NULL || dwarf_line == NULL || dwarf_abbrev == NULL
+      || dwarf_str == NULL)
+    {
+      error_callback (data, "missing debug data in executable", 0);
+      goto fail;
+    }
+
+  return backtrace_dwarf_initialize (dwarf_info, dwarf_info_size,
+				     dwarf_line, dwarf_line_size,
+				     dwarf_abbrev, dwarf_abbrev_size,
+				     dwarf_ranges, dwarf_ranges_size,
+				     dwarf_str, dwarf_str_size,
+				     ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+				     error_callback, data,
+				     fileline_fn, fileline_data);
+
+ fail:
+  free (dwarf_info);
+  free (dwarf_line);
+  free (dwarf_abbrev);
+  free (dwarf_ranges);
+  free (dwarf_str);
+  free (names);
+  free (shdrs);
+  return 0;
+}
Index: libbacktrace/thread.c
===================================================================
--- libbacktrace/thread.c	(revision 0)
+++ libbacktrace/thread.c	(revision 0)
@@ -0,0 +1,77 @@
+/* thread.c -- Threading routines for the backtrace library.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <pthread.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The backtrace lock and unlock routines when using threads.  */
+
+/* The lock.  */
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Acquire the lock.  */
+
+int
+backtrace_lock (backtrace_error_callback error_callback, void *data)
+{
+  int errnum;
+
+  errnum = pthread_mutex_lock (&lock);
+  if (errnum != 0)
+    {
+      error_callback (data, "pthread_mutex_lock", errnum);
+      return 0;
+    }
+  return 1;
+}
+
+/* Release the lock.  */
+
+int
+backtrace_unlock (backtrace_error_callback error_callback, void *data)
+{
+  int errnum;
+
+  errnum = pthread_mutex_unlock (&lock);
+  if (errnum != 0)
+    {
+      error_callback (data, "pthread_mutex_unlock", errnum);
+      return 0;
+    }
+  return 1;
+}
Index: libbacktrace/backtrace.c
===================================================================
--- libbacktrace/backtrace.c	(revision 0)
+++ libbacktrace/backtrace.c	(revision 0)
@@ -0,0 +1,104 @@
+/* backtrace.c -- Entry point for stack backtrace library.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include "unwind.h"
+#include "backtrace.h"
+
+/* The main backtrace routine.  */
+
+/* Data passed through _Unwind_Backtrace.  */
+
+struct backtrace_data
+{
+  /* Number of frames to skip.  */
+  int skip;
+  /* Callback routine.  */
+  backtrace_callback callback;
+  /* Error callback routine.  */
+  backtrace_error_callback error_callback;
+  /* Data to pass to callback routines.  */
+  void *data;
+  /* Value to return from backtrace.  */
+  int ret;
+};
+
+/* Unwind library callback routine.  This is passed to
+   _Unwind_Backtrace.  */
+
+static _Unwind_Reason_Code
+unwind (struct _Unwind_Context *context, void *vdata)
+{
+  struct backtrace_data *bdata = (struct backtrace_data *) vdata;
+  uintptr_t pc;
+  int ip_before_insn = 0;
+
+#ifdef HAVE_GETIPINFO
+  pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+  pc = _Unwind_GetIP (context);
+#endif
+
+  if (bdata->skip > 0)
+    {
+      --bdata->skip;
+      return _URC_NO_REASON;
+    }
+
+  if (!ip_before_insn)
+    --pc;
+
+  bdata->ret = backtrace_pcinfo (pc, bdata->callback, bdata->error_callback,
+				 bdata->data);
+  if (bdata->ret != 0)
+    return _URC_END_OF_STACK;
+
+  return _URC_NO_REASON;
+}
+
+/* Get a stack backtrace.  */
+
+int
+backtrace (int skip, backtrace_callback callback,
+	   backtrace_error_callback error_callback, void *data)
+{
+  struct backtrace_data bdata;
+
+  bdata.skip = skip + 1;
+  bdata.callback = callback;
+  bdata.error_callback = error_callback;
+  bdata.data = data;
+  bdata.ret = 0;
+  _Unwind_Backtrace (unwind, &bdata);
+  return bdata.ret;
+}
Index: libbacktrace/config.h.in
===================================================================
--- libbacktrace/config.h.in	(revision 0)
+++ libbacktrace/config.h.in	(revision 0)
@@ -0,0 +1,22 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* ELF size: 32 or 64 */
+#undef BACKTRACE_ELF_SIZE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
Index: libbacktrace/internal.h
===================================================================
--- libbacktrace/internal.h	(revision 0)
+++ libbacktrace/internal.h	(revision 0)
@@ -0,0 +1,117 @@
+/* internal.h -- Internal header file for stack backtrace library.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#ifndef BACKTRACE_INTERNAL_H
+#define BACKTRACE_INTERNAL_H
+
+/* We assume that <sys/types.h> and "backtrace.h" have already been
+   included.  */
+
+#ifndef GCC_VERSION
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif
+
+#if (GCC_VERSION < 2007)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+/* Lock the mutex that controls shared data structures.  Returns 1 on
+   success, 0 on error.  */
+
+extern int backtrace_lock (backtrace_error_callback error_callback,
+			   void *data);
+
+/* Unlock the mutex that controls shared data structures.  Returns 1
+   on success, 0 on error.  */
+
+extern int backtrace_unlock (backtrace_error_callback error_callback,
+			     void *data);
+
+/* Open a file for reading.  Returns -1 on error.  */
+extern int backtrace_open (const char *filename,
+			   backtrace_error_callback error_callback,
+			   void *data);
+
+/* Read SIZE bytes from DESCRIPTOR at OFFSET.  Returns 1 on success, 0
+   on error.  */
+extern int backtrace_read (int descriptor, off_t offset, void *buffer,
+			   size_t size,
+			   backtrace_error_callback error_callback,
+			   void *data);
+
+/* Close a file opened by backtrace_open.  Returns 1 on success, 0 on
+   error.  */
+
+extern int backtrace_close (int descriptor,
+			    backtrace_error_callback error_callback,
+			    void *data);
+
+/* The type of the function that collects file/line information.  This
+   is like backtrace_pcinfo.  */
+
+typedef int (*fileline) (void *fileline_data, uintptr_t pc,
+			 backtrace_callback callback,
+			 backtrace_error_callback error_callback, void *data);
+
+/* Read initial debug data from a descriptor.  This is called after
+   the descriptor has first been opened, with backtrace_lock held.
+   Returns 1 on success, 0 on error.  There will be multiple
+   implementations of this function, for different file formats.  Each
+   system will compile the appropriate one.  */
+
+extern int backtrace_initialize (int descriptor,
+				 backtrace_error_callback error_callback,
+				 void *data, fileline *fn,
+				 void **fileline_data);
+
+/* Prepare to read file/line information from DWARF debug data.  */
+
+extern int backtrace_dwarf_initialize (unsigned char* dwarf_info,
+				       size_t dwarf_info_size,
+				       unsigned char *dwarf_line,
+				       size_t dwarf_line_size,
+				       unsigned char *dwarf_abbrev,
+				       size_t dwarf_abbrev_size,
+				       unsigned char *dwarf_ranges,
+				       size_t dwarf_range_size,
+				       unsigned char *dwarf_str,
+				       size_t dwarf_str_size,
+				       int is_bigendian,
+				       backtrace_error_callback error_callback,
+				       void *data,
+				       fileline *fn, void **fileline_data);
+
+#endif
Index: libbacktrace/filetype.awk
===================================================================
--- libbacktrace/filetype.awk	(revision 0)
+++ libbacktrace/filetype.awk	(revision 0)
@@ -0,0 +1,3 @@
+# An awk script to determine the type of a file.
+/\177ELF\001/ { if (NR == 1) { print "elf32"; exit } }
+/\177ELF\002/ { if (NR == 1) { print "elf64"; exit } }
Index: libbacktrace/configure.ac
===================================================================
--- libbacktrace/configure.ac	(revision 0)
+++ libbacktrace/configure.ac	(revision 0)
@@ -0,0 +1,165 @@
+# configure.ac -- Backtrace configure script.
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+
+#     (1) Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer. 
+
+#     (2) Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.  
+    
+#     (3) The name of the author may not be used to
+#     endorse or promote products derived from this software without
+#     specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+AC_PREREQ(2.64)
+AC_INIT(package-unused, version-unused,, libbacktrace)
+AC_CONFIG_SRCDIR(backtrace.h)
+AC_CONFIG_HEADER(config.h)
+
+AC_CANONICAL_SYSTEM
+target_alias=${target_alias-$host_alias}
+
+AM_INIT_AUTOMAKE([1.11.1 foreign no-dist no-define -Wall -Wno-portability])
+
+if test -n "${with_target_subdir}"; then
+  AM_ENABLE_MULTILIB(, ..)
+fi
+
+AC_ARG_ENABLE([backtrace-threads],
+  AC_HELP_STRING([--enable-backtrace-threads],
+		 [Specify that the backtrace library will be used by threaded programs]),
+  [case "$enableval" in
+   yes) backtrace_threads=yes ;;
+   no) backtrace_threads=no ;;
+   *) AC_MSG_ERROR([Unknown argument to --enable-backtrace-threads/--disable-backtrace-threads]) ;;
+   esac],
+   [backtrace_threads=no])
+
+# We must force CC to /not/ be precious variables; otherwise
+# the wrong, non-multilib-adjusted value will be used in multilibs.
+# As a side effect, we have to subst CFLAGS ourselves.
+m4_rename([_AC_ARG_VAR_PRECIOUS],[backtrace_PRECIOUS])
+m4_define([_AC_ARG_VAR_PRECIOUS],[])
+AC_PROG_CC
+m4_rename_force([backtrace_PRECIOUS],[_AC_ARG_VAR_PRECIOUS])
+
+AC_SUBST(CFLAGS)
+
+AC_PROG_RANLIB
+
+AC_PROG_AWK
+case "$AWK" in
+"") AC_MSG_ERROR([can't build without awk]) ;;
+esac
+
+ACX_PROG_CC_WARNING_OPTS([-W -Wall -Wwrite-strings -Wstrict-prototypes \
+			  -Wmissing-prototypes -Wold-style-definition \
+			  -Wmissing-format-attribute -Wcast-qual],
+			  [WARN_FLAGS])
+
+if test "x$GCC" = "xyes"; then
+  WARN_FLAGS="$WARN_FLAGS -Werror"
+fi
+
+AC_SUBST(WARN_FLAGS)
+
+AM_MAINTAINER_MODE
+
+# The library needs to be able to read the executable itself.  Compile
+# a file to determine the executable format.  The awk script
+# filetype.awk prints out the file type.
+AC_CACHE_CHECK([output filetype],
+[libbacktrace_cv_sys_filetype],
+[filetype=
+AC_COMPILE_IFELSE(
+  [AC_LANG_PROGRAM([int i;], [int j;])],
+  [filetype=`${AWK} -f $srcdir/filetype.awk conftest.$ac_objext`],
+  [AC_MSG_FAILURE([compiler failed])])
+libbacktrace_cv_sys_filetype=$filetype])
+
+# Match the file type to decide what files to compile.
+FORMAT_FILE=
+case "$libbacktrace_cv_sys_filetype" in
+elf*) FORMAT_FILE="elf.$ac_objext" ;;
+*) AC_MSG_WARN([could not determine output file type])
+   FORMAT_FILE="unknown.$ac_objext" ;;
+esac
+AC_SUBST(FORMAT_FILE)
+
+if test "$backtrace_threads" = "yes"; then
+  THREAD_FILE=thread.$ac_objext
+else
+  THREAD_FILE=nothread.$ac_objext
+fi
+AC_SUBST(THREAD_FILE)
+
+# ELF defines.
+elfsize=
+case "$libbacktrace_cv_sys_filetype" in
+elf32) elfsize=32 ;;
+elf64) elfsize=64 ;;
+esac
+AC_DEFINE_UNQUOTED([BACKTRACE_ELF_SIZE], [$elfsize], [ELF size: 32 or 64])
+
+if test "${multilib}" = "yes"; then
+  multilib_arg="--enable-multilib"
+else
+  multilib_arg=
+fi
+
+AC_CONFIG_FILES(Makefile)
+
+# We need multilib support, but only if configuring for the target.
+AC_CONFIG_COMMANDS([default],
+[if test -n "$CONFIG_FILES"; then
+   if test -n "${with_target_subdir}"; then
+     # Multilibs need MULTISUBDIR defined correctly in certain makefiles so
+     # that multilib installs will end up installed in the correct place.
+     # The testsuite needs it for multilib-aware ABI baseline files.
+     # To work around this not being passed down from config-ml.in ->
+     # srcdir/Makefile.am -> srcdir/{src,libsupc++,...}/Makefile.am, manually
+     # append it here.  Only modify Makefiles that have just been created.
+     #
+     # Also, get rid of this simulated-VPATH thing that automake does.
+     cat > vpsed << \_EOF
+  s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+     for i in $SUBDIRS; do
+      case $CONFIG_FILES in
+       *${i}/Makefile*)
+	 #echo "Adding MULTISUBDIR to $i/Makefile"
+	 sed -f vpsed $i/Makefile > tmp
+	 grep '^MULTISUBDIR =' Makefile >> tmp
+	 mv tmp $i/Makefile
+	 ;;
+      esac
+     done
+     rm vpsed
+   fi
+ fi
+],
+[
+# Variables needed in config.status (file generation) which aren't already
+# passed by autoconf.
+SUBDIRS="$SUBDIRS"
+])
+
+AC_OUTPUT
Index: libbacktrace/backtrace.h
===================================================================
--- libbacktrace/backtrace.h	(revision 0)
+++ libbacktrace/backtrace.h	(revision 0)
@@ -0,0 +1,128 @@
+/* backtrace.h -- Public header file for stack backtrace library.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#ifndef BACKTRACE_H
+#define BACKTRACE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The backtrace code needs to open the executable file in order to
+   find the debug info.  On systems that do not support
+   /proc/self/exe, the program using the backtrace library needs to
+   tell the backtrace library the name of the executable to open.  It
+   does so by calling backtrace_set_executable_name.  The FILENAME
+   argument must point to a permanent buffer.  */
+
+extern void backtrace_set_executable_name (const char *filename);
+
+/* The type of the callback argument to the backtrace function.  DATA
+   is the argument passed to backtrace.  PC is the program counter.
+   FILENAME is the name of the file containing PC, or NULL if not
+   available.  LINENO is the line number in FILENAME containing PC, or
+   0 if not available.  FUNCTION is the name of the function
+   containing PC, or NULL if not available.  This should return 0 to
+   continuing tracing.  The FILENAME and FUNCTION buffers may become
+   invalid after this function returns.  */
+
+typedef int (*backtrace_callback) (void *data, uintptr_t pc,
+				   const char *filename, int lineno,
+				   const char *function);
+
+/* The type of the error callback argument to backtrace functions.
+   This function, if not NULL, will be called for certain error cases.
+   The DATA argument is passed to the function that calls this one.
+   The MSG argument is an error message.  The ERRNUM argument, if not
+   0, holds an errno value.  The MSG buffer may become invalid after
+   this function returns.  */
+
+typedef void (*backtrace_error_callback) (void *data, const char *msg,
+					  int errnum);
+
+/* Get a full stack backtrace.  SKIP is the number of frames to skip;
+   passing 0 will start the trace with the function calling backtrace.
+   DATA is passed to the callback routine.  If any call to CALLBACK
+   returns a non-zero value, the stack backtrace stops, and backtrace
+   returns that value; this may be used to limit the number of stack
+   frames desired.  If all calls to CALLBACK return 0, backtrace
+   returns 0.  The backtrace function will make at least one call to
+   either CALLBACK or ERROR_CALLBACK.  */
+
+extern int backtrace (int skip, backtrace_callback callback,
+		      backtrace_error_callback error_callback, void *data);
+
+/* The type of the callback argument to the simple backtrace function.
+   DATA is the argument passed to simple_backtrace.  PC is the program
+   counter.  This should return 0 to continue tracing.  */
+
+typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
+
+/* Get a simple backtrace.  SKIP is the number of frames to skip, as
+   in backtrace.  DATA is passed to the callback routine.  If any call
+   to CALLBACK returns a non-zero value, the stack backtrace stops,
+   and simple_backtrace returns that value.  Otherwise
+   simple_backtrace returns 0.  The backtrace_simple function will
+   make at least one call to either CALLBACK or ERROR_CALLBACK.  */
+
+extern int backtrace_simple (int skip, backtrace_simple_callback callback,
+			     backtrace_error_callback error_callback,
+			     void *data);
+
+/* Print the current backtrace in a user readable format to a FILE.
+   SKIP is the number of frames to skip, as in backtrace.  Any error
+   messages are printed to stderr.  */
+
+extern void backtrace_print (int skip, FILE *);
+
+/* Given PC, a program counter in the current program, call the
+   callback function with filename, line number, and function name
+   information.  This will normally call the callback function exactly
+   once.  However, if the PC happens to describe an inlined call, and
+   the debugging information contains the necessary information, then
+   this may call the callback function multiple times.  This will make
+   at least one call to either CALLBACK or ERROR_CALLBACK.  This
+   returns the first non-zero value returned by CALLBACK, or 0.  */
+
+extern int backtrace_pcinfo (uintptr_t pc, backtrace_callback callback,
+			     backtrace_error_callback error_callback,
+			     void *data);
+
+#ifdef __cplusplus
+} /* End extern "C".  */
+#endif
+
+#endif
Index: libbacktrace/fileline.c
===================================================================
--- libbacktrace/fileline.c	(revision 0)
+++ libbacktrace/fileline.c	(revision 0)
@@ -0,0 +1,112 @@
+/* fileline.c -- Get file and line number information in a backtrace.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The name of the executable, if the program sets it.  */
+
+static const char *executable_name;
+
+/* Set the name of the executable.  */
+
+void
+backtrace_set_executable_name (const char *name)
+{
+  executable_name = name;
+}
+
+/* The function we call to get file/line information.  */
+
+static fileline fileline_fn;
+
+/* Data to pass to fileline_fn.  */
+
+static void *fileline_data;
+
+/* Set non-zero if we could not find the executable information.  This
+   is used so that if we are called many times we don't keep trying
+   the same failing attempt.  */
+
+static int fileline_initialization_failed;
+
+/* Initialize the fileline information from the executable.  */
+
+static void
+fileline_initialize (backtrace_error_callback error_callback, void *data)
+{
+  int descriptor;
+
+  if (!backtrace_lock (error_callback, data))
+    return;
+
+  if (fileline_fn != NULL || fileline_initialization_failed)
+    return;
+
+  if (executable_name != NULL)
+    descriptor = backtrace_open (executable_name, error_callback, data);
+  else
+    descriptor = backtrace_open ("/proc/self/exe", error_callback, data);
+  if (descriptor < 0)
+    {
+      fileline_initialization_failed = 1;
+      backtrace_unlock (error_callback, data);
+      return;
+    }
+
+  if (!backtrace_initialize (descriptor, error_callback, data,
+			     &fileline_fn, &fileline_data))
+    fileline_initialization_failed = 1;
+
+  if (!backtrace_unlock (error_callback, data))
+    fileline_initialization_failed = 1;
+}
+
+/* Given a PC, find the file name, line number, and function name.  */
+
+int
+backtrace_pcinfo (uintptr_t pc, backtrace_callback callback,
+		  backtrace_error_callback error_callback, void *data)
+{
+  fileline_initialize (error_callback, data);
+
+  if (fileline_initialization_failed || fileline_fn == NULL)
+    return 0;
+
+  return fileline_fn (fileline_data, pc, callback, error_callback, data);
+}
Index: libbacktrace/Makefile.am
===================================================================
--- libbacktrace/Makefile.am	(revision 0)
+++ libbacktrace/Makefile.am	(revision 0)
@@ -0,0 +1,55 @@
+# Makefile.am -- Backtrace Makefile.
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+
+#     (1) Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer. 
+
+#     (2) Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.  
+    
+#     (3) The name of the author may not be used to
+#     endorse or promote products derived from this software without
+#     specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+ACLOCAL_AMFLAGS = -I ../config
+
+AM_CFLAGS = $(WARN_FLAGS) -I $(srcdir)/../include
+
+noinst_LIBRARIES = libbacktrace.a
+
+libbacktrace_a_SOURCES = \
+	backtrace.c \
+	backtrace.h \
+	dwarf.c \
+	fileline.c \
+	internal.h \
+	posix.c \
+	print.c \
+	simple.c
+
+EXTRA_libbacktrace_a_SOURCES = \
+	elf.c \
+	thread.c \
+	nothread.c \
+	unknown.c
+
+libbacktrace_a_DEPENDENCIES = $(FORMAT_FILE) $(THREAD_FILE)
+libbacktrace_a_LIBADD = $(FORMAT_FILE) $(THREAD_FILE)
Index: libbacktrace/simple.c
===================================================================
--- libbacktrace/simple.c	(revision 0)
+++ libbacktrace/simple.c	(revision 0)
@@ -0,0 +1,104 @@
+/* simple.c -- The backtrace_simple function.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include "unwind.h"
+#include "backtrace.h"
+
+/* The simple_backtrace routine.  */
+
+/* Data passed through _Unwind_Backtrace.  */
+
+struct backtrace_simple_data
+{
+  /* Number of frames to skip.  */
+  int skip;
+  /* Callback routine.  */
+  backtrace_simple_callback callback;
+  /* Error callback routine.  */
+  backtrace_error_callback error_callback;
+  /* Data to pass to callback routine.  */
+  void *data;
+  /* Value to return from backtrace.  */
+  int ret;
+};
+
+/* Unwind library callback routine.  This is passd to
+   _Unwind_Backtrace.  */
+
+static _Unwind_Reason_Code
+simple_unwind (struct _Unwind_Context *context, void *vdata)
+{
+  struct backtrace_simple_data *bdata = (struct backtrace_simple_data *) vdata;
+  uintptr_t pc;
+  int ip_before_insn = 0;
+
+#ifdef HAVE_GETIPINFO
+  pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+  pc = _Unwind_GetIP (context);
+#endif
+
+  if (bdata->skip > 0)
+    {
+      --bdata->skip;
+      return _URC_NO_REASON;
+    }
+
+  if (!ip_before_insn)
+    --pc;
+
+  bdata->ret = bdata->callback (bdata->data, pc);
+
+  if (bdata->ret != 0)
+    return _URC_END_OF_STACK;
+
+  return _URC_NO_REASON;
+}
+
+/* Get a simple stack backtrace.  */
+
+int
+backtrace_simple (int skip, backtrace_simple_callback callback,
+		  backtrace_error_callback error_callback, void *data)
+{
+  struct backtrace_simple_data bdata;
+
+  bdata.skip = skip;
+  bdata.callback = callback;
+  bdata.error_callback = error_callback;
+  bdata.data = data;
+  bdata.ret = 0;
+  _Unwind_Backtrace (simple_unwind, &bdata);
+  return bdata.ret;
+}
Index: libbacktrace/print.c
===================================================================
--- libbacktrace/print.c	(revision 0)
+++ libbacktrace/print.c	(revision 0)
@@ -0,0 +1,75 @@
+/* print.c -- Print the current backtrace.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Print one level of a backtrace.  */
+
+static int
+print_callback (void *data, uintptr_t pc, const char *filename, int lineno,
+		const char *function)
+{
+  FILE *f = (FILE *) data;
+
+  fprintf (f, "0x%lx %s\n\t%s:%d\n",
+	   (unsigned long) pc,
+	   function == NULL ? "???" : function,
+	   filename == NULL ? "???" : filename,
+	   lineno);
+  return 0;
+}
+
+/* Print errors to stderr.  */
+
+static void
+error_callback (void *data ATTRIBUTE_UNUSED, const char *msg, int errnum)
+{
+  fputs (msg, stderr);
+  if (errnum != 0)
+    fprintf (stderr, ": %s", strerror (errnum));
+  fputc ('\n', stderr);
+}
+
+/* Print a backtrace.  */
+
+void
+backtrace_print (int skip, FILE *f)
+{
+  backtrace (skip + 1, print_callback, error_callback, (void *) f);
+}
Index: configure.ac
===================================================================
--- configure.ac	(revision 190404)
+++ configure.ac	(working copy)
@@ -133,7 +133,7 @@ build_tools="build-texinfo build-flex bu
 
 # these libraries are used by various programs built for the host environment
 #
-host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libcpp libdecnumber gmp mpfr mpc isl cloog libelf libiconv"
+host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libbacktrace libcpp libdecnumber gmp mpfr mpc isl cloog libelf libiconv"
 
 # these tools are built for the host environment
 # Note, the powerpc-eabi build depends on sim occurring before gdb in order to
Index: Makefile.def
===================================================================
--- Makefile.def	(revision 190404)
+++ Makefile.def	(working copy)
@@ -80,6 +80,7 @@ host_modules= { module= tcl;
                 missing=mostlyclean; };
 host_modules= { module= itcl; };
 host_modules= { module= ld; bootstrap=true; };
+host_modules= { module= libbacktrace; bootstrap=true; };
 host_modules= { module= libcpp; bootstrap=true; };
 host_modules= { module= libdecnumber; bootstrap=true; };
 host_modules= { module= libgui; };
@@ -303,6 +304,7 @@ dependencies = { module=all-gcc; on=all-
 dependencies = { module=all-gcc; on=all-build-libiberty; };
 dependencies = { module=all-gcc; on=all-build-fixincludes; };
 dependencies = { module=all-gcc; on=all-zlib; };
+dependencies = { module=all-gcc; on=all-libbacktrace; hard=true; };
 dependencies = { module=all-gcc; on=all-libcpp; hard=true; };
 dependencies = { module=all-gcc; on=all-libdecnumber; hard=true; };
 dependencies = { module=all-gcc; on=all-libiberty; };
Index: diagnostic.c
===================================================================
--- diagnostic.c	(revision 190404)
+++ diagnostic.c	(working copy)
@@ -27,8 +27,10 @@ along with GCC; see the file COPYING3.  
 #include "system.h"
 #include "coretypes.h"
 #include "version.h"
+#include "demangle.h"
 #include "input.h"
 #include "intl.h"
+#include "backtrace.h"
 #include "diagnostic.h"
 
 #define pedantic_warning_kind(DC)			\
@@ -296,6 +298,48 @@ diagnostic_show_locus (diagnostic_contex
   pp_set_prefix (context->printer, saved_prefix);
 }
 
+/* A callback function passed to the backtrace function.  */
+
+static int
+bt_callback (void *data ATTRIBUTE_UNUSED, uintptr_t pc, const char *filename,
+	     int lineno, const char *function)
+{
+  fprintf (stderr, "0x%lx ", pc);
+
+  if (function == NULL)
+    fputs ("???", stderr);
+  else
+    {
+      char *str;
+
+      str = cplus_demangle_v3 (function,
+			       (DMGL_VERBOSE | DMGL_ANSI
+				| DMGL_GNU_V3 | DMGL_PARAMS));
+      if (str == NULL)
+	fputs (function, stderr);
+      else
+	{
+	  fputs (str, stderr);
+	  free (str);
+	}
+    }
+  fputc ('\n', stderr);
+
+  fprintf (stderr, "\t%s:%d\n", filename == NULL ? "???" : filename, lineno);
+
+  return 0;
+}
+
+/* A callback function passed to the backtrace function.  This is
+   called if backtrace has an error.  */
+
+static void
+bt_err_callback (void *data ATTRIBUTE_UNUSED, const char *msg, int errnum)
+{
+  fprintf (stderr, "%s%s%s\n", msg, errno == 0 ? "" : ": ",
+	   errno == 0 ? "" : xstrerror (errnum));
+}
+
 /* Take any action which is expected to happen after the diagnostic
    is written out.  This function does not always return.  */
 static void
@@ -334,6 +378,8 @@ diagnostic_action_after_output (diagnost
       break;
 
     case DK_ICE:
+      backtrace (1, bt_callback, bt_err_callback, NULL);
+
       if (context->abort_on_error)
 	real_abort ();
 
Index: ggc-page.c
===================================================================
--- ggc-page.c	(revision 190404)
+++ ggc-page.c	(working copy)
@@ -1322,7 +1322,7 @@ ggc_internal_alloc_stat (size_t size MEM
 
   /* Calculate the object's address.  */
   result = entry->page + object_offset;
-  if (GATHER_STATISTICS)
+  if (mem_report)
     ggc_record_overhead (OBJECT_SIZE (order), OBJECT_SIZE (order) - size,
 			 result FINAL_PASS_MEM_STAT);
 
@@ -1355,7 +1355,7 @@ ggc_internal_alloc_stat (size_t size MEM
   /* For timevar statistics.  */
   timevar_ggc_mem_total += object_size;
 
-  if (GATHER_STATISTICS)
+  if (mem_report)
     {
       size_t overhead = object_size - size;
 
@@ -1540,7 +1540,7 @@ ggc_free (void *p)
   size_t order = pe->order;
   size_t size = OBJECT_SIZE (order);
 
-  if (GATHER_STATISTICS)
+  if (mem_report)
     ggc_free_overhead (p);
 
   if (GGC_DEBUG_LEVEL >= 3)
@@ -2086,7 +2086,7 @@ ggc_collect (void)
   clear_marks ();
   ggc_mark_roots ();
 
-  if (GATHER_STATISTICS)
+  if (mem_report)
     ggc_prune_overhead_list ();
 
   poison_pages ();
@@ -2176,7 +2176,7 @@ ggc_print_statistics (void)
 	   SCALE (G.allocated), STAT_LABEL(G.allocated),
 	   SCALE (total_overhead), STAT_LABEL (total_overhead));
 
-  if (GATHER_STATISTICS)
+  if (mem_report)
     {
       fprintf (stderr, "\nTotal allocations and overheads during the compilation process\n");
 
Index: ggc-common.c
===================================================================
--- ggc-common.c	(revision 190404)
+++ ggc-common.c	(working copy)
@@ -25,6 +25,8 @@ along with GCC; see the file COPYING3.  
 #include "system.h"
 #include "coretypes.h"
 #include "hashtab.h"
+#include "demangle.h"
+#include "backtrace.h"
 #include "ggc.h"
 #include "ggc-internal.h"
 #include "diagnostic-core.h"
@@ -846,11 +848,10 @@ init_ggc_heuristics (void)
 }
 
 /* Datastructure used to store per-call-site statistics.  */
+#define LOC_ADDRS (20)
 struct loc_descriptor
 {
-  const char *file;
-  int line;
-  const char *function;
+  uintptr_t addrs[LOC_ADDRS];
   int times;
   size_t allocated;
   size_t overhead;
@@ -866,8 +867,13 @@ static hashval_t
 hash_descriptor (const void *p)
 {
   const struct loc_descriptor *const d = (const struct loc_descriptor *) p;
+  int i;
+  hashval_t ret;
 
-  return htab_hash_pointer (d->function) | d->line;
+  ret = 0;
+  for (i = 0; i < LOC_ADDRS; ++i)
+    ret ^= d->addrs[i];
+  return ret;
 }
 
 static int
@@ -876,8 +882,7 @@ eq_descriptor (const void *p1, const voi
   const struct loc_descriptor *const d = (const struct loc_descriptor *) p1;
   const struct loc_descriptor *const d2 = (const struct loc_descriptor *) p2;
 
-  return (d->file == d2->file && d->line == d2->line
-	  && d->function == d2->function);
+  return memcmp (d->addrs, d2->addrs, LOC_ADDRS * sizeof (uintptr_t)) == 0;
 }
 
 /* Hashtable converting address of allocated field to loc descriptor.  */
@@ -906,16 +911,53 @@ eq_ptr (const void *p1, const void *p2)
   return (p->ptr == p2);
 }
 
+/* Struct used to pass data through backtrace_simple.  */
+
+struct loc_data
+{
+  struct loc_descriptor *loc;
+  int index;
+};
+
+/* Callback function for backtrace_simple.  */
+
+static int
+loc_backtrace (void *data, uintptr_t pc)
+{
+  struct loc_data *ldata = (struct loc_data *) data;
+
+  if (ldata->index >= LOC_ADDRS)
+    return 1;
+  ldata->loc->addrs[ldata->index] = pc;
+  ++ldata->index;
+  return 0;
+}
+
+/* Error callback function for backtrace_simple.  */
+
+static void
+loc_backtrace_error (void *data ATTRIBUTE_UNUSED, const char *msg, int errnum)
+{
+  /* FIXME: We should just ignore errors here.  */
+  fprintf (stderr, "%s%s%s\n", msg, errnum == 0 ? "" : ": ",
+	   errnum == 0 ? "" : xstrerror (errnum));
+}
+
 /* Return descriptor for given call site, create new one if needed.  */
 static struct loc_descriptor *
-loc_descriptor (const char *name, int line, const char *function)
+loc_descriptor (const char *name ATTRIBUTE_UNUSED,
+		int line ATTRIBUTE_UNUSED,
+		const char *function ATTRIBUTE_UNUSED)
 {
   struct loc_descriptor loc;
   struct loc_descriptor **slot;
+  struct loc_data ldata;
+
+  memset (&loc, 0, sizeof loc);
+  ldata.loc = &loc;
+  ldata.index = 0;
+  backtrace_simple (5, loc_backtrace, loc_backtrace_error, (void *) &ldata);
 
-  loc.file = name;
-  loc.line = line;
-  loc.function = function;
   if (!loc_hash)
     loc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL);
 
@@ -923,9 +965,7 @@ loc_descriptor (const char *name, int li
   if (*slot)
     return *slot;
   *slot = XCNEW (struct loc_descriptor);
-  (*slot)->file = name;
-  (*slot)->line = line;
-  (*slot)->function = function;
+  memcpy ((*slot)->addrs, loc.addrs, LOC_ADDRS * sizeof (uintptr_t));
   return *slot;
 }
 
@@ -1037,18 +1077,61 @@ add_statistics (void **slot, void *b)
   return 1;
 }
 
+/* Dump callback.  */
+
+static int
+dump_loc_callback (void *data ATTRIBUTE_UNUSED, uintptr_t pc,
+		   const char *filename, int lineno, const char *function)
+{
+  char *str;
+
+  if (function == NULL)
+    str = NULL;
+  else
+    {
+      str = cplus_demangle_v3 (function,
+			       (DMGL_VERBOSE | DMGL_ANSI
+				| DMGL_GNU_V3 | DMGL_PARAMS));
+      if (str != NULL)
+	function = str;
+    }
+  fprintf (stderr, "0x%lx %s:%d %s\n", (unsigned long) pc,
+	   filename == NULL ? "???" : filename,
+	   lineno,
+	   function == NULL ? "???" : function);
+  free (str);
+  return 0;
+}
+
+/* Dump a loc descriptor.  */
+
+static void
+dump_loc (const struct loc_descriptor *d)
+{
+  int i;
+
+  for (i = 0; i < LOC_ADDRS; ++i)
+    {
+      uintptr_t pc = d->addrs[i];
+      if (pc == 0)
+	break;
+      backtrace_pcinfo (pc, dump_loc_callback, loc_backtrace_error, NULL);
+    }
+}
+
 /* Dump per-site memory statistics.  */
 
 void
 dump_ggc_loc_statistics (bool final)
 {
   int nentries = 0;
-  char s[4096];
   size_t collected = 0, freed = 0, allocated = 0, overhead = 0, times = 0;
   int i;
 
+#if 0
   if (! GATHER_STATISTICS)
     return;
+#endif
 
   ggc_force_collect = true;
   ggc_collect ();
@@ -1075,13 +1158,8 @@ dump_ggc_loc_statistics (bool final)
       struct loc_descriptor *d = loc_array[i];
       if (d->allocated)
 	{
-	  const char *s1 = d->file;
-	  const char *s2;
-	  while ((s2 = strstr (s1, "gcc/")))
-	    s1 = s2 + 4;
-	  sprintf (s, "%s:%i (%s)", s1, d->line, d->function);
-	  s[48] = 0;
-	  fprintf (stderr, "%-48s %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li\n", s,
+	  dump_loc (d);
+	  fprintf (stderr, "%10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li\n",
 		   (long)d->collected,
 		   (d->collected) * 100.0 / collected,
 		   (long)d->freed,

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