Patchwork Backtrace library [3/3]

login
register
mail settings
Submitter Ian Taylor
Date Sept. 11, 2012, 10:55 p.m.
Message ID <mcrliggp3uf.fsf@google.com>
Download mbox | patch
Permalink /patch/183211/
State New
Headers show

Comments

Ian Taylor - Sept. 11, 2012, 10:55 p.m.
I have finished the initial implementation of the backtrace library I
proposed at http://gcc.gnu.org/ml/gcc/2012-08/msg00317.html .  I've
separated the work into three patches.  These patches only implement the
backtrace library itself; actual use of the library will follow in
separate patches.

This patch is the actual implementation of libbacktrace.  I would be
happy to have a detailed review of this code but I don't really expect
it.  A quick glance will be satisfactory.  The most complex file is
dwarf.c, the second most complex is elf.c.  Everything else is
straightforward.

Bootstrapped on x86_64-unknown-linux-gnu in conjunction with the other
two patches.  OK for mainline?

Ian


2012-09-11  Ian Lance Taylor  <iant@google.com>

	* Initial implementation.
Jakub Jelinek - Sept. 12, 2012, 5:16 a.m.
On Tue, Sep 11, 2012 at 03:55:04PM -0700, Ian Lance Taylor wrote:
> +/* 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 simply keep a sorted vector,
> +   and try to just look up the code.  */

As soon as you run dwz on it, the abbrevs won't be in numerical order,
and even GCC really should be changed to sort the abbrevs by decreasing
use count if there are more than 127 abbrevs (special casing the base types
that are used for typed DWARF stack).

The patch also doesn't handle DW_TAG_imported_unit/DW_TAG_partial_unit,
which is a show stopper if dwz is used (and
DW_FORM_GNU_strp_alt/DW_FORM_GNU_ref_alt isn't handled properly).

BTW, the library doesn't seem to implement what gdb does, in particular
improved support for tail calls.  In gdb that was (at least);
2011-10-09  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Protect entry values against self tail calls.
...

2011-10-09  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Recognize virtual tail call frames.
...

	Jakub
Florian Weimer - Sept. 12, 2012, 9:01 a.m.
On 09/12/2012 12:55 AM, Ian Lance Taylor wrote:
> I have finished the initial implementation of the backtrace library I
> proposed at http://gcc.gnu.org/ml/gcc/2012-08/msg00317.html .  I've
> separated the work into three patches.  These patches only implement the
> backtrace library itself; actual use of the library will follow in
> separate patches.

I'm trying to add a few comments below.  I hope Thunderbird does not 
garble them too much.

+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);
+    }

You should call fcntl unconditionally.  O_CLOEXEC might be non-zero 
during build, but could still be ignored by the kernel.

+static void
+fileline_initialize (backtrace_error_callback error_callback, void *data)
+{
...
+  if (executable_name != NULL)
+    descriptor = backtrace_open (executable_name, error_callback, data);
+  else
+    descriptor = backtrace_open ("/proc/self/exe", error_callback, data);

You should try getauxval(AT_EXECFN) as well (needs recent glibc), so 
that this works with a mounted /proc.

This library should only be used when getauxval(AT_SECURE) is zero, so 
that the program doesn't try to read files with elevated privileges to 
which the original user wouldn't have access.  I don't think this has to 
be addressed within the library itself.

Adding /usr/lib/debug support shouldn't be too hard, I will try to 
figure out the required path transformations (which are somewhat 
system-specific).
Ian Taylor - Sept. 12, 2012, 2:48 p.m.
On Tue, Sep 11, 2012 at 3:55 PM, Ian Lance Taylor <iant@google.com> wrote:
>
> This patch is the actual implementation of libbacktrace.

This is the updated version of this patch with a state parameter.

Ian
Ian Taylor - Sept. 12, 2012, 2:51 p.m.
On Tue, Sep 11, 2012 at 10:16 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Sep 11, 2012 at 03:55:04PM -0700, Ian Lance Taylor wrote:
>> +/* 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 simply keep a sorted vector,
>> +   and try to just look up the code.  */
>
> As soon as you run dwz on it, the abbrevs won't be in numerical order,
> and even GCC really should be changed to sort the abbrevs by decreasing
> use count if there are more than 127 abbrevs (special casing the base types
> that are used for typed DWARF stack).

OK.  Still, the current code should work correctly.

> The patch also doesn't handle DW_TAG_imported_unit/DW_TAG_partial_unit,
> which is a show stopper if dwz is used (and
> DW_FORM_GNU_strp_alt/DW_FORM_GNU_ref_alt isn't handled properly).
>
> BTW, the library doesn't seem to implement what gdb does, in particular
> improved support for tail calls.  In gdb that was (at least);
> 2011-10-09  Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>         Protect entry values against self tail calls.
> ...
>
> 2011-10-09  Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>         Recognize virtual tail call frames.


Yes.  I think these are issues that can be addressed after the initial
version of the library is approved and committed.

Ian
Ian Taylor - Sept. 12, 2012, 2:54 p.m.
On Wed, Sep 12, 2012 at 2:01 AM, Florian Weimer <fweimer@redhat.com> wrote:
>
> +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);
> +    }
>
> You should call fcntl unconditionally.  O_CLOEXEC might be non-zero during
> build, but could still be ignored by the kernel.

OK, done.

> +static void
> +fileline_initialize (backtrace_error_callback error_callback, void *data)
> +{
> ...
> +  if (executable_name != NULL)
> +    descriptor = backtrace_open (executable_name, error_callback, data);
> +  else
> +    descriptor = backtrace_open ("/proc/self/exe", error_callback, data);
>
> You should try getauxval(AT_EXECFN) as well (needs recent glibc), so that
> this works with a mounted /proc.

I'm going to postpone this--my glibc doesn't have this function.

> This library should only be used when getauxval(AT_SECURE) is zero, so that
> the program doesn't try to read files with elevated privileges to which the
> original user wouldn't have access.  I don't think this has to be addressed
> within the library itself.

The library doesn't try to elevate privileges, so, yes, I think this
is entirely the responsibility of the program calling the library.

> Adding /usr/lib/debug support shouldn't be too hard, I will try to figure
> out the required path transformations (which are somewhat system-specific).

Thanks!

Ian
Diego Novillo - Sept. 13, 2012, 8 p.m.
On 2012-09-12 10:48 , Ian Lance Taylor wrote:
> On Tue, Sep 11, 2012 at 3:55 PM, Ian Lance Taylor <iant@google.com> wrote:
>>
>> This patch is the actual implementation of libbacktrace.
>
> This is the updated version of this patch with a state parameter.

This is OK.

Thank you so much for doing this!  This will help reducing the testing 
matrix a bit.  There should be fewer differences between 
--enable-checking values.

So, now we only need to hook gcc_assert to use this library.  I think we 
want a --param to specify how deep the backtrace should be.  A default 
value of 3-5 stack frames should be good to start, I think.


Diego.

Patch

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/nounwind.c
===================================================================
--- libbacktrace/nounwind.c	(revision 0)
+++ libbacktrace/nounwind.c	(revision 0)
@@ -0,0 +1,64 @@ 
+/* 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 <sys/types.h>
+
+#include "backtrace.h"
+
+#include "internal.h"
+
+/* This source file is compiled if the unwind library is not
+   available.  */
+
+int
+backtrace (int skip ATTRIBUTE_UNUSED,
+	   backtrace_callback callback ATTRIBUTE_UNUSED,
+	   backtrace_error_callback error_callback, void *data)
+{
+  error_callback (data,
+		  "no stack trace because unwind library not available",
+		  0);
+  return 0;
+}
+
+int
+backtrace_simple (int skip ATTRIBUTE_UNUSED,
+		  backtrace_simple_callback callback ATTRIBUTE_UNUSED,
+		  backtrace_error_callback error_callback, void *data)
+{
+  error_callback (data,
+		  "no stack trace because unwind library not available",
+		  0);
+  return 0;
+}
Index: libbacktrace/posix.c
===================================================================
--- libbacktrace/posix.c	(revision 0)
+++ libbacktrace/posix.c	(revision 0)
@@ -0,0 +1,86 @@ 
+/* 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;
+}
+
+/* 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,2659 @@ 
+/* 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 abbrev code--the number used to refer to the abbrev.  */
+  uint64_t code;
+  /* 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;
+};
+
+/* 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 simply keep a sorted vector,
+   and try to just look up the code.  */
+
+struct abbrevs
+{
+  /* The number of abbrevs in the vector.  */
+  size_t num_abbrevs;
+  /* The abbrevs, sorted by the code field.  */
+  struct abbrev *abbrevs;
+};
+
+/* The different kinds of attribute values.  */
+
+enum attr_val_encoding
+{
+  /* An address.  */
+  ATTR_VAL_ADDRESS,
+  /* 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_ADDRESS, 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.  */
+  const 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 growable vector of line number information.  This is used while
+   reading the line numbers.  */
+
+struct line_vector
+{
+  /* Memory.  This is an array of struct line.  */
+  struct backtrace_vector vec;
+  /* Number of valid mappings.  */
+  size_t count;
+};
+
+/* 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
+{
+  /* Memory.  This is an array of struct function_addrs.  */
+  struct backtrace_vector vec;
+  /* Number of address ranges present.  */
+  size_t count;
+};
+
+/* 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.  */
+  const 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
+{
+  /* Memory.  This is an array of struct unit_addrs.  */
+  struct backtrace_vector vec;
+  /* Number of address ranges present.  */
+  size_t count;
+};
+
+/* 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.  */
+  const unsigned char *dwarf_info;
+  size_t dwarf_info_size;
+  /* The unparsed .debug_line section.  */
+  const unsigned char *dwarf_line;
+  size_t dwarf_line_size;
+  /* The unparsed .debug_ranges section.  */
+  const unsigned char *dwarf_ranges;
+  size_t dwarf_ranges_size;
+  /* The unparsed .debug_str section.  */
+  const unsigned char *dwarf_str;
+  size_t dwarf_str_size;
+  /* Whether the data is big-endian or not.  */
+  int is_bigendian;
+  /* A vector used for function addresses.  We keep this here so that
+     we can grow the vector as we read more functions.  */
+  struct function_vector fvec;
+};
+
+/* 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 structure.  */
+
+static void
+free_abbrevs (struct abbrevs *abbrevs, backtrace_error_callback error_callback,
+	      void *data)
+{
+  size_t i;
+
+  for (i = 0; i < abbrevs->num_abbrevs; ++i)
+    backtrace_free (abbrevs->abbrevs[i].attrs,
+		    abbrevs->abbrevs[i].num_attrs * sizeof (struct attr),
+		    error_callback, data);
+  backtrace_free (abbrevs->abbrevs,
+		  abbrevs->num_abbrevs * sizeof (struct abbrev),
+		  error_callback, data);
+  abbrevs->num_abbrevs = 0;
+  abbrevs->abbrevs = NULL;
+}
+
+/* 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_ADDRESS;
+      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)
+{
+  struct unit_addrs *p;
+
+  /* Try to merge with the last entry.  */
+  if (vec->count > 0)
+    {
+      p = (struct unit_addrs *) vec->vec.base + (vec->count - 1);
+      if ((addrs.low == p->high || addrs.low == p->high + 1)
+	  && addrs.u == p->u)
+	{
+	  if (addrs.high > p->high)
+	    p->high = addrs.high;
+	  return 1;
+	}
+    }
+
+  p = ((struct unit_addrs *)
+       backtrace_vector_grow (sizeof (struct unit_addrs), error_callback,
+			      data, &vec->vec));
+  if (p == NULL)
+    return 0;
+
+  *p = addrs;
+  ++vec->count;
+  return 1;
+}
+
+/* Free a unit address vector.  */
+
+static void
+free_unit_addrs_vector (struct unit_addrs_vector *vec,
+			backtrace_error_callback error_callback, void *data)
+{
+  struct unit_addrs *addrs;
+  size_t i;
+
+  addrs = (struct unit_addrs *) vec->vec.base;
+  for (i = 0; i < vec->count; ++i)
+    free_abbrevs (&addrs[i].u->abbrevs, error_callback, data);
+}
+
+/* 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;
+}
+
+/* Sort the abbrevs by the abbrev code.  This function is passed to
+   both qsort and bsearch.  */
+
+static int
+abbrev_compare (const void *v1, const void *v2)
+{
+  const struct abbrev *a1 = (const struct abbrev *) v1;
+  const struct abbrev *a2 = (const struct abbrev *) v2;
+
+  if (a1->code < a2->code)
+    return -1;
+  else if (a1->code > a2->code)
+    return 1;
+  else
+    {
+      /* This really shouldn't happen.  It means there are two
+	 different abbrevs with the same code, and that means we don't
+	 know which one lookup_abbrev should return.  */
+      return 0;
+    }
+}
+
+/* Read the abbreviation table for a compilation unit.  Returns 1 on
+   success, 0 on failure.  */
+
+static int
+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 abbrevs *abbrevs)
+{
+  struct dwarf_buf abbrev_buf;
+  struct dwarf_buf count_buf;
+  size_t num_abbrevs;
+
+  abbrevs->num_abbrevs = 0;
+  abbrevs->abbrevs = NULL;
+
+  if (abbrev_offset >= dwarf_abbrev_size)
+    {
+      error_callback (data, "abbrev offset out of range", 0);
+      return 0;
+    }
+
+  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;
+
+  /* Count the number of abbrevs in this list.  */
+
+  count_buf = abbrev_buf;
+  num_abbrevs = 0;
+  while (read_uleb128 (&count_buf) != 0)
+    {
+      if (count_buf.reported_underflow)
+	return 0;
+      ++num_abbrevs;
+      // Skip tag.
+      read_uleb128 (&count_buf);
+      // Skip has_children.
+      read_byte (&count_buf);
+      // Skip attributes.
+      while (read_uleb128 (&count_buf) != 0)
+	read_uleb128 (&count_buf);
+      // Skip form of last attribute.
+      read_uleb128 (&count_buf);
+    }
+
+  if (count_buf.reported_underflow)
+    return 0;
+
+  if (num_abbrevs == 0)
+    return 1;
+
+  abbrevs->num_abbrevs = num_abbrevs;
+  abbrevs->abbrevs = ((struct abbrev *)
+		      backtrace_alloc (num_abbrevs * sizeof (struct abbrev),
+				       error_callback, data));
+  if (abbrevs->abbrevs == NULL)
+    return 0;
+  memset (abbrevs->abbrevs, 0, num_abbrevs * sizeof (struct abbrev));
+
+  num_abbrevs = 0;
+  while (1)
+    {
+      uint64_t code;
+      struct abbrev a;
+      size_t num_attrs;
+      struct attr *attrs;
+
+      if (abbrev_buf.reported_underflow)
+	goto fail;
+
+      code = read_uleb128 (&abbrev_buf);
+      if (code == 0)
+	break;
+
+      a.code = code;
+      a.tag = (enum dwarf_tag) read_uleb128 (&abbrev_buf);
+      a.has_children = read_byte (&abbrev_buf);
+
+      count_buf = abbrev_buf;
+      num_attrs = 0;
+      while (read_uleb128 (&count_buf) != 0)
+	{
+	  ++num_attrs;
+	  read_uleb128 (&count_buf);
+	}
+
+      if (num_attrs == 0)
+	{
+	  attrs = NULL;
+	  read_uleb128 (&abbrev_buf);
+	  read_uleb128 (&abbrev_buf);
+	}
+      else
+	{
+	  attrs = ((struct attr *)
+		   backtrace_alloc (num_attrs * sizeof *attrs,
+				    error_callback, data));
+	  if (attrs == NULL)
+	    goto fail;
+	  num_attrs = 0;
+	  while (1)
+	    {
+	      uint64_t name;
+	      uint64_t form;
+
+	      name = read_uleb128 (&abbrev_buf);
+	      form = read_uleb128 (&abbrev_buf);
+	      if (name == 0)
+		break;
+	      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;
+
+      abbrevs->abbrevs[num_abbrevs] = a;
+      ++num_abbrevs;
+    }
+
+  qsort (abbrevs->abbrevs, abbrevs->num_abbrevs, sizeof (struct abbrev),
+	 abbrev_compare);
+
+  return 1;
+
+ fail:
+  free_abbrevs (abbrevs, error_callback, data);
+  return 0;
+}
+
+/* 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)
+{
+  struct abbrev key;
+  void *p;
+
+  /* With GCC, where abbrevs are simply numbered in order, we should
+     be able to just look up the entry.  */
+  if (code - 1 < abbrevs->num_abbrevs
+      && abbrevs->abbrevs[code - 1].code == code)
+    return &abbrevs->abbrevs[code - 1];
+
+  /* Otherwise we have to search.  */
+  memset (&key, 0, sizeof key);
+  key.code = code;
+  p = bsearch (&key, abbrevs->abbrevs, abbrevs->num_abbrevs,
+	       sizeof (struct abbrev), abbrev_compare);
+  if (p == NULL)
+    {
+      error_callback (data, "invalid abbreviation code", 0);
+      return NULL;
+    }
+  return (const struct abbrev *) p;
+}
+
+/* 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 base,
+		 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;
+
+  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;
+
+  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;
+
+  memset (&addrs->vec, 0, sizeof addrs->vec);
+  addrs->count = 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;
+
+  memset (&abbrevs, 0, sizeof abbrevs);
+  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;
+      int highpc_is_relative;
+      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);
+      if (!read_abbrevs (abbrev_offset, dwarf_abbrev, dwarf_abbrev_size,
+			 is_bigendian, error_callback, data, &abbrevs))
+	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;
+      highpc_is_relative = 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_ADDRESS)
+		{
+		  lowpc = val.u.uint;
+		  have_lowpc = 1;
+		}
+	      break;
+	    case DW_AT_high_pc:
+	      if (val.encoding == ATTR_VAL_ADDRESS)
+		{
+		  highpc = val.u.uint;
+		  have_highpc = 1;
+		}
+	      else if (val.encoding == ATTR_VAL_UINT)
+		{
+		  highpc = val.u.uint;
+		  have_highpc = 1;
+		  highpc_is_relative = 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 *)
+	       backtrace_alloc (sizeof *u, error_callback, data));
+	  if (u == NULL)
+	    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;
+	  u->comp_dir = comp_dir;
+	  u->lineoff = lineoff;
+	  u->abbrevs = abbrevs;
+	  memset (&abbrevs, 0, sizeof abbrevs);
+
+	  /* 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, error_callback, data);
+		  backtrace_free (u, sizeof *u, error_callback, data);
+		  goto fail;
+		}
+	    }
+	  else
+	    {
+	      if (highpc_is_relative)
+		highpc += lowpc;
+	      a.low = lowpc;
+	      a.high = highpc;
+	      a.u = u;
+
+	      if (!add_unit_addr (a, error_callback, data, addrs))
+		{
+		  free_abbrevs (&u->abbrevs, error_callback, data);
+		  backtrace_free (u, sizeof *u, error_callback, data);
+		  goto fail;
+		}
+	    }
+	}
+      else
+	{
+	  free_abbrevs (&abbrevs, error_callback, data);
+	  memset (&abbrevs, 0, sizeof abbrevs);
+	}
+    }
+  if (info.reported_underflow)
+    goto fail;
+
+  return 1;
+
+ fail:
+  free_abbrevs (&abbrevs, error_callback, data);
+  free_unit_addrs_vector (addrs, error_callback, data);
+  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)
+    {
+      ln = (struct line *) vec->vec.base + (vec->count - 1);
+      if (pc == ln->pc && filename == ln->filename && lineno == ln->lineno)
+	return 1;
+    }
+
+  ln = ((struct line *)
+	backtrace_vector_grow (sizeof (struct line), error_callback, data,
+			       &vec->vec));
+  if (ln == NULL)
+    return 0;
+
+  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,
+		  backtrace_error_callback error_callback, void *data)
+{
+  backtrace_free (hdr->dirs, hdr->dirs_count * sizeof (const char *),
+		  error_callback, data);
+  backtrace_free (hdr->filenames, hdr->filenames_count * sizeof (char *),
+		  error_callback, data);
+}
+
+/* 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 **)
+	       backtrace_alloc (hdr->dirs_count * sizeof (const char *),
+				line_buf->error_callback, line_buf->data));
+  if (hdr->dirs == NULL)
+    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 = ((const char **)
+		    backtrace_alloc (hdr->filenames_count * sizeof (char *),
+				     line_buf->error_callback,
+				     line_buf->data));
+  if (hdr->filenames == NULL)
+    return 0;
+  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] = filename;
+      else
+	{
+	  const char *dir;
+	  size_t dir_len;
+	  size_t filename_len;
+	  char *s;
+
+	  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);
+	  s = ((char *)
+	       backtrace_alloc (dir_len + filename_len + 2,
+				line_buf->error_callback, line_buf->data));
+	  if (s == NULL)
+	    return 0;
+	  memcpy (s, 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.  */
+	  s[dir_len] = '/';
+	  memcpy (s + dir_len + 1, filename, filename_len + 1);
+	  hdr->filenames[i] = s;
+	}
+
+      /* 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 *)
+			 backtrace_alloc (dir_len + f_len + 2,
+					  line_buf->error_callback,
+					  line_buf->data));
+		    if (p == NULL)
+		      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;
+  struct line *ln;
+
+  memset (&vec.vec, 0, sizeof vec.vec);
+  vec.count = 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.  */
+  ln = ((struct line *)
+	backtrace_vector_grow (sizeof (struct line), error_callback, data,
+			       &vec.vec));
+  if (ln == NULL)
+    goto fail;
+  ln->pc = (uintptr_t) -1;
+  ln->filename = NULL;
+  ln->lineno = 0;
+
+  if (!backtrace_vector_release (&vec.vec, error_callback, data))
+    goto fail;
+
+  ln = (struct line *) vec.vec.base;
+  qsort (ln, vec.count, sizeof (struct line), line_compare);
+
+  u->lines = ln;
+  u->lines_count = vec.count;
+
+  return 1;
+
+ fail:
+  vec.vec.alc += vec.vec.size;
+  vec.vec.size = 0;
+  backtrace_vector_release (&vec.vec, error_callback, data);
+  free_line_header (hdr, error_callback, data);
+  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)
+    {
+      p = (struct function_addrs *) vec->vec.base + vec->count - 1;
+      if ((lowpc == p->high || lowpc == p->high + 1)
+	  && function == p->function)
+	{
+	  if (highpc > p->high)
+	    p->high = highpc;
+	  return 1;
+	}
+    }
+
+  p = ((struct function_addrs *)
+       backtrace_vector_grow (sizeof (struct function_addrs), error_callback,
+			      data, &vec->vec));
+  if (p == NULL)
+    return 0;
+
+  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 base, backtrace_error_callback error_callback,
+		     void *data, struct function_vector *vec)
+{
+  struct dwarf_buf ranges_buf;
+
+  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;
+
+  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,
+		     uint64_t base, 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;
+      int highpc_is_relative;
+      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 *)
+		      backtrace_alloc (sizeof *function,
+				       error_callback, data));
+	  if (function == NULL)
+	    return 0;
+	  memset (function, 0, sizeof *function);
+	}
+
+      lowpc = 0;
+      have_lowpc = 0;
+      highpc = 0;
+      have_highpc = 0;
+      highpc_is_relative = 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;
+
+	  /* The compile unit sets the base address for any address
+	     ranges in the function entries.  */
+	  if (abbrev->tag == DW_TAG_compile_unit
+	      && abbrev->attrs[i].name == DW_AT_low_pc
+	      && val.encoding == ATTR_VAL_ADDRESS)
+	    base = val.u.uint;
+
+	  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 - 1 >= 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_ADDRESS)
+		    {
+		      lowpc = val.u.uint;
+		      have_lowpc = 1;
+		    }
+		  break;
+
+		case DW_AT_high_pc:
+		  if (val.encoding == ATTR_VAL_ADDRESS)
+		    {
+		      highpc = val.u.uint;
+		      have_highpc = 1;
+		    }
+		  else if (val.encoding == ATTR_VAL_UINT)
+		    {
+		      highpc = val.u.uint;
+		      have_highpc = 1;
+		      highpc_is_relative = 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)
+	{
+	  backtrace_free (function, sizeof *function, error_callback, data);
+	  is_function = 0;
+	}
+
+      if (is_function)
+	{
+	  if (have_ranges)
+	    {
+	      if (!add_function_ranges (ddata, u, function, ranges, base,
+					error_callback, data, vec))
+		return 0;
+	    }
+	  else if (have_lowpc && have_highpc)
+	    {
+	      if (highpc_is_relative)
+		highpc += lowpc;
+	      if (!add_function_range (function, lowpc, highpc, error_callback,
+				       data, vec))
+		return 0;
+	    }
+	  else
+	    {
+	      backtrace_free (function, sizeof *function, error_callback,
+			      data);
+	      is_function = 0;
+	    }
+	}
+
+      if (abbrev->has_children)
+	{
+	  if (!is_function)
+	    {
+	      if (!read_function_entry (ddata, u, base, unit_buf, lhdr,
+					error_callback, data, vec))
+		return 0;
+	    }
+	  else
+	    {
+	      struct function_vector fvec;
+
+	      /* Gather any information for inlined functions in
+		 FVEC.  */
+
+	      memset (&fvec, 0, sizeof fvec);
+
+	      if (!read_function_entry (ddata, u, base, unit_buf, lhdr,
+					error_callback, data, &fvec))
+		return 0;
+
+	      if (fvec.count > 0)
+		{
+		  struct function_addrs *faddrs;
+
+		  if (!backtrace_vector_release (&fvec.vec, error_callback,
+						 data))
+		    return 0;
+
+		  faddrs = (struct function_addrs *) fvec.vec.base;
+		  qsort (faddrs, fvec.count,
+			 sizeof (struct function_addrs),
+			 function_addrs_compare);
+
+		  function->function_addrs = faddrs;
+		  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 function_vector *fvec)
+{
+  struct dwarf_buf unit_buf;
+  struct function_addrs *addrs;
+  size_t addrs_count;
+
+  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;
+
+  while (unit_buf.left > 0)
+    {
+      if (!read_function_entry (ddata, u, 0, &unit_buf, lhdr, error_callback,
+				data, fvec))
+	return;
+    }
+
+  if (fvec->count == 0)
+    return;
+
+  addrs = (struct function_addrs *) fvec->vec.base;
+  addrs_count = fvec->count;
+
+  /* Finish this list of addresses, but leave the remaining space in
+     the vector available for the next function unit.  */
+  backtrace_vector_finish (&fvec->vec);
+  fvec->count = 0;
+
+  qsort (addrs, addrs_count, sizeof (struct function_addrs),
+	 function_addrs_compare);
+
+  u->function_addrs = addrs;
+  u->function_addrs_count = addrs_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 ((size_t) (entry - ddata->addrs) + 1 < ddata->addrs_count
+	 && 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) + 1 < 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,
+			      &ddata->fvec);
+	  free_line_header (&lhdr, error_callback, data);
+	}
+      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 (const unsigned char *dwarf_info,
+			    size_t dwarf_info_size,
+			    const unsigned char *dwarf_line,
+			    size_t dwarf_line_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, fileline *fileline_fn,
+			    void **fileline_data)
+{
+  struct unit_addrs_vector addrs_vec;
+  struct unit_addrs *addrs;
+  size_t addrs_count;
+  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_vec))
+    return 0;
+
+  if (!backtrace_vector_release (&addrs_vec.vec, error_callback, data))
+    return 0;
+  addrs = (struct unit_addrs *) addrs_vec.vec.base;
+  addrs_count = addrs_vec.count;
+  qsort (addrs, addrs_count, sizeof (struct unit_addrs), unit_addrs_compare);
+
+  fdata = ((struct dwarf_data *)
+	   backtrace_alloc (sizeof (struct dwarf_data), error_callback, data));
+  if (fdata == NULL)
+    return 0;
+
+  fdata->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;
+  memset (&fdata->fvec, 0, sizeof fdata->fvec);
+
+  *fileline_fn = dwarf_fileline;
+  *fileline_data = fdata;
+
+  return 1;
+}
Index: libbacktrace/nothread.c
===================================================================
--- libbacktrace/nothread.c	(revision 0)
+++ libbacktrace/nothread.c	(revision 0)
@@ -0,0 +1,78 @@ 
+/* 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;
+}
+
+/* Acquire the memory lock.  */
+
+int
+backtrace_lock_alloc (
+    backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+    void *data ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
+
+/* Release the memory lock.  */
+
+int
+backtrace_unlock_alloc (
+    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,682 @@ 
+/* 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 <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 */
+
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_DYNSYM 11
+
+#if BACKTRACE_ELF_SIZE == 32
+
+typedef struct
+{
+  Elf_Word	st_name;		/* Symbol name, index in string tbl */
+  Elf_Addr	st_value;		/* Symbol value */
+  Elf_Word	st_size;		/* Symbol size */
+  unsigned char	st_info;		/* Symbol binding and type */
+  unsigned char	st_other;		/* Visibility and other data */
+  Elf_Half	st_shndx;		/* Symbol section index */
+} Elf_Sym;
+
+#else /* BACKTRACE_ELF_SIZE != 32 */
+
+typedef struct
+{
+  Elf_Word	st_name;		/* Symbol name, index in string tbl */
+  unsigned char	st_info;		/* Symbol binding and type */
+  unsigned char	st_other;		/* Visibility and other data */
+  Elf_Half	st_shndx;		/* Symbol section index */
+  Elf_Addr	st_value;		/* Symbol value */
+  Elf_Xword	st_size;		/* Symbol size */
+} Elf_Sym;
+
+#endif /* BACKTRACE_ELF_SIZE != 32 */
+
+#define STT_FUNC 2
+
+/* An index of ELF sections we care about.  */
+
+enum debug_section
+{
+  DEBUG_INFO,
+  DEBUG_LINE,
+  DEBUG_ABBREV,
+  DEBUG_RANGES,
+  DEBUG_STR,
+  DEBUG_MAX
+};
+
+/* Names of sections, indexed by enum elf_section.  */
+
+const char * const debug_section_names[DEBUG_MAX] =
+{
+  ".debug_info",
+  ".debug_line",
+  ".debug_abbrev",
+  ".debug_ranges",
+  ".debug_str"
+};
+
+/* Information we gather for the sections we care about.  */
+
+struct debug_section_info
+{
+  /* Section file offset.  */
+  off_t offset;
+  /* Section size.  */
+  size_t size;
+  /* Section contents, after read from file.  */
+  const unsigned char *data;
+};
+
+/* Information we keep for an ELF symbol.  */
+
+struct elf_symbol
+{
+  /* The name of the symbol.  */
+  const char *name;
+  /* The address of the symbol.  */
+  uintptr_t address;
+  /* The size of the symbol.  */
+  size_t size;
+};
+
+/* Information to pass to elf_syminfo.  */
+
+struct elf_syminfo_data
+{
+  /* The ELF symbols, sorted by address.  */
+  struct elf_symbol *symbols;
+  /* The number of symbols.  */
+  size_t count;
+};
+
+/* A dummy callback function used when we can't find any debug info.  */
+
+static int
+elf_nodebug (void *fileline_data ATTRIBUTE_UNUSED,
+	     uintptr_t pc ATTRIBUTE_UNUSED,
+	     backtrace_callback callback ATTRIBUTE_UNUSED,
+	     backtrace_error_callback error_callback, void *data)
+{
+  error_callback (data, "no debug info in ELF executable", -1);
+  return 0;
+}
+
+/* A dummy callback function used when we can't find a symbol
+   table.  */
+
+static void
+elf_nosyms (void *syminfo_data ATTRIBUTE_UNUSED,
+	    uintptr_t pc ATTRIBUTE_UNUSED,
+	    backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
+	    backtrace_error_callback error_callback, void *data)
+{
+  error_callback (data, "no symbol table in ELF executable", -1);
+}
+
+/* Compare struct elf_symbol for qsort.  */
+
+static int
+elf_symbol_compare (const void *v1, const void *v2)
+{
+  const struct elf_symbol *e1 = (const struct elf_symbol *) v1;
+  const struct elf_symbol *e2 = (const struct elf_symbol *) v2;
+
+  if (e1->address < e2->address)
+    return -1;
+  else if (e1->address > e2->address)
+    return 1;
+  else
+    return 0;
+}
+
+/* Comparea PC against an elf_symbol for bsearch.  We allocate one
+   extra entry in the array so that this can look safely at the next
+   entry.  */
+
+static int
+elf_symbol_search (const void *vkey, const void *ventry)
+{
+  const uintptr_t *key = (const uintptr_t *) vkey;
+  const struct elf_symbol *entry = (const struct elf_symbol *) ventry;
+  uintptr_t pc;
+
+  pc = *key;
+  if (pc < entry->address)
+    return -1;
+  else if (pc >= entry->address + entry->size)
+    return 1;
+  else
+    return 0;
+}
+
+/* Initialize the symbol table info for elf_syminfo.  */
+
+static int
+elf_initialize_syminfo (const unsigned char *symtab_data, size_t symtab_size,
+			const unsigned char *strtab, size_t strtab_size,
+			backtrace_error_callback error_callback,
+			void *data, struct elf_syminfo_data *sdata)
+{
+  size_t sym_count;
+  const Elf_Sym *sym;
+  size_t elf_symbol_count;
+  size_t elf_symbol_size;
+  struct elf_symbol *elf_symbols;
+  size_t i;
+  unsigned int j;
+
+  sym_count = symtab_size / sizeof (Elf_Sym);
+
+  /* We only care about function symbols.  Count them.  */
+  sym = (const Elf_Sym *) symtab_data;
+  elf_symbol_count = 0;
+  for (i = 0; i < sym_count; ++i, ++sym)
+    {
+      if ((sym->st_info & 0xf) == STT_FUNC)
+	++elf_symbol_count;
+    }
+
+  elf_symbol_size = elf_symbol_count * sizeof (struct elf_symbol);
+  elf_symbols = ((struct elf_symbol *)
+		 backtrace_alloc (elf_symbol_size, error_callback, data));
+  if (elf_symbols == NULL)
+    return 0;
+
+  sym = (const Elf_Sym *) symtab_data;
+  j = 0;
+  for (i = 0; i < sym_count; ++i, ++sym)
+    {
+      if ((sym->st_info & 0xf) != STT_FUNC)
+	continue;
+      if (sym->st_name >= strtab_size)
+	{
+	  error_callback (data, "symbol string index out of range", 0);
+	  backtrace_free (elf_symbols, elf_symbol_size, error_callback, data);
+	  return 0;
+	}
+      elf_symbols[j].name = (const char *) strtab + sym->st_name;
+      elf_symbols[j].address = sym->st_value;
+      elf_symbols[j].size = sym->st_size;
+      ++j;
+    }
+
+  qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol),
+	 elf_symbol_compare);
+
+  sdata->symbols = elf_symbols;
+  sdata->count = elf_symbol_count;
+
+  return 1;
+}
+
+/* Return the symbol name and value for a PC.  */
+
+static void
+elf_syminfo (void *syminfo_data, uintptr_t pc,
+	     backtrace_syminfo_callback callback,
+	     backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+	     void *data)
+{
+  struct elf_syminfo_data *edata = (struct elf_syminfo_data *) syminfo_data;
+  struct elf_symbol *sym;
+
+  sym = ((struct elf_symbol *)
+	 bsearch (&pc, edata->symbols, edata->count,
+		  sizeof (struct elf_symbol), elf_symbol_search));
+  if (sym == NULL)
+    callback (data, pc, NULL, 0);
+  else
+    callback (data, pc, sym->name, sym->address);
+}
+
+/* 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,
+		      syminfo *syminfo_fn, void **syminfo_data)
+{
+  struct backtrace_view ehdr_view;
+  Elf_Ehdr ehdr;
+  off_t shoff;
+  unsigned int shnum;
+  unsigned int shstrndx;
+  struct backtrace_view shdrs_view;
+  int shdrs_view_valid;
+  const Elf_Shdr *shdrs;
+  const Elf_Shdr *shstrhdr;
+  size_t shstr_size;
+  off_t shstr_off;
+  struct backtrace_view names_view;
+  int names_view_valid;
+  const char *names;
+  unsigned int symtab_shndx;
+  unsigned int dynsym_shndx;
+  unsigned int i;
+  struct debug_section_info sections[DEBUG_MAX];
+  struct backtrace_view symtab_view;
+  int symtab_view_valid;
+  struct backtrace_view strtab_view;
+  int strtab_view_valid;
+  off_t min_offset;
+  off_t max_offset;
+  struct backtrace_view debug_view;
+  int debug_view_valid;
+
+  shdrs_view_valid = 0;
+  names_view_valid = 0;
+  symtab_view_valid = 0;
+  strtab_view_valid = 0;
+  debug_view_valid = 0;
+
+  if (!backtrace_get_view (descriptor, 0, sizeof ehdr, error_callback, data,
+			   &ehdr_view))
+    goto fail;
+
+  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
+
+  backtrace_release_view (&ehdr_view, error_callback, data);
+
+  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);
+      goto fail;
+    }
+  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "executable file is unrecognized ELF version", 0);
+      goto fail;
+    }
+
+#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);
+      goto fail;
+    }
+
+  if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB
+      && ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
+    {
+      error_callback (data, "executable file has unknown endianness", 0);
+      goto fail;
+    }
+
+  shoff = ehdr.e_shoff;
+  shnum = ehdr.e_shnum;
+  shstrndx = ehdr.e_shstrndx;
+
+  if ((shnum == 0 || shstrndx == SHN_XINDEX)
+      && shoff != 0)
+    {
+      struct backtrace_view shdr_view;
+      const Elf_Shdr *shdr;
+
+      if (!backtrace_get_view (descriptor, shoff, sizeof shdr,
+			       error_callback, data, &shdr_view))
+	goto fail;
+
+      shdr = (const Elf_Shdr *) shdr_view.data;
+
+      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;
+	}
+
+      backtrace_release_view (&shdr_view, error_callback, data);
+    }
+
+  /* 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.  */
+
+  if (!backtrace_get_view (descriptor, shoff + sizeof (Elf_Shdr),
+			   (shnum - 1) * sizeof (Elf_Shdr),
+			   error_callback, data, &shdrs_view))
+    goto fail;
+  shdrs_view_valid = 1;
+  shdrs = (const Elf_Shdr *) shdrs_view.data;
+
+  /* Read the section names.  */
+
+  shstrhdr = &shdrs[shstrndx - 1];
+  shstr_size = shstrhdr->sh_size;
+  shstr_off = shstrhdr->sh_offset;
+
+  if (!backtrace_get_view (descriptor, shstr_off, shstr_size,
+			   error_callback, data, &names_view))
+    goto fail;
+  names_view_valid = 1;
+  names = (const char *) names_view.data;
+
+  symtab_shndx = 0;
+  dynsym_shndx = 0;
+
+  memset (sections, 0, sizeof sections);
+  for (i = 1; i < shnum; ++i)
+    {
+      const Elf_Shdr *shdr;
+      unsigned int sh_name;
+      const char *name;
+      int j;
+
+      shdr = &shdrs[i - 1];
+
+      if (shdr->sh_type == SHT_SYMTAB)
+	symtab_shndx = i;
+      else if (shdr->sh_type == SHT_DYNSYM)
+	dynsym_shndx = i;
+
+      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;
+
+      for (j = 0; j < (int) DEBUG_MAX; ++j)
+	{
+	  if (strcmp (name, debug_section_names[j]) == 0)
+	    {
+	      sections[j].offset = shdr->sh_offset;
+	      sections[j].size = shdr->sh_size;
+	      break;
+	    }
+	}
+    }
+
+  if (symtab_shndx == 0)
+    symtab_shndx = dynsym_shndx;
+  if (symtab_shndx == 0)
+    {
+      *syminfo_fn = elf_nosyms;
+      *syminfo_data = NULL;
+    }
+  else
+    {
+      const Elf_Shdr *symtab_shdr;
+      unsigned int strtab_shndx;
+      const Elf_Shdr *strtab_shdr;
+      struct elf_syminfo_data *sdata;
+
+      symtab_shdr = &shdrs[symtab_shndx - 1];
+      strtab_shndx = symtab_shdr->sh_link;
+      if (strtab_shndx >= shnum)
+	{
+	  error_callback (data,
+			  "ELF symbol table strtab link out of range", 0);
+	  goto fail;
+	}
+      strtab_shdr = &shdrs[strtab_shndx - 1];
+
+      if (!backtrace_get_view (descriptor, symtab_shdr->sh_offset,
+			       symtab_shdr->sh_size, error_callback, data,
+			       &symtab_view))
+	goto fail;
+      symtab_view_valid = 1;
+
+      if (!backtrace_get_view (descriptor, strtab_shdr->sh_offset,
+			       strtab_shdr->sh_size, error_callback, data,
+			       &strtab_view))
+	goto fail;
+      strtab_view_valid = 1;
+
+      sdata = ((struct elf_syminfo_data *)
+	       backtrace_alloc (sizeof *sdata, error_callback, data));
+      if (sdata == NULL)
+	goto fail;
+
+      if (!elf_initialize_syminfo (symtab_view.data, symtab_shdr->sh_size,
+				   strtab_view.data, strtab_shdr->sh_size,
+				   error_callback, data, sdata))
+	{
+	  backtrace_free (sdata, sizeof *sdata, error_callback, data);
+	  goto fail;
+	}
+
+      /* We no longer need the symbol table, but we hold on to the
+	 string table permanently.  */
+      backtrace_release_view (&symtab_view, error_callback, data);
+
+      *syminfo_fn = elf_syminfo;
+      *syminfo_data = sdata;
+    }
+
+  /* FIXME: Need to handle compressed debug sections.  */
+
+  backtrace_release_view (&shdrs_view, error_callback, data);
+  shdrs_view_valid = 0;
+  backtrace_release_view (&names_view, error_callback, data);
+  names_view_valid = 0;
+
+  /* Read all the debug sections in a single view, since they are
+     probably adjacent in the file.  We never release this view.  */
+
+  min_offset = 0;
+  max_offset = 0;
+  for (i = 0; i < (int) DEBUG_MAX; ++i)
+    {
+      off_t end;
+
+      if (min_offset == 0 || sections[i].offset < min_offset)
+	min_offset = sections[i].offset;
+      end = sections[i].offset + sections[i].size;
+      if (end > max_offset)
+	max_offset = end;
+    }
+  if (min_offset == 0 || max_offset == 0)
+    {
+      if (!backtrace_close (descriptor, error_callback, data))
+	goto fail;
+      *fileline_fn = elf_nodebug;
+      *fileline_data = NULL;
+      return 1;
+    }
+
+  if (!backtrace_get_view (descriptor, min_offset, max_offset - min_offset,
+			   error_callback, data, &debug_view))
+    goto fail;
+  debug_view_valid = 1;
+
+  /* We've read all we need from the executable.  */
+  if (!backtrace_close (descriptor, error_callback, data))
+    goto fail;
+  descriptor = -1;
+
+  for (i = 0; i < (int) DEBUG_MAX; ++i)
+    sections[i].data = ((const unsigned char *) debug_view.data
+			+ (sections[i].offset - min_offset));
+
+  if (!backtrace_dwarf_initialize (sections[DEBUG_INFO].data,
+				   sections[DEBUG_INFO].size,
+				   sections[DEBUG_LINE].data,
+				   sections[DEBUG_LINE].size,
+				   sections[DEBUG_ABBREV].data,
+				   sections[DEBUG_ABBREV].size,
+				   sections[DEBUG_RANGES].data,
+				   sections[DEBUG_RANGES].size,
+				   sections[DEBUG_STR].data,
+				   sections[DEBUG_STR].size,
+				   ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+				   error_callback, data,
+				   fileline_fn, fileline_data))
+    goto fail;
+
+  return 1;
+
+ fail:
+  if (shdrs_view_valid)
+    backtrace_release_view (&shdrs_view, error_callback, data);
+  if (names_view_valid)
+    backtrace_release_view (&names_view, error_callback, data);
+  if (symtab_view_valid)
+    backtrace_release_view (&symtab_view, error_callback, data);
+  if (strtab_view_valid)
+    backtrace_release_view (&strtab_view, error_callback, data);
+  if (debug_view_valid)
+    backtrace_release_view (&debug_view, error_callback, data);
+  if (descriptor != -1)
+    backtrace_close (descriptor, error_callback, data);
+  return 0;
+}
Index: libbacktrace/thread.c
===================================================================
--- libbacktrace/thread.c	(revision 0)
+++ libbacktrace/thread.c	(revision 0)
@@ -0,0 +1,117 @@ 
+/* 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;
+
+/* The lock for the memory allocator.  */
+
+static pthread_mutex_t lock_alloc = 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;
+}
+
+/* Acquire the memory lock.  */
+
+int
+backtrace_lock_alloc (
+    backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+    void *data ATTRIBUTE_UNUSED)
+{
+  int errnum;
+
+  errnum = pthread_mutex_lock (&lock_alloc);
+  if (errnum != 0)
+    {
+      error_callback (data, "pthread_mutex_lock", errnum);
+      return 0;
+    }
+  return 1;
+}
+
+/* Release the memory lock.  */
+
+int
+backtrace_unlock_alloc (
+    backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+    void *data ATTRIBUTE_UNUSED)
+{
+  int errnum;
+
+  errnum = pthread_mutex_unlock (&lock_alloc);
+  if (errnum != 0)
+    {
+      error_callback (data, "pthread_mutex_unlock", errnum);
+      return 0;
+    }
+  return 1;
+}
Index: libbacktrace/read.c
===================================================================
--- libbacktrace/read.c	(revision 0)
+++ libbacktrace/read.c	(revision 0)
@@ -0,0 +1,94 @@ 
+/* read.c -- File views without mmap.
+   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 <sys/types.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* This file implements file views when mmap is not available.  */
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET.  */
+
+int
+backtrace_get_view (int descriptor, off_t offset, size_t size,
+		    backtrace_error_callback error_callback,
+		    void *data, struct backtrace_view *view)
+{
+  ssize_t got;
+
+  if (lseek (descriptor, offset, SEEK_SET) < 0)
+    {
+      error_callback (data, "lseek", errno);
+      return 0;
+    }
+
+  view->base = backtrace_alloc (size, error_callback, data);
+  if (view->base == NULL)
+    return 0;
+  view->data = view->base;
+  view->len = size;
+
+  got = read (descriptor, view->base, size);
+  if (got < 0)
+    {
+      error_callback (data, "read", errno);
+      free (view->base);
+      return 0;
+    }
+
+  if ((size_t) got < size)
+    {
+      error_callback (data, "file too short", 0);
+      free (view->base);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Release a view read by backtrace_get_view.  */
+
+void
+backtrace_release_view (struct backtrace_view *view,
+			backtrace_error_callback error_callback,
+			void *data)
+{
+  backtrace_free (view->base, view->len, error_callback, data);
+  view->data = NULL;
+  view->base = NULL;
+}
Index: libbacktrace/gthread.c
===================================================================
--- libbacktrace/gthread.c	(revision 0)
+++ libbacktrace/gthread.c	(revision 0)
@@ -0,0 +1,134 @@ 
+/* gthread.c -- GCC 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.  */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include "gthr-default.h"
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The backtrace lock and unlock routines when using the GCC thread
+   interface.  */
+
+/* The lock.  */
+
+#ifdef __GTHREADS
+static __gthread_mutex_t lock = __GTHREAD_MUTEX_INIT;
+static __gthread_mutex_t lock_alloc = __GTHREAD_MUTEX_INIT;
+#endif
+
+/* Acquire the lock.  */
+
+int
+backtrace_lock (backtrace_error_callback error_callback, void *data)
+{
+  int errnum;
+
+#ifdef __GTHREADS
+  errnum = __gthread_mutex_lock (&lock);
+#else
+  errnum = 0;
+#endif
+  if (errnum != 0)
+    {
+      error_callback (data, "__gthread_mutex_lock", errnum);
+      return 0;
+    }
+  return 1;
+}
+
+/* Release the lock.  */
+
+int
+backtrace_unlock (backtrace_error_callback error_callback, void *data)
+{
+  int errnum;
+
+#ifdef __GTHREADS
+  errnum = __gthread_mutex_unlock (&lock);
+#else
+  errnum = 0;
+#endif
+  if (errnum != 0)
+    {
+      error_callback (data, "__gthread_mutex_unlock", errnum);
+      return 0;
+    }
+  return 1;
+}
+
+/* Acquire the memory lock.  */
+
+int
+backtrace_lock_alloc (
+    backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+    void *data ATTRIBUTE_UNUSED)
+{
+  int errnum;
+
+#ifdef __GTHREADS
+  errnum = __gthread_mutex_lock (&lock_alloc);
+#else
+  errnum = 0;
+#endif
+  if (errnum != 0)
+    {
+      error_callback (data, "__gthread_mutex_lock", errnum);
+      return 0;
+    }
+  return 1;
+}
+
+/* Release the memory lock.  */
+
+int
+backtrace_unlock_alloc (
+    backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+    void *data ATTRIBUTE_UNUSED)
+{
+  int errnum;
+
+#ifdef __GTHREADS
+  errnum = __gthread_mutex_unlock (&lock_alloc);
+#else
+  errnum = 0;
+#endif
+  if (errnum != 0)
+    {
+      error_callback (data, "__gthread_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/internal.h
===================================================================
--- libbacktrace/internal.h	(revision 0)
+++ libbacktrace/internal.h	(revision 0)
@@ -0,0 +1,214 @@ 
+/* 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
+
+#ifndef ATTRIBUTE_MALLOC
+# if (GCC_VERSION >= 2096)
+#  define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
+# else
+#  define ATTRIBUTE_MALLOC
+# endif
+#endif
+
+/* Return the executable name, if known.  */
+
+extern const char *backtrace_executable_name (void);
+
+/* 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);
+
+/* Like backtrace_lock, for the lock for the memory allocator.  */
+
+extern int backtrace_lock_alloc (backtrace_error_callback error_callback,
+				 void *data);
+
+/* Like backtrace_unlock, for the lock for the memory allocator.  */
+
+extern int backtrace_unlock_alloc (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);
+
+/* A view of the contents of a file.  This supports mmap when
+   available.  A view will remain in memory even after backtrace_close
+   is called on the file descriptor from which the view was
+   obtained.  */
+
+struct backtrace_view
+{
+  /* The data that the caller requested.  */
+  const void *data;
+  /* The base of the view.  */
+  void *base;
+  /* The total length of the view.  */
+  size_t len;
+};
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET.  Store the
+   result in *VIEW.  Returns 1 on success, 0 on error.  */
+extern int backtrace_get_view (int descriptor, off_t offset, size_t size,
+			       backtrace_error_callback error_callback,
+			       void *data, struct backtrace_view *view);
+
+/* Release a view created by backtrace_get_view.  */
+extern void backtrace_release_view (struct backtrace_view *view,
+				    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);
+
+/* Allocate memory.  This is like malloc.  */
+
+extern void *backtrace_alloc (size_t size,
+			      backtrace_error_callback error_callback,
+			      void *data) ATTRIBUTE_MALLOC;
+
+/* Free memory allocated by backtrace_alloc.  */
+
+extern void backtrace_free (void *mem, size_t size,
+			    backtrace_error_callback error_callback,
+			    void *data);
+
+/* A growable vector of some struct.  This is used for more efficient
+   allocation when we don't know the final size of some group of data
+   that we want to represent as an array.  */
+
+struct backtrace_vector
+{
+  /* The base of the vector.  */
+  void *base;
+  /* The number of bytes in the vector.  */
+  size_t size;
+  /* The number of bytes available at the current allocation.  */
+  size_t alc;
+};
+
+/* Grow VEC by SIZE bytes.  Return a pointer to the newly allocated
+   bytes.  Note that this may move the entire vector to a new memory
+   location.  Returns NULL on failure.  */
+
+extern void *backtrace_vector_grow (size_t size,
+				    backtrace_error_callback error_callback,
+				    void *data,
+				    struct backtrace_vector *vec);
+
+/* Finish the current allocation on VEC.  Prepare to start a new
+   allocation.  The finished allocation will never be freed.  */
+
+extern void backtrace_vector_finish (struct backtrace_vector *vec);
+
+/* Release any extra space allocated for VEC.  Returns 1 on success, 0
+   on failure.  */
+
+extern int backtrace_vector_release (struct backtrace_vector *vec,
+				     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);
+
+/* The type of the function that collects symbol information.  This is
+   like backtrace_syminfo.  */
+
+typedef void (*syminfo) (void *syminfo_data, uintptr_t pc,
+			 backtrace_syminfo_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,
+				 syminfo *sfn,
+				 void **syminfo_data);
+
+/* Prepare to read file/line information from DWARF debug data.  */
+
+extern int backtrace_dwarf_initialize (const unsigned char* dwarf_info,
+				       size_t dwarf_info_size,
+				       const unsigned char *dwarf_line,
+				       size_t dwarf_line_size,
+				       const unsigned char *dwarf_abbrev,
+				       size_t dwarf_abbrev_size,
+				       const unsigned char *dwarf_ranges,
+				       size_t dwarf_range_size,
+				       const 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/btest.c
===================================================================
--- libbacktrace/btest.c	(revision 0)
+++ libbacktrace/btest.c	(revision 0)
@@ -0,0 +1,613 @@ 
+/* btest.c -- Test for libbacktrace 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.  */
+
+/* This program tests the externally visible interfaces of the
+   libbacktrace library.  */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "filenames.h"
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+
+/* Portable attribute syntax.  Actually some of these tests probably
+   won't work if the attributes are not recognized.  */
+
+#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
+
+/* Used to collect backtrace info.  */
+
+struct info
+{
+  char *filename;
+  int lineno;
+  char *function;
+};
+
+/* Passed to backtrace callback function.  */
+
+struct bdata
+{
+  struct info *all;
+  size_t index;
+  size_t max;
+  int failed;
+};
+
+/* Passed to backtrace_simple callback function.  */
+
+struct sdata
+{
+  uintptr_t *addrs;
+  size_t index;
+  size_t max;
+  int failed;
+};
+
+/* Passed to backtrace_syminfo callback function.  */
+
+struct symdata
+{
+  const char *name;
+  uintptr_t val;
+  int failed;
+};
+
+/* The number of failures.  */
+
+static int failures;
+
+/* Return the base name in a path.  */
+
+static const char *
+base (const char *p)
+{
+  const char *last;
+  const char *s;
+
+  last = NULL;
+  for (s = p; *s != '\0'; ++s)
+    {
+      if (IS_DIR_SEPARATOR (*s))
+	last = s + 1;
+    }
+  return last != NULL ? last : p;
+}
+
+/* Check an entry in a struct info array.  */
+
+static void
+check (const char *name, int index, const struct info *all, int want_lineno,
+       const char *want_function, int *failed)
+{
+  if (*failed)
+    return;
+  if (strcmp (base (all[index].filename), "btest.c") != 0)
+    {
+      fprintf (stderr, "%s: [%d]: got %s expected test.c\n", name, index,
+	       all[index].filename);
+      *failed = 1;
+    }
+  if (all[index].lineno != want_lineno)
+    {
+      fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index,
+	       all[index].lineno, want_lineno);
+      *failed = 1;
+    }
+  if (strcmp (all[index].function, want_function) != 0)
+    {
+      fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
+	       all[index].function, want_function);
+      *failed = 1;
+    }
+}
+
+/* The backtrace callback function.  */
+
+static int
+callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
+	      const char *filename, int lineno, const char *function)
+{
+  struct bdata *data = (struct bdata *) vdata;
+  struct info *p;
+
+  if (data->index >= data->max)
+    {
+      fprintf (stderr, "callback_one: callback called too many times\n");
+      data->failed = 1;
+      return 1;
+    }
+
+  p = &data->all[data->index];
+  if (filename == NULL)
+    p->filename = NULL;
+  else
+    {
+      p->filename = strdup (filename);
+      assert (p->filename != NULL);
+    }
+  p->lineno = lineno;
+  if (function == NULL)
+    p->function = NULL;
+  else
+    {
+      p->function = strdup (function);
+      assert (p->function != NULL);
+    }
+  ++data->index;
+
+  return 0;
+}
+
+/* An error callback passed to backtrace.  */
+
+static void
+error_callback_one (void *vdata, const char *msg, int errnum)
+{
+  struct bdata *data = (struct bdata *) vdata;
+
+  fprintf (stderr, "%s", msg);
+  if (errnum > 0)
+    fprintf (stderr, ": %s", strerror (errnum));
+  fprintf (stderr, "\n");
+  data->failed = 1;
+}
+
+/* The backtrace_simple callback function.  */
+
+static int
+callback_two (void *vdata, uintptr_t pc)
+{
+  struct sdata *data = (struct sdata *) vdata;
+
+  if (data->index >= data->max)
+    {
+      fprintf (stderr, "callback_two: callback called too many times\n");
+      data->failed = 1;
+      return 1;
+    }
+
+  data->addrs[data->index] = pc;
+  ++data->index;
+
+  return 0;
+}
+
+/* An error callback passed to backtrace_simple.  */
+
+static void
+error_callback_two (void *vdata, const char *msg, int errnum)
+{
+  struct sdata *data = (struct sdata *) vdata;
+
+  fprintf (stderr, "%s", msg);
+  if (errnum > 0)
+    fprintf (stderr, ": %s", strerror (errnum));
+  fprintf (stderr, "\n");
+  data->failed = 1;
+}
+
+/* The backtrace_syminfo callback function.  */
+
+static void
+callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
+		const char *symname, uintptr_t symval)
+{
+  struct symdata *data = (struct symdata *) vdata;
+
+  if (symname == NULL)
+    data->name = NULL;
+  else
+    {
+      data->name = strdup (symname);
+      assert (data->name != NULL);
+    }
+  data->val = symval;
+}
+
+/* The backtrace_syminfo error callback function.  */
+
+static void
+error_callback_three (void *vdata, const char *msg, int errnum)
+{
+  struct symdata *data = (struct symdata *) vdata;
+
+  fprintf (stderr, "%s", msg);
+  if (errnum > 0)
+    fprintf (stderr, ": %s", strerror (errnum));
+  fprintf (stderr, "\n");
+  data->failed = 1;
+}
+
+/* Test the backtrace function with non-inlined functions.  */
+
+static int test1 (void) __attribute__ ((noinline));
+static int f2 (int) __attribute__ ((noinline));
+static int f3 (int, int) __attribute__ ((noinline));
+
+static int
+test1 (void)
+{
+  /* Returning a value here and elsewhere avoids a tailcall which
+     would mess up the backtrace.  */
+  return f2 (__LINE__) + 1;
+}
+
+static int
+f2 (int f1line)
+{
+  return f3 (f1line, __LINE__) + 2;
+}
+
+static int
+f3 (int f1line, int f2line)
+{
+  struct info all[20];
+  struct bdata data;
+  int f3line;
+  int i;
+
+  data.all = &all[0];
+  data.index = 0;
+  data.max = 20;
+  data.failed = 0;
+
+  f3line = __LINE__ + 1;
+  i = backtrace (0, callback_one, error_callback_one, &data);
+
+  if (i != 0)
+    {
+      fprintf (stderr, "test1: unexpected return value %d\n", i);
+      data.failed = 1;
+    }
+
+  check ("test1", 0, all, f3line, "f3", &data.failed);
+  check ("test1", 1, all, f2line, "f2", &data.failed);
+  check ("test1", 2, all, f1line, "test1", &data.failed);
+
+  printf ("%s: backtrace noinline\n", data.failed ? "FAIL" : "PASS");
+
+  if (data.failed)
+    ++failures;
+
+  return failures;
+}
+
+/* Test the backtrace function with inlined functions.  */
+
+static inline int test2 (void) __attribute__ ((always_inline));
+static inline int f12 (int) __attribute__ ((always_inline));
+static inline int f13 (int, int) __attribute__ ((always_inline));
+
+static inline int
+test2 (void)
+{
+  return f12 (__LINE__) + 1;
+}
+
+static inline int
+f12 (int f1line)
+{
+  return f13 (f1line, __LINE__) + 2;
+}
+
+static inline int
+f13 (int f1line, int f2line)
+{
+  struct info all[20];
+  struct bdata data;
+  int f3line;
+  int i;
+
+  data.all = &all[0];
+  data.index = 0;
+  data.max = 20;
+  data.failed = 0;
+
+  f3line = __LINE__ + 1;
+  i = backtrace (0, callback_one, error_callback_one, &data);
+
+  if (i != 0)
+    {
+      fprintf (stderr, "test2: unexpected return value %d\n", i);
+      data.failed = 1;
+    }
+
+  check ("test2", 0, all, f3line, "f13", &data.failed);
+  check ("test2", 1, all, f2line, "f12", &data.failed);
+  check ("test2", 2, all, f1line, "test2", &data.failed);
+
+  printf ("%s: backtrace inline\n", data.failed ? "FAIL" : "PASS");
+
+  if (data.failed)
+    ++failures;
+
+  return failures;
+}
+
+/* Test the backtrace_simple function with non-inlined functions.  */
+
+static int test3 (void) __attribute__ ((noinline));
+static int f22 (int) __attribute__ ((noinline));
+static int f23 (int, int) __attribute__ ((noinline));
+
+static int
+test3 (void)
+{
+  return f22 (__LINE__) + 1;
+}
+
+static int
+f22 (int f1line)
+{
+  return f23 (f1line, __LINE__) + 2;
+}
+
+static int
+f23 (int f1line, int f2line)
+{
+  uintptr_t addrs[20];
+  struct sdata data;
+  int f3line;
+  int i;
+
+  data.addrs = &addrs[0];
+  data.index = 0;
+  data.max = 20;
+  data.failed = 0;
+
+  f3line = __LINE__ + 1;
+  i = backtrace_simple (0, callback_two, error_callback_two, &data);
+
+  if (i != 0)
+    {
+      fprintf (stderr, "test3: unexpected return value %d\n", i);
+      data.failed = 1;
+    }
+
+  if (!data.failed)
+    {
+      struct info all[20];
+      struct bdata bdata;
+      int j;
+
+      bdata.all = &all[0];
+      bdata.index = 0;
+      bdata.max = 20;
+      bdata.failed = 0;
+
+      for (j = 0; j < 3; ++j)
+	{
+	  i = backtrace_pcinfo (addrs[j], callback_one, error_callback_one,
+				&bdata);
+	  if (i != 0)
+	    {
+	      fprintf (stderr,
+		       ("test3: unexpected return value "
+			"from backtrace_pcinfo %d\n"),
+		       i);
+	      bdata.failed = 1;
+	    }
+	  if (!bdata.failed && bdata.index != (size_t) (j + 1))
+	    {
+	      fprintf (stderr,
+		       ("wrong number of calls from backtrace_pcinfo "
+			"got %u expected %d\n"),
+		       (unsigned int) bdata.index, j + 1);
+	      bdata.failed = 1;
+	    }
+	}      
+
+      check ("test3", 0, all, f3line, "f23", &bdata.failed);
+      check ("test3", 1, all, f2line, "f22", &bdata.failed);
+      check ("test3", 2, all, f1line, "test3", &bdata.failed);
+
+      if (bdata.failed)
+	data.failed = 1;
+
+      for (j = 0; j < 3; ++j)
+	{
+	  struct symdata symdata;
+
+	  symdata.name = NULL;
+	  symdata.val = 0;
+	  symdata.failed = 0;
+
+	  i = backtrace_syminfo (addrs[j], callback_three,
+				 error_callback_three, &symdata);
+	  if (i == 0)
+	    {
+	      fprintf (stderr,
+		       ("test3: [%d]: unexpected return value "
+			"from backtrace_syminfo %d\n"),
+		       j, i);
+	      symdata.failed = 1;
+	    }
+
+	  if (!symdata.failed)
+	    {
+	      const char *expected;
+
+	      switch (j)
+		{
+		case 0:
+		  expected = "f23";
+		  break;
+		case 1:
+		  expected = "f22";
+		  break;
+		case 2:
+		  expected = "test3";
+		  break;
+		case 3:
+		  assert (0);
+		}
+
+	      if (symdata.name == NULL)
+		{
+		  fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j);
+		  symdata.failed = 1;
+		}
+	      /* Use strncmp, not strcmp, because GCC might create a
+		 clone.  */
+	      else if (strncmp (symdata.name, expected, strlen (expected))
+		       != 0)
+		{
+		  fprintf (stderr,
+			   ("test3: [%d]: unexpected syminfo name "
+			    "got %s expected %s\n"),
+			   j, symdata.name, expected);
+		  symdata.failed = 1;
+		}
+	    }
+
+	  if (symdata.failed)
+	    data.failed = 1;
+	}
+    }
+
+  printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS");
+
+  if (data.failed)
+    ++failures;
+
+  return failures;
+}
+
+/* Test the backtrace_simple function with inlined functions.  */
+
+static inline int test4 (void) __attribute__ ((always_inline));
+static inline int f32 (int) __attribute__ ((always_inline));
+static inline int f33 (int, int) __attribute__ ((always_inline));
+
+static inline int
+test4 (void)
+{
+  return f32 (__LINE__) + 1;
+}
+
+static inline int
+f32 (int f1line)
+{
+  return f33 (f1line, __LINE__) + 2;
+}
+
+static inline int
+f33 (int f1line, int f2line)
+{
+  uintptr_t addrs[20];
+  struct sdata data;
+  int f3line;
+  int i;
+
+  data.addrs = &addrs[0];
+  data.index = 0;
+  data.max = 20;
+  data.failed = 0;
+
+  f3line = __LINE__ + 1;
+  i = backtrace_simple (0, callback_two, error_callback_two, &data);
+
+  if (i != 0)
+    {
+      fprintf (stderr, "test3: unexpected return value %d\n", i);
+      data.failed = 1;
+    }
+
+  if (!data.failed)
+    {
+      struct info all[20];
+      struct bdata bdata;
+
+      bdata.all = &all[0];
+      bdata.index = 0;
+      bdata.max = 20;
+      bdata.failed = 0;
+
+      i = backtrace_pcinfo (addrs[0], callback_one, error_callback_one,
+			    &bdata);
+      if (i != 0)
+	{
+	  fprintf (stderr,
+		   ("test4: unexpected return value "
+		    "from backtrace_pcinfo %d\n"),
+		   i);
+	  bdata.failed = 1;
+	}
+
+      check ("test4", 0, all, f3line, "f33", &bdata.failed);
+      check ("test4", 1, all, f2line, "f32", &bdata.failed);
+      check ("test4", 2, all, f1line, "test4", &bdata.failed);
+
+      if (bdata.failed)
+	data.failed = 1;
+    }
+
+  printf ("%s: backtrace_simple inline\n", data.failed ? "FAIL" : "PASS");
+
+  if (data.failed)
+    ++failures;
+
+  return failures;
+}
+
+/* Run all the tests.  */
+
+int
+main (int argc ATTRIBUTE_UNUSED, char **argv)
+{
+  backtrace_set_executable_name (argv[0]);
+
+#if BACKTRACE_SUPPORTED
+  test1 ();
+  test2 ();
+  test3 ();
+  test4 ();
+#endif
+
+  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
+}
Index: libbacktrace/mmapio.c
===================================================================
--- libbacktrace/mmapio.c	(revision 0)
+++ libbacktrace/mmapio.c	(revision 0)
@@ -0,0 +1,96 @@ 
+/* mmapio.c -- File views using mmap.
+   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.  */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* This file implements file views and memory allocation when mmap is
+   available.  */
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET.  */
+
+int
+backtrace_get_view (int descriptor, off_t offset, size_t size,
+		    backtrace_error_callback error_callback,
+		    void *data, struct backtrace_view *view)
+{
+  size_t pagesize;
+  unsigned int inpage;
+  off_t pageoff;
+  void *map;
+
+  pagesize = getpagesize ();
+  inpage = offset % pagesize;
+  pageoff = offset - inpage;
+
+  size += inpage;
+  size = (size + (pagesize - 1)) & ~ (pagesize - 1);
+
+  map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff);
+  if (map == MAP_FAILED)
+    {
+      error_callback (data, "mmap", errno);
+      return 0;
+    }
+
+  view->data = (char *) map + inpage;
+  view->base = map;
+  view->len = size;
+
+  return 1;
+}
+
+/* Release a view read by backtrace_get_view.  */
+
+void
+backtrace_release_view (struct backtrace_view *view,
+			backtrace_error_callback error_callback,
+			void *data)
+{
+  union {
+    const void *cv;
+    void *v;
+  } const_cast;
+
+  const_cast.cv = view->base;
+  if (munmap (const_cast.v, view->len) < 0)
+    error_callback (data, "munmap", errno);
+}
Index: libbacktrace/fileline.c
===================================================================
--- libbacktrace/fileline.c	(revision 0)
+++ libbacktrace/fileline.c	(revision 0)
@@ -0,0 +1,150 @@ 
+/* 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;
+}
+
+/* Get the name of the executable--an internal function, to avoid a
+   globally visible variable name.  */
+
+const char *
+backtrace_executable_name (void)
+{
+  return executable_name;
+}
+
+/* The function we call to get file/line information.  */
+
+static fileline fileline_fn;
+
+/* Data to pass to fileline_fn.  */
+
+static void *fileline_data;
+
+/* The function we call to get symbol information.  */
+
+static syminfo syminfo_fn;
+
+/* Data to pass to syminfo_fn.  */
+
+static void *syminfo_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)
+    {
+      backtrace_unlock (error_callback, data);
+      if (fileline_initialization_failed)
+	error_callback (data, "failed to read executable information", 0);
+      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, &syminfo_fn,
+			     &syminfo_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)
+    return 0;
+
+  return fileline_fn (fileline_data, pc, callback, error_callback, data);
+}
+
+/* Given a PC, find the symbol for it, and its value.  */
+
+int
+backtrace_syminfo (uintptr_t pc, backtrace_syminfo_callback callback,
+		   backtrace_error_callback error_callback, void *data)
+{
+  fileline_initialize (error_callback, data);
+
+  if (fileline_initialization_failed)
+    return 0;
+
+  syminfo_fn (syminfo_data, pc, callback, error_callback, data);
+  return 1;
+}
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 + 1;
+  bdata.callback = callback;
+  bdata.error_callback = error_callback;
+  bdata.data = data;
+  bdata.ret = 0;
+  _Unwind_Backtrace (simple_unwind, &bdata);
+  return bdata.ret;
+}
Index: libbacktrace/mmap.c
===================================================================
--- libbacktrace/mmap.c	(revision 0)
+++ libbacktrace/mmap.c	(revision 0)
@@ -0,0 +1,226 @@ 
+/* mmap.c -- Memory allocation with mmap.
+   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 <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Memory allocation on systems that provide anonymous mmap.  This
+   permits the backtrace functions to be invoked from a signal
+   handler, assuming that mmap is async-signal safe.  */
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* A list of free memory blocks.  */
+
+struct backtrace_freelist_struct
+{
+  /* Next on list.  */
+  struct backtrace_freelist_struct *next;
+  /* Size of this block, including this structure.  */
+  size_t size;
+};
+
+/* Free list.  */
+
+struct backtrace_freelist_struct *backtrace_freelist;
+
+/* Free memory allocated by backtrace_alloc.  */
+
+static void
+backtrace_free_locked (void *addr, size_t size)
+{
+  /* Just leak small blocks.  We don't have to be perfect.  */
+  if (size >= sizeof (struct backtrace_freelist_struct))
+    {
+      struct backtrace_freelist_struct *p;
+
+      p = (struct backtrace_freelist_struct *) addr;
+      p->next = backtrace_freelist;
+      p->size = size;
+    }
+}
+
+/* Allocate memory like malloc.  */
+
+void *
+backtrace_alloc (size_t size, backtrace_error_callback error_callback,
+		 void *data)
+{
+  void *ret;
+  struct backtrace_freelist_struct **pp;
+  size_t pagesize;
+  size_t asksize;
+  void *page;
+
+  ret = NULL;
+
+  if (!backtrace_lock_alloc (error_callback, data))
+    return NULL;
+
+  /* Take space from the free list.  */
+
+  for (pp = &backtrace_freelist; *pp != NULL; pp = &(*pp)->next)
+    {
+      if ((*pp)->size >= size)
+	{
+	  struct backtrace_freelist_struct *p;
+
+	  p = *pp;
+	  *pp = p->next;
+
+	  /* Round for alignment; we assume that no type we care about
+	     is more than 8 bytes.  */
+	  size = (size + 7) & ~ (size_t) 7;
+	  if (size < p->size)
+	    backtrace_free_locked ((char *) p + size, p->size - size);
+
+	  ret = (void *) p;
+
+	  break;
+	}
+    }
+
+  if (!backtrace_unlock_alloc (error_callback, data))
+    return NULL;
+
+  if (ret == NULL)
+    {
+      /* Allocate a new page.  */
+
+      pagesize = getpagesize ();
+      asksize = (size + pagesize - 1) & ~ (pagesize - 1);
+      page = mmap (NULL, asksize, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+      if (page == NULL)
+	error_callback (data, "mmap", errno);
+      else
+	{
+	  size = (size + 7) & ~ (size_t) 7;
+	  if (size < asksize)
+	    backtrace_free ((char *) page + size, asksize - size,
+			    error_callback, data);
+
+	  ret = page;
+	}
+    }
+
+  return ret;
+}
+
+/* Free memory allocated by backtrace_alloc.  */
+
+void
+backtrace_free (void *addr, size_t size,
+		backtrace_error_callback error_callback, void *data)
+{
+  if (!backtrace_lock_alloc (error_callback, data))
+    return;
+
+  backtrace_free_locked (addr, size);
+
+  backtrace_unlock_alloc (error_callback, data);
+}
+
+/* Grow VEC by SIZE bytes.  */
+
+void *
+backtrace_vector_grow (size_t size, backtrace_error_callback error_callback,
+		       void *data, struct backtrace_vector *vec)
+{
+  void *ret;
+
+  if (size > vec->alc)
+    {
+      size_t pagesize;
+      size_t alc;
+      void *base;
+
+      pagesize = getpagesize ();
+      alc = vec->size + size;
+      if (vec->size == 0)
+	alc = 16 * size;
+      else if (alc < pagesize)
+	{
+	  alc *= 2;
+	  if (alc > pagesize)
+	    alc = pagesize;
+	}
+      else
+	alc = (alc + pagesize - 1) & ~ (pagesize - 1);
+      base = backtrace_alloc (alc, error_callback, data);
+      if (base == NULL)
+	return NULL;
+      if (vec->base != NULL)
+	{
+	  memcpy (base, vec->base, vec->size);
+	  backtrace_free (vec->base, vec->alc, error_callback, data);
+	}
+      vec->base = base;
+      vec->alc = alc - vec->size;
+    }
+
+  ret = (char *) vec->base + vec->size;
+  vec->size += size;
+  vec->alc -= size;
+  return ret;
+}
+
+/* Finish the current allocation on VEC.  */
+
+void
+backtrace_vector_finish (struct backtrace_vector *vec)
+{
+  vec->base = (char *) vec->base + vec->size;
+  vec->size = 0;
+}
+
+/* Release any extra space allocated for VEC.  */
+
+int
+backtrace_vector_release (struct backtrace_vector *vec,
+			  backtrace_error_callback error_callback,
+			  void *data)
+{
+  backtrace_free ((char *) vec->base + vec->size, vec->alc,
+		  error_callback, data);
+  vec->alc = 0;
+  return 1;
+}
Index: libbacktrace/alloc.c
===================================================================
--- libbacktrace/alloc.c	(revision 0)
+++ libbacktrace/alloc.c	(revision 0)
@@ -0,0 +1,134 @@ 
+/* alloc.c -- Memory allocation without mmap.
+   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 "backtrace.h"
+#include "internal.h"
+
+/* Allocation routines to use on systems that do not support anonymous
+   mmap.  This implementation just uses malloc, which means that the
+   backtrace functions may not be safely invoked from a signal
+   handler.  */
+
+/* Allocate memory like malloc.  */
+
+void *
+backtrace_alloc (size_t size, backtrace_error_callback error_callback,
+		 void *data)
+{
+  void *ret;
+
+  ret = malloc (size);
+  if (ret == NULL)
+    error_callback (data, "malloc", errno);
+  return ret;
+}
+
+/* Free memory.  */
+
+void
+backtrace_free (void *p, size_t size ATTRIBUTE_UNUSED,
+		backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+		void *data ATTRIBUTE_UNUSED)
+{
+  free (p);
+}
+
+/* Grow VEC by SIZE bytes.  */
+
+void *
+backtrace_vector_grow (size_t size, backtrace_error_callback error_callback,
+		       void *data, struct backtrace_vector *vec)
+{
+  void *ret;
+
+  if (size > vec->alc)
+    {
+      size_t alc;
+      void *base;
+
+      if (vec->size == 0)
+	alc = 32 * size;
+      else if (vec->size >= 4096)
+	alc = vec->size + 4096;
+      else
+	alc = 2 * vec->size;
+
+      if (alc < vec->size + size)
+	alc = vec->size + size;
+
+      base = realloc (vec->base, alc);
+      if (base == NULL)
+	{
+	  error_callback (data, "realloc", errno);
+	  return NULL;
+	}
+
+      vec->base = base;
+      vec->alc = alc - vec->size;
+    }
+
+  ret = (char *) vec->base + vec->size;
+  vec->size += size;
+  vec->alc -= size;
+  return ret;
+}
+
+/* Finish the current allocation on VEC.  */
+
+void
+backtrace_vector_finish (struct backtrace_vector *vec)
+{
+  vec->base = (char *) vec->base + vec->size;
+  vec->size = 0;
+}
+
+/* Release any extra space allocated for VEC.  */
+
+int
+backtrace_vector_release (struct backtrace_vector *vec,
+			  backtrace_error_callback error_callback,
+			  void *data)
+{
+  vec->base = realloc (vec->base, vec->size);
+  if (vec->base == NULL)
+    {
+      error_callback (data, "realloc", errno);
+      return 0;
+    }
+  vec->alc = 0;
+  return 1;
+}
Index: libbacktrace/print.c
===================================================================
--- libbacktrace/print.c	(revision 0)
+++ libbacktrace/print.c	(revision 0)
@@ -0,0 +1,80 @@ 
+/* 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)
+{
+  const char *name;
+
+  name = backtrace_executable_name ();
+  if (name == NULL)
+    name = "/proc/self/exe";
+  fprintf (stderr, "%s: libbacktrace: %s", name, msg);
+  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);
+}