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)
--- /dev/null
+# 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}"
+)
--- /dev/null
+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.
--- /dev/null
+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.
+
--- /dev/null
+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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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@
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+/**************************************************************************
+ *
+ * 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;
+}
--- /dev/null
+/**************************************************************************
+ *
+ * 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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,
+ ¬e_view))
+ {
+ uuid_len = elf_parse_gnu_buildid(note_view.data, shdr->sh_size, uuid);
+ backtrace_release_view (state, ¬e_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,
+ ¬e_view))
+ {
+ *uuid_len = elf_parse_gnu_buildid(note_view.data, shdr->sh_size, uuid);
+ backtrace_release_view (state, ¬e_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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+/*-
+ * 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
#define UNW_LOCAL_ONLY
#include <libunwind.h>
+#include "libbacktrace/btrace.h"
+
using namespace std;
namespace {
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;
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
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;
}
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) {
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);
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;
}
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);
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);
}
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");
real_valloc = findReal<valloc_t>("valloc");
real_aligned_alloc = findReal<aligned_alloc_t>("aligned_alloc");
+ btrace_dlopen_notify(nullptr);
in_handler = false;
}
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;
+}
+
}
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