Get proper dwarf-based backtraces with filename and line numbers.
authorMilian Wolff <mail@milianw.de>
Mon, 19 May 2014 06:26:44 +0000 (08:26 +0200)
committerMilian Wolff <mail@milianw.de>
Mon, 19 May 2014 06:38:12 +0000 (08:38 +0200)
This code is based on the libbacktrace from GCC, see:
https://github.com/mirrors/gcc/tree/master/libbacktrace

Furthermore, we include btrace.{cpp,h} and CMakeLists.txt, based
on Vogl from Valve Software/RAD Game Tools, see:
https://github.com/ValveSoftware/vogl.git

31 files changed:
CMakeLists.txt
libbacktrace/CMakeLists.txt [new file with mode: 0644]
libbacktrace/ChangeLog [new file with mode: 0644]
libbacktrace/LICENSE.vogl [new file with mode: 0644]
libbacktrace/README [new file with mode: 0644]
libbacktrace/alloc.c [new file with mode: 0644]
libbacktrace/atomic.c [new file with mode: 0644]
libbacktrace/auxincl/dwarf2.h [new file with mode: 0644]
libbacktrace/auxincl/filenames.h [new file with mode: 0644]
libbacktrace/backtrace-supported.h.in [new file with mode: 0644]
libbacktrace/backtrace.c [new file with mode: 0644]
libbacktrace/backtrace.h [new file with mode: 0644]
libbacktrace/btrace.cpp [new file with mode: 0644]
libbacktrace/btrace.h [new file with mode: 0644]
libbacktrace/config.h.in.cmake [new file with mode: 0644]
libbacktrace/dwarf.c [new file with mode: 0644]
libbacktrace/elf.c [new file with mode: 0644]
libbacktrace/fileline.c [new file with mode: 0644]
libbacktrace/internal.h [new file with mode: 0644]
libbacktrace/libelftc_dem_gnu3.c [new file with mode: 0644]
libbacktrace/mmap.c [new file with mode: 0644]
libbacktrace/mmapio.c [new file with mode: 0644]
libbacktrace/nounwind.c [new file with mode: 0644]
libbacktrace/posix.c [new file with mode: 0644]
libbacktrace/print.c [new file with mode: 0644]
libbacktrace/read.c [new file with mode: 0644]
libbacktrace/simple.c [new file with mode: 0644]
libbacktrace/state.c [new file with mode: 0644]
libbacktrace/unknown.c [new file with mode: 0644]
malloctrace.cpp
trace.sh

index a938053..ac4133e 100644 (file)
@@ -1,17 +1,18 @@
 cmake_minimum_required(VERSION 2.8)
 project(mallocinfo)
 
+set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++11")
+
+add_subdirectory(tests)
+add_subdirectory(libbacktrace)
+
 add_library(dumpmallocinfo SHARED dumpmallocinfo.cpp)
 target_link_libraries(dumpmallocinfo -lpthread)
 
-set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++11")
-
 add_executable(mallocinfo main.cpp)
 target_link_libraries(mallocinfo dumpmallocinfo)
 
 add_library(malloctrace SHARED malloctrace.cpp)
-target_link_libraries(malloctrace -ldl -lunwind)
+target_link_libraries(malloctrace -ldl backtrace)
 
 install(TARGETS mallocinfo RUNTIME DESTINATION bin)
-
-add_subdirectory(tests)
diff --git a/libbacktrace/CMakeLists.txt b/libbacktrace/CMakeLists.txt
new file mode 100644 (file)
index 0000000..21fc9f3
--- /dev/null
@@ -0,0 +1,197 @@
+# based on: https://github.com/ValveSoftware/vogl/blob/master/src/libbacktrace/CMakeLists.txt
+#
+#    CMakeLists.txt -- libbacktrace CMake build script
+#    Contributed by Alexander Monakov, ISP RAS
+#
+# 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.  */
+
+cmake_minimum_required (VERSION 2.8)
+
+project (libbacktrace)
+
+set (BACKTRACE_SUPPORTED 1)
+
+include (CheckSymbolExists)
+if (true)
+    # When unwind.h is in /usr/local/include, check_symbol_exists() is failing. But
+    #  these functions are in the Linux Standard Base Core Spec and should just exist
+    #  for Linux, yes?  I'm assuming that's the case for now and using them.
+    #  (Otherwise stack tracing won't work anyway).
+    set (HAVE_BACKTRACE 1)
+    set (HAVE_GETIPINFO 1)
+else()
+    check_symbol_exists (_Unwind_Backtrace unwind.h HAVE_BACKTRACE)
+    check_symbol_exists (_Unwind_GetIPInfo unwind.h HAVE_GETIPINFO)
+endif()
+
+if (HAVE_BACKTRACE)
+    set (BACKTRACE_FILE backtrace.c simple.c)
+else ()
+    message("WARNING: No unwind.h, setting BACKTRACE_SUPPORTED=0")
+    set (BACKTRACE_FILE nounwind.c)
+    set (BACKTRACE_SUPPORTED 0)
+endif ()
+
+include (CheckCCompilerFlag)
+check_c_compiler_flag ("-funwind-tables" FLAG_UNWIND_TABLES)
+if (FLAG_UNWIND_TABLES)
+    add_definitions ("-funwind-tables")
+endif ()
+
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+    add_definitions("-D_DEBUG -DDEBUG")
+endif()
+
+# Adjust warnings
+if (CMAKE_COMPILER_IS_GNUCC)
+    add_definitions ("-Wno-switch -Wno-enum-compare")
+endif ()
+
+# Add these compiler options to match how voglcore and voglcommon are compiled, and to get libbacktrace compiling with gcc.
+# -fno-strict-aliasing is particularly important if voglcore is called, and -fvisibility=hidden must be used otherwide symbols from this lib could be made visible in libvogltrace.
+add_definitions ("-g -fno-omit-frame-pointer -fno-strict-aliasing -fno-math-errno -fvisibility=hidden")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -g -DNDEBUG")
+
+if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+   if ( NOT BUILD_X64 )
+      # Fix startup crash in dlopen_notify_callback (called indirectly from our dlopen() function) when tracing glxspheres on my AMD dev box (x86 release only)
+      #add_definitions ("-mstack-alignment=8")
+   endif()
+endif()
+
+# clang doesn't print colored diagnostics when invoked from Ninja
+if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+  if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
+      add_definitions ("-fcolor-diagnostics")
+  endif()
+endif()
+
+check_c_source_compiles (
+    "int i;
+    int main() {
+    __sync_bool_compare_and_swap (&i, i, i);
+    __sync_lock_test_and_set (&i, 1);
+    __sync_lock_release (&i);}"
+    HAVE_SYNC_FUNCTIONS)
+
+if (HAVE_SYNC_FUNCTIONS)
+    set (BACKTRACE_SUPPORTS_THREADS 1)
+else ()
+    set (BACKTRACE_SUPPORTS_THREADS 0)
+endif ()
+
+if (CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+    set (FORMAT_FILE elf.c dwarf.c)
+    math (EXPR BACKTRACE_ELF_SIZE 8*${CMAKE_C_SIZEOF_DATA_PTR})
+else ()
+    set (FORMAT_FILE unknown.c)
+    set (BACKTRACE_SUPPORTED 0)
+endif ()
+
+check_symbol_exists (mmap sys/mman.h HAVE_MMAP)
+
+if (HAVE_MMAP)
+    set (VIEW_FILE mmapio.c)
+    check_symbol_exists (MAP_ANONYMOUS sys/mman.h HAVE_MMAP_ANONYMOUS)
+    check_symbol_exists (MAP_ANON sys/mman.h HAVE_MMAP_ANON)
+    if (HAVE_MMAP_ANONYMOUS AND HAVE_MMAP_ANON)
+    set (ALLOC_FILE mmap.c)
+    else ()
+    set (ALLOC_FILE alloc.c)
+    endif ()
+else ()
+    set (VIEW_FILE read.c)
+    set (ALLOC_FILE alloc.c)
+endif ()
+
+if (ALLOC_FILE STREQUAL "alloc.c")
+    set (BACKTRACE_USES_MALLOC 1)
+else ()
+    set (BACKTRACE_USES_MALLOC 0)
+endif ()
+
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+    add_definitions("-D_DEBUG -DDEBUG")
+endif()
+add_definitions (-D_GNU_SOURCE)
+set (CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_GNU_SOURCE)
+check_symbol_exists (dl_iterate_phdr link.h HAVE_DL_ITERATE_PHDR)
+
+include (CheckFunctionExists)
+check_function_exists (fcntl HAVE_FCNTL)
+
+check_function_exists (strnlen HAVE_DECL_STRNLEN)
+
+check_function_exists (getexecname HAVE_GETEXECNAME)
+
+find_library(LIBUNWIND_LIBRARY NAMES unwind libunwind REQUIRE)
+
+include (CheckLibraryExists)
+check_library_exists (${LIBUNWIND_LIBRARY} unw_backtrace_skip "" HAVE_UNW_BACKTRACE_SKIP)
+
+include (CheckIncludeFile)
+check_include_file (dwarf.h HAVE_DWARF_H)
+if (NOT HAVE_DWARF_H)
+    message("WARNING: No dwarf.h, setting BACKTRACE_SUPPORTED=0")
+    set (FORMAT_FILE unknown.c)
+    set (BACKTRACE_SUPPORTED 0)
+endif ()
+
+configure_file (backtrace-supported.h.in backtrace-supported.h)
+
+configure_file (config.h.in.cmake config.h)
+
+find_path(LIBUNWIND_INCLUDE_DIR libunwind.h)
+
+include_directories (BEFORE
+    ${CMAKE_CURRENT_BINARY_DIR}
+)
+include_directories (
+    auxincl
+    ${LIBUNWIND_INCLUDE_DIR}
+)
+
+add_library (backtrace STATIC
+    ${BACKTRACE_FILE}
+    ${FORMAT_FILE}
+    ${VIEW_FILE}
+    ${ALLOC_FILE}
+    atomic.c
+    fileline.c
+    posix.c
+    print.c
+    state.c
+    btrace.cpp
+)
+
+target_link_libraries(backtrace ${LIBUNWIND_LIBRARY})
+
+set_target_properties (backtrace PROPERTIES
+    COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}"
+)
diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
new file mode 100644 (file)
index 0000000..2b0b439
--- /dev/null
@@ -0,0 +1,401 @@
+2014-01-02  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       Update copyright years
+
+2013-12-06  Jakub Jelinek  <jakub@redhat.com>
+
+       * elf.c (ET_DYN): Undefine and define again.
+       (elf_add): Add exe argument, if true and ehdr.e_type is ET_DYN,
+       return early -1 without closing the descriptor.
+       (struct phdr_data): Add exe_descriptor.
+       (phdr_callback): If pd->exe_descriptor is not -1, for very first
+       call if dlpi_name is NULL just call elf_add with the exe_descriptor,
+       otherwise backtrace_close the exe_descriptor if not -1.  Adjust
+       call to elf_add.
+       (backtrace_initialize): Adjust call to elf_add.  If it returns
+       -1, set pd.exe_descriptor to descriptor, otherwise set it to -1.
+
+2013-12-05  Ian Lance Taylor  <iant@google.com>
+
+       * alloc.c (backtrace_vector_finish): Add error_callback and data
+       parameters.  Call backtrace_vector_release.  Return address base.
+       * mmap.c (backtrace_vector_finish): Add error_callback and data
+       parameters.  Return address base.
+       * dwarf.c (read_function_info): Get new address base from
+       backtrace_vector_finish.
+       * internal.h (backtrace_vector_finish): Update declaration.
+
+2013-11-27  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c (find_address_ranges): New static function, broken out
+       of build_address_map.
+       (build_address_map): Call it.
+       * btest.c (check): Check for missing filename or function, rather
+       than crashing.
+       (f3): Check that enough frames were returned.
+
+2013-11-19  Jakub Jelinek  <jakub@redhat.com>
+
+       * backtrace.h (backtrace_syminfo_callback): Add symsize argument.
+       * elf.c (elf_syminfo): Pass 0 or sym->size to the callback as
+       last argument.
+       * btest.c (struct symdata): Add size field.
+       (callback_three): Add symsize argument.  Copy it to the data->size
+       field.
+       (f23): Set symdata.size to 0.
+       (test5): Likewise.  If sizeof (int) > 1, lookup address of
+       ((uintptr_t) &global) + 1.  Verify symdata.val and symdata.size
+       values.
+
+       * atomic.c: Include sys/types.h.
+
+2013-11-18  Ian Lance Taylor  <iant@google.com>
+
+       * configure.ac: Check for support of __atomic extensions.
+       * internal.h: Declare or #define atomic functions for use in
+       backtrace code.
+       * atomic.c: New file.
+       * dwarf.c (dwarf_lookup_pc): Use atomic functions.
+       (dwarf_fileline, backtrace_dwarf_add): Likewise.
+       * elf.c (elf_add_syminfo_data, elf_syminfo): Likewise.
+       (backtrace_initialize): Likewise.
+       * fileline.c (fileline_initialize): Likewise.
+       * Makefile.am (libbacktrace_la_SOURCES): Add atomic.c.
+       * configure, config.h.in, Makefile.in: Rebuild.
+
+2013-11-18  Jakub Jelinek  <jakub@redhat.com>
+
+       * elf.c (SHN_UNDEF): Define.
+       (elf_initialize_syminfo): Add base_address argument.  Ignore symbols
+       with st_shndx == SHN_UNDEF.  Add base_address to address fields.
+       (elf_add): Adjust caller.
+
+       * elf.c (phdr_callback): Process info->dlpi_addr == 0 normally.
+
+2013-11-16  Ian Lance Taylor  <iant@google.com>
+
+       * backtrace.h (backtrace_create_state): Correct comment about
+       threading.
+
+2013-11-15  Ian Lance Taylor  <iant@google.com>
+
+       * backtrace.h (backtrace_syminfo): Update comment and parameter
+       name to take any address, not just a PC value.
+       * elf.c (STT_OBJECT): Define.
+       (elf_nosyms): Rename parameter pc to addr.
+       (elf_symbol_search): Rename local variable pc to addr.
+       (elf_initialize_syminfo): Add STT_OBJECT symbols to elf_symbols.
+       (elf_syminfo): Rename parameter pc to addr.
+       * btest.c (global): New global variable.
+       (test5): New test.
+       (main): Call test5.
+
+2013-10-17  Ian Lance Taylor  <iant@google.com>
+
+       * elf.c (elf_add): Don't get the wrong offsets if a debug section
+       is missing.
+
+2013-10-15  David Malcolm  <dmalcolm@redhat.com>
+
+       * configure.ac: Add --enable-host-shared, setting up
+       pre-existing PIC_FLAG variable within Makefile.am et al.
+       * configure: Regenerate.
+
+2013-09-20  Alan Modra  <amodra@gmail.com>
+
+       * configure: Regenerate.
+
+2013-07-23  Alexander Monakov  <amonakov@ispras.ru>
+
+       * elf.c (elf_syminfo): Loop over the elf_syminfo_data chain.
+
+2013-07-23  Alexander Monakov  <amonakov@ispras.ru>
+
+       * elf.c (backtrace_initialize): Pass elf_fileline_fn to
+       dl_iterate_phdr callbacks.
+
+2013-03-25  Ian Lance Taylor  <iant@google.com>
+
+       * alloc.c: #include <sys/types.h>.
+       * mmap.c: Likewise.
+
+2013-01-31  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c (read_function_info): Permit fvec parameter to be NULL.
+       (dwarf_lookup_pc): Don't use ddata->fvec if threaded.
+
+2013-01-25  Jakub Jelinek  <jakub@redhat.com>
+
+       PR other/56076
+       * dwarf.c (read_line_header): Don't crash if DW_AT_comp_dir
+       attribute was not seen.
+
+2013-01-16  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c (struct unit): Add filename and abs_filename fields.
+       (build_address_map): Set new fields when reading unit.
+       (dwarf_lookup_pc): If we don't find an entry in the line table,
+       just return the main file name.
+
+2013-01-14  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       Update copyright years.
+
+2013-01-01  Ian Lance Taylor  <iant@google.com>
+
+       PR bootstrap/54834
+       * Makefile.am (AM_CPPFLAGS): Remove -I ../gcc/include and -I
+       $(MULTIBUILDTOP)/../../gcc/include.
+       * Makefile.in: Rebuild.
+
+2013-01-01  Ian Lance Taylor  <iant@google.com>
+
+       PR other/55536
+       * mmap.c (backtrace_alloc): Don't call sync functions if not
+       threaded.
+       (backtrace_free): Likewise.
+
+2012-12-12  John David Anglin  <dave.anglin@nrc-cnrc.gc.ca>
+
+       * mmapio.c: Define MAP_FAILED if not defined.
+
+2012-12-11  Jakub Jelinek  <jakub@redhat.com>
+
+       PR bootstrap/54926
+       * Makefile.am (AM_CFLAGS): Remove -frandom-seed=$@.
+       * configure.ac: If --with-target-subdir, add -frandom-seed=$@
+       to EXTRA_FLAGS unconditionally, otherwise check whether the compiler
+       accepts it.
+       * Makefile.in: Regenerated.
+       * configure: Regenerated.
+
+2012-12-07  Jakub Jelinek  <jakub@redhat.com>
+
+       PR bootstrap/54926
+       * Makefile.am (AM_CFLAGS): Add -frandom-seed=$@.
+       * Makefile.in: Regenerated.
+
+2012-11-20  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c (read_attribute): Always clear val.
+
+2012-11-13  Ian Lance Taylor  <iant@google.com>
+
+       PR other/55312
+       * configure.ac: Only add -Werror if building a target library.
+       * configure: Rebuild.
+
+2012-11-12  Ian Lance Taylor  <iant@google.com>
+           Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
+           Gerald Pfeifer  <gerald@pfeifer.com>
+
+       * configure.ac: Check for getexecname.
+       * fileline.c: #include <errno.h>.  Define getexecname if not
+       available.
+       (fileline_initialize): Try to find the executable in a few
+       different ways.
+       * print.c (error_callback): Only print the filename if it came
+       from the backtrace state.
+       * configure, config.h.in: Rebuild.
+
+2012-10-29  Ian Lance Taylor  <iant@google.com>
+
+       * mmap.c (backtrace_vector_release): Correct last patch: add
+       aligned, not size.
+
+2012-10-29  Ian Lance Taylor  <iant@google.com>
+
+       * mmap.c (backtrace_vector_release): Make sure freed block is
+       aligned on 8-byte boundary.
+
+2012-10-26  Ian Lance Taylor  <iant@google.com>
+
+       PR other/55087
+       * posix.c (backtrace_open): Add does_not_exist parameter.
+       * elf.c (phdr_callback): Do not warn if shared library could not
+       be opened.
+       * fileline.c (fileline_initialize): Update calls to
+       backtrace_open.
+       * internal.h (backtrace_open): Update declaration.
+
+2012-10-26  Jack Howarth  <howarth@bromo.med.uc.edu>
+
+       PR target/55061
+       * configure.ac: Check for _Unwind_GetIPInfo function declaration.
+       * configure: Regenerate.
+
+2012-10-24  Ian Lance Taylor  <iant@google.com>
+
+       PR target/55061
+       * configure.ac: Check whether -funwind-tables option works.
+       * configure: Rebuild.
+
+2012-10-11  Ian Lance Taylor  <iant@google.com>
+
+       * configure.ac: Do not use dl_iterate_phdr on Solaris 10.
+       * configure: Rebuild.
+
+2012-10-10  Ian Lance Taylor  <iant@google.com>
+
+       * elf.c: Rename all Elf typedefs to start with b_elf, and be all
+       lower case.
+
+2012-10-10  Hans-Peter Nilsson  <hp@bitrange.com>
+
+       * elf.c (elf_add_syminfo_data): Add casts to avoid warning.
+
+2012-10-09  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c (dwarf_fileline): Add cast to avoid warning.
+       (backtrace_dwarf_add): Likewise.
+
+2012-10-09  Ian Lance Taylor  <iant@google.com>
+
+       Add support for tracing through shared libraries.
+       * configure.ac: Check for link.h and dl_iterate_phdr.
+       * elf.c: #include <link.h> if system has dl_iterate_phdr.  #undef
+       ELF macros before #defining them.
+       (dl_phdr_info, dl_iterate_phdr): Define if system does not have
+       dl_iterate_phdr.
+       (struct elf_syminfo_data): Add next field.
+       (elf_initialize_syminfo): Initialize next field.
+       (elf_add_syminfo_data): New static function.
+       (elf_add): New static function, broken out of
+       backtrace_initialize.  Call backtrace_dwarf_add instead of
+       backtrace_dwarf_initialize.
+       (struct phdr_data): Define.
+       (phdr_callback): New static function.
+       (backtrace_initialize): Call elf_add.
+       * dwarf.c (struct dwarf_data): Add next and base_address fields.
+       (add_unit_addr): Add base_address parameter.  Change all callers.
+       (add_unit_ranges, build_address_map): Likewise.
+       (add_line): Add ddata parameter.  Change all callers.
+       (read_line_program, add_function_range): Likewise.
+       (dwarf_lookup_pc): New static function, broken out of
+       dwarf_fileline.
+       (dwarf_fileline): Call dwarf_lookup_pc.
+       (build_dwarf_data): New static function.
+       (backtrace_dwarf_add): New function.
+       (backtrace_dwarf_initialize): Remove.
+       * internal.h (backtrace_dwarf_initialize): Don't declare.
+       (backtrace_dwarf_add): Declare.
+       * configure, config.h.in: Rebuild.
+
+2012-10-04  Gerald Pfeifer  <gerald@pfeifer.com>
+
+       * btest.c (f23): Avoid uninitialized variable warning.
+
+2012-10-04  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c: If the system header files do not declare strnlen,
+       provide our own version.
+
+2012-10-03  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c (read_uleb128): Fix overflow test.
+       (read_sleb128): Likewise.
+       (build_address_map): Don't change unit_buf.start.
+
+2012-10-02  Uros Bizjak  <ubizjak@gmail.com>
+
+       PR other/54761
+       * configure.ac (EXTRA_FLAGS): New.
+       * Makefile.am (AM_FLAGS): Add $(EXTRA_FLAGS).
+       * configure, Makefile.in: Regenerate.
+
+2012-09-29  Ian Lance Taylor  <iant@google.com>
+
+       PR other/54749
+       * fileline.c (fileline_initialize): Pass errnum as -1 when
+       reporting that we could not read executable information after a
+       previous failure.
+
+2012-09-27  Ian Lance Taylor  <iant@google.com>
+
+       PR bootstrap/54732
+       * configure.ac: Add no-dependencies to AM_INIT_AUTOMAKE.
+       * Makefile.am: Add dependencies for all objects.
+       * configure, aclocal.m4, Makefile.in: Rebuild.
+
+2012-09-27  Ian Lance Taylor  <iant@google.com>
+
+       PR other/54726
+       * elf.c (backtrace_initialize): Set *fileln_fn, not
+       state->fileln_fn.
+
+2012-09-19  Ian Lance Taylor  <iant@google.com>
+
+       * configure.ac: Only use GCC_CHECK_UNWIND_GETIPINFO when compiled
+       as a target library.
+       * configure: Rebuild.
+
+2012-09-19  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
+           Ian Lance Taylor  <iant@google.com>
+
+        * configure.ac (GCC_HEADER_STDINT): Invoke.
+        * backtrace.h: If we can't find <stdint.h>, use "gstdint.h".
+        * btest.c: Don't include <stdint.h>.
+        * dwarf.c: Likewise.
+        * configure, aclocal.m4, Makefile.in, config.h.in: Rebuild.
+
+2012-09-18  Ian Lance Taylor  <iant@google.com>
+
+       PR bootstrap/54623
+       * Makefile.am (AM_CPPFLAGS): Define.
+       (AM_CFLAGS): Remove -I options.
+       * Makefile.in: Rebuild.
+
+2012-09-18  Ian Lance Taylor  <iant@google.com>
+
+       * posix.c (O_BINARY): Define if not defined.
+       (backtrace_open): Pass O_BINARY to open.  Only call fcntl if
+       HAVE_FCNTL is defined.
+       * configure.ac: Test for the fcntl function.
+       * configure, config.h.in: Rebuild.
+
+2012-09-18  Ian Lance Taylor  <iant@google.com>
+
+       * btest.c (test1, test2, test3, test4): Add the unused attribute.
+
+2012-09-18  Ian Lance Taylor  <iant@google.com>
+
+       * dwarf.c: Correct test of HAVE_DECL_STRNLEN.
+
+2012-09-18  Ian Lance Taylor  <iant@google.com>
+
+       * configure.ac: Add AC_USE_SYSTEM_EXTENSIONS.
+       * mmapio.c: Don't define _GNU_SOURCE.
+       * configure, config.h.in: Rebuild.
+
+2012-09-18  Ian Lance Taylor  <iant@google.com>
+
+       * configure.ac: Check whether strnlen is declared.
+       * dwarf.c: Declare strnlen if not declared.
+       * configure, config.h.in: Rebuild.
+
+2012-09-18  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
+
+       * fileline.c: Include <stdlib.h>.
+       * mmap.c: Likewise.
+
+2012-09-17  Ian Lance Taylor  <iant@google.com>
+
+       PR bootstrap/54611
+       * nounwind.c (backtrace_full): Rename from backtrace.  Add state
+       parameter.
+
+2012-09-17  Gerald Pfeifer  <gerald@pfeifer.com>
+
+       PR bootstrap/54611
+       * nounwind.c (backtrace_simple): Add state parameter.
+
+2012-09-17  Ian Lance Taylor  <iant@google.com>
+
+       PR bootstrap/54609
+       * unknown.c (unknown_fileline): Add state parameter, remove
+       fileline_data parameter, name error_callback parameter.
+       (backtrace_initialize): Add state parameter.
+
+2012-09-17  Ian Lance Taylor  <iant@google.com>
+
+       * Initial implementation.
diff --git a/libbacktrace/LICENSE.vogl b/libbacktrace/LICENSE.vogl
new file mode 100644 (file)
index 0000000..8953561
--- /dev/null
@@ -0,0 +1,24 @@
+applies to: btrace.h, btrace.cpp, CMakeLists.txt
+
+Copyright 2013-2014 RAD Game Tools and Valve Software
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/libbacktrace/README b/libbacktrace/README
new file mode 100644 (file)
index 0000000..fa1a8b0
--- /dev/null
@@ -0,0 +1,25 @@
+https://github.com/mirrors/gcc/tree/master/libbacktrace
+
+The libbacktrace library
+Initially written by Ian Lance Taylor <iant@google.com>
+
+The libbacktrace library may be linked into a program or library and
+used to produce symbolic backtraces.  Sample uses would be to print a
+detailed backtrace when an error occurs or to gather detailed
+profiling information.
+
+The libbacktrace library is provided under a BSD license.  See the
+source files for the exact license text.
+
+The public functions are declared and documented in the header file
+backtrace.h, which should be #include'd by a user of the library.
+
+Building libbacktrace will generate a file backtrace-supported.h,
+which a user of the library may use to determine whether backtraces
+will work.  See the source file backtrace-supported.h.in for the
+macros that it defines.
+
+As of September 2012, libbacktrace only supports ELF executables with
+DWARF debugging information.  The library is written to make it
+straightforward to add support for other object file and debugging
+formats.
diff --git a/libbacktrace/alloc.c b/libbacktrace/alloc.c
new file mode 100644 (file)
index 0000000..37eca65
--- /dev/null
@@ -0,0 +1,152 @@
+/* alloc.c -- Memory allocation without mmap.
+   Copyright (C) 2012-2014 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 "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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+               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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                      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_state *state,
+                        struct backtrace_vector *vec,
+                        backtrace_error_callback error_callback,
+                        void *data)
+{
+  void *ret;
+
+  /* With this allocator we call realloc in backtrace_vector_grow,
+     which means we can't easily reuse the memory here.  So just
+     release it.  */
+  if (!backtrace_vector_release (state, vec, error_callback, data))
+    return NULL;
+  ret = vec->base;
+  vec->base = NULL;
+  vec->size = 0;
+  vec->alc = 0;
+  return ret;
+}
+
+/* Release any extra space allocated for VEC.  */
+
+int
+backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                         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;
+}
diff --git a/libbacktrace/atomic.c b/libbacktrace/atomic.c
new file mode 100644 (file)
index 0000000..f139d74
--- /dev/null
@@ -0,0 +1,113 @@
+/* atomic.c -- Support for atomic functions if not present.
+   Copyright (C) 2013-2014 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 "backtrace-supported.h"
+#include "internal.h"
+
+/* This file holds implementations of the atomic functions that are
+   used if the host compiler has the sync functions but not the atomic
+   functions, as is true of versions of GCC before 4.7.  */
+
+#if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS)
+
+/* Do an atomic load of a pointer.  */
+
+void *
+backtrace_atomic_load_pointer (void *arg)
+{
+  void **pp;
+  void *p;
+
+  pp = (void **) arg;
+  p = *pp;
+  while (!__sync_bool_compare_and_swap (pp, p, p))
+    p = *pp;
+  return p;
+}
+
+/* Do an atomic load of an int.  */
+
+int
+backtrace_atomic_load_int (int *p)
+{
+  int i;
+
+  i = *p;
+  while (!__sync_bool_compare_and_swap (p, i, i))
+    i = *p;
+  return i;
+}
+
+/* Do an atomic store of a pointer.  */
+
+void
+backtrace_atomic_store_pointer (void *arg, void *p)
+{
+  void **pp;
+  void *old;
+
+  pp = (void **) arg;
+  old = *pp;
+  while (!__sync_bool_compare_and_swap (pp, old, p))
+    old = *pp;
+}
+
+/* Do an atomic store of a size_t value.  */
+
+void
+backtrace_atomic_store_size_t (size_t *p, size_t v)
+{
+  size_t old;
+
+  old = *p;
+  while (!__sync_bool_compare_and_swap (p, old, v))
+    old = *p;
+}
+
+/* Do an atomic store of a int value.  */
+
+void
+backtrace_atomic_store_int (int *p, int v)
+{
+  size_t old;
+
+  old = *p;
+  while (!__sync_bool_compare_and_swap (p, old, v))
+    old = *p;
+}
+
+#endif
diff --git a/libbacktrace/auxincl/dwarf2.h b/libbacktrace/auxincl/dwarf2.h
new file mode 100644 (file)
index 0000000..d135d8a
--- /dev/null
@@ -0,0 +1,52 @@
+/* dwarf2.h -- minimal GCC dwarf2.h replacement for libbacktrace
+   Contributed by Alexander Monakov, ISP RAS
+
+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_AUX_DWARF2_H
+#define BACKTRACE_AUX_DWARF2_H
+
+/* Use the system header for the bulk of the definitions.  */
+#include <dwarf.h>
+
+/* Provide stub enum tags.  */
+enum dwarf_attribute {_dummy_dwarf_attribute};
+enum dwarf_form {_dummy_dwarf_form};
+enum dwarf_tag {_dummy_dwarf_tag};
+
+/* Define potentially missing enum values.  */
+#define DW_FORM_GNU_addr_index 0x1f01
+#define DW_FORM_GNU_str_index  0x1f02
+
+#define DW_FORM_GNU_ref_alt    0x1f20
+#define DW_FORM_GNU_strp_alt   0x1f21
+
+#define DW_LNS_extended_op 0
+
+#endif
diff --git a/libbacktrace/auxincl/filenames.h b/libbacktrace/auxincl/filenames.h
new file mode 100644 (file)
index 0000000..000dd30
--- /dev/null
@@ -0,0 +1,40 @@
+/* filenames.h -- minimal GCC filenames.h replacement for libbacktrace
+   Contributed by Alexander Monakov, ISP RAS
+
+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_AUX_FILENAMES_H
+#define BACKTRACE_AUX_FILENAMES_H
+
+/* Assume POSIX paths.  */
+
+#define IS_DIR_SEPARATOR(c) ((c) == '/')
+#define IS_ABSOLUTE_PATH(f) ((f)[0] == '/')
+
+#endif
diff --git a/libbacktrace/backtrace-supported.h.in b/libbacktrace/backtrace-supported.h.in
new file mode 100644 (file)
index 0000000..901e3f7
--- /dev/null
@@ -0,0 +1,61 @@
+/* backtrace-supported.h.in -- Whether stack backtrace is supported.
+   Copyright (C) 2012-2014 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.  */
+
+/* The file backtrace-supported.h.in is used by configure to generate
+   the file backtrace-supported.h.  The file backtrace-supported.h may
+   be #include'd to see whether the backtrace library will be able to
+   get a backtrace and produce symbolic information.  */
+
+
+/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
+   should work, 0 if it will not.  Libraries may #include this to make
+   other arrangements.  */
+
+#define BACKTRACE_SUPPORTED @BACKTRACE_SUPPORTED@
+
+/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
+   library will call malloc as it works, 0 if it will call mmap
+   instead.  This may be used to determine whether it is safe to call
+   the backtrace functions from a signal handler.  In general this
+   only applies to calls like backtrace and backtrace_pcinfo.  It does
+   not apply to backtrace_simple, which never calls malloc.  It does
+   not apply to backtrace_print, which always calls fprintf and
+   therefore malloc.  */
+
+#define BACKTRACE_USES_MALLOC @BACKTRACE_USES_MALLOC@
+
+/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
+   library is configured with threading support, 0 if not.  If this is
+   0, the threaded parameter to backtrace_create_state must be passed
+   as 0.  */
+
+#define BACKTRACE_SUPPORTS_THREADS @BACKTRACE_SUPPORTS_THREADS@
diff --git a/libbacktrace/backtrace.c b/libbacktrace/backtrace.c
new file mode 100644 (file)
index 0000000..cc3105c
--- /dev/null
@@ -0,0 +1,108 @@
+/* backtrace.c -- Entry point for stack backtrace library.
+   Copyright (C) 2012-2014 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_full routine.  */
+
+/* Data passed through _Unwind_Backtrace.  */
+
+struct backtrace_data
+{
+  /* Number of frames to skip.  */
+  int skip;
+  /* Library state.  */
+  struct backtrace_state *state;
+  /* Callback routine.  */
+  backtrace_full_callback callback;
+  /* Error callback routine.  */
+  backtrace_error_callback error_callback;
+  /* Data to pass to callback routines.  */
+  void *data;
+  /* Value to return from backtrace_full.  */
+  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 (bdata->state, 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_full (struct backtrace_state *state, int skip,
+               backtrace_full_callback callback,
+               backtrace_error_callback error_callback, void *data)
+{
+  struct backtrace_data bdata;
+
+  bdata.skip = skip + 1;
+  bdata.state = state;
+  bdata.callback = callback;
+  bdata.error_callback = error_callback;
+  bdata.data = data;
+  bdata.ret = 0;
+  _Unwind_Backtrace (unwind, &bdata);
+  return bdata.ret;
+}
diff --git a/libbacktrace/backtrace.h b/libbacktrace/backtrace.h
new file mode 100644 (file)
index 0000000..e8b8ccb
--- /dev/null
@@ -0,0 +1,213 @@
+/* backtrace.h -- Public header file for stack backtrace library.
+   Copyright (C) 2012-2014 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#ifndef BACKTRACE_H
+#define BACKTRACE_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+/* We want to get a definition for uintptr_t, but we still care about
+   systems that don't have <stdint.h>.  */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+
+#include <stdint.h>
+
+#elif defined(HAVE_STDINT_H)
+
+#include <stdint.h>
+
+#else
+
+/* Systems that don't have <stdint.h> must provide gstdint.h, e.g.,
+   from GCC_HEADER_STDINT in configure.ac.  */
+#include "gstdint.h"
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The backtrace state.  This struct is intentionally not defined in
+   the public interface.  */
+
+struct backtrace_state;
+
+/* The type of the error callback argument to backtrace functions.
+   This function, if not NULL, will be called for certain error cases.
+   The DATA argument is passed to the function that calls this one.
+   The MSG argument is an error message.  The ERRNUM argument, if
+   greater than 0, holds an errno value.  The MSG buffer may become
+   invalid after this function returns.
+
+   As a special case, the ERRNUM argument will be passed as -1 if no
+   debug info can be found for the executable, but the function
+   requires debug info (e.g., backtrace_full, backtrace_pcinfo).  The
+   MSG in this case will be something along the lines of "no debug
+   info".  Similarly, ERRNUM will be passed as -1 if there is no
+   symbol table, but the function requires a symbol table (e.g.,
+   backtrace_syminfo).  This may be used as a signal that some other
+   approach should be tried.  */
+
+typedef void (*backtrace_error_callback) (void *data, const char *msg,
+                                         int errnum);
+
+/* Create state information for the backtrace routines.  This must be
+   called before any of the other routines, and its return value must
+   be passed to all of the other routines.  FILENAME is the path name
+   of the executable file; if it is NULL the library will try
+   system-specific path names.  If not NULL, FILENAME must point to a
+   permanent buffer.  If THREADED is non-zero the state may be
+   accessed by multiple threads simultaneously, and the library will
+   use appropriate atomic operations.  If THREADED is zero the state
+   may only be accessed by one thread at a time.  This returns a state
+   pointer on success, NULL on error.  If an error occurs, this will
+   call the ERROR_CALLBACK routine.  */
+
+extern struct backtrace_state *backtrace_create_state (
+    const char *filename, int threaded,
+    backtrace_error_callback error_callback, void *data);
+
+/* The type of the callback argument to the backtrace_full function.
+   DATA is the argument passed to backtrace_full.  PC is the program
+   counter.  FILENAME is the name of the file containing PC, or NULL
+   if not available.  LINENO is the line number in FILENAME containing
+   PC, or 0 if not available.  FUNCTION is the name of the function
+   containing PC, or NULL if not available.  This should return 0 to
+   continuing tracing.  The FILENAME and FUNCTION buffers may become
+   invalid after this function returns.  */
+
+typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
+                                       const char *filename, int lineno,
+                                       const char *function);
+
+/* Get a full stack backtrace.  SKIP is the number of frames to skip;
+   passing 0 will start the trace with the function calling
+   backtrace_full.  DATA is passed to the callback routine.  If any
+   call to CALLBACK returns a non-zero value, the stack backtrace
+   stops, and backtrace returns that value; this may be used to limit
+   the number of stack frames desired.  If all calls to CALLBACK
+   return 0, backtrace returns 0.  The backtrace_full function will
+   make at least one call to either CALLBACK or ERROR_CALLBACK.  This
+   function requires debug info for the executable.  */
+
+extern int backtrace_full (struct backtrace_state *state, int skip,
+                          backtrace_full_callback callback,
+                          backtrace_error_callback error_callback,
+                          void *data);
+
+/* The type of the callback argument to the backtrace_simple function.
+   DATA is the argument passed to simple_backtrace.  PC is the program
+   counter.  This should return 0 to continue tracing.  */
+
+typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
+
+/* Get a simple backtrace.  SKIP is the number of frames to skip, as
+   in backtrace.  DATA is passed to the callback routine.  If any call
+   to CALLBACK returns a non-zero value, the stack backtrace stops,
+   and backtrace_simple returns that value.  Otherwise
+   backtrace_simple returns 0.  The backtrace_simple function will
+   make at least one call to either CALLBACK or ERROR_CALLBACK.  This
+   function does not require any debug info for the executable.  */
+
+extern int backtrace_simple (struct backtrace_state *state, int skip,
+                            backtrace_simple_callback callback,
+                            backtrace_error_callback error_callback,
+                            void *data);
+
+/* Print the current backtrace in a user readable format to a FILE.
+   SKIP is the number of frames to skip, as in backtrace_full.  Any
+   error messages are printed to stderr.  This function requires debug
+   info for the executable.  */
+
+extern void backtrace_print (struct backtrace_state *state, int skip, FILE *);
+
+/* Given PC, a program counter in the current program, call the
+   callback function with filename, line number, and function name
+   information.  This will normally call the callback function exactly
+   once.  However, if the PC happens to describe an inlined call, and
+   the debugging information contains the necessary information, then
+   this may call the callback function multiple times.  This will make
+   at least one call to either CALLBACK or ERROR_CALLBACK.  This
+   returns the first non-zero value returned by CALLBACK, or 0.  */
+
+extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
+                            backtrace_full_callback callback,
+                            backtrace_error_callback error_callback,
+                            void *data);
+
+/* The type of the callback argument to backtrace_syminfo.  DATA and
+   PC are the arguments passed to backtrace_syminfo.  SYMNAME is the
+   name of the symbol for the corresponding code.  SYMVAL is the
+   value and SYMSIZE is the size of the symbol.  SYMNAME will be NULL
+   if no error occurred but the symbol could not be found.  */
+
+typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
+                                           const char *symname,
+                                           uintptr_t symval,
+                                           uintptr_t symsize);
+
+/* Given ADDR, an address or program counter in the current program,
+   call the callback information with the symbol name and value
+   describing the function or variable in which ADDR may be found.
+   This will call either CALLBACK or ERROR_CALLBACK exactly once.
+   This returns 1 on success, 0 on failure.  This function requires
+   the symbol table but does not require the debug info.  Note that if
+   the symbol table is present but ADDR could not be found in the
+   table, CALLBACK will be called with a NULL SYMNAME argument.
+   Returns 1 on success, 0 on error.  */
+
+extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
+                             backtrace_syminfo_callback callback,
+                             backtrace_error_callback error_callback,
+                             void *data);
+
+/* Add a single file to a backtrace_state with a base_address. */
+
+extern int backtrace_fileline_initialize (struct backtrace_state *state, uintptr_t base_address,
+                                          int is_exe, backtrace_error_callback error_callback, void *data);
+
+/* Get the debug filename (or return NULL if not found). */
+
+extern const char *backtrace_get_debug_filename (struct backtrace_state *state);
+                              
+/* Given a filename, try to find the buildid for it. */
+
+extern int elf_get_uuid (struct backtrace_state *state,
+                         const char *filename, uint8_t uuid[20], int *uuid_len);
+
+#ifdef __cplusplus
+} /* End extern "C".  */
+#endif
+
+#endif
diff --git a/libbacktrace/btrace.cpp b/libbacktrace/btrace.cpp
new file mode 100644 (file)
index 0000000..9d81e60
--- /dev/null
@@ -0,0 +1,623 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+//
+// btrace.cpp
+//
+#include <inttypes.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <execinfo.h>
+#include <link.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <cassert>
+
+#include <mutex>
+#include <vector>
+#include <algorithm>
+
+// We need local unwinding only (should be more optimal than local + remote).
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+#include "btrace.h"
+#include "backtrace.h"
+
+// our demangle routine from libelftc_dem_gnu3.c (from libcxxrt)
+extern "C" char * __cxa_demangle_gnu3(const char *org);
+
+#ifdef HAVE_UNW_BACKTRACE_SKIP
+// fast(er) backtrace routine from libunwind
+extern "C" int unw_backtrace_skip (void **buffer, int size, int skip);
+#endif
+
+// Our dlopen mutex to protect g_module_infos operations
+static std::mutex &get_dlopen_mutex()
+{
+    static std::mutex g_dlopen_mutex;
+    return g_dlopen_mutex;
+}
+
+// An array of all the modules that have been loaded in this process.
+static std::vector<btrace_module_info> &get_module_infos()
+{
+    static std::vector<btrace_module_info> s_module_infos;
+    return s_module_infos;
+}
+
+static void btrace_dlopen_notify_impl();
+
+int
+btrace_get(uintptr_t *addrs, size_t count_addrs, uint32_t addrs_to_skip)
+{
+#ifdef HAVE_UNW_BACKTRACE_SKIP
+    return unw_backtrace_skip((void **)addrs, (int)count_addrs, addrs_to_skip);
+#else
+    size_t count = 0;
+    unw_cursor_t cursor;
+    unw_context_t context;
+
+    unw_getcontext(&context);
+    unw_init_local(&cursor, &context);
+
+    while (count < count_addrs)
+    {
+        unw_word_t addr;
+
+        if (unw_step(&cursor) <= 0)
+            break;
+
+        unw_get_reg(&cursor, UNW_REG_IP, &addr);
+        // Could retrieve registers via something like this:
+        //   unw_get_reg(&cursor, UNW_X86_EAX, &eax), ...
+
+        if (addrs_to_skip)
+        {
+            addrs_to_skip--;
+            continue;
+        }
+
+        addrs[count++] = (uintptr_t)addr;
+
+#if 0
+        // Get function name and offset from libunwind. Should match
+        //  the libbacktrace code down below.
+        unw_word_t offset;
+        char function[512];
+        function[0] = 0;
+        unw_get_proc_name(&cursor, function, sizeof(function), &offset);
+        printf ("0x%" PRIxPTR ": %s [+0x%" PRIxPTR "]\n", addr, function, offset);
+#endif
+    }
+
+    return (int)count;
+#endif
+}
+
+const char *
+btrace_get_calling_module()
+{
+    unw_cursor_t cursor;
+    unw_context_t context;
+    const char *calling_fname = NULL;
+
+    unw_getcontext(&context);
+    unw_init_local(&cursor, &context);
+
+    for(;;)
+    {
+        unw_word_t addr;
+        Dl_info dl_info;
+
+        if (unw_step(&cursor) <= 0)
+            break;
+
+        unw_get_reg(&cursor, UNW_REG_IP, &addr);
+
+        // Get module name.
+        if (dladdr((void *)addr, &dl_info) == 0)
+            return NULL;
+
+        if (dl_info.dli_fname)
+        {
+            if (!calling_fname)
+            {
+                calling_fname = dl_info.dli_fname;
+            }
+            else if(strcmp(calling_fname, dl_info.dli_fname))
+            {
+                return dl_info.dli_fname;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static int
+btrace_module_search (const void *vkey, const void *ventry)
+{
+    const uintptr_t *key = (const uintptr_t *)vkey;
+    const struct btrace_module_info *entry = (const struct btrace_module_info *) ventry;
+    uintptr_t addr;
+
+    addr = *key;
+    if (addr < entry->base_address)
+        return -1;
+    else if (addr >= entry->base_address + entry->address_size)
+        return 1;
+    return 0;
+}
+
+const char *
+btrace_get_current_module()
+{
+    void *paddr = __builtin_return_address(0);
+    uintptr_t addr = (uintptr_t)paddr;
+    std::lock_guard<std::mutex> lock(get_dlopen_mutex());
+
+    // Try to search for the module name in our list. Should be faster than dladdr which
+    //  goes through a bunch of symbol information.
+    std::vector<btrace_module_info>& module_infos = get_module_infos();
+    if (module_infos.size())
+    {
+        btrace_module_info *module_info = (btrace_module_info *)bsearch(&addr,
+            &module_infos[0], module_infos.size(), sizeof(btrace_module_info), btrace_module_search);
+        if (module_info && module_info->filename)
+            return module_info->filename;
+    }
+
+    // Well, that failed for some reason. Try dladdr.
+    Dl_info dl_info;
+    if (dladdr(paddr, &dl_info) && dl_info.dli_fname)
+        return dl_info.dli_fname;
+
+    assert(false);
+    return nullptr;
+}
+
+static void
+btrace_err_callback(void */*data*/, const char *msg, int errnum)
+{
+    if (errnum == -1)
+    {
+        // Missing dwarf information. This happens when folks haven't compiled with -g or they
+        //  stripped the symbols and we couldn't find em.
+    }
+    else
+    {
+        const char *errstr = errnum ? strerror(errnum) : "";
+        printf("libbacktrace error: %s %s\n", msg, errstr);
+        assert(false);
+    }
+}
+
+static void
+btrace_syminfo_callback(void *data, uintptr_t addr, const char *symname, uintptr_t symval, uintptr_t /*symsize*/)
+{
+    if (symname)
+    {
+        btrace_info *info = (btrace_info *)data;
+        info->function = symname;
+        info->offset = addr - symval;
+    }
+}
+
+static int
+btrace_pcinfo_callback(void *data, uintptr_t /*addr*/, const char *file, int line, const char *func)
+{
+    btrace_info *frame = (btrace_info *)data;
+
+    frame->filename = file;
+    frame->linenumber = line;
+
+    // Don't overwrite function string if we got a blank one for some reason.
+    if (func && func[0])
+        frame->function = func;
+    return 0;
+}
+
+static void
+backtrace_initialize_error_callback(void */*data*/, const char */*msg*/, int /*errnum*/)
+{
+    // Backtrace_initialize only fails with alloc error and will be handled below.
+}
+
+static bool
+module_info_init_state(btrace_module_info *module_info)
+{
+    if (!module_info->backtrace_state)
+    {
+        module_info->backtrace_state = backtrace_create_state(
+                    module_info->filename, 0, backtrace_initialize_error_callback, NULL);
+        if (module_info->backtrace_state)
+        {
+            elf_get_uuid(module_info->backtrace_state, module_info->filename,
+                         module_info->uuid, &module_info->uuid_len);
+        }
+    }
+
+    return !!module_info->backtrace_state;
+}
+
+bool
+btrace_resolve_addr(btrace_info *info, uintptr_t addr, uint32_t flags)
+{
+    std::lock_guard<std::mutex> lock(get_dlopen_mutex());
+    std::vector<btrace_module_info>& module_infos = get_module_infos();
+
+    if (!module_infos.size())
+        btrace_dlopen_notify_impl();
+
+    info->addr = addr;
+    info->offset = 0;
+    info->module = NULL;
+    info->function = NULL;
+    info->filename = NULL;
+    info->linenumber = 0;
+    info->demangled_func_buf[0] = 0;
+
+    btrace_module_info *module_info = (btrace_module_info *)bsearch(&addr,
+        &module_infos[0], module_infos.size(), sizeof(btrace_module_info), btrace_module_search);
+    if (module_info)
+    {
+        info->module = module_info->filename;
+
+        if (module_info_init_state(module_info))
+        {
+            backtrace_fileline_initialize(module_info->backtrace_state, module_info->base_address,
+                                          module_info->is_exe, backtrace_initialize_error_callback, NULL);
+
+            // Get function name and offset.
+            backtrace_syminfo(module_info->backtrace_state, addr, btrace_syminfo_callback,
+                              btrace_err_callback, info);
+
+            if (flags & BTRACE_RESOLVE_ADDR_GET_FILENAME)
+            {
+                // Get filename and line number (and maybe function).
+                backtrace_pcinfo(module_info->backtrace_state, addr, btrace_pcinfo_callback,
+                                 btrace_err_callback, info);
+            }
+
+            if ((flags & BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC) && info->function && info->function[0])
+            {
+                info->function = btrace_demangle_function(info->function, info->demangled_func_buf, sizeof(info->demangled_func_buf));
+            }
+        }
+
+        if (!info->offset)
+            info->offset = addr - module_info->base_address;
+    }
+
+    // Get module name.
+    if (!info->module || !info->module[0])
+    {
+        Dl_info dl_info;
+        if (dladdr((void *)addr, &dl_info))
+            info->module = dl_info.dli_fname;
+        if (!info->offset)
+            info->offset = addr - (uintptr_t)dl_info.dli_fbase;
+    }
+
+    if (info->module)
+    {
+        const char *module_name = strrchr(info->module, '/');
+        if (module_name)
+            info->module = module_name + 1;
+    }
+
+    if (!info->module)
+        info->module = "";
+    if (!info->function)
+        info->function = "";
+    if (!info->filename)
+        info->filename = "";
+    return 1;
+}
+
+static int
+get_hex_value(char ch)
+{
+    if (ch >= 'A' && ch <= 'F')
+        return 10 + ch - 'A';
+    else if (ch >= 'a' && ch <= 'f')
+        return 10 + ch - 'a';
+    else if (ch >= '0' && ch <= '9')
+        return ch - '0';
+
+    return -1;
+}
+
+int
+btrace_uuid_str_to_uuid(uint8_t uuid[20], const char *uuid_str)
+{
+    int len;
+
+    for (len = 0; (len < 20) && *uuid_str; len++)
+    {
+        int val0 = get_hex_value(*uuid_str++);
+        int val1 = get_hex_value(*uuid_str++);
+        if (val0 < 0 || val1 < 0)
+            break;
+        uuid[len] = (val0 << 4) | val1;
+    }
+
+    return len;
+}
+
+void
+btrace_uuid_to_str(char uuid_str[41], const uint8_t *uuid, int len)
+{
+    int i;
+    static const char hex[] = "0123456789abcdef";
+
+    if (len > 40)
+        len = 40;
+    for (i = 0; i < len; i++)
+    {
+        uint8_t c = *uuid++;
+
+        *uuid_str++ = hex[c >> 4];
+        *uuid_str++ = hex[c & 0xf];
+    }
+    *uuid_str = 0;
+}
+
+int
+btrace_dump()
+{
+    int i;
+    const int addrs_size = 128;
+    uintptr_t addrs[addrs_size];
+    int count = btrace_get(addrs, addrs_size, 0);
+
+    for (i = 0; i < count; i++)
+    {
+        int ret;
+        btrace_info info;
+
+        ret = btrace_resolve_addr(&info, addrs[i],
+                                BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC);
+
+        printf(" 0x%" PRIxPTR " ", addrs[i]);
+        if (ret)
+        {
+            printf("%s", info.module);
+
+            if (info.function[0])
+            {
+                printf(": %s", info.function);
+            }
+
+            printf("+0x%" PRIxPTR, info.offset);
+
+            if (info.filename && info.filename[0])
+            {
+                // Print last directory plus filename if possible.
+                const char *slash_cur = info.filename;
+                const char *slash_last = NULL;
+                for (const char *ch = info.filename; *ch; ch++)
+                {
+                    if (*ch == '/')
+                    {
+                        slash_last = slash_cur;
+                        slash_cur = ch + 1;
+                    }
+                }
+                const char *filename = slash_last ? slash_last : slash_cur;
+                printf(": %s:%d", filename, info.linenumber);
+            }
+        }
+
+        printf("\n");
+    }
+
+    return count;
+}
+
+static int
+dlopen_notify_callback(struct dl_phdr_info *info, size_t /*size*/, void *data)
+{
+    const int PATH_MAX = 1024;
+    char buf[PATH_MAX];
+    bool is_exe = false;
+    const char *filename = info->dlpi_name;
+    std::vector<btrace_module_info> *new_module_infos = (std::vector<btrace_module_info> *)data;
+    std::vector<btrace_module_info>& module_infos = get_module_infos();
+
+    // If we don't have a filename and we haven't added our main exe yet, do it.
+    if (!filename || !filename[0])
+    {
+        if (!module_infos.size() && !new_module_infos->size())
+        {
+            is_exe = true;
+            ssize_t ret =  readlink("/proc/self/exe", buf, sizeof(buf));
+            if ((ret > 0) && (ret < (ssize_t)sizeof(buf)))
+            {
+                buf[ret] = 0;
+                filename = buf;
+            }
+        }
+        if (!filename || !filename[0])
+            return 0;
+    }
+
+    uintptr_t addr_start = 0;
+    uintptr_t addr_end = 0;
+
+    for (int i = 0; i < info->dlpi_phnum; i++)
+    {
+        if (info->dlpi_phdr[i].p_type == PT_LOAD)
+        {
+            if (addr_end == 0)
+            {
+                addr_start = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+                addr_end = addr_start + info->dlpi_phdr[i].p_memsz;
+            }
+            else
+            {
+                uintptr_t addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
+                if (addr > addr_end)
+                    addr_end = addr;
+            }
+        }
+    }
+
+    btrace_module_info module_info;
+    module_info.base_address = addr_start;
+    module_info.address_size = (uint32_t)(addr_end - addr_start);
+    module_info.filename = filename;
+    module_info.is_exe = is_exe;
+    module_info.backtrace_state = NULL;
+    module_info.uuid_len = 0;
+    memset(module_info.uuid, 0, sizeof(module_info.uuid));
+
+    auto it = std::lower_bound(module_infos.begin(), module_infos.end(), module_info);
+    if (it == module_infos.end() || *it != module_info)
+    {
+        module_info.filename = strdup(filename);
+        if (module_info.filename)
+        {
+            new_module_infos->push_back(module_info);
+        }
+    }
+    return 0;
+}
+
+bool
+btrace_dlopen_add_module(const btrace_module_info &module_info_in)
+{
+    std::lock_guard<std::mutex> lock(get_dlopen_mutex());
+    std::vector<btrace_module_info>& module_infos = get_module_infos();
+
+    auto it = std::lower_bound(module_infos.begin(), module_infos.end(), module_info_in);
+    if (it == module_infos.end() || *it != module_info_in)
+    {
+        btrace_module_info module_info = module_info_in;
+
+        if (module_info_init_state(&module_info))
+        {
+            // Make sure the UUID of the file on disk matches with what we were asked for.
+            if ((module_info_in.uuid_len == module_info.uuid_len) &&
+                    !memcmp(module_info_in.uuid, module_info.uuid, module_info.uuid_len))
+            {
+                module_infos.push_back(module_info);
+                std::sort(module_infos.begin(), module_infos.end());
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+const char *
+btrace_get_debug_filename(const char *filename)
+{
+    std::lock_guard<std::mutex> lock(get_dlopen_mutex());
+    std::vector<btrace_module_info>& module_infos = get_module_infos();
+
+    std::string fname = filename;
+    for (uint i = 0; i < module_infos.size(); i++)
+    {
+        btrace_module_info &module_info = module_infos[i];
+
+        if (fname == module_info.filename)
+        {
+            if (module_info_init_state(&module_info))
+            {
+                backtrace_fileline_initialize(module_info.backtrace_state, module_info.base_address,
+                                              module_info.is_exe, backtrace_initialize_error_callback, NULL);
+
+                return backtrace_get_debug_filename(module_info.backtrace_state);
+            }
+        }
+    }
+    return NULL;
+}
+
+static void btrace_dlopen_notify_impl()
+{
+    std::vector<btrace_module_info> new_module_infos;
+
+    // Iterator through all the currently loaded modules.
+    dl_iterate_phdr(dlopen_notify_callback, &new_module_infos);
+
+    if (new_module_infos.size())
+    {
+        std::vector<btrace_module_info>& module_infos = get_module_infos();
+        module_infos.insert(module_infos.end(), new_module_infos.begin(), new_module_infos.end());
+        std::sort(module_infos.begin(), module_infos.end());
+    }
+}
+
+// This function is called from a dlopen hook, which means it could be
+//  called from the driver or other code which hasn't aligned the stack.
+#ifdef __i386
+void __attribute__((force_align_arg_pointer))
+#else
+void
+#endif
+btrace_dlopen_notify(const char */*filename*/)
+{
+    std::lock_guard<std::mutex> lock(get_dlopen_mutex());
+    btrace_dlopen_notify_impl();
+}
+
+//
+// Other possibilities:
+//   abi::__cxa_demangle() (cxxabi.h)
+//   bfd_demangle (bfd.h)
+//   cplus_demangle (demangle.h) libiberty code from gcc
+//
+#include <cxxabi.h>
+// char * abi::__cxa_demangle(const char* __mangled_name, char* __output_buffer,
+//    size_t* __length, int* __status);
+// int status = 0;
+// char *function = abi::__cxa_demangle(name, NULL, NULL, &status);
+//
+const char *
+btrace_demangle_function(const char *name, char *buffer, size_t buflen)
+{
+    char *function = NULL;
+
+    // Mangled-name is function or variable name...
+    if (name[0] == '_' && name[1] == 'Z') {
+        int status = 0;
+        function = abi::__cxa_demangle(name, nullptr, nullptr, &status);
+    }
+
+    if (function && function[0])
+        snprintf(buffer, buflen, "%s", function);
+    else
+        snprintf(buffer, buflen, "%s", name);
+
+    buffer[buflen - 1] = 0;
+    free(function);
+    return buffer;
+}
diff --git a/libbacktrace/btrace.h b/libbacktrace/btrace.h
new file mode 100644 (file)
index 0000000..146a146
--- /dev/null
@@ -0,0 +1,135 @@
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef BTRACE_H
+#define BTRACE_H
+
+#include <stdint.h>
+#include <string.h>
+
+struct btrace_info
+{
+    uintptr_t addr;
+    uintptr_t offset;
+    const char *module;   // Guaranteed to be !NULL.
+    const char *function; //
+    const char *filename; //
+    int linenumber;
+    char demangled_func_buf[512];
+};
+
+/*
+ * Walk up the stack getting the instruction pointers and stuffing them into your 
+ *  array. We will skip the first <addrs_to_skip> count of addrs, and we will return
+ *  the count of addrs we found. You can pass the addrs to btrace_resolve_addr() to
+ *  get modules, function names, etc.
+ */
+int btrace_get(uintptr_t *addrs, size_t count_addrs, uint32_t addrs_to_skip);
+
+/*
+ * Get symbol information given an instruction pointer.
+ */
+#define BTRACE_RESOLVE_ADDR_GET_FILENAME   0x00000001
+#define BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC  0x00000002
+bool btrace_resolve_addr(btrace_info *info, uintptr_t addr, uint32_t flags);
+
+/*
+ * Get debug filename (or NULL if not found).
+ */
+const char *btrace_get_debug_filename(const char *filename);
+
+/*
+ * Walk up the stack until we find a module that isn't the same as the one we're in.
+ *  Return the full path string to that module. Will return NULL if nothing is found.
+ */
+const char *btrace_get_calling_module();
+const char *btrace_get_current_module();
+
+/*
+ * Called when a new module is dlopen'd.
+ */
+void btrace_dlopen_notify(const char *filename);
+
+int btrace_dump();
+const char *btrace_demangle_function(const char *name, char *buffer, size_t buflen);
+
+struct btrace_module_info
+{
+    //$ TODO mikesart: need an ID number in here. This will get incremented
+    // everytime we have an address conflict. All stack traces should get this
+    // ID so they know which module they should get symbols from.
+    uintptr_t base_address;
+    uint32_t address_size;
+    struct backtrace_state *backtrace_state;
+    const char *filename;
+    int uuid_len;
+    uint8_t uuid[20];
+    int is_exe;
+};
+
+/*
+ * Explicitly add a module to the module list. Used for symbol resolving.
+ */
+bool btrace_dlopen_add_module(const btrace_module_info &module_info);
+
+/*
+ * Uuid helper functions.
+ */
+int btrace_uuid_str_to_uuid(uint8_t uuid[20], const char *uuid_str);
+void btrace_uuid_to_str(char uuid_str[41], const uint8_t *uuid, int len);
+
+inline bool
+operator <(const btrace_module_info &info1, const btrace_module_info &info2)
+{
+    if (info1.base_address < info2.base_address)
+        return true;
+    if(info1.base_address == info2.base_address)
+    {
+        if(info1.address_size < info2.address_size)
+            return true;
+        if (info1.address_size == info2.address_size)
+        {
+            if (strcmp(info1.filename, info2.filename) < 0)
+                return false;
+        }
+    }
+    return false;
+}
+
+inline bool
+operator ==(const btrace_module_info &info1, const btrace_module_info &info2)
+{
+    return (info1.base_address == info2.base_address) &&
+           (info1.address_size == info2.address_size) &&
+            !strcmp(info1.filename, info2.filename);
+}
+
+inline bool
+operator !=(const btrace_module_info &info1, const btrace_module_info &info2)
+{
+    return !(info1 == info2);
+}
+
+#endif // BTRACE_H
diff --git a/libbacktrace/config.h.in.cmake b/libbacktrace/config.h.in.cmake
new file mode 100644 (file)
index 0000000..b78e970
--- /dev/null
@@ -0,0 +1,21 @@
+/* ELF size: 32 or 64 */
+#define BACKTRACE_ELF_SIZE @BACKTRACE_ELF_SIZE@
+
+/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_STRNLEN 1
+
+/* Define if dl_iterate_phdr is available. */
+#cmakedefine HAVE_DL_ITERATE_PHDR 1
+
+/* Define to 1 if you have the fcntl function */
+#cmakedefine HAVE_FCNTL 1
+
+/* Define if getexecname is available. */
+#cmakedefine HAVE_GETEXECNAME 1
+
+/* Define if _Unwind_GetIPInfo is available. */
+#cmakedefine HAVE_GETIPINFO 1
+
+/* Define to 1 if you have the __sync functions */
+#cmakedefine HAVE_SYNC_FUNCTIONS 1
diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c
new file mode 100644 (file)
index 0000000..4aded19
--- /dev/null
@@ -0,0 +1,3033 @@
+/* dwarf.c -- Get file/line information from DWARF for backtraces.
+   Copyright (C) 2012-2014 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    (1) Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer. 
+
+    (2) Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.  
+    
+    (3) The name of the author may not be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.  */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "dwarf2.h"
+#include "filenames.h"
+
+#include "backtrace.h"
+#include "internal.h"
+
+//$$$ mikesart added
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
+#endif
+#endif
+//$$$
+
+#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN
+
+/* If strnlen is not declared, provide our own version.  */
+
+static size_t
+xstrnlen (const char *s, size_t maxlen)
+{
+  size_t i;
+
+  for (i = 0; i < maxlen; ++i)
+    if (s[i] == '\0')
+      break;
+  return i;
+}
+
+#define strnlen xstrnlen
+
+#endif
+
+/* 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;
+  /* Primary source file.  */
+  const char *filename;
+  /* Compilation command working directory.  */
+  const char *comp_dir;
+  /* Absolute file name, only set if needed.  */
+  const char *abs_filename;
+  /* The abbreviations for this unit.  */
+  struct abbrevs abbrevs;
+
+  /* The fields above this point are read in during initialization and
+     may be accessed freely.  The fields below this point are read in
+     as needed, and therefore require care, as different threads may
+     try to initialize them simultaneously.  */
+
+  /* PC to line number mapping.  This is NULL if the values have not
+     been read.  This is (struct line *) -1 if there was an error
+     reading the values.  */
+  struct line *lines;
+  /* Number of entries in lines.  */
+  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
+{
+  /* The data for the next file we know about.  */
+  struct dwarf_data *next;
+  /* The base address for this file.  */
+  uintptr_t base_address;
+  /* 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;
+  int overflow;
+  unsigned char b;
+
+  ret = 0;
+  shift = 0;
+  overflow = 0;
+  do
+    {
+      const unsigned char *p;
+
+      p = buf->buf;
+      if (!advance (buf, 1))
+       return 0;
+      b = *p;
+      if (shift < 64)
+       ret |= ((uint64_t) (b & 0x7f)) << shift;
+      else if (!overflow)
+       {
+         dwarf_buf_error (buf, "LEB128 overflows uint64_t");
+         overflow = 1;
+       }
+      shift += 7;
+    }
+  while ((b & 0x80) != 0);
+
+  return ret;
+}
+
+/* Read a signed LEB128 number.  */
+
+static int64_t
+read_sleb128 (struct dwarf_buf *buf)
+{
+  uint64_t val;
+  unsigned int shift;
+  int overflow;
+  unsigned char b;
+
+  val = 0;
+  shift = 0;
+  overflow = 0;
+  do
+    {
+      const unsigned char *p;
+
+      p = buf->buf;
+      if (!advance (buf, 1))
+       return 0;
+      b = *p;
+      if (shift < 64)
+       val |= ((uint64_t) (b & 0x7f)) << shift;
+      else if (!overflow)
+       {
+         dwarf_buf_error (buf, "signed LEB128 overflows uint64_t");
+         overflow = 1;
+       }
+      shift += 7;
+    }
+  while ((b & 0x80) != 0);
+
+  if ((b & 0x40) != 0 && shift < 64)
+    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 backtrace_state *state, struct abbrevs *abbrevs,
+             backtrace_error_callback error_callback, void *data)
+{
+  size_t i;
+
+  for (i = 0; i < abbrevs->num_abbrevs; ++i)
+    backtrace_free (state, abbrevs->abbrevs[i].attrs,
+                   abbrevs->abbrevs[i].num_attrs * sizeof (struct attr),
+                   error_callback, data);
+  backtrace_free (state, 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)
+{
+  /* Avoid warnings about val.u.FIELD may be used uninitialized if
+     this function is inlined.  The warnings aren't valid but can
+     occur because the different fields are set and used
+     conditionally.  */
+  memset (val, 0, sizeof *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 dw_form;
+
+       dw_form = read_uleb128 (buf);
+       return read_attribute ((enum dwarf_form) dw_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 backtrace_state *state, uintptr_t base_address,
+              struct unit_addrs addrs,
+              backtrace_error_callback error_callback, void *data,
+              struct unit_addrs_vector *vec)
+{
+  struct unit_addrs *p;
+
+  /* Add in the base address of the module here, so that we can look
+     up the PC directly.  */
+  addrs.low += base_address;
+  addrs.high += base_address;
+
+  /* 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 (state, 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 backtrace_state *state,
+                       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 (state, &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 (struct backtrace_state *state, 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 (state,
+                                      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)(uint32_t)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 (state, 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 (state, 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 backtrace_state *state, uintptr_t base_address,
+                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 (state, base_address, a, error_callback, data,
+                             addrs))
+           return 0;
+       }
+    }
+
+  if (ranges_buf.reported_underflow)
+    return 0;
+
+  return 1;
+}
+
+/* Find the address range covered by a compilation unit, reading from
+   UNIT_BUF and adding values to U.  Returns 1 if all data could be
+   read, 0 if there is some error.  */
+
+static int
+find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
+                    struct dwarf_buf *unit_buf, 
+                    const unsigned char *dwarf_str, size_t dwarf_str_size,
+                    const unsigned char *dwarf_ranges,
+                    size_t dwarf_ranges_size,
+                    int is_bigendian, backtrace_error_callback error_callback,
+                    void *data, struct unit *u,
+                    struct unit_addrs_vector *addrs)
+{
+  while (unit_buf->left > 0)
+    {
+      uint64_t code;
+      const struct abbrev *abbrev;
+      uint64_t lowpc;
+      int have_lowpc;
+      uint64_t highpc;
+      int have_highpc;
+      int highpc_is_relative;
+      uint64_t ranges;
+      int have_ranges;
+      size_t i;
+
+      code = read_uleb128 (unit_buf);
+      if (code == 0)
+       return 1;
+
+      abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+      if (abbrev == NULL)
+       return 0;
+
+      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,
+                              dwarf_str, dwarf_str_size, &val))
+           return 0;
+
+         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 (abbrev->tag == DW_TAG_compile_unit
+                 && (val.encoding == ATTR_VAL_UINT
+                     || val.encoding == ATTR_VAL_REF_SECTION))
+               u->lineoff = val.u.uint;
+             break;
+
+           case DW_AT_name:
+             if (abbrev->tag == DW_TAG_compile_unit
+                 && val.encoding == ATTR_VAL_STRING)
+               u->filename = val.u.string;
+             break;
+
+           case DW_AT_comp_dir:
+             if (abbrev->tag == DW_TAG_compile_unit
+                 && val.encoding == ATTR_VAL_STRING)
+               u->comp_dir = val.u.string;
+             break;
+
+           default:
+             break;
+           }
+       }
+
+      if (abbrev->tag == DW_TAG_compile_unit
+         || abbrev->tag == DW_TAG_subprogram)
+       {
+         if (have_ranges)
+           {
+             if (!add_unit_ranges (state, base_address, u, ranges, lowpc,
+                                   is_bigendian, dwarf_ranges,
+                                   dwarf_ranges_size, error_callback,
+                                   data, addrs))
+               return 0;
+           }
+         else if (have_lowpc && have_highpc)
+           {
+             struct unit_addrs a;
+
+             if (highpc_is_relative)
+               highpc += lowpc;
+             a.low = lowpc;
+             a.high = highpc;
+             a.u = u;
+
+             if (!add_unit_addr (state, base_address, a, error_callback, data,
+                                 addrs))
+               return 0;
+           }
+
+         /* If we found the PC range in the DW_TAG_compile_unit, we
+            can stop now.  */
+         if (abbrev->tag == DW_TAG_compile_unit
+             && (have_ranges || (have_lowpc && have_highpc)))
+           return 1;
+       }
+
+      if (abbrev->has_children)
+       {
+         if (!find_address_ranges (state, base_address, unit_buf,
+                                   dwarf_str, dwarf_str_size,
+                                   dwarf_ranges, dwarf_ranges_size,
+                                   is_bigendian, error_callback, data,
+                                   u, addrs))
+           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 (struct backtrace_state *state, uintptr_t base_address,
+                  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;
+      int addrsize;
+      struct unit *u;
+
+      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.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 (state, abbrev_offset, dwarf_abbrev, dwarf_abbrev_size,
+                        is_bigendian, error_callback, data, &abbrevs))
+       goto fail;
+
+      addrsize = read_byte (&unit_buf);
+
+      u = ((struct unit *)
+          backtrace_alloc (state, sizeof *u, error_callback, data));
+      if (u == NULL)
+       goto fail;
+      u->unit_data = unit_buf.buf;
+      u->unit_data_len = unit_buf.left;
+      u->unit_data_offset = unit_buf.buf - unit_data_start;
+      u->version = version;
+      u->is_dwarf64 = is_dwarf64;
+      u->addrsize = addrsize;
+      u->filename = NULL;
+      u->comp_dir = NULL;
+      u->abs_filename = NULL;
+      u->lineoff = 0;
+      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;
+      u->function_addrs = NULL;
+      u->function_addrs_count = 0;
+
+      if (!find_address_ranges (state, base_address, &unit_buf,
+                               dwarf_str, dwarf_str_size,
+                               dwarf_ranges, dwarf_ranges_size,
+                               is_bigendian, error_callback, data,
+                               u, addrs))
+       {
+         free_abbrevs (state, &u->abbrevs, error_callback, data);
+         backtrace_free (state, u, sizeof *u, error_callback, data);
+         goto fail;
+       }
+
+      if (unit_buf.reported_underflow)
+       {
+         free_abbrevs (state, &u->abbrevs, error_callback, data);
+         backtrace_free (state, u, sizeof *u, error_callback, data);
+         goto fail;
+       }
+    }
+  if (info.reported_underflow)
+    goto fail;
+
+  return 1;
+
+ fail:
+  free_abbrevs (state, &abbrevs, error_callback, data);
+  free_unit_addrs_vector (state, 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 (struct backtrace_state *state, struct dwarf_data *ddata,
+         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 (state, sizeof (struct line), error_callback,
+                              data, &vec->vec));
+  if (ln == NULL)
+    return 0;
+
+  /* Add in the base address here, so that we can look up the PC
+     directly.  */
+  ln->pc = pc + ddata->base_address;
+
+  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 backtrace_state *state, struct line_header *hdr,
+                 backtrace_error_callback error_callback, void *data)
+{
+  backtrace_free (state, hdr->dirs, hdr->dirs_count * sizeof (const char *),
+                 error_callback, data);
+  backtrace_free (state, 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 backtrace_state *state, 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 (state,
+                               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 (state,
+                                    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)
+         || (dir_index == 0 && u->comp_dir == NULL))
+       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 (state, 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 backtrace_state *state, struct dwarf_data *ddata,
+                  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 (state, ddata, 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 (state, 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 (state, ddata, 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.  Returns 1
+   on success, 0 on failure.  */
+
+static int
+read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
+               backtrace_error_callback error_callback, void *data,
+               struct unit *u, struct line_header *hdr, struct line **lines,
+               size_t *lines_count)
+{
+  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 (state, u, is_dwarf64, &line_buf, hdr))
+    goto fail;
+
+  if (!read_line_program (state, ddata, 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 (state, 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 (state, &vec.vec, error_callback, data))
+    goto fail;
+
+  ln = (struct line *) vec.vec.base;
+  qsort (ln, vec.count, sizeof (struct line), line_compare);
+
+  *lines = ln;
+  *lines_count = vec.count;
+
+  return 1;
+
+ fail:
+  vec.vec.alc += vec.vec.size;
+  vec.vec.size = 0;
+  backtrace_vector_release (state, &vec.vec, error_callback, data);
+  free_line_header (state, hdr, error_callback, data);
+  *lines = (struct line *) (uintptr_t) -1;
+  *lines_count = 0;
+  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 backtrace_state *state, struct dwarf_data *ddata,
+                   struct function *function, uint64_t lowpc, uint64_t highpc,
+                   backtrace_error_callback error_callback,
+                   void *data, struct function_vector *vec)
+{
+  struct function_addrs *p;
+
+  /* Add in the base address here, so that we can look up the PC
+     directly.  */
+  lowpc += ddata->base_address;
+  highpc += ddata->base_address;
+
+  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 (state, 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 backtrace_state *state, 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 (state, ddata, 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 backtrace_state *state, 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 (state, 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 (state, function, sizeof *function,
+                         error_callback, data);
+         is_function = 0;
+       }
+
+      if (is_function)
+       {
+         if (have_ranges)
+           {
+             if (!add_function_ranges (state, 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 (state, ddata, function, lowpc, highpc,
+                                      error_callback, data, vec))
+               return 0;
+           }
+         else
+           {
+             backtrace_free (state, function, sizeof *function,
+                             error_callback, data);
+             is_function = 0;
+           }
+       }
+
+      if (abbrev->has_children)
+       {
+         if (!is_function)
+           {
+             if (!read_function_entry (state, 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 (state, ddata, u, base, unit_buf, lhdr,
+                                       error_callback, data, &fvec))
+               return 0;
+
+             if (fvec.count > 0)
+               {
+                 struct function_addrs *faddrs;
+
+                 if (!backtrace_vector_release (state, &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.  */
+
+static void
+read_function_info (struct backtrace_state *state, struct dwarf_data *ddata,
+                   const struct line_header *lhdr,
+                   backtrace_error_callback error_callback, void *data,
+                   struct unit *u, struct function_vector *fvec,
+                   struct function_addrs **ret_addrs,
+                   size_t *ret_addrs_count)
+{
+  struct function_vector lvec;
+  struct function_vector *pfvec;
+  struct dwarf_buf unit_buf;
+  struct function_addrs *addrs;
+  size_t addrs_count;
+
+  /* Use FVEC if it is not NULL.  Otherwise use our own vector.  */
+  if (fvec != NULL)
+    pfvec = fvec;
+  else
+    {
+      memset (&lvec, 0, sizeof lvec);
+      pfvec = &lvec;
+    }
+
+  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 (state, ddata, u, 0, &unit_buf, lhdr,
+                               error_callback, data, pfvec))
+       return;
+    }
+
+  if (pfvec->count == 0)
+    return;
+
+  addrs_count = pfvec->count;
+
+  if (fvec == NULL)
+    {
+      if (!backtrace_vector_release (state, &lvec.vec, error_callback, data))
+       return;
+      addrs = (struct function_addrs *) pfvec->vec.base;
+    }
+  else
+    {
+      /* Finish this list of addresses, but leave the remaining space in
+        the vector available for the next function unit.  */
+      addrs = ((struct function_addrs *)
+              backtrace_vector_finish (state, &fvec->vec,
+                                       error_callback, data));
+      if (addrs == NULL)
+       return;
+      fvec->count = 0;
+    }
+
+  qsort (addrs, addrs_count, sizeof (struct function_addrs),
+        function_addrs_compare);
+
+  *ret_addrs = addrs;
+  *ret_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_full_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;
+}
+
+/* Look for a PC in the DWARF mapping for one module.  On success,
+   call CALLBACK and return whatever it returns.  On error, call
+   ERROR_CALLBACK and return 0.  Sets *FOUND to 1 if the PC is found,
+   0 if not.  */
+
+static int
+dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
+                uintptr_t pc, backtrace_full_callback callback,
+                backtrace_error_callback error_callback, void *data,
+                int *found)
+{
+  struct unit_addrs *entry;
+  struct unit *u;
+  int new_data;
+  struct line *lines;
+  struct line *ln;
+  struct function_addrs *function_addrs;
+  struct function *function;
+  const char *filename;
+  int lineno;
+  int ret;
+
+  *found = 1;
+
+  /* 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)
+    {
+      *found = 0;
+      return 0;
+    }
+
+//$ TODO mikesart: this isn't the case with clang. We need to search all ranges...
+
+  /* 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 need the lines, lines_count, function_addrs,
+     function_addrs_count fields of u.  If they are not set, we need
+     to set them.  When running in threaded mode, we need to allow for
+     the possibility that some other thread is setting them
+     simultaneously.  */
+
+  u = entry->u;
+  lines = u->lines;
+
+  /* Skip units with no useful line number information by walking
+     backward.  Useless line number information is marked by setting
+     lines == -1.  */
+  while (entry > ddata->addrs
+        && pc >= (entry - 1)->low
+        && pc < (entry - 1)->high)
+    {
+      if (state->threaded)
+       lines = (struct line *) backtrace_atomic_load_pointer (&u->lines);
+
+      if (lines != (struct line *) (uintptr_t) -1)
+       break;
+
+      --entry;
+
+      u = entry->u;
+      lines = u->lines;
+    }
+
+  if (state->threaded)
+    lines = backtrace_atomic_load_pointer (&u->lines);
+
+  new_data = 0;
+  if (lines == NULL)
+    {
+      size_t function_addrs_count;
+      struct line_header lhdr;
+      size_t count;
+
+      /* We have never read the line information for this unit.  Read
+        it now.  */
+
+      function_addrs = NULL;
+      function_addrs_count = 0;
+      if (read_line_info (state, ddata, error_callback, data, entry->u, &lhdr,
+                         &lines, &count))
+       {
+         struct function_vector *pfvec;
+
+         /* If not threaded, reuse DDATA->FVEC for better memory
+            consumption.  */
+         if (state->threaded)
+           pfvec = NULL;
+         else
+           pfvec = &ddata->fvec;
+         read_function_info (state, ddata, &lhdr, error_callback, data,
+                             entry->u, pfvec, &function_addrs,
+                             &function_addrs_count);
+         free_line_header (state, &lhdr, error_callback, data);
+         new_data = 1;
+       }
+
+      /* Atomically store the information we just read into the unit.
+        If another thread is simultaneously writing, it presumably
+        read the same information, and we don't care which one we
+        wind up with; we just leak the other one.  We do have to
+        write the lines field last, so that the acquire-loads above
+        ensure that the other fields are set.  */
+
+      if (!state->threaded)
+       {
+         u->lines_count = count;
+         u->function_addrs = function_addrs;
+         u->function_addrs_count = function_addrs_count;
+         u->lines = lines;
+       }
+      else
+       {
+         backtrace_atomic_store_size_t (&u->lines_count, count);
+         backtrace_atomic_store_pointer (&u->function_addrs, function_addrs);
+         backtrace_atomic_store_size_t (&u->function_addrs_count,
+                                        function_addrs_count);
+         backtrace_atomic_store_pointer (&u->lines, lines);
+       }
+    }
+
+  /* Now all fields of U have been initialized.  */
+
+  if (lines == (struct line *) (uintptr_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)
+       return dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+                               data, found);
+      return callback (data, pc, NULL, 0, NULL);
+    }
+
+  /* Search for PC within this unit.  */
+
+  ln = (struct line *) bsearch (&pc, lines, entry->u->lines_count,
+                               sizeof (struct line), line_search);
+  if (ln == NULL)
+    {
+      /* The PC is between the low_pc and high_pc attributes of the
+        compilation unit, but no entry in the line table covers it.
+        This implies that the start of the compilation unit has no
+        line number information.  */
+
+      if (entry->u->abs_filename == NULL)
+       {
+         const char *entry_filename;
+
+         entry_filename = entry->u->filename;
+         if (entry_filename != NULL
+             && !IS_ABSOLUTE_PATH (entry_filename)
+             && entry->u->comp_dir != NULL)
+           {
+             size_t filename_len;
+             const char *dir;
+             size_t dir_len;
+             char *s;
+
+             filename_len = strlen (entry_filename);
+             dir = entry->u->comp_dir;
+             dir_len = strlen (dir);
+             s = (char *) backtrace_alloc (state, dir_len + filename_len + 2,
+                                           error_callback, data);
+             if (s == NULL)
+               {
+                 *found = 0;
+                 return 0;
+               }
+             memcpy (s, dir, dir_len);
+             /* FIXME: Should use backslash if DOS file system.  */
+             s[dir_len] = '/';
+             memcpy (s + dir_len + 1, entry_filename, filename_len + 1);
+             entry_filename = s;
+           }
+         entry->u->abs_filename = entry_filename;
+       }
+
+      return callback (data, pc, entry->u->abs_filename, 0, NULL);
+    }
+
+  /* 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);
+}
+
+
+/* Return the file/line information for a PC using the DWARF mapping
+   we built earlier.  */
+
+static int
+dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
+               backtrace_full_callback callback,
+               backtrace_error_callback error_callback, void *data)
+{
+  struct dwarf_data *ddata;
+  int found;
+  int ret;
+
+  if (!state->threaded)
+    {
+      for (ddata = (struct dwarf_data *) state->fileline_data;
+          ddata != NULL;
+          ddata = ddata->next)
+       {
+         ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+                                data, &found);
+         if (ret != 0 || found)
+           return ret;
+       }
+    }
+  else
+    {
+      struct dwarf_data **pp;
+
+      pp = (struct dwarf_data **) (void *) &state->fileline_data;
+      while (1)
+       {
+         ddata = backtrace_atomic_load_pointer (pp);
+         if (ddata == NULL)
+           break;
+
+         ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+                                data, &found);
+         if (ret != 0 || found)
+           return ret;
+
+         pp = &ddata->next;
+       }
+    }
+
+  /* FIXME: See if any libraries have been dlopen'ed.  */
+
+  return callback (data, pc, NULL, 0, NULL);
+}
+
+/* Initialize our data structures from the DWARF debug info for a
+   file.  Return NULL on failure.  */
+
+static struct dwarf_data *
+build_dwarf_data (struct backtrace_state *state,
+                 uintptr_t base_address,
+                 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)
+{
+  struct unit_addrs_vector addrs_vec;
+  struct unit_addrs *addrs;
+  size_t addrs_count;
+  struct dwarf_data *fdata;
+
+  if (!build_address_map (state, base_address, 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 NULL;
+
+  if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data))
+    return NULL;
+  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 (state, sizeof (struct dwarf_data),
+                           error_callback, data));
+  if (fdata == NULL)
+    return NULL;
+
+  fdata->next = NULL;
+  fdata->base_address = base_address;
+  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);
+
+  return fdata;
+}
+
+/* Build our data structures from the DWARF sections for a module.
+   Set FILELINE_FN and STATE->FILELINE_DATA.  Return 1 on success, 0
+   on failure.  */
+
+int
+backtrace_dwarf_add (struct backtrace_state *state,
+                    uintptr_t base_address,
+                    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)
+{
+  struct dwarf_data *fdata;
+
+  fdata = build_dwarf_data (state, base_address, dwarf_info, dwarf_info_size,
+                           dwarf_line, dwarf_line_size, dwarf_abbrev,
+                           dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size,
+                           dwarf_str, dwarf_str_size, is_bigendian,
+                           error_callback, data);
+  if (fdata == NULL)
+    return 0;
+
+  if (!state->threaded)
+    {
+      struct dwarf_data **pp;
+
+      for (pp = (struct dwarf_data **) (void *) &state->fileline_data;
+          *pp != NULL;
+          pp = &(*pp)->next)
+       ;
+      *pp = fdata;
+    }
+  else
+    {
+      while (1)
+       {
+         struct dwarf_data **pp;
+
+         pp = (struct dwarf_data **) (void *) &state->fileline_data;
+
+         while (1)
+           {
+             struct dwarf_data *p;
+
+             p = backtrace_atomic_load_pointer (pp);
+
+             if (p == NULL)
+               break;
+
+             pp = &p->next;
+           }
+
+         if (__sync_bool_compare_and_swap (pp, NULL, fdata))
+           break;
+       }
+    }
+
+  *fileline_fn = dwarf_fileline;
+
+  return 1;
+}
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
new file mode 100644 (file)
index 0000000..aec3494
--- /dev/null
@@ -0,0 +1,1376 @@
+/* elf.c -- Get debug data from an ELF file for backtraces.
+   Copyright (C) 2012-2014 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 <unistd.h>
+#include <limits.h>
+#include <pwd.h>
+
+#ifdef HAVE_DL_ITERATE_PHDR
+#include <link.h>
+#endif
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef HAVE_DL_ITERATE_PHDR
+
+/* Dummy version of dl_iterate_phdr for systems that don't have it.  */
+
+#define dl_phdr_info x_dl_phdr_info
+#define dl_iterate_phdr x_dl_iterate_phdr
+
+struct dl_phdr_info
+{
+  uintptr_t dlpi_addr;
+  const char *dlpi_name;
+};
+
+static int
+dl_iterate_phdr (int (*callback) (struct dl_phdr_info *,
+                                 size_t, void *) ATTRIBUTE_UNUSED,
+                void *data ATTRIBUTE_UNUSED)
+{
+  return 0;
+}
+
+#endif /* ! defined (HAVE_DL_ITERATE_PHDR) */
+
+/* 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
+
+/* <link.h> might #include <elf.h> which might define our constants
+   with slightly different values.  Undefine them to be safe.  */
+
+#undef EI_NIDENT
+#undef EI_MAG0
+#undef EI_MAG1
+#undef EI_MAG2
+#undef EI_MAG3
+#undef EI_CLASS
+#undef EI_DATA
+#undef EI_VERSION
+#undef ELF_MAG0
+#undef ELF_MAG1
+#undef ELF_MAG2
+#undef ELF_MAG3
+#undef ELFCLASS32
+#undef ELFCLASS64
+#undef ELFDATA2LSB
+#undef ELFDATA2MSB
+#undef EV_CURRENT
+#undef ET_DYN
+#undef SHN_LORESERVE
+#undef SHN_XINDEX
+#undef SHN_UNDEF
+#undef SHT_SYMTAB
+#undef SHT_STRTAB
+#undef SHT_DYNSYM
+#undef STT_OBJECT
+#undef STT_FUNC
+
+/* Basic types.  */
+
+typedef uint16_t b_elf_half;    /* Elf_Half.  */
+typedef uint32_t b_elf_word;    /* Elf_Word.  */
+typedef int32_t  b_elf_sword;   /* Elf_Sword.  */
+
+#if BACKTRACE_ELF_SIZE == 32
+
+typedef uint32_t b_elf_addr;    /* Elf_Addr.  */
+typedef uint32_t b_elf_off;     /* Elf_Off.  */
+
+typedef uint32_t b_elf_wxword;  /* 32-bit Elf_Word, 64-bit ELF_Xword.  */
+
+#else
+
+typedef uint64_t b_elf_addr;    /* Elf_Addr.  */
+typedef uint64_t b_elf_off;     /* Elf_Off.  */
+typedef uint64_t b_elf_xword;   /* Elf_Xword.  */
+
+typedef uint64_t b_elf_wxword;  /* 32-bit Elf_Word, 64-bit ELF_Xword.  */
+
+#endif
+
+/* Data structures and associated constants.  */
+
+#define EI_NIDENT 16
+
+typedef struct {
+  unsigned char        e_ident[EI_NIDENT];     /* ELF "magic number" */
+  b_elf_half   e_type;                 /* Identifies object file type */
+  b_elf_half   e_machine;              /* Specifies required architecture */
+  b_elf_word   e_version;              /* Identifies object file version */
+  b_elf_addr   e_entry;                /* Entry point virtual address */
+  b_elf_off    e_phoff;                /* Program header table file offset */
+  b_elf_off    e_shoff;                /* Section header table file offset */
+  b_elf_word   e_flags;                /* Processor-specific flags */
+  b_elf_half   e_ehsize;               /* ELF header size in bytes */
+  b_elf_half   e_phentsize;            /* Program header table entry size */
+  b_elf_half   e_phnum;                /* Program header table entry count */
+  b_elf_half   e_shentsize;            /* Section header table entry size */
+  b_elf_half   e_shnum;                /* Section header table entry count */
+  b_elf_half   e_shstrndx;             /* Section header string table index */
+} b_elf_ehdr;  /* 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
+
+#define ET_DYN 3
+
+typedef struct {
+  b_elf_word   sh_name;                /* Section name, index in string tbl */
+  b_elf_word   sh_type;                /* Type of section */
+  b_elf_wxword sh_flags;               /* Miscellaneous section attributes */
+  b_elf_addr   sh_addr;                /* Section virtual addr at execution */
+  b_elf_off    sh_offset;              /* Section file offset */
+  b_elf_wxword sh_size;                /* Size of section in bytes */
+  b_elf_word   sh_link;                /* Index of another section */
+  b_elf_word   sh_info;                /* Additional section information */
+  b_elf_wxword sh_addralign;           /* Section alignment */
+  b_elf_wxword sh_entsize;             /* Entry size if section holds table */
+} b_elf_shdr;  /* Elf_Shdr.  */
+
+#define SHN_UNDEF      0x0000          /* Undefined section */
+#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
+{
+  b_elf_word   st_name;                /* Symbol name, index in string tbl */
+  b_elf_addr   st_value;               /* Symbol value */
+  b_elf_word   st_size;                /* Symbol size */
+  unsigned char        st_info;                /* Symbol binding and type */
+  unsigned char        st_other;               /* Visibility and other data */
+  b_elf_half   st_shndx;               /* Symbol section index */
+} b_elf_sym;  /* Elf_Sym.  */
+
+#else /* BACKTRACE_ELF_SIZE != 32 */
+
+typedef struct
+{
+  b_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 */
+  b_elf_half   st_shndx;               /* Symbol section index */
+  b_elf_addr   st_value;               /* Symbol value */
+  b_elf_xword  st_size;                /* Symbol size */
+} b_elf_sym;  /* Elf_Sym.  */
+
+#endif /* BACKTRACE_ELF_SIZE != 32 */
+
+#define STT_OBJECT 1
+#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,
+  GNU_DEBUGLINK,
+  DEBUG_MAX
+};
+
+/* Names of sections, indexed by enum elf_section.  */
+
+static const char * const debug_section_names[DEBUG_MAX] =
+{
+  ".debug_info",
+  ".debug_line",
+  ".debug_abbrev",
+  ".debug_ranges",
+  ".debug_str",
+  ".gnu_debuglink"
+};
+
+/* 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
+{
+  /* Symbols for the next module.  */
+  struct elf_syminfo_data *next;
+  /* The ELF symbols, sorted by address.  */
+  struct elf_symbol *symbols;
+  /* The number of symbols.  */
+  size_t count;
+  /* The base address for this module. */
+  uintptr_t base_address;
+  /* Address symbols size. */
+  uintptr_t symbol_size;
+};
+
+/* A dummy callback function used when we can't find any debug info.  */
+
+static int
+elf_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
+            uintptr_t pc ATTRIBUTE_UNUSED,
+            backtrace_full_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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+           uintptr_t addr 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;
+}
+
+/* Compare an ADDR 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 addr;
+
+  addr = *key;
+  if (addr < entry->address)
+    return -1;
+  else if (addr >= entry->address + entry->size)
+    return 1;
+  else
+    return 0;
+}
+
+/* Initialize the symbol table info for elf_syminfo.  */
+
+static int
+elf_initialize_syminfo (struct backtrace_state *state,
+                       uintptr_t base_address,
+                       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 b_elf_sym *sym;
+  size_t elf_symbol_count;
+  size_t elf_symbol_size;
+  struct elf_symbol *elf_symbols;
+  size_t symbol_size = 0;
+  size_t i;
+  unsigned int j;
+
+  sym_count = symtab_size / sizeof (b_elf_sym);
+
+  /* We only care about function symbols.  Count them.  */
+  sym = (const b_elf_sym *) symtab_data;
+  elf_symbol_count = 0;
+  for (i = 0; i < sym_count; ++i, ++sym)
+    {
+      int info;
+
+      info = sym->st_info & 0xf;
+      if ((info == STT_FUNC || info == STT_OBJECT)
+         && sym->st_shndx != SHN_UNDEF)
+       ++elf_symbol_count;
+    }
+
+  elf_symbol_size = elf_symbol_count * sizeof (struct elf_symbol);
+  elf_symbols = ((struct elf_symbol *)
+                backtrace_alloc (state, elf_symbol_size, error_callback,
+                                 data));
+  if (elf_symbols == NULL)
+    return 0;
+
+  sym = (const b_elf_sym *) symtab_data;
+  j = 0;
+  for (i = 0; i < sym_count; ++i, ++sym)
+    {
+      int info;
+
+      info = sym->st_info & 0xf;
+      if (info != STT_FUNC && info != STT_OBJECT)
+       continue;
+      if (sym->st_shndx == SHN_UNDEF)
+       continue;
+      if (sym->st_name >= strtab_size)
+       {
+         error_callback (data, "symbol string index out of range", 0);
+         backtrace_free (state, 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 + base_address;
+      elf_symbols[j].size = sym->st_size;
+
+      if (symbol_size < sym->st_value + sym->st_size)
+        symbol_size = sym->st_value + sym->st_size;
+      ++j;
+    }
+
+  qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol),
+        elf_symbol_compare);
+
+  sdata->next = NULL;
+  sdata->symbols = elf_symbols;
+  sdata->count = elf_symbol_count;
+  sdata->base_address = base_address;
+  sdata->symbol_size = symbol_size;
+
+  return 1;
+}
+
+/* Add EDATA to the list in STATE.  */
+
+static void
+elf_add_syminfo_data (struct backtrace_state *state,
+                     struct elf_syminfo_data *edata)
+{
+  if (!state->threaded)
+    {
+      struct elf_syminfo_data **pp;
+
+      for (pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+          *pp != NULL;
+          pp = &(*pp)->next)
+       ;
+      *pp = edata;
+    }
+  else
+    {
+      while (1)
+       {
+         struct elf_syminfo_data **pp;
+
+         pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+
+         while (1)
+           {
+             struct elf_syminfo_data *p;
+
+             p = backtrace_atomic_load_pointer (pp);
+
+             if (p == NULL)
+               break;
+
+             pp = &p->next;
+           }
+
+         if (__sync_bool_compare_and_swap (pp, NULL, edata))
+           break;
+       }
+    }
+}
+
+/* Return the symbol name and value for an ADDR.  */
+
+static void
+elf_syminfo (struct backtrace_state *state, uintptr_t addr,
+            backtrace_syminfo_callback callback,
+            backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+            void *data)
+{
+  struct elf_syminfo_data *edata;
+  struct elf_symbol *sym = NULL;
+
+  if (!state->threaded)
+    {
+      for (edata = (struct elf_syminfo_data *) state->syminfo_data;
+          edata != NULL;
+          edata = edata->next)
+       {
+          if ((addr >= edata->base_address) && (addr < edata->base_address + edata->symbol_size))
+            {
+             sym = ((struct elf_symbol *)
+                    bsearch (&addr, edata->symbols, edata->count,
+                             sizeof (struct elf_symbol), elf_symbol_search));
+             if (sym != NULL)
+               break;
+            }
+       }
+    }
+  else
+    {
+      struct elf_syminfo_data **pp;
+
+      pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+      while (1)
+       {
+         edata = backtrace_atomic_load_pointer (pp);
+         if (edata == NULL)
+           break;
+
+          if ((addr >= edata->base_address) && (addr < edata->base_address + edata->symbol_size))
+            {
+             sym = ((struct elf_symbol *)
+                    bsearch (&addr, edata->symbols, edata->count,
+                             sizeof (struct elf_symbol), elf_symbol_search));
+             if (sym != NULL)
+               break;
+            }
+
+         pp = &edata->next;
+       }
+    }
+
+  if (sym == NULL)
+    callback (data, addr, NULL, 0, 0);
+  else
+    callback (data, addr, sym->name, sym->address, sym->size);
+}
+
+/* Search this section for the .gnu_debuglink build id uuid. */
+
+static uint32_t 
+elf_parse_gnu_buildid(const uint8_t *data, size_t len, uint8_t uuid[20])
+{
+    typedef struct
+    {
+        uint32_t name_len;  // Length of note name
+        uint32_t desc_len;  // Length of note descriptor
+        uint32_t type;      // Type of note (1 is ABI_TAG, 3 is BUILD_ID)
+        char name[4];       // "GNU\0"
+        uint8_t uuid[16];   // This can be 16 or 20 bytes
+    } elf_notehdr;
+
+    /* Try to parse the note section (ie .note.gnu.build-id|.notes|.note|...) and get the build id.
+       BuildID documentation: https://fedoraproject.org/wiki/Releases/FeatureBuildId */
+    static const uint32_t s_gnu_build_id = 3; // NT_GNU_BUILD_ID from elf.h
+
+    while (len >= sizeof(elf_notehdr))
+    {
+        const elf_notehdr *notehdr = (const elf_notehdr *)data;
+        uint32_t name_len = (notehdr->name_len + 3) & ~3;
+        uint32_t desc_len = (notehdr->desc_len + 3) & ~3;
+        size_t offset_next_note = name_len + desc_len;
+
+        /* 16 bytes is UUID|MD5, 20 bytes is SHA1 */
+        if ((notehdr->type == s_gnu_build_id) &&
+            (desc_len == 16 || desc_len == 20) &&
+            (name_len == 4) && !strcmp(notehdr->name, "GNU"))
+        {
+            const uint8_t *uuidbuf = data + offsetof(elf_notehdr, uuid);
+
+            memcpy(uuid, uuidbuf, desc_len);
+            return desc_len;
+        }
+
+        if (offset_next_note >= len)
+            break;
+        data += offset_next_note;
+        len -= offset_next_note;
+    }
+
+    return 0;
+}
+
+static void
+uuid_to_str(char *uuid_str, const uint8_t *uuid, int len) 
+{
+    int i;
+    static const char hex[] = "0123456789abcdef";
+
+    for (i = 0; i < len; i++)
+      {
+        uint8_t c = *uuid++;
+
+        *uuid_str++ = hex[c >> 4];
+        *uuid_str++ = hex[c & 0xf];
+      }    
+    *uuid_str = 0;
+}
+
+static int
+elf_add (struct backtrace_state *state, const char *filename, uintptr_t base_address,
+        backtrace_error_callback error_callback, void *data,
+        fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe,
+         const uint8_t *uuid_to_match, uint32_t uuid_to_match_len);
+
+/* Search and add the backtrace for a gnu_debuglink file. */
+
+static int
+elf_add_gnu_debuglink (struct backtrace_state *state, const char *modulename, const char *gnu_debuglink,
+                       uintptr_t base_address, backtrace_error_callback error_callback, void *data,
+                              fileline *fileline_fn, int *found_sym, int *found_dwarf,
+                       const uint8_t *uuid_to_match, uint32_t uuid_to_match_len)
+{
+  int pass;
+  char file[PATH_MAX];
+  char uuid_str[20 * 2 + 1];
+  const char *homedir = NULL;
+  const char *modulefile = strrchr(modulename, '/');
+  int moduledirlen = modulefile ? (modulefile - modulename) : 0;
+
+  uuid_to_str(uuid_str, uuid_to_match, uuid_to_match_len); 
+
+  for (pass = 0;; ++pass)
+    {
+      file[0] = 0;
+
+      switch (pass)
+      {
+      case 0:
+        /* Try the gnu_debuglink filename we were passed in. */
+        strncpy(file, gnu_debuglink, sizeof(file));
+        break;
+
+      case 1:
+        if (moduledirlen > 0)
+        {
+          /* Try current module directory. */
+          snprintf(file, sizeof(file), "%.*s/%s", moduledirlen, modulename, gnu_debuglink);
+        }
+        break;
+
+      case 2:
+        {
+          /* current exe directory. */
+          char buf[PATH_MAX];
+          int len = readlink("/proc/self/exe", buf, sizeof(buf));
+          if (len > 0)
+            {
+              snprintf(file, sizeof(file), "%.*s/%s", len, buf, gnu_debuglink);
+            }
+        }
+        break;
+
+      case 3:
+        if (moduledirlen > 0)
+        {
+          /* Try /lib/x86_64-linux-gnu/libc-2.15.so --> /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.15.so */
+          snprintf(file, sizeof(file), "/usr/lib/debug%.*s/%s", moduledirlen, modulename, gnu_debuglink);
+        }
+        break;
+
+      case 4:
+        {
+          struct passwd *pw = getpwuid(getuid());
+          homedir = pw ? pw->pw_dir : NULL;
+          if (homedir)
+            {
+              /* try ~/.debug/.build-id/af/1da84a6c83719a4c92cc6e7144623c5a1d1f6d */
+              snprintf(file, sizeof(file), "%s/.debug/.build-id/%.2s/%s", homedir, uuid_str, uuid_str + 2);
+            }
+        }
+        break;
+
+      case 5:
+        if (homedir)
+        {
+            /* try /home/mikesart/.debug/.build-id/af/1da84a6c83719a4c92cc6e7144623c5a1d1f6d.debug */
+            snprintf(file, sizeof(file), "%s/.debug/.build-id/%.2s/%s.debug", homedir, uuid_str, uuid_str + 2);
+        }
+        break;
+
+      case 6:
+          snprintf(file, sizeof(file), "%s/.build-id/%.2s/%s.debug", "/mnt/symstoresymbols/Debug", uuid_str, uuid_str + 2);
+          //$ TODO: mikesart
+          // Check debug-file-directory from .gdbinit maybe?
+          //   /mnt/symstoresymbols/Debug/.build-id/f5/a99d37b2b105c50bccc3fd87e4cd27492f3032.debug
+          //   set debug-file-directory /usr/lib/debug:/mnt/symstoresymbols/debug
+          break;
+
+      default:
+        return 0;
+      }
+
+      file[sizeof(file) - 1] = 0;
+
+      /* If we've got a filename and the file exists, try loading it. */
+
+      if (file[0] && (access(file, F_OK) == 0))
+        {
+          if (elf_add(state, file, base_address,
+                      error_callback, data,
+                      fileline_fn, found_sym, found_dwarf, 0,
+                      uuid_to_match, uuid_to_match_len))
+          {
+            /* Success. Woot! Return our fresh new symbols. */
+            return 1;
+          }
+        }
+    }
+
+  return 0;
+}
+
+
+/* Add the backtrace data for one ELF file.  */
+
+static int
+elf_add (struct backtrace_state *state, const char *filename, uintptr_t base_address,
+        backtrace_error_callback error_callback, void *data,
+        fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe,
+         const uint8_t *uuid_to_match, uint32_t uuid_to_match_len)
+{
+  struct backtrace_view ehdr_view;
+  b_elf_ehdr ehdr;
+  off_t shoff;
+  unsigned int shnum;
+  unsigned int shstrndx;
+  struct backtrace_view shdrs_view;
+  int shdrs_view_valid;
+  const b_elf_shdr *shdrs;
+  const b_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;
+  int retval = 0;
+  uint32_t uuid_len = 0;
+  uint8_t uuid[20];
+
+  *found_sym = 0;
+  *found_dwarf = 0;
+
+  shdrs_view_valid = 0;
+  names_view_valid = 0;
+  symtab_view_valid = 0;
+  strtab_view_valid = 0;
+  debug_view_valid = 0;
+
+  int descriptor = backtrace_open (filename, error_callback, data, NULL);
+  if (descriptor < 0)
+    return 0;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
+                          data, &ehdr_view))
+    goto fail;
+
+  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
+
+  backtrace_release_view (state, &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;
+    }
+
+  /* If the executable is ET_DYN, it is either a PIE, or we are running
+     directly a shared library with .interp.  We need to wait for
+     dl_iterate_phdr in that case to determine the actual base_address.  */
+  if (exe)
+  {
+    /* If we were given a base address and this isn't PIE, set addr to 0. */
+    if (base_address && (ehdr.e_type != ET_DYN))
+      base_address = 0;
+    else if (!base_address && (ehdr.e_type == ET_DYN))
+      goto succeeded;
+  }
+
+  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 b_elf_shdr *shdr;
+
+      if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr,
+                              error_callback, data, &shdr_view))
+       goto fail;
+
+      shdr = (const b_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 (state, &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 (state, descriptor, shoff + sizeof (b_elf_shdr),
+                          (shnum - 1) * sizeof (b_elf_shdr),
+                          error_callback, data, &shdrs_view))
+    goto fail;
+  shdrs_view_valid = 1;
+  shdrs = (const b_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 (state, 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);
+  memset (uuid, 0, sizeof(uuid));
+
+  /* Look for the symbol table.  */
+  for (i = 1; i < shnum; ++i)
+    {
+      const b_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;
+      else if ((shdr->sh_type == SHT_NOTE) && (uuid_len == 0))
+      {
+        struct backtrace_view note_view;
+
+        if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+                                 shdr->sh_size, error_callback, data,
+                                 &note_view))
+        {
+          uuid_len = elf_parse_gnu_buildid(note_view.data, shdr->sh_size, uuid);
+          backtrace_release_view (state, &note_view, error_callback, data);
+        }
+      }
+
+      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 (uuid_to_match_len)
+    {
+      /* We're supposed to be looking for a gnu_debuglink file... */
+
+      /* Found debug file but uuids don't match... */
+      if ((uuid_to_match_len != uuid_len) || memcmp(uuid_to_match, uuid, uuid_len))
+        goto fail;
+
+      /* Found debug file, but no .debuginfo in it? */
+      if (!sections[DEBUG_INFO].size)
+        goto fail;
+    }
+  else if (uuid_len && !sections[DEBUG_INFO].size && sections[GNU_DEBUGLINK].size)
+    {
+      /* We're not searching for a gnu_debuglink file, we have no .debug_info section, we
+         have a uuid, and we have a gnu_debuglink filename - try looking for this debug file. */
+      struct backtrace_view gnu_debuglink_view;
+
+      if (backtrace_get_view (state, descriptor, sections[GNU_DEBUGLINK].offset,
+                             sections[GNU_DEBUGLINK].size, error_callback, data,
+                             &gnu_debuglink_view))
+        {
+          int added = 0;
+          int debug_found_sym = 0;
+          int debug_found_dwarf = 0;
+          const char *gnu_debuglink_file = gnu_debuglink_view.data;
+
+          if (gnu_debuglink_file && gnu_debuglink_file[0])
+            {
+              added = elf_add_gnu_debuglink (state, filename, gnu_debuglink_file, base_address, error_callback,
+                                             data, fileline_fn, &debug_found_sym, &debug_found_dwarf,
+                                             uuid, uuid_len);
+            }
+
+          backtrace_release_view (state, &gnu_debuglink_view, error_callback, data);
+          gnu_debuglink_file = NULL;
+
+          /* If we found symbols of any kind, head on out... */
+          if (added && (debug_found_sym || debug_found_dwarf))
+            {
+              *found_sym = debug_found_sym;
+              *found_dwarf = debug_found_dwarf;
+              goto succeeded;
+            }
+        }
+    }
+
+  if (symtab_shndx == 0)
+    symtab_shndx = dynsym_shndx;
+  if (symtab_shndx != 0)
+    {
+      const b_elf_shdr *symtab_shdr;
+      unsigned int strtab_shndx;
+      const b_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 (state, descriptor, symtab_shdr->sh_offset,
+                              symtab_shdr->sh_size, error_callback, data,
+                              &symtab_view))
+       goto fail;
+      symtab_view_valid = 1;
+
+      if (!backtrace_get_view (state, 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 (state, sizeof *sdata, error_callback, data));
+      if (sdata == NULL)
+       goto fail;
+
+      if (!elf_initialize_syminfo (state, base_address,
+                                  symtab_view.data, symtab_shdr->sh_size,
+                                  strtab_view.data, strtab_shdr->sh_size,
+                                  error_callback, data, sdata))
+       {
+         backtrace_free (state, 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 (state, &symtab_view, error_callback, data);
+
+      *found_sym = 1;
+
+      elf_add_syminfo_data (state, sdata);
+    }
+
+  /* FIXME: Need to handle compressed debug sections.  */
+
+  backtrace_release_view (state, &shdrs_view, error_callback, data);
+  shdrs_view_valid = 0;
+  backtrace_release_view (state, &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 (sections[i].size == 0)
+       continue;
+      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))
+      {
+        descriptor = -1;
+       goto fail;
+      }
+      *fileline_fn = elf_nodebug;
+      return 1;
+    }
+
+  if (!backtrace_get_view (state, 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))
+  {
+    descriptor = -1;
+    goto fail;
+  }
+  descriptor = -1;
+
+  for (i = 0; i < (int) DEBUG_MAX; ++i)
+    {
+      if (sections[i].size == 0)
+       sections[i].data = NULL;
+      else
+       sections[i].data = ((const unsigned char *) debug_view.data
+                           + (sections[i].offset - min_offset));
+    }
+
+  if (!backtrace_dwarf_add (state, base_address,
+                           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))
+    goto fail;
+
+  state->debug_filename = backtrace_strdup(state, filename, error_callback, data);
+  *found_dwarf = 1;
+  return 1;
+
+succeeded:
+  retval = 1;
+
+fail:
+  if (shdrs_view_valid)
+    backtrace_release_view (state, &shdrs_view, error_callback, data);
+  if (names_view_valid)
+    backtrace_release_view (state, &names_view, error_callback, data);
+  if (symtab_view_valid)
+    backtrace_release_view (state, &symtab_view, error_callback, data);
+  if (strtab_view_valid)
+    backtrace_release_view (state, &strtab_view, error_callback, data);
+  if (debug_view_valid)
+    backtrace_release_view (state, &debug_view, error_callback, data);
+  if (descriptor != -1)
+    backtrace_close (descriptor, error_callback, data);
+  return retval;
+}
+
+/* Data passed to phdr_callback.  */
+
+struct phdr_data
+{
+  struct backtrace_state *state;
+  backtrace_error_callback error_callback;
+  void *data;
+  fileline *fileline_fn;
+  int *found_sym;
+  int *found_dwarf;
+  const char * exe_filename;
+};
+
+/* Callback passed to dl_iterate_phdr.  Load debug info from shared
+   libraries.  */
+
+static int
+phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
+              void *pdata)
+{
+  struct phdr_data *pd = (struct phdr_data *) pdata;
+  fileline elf_fileline_fn;
+  int found_dwarf;
+  const char * filename = info->dlpi_name;
+
+  /* There is not much we can do if we don't have the module name,
+     unless executable is ET_DYN, where we expect the very first
+     phdr_callback to be for the PIE.  */
+  if (filename == NULL || filename[0] == '\0')
+    {
+      if (!pd->exe_filename)
+       return 0;
+      filename = pd->exe_filename;
+      pd->exe_filename = NULL;
+    }
+
+  if (elf_add (pd->state, filename, info->dlpi_addr, pd->error_callback,
+              pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0,
+               NULL, 0))
+    {
+      if (found_dwarf)
+       {
+         *pd->found_dwarf = 1;
+         *pd->fileline_fn = elf_fileline_fn;
+       }
+    }
+
+  return 0;
+}
+
+/* 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 (struct backtrace_state *state, const char *filename,
+                     uintptr_t base_address, int is_exe,
+                     backtrace_error_callback error_callback,
+                     void *data, fileline *fileline_fn)
+{
+  int ret;
+  int found_sym;
+  int found_dwarf;
+  fileline elf_fileline_fn;
+
+  ret = elf_add (state, filename, base_address, error_callback, data, &elf_fileline_fn,
+               &found_sym, &found_dwarf, is_exe, NULL, 0);
+  if (!ret)
+    return 0;
+
+  if (!base_address)
+    {
+      struct phdr_data pd;
+
+      pd.state = state;
+      pd.error_callback = error_callback;
+      pd.data = data;
+      pd.fileline_fn = &elf_fileline_fn;
+      pd.found_sym = &found_sym;
+      pd.found_dwarf = &found_dwarf;
+      pd.exe_filename = filename;
+    
+      dl_iterate_phdr (phdr_callback, (void *) &pd);
+    }
+
+  if (!state->threaded)
+    {
+      if (found_sym)
+       state->syminfo_fn = elf_syminfo;
+      else if (state->syminfo_fn == NULL)
+       state->syminfo_fn = elf_nosyms;
+    }
+  else
+    {
+      if (found_sym)
+       backtrace_atomic_store_pointer (&state->syminfo_fn, elf_syminfo);
+      else
+       __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, elf_nosyms);
+    }
+
+  if (!state->threaded)
+    {
+      if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug)
+       *fileline_fn = elf_fileline_fn;
+    }
+  else
+    {
+      fileline current_fn;
+
+      current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+      if (current_fn == NULL || current_fn == elf_nodebug)
+       *fileline_fn = elf_fileline_fn;
+    }
+
+  return 1;
+}
+
+static void
+elf_get_uuid_error_callback(void *data, const char *msg, int errnum)
+{
+    // Do nothing here. elf_get_uuid() will return the error.
+}
+
+int
+elf_get_uuid (struct backtrace_state *state,
+              const char *filename, uint8_t uuid[20], int *uuid_len)
+{
+  struct backtrace_view ehdr_view;
+  b_elf_ehdr ehdr;
+  off_t shoff;
+  unsigned int shnum;
+  struct backtrace_view shdrs_view;
+  int shdrs_view_valid = 0;
+  const b_elf_shdr *shdrs;
+  unsigned int i;
+  void *data = NULL;
+  int retval = 0;
+  struct backtrace_state *state_alloced = NULL;
+
+  if (!state)
+  {
+      state_alloced = backtrace_create_state(filename, 0, elf_get_uuid_error_callback, NULL);
+      state = state_alloced;
+  }
+
+  int descriptor = backtrace_open (filename, elf_get_uuid_error_callback, data, NULL);
+  if (descriptor < 0)
+    return 0;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, elf_get_uuid_error_callback,
+                          data, &ehdr_view))
+    goto fail;
+
+  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
+
+  backtrace_release_view (state, &ehdr_view, elf_get_uuid_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)
+    {
+      // elf_get_uuid_error_callback (data, "executable file is not ELF", 0);
+      goto fail;
+    }
+  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      // elf_get_uuid_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)
+    {
+      // elf_get_uuid_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)
+    {
+      // elf_get_uuid_error_callback (data, "executable file has unknown endianness", 0);
+      goto fail;
+    }
+
+  shoff = ehdr.e_shoff;
+  shnum = ehdr.e_shnum;
+
+  if ((shnum == 0) && (shoff != 0))
+    {
+      struct backtrace_view shdr_view;
+      const b_elf_shdr *shdr;
+
+      if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr,
+                              elf_get_uuid_error_callback, data, &shdr_view))
+       goto fail;
+
+      shdr = (const b_elf_shdr *) shdr_view.data;
+
+      if (shnum == 0)
+       shnum = shdr->sh_size;
+
+      backtrace_release_view (state, &shdr_view, elf_get_uuid_error_callback, data);
+    }
+
+  if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr),
+                          (shnum - 1) * sizeof (b_elf_shdr),
+                          elf_get_uuid_error_callback, data, &shdrs_view))
+    goto fail;
+  shdrs_view_valid = 1;
+  shdrs = (const b_elf_shdr *) shdrs_view.data;
+
+  for (i = 1; i < shnum; ++i)
+    {
+      const b_elf_shdr *shdr = &shdrs[i - 1];
+
+      if (shdr->sh_type == SHT_NOTE)
+      {
+        struct backtrace_view note_view;
+
+        if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+                                 shdr->sh_size, elf_get_uuid_error_callback, data,
+                                 &note_view))
+        {
+          *uuid_len = elf_parse_gnu_buildid(note_view.data, shdr->sh_size, uuid);
+          backtrace_release_view (state, &note_view, elf_get_uuid_error_callback, data);
+          if (*uuid_len)
+          {
+             retval = 1;
+             break;
+          }
+        }
+      }
+    }
+
+fail:
+  if (shdrs_view_valid)
+    backtrace_release_view (state, &shdrs_view, elf_get_uuid_error_callback, data);
+  if (descriptor != -1)
+    backtrace_close (descriptor, elf_get_uuid_error_callback, data);
+  if (state_alloced)
+    backtrace_free(state_alloced, state_alloced, sizeof(*state_alloced), elf_get_uuid_error_callback, NULL);
+  return retval;
+}
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
new file mode 100644 (file)
index 0000000..d8ac203
--- /dev/null
@@ -0,0 +1,251 @@
+/* fileline.c -- Get file and line number information in a backtrace.
+   Copyright (C) 2012-2014 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 <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h> // For access()
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef HAVE_GETEXECNAME
+#define getexecname() NULL
+#endif
+
+/* Initialize the fileline information from the executable.  Returns 1
+   on success, 0 on failure.  */
+
+static int
+fileline_initialize (struct backtrace_state *state,
+                    backtrace_error_callback error_callback, void *data)
+{
+  int failed;
+  fileline fileline_fn;
+  int pass;
+
+  if (!state->threaded)
+    failed = state->fileline_initialization_failed;
+  else
+    failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
+
+  if (failed)
+    {
+      error_callback (data, "failed to read executable information", -1);
+      return 0;
+    }
+
+  if (!state->threaded)
+    fileline_fn = state->fileline_fn;
+  else
+    fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+  if (fileline_fn != NULL)
+    return 1;
+
+  /* We have not initialized the information.  Do it now.  */
+
+  const char *filename = NULL;
+  for (pass = 0; pass < 4; ++pass)
+    {
+      switch (pass)
+       {
+       case 0:
+         filename = state->filename;
+         break;
+       case 1:
+         filename = getexecname ();
+         break;
+       case 2:
+         filename = "/proc/self/exe";
+         break;
+       case 3:
+         filename = "/proc/curproc/file";
+         break;
+       default:
+         abort ();
+       }
+
+      /* If we've got a filename and the file exists, break. */
+      if (filename && filename[0] && (access(filename, F_OK) == 0))
+       break;
+
+      filename = NULL;
+    }
+
+  if (!filename)
+    {
+      if (state->filename != NULL)
+        error_callback (data, state->filename, ENOENT);
+      else
+        error_callback (data,
+                        "libbacktrace could not find executable to open",
+                        0);
+      failed = 1;
+    }
+
+  if (!failed)
+    {
+      if (!backtrace_initialize (state, filename, 0, 1, error_callback, data,
+                                &fileline_fn))
+       failed = 1;
+    }
+
+  if (failed)
+    {
+      if (!state->threaded)
+       state->fileline_initialization_failed = 1;
+      else
+       backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
+      return 0;
+    }
+
+  if (!state->threaded)
+    state->fileline_fn = fileline_fn;
+  else
+    {
+      backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
+
+      /* Note that if two threads initialize at once, one of the data
+        sets may be leaked.  */
+    }
+
+  return 1;
+}
+
+/* Return the debug filename with dwarf debug information (or NULL if not found). */
+
+const char *
+backtrace_get_debug_filename (struct backtrace_state *state)
+{
+    return state ? state->debug_filename : NULL;
+}
+
+/* Initialize the fileline information from a user specified filename.  Returns 1
+   on success, 0 on failure.  */
+
+int
+backtrace_fileline_initialize (struct backtrace_state *state, uintptr_t base_address, int is_exe,
+                               backtrace_error_callback error_callback, void *data)
+{
+  int failed;
+  fileline fileline_fn;
+
+  if (!state->threaded)
+    failed = state->fileline_initialization_failed;
+  else
+    failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
+
+  if (failed)
+    {
+      error_callback (data, "failed to read executable information", -1);
+      return 0;
+    }
+
+  if (!state->threaded)
+    fileline_fn = state->fileline_fn;
+  else
+    fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+  if (fileline_fn != NULL)
+    return 1;
+
+  /* We have not initialized information. Do it now.  */
+  /* For this function, we need a filename and base address. */
+
+  if (!state->filename || !base_address)
+    {
+      error_callback (data, state->filename, ENOENT);
+      failed = 1;
+    }
+  else
+    {
+      if (!backtrace_initialize (state, state->filename, base_address, is_exe,
+                                 error_callback, data, &fileline_fn))
+       failed = 1;
+    }
+
+  if (failed)
+    {
+      if (!state->threaded)
+       state->fileline_initialization_failed = 1;
+      else
+       backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
+      return 0;
+    }
+
+  if (!state->threaded)
+    state->fileline_fn = fileline_fn;
+  else
+    {
+      backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
+
+      /* Note that if two threads initialize at once, one of the data
+        sets may be leaked.  */
+    }
+
+  return 1;
+}
+/* Given a PC, find the file name, line number, and function name.  */
+
+int
+backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
+                 backtrace_full_callback callback,
+                 backtrace_error_callback error_callback, void *data)
+{
+  if (!fileline_initialize (state, error_callback, data))
+    return 0;
+
+  if (state->fileline_initialization_failed)
+    return 0;
+
+  return state->fileline_fn (state, pc, callback, error_callback, data);
+}
+
+/* Given a PC, find the symbol for it, and its value.  */
+
+int
+backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
+                  backtrace_syminfo_callback callback,
+                  backtrace_error_callback error_callback, void *data)
+{
+  if (!fileline_initialize (state, error_callback, data))
+    return 0;
+
+  if (state->fileline_initialization_failed)
+    return 0;
+
+  state->syminfo_fn (state, pc, callback, error_callback, data);
+  return 1;
+}
diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h
new file mode 100644 (file)
index 0000000..2bce4a2
--- /dev/null
@@ -0,0 +1,297 @@
+/* internal.h -- Internal header file for stack backtrace library.
+   Copyright (C) 2012-2014 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
+
+#ifndef HAVE_SYNC_FUNCTIONS
+
+/* Define out the sync functions.  These should never be called if
+   they are not available.  */
+
+#define __sync_bool_compare_and_swap(A, B, C) (abort(), 1)
+#define __sync_lock_test_and_set(A, B) (abort(), 0)
+#define __sync_lock_release(A) abort()
+
+#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
+
+#ifdef HAVE_ATOMIC_FUNCTIONS
+
+/* We have the atomic builtin functions.  */
+
+#define backtrace_atomic_load_pointer(p) \
+    __atomic_load_n ((p), __ATOMIC_ACQUIRE)
+#define backtrace_atomic_load_int(p) \
+    __atomic_load_n ((p), __ATOMIC_ACQUIRE)
+#define backtrace_atomic_store_pointer(p, v) \
+    __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+#define backtrace_atomic_store_size_t(p, v) \
+    __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+#define backtrace_atomic_store_int(p, v) \
+    __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+
+#else /* !defined (HAVE_ATOMIC_FUNCTIONS) */
+#ifdef HAVE_SYNC_FUNCTIONS
+
+/* We have the sync functions but not the atomic functions.  Define
+   the atomic ones in terms of the sync ones.  */
+
+extern void *backtrace_atomic_load_pointer (void *);
+extern int backtrace_atomic_load_int (int *);
+extern void backtrace_atomic_store_pointer (void *, void *);
+extern void backtrace_atomic_store_size_t (size_t *, size_t);
+extern void backtrace_atomic_store_int (int *, int);
+
+#else /* !defined (HAVE_SYNC_FUNCTIONS) */
+
+/* We have neither the sync nor the atomic functions.  These will
+   never be called.  */
+
+#define backtrace_atomic_load_pointer(p) (abort(), 0)
+#define backtrace_atomic_load_int(p) (abort(), 0)
+#define backtrace_atomic_store_pointer(p, v) abort()
+#define backtrace_atomic_store_size_t(p, v) abort()
+#define backtrace_atomic_store_int(p, v) abort()
+
+#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
+#endif /* !defined (HAVE_ATOMIC_FUNCTIONS) */
+
+/* The type of the function that collects file/line information.  This
+   is like backtrace_pcinfo.  */
+
+typedef int (*fileline) (struct backtrace_state *state, uintptr_t pc,
+                        backtrace_full_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) (struct backtrace_state *state, uintptr_t pc,
+                        backtrace_syminfo_callback callback,
+                        backtrace_error_callback error_callback, void *data);
+
+/* What the backtrace state pointer points to.  */
+
+struct backtrace_state
+{
+  /* The name of the executable.  */
+  const char *filename;
+  /* The name of the debug symbols filename.  */
+  const char *debug_filename;
+  /* Non-zero if threaded.  */
+  int threaded;
+  /* The master lock for fileline_fn, fileline_data, syminfo_fn,
+     syminfo_data, fileline_initialization_failed and everything the
+     data pointers point to.  */
+  void *lock;
+  /* The function that returns file/line information.  */
+  fileline fileline_fn;
+  /* The data to pass to FILELINE_FN.  */
+  void *fileline_data;
+  /* The function that returns symbol information.  */
+  syminfo syminfo_fn;
+  /* The data to pass to SYMINFO_FN.  */
+  void *syminfo_data;
+  /* Whether initializing the file/line information failed.  */
+  int fileline_initialization_failed;
+  /* The lock for the freelist.  */
+  int lock_alloc;
+  /* The freelist when using mmap.  */
+  struct backtrace_freelist_struct *freelist;
+};
+
+/* Open a file for reading.  Returns -1 on error.  If DOES_NOT_EXIST
+   is not NULL, *DOES_NOT_EXIST will be set to 0 normally and set to 1
+   if the file does not exist.  If the file does not exist and
+   DOES_NOT_EXIST is not NULL, the function will return -1 and will
+   not call ERROR_CALLBACK.  On other errors, or if DOES_NOT_EXIST is
+   NULL, the function will call ERROR_CALLBACK before returning.  */
+extern int backtrace_open (const char *filename,
+                          backtrace_error_callback error_callback,
+                          void *data,
+                          int *does_not_exist);
+
+/* 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 (struct backtrace_state *state, 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_state *state,
+                                   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 (struct backtrace_state *state, size_t size,
+                             backtrace_error_callback error_callback,
+                             void *data) ATTRIBUTE_MALLOC;
+
+/* Free memory allocated by backtrace_alloc.  */
+
+extern void backtrace_free (struct backtrace_state *state, void *mem,
+                           size_t size,
+                           backtrace_error_callback error_callback,
+                           void *data);
+
+/* Allocate memory like strdup. */
+
+extern char *backtrace_strdup (struct backtrace_state *state, const char *str,
+                               backtrace_error_callback error_callback,
+                               void *data) ATTRIBUTE_MALLOC;
+
+/* 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 (struct backtrace_state *state, 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.  Returns
+   a pointer to the base of the finished entries, or NULL on
+   failure.  */
+
+extern void* backtrace_vector_finish (struct backtrace_state *state,
+                                     struct backtrace_vector *vec,
+                                     backtrace_error_callback error_callback,
+                                     void *data);
+
+/* Release any extra space allocated for VEC.  This may change
+   VEC->base.  Returns 1 on success, 0 on failure.  */
+
+extern int backtrace_vector_release (struct backtrace_state *state,
+                                    struct backtrace_vector *vec,
+                                    backtrace_error_callback error_callback,
+                                    void *data);
+
+/* Read initial debug data from a descriptor, and set the
+   fileline_data, syminfo_fn, and syminfo_data fields of STATE.
+   Return the fileln_fn field in *FILELN_FN--this is done this way so
+   that the synchronization code is only implemented once.  This is
+   called after the descriptor has first been opened.  It will close
+   the descriptor if it is no longer needed.  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 (struct backtrace_state *state,
+                                const char *filename,
+                                uintptr_t base_address,
+                 int is_exe,
+                                backtrace_error_callback error_callback,
+                                void *data,
+                                fileline *fileline_fn);
+
+/* Add file/line information for a DWARF module.  */
+
+extern int backtrace_dwarf_add (struct backtrace_state *state,
+                               uintptr_t base_address,
+                               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 *fileline_fn);
+
+#endif
diff --git a/libbacktrace/libelftc_dem_gnu3.c b/libbacktrace/libelftc_dem_gnu3.c
new file mode 100644 (file)
index 0000000..abd6edd
--- /dev/null
@@ -0,0 +1,3529 @@
+/*-
+ * Copyright (c) 2007, 2008 Hyogeol Lee <hyogeollee@gmail.com>
+ * All rights reserved.
+ *
+ * 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
+ *    in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 <sys/types.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__linux__)
+
+#include <endian.h>
+
+#define ELFTC_BYTE_ORDER                        __BYTE_ORDER
+#define ELFTC_BYTE_ORDER_LITTLE_ENDIAN          __LITTLE_ENDIAN
+#define ELFTC_BYTE_ORDER_BIG_ENDIAN             __BIG_ENDIAN
+
+#endif
+
+/**
+ * @file cpp_demangle.c
+ * @brief Decode IA-64 C++ ABI style implementation.
+ *
+ * IA-64 standard ABI(Itanium C++ ABI) references.
+ *
+ * http://www.codesourcery.com/cxx-abi/abi.html#mangling \n
+ * http://www.codesourcery.com/cxx-abi/abi-mangling.html
+ */
+
+/** @brief Dynamic vector data for string. */
+struct vector_str {
+       /** Current size */
+       size_t          size;
+       /** Total capacity */
+       size_t          capacity;
+       /** String array */
+       char            **container;
+};
+
+#define BUFFER_GROWFACTOR      1.618
+#define VECTOR_DEF_CAPACITY    8
+#define        ELFTC_ISDIGIT(C)        (isdigit((C) & 0xFF))
+
+enum type_qualifier {
+       TYPE_PTR, TYPE_REF, TYPE_CMX, TYPE_IMG, TYPE_EXT, TYPE_RST, TYPE_VAT,
+       TYPE_CST
+};
+
+struct vector_type_qualifier {
+       size_t size, capacity;
+       enum type_qualifier *q_container;
+       struct vector_str ext_name;
+};
+
+enum read_cmd {
+       READ_FAIL, READ_NEST, READ_TMPL, READ_EXPR, READ_EXPL, READ_LOCAL,
+       READ_TYPE, READ_FUNC, READ_PTRMEM
+};
+
+struct vector_read_cmd {
+       size_t size, capacity;
+       enum read_cmd *r_container;
+};
+
+struct cpp_demangle_data {
+       struct vector_str        output;        /* output string vector */
+       struct vector_str        output_tmp;
+       struct vector_str        subst;         /* substitution string vector */
+       struct vector_str        tmpl;
+       struct vector_str        class_type;
+       struct vector_read_cmd   cmd;
+       bool                     paren;         /* parenthesis opened */
+       bool                     pfirst;        /* first element of parameter */
+       bool                     mem_rst;       /* restrict member function */
+       bool                     mem_vat;       /* volatile member function */
+       bool                     mem_cst;       /* const member function */
+       int                      func_type;
+       const char              *cur;           /* current mangled name ptr */
+       const char              *last_sname;    /* last source name */
+       int                      push_head;
+};
+
+#define        CPP_DEMANGLE_TRY_LIMIT  128
+#define        FLOAT_SPRINTF_TRY_LIMIT 5
+#define        FLOAT_QUADRUPLE_BYTES   16
+#define        FLOAT_EXTENED_BYTES     10
+
+#define SIMPLE_HASH(x,y)       (64 * x + y)
+
+static size_t  get_strlen_sum(const struct vector_str *v);
+static bool    vector_str_grow(struct vector_str *v);
+
+static size_t
+get_strlen_sum(const struct vector_str *v)
+{
+       size_t i, len = 0;
+
+       if (v == NULL)
+               return (0);
+
+       assert(v->size > 0);
+
+       for (i = 0; i < v->size; ++i)
+               len += strlen(v->container[i]);
+
+       return (len);
+}
+
+/**
+ * @brief Deallocate resource in vector_str.
+ */
+static void
+vector_str_dest(struct vector_str *v)
+{
+       size_t i;
+
+       if (v == NULL)
+               return;
+
+       for (i = 0; i < v->size; ++i)
+               free(v->container[i]);
+
+       free(v->container);
+}
+
+/**
+ * @brief Find string in vector_str.
+ * @param v Destination vector.
+ * @param o String to find.
+ * @param l Length of the string.
+ * @return -1 at failed, 0 at not found, 1 at found.
+ */
+static int
+vector_str_find(const struct vector_str *v, const char *o, size_t l)
+{
+       size_t i;
+
+       if (v == NULL || o == NULL)
+               return (-1);
+
+       for (i = 0; i < v->size; ++i)
+               if (strncmp(v->container[i], o, l) == 0)
+                       return (1);
+
+       return (0);
+}
+
+/**
+ * @brief Get new allocated flat string from vector.
+ *
+ * If l is not NULL, return length of the string.
+ * @param v Destination vector.
+ * @param l Length of the string.
+ * @return NULL at failed or NUL terminated new allocated string.
+ */
+static char *
+vector_str_get_flat(const struct vector_str *v, size_t *l)
+{
+       ssize_t elem_pos, elem_size, rtn_size;
+       size_t i;
+       char *rtn;
+
+       if (v == NULL || v->size == 0)
+               return (NULL);
+
+       if ((rtn_size = get_strlen_sum(v)) == 0)
+               return (NULL);
+
+       if ((rtn = malloc(sizeof(char) * (rtn_size + 1))) == NULL)
+               return (NULL);
+
+       elem_pos = 0;
+       for (i = 0; i < v->size; ++i) {
+               elem_size = strlen(v->container[i]);
+
+               memcpy(rtn + elem_pos, v->container[i], elem_size);
+
+               elem_pos += elem_size;
+       }
+
+       rtn[rtn_size] = '\0';
+
+       if (l != NULL)
+               *l = rtn_size;
+
+       return (rtn);
+}
+
+static bool
+vector_str_grow(struct vector_str *v)
+{
+       size_t i, tmp_cap;
+       char **tmp_ctn;
+
+       if (v == NULL)
+               return (false);
+
+       assert(v->capacity > 0);
+
+       tmp_cap = v->capacity * BUFFER_GROWFACTOR;
+
+       assert(tmp_cap > v->capacity);
+
+       if ((tmp_ctn = malloc(sizeof(char *) * tmp_cap)) == NULL)
+               return (false);
+
+       for (i = 0; i < v->size; ++i)
+               tmp_ctn[i] = v->container[i];
+
+       free(v->container);
+
+       v->container = tmp_ctn;
+       v->capacity = tmp_cap;
+
+       return (true);
+}
+
+/**
+ * @brief Initialize vector_str.
+ * @return false at failed, true at success.
+ */
+static bool
+vector_str_init(struct vector_str *v)
+{
+
+       if (v == NULL)
+               return (false);
+
+       v->size = 0;
+       v->capacity = VECTOR_DEF_CAPACITY;
+
+       assert(v->capacity > 0);
+
+       if ((v->container = malloc(sizeof(char *) * v->capacity)) == NULL)
+               return (false);
+
+       assert(v->container != NULL);
+
+       return (true);
+}
+
+/**
+ * @brief Remove last element in vector_str.
+ * @return false at failed, true at success.
+ */
+static bool
+vector_str_pop(struct vector_str *v)
+{
+
+       if (v == NULL)
+               return (false);
+
+       if (v->size == 0)
+               return (true);
+
+       --v->size;
+
+       free(v->container[v->size]);
+       v->container[v->size] = NULL;
+
+       return (true);
+}
+
+/**
+ * @brief Push back string to vector.
+ * @return false at failed, true at success.
+ */
+static bool
+vector_str_push(struct vector_str *v, const char *str, size_t len)
+{
+
+       if (v == NULL || str == NULL)
+               return (false);
+
+       if (v->size == v->capacity && vector_str_grow(v) == false)
+               return (false);
+
+       if ((v->container[v->size] = malloc(sizeof(char) * (len + 1))) == NULL)
+               return (false);
+
+       snprintf(v->container[v->size], len + 1, "%s", str);
+
+       ++v->size;
+
+       return (true);
+}
+
+/**
+ * @brief Push front org vector to det vector.
+ * @return false at failed, true at success.
+ */
+static bool
+vector_str_push_vector_head(struct vector_str *dst, struct vector_str *org)
+{
+       size_t i, j, tmp_cap;
+       char **tmp_ctn;
+
+       if (dst == NULL || org == NULL)
+               return (false);
+
+       tmp_cap = (dst->size + org->size) * BUFFER_GROWFACTOR;
+
+       if ((tmp_ctn = malloc(sizeof(char *) * tmp_cap)) == NULL)
+               return (false);
+
+       for (i = 0; i < org->size; ++i)
+               if ((tmp_ctn[i] = strdup(org->container[i])) == NULL) {
+                       for (j = 0; j < i; ++j)
+                               free(tmp_ctn[j]);
+
+                       free(tmp_ctn);
+
+                       return (false);
+               }
+
+       for (i = 0; i < dst->size; ++i)
+               tmp_ctn[i + org->size] = dst->container[i];
+
+       free(dst->container);
+
+       dst->container = tmp_ctn;
+       dst->capacity = tmp_cap;
+       dst->size += org->size;
+
+       return (true);
+}
+
+/**
+ * @brief Get new allocated flat string from vector between begin and end.
+ *
+ * If r_len is not NULL, string length will be returned.
+ * @return NULL at failed or NUL terminated new allocated string.
+ */
+static char *
+vector_str_substr(const struct vector_str *v, size_t begin, size_t end,
+    size_t *r_len)
+{
+       size_t cur, i, len;
+       char *rtn;
+
+       if (v == NULL || begin > end)
+               return (NULL);
+
+       len = 0;
+       for (i = begin; i < end + 1; ++i)
+               len += strlen(v->container[i]);
+
+       if ((rtn = malloc(sizeof(char) * (len + 1))) == NULL)
+               return (NULL);
+
+       if (r_len != NULL)
+               *r_len = len;
+
+       cur = 0;
+       for (i = begin; i < end + 1; ++i) {
+               len = strlen(v->container[i]);
+               memcpy(rtn + cur, v->container[i], len);
+               cur += len;
+       }
+       rtn[cur] = '\0';
+
+       return (rtn);
+}
+
+static void    cpp_demangle_data_dest(struct cpp_demangle_data *);
+static int     cpp_demangle_data_init(struct cpp_demangle_data *,
+                   const char *);
+static int     cpp_demangle_get_subst(struct cpp_demangle_data *, size_t);
+static int     cpp_demangle_get_tmpl_param(struct cpp_demangle_data *, size_t);
+static int     cpp_demangle_push_fp(struct cpp_demangle_data *,
+                   char *(*)(const char *, size_t));
+static int     cpp_demangle_push_str(struct cpp_demangle_data *, const char *,
+                   size_t);
+static int     cpp_demangle_push_subst(struct cpp_demangle_data *,
+                   const char *, size_t);
+static int     cpp_demangle_push_subst_v(struct cpp_demangle_data *,
+                   struct vector_str *);
+static int     cpp_demangle_push_type_qualifier(struct cpp_demangle_data *,
+                   struct vector_type_qualifier *, const char *);
+static int     cpp_demangle_read_array(struct cpp_demangle_data *);
+static int     cpp_demangle_read_encoding(struct cpp_demangle_data *);
+static int     cpp_demangle_read_expr_primary(struct cpp_demangle_data *);
+static int     cpp_demangle_read_expression(struct cpp_demangle_data *);
+static int     cpp_demangle_read_expression_binary(struct cpp_demangle_data *,
+                   const char *, size_t);
+static int     cpp_demangle_read_expression_unary(struct cpp_demangle_data *,
+                   const char *, size_t);
+static int     cpp_demangle_read_expression_trinary(struct cpp_demangle_data *,
+                   const char *, size_t, const char *, size_t);
+static int     cpp_demangle_read_function(struct cpp_demangle_data *, int *,
+                   struct vector_type_qualifier *);
+static int     cpp_demangle_local_source_name(struct cpp_demangle_data *ddata);
+static int     cpp_demangle_read_local_name(struct cpp_demangle_data *);
+static int     cpp_demangle_read_name(struct cpp_demangle_data *);
+static int     cpp_demangle_read_nested_name(struct cpp_demangle_data *);
+static int     cpp_demangle_read_number(struct cpp_demangle_data *, long *);
+static int     cpp_demangle_read_nv_offset(struct cpp_demangle_data *);
+static int     cpp_demangle_read_offset(struct cpp_demangle_data *);
+static int     cpp_demangle_read_offset_number(struct cpp_demangle_data *);
+static int     cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *);
+static int     cpp_demangle_read_sname(struct cpp_demangle_data *);
+static int     cpp_demangle_read_subst(struct cpp_demangle_data *);
+static int     cpp_demangle_read_subst_std(struct cpp_demangle_data *);
+static int     cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *,
+                   const char *, size_t);
+static int     cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *);
+static int     cpp_demangle_read_tmpl_args(struct cpp_demangle_data *);
+static int     cpp_demangle_read_tmpl_param(struct cpp_demangle_data *);
+static int     cpp_demangle_read_type(struct cpp_demangle_data *, int);
+static int     cpp_demangle_read_uqname(struct cpp_demangle_data *);
+static int     cpp_demangle_read_v_offset(struct cpp_demangle_data *);
+static char    *decode_fp_to_double(const char *, size_t);
+static char    *decode_fp_to_float(const char *, size_t);
+static char    *decode_fp_to_float128(const char *, size_t);
+static char    *decode_fp_to_float80(const char *, size_t);
+static char    *decode_fp_to_long_double(const char *, size_t);
+static int     hex_to_dec(char);
+static void    vector_read_cmd_dest(struct vector_read_cmd *);
+static int     vector_read_cmd_find(struct vector_read_cmd *, enum read_cmd);
+static int     vector_read_cmd_init(struct vector_read_cmd *);
+static int     vector_read_cmd_pop(struct vector_read_cmd *);
+static int     vector_read_cmd_push(struct vector_read_cmd *, enum read_cmd);
+static void    vector_type_qualifier_dest(struct vector_type_qualifier *);
+static int     vector_type_qualifier_init(struct vector_type_qualifier *);
+static int     vector_type_qualifier_push(struct vector_type_qualifier *,
+                   enum type_qualifier);
+
+/**
+ * @brief Decode the input string by IA-64 C++ ABI style.
+ *
+ * GNU GCC v3 use IA-64 standard ABI.
+ * @return New allocated demangled string or NULL if failed.
+ * @todo 1. Testing and more test case. 2. Code cleaning.
+ */
+char *
+__cxa_demangle_gnu3(const char *org)
+{
+       struct cpp_demangle_data ddata;
+       ssize_t org_len;
+       unsigned int limit;
+       char *rtn = NULL;
+
+       if (org == NULL)
+               return (NULL);
+
+       org_len = strlen(org);
+       if (org_len > 11 && !strncmp(org, "_GLOBAL__I_", 11)) {
+               if ((rtn = malloc(org_len + 19)) == NULL)
+                       return (NULL);
+               snprintf(rtn, org_len + 19,
+                   "global constructors keyed to %s", org + 11);
+               return (rtn);
+       }
+
+       // Try demangling as a type for short encodings
+       if ((org_len < 2) || (org[0] != '_' || org[1] != 'Z' )) {
+               if (!cpp_demangle_data_init(&ddata, org))
+                       return (NULL);
+               if (!cpp_demangle_read_type(&ddata, 0))
+                       goto clean;
+               rtn = vector_str_get_flat(&ddata.output, (size_t *) NULL);
+               goto clean;
+       }
+
+
+       if (!cpp_demangle_data_init(&ddata, org + 2))
+               return (NULL);
+
+       rtn = NULL;
+
+       if (!cpp_demangle_read_encoding(&ddata))
+               goto clean;
+
+       limit = 0;
+       while (*ddata.cur != '\0') {
+               /*
+                * Breaking at some gcc info at tail. e.g) @@GLIBCXX_3.4
+                */
+               if (*ddata.cur == '@' && *(ddata.cur + 1) == '@')
+                       break;
+               if (!cpp_demangle_read_type(&ddata, 1))
+                       goto clean;
+               if (limit++ > CPP_DEMANGLE_TRY_LIMIT)
+                       goto clean;
+       }
+
+       if (ddata.output.size == 0)
+               goto clean;
+       if (ddata.paren && !vector_str_push(&ddata.output, ")", 1))
+               goto clean;
+       if (ddata.mem_vat && !vector_str_push(&ddata.output, " volatile", 9))
+               goto clean;
+       if (ddata.mem_cst && !vector_str_push(&ddata.output, " const", 6))
+               goto clean;
+       if (ddata.mem_rst && !vector_str_push(&ddata.output, " restrict", 9))
+               goto clean;
+
+       rtn = vector_str_get_flat(&ddata.output, (size_t *) NULL);
+
+clean:
+       cpp_demangle_data_dest(&ddata);
+
+       return (rtn);
+}
+
+static void
+cpp_demangle_data_dest(struct cpp_demangle_data *d)
+{
+
+       if (d == NULL)
+               return;
+
+       vector_read_cmd_dest(&d->cmd);
+       vector_str_dest(&d->class_type);
+       vector_str_dest(&d->tmpl);
+       vector_str_dest(&d->subst);
+       vector_str_dest(&d->output_tmp);
+       vector_str_dest(&d->output);
+}
+
+static int
+cpp_demangle_data_init(struct cpp_demangle_data *d, const char *cur)
+{
+
+       if (d == NULL || cur == NULL)
+               return (0);
+
+       if (!vector_str_init(&d->output))
+               return (0);
+       if (!vector_str_init(&d->output_tmp))
+               goto clean1;
+       if (!vector_str_init(&d->subst))
+               goto clean2;
+       if (!vector_str_init(&d->tmpl))
+               goto clean3;
+       if (!vector_str_init(&d->class_type))
+               goto clean4;
+       if (!vector_read_cmd_init(&d->cmd))
+               goto clean5;
+
+       assert(d->output.container != NULL);
+       assert(d->output_tmp.container != NULL);
+       assert(d->subst.container != NULL);
+       assert(d->tmpl.container != NULL);
+       assert(d->class_type.container != NULL);
+
+       d->paren = false;
+       d->pfirst = false;
+       d->mem_rst = false;
+       d->mem_vat = false;
+       d->mem_cst = false;
+       d->func_type = 0;
+       d->cur = cur;
+       d->last_sname = NULL;
+       d->push_head = 0;
+
+       return (1);
+
+clean5:
+       vector_str_dest(&d->class_type);
+clean4:
+       vector_str_dest(&d->tmpl);
+clean3:
+       vector_str_dest(&d->subst);
+clean2:
+       vector_str_dest(&d->output_tmp);
+clean1:
+       vector_str_dest(&d->output);
+
+       return (0);
+}
+
+static int
+cpp_demangle_push_fp(struct cpp_demangle_data *ddata,
+    char *(*decoder)(const char *, size_t))
+{
+       size_t len;
+       int rtn;
+       const char *fp;
+       char *f;
+
+       if (ddata == NULL || decoder == NULL)
+               return (0);
+
+       fp = ddata->cur;
+       while (*ddata->cur != 'E')
+               ++ddata->cur;
+       ++ddata->cur;
+
+       if ((f = decoder(fp, ddata->cur - fp)) == NULL)
+               return (0);
+
+       rtn = 0;
+       if ((len = strlen(f)) > 0)
+               rtn = cpp_demangle_push_str(ddata, f, len); 
+
+       free(f);
+
+       return (rtn);
+}
+
+static int
+cpp_demangle_push_str(struct cpp_demangle_data *ddata, const char *str,
+    size_t len)
+{
+
+       if (ddata == NULL || str == NULL || len == 0)
+               return (0);
+
+       if (ddata->push_head > 0)
+               return (vector_str_push(&ddata->output_tmp, str, len));
+
+       return (vector_str_push(&ddata->output, str, len));
+}
+
+static int
+cpp_demangle_push_subst(struct cpp_demangle_data *ddata, const char *str,
+    size_t len)
+{
+
+       if (ddata == NULL || str == NULL || len == 0)
+               return (0);
+
+       if (!vector_str_find(&ddata->subst, str, len))
+               return (vector_str_push(&ddata->subst, str, len));
+
+       return (1);
+}
+
+static int
+cpp_demangle_push_subst_v(struct cpp_demangle_data *ddata, struct vector_str *v)
+{
+       size_t str_len;
+       int rtn;
+       char *str;
+
+       if (ddata == NULL || v == NULL)
+               return (0);
+
+       if ((str = vector_str_get_flat(v, &str_len)) == NULL)
+               return (0);
+
+       rtn = cpp_demangle_push_subst(ddata, str, str_len);
+
+       free(str);
+
+       return (rtn);
+}
+
+static int
+cpp_demangle_push_type_qualifier(struct cpp_demangle_data *ddata,
+    struct vector_type_qualifier *v, const char *type_str)
+{
+       struct vector_str subst_v;
+       size_t idx, e_idx, e_len;
+       int rtn;
+       char *buf;
+
+       if (ddata == NULL || v == NULL)
+               return (0);
+
+       if ((idx = v->size) == 0)
+               return (1);
+
+       rtn = 0;
+       if (type_str != NULL) {
+               if (!vector_str_init(&subst_v))
+                       return (0);
+               if (!vector_str_push(&subst_v, type_str, strlen(type_str)))
+                       goto clean;
+       }
+
+       e_idx = 0;
+       while (idx > 0) {
+               switch (v->q_container[idx - 1]) {
+               case TYPE_PTR:
+                       if (!cpp_demangle_push_str(ddata, "*", 1))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, "*", 1))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               case TYPE_REF:
+                       if (!cpp_demangle_push_str(ddata, "&", 1))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, "&", 1))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               case TYPE_CMX:
+                       if (!cpp_demangle_push_str(ddata, " complex", 8))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, " complex", 8))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               case TYPE_IMG:
+                       if (!cpp_demangle_push_str(ddata, " imaginary", 10))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, " imaginary", 10))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               case TYPE_EXT:
+                       if (e_idx > v->ext_name.size - 1)
+                               goto clean;
+                       if ((e_len = strlen(v->ext_name.container[e_idx])) == 0)
+                               goto clean;
+                       if ((buf = malloc(sizeof(char) * (e_len + 1))) == NULL)
+                               goto clean;
+
+                       memcpy(buf, " ", 1);
+                       memcpy(buf + 1, v->ext_name.container[e_idx], e_len);
+
+                       if (!cpp_demangle_push_str(ddata, buf, e_len + 1)) {
+                               free(buf);
+                               goto clean;
+                       }
+
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, buf,
+                                   e_len + 1)) {
+                                       free(buf);
+                                       goto clean;
+                               }
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v)) {
+                                       free(buf);
+                                       goto clean;
+                               }
+                       }
+                       free(buf);
+                       ++e_idx;
+                       break;
+
+               case TYPE_RST:
+                       if (!cpp_demangle_push_str(ddata, " restrict", 9))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, " restrict", 9))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               case TYPE_VAT:
+                       if (!cpp_demangle_push_str(ddata, " volatile", 9))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, " volatile", 9))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               case TYPE_CST:
+                       if (!cpp_demangle_push_str(ddata, " const", 6))
+                               goto clean;
+                       if (type_str != NULL) {
+                               if (!vector_str_push(&subst_v, " const", 6))
+                                       goto clean;
+                               if (!cpp_demangle_push_subst_v(ddata, &subst_v))
+                                       goto clean;
+                       }
+                       break;
+
+               };
+               --idx;
+       }
+
+       rtn = 1;
+clean:
+       if (type_str != NULL)
+               vector_str_dest(&subst_v);
+
+       return (rtn);
+}
+
+static int
+cpp_demangle_get_subst(struct cpp_demangle_data *ddata, size_t idx)
+{
+       size_t len;
+
+       if (ddata == NULL || ddata->subst.size <= idx)
+               return (0);
+       if ((len = strlen(ddata->subst.container[idx])) == 0)
+               return (0);
+       if (!cpp_demangle_push_str(ddata, ddata->subst.container[idx], len))
+               return (0);
+
+       /* skip '_' */
+       ++ddata->cur;
+
+       return (1);
+}
+
+static int
+cpp_demangle_get_tmpl_param(struct cpp_demangle_data *ddata, size_t idx)
+{
+       size_t len;
+
+       if (ddata == NULL || ddata->tmpl.size <= idx)
+               return (0);
+       if ((len = strlen(ddata->tmpl.container[idx])) == 0)
+               return (0);
+       if (!cpp_demangle_push_str(ddata, ddata->tmpl.container[idx], len))
+               return (0);
+
+       ++ddata->cur;
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_array(struct cpp_demangle_data *ddata)
+{
+       size_t i, num_len, exp_len, p_idx, idx;
+       const char *num;
+       char *exp;
+
+       if (ddata == NULL || *(++ddata->cur) == '\0')
+               return (0);
+
+       if (*ddata->cur == '_') {
+               if (*(++ddata->cur) == '\0')
+                       return (0);
+
+               if (!cpp_demangle_read_type(ddata, 0))
+                       return (0);
+
+               if (!cpp_demangle_push_str(ddata, "[]", 2))
+                       return (0);
+       } else {
+               if (ELFTC_ISDIGIT(*ddata->cur) != 0) {
+                       num = ddata->cur;
+                       while (ELFTC_ISDIGIT(*ddata->cur) != 0)
+                               ++ddata->cur;
+                       if (*ddata->cur != '_')
+                               return (0);
+                       num_len = ddata->cur - num;
+                       assert(num_len > 0);
+                       if (*(++ddata->cur) == '\0')
+                               return (0);
+                       if (!cpp_demangle_read_type(ddata, 0))
+                               return (0);
+                       if (!cpp_demangle_push_str(ddata, "[", 1))
+                               return (0);
+                       if (!cpp_demangle_push_str(ddata, num, num_len))
+                               return (0);
+                       if (!cpp_demangle_push_str(ddata, "]", 1))
+                               return (0);
+               } else {
+                       p_idx = ddata->output.size;
+                       if (!cpp_demangle_read_expression(ddata))
+                               return (0);
+                       if ((exp = vector_str_substr(&ddata->output, p_idx,
+                                ddata->output.size - 1, &exp_len)) == NULL)
+                               return (0);
+                       idx = ddata->output.size;
+                       for (i = p_idx; i < idx; ++i)
+                               if (!vector_str_pop(&ddata->output)) {
+                                       free(exp);
+                                       return (0);
+                               }
+                       if (*ddata->cur != '_') {
+                               free(exp);
+                               return (0);
+                       }
+                       ++ddata->cur;
+                       if (*ddata->cur == '\0') {
+                               free(exp);
+                               return (0);
+                       }
+                       if (!cpp_demangle_read_type(ddata, 0)) {
+                               free(exp);
+                               return (0);
+                       }
+                       if (!cpp_demangle_push_str(ddata, "[", 1)) {
+                               free(exp);
+                               return (0);
+                       }
+                       if (!cpp_demangle_push_str(ddata, exp, exp_len)) {
+                               free(exp);
+                               return (0);
+                       }
+                       if (!cpp_demangle_push_str(ddata, "]", 1)) {
+                               free(exp);
+                               return (0);
+                       }
+                       free(exp);
+               }
+       }
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_expr_primary(struct cpp_demangle_data *ddata)
+{
+       const char *num;
+
+       if (ddata == NULL || *(++ddata->cur) == '\0')
+               return (0);
+
+       if (*ddata->cur == '_' && *(ddata->cur + 1) == 'Z') {
+               ddata->cur += 2;
+               if (*ddata->cur == '\0')
+                       return (0);
+               if (!cpp_demangle_read_encoding(ddata))
+                       return (0);
+               ++ddata->cur;
+               return (1);
+       }
+
+       switch (*ddata->cur) {
+       case 'b':
+               switch (*(++ddata->cur)) {
+               case '0':
+                       return (cpp_demangle_push_str(ddata, "false", 5));
+               case '1':
+                       return (cpp_demangle_push_str(ddata, "true", 4));
+               default:
+                       return (0);
+               };
+
+       case 'd':
+               ++ddata->cur;
+               return (cpp_demangle_push_fp(ddata, decode_fp_to_double));
+
+       case 'e':
+               ++ddata->cur;
+               if (sizeof(long double) == 10)
+                       return (cpp_demangle_push_fp(ddata,
+                           decode_fp_to_double));
+               return (cpp_demangle_push_fp(ddata, decode_fp_to_float80));
+
+       case 'f':
+               ++ddata->cur;
+               return (cpp_demangle_push_fp(ddata, decode_fp_to_float));
+
+       case 'g':
+               ++ddata->cur;
+               if (sizeof(long double) == 16)
+                       return (cpp_demangle_push_fp(ddata,
+                           decode_fp_to_double));
+               return (cpp_demangle_push_fp(ddata, decode_fp_to_float128));
+
+       case 'i':
+       case 'j':
+       case 'l':
+       case 'm':
+       case 'n':
+       case 's':
+       case 't':
+       case 'x':
+       case 'y':
+               if (*(++ddata->cur) == 'n') {
+                       if (!cpp_demangle_push_str(ddata, "-", 1))
+                               return (0);
+                       ++ddata->cur;
+               }
+               num = ddata->cur;
+               while (*ddata->cur != 'E') {
+                       if (!ELFTC_ISDIGIT(*ddata->cur))
+                               return (0);
+                       ++ddata->cur;
+               }
+               ++ddata->cur;
+               return (cpp_demangle_push_str(ddata, num, ddata->cur - num));
+
+       default:
+               return (0);
+       };
+}
+
+static int
+cpp_demangle_read_expression(struct cpp_demangle_data *ddata)
+{
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) {
+       case SIMPLE_HASH('s', 't'):
+               ddata->cur += 2;
+               return (cpp_demangle_read_type(ddata, 0));
+
+       case SIMPLE_HASH('s', 'r'):
+               ddata->cur += 2;
+               if (!cpp_demangle_read_type(ddata, 0))
+                       return (0);
+               if (!cpp_demangle_read_uqname(ddata))
+                       return (0);
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_tmpl_args(ddata));
+               return (1);
+
+       case SIMPLE_HASH('a', 'a'):
+               /* operator && */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "&&", 2));
+
+       case SIMPLE_HASH('a', 'd'):
+               /* operator & (unary) */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "&", 1));
+
+       case SIMPLE_HASH('a', 'n'):
+               /* operator & */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "&", 1));
+
+       case SIMPLE_HASH('a', 'N'):
+               /* operator &= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "&=", 2));
+
+       case SIMPLE_HASH('a', 'S'):
+               /* operator = */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "=", 1));
+
+       case SIMPLE_HASH('c', 'l'):
+               /* operator () */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "()", 2));
+
+       case SIMPLE_HASH('c', 'm'):
+               /* operator , */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, ",", 1));
+
+       case SIMPLE_HASH('c', 'o'):
+               /* operator ~ */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "~", 1));
+
+       case SIMPLE_HASH('c', 'v'):
+               /* operator (cast) */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "(cast)", 6));
+
+       case SIMPLE_HASH('d', 'a'):
+               /* operator delete [] */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "delete []", 9));
+
+       case SIMPLE_HASH('d', 'e'):
+               /* operator * (unary) */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "*", 1));
+
+       case SIMPLE_HASH('d', 'l'):
+               /* operator delete */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "delete", 6));
+
+       case SIMPLE_HASH('d', 'v'):
+               /* operator / */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "/", 1));
+
+       case SIMPLE_HASH('d', 'V'):
+               /* operator /= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "/=", 2));
+
+       case SIMPLE_HASH('e', 'o'):
+               /* operator ^ */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "^", 1));
+
+       case SIMPLE_HASH('e', 'O'):
+               /* operator ^= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "^=", 2));
+
+       case SIMPLE_HASH('e', 'q'):
+               /* operator == */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "==", 2));
+
+       case SIMPLE_HASH('g', 'e'):
+               /* operator >= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, ">=", 2));
+
+       case SIMPLE_HASH('g', 't'):
+               /* operator > */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, ">", 1));
+
+       case SIMPLE_HASH('i', 'x'):
+               /* operator [] */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "[]", 2));
+
+       case SIMPLE_HASH('l', 'e'):
+               /* operator <= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "<=", 2));
+
+       case SIMPLE_HASH('l', 's'):
+               /* operator << */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "<<", 2));
+
+       case SIMPLE_HASH('l', 'S'):
+               /* operator <<= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "<<=", 3));
+
+       case SIMPLE_HASH('l', 't'):
+               /* operator < */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "<", 1));
+
+       case SIMPLE_HASH('m', 'i'):
+               /* operator - */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "-", 1));
+
+       case SIMPLE_HASH('m', 'I'):
+               /* operator -= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "-=", 2));
+
+       case SIMPLE_HASH('m', 'l'):
+               /* operator * */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "*", 1));
+
+       case SIMPLE_HASH('m', 'L'):
+               /* operator *= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "*=", 2));
+
+       case SIMPLE_HASH('m', 'm'):
+               /* operator -- */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "--", 2));
+
+       case SIMPLE_HASH('n', 'a'):
+               /* operator new[] */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "new []", 6));
+
+       case SIMPLE_HASH('n', 'e'):
+               /* operator != */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "!=", 2));
+
+       case SIMPLE_HASH('n', 'g'):
+               /* operator - (unary) */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "-", 1));
+
+       case SIMPLE_HASH('n', 't'):
+               /* operator ! */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "!", 1));
+
+       case SIMPLE_HASH('n', 'w'):
+               /* operator new */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "new", 3));
+
+       case SIMPLE_HASH('o', 'o'):
+               /* operator || */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "||", 2));
+
+       case SIMPLE_HASH('o', 'r'):
+               /* operator | */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "|", 1));
+
+       case SIMPLE_HASH('o', 'R'):
+               /* operator |= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "|=", 2));
+
+       case SIMPLE_HASH('p', 'l'):
+               /* operator + */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "+", 1));
+
+       case SIMPLE_HASH('p', 'L'):
+               /* operator += */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "+=", 2));
+
+       case SIMPLE_HASH('p', 'm'):
+               /* operator ->* */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "->*", 3));
+
+       case SIMPLE_HASH('p', 'p'):
+               /* operator ++ */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "++", 2));
+
+       case SIMPLE_HASH('p', 's'):
+               /* operator + (unary) */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "+", 1));
+
+       case SIMPLE_HASH('p', 't'):
+               /* operator -> */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "->", 2));
+
+       case SIMPLE_HASH('q', 'u'):
+               /* operator ? */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_trinary(ddata, "?", 1,
+                   ":", 1));
+
+       case SIMPLE_HASH('r', 'm'):
+               /* operator % */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "%", 1));
+
+       case SIMPLE_HASH('r', 'M'):
+               /* operator %= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, "%=", 2));
+
+       case SIMPLE_HASH('r', 's'):
+               /* operator >> */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, ">>", 2));
+
+       case SIMPLE_HASH('r', 'S'):
+               /* operator >>= */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_binary(ddata, ">>=", 3));
+
+       case SIMPLE_HASH('r', 'z'):
+               /* operator sizeof */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6));
+
+       case SIMPLE_HASH('s', 'v'):
+               /* operator sizeof */
+               ddata->cur += 2;
+               return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6));
+       };
+
+       switch (*ddata->cur) {
+       case 'L':
+               return (cpp_demangle_read_expr_primary(ddata));
+       case 'T':
+               return (cpp_demangle_read_tmpl_param(ddata));
+       };
+
+       return (0);
+}
+
+static int
+cpp_demangle_read_expression_binary(struct cpp_demangle_data *ddata,
+    const char *name, size_t len)
+{
+
+       if (ddata == NULL || name == NULL || len == 0)
+               return (0);
+       if (!cpp_demangle_read_expression(ddata))
+               return (0);
+       if (!cpp_demangle_push_str(ddata, name, len))
+               return (0);
+
+       return (cpp_demangle_read_expression(ddata));
+}
+
+static int
+cpp_demangle_read_expression_unary(struct cpp_demangle_data *ddata,
+    const char *name, size_t len)
+{
+
+       if (ddata == NULL || name == NULL || len == 0)
+               return (0);
+       if (!cpp_demangle_read_expression(ddata))
+               return (0);
+
+       return (cpp_demangle_push_str(ddata, name, len));
+}
+
+static int
+cpp_demangle_read_expression_trinary(struct cpp_demangle_data *ddata,
+    const char *name1, size_t len1, const char *name2, size_t len2)
+{
+
+       if (ddata == NULL || name1 == NULL || len1 == 0 || name2 == NULL ||
+           len2 == 0)
+               return (0);
+
+       if (!cpp_demangle_read_expression(ddata))
+               return (0);
+       if (!cpp_demangle_push_str(ddata, name1, len1))
+               return (0);
+       if (!cpp_demangle_read_expression(ddata))
+               return (0);
+       if (!cpp_demangle_push_str(ddata, name2, len2))
+               return (0);
+
+       return (cpp_demangle_read_expression(ddata));
+}
+
+static int
+cpp_demangle_read_function(struct cpp_demangle_data *ddata, int *ext_c,
+    struct vector_type_qualifier *v)
+{
+       size_t class_type_size, class_type_len, limit;
+       const char *class_type;
+
+       if (ddata == NULL || *ddata->cur != 'F' || v == NULL)
+               return (0);
+
+       ++ddata->cur;
+       if (*ddata->cur == 'Y') {
+               if (ext_c != NULL)
+                       *ext_c = 1;
+               ++ddata->cur;
+       }
+       if (!cpp_demangle_read_type(ddata, 0))
+               return (0);
+       if (*ddata->cur != 'E') {
+               if (!cpp_demangle_push_str(ddata, "(", 1))
+                       return (0);
+               if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM)) {
+                       if ((class_type_size = ddata->class_type.size) == 0)
+                               return (0);
+                       class_type =
+                           ddata->class_type.container[class_type_size - 1];
+                       if (class_type == NULL)
+                               return (0);
+                       if ((class_type_len = strlen(class_type)) == 0)
+                               return (0);
+                       if (!cpp_demangle_push_str(ddata, class_type,
+                           class_type_len))
+                               return (0);
+                       if (!cpp_demangle_push_str(ddata, "::*", 3))
+                               return (0);
+                       ++ddata->func_type;
+               } else {
+                       if (!cpp_demangle_push_type_qualifier(ddata, v,
+                           (const char *) NULL))
+                               return (0);
+                       vector_type_qualifier_dest(v);
+                       if (!vector_type_qualifier_init(v))
+                               return (0);
+               }
+
+               if (!cpp_demangle_push_str(ddata, ")(", 2))
+                       return (0);
+
+               limit = 0;
+               for (;;) {
+                       if (!cpp_demangle_read_type(ddata, 0))
+                               return (0);
+                       if (*ddata->cur == 'E')
+                               break;
+                       if (limit++ > CPP_DEMANGLE_TRY_LIMIT)
+                               return (0);
+               }
+
+               if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM) == 1) {
+                       if (!cpp_demangle_push_type_qualifier(ddata, v,
+                           (const char *) NULL))
+                               return (0);
+                       vector_type_qualifier_dest(v);
+                       if (!vector_type_qualifier_init(v))
+                               return (0);
+               }
+
+               if (!cpp_demangle_push_str(ddata, ")", 1))
+                       return (0);
+       }
+
+       ++ddata->cur;
+
+       return (1);
+}
+
+/* read encoding, encoding are function name, data name, special-name */
+static int
+cpp_demangle_read_encoding(struct cpp_demangle_data *ddata)
+{
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       /* special name */
+       switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) {
+       case SIMPLE_HASH('G', 'V'):
+               /* sentry object for 1 time init */
+               if (!cpp_demangle_push_str(ddata, "guard variable for ", 20))
+                       return (0);
+               ddata->cur += 2;
+               break;
+
+       case SIMPLE_HASH('T', 'c'):
+               /* virtual function covariant override thunk */
+               if (!cpp_demangle_push_str(ddata,
+                   "virtual function covariant override ", 36))
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == '\0')
+                       return (0);
+               if (!cpp_demangle_read_offset(ddata))
+                       return (0);
+               if (!cpp_demangle_read_offset(ddata))
+                       return (0);
+               return (cpp_demangle_read_encoding(ddata));
+
+       case SIMPLE_HASH('T', 'D'):
+               /* typeinfo common proxy */
+               break;
+
+       case SIMPLE_HASH('T', 'h'):
+               /* virtual function non-virtual override thunk */
+               if (cpp_demangle_push_str(ddata,
+                   "virtual function non-virtual override ", 38) == 0)
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == '\0')
+                       return (0);
+               if (!cpp_demangle_read_nv_offset(ddata))
+                       return (0);
+               return (cpp_demangle_read_encoding(ddata));
+
+       case SIMPLE_HASH('T', 'I'):
+               /* typeinfo structure */
+               /* FALLTHROUGH */
+       case SIMPLE_HASH('T', 'S'):
+               /* RTTI name (NTBS) */
+               if (!cpp_demangle_push_str(ddata, "typeinfo for ", 14))
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == '\0')
+                       return (0);
+               return (cpp_demangle_read_type(ddata, 1));
+
+       case SIMPLE_HASH('T', 'T'):
+               /* VTT table */
+               if (!cpp_demangle_push_str(ddata, "VTT for ", 8))
+                       return (0);
+               ddata->cur += 2;
+               return (cpp_demangle_read_type(ddata, 1));
+
+       case SIMPLE_HASH('T', 'v'):
+               /* virtual function virtual override thunk */
+               if (!cpp_demangle_push_str(ddata,
+                   "virtual function virtual override ", 34))
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == '\0')
+                       return (0);
+               if (!cpp_demangle_read_v_offset(ddata))
+                       return (0);
+               return (cpp_demangle_read_encoding(ddata));
+
+       case SIMPLE_HASH('T', 'V'):
+               /* virtual table */
+               if (!cpp_demangle_push_str(ddata, "vtable for ", 12))
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == '\0')
+                       return (0);
+               return (cpp_demangle_read_type(ddata, 1));
+       };
+
+       return (cpp_demangle_read_name(ddata));
+}
+
+static int
+cpp_demangle_read_local_name(struct cpp_demangle_data *ddata)
+{
+       size_t limit;
+
+       if (ddata == NULL)
+               return (0);
+       if (*(++ddata->cur) == '\0')
+               return (0);
+       if (!cpp_demangle_read_encoding(ddata))
+               return (0);
+
+       limit = 0;
+       for (;;) {
+               if (!cpp_demangle_read_type(ddata, 1))
+                       return (0);
+               if (*ddata->cur == 'E')
+                       break;
+               if (limit++ > CPP_DEMANGLE_TRY_LIMIT)
+                       return (0);
+       }
+       if (*(++ddata->cur) == '\0')
+               return (0);
+       if (ddata->paren == true) {
+               if (!cpp_demangle_push_str(ddata, ")", 1))
+                       return (0);
+               ddata->paren = false;
+       }
+       if (*ddata->cur == 's')
+               ++ddata->cur;
+       else {
+               if (!cpp_demangle_push_str(ddata, "::", 2))
+                       return (0);
+               if (!cpp_demangle_read_name(ddata))
+                       return (0);
+       }
+       if (*ddata->cur == '_') {
+               ++ddata->cur;
+               while (ELFTC_ISDIGIT(*ddata->cur) != 0)
+                       ++ddata->cur;
+       }
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_name(struct cpp_demangle_data *ddata)
+{
+       struct vector_str *output, v;
+       size_t p_idx, subst_str_len;
+       int rtn;
+       char *subst_str;
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output;
+
+       subst_str = NULL;
+
+       switch (*ddata->cur) {
+       case 'S':
+               return (cpp_demangle_read_subst(ddata));
+       case 'N':
+               return (cpp_demangle_read_nested_name(ddata));
+       case 'Z':
+               return (cpp_demangle_read_local_name(ddata));
+       };
+
+       if (!vector_str_init(&v))
+               return (0);
+
+       p_idx = output->size;
+       rtn = 0;
+       if (!cpp_demangle_read_uqname(ddata))
+               goto clean;
+       if ((subst_str = vector_str_substr(output, p_idx, output->size - 1,
+           &subst_str_len)) == NULL)
+               goto clean;
+       if (subst_str_len > 8 && strstr(subst_str, "operator") != NULL) {
+               rtn = 1;
+               goto clean;
+       }
+       if (!vector_str_push(&v, subst_str, subst_str_len))
+               goto clean;
+       if (!cpp_demangle_push_subst_v(ddata, &v))
+               goto clean;
+
+       if (*ddata->cur == 'I') {
+               p_idx = output->size;
+               if (!cpp_demangle_read_tmpl_args(ddata))
+                       goto clean;
+               free(subst_str);
+               if ((subst_str = vector_str_substr(output, p_idx,
+                   output->size - 1, &subst_str_len)) == NULL)
+                       goto clean;
+               if (!vector_str_push(&v, subst_str, subst_str_len))
+                       goto clean;
+               if (!cpp_demangle_push_subst_v(ddata, &v))
+                       goto clean;
+       }
+
+       rtn = 1;
+
+clean:
+       free(subst_str);
+       vector_str_dest(&v);
+
+       return (rtn);
+}
+
+static int
+cpp_demangle_read_nested_name(struct cpp_demangle_data *ddata)
+{
+       struct vector_str *output, v;
+       size_t limit, p_idx, subst_str_len;
+       int rtn;
+       char *subst_str;
+
+       if (ddata == NULL || *ddata->cur != 'N')
+               return (0);
+       if (*(++ddata->cur) == '\0')
+               return (0);
+
+       while (*ddata->cur == 'r' || *ddata->cur == 'V' ||
+           *ddata->cur == 'K') {
+               switch (*ddata->cur) {
+               case 'r':
+                       ddata->mem_rst = true;
+                       break;
+               case 'V':
+                       ddata->mem_vat = true;
+                       break;
+               case 'K':
+                       ddata->mem_cst = true;
+                       break;
+               };
+               ++ddata->cur;
+       }
+
+       output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output;
+       if (!vector_str_init(&v))
+               return (0);
+
+       rtn = 0;
+       limit = 0;
+       for (;;) {
+               p_idx = output->size;
+               switch (*ddata->cur) {
+               case 'I':
+                       if (!cpp_demangle_read_tmpl_args(ddata))
+                               goto clean;
+                       break;
+               case 'S':
+                       if (!cpp_demangle_read_subst(ddata))
+                               goto clean;
+                       break;
+               case 'T':
+                       if (!cpp_demangle_read_tmpl_param(ddata))
+                               goto clean;
+                       break;
+               default:
+                       if (!cpp_demangle_read_uqname(ddata))
+                               goto clean;
+               };
+
+               if ((subst_str = vector_str_substr(output, p_idx,
+                   output->size - 1, &subst_str_len)) == NULL)
+                       goto clean;
+               if (!vector_str_push(&v, subst_str, subst_str_len)) {
+                       free(subst_str);
+                       goto clean;
+               }
+               free(subst_str);
+
+               if (!cpp_demangle_push_subst_v(ddata, &v))
+                       goto clean;
+               if (*ddata->cur == 'E')
+                       break;
+               else if (*ddata->cur != 'I' &&
+                   *ddata->cur != 'C' && *ddata->cur != 'D') {
+                       if (!cpp_demangle_push_str(ddata, "::", 2))
+                               goto clean;
+                       if (!vector_str_push(&v, "::", 2))
+                               goto clean;
+               }
+               if (limit++ > CPP_DEMANGLE_TRY_LIMIT)
+                       goto clean;
+       }
+
+       ++ddata->cur;
+       rtn = 1;
+
+clean:
+       vector_str_dest(&v);
+
+       return (rtn);
+}
+
+/*
+ * read number
+ * number ::= [n] <decimal>
+ */
+static int
+cpp_demangle_read_number(struct cpp_demangle_data *ddata, long *rtn)
+{
+       long len, negative_factor;
+
+       if (ddata == NULL || rtn == NULL)
+               return (0);
+
+       negative_factor = 1;
+       if (*ddata->cur == 'n') {
+               negative_factor = -1;
+
+               ++ddata->cur;
+       }
+       if (ELFTC_ISDIGIT(*ddata->cur) == 0)
+               return (0);
+
+       errno = 0;
+       if ((len = strtol(ddata->cur, (char **) NULL, 10)) == 0 &&
+           errno != 0)
+               return (0);
+
+       while (ELFTC_ISDIGIT(*ddata->cur) != 0)
+               ++ddata->cur;
+
+       assert(len >= 0);
+       assert(negative_factor == 1 || negative_factor == -1);
+
+       *rtn = len * negative_factor;
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_nv_offset(struct cpp_demangle_data *ddata)
+{
+
+       if (ddata == NULL)
+               return (0);
+
+       if (!cpp_demangle_push_str(ddata, "offset : ", 9))
+               return (0);
+
+       return (cpp_demangle_read_offset_number(ddata));
+}
+
+/* read offset, offset are nv-offset, v-offset */
+static int
+cpp_demangle_read_offset(struct cpp_demangle_data *ddata)
+{
+
+       if (ddata == NULL)
+               return (0);
+
+       if (*ddata->cur == 'h') {
+               ++ddata->cur;
+               return (cpp_demangle_read_nv_offset(ddata));
+       } else if (*ddata->cur == 'v') {
+               ++ddata->cur;
+               return (cpp_demangle_read_v_offset(ddata));
+       }
+
+       return (0);
+}
+
+static int
+cpp_demangle_read_offset_number(struct cpp_demangle_data *ddata)
+{
+       bool negative;
+       const char *start;
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       /* offset could be negative */
+       if (*ddata->cur == 'n') {
+               negative = true;
+               start = ddata->cur + 1;
+       } else {
+               negative = false;
+               start = ddata->cur;
+       }
+
+       while (*ddata->cur != '_')
+               ++ddata->cur;
+
+       if (negative && !cpp_demangle_push_str(ddata, "-", 1))
+               return (0);
+
+       assert(start != NULL);
+
+       if (!cpp_demangle_push_str(ddata, start, ddata->cur - start))
+               return (0);
+       if (!cpp_demangle_push_str(ddata, " ", 1))
+               return (0);
+
+       ++ddata->cur;
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *ddata)
+{
+       size_t class_type_len, i, idx, p_idx;
+       int p_func_type, rtn;
+       char *class_type;
+
+       if (ddata == NULL || *ddata->cur != 'M' || *(++ddata->cur) == '\0')
+               return (0);
+
+       p_idx = ddata->output.size;
+       if (!cpp_demangle_read_type(ddata, 0))
+               return (0);
+
+       if ((class_type = vector_str_substr(&ddata->output, p_idx,
+           ddata->output.size - 1, &class_type_len)) == NULL)
+               return (0);
+
+       rtn = 0;
+       idx = ddata->output.size;
+       for (i = p_idx; i < idx; ++i)
+               if (!vector_str_pop(&ddata->output))
+                       goto clean1;
+
+       if (!vector_read_cmd_push(&ddata->cmd, READ_PTRMEM))
+               goto clean1;
+
+       if (!vector_str_push(&ddata->class_type, class_type, class_type_len))
+               goto clean2;
+
+       p_func_type = ddata->func_type;
+       if (!cpp_demangle_read_type(ddata, 0))
+               goto clean3;
+
+       if (p_func_type == ddata->func_type) {
+               if (!cpp_demangle_push_str(ddata, " ", 1))
+                       goto clean3;
+               if (!cpp_demangle_push_str(ddata, class_type, class_type_len))
+                       goto clean3;
+               if (!cpp_demangle_push_str(ddata, "::*", 3))
+                       goto clean3;
+       }
+
+       rtn = 1;
+clean3:
+       if (!vector_str_pop(&ddata->class_type))
+               rtn = 0;
+clean2:
+       if (!vector_read_cmd_pop(&ddata->cmd))
+               rtn = 0;
+clean1:
+       free(class_type);
+
+       return (rtn);
+}
+
+/* read source-name, source-name is <len> <ID> */
+static int
+cpp_demangle_read_sname(struct cpp_demangle_data *ddata)
+{
+       long len;
+       int err;
+
+       if (ddata == NULL || cpp_demangle_read_number(ddata, &len) == 0 ||
+           len <= 0)
+               return (0);
+
+       if (len == 12 && (memcmp("_GLOBAL__N_1", ddata->cur, 12) == 0))
+               err = cpp_demangle_push_str(ddata, "(anonymous namespace)", 21);
+       else
+               err = cpp_demangle_push_str(ddata, ddata->cur, len);
+       
+       if (err == 0)
+               return (0);
+
+       assert(ddata->output.size > 0);
+       if (vector_read_cmd_find(&ddata->cmd, READ_TMPL) == 0)
+               ddata->last_sname =
+                   ddata->output.container[ddata->output.size - 1];
+
+       ddata->cur += len;
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_subst(struct cpp_demangle_data *ddata)
+{
+       long nth;
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       /* abbreviations of the form Sx */
+       switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) {
+       case SIMPLE_HASH('S', 'a'):
+               /* std::allocator */
+               if (cpp_demangle_push_str(ddata, "std::allocator", 14) == 0)
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_subst_stdtmpl(ddata,
+                           "std::allocator", 14));
+               return (1);
+
+       case SIMPLE_HASH('S', 'b'):
+               /* std::basic_string */
+               if (!cpp_demangle_push_str(ddata, "std::basic_string", 17))
+                       return (0);
+               ddata->cur += 2;
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_subst_stdtmpl(ddata,
+                           "std::basic_string", 17));
+               return (1);
+
+       case SIMPLE_HASH('S', 'd'):
+               /* std::basic_iostream<char, std::char_traits<char> > */
+               if (!cpp_demangle_push_str(ddata, "std::iostream", 19))
+                       return (0);
+               ddata->last_sname = "iostream";
+               ddata->cur += 2;
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_subst_stdtmpl(ddata,
+                           "std::iostream", 19));
+               return (1);
+
+       case SIMPLE_HASH('S', 'i'):
+               /* std::basic_istream<char, std::char_traits<char> > */
+               if (!cpp_demangle_push_str(ddata, "std::istream", 18))
+                       return (0);
+               ddata->last_sname = "istream";
+               ddata->cur += 2;
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_subst_stdtmpl(ddata,
+                           "std::istream", 18));
+               return (1);
+
+       case SIMPLE_HASH('S', 'o'):
+               /* std::basic_ostream<char, std::char_traits<char> > */
+               if (!cpp_demangle_push_str(ddata, "std::ostream", 18))
+                       return (0);
+               ddata->last_sname = "istream";
+               ddata->cur += 2;
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_subst_stdtmpl(ddata,
+                           "std::ostream", 18));
+               return (1);
+
+       case SIMPLE_HASH('S', 's'):
+               /*
+                * std::basic_string<char, std::char_traits<char>,
+                * std::allocator<char> >
+                *
+                * a.k.a std::string
+                */
+               if (!cpp_demangle_push_str(ddata, "std::string", 11))
+                       return (0);
+               ddata->last_sname = "string";
+               ddata->cur += 2;
+               if (*ddata->cur == 'I')
+                       return (cpp_demangle_read_subst_stdtmpl(ddata,
+                           "std::string", 11));
+               return (1);
+
+       case SIMPLE_HASH('S', 't'):
+               /* std:: */
+               return (cpp_demangle_read_subst_std(ddata));
+       };
+
+       if (*(++ddata->cur) == '\0')
+               return (0);
+
+       /* substitution */
+       if (*ddata->cur == '_')
+               return (cpp_demangle_get_subst(ddata, 0));
+       else {
+               errno = 0;
+               /* substitution number is base 36 */
+               if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 &&
+                   errno != 0)
+                       return (0);
+
+               /* first was '_', so increase one */
+               ++nth;
+
+               while (*ddata->cur != '_')
+                       ++ddata->cur;
+
+               assert(nth > 0);
+
+               return (cpp_demangle_get_subst(ddata, nth));
+       }
+
+       /* NOTREACHED */
+       return (0);
+}
+
+static int
+cpp_demangle_read_subst_std(struct cpp_demangle_data *ddata)
+{
+       struct vector_str *output, v;
+       size_t p_idx, subst_str_len;
+       int rtn;
+       char *subst_str;
+
+       if (ddata == NULL)
+               return (0);
+
+       if (!vector_str_init(&v))
+               return (0);
+
+       subst_str = NULL;
+       rtn = 0;
+       if (!cpp_demangle_push_str(ddata, "std::", 5))
+               goto clean;
+
+       if (!vector_str_push(&v, "std::", 5))
+               goto clean;
+
+       ddata->cur += 2;
+
+       output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output;
+
+       p_idx = output->size;
+       if (!cpp_demangle_read_uqname(ddata))
+               goto clean;
+
+       if ((subst_str = vector_str_substr(output, p_idx, output->size - 1,
+           &subst_str_len)) == NULL)
+               goto clean;
+
+       if (!vector_str_push(&v, subst_str, subst_str_len))
+               goto clean;
+
+       if (!cpp_demangle_push_subst_v(ddata, &v))
+               goto clean;
+
+       if (*ddata->cur == 'I') {
+               p_idx = output->size;
+               if (!cpp_demangle_read_tmpl_args(ddata))
+                       goto clean;
+               free(subst_str);
+               if ((subst_str = vector_str_substr(output, p_idx,
+                   output->size - 1, &subst_str_len)) == NULL)
+                       goto clean;
+               if (!vector_str_push(&v, subst_str, subst_str_len))
+                       goto clean;
+               if (!cpp_demangle_push_subst_v(ddata, &v))
+                       goto clean;
+       }
+
+       rtn = 1;
+clean:
+       free(subst_str);
+       vector_str_dest(&v);
+
+       return (rtn);
+}
+
+static int
+cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *ddata,
+    const char *str, size_t len)
+{
+       struct vector_str *output;
+       size_t p_idx, substr_len;
+       int rtn;
+       char *subst_str, *substr;
+
+       if (ddata == NULL || str == NULL || len == 0)
+               return (0);
+
+       output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output;
+
+       p_idx = output->size;
+       substr = NULL;
+       subst_str = NULL;
+
+       if (!cpp_demangle_read_tmpl_args(ddata))
+               return (0);
+       if ((substr = vector_str_substr(output, p_idx, output->size - 1,
+           &substr_len)) == NULL)
+               return (0);
+
+       rtn = 0;
+       if ((subst_str = malloc(sizeof(char) * (substr_len + len + 1))) ==
+           NULL)
+               goto clean;
+
+       memcpy(subst_str, str, len);
+       memcpy(subst_str + len, substr, substr_len);
+       subst_str[substr_len + len] = '\0';
+
+       if (!cpp_demangle_push_subst(ddata, subst_str, substr_len + len))
+               goto clean;
+
+       rtn = 1;
+clean:
+       free(subst_str);
+       free(substr);
+
+       return (rtn);
+}
+
+static int
+cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *ddata)
+{
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       switch (*ddata->cur) {
+       case 'L':
+               return (cpp_demangle_read_expr_primary(ddata));
+       case 'X':
+               return (cpp_demangle_read_expression(ddata));
+       };
+
+       return (cpp_demangle_read_type(ddata, 0));
+}
+
+static int
+cpp_demangle_read_tmpl_args(struct cpp_demangle_data *ddata)
+{
+       struct vector_str *v;
+       size_t arg_len, idx, limit, size;
+       char *arg;
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       ++ddata->cur;
+
+       if (!vector_read_cmd_push(&ddata->cmd, READ_TMPL))
+               return (0);
+
+       if (!cpp_demangle_push_str(ddata, "<", 1))
+               return (0);
+
+       limit = 0;
+       v = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output;
+       for (;;) {
+               idx = v->size;
+               if (!cpp_demangle_read_tmpl_arg(ddata))
+                       return (0);
+               if ((arg = vector_str_substr(v, idx, v->size - 1, &arg_len)) ==
+                   NULL)
+                       return (0);
+               if (!vector_str_find(&ddata->tmpl, arg, arg_len) &&
+                   !vector_str_push(&ddata->tmpl, arg, arg_len)) {
+                       free(arg);
+                       return (0);
+               }
+
+               free(arg);
+
+               if (*ddata->cur == 'E') {
+                       ++ddata->cur;
+                       size = v->size;
+                       assert(size > 0);
+                       if (!strncmp(v->container[size - 1], ">", 1)) {
+                               if (!cpp_demangle_push_str(ddata, " >", 2))
+                                       return (0);
+                       } else if (!cpp_demangle_push_str(ddata, ">", 1))
+                               return (0);
+                       break;
+               } else if (*ddata->cur != 'I' &&
+                   !cpp_demangle_push_str(ddata, ", ", 2))
+                       return (0);
+
+               if (limit++ > CPP_DEMANGLE_TRY_LIMIT)
+                       return (0);
+       }
+
+       return (vector_read_cmd_pop(&ddata->cmd));
+}
+
+/*
+ * Read template parameter that forms in 'T[number]_'.
+ * This function much like to read_subst but only for types.
+ */
+static int
+cpp_demangle_read_tmpl_param(struct cpp_demangle_data *ddata)
+{
+       long nth;
+
+       if (ddata == NULL || *ddata->cur != 'T')
+               return (0);
+
+       ++ddata->cur;
+
+       if (*ddata->cur == '_')
+               return (cpp_demangle_get_tmpl_param(ddata, 0));
+       else {
+
+               errno = 0;
+               if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 &&
+                   errno != 0)
+                       return (0);
+
+               /* T_ is first */
+               ++nth;
+
+               while (*ddata->cur != '_')
+                       ++ddata->cur;
+
+               assert(nth > 0);
+
+               return (cpp_demangle_get_tmpl_param(ddata, nth));
+       }
+
+       /* NOTREACHED */
+       return (0);
+}
+
+static int
+cpp_demangle_read_type(struct cpp_demangle_data *ddata, int delimit)
+{
+       struct vector_type_qualifier v;
+       struct vector_str *output;
+       size_t p_idx, type_str_len;
+       int extern_c, is_builtin;
+       long len;
+       char *type_str;
+
+       if (ddata == NULL)
+               return (0);
+
+       output = &ddata->output;
+       if (ddata->output.size > 0 && !strncmp(ddata->output.container[ddata->output.size - 1], ">", 1)) {
+               ddata->push_head++;
+               output = &ddata->output_tmp;
+       } else if (delimit == 1) {
+               if (ddata->paren == false) {
+                       if (!cpp_demangle_push_str(ddata, "(", 1))
+                               return (0);
+                       if (ddata->output.size < 2)
+                               return (0);
+                       ddata->paren = true;
+                       ddata->pfirst = true;
+                       /* Need pop function name */
+                       if (ddata->subst.size == 1 &&
+                           !vector_str_pop(&ddata->subst))
+                               return (0);
+               }
+
+               if (ddata->pfirst)
+                       ddata->pfirst = false;
+               else if (*ddata->cur != 'I' &&
+                   !cpp_demangle_push_str(ddata, ", ", 2))
+                       return (0);
+       }
+
+       assert(output != NULL);
+       /*
+        * [r, V, K] [P, R, C, G, U] builtin, function, class-enum, array
+        * pointer-to-member, template-param, template-template-param, subst
+        */
+
+       if (!vector_type_qualifier_init(&v))
+               return (0);
+
+       extern_c = 0;
+       is_builtin = 1;
+       p_idx = output->size;
+       type_str = NULL;
+again:
+       /* builtin type */
+       switch (*ddata->cur) {
+       case 'a':
+               /* signed char */
+               if (!cpp_demangle_push_str(ddata, "signed char", 11))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'A':
+               /* array type */
+               if (!cpp_demangle_read_array(ddata))
+                       goto clean;
+               is_builtin = 0;
+               goto rtn;
+
+       case 'b':
+               /* bool */
+               if (!cpp_demangle_push_str(ddata, "bool", 4))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'C':
+               /* complex pair */
+               if (!vector_type_qualifier_push(&v, TYPE_CMX))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 'c':
+               /* char */
+               if (!cpp_demangle_push_str(ddata, "char", 4))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'd':
+               /* double */
+               if (!cpp_demangle_push_str(ddata, "double", 6))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'e':
+               /* long double */
+               if (!cpp_demangle_push_str(ddata, "long double", 11))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'f':
+               /* float */
+               if (!cpp_demangle_push_str(ddata, "float", 5))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'F':
+               /* function */
+               if (!cpp_demangle_read_function(ddata, &extern_c, &v))
+                       goto clean;
+               is_builtin = 0;
+               goto rtn;
+
+       case 'g':
+               /* __float128 */
+               if (!cpp_demangle_push_str(ddata, "__float128", 10))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'G':
+               /* imaginary */
+               if (!vector_type_qualifier_push(&v, TYPE_IMG))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 'h':
+               /* unsigned char */
+               if (!cpp_demangle_push_str(ddata, "unsigned char", 13))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'i':
+               /* int */
+               if (!cpp_demangle_push_str(ddata, "int", 3))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'j':
+               /* unsigned int */
+               if (!cpp_demangle_push_str(ddata, "unsigned int", 12))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'K':
+               /* const */
+               if (!vector_type_qualifier_push(&v, TYPE_CST))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 'l':
+               /* long */
+               if (!cpp_demangle_push_str(ddata, "long", 4))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'm':
+               /* unsigned long */
+               if (!cpp_demangle_push_str(ddata, "unsigned long", 13))
+                       goto clean;
+
+               ++ddata->cur;
+
+               goto rtn;
+       case 'M':
+               /* pointer to member */
+               if (!cpp_demangle_read_pointer_to_member(ddata))
+                       goto clean;
+               is_builtin = 0;
+               goto rtn;
+
+       case 'n':
+               /* __int128 */
+               if (!cpp_demangle_push_str(ddata, "__int128", 8))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'o':
+               /* unsigned __int128 */
+               if (!cpp_demangle_push_str(ddata, "unsigned _;int128", 17))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'P':
+               /* pointer */
+               if (!vector_type_qualifier_push(&v, TYPE_PTR))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 'r':
+               /* restrict */
+               if (!vector_type_qualifier_push(&v, TYPE_RST))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 'R':
+               /* reference */
+               if (!vector_type_qualifier_push(&v, TYPE_REF))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 's':
+               /* short, local string */
+               if (!cpp_demangle_push_str(ddata, "short", 5))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'S':
+               /* substitution */
+               if (!cpp_demangle_read_subst(ddata))
+                       goto clean;
+               is_builtin = 0;
+               goto rtn;
+
+       case 't':
+               /* unsigned short */
+               if (!cpp_demangle_push_str(ddata, "unsigned short", 14))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'T':
+               /* template parameter */
+               if (!cpp_demangle_read_tmpl_param(ddata))
+                       goto clean;
+               is_builtin = 0;
+               goto rtn;
+
+       case 'u':
+               /* vendor extended builtin */
+               ++ddata->cur;
+               if (!cpp_demangle_read_sname(ddata))
+                       goto clean;
+               is_builtin = 0;
+               goto rtn;
+
+       case 'U':
+               /* vendor extended type qualifier */
+               if (!cpp_demangle_read_number(ddata, &len))
+                       goto clean;
+               if (len <= 0)
+                       goto clean;
+               if (!vector_str_push(&v.ext_name, ddata->cur, len))
+                       return (0);
+               ddata->cur += len;
+               goto again;
+
+       case 'v':
+               /* void */
+               if (!cpp_demangle_push_str(ddata, "void", 4))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'V':
+               /* volatile */
+               if (!vector_type_qualifier_push(&v, TYPE_VAT))
+                       goto clean;
+               ++ddata->cur;
+               goto again;
+
+       case 'w':
+               /* wchar_t */
+               if (!cpp_demangle_push_str(ddata, "wchar_t", 6))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'x':
+               /* long long */
+               if (!cpp_demangle_push_str(ddata, "long long", 9))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'y':
+               /* unsigned long long */
+               if (!cpp_demangle_push_str(ddata, "unsigned long long", 18))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+
+       case 'z':
+               /* ellipsis */
+               if (!cpp_demangle_push_str(ddata, "ellipsis", 8))
+                       goto clean;
+               ++ddata->cur;
+               goto rtn;
+       };
+
+       if (!cpp_demangle_read_name(ddata))
+               goto clean;
+
+       is_builtin = 0;
+rtn:
+       if ((type_str = vector_str_substr(output, p_idx, output->size - 1,
+           &type_str_len)) == NULL)
+               goto clean;
+
+       if (is_builtin == 0) {
+               if (!vector_str_find(&ddata->subst, type_str, type_str_len) &&
+                   !vector_str_push(&ddata->subst, type_str, type_str_len))
+                       goto clean;
+       }
+
+       if (!cpp_demangle_push_type_qualifier(ddata, &v, type_str))
+               goto clean;
+
+       free(type_str);
+       vector_type_qualifier_dest(&v);
+
+       if (ddata->push_head > 0) {
+               if (*ddata->cur == 'I' && cpp_demangle_read_tmpl_args(ddata)
+                   == 0)
+                       return (0);
+
+               if (--ddata->push_head > 0)
+                       return (1);
+
+               if (!vector_str_push(&ddata->output_tmp, " ", 1))
+                       return (0);
+
+               if (!vector_str_push_vector_head(&ddata->output,
+                   &ddata->output_tmp))
+                       return (0);
+
+               vector_str_dest(&ddata->output_tmp);
+               if (!vector_str_init(&ddata->output_tmp))
+                       return (0);
+
+               if (!cpp_demangle_push_str(ddata, "(", 1))
+                       return (0);
+
+               ddata->paren = true;
+               ddata->pfirst = true;
+       }
+
+       return (1);
+clean:
+       free(type_str);
+       vector_type_qualifier_dest(&v);
+
+       return (0);
+}
+
+/*
+ * read unqualified-name, unqualified name are operator-name, ctor-dtor-name,
+ * source-name
+ */
+static int
+cpp_demangle_read_uqname(struct cpp_demangle_data *ddata)
+{
+       size_t len;
+
+       if (ddata == NULL || *ddata->cur == '\0')
+               return (0);
+
+       /* operator name */
+       switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) {
+       case SIMPLE_HASH('a', 'a'):
+               /* operator && */
+               if (!cpp_demangle_push_str(ddata, "operator&&", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('a', 'd'):
+               /* operator & (unary) */
+               if (!cpp_demangle_push_str(ddata, "operator&", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('a', 'n'):
+               /* operator & */
+               if (!cpp_demangle_push_str(ddata, "operator&", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('a', 'N'):
+               /* operator &= */
+               if (!cpp_demangle_push_str(ddata, "operator&=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('a', 'S'):
+               /* operator = */
+               if (!cpp_demangle_push_str(ddata, "operator=", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('c', 'l'):
+               /* operator () */
+               if (!cpp_demangle_push_str(ddata, "operator()", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('c', 'm'):
+               /* operator , */
+               if (!cpp_demangle_push_str(ddata, "operator,", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('c', 'o'):
+               /* operator ~ */
+               if (!cpp_demangle_push_str(ddata, "operator~", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('c', 'v'):
+               /* operator (cast) */
+               if (!cpp_demangle_push_str(ddata, "operator(cast)", 14))
+                       return (0);
+               ddata->cur += 2;
+               return (cpp_demangle_read_type(ddata, 1));
+
+       case SIMPLE_HASH('d', 'a'):
+               /* operator delete [] */
+               if (!cpp_demangle_push_str(ddata, "operator delete []", 18))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('d', 'e'):
+               /* operator * (unary) */
+               if (!cpp_demangle_push_str(ddata, "operator*", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('d', 'l'):
+               /* operator delete */
+               if (!cpp_demangle_push_str(ddata, "operator delete", 15))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('d', 'v'):
+               /* operator / */
+               if (!cpp_demangle_push_str(ddata, "operator/", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('d', 'V'):
+               /* operator /= */
+               if (!cpp_demangle_push_str(ddata, "operator/=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('e', 'o'):
+               /* operator ^ */
+               if (!cpp_demangle_push_str(ddata, "operator^", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('e', 'O'):
+               /* operator ^= */
+               if (!cpp_demangle_push_str(ddata, "operator^=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('e', 'q'):
+               /* operator == */
+               if (!cpp_demangle_push_str(ddata, "operator==", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('g', 'e'):
+               /* operator >= */
+               if (!cpp_demangle_push_str(ddata, "operator>=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('g', 't'):
+               /* operator > */
+               if (!cpp_demangle_push_str(ddata, "operator>", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('i', 'x'):
+               /* operator [] */
+               if (!cpp_demangle_push_str(ddata, "operator[]", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('l', 'e'):
+               /* operator <= */
+               if (!cpp_demangle_push_str(ddata, "operator<=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('l', 's'):
+               /* operator << */
+               if (!cpp_demangle_push_str(ddata, "operator<<", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('l', 'S'):
+               /* operator <<= */
+               if (!cpp_demangle_push_str(ddata, "operator<<=", 11))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('l', 't'):
+               /* operator < */
+               if (!cpp_demangle_push_str(ddata, "operator<", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('m', 'i'):
+               /* operator - */
+               if (!cpp_demangle_push_str(ddata, "operator-", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('m', 'I'):
+               /* operator -= */
+               if (!cpp_demangle_push_str(ddata, "operator-=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('m', 'l'):
+               /* operator * */
+               if (!cpp_demangle_push_str(ddata, "operator*", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('m', 'L'):
+               /* operator *= */
+               if (!cpp_demangle_push_str(ddata, "operator*=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('m', 'm'):
+               /* operator -- */
+               if (!cpp_demangle_push_str(ddata, "operator--", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('n', 'a'):
+               /* operator new[] */
+               if (!cpp_demangle_push_str(ddata, "operator new []", 15))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('n', 'e'):
+               /* operator != */
+               if (!cpp_demangle_push_str(ddata, "operator!=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('n', 'g'):
+               /* operator - (unary) */
+               if (!cpp_demangle_push_str(ddata, "operator-", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('n', 't'):
+               /* operator ! */
+               if (!cpp_demangle_push_str(ddata, "operator!", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('n', 'w'):
+               /* operator new */
+               if (!cpp_demangle_push_str(ddata, "operator new", 12))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('o', 'o'):
+               /* operator || */
+               if (!cpp_demangle_push_str(ddata, "operator||", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('o', 'r'):
+               /* operator | */
+               if (!cpp_demangle_push_str(ddata, "operator|", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('o', 'R'):
+               /* operator |= */
+               if (!cpp_demangle_push_str(ddata, "operator|=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('p', 'l'):
+               /* operator + */
+               if (!cpp_demangle_push_str(ddata, "operator+", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('p', 'L'):
+               /* operator += */
+               if (!cpp_demangle_push_str(ddata, "operator+=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('p', 'm'):
+               /* operator ->* */
+               if (!cpp_demangle_push_str(ddata, "operator->*", 11))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('p', 'p'):
+               /* operator ++ */
+               if (!cpp_demangle_push_str(ddata, "operator++", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('p', 's'):
+               /* operator + (unary) */
+               if (!cpp_demangle_push_str(ddata, "operator+", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('p', 't'):
+               /* operator -> */
+               if (!cpp_demangle_push_str(ddata, "operator->", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('q', 'u'):
+               /* operator ? */
+               if (!cpp_demangle_push_str(ddata, "operator?", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('r', 'm'):
+               /* operator % */
+               if (!cpp_demangle_push_str(ddata, "operator%", 9))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('r', 'M'):
+               /* operator %= */
+               if (!cpp_demangle_push_str(ddata, "operator%=", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('r', 's'):
+               /* operator >> */
+               if (!cpp_demangle_push_str(ddata, "operator>>", 10))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('r', 'S'):
+               /* operator >>= */
+               if (!cpp_demangle_push_str(ddata, "operator>>=", 11))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('r', 'z'):
+               /* operator sizeof */
+               if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('s', 'r'):
+               /* scope resolution operator */
+               if (!cpp_demangle_push_str(ddata, "scope resolution operator ",
+                   26))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+
+       case SIMPLE_HASH('s', 'v'):
+               /* operator sizeof */
+               if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16))
+                       return (0);
+               ddata->cur += 2;
+               return (1);
+       };
+
+       /* vendor extened operator */
+       if (*ddata->cur == 'v' && ELFTC_ISDIGIT(*(ddata->cur + 1))) {
+               if (!cpp_demangle_push_str(ddata, "vendor extened operator ",
+                   24))
+                       return (0);
+               if (!cpp_demangle_push_str(ddata, ddata->cur + 1, 1))
+                       return (0);
+               ddata->cur += 2;
+               return (cpp_demangle_read_sname(ddata));
+       }
+
+       /* ctor-dtor-name */
+       switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) {
+       case SIMPLE_HASH('C', '1'):
+               /* FALLTHROUGH */
+       case SIMPLE_HASH('C', '2'):
+               /* FALLTHROUGH */
+       case SIMPLE_HASH('C', '3'):
+               if (ddata->last_sname == NULL)
+                       return (0);
+               if ((len = strlen(ddata->last_sname)) == 0)
+                       return (0);
+               if (!cpp_demangle_push_str(ddata, "::", 2))
+                       return (0);
+               if (!cpp_demangle_push_str(ddata, ddata->last_sname, len))
+                       return (0);
+               ddata->cur +=2;
+               return (1);
+
+       case SIMPLE_HASH('D', '0'):
+               /* FALLTHROUGH */
+       case SIMPLE_HASH('D', '1'):
+               /* FALLTHROUGH */
+       case SIMPLE_HASH('D', '2'):
+               if (ddata->last_sname == NULL)
+                       return (0);
+               if ((len = strlen(ddata->last_sname)) == 0)
+                       return (0);
+               if (!cpp_demangle_push_str(ddata, "::~", 3))
+                       return (0);
+               if (!cpp_demangle_push_str(ddata, ddata->last_sname, len))
+                       return (0);
+               ddata->cur +=2;
+               return (1);
+       };
+
+       /* source name */
+       if (ELFTC_ISDIGIT(*ddata->cur) != 0)
+               return (cpp_demangle_read_sname(ddata));
+
+       /* local source name */ 
+       if (*ddata->cur == 'L') 
+               return (cpp_demangle_local_source_name(ddata)); 
+       return (1); 
+} 
+/* 
+ * Read local source name. 
+ * 
+ * References: 
+ *   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 
+ *   http://gcc.gnu.org/viewcvs?view=rev&revision=124467 
+ */ 
+static int 
+cpp_demangle_local_source_name(struct cpp_demangle_data *ddata) 
+{ 
+       /* L */ 
+       if (ddata == NULL || *ddata->cur != 'L') 
+               return (0); 
+       ++ddata->cur; 
+
+       /* source name */ 
+       if (!cpp_demangle_read_sname(ddata)) 
+               return (0); 
+
+       /* discriminator */ 
+       if (*ddata->cur == '_') { 
+               ++ddata->cur; 
+               while (ELFTC_ISDIGIT(*ddata->cur) != 0) 
+                       ++ddata->cur; 
+       } 
+
+       return (1);
+}
+
+static int
+cpp_demangle_read_v_offset(struct cpp_demangle_data *ddata)
+{
+
+       if (ddata == NULL)
+               return (0);
+
+       if (!cpp_demangle_push_str(ddata, "offset : ", 9))
+               return (0);
+
+       if (!cpp_demangle_read_offset_number(ddata))
+               return (0);
+
+       if (!cpp_demangle_push_str(ddata, "virtual offset : ", 17))
+               return (0);
+
+       return (!cpp_demangle_read_offset_number(ddata));
+}
+
+/*
+ * Decode floating point representation to string
+ * Return new allocated string or NULL
+ *
+ * Todo
+ * Replace these functions to macro.
+ */
+static char *
+decode_fp_to_double(const char *p, size_t len)
+{
+       double f;
+       size_t rtn_len, limit, i;
+       int byte;
+       char *rtn;
+
+       if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(double))
+               return (NULL);
+
+       memset(&f, 0, sizeof(double));
+
+       for (i = 0; i < len / 2; ++i) {
+               byte = hex_to_dec(p[len - i * 2 - 1]) +
+                   hex_to_dec(p[len - i * 2 - 2]) * 16;
+
+               if (byte < 0 || byte > 255)
+                       return (NULL);
+
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+               ((unsigned char *)&f)[i] = (unsigned char)(byte);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               ((unsigned char *)&f)[sizeof(double) - i - 1] =
+                   (unsigned char)(byte);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+       }
+
+       rtn_len = 64;
+       limit = 0;
+again:
+       if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL)
+               return (NULL);
+
+       if (snprintf(rtn, rtn_len, "%fld", f) >= (int)rtn_len) {
+               free(rtn);
+               if (limit++ > FLOAT_SPRINTF_TRY_LIMIT)
+                       return (NULL);
+               rtn_len *= BUFFER_GROWFACTOR;
+               goto again;
+       }
+
+       return rtn;
+}
+
+static char *
+decode_fp_to_float(const char *p, size_t len)
+{
+       size_t i, rtn_len, limit;
+       float f;
+       int byte;
+       char *rtn;
+
+       if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(float))
+               return (NULL);
+
+       memset(&f, 0, sizeof(float));
+
+       for (i = 0; i < len / 2; ++i) {
+               byte = hex_to_dec(p[len - i * 2 - 1]) +
+                   hex_to_dec(p[len - i * 2 - 2]) * 16;
+               if (byte < 0 || byte > 255)
+                       return (NULL);
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+               ((unsigned char *)&f)[i] = (unsigned char)(byte);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               ((unsigned char *)&f)[sizeof(float) - i - 1] =
+                   (unsigned char)(byte);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+       }
+
+       rtn_len = 64;
+       limit = 0;
+again:
+       if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL)
+               return (NULL);
+
+       if (snprintf(rtn, rtn_len, "%ff", f) >= (int)rtn_len) {
+               free(rtn);
+               if (limit++ > FLOAT_SPRINTF_TRY_LIMIT)
+                       return (NULL);
+               rtn_len *= BUFFER_GROWFACTOR;
+               goto again;
+       }
+
+       return rtn;
+}
+
+static char *
+decode_fp_to_float128(const char *p, size_t len)
+{
+       long double f;
+       size_t rtn_len, limit, i;
+       int byte;
+       unsigned char buf[FLOAT_QUADRUPLE_BYTES];
+       char *rtn;
+
+       switch(sizeof(long double)) {
+       case FLOAT_QUADRUPLE_BYTES:
+               return (decode_fp_to_long_double(p, len));
+       case FLOAT_EXTENED_BYTES:
+               if (p == NULL || len == 0 || len % 2 != 0 ||
+                   len / 2 > FLOAT_QUADRUPLE_BYTES)
+                       return (NULL);
+
+               memset(buf, 0, FLOAT_QUADRUPLE_BYTES);
+
+               for (i = 0; i < len / 2; ++i) {
+                       byte = hex_to_dec(p[len - i * 2 - 1]) +
+                           hex_to_dec(p[len - i * 2 - 2]) * 16;
+                       if (byte < 0 || byte > 255)
+                               return (NULL);
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+                       buf[i] = (unsigned char)(byte);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+                       buf[FLOAT_QUADRUPLE_BYTES - i -1] =
+                           (unsigned char)(byte);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               }
+               memset(&f, 0, FLOAT_EXTENED_BYTES);
+
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+               memcpy(&f, buf, FLOAT_EXTENED_BYTES);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               memcpy(&f, buf + 6, FLOAT_EXTENED_BYTES);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+
+               rtn_len = 256;
+               limit = 0;
+again:
+               if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL)
+                       return (NULL);
+
+               if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) {
+                       free(rtn);
+                       if (limit++ > FLOAT_SPRINTF_TRY_LIMIT)
+                               return (NULL);
+                       rtn_len *= BUFFER_GROWFACTOR;
+                       goto again;
+               }
+
+               return (rtn);
+       default:
+               return (NULL);
+       }
+}
+
+static char *
+decode_fp_to_float80(const char *p, size_t len)
+{
+       long double f;
+       size_t rtn_len, limit, i;
+       int byte;
+       unsigned char buf[FLOAT_EXTENED_BYTES];
+       char *rtn;
+
+       switch(sizeof(long double)) {
+       case FLOAT_QUADRUPLE_BYTES:
+               if (p == NULL || len == 0 || len % 2 != 0 ||
+                   len / 2 > FLOAT_EXTENED_BYTES)
+                       return (NULL);
+
+               memset(buf, 0, FLOAT_EXTENED_BYTES);
+
+               for (i = 0; i < len / 2; ++i) {
+                       byte = hex_to_dec(p[len - i * 2 - 1]) +
+                           hex_to_dec(p[len - i * 2 - 2]) * 16;
+
+                       if (byte < 0 || byte > 255)
+                               return (NULL);
+
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+                       buf[i] = (unsigned char)(byte);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+                       buf[FLOAT_EXTENED_BYTES - i -1] =
+                           (unsigned char)(byte);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               }
+
+               memset(&f, 0, FLOAT_QUADRUPLE_BYTES);
+
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+               memcpy(&f, buf, FLOAT_EXTENED_BYTES);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               memcpy((unsigned char *)(&f) + 6, buf, FLOAT_EXTENED_BYTES);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+
+               rtn_len = 256;
+               limit = 0;
+again:
+               if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL)
+                       return (NULL);
+
+               if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) {
+                       free(rtn);
+                       if (limit++ > FLOAT_SPRINTF_TRY_LIMIT)
+                               return (NULL);
+                       rtn_len *= BUFFER_GROWFACTOR;
+                       goto again;
+               }
+
+               return (rtn);
+       case FLOAT_EXTENED_BYTES:
+               return (decode_fp_to_long_double(p, len));
+       default:
+               return (NULL);
+       }
+}
+
+static char *
+decode_fp_to_long_double(const char *p, size_t len)
+{
+       long double f;
+       size_t rtn_len, limit, i;
+       int byte;
+       char *rtn;
+
+       if (p == NULL || len == 0 || len % 2 != 0 ||
+           len / 2 > sizeof(long double))
+               return (NULL);
+
+       memset(&f, 0, sizeof(long double));
+
+       for (i = 0; i < len / 2; ++i) {
+               byte = hex_to_dec(p[len - i * 2 - 1]) +
+                   hex_to_dec(p[len - i * 2 - 2]) * 16;
+
+               if (byte < 0 || byte > 255)
+                       return (NULL);
+
+#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN
+               ((unsigned char *)&f)[i] = (unsigned char)(byte);
+#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+               ((unsigned char *)&f)[sizeof(long double) - i - 1] =
+                   (unsigned char)(byte);
+#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */
+       }
+
+       rtn_len = 256;
+       limit = 0;
+again:
+       if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL)
+               return (NULL);
+
+       if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) {
+               free(rtn);
+               if (limit++ > FLOAT_SPRINTF_TRY_LIMIT)
+                       return (NULL);
+               rtn_len *= BUFFER_GROWFACTOR;
+               goto again;
+       }
+
+       return (rtn);
+}
+
+/* Simple hex to integer function used by decode_to_* function. */
+static int
+hex_to_dec(char c)
+{
+
+       switch (c) {
+       case '0':
+               return (0);
+       case '1':
+               return (1);
+       case '2':
+               return (2);
+       case '3':
+               return (3);
+       case '4':
+               return (4);
+       case '5':
+               return (5);
+       case '6':
+               return (6);
+       case '7':
+               return (7);
+       case '8':
+               return (8);
+       case '9':
+               return (9);
+       case 'a':
+               return (10);
+       case 'b':
+               return (11);
+       case 'c':
+               return (12);
+       case 'd':
+               return (13);
+       case 'e':
+               return (14);
+       case 'f':
+               return (15);
+       default:
+               return (-1);
+       };
+}
+
+static void
+vector_read_cmd_dest(struct vector_read_cmd *v)
+{
+
+       if (v == NULL)
+               return;
+
+       free(v->r_container);
+}
+
+/* return -1 at failed, 0 at not found, 1 at found. */
+static int
+vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst)
+{
+       size_t i;
+
+       if (v == NULL || dst == READ_FAIL)
+               return (-1);
+
+       for (i = 0; i < v->size; ++i)
+               if (v->r_container[i] == dst)
+                       return (1);
+
+       return (0);
+}
+
+static int
+vector_read_cmd_init(struct vector_read_cmd *v)
+{
+
+       if (v == NULL)
+               return (0);
+
+       v->size = 0;
+       v->capacity = VECTOR_DEF_CAPACITY;
+
+       if ((v->r_container = malloc(sizeof(enum read_cmd) * v->capacity))
+           == NULL)
+               return (0);
+
+       return (1);
+}
+
+static int
+vector_read_cmd_pop(struct vector_read_cmd *v)
+{
+
+       if (v == NULL || v->size == 0)
+               return (0);
+
+       --v->size;
+       v->r_container[v->size] = READ_FAIL;
+
+       return (1);
+}
+
+static int
+vector_read_cmd_push(struct vector_read_cmd *v, enum read_cmd cmd)
+{
+       enum read_cmd *tmp_r_ctn;
+       size_t tmp_cap;
+       size_t i;
+
+       if (v == NULL)
+               return (0);
+
+       if (v->size == v->capacity) {
+               tmp_cap = v->capacity * BUFFER_GROWFACTOR;
+               if ((tmp_r_ctn = malloc(sizeof(enum read_cmd) * tmp_cap))
+                   == NULL)
+                       return (0);
+               for (i = 0; i < v->size; ++i)
+                       tmp_r_ctn[i] = v->r_container[i];
+               free(v->r_container);
+               v->r_container = tmp_r_ctn;
+               v->capacity = tmp_cap;
+       }
+
+       v->r_container[v->size] = cmd;
+       ++v->size;
+
+       return (1);
+}
+
+static void
+vector_type_qualifier_dest(struct vector_type_qualifier *v)
+{
+
+       if (v == NULL)
+               return;
+
+       free(v->q_container);
+       vector_str_dest(&v->ext_name);
+}
+
+/* size, capacity, ext_name */
+static int
+vector_type_qualifier_init(struct vector_type_qualifier *v)
+{
+
+       if (v == NULL)
+               return (0);
+
+       v->size = 0;
+       v->capacity = VECTOR_DEF_CAPACITY;
+
+       if ((v->q_container = malloc(sizeof(enum type_qualifier) * v->capacity))
+           == NULL)
+               return (0);
+
+       assert(v->q_container != NULL);
+
+       if (vector_str_init(&v->ext_name) == false) {
+               free(v->q_container);
+               return (0);
+       }
+
+       return (1);
+}
+
+static int
+vector_type_qualifier_push(struct vector_type_qualifier *v,
+    enum type_qualifier t)
+{
+       enum type_qualifier *tmp_ctn;
+       size_t tmp_cap;
+       size_t i;
+
+       if (v == NULL)
+               return (0);
+
+       if (v->size == v->capacity) {
+               tmp_cap = v->capacity * BUFFER_GROWFACTOR;
+               if ((tmp_ctn = malloc(sizeof(enum type_qualifier) * tmp_cap))
+                   == NULL)
+                       return (0);
+               for (i = 0; i < v->size; ++i)
+                       tmp_ctn[i] = v->q_container[i];
+               free(v->q_container);
+               v->q_container = tmp_ctn;
+               v->capacity = tmp_cap;
+       }
+
+       v->q_container[v->size] = t;
+       ++v->size;
+
+       return (1);
+}
diff --git a/libbacktrace/mmap.c b/libbacktrace/mmap.c
new file mode 100644 (file)
index 0000000..a82c22b
--- /dev/null
@@ -0,0 +1,298 @@
+/* mmap.c -- Memory allocation with mmap.
+   Copyright (C) 2012-2014 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 <stdlib.h>
+#include <unistd.h>
+#include <sys/types.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 memory allocated by backtrace_alloc.  */
+
+static void
+backtrace_free_locked (struct backtrace_state *state, 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 = state->freelist;
+      p->size = size;
+      state->freelist = p;
+    }
+}
+
+/* Allocate memory like malloc.  */
+
+void *
+backtrace_alloc (struct backtrace_state *state,
+                size_t size, backtrace_error_callback error_callback,
+                void *data)
+{
+  void *ret;
+  int locked;
+  struct backtrace_freelist_struct **pp;
+  size_t pagesize;
+  size_t asksize;
+  void *page;
+
+  ret = NULL;
+
+  /* If we can acquire the lock, then see if there is space on the
+     free list.  If we can't acquire the lock, drop straight into
+     using mmap.  __sync_lock_test_and_set returns the old state of
+     the lock, so we have acquired it if it returns 0.  */
+
+  if (!state->threaded)
+    locked = 1;
+  else
+    locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0;
+
+  if (locked)
+    {
+      for (pp = &state->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 (state, (char *) p + size,
+                                      p->size - size);
+
+             ret = (void *) p;
+
+             break;
+           }
+       }
+
+      if (state->threaded)
+       __sync_lock_release (&state->lock_alloc);
+    }
+
+  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 (state, (char *) page + size, asksize - size,
+                           error_callback, data);
+
+         ret = page;
+       }
+    }
+
+  return ret;
+}
+
+/* Allocate memory like strdup. */
+
+char *
+backtrace_strdup (struct backtrace_state *state, const char *str,
+                  backtrace_error_callback error_callback,
+                  void *data)
+{
+   char *ret;
+
+   ret = NULL;
+
+   if (str)
+   {
+      size_t size;
+      void *mem;
+
+      size = strlen(str) + 1;
+      mem = backtrace_alloc (state, size, error_callback, data);
+      if (mem)
+      {
+         memcpy(mem, str, size);
+         ret = (char *)mem;
+      }
+   }
+   return ret;
+}
+
+/* Free memory allocated by backtrace_alloc.  */
+
+void
+backtrace_free (struct backtrace_state *state, void *addr, size_t size,
+               backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+               void *data ATTRIBUTE_UNUSED)
+{
+  int locked;
+
+  /* If we can acquire the lock, add the new space to the free list.
+     If we can't acquire the lock, just leak the memory.
+     __sync_lock_test_and_set returns the old state of the lock, so we
+     have acquired it if it returns 0.  */
+
+  if (!state->threaded)
+    locked = 1;
+  else
+    locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0;
+
+  if (locked)
+    {
+      backtrace_free_locked (state, addr, size);
+
+      if (state->threaded)
+       __sync_lock_release (&state->lock_alloc);
+    }
+}
+
+/* Grow VEC by SIZE bytes.  */
+
+void *
+backtrace_vector_grow (struct backtrace_state *state,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 (state, alc, error_callback, data);
+      if (base == NULL)
+       return NULL;
+      if (vec->base != NULL)
+       {
+         memcpy (base, vec->base, vec->size);
+         backtrace_free (state, 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_state *state ATTRIBUTE_UNUSED,
+  struct backtrace_vector *vec,
+  backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+  void *data ATTRIBUTE_UNUSED)
+{
+  void *ret;
+
+  ret = vec->base;
+  vec->base = (char *) vec->base + vec->size;
+  vec->size = 0;
+  return ret;
+}
+
+/* Release any extra space allocated for VEC.  */
+
+int
+backtrace_vector_release (struct backtrace_state *state,
+                         struct backtrace_vector *vec,
+                         backtrace_error_callback error_callback,
+                         void *data)
+{
+  size_t size;
+  size_t alc;
+  size_t aligned;
+
+  /* Make sure that the block that we free is aligned on an 8-byte
+     boundary.  */
+  size = vec->size;
+  alc = vec->alc;
+  aligned = (size + 7) & ~ (size_t) 7;
+  alc -= aligned - size;
+
+  backtrace_free (state, (char *) vec->base + aligned, alc,
+                 error_callback, data);
+  vec->alc = 0;
+  return 1;
+}
diff --git a/libbacktrace/mmapio.c b/libbacktrace/mmapio.c
new file mode 100644 (file)
index 0000000..72940b4
--- /dev/null
@@ -0,0 +1,100 @@
+/* mmapio.c -- File views using mmap.
+   Copyright (C) 2012-2014 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/mman.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* 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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                   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_state *state ATTRIBUTE_UNUSED,
+                       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);
+}
diff --git a/libbacktrace/nounwind.c b/libbacktrace/nounwind.c
new file mode 100644 (file)
index 0000000..0097966
--- /dev/null
@@ -0,0 +1,66 @@
+/* backtrace.c -- Entry point for stack backtrace library.
+   Copyright (C) 2012-2014 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_full (struct backtrace_state *state ATTRIBUTE_UNUSED,
+               int skip ATTRIBUTE_UNUSED,
+               backtrace_full_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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                 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;
+}
diff --git a/libbacktrace/posix.c b/libbacktrace/posix.c
new file mode 100644 (file)
index 0000000..dba9e52
--- /dev/null
@@ -0,0 +1,100 @@
+/* posix.c -- POSIX file I/O routines for the backtrace library.
+   Copyright (C) 2012-2014 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_BINARY
+#define O_BINARY 0
+#endif
+
+#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 *does_not_exist)
+{
+  int descriptor;
+
+  if (does_not_exist != NULL)
+    *does_not_exist = 0;
+
+  descriptor = open (filename, O_RDONLY | O_BINARY | O_CLOEXEC);
+  if (descriptor < 0)
+    {
+      if (does_not_exist != NULL && errno == ENOENT)
+       *does_not_exist = 1;
+      else
+       error_callback (data, filename, errno);
+      return -1;
+    }
+
+#ifdef HAVE_FCNTL
+  /* Set FD_CLOEXEC just in case the kernel does not support
+     O_CLOEXEC. It doesn't matter if this fails for some reason.
+     FIXME: At some point it should be safe to only do this if
+     O_CLOEXEC == 0.  */
+  fcntl (descriptor, F_SETFD, FD_CLOEXEC);
+#endif
+
+  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;
+}
diff --git a/libbacktrace/print.c b/libbacktrace/print.c
new file mode 100644 (file)
index 0000000..bd224e6
--- /dev/null
@@ -0,0 +1,92 @@
+/* print.c -- Print the current backtrace.
+   Copyright (C) 2012-2014 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"
+
+/* Passed to callbacks.  */
+
+struct print_data
+{
+  struct backtrace_state *state;
+  FILE *f;
+};
+
+/* Print one level of a backtrace.  */
+
+static int
+print_callback (void *data, uintptr_t pc, const char *filename, int lineno,
+               const char *function)
+{
+  struct print_data *pdata = (struct print_data *) data;
+
+  fprintf (pdata->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, const char *msg, int errnum)
+{
+  struct print_data *pdata = (struct print_data *) data;
+
+  if (pdata->state->filename != NULL)
+    fprintf (stderr, "%s: ", pdata->state->filename);
+  fprintf (stderr, "libbacktrace: %s", msg);
+  if (errnum > 0)
+    fprintf (stderr, ": %s", strerror (errnum));
+  fputc ('\n', stderr);
+}
+
+/* Print a backtrace.  */
+
+void
+backtrace_print (struct backtrace_state *state, int skip, FILE *f)
+{
+  struct print_data data;
+
+  data.state = state;
+  data.f = f;
+  backtrace_full (state, skip + 1, print_callback, error_callback,
+                 (void *) &data);
+}
diff --git a/libbacktrace/read.c b/libbacktrace/read.c
new file mode 100644 (file)
index 0000000..c618a50
--- /dev/null
@@ -0,0 +1,96 @@
+/* read.c -- File views without mmap.
+   Copyright (C) 2012-2014 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 (struct backtrace_state *state, 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 (state, 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_state *state,
+                       struct backtrace_view *view,
+                       backtrace_error_callback error_callback,
+                       void *data)
+{
+  backtrace_free (state, view->base, view->len, error_callback, data);
+  view->data = NULL;
+  view->base = NULL;
+}
diff --git a/libbacktrace/simple.c b/libbacktrace/simple.c
new file mode 100644 (file)
index 0000000..efa8b6f
--- /dev/null
@@ -0,0 +1,108 @@
+/* simple.c -- The backtrace_simple function.
+   Copyright (C) 2012-2014 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;
+  /* Library state.  */
+  struct backtrace_state *state;
+  /* 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 (struct backtrace_state *state, int skip,
+                 backtrace_simple_callback callback,
+                 backtrace_error_callback error_callback, void *data)
+{
+  struct backtrace_simple_data bdata;
+
+  bdata.skip = skip + 1;
+  bdata.state = state;
+  bdata.callback = callback;
+  bdata.error_callback = error_callback;
+  bdata.data = data;
+  bdata.ret = 0;
+  _Unwind_Backtrace (simple_unwind, &bdata);
+  return bdata.ret;
+}
diff --git a/libbacktrace/state.c b/libbacktrace/state.c
new file mode 100644 (file)
index 0000000..a7d4e07
--- /dev/null
@@ -0,0 +1,72 @@
+/* state.c -- Create the backtrace state.
+   Copyright (C) 2012-2014 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 <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+#include "internal.h"
+
+/* Create the backtrace state.  This will then be passed to all the
+   other routines.  */
+
+struct backtrace_state *
+backtrace_create_state (const char *filename, int threaded,
+                       backtrace_error_callback error_callback,
+                       void *data)
+{
+  struct backtrace_state init_state;
+  struct backtrace_state *state;
+
+#ifndef HAVE_SYNC_FUNCTIONS
+  if (threaded)
+    {
+      error_callback (data, "backtrace library does not support threads", 0);
+      return NULL;
+    }
+#endif
+
+  memset (&init_state, 0, sizeof init_state);
+  init_state.filename = filename;
+  init_state.threaded = threaded;
+
+  state = ((struct backtrace_state *)
+          backtrace_alloc (&init_state, sizeof *state, error_callback, data));
+  if (state == NULL)
+    return NULL;
+  *state = init_state;
+
+  return state;
+}
diff --git a/libbacktrace/unknown.c b/libbacktrace/unknown.c
new file mode 100644 (file)
index 0000000..d1531da
--- /dev/null
@@ -0,0 +1,66 @@
+/* unknown.c -- used when backtrace configury does not know file format.
+   Copyright (C) 2012-2014 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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                 uintptr_t pc, backtrace_full_callback callback,
+                 backtrace_error_callback 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 (struct backtrace_state *state ATTRIBUTE_UNUSED,
+                     const char *filename ATTRIBUTE_UNUSED,
+                     uintptr_t base_address ATTRIBUTE_UNUSED,
+                     int is_exe ATTRIBUTE_UNUSED,
+                     backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+                     void *data ATTRIBUTE_UNUSED, fileline *fileline_fn)
+{
+  state->fileline_data = NULL;
+  *fileline_fn = unknown_fileline;
+  return 1;
+}
index 069e980..b900984 100644 (file)
@@ -35,6 +35,8 @@
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
 
+#include "libbacktrace/btrace.h"
+
 using namespace std;
 
 namespace {
@@ -46,6 +48,7 @@ using calloc_t = void* (*) (size_t, size_t);
 using posix_memalign_t = int (*) (void **, size_t, size_t);
 using valloc_t = void* (*) (size_t);
 using aligned_alloc_t = void* (*) (size_t, size_t);
+using dlopen_t = void* (*) (const char*, int);
 
 malloc_t real_malloc = nullptr;
 free_t real_free = nullptr;
@@ -54,16 +57,18 @@ calloc_t real_calloc = nullptr;
 posix_memalign_t real_posix_memalign = nullptr;
 valloc_t real_valloc = nullptr;
 aligned_alloc_t real_aligned_alloc = nullptr;
+dlopen_t real_dlopen = nullptr;
 
 struct IPCacheEntry
 {
     unsigned int id;
-    bool skip;
     bool stop;
 };
 
-atomic<unsigned int> next_cache_id;
+atomic<unsigned int> next_ipCache_id;
+atomic<unsigned int> next_traceCache_id;
 atomic<unsigned int> next_thread_id;
+atomic<unsigned int> next_string_id(1);
 
 // must be kept separately from ThreadData to ensure it stays valid
 // even until after ThreadData is destroyed
@@ -75,26 +80,26 @@ string env(const char* variable)
     return value ? string(value) : string();
 }
 
-struct Tree
+struct Trace
 {
     static const unsigned int MAX_DEPTH = 64;
     unsigned int data[MAX_DEPTH];
     unsigned int depth;
 
-    bool operator==(const Tree& o) const
+    bool operator==(const Trace& o) const
     {
         return depth == o.depth && !memcmp(data, o.data, sizeof(unsigned int) * depth);
     }
 };
 
-struct TreeHasher
+struct TraceHasher
 {
-    size_t operator()(const Tree& tree) const
+    size_t operator()(const Trace& trace) const
     {
         size_t seed = 0;
         hash<unsigned int> nodeHash;
-        for (unsigned int i = 0; i < tree.depth; ++i) {
-            boost::hash_combine(seed, nodeHash(tree.data[i]));
+        for (unsigned int i = 0; i < trace.depth; ++i) {
+            boost::hash_combine(seed, nodeHash(trace.data[i]));
         }
         return seed;
     }
@@ -110,7 +115,7 @@ struct ThreadData
         bool wasInHandler = in_handler;
         in_handler = true;
         ipCache.reserve(16384);
-        treeCache.reserve(16384);
+        traceCache.reserve(16384);
         string outputFileName = env("DUMP_MALLOC_TRACE_OUTPUT") + to_string(getpid()) + '.' + to_string(thread_id);
         out = fopen(outputFileName.c_str(), "wa");
         if (!out) {
@@ -126,15 +131,35 @@ struct ThreadData
         fclose(out);
     }
 
+    // assumes unique string ptrs as input, i.e. does not compare string data but only string ptrs
+    unsigned int stringId(const char* string)
+    {
+        if (!strcmp(string, "")) {
+            return 0;
+        }
+
+        auto it = stringCache.find(string);
+        if (it != stringCache.end()) {
+            return it->second;
+        }
+
+        auto id = next_string_id++;
+        stringCache.insert(it, make_pair(string, id));
+        fprintf(out, "s%u=%s\n", id, string);
+        return id;
+    }
+
     unordered_map<unw_word_t, IPCacheEntry> ipCache;
-    unordered_map<Tree, unsigned int, TreeHasher> treeCache;
+    unordered_map<Trace, unsigned int, TraceHasher> traceCache;
+    // maps known file names and module names to string ID
+    unordered_map<const char*, unsigned int> stringCache;
     unsigned int thread_id;
     FILE* out;
 };
 
 thread_local ThreadData threadData;
 
-unsigned int print_caller()
+unsigned int print_caller(const int skip = 2)
 {
     unw_context_t uc;
     unw_getcontext (&uc);
@@ -142,8 +167,8 @@ unsigned int print_caller()
     unw_cursor_t cursor;
     unw_init_local (&cursor, &uc);
 
-    // skip handleMalloc & malloc
-    for (int i = 0; i < 2; ++i) {
+    // skip functions we are not interested in
+    for (int i = 0; i < skip; ++i) {
         if (unw_step(&cursor) <= 0) {
             return 0;
         }
@@ -151,9 +176,9 @@ unsigned int print_caller()
 
     auto& ipCache = threadData.ipCache;
 
-    Tree tree;
-    tree.depth = 0;
-    while (unw_step(&cursor) > 0 && tree.depth < Tree::MAX_DEPTH) {
+    Trace trace;
+    trace.depth = 0;
+    while (unw_step(&cursor) > 0 && trace.depth < Trace::MAX_DEPTH) {
         unw_word_t ip;
         unw_get_reg(&cursor, UNW_REG_IP, &ip);
 
@@ -161,50 +186,52 @@ unsigned int print_caller()
         auto it = ipCache.find(ip);
         if (it == ipCache.end()) {
             // not cached yet, get data
-            const size_t BUF_SIZE = 256;
-            char name[BUF_SIZE];
-            name[0] = '\0';
-            unw_word_t offset;
-            unw_get_proc_name(&cursor, name, BUF_SIZE, &offset);
-            // skip operator new (_Znwm) and operator new[] (_Znam)
-            const bool skip = name[0] == '_' && name[1] == 'Z' && name[2] == 'n'
-                        && name[4] == 'm' && name[5] == '\0'
-                        && (name[3] == 'w' || name[3] == 'a');
-            const bool stop = !skip && (!strcmp(name, "main")
-                                    || !strcmp(name, "_GLOBAL__sub_I_main")
-                                    || !strcmp(name, "_Z41__static_initialization_and_destruction_0ii"));
-            const unsigned int id = next_cache_id++;
+            btrace_info info;
+            btrace_resolve_addr(&info, static_cast<uintptr_t>(ip),
+                                BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC | BTRACE_RESOLVE_ADDR_GET_FILENAME);
 
-            // and store it in the cache
-            it = ipCache.insert(it, make_pair(ip, IPCacheEntry{id, skip, stop}));
+            const bool stop = !strcmp(info.function, "__libc_start_main")
+                           || !strcmp(info.function, "__static_initialization_and_destruction_0");
+            const unsigned int id = next_ipCache_id++;
 
-            if (!skip) {
-                fprintf(threadData.out, "%u=%lx@%s+0x%lx\n", id, ip, name, offset);
+            // and store it in the cache
+            it = ipCache.insert(it, make_pair(ip, IPCacheEntry{id, stop}));
+
+            const auto funcId = threadData.stringId(info.demangled_func_buf);
+            const auto moduleId = threadData.stringId(info.module);
+            const auto fileId = threadData.stringId(info.filename);
+
+            fprintf(threadData.out, "%u=%u;%u;", id, funcId, moduleId);
+            if (fileId) {
+                if (info.linenumber > 0) {
+                    fprintf(threadData.out, "%u:%d", fileId, info.linenumber);
+                } else {
+                    fprintf(threadData.out, "%u", fileId);
+                }
             }
+            fputs("\n", threadData.out);
         }
 
         const auto& frame = it->second;
-        if (!frame.skip) {
-            tree.data[tree.depth++] = frame.id;
-        }
+        trace.data[trace.depth++] = frame.id;
         if (frame.stop) {
             break;
         }
     }
 
-    if (tree.depth == 1) {
-        return tree.data[0];
+    if (trace.depth == 1) {
+        return trace.data[0];
     }
 
     // TODO: sub-tree matching
-    auto& treeCache = threadData.treeCache;
-    auto it = treeCache.find(tree);
-    if (it == treeCache.end()) {
-        it = treeCache.insert(it, make_pair(tree, next_cache_id++));
-
-        fprintf(threadData.out, "%u=", it->second);
-        for (unsigned int i = 0; i < tree.depth; ++i) {
-            fprintf(threadData.out, "%u;", tree.data[i]);
+    auto& traceCache = threadData.traceCache;
+    auto it = traceCache.find(trace);
+    if (it == traceCache.end()) {
+        it = traceCache.insert(it, make_pair(trace, next_traceCache_id++));
+
+        fprintf(threadData.out, "t%u=", it->second);
+        for (unsigned int i = 0; i < trace.depth; ++i) {
+            fprintf(threadData.out, "%u;", trace.data[i]);
         }
         fputs("\n", threadData.out);
     }
@@ -256,6 +283,7 @@ void init()
 
     real_calloc = &dummy_calloc;
     real_calloc = findReal<calloc_t>("calloc");
+    real_dlopen = findReal<dlopen_t>("dlopen");
     real_malloc = findReal<malloc_t>("malloc");
     real_free = findReal<free_t>("free");
     real_realloc = findReal<realloc_t>("realloc");
@@ -263,6 +291,7 @@ void init()
     real_valloc = findReal<valloc_t>("valloc");
     real_aligned_alloc = findReal<aligned_alloc_t>("aligned_alloc");
 
+    btrace_dlopen_notify(nullptr);
     in_handler = false;
 }
 
@@ -401,4 +430,21 @@ void* valloc(size_t size)
     return ret;
 }
 
+void *dlopen(const char *filename, int flag)
+{
+    if (!real_dlopen) {
+        init();
+    }
+
+    void* ret = real_dlopen(filename, flag);
+
+    if (!in_handler) {
+        in_handler = true;
+        btrace_dlopen_notify(filename);
+        in_handler = false;
+    }
+
+    return ret;
+}
+
 }
index af40c96..ace2bc6 100755 (executable)
--- a/trace.sh
+++ b/trace.sh
@@ -28,8 +28,10 @@ fi
 pid=$!
 wait $pid
 
-echo "finished application, merging and zipping data files"
-cd $(dirname $output)
-output=$(basename $output)$pid
-tar -cjf "$output.tar.bz2" "$output."[0-9]* && rm "$output."[0-9]*
-du -h "$output.tar.bz2"
+if [[ "$(ls $output$pid* 2> /dev/null)" != "" ]]; then
+    echo "finished application, merging and zipping data files"
+    cd $(dirname $output)
+    output=$(basename $output)$pid
+    tar -cjf "$output.tar.bz2" "$output."[0-9]* && rm "$output."[0-9]*
+    du -h "$output.tar.bz2"
+fi