forked from M-Labs/artiq-zynq
Copied LLVM Libunwind from commit 3e6ec2a
This commit is contained in:
parent
29dd311c0f
commit
fd229a1b26
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"repository.callsign" : "UNW",
|
||||||
|
"conduit_uri" : "https://reviews.llvm.org/"
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
|
|
@ -0,0 +1,372 @@
|
||||||
|
#===============================================================================
|
||||||
|
# Setup Project
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.4.3)
|
||||||
|
|
||||||
|
if (POLICY CMP0042)
|
||||||
|
cmake_policy(SET CMP0042 NEW) # Set MACOSX_RPATH=YES by default
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add path for custom modules
|
||||||
|
set(CMAKE_MODULE_PATH
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules"
|
||||||
|
${CMAKE_MODULE_PATH}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR LIBUNWIND_STANDALONE_BUILD)
|
||||||
|
project(libunwind)
|
||||||
|
|
||||||
|
# Rely on llvm-config.
|
||||||
|
set(CONFIG_OUTPUT)
|
||||||
|
if(NOT LLVM_CONFIG_PATH)
|
||||||
|
find_program(LLVM_CONFIG_PATH "llvm-config")
|
||||||
|
endif()
|
||||||
|
if (DEFINED LLVM_PATH)
|
||||||
|
set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIR} CACHE PATH "Path to llvm/include")
|
||||||
|
set(LLVM_PATH ${LLVM_PATH} CACHE PATH "Path to LLVM source tree")
|
||||||
|
set(LLVM_MAIN_SRC_DIR ${LLVM_PATH})
|
||||||
|
set(LLVM_CMAKE_PATH "${LLVM_PATH}/cmake/modules")
|
||||||
|
elseif(LLVM_CONFIG_PATH)
|
||||||
|
message(STATUS "Found LLVM_CONFIG_PATH as ${LLVM_CONFIG_PATH}")
|
||||||
|
set(CONFIG_COMMAND ${LLVM_CONFIG_PATH} "--includedir" "--prefix" "--src-root")
|
||||||
|
execute_process(COMMAND ${CONFIG_COMMAND}
|
||||||
|
RESULT_VARIABLE HAD_ERROR
|
||||||
|
OUTPUT_VARIABLE CONFIG_OUTPUT)
|
||||||
|
if (NOT HAD_ERROR)
|
||||||
|
string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";"
|
||||||
|
CONFIG_OUTPUT ${CONFIG_OUTPUT})
|
||||||
|
else()
|
||||||
|
string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}")
|
||||||
|
message(STATUS "${CONFIG_COMMAND_STR}")
|
||||||
|
message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(GET CONFIG_OUTPUT 0 INCLUDE_DIR)
|
||||||
|
list(GET CONFIG_OUTPUT 1 LLVM_OBJ_ROOT)
|
||||||
|
list(GET CONFIG_OUTPUT 2 MAIN_SRC_DIR)
|
||||||
|
|
||||||
|
set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include")
|
||||||
|
set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree")
|
||||||
|
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
|
||||||
|
set(LLVM_LIT_PATH "${LLVM_PATH}/utils/lit/lit.py")
|
||||||
|
|
||||||
|
# --cmakedir is supported since llvm r291218 (4.0 release)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${LLVM_CONFIG_PATH} --cmakedir
|
||||||
|
RESULT_VARIABLE HAD_ERROR
|
||||||
|
OUTPUT_VARIABLE CONFIG_OUTPUT
|
||||||
|
ERROR_QUIET)
|
||||||
|
if(NOT HAD_ERROR)
|
||||||
|
string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH_FROM_LLVM_CONFIG)
|
||||||
|
file(TO_CMAKE_PATH "${LLVM_CMAKE_PATH_FROM_LLVM_CONFIG}" LLVM_CMAKE_PATH)
|
||||||
|
else()
|
||||||
|
file(TO_CMAKE_PATH "${LLVM_BINARY_DIR}" LLVM_BINARY_DIR_CMAKE_STYLE)
|
||||||
|
set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "UNSUPPORTED LIBUNWIND CONFIGURATION DETECTED: "
|
||||||
|
"llvm-config not found and LLVM_MAIN_SRC_DIR not defined. "
|
||||||
|
"Reconfigure with -DLLVM_CONFIG=path/to/llvm-config "
|
||||||
|
"or -DLLVM_PATH=path/to/llvm-source-root.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (EXISTS ${LLVM_CMAKE_PATH})
|
||||||
|
# Enable warnings, otherwise -w gets added to the cflags by HandleLLVMOptions.
|
||||||
|
set(LLVM_ENABLE_WARNINGS ON)
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
|
||||||
|
include("${LLVM_CMAKE_PATH}/AddLLVM.cmake")
|
||||||
|
include("${LLVM_CMAKE_PATH}/HandleLLVMOptions.cmake")
|
||||||
|
else()
|
||||||
|
message(WARNING "Not found: ${LLVM_CMAKE_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(PACKAGE_NAME libunwind)
|
||||||
|
set(PACKAGE_VERSION 10.0.0svn)
|
||||||
|
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||||
|
set(PACKAGE_BUGREPORT "llvm-bugs@lists.llvm.org")
|
||||||
|
|
||||||
|
if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||||
|
set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||||
|
else()
|
||||||
|
# Seek installed Lit.
|
||||||
|
find_program(LLVM_LIT "lit.py" ${LLVM_MAIN_SRC_DIR}/utils/lit
|
||||||
|
DOC "Path to lit.py")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (LLVM_LIT)
|
||||||
|
# Define the default arguments to use with 'lit', and an option for the user
|
||||||
|
# to override.
|
||||||
|
set(LIT_ARGS_DEFAULT "-sv")
|
||||||
|
if (MSVC OR XCODE)
|
||||||
|
set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
|
||||||
|
endif()
|
||||||
|
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
|
||||||
|
|
||||||
|
# On Win32 hosts, provide an option to specify the path to the GnuWin32 tools.
|
||||||
|
if (WIN32 AND NOT CYGWIN)
|
||||||
|
set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(LLVM_INCLUDE_TESTS OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX})
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX})
|
||||||
|
else()
|
||||||
|
set(LLVM_LIT "${CMAKE_SOURCE_DIR}/utils/lit/lit.py")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Setup CMake Options
|
||||||
|
#===============================================================================
|
||||||
|
include(CMakeDependentOption)
|
||||||
|
include(HandleCompilerRT)
|
||||||
|
|
||||||
|
# Define options.
|
||||||
|
option(LIBUNWIND_BUILD_32_BITS "Build 32 bit libunwind" ${LLVM_BUILD_32_BITS})
|
||||||
|
option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON)
|
||||||
|
option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON)
|
||||||
|
option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF)
|
||||||
|
option(LIBUNWIND_ENABLE_SHARED "Build libunwind as a shared library." ON)
|
||||||
|
option(LIBUNWIND_ENABLE_STATIC "Build libunwind as a static library." ON)
|
||||||
|
option(LIBUNWIND_ENABLE_CROSS_UNWINDING "Enable cross-platform unwinding support." OFF)
|
||||||
|
option(LIBUNWIND_ENABLE_ARM_WMMX "Enable unwinding support for ARM WMMX registers." OFF)
|
||||||
|
option(LIBUNWIND_ENABLE_THREADS "Build libunwind with threading support." ON)
|
||||||
|
option(LIBUNWIND_WEAK_PTHREAD_LIB "Use weak references to refer to pthread functions." OFF)
|
||||||
|
option(LIBUNWIND_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF)
|
||||||
|
option(LIBUNWIND_INCLUDE_DOCS "Build the libunwind documentation." ${LLVM_INCLUDE_DOCS})
|
||||||
|
|
||||||
|
set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING
|
||||||
|
"Define suffix of library directory name (32/64)")
|
||||||
|
option(LIBUNWIND_INSTALL_LIBRARY "Install the libunwind library." ON)
|
||||||
|
cmake_dependent_option(LIBUNWIND_INSTALL_STATIC_LIBRARY
|
||||||
|
"Install the static libunwind library." ON
|
||||||
|
"LIBUNWIND_ENABLE_STATIC;LIBUNWIND_INSTALL_LIBRARY" OFF)
|
||||||
|
cmake_dependent_option(LIBUNWIND_INSTALL_SHARED_LIBRARY
|
||||||
|
"Install the shared libunwind library." ON
|
||||||
|
"LIBUNWIND_ENABLE_SHARED;LIBUNWIND_INSTALL_LIBRARY" OFF)
|
||||||
|
set(LIBUNWIND_TARGET_TRIPLE "" CACHE STRING "Target triple for cross compiling.")
|
||||||
|
set(LIBUNWIND_GCC_TOOLCHAIN "" CACHE PATH "GCC toolchain for cross compiling.")
|
||||||
|
set(LIBUNWIND_SYSROOT "" CACHE PATH "Sysroot for cross compiling.")
|
||||||
|
set(LIBUNWIND_TEST_LINKER_FLAGS "" CACHE STRING
|
||||||
|
"Additional linker flags for test programs.")
|
||||||
|
set(LIBUNWIND_TEST_COMPILER_FLAGS "" CACHE STRING
|
||||||
|
"Additional compiler flags for test programs.")
|
||||||
|
|
||||||
|
if (NOT LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_STATIC)
|
||||||
|
message(FATAL_ERROR "libunwind must be built as either a shared or static library.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check that we can build with 32 bits if requested.
|
||||||
|
if (CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32)
|
||||||
|
if (LIBUNWIND_BUILD_32_BITS AND NOT LLVM_BUILD_32_BITS) # Don't duplicate the output from LLVM
|
||||||
|
message(STATUS "Building 32 bits executables and libraries.")
|
||||||
|
endif()
|
||||||
|
elseif(LIBUNWIND_BUILD_32_BITS)
|
||||||
|
message(FATAL_ERROR "LIBUNWIND_BUILD_32_BITS=ON is not supported on this platform.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(LIBUNWIND_HERMETIC_STATIC_LIBRARY
|
||||||
|
"Do not export any symbols from the static library." OFF)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Configure System
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
# Add path for custom modules
|
||||||
|
set(CMAKE_MODULE_PATH
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
|
||||||
|
${CMAKE_MODULE_PATH})
|
||||||
|
|
||||||
|
set(LIBUNWIND_COMPILER ${CMAKE_CXX_COMPILER})
|
||||||
|
set(LIBUNWIND_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
set(LIBUNWIND_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION
|
||||||
|
${PACKAGE_VERSION})
|
||||||
|
|
||||||
|
if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
|
||||||
|
set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}/c++)
|
||||||
|
set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE}/c++)
|
||||||
|
if(LIBCXX_LIBDIR_SUBDIR)
|
||||||
|
string(APPEND LIBUNWIND_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR})
|
||||||
|
string(APPEND LIBUNWIND_INSTALL_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR})
|
||||||
|
endif()
|
||||||
|
elseif(LLVM_LIBRARY_OUTPUT_INTDIR)
|
||||||
|
set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR})
|
||||||
|
set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX})
|
||||||
|
else()
|
||||||
|
set(LIBUNWIND_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBUNWIND_LIBDIR_SUFFIX})
|
||||||
|
set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR})
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR})
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR})
|
||||||
|
|
||||||
|
set(LIBUNWIND_INSTALL_PREFIX "" CACHE STRING "Define libunwind destination prefix.")
|
||||||
|
|
||||||
|
set(LIBUNWIND_C_FLAGS "")
|
||||||
|
set(LIBUNWIND_CXX_FLAGS "")
|
||||||
|
set(LIBUNWIND_COMPILE_FLAGS "")
|
||||||
|
set(LIBUNWIND_LINK_FLAGS "")
|
||||||
|
|
||||||
|
# Include macros for adding and removing libunwind flags.
|
||||||
|
include(HandleLibunwindFlags)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Setup Compiler Flags
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
# Get required flags.
|
||||||
|
add_target_flags_if(LIBUNWIND_BUILD_32_BITS "-m32")
|
||||||
|
|
||||||
|
if(LIBUNWIND_TARGET_TRIPLE)
|
||||||
|
add_target_flags("--target=${LIBUNWIND_TARGET_TRIPLE}")
|
||||||
|
elseif(CMAKE_CXX_COMPILER_TARGET)
|
||||||
|
set(LIBUNWIND_TARGET_TRIPLE "${CMAKE_CXX_COMPILER_TARGET}")
|
||||||
|
endif()
|
||||||
|
if(LIBUNWIND_GCC_TOOLCHAIN)
|
||||||
|
add_target_flags("--gcc-toolchain=${LIBUNWIND_GCC_TOOLCHAIN}")
|
||||||
|
elseif(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN)
|
||||||
|
set(LIBUNWIND_GCC_TOOLCHAIN "${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}")
|
||||||
|
endif()
|
||||||
|
if(LIBUNWIND_SYSROOT)
|
||||||
|
add_target_flags("--sysroot=${LIBUNWIND_SYSROOT}")
|
||||||
|
elseif(CMAKE_SYSROOT)
|
||||||
|
set(LIBUNWIND_SYSROOT "${CMAKE_SYSROOT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (LIBUNWIND_TARGET_TRIPLE)
|
||||||
|
set(TARGET_TRIPLE "${LIBUNWIND_TARGET_TRIPLE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Configure compiler.
|
||||||
|
include(config-ix)
|
||||||
|
|
||||||
|
if (LIBUNWIND_USE_COMPILER_RT AND NOT LIBUNWIND_HAS_NODEFAULTLIBS_FLAG)
|
||||||
|
list(APPEND LIBUNWIND_LINK_FLAGS "-rtlib=compiler-rt")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_compile_flags_if_supported(-Werror=return-type)
|
||||||
|
|
||||||
|
# Get warning flags
|
||||||
|
add_compile_flags_if_supported(-W)
|
||||||
|
add_compile_flags_if_supported(-Wall)
|
||||||
|
add_compile_flags_if_supported(-Wchar-subscripts)
|
||||||
|
add_compile_flags_if_supported(-Wconversion)
|
||||||
|
add_compile_flags_if_supported(-Wmismatched-tags)
|
||||||
|
add_compile_flags_if_supported(-Wmissing-braces)
|
||||||
|
add_compile_flags_if_supported(-Wnewline-eof)
|
||||||
|
add_compile_flags_if_supported(-Wno-unused-function)
|
||||||
|
add_compile_flags_if_supported(-Wshadow)
|
||||||
|
add_compile_flags_if_supported(-Wshorten-64-to-32)
|
||||||
|
add_compile_flags_if_supported(-Wsign-compare)
|
||||||
|
add_compile_flags_if_supported(-Wsign-conversion)
|
||||||
|
add_compile_flags_if_supported(-Wstrict-aliasing=2)
|
||||||
|
add_compile_flags_if_supported(-Wstrict-overflow=4)
|
||||||
|
add_compile_flags_if_supported(-Wunused-parameter)
|
||||||
|
add_compile_flags_if_supported(-Wunused-variable)
|
||||||
|
add_compile_flags_if_supported(-Wwrite-strings)
|
||||||
|
add_compile_flags_if_supported(-Wundef)
|
||||||
|
|
||||||
|
if (LIBUNWIND_ENABLE_WERROR)
|
||||||
|
add_compile_flags_if_supported(-Werror)
|
||||||
|
add_compile_flags_if_supported(-WX)
|
||||||
|
else()
|
||||||
|
add_compile_flags_if_supported(-Wno-error)
|
||||||
|
add_compile_flags_if_supported(-WX-)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (LIBUNWIND_ENABLE_PEDANTIC)
|
||||||
|
add_compile_flags_if_supported(-pedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Get feature flags.
|
||||||
|
# Exceptions
|
||||||
|
# Catches C++ exceptions only and tells the compiler to assume that extern C
|
||||||
|
# functions never throw a C++ exception.
|
||||||
|
add_cxx_compile_flags_if_supported(-fstrict-aliasing)
|
||||||
|
add_cxx_compile_flags_if_supported(-EHsc)
|
||||||
|
|
||||||
|
add_compile_flags_if_supported(-funwind-tables)
|
||||||
|
add_cxx_compile_flags_if_supported(-fno-exceptions)
|
||||||
|
add_cxx_compile_flags_if_supported(-fno-rtti)
|
||||||
|
|
||||||
|
# Ensure that we don't depend on C++ standard library.
|
||||||
|
if (LIBUNWIND_HAS_NOSTDINCXX_FLAG)
|
||||||
|
list(APPEND LIBUNWIND_COMPILE_FLAGS -nostdinc++)
|
||||||
|
# Remove -stdlib flags to prevent them from causing an unused flag warning.
|
||||||
|
string(REPLACE "-stdlib=libc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
|
string(REPLACE "-stdlib=libstdc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
|
||||||
|
if (LIBUNWIND_ENABLE_ASSERTIONS)
|
||||||
|
# MSVC doesn't like _DEBUG on release builds. See PR 4379.
|
||||||
|
if (NOT MSVC)
|
||||||
|
add_compile_flags(-D_DEBUG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# On Release builds cmake automatically defines NDEBUG, so we
|
||||||
|
# explicitly undefine it:
|
||||||
|
if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
|
||||||
|
add_compile_flags(-UNDEBUG)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if (NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
|
||||||
|
add_compile_flags(-DNDEBUG)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Cross-unwinding
|
||||||
|
if (NOT LIBUNWIND_ENABLE_CROSS_UNWINDING)
|
||||||
|
add_compile_flags(-D_LIBUNWIND_IS_NATIVE_ONLY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Threading-support
|
||||||
|
if (NOT LIBUNWIND_ENABLE_THREADS)
|
||||||
|
add_compile_flags(-D_LIBUNWIND_HAS_NO_THREADS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ARM WMMX register support
|
||||||
|
if (LIBUNWIND_ENABLE_ARM_WMMX)
|
||||||
|
# __ARM_WMMX is a compiler pre-define (as per the ACLE 2.0). Clang does not
|
||||||
|
# define this macro for any supported target at present. Therefore, here we
|
||||||
|
# provide the option to explicitly enable support for WMMX registers in the
|
||||||
|
# unwinder.
|
||||||
|
add_compile_flags(-D__ARM_WMMX)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# This is the _ONLY_ place where add_definitions is called.
|
||||||
|
if (MSVC)
|
||||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Disable DLL annotations on Windows for static builds.
|
||||||
|
if (WIN32 AND LIBUNWIND_ENABLE_STATIC AND NOT LIBUNWIND_ENABLE_SHARED)
|
||||||
|
add_definitions(-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (LIBUNWIND_HAS_COMMENT_LIB_PRAGMA)
|
||||||
|
add_definitions(-D_LIBUNWIND_HAS_COMMENT_LIB_PRAGMA)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Setup Source Code
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
include_directories(include)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
if (LIBUNWIND_INCLUDE_DOCS)
|
||||||
|
add_subdirectory(docs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (EXISTS ${LLVM_CMAKE_PATH})
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
|
@ -0,0 +1,311 @@
|
||||||
|
==============================================================================
|
||||||
|
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into an Object form of such source code, you
|
||||||
|
may redistribute such embedded portions in such Object form without complying
|
||||||
|
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||||
|
|
||||||
|
In addition, if you combine or link compiled forms of this Software with
|
||||||
|
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||||
|
court of competent jurisdiction determines that the patent provision (Section
|
||||||
|
3), the indemnity provision (Section 9) or other Section of the License
|
||||||
|
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||||
|
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||||
|
the License, but only in their entirety and only with respect to the Combined
|
||||||
|
Software.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Software from third parties included in the LLVM Project:
|
||||||
|
==============================================================================
|
||||||
|
The LLVM Project contains third party software which is under different license
|
||||||
|
terms. All such code will be identified clearly using at least one of two
|
||||||
|
mechanisms:
|
||||||
|
1) It will be in a separate directory tree with its own `LICENSE.txt` or
|
||||||
|
`LICENSE` file at the top containing the specific license and restrictions
|
||||||
|
which apply to that software, or
|
||||||
|
2) It will contain specific license and restriction terms at the top of every
|
||||||
|
file.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
The libunwind library is dual licensed under both the University of Illinois
|
||||||
|
"BSD-Like" license and the MIT license. As a user of this code you may choose
|
||||||
|
to use it under either license. As a contributor, you agree to allow your code
|
||||||
|
to be used under both.
|
||||||
|
|
||||||
|
Full text of the relevant licenses is included below.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
University of Illinois/NCSA
|
||||||
|
Open Source License
|
||||||
|
|
||||||
|
Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Developed by:
|
||||||
|
|
||||||
|
LLVM Team
|
||||||
|
|
||||||
|
University of Illinois at Urbana-Champaign
|
||||||
|
|
||||||
|
http://llvm.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal with
|
||||||
|
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:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimers.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimers in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the names of the LLVM Team, University of Illinois at
|
||||||
|
Urbana-Champaign, nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this Software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
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
|
||||||
|
CONTRIBUTORS 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 WITH THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,64 @@
|
||||||
|
function(find_compiler_rt_library name dest)
|
||||||
|
if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS)
|
||||||
|
message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function")
|
||||||
|
endif()
|
||||||
|
set(dest "" PARENT_SCOPE)
|
||||||
|
set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS}
|
||||||
|
"--rtlib=compiler-rt" "--print-libgcc-file-name")
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET)
|
||||||
|
list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}")
|
||||||
|
endif()
|
||||||
|
get_property(LIBUNWIND_CXX_FLAGS CACHE CMAKE_CXX_FLAGS PROPERTY VALUE)
|
||||||
|
string(REPLACE " " ";" LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}")
|
||||||
|
list(APPEND CLANG_COMMAND ${LIBUNWIND_CXX_FLAGS})
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CLANG_COMMAND}
|
||||||
|
RESULT_VARIABLE HAD_ERROR
|
||||||
|
OUTPUT_VARIABLE LIBRARY_FILE
|
||||||
|
)
|
||||||
|
string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE)
|
||||||
|
file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE)
|
||||||
|
string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}")
|
||||||
|
if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}")
|
||||||
|
message(STATUS "Found compiler-rt library: ${LIBRARY_FILE}")
|
||||||
|
set(${dest} "${LIBRARY_FILE}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(STATUS "Failed to find compiler-rt library")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(find_compiler_rt_dir dest)
|
||||||
|
if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS)
|
||||||
|
message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function")
|
||||||
|
endif()
|
||||||
|
set(dest "" PARENT_SCOPE)
|
||||||
|
if (APPLE)
|
||||||
|
set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS}
|
||||||
|
"-print-file-name=lib")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CLANG_COMMAND}
|
||||||
|
RESULT_VARIABLE HAD_ERROR
|
||||||
|
OUTPUT_VARIABLE LIBRARY_DIR
|
||||||
|
)
|
||||||
|
string(STRIP "${LIBRARY_DIR}" LIBRARY_DIR)
|
||||||
|
file(TO_CMAKE_PATH "${LIBRARY_DIR}" LIBRARY_DIR)
|
||||||
|
set(LIBRARY_DIR "${LIBRARY_DIR}/darwin")
|
||||||
|
else()
|
||||||
|
set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS}
|
||||||
|
"--rtlib=compiler-rt" "--print-libgcc-file-name")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CLANG_COMMAND}
|
||||||
|
RESULT_VARIABLE HAD_ERROR
|
||||||
|
OUTPUT_VARIABLE LIBRARY_FILE
|
||||||
|
)
|
||||||
|
string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE)
|
||||||
|
file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE)
|
||||||
|
get_filename_component(LIBRARY_DIR "${LIBRARY_FILE}" DIRECTORY)
|
||||||
|
endif()
|
||||||
|
if (NOT HAD_ERROR AND EXISTS "${LIBRARY_DIR}")
|
||||||
|
message(STATUS "Found compiler-rt directory: ${LIBRARY_DIR}")
|
||||||
|
set(${dest} "${LIBRARY_DIR}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(STATUS "Failed to find compiler-rt directory")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
|
@ -0,0 +1,272 @@
|
||||||
|
# HandleLibcxxFlags - A set of macros used to setup the flags used to compile
|
||||||
|
# and link libc++abi. These macros add flags to the following CMake variables.
|
||||||
|
# - LIBUNWIND_COMPILE_FLAGS: flags used to compile libunwind
|
||||||
|
# - LIBUNWIND_LINK_FLAGS: flags used to link libunwind
|
||||||
|
# - LIBUNWIND_LIBRARIES: libraries to link libunwind to.
|
||||||
|
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
unset(add_flag_if_supported)
|
||||||
|
|
||||||
|
# Mangle the name of a compiler flag into a valid CMake identifier.
|
||||||
|
# Ex: --std=c++11 -> STD_EQ_CXX11
|
||||||
|
macro(mangle_name str output)
|
||||||
|
string(STRIP "${str}" strippedStr)
|
||||||
|
string(REGEX REPLACE "^/" "" strippedStr "${strippedStr}")
|
||||||
|
string(REGEX REPLACE "^-+" "" strippedStr "${strippedStr}")
|
||||||
|
string(REGEX REPLACE "-+$" "" strippedStr "${strippedStr}")
|
||||||
|
string(REPLACE "-" "_" strippedStr "${strippedStr}")
|
||||||
|
string(REPLACE "=" "_EQ_" strippedStr "${strippedStr}")
|
||||||
|
string(REPLACE "+" "X" strippedStr "${strippedStr}")
|
||||||
|
string(TOUPPER "${strippedStr}" ${output})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Remove a list of flags from all CMake variables that affect compile flags.
|
||||||
|
# This can be used to remove unwanted flags specified on the command line
|
||||||
|
# or added in other parts of LLVM's cmake configuration.
|
||||||
|
macro(remove_flags)
|
||||||
|
foreach(var ${ARGN})
|
||||||
|
string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
|
||||||
|
string(REPLACE "${var}" "" CMAKE_SHARED_MODULE_FLAGS "${CMAKE_SHARED_MODULE_FLAGS}")
|
||||||
|
remove_definitions(${var})
|
||||||
|
endforeach()
|
||||||
|
endmacro(remove_flags)
|
||||||
|
|
||||||
|
macro(check_flag_supported flag)
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(append_flags DEST)
|
||||||
|
foreach(value ${ARGN})
|
||||||
|
list(APPEND ${DEST} ${value})
|
||||||
|
list(APPEND ${DEST} ${value})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If the specified 'condition' is true then append the specified list of flags to DEST
|
||||||
|
macro(append_flags_if condition DEST)
|
||||||
|
if (${condition})
|
||||||
|
list(APPEND ${DEST} ${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add each flag in the list specified by DEST if that flag is supported by the current compiler.
|
||||||
|
macro(append_flags_if_supported DEST)
|
||||||
|
foreach(flag ${ARGN})
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
append_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${DEST} ${flag})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a macro definition if condition is true.
|
||||||
|
macro(define_if condition def)
|
||||||
|
if (${condition})
|
||||||
|
add_definitions(${def})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a macro definition if condition is not true.
|
||||||
|
macro(define_if_not condition def)
|
||||||
|
if (NOT ${condition})
|
||||||
|
add_definitions(${def})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a macro definition to the __config_site file if the specified condition
|
||||||
|
# is 'true'. Note that '-D${def}' is not added. Instead it is expected that
|
||||||
|
# the build include the '__config_site' header.
|
||||||
|
macro(config_define_if condition def)
|
||||||
|
if (${condition})
|
||||||
|
set(${def} ON)
|
||||||
|
set(LIBUNWIND_NEEDS_SITE_CONFIG ON)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(config_define_if_not condition def)
|
||||||
|
if (NOT ${condition})
|
||||||
|
set(${def} ON)
|
||||||
|
set(LIBUNWIND_NEEDS_SITE_CONFIG ON)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(config_define value def)
|
||||||
|
set(${def} ${value})
|
||||||
|
set(LIBUNWIND_NEEDS_SITE_CONFIG ON)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a list of flags to all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS',
|
||||||
|
# 'LIBUNWIND_COMPILE_FLAGS' and 'LIBUNWIND_LINK_FLAGS'.
|
||||||
|
macro(add_target_flags)
|
||||||
|
foreach(value ${ARGN})
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${value}")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${value}")
|
||||||
|
list(APPEND LIBUNWIND_COMPILE_FLAGS ${value})
|
||||||
|
list(APPEND LIBUNWIND_LINK_FLAGS ${value})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If the specified 'condition' is true then add a list of flags to
|
||||||
|
# all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', 'LIBUNWIND_COMPILE_FLAGS'
|
||||||
|
# and 'LIBUNWIND_LINK_FLAGS'.
|
||||||
|
macro(add_target_flags_if condition)
|
||||||
|
if (${condition})
|
||||||
|
add_target_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a specified list of flags to both 'LIBUNWIND_COMPILE_FLAGS' and
|
||||||
|
# 'LIBUNWIND_LINK_FLAGS'.
|
||||||
|
macro(add_flags)
|
||||||
|
foreach(value ${ARGN})
|
||||||
|
list(APPEND LIBUNWIND_COMPILE_FLAGS ${value})
|
||||||
|
list(APPEND LIBUNWIND_LINK_FLAGS ${value})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If the specified 'condition' is true then add a list of flags to both
|
||||||
|
# 'LIBUNWIND_COMPILE_FLAGS' and 'LIBUNWIND_LINK_FLAGS'.
|
||||||
|
macro(add_flags_if condition)
|
||||||
|
if (${condition})
|
||||||
|
add_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add each flag in the list to LIBUNWIND_COMPILE_FLAGS and LIBUNWIND_LINK_FLAGS
|
||||||
|
# if that flag is supported by the current compiler.
|
||||||
|
macro(add_flags_if_supported)
|
||||||
|
foreach(flag ${ARGN})
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
add_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a list of flags to 'LIBUNWIND_COMPILE_FLAGS'.
|
||||||
|
macro(add_compile_flags)
|
||||||
|
foreach(f ${ARGN})
|
||||||
|
list(APPEND LIBUNWIND_COMPILE_FLAGS ${f})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If 'condition' is true then add the specified list of flags to
|
||||||
|
# 'LIBUNWIND_COMPILE_FLAGS'
|
||||||
|
macro(add_compile_flags_if condition)
|
||||||
|
if (${condition})
|
||||||
|
add_compile_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# For each specified flag, add that flag to 'LIBUNWIND_COMPILE_FLAGS' if the
|
||||||
|
# flag is supported by the C++ compiler.
|
||||||
|
macro(add_compile_flags_if_supported)
|
||||||
|
foreach(flag ${ARGN})
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
add_compile_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a list of flags to 'LIBUNWIND_C_FLAGS'.
|
||||||
|
macro(add_c_flags)
|
||||||
|
foreach(f ${ARGN})
|
||||||
|
list(APPEND LIBUNWIND_C_FLAGS ${f})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If 'condition' is true then add the specified list of flags to
|
||||||
|
# 'LIBUNWIND_C_FLAGS'
|
||||||
|
macro(add_c_flags_if condition)
|
||||||
|
if (${condition})
|
||||||
|
add_c_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# For each specified flag, add that flag to 'LIBUNWIND_C_FLAGS' if the
|
||||||
|
# flag is supported by the C compiler.
|
||||||
|
macro(add_c_compile_flags_if_supported)
|
||||||
|
foreach(flag ${ARGN})
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_c_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
add_c_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a list of flags to 'LIBUNWIND_CXX_FLAGS'.
|
||||||
|
macro(add_cxx_flags)
|
||||||
|
foreach(f ${ARGN})
|
||||||
|
list(APPEND LIBUNWIND_CXX_FLAGS ${f})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If 'condition' is true then add the specified list of flags to
|
||||||
|
# 'LIBUNWIND_CXX_FLAGS'
|
||||||
|
macro(add_cxx_flags_if condition)
|
||||||
|
if (${condition})
|
||||||
|
add_cxx_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# For each specified flag, add that flag to 'LIBUNWIND_CXX_FLAGS' if the
|
||||||
|
# flag is supported by the C compiler.
|
||||||
|
macro(add_cxx_compile_flags_if_supported)
|
||||||
|
foreach(flag ${ARGN})
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
add_cxx_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a list of flags to 'LIBUNWIND_LINK_FLAGS'.
|
||||||
|
macro(add_link_flags)
|
||||||
|
foreach(f ${ARGN})
|
||||||
|
list(APPEND LIBUNWIND_LINK_FLAGS ${f})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# If 'condition' is true then add the specified list of flags to
|
||||||
|
# 'LIBUNWIND_LINK_FLAGS'
|
||||||
|
macro(add_link_flags_if condition)
|
||||||
|
if (${condition})
|
||||||
|
add_link_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# For each specified flag, add that flag to 'LIBUNWIND_LINK_FLAGS' if the
|
||||||
|
# flag is supported by the C++ compiler.
|
||||||
|
macro(add_link_flags_if_supported)
|
||||||
|
foreach(flag ${ARGN})
|
||||||
|
mangle_name("${flag}" flagname)
|
||||||
|
check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG")
|
||||||
|
add_link_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add a list of libraries or link flags to 'LIBUNWIND_LIBRARIES'.
|
||||||
|
macro(add_library_flags)
|
||||||
|
foreach(lib ${ARGN})
|
||||||
|
list(APPEND LIBUNWIND_LIBRARIES ${lib})
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# if 'condition' is true then add the specified list of libraries and flags
|
||||||
|
# to 'LIBUNWIND_LIBRARIES'.
|
||||||
|
macro(add_library_flags_if condition)
|
||||||
|
if(${condition})
|
||||||
|
add_library_flags(${ARGN})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Turn a comma separated CMake list into a space separated string.
|
||||||
|
macro(split_list listname)
|
||||||
|
string(REPLACE ";" " " ${listname} "${${listname}}")
|
||||||
|
endmacro()
|
|
@ -0,0 +1,75 @@
|
||||||
|
include(CMakePushCheckState)
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckLibraryExists)
|
||||||
|
include(CheckCSourceCompiles)
|
||||||
|
|
||||||
|
check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB)
|
||||||
|
|
||||||
|
if (NOT LIBUNWIND_USE_COMPILER_RT)
|
||||||
|
check_library_exists(gcc_s __gcc_personality_v0 "" LIBUNWIND_HAS_GCC_S_LIB)
|
||||||
|
check_library_exists(gcc __absvdi2 "" LIBUNWIND_HAS_GCC_LIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# libunwind is built with -nodefaultlibs, so we want all our checks to also
|
||||||
|
# use this option, otherwise we may end up with an inconsistency between
|
||||||
|
# the flags we think we require during configuration (if the checks are
|
||||||
|
# performed without -nodefaultlibs) and the flags that are actually
|
||||||
|
# required during compilation (which has the -nodefaultlibs). libc is
|
||||||
|
# required for the link to go through. We remove sanitizers from the
|
||||||
|
# configuration checks to avoid spurious link errors.
|
||||||
|
check_c_compiler_flag(-nodefaultlibs LIBUNWIND_HAS_NODEFAULTLIBS_FLAG)
|
||||||
|
if (LIBUNWIND_HAS_NODEFAULTLIBS_FLAG)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs")
|
||||||
|
if (LIBUNWIND_HAS_C_LIB)
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES c)
|
||||||
|
endif ()
|
||||||
|
if (LIBUNWIND_USE_COMPILER_RT)
|
||||||
|
find_compiler_rt_library(builtins LIBUNWIND_BUILTINS_LIBRARY)
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBUNWIND_BUILTINS_LIBRARY}")
|
||||||
|
else ()
|
||||||
|
if (LIBUNWIND_HAS_GCC_S_LIB)
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES gcc_s)
|
||||||
|
endif ()
|
||||||
|
if (LIBUNWIND_HAS_GCC_LIB)
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES gcc)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
if (MINGW)
|
||||||
|
# Mingw64 requires quite a few "C" runtime libraries in order for basic
|
||||||
|
# programs to link successfully with -nodefaultlibs.
|
||||||
|
if (LIBUNWIND_USE_COMPILER_RT)
|
||||||
|
set(MINGW_RUNTIME ${LIBUNWIND_BUILTINS_LIBRARY})
|
||||||
|
else ()
|
||||||
|
set(MINGW_RUNTIME gcc_s gcc)
|
||||||
|
endif()
|
||||||
|
set(MINGW_LIBRARIES mingw32 ${MINGW_RUNTIME} moldname mingwex msvcrt advapi32
|
||||||
|
shell32 user32 kernel32 mingw32 ${MINGW_RUNTIME}
|
||||||
|
moldname mingwex msvcrt)
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES ${MINGW_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
if (CMAKE_C_FLAGS MATCHES -fsanitize OR CMAKE_CXX_FLAGS MATCHES -fsanitize)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize=all")
|
||||||
|
endif ()
|
||||||
|
if (CMAKE_C_FLAGS MATCHES -fsanitize-coverage OR CMAKE_CXX_FLAGS MATCHES -fsanitize-coverage)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check compiler pragmas
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
cmake_push_check_state()
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unknown-pragmas")
|
||||||
|
check_c_source_compiles("
|
||||||
|
#pragma comment(lib, \"c\")
|
||||||
|
int main() { return 0; }
|
||||||
|
" LIBUNWIND_HAS_COMMENT_LIB_PRAGMA)
|
||||||
|
cmake_pop_check_state()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check compiler flags
|
||||||
|
check_cxx_compiler_flag(-nostdinc++ LIBUNWIND_HAS_NOSTDINCXX_FLAG)
|
||||||
|
|
||||||
|
# Check libraries
|
||||||
|
check_library_exists(dl dladdr "" LIBUNWIND_HAS_DL_LIB)
|
||||||
|
check_library_exists(pthread pthread_once "" LIBUNWIND_HAS_PTHREAD_LIB)
|
|
@ -0,0 +1,161 @@
|
||||||
|
.. _BuildingLibunwind:
|
||||||
|
|
||||||
|
==================
|
||||||
|
Building libunwind
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
|
||||||
|
.. _build instructions:
|
||||||
|
|
||||||
|
Getting Started
|
||||||
|
===============
|
||||||
|
|
||||||
|
On Mac OS, the easiest way to get this library is to link with -lSystem.
|
||||||
|
However if you want to build tip-of-trunk from here (getting the bleeding
|
||||||
|
edge), read on.
|
||||||
|
|
||||||
|
The basic steps needed to build libc++ are:
|
||||||
|
|
||||||
|
#. Checkout LLVM, libunwind, and related projects:
|
||||||
|
|
||||||
|
* ``cd where-you-want-llvm-to-live``
|
||||||
|
* ``git clone https://github.com/llvm/llvm-project.git``
|
||||||
|
|
||||||
|
#. Configure and build libunwind:
|
||||||
|
|
||||||
|
CMake is the only supported configuration system.
|
||||||
|
|
||||||
|
Clang is the preferred compiler when building and using libunwind.
|
||||||
|
|
||||||
|
* ``cd where you want to build llvm``
|
||||||
|
* ``mkdir build``
|
||||||
|
* ``cd build``
|
||||||
|
* ``cmake -G <generator> -DLLVM_ENABLE_PROJECTS=libunwind [options] <path to llvm sources>``
|
||||||
|
|
||||||
|
For more information about configuring libunwind see :ref:`CMake Options`.
|
||||||
|
|
||||||
|
* ``make unwind`` --- will build libunwind.
|
||||||
|
* ``make check-unwind`` --- will run the test suite.
|
||||||
|
|
||||||
|
Shared and static libraries for libunwind should now be present in llvm/build/lib.
|
||||||
|
|
||||||
|
#. **Optional**: Install libunwind
|
||||||
|
|
||||||
|
If your system already provides an unwinder, it is important to be careful
|
||||||
|
not to replace it. Remember Use the CMake option ``CMAKE_INSTALL_PREFIX`` to
|
||||||
|
select a safe place to install libunwind.
|
||||||
|
|
||||||
|
* ``make install-unwind`` --- Will install the libraries and the headers
|
||||||
|
|
||||||
|
|
||||||
|
It is sometimes beneficial to build outside of the LLVM tree. An out-of-tree
|
||||||
|
build would look like this:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ cd where-you-want-libunwind-to-live
|
||||||
|
$ # Check out llvm, and libunwind
|
||||||
|
$ ``svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm``
|
||||||
|
$ ``svn co http://llvm.org/svn/llvm-project/libunwind/trunk libunwind``
|
||||||
|
$ cd where-you-want-to-build
|
||||||
|
$ mkdir build && cd build
|
||||||
|
$ export CC=clang CXX=clang++
|
||||||
|
$ cmake -DLLVM_PATH=path/to/llvm \
|
||||||
|
path/to/libunwind
|
||||||
|
$ make
|
||||||
|
|
||||||
|
|
||||||
|
.. _CMake Options:
|
||||||
|
|
||||||
|
CMake Options
|
||||||
|
=============
|
||||||
|
|
||||||
|
Here are some of the CMake variables that are used often, along with a
|
||||||
|
brief explanation and LLVM-specific notes. For full documentation, check the
|
||||||
|
CMake docs or execute ``cmake --help-variable VARIABLE_NAME``.
|
||||||
|
|
||||||
|
**CMAKE_BUILD_TYPE**:STRING
|
||||||
|
Sets the build type for ``make`` based generators. Possible values are
|
||||||
|
Release, Debug, RelWithDebInfo and MinSizeRel. On systems like Visual Studio
|
||||||
|
the user sets the build type with the IDE settings.
|
||||||
|
|
||||||
|
**CMAKE_INSTALL_PREFIX**:PATH
|
||||||
|
Path where LLVM will be installed if "make install" is invoked or the
|
||||||
|
"INSTALL" target is built.
|
||||||
|
|
||||||
|
**CMAKE_CXX_COMPILER**:STRING
|
||||||
|
The C++ compiler to use when building and testing libunwind.
|
||||||
|
|
||||||
|
|
||||||
|
.. _libunwind-specific options:
|
||||||
|
|
||||||
|
libunwind specific options
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_BUILD_32_BITS:BOOL
|
||||||
|
|
||||||
|
**Default**: Same as LLVM_BUILD_32_BITS
|
||||||
|
|
||||||
|
Toggle whether libunwind should be built with -m32.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_ASSERTIONS:BOOL
|
||||||
|
|
||||||
|
**Default**: ``ON``
|
||||||
|
|
||||||
|
Toggle assertions independent of the build mode.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_PEDANTIC:BOOL
|
||||||
|
|
||||||
|
**Default**: ``ON``
|
||||||
|
|
||||||
|
Compile with -Wpedantic.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_WERROR:BOOL
|
||||||
|
|
||||||
|
**Default**: ``ON``
|
||||||
|
|
||||||
|
Compile with -Werror
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_SHARED:BOOL
|
||||||
|
|
||||||
|
**Default**: ``ON``
|
||||||
|
|
||||||
|
Build libunwind as a shared library.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_STATIC:BOOL
|
||||||
|
|
||||||
|
**Default**: ``ON``
|
||||||
|
|
||||||
|
Build libunwind as a static archive.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_CROSS_UNWINDING:BOOL
|
||||||
|
|
||||||
|
**Default**: ``OFF``
|
||||||
|
|
||||||
|
Enable cross-platform unwinding support.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_ARM_WMMX:BOOL
|
||||||
|
|
||||||
|
**Default**: ``OFF``
|
||||||
|
|
||||||
|
Enable unwinding support for ARM WMMX registers.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_ENABLE_THREADS:BOOL
|
||||||
|
|
||||||
|
**Default**: ``ON``
|
||||||
|
|
||||||
|
Build libunwind with threading support.
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_TARGET_TRIPLE:STRING
|
||||||
|
|
||||||
|
Target triple for cross compiling
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_GCC_TOOLCHAIN:PATH
|
||||||
|
|
||||||
|
GCC toolchain for cross compiling
|
||||||
|
|
||||||
|
.. option:: LIBUNWIND_SYSROOT
|
||||||
|
|
||||||
|
Sysroot for cross compiling
|
|
@ -0,0 +1,7 @@
|
||||||
|
include(FindSphinx)
|
||||||
|
if (SPHINX_FOUND)
|
||||||
|
include(AddSphinxTarget)
|
||||||
|
if (${SPHINX_OUTPUT_HTML})
|
||||||
|
add_sphinx_target(html libunwind)
|
||||||
|
endif()
|
||||||
|
endif()
|
|
@ -0,0 +1,13 @@
|
||||||
|
libunwind Documentation
|
||||||
|
====================
|
||||||
|
|
||||||
|
The libunwind documentation is written using the Sphinx documentation generator. It is
|
||||||
|
currently tested with Sphinx 1.1.3.
|
||||||
|
|
||||||
|
To build the documents into html configure libunwind with the following cmake options:
|
||||||
|
|
||||||
|
* -DLLVM_ENABLE_SPHINX=ON
|
||||||
|
* -DLIBUNWIND_INCLUDE_DOCS=ON
|
||||||
|
|
||||||
|
After configuring libunwind with these options the make rule `docs-libunwind-html`
|
||||||
|
should be available.
|
|
@ -0,0 +1,252 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# libunwind documentation build configuration file.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'libunwind'
|
||||||
|
copyright = u'2011-%d, LLVM Project' % date.today().year
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '10.0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '10.0'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
today_fmt = '%Y-%m-%d'
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
show_authors = True
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'friendly'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
html_theme = 'haiku'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
#html_theme_path = []
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = []
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_domain_indices = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_sphinx = True
|
||||||
|
|
||||||
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_copyright = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = None
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'libunwinddoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('contents', 'libunwind.tex', u'libunwind Documentation',
|
||||||
|
u'LLVM project', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# If true, show page references after internal links.
|
||||||
|
#latex_show_pagerefs = False
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#latex_show_urls = False
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
('contents', 'libunwind', u'libunwind Documentation',
|
||||||
|
[u'LLVM project'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
('contents', 'libunwind', u'libunwind Documentation',
|
||||||
|
u'LLVM project', 'libunwind', 'LLVM Unwinder',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#texinfo_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#texinfo_domain_indices = True
|
||||||
|
|
||||||
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
|
#texinfo_show_urls = 'footnote'
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: Define intersphinx configration.
|
||||||
|
intersphinx_mapping = {}
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for extensions ----------------------------------------------------
|
||||||
|
|
||||||
|
# Enable this if you want TODOs to show up in the generated documentation.
|
||||||
|
todo_include_todos = True
|
|
@ -0,0 +1,104 @@
|
||||||
|
.. _index:
|
||||||
|
|
||||||
|
=======================
|
||||||
|
libunwind LLVM Unwinder
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
libunwind is an implementation of the interface defined by the HP libunwind
|
||||||
|
project. It was contributed by Apple as a way to enable clang++ to port to
|
||||||
|
platforms that do not have a system unwinder. It is intended to be a small and
|
||||||
|
fast implementation of the ABI, leaving off some features of HP's libunwind
|
||||||
|
that never materialized (e.g. remote unwinding).
|
||||||
|
|
||||||
|
The unwinder has two levels of API. The high level APIs are the `_Unwind_*`
|
||||||
|
functions which implement functionality required by `__cxa_*` exception
|
||||||
|
functions. The low level APIs are the `unw_*` functions which are an interface
|
||||||
|
defined by the old HP libunwind project.
|
||||||
|
|
||||||
|
Getting Started with libunwind
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
BuildingLibunwind
|
||||||
|
|
||||||
|
Current Status
|
||||||
|
--------------
|
||||||
|
|
||||||
|
libunwind is a production-quality unwinder, with platform support for DWARF
|
||||||
|
unwind info, SjLj, and ARM EHABI.
|
||||||
|
|
||||||
|
The low level libunwind API was designed to work either in-process (aka local)
|
||||||
|
or to operate on another process (aka remote), but only the local path has been
|
||||||
|
implemented. Remote unwinding remains as future work.
|
||||||
|
|
||||||
|
Platform and Compiler Support
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
libunwind is known to work on the following platforms:
|
||||||
|
|
||||||
|
============ ======================== ============ ========================
|
||||||
|
OS Arch Compilers Unwind Info
|
||||||
|
============ ======================== ============ ========================
|
||||||
|
Any i386, x86_64, ARM Clang SjLj
|
||||||
|
Bare Metal ARM Clang, GCC EHABI
|
||||||
|
FreeBSD i386, x86_64, ARM64 Clang DWARF CFI
|
||||||
|
iOS ARM Clang SjLj
|
||||||
|
Linux ARM Clang, GCC EHABI
|
||||||
|
Linux i386, x86_64, ARM64 Clang, GCC DWARF CFI
|
||||||
|
macOS i386, x86_64 Clang, GCC DWARF CFI
|
||||||
|
NetBSD x86_64 Clang, GCC DWARF CFI
|
||||||
|
Windows i386, x86_64, ARM, ARM64 Clang DWARF CFI
|
||||||
|
============ ======================== ============ ========================
|
||||||
|
|
||||||
|
The following minimum compiler versions are strongly recommended.
|
||||||
|
|
||||||
|
* Clang 3.5 and above
|
||||||
|
* GCC 4.7 and above.
|
||||||
|
|
||||||
|
Anything older *may* work.
|
||||||
|
|
||||||
|
Notes and Known Issues
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* TODO
|
||||||
|
|
||||||
|
|
||||||
|
Getting Involved
|
||||||
|
================
|
||||||
|
|
||||||
|
First please review our `Developer's Policy <http://llvm.org/docs/DeveloperPolicy.html>`__
|
||||||
|
and `Getting started with LLVM <http://llvm.org/docs/GettingStarted.html>`__.
|
||||||
|
|
||||||
|
**Bug Reports**
|
||||||
|
|
||||||
|
If you think you've found a bug in libunwind, please report it using
|
||||||
|
the `LLVM Bugzilla`_. If you're not sure, you
|
||||||
|
can post a message to the `cfe-dev mailing list`_ or on IRC.
|
||||||
|
Please include "libunwind" in your subject.
|
||||||
|
|
||||||
|
**Patches**
|
||||||
|
|
||||||
|
If you want to contribute a patch to libunwind, the best place for that is
|
||||||
|
`Phabricator <http://llvm.org/docs/Phabricator.html>`_. Please include [libunwind] in the subject and
|
||||||
|
add `cfe-commits` as a subscriber. Also make sure you are subscribed to the
|
||||||
|
`cfe-commits mailing list <http://lists.llvm.org/mailman/listinfo/cfe-commits>`_.
|
||||||
|
|
||||||
|
**Discussion and Questions**
|
||||||
|
|
||||||
|
Send discussions and questions to the
|
||||||
|
`cfe-dev mailing list <http://lists.llvm.org/mailman/listinfo/cfe-dev>`_.
|
||||||
|
Please include [libunwind] in the subject.
|
||||||
|
|
||||||
|
|
||||||
|
Quick Links
|
||||||
|
===========
|
||||||
|
* `LLVM Homepage <http://llvm.org/>`_
|
||||||
|
* `LLVM Bugzilla <https://bugs.llvm.org/>`_
|
||||||
|
* `cfe-commits Mailing List`_
|
||||||
|
* `cfe-dev Mailing List`_
|
||||||
|
* `Browse libunwind Sources <https://github.com/llvm/llvm-project/blob/master/libunwind/>`_
|
|
@ -0,0 +1,140 @@
|
||||||
|
//===------------------------- __libunwind_config.h -----------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef ____LIBUNWIND_CONFIG_H__
|
||||||
|
#define ____LIBUNWIND_CONFIG_H__
|
||||||
|
|
||||||
|
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
|
||||||
|
!defined(__ARM_DWARF_EH__)
|
||||||
|
#define _LIBUNWIND_ARM_EHABI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 8
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 32
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC 112
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 116
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 95
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM 287
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC 31
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||||
|
# if defined(__i386__)
|
||||||
|
# define _LIBUNWIND_TARGET_I386
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 8
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 15
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86
|
||||||
|
# elif defined(__x86_64__)
|
||||||
|
# define _LIBUNWIND_TARGET_X86_64 1
|
||||||
|
# if defined(_WIN64)
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 54
|
||||||
|
# ifdef __SEH__
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 204
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 66
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 21
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 33
|
||||||
|
# endif
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64
|
||||||
|
# elif defined(__powerpc64__)
|
||||||
|
# define _LIBUNWIND_TARGET_PPC64 1
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 167
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 179
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64
|
||||||
|
# elif defined(__ppc__)
|
||||||
|
# define _LIBUNWIND_TARGET_PPC 1
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 117
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 124
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC
|
||||||
|
# elif defined(__aarch64__)
|
||||||
|
# define _LIBUNWIND_TARGET_AARCH64 1
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 66
|
||||||
|
# if defined(__SEH__)
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 164
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 78
|
||||||
|
# endif
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64
|
||||||
|
# elif defined(__arm__)
|
||||||
|
# define _LIBUNWIND_TARGET_ARM 1
|
||||||
|
# if defined(__SEH__)
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 42
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 80
|
||||||
|
# elif defined(__ARM_WMMX)
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 61
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 68
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 42
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 49
|
||||||
|
# endif
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM
|
||||||
|
# elif defined(__or1k__)
|
||||||
|
# define _LIBUNWIND_TARGET_OR1K 1
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 16
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 24
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K
|
||||||
|
# elif defined(__mips__)
|
||||||
|
# if defined(_ABIO32) && _MIPS_SIM == _ABIO32
|
||||||
|
# define _LIBUNWIND_TARGET_MIPS_O32 1
|
||||||
|
# if defined(__mips_hard_float)
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 50
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 57
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 18
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 24
|
||||||
|
# endif
|
||||||
|
# elif defined(_ABIN32) && _MIPS_SIM == _ABIN32
|
||||||
|
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
|
||||||
|
# if defined(__mips_hard_float)
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 67
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 74
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 35
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 42
|
||||||
|
# endif
|
||||||
|
# elif defined(_ABI64) && _MIPS_SIM == _ABI64
|
||||||
|
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
|
||||||
|
# if defined(__mips_hard_float)
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 67
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 79
|
||||||
|
# else
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 35
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 47
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# error "Unsupported MIPS ABI and/or environment"
|
||||||
|
# endif
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS
|
||||||
|
# elif defined(__sparc__)
|
||||||
|
#define _LIBUNWIND_TARGET_SPARC 1
|
||||||
|
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC
|
||||||
|
#define _LIBUNWIND_CONTEXT_SIZE 16
|
||||||
|
#define _LIBUNWIND_CURSOR_SIZE 23
|
||||||
|
# else
|
||||||
|
# error "Unsupported architecture."
|
||||||
|
# endif
|
||||||
|
#else // !_LIBUNWIND_IS_NATIVE_ONLY
|
||||||
|
# define _LIBUNWIND_TARGET_I386
|
||||||
|
# define _LIBUNWIND_TARGET_X86_64 1
|
||||||
|
# define _LIBUNWIND_TARGET_PPC 1
|
||||||
|
# define _LIBUNWIND_TARGET_PPC64 1
|
||||||
|
# define _LIBUNWIND_TARGET_AARCH64 1
|
||||||
|
# define _LIBUNWIND_TARGET_ARM 1
|
||||||
|
# define _LIBUNWIND_TARGET_OR1K 1
|
||||||
|
# define _LIBUNWIND_TARGET_MIPS_O32 1
|
||||||
|
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
|
||||||
|
# define _LIBUNWIND_TARGET_SPARC 1
|
||||||
|
# define _LIBUNWIND_CONTEXT_SIZE 167
|
||||||
|
# define _LIBUNWIND_CURSOR_SIZE 179
|
||||||
|
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287
|
||||||
|
#endif // _LIBUNWIND_IS_NATIVE_ONLY
|
||||||
|
|
||||||
|
#endif // ____LIBUNWIND_CONFIG_H__
|
|
@ -0,0 +1,835 @@
|
||||||
|
//===---------------------------- libunwind.h -----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Compatible with libunwind API documented at:
|
||||||
|
// http://www.nongnu.org/libunwind/man/libunwind(3).html
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __LIBUNWIND__
|
||||||
|
#define __LIBUNWIND__
|
||||||
|
|
||||||
|
#include <__libunwind_config.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#if __clang__
|
||||||
|
#if __has_include(<Availability.h>)
|
||||||
|
#include <Availability.h>
|
||||||
|
#endif
|
||||||
|
#elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
|
||||||
|
#include <Availability.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
#define LIBUNWIND_AVAIL __attribute__((unavailable))
|
||||||
|
#elif defined(__OSX_AVAILABLE_STARTING)
|
||||||
|
#define LIBUNWIND_AVAIL __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_5_0)
|
||||||
|
#else
|
||||||
|
#include <AvailabilityMacros.h>
|
||||||
|
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
|
||||||
|
#define LIBUNWIND_AVAIL AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
|
||||||
|
#else
|
||||||
|
#define LIBUNWIND_AVAIL __attribute__((unavailable))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define LIBUNWIND_AVAIL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* error codes */
|
||||||
|
enum {
|
||||||
|
UNW_ESUCCESS = 0, /* no error */
|
||||||
|
UNW_EUNSPEC = -6540, /* unspecified (general) error */
|
||||||
|
UNW_ENOMEM = -6541, /* out of memory */
|
||||||
|
UNW_EBADREG = -6542, /* bad register number */
|
||||||
|
UNW_EREADONLYREG = -6543, /* attempt to write read-only register */
|
||||||
|
UNW_ESTOPUNWIND = -6544, /* stop unwinding */
|
||||||
|
UNW_EINVALIDIP = -6545, /* invalid IP */
|
||||||
|
UNW_EBADFRAME = -6546, /* bad frame */
|
||||||
|
UNW_EINVAL = -6547, /* unsupported operation or bad value */
|
||||||
|
UNW_EBADVERSION = -6548, /* unwind info has unsupported version */
|
||||||
|
UNW_ENOINFO = -6549 /* no unwind info found */
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||||
|
, UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unw_context_t {
|
||||||
|
uint64_t data[_LIBUNWIND_CONTEXT_SIZE];
|
||||||
|
};
|
||||||
|
typedef struct unw_context_t unw_context_t;
|
||||||
|
|
||||||
|
struct unw_cursor_t {
|
||||||
|
uint64_t data[_LIBUNWIND_CURSOR_SIZE];
|
||||||
|
};
|
||||||
|
typedef struct unw_cursor_t unw_cursor_t;
|
||||||
|
|
||||||
|
typedef struct unw_addr_space *unw_addr_space_t;
|
||||||
|
|
||||||
|
typedef int unw_regnum_t;
|
||||||
|
typedef uintptr_t unw_word_t;
|
||||||
|
#if defined(__arm__) && !defined(__ARM_DWARF_EH__)
|
||||||
|
typedef uint64_t unw_fpreg_t;
|
||||||
|
#else
|
||||||
|
typedef double unw_fpreg_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct unw_proc_info_t {
|
||||||
|
unw_word_t start_ip; /* start address of function */
|
||||||
|
unw_word_t end_ip; /* address after end of function */
|
||||||
|
unw_word_t lsda; /* address of language specific data area, */
|
||||||
|
/* or zero if not used */
|
||||||
|
unw_word_t handler; /* personality routine, or zero if not used */
|
||||||
|
unw_word_t gp; /* not used */
|
||||||
|
unw_word_t flags; /* not used */
|
||||||
|
uint32_t format; /* compact unwind encoding, or zero if none */
|
||||||
|
uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */
|
||||||
|
unw_word_t unwind_info; /* address of DWARF unwind info, or zero */
|
||||||
|
unw_word_t extra; /* mach_header of mach-o image containing func */
|
||||||
|
};
|
||||||
|
typedef struct unw_proc_info_t unw_proc_info_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
/* Save VFP registers in FSTMX format (instead of FSTMD). */
|
||||||
|
extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||||
|
extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL;
|
||||||
|
//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*);
|
||||||
|
|
||||||
|
extern unw_addr_space_t unw_local_addr_space;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// architecture independent register numbers
|
||||||
|
enum {
|
||||||
|
UNW_REG_IP = -1, // instruction pointer
|
||||||
|
UNW_REG_SP = -2, // stack pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
// 32-bit x86 registers
|
||||||
|
enum {
|
||||||
|
UNW_X86_EAX = 0,
|
||||||
|
UNW_X86_ECX = 1,
|
||||||
|
UNW_X86_EDX = 2,
|
||||||
|
UNW_X86_EBX = 3,
|
||||||
|
UNW_X86_EBP = 4,
|
||||||
|
UNW_X86_ESP = 5,
|
||||||
|
UNW_X86_ESI = 6,
|
||||||
|
UNW_X86_EDI = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
// 64-bit x86_64 registers
|
||||||
|
enum {
|
||||||
|
UNW_X86_64_RAX = 0,
|
||||||
|
UNW_X86_64_RDX = 1,
|
||||||
|
UNW_X86_64_RCX = 2,
|
||||||
|
UNW_X86_64_RBX = 3,
|
||||||
|
UNW_X86_64_RSI = 4,
|
||||||
|
UNW_X86_64_RDI = 5,
|
||||||
|
UNW_X86_64_RBP = 6,
|
||||||
|
UNW_X86_64_RSP = 7,
|
||||||
|
UNW_X86_64_R8 = 8,
|
||||||
|
UNW_X86_64_R9 = 9,
|
||||||
|
UNW_X86_64_R10 = 10,
|
||||||
|
UNW_X86_64_R11 = 11,
|
||||||
|
UNW_X86_64_R12 = 12,
|
||||||
|
UNW_X86_64_R13 = 13,
|
||||||
|
UNW_X86_64_R14 = 14,
|
||||||
|
UNW_X86_64_R15 = 15,
|
||||||
|
UNW_X86_64_RIP = 16,
|
||||||
|
UNW_X86_64_XMM0 = 17,
|
||||||
|
UNW_X86_64_XMM1 = 18,
|
||||||
|
UNW_X86_64_XMM2 = 19,
|
||||||
|
UNW_X86_64_XMM3 = 20,
|
||||||
|
UNW_X86_64_XMM4 = 21,
|
||||||
|
UNW_X86_64_XMM5 = 22,
|
||||||
|
UNW_X86_64_XMM6 = 23,
|
||||||
|
UNW_X86_64_XMM7 = 24,
|
||||||
|
UNW_X86_64_XMM8 = 25,
|
||||||
|
UNW_X86_64_XMM9 = 26,
|
||||||
|
UNW_X86_64_XMM10 = 27,
|
||||||
|
UNW_X86_64_XMM11 = 28,
|
||||||
|
UNW_X86_64_XMM12 = 29,
|
||||||
|
UNW_X86_64_XMM13 = 30,
|
||||||
|
UNW_X86_64_XMM14 = 31,
|
||||||
|
UNW_X86_64_XMM15 = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 32-bit ppc register numbers
|
||||||
|
enum {
|
||||||
|
UNW_PPC_R0 = 0,
|
||||||
|
UNW_PPC_R1 = 1,
|
||||||
|
UNW_PPC_R2 = 2,
|
||||||
|
UNW_PPC_R3 = 3,
|
||||||
|
UNW_PPC_R4 = 4,
|
||||||
|
UNW_PPC_R5 = 5,
|
||||||
|
UNW_PPC_R6 = 6,
|
||||||
|
UNW_PPC_R7 = 7,
|
||||||
|
UNW_PPC_R8 = 8,
|
||||||
|
UNW_PPC_R9 = 9,
|
||||||
|
UNW_PPC_R10 = 10,
|
||||||
|
UNW_PPC_R11 = 11,
|
||||||
|
UNW_PPC_R12 = 12,
|
||||||
|
UNW_PPC_R13 = 13,
|
||||||
|
UNW_PPC_R14 = 14,
|
||||||
|
UNW_PPC_R15 = 15,
|
||||||
|
UNW_PPC_R16 = 16,
|
||||||
|
UNW_PPC_R17 = 17,
|
||||||
|
UNW_PPC_R18 = 18,
|
||||||
|
UNW_PPC_R19 = 19,
|
||||||
|
UNW_PPC_R20 = 20,
|
||||||
|
UNW_PPC_R21 = 21,
|
||||||
|
UNW_PPC_R22 = 22,
|
||||||
|
UNW_PPC_R23 = 23,
|
||||||
|
UNW_PPC_R24 = 24,
|
||||||
|
UNW_PPC_R25 = 25,
|
||||||
|
UNW_PPC_R26 = 26,
|
||||||
|
UNW_PPC_R27 = 27,
|
||||||
|
UNW_PPC_R28 = 28,
|
||||||
|
UNW_PPC_R29 = 29,
|
||||||
|
UNW_PPC_R30 = 30,
|
||||||
|
UNW_PPC_R31 = 31,
|
||||||
|
UNW_PPC_F0 = 32,
|
||||||
|
UNW_PPC_F1 = 33,
|
||||||
|
UNW_PPC_F2 = 34,
|
||||||
|
UNW_PPC_F3 = 35,
|
||||||
|
UNW_PPC_F4 = 36,
|
||||||
|
UNW_PPC_F5 = 37,
|
||||||
|
UNW_PPC_F6 = 38,
|
||||||
|
UNW_PPC_F7 = 39,
|
||||||
|
UNW_PPC_F8 = 40,
|
||||||
|
UNW_PPC_F9 = 41,
|
||||||
|
UNW_PPC_F10 = 42,
|
||||||
|
UNW_PPC_F11 = 43,
|
||||||
|
UNW_PPC_F12 = 44,
|
||||||
|
UNW_PPC_F13 = 45,
|
||||||
|
UNW_PPC_F14 = 46,
|
||||||
|
UNW_PPC_F15 = 47,
|
||||||
|
UNW_PPC_F16 = 48,
|
||||||
|
UNW_PPC_F17 = 49,
|
||||||
|
UNW_PPC_F18 = 50,
|
||||||
|
UNW_PPC_F19 = 51,
|
||||||
|
UNW_PPC_F20 = 52,
|
||||||
|
UNW_PPC_F21 = 53,
|
||||||
|
UNW_PPC_F22 = 54,
|
||||||
|
UNW_PPC_F23 = 55,
|
||||||
|
UNW_PPC_F24 = 56,
|
||||||
|
UNW_PPC_F25 = 57,
|
||||||
|
UNW_PPC_F26 = 58,
|
||||||
|
UNW_PPC_F27 = 59,
|
||||||
|
UNW_PPC_F28 = 60,
|
||||||
|
UNW_PPC_F29 = 61,
|
||||||
|
UNW_PPC_F30 = 62,
|
||||||
|
UNW_PPC_F31 = 63,
|
||||||
|
UNW_PPC_MQ = 64,
|
||||||
|
UNW_PPC_LR = 65,
|
||||||
|
UNW_PPC_CTR = 66,
|
||||||
|
UNW_PPC_AP = 67,
|
||||||
|
UNW_PPC_CR0 = 68,
|
||||||
|
UNW_PPC_CR1 = 69,
|
||||||
|
UNW_PPC_CR2 = 70,
|
||||||
|
UNW_PPC_CR3 = 71,
|
||||||
|
UNW_PPC_CR4 = 72,
|
||||||
|
UNW_PPC_CR5 = 73,
|
||||||
|
UNW_PPC_CR6 = 74,
|
||||||
|
UNW_PPC_CR7 = 75,
|
||||||
|
UNW_PPC_XER = 76,
|
||||||
|
UNW_PPC_V0 = 77,
|
||||||
|
UNW_PPC_V1 = 78,
|
||||||
|
UNW_PPC_V2 = 79,
|
||||||
|
UNW_PPC_V3 = 80,
|
||||||
|
UNW_PPC_V4 = 81,
|
||||||
|
UNW_PPC_V5 = 82,
|
||||||
|
UNW_PPC_V6 = 83,
|
||||||
|
UNW_PPC_V7 = 84,
|
||||||
|
UNW_PPC_V8 = 85,
|
||||||
|
UNW_PPC_V9 = 86,
|
||||||
|
UNW_PPC_V10 = 87,
|
||||||
|
UNW_PPC_V11 = 88,
|
||||||
|
UNW_PPC_V12 = 89,
|
||||||
|
UNW_PPC_V13 = 90,
|
||||||
|
UNW_PPC_V14 = 91,
|
||||||
|
UNW_PPC_V15 = 92,
|
||||||
|
UNW_PPC_V16 = 93,
|
||||||
|
UNW_PPC_V17 = 94,
|
||||||
|
UNW_PPC_V18 = 95,
|
||||||
|
UNW_PPC_V19 = 96,
|
||||||
|
UNW_PPC_V20 = 97,
|
||||||
|
UNW_PPC_V21 = 98,
|
||||||
|
UNW_PPC_V22 = 99,
|
||||||
|
UNW_PPC_V23 = 100,
|
||||||
|
UNW_PPC_V24 = 101,
|
||||||
|
UNW_PPC_V25 = 102,
|
||||||
|
UNW_PPC_V26 = 103,
|
||||||
|
UNW_PPC_V27 = 104,
|
||||||
|
UNW_PPC_V28 = 105,
|
||||||
|
UNW_PPC_V29 = 106,
|
||||||
|
UNW_PPC_V30 = 107,
|
||||||
|
UNW_PPC_V31 = 108,
|
||||||
|
UNW_PPC_VRSAVE = 109,
|
||||||
|
UNW_PPC_VSCR = 110,
|
||||||
|
UNW_PPC_SPE_ACC = 111,
|
||||||
|
UNW_PPC_SPEFSCR = 112
|
||||||
|
};
|
||||||
|
|
||||||
|
// 64-bit ppc register numbers
|
||||||
|
enum {
|
||||||
|
UNW_PPC64_R0 = 0,
|
||||||
|
UNW_PPC64_R1 = 1,
|
||||||
|
UNW_PPC64_R2 = 2,
|
||||||
|
UNW_PPC64_R3 = 3,
|
||||||
|
UNW_PPC64_R4 = 4,
|
||||||
|
UNW_PPC64_R5 = 5,
|
||||||
|
UNW_PPC64_R6 = 6,
|
||||||
|
UNW_PPC64_R7 = 7,
|
||||||
|
UNW_PPC64_R8 = 8,
|
||||||
|
UNW_PPC64_R9 = 9,
|
||||||
|
UNW_PPC64_R10 = 10,
|
||||||
|
UNW_PPC64_R11 = 11,
|
||||||
|
UNW_PPC64_R12 = 12,
|
||||||
|
UNW_PPC64_R13 = 13,
|
||||||
|
UNW_PPC64_R14 = 14,
|
||||||
|
UNW_PPC64_R15 = 15,
|
||||||
|
UNW_PPC64_R16 = 16,
|
||||||
|
UNW_PPC64_R17 = 17,
|
||||||
|
UNW_PPC64_R18 = 18,
|
||||||
|
UNW_PPC64_R19 = 19,
|
||||||
|
UNW_PPC64_R20 = 20,
|
||||||
|
UNW_PPC64_R21 = 21,
|
||||||
|
UNW_PPC64_R22 = 22,
|
||||||
|
UNW_PPC64_R23 = 23,
|
||||||
|
UNW_PPC64_R24 = 24,
|
||||||
|
UNW_PPC64_R25 = 25,
|
||||||
|
UNW_PPC64_R26 = 26,
|
||||||
|
UNW_PPC64_R27 = 27,
|
||||||
|
UNW_PPC64_R28 = 28,
|
||||||
|
UNW_PPC64_R29 = 29,
|
||||||
|
UNW_PPC64_R30 = 30,
|
||||||
|
UNW_PPC64_R31 = 31,
|
||||||
|
UNW_PPC64_F0 = 32,
|
||||||
|
UNW_PPC64_F1 = 33,
|
||||||
|
UNW_PPC64_F2 = 34,
|
||||||
|
UNW_PPC64_F3 = 35,
|
||||||
|
UNW_PPC64_F4 = 36,
|
||||||
|
UNW_PPC64_F5 = 37,
|
||||||
|
UNW_PPC64_F6 = 38,
|
||||||
|
UNW_PPC64_F7 = 39,
|
||||||
|
UNW_PPC64_F8 = 40,
|
||||||
|
UNW_PPC64_F9 = 41,
|
||||||
|
UNW_PPC64_F10 = 42,
|
||||||
|
UNW_PPC64_F11 = 43,
|
||||||
|
UNW_PPC64_F12 = 44,
|
||||||
|
UNW_PPC64_F13 = 45,
|
||||||
|
UNW_PPC64_F14 = 46,
|
||||||
|
UNW_PPC64_F15 = 47,
|
||||||
|
UNW_PPC64_F16 = 48,
|
||||||
|
UNW_PPC64_F17 = 49,
|
||||||
|
UNW_PPC64_F18 = 50,
|
||||||
|
UNW_PPC64_F19 = 51,
|
||||||
|
UNW_PPC64_F20 = 52,
|
||||||
|
UNW_PPC64_F21 = 53,
|
||||||
|
UNW_PPC64_F22 = 54,
|
||||||
|
UNW_PPC64_F23 = 55,
|
||||||
|
UNW_PPC64_F24 = 56,
|
||||||
|
UNW_PPC64_F25 = 57,
|
||||||
|
UNW_PPC64_F26 = 58,
|
||||||
|
UNW_PPC64_F27 = 59,
|
||||||
|
UNW_PPC64_F28 = 60,
|
||||||
|
UNW_PPC64_F29 = 61,
|
||||||
|
UNW_PPC64_F30 = 62,
|
||||||
|
UNW_PPC64_F31 = 63,
|
||||||
|
// 64: reserved
|
||||||
|
UNW_PPC64_LR = 65,
|
||||||
|
UNW_PPC64_CTR = 66,
|
||||||
|
// 67: reserved
|
||||||
|
UNW_PPC64_CR0 = 68,
|
||||||
|
UNW_PPC64_CR1 = 69,
|
||||||
|
UNW_PPC64_CR2 = 70,
|
||||||
|
UNW_PPC64_CR3 = 71,
|
||||||
|
UNW_PPC64_CR4 = 72,
|
||||||
|
UNW_PPC64_CR5 = 73,
|
||||||
|
UNW_PPC64_CR6 = 74,
|
||||||
|
UNW_PPC64_CR7 = 75,
|
||||||
|
UNW_PPC64_XER = 76,
|
||||||
|
UNW_PPC64_V0 = 77,
|
||||||
|
UNW_PPC64_V1 = 78,
|
||||||
|
UNW_PPC64_V2 = 79,
|
||||||
|
UNW_PPC64_V3 = 80,
|
||||||
|
UNW_PPC64_V4 = 81,
|
||||||
|
UNW_PPC64_V5 = 82,
|
||||||
|
UNW_PPC64_V6 = 83,
|
||||||
|
UNW_PPC64_V7 = 84,
|
||||||
|
UNW_PPC64_V8 = 85,
|
||||||
|
UNW_PPC64_V9 = 86,
|
||||||
|
UNW_PPC64_V10 = 87,
|
||||||
|
UNW_PPC64_V11 = 88,
|
||||||
|
UNW_PPC64_V12 = 89,
|
||||||
|
UNW_PPC64_V13 = 90,
|
||||||
|
UNW_PPC64_V14 = 91,
|
||||||
|
UNW_PPC64_V15 = 92,
|
||||||
|
UNW_PPC64_V16 = 93,
|
||||||
|
UNW_PPC64_V17 = 94,
|
||||||
|
UNW_PPC64_V18 = 95,
|
||||||
|
UNW_PPC64_V19 = 96,
|
||||||
|
UNW_PPC64_V20 = 97,
|
||||||
|
UNW_PPC64_V21 = 98,
|
||||||
|
UNW_PPC64_V22 = 99,
|
||||||
|
UNW_PPC64_V23 = 100,
|
||||||
|
UNW_PPC64_V24 = 101,
|
||||||
|
UNW_PPC64_V25 = 102,
|
||||||
|
UNW_PPC64_V26 = 103,
|
||||||
|
UNW_PPC64_V27 = 104,
|
||||||
|
UNW_PPC64_V28 = 105,
|
||||||
|
UNW_PPC64_V29 = 106,
|
||||||
|
UNW_PPC64_V30 = 107,
|
||||||
|
UNW_PPC64_V31 = 108,
|
||||||
|
// 109, 111-113: OpenPOWER ELF V2 ABI: reserved
|
||||||
|
// Borrowing VRSAVE number from PPC32.
|
||||||
|
UNW_PPC64_VRSAVE = 109,
|
||||||
|
UNW_PPC64_VSCR = 110,
|
||||||
|
UNW_PPC64_TFHAR = 114,
|
||||||
|
UNW_PPC64_TFIAR = 115,
|
||||||
|
UNW_PPC64_TEXASR = 116,
|
||||||
|
UNW_PPC64_VS0 = UNW_PPC64_F0,
|
||||||
|
UNW_PPC64_VS1 = UNW_PPC64_F1,
|
||||||
|
UNW_PPC64_VS2 = UNW_PPC64_F2,
|
||||||
|
UNW_PPC64_VS3 = UNW_PPC64_F3,
|
||||||
|
UNW_PPC64_VS4 = UNW_PPC64_F4,
|
||||||
|
UNW_PPC64_VS5 = UNW_PPC64_F5,
|
||||||
|
UNW_PPC64_VS6 = UNW_PPC64_F6,
|
||||||
|
UNW_PPC64_VS7 = UNW_PPC64_F7,
|
||||||
|
UNW_PPC64_VS8 = UNW_PPC64_F8,
|
||||||
|
UNW_PPC64_VS9 = UNW_PPC64_F9,
|
||||||
|
UNW_PPC64_VS10 = UNW_PPC64_F10,
|
||||||
|
UNW_PPC64_VS11 = UNW_PPC64_F11,
|
||||||
|
UNW_PPC64_VS12 = UNW_PPC64_F12,
|
||||||
|
UNW_PPC64_VS13 = UNW_PPC64_F13,
|
||||||
|
UNW_PPC64_VS14 = UNW_PPC64_F14,
|
||||||
|
UNW_PPC64_VS15 = UNW_PPC64_F15,
|
||||||
|
UNW_PPC64_VS16 = UNW_PPC64_F16,
|
||||||
|
UNW_PPC64_VS17 = UNW_PPC64_F17,
|
||||||
|
UNW_PPC64_VS18 = UNW_PPC64_F18,
|
||||||
|
UNW_PPC64_VS19 = UNW_PPC64_F19,
|
||||||
|
UNW_PPC64_VS20 = UNW_PPC64_F20,
|
||||||
|
UNW_PPC64_VS21 = UNW_PPC64_F21,
|
||||||
|
UNW_PPC64_VS22 = UNW_PPC64_F22,
|
||||||
|
UNW_PPC64_VS23 = UNW_PPC64_F23,
|
||||||
|
UNW_PPC64_VS24 = UNW_PPC64_F24,
|
||||||
|
UNW_PPC64_VS25 = UNW_PPC64_F25,
|
||||||
|
UNW_PPC64_VS26 = UNW_PPC64_F26,
|
||||||
|
UNW_PPC64_VS27 = UNW_PPC64_F27,
|
||||||
|
UNW_PPC64_VS28 = UNW_PPC64_F28,
|
||||||
|
UNW_PPC64_VS29 = UNW_PPC64_F29,
|
||||||
|
UNW_PPC64_VS30 = UNW_PPC64_F30,
|
||||||
|
UNW_PPC64_VS31 = UNW_PPC64_F31,
|
||||||
|
UNW_PPC64_VS32 = UNW_PPC64_V0,
|
||||||
|
UNW_PPC64_VS33 = UNW_PPC64_V1,
|
||||||
|
UNW_PPC64_VS34 = UNW_PPC64_V2,
|
||||||
|
UNW_PPC64_VS35 = UNW_PPC64_V3,
|
||||||
|
UNW_PPC64_VS36 = UNW_PPC64_V4,
|
||||||
|
UNW_PPC64_VS37 = UNW_PPC64_V5,
|
||||||
|
UNW_PPC64_VS38 = UNW_PPC64_V6,
|
||||||
|
UNW_PPC64_VS39 = UNW_PPC64_V7,
|
||||||
|
UNW_PPC64_VS40 = UNW_PPC64_V8,
|
||||||
|
UNW_PPC64_VS41 = UNW_PPC64_V9,
|
||||||
|
UNW_PPC64_VS42 = UNW_PPC64_V10,
|
||||||
|
UNW_PPC64_VS43 = UNW_PPC64_V11,
|
||||||
|
UNW_PPC64_VS44 = UNW_PPC64_V12,
|
||||||
|
UNW_PPC64_VS45 = UNW_PPC64_V13,
|
||||||
|
UNW_PPC64_VS46 = UNW_PPC64_V14,
|
||||||
|
UNW_PPC64_VS47 = UNW_PPC64_V15,
|
||||||
|
UNW_PPC64_VS48 = UNW_PPC64_V16,
|
||||||
|
UNW_PPC64_VS49 = UNW_PPC64_V17,
|
||||||
|
UNW_PPC64_VS50 = UNW_PPC64_V18,
|
||||||
|
UNW_PPC64_VS51 = UNW_PPC64_V19,
|
||||||
|
UNW_PPC64_VS52 = UNW_PPC64_V20,
|
||||||
|
UNW_PPC64_VS53 = UNW_PPC64_V21,
|
||||||
|
UNW_PPC64_VS54 = UNW_PPC64_V22,
|
||||||
|
UNW_PPC64_VS55 = UNW_PPC64_V23,
|
||||||
|
UNW_PPC64_VS56 = UNW_PPC64_V24,
|
||||||
|
UNW_PPC64_VS57 = UNW_PPC64_V25,
|
||||||
|
UNW_PPC64_VS58 = UNW_PPC64_V26,
|
||||||
|
UNW_PPC64_VS59 = UNW_PPC64_V27,
|
||||||
|
UNW_PPC64_VS60 = UNW_PPC64_V28,
|
||||||
|
UNW_PPC64_VS61 = UNW_PPC64_V29,
|
||||||
|
UNW_PPC64_VS62 = UNW_PPC64_V30,
|
||||||
|
UNW_PPC64_VS63 = UNW_PPC64_V31
|
||||||
|
};
|
||||||
|
|
||||||
|
// 64-bit ARM64 registers
|
||||||
|
enum {
|
||||||
|
UNW_ARM64_X0 = 0,
|
||||||
|
UNW_ARM64_X1 = 1,
|
||||||
|
UNW_ARM64_X2 = 2,
|
||||||
|
UNW_ARM64_X3 = 3,
|
||||||
|
UNW_ARM64_X4 = 4,
|
||||||
|
UNW_ARM64_X5 = 5,
|
||||||
|
UNW_ARM64_X6 = 6,
|
||||||
|
UNW_ARM64_X7 = 7,
|
||||||
|
UNW_ARM64_X8 = 8,
|
||||||
|
UNW_ARM64_X9 = 9,
|
||||||
|
UNW_ARM64_X10 = 10,
|
||||||
|
UNW_ARM64_X11 = 11,
|
||||||
|
UNW_ARM64_X12 = 12,
|
||||||
|
UNW_ARM64_X13 = 13,
|
||||||
|
UNW_ARM64_X14 = 14,
|
||||||
|
UNW_ARM64_X15 = 15,
|
||||||
|
UNW_ARM64_X16 = 16,
|
||||||
|
UNW_ARM64_X17 = 17,
|
||||||
|
UNW_ARM64_X18 = 18,
|
||||||
|
UNW_ARM64_X19 = 19,
|
||||||
|
UNW_ARM64_X20 = 20,
|
||||||
|
UNW_ARM64_X21 = 21,
|
||||||
|
UNW_ARM64_X22 = 22,
|
||||||
|
UNW_ARM64_X23 = 23,
|
||||||
|
UNW_ARM64_X24 = 24,
|
||||||
|
UNW_ARM64_X25 = 25,
|
||||||
|
UNW_ARM64_X26 = 26,
|
||||||
|
UNW_ARM64_X27 = 27,
|
||||||
|
UNW_ARM64_X28 = 28,
|
||||||
|
UNW_ARM64_X29 = 29,
|
||||||
|
UNW_ARM64_FP = 29,
|
||||||
|
UNW_ARM64_X30 = 30,
|
||||||
|
UNW_ARM64_LR = 30,
|
||||||
|
UNW_ARM64_X31 = 31,
|
||||||
|
UNW_ARM64_SP = 31,
|
||||||
|
// reserved block
|
||||||
|
UNW_ARM64_RA_SIGN_STATE = 34,
|
||||||
|
// reserved block
|
||||||
|
UNW_ARM64_D0 = 64,
|
||||||
|
UNW_ARM64_D1 = 65,
|
||||||
|
UNW_ARM64_D2 = 66,
|
||||||
|
UNW_ARM64_D3 = 67,
|
||||||
|
UNW_ARM64_D4 = 68,
|
||||||
|
UNW_ARM64_D5 = 69,
|
||||||
|
UNW_ARM64_D6 = 70,
|
||||||
|
UNW_ARM64_D7 = 71,
|
||||||
|
UNW_ARM64_D8 = 72,
|
||||||
|
UNW_ARM64_D9 = 73,
|
||||||
|
UNW_ARM64_D10 = 74,
|
||||||
|
UNW_ARM64_D11 = 75,
|
||||||
|
UNW_ARM64_D12 = 76,
|
||||||
|
UNW_ARM64_D13 = 77,
|
||||||
|
UNW_ARM64_D14 = 78,
|
||||||
|
UNW_ARM64_D15 = 79,
|
||||||
|
UNW_ARM64_D16 = 80,
|
||||||
|
UNW_ARM64_D17 = 81,
|
||||||
|
UNW_ARM64_D18 = 82,
|
||||||
|
UNW_ARM64_D19 = 83,
|
||||||
|
UNW_ARM64_D20 = 84,
|
||||||
|
UNW_ARM64_D21 = 85,
|
||||||
|
UNW_ARM64_D22 = 86,
|
||||||
|
UNW_ARM64_D23 = 87,
|
||||||
|
UNW_ARM64_D24 = 88,
|
||||||
|
UNW_ARM64_D25 = 89,
|
||||||
|
UNW_ARM64_D26 = 90,
|
||||||
|
UNW_ARM64_D27 = 91,
|
||||||
|
UNW_ARM64_D28 = 92,
|
||||||
|
UNW_ARM64_D29 = 93,
|
||||||
|
UNW_ARM64_D30 = 94,
|
||||||
|
UNW_ARM64_D31 = 95,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1.
|
||||||
|
// Naming scheme uses recommendations given in Note 4 for VFP-v2 and VFP-v3.
|
||||||
|
// In this scheme, even though the 64-bit floating point registers D0-D31
|
||||||
|
// overlap physically with the 32-bit floating pointer registers S0-S31,
|
||||||
|
// they are given a non-overlapping range of register numbers.
|
||||||
|
//
|
||||||
|
// Commented out ranges are not preserved during unwinding.
|
||||||
|
enum {
|
||||||
|
UNW_ARM_R0 = 0,
|
||||||
|
UNW_ARM_R1 = 1,
|
||||||
|
UNW_ARM_R2 = 2,
|
||||||
|
UNW_ARM_R3 = 3,
|
||||||
|
UNW_ARM_R4 = 4,
|
||||||
|
UNW_ARM_R5 = 5,
|
||||||
|
UNW_ARM_R6 = 6,
|
||||||
|
UNW_ARM_R7 = 7,
|
||||||
|
UNW_ARM_R8 = 8,
|
||||||
|
UNW_ARM_R9 = 9,
|
||||||
|
UNW_ARM_R10 = 10,
|
||||||
|
UNW_ARM_R11 = 11,
|
||||||
|
UNW_ARM_R12 = 12,
|
||||||
|
UNW_ARM_SP = 13, // Logical alias for UNW_REG_SP
|
||||||
|
UNW_ARM_R13 = 13,
|
||||||
|
UNW_ARM_LR = 14,
|
||||||
|
UNW_ARM_R14 = 14,
|
||||||
|
UNW_ARM_IP = 15, // Logical alias for UNW_REG_IP
|
||||||
|
UNW_ARM_R15 = 15,
|
||||||
|
// 16-63 -- OBSOLETE. Used in VFP1 to represent both S0-S31 and D0-D31.
|
||||||
|
UNW_ARM_S0 = 64,
|
||||||
|
UNW_ARM_S1 = 65,
|
||||||
|
UNW_ARM_S2 = 66,
|
||||||
|
UNW_ARM_S3 = 67,
|
||||||
|
UNW_ARM_S4 = 68,
|
||||||
|
UNW_ARM_S5 = 69,
|
||||||
|
UNW_ARM_S6 = 70,
|
||||||
|
UNW_ARM_S7 = 71,
|
||||||
|
UNW_ARM_S8 = 72,
|
||||||
|
UNW_ARM_S9 = 73,
|
||||||
|
UNW_ARM_S10 = 74,
|
||||||
|
UNW_ARM_S11 = 75,
|
||||||
|
UNW_ARM_S12 = 76,
|
||||||
|
UNW_ARM_S13 = 77,
|
||||||
|
UNW_ARM_S14 = 78,
|
||||||
|
UNW_ARM_S15 = 79,
|
||||||
|
UNW_ARM_S16 = 80,
|
||||||
|
UNW_ARM_S17 = 81,
|
||||||
|
UNW_ARM_S18 = 82,
|
||||||
|
UNW_ARM_S19 = 83,
|
||||||
|
UNW_ARM_S20 = 84,
|
||||||
|
UNW_ARM_S21 = 85,
|
||||||
|
UNW_ARM_S22 = 86,
|
||||||
|
UNW_ARM_S23 = 87,
|
||||||
|
UNW_ARM_S24 = 88,
|
||||||
|
UNW_ARM_S25 = 89,
|
||||||
|
UNW_ARM_S26 = 90,
|
||||||
|
UNW_ARM_S27 = 91,
|
||||||
|
UNW_ARM_S28 = 92,
|
||||||
|
UNW_ARM_S29 = 93,
|
||||||
|
UNW_ARM_S30 = 94,
|
||||||
|
UNW_ARM_S31 = 95,
|
||||||
|
// 96-103 -- OBSOLETE. F0-F7. Used by the FPA system. Superseded by VFP.
|
||||||
|
// 104-111 -- wCGR0-wCGR7, ACC0-ACC7 (Intel wireless MMX)
|
||||||
|
UNW_ARM_WR0 = 112,
|
||||||
|
UNW_ARM_WR1 = 113,
|
||||||
|
UNW_ARM_WR2 = 114,
|
||||||
|
UNW_ARM_WR3 = 115,
|
||||||
|
UNW_ARM_WR4 = 116,
|
||||||
|
UNW_ARM_WR5 = 117,
|
||||||
|
UNW_ARM_WR6 = 118,
|
||||||
|
UNW_ARM_WR7 = 119,
|
||||||
|
UNW_ARM_WR8 = 120,
|
||||||
|
UNW_ARM_WR9 = 121,
|
||||||
|
UNW_ARM_WR10 = 122,
|
||||||
|
UNW_ARM_WR11 = 123,
|
||||||
|
UNW_ARM_WR12 = 124,
|
||||||
|
UNW_ARM_WR13 = 125,
|
||||||
|
UNW_ARM_WR14 = 126,
|
||||||
|
UNW_ARM_WR15 = 127,
|
||||||
|
// 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC}
|
||||||
|
// 134-143 -- Reserved
|
||||||
|
// 144-150 -- R8_USR-R14_USR
|
||||||
|
// 151-157 -- R8_FIQ-R14_FIQ
|
||||||
|
// 158-159 -- R13_IRQ-R14_IRQ
|
||||||
|
// 160-161 -- R13_ABT-R14_ABT
|
||||||
|
// 162-163 -- R13_UND-R14_UND
|
||||||
|
// 164-165 -- R13_SVC-R14_SVC
|
||||||
|
// 166-191 -- Reserved
|
||||||
|
UNW_ARM_WC0 = 192,
|
||||||
|
UNW_ARM_WC1 = 193,
|
||||||
|
UNW_ARM_WC2 = 194,
|
||||||
|
UNW_ARM_WC3 = 195,
|
||||||
|
// 196-199 -- wC4-wC7 (Intel wireless MMX control)
|
||||||
|
// 200-255 -- Reserved
|
||||||
|
UNW_ARM_D0 = 256,
|
||||||
|
UNW_ARM_D1 = 257,
|
||||||
|
UNW_ARM_D2 = 258,
|
||||||
|
UNW_ARM_D3 = 259,
|
||||||
|
UNW_ARM_D4 = 260,
|
||||||
|
UNW_ARM_D5 = 261,
|
||||||
|
UNW_ARM_D6 = 262,
|
||||||
|
UNW_ARM_D7 = 263,
|
||||||
|
UNW_ARM_D8 = 264,
|
||||||
|
UNW_ARM_D9 = 265,
|
||||||
|
UNW_ARM_D10 = 266,
|
||||||
|
UNW_ARM_D11 = 267,
|
||||||
|
UNW_ARM_D12 = 268,
|
||||||
|
UNW_ARM_D13 = 269,
|
||||||
|
UNW_ARM_D14 = 270,
|
||||||
|
UNW_ARM_D15 = 271,
|
||||||
|
UNW_ARM_D16 = 272,
|
||||||
|
UNW_ARM_D17 = 273,
|
||||||
|
UNW_ARM_D18 = 274,
|
||||||
|
UNW_ARM_D19 = 275,
|
||||||
|
UNW_ARM_D20 = 276,
|
||||||
|
UNW_ARM_D21 = 277,
|
||||||
|
UNW_ARM_D22 = 278,
|
||||||
|
UNW_ARM_D23 = 279,
|
||||||
|
UNW_ARM_D24 = 280,
|
||||||
|
UNW_ARM_D25 = 281,
|
||||||
|
UNW_ARM_D26 = 282,
|
||||||
|
UNW_ARM_D27 = 283,
|
||||||
|
UNW_ARM_D28 = 284,
|
||||||
|
UNW_ARM_D29 = 285,
|
||||||
|
UNW_ARM_D30 = 286,
|
||||||
|
UNW_ARM_D31 = 287,
|
||||||
|
// 288-319 -- Reserved for VFP/Neon
|
||||||
|
// 320-8191 -- Reserved
|
||||||
|
// 8192-16383 -- Unspecified vendor co-processor register.
|
||||||
|
};
|
||||||
|
|
||||||
|
// OpenRISC1000 register numbers
|
||||||
|
enum {
|
||||||
|
UNW_OR1K_R0 = 0,
|
||||||
|
UNW_OR1K_R1 = 1,
|
||||||
|
UNW_OR1K_R2 = 2,
|
||||||
|
UNW_OR1K_R3 = 3,
|
||||||
|
UNW_OR1K_R4 = 4,
|
||||||
|
UNW_OR1K_R5 = 5,
|
||||||
|
UNW_OR1K_R6 = 6,
|
||||||
|
UNW_OR1K_R7 = 7,
|
||||||
|
UNW_OR1K_R8 = 8,
|
||||||
|
UNW_OR1K_R9 = 9,
|
||||||
|
UNW_OR1K_R10 = 10,
|
||||||
|
UNW_OR1K_R11 = 11,
|
||||||
|
UNW_OR1K_R12 = 12,
|
||||||
|
UNW_OR1K_R13 = 13,
|
||||||
|
UNW_OR1K_R14 = 14,
|
||||||
|
UNW_OR1K_R15 = 15,
|
||||||
|
UNW_OR1K_R16 = 16,
|
||||||
|
UNW_OR1K_R17 = 17,
|
||||||
|
UNW_OR1K_R18 = 18,
|
||||||
|
UNW_OR1K_R19 = 19,
|
||||||
|
UNW_OR1K_R20 = 20,
|
||||||
|
UNW_OR1K_R21 = 21,
|
||||||
|
UNW_OR1K_R22 = 22,
|
||||||
|
UNW_OR1K_R23 = 23,
|
||||||
|
UNW_OR1K_R24 = 24,
|
||||||
|
UNW_OR1K_R25 = 25,
|
||||||
|
UNW_OR1K_R26 = 26,
|
||||||
|
UNW_OR1K_R27 = 27,
|
||||||
|
UNW_OR1K_R28 = 28,
|
||||||
|
UNW_OR1K_R29 = 29,
|
||||||
|
UNW_OR1K_R30 = 30,
|
||||||
|
UNW_OR1K_R31 = 31,
|
||||||
|
UNW_OR1K_EPCR = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// MIPS registers
|
||||||
|
enum {
|
||||||
|
UNW_MIPS_R0 = 0,
|
||||||
|
UNW_MIPS_R1 = 1,
|
||||||
|
UNW_MIPS_R2 = 2,
|
||||||
|
UNW_MIPS_R3 = 3,
|
||||||
|
UNW_MIPS_R4 = 4,
|
||||||
|
UNW_MIPS_R5 = 5,
|
||||||
|
UNW_MIPS_R6 = 6,
|
||||||
|
UNW_MIPS_R7 = 7,
|
||||||
|
UNW_MIPS_R8 = 8,
|
||||||
|
UNW_MIPS_R9 = 9,
|
||||||
|
UNW_MIPS_R10 = 10,
|
||||||
|
UNW_MIPS_R11 = 11,
|
||||||
|
UNW_MIPS_R12 = 12,
|
||||||
|
UNW_MIPS_R13 = 13,
|
||||||
|
UNW_MIPS_R14 = 14,
|
||||||
|
UNW_MIPS_R15 = 15,
|
||||||
|
UNW_MIPS_R16 = 16,
|
||||||
|
UNW_MIPS_R17 = 17,
|
||||||
|
UNW_MIPS_R18 = 18,
|
||||||
|
UNW_MIPS_R19 = 19,
|
||||||
|
UNW_MIPS_R20 = 20,
|
||||||
|
UNW_MIPS_R21 = 21,
|
||||||
|
UNW_MIPS_R22 = 22,
|
||||||
|
UNW_MIPS_R23 = 23,
|
||||||
|
UNW_MIPS_R24 = 24,
|
||||||
|
UNW_MIPS_R25 = 25,
|
||||||
|
UNW_MIPS_R26 = 26,
|
||||||
|
UNW_MIPS_R27 = 27,
|
||||||
|
UNW_MIPS_R28 = 28,
|
||||||
|
UNW_MIPS_R29 = 29,
|
||||||
|
UNW_MIPS_R30 = 30,
|
||||||
|
UNW_MIPS_R31 = 31,
|
||||||
|
UNW_MIPS_F0 = 32,
|
||||||
|
UNW_MIPS_F1 = 33,
|
||||||
|
UNW_MIPS_F2 = 34,
|
||||||
|
UNW_MIPS_F3 = 35,
|
||||||
|
UNW_MIPS_F4 = 36,
|
||||||
|
UNW_MIPS_F5 = 37,
|
||||||
|
UNW_MIPS_F6 = 38,
|
||||||
|
UNW_MIPS_F7 = 39,
|
||||||
|
UNW_MIPS_F8 = 40,
|
||||||
|
UNW_MIPS_F9 = 41,
|
||||||
|
UNW_MIPS_F10 = 42,
|
||||||
|
UNW_MIPS_F11 = 43,
|
||||||
|
UNW_MIPS_F12 = 44,
|
||||||
|
UNW_MIPS_F13 = 45,
|
||||||
|
UNW_MIPS_F14 = 46,
|
||||||
|
UNW_MIPS_F15 = 47,
|
||||||
|
UNW_MIPS_F16 = 48,
|
||||||
|
UNW_MIPS_F17 = 49,
|
||||||
|
UNW_MIPS_F18 = 50,
|
||||||
|
UNW_MIPS_F19 = 51,
|
||||||
|
UNW_MIPS_F20 = 52,
|
||||||
|
UNW_MIPS_F21 = 53,
|
||||||
|
UNW_MIPS_F22 = 54,
|
||||||
|
UNW_MIPS_F23 = 55,
|
||||||
|
UNW_MIPS_F24 = 56,
|
||||||
|
UNW_MIPS_F25 = 57,
|
||||||
|
UNW_MIPS_F26 = 58,
|
||||||
|
UNW_MIPS_F27 = 59,
|
||||||
|
UNW_MIPS_F28 = 60,
|
||||||
|
UNW_MIPS_F29 = 61,
|
||||||
|
UNW_MIPS_F30 = 62,
|
||||||
|
UNW_MIPS_F31 = 63,
|
||||||
|
UNW_MIPS_HI = 64,
|
||||||
|
UNW_MIPS_LO = 65,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SPARC registers
|
||||||
|
enum {
|
||||||
|
UNW_SPARC_G0 = 0,
|
||||||
|
UNW_SPARC_G1 = 1,
|
||||||
|
UNW_SPARC_G2 = 2,
|
||||||
|
UNW_SPARC_G3 = 3,
|
||||||
|
UNW_SPARC_G4 = 4,
|
||||||
|
UNW_SPARC_G5 = 5,
|
||||||
|
UNW_SPARC_G6 = 6,
|
||||||
|
UNW_SPARC_G7 = 7,
|
||||||
|
UNW_SPARC_O0 = 8,
|
||||||
|
UNW_SPARC_O1 = 9,
|
||||||
|
UNW_SPARC_O2 = 10,
|
||||||
|
UNW_SPARC_O3 = 11,
|
||||||
|
UNW_SPARC_O4 = 12,
|
||||||
|
UNW_SPARC_O5 = 13,
|
||||||
|
UNW_SPARC_O6 = 14,
|
||||||
|
UNW_SPARC_O7 = 15,
|
||||||
|
UNW_SPARC_L0 = 16,
|
||||||
|
UNW_SPARC_L1 = 17,
|
||||||
|
UNW_SPARC_L2 = 18,
|
||||||
|
UNW_SPARC_L3 = 19,
|
||||||
|
UNW_SPARC_L4 = 20,
|
||||||
|
UNW_SPARC_L5 = 21,
|
||||||
|
UNW_SPARC_L6 = 22,
|
||||||
|
UNW_SPARC_L7 = 23,
|
||||||
|
UNW_SPARC_I0 = 24,
|
||||||
|
UNW_SPARC_I1 = 25,
|
||||||
|
UNW_SPARC_I2 = 26,
|
||||||
|
UNW_SPARC_I3 = 27,
|
||||||
|
UNW_SPARC_I4 = 28,
|
||||||
|
UNW_SPARC_I5 = 29,
|
||||||
|
UNW_SPARC_I6 = 30,
|
||||||
|
UNW_SPARC_I7 = 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,477 @@
|
||||||
|
//===------------------ mach-o/compact_unwind_encoding.h ------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Darwin's alternative to DWARF based unwind encodings.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __COMPACT_UNWIND_ENCODING__
|
||||||
|
#define __COMPACT_UNWIND_ENCODING__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compilers can emit standard DWARF FDEs in the __TEXT,__eh_frame section
|
||||||
|
// of object files. Or compilers can emit compact unwind information in
|
||||||
|
// the __LD,__compact_unwind section.
|
||||||
|
//
|
||||||
|
// When the linker creates a final linked image, it will create a
|
||||||
|
// __TEXT,__unwind_info section. This section is a small and fast way for the
|
||||||
|
// runtime to access unwind info for any given function. If the compiler
|
||||||
|
// emitted compact unwind info for the function, that compact unwind info will
|
||||||
|
// be encoded in the __TEXT,__unwind_info section. If the compiler emitted
|
||||||
|
// DWARF unwind info, the __TEXT,__unwind_info section will contain the offset
|
||||||
|
// of the FDE in the __TEXT,__eh_frame section in the final linked image.
|
||||||
|
//
|
||||||
|
// Note: Previously, the linker would transform some DWARF unwind infos into
|
||||||
|
// compact unwind info. But that is fragile and no longer done.
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// The compact unwind endoding is a 32-bit value which encoded in an
|
||||||
|
// architecture specific way, which registers to restore from where, and how
|
||||||
|
// to unwind out of the function.
|
||||||
|
//
|
||||||
|
typedef uint32_t compact_unwind_encoding_t;
|
||||||
|
|
||||||
|
|
||||||
|
// architecture independent bits
|
||||||
|
enum {
|
||||||
|
UNWIND_IS_NOT_FUNCTION_START = 0x80000000,
|
||||||
|
UNWIND_HAS_LSDA = 0x40000000,
|
||||||
|
UNWIND_PERSONALITY_MASK = 0x30000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// x86
|
||||||
|
//
|
||||||
|
// 1-bit: start
|
||||||
|
// 1-bit: has lsda
|
||||||
|
// 2-bit: personality index
|
||||||
|
//
|
||||||
|
// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=DWARF
|
||||||
|
// ebp based:
|
||||||
|
// 15-bits (5*3-bits per reg) register permutation
|
||||||
|
// 8-bits for stack offset
|
||||||
|
// frameless:
|
||||||
|
// 8-bits stack size
|
||||||
|
// 3-bits stack adjust
|
||||||
|
// 3-bits register count
|
||||||
|
// 10-bits register permutation
|
||||||
|
//
|
||||||
|
enum {
|
||||||
|
UNWIND_X86_MODE_MASK = 0x0F000000,
|
||||||
|
UNWIND_X86_MODE_EBP_FRAME = 0x01000000,
|
||||||
|
UNWIND_X86_MODE_STACK_IMMD = 0x02000000,
|
||||||
|
UNWIND_X86_MODE_STACK_IND = 0x03000000,
|
||||||
|
UNWIND_X86_MODE_DWARF = 0x04000000,
|
||||||
|
|
||||||
|
UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF,
|
||||||
|
UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000,
|
||||||
|
|
||||||
|
UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000,
|
||||||
|
UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000,
|
||||||
|
UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
|
||||||
|
UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
|
||||||
|
|
||||||
|
UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UNWIND_X86_REG_NONE = 0,
|
||||||
|
UNWIND_X86_REG_EBX = 1,
|
||||||
|
UNWIND_X86_REG_ECX = 2,
|
||||||
|
UNWIND_X86_REG_EDX = 3,
|
||||||
|
UNWIND_X86_REG_EDI = 4,
|
||||||
|
UNWIND_X86_REG_ESI = 5,
|
||||||
|
UNWIND_X86_REG_EBP = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// For x86 there are four modes for the compact unwind encoding:
|
||||||
|
// UNWIND_X86_MODE_EBP_FRAME:
|
||||||
|
// EBP based frame where EBP is push on stack immediately after return address,
|
||||||
|
// then ESP is moved to EBP. Thus, to unwind ESP is restored with the current
|
||||||
|
// EPB value, then EBP is restored by popping off the stack, and the return
|
||||||
|
// is done by popping the stack once more into the pc.
|
||||||
|
// All non-volatile registers that need to be restored must have been saved
|
||||||
|
// in a small range in the stack that starts EBP-4 to EBP-1020. The offset/4
|
||||||
|
// is encoded in the UNWIND_X86_EBP_FRAME_OFFSET bits. The registers saved
|
||||||
|
// are encoded in the UNWIND_X86_EBP_FRAME_REGISTERS bits as five 3-bit entries.
|
||||||
|
// Each entry contains which register to restore.
|
||||||
|
// UNWIND_X86_MODE_STACK_IMMD:
|
||||||
|
// A "frameless" (EBP not used as frame pointer) function with a small
|
||||||
|
// constant stack size. To return, a constant (encoded in the compact
|
||||||
|
// unwind encoding) is added to the ESP. Then the return is done by
|
||||||
|
// popping the stack into the pc.
|
||||||
|
// All non-volatile registers that need to be restored must have been saved
|
||||||
|
// on the stack immediately after the return address. The stack_size/4 is
|
||||||
|
// encoded in the UNWIND_X86_FRAMELESS_STACK_SIZE (max stack size is 1024).
|
||||||
|
// The number of registers saved is encoded in UNWIND_X86_FRAMELESS_STACK_REG_COUNT.
|
||||||
|
// UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION constains which registers were
|
||||||
|
// saved and their order.
|
||||||
|
// UNWIND_X86_MODE_STACK_IND:
|
||||||
|
// A "frameless" (EBP not used as frame pointer) function large constant
|
||||||
|
// stack size. This case is like the previous, except the stack size is too
|
||||||
|
// large to encode in the compact unwind encoding. Instead it requires that
|
||||||
|
// the function contains "subl $nnnnnnnn,ESP" in its prolog. The compact
|
||||||
|
// encoding contains the offset to the nnnnnnnn value in the function in
|
||||||
|
// UNWIND_X86_FRAMELESS_STACK_SIZE.
|
||||||
|
// UNWIND_X86_MODE_DWARF:
|
||||||
|
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||||
|
// compact encoding is the offset of the DWARF FDE in the __eh_frame section.
|
||||||
|
// This mode is never used in object files. It is only generated by the
|
||||||
|
// linker in final linked images which have only DWARF unwind info for a
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// The permutation encoding is a Lehmer code sequence encoded into a
|
||||||
|
// single variable-base number so we can encode the ordering of up to
|
||||||
|
// six registers in a 10-bit space.
|
||||||
|
//
|
||||||
|
// The following is the algorithm used to create the permutation encoding used
|
||||||
|
// with frameless stacks. It is passed the number of registers to be saved and
|
||||||
|
// an array of the register numbers saved.
|
||||||
|
//
|
||||||
|
//uint32_t permute_encode(uint32_t registerCount, const uint32_t registers[6])
|
||||||
|
//{
|
||||||
|
// uint32_t renumregs[6];
|
||||||
|
// for (int i=6-registerCount; i < 6; ++i) {
|
||||||
|
// int countless = 0;
|
||||||
|
// for (int j=6-registerCount; j < i; ++j) {
|
||||||
|
// if ( registers[j] < registers[i] )
|
||||||
|
// ++countless;
|
||||||
|
// }
|
||||||
|
// renumregs[i] = registers[i] - countless -1;
|
||||||
|
// }
|
||||||
|
// uint32_t permutationEncoding = 0;
|
||||||
|
// switch ( registerCount ) {
|
||||||
|
// case 6:
|
||||||
|
// permutationEncoding |= (120*renumregs[0] + 24*renumregs[1]
|
||||||
|
// + 6*renumregs[2] + 2*renumregs[3]
|
||||||
|
// + renumregs[4]);
|
||||||
|
// break;
|
||||||
|
// case 5:
|
||||||
|
// permutationEncoding |= (120*renumregs[1] + 24*renumregs[2]
|
||||||
|
// + 6*renumregs[3] + 2*renumregs[4]
|
||||||
|
// + renumregs[5]);
|
||||||
|
// break;
|
||||||
|
// case 4:
|
||||||
|
// permutationEncoding |= (60*renumregs[2] + 12*renumregs[3]
|
||||||
|
// + 3*renumregs[4] + renumregs[5]);
|
||||||
|
// break;
|
||||||
|
// case 3:
|
||||||
|
// permutationEncoding |= (20*renumregs[3] + 4*renumregs[4]
|
||||||
|
// + renumregs[5]);
|
||||||
|
// break;
|
||||||
|
// case 2:
|
||||||
|
// permutationEncoding |= (5*renumregs[4] + renumregs[5]);
|
||||||
|
// break;
|
||||||
|
// case 1:
|
||||||
|
// permutationEncoding |= (renumregs[5]);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// return permutationEncoding;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// x86_64
|
||||||
|
//
|
||||||
|
// 1-bit: start
|
||||||
|
// 1-bit: has lsda
|
||||||
|
// 2-bit: personality index
|
||||||
|
//
|
||||||
|
// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=DWARF
|
||||||
|
// rbp based:
|
||||||
|
// 15-bits (5*3-bits per reg) register permutation
|
||||||
|
// 8-bits for stack offset
|
||||||
|
// frameless:
|
||||||
|
// 8-bits stack size
|
||||||
|
// 3-bits stack adjust
|
||||||
|
// 3-bits register count
|
||||||
|
// 10-bits register permutation
|
||||||
|
//
|
||||||
|
enum {
|
||||||
|
UNWIND_X86_64_MODE_MASK = 0x0F000000,
|
||||||
|
UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000,
|
||||||
|
UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000,
|
||||||
|
UNWIND_X86_64_MODE_STACK_IND = 0x03000000,
|
||||||
|
UNWIND_X86_64_MODE_DWARF = 0x04000000,
|
||||||
|
|
||||||
|
UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF,
|
||||||
|
UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000,
|
||||||
|
|
||||||
|
UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000,
|
||||||
|
UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000,
|
||||||
|
UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
|
||||||
|
UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
|
||||||
|
|
||||||
|
UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UNWIND_X86_64_REG_NONE = 0,
|
||||||
|
UNWIND_X86_64_REG_RBX = 1,
|
||||||
|
UNWIND_X86_64_REG_R12 = 2,
|
||||||
|
UNWIND_X86_64_REG_R13 = 3,
|
||||||
|
UNWIND_X86_64_REG_R14 = 4,
|
||||||
|
UNWIND_X86_64_REG_R15 = 5,
|
||||||
|
UNWIND_X86_64_REG_RBP = 6,
|
||||||
|
};
|
||||||
|
//
|
||||||
|
// For x86_64 there are four modes for the compact unwind encoding:
|
||||||
|
// UNWIND_X86_64_MODE_RBP_FRAME:
|
||||||
|
// RBP based frame where RBP is push on stack immediately after return address,
|
||||||
|
// then RSP is moved to RBP. Thus, to unwind RSP is restored with the current
|
||||||
|
// EPB value, then RBP is restored by popping off the stack, and the return
|
||||||
|
// is done by popping the stack once more into the pc.
|
||||||
|
// All non-volatile registers that need to be restored must have been saved
|
||||||
|
// in a small range in the stack that starts RBP-8 to RBP-2040. The offset/8
|
||||||
|
// is encoded in the UNWIND_X86_64_RBP_FRAME_OFFSET bits. The registers saved
|
||||||
|
// are encoded in the UNWIND_X86_64_RBP_FRAME_REGISTERS bits as five 3-bit entries.
|
||||||
|
// Each entry contains which register to restore.
|
||||||
|
// UNWIND_X86_64_MODE_STACK_IMMD:
|
||||||
|
// A "frameless" (RBP not used as frame pointer) function with a small
|
||||||
|
// constant stack size. To return, a constant (encoded in the compact
|
||||||
|
// unwind encoding) is added to the RSP. Then the return is done by
|
||||||
|
// popping the stack into the pc.
|
||||||
|
// All non-volatile registers that need to be restored must have been saved
|
||||||
|
// on the stack immediately after the return address. The stack_size/8 is
|
||||||
|
// encoded in the UNWIND_X86_64_FRAMELESS_STACK_SIZE (max stack size is 2048).
|
||||||
|
// The number of registers saved is encoded in UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT.
|
||||||
|
// UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION constains which registers were
|
||||||
|
// saved and their order.
|
||||||
|
// UNWIND_X86_64_MODE_STACK_IND:
|
||||||
|
// A "frameless" (RBP not used as frame pointer) function large constant
|
||||||
|
// stack size. This case is like the previous, except the stack size is too
|
||||||
|
// large to encode in the compact unwind encoding. Instead it requires that
|
||||||
|
// the function contains "subq $nnnnnnnn,RSP" in its prolog. The compact
|
||||||
|
// encoding contains the offset to the nnnnnnnn value in the function in
|
||||||
|
// UNWIND_X86_64_FRAMELESS_STACK_SIZE.
|
||||||
|
// UNWIND_X86_64_MODE_DWARF:
|
||||||
|
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||||
|
// compact encoding is the offset of the DWARF FDE in the __eh_frame section.
|
||||||
|
// This mode is never used in object files. It is only generated by the
|
||||||
|
// linker in final linked images which have only DWARF unwind info for a
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// ARM64
|
||||||
|
//
|
||||||
|
// 1-bit: start
|
||||||
|
// 1-bit: has lsda
|
||||||
|
// 2-bit: personality index
|
||||||
|
//
|
||||||
|
// 4-bits: 4=frame-based, 3=DWARF, 2=frameless
|
||||||
|
// frameless:
|
||||||
|
// 12-bits of stack size
|
||||||
|
// frame-based:
|
||||||
|
// 4-bits D reg pairs saved
|
||||||
|
// 5-bits X reg pairs saved
|
||||||
|
// DWARF:
|
||||||
|
// 24-bits offset of DWARF FDE in __eh_frame section
|
||||||
|
//
|
||||||
|
enum {
|
||||||
|
UNWIND_ARM64_MODE_MASK = 0x0F000000,
|
||||||
|
UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
|
||||||
|
UNWIND_ARM64_MODE_DWARF = 0x03000000,
|
||||||
|
UNWIND_ARM64_MODE_FRAME = 0x04000000,
|
||||||
|
|
||||||
|
UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
|
||||||
|
UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
|
||||||
|
UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
|
||||||
|
UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
|
||||||
|
UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
|
||||||
|
UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
|
||||||
|
UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
|
||||||
|
UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
|
||||||
|
UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
|
||||||
|
|
||||||
|
UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
|
||||||
|
UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||||
|
};
|
||||||
|
// For arm64 there are three modes for the compact unwind encoding:
|
||||||
|
// UNWIND_ARM64_MODE_FRAME:
|
||||||
|
// This is a standard arm64 prolog where FP/LR are immediately pushed on the
|
||||||
|
// stack, then SP is copied to FP. If there are any non-volatile registers
|
||||||
|
// saved, then are copied into the stack frame in pairs in a contiguous
|
||||||
|
// range right below the saved FP/LR pair. Any subset of the five X pairs
|
||||||
|
// and four D pairs can be saved, but the memory layout must be in register
|
||||||
|
// number order.
|
||||||
|
// UNWIND_ARM64_MODE_FRAMELESS:
|
||||||
|
// A "frameless" leaf function, where FP/LR are not saved. The return address
|
||||||
|
// remains in LR throughout the function. If any non-volatile registers
|
||||||
|
// are saved, they must be pushed onto the stack before any stack space is
|
||||||
|
// allocated for local variables. The stack sized (including any saved
|
||||||
|
// non-volatile registers) divided by 16 is encoded in the bits
|
||||||
|
// UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK.
|
||||||
|
// UNWIND_ARM64_MODE_DWARF:
|
||||||
|
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||||
|
// compact encoding is the offset of the DWARF FDE in the __eh_frame section.
|
||||||
|
// This mode is never used in object files. It is only generated by the
|
||||||
|
// linker in final linked images which have only DWARF unwind info for a
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Relocatable Object Files: __LD,__compact_unwind
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// A compiler can generated compact unwind information for a function by adding
|
||||||
|
// a "row" to the __LD,__compact_unwind section. This section has the
|
||||||
|
// S_ATTR_DEBUG bit set, so the section will be ignored by older linkers.
|
||||||
|
// It is removed by the new linker, so never ends up in final executables.
|
||||||
|
// This section is a table, initially with one row per function (that needs
|
||||||
|
// unwind info). The table columns and some conceptual entries are:
|
||||||
|
//
|
||||||
|
// range-start pointer to start of function/range
|
||||||
|
// range-length
|
||||||
|
// compact-unwind-encoding 32-bit encoding
|
||||||
|
// personality-function or zero if no personality function
|
||||||
|
// lsda or zero if no LSDA data
|
||||||
|
//
|
||||||
|
// The length and encoding fields are 32-bits. The other are all pointer sized.
|
||||||
|
//
|
||||||
|
// In x86_64 assembly, these entry would look like:
|
||||||
|
//
|
||||||
|
// .section __LD,__compact_unwind,regular,debug
|
||||||
|
//
|
||||||
|
// #compact unwind for _foo
|
||||||
|
// .quad _foo
|
||||||
|
// .set L1,LfooEnd-_foo
|
||||||
|
// .long L1
|
||||||
|
// .long 0x01010001
|
||||||
|
// .quad 0
|
||||||
|
// .quad 0
|
||||||
|
//
|
||||||
|
// #compact unwind for _bar
|
||||||
|
// .quad _bar
|
||||||
|
// .set L2,LbarEnd-_bar
|
||||||
|
// .long L2
|
||||||
|
// .long 0x01020011
|
||||||
|
// .quad __gxx_personality
|
||||||
|
// .quad except_tab1
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Notes: There is no need for any labels in the the __compact_unwind section.
|
||||||
|
// The use of the .set directive is to force the evaluation of the
|
||||||
|
// range-length at assembly time, instead of generating relocations.
|
||||||
|
//
|
||||||
|
// To support future compiler optimizations where which non-volatile registers
|
||||||
|
// are saved changes within a function (e.g. delay saving non-volatiles until
|
||||||
|
// necessary), there can by multiple lines in the __compact_unwind table for one
|
||||||
|
// function, each with a different (non-overlapping) range and each with
|
||||||
|
// different compact unwind encodings that correspond to the non-volatiles
|
||||||
|
// saved at that range of the function.
|
||||||
|
//
|
||||||
|
// If a particular function is so wacky that there is no compact unwind way
|
||||||
|
// to encode it, then the compiler can emit traditional DWARF unwind info.
|
||||||
|
// The runtime will use which ever is available.
|
||||||
|
//
|
||||||
|
// Runtime support for compact unwind encodings are only available on 10.6
|
||||||
|
// and later. So, the compiler should not generate it when targeting pre-10.6.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Final Linked Images: __TEXT,__unwind_info
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//
|
||||||
|
// The __TEXT,__unwind_info section is laid out for an efficient two level lookup.
|
||||||
|
// The header of the section contains a coarse index that maps function address
|
||||||
|
// to the page (4096 byte block) containing the unwind info for that function.
|
||||||
|
//
|
||||||
|
|
||||||
|
#define UNWIND_SECTION_VERSION 1
|
||||||
|
struct unwind_info_section_header
|
||||||
|
{
|
||||||
|
uint32_t version; // UNWIND_SECTION_VERSION
|
||||||
|
uint32_t commonEncodingsArraySectionOffset;
|
||||||
|
uint32_t commonEncodingsArrayCount;
|
||||||
|
uint32_t personalityArraySectionOffset;
|
||||||
|
uint32_t personalityArrayCount;
|
||||||
|
uint32_t indexSectionOffset;
|
||||||
|
uint32_t indexCount;
|
||||||
|
// compact_unwind_encoding_t[]
|
||||||
|
// uint32_t personalities[]
|
||||||
|
// unwind_info_section_header_index_entry[]
|
||||||
|
// unwind_info_section_header_lsda_index_entry[]
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unwind_info_section_header_index_entry
|
||||||
|
{
|
||||||
|
uint32_t functionOffset;
|
||||||
|
uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page
|
||||||
|
uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unwind_info_section_header_lsda_index_entry
|
||||||
|
{
|
||||||
|
uint32_t functionOffset;
|
||||||
|
uint32_t lsdaOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// There are two kinds of second level index pages: regular and compressed.
|
||||||
|
// A compressed page can hold up to 1021 entries, but it cannot be used
|
||||||
|
// if too many different encoding types are used. The regular page holds
|
||||||
|
// 511 entries.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct unwind_info_regular_second_level_entry
|
||||||
|
{
|
||||||
|
uint32_t functionOffset;
|
||||||
|
compact_unwind_encoding_t encoding;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UNWIND_SECOND_LEVEL_REGULAR 2
|
||||||
|
struct unwind_info_regular_second_level_page_header
|
||||||
|
{
|
||||||
|
uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR
|
||||||
|
uint16_t entryPageOffset;
|
||||||
|
uint16_t entryCount;
|
||||||
|
// entry array
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UNWIND_SECOND_LEVEL_COMPRESSED 3
|
||||||
|
struct unwind_info_compressed_second_level_page_header
|
||||||
|
{
|
||||||
|
uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED
|
||||||
|
uint16_t entryPageOffset;
|
||||||
|
uint16_t entryCount;
|
||||||
|
uint16_t encodingsPageOffset;
|
||||||
|
uint16_t encodingsCount;
|
||||||
|
// 32-bit entry array
|
||||||
|
// encodings array
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF)
|
||||||
|
#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,400 @@
|
||||||
|
//===------------------------------- unwind.h -----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// C++ ABI Level 1 ABI documented at:
|
||||||
|
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __UNWIND_H__
|
||||||
|
#define __UNWIND_H__
|
||||||
|
|
||||||
|
#include <__libunwind_config.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) && defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ntverp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#define LIBUNWIND_UNAVAIL __attribute__ (( unavailable ))
|
||||||
|
#else
|
||||||
|
#define LIBUNWIND_UNAVAIL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
_URC_NO_REASON = 0,
|
||||||
|
_URC_OK = 0,
|
||||||
|
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
||||||
|
_URC_FATAL_PHASE2_ERROR = 2,
|
||||||
|
_URC_FATAL_PHASE1_ERROR = 3,
|
||||||
|
_URC_NORMAL_STOP = 4,
|
||||||
|
_URC_END_OF_STACK = 5,
|
||||||
|
_URC_HANDLER_FOUND = 6,
|
||||||
|
_URC_INSTALL_CONTEXT = 7,
|
||||||
|
_URC_CONTINUE_UNWIND = 8,
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
_URC_FAILURE = 9
|
||||||
|
#endif
|
||||||
|
} _Unwind_Reason_Code;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
_UA_SEARCH_PHASE = 1,
|
||||||
|
_UA_CLEANUP_PHASE = 2,
|
||||||
|
_UA_HANDLER_FRAME = 4,
|
||||||
|
_UA_FORCE_UNWIND = 8,
|
||||||
|
_UA_END_OF_STACK = 16 // gcc extension to C++ ABI
|
||||||
|
} _Unwind_Action;
|
||||||
|
|
||||||
|
typedef struct _Unwind_Context _Unwind_Context; // opaque
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
typedef uint32_t _Unwind_State;
|
||||||
|
|
||||||
|
static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0;
|
||||||
|
static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1;
|
||||||
|
static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2;
|
||||||
|
static const _Unwind_State _US_ACTION_MASK = 3;
|
||||||
|
/* Undocumented flag for force unwinding. */
|
||||||
|
static const _Unwind_State _US_FORCE_UNWIND = 8;
|
||||||
|
|
||||||
|
typedef uint32_t _Unwind_EHT_Header;
|
||||||
|
|
||||||
|
struct _Unwind_Control_Block;
|
||||||
|
typedef struct _Unwind_Control_Block _Unwind_Control_Block;
|
||||||
|
typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */
|
||||||
|
|
||||||
|
struct _Unwind_Control_Block {
|
||||||
|
uint64_t exception_class;
|
||||||
|
void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*);
|
||||||
|
|
||||||
|
/* Unwinder cache, private fields for the unwinder's use */
|
||||||
|
struct {
|
||||||
|
uint32_t reserved1; /* init reserved1 to 0, then don't touch */
|
||||||
|
uint32_t reserved2;
|
||||||
|
uint32_t reserved3;
|
||||||
|
uint32_t reserved4;
|
||||||
|
uint32_t reserved5;
|
||||||
|
} unwinder_cache;
|
||||||
|
|
||||||
|
/* Propagation barrier cache (valid after phase 1): */
|
||||||
|
struct {
|
||||||
|
uint32_t sp;
|
||||||
|
uint32_t bitpattern[5];
|
||||||
|
} barrier_cache;
|
||||||
|
|
||||||
|
/* Cleanup cache (preserved over cleanup): */
|
||||||
|
struct {
|
||||||
|
uint32_t bitpattern[4];
|
||||||
|
} cleanup_cache;
|
||||||
|
|
||||||
|
/* Pr cache (for pr's benefit): */
|
||||||
|
struct {
|
||||||
|
uint32_t fnstart; /* function start address */
|
||||||
|
_Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */
|
||||||
|
uint32_t additional;
|
||||||
|
uint32_t reserved1;
|
||||||
|
} pr_cache;
|
||||||
|
|
||||||
|
long long int :0; /* Enforce the 8-byte alignment */
|
||||||
|
} __attribute__((__aligned__(8)));
|
||||||
|
|
||||||
|
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
|
||||||
|
(_Unwind_State state,
|
||||||
|
_Unwind_Exception* exceptionObject,
|
||||||
|
struct _Unwind_Context* context);
|
||||||
|
|
||||||
|
typedef _Unwind_Reason_Code (*__personality_routine)
|
||||||
|
(_Unwind_State state,
|
||||||
|
_Unwind_Exception* exceptionObject,
|
||||||
|
struct _Unwind_Context* context);
|
||||||
|
#else
|
||||||
|
struct _Unwind_Context; // opaque
|
||||||
|
struct _Unwind_Exception; // forward declaration
|
||||||
|
typedef struct _Unwind_Exception _Unwind_Exception;
|
||||||
|
|
||||||
|
struct _Unwind_Exception {
|
||||||
|
uint64_t exception_class;
|
||||||
|
void (*exception_cleanup)(_Unwind_Reason_Code reason,
|
||||||
|
_Unwind_Exception *exc);
|
||||||
|
#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
uintptr_t private_[6];
|
||||||
|
#else
|
||||||
|
uintptr_t private_1; // non-zero means forced unwind
|
||||||
|
uintptr_t private_2; // holds sp that phase1 found for phase2 to use
|
||||||
|
#endif
|
||||||
|
#if __SIZEOF_POINTER__ == 4
|
||||||
|
// The implementation of _Unwind_Exception uses an attribute mode on the
|
||||||
|
// above fields which has the side effect of causing this whole struct to
|
||||||
|
// round up to 32 bytes in size (48 with SEH). To be more explicit, we add
|
||||||
|
// pad fields added for binary compatibility.
|
||||||
|
uint32_t reserved[3];
|
||||||
|
#endif
|
||||||
|
// The Itanium ABI requires that _Unwind_Exception objects are "double-word
|
||||||
|
// aligned". GCC has interpreted this to mean "use the maximum useful
|
||||||
|
// alignment for the target"; so do we.
|
||||||
|
} __attribute__((__aligned__));
|
||||||
|
|
||||||
|
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
|
||||||
|
(int version,
|
||||||
|
_Unwind_Action actions,
|
||||||
|
uint64_t exceptionClass,
|
||||||
|
_Unwind_Exception* exceptionObject,
|
||||||
|
struct _Unwind_Context* context,
|
||||||
|
void* stop_parameter );
|
||||||
|
|
||||||
|
typedef _Unwind_Reason_Code (*__personality_routine)
|
||||||
|
(int version,
|
||||||
|
_Unwind_Action actions,
|
||||||
|
uint64_t exceptionClass,
|
||||||
|
_Unwind_Exception* exceptionObject,
|
||||||
|
struct _Unwind_Context* context);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// The following are the base functions documented by the C++ ABI
|
||||||
|
//
|
||||||
|
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||||
|
extern _Unwind_Reason_Code
|
||||||
|
_Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object);
|
||||||
|
extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object);
|
||||||
|
#else
|
||||||
|
extern _Unwind_Reason_Code
|
||||||
|
_Unwind_RaiseException(_Unwind_Exception *exception_object);
|
||||||
|
extern void _Unwind_Resume(_Unwind_Exception *exception_object);
|
||||||
|
#endif
|
||||||
|
extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
typedef enum {
|
||||||
|
_UVRSC_CORE = 0, /* integer register */
|
||||||
|
_UVRSC_VFP = 1, /* vfp */
|
||||||
|
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
|
||||||
|
_UVRSC_WMMXC = 4 /* Intel WMMX control register */
|
||||||
|
} _Unwind_VRS_RegClass;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
_UVRSD_UINT32 = 0,
|
||||||
|
_UVRSD_VFPX = 1,
|
||||||
|
_UVRSD_UINT64 = 3,
|
||||||
|
_UVRSD_FLOAT = 4,
|
||||||
|
_UVRSD_DOUBLE = 5
|
||||||
|
} _Unwind_VRS_DataRepresentation;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
_UVRSR_OK = 0,
|
||||||
|
_UVRSR_NOT_IMPLEMENTED = 1,
|
||||||
|
_UVRSR_FAILED = 2
|
||||||
|
} _Unwind_VRS_Result;
|
||||||
|
|
||||||
|
extern void _Unwind_Complete(_Unwind_Exception* exception_object);
|
||||||
|
|
||||||
|
extern _Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||||
|
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||||
|
void *valuep);
|
||||||
|
|
||||||
|
extern _Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||||
|
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||||
|
void *valuep);
|
||||||
|
|
||||||
|
extern _Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||||
|
uint32_t discriminator,
|
||||||
|
_Unwind_VRS_DataRepresentation representation);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index);
|
||||||
|
extern void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||||
|
uintptr_t new_value);
|
||||||
|
extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
|
||||||
|
extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value);
|
||||||
|
|
||||||
|
#else // defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE)
|
||||||
|
#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 extern
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 static __inline__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// These are de facto helper functions for ARM, which delegate the function
|
||||||
|
// calls to _Unwind_VRS_Get/Set(). These are not a part of ARM EHABI
|
||||||
|
// specification, thus these function MUST be inlined. Please don't replace
|
||||||
|
// these with the "extern" function declaration; otherwise, the program
|
||||||
|
// including this <unwind.h> header won't be ABI compatible and will result in
|
||||||
|
// link error when we are linking the program with libgcc.
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||||
|
uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) {
|
||||||
|
uintptr_t value = 0;
|
||||||
|
_Unwind_VRS_Get(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||||
|
void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||||
|
uintptr_t value) {
|
||||||
|
_Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||||
|
uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||||
|
// remove the thumb-bit before returning
|
||||||
|
return _Unwind_GetGR(context, 15) & (~(uintptr_t)0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||||
|
void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t value) {
|
||||||
|
uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1);
|
||||||
|
_Unwind_SetGR(context, 15, value | thumb_bit);
|
||||||
|
}
|
||||||
|
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context);
|
||||||
|
extern uintptr_t
|
||||||
|
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context);
|
||||||
|
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||||
|
extern _Unwind_Reason_Code
|
||||||
|
_Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter);
|
||||||
|
#else
|
||||||
|
extern _Unwind_Reason_Code
|
||||||
|
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||||
|
typedef struct _Unwind_FunctionContext *_Unwind_FunctionContext_t;
|
||||||
|
extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc);
|
||||||
|
extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// The following are semi-suppoted extensions to the C++ ABI
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// called by __cxa_rethrow().
|
||||||
|
//
|
||||||
|
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||||
|
extern _Unwind_Reason_Code
|
||||||
|
_Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object);
|
||||||
|
#else
|
||||||
|
extern _Unwind_Reason_Code
|
||||||
|
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the
|
||||||
|
// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack
|
||||||
|
// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON.
|
||||||
|
typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *,
|
||||||
|
void *);
|
||||||
|
extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
|
||||||
|
|
||||||
|
// _Unwind_GetCFA is a gcc extension that can be called from within a
|
||||||
|
// personality handler to get the CFA (stack pointer before call) of
|
||||||
|
// current frame.
|
||||||
|
extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *);
|
||||||
|
|
||||||
|
|
||||||
|
// _Unwind_GetIPInfo is a gcc extension that can be called from within a
|
||||||
|
// personality handler. Similar to _Unwind_GetIP() but also returns in
|
||||||
|
// *ipBefore a non-zero value if the instruction pointer is at or before the
|
||||||
|
// instruction causing the unwind. Normally, in a function call, the IP returned
|
||||||
|
// is the return address which is after the call instruction and may be past the
|
||||||
|
// end of the function containing the call instruction.
|
||||||
|
extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||||
|
int *ipBefore);
|
||||||
|
|
||||||
|
|
||||||
|
// __register_frame() is used with dynamically generated code to register the
|
||||||
|
// FDE for a generated (JIT) code. The FDE must use pc-rel addressing to point
|
||||||
|
// to its function and optional LSDA.
|
||||||
|
// __register_frame() has existed in all versions of Mac OS X, but in 10.4 and
|
||||||
|
// 10.5 it was buggy and did not actually register the FDE with the unwinder.
|
||||||
|
// In 10.6 and later it does register properly.
|
||||||
|
extern void __register_frame(const void *fde);
|
||||||
|
extern void __deregister_frame(const void *fde);
|
||||||
|
|
||||||
|
// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has
|
||||||
|
// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind
|
||||||
|
// info" which the runtime uses in preference to DWARF unwind info. This
|
||||||
|
// function will only work if the target function has an FDE but no compact
|
||||||
|
// unwind info.
|
||||||
|
struct dwarf_eh_bases {
|
||||||
|
uintptr_t tbase;
|
||||||
|
uintptr_t dbase;
|
||||||
|
uintptr_t func;
|
||||||
|
};
|
||||||
|
extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *);
|
||||||
|
|
||||||
|
|
||||||
|
// This function attempts to find the start (address of first instruction) of
|
||||||
|
// a function given an address inside the function. It only works if the
|
||||||
|
// function has an FDE (DWARF unwind info).
|
||||||
|
// This function is unimplemented on Mac OS X 10.6 and later. Instead, use
|
||||||
|
// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result.
|
||||||
|
extern void *_Unwind_FindEnclosingFunction(void *pc);
|
||||||
|
|
||||||
|
// Mac OS X does not support text-rel and data-rel addressing so these functions
|
||||||
|
// are unimplemented
|
||||||
|
extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
|
||||||
|
// Mac OS X 10.4 and 10.5 had implementations of these functions in
|
||||||
|
// libgcc_s.dylib, but they never worked.
|
||||||
|
/// These functions are no longer available on Mac OS X.
|
||||||
|
extern void __register_frame_info_bases(const void *fde, void *ob, void *tb,
|
||||||
|
void *db) LIBUNWIND_UNAVAIL;
|
||||||
|
extern void __register_frame_info(const void *fde, void *ob)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
extern void __register_frame_info_table_bases(const void *fde, void *ob,
|
||||||
|
void *tb, void *db)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
extern void __register_frame_info_table(const void *fde, void *ob)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
extern void __register_frame_table(const void *fde)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
extern void *__deregister_frame_info(const void *fde)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
extern void *__deregister_frame_info_bases(const void *fde)
|
||||||
|
LIBUNWIND_UNAVAIL;
|
||||||
|
|
||||||
|
#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
#ifndef _WIN32
|
||||||
|
typedef struct _EXCEPTION_RECORD EXCEPTION_RECORD;
|
||||||
|
typedef struct _CONTEXT CONTEXT;
|
||||||
|
typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT;
|
||||||
|
#elif !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000
|
||||||
|
typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT;
|
||||||
|
#endif
|
||||||
|
// This is the common wrapper for GCC-style personality functions with SEH.
|
||||||
|
extern EXCEPTION_DISPOSITION _GCC_specific_handler(EXCEPTION_RECORD *exc,
|
||||||
|
void *frame,
|
||||||
|
CONTEXT *ctx,
|
||||||
|
DISPATCHER_CONTEXT *disp,
|
||||||
|
__personality_routine pers);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __UNWIND_H__
|
|
@ -0,0 +1,627 @@
|
||||||
|
//===------------------------- AddressSpace.hpp ---------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Abstracts accessing local vs remote address spaces.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __ADDRESSSPACE_HPP__
|
||||||
|
#define __ADDRESSSPACE_HPP__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef _LIBUNWIND_USE_DLADDR
|
||||||
|
#if !defined(_LIBUNWIND_IS_BAREMETAL) && !defined(_WIN32)
|
||||||
|
#define _LIBUNWIND_USE_DLADDR 1
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_USE_DLADDR 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _LIBUNWIND_USE_DLADDR
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#if defined(__unix__) && defined(__ELF__) && defined(_LIBUNWIND_HAS_COMMENT_LIB_PRAGMA)
|
||||||
|
#pragma comment(lib, "dl")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
struct EHABIIndexEntry {
|
||||||
|
uint32_t functionOffset;
|
||||||
|
uint32_t data;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <mach-o/getsect.h>
|
||||||
|
namespace libunwind {
|
||||||
|
bool checkKeyMgrRegisteredFDEs(uintptr_t targetAddr, void *&fde);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libunwind.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "dwarf2.h"
|
||||||
|
#include "EHHeaderParser.hpp"
|
||||||
|
#include "Registers.hpp"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
struct dyld_unwind_sections
|
||||||
|
{
|
||||||
|
const struct mach_header* mh;
|
||||||
|
const void* dwarf_section;
|
||||||
|
uintptr_t dwarf_section_length;
|
||||||
|
const void* compact_unwind_section;
|
||||||
|
uintptr_t compact_unwind_section_length;
|
||||||
|
};
|
||||||
|
#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
|
||||||
|
&& (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) \
|
||||||
|
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
|
||||||
|
// In 10.7.0 or later, libSystem.dylib implements this function.
|
||||||
|
extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *);
|
||||||
|
#else
|
||||||
|
// In 10.6.x and earlier, we need to implement this functionality. Note
|
||||||
|
// that this requires a newer version of libmacho (from cctools) than is
|
||||||
|
// present in libSystem on 10.6.x (for getsectiondata).
|
||||||
|
static inline bool _dyld_find_unwind_sections(void* addr,
|
||||||
|
dyld_unwind_sections* info) {
|
||||||
|
// Find mach-o image containing address.
|
||||||
|
Dl_info dlinfo;
|
||||||
|
if (!dladdr(addr, &dlinfo))
|
||||||
|
return false;
|
||||||
|
#if __LP64__
|
||||||
|
const struct mach_header_64 *mh = (const struct mach_header_64 *)dlinfo.dli_fbase;
|
||||||
|
#else
|
||||||
|
const struct mach_header *mh = (const struct mach_header *)dlinfo.dli_fbase;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize the return struct
|
||||||
|
info->mh = (const struct mach_header *)mh;
|
||||||
|
info->dwarf_section = getsectiondata(mh, "__TEXT", "__eh_frame", &info->dwarf_section_length);
|
||||||
|
info->compact_unwind_section = getsectiondata(mh, "__TEXT", "__unwind_info", &info->compact_unwind_section_length);
|
||||||
|
|
||||||
|
if (!info->dwarf_section) {
|
||||||
|
info->dwarf_section_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->compact_unwind_section) {
|
||||||
|
info->compact_unwind_section_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||||
|
|
||||||
|
// When statically linked on bare-metal, the symbols for the EH table are looked
|
||||||
|
// up without going through the dynamic loader.
|
||||||
|
|
||||||
|
// The following linker script may be used to produce the necessary sections and symbols.
|
||||||
|
// Unless the --eh-frame-hdr linker option is provided, the section is not generated
|
||||||
|
// and does not take space in the output file.
|
||||||
|
//
|
||||||
|
// .eh_frame :
|
||||||
|
// {
|
||||||
|
// __eh_frame_start = .;
|
||||||
|
// KEEP(*(.eh_frame))
|
||||||
|
// __eh_frame_end = .;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// .eh_frame_hdr :
|
||||||
|
// {
|
||||||
|
// KEEP(*(.eh_frame_hdr))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
|
||||||
|
// __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
|
||||||
|
|
||||||
|
extern char __eh_frame_start;
|
||||||
|
extern char __eh_frame_end;
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||||
|
extern char __eh_frame_hdr_start;
|
||||||
|
extern char __eh_frame_hdr_end;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||||
|
|
||||||
|
// When statically linked on bare-metal, the symbols for the EH table are looked
|
||||||
|
// up without going through the dynamic loader.
|
||||||
|
extern char __exidx_start;
|
||||||
|
extern char __exidx_end;
|
||||||
|
|
||||||
|
#elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
|
||||||
|
// ELF-based systems may use dl_iterate_phdr() to access sections
|
||||||
|
// containing unwinding information. The ElfW() macro for pointer-size
|
||||||
|
// independent ELF header traversal is not provided by <link.h> on some
|
||||||
|
// systems (e.g., FreeBSD). On these systems the data structures are
|
||||||
|
// just called Elf_XXX. Define ElfW() locally.
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <link.h>
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
#endif
|
||||||
|
#if !defined(ElfW)
|
||||||
|
#define ElfW(type) Elf_##type
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
/// Used by findUnwindSections() to return info about needed sections.
|
||||||
|
struct UnwindInfoSections {
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) || \
|
||||||
|
defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
|
||||||
|
// No dso_base for SEH or ARM EHABI.
|
||||||
|
uintptr_t dso_base;
|
||||||
|
#endif
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
uintptr_t dwarf_section;
|
||||||
|
uintptr_t dwarf_section_length;
|
||||||
|
#endif
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||||
|
uintptr_t dwarf_index_section;
|
||||||
|
uintptr_t dwarf_index_section_length;
|
||||||
|
#endif
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
|
||||||
|
uintptr_t compact_unwind_section;
|
||||||
|
uintptr_t compact_unwind_section_length;
|
||||||
|
#endif
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
uintptr_t arm_section;
|
||||||
|
uintptr_t arm_section_length;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// LocalAddressSpace is used as a template parameter to UnwindCursor when
|
||||||
|
/// unwinding a thread in the same process. The wrappers compile away,
|
||||||
|
/// making local unwinds fast.
|
||||||
|
class _LIBUNWIND_HIDDEN LocalAddressSpace {
|
||||||
|
public:
|
||||||
|
typedef uintptr_t pint_t;
|
||||||
|
typedef intptr_t sint_t;
|
||||||
|
uint8_t get8(pint_t addr) {
|
||||||
|
uint8_t val;
|
||||||
|
memcpy(&val, (void *)addr, sizeof(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
uint16_t get16(pint_t addr) {
|
||||||
|
uint16_t val;
|
||||||
|
memcpy(&val, (void *)addr, sizeof(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
uint32_t get32(pint_t addr) {
|
||||||
|
uint32_t val;
|
||||||
|
memcpy(&val, (void *)addr, sizeof(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
uint64_t get64(pint_t addr) {
|
||||||
|
uint64_t val;
|
||||||
|
memcpy(&val, (void *)addr, sizeof(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
double getDouble(pint_t addr) {
|
||||||
|
double val;
|
||||||
|
memcpy(&val, (void *)addr, sizeof(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
v128 getVector(pint_t addr) {
|
||||||
|
v128 val;
|
||||||
|
memcpy(&val, (void *)addr, sizeof(val));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
uintptr_t getP(pint_t addr);
|
||||||
|
uint64_t getRegister(pint_t addr);
|
||||||
|
static uint64_t getULEB128(pint_t &addr, pint_t end);
|
||||||
|
static int64_t getSLEB128(pint_t &addr, pint_t end);
|
||||||
|
|
||||||
|
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||||
|
pint_t datarelBase = 0);
|
||||||
|
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
|
||||||
|
unw_word_t *offset);
|
||||||
|
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
|
||||||
|
bool findOtherFDE(pint_t targetAddr, pint_t &fde);
|
||||||
|
|
||||||
|
static LocalAddressSpace sThisAddressSpace;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uintptr_t LocalAddressSpace::getP(pint_t addr) {
|
||||||
|
#if __SIZEOF_POINTER__ == 8
|
||||||
|
return get64(addr);
|
||||||
|
#else
|
||||||
|
return get32(addr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t LocalAddressSpace::getRegister(pint_t addr) {
|
||||||
|
#if __SIZEOF_POINTER__ == 8 || defined(__mips64)
|
||||||
|
return get64(addr);
|
||||||
|
#else
|
||||||
|
return get32(addr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a ULEB128 into a 64-bit word.
|
||||||
|
inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
|
||||||
|
const uint8_t *p = (uint8_t *)addr;
|
||||||
|
const uint8_t *pend = (uint8_t *)end;
|
||||||
|
uint64_t result = 0;
|
||||||
|
int bit = 0;
|
||||||
|
do {
|
||||||
|
uint64_t b;
|
||||||
|
|
||||||
|
if (p == pend)
|
||||||
|
_LIBUNWIND_ABORT("truncated uleb128 expression");
|
||||||
|
|
||||||
|
b = *p & 0x7f;
|
||||||
|
|
||||||
|
if (bit >= 64 || b << bit >> bit != b) {
|
||||||
|
_LIBUNWIND_ABORT("malformed uleb128 expression");
|
||||||
|
} else {
|
||||||
|
result |= b << bit;
|
||||||
|
bit += 7;
|
||||||
|
}
|
||||||
|
} while (*p++ >= 0x80);
|
||||||
|
addr = (pint_t) p;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a SLEB128 into a 64-bit word.
|
||||||
|
inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
|
||||||
|
const uint8_t *p = (uint8_t *)addr;
|
||||||
|
const uint8_t *pend = (uint8_t *)end;
|
||||||
|
int64_t result = 0;
|
||||||
|
int bit = 0;
|
||||||
|
uint8_t byte;
|
||||||
|
do {
|
||||||
|
if (p == pend)
|
||||||
|
_LIBUNWIND_ABORT("truncated sleb128 expression");
|
||||||
|
byte = *p++;
|
||||||
|
result |= ((byte & 0x7f) << bit);
|
||||||
|
bit += 7;
|
||||||
|
} while (byte & 0x80);
|
||||||
|
// sign extend negative numbers
|
||||||
|
if ((byte & 0x40) != 0)
|
||||||
|
result |= (-1ULL) << bit;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LocalAddressSpace::pint_t
|
||||||
|
LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||||
|
pint_t datarelBase) {
|
||||||
|
pint_t startAddr = addr;
|
||||||
|
const uint8_t *p = (uint8_t *)addr;
|
||||||
|
pint_t result;
|
||||||
|
|
||||||
|
// first get value
|
||||||
|
switch (encoding & 0x0F) {
|
||||||
|
case DW_EH_PE_ptr:
|
||||||
|
result = getP(addr);
|
||||||
|
p += sizeof(pint_t);
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_uleb128:
|
||||||
|
result = (pint_t)getULEB128(addr, end);
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_udata2:
|
||||||
|
result = get16(addr);
|
||||||
|
p += 2;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_udata4:
|
||||||
|
result = get32(addr);
|
||||||
|
p += 4;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_udata8:
|
||||||
|
result = (pint_t)get64(addr);
|
||||||
|
p += 8;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_sleb128:
|
||||||
|
result = (pint_t)getSLEB128(addr, end);
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_sdata2:
|
||||||
|
// Sign extend from signed 16-bit value.
|
||||||
|
result = (pint_t)(int16_t)get16(addr);
|
||||||
|
p += 2;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_sdata4:
|
||||||
|
// Sign extend from signed 32-bit value.
|
||||||
|
result = (pint_t)(int32_t)get32(addr);
|
||||||
|
p += 4;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_sdata8:
|
||||||
|
result = (pint_t)get64(addr);
|
||||||
|
p += 8;
|
||||||
|
addr = (pint_t) p;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_ABORT("unknown pointer encoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
// then add relative offset
|
||||||
|
switch (encoding & 0x70) {
|
||||||
|
case DW_EH_PE_absptr:
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_pcrel:
|
||||||
|
result += startAddr;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_textrel:
|
||||||
|
_LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_datarel:
|
||||||
|
// DW_EH_PE_datarel is only valid in a few places, so the parameter has a
|
||||||
|
// default value of 0, and we abort in the event that someone calls this
|
||||||
|
// function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
|
||||||
|
if (datarelBase == 0)
|
||||||
|
_LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
|
||||||
|
result += datarelBase;
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_funcrel:
|
||||||
|
_LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
|
||||||
|
break;
|
||||||
|
case DW_EH_PE_aligned:
|
||||||
|
_LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_ABORT("unknown pointer encoding");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoding & DW_EH_PE_indirect)
|
||||||
|
result = getP(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
|
||||||
|
UnwindInfoSections &info) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
dyld_unwind_sections dyldInfo;
|
||||||
|
if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) {
|
||||||
|
info.dso_base = (uintptr_t)dyldInfo.mh;
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section;
|
||||||
|
info.dwarf_section_length = dyldInfo.dwarf_section_length;
|
||||||
|
#endif
|
||||||
|
info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section;
|
||||||
|
info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||||
|
// Bare metal is statically linked, so no need to ask the dynamic loader
|
||||||
|
info.dwarf_section_length = (uintptr_t)(&__eh_frame_end - &__eh_frame_start);
|
||||||
|
info.dwarf_section = (uintptr_t)(&__eh_frame_start);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
|
||||||
|
(void *)info.dwarf_section, (void *)info.dwarf_section_length);
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||||
|
info.dwarf_index_section = (uintptr_t)(&__eh_frame_hdr_start);
|
||||||
|
info.dwarf_index_section_length = (uintptr_t)(&__eh_frame_hdr_end - &__eh_frame_hdr_start);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p",
|
||||||
|
(void *)info.dwarf_index_section, (void *)info.dwarf_index_section_length);
|
||||||
|
#endif
|
||||||
|
if (info.dwarf_section_length)
|
||||||
|
return true;
|
||||||
|
#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||||
|
// Bare metal is statically linked, so no need to ask the dynamic loader
|
||||||
|
info.arm_section = (uintptr_t)(&__exidx_start);
|
||||||
|
info.arm_section_length = (uintptr_t)(&__exidx_end - &__exidx_start);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
|
||||||
|
(void *)info.arm_section, (void *)info.arm_section_length);
|
||||||
|
if (info.arm_section && info.arm_section_length)
|
||||||
|
return true;
|
||||||
|
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
|
||||||
|
HMODULE mods[1024];
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
DWORD needed;
|
||||||
|
|
||||||
|
if (!EnumProcessModules(process, mods, sizeof(mods), &needed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < (needed / sizeof(HMODULE)); i++) {
|
||||||
|
PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)mods[i];
|
||||||
|
PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE *)pidh + pidh->e_lfanew);
|
||||||
|
PIMAGE_FILE_HEADER pifh = (PIMAGE_FILE_HEADER)&pinh->FileHeader;
|
||||||
|
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinh);
|
||||||
|
bool found_obj = false;
|
||||||
|
bool found_hdr = false;
|
||||||
|
|
||||||
|
info.dso_base = (uintptr_t)mods[i];
|
||||||
|
for (unsigned j = 0; j < pifh->NumberOfSections; j++, pish++) {
|
||||||
|
uintptr_t begin = pish->VirtualAddress + (uintptr_t)mods[i];
|
||||||
|
uintptr_t end = begin + pish->Misc.VirtualSize;
|
||||||
|
if (!strncmp((const char *)pish->Name, ".text",
|
||||||
|
IMAGE_SIZEOF_SHORT_NAME)) {
|
||||||
|
if (targetAddr >= begin && targetAddr < end)
|
||||||
|
found_obj = true;
|
||||||
|
} else if (!strncmp((const char *)pish->Name, ".eh_frame",
|
||||||
|
IMAGE_SIZEOF_SHORT_NAME)) {
|
||||||
|
info.dwarf_section = begin;
|
||||||
|
info.dwarf_section_length = pish->Misc.VirtualSize;
|
||||||
|
found_hdr = true;
|
||||||
|
}
|
||||||
|
if (found_obj && found_hdr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
|
||||||
|
// Don't even bother, since Windows has functions that do all this stuff
|
||||||
|
// for us.
|
||||||
|
(void)targetAddr;
|
||||||
|
(void)info;
|
||||||
|
return true;
|
||||||
|
#elif defined(_LIBUNWIND_ARM_EHABI) && defined(__BIONIC__)
|
||||||
|
// For ARM EHABI, Bionic didn't implement dl_iterate_phdr until API 21. After
|
||||||
|
// API 21, dl_iterate_phdr exists, but dl_unwind_find_exidx is much faster.
|
||||||
|
int length = 0;
|
||||||
|
info.arm_section =
|
||||||
|
(uintptr_t)dl_unwind_find_exidx((_Unwind_Ptr)targetAddr, &length);
|
||||||
|
info.arm_section_length = (uintptr_t)length * sizeof(EHABIIndexEntry);
|
||||||
|
if (info.arm_section && info.arm_section_length)
|
||||||
|
return true;
|
||||||
|
#elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
struct dl_iterate_cb_data {
|
||||||
|
LocalAddressSpace *addressSpace;
|
||||||
|
UnwindInfoSections *sects;
|
||||||
|
uintptr_t targetAddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
dl_iterate_cb_data cb_data = {this, &info, targetAddr};
|
||||||
|
int found = dl_iterate_phdr(
|
||||||
|
[](struct dl_phdr_info *pinfo, size_t, void *data) -> int {
|
||||||
|
auto cbdata = static_cast<dl_iterate_cb_data *>(data);
|
||||||
|
bool found_obj = false;
|
||||||
|
bool found_hdr = false;
|
||||||
|
|
||||||
|
assert(cbdata);
|
||||||
|
assert(cbdata->sects);
|
||||||
|
|
||||||
|
if (cbdata->targetAddr < pinfo->dlpi_addr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(Elf_Half)
|
||||||
|
typedef ElfW(Half) Elf_Half;
|
||||||
|
#endif
|
||||||
|
#if !defined(Elf_Phdr)
|
||||||
|
typedef ElfW(Phdr) Elf_Phdr;
|
||||||
|
#endif
|
||||||
|
#if !defined(Elf_Addr)
|
||||||
|
typedef ElfW(Addr) Elf_Addr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Elf_Addr image_base = pinfo->dlpi_addr;
|
||||||
|
|
||||||
|
#if defined(__ANDROID__) && __ANDROID_API__ < 18
|
||||||
|
if (image_base == 0) {
|
||||||
|
// Normally, an image base of 0 indicates a non-PIE executable. On
|
||||||
|
// versions of Android prior to API 18, the dynamic linker reported a
|
||||||
|
// dlpi_addr of 0 for PIE executables. Compute the true image base
|
||||||
|
// using the PT_PHDR segment.
|
||||||
|
// See https://github.com/android/ndk/issues/505.
|
||||||
|
for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
|
||||||
|
const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
|
||||||
|
if (phdr->p_type == PT_PHDR) {
|
||||||
|
image_base = reinterpret_cast<Elf_Addr>(pinfo->dlpi_phdr) -
|
||||||
|
phdr->p_vaddr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
#if !defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||||
|
#error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform."
|
||||||
|
#endif
|
||||||
|
size_t object_length;
|
||||||
|
|
||||||
|
for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
|
||||||
|
const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
|
||||||
|
if (phdr->p_type == PT_LOAD) {
|
||||||
|
uintptr_t begin = image_base + phdr->p_vaddr;
|
||||||
|
uintptr_t end = begin + phdr->p_memsz;
|
||||||
|
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
|
||||||
|
cbdata->sects->dso_base = begin;
|
||||||
|
object_length = phdr->p_memsz;
|
||||||
|
found_obj = true;
|
||||||
|
}
|
||||||
|
} else if (phdr->p_type == PT_GNU_EH_FRAME) {
|
||||||
|
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
|
||||||
|
uintptr_t eh_frame_hdr_start = image_base + phdr->p_vaddr;
|
||||||
|
cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
|
||||||
|
cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
|
||||||
|
found_hdr = EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
|
||||||
|
*cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
|
||||||
|
hdrInfo);
|
||||||
|
if (found_hdr)
|
||||||
|
cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_obj && found_hdr) {
|
||||||
|
cbdata->sects->dwarf_section_length = object_length;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else // defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
|
||||||
|
const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
|
||||||
|
if (phdr->p_type == PT_LOAD) {
|
||||||
|
uintptr_t begin = image_base + phdr->p_vaddr;
|
||||||
|
uintptr_t end = begin + phdr->p_memsz;
|
||||||
|
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end)
|
||||||
|
found_obj = true;
|
||||||
|
} else if (phdr->p_type == PT_ARM_EXIDX) {
|
||||||
|
uintptr_t exidx_start = image_base + phdr->p_vaddr;
|
||||||
|
cbdata->sects->arm_section = exidx_start;
|
||||||
|
cbdata->sects->arm_section_length = phdr->p_memsz;
|
||||||
|
found_hdr = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found_obj && found_hdr;
|
||||||
|
#endif
|
||||||
|
},
|
||||||
|
&cb_data);
|
||||||
|
return static_cast<bool>(found);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
return checkKeyMgrRegisteredFDEs(targetAddr, *((void**)&fde));
|
||||||
|
#else
|
||||||
|
// TO DO: if OS has way to dynamically register FDEs, check that.
|
||||||
|
(void)targetAddr;
|
||||||
|
(void)fde;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
|
||||||
|
size_t bufLen,
|
||||||
|
unw_word_t *offset) {
|
||||||
|
#if _LIBUNWIND_USE_DLADDR
|
||||||
|
Dl_info dyldInfo;
|
||||||
|
if (dladdr((void *)addr, &dyldInfo)) {
|
||||||
|
if (dyldInfo.dli_sname != NULL) {
|
||||||
|
snprintf(buf, bufLen, "%s", dyldInfo.dli_sname);
|
||||||
|
*offset = (addr - (pint_t) dyldInfo.dli_saddr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)addr;
|
||||||
|
(void)buf;
|
||||||
|
(void)bufLen;
|
||||||
|
(void)offset;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace libunwind
|
||||||
|
|
||||||
|
#endif // __ADDRESSSPACE_HPP__
|
|
@ -0,0 +1,195 @@
|
||||||
|
# Get sources
|
||||||
|
|
||||||
|
set(LIBUNWIND_CXX_SOURCES
|
||||||
|
libunwind.cpp
|
||||||
|
Unwind-EHABI.cpp
|
||||||
|
Unwind-seh.cpp
|
||||||
|
)
|
||||||
|
if(APPLE)
|
||||||
|
list(APPEND LIBUNWIND_CXX_SOURCES
|
||||||
|
Unwind_AppleExtras.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(LIBUNWIND_C_SOURCES
|
||||||
|
UnwindLevel1.c
|
||||||
|
UnwindLevel1-gcc-ext.c
|
||||||
|
Unwind-sjlj.c
|
||||||
|
)
|
||||||
|
set_source_files_properties(${LIBUNWIND_C_SOURCES}
|
||||||
|
PROPERTIES
|
||||||
|
COMPILE_FLAGS "-std=c99")
|
||||||
|
|
||||||
|
set(LIBUNWIND_ASM_SOURCES
|
||||||
|
UnwindRegistersRestore.S
|
||||||
|
UnwindRegistersSave.S
|
||||||
|
)
|
||||||
|
set_source_files_properties(${LIBUNWIND_ASM_SOURCES}
|
||||||
|
PROPERTIES
|
||||||
|
LANGUAGE C)
|
||||||
|
|
||||||
|
set(LIBUNWIND_HEADERS
|
||||||
|
AddressSpace.hpp
|
||||||
|
assembly.h
|
||||||
|
CompactUnwinder.hpp
|
||||||
|
config.h
|
||||||
|
dwarf2.h
|
||||||
|
DwarfInstructions.hpp
|
||||||
|
DwarfParser.hpp
|
||||||
|
libunwind_ext.h
|
||||||
|
Registers.hpp
|
||||||
|
RWMutex.hpp
|
||||||
|
UnwindCursor.hpp
|
||||||
|
../include/libunwind.h
|
||||||
|
../include/unwind.h
|
||||||
|
)
|
||||||
|
if(APPLE)
|
||||||
|
list(APPEND LIBUNWIND_HEADERS
|
||||||
|
../include/mach-o/compact_unwind_encoding.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC_IDE)
|
||||||
|
# Force them all into the headers dir on MSVC, otherwise they end up at
|
||||||
|
# project scope because they don't have extensions.
|
||||||
|
source_group("Header Files" FILES ${LIBUNWIND_HEADERS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(LIBUNWIND_SOURCES
|
||||||
|
${LIBUNWIND_CXX_SOURCES}
|
||||||
|
${LIBUNWIND_C_SOURCES}
|
||||||
|
${LIBUNWIND_ASM_SOURCES})
|
||||||
|
|
||||||
|
# Generate library list.
|
||||||
|
add_library_flags_if(LIBUNWIND_HAS_C_LIB c)
|
||||||
|
if (LIBUNWIND_USE_COMPILER_RT)
|
||||||
|
add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}")
|
||||||
|
else()
|
||||||
|
add_library_flags_if(LIBUNWIND_HAS_GCC_S_LIB gcc_s)
|
||||||
|
add_library_flags_if(LIBUNWIND_HAS_GCC_LIB gcc)
|
||||||
|
endif()
|
||||||
|
add_library_flags_if(LIBUNWIND_HAS_DL_LIB dl)
|
||||||
|
if (LIBUNWIND_ENABLE_THREADS)
|
||||||
|
add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread)
|
||||||
|
add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Setup flags.
|
||||||
|
add_link_flags_if_supported(-nodefaultlibs)
|
||||||
|
|
||||||
|
# MINGW_LIBRARIES is defined in config-ix.cmake
|
||||||
|
add_library_flags_if(MINGW "${MINGW_LIBRARIES}")
|
||||||
|
|
||||||
|
if (LIBUNWIND_ENABLE_SHARED AND
|
||||||
|
NOT (LIBUNWIND_SUPPORTS_FNO_EXCEPTIONS_FLAG AND
|
||||||
|
LIBUNWIND_SUPPORTS_FUNWIND_TABLES_FLAG))
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Compiler doesn't support generation of unwind tables if exception "
|
||||||
|
"support is disabled. Building libunwind DSO with runtime dependency "
|
||||||
|
"on C++ ABI library is not supported.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
add_compile_flags("-U__STRICT_ANSI__")
|
||||||
|
add_link_flags("-compatibility_version 1" "-install_name /usr/lib/libunwind.1.dylib")
|
||||||
|
|
||||||
|
if (CMAKE_OSX_DEPLOYMENT_TARGET STREQUAL "10.6")
|
||||||
|
add_link_flags("-current_version ${LIBUNWIND_VERSION}" "/usr/lib/libSystem.B.dylib")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
string(REPLACE ";" " " LIBUNWIND_COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}")
|
||||||
|
string(REPLACE ";" " " LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}")
|
||||||
|
string(REPLACE ";" " " LIBUNWIND_C_FLAGS "${LIBUNWIND_C_FLAGS}")
|
||||||
|
string(REPLACE ";" " " LIBUNWIND_LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}")
|
||||||
|
|
||||||
|
set_property(SOURCE ${LIBUNWIND_CXX_SOURCES}
|
||||||
|
APPEND_STRING PROPERTY COMPILE_FLAGS " ${LIBUNWIND_CXX_FLAGS}")
|
||||||
|
set_property(SOURCE ${LIBUNWIND_C_SOURCES}
|
||||||
|
APPEND_STRING PROPERTY COMPILE_FLAGS " ${LIBUNWIND_C_FLAGS}")
|
||||||
|
|
||||||
|
# Build the shared library.
|
||||||
|
if (LIBUNWIND_ENABLE_SHARED)
|
||||||
|
add_library(unwind_shared SHARED ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS})
|
||||||
|
if(COMMAND llvm_setup_rpath)
|
||||||
|
llvm_setup_rpath(unwind_shared)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(unwind_shared PRIVATE ${LIBUNWIND_LIBRARIES})
|
||||||
|
set_target_properties(unwind_shared
|
||||||
|
PROPERTIES
|
||||||
|
CXX_EXTENSIONS
|
||||||
|
OFF
|
||||||
|
CXX_STANDARD
|
||||||
|
11
|
||||||
|
CXX_STANDARD_REQUIRED
|
||||||
|
ON
|
||||||
|
COMPILE_FLAGS
|
||||||
|
"${LIBUNWIND_COMPILE_FLAGS}"
|
||||||
|
LINK_FLAGS
|
||||||
|
"${LIBUNWIND_LINK_FLAGS}"
|
||||||
|
OUTPUT_NAME
|
||||||
|
"unwind"
|
||||||
|
VERSION
|
||||||
|
"1.0"
|
||||||
|
SOVERSION
|
||||||
|
"1")
|
||||||
|
list(APPEND LIBUNWIND_BUILD_TARGETS "unwind_shared")
|
||||||
|
if (LIBUNWIND_INSTALL_SHARED_LIBRARY)
|
||||||
|
list(APPEND LIBUNWIND_INSTALL_TARGETS "unwind_shared")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Build the static library.
|
||||||
|
if (LIBUNWIND_ENABLE_STATIC)
|
||||||
|
add_library(unwind_static STATIC ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS})
|
||||||
|
target_link_libraries(unwind_static PRIVATE ${LIBUNWIND_LIBRARIES})
|
||||||
|
set_target_properties(unwind_static
|
||||||
|
PROPERTIES
|
||||||
|
CXX_EXTENSIONS
|
||||||
|
OFF
|
||||||
|
CXX_STANDARD
|
||||||
|
11
|
||||||
|
CXX_STANDARD_REQUIRED
|
||||||
|
ON
|
||||||
|
COMPILE_FLAGS
|
||||||
|
"${LIBUNWIND_COMPILE_FLAGS}"
|
||||||
|
LINK_FLAGS
|
||||||
|
"${LIBUNWIND_LINK_FLAGS}"
|
||||||
|
OUTPUT_NAME
|
||||||
|
"unwind")
|
||||||
|
|
||||||
|
if(LIBUNWIND_HERMETIC_STATIC_LIBRARY)
|
||||||
|
append_flags_if_supported(UNWIND_STATIC_LIBRARY_FLAGS -fvisibility=hidden)
|
||||||
|
append_flags_if_supported(UNWIND_STATIC_LIBRARY_FLAGS -fvisibility-global-new-delete-hidden)
|
||||||
|
target_compile_options(unwind_static PRIVATE ${UNWIND_STATIC_LIBRARY_FLAGS})
|
||||||
|
target_compile_definitions(unwind_static PRIVATE _LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND LIBUNWIND_BUILD_TARGETS "unwind_static")
|
||||||
|
if (LIBUNWIND_INSTALL_STATIC_LIBRARY)
|
||||||
|
list(APPEND LIBUNWIND_INSTALL_TARGETS "unwind_static")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add a meta-target for both libraries.
|
||||||
|
add_custom_target(unwind DEPENDS ${LIBUNWIND_BUILD_TARGETS})
|
||||||
|
|
||||||
|
if (LIBUNWIND_INSTALL_LIBRARY)
|
||||||
|
install(TARGETS ${LIBUNWIND_INSTALL_TARGETS}
|
||||||
|
LIBRARY DESTINATION ${LIBUNWIND_INSTALL_PREFIX}${LIBUNWIND_INSTALL_LIBRARY_DIR} COMPONENT unwind
|
||||||
|
ARCHIVE DESTINATION ${LIBUNWIND_INSTALL_PREFIX}${LIBUNWIND_INSTALL_LIBRARY_DIR} COMPONENT unwind)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT CMAKE_CONFIGURATION_TYPES AND LIBUNWIND_INSTALL_LIBRARY)
|
||||||
|
add_custom_target(install-unwind
|
||||||
|
DEPENDS unwind
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
-DCMAKE_INSTALL_COMPONENT=unwind
|
||||||
|
-P "${LIBUNWIND_BINARY_DIR}/cmake_install.cmake")
|
||||||
|
add_custom_target(install-unwind-stripped
|
||||||
|
DEPENDS unwind
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
-DCMAKE_INSTALL_COMPONENT=unwind
|
||||||
|
-DCMAKE_INSTALL_DO_STRIP=1
|
||||||
|
-P "${LIBUNWIND_BINARY_DIR}/cmake_install.cmake")
|
||||||
|
endif()
|
|
@ -0,0 +1,697 @@
|
||||||
|
//===-------------------------- CompactUnwinder.hpp -----------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Does runtime stack unwinding using compact unwind encodings.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __COMPACT_UNWINDER_HPP__
|
||||||
|
#define __COMPACT_UNWINDER_HPP__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <libunwind.h>
|
||||||
|
#include <mach-o/compact_unwind_encoding.h>
|
||||||
|
|
||||||
|
#include "Registers.hpp"
|
||||||
|
|
||||||
|
#define EXTRACT_BITS(value, mask) \
|
||||||
|
((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_I386)
|
||||||
|
/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
|
||||||
|
/// unwind) by modifying a Registers_x86 register set
|
||||||
|
template <typename A>
|
||||||
|
class CompactUnwinder_x86 {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static int stepWithCompactEncoding(compact_unwind_encoding_t info,
|
||||||
|
uint32_t functionStart, A &addressSpace,
|
||||||
|
Registers_x86 ®isters);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename A::pint_t pint_t;
|
||||||
|
|
||||||
|
static void frameUnwind(A &addressSpace, Registers_x86 ®isters);
|
||||||
|
static void framelessUnwind(A &addressSpace,
|
||||||
|
typename A::pint_t returnAddressLocation,
|
||||||
|
Registers_x86 ®isters);
|
||||||
|
static int
|
||||||
|
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
|
||||||
|
uint32_t functionStart, A &addressSpace,
|
||||||
|
Registers_x86 ®isters);
|
||||||
|
static int stepWithCompactEncodingFrameless(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_x86<A>::stepWithCompactEncoding(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86 ®isters) {
|
||||||
|
switch (compactEncoding & UNWIND_X86_MODE_MASK) {
|
||||||
|
case UNWIND_X86_MODE_EBP_FRAME:
|
||||||
|
return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers);
|
||||||
|
case UNWIND_X86_MODE_STACK_IMMD:
|
||||||
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers, false);
|
||||||
|
case UNWIND_X86_MODE_STACK_IND:
|
||||||
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers, true);
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86 ®isters) {
|
||||||
|
uint32_t savedRegistersOffset =
|
||||||
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
|
||||||
|
uint32_t savedRegistersLocations =
|
||||||
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
|
||||||
|
|
||||||
|
uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
switch (savedRegistersLocations & 0x7) {
|
||||||
|
case UNWIND_X86_REG_NONE:
|
||||||
|
// no register saved in this slot
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_EBX:
|
||||||
|
registers.setEBX(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_ECX:
|
||||||
|
registers.setECX(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_EDX:
|
||||||
|
registers.setEDX(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_EDI:
|
||||||
|
registers.setEDI(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_ESI:
|
||||||
|
registers.setESI(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
(void)functionStart;
|
||||||
|
_LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for "
|
||||||
|
"function starting at 0x%X",
|
||||||
|
compactEncoding, functionStart);
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
savedRegisters += 4;
|
||||||
|
savedRegistersLocations = (savedRegistersLocations >> 3);
|
||||||
|
}
|
||||||
|
frameUnwind(addressSpace, registers);
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
|
||||||
|
compact_unwind_encoding_t encoding, uint32_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) {
|
||||||
|
uint32_t stackSizeEncoded =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
|
||||||
|
uint32_t stackAdjust =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
|
||||||
|
uint32_t regCount =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
|
||||||
|
uint32_t permutation =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
|
||||||
|
uint32_t stackSize = stackSizeEncoded * 4;
|
||||||
|
if (indirectStackSize) {
|
||||||
|
// stack size is encoded in subl $xxx,%esp instruction
|
||||||
|
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
||||||
|
stackSize = subl + 4 * stackAdjust;
|
||||||
|
}
|
||||||
|
// decompress permutation
|
||||||
|
uint32_t permunreg[6];
|
||||||
|
switch (regCount) {
|
||||||
|
case 6:
|
||||||
|
permunreg[0] = permutation / 120;
|
||||||
|
permutation -= (permunreg[0] * 120);
|
||||||
|
permunreg[1] = permutation / 24;
|
||||||
|
permutation -= (permunreg[1] * 24);
|
||||||
|
permunreg[2] = permutation / 6;
|
||||||
|
permutation -= (permunreg[2] * 6);
|
||||||
|
permunreg[3] = permutation / 2;
|
||||||
|
permutation -= (permunreg[3] * 2);
|
||||||
|
permunreg[4] = permutation;
|
||||||
|
permunreg[5] = 0;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
permunreg[0] = permutation / 120;
|
||||||
|
permutation -= (permunreg[0] * 120);
|
||||||
|
permunreg[1] = permutation / 24;
|
||||||
|
permutation -= (permunreg[1] * 24);
|
||||||
|
permunreg[2] = permutation / 6;
|
||||||
|
permutation -= (permunreg[2] * 6);
|
||||||
|
permunreg[3] = permutation / 2;
|
||||||
|
permutation -= (permunreg[3] * 2);
|
||||||
|
permunreg[4] = permutation;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
permunreg[0] = permutation / 60;
|
||||||
|
permutation -= (permunreg[0] * 60);
|
||||||
|
permunreg[1] = permutation / 12;
|
||||||
|
permutation -= (permunreg[1] * 12);
|
||||||
|
permunreg[2] = permutation / 3;
|
||||||
|
permutation -= (permunreg[2] * 3);
|
||||||
|
permunreg[3] = permutation;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
permunreg[0] = permutation / 20;
|
||||||
|
permutation -= (permunreg[0] * 20);
|
||||||
|
permunreg[1] = permutation / 4;
|
||||||
|
permutation -= (permunreg[1] * 4);
|
||||||
|
permunreg[2] = permutation;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
permunreg[0] = permutation / 5;
|
||||||
|
permutation -= (permunreg[0] * 5);
|
||||||
|
permunreg[1] = permutation;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
permunreg[0] = permutation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// re-number registers back to standard numbers
|
||||||
|
int registersSaved[6];
|
||||||
|
bool used[7] = { false, false, false, false, false, false, false };
|
||||||
|
for (uint32_t i = 0; i < regCount; ++i) {
|
||||||
|
uint32_t renum = 0;
|
||||||
|
for (int u = 1; u < 7; ++u) {
|
||||||
|
if (!used[u]) {
|
||||||
|
if (renum == permunreg[i]) {
|
||||||
|
registersSaved[i] = u;
|
||||||
|
used[u] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++renum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
|
||||||
|
for (uint32_t i = 0; i < regCount; ++i) {
|
||||||
|
switch (registersSaved[i]) {
|
||||||
|
case UNWIND_X86_REG_EBX:
|
||||||
|
registers.setEBX(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_ECX:
|
||||||
|
registers.setECX(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_EDX:
|
||||||
|
registers.setEDX(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_EDI:
|
||||||
|
registers.setEDI(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_ESI:
|
||||||
|
registers.setESI(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_REG_EBP:
|
||||||
|
registers.setEBP(addressSpace.get32(savedRegisters));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
||||||
|
"function starting at 0x%X",
|
||||||
|
encoding, functionStart);
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
savedRegisters += 4;
|
||||||
|
}
|
||||||
|
framelessUnwind(addressSpace, savedRegisters, registers);
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
|
||||||
|
Registers_x86 ®isters) {
|
||||||
|
typename A::pint_t bp = registers.getEBP();
|
||||||
|
// ebp points to old ebp
|
||||||
|
registers.setEBP(addressSpace.get32(bp));
|
||||||
|
// old esp is ebp less saved ebp and return address
|
||||||
|
registers.setSP((uint32_t)bp + 8);
|
||||||
|
// pop return address into eip
|
||||||
|
registers.setIP(addressSpace.get32(bp + 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
void CompactUnwinder_x86<A>::framelessUnwind(
|
||||||
|
A &addressSpace, typename A::pint_t returnAddressLocation,
|
||||||
|
Registers_x86 ®isters) {
|
||||||
|
// return address is on stack after last saved register
|
||||||
|
registers.setIP(addressSpace.get32(returnAddressLocation));
|
||||||
|
// old esp is before return address
|
||||||
|
registers.setSP((uint32_t)returnAddressLocation + 4);
|
||||||
|
}
|
||||||
|
#endif // _LIBUNWIND_TARGET_I386
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_X86_64)
|
||||||
|
/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
|
||||||
|
/// unwind) by modifying a Registers_x86_64 register set
|
||||||
|
template <typename A>
|
||||||
|
class CompactUnwinder_x86_64 {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
||||||
|
uint64_t functionStart, A &addressSpace,
|
||||||
|
Registers_x86_64 ®isters);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename A::pint_t pint_t;
|
||||||
|
|
||||||
|
static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters);
|
||||||
|
static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
|
||||||
|
Registers_x86_64 ®isters);
|
||||||
|
static int
|
||||||
|
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
|
||||||
|
uint64_t functionStart, A &addressSpace,
|
||||||
|
Registers_x86_64 ®isters);
|
||||||
|
static int stepWithCompactEncodingFrameless(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86_64 ®isters) {
|
||||||
|
switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
|
||||||
|
case UNWIND_X86_64_MODE_RBP_FRAME:
|
||||||
|
return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers);
|
||||||
|
case UNWIND_X86_64_MODE_STACK_IMMD:
|
||||||
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers, false);
|
||||||
|
case UNWIND_X86_64_MODE_STACK_IND:
|
||||||
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers, true);
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||||
|
A &addressSpace, Registers_x86_64 ®isters) {
|
||||||
|
uint32_t savedRegistersOffset =
|
||||||
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
|
||||||
|
uint32_t savedRegistersLocations =
|
||||||
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
|
||||||
|
|
||||||
|
uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
switch (savedRegistersLocations & 0x7) {
|
||||||
|
case UNWIND_X86_64_REG_NONE:
|
||||||
|
// no register saved in this slot
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_RBX:
|
||||||
|
registers.setRBX(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R12:
|
||||||
|
registers.setR12(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R13:
|
||||||
|
registers.setR13(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R14:
|
||||||
|
registers.setR14(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R15:
|
||||||
|
registers.setR15(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
(void)functionStart;
|
||||||
|
_LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
|
||||||
|
"function starting at 0x%llX",
|
||||||
|
compactEncoding, functionStart);
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
savedRegisters += 8;
|
||||||
|
savedRegistersLocations = (savedRegistersLocations >> 3);
|
||||||
|
}
|
||||||
|
frameUnwind(addressSpace, registers);
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
|
||||||
|
compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
|
||||||
|
Registers_x86_64 ®isters, bool indirectStackSize) {
|
||||||
|
uint32_t stackSizeEncoded =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
|
||||||
|
uint32_t stackAdjust =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
|
||||||
|
uint32_t regCount =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
|
||||||
|
uint32_t permutation =
|
||||||
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
|
||||||
|
uint32_t stackSize = stackSizeEncoded * 8;
|
||||||
|
if (indirectStackSize) {
|
||||||
|
// stack size is encoded in subl $xxx,%esp instruction
|
||||||
|
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
||||||
|
stackSize = subl + 8 * stackAdjust;
|
||||||
|
}
|
||||||
|
// decompress permutation
|
||||||
|
uint32_t permunreg[6];
|
||||||
|
switch (regCount) {
|
||||||
|
case 6:
|
||||||
|
permunreg[0] = permutation / 120;
|
||||||
|
permutation -= (permunreg[0] * 120);
|
||||||
|
permunreg[1] = permutation / 24;
|
||||||
|
permutation -= (permunreg[1] * 24);
|
||||||
|
permunreg[2] = permutation / 6;
|
||||||
|
permutation -= (permunreg[2] * 6);
|
||||||
|
permunreg[3] = permutation / 2;
|
||||||
|
permutation -= (permunreg[3] * 2);
|
||||||
|
permunreg[4] = permutation;
|
||||||
|
permunreg[5] = 0;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
permunreg[0] = permutation / 120;
|
||||||
|
permutation -= (permunreg[0] * 120);
|
||||||
|
permunreg[1] = permutation / 24;
|
||||||
|
permutation -= (permunreg[1] * 24);
|
||||||
|
permunreg[2] = permutation / 6;
|
||||||
|
permutation -= (permunreg[2] * 6);
|
||||||
|
permunreg[3] = permutation / 2;
|
||||||
|
permutation -= (permunreg[3] * 2);
|
||||||
|
permunreg[4] = permutation;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
permunreg[0] = permutation / 60;
|
||||||
|
permutation -= (permunreg[0] * 60);
|
||||||
|
permunreg[1] = permutation / 12;
|
||||||
|
permutation -= (permunreg[1] * 12);
|
||||||
|
permunreg[2] = permutation / 3;
|
||||||
|
permutation -= (permunreg[2] * 3);
|
||||||
|
permunreg[3] = permutation;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
permunreg[0] = permutation / 20;
|
||||||
|
permutation -= (permunreg[0] * 20);
|
||||||
|
permunreg[1] = permutation / 4;
|
||||||
|
permutation -= (permunreg[1] * 4);
|
||||||
|
permunreg[2] = permutation;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
permunreg[0] = permutation / 5;
|
||||||
|
permutation -= (permunreg[0] * 5);
|
||||||
|
permunreg[1] = permutation;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
permunreg[0] = permutation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// re-number registers back to standard numbers
|
||||||
|
int registersSaved[6];
|
||||||
|
bool used[7] = { false, false, false, false, false, false, false };
|
||||||
|
for (uint32_t i = 0; i < regCount; ++i) {
|
||||||
|
uint32_t renum = 0;
|
||||||
|
for (int u = 1; u < 7; ++u) {
|
||||||
|
if (!used[u]) {
|
||||||
|
if (renum == permunreg[i]) {
|
||||||
|
registersSaved[i] = u;
|
||||||
|
used[u] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++renum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
|
||||||
|
for (uint32_t i = 0; i < regCount; ++i) {
|
||||||
|
switch (registersSaved[i]) {
|
||||||
|
case UNWIND_X86_64_REG_RBX:
|
||||||
|
registers.setRBX(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R12:
|
||||||
|
registers.setR12(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R13:
|
||||||
|
registers.setR13(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R14:
|
||||||
|
registers.setR14(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_R15:
|
||||||
|
registers.setR15(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
case UNWIND_X86_64_REG_RBP:
|
||||||
|
registers.setRBP(addressSpace.get64(savedRegisters));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
||||||
|
"function starting at 0x%llX",
|
||||||
|
encoding, functionStart);
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
savedRegisters += 8;
|
||||||
|
}
|
||||||
|
framelessUnwind(addressSpace, savedRegisters, registers);
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
|
||||||
|
Registers_x86_64 ®isters) {
|
||||||
|
uint64_t rbp = registers.getRBP();
|
||||||
|
// ebp points to old ebp
|
||||||
|
registers.setRBP(addressSpace.get64(rbp));
|
||||||
|
// old esp is ebp less saved ebp and return address
|
||||||
|
registers.setSP(rbp + 16);
|
||||||
|
// pop return address into eip
|
||||||
|
registers.setIP(addressSpace.get64(rbp + 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
|
||||||
|
uint64_t returnAddressLocation,
|
||||||
|
Registers_x86_64 ®isters) {
|
||||||
|
// return address is on stack after last saved register
|
||||||
|
registers.setIP(addressSpace.get64(returnAddressLocation));
|
||||||
|
// old esp is before return address
|
||||||
|
registers.setSP(returnAddressLocation + 8);
|
||||||
|
}
|
||||||
|
#endif // _LIBUNWIND_TARGET_X86_64
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
|
||||||
|
/// unwind) by modifying a Registers_arm64 register set
|
||||||
|
template <typename A>
|
||||||
|
class CompactUnwinder_arm64 {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
||||||
|
uint64_t functionStart, A &addressSpace,
|
||||||
|
Registers_arm64 ®isters);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename A::pint_t pint_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
|
||||||
|
uint64_t functionStart, A &addressSpace,
|
||||||
|
Registers_arm64 ®isters);
|
||||||
|
static int stepWithCompactEncodingFrameless(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||||
|
A &addressSpace, Registers_arm64 ®isters);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
|
||||||
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||||
|
A &addressSpace, Registers_arm64 ®isters) {
|
||||||
|
switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
|
||||||
|
case UNWIND_ARM64_MODE_FRAME:
|
||||||
|
return stepWithCompactEncodingFrame(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers);
|
||||||
|
case UNWIND_ARM64_MODE_FRAMELESS:
|
||||||
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||||
|
addressSpace, registers);
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
|
||||||
|
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
||||||
|
Registers_arm64 ®isters) {
|
||||||
|
uint32_t stackSize =
|
||||||
|
16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
|
||||||
|
|
||||||
|
uint64_t savedRegisterLoc = registers.getSP() + stackSize;
|
||||||
|
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D8,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D9,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D10,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D11,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D12,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D13,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D14,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D15,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtract stack size off of sp
|
||||||
|
registers.setSP(savedRegisterLoc);
|
||||||
|
|
||||||
|
// set pc to be value in lr
|
||||||
|
registers.setIP(registers.getRegister(UNW_ARM64_LR));
|
||||||
|
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
|
||||||
|
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
||||||
|
Registers_arm64 ®isters) {
|
||||||
|
uint64_t savedRegisterLoc = registers.getFP() - 8;
|
||||||
|
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
||||||
|
registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D8,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D9,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D10,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D11,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D12,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D13,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D14,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
registers.setFloatRegister(UNW_ARM64_D15,
|
||||||
|
addressSpace.getDouble(savedRegisterLoc));
|
||||||
|
savedRegisterLoc -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fp = registers.getFP();
|
||||||
|
// fp points to old fp
|
||||||
|
registers.setFP(addressSpace.get64(fp));
|
||||||
|
// old sp is fp less saved fp and lr
|
||||||
|
registers.setSP(fp + 16);
|
||||||
|
// pop return address into pc
|
||||||
|
registers.setIP(addressSpace.get64(fp + 8));
|
||||||
|
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif // _LIBUNWIND_TARGET_AARCH64
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace libunwind
|
||||||
|
|
||||||
|
#endif // __COMPACT_UNWINDER_HPP__
|
|
@ -0,0 +1,818 @@
|
||||||
|
//===-------------------------- DwarfInstructions.hpp ---------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Processor specific interpretation of DWARF unwind info.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __DWARF_INSTRUCTIONS_HPP__
|
||||||
|
#define __DWARF_INSTRUCTIONS_HPP__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "dwarf2.h"
|
||||||
|
#include "Registers.hpp"
|
||||||
|
#include "DwarfParser.hpp"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
|
||||||
|
/// DwarfInstructions maps abtract DWARF unwind instructions to a particular
|
||||||
|
/// architecture
|
||||||
|
template <typename A, typename R>
|
||||||
|
class DwarfInstructions {
|
||||||
|
public:
|
||||||
|
typedef typename A::pint_t pint_t;
|
||||||
|
typedef typename A::sint_t sint_t;
|
||||||
|
|
||||||
|
static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart,
|
||||||
|
R ®isters);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DW_X86_64_RET_ADDR = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DW_X86_RET_ADDR = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef typename CFI_Parser<A>::RegisterLocation RegisterLocation;
|
||||||
|
typedef typename CFI_Parser<A>::PrologInfo PrologInfo;
|
||||||
|
typedef typename CFI_Parser<A>::FDE_Info FDE_Info;
|
||||||
|
typedef typename CFI_Parser<A>::CIE_Info CIE_Info;
|
||||||
|
|
||||||
|
static pint_t evaluateExpression(pint_t expression, A &addressSpace,
|
||||||
|
const R ®isters,
|
||||||
|
pint_t initialStackValue);
|
||||||
|
static pint_t getSavedRegister(A &addressSpace, const R ®isters,
|
||||||
|
pint_t cfa, const RegisterLocation &savedReg);
|
||||||
|
static double getSavedFloatRegister(A &addressSpace, const R ®isters,
|
||||||
|
pint_t cfa, const RegisterLocation &savedReg);
|
||||||
|
static v128 getSavedVectorRegister(A &addressSpace, const R ®isters,
|
||||||
|
pint_t cfa, const RegisterLocation &savedReg);
|
||||||
|
|
||||||
|
static pint_t getCFA(A &addressSpace, const PrologInfo &prolog,
|
||||||
|
const R ®isters) {
|
||||||
|
if (prolog.cfaRegister != 0)
|
||||||
|
return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) +
|
||||||
|
prolog.cfaRegisterOffset);
|
||||||
|
if (prolog.cfaExpression != 0)
|
||||||
|
return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace,
|
||||||
|
registers, 0);
|
||||||
|
assert(0 && "getCFA(): unknown location");
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename A, typename R>
|
||||||
|
typename A::pint_t DwarfInstructions<A, R>::getSavedRegister(
|
||||||
|
A &addressSpace, const R ®isters, pint_t cfa,
|
||||||
|
const RegisterLocation &savedReg) {
|
||||||
|
switch (savedReg.location) {
|
||||||
|
case CFI_Parser<A>::kRegisterInCFA:
|
||||||
|
return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value);
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterAtExpression:
|
||||||
|
return (pint_t)addressSpace.getRegister(evaluateExpression(
|
||||||
|
(pint_t)savedReg.value, addressSpace, registers, cfa));
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterIsExpression:
|
||||||
|
return evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||||
|
registers, cfa);
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterInRegister:
|
||||||
|
return registers.getRegister((int)savedReg.value);
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterUnused:
|
||||||
|
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||||
|
// FIX ME
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("unsupported restore location for register");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename R>
|
||||||
|
double DwarfInstructions<A, R>::getSavedFloatRegister(
|
||||||
|
A &addressSpace, const R ®isters, pint_t cfa,
|
||||||
|
const RegisterLocation &savedReg) {
|
||||||
|
switch (savedReg.location) {
|
||||||
|
case CFI_Parser<A>::kRegisterInCFA:
|
||||||
|
return addressSpace.getDouble(cfa + (pint_t)savedReg.value);
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterAtExpression:
|
||||||
|
return addressSpace.getDouble(
|
||||||
|
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||||
|
registers, cfa));
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterIsExpression:
|
||||||
|
case CFI_Parser<A>::kRegisterUnused:
|
||||||
|
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||||
|
case CFI_Parser<A>::kRegisterInRegister:
|
||||||
|
// FIX ME
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("unsupported restore location for float register");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename R>
|
||||||
|
v128 DwarfInstructions<A, R>::getSavedVectorRegister(
|
||||||
|
A &addressSpace, const R ®isters, pint_t cfa,
|
||||||
|
const RegisterLocation &savedReg) {
|
||||||
|
switch (savedReg.location) {
|
||||||
|
case CFI_Parser<A>::kRegisterInCFA:
|
||||||
|
return addressSpace.getVector(cfa + (pint_t)savedReg.value);
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterAtExpression:
|
||||||
|
return addressSpace.getVector(
|
||||||
|
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||||
|
registers, cfa));
|
||||||
|
|
||||||
|
case CFI_Parser<A>::kRegisterIsExpression:
|
||||||
|
case CFI_Parser<A>::kRegisterUnused:
|
||||||
|
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||||
|
case CFI_Parser<A>::kRegisterInRegister:
|
||||||
|
// FIX ME
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("unsupported restore location for vector register");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename R>
|
||||||
|
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
||||||
|
pint_t fdeStart, R ®isters) {
|
||||||
|
FDE_Info fdeInfo;
|
||||||
|
CIE_Info cieInfo;
|
||||||
|
if (CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo,
|
||||||
|
&cieInfo) == NULL) {
|
||||||
|
PrologInfo prolog;
|
||||||
|
if (CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc,
|
||||||
|
R::getArch(), &prolog)) {
|
||||||
|
// get pointer to cfa (architecture specific)
|
||||||
|
pint_t cfa = getCFA(addressSpace, prolog, registers);
|
||||||
|
|
||||||
|
// restore registers that DWARF says were saved
|
||||||
|
R newRegisters = registers;
|
||||||
|
pint_t returnAddress = 0;
|
||||||
|
const int lastReg = R::lastDwarfRegNum();
|
||||||
|
assert(static_cast<int>(CFI_Parser<A>::kMaxRegisterNumber) >= lastReg &&
|
||||||
|
"register range too large");
|
||||||
|
assert(lastReg >= (int)cieInfo.returnAddressRegister &&
|
||||||
|
"register range does not contain return address register");
|
||||||
|
for (int i = 0; i <= lastReg; ++i) {
|
||||||
|
if (prolog.savedRegisters[i].location !=
|
||||||
|
CFI_Parser<A>::kRegisterUnused) {
|
||||||
|
if (registers.validFloatRegister(i))
|
||||||
|
newRegisters.setFloatRegister(
|
||||||
|
i, getSavedFloatRegister(addressSpace, registers, cfa,
|
||||||
|
prolog.savedRegisters[i]));
|
||||||
|
else if (registers.validVectorRegister(i))
|
||||||
|
newRegisters.setVectorRegister(
|
||||||
|
i, getSavedVectorRegister(addressSpace, registers, cfa,
|
||||||
|
prolog.savedRegisters[i]));
|
||||||
|
else if (i == (int)cieInfo.returnAddressRegister)
|
||||||
|
returnAddress = getSavedRegister(addressSpace, registers, cfa,
|
||||||
|
prolog.savedRegisters[i]);
|
||||||
|
else if (registers.validRegister(i))
|
||||||
|
newRegisters.setRegister(
|
||||||
|
i, getSavedRegister(addressSpace, registers, cfa,
|
||||||
|
prolog.savedRegisters[i]));
|
||||||
|
else
|
||||||
|
return UNW_EBADREG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// By definition, the CFA is the stack pointer at the call site, so
|
||||||
|
// restoring SP means setting it to CFA.
|
||||||
|
newRegisters.setSP(cfa);
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
// If the target is aarch64 then the return address may have been signed
|
||||||
|
// using the v8.3 pointer authentication extensions. The original
|
||||||
|
// return address needs to be authenticated before the return address is
|
||||||
|
// restored. autia1716 is used instead of autia as autia1716 assembles
|
||||||
|
// to a NOP on pre-v8.3a architectures.
|
||||||
|
if ((R::getArch() == REGISTERS_ARM64) &&
|
||||||
|
prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) {
|
||||||
|
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||||
|
return UNW_ECROSSRASIGNING;
|
||||||
|
#else
|
||||||
|
register unsigned long long x17 __asm("x17") = returnAddress;
|
||||||
|
register unsigned long long x16 __asm("x16") = cfa;
|
||||||
|
|
||||||
|
// These are the autia1716/autib1716 instructions. The hint instructions
|
||||||
|
// are used here as gcc does not assemble autia1716/autib1716 for pre
|
||||||
|
// armv8.3a targets.
|
||||||
|
if (cieInfo.addressesSignedWithBKey)
|
||||||
|
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
|
||||||
|
else
|
||||||
|
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
|
||||||
|
returnAddress = x17;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_SPARC)
|
||||||
|
if (R::getArch() == REGISTERS_SPARC) {
|
||||||
|
// Skip call site instruction and delay slot
|
||||||
|
returnAddress += 8;
|
||||||
|
// Skip unimp instruction if function returns a struct
|
||||||
|
if ((addressSpace.get32(returnAddress) & 0xC1C00000) == 0)
|
||||||
|
returnAddress += 4;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_PPC64)
|
||||||
|
#define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1)
|
||||||
|
#define PPC64_ELFV1_R2_OFFSET 40
|
||||||
|
#define PPC64_ELFV2_R2_LOAD_INST_ENCODING 0xe8410018u // ld r2,24(r1)
|
||||||
|
#define PPC64_ELFV2_R2_OFFSET 24
|
||||||
|
// If the instruction at return address is a TOC (r2) restore,
|
||||||
|
// then r2 was saved and needs to be restored.
|
||||||
|
// ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24,
|
||||||
|
// while in ELFv1 ABI it is saved at SP + 40.
|
||||||
|
if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) {
|
||||||
|
pint_t sp = newRegisters.getRegister(UNW_REG_SP);
|
||||||
|
pint_t r2 = 0;
|
||||||
|
switch (addressSpace.get32(returnAddress)) {
|
||||||
|
case PPC64_ELFV1_R2_LOAD_INST_ENCODING:
|
||||||
|
r2 = addressSpace.get64(sp + PPC64_ELFV1_R2_OFFSET);
|
||||||
|
break;
|
||||||
|
case PPC64_ELFV2_R2_LOAD_INST_ENCODING:
|
||||||
|
r2 = addressSpace.get64(sp + PPC64_ELFV2_R2_OFFSET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r2)
|
||||||
|
newRegisters.setRegister(UNW_PPC64_R2, r2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Return address is address after call site instruction, so setting IP to
|
||||||
|
// that does simualates a return.
|
||||||
|
newRegisters.setIP(returnAddress);
|
||||||
|
|
||||||
|
// Simulate the step by replacing the register set with the new ones.
|
||||||
|
registers = newRegisters;
|
||||||
|
|
||||||
|
return UNW_STEP_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UNW_EBADFRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename R>
|
||||||
|
typename A::pint_t
|
||||||
|
DwarfInstructions<A, R>::evaluateExpression(pint_t expression, A &addressSpace,
|
||||||
|
const R ®isters,
|
||||||
|
pint_t initialStackValue) {
|
||||||
|
const bool log = false;
|
||||||
|
pint_t p = expression;
|
||||||
|
pint_t expressionEnd = expression + 20; // temp, until len read
|
||||||
|
pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd);
|
||||||
|
expressionEnd = p + length;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n",
|
||||||
|
(uint64_t)length);
|
||||||
|
pint_t stack[100];
|
||||||
|
pint_t *sp = stack;
|
||||||
|
*(++sp) = initialStackValue;
|
||||||
|
|
||||||
|
while (p < expressionEnd) {
|
||||||
|
if (log) {
|
||||||
|
for (pint_t *t = sp; t > stack; --t) {
|
||||||
|
fprintf(stderr, "sp[] = 0x%" PRIx64 "\n", (uint64_t)(*t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t opcode = addressSpace.get8(p++);
|
||||||
|
sint_t svalue, svalue2;
|
||||||
|
pint_t value;
|
||||||
|
uint32_t reg;
|
||||||
|
switch (opcode) {
|
||||||
|
case DW_OP_addr:
|
||||||
|
// push immediate address sized value
|
||||||
|
value = addressSpace.getP(p);
|
||||||
|
p += sizeof(pint_t);
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_deref:
|
||||||
|
// pop stack, dereference, push result
|
||||||
|
value = *sp--;
|
||||||
|
*(++sp) = addressSpace.getP(value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const1u:
|
||||||
|
// push immediate 1 byte value
|
||||||
|
value = addressSpace.get8(p);
|
||||||
|
p += 1;
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const1s:
|
||||||
|
// push immediate 1 byte signed value
|
||||||
|
svalue = (int8_t) addressSpace.get8(p);
|
||||||
|
p += 1;
|
||||||
|
*(++sp) = (pint_t)svalue;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const2u:
|
||||||
|
// push immediate 2 byte value
|
||||||
|
value = addressSpace.get16(p);
|
||||||
|
p += 2;
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const2s:
|
||||||
|
// push immediate 2 byte signed value
|
||||||
|
svalue = (int16_t) addressSpace.get16(p);
|
||||||
|
p += 2;
|
||||||
|
*(++sp) = (pint_t)svalue;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const4u:
|
||||||
|
// push immediate 4 byte value
|
||||||
|
value = addressSpace.get32(p);
|
||||||
|
p += 4;
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const4s:
|
||||||
|
// push immediate 4 byte signed value
|
||||||
|
svalue = (int32_t)addressSpace.get32(p);
|
||||||
|
p += 4;
|
||||||
|
*(++sp) = (pint_t)svalue;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const8u:
|
||||||
|
// push immediate 8 byte value
|
||||||
|
value = (pint_t)addressSpace.get64(p);
|
||||||
|
p += 8;
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_const8s:
|
||||||
|
// push immediate 8 byte signed value
|
||||||
|
value = (pint_t)addressSpace.get64(p);
|
||||||
|
p += 8;
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_constu:
|
||||||
|
// push immediate ULEB128 value
|
||||||
|
value = (pint_t)addressSpace.getULEB128(p, expressionEnd);
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_consts:
|
||||||
|
// push immediate SLEB128 value
|
||||||
|
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||||
|
*(++sp) = (pint_t)svalue;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_dup:
|
||||||
|
// push top of stack
|
||||||
|
value = *sp;
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "duplicate top of stack\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_drop:
|
||||||
|
// pop
|
||||||
|
--sp;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "pop top of stack\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_over:
|
||||||
|
// dup second
|
||||||
|
value = sp[-1];
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "duplicate second in stack\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_pick:
|
||||||
|
// pick from
|
||||||
|
reg = addressSpace.get8(p);
|
||||||
|
p += 1;
|
||||||
|
value = sp[-reg];
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "duplicate %d in stack\n", reg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_swap:
|
||||||
|
// swap top two
|
||||||
|
value = sp[0];
|
||||||
|
sp[0] = sp[-1];
|
||||||
|
sp[-1] = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "swap top of stack\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_rot:
|
||||||
|
// rotate top three
|
||||||
|
value = sp[0];
|
||||||
|
sp[0] = sp[-1];
|
||||||
|
sp[-1] = sp[-2];
|
||||||
|
sp[-2] = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "rotate top three of stack\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_xderef:
|
||||||
|
// pop stack, dereference, push result
|
||||||
|
value = *sp--;
|
||||||
|
*sp = *((pint_t*)value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_abs:
|
||||||
|
svalue = (sint_t)*sp;
|
||||||
|
if (svalue < 0)
|
||||||
|
*sp = (pint_t)(-svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "abs\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_and:
|
||||||
|
value = *sp--;
|
||||||
|
*sp &= value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "and\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_div:
|
||||||
|
svalue = (sint_t)(*sp--);
|
||||||
|
svalue2 = (sint_t)*sp;
|
||||||
|
*sp = (pint_t)(svalue2 / svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "div\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_minus:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = *sp - value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "minus\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_mod:
|
||||||
|
svalue = (sint_t)(*sp--);
|
||||||
|
svalue2 = (sint_t)*sp;
|
||||||
|
*sp = (pint_t)(svalue2 % svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "module\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_mul:
|
||||||
|
svalue = (sint_t)(*sp--);
|
||||||
|
svalue2 = (sint_t)*sp;
|
||||||
|
*sp = (pint_t)(svalue2 * svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "mul\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_neg:
|
||||||
|
*sp = 0 - *sp;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "neg\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_not:
|
||||||
|
svalue = (sint_t)(*sp);
|
||||||
|
*sp = (pint_t)(~svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "not\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_or:
|
||||||
|
value = *sp--;
|
||||||
|
*sp |= value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "or\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_plus:
|
||||||
|
value = *sp--;
|
||||||
|
*sp += value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "plus\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_plus_uconst:
|
||||||
|
// pop stack, add uelb128 constant, push result
|
||||||
|
*sp += static_cast<pint_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "add constant\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_shl:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = *sp << value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "shift left\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_shr:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = *sp >> value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "shift left\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_shra:
|
||||||
|
value = *sp--;
|
||||||
|
svalue = (sint_t)*sp;
|
||||||
|
*sp = (pint_t)(svalue >> value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "shift left arithmetric\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_xor:
|
||||||
|
value = *sp--;
|
||||||
|
*sp ^= value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "xor\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_skip:
|
||||||
|
svalue = (int16_t) addressSpace.get16(p);
|
||||||
|
p += 2;
|
||||||
|
p = (pint_t)((sint_t)p + svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "skip %" PRIu64 "\n", (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_bra:
|
||||||
|
svalue = (int16_t) addressSpace.get16(p);
|
||||||
|
p += 2;
|
||||||
|
if (*sp--)
|
||||||
|
p = (pint_t)((sint_t)p + svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "bra %" PRIu64 "\n", (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_eq:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = (*sp == value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "eq\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_ge:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = (*sp >= value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "ge\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_gt:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = (*sp > value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "gt\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_le:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = (*sp <= value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "le\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_lt:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = (*sp < value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "lt\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_ne:
|
||||||
|
value = *sp--;
|
||||||
|
*sp = (*sp != value);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "ne\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_lit0:
|
||||||
|
case DW_OP_lit1:
|
||||||
|
case DW_OP_lit2:
|
||||||
|
case DW_OP_lit3:
|
||||||
|
case DW_OP_lit4:
|
||||||
|
case DW_OP_lit5:
|
||||||
|
case DW_OP_lit6:
|
||||||
|
case DW_OP_lit7:
|
||||||
|
case DW_OP_lit8:
|
||||||
|
case DW_OP_lit9:
|
||||||
|
case DW_OP_lit10:
|
||||||
|
case DW_OP_lit11:
|
||||||
|
case DW_OP_lit12:
|
||||||
|
case DW_OP_lit13:
|
||||||
|
case DW_OP_lit14:
|
||||||
|
case DW_OP_lit15:
|
||||||
|
case DW_OP_lit16:
|
||||||
|
case DW_OP_lit17:
|
||||||
|
case DW_OP_lit18:
|
||||||
|
case DW_OP_lit19:
|
||||||
|
case DW_OP_lit20:
|
||||||
|
case DW_OP_lit21:
|
||||||
|
case DW_OP_lit22:
|
||||||
|
case DW_OP_lit23:
|
||||||
|
case DW_OP_lit24:
|
||||||
|
case DW_OP_lit25:
|
||||||
|
case DW_OP_lit26:
|
||||||
|
case DW_OP_lit27:
|
||||||
|
case DW_OP_lit28:
|
||||||
|
case DW_OP_lit29:
|
||||||
|
case DW_OP_lit30:
|
||||||
|
case DW_OP_lit31:
|
||||||
|
value = static_cast<pint_t>(opcode - DW_OP_lit0);
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push literal 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_reg0:
|
||||||
|
case DW_OP_reg1:
|
||||||
|
case DW_OP_reg2:
|
||||||
|
case DW_OP_reg3:
|
||||||
|
case DW_OP_reg4:
|
||||||
|
case DW_OP_reg5:
|
||||||
|
case DW_OP_reg6:
|
||||||
|
case DW_OP_reg7:
|
||||||
|
case DW_OP_reg8:
|
||||||
|
case DW_OP_reg9:
|
||||||
|
case DW_OP_reg10:
|
||||||
|
case DW_OP_reg11:
|
||||||
|
case DW_OP_reg12:
|
||||||
|
case DW_OP_reg13:
|
||||||
|
case DW_OP_reg14:
|
||||||
|
case DW_OP_reg15:
|
||||||
|
case DW_OP_reg16:
|
||||||
|
case DW_OP_reg17:
|
||||||
|
case DW_OP_reg18:
|
||||||
|
case DW_OP_reg19:
|
||||||
|
case DW_OP_reg20:
|
||||||
|
case DW_OP_reg21:
|
||||||
|
case DW_OP_reg22:
|
||||||
|
case DW_OP_reg23:
|
||||||
|
case DW_OP_reg24:
|
||||||
|
case DW_OP_reg25:
|
||||||
|
case DW_OP_reg26:
|
||||||
|
case DW_OP_reg27:
|
||||||
|
case DW_OP_reg28:
|
||||||
|
case DW_OP_reg29:
|
||||||
|
case DW_OP_reg30:
|
||||||
|
case DW_OP_reg31:
|
||||||
|
reg = static_cast<uint32_t>(opcode - DW_OP_reg0);
|
||||||
|
*(++sp) = registers.getRegister((int)reg);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push reg %d\n", reg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_regx:
|
||||||
|
reg = static_cast<uint32_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||||
|
*(++sp) = registers.getRegister((int)reg);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_breg0:
|
||||||
|
case DW_OP_breg1:
|
||||||
|
case DW_OP_breg2:
|
||||||
|
case DW_OP_breg3:
|
||||||
|
case DW_OP_breg4:
|
||||||
|
case DW_OP_breg5:
|
||||||
|
case DW_OP_breg6:
|
||||||
|
case DW_OP_breg7:
|
||||||
|
case DW_OP_breg8:
|
||||||
|
case DW_OP_breg9:
|
||||||
|
case DW_OP_breg10:
|
||||||
|
case DW_OP_breg11:
|
||||||
|
case DW_OP_breg12:
|
||||||
|
case DW_OP_breg13:
|
||||||
|
case DW_OP_breg14:
|
||||||
|
case DW_OP_breg15:
|
||||||
|
case DW_OP_breg16:
|
||||||
|
case DW_OP_breg17:
|
||||||
|
case DW_OP_breg18:
|
||||||
|
case DW_OP_breg19:
|
||||||
|
case DW_OP_breg20:
|
||||||
|
case DW_OP_breg21:
|
||||||
|
case DW_OP_breg22:
|
||||||
|
case DW_OP_breg23:
|
||||||
|
case DW_OP_breg24:
|
||||||
|
case DW_OP_breg25:
|
||||||
|
case DW_OP_breg26:
|
||||||
|
case DW_OP_breg27:
|
||||||
|
case DW_OP_breg28:
|
||||||
|
case DW_OP_breg29:
|
||||||
|
case DW_OP_breg30:
|
||||||
|
case DW_OP_breg31:
|
||||||
|
reg = static_cast<uint32_t>(opcode - DW_OP_breg0);
|
||||||
|
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||||
|
svalue += static_cast<sint_t>(registers.getRegister((int)reg));
|
||||||
|
*(++sp) = (pint_t)(svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_bregx:
|
||||||
|
reg = static_cast<uint32_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||||
|
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||||
|
svalue += static_cast<sint_t>(registers.getRegister((int)reg));
|
||||||
|
*(++sp) = (pint_t)(svalue);
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_fbreg:
|
||||||
|
_LIBUNWIND_ABORT("DW_OP_fbreg not implemented");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_piece:
|
||||||
|
_LIBUNWIND_ABORT("DW_OP_piece not implemented");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_deref_size:
|
||||||
|
// pop stack, dereference, push result
|
||||||
|
value = *sp--;
|
||||||
|
switch (addressSpace.get8(p++)) {
|
||||||
|
case 1:
|
||||||
|
value = addressSpace.get8(value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = addressSpace.get16(value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
value = addressSpace.get32(value);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
value = (pint_t)addressSpace.get64(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_ABORT("DW_OP_deref_size with bad size");
|
||||||
|
}
|
||||||
|
*(++sp) = value;
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "sized dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_OP_xderef_size:
|
||||||
|
case DW_OP_nop:
|
||||||
|
case DW_OP_push_object_addres:
|
||||||
|
case DW_OP_call2:
|
||||||
|
case DW_OP_call4:
|
||||||
|
case DW_OP_call_ref:
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_ABORT("DWARF opcode not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (log)
|
||||||
|
fprintf(stderr, "expression evaluates to 0x%" PRIx64 "\n", (uint64_t)*sp);
|
||||||
|
return *sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace libunwind
|
||||||
|
|
||||||
|
#endif // __DWARF_INSTRUCTIONS_HPP__
|
|
@ -0,0 +1,765 @@
|
||||||
|
//===--------------------------- DwarfParser.hpp --------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Parses DWARF CFIs (FDEs and CIEs).
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __DWARF_PARSER_HPP__
|
||||||
|
#define __DWARF_PARSER_HPP__
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "libunwind.h"
|
||||||
|
#include "dwarf2.h"
|
||||||
|
#include "Registers.hpp"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
|
||||||
|
/// See DWARF Spec for details:
|
||||||
|
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||||
|
///
|
||||||
|
template <typename A>
|
||||||
|
class CFI_Parser {
|
||||||
|
public:
|
||||||
|
typedef typename A::pint_t pint_t;
|
||||||
|
|
||||||
|
/// Information encoded in a CIE (Common Information Entry)
|
||||||
|
struct CIE_Info {
|
||||||
|
pint_t cieStart;
|
||||||
|
pint_t cieLength;
|
||||||
|
pint_t cieInstructions;
|
||||||
|
uint8_t pointerEncoding;
|
||||||
|
uint8_t lsdaEncoding;
|
||||||
|
uint8_t personalityEncoding;
|
||||||
|
uint8_t personalityOffsetInCIE;
|
||||||
|
pint_t personality;
|
||||||
|
uint32_t codeAlignFactor;
|
||||||
|
int dataAlignFactor;
|
||||||
|
bool isSignalFrame;
|
||||||
|
bool fdesHaveAugmentationData;
|
||||||
|
uint8_t returnAddressRegister;
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
bool addressesSignedWithBKey;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information about an FDE (Frame Description Entry)
|
||||||
|
struct FDE_Info {
|
||||||
|
pint_t fdeStart;
|
||||||
|
pint_t fdeLength;
|
||||||
|
pint_t fdeInstructions;
|
||||||
|
pint_t pcStart;
|
||||||
|
pint_t pcEnd;
|
||||||
|
pint_t lsda;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER
|
||||||
|
};
|
||||||
|
enum RegisterSavedWhere {
|
||||||
|
kRegisterUnused,
|
||||||
|
kRegisterInCFA,
|
||||||
|
kRegisterOffsetFromCFA,
|
||||||
|
kRegisterInRegister,
|
||||||
|
kRegisterAtExpression,
|
||||||
|
kRegisterIsExpression
|
||||||
|
};
|
||||||
|
struct RegisterLocation {
|
||||||
|
RegisterSavedWhere location;
|
||||||
|
int64_t value;
|
||||||
|
};
|
||||||
|
/// Information about a frame layout and registers saved determined
|
||||||
|
/// by "running" the DWARF FDE "instructions"
|
||||||
|
struct PrologInfo {
|
||||||
|
uint32_t cfaRegister;
|
||||||
|
int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
|
||||||
|
int64_t cfaExpression; // CFA = expression
|
||||||
|
uint32_t spExtraArgSize;
|
||||||
|
uint32_t codeOffsetAtStackDecrement;
|
||||||
|
bool registersInOtherRegisters;
|
||||||
|
bool sameValueUsed;
|
||||||
|
RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PrologInfoStackEntry {
|
||||||
|
PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i)
|
||||||
|
: next(n), info(i) {}
|
||||||
|
PrologInfoStackEntry *next;
|
||||||
|
PrologInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
|
||||||
|
uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
|
||||||
|
CIE_Info *cieInfo);
|
||||||
|
static const char *decodeFDE(A &addressSpace, pint_t fdeStart,
|
||||||
|
FDE_Info *fdeInfo, CIE_Info *cieInfo);
|
||||||
|
static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo,
|
||||||
|
const CIE_Info &cieInfo, pint_t upToPC,
|
||||||
|
int arch, PrologInfo *results);
|
||||||
|
|
||||||
|
static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool parseInstructions(A &addressSpace, pint_t instructions,
|
||||||
|
pint_t instructionsEnd, const CIE_Info &cieInfo,
|
||||||
|
pint_t pcoffset,
|
||||||
|
PrologInfoStackEntry *&rememberStack, int arch,
|
||||||
|
PrologInfo *results);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Parse a FDE into a CIE_Info and an FDE_Info
|
||||||
|
template <typename A>
|
||||||
|
const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
|
||||||
|
FDE_Info *fdeInfo, CIE_Info *cieInfo) {
|
||||||
|
pint_t p = fdeStart;
|
||||||
|
pint_t cfiLength = (pint_t)addressSpace.get32(p);
|
||||||
|
p += 4;
|
||||||
|
if (cfiLength == 0xffffffff) {
|
||||||
|
// 0xffffffff means length is really next 8 bytes
|
||||||
|
cfiLength = (pint_t)addressSpace.get64(p);
|
||||||
|
p += 8;
|
||||||
|
}
|
||||||
|
if (cfiLength == 0)
|
||||||
|
return "FDE has zero length"; // end marker
|
||||||
|
uint32_t ciePointer = addressSpace.get32(p);
|
||||||
|
if (ciePointer == 0)
|
||||||
|
return "FDE is really a CIE"; // this is a CIE not an FDE
|
||||||
|
pint_t nextCFI = p + cfiLength;
|
||||||
|
pint_t cieStart = p - ciePointer;
|
||||||
|
const char *err = parseCIE(addressSpace, cieStart, cieInfo);
|
||||||
|
if (err != NULL)
|
||||||
|
return err;
|
||||||
|
p += 4;
|
||||||
|
// Parse pc begin and range.
|
||||||
|
pint_t pcStart =
|
||||||
|
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
|
||||||
|
pint_t pcRange =
|
||||||
|
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
|
||||||
|
// Parse rest of info.
|
||||||
|
fdeInfo->lsda = 0;
|
||||||
|
// Check for augmentation length.
|
||||||
|
if (cieInfo->fdesHaveAugmentationData) {
|
||||||
|
pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
|
||||||
|
pint_t endOfAug = p + augLen;
|
||||||
|
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
|
||||||
|
// Peek at value (without indirection). Zero means no LSDA.
|
||||||
|
pint_t lsdaStart = p;
|
||||||
|
if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) !=
|
||||||
|
0) {
|
||||||
|
// Reset pointer and re-parse LSDA address.
|
||||||
|
p = lsdaStart;
|
||||||
|
fdeInfo->lsda =
|
||||||
|
addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = endOfAug;
|
||||||
|
}
|
||||||
|
fdeInfo->fdeStart = fdeStart;
|
||||||
|
fdeInfo->fdeLength = nextCFI - fdeStart;
|
||||||
|
fdeInfo->fdeInstructions = p;
|
||||||
|
fdeInfo->pcStart = pcStart;
|
||||||
|
fdeInfo->pcEnd = pcStart + pcRange;
|
||||||
|
return NULL; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scan an eh_frame section to find an FDE for a pc
|
||||||
|
template <typename A>
|
||||||
|
bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
|
||||||
|
uint32_t sectionLength, pint_t fdeHint,
|
||||||
|
FDE_Info *fdeInfo, CIE_Info *cieInfo) {
|
||||||
|
//fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
|
||||||
|
pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
|
||||||
|
const pint_t ehSectionEnd = p + sectionLength;
|
||||||
|
while (p < ehSectionEnd) {
|
||||||
|
pint_t currentCFI = p;
|
||||||
|
//fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
|
||||||
|
pint_t cfiLength = addressSpace.get32(p);
|
||||||
|
p += 4;
|
||||||
|
if (cfiLength == 0xffffffff) {
|
||||||
|
// 0xffffffff means length is really next 8 bytes
|
||||||
|
cfiLength = (pint_t)addressSpace.get64(p);
|
||||||
|
p += 8;
|
||||||
|
}
|
||||||
|
if (cfiLength == 0)
|
||||||
|
return false; // end marker
|
||||||
|
uint32_t id = addressSpace.get32(p);
|
||||||
|
if (id == 0) {
|
||||||
|
// Skip over CIEs.
|
||||||
|
p += cfiLength;
|
||||||
|
} else {
|
||||||
|
// Process FDE to see if it covers pc.
|
||||||
|
pint_t nextCFI = p + cfiLength;
|
||||||
|
uint32_t ciePointer = addressSpace.get32(p);
|
||||||
|
pint_t cieStart = p - ciePointer;
|
||||||
|
// Validate pointer to CIE is within section.
|
||||||
|
if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) {
|
||||||
|
if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) {
|
||||||
|
p += 4;
|
||||||
|
// Parse pc begin and range.
|
||||||
|
pint_t pcStart =
|
||||||
|
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
|
||||||
|
pint_t pcRange = addressSpace.getEncodedP(
|
||||||
|
p, nextCFI, cieInfo->pointerEncoding & 0x0F);
|
||||||
|
// Test if pc is within the function this FDE covers.
|
||||||
|
if ((pcStart < pc) && (pc <= pcStart + pcRange)) {
|
||||||
|
// parse rest of info
|
||||||
|
fdeInfo->lsda = 0;
|
||||||
|
// check for augmentation length
|
||||||
|
if (cieInfo->fdesHaveAugmentationData) {
|
||||||
|
pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
|
||||||
|
pint_t endOfAug = p + augLen;
|
||||||
|
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
|
||||||
|
// Peek at value (without indirection). Zero means no LSDA.
|
||||||
|
pint_t lsdaStart = p;
|
||||||
|
if (addressSpace.getEncodedP(
|
||||||
|
p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) {
|
||||||
|
// Reset pointer and re-parse LSDA address.
|
||||||
|
p = lsdaStart;
|
||||||
|
fdeInfo->lsda = addressSpace
|
||||||
|
.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = endOfAug;
|
||||||
|
}
|
||||||
|
fdeInfo->fdeStart = currentCFI;
|
||||||
|
fdeInfo->fdeLength = nextCFI - currentCFI;
|
||||||
|
fdeInfo->fdeInstructions = p;
|
||||||
|
fdeInfo->pcStart = pcStart;
|
||||||
|
fdeInfo->pcEnd = pcStart + pcRange;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// pc is not in begin/range, skip this FDE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Malformed CIE, now augmentation describing pc range encoding.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// malformed FDE. CIE is bad
|
||||||
|
}
|
||||||
|
p = nextCFI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract info from a CIE
|
||||||
|
template <typename A>
|
||||||
|
const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
|
||||||
|
CIE_Info *cieInfo) {
|
||||||
|
cieInfo->pointerEncoding = 0;
|
||||||
|
cieInfo->lsdaEncoding = DW_EH_PE_omit;
|
||||||
|
cieInfo->personalityEncoding = 0;
|
||||||
|
cieInfo->personalityOffsetInCIE = 0;
|
||||||
|
cieInfo->personality = 0;
|
||||||
|
cieInfo->codeAlignFactor = 0;
|
||||||
|
cieInfo->dataAlignFactor = 0;
|
||||||
|
cieInfo->isSignalFrame = false;
|
||||||
|
cieInfo->fdesHaveAugmentationData = false;
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
cieInfo->addressesSignedWithBKey = false;
|
||||||
|
#endif
|
||||||
|
cieInfo->cieStart = cie;
|
||||||
|
pint_t p = cie;
|
||||||
|
pint_t cieLength = (pint_t)addressSpace.get32(p);
|
||||||
|
p += 4;
|
||||||
|
pint_t cieContentEnd = p + cieLength;
|
||||||
|
if (cieLength == 0xffffffff) {
|
||||||
|
// 0xffffffff means length is really next 8 bytes
|
||||||
|
cieLength = (pint_t)addressSpace.get64(p);
|
||||||
|
p += 8;
|
||||||
|
cieContentEnd = p + cieLength;
|
||||||
|
}
|
||||||
|
if (cieLength == 0)
|
||||||
|
return NULL;
|
||||||
|
// CIE ID is always 0
|
||||||
|
if (addressSpace.get32(p) != 0)
|
||||||
|
return "CIE ID is not zero";
|
||||||
|
p += 4;
|
||||||
|
// Version is always 1 or 3
|
||||||
|
uint8_t version = addressSpace.get8(p);
|
||||||
|
if ((version != 1) && (version != 3))
|
||||||
|
return "CIE version is not 1 or 3";
|
||||||
|
++p;
|
||||||
|
// save start of augmentation string and find end
|
||||||
|
pint_t strStart = p;
|
||||||
|
while (addressSpace.get8(p) != 0)
|
||||||
|
++p;
|
||||||
|
++p;
|
||||||
|
// parse code aligment factor
|
||||||
|
cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd);
|
||||||
|
// parse data alignment factor
|
||||||
|
cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd);
|
||||||
|
// parse return address register
|
||||||
|
uint64_t raReg = addressSpace.getULEB128(p, cieContentEnd);
|
||||||
|
assert(raReg < 255 && "return address register too large");
|
||||||
|
cieInfo->returnAddressRegister = (uint8_t)raReg;
|
||||||
|
// parse augmentation data based on augmentation string
|
||||||
|
const char *result = NULL;
|
||||||
|
if (addressSpace.get8(strStart) == 'z') {
|
||||||
|
// parse augmentation data length
|
||||||
|
addressSpace.getULEB128(p, cieContentEnd);
|
||||||
|
for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) {
|
||||||
|
switch (addressSpace.get8(s)) {
|
||||||
|
case 'z':
|
||||||
|
cieInfo->fdesHaveAugmentationData = true;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
cieInfo->personalityEncoding = addressSpace.get8(p);
|
||||||
|
++p;
|
||||||
|
cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
|
||||||
|
cieInfo->personality = addressSpace
|
||||||
|
.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
cieInfo->lsdaEncoding = addressSpace.get8(p);
|
||||||
|
++p;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
cieInfo->pointerEncoding = addressSpace.get8(p);
|
||||||
|
++p;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
cieInfo->isSignalFrame = true;
|
||||||
|
break;
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
case 'B':
|
||||||
|
cieInfo->addressesSignedWithBKey = true;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
// ignore unknown letters
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
|
||||||
|
cieInfo->cieInstructions = p;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// "run" the DWARF instructions and create the abstact PrologInfo for an FDE
|
||||||
|
template <typename A>
|
||||||
|
bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
|
||||||
|
const FDE_Info &fdeInfo,
|
||||||
|
const CIE_Info &cieInfo, pint_t upToPC,
|
||||||
|
int arch, PrologInfo *results) {
|
||||||
|
// clear results
|
||||||
|
memset(results, '\0', sizeof(PrologInfo));
|
||||||
|
PrologInfoStackEntry *rememberStack = NULL;
|
||||||
|
|
||||||
|
// parse CIE then FDE instructions
|
||||||
|
return parseInstructions(addressSpace, cieInfo.cieInstructions,
|
||||||
|
cieInfo.cieStart + cieInfo.cieLength, cieInfo,
|
||||||
|
(pint_t)(-1), rememberStack, arch, results) &&
|
||||||
|
parseInstructions(addressSpace, fdeInfo.fdeInstructions,
|
||||||
|
fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo,
|
||||||
|
upToPC - fdeInfo.pcStart, rememberStack, arch,
|
||||||
|
results);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "run" the DWARF instructions
|
||||||
|
template <typename A>
|
||||||
|
bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions,
|
||||||
|
pint_t instructionsEnd,
|
||||||
|
const CIE_Info &cieInfo, pint_t pcoffset,
|
||||||
|
PrologInfoStackEntry *&rememberStack,
|
||||||
|
int arch, PrologInfo *results) {
|
||||||
|
pint_t p = instructions;
|
||||||
|
pint_t codeOffset = 0;
|
||||||
|
PrologInfo initialState = *results;
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n",
|
||||||
|
static_cast<uint64_t>(instructionsEnd));
|
||||||
|
|
||||||
|
// see DWARF Spec, section 6.4.2 for details on unwind opcodes
|
||||||
|
while ((p < instructionsEnd) && (codeOffset < pcoffset)) {
|
||||||
|
uint64_t reg;
|
||||||
|
uint64_t reg2;
|
||||||
|
int64_t offset;
|
||||||
|
uint64_t length;
|
||||||
|
uint8_t opcode = addressSpace.get8(p);
|
||||||
|
uint8_t operand;
|
||||||
|
#if !defined(_LIBUNWIND_NO_HEAP)
|
||||||
|
PrologInfoStackEntry *entry;
|
||||||
|
#endif
|
||||||
|
++p;
|
||||||
|
switch (opcode) {
|
||||||
|
case DW_CFA_nop:
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n");
|
||||||
|
break;
|
||||||
|
case DW_CFA_set_loc:
|
||||||
|
codeOffset =
|
||||||
|
addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n");
|
||||||
|
break;
|
||||||
|
case DW_CFA_advance_loc1:
|
||||||
|
codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
|
||||||
|
p += 1;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n",
|
||||||
|
static_cast<uint64_t>(codeOffset));
|
||||||
|
break;
|
||||||
|
case DW_CFA_advance_loc2:
|
||||||
|
codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
|
||||||
|
p += 2;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n",
|
||||||
|
static_cast<uint64_t>(codeOffset));
|
||||||
|
break;
|
||||||
|
case DW_CFA_advance_loc4:
|
||||||
|
codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
|
||||||
|
p += 4;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n",
|
||||||
|
static_cast<uint64_t>(codeOffset));
|
||||||
|
break;
|
||||||
|
case DW_CFA_offset_extended:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||||
|
* cieInfo.dataAlignFactor;
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_offset_extended DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||||
|
results->savedRegisters[reg].value = offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", "
|
||||||
|
"offset=%" PRId64 ")\n",
|
||||||
|
reg, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_restore_extended:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_restore_extended DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg] = initialState.savedRegisters[reg];
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg);
|
||||||
|
break;
|
||||||
|
case DW_CFA_undefined:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_undefined DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg].location = kRegisterUnused;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg);
|
||||||
|
break;
|
||||||
|
case DW_CFA_same_value:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_same_value DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// <rdar://problem/8456377> DW_CFA_same_value unsupported
|
||||||
|
// "same value" means register was stored in frame, but its current
|
||||||
|
// value has not changed, so no need to restore from frame.
|
||||||
|
// We model this as if the register was never saved.
|
||||||
|
results->savedRegisters[reg].location = kRegisterUnused;
|
||||||
|
// set flag to disable conversion to compact unwind
|
||||||
|
results->sameValueUsed = true;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg);
|
||||||
|
break;
|
||||||
|
case DW_CFA_register:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
reg2 = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_register DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (reg2 > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_register DWARF unwind, reg2 too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg].location = kRegisterInRegister;
|
||||||
|
results->savedRegisters[reg].value = (int64_t)reg2;
|
||||||
|
// set flag to disable conversion to compact unwind
|
||||||
|
results->registersInOtherRegisters = true;
|
||||||
|
_LIBUNWIND_TRACE_DWARF(
|
||||||
|
"DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2);
|
||||||
|
break;
|
||||||
|
#if !defined(_LIBUNWIND_NO_HEAP)
|
||||||
|
case DW_CFA_remember_state:
|
||||||
|
// avoid operator new, because that would be an upward dependency
|
||||||
|
entry = (PrologInfoStackEntry *)malloc(sizeof(PrologInfoStackEntry));
|
||||||
|
if (entry != NULL) {
|
||||||
|
entry->next = rememberStack;
|
||||||
|
entry->info = *results;
|
||||||
|
rememberStack = entry;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n");
|
||||||
|
break;
|
||||||
|
case DW_CFA_restore_state:
|
||||||
|
if (rememberStack != NULL) {
|
||||||
|
PrologInfoStackEntry *top = rememberStack;
|
||||||
|
*results = top->info;
|
||||||
|
rememberStack = top->next;
|
||||||
|
free((char *)top);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n");
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case DW_CFA_def_cfa:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->cfaRegister = (uint32_t)reg;
|
||||||
|
results->cfaRegisterOffset = (int32_t)offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF(
|
||||||
|
"DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n", reg, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_def_cfa_register:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_def_cfa_register DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->cfaRegister = (uint32_t)reg;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg);
|
||||||
|
break;
|
||||||
|
case DW_CFA_def_cfa_offset:
|
||||||
|
results->cfaRegisterOffset = (int32_t)
|
||||||
|
addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n",
|
||||||
|
results->cfaRegisterOffset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_def_cfa_expression:
|
||||||
|
results->cfaRegister = 0;
|
||||||
|
results->cfaExpression = (int64_t)p;
|
||||||
|
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
||||||
|
p += static_cast<pint_t>(length);
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64
|
||||||
|
", length=%" PRIu64 ")\n",
|
||||||
|
results->cfaExpression, length);
|
||||||
|
break;
|
||||||
|
case DW_CFA_expression:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_expression DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg].location = kRegisterAtExpression;
|
||||||
|
results->savedRegisters[reg].value = (int64_t)p;
|
||||||
|
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
||||||
|
p += static_cast<pint_t>(length);
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", "
|
||||||
|
"expression=0x%" PRIx64 ", "
|
||||||
|
"length=%" PRIu64 ")\n",
|
||||||
|
reg, results->savedRegisters[reg].value, length);
|
||||||
|
break;
|
||||||
|
case DW_CFA_offset_extended_sf:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset =
|
||||||
|
addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
|
||||||
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||||
|
results->savedRegisters[reg].value = offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", "
|
||||||
|
"offset=%" PRId64 ")\n",
|
||||||
|
reg, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_def_cfa_sf:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
offset =
|
||||||
|
addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->cfaRegister = (uint32_t)reg;
|
||||||
|
results->cfaRegisterOffset = (int32_t)offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", "
|
||||||
|
"offset=%" PRId64 ")\n",
|
||||||
|
reg, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_def_cfa_offset_sf:
|
||||||
|
results->cfaRegisterOffset = (int32_t)
|
||||||
|
(addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor);
|
||||||
|
results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n",
|
||||||
|
results->cfaRegisterOffset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_val_offset:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG(
|
||||||
|
"malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64
|
||||||
|
") out of range\n",
|
||||||
|
reg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||||
|
* cieInfo.dataAlignFactor;
|
||||||
|
results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
|
||||||
|
results->savedRegisters[reg].value = offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", "
|
||||||
|
"offset=%" PRId64 "\n",
|
||||||
|
reg, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_val_offset_sf:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_val_offset_sf DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset =
|
||||||
|
addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
|
||||||
|
results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
|
||||||
|
results->savedRegisters[reg].value = offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", "
|
||||||
|
"offset=%" PRId64 "\n",
|
||||||
|
reg, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_val_expression:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0(
|
||||||
|
"malformed DW_CFA_val_expression DWARF unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg].location = kRegisterIsExpression;
|
||||||
|
results->savedRegisters[reg].value = (int64_t)p;
|
||||||
|
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
||||||
|
p += static_cast<pint_t>(length);
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", "
|
||||||
|
"expression=0x%" PRIx64 ", length=%" PRIu64 ")\n",
|
||||||
|
reg, results->savedRegisters[reg].value, length);
|
||||||
|
break;
|
||||||
|
case DW_CFA_GNU_args_size:
|
||||||
|
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
results->spExtraArgSize = (uint32_t)length;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length);
|
||||||
|
break;
|
||||||
|
case DW_CFA_GNU_negative_offset_extended:
|
||||||
|
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF "
|
||||||
|
"unwind, reg too big");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||||
|
* cieInfo.dataAlignFactor;
|
||||||
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||||
|
results->savedRegisters[reg].value = -offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF(
|
||||||
|
"DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC)
|
||||||
|
// The same constant is used to represent different instructions on
|
||||||
|
// AArch64 (negate_ra_state) and SPARC (window_save).
|
||||||
|
static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save,
|
||||||
|
"uses the same constant");
|
||||||
|
case DW_CFA_AARCH64_negate_ra_state:
|
||||||
|
switch (arch) {
|
||||||
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
case REGISTERS_ARM64:
|
||||||
|
results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n");
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(_LIBUNWIND_TARGET_SPARC)
|
||||||
|
// case DW_CFA_GNU_window_save:
|
||||||
|
case REGISTERS_SPARC:
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n");
|
||||||
|
for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) {
|
||||||
|
results->savedRegisters[reg].location = kRegisterInRegister;
|
||||||
|
results->savedRegisters[reg].value =
|
||||||
|
((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
|
||||||
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||||
|
results->savedRegisters[reg].value =
|
||||||
|
((int64_t)reg - UNW_SPARC_L0) * 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
(void)arch;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
operand = opcode & 0x3F;
|
||||||
|
switch (opcode & 0xC0) {
|
||||||
|
case DW_CFA_offset:
|
||||||
|
reg = operand;
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64
|
||||||
|
") out of range",
|
||||||
|
reg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||||
|
* cieInfo.dataAlignFactor;
|
||||||
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||||
|
results->savedRegisters[reg].value = offset;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n",
|
||||||
|
operand, offset);
|
||||||
|
break;
|
||||||
|
case DW_CFA_advance_loc:
|
||||||
|
codeOffset += operand * cieInfo.codeAlignFactor;
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n",
|
||||||
|
static_cast<uint64_t>(codeOffset));
|
||||||
|
break;
|
||||||
|
case DW_CFA_restore:
|
||||||
|
reg = operand;
|
||||||
|
if (reg > kMaxRegisterNumber) {
|
||||||
|
_LIBUNWIND_LOG("malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64
|
||||||
|
") out of range",
|
||||||
|
reg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
results->savedRegisters[reg] = initialState.savedRegisters[reg];
|
||||||
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n",
|
||||||
|
static_cast<uint64_t>(operand));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace libunwind
|
||||||
|
|
||||||
|
#endif // __DWARF_PARSER_HPP__
|
|
@ -0,0 +1,167 @@
|
||||||
|
//===------------------------- EHHeaderParser.hpp -------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Parses ELF .eh_frame_hdr sections.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __EHHEADERPARSER_HPP__
|
||||||
|
#define __EHHEADERPARSER_HPP__
|
||||||
|
|
||||||
|
#include "libunwind.h"
|
||||||
|
|
||||||
|
#include "DwarfParser.hpp"
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section.
|
||||||
|
///
|
||||||
|
/// See DWARF spec for details:
|
||||||
|
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||||
|
///
|
||||||
|
template <typename A> class EHHeaderParser {
|
||||||
|
public:
|
||||||
|
typedef typename A::pint_t pint_t;
|
||||||
|
|
||||||
|
/// Information encoded in the EH frame header.
|
||||||
|
struct EHHeaderInfo {
|
||||||
|
pint_t eh_frame_ptr;
|
||||||
|
size_t fde_count;
|
||||||
|
pint_t table;
|
||||||
|
uint8_t table_enc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||||
|
EHHeaderInfo &ehHdrInfo);
|
||||||
|
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||||
|
uint32_t sectionLength,
|
||||||
|
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||||
|
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry,
|
||||||
|
pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||||
|
uint8_t tableEnc,
|
||||||
|
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||||
|
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||||
|
static size_t getTableEntrySize(uint8_t tableEnc);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
bool EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart,
|
||||||
|
pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) {
|
||||||
|
pint_t p = ehHdrStart;
|
||||||
|
uint8_t version = addressSpace.get8(p++);
|
||||||
|
if (version != 1) {
|
||||||
|
_LIBUNWIND_LOG0("Unsupported .eh_frame_hdr version");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t eh_frame_ptr_enc = addressSpace.get8(p++);
|
||||||
|
uint8_t fde_count_enc = addressSpace.get8(p++);
|
||||||
|
ehHdrInfo.table_enc = addressSpace.get8(p++);
|
||||||
|
|
||||||
|
ehHdrInfo.eh_frame_ptr =
|
||||||
|
addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart);
|
||||||
|
ehHdrInfo.fde_count =
|
||||||
|
fde_count_enc == DW_EH_PE_omit
|
||||||
|
? 0
|
||||||
|
: addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart);
|
||||||
|
ehHdrInfo.table = p;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
bool EHHeaderParser<A>::decodeTableEntry(
|
||||||
|
A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||||
|
uint8_t tableEnc, typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||||
|
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||||
|
// Have to decode the whole FDE for the PC range anyway, so just throw away
|
||||||
|
// the PC start.
|
||||||
|
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||||
|
pint_t fde =
|
||||||
|
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||||
|
const char *message =
|
||||||
|
CFI_Parser<A>::decodeFDE(addressSpace, fde, fdeInfo, cieInfo);
|
||||||
|
if (message != NULL) {
|
||||||
|
_LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s",
|
||||||
|
message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
bool EHHeaderParser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||||
|
uint32_t sectionLength,
|
||||||
|
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||||
|
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||||
|
pint_t ehHdrEnd = ehHdrStart + sectionLength;
|
||||||
|
|
||||||
|
EHHeaderParser<A>::EHHeaderInfo hdrInfo;
|
||||||
|
if (!EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd,
|
||||||
|
hdrInfo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc);
|
||||||
|
pint_t tableEntry;
|
||||||
|
|
||||||
|
size_t low = 0;
|
||||||
|
for (size_t len = hdrInfo.fde_count; len > 1;) {
|
||||||
|
size_t mid = low + (len / 2);
|
||||||
|
tableEntry = hdrInfo.table + mid * tableEntrySize;
|
||||||
|
pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd,
|
||||||
|
hdrInfo.table_enc, ehHdrStart);
|
||||||
|
|
||||||
|
if (start == pc) {
|
||||||
|
low = mid;
|
||||||
|
break;
|
||||||
|
} else if (start < pc) {
|
||||||
|
low = mid;
|
||||||
|
len -= (len / 2);
|
||||||
|
} else {
|
||||||
|
len /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableEntry = hdrInfo.table + low * tableEntrySize;
|
||||||
|
if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd,
|
||||||
|
hdrInfo.table_enc, fdeInfo, cieInfo)) {
|
||||||
|
if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
size_t EHHeaderParser<A>::getTableEntrySize(uint8_t tableEnc) {
|
||||||
|
switch (tableEnc & 0x0f) {
|
||||||
|
case DW_EH_PE_sdata2:
|
||||||
|
case DW_EH_PE_udata2:
|
||||||
|
return 4;
|
||||||
|
case DW_EH_PE_sdata4:
|
||||||
|
case DW_EH_PE_udata4:
|
||||||
|
return 8;
|
||||||
|
case DW_EH_PE_sdata8:
|
||||||
|
case DW_EH_PE_udata8:
|
||||||
|
return 16;
|
||||||
|
case DW_EH_PE_sleb128:
|
||||||
|
case DW_EH_PE_uleb128:
|
||||||
|
_LIBUNWIND_ABORT("Can't binary search on variable length encoded data.");
|
||||||
|
case DW_EH_PE_omit:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_ABORT("Unknown DWARF encoding for search table.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,114 @@
|
||||||
|
//===----------------------------- Registers.hpp --------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Abstract interface to shared reader/writer log, hiding platform and
|
||||||
|
// configuration differences.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __RWMUTEX_HPP__
|
||||||
|
#define __RWMUTEX_HPP__
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#elif !defined(_LIBUNWIND_HAS_NO_THREADS)
|
||||||
|
#include <pthread.h>
|
||||||
|
#if defined(__unix__) && !defined(__ANDROID__) && defined(__ELF__) && defined(_LIBUNWIND_HAS_COMMENT_LIB_PRAGMA)
|
||||||
|
#pragma comment(lib, "pthread")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_HAS_NO_THREADS)
|
||||||
|
|
||||||
|
class _LIBUNWIND_HIDDEN RWMutex {
|
||||||
|
public:
|
||||||
|
bool lock_shared() { return true; }
|
||||||
|
bool unlock_shared() { return true; }
|
||||||
|
bool lock() { return true; }
|
||||||
|
bool unlock() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
class _LIBUNWIND_HIDDEN RWMutex {
|
||||||
|
public:
|
||||||
|
bool lock_shared() {
|
||||||
|
AcquireSRWLockShared(&_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool unlock_shared() {
|
||||||
|
ReleaseSRWLockShared(&_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool lock() {
|
||||||
|
AcquireSRWLockExclusive(&_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool unlock() {
|
||||||
|
ReleaseSRWLockExclusive(&_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SRWLOCK _lock = SRWLOCK_INIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif !defined(LIBUNWIND_USE_WEAK_PTHREAD)
|
||||||
|
|
||||||
|
class _LIBUNWIND_HIDDEN RWMutex {
|
||||||
|
public:
|
||||||
|
bool lock_shared() { return pthread_rwlock_rdlock(&_lock) == 0; }
|
||||||
|
bool unlock_shared() { return pthread_rwlock_unlock(&_lock) == 0; }
|
||||||
|
bool lock() { return pthread_rwlock_wrlock(&_lock) == 0; }
|
||||||
|
bool unlock() { return pthread_rwlock_unlock(&_lock) == 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
extern "C" int __attribute__((weak))
|
||||||
|
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
void *(*start_routine)(void *), void *arg);
|
||||||
|
extern "C" int __attribute__((weak))
|
||||||
|
pthread_rwlock_rdlock(pthread_rwlock_t *lock);
|
||||||
|
extern "C" int __attribute__((weak))
|
||||||
|
pthread_rwlock_wrlock(pthread_rwlock_t *lock);
|
||||||
|
extern "C" int __attribute__((weak))
|
||||||
|
pthread_rwlock_unlock(pthread_rwlock_t *lock);
|
||||||
|
|
||||||
|
// Calls to the locking functions are gated on pthread_create, and not the
|
||||||
|
// functions themselves, because the data structure should only be locked if
|
||||||
|
// another thread has been created. This is what similar libraries do.
|
||||||
|
|
||||||
|
class _LIBUNWIND_HIDDEN RWMutex {
|
||||||
|
public:
|
||||||
|
bool lock_shared() {
|
||||||
|
return !pthread_create || (pthread_rwlock_rdlock(&_lock) == 0);
|
||||||
|
}
|
||||||
|
bool unlock_shared() {
|
||||||
|
return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0);
|
||||||
|
}
|
||||||
|
bool lock() {
|
||||||
|
return !pthread_create || (pthread_rwlock_wrlock(&_lock) == 0);
|
||||||
|
}
|
||||||
|
bool unlock() {
|
||||||
|
return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace libunwind
|
||||||
|
|
||||||
|
#endif // __RWMUTEX_HPP__
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,999 @@
|
||||||
|
//===--------------------------- Unwind-EHABI.cpp -------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Implements ARM zero-cost C++ exceptions
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "Unwind-EHABI.h"
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "libunwind.h"
|
||||||
|
#include "libunwind_ext.h"
|
||||||
|
#include "unwind.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Strange order: take words in order, but inside word, take from most to least
|
||||||
|
// signinficant byte.
|
||||||
|
uint8_t getByte(const uint32_t* data, size_t offset) {
|
||||||
|
const uint8_t* byteData = reinterpret_cast<const uint8_t*>(data);
|
||||||
|
#ifdef __LITTLE_ENDIAN__
|
||||||
|
return byteData[(offset & ~(size_t)0x03) + (3 - (offset & (size_t)0x03))];
|
||||||
|
#else
|
||||||
|
return byteData[offset];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getNextWord(const char* data, uint32_t* out) {
|
||||||
|
*out = *reinterpret_cast<const uint32_t*>(data);
|
||||||
|
return data + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getNextNibble(const char* data, uint32_t* out) {
|
||||||
|
*out = *reinterpret_cast<const uint16_t*>(data);
|
||||||
|
return data + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Descriptor {
|
||||||
|
// See # 9.2
|
||||||
|
typedef enum {
|
||||||
|
SU16 = 0, // Short descriptor, 16-bit entries
|
||||||
|
LU16 = 1, // Long descriptor, 16-bit entries
|
||||||
|
LU32 = 3, // Long descriptor, 32-bit entries
|
||||||
|
RESERVED0 = 4, RESERVED1 = 5, RESERVED2 = 6, RESERVED3 = 7,
|
||||||
|
RESERVED4 = 8, RESERVED5 = 9, RESERVED6 = 10, RESERVED7 = 11,
|
||||||
|
RESERVED8 = 12, RESERVED9 = 13, RESERVED10 = 14, RESERVED11 = 15
|
||||||
|
} Format;
|
||||||
|
|
||||||
|
// See # 9.2
|
||||||
|
typedef enum {
|
||||||
|
CLEANUP = 0x0,
|
||||||
|
FUNC = 0x1,
|
||||||
|
CATCH = 0x2,
|
||||||
|
INVALID = 0x4
|
||||||
|
} Kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
_Unwind_Reason_Code ProcessDescriptors(
|
||||||
|
_Unwind_State state,
|
||||||
|
_Unwind_Control_Block* ucbp,
|
||||||
|
struct _Unwind_Context* context,
|
||||||
|
Descriptor::Format format,
|
||||||
|
const char* descriptorStart,
|
||||||
|
uint32_t flags) {
|
||||||
|
|
||||||
|
// EHT is inlined in the index using compact form. No descriptors. #5
|
||||||
|
if (flags & 0x1)
|
||||||
|
return _URC_CONTINUE_UNWIND;
|
||||||
|
|
||||||
|
// TODO: We should check the state here, and determine whether we need to
|
||||||
|
// perform phase1 or phase2 unwinding.
|
||||||
|
(void)state;
|
||||||
|
|
||||||
|
const char* descriptor = descriptorStart;
|
||||||
|
uint32_t descriptorWord;
|
||||||
|
getNextWord(descriptor, &descriptorWord);
|
||||||
|
while (descriptorWord) {
|
||||||
|
// Read descriptor based on # 9.2.
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t offset;
|
||||||
|
switch (format) {
|
||||||
|
case Descriptor::LU32:
|
||||||
|
descriptor = getNextWord(descriptor, &length);
|
||||||
|
descriptor = getNextWord(descriptor, &offset);
|
||||||
|
case Descriptor::LU16:
|
||||||
|
descriptor = getNextNibble(descriptor, &length);
|
||||||
|
descriptor = getNextNibble(descriptor, &offset);
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return _URC_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See # 9.2 table for decoding the kind of descriptor. It's a 2-bit value.
|
||||||
|
Descriptor::Kind kind =
|
||||||
|
static_cast<Descriptor::Kind>((length & 0x1) | ((offset & 0x1) << 1));
|
||||||
|
|
||||||
|
// Clear off flag from last bit.
|
||||||
|
length &= ~1u;
|
||||||
|
offset &= ~1u;
|
||||||
|
uintptr_t scopeStart = ucbp->pr_cache.fnstart + offset;
|
||||||
|
uintptr_t scopeEnd = scopeStart + length;
|
||||||
|
uintptr_t pc = _Unwind_GetIP(context);
|
||||||
|
bool isInScope = (scopeStart <= pc) && (pc < scopeEnd);
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
case Descriptor::CLEANUP: {
|
||||||
|
// TODO(ajwong): Handle cleanup descriptors.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::FUNC: {
|
||||||
|
// TODO(ajwong): Handle function descriptors.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::CATCH: {
|
||||||
|
// Catch descriptors require gobbling one more word.
|
||||||
|
uint32_t landing_pad;
|
||||||
|
descriptor = getNextWord(descriptor, &landing_pad);
|
||||||
|
|
||||||
|
if (isInScope) {
|
||||||
|
// TODO(ajwong): This is only phase1 compatible logic. Implement
|
||||||
|
// phase2.
|
||||||
|
landing_pad = signExtendPrel31(landing_pad & ~0x80000000);
|
||||||
|
if (landing_pad == 0xffffffff) {
|
||||||
|
return _URC_HANDLER_FOUND;
|
||||||
|
} else if (landing_pad == 0xfffffffe) {
|
||||||
|
return _URC_FAILURE;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
bool is_reference_type = landing_pad & 0x80000000;
|
||||||
|
void* matched_object;
|
||||||
|
if (__cxxabiv1::__cxa_type_match(
|
||||||
|
ucbp, reinterpret_cast<const std::type_info *>(landing_pad),
|
||||||
|
is_reference_type,
|
||||||
|
&matched_object) != __cxxabiv1::ctm_failed)
|
||||||
|
return _URC_HANDLER_FOUND;
|
||||||
|
*/
|
||||||
|
_LIBUNWIND_ABORT("Type matching not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_LIBUNWIND_ABORT("Invalid descriptor kind found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextWord(descriptor, &descriptorWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _URC_CONTINUE_UNWIND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state,
|
||||||
|
_Unwind_Control_Block* ucbp,
|
||||||
|
struct _Unwind_Context* context) {
|
||||||
|
// Read the compact model EHT entry's header # 6.3
|
||||||
|
const uint32_t* unwindingData = ucbp->pr_cache.ehtp;
|
||||||
|
assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry");
|
||||||
|
Descriptor::Format format =
|
||||||
|
static_cast<Descriptor::Format>((*unwindingData & 0x0f000000) >> 24);
|
||||||
|
|
||||||
|
const char *lsda =
|
||||||
|
reinterpret_cast<const char *>(_Unwind_GetLanguageSpecificData(context));
|
||||||
|
|
||||||
|
// Handle descriptors before unwinding so they are processed in the context
|
||||||
|
// of the correct stack frame.
|
||||||
|
_Unwind_Reason_Code result =
|
||||||
|
ProcessDescriptors(state, ucbp, context, format, lsda,
|
||||||
|
ucbp->pr_cache.additional);
|
||||||
|
|
||||||
|
if (result != _URC_CONTINUE_UNWIND)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (__unw_step(reinterpret_cast<unw_cursor_t *>(context)) != UNW_STEP_SUCCESS)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
return _URC_CONTINUE_UNWIND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE /
|
||||||
|
// _UVRSD_UINT32.
|
||||||
|
uint32_t RegisterMask(uint8_t start, uint8_t count_minus_one) {
|
||||||
|
return ((1U << (count_minus_one + 1)) - 1) << start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_VFP /
|
||||||
|
// _UVRSD_DOUBLE.
|
||||||
|
uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) {
|
||||||
|
return ((uint32_t)start << 16) | ((uint32_t)count_minus_one + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes an EHT entry.
|
||||||
|
*
|
||||||
|
* @param data Pointer to EHT.
|
||||||
|
* @param[out] off Offset from return value (in bytes) to begin interpretation.
|
||||||
|
* @param[out] len Number of bytes in unwind code.
|
||||||
|
* @return Pointer to beginning of unwind code.
|
||||||
|
*/
|
||||||
|
extern "C" const uint32_t*
|
||||||
|
decode_eht_entry(const uint32_t* data, size_t* off, size_t* len) {
|
||||||
|
if ((*data & 0x80000000) == 0) {
|
||||||
|
// 6.2: Generic Model
|
||||||
|
//
|
||||||
|
// EHT entry is a prel31 pointing to the PR, followed by data understood
|
||||||
|
// only by the personality routine. Fortunately, all existing assembler
|
||||||
|
// implementations, including GNU assembler, LLVM integrated assembler,
|
||||||
|
// and ARM assembler, assume that the unwind opcodes come after the
|
||||||
|
// personality rountine address.
|
||||||
|
*off = 1; // First byte is size data.
|
||||||
|
*len = (((data[1] >> 24) & 0xff) + 1) * 4;
|
||||||
|
data++; // Skip the first word, which is the prel31 offset.
|
||||||
|
} else {
|
||||||
|
// 6.3: ARM Compact Model
|
||||||
|
//
|
||||||
|
// EHT entries here correspond to the __aeabi_unwind_cpp_pr[012] PRs indeded
|
||||||
|
// by format:
|
||||||
|
Descriptor::Format format =
|
||||||
|
static_cast<Descriptor::Format>((*data & 0x0f000000) >> 24);
|
||||||
|
switch (format) {
|
||||||
|
case Descriptor::SU16:
|
||||||
|
*len = 4;
|
||||||
|
*off = 1;
|
||||||
|
break;
|
||||||
|
case Descriptor::LU16:
|
||||||
|
case Descriptor::LU32:
|
||||||
|
*len = 4 + 4 * ((*data & 0x00ff0000) >> 16);
|
||||||
|
*off = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data,
|
||||||
|
size_t offset, size_t len) {
|
||||||
|
bool wrotePC = false;
|
||||||
|
bool finish = false;
|
||||||
|
while (offset < len && !finish) {
|
||||||
|
uint8_t byte = getByte(data, offset++);
|
||||||
|
if ((byte & 0x80) == 0) {
|
||||||
|
uint32_t sp;
|
||||||
|
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp);
|
||||||
|
if (byte & 0x40)
|
||||||
|
sp -= (((uint32_t)byte & 0x3f) << 2) + 4;
|
||||||
|
else
|
||||||
|
sp += ((uint32_t)byte << 2) + 4;
|
||||||
|
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp);
|
||||||
|
} else {
|
||||||
|
switch (byte & 0xf0) {
|
||||||
|
case 0x80: {
|
||||||
|
if (offset >= len)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
uint32_t registers =
|
||||||
|
(((uint32_t)byte & 0x0f) << 12) |
|
||||||
|
(((uint32_t)getByte(data, offset++)) << 4);
|
||||||
|
if (!registers)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
if (registers & (1 << 15))
|
||||||
|
wrotePC = true;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x90: {
|
||||||
|
uint8_t reg = byte & 0x0f;
|
||||||
|
if (reg == 13 || reg == 15)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
uint32_t sp;
|
||||||
|
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_R0 + reg,
|
||||||
|
_UVRSD_UINT32, &sp);
|
||||||
|
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
|
||||||
|
&sp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xa0: {
|
||||||
|
uint32_t registers = RegisterMask(4, byte & 0x07);
|
||||||
|
if (byte & 0x08)
|
||||||
|
registers |= 1 << 14;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xb0: {
|
||||||
|
switch (byte) {
|
||||||
|
case 0xb0:
|
||||||
|
finish = true;
|
||||||
|
break;
|
||||||
|
case 0xb1: {
|
||||||
|
if (offset >= len)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
uint8_t registers = getByte(data, offset++);
|
||||||
|
if (registers & 0xf0 || !registers)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xb2: {
|
||||||
|
uint32_t addend = 0;
|
||||||
|
uint32_t shift = 0;
|
||||||
|
// This decodes a uleb128 value.
|
||||||
|
while (true) {
|
||||||
|
if (offset >= len)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
uint32_t v = getByte(data, offset++);
|
||||||
|
addend |= (v & 0x7f) << shift;
|
||||||
|
if ((v & 0x80) == 0)
|
||||||
|
break;
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
uint32_t sp;
|
||||||
|
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
|
||||||
|
&sp);
|
||||||
|
sp += 0x204 + (addend << 2);
|
||||||
|
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
|
||||||
|
&sp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xb3: {
|
||||||
|
uint8_t v = getByte(data, offset++);
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_VFP,
|
||||||
|
RegisterRange(static_cast<uint8_t>(v >> 4),
|
||||||
|
v & 0x0f), _UVRSD_VFPX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xb4:
|
||||||
|
case 0xb5:
|
||||||
|
case 0xb6:
|
||||||
|
case 0xb7:
|
||||||
|
return _URC_FAILURE;
|
||||||
|
default:
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_VFP,
|
||||||
|
RegisterRange(8, byte & 0x07), _UVRSD_VFPX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xc0: {
|
||||||
|
switch (byte) {
|
||||||
|
#if defined(__ARM_WMMX)
|
||||||
|
case 0xc0:
|
||||||
|
case 0xc1:
|
||||||
|
case 0xc2:
|
||||||
|
case 0xc3:
|
||||||
|
case 0xc4:
|
||||||
|
case 0xc5:
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_WMMXD,
|
||||||
|
RegisterRange(10, byte & 0x7), _UVRSD_DOUBLE);
|
||||||
|
break;
|
||||||
|
case 0xc6: {
|
||||||
|
uint8_t v = getByte(data, offset++);
|
||||||
|
uint8_t start = static_cast<uint8_t>(v >> 4);
|
||||||
|
uint8_t count_minus_one = v & 0xf;
|
||||||
|
if (start + count_minus_one >= 16)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_WMMXD,
|
||||||
|
RegisterRange(start, count_minus_one),
|
||||||
|
_UVRSD_DOUBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xc7: {
|
||||||
|
uint8_t v = getByte(data, offset++);
|
||||||
|
if (!v || v & 0xf0)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_WMMXC, v, _UVRSD_DOUBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
case 0xc8:
|
||||||
|
case 0xc9: {
|
||||||
|
uint8_t v = getByte(data, offset++);
|
||||||
|
uint8_t start =
|
||||||
|
static_cast<uint8_t>(((byte == 0xc8) ? 16 : 0) + (v >> 4));
|
||||||
|
uint8_t count_minus_one = v & 0xf;
|
||||||
|
if (start + count_minus_one >= 32)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_VFP,
|
||||||
|
RegisterRange(start, count_minus_one),
|
||||||
|
_UVRSD_DOUBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return _URC_FAILURE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xd0: {
|
||||||
|
if (byte & 0x08)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
_Unwind_VRS_Pop(context, _UVRSC_VFP, RegisterRange(8, byte & 0x7),
|
||||||
|
_UVRSD_DOUBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return _URC_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!wrotePC) {
|
||||||
|
uint32_t lr;
|
||||||
|
_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr);
|
||||||
|
_Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr);
|
||||||
|
}
|
||||||
|
return _URC_CONTINUE_UNWIND;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
__aeabi_unwind_cpp_pr0(_Unwind_State state, _Unwind_Control_Block *ucbp,
|
||||||
|
_Unwind_Context *context) {
|
||||||
|
return unwindOneFrame(state, ucbp, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
__aeabi_unwind_cpp_pr1(_Unwind_State state, _Unwind_Control_Block *ucbp,
|
||||||
|
_Unwind_Context *context) {
|
||||||
|
return unwindOneFrame(state, ucbp, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
__aeabi_unwind_cpp_pr2(_Unwind_State state, _Unwind_Control_Block *ucbp,
|
||||||
|
_Unwind_Context *context) {
|
||||||
|
return unwindOneFrame(state, ucbp, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
|
||||||
|
// EHABI #7.3 discusses preserving the VRS in a "temporary VRS" during
|
||||||
|
// phase 1 and then restoring it to the "primary VRS" for phase 2. The
|
||||||
|
// effect is phase 2 doesn't see any of the VRS manipulations from phase 1.
|
||||||
|
// In this implementation, the phases don't share the VRS backing store.
|
||||||
|
// Instead, they are passed the original |uc| and they create a new VRS
|
||||||
|
// from scratch thus achieving the same effect.
|
||||||
|
__unw_init_local(cursor, uc);
|
||||||
|
|
||||||
|
// Walk each frame looking for a place to stop.
|
||||||
|
for (bool handlerNotFound = true; handlerNotFound;) {
|
||||||
|
|
||||||
|
// See if frame has code to run (has personality routine).
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): __unw_get_proc_info "
|
||||||
|
"failed => _URC_FATAL_PHASE1_ERROR",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
return _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tracing, print state information.
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionBuf[512];
|
||||||
|
const char *functionName = functionBuf;
|
||||||
|
unw_word_t offset;
|
||||||
|
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||||
|
&offset) != UNW_ESUCCESS) ||
|
||||||
|
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||||
|
functionName = ".anonymous.";
|
||||||
|
unw_word_t pc;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR ", func=%s, "
|
||||||
|
"lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR,
|
||||||
|
static_cast<void *>(exception_object), pc,
|
||||||
|
frameInfo.start_ip, functionName,
|
||||||
|
frameInfo.lsda, frameInfo.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a personality routine, ask it if it will want to stop at
|
||||||
|
// this frame.
|
||||||
|
if (frameInfo.handler != 0) {
|
||||||
|
__personality_routine p =
|
||||||
|
(__personality_routine)(long)(frameInfo.handler);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): calling personality function %p",
|
||||||
|
static_cast<void *>(exception_object),
|
||||||
|
reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p)));
|
||||||
|
struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor);
|
||||||
|
exception_object->pr_cache.fnstart = frameInfo.start_ip;
|
||||||
|
exception_object->pr_cache.ehtp =
|
||||||
|
(_Unwind_EHT_Header *)frameInfo.unwind_info;
|
||||||
|
exception_object->pr_cache.additional = frameInfo.flags;
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(_US_VIRTUAL_UNWIND_FRAME, exception_object, context);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): personality result %d start_ip %x ehtp %p "
|
||||||
|
"additional %x",
|
||||||
|
static_cast<void *>(exception_object), personalityResult,
|
||||||
|
exception_object->pr_cache.fnstart,
|
||||||
|
static_cast<void *>(exception_object->pr_cache.ehtp),
|
||||||
|
exception_object->pr_cache.additional);
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_HANDLER_FOUND:
|
||||||
|
// found a catch clause or locals that need destructing in this frame
|
||||||
|
// stop search and remember stack pointer at the frame
|
||||||
|
handlerNotFound = false;
|
||||||
|
// p should have initialized barrier_cache. EHABI #7.3.5
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
// continue unwinding
|
||||||
|
break;
|
||||||
|
|
||||||
|
// EHABI #7.3.3
|
||||||
|
case _URC_FAILURE:
|
||||||
|
return _URC_FAILURE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// something went wrong
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
return _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
|
||||||
|
_Unwind_Exception *exception_object,
|
||||||
|
bool resume) {
|
||||||
|
// See comment at the start of unwind_phase1 regarding VRS integrity.
|
||||||
|
__unw_init_local(cursor, uc);
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
int frame_count = 0;
|
||||||
|
|
||||||
|
// Walk each frame until we reach where search phase said to stop.
|
||||||
|
while (true) {
|
||||||
|
// Ask libunwind to get next frame (skip over first which is
|
||||||
|
// _Unwind_RaiseException or _Unwind_Resume).
|
||||||
|
//
|
||||||
|
// Resume only ever makes sense for 1 frame.
|
||||||
|
_Unwind_State state =
|
||||||
|
resume ? _US_UNWIND_FRAME_RESUME : _US_UNWIND_FRAME_STARTING;
|
||||||
|
if (resume && frame_count == 1) {
|
||||||
|
// On a resume, first unwind the _Unwind_Resume() frame. The next frame
|
||||||
|
// is now the landing pad for the cleanup from a previous execution of
|
||||||
|
// phase2. To continue unwindingly correctly, replace VRS[15] with the
|
||||||
|
// IP of the frame that the previous run of phase2 installed the context
|
||||||
|
// for. After this, continue unwinding as if normal.
|
||||||
|
//
|
||||||
|
// See #7.4.6 for details.
|
||||||
|
__unw_set_reg(cursor, UNW_REG_IP,
|
||||||
|
exception_object->unwinder_cache.reserved2);
|
||||||
|
resume = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get info about this frame.
|
||||||
|
unw_word_t sp;
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): __unw_get_proc_info "
|
||||||
|
"failed => _URC_FATAL_PHASE2_ERROR",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tracing, print state information.
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionBuf[512];
|
||||||
|
const char *functionName = functionBuf;
|
||||||
|
unw_word_t offset;
|
||||||
|
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||||
|
&offset) != UNW_ESUCCESS) ||
|
||||||
|
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||||
|
functionName = ".anonymous.";
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", "
|
||||||
|
"lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "",
|
||||||
|
static_cast<void *>(exception_object), frameInfo.start_ip,
|
||||||
|
functionName, sp, frameInfo.lsda,
|
||||||
|
frameInfo.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a personality routine, tell it we are unwinding.
|
||||||
|
if (frameInfo.handler != 0) {
|
||||||
|
__personality_routine p =
|
||||||
|
(__personality_routine)(long)(frameInfo.handler);
|
||||||
|
struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor);
|
||||||
|
// EHABI #7.2
|
||||||
|
exception_object->pr_cache.fnstart = frameInfo.start_ip;
|
||||||
|
exception_object->pr_cache.ehtp =
|
||||||
|
(_Unwind_EHT_Header *)frameInfo.unwind_info;
|
||||||
|
exception_object->pr_cache.additional = frameInfo.flags;
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(state, exception_object, context);
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
// Continue unwinding
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
// EHABI #7.2
|
||||||
|
if (sp == exception_object->barrier_cache.sp) {
|
||||||
|
// Phase 1 said we would stop at this frame, but we did not...
|
||||||
|
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||||
|
"stop here, but now in phase2 it did not stop here");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _URC_INSTALL_CONTEXT:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
// Personality routine says to transfer control to landing pad.
|
||||||
|
// We may get control back if landing pad calls _Unwind_Resume().
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
unw_word_t pc;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||||
|
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering "
|
||||||
|
"user code with ip=0x%" PRIxPTR ", sp=0x%" PRIxPTR,
|
||||||
|
static_cast<void *>(exception_object),
|
||||||
|
pc, sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume
|
||||||
|
// is called back, to find this same frame.
|
||||||
|
unw_word_t pc;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||||
|
exception_object->unwinder_cache.reserved2 = (uint32_t)pc;
|
||||||
|
}
|
||||||
|
__unw_resume(cursor);
|
||||||
|
// __unw_resume() only returns if there was an error.
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
|
||||||
|
// # EHABI #7.4.3
|
||||||
|
case _URC_FAILURE:
|
||||||
|
abort();
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Personality routine returned an unknown result code.
|
||||||
|
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||||
|
personalityResult);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up phase did not resume at the frame that the search phase
|
||||||
|
// said it would...
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by __cxa_throw. Only returns if there is a fatal error.
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
|
||||||
|
// This field for is for compatibility with GCC to say this isn't a forced
|
||||||
|
// unwind. EHABI #7.2
|
||||||
|
exception_object->unwinder_cache.reserved1 = 0;
|
||||||
|
|
||||||
|
// phase 1: the search phase
|
||||||
|
_Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object);
|
||||||
|
if (phase1 != _URC_NO_REASON)
|
||||||
|
return phase1;
|
||||||
|
|
||||||
|
// phase 2: the clean up phase
|
||||||
|
return unwind_phase2(&uc, &cursor, exception_object, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void _Unwind_Complete(_Unwind_Exception* exception_object) {
|
||||||
|
// This is to be called when exception handling completes to give us a chance
|
||||||
|
// to perform any housekeeping. EHABI #7.2. But we have nothing to do here.
|
||||||
|
(void)exception_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||||
|
/// to the personality function at each frame. The personality
|
||||||
|
/// may force a jump to a landing pad in that function, the landing
|
||||||
|
/// pad code may then call _Unwind_Resume() to continue with the
|
||||||
|
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||||
|
/// geneated user code. All other _Unwind_* routines are called
|
||||||
|
/// by the C++ runtime __cxa_* routines.
|
||||||
|
///
|
||||||
|
/// Note: re-throwing an exception (as opposed to continuing the unwind)
|
||||||
|
/// is implemented by having the code call __cxa_rethrow() which
|
||||||
|
/// in turn calls _Unwind_Resume_or_Rethrow().
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_Resume(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
|
||||||
|
// _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
|
||||||
|
// which is in the same position as private_1 below.
|
||||||
|
// TODO(ajwong): Who wronte the above? Why is it true?
|
||||||
|
unwind_phase2(&uc, &cursor, exception_object, true);
|
||||||
|
|
||||||
|
// Clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_Resume() can't return");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
uintptr_t result = 0;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||||
|
result = (uintptr_t)frameInfo.lsda;
|
||||||
|
_LIBUNWIND_TRACE_API(
|
||||||
|
"_Unwind_GetLanguageSpecificData(context=%p) => 0x%llx",
|
||||||
|
static_cast<void *>(context), (long long)result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation,
|
||||||
|
void* valuep) {
|
||||||
|
uint64_t value = 0;
|
||||||
|
switch (representation) {
|
||||||
|
case _UVRSD_UINT32:
|
||||||
|
case _UVRSD_FLOAT:
|
||||||
|
memcpy(&value, valuep, sizeof(uint32_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case _UVRSD_VFPX:
|
||||||
|
case _UVRSD_UINT64:
|
||||||
|
case _UVRSD_DOUBLE:
|
||||||
|
memcpy(&value, valuep, sizeof(uint64_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||||
|
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||||
|
void *valuep) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_VRS_Set(context=%p, regclass=%d, reg=%d, "
|
||||||
|
"rep=%d, value=0x%llX)",
|
||||||
|
static_cast<void *>(context), regclass, regno,
|
||||||
|
representation,
|
||||||
|
ValueAsBitPattern(representation, valuep));
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
switch (regclass) {
|
||||||
|
case _UVRSC_CORE:
|
||||||
|
if (representation != _UVRSD_UINT32 || regno > 15)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno),
|
||||||
|
*(unw_word_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
case _UVRSC_VFP:
|
||||||
|
if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
if (representation == _UVRSD_VFPX) {
|
||||||
|
// Can only touch d0-15 with FSTMFDX.
|
||||||
|
if (regno > 15)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
__unw_save_vfp_as_X(cursor);
|
||||||
|
} else {
|
||||||
|
if (regno > 31)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
}
|
||||||
|
return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno),
|
||||||
|
*(unw_fpreg_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
#if defined(__ARM_WMMX)
|
||||||
|
case _UVRSC_WMMXC:
|
||||||
|
if (representation != _UVRSD_UINT32 || regno > 3)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno),
|
||||||
|
*(unw_word_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
case _UVRSC_WMMXD:
|
||||||
|
if (representation != _UVRSD_DOUBLE || regno > 31)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno),
|
||||||
|
*(unw_fpreg_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
#else
|
||||||
|
case _UVRSC_WMMXC:
|
||||||
|
case _UVRSC_WMMXD:
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("unsupported register class");
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Get_Internal(_Unwind_Context *context,
|
||||||
|
_Unwind_VRS_RegClass regclass, uint32_t regno,
|
||||||
|
_Unwind_VRS_DataRepresentation representation,
|
||||||
|
void *valuep) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
switch (regclass) {
|
||||||
|
case _UVRSC_CORE:
|
||||||
|
if (representation != _UVRSD_UINT32 || regno > 15)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno),
|
||||||
|
(unw_word_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
case _UVRSC_VFP:
|
||||||
|
if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
if (representation == _UVRSD_VFPX) {
|
||||||
|
// Can only touch d0-15 with FSTMFDX.
|
||||||
|
if (regno > 15)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
__unw_save_vfp_as_X(cursor);
|
||||||
|
} else {
|
||||||
|
if (regno > 31)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
}
|
||||||
|
return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno),
|
||||||
|
(unw_fpreg_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
#if defined(__ARM_WMMX)
|
||||||
|
case _UVRSC_WMMXC:
|
||||||
|
if (representation != _UVRSD_UINT32 || regno > 3)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno),
|
||||||
|
(unw_word_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
case _UVRSC_WMMXD:
|
||||||
|
if (representation != _UVRSD_DOUBLE || regno > 31)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno),
|
||||||
|
(unw_fpreg_t *)valuep) == UNW_ESUCCESS
|
||||||
|
? _UVRSR_OK
|
||||||
|
: _UVRSR_FAILED;
|
||||||
|
#else
|
||||||
|
case _UVRSC_WMMXC:
|
||||||
|
case _UVRSC_WMMXD:
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("unsupported register class");
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||||
|
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||||
|
void *valuep) {
|
||||||
|
_Unwind_VRS_Result result =
|
||||||
|
_Unwind_VRS_Get_Internal(context, regclass, regno, representation,
|
||||||
|
valuep);
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_VRS_Get(context=%p, regclass=%d, reg=%d, "
|
||||||
|
"rep=%d, value=0x%llX, result = %d)",
|
||||||
|
static_cast<void *>(context), regclass, regno,
|
||||||
|
representation,
|
||||||
|
ValueAsBitPattern(representation, valuep), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Unwind_VRS_Result
|
||||||
|
_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||||
|
uint32_t discriminator,
|
||||||
|
_Unwind_VRS_DataRepresentation representation) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_VRS_Pop(context=%p, regclass=%d, "
|
||||||
|
"discriminator=%d, representation=%d)",
|
||||||
|
static_cast<void *>(context), regclass, discriminator,
|
||||||
|
representation);
|
||||||
|
switch (regclass) {
|
||||||
|
case _UVRSC_WMMXC:
|
||||||
|
#if !defined(__ARM_WMMX)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case _UVRSC_CORE: {
|
||||||
|
if (representation != _UVRSD_UINT32)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
// When popping SP from the stack, we don't want to override it from the
|
||||||
|
// computed new stack location. See EHABI #7.5.4 table 3.
|
||||||
|
bool poppedSP = false;
|
||||||
|
uint32_t* sp;
|
||||||
|
if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP,
|
||||||
|
_UVRSD_UINT32, &sp) != _UVRSR_OK) {
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < 16; ++i) {
|
||||||
|
if (!(discriminator & static_cast<uint32_t>(1 << i)))
|
||||||
|
continue;
|
||||||
|
uint32_t value = *sp++;
|
||||||
|
if (regclass == _UVRSC_CORE && i == 13)
|
||||||
|
poppedSP = true;
|
||||||
|
if (_Unwind_VRS_Set(context, regclass, i,
|
||||||
|
_UVRSD_UINT32, &value) != _UVRSR_OK) {
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!poppedSP) {
|
||||||
|
return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP,
|
||||||
|
_UVRSD_UINT32, &sp);
|
||||||
|
}
|
||||||
|
return _UVRSR_OK;
|
||||||
|
}
|
||||||
|
case _UVRSC_WMMXD:
|
||||||
|
#if !defined(__ARM_WMMX)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case _UVRSC_VFP: {
|
||||||
|
if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
uint32_t first = discriminator >> 16;
|
||||||
|
uint32_t count = discriminator & 0xffff;
|
||||||
|
uint32_t end = first+count;
|
||||||
|
uint32_t* sp;
|
||||||
|
if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP,
|
||||||
|
_UVRSD_UINT32, &sp) != _UVRSR_OK) {
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
}
|
||||||
|
// For _UVRSD_VFPX, we're assuming the data is stored in FSTMX "standard
|
||||||
|
// format 1", which is equivalent to FSTMD + a padding word.
|
||||||
|
for (uint32_t i = first; i < end; ++i) {
|
||||||
|
// SP is only 32-bit aligned so don't copy 64-bit at a time.
|
||||||
|
uint64_t w0 = *sp++;
|
||||||
|
uint64_t w1 = *sp++;
|
||||||
|
#ifdef __LITTLE_ENDIAN__
|
||||||
|
uint64_t value = (w1 << 32) | w0;
|
||||||
|
#else
|
||||||
|
uint64_t value = (w0 << 32) | w1;
|
||||||
|
#endif
|
||||||
|
if (_Unwind_VRS_Set(context, regclass, i, representation, &value) !=
|
||||||
|
_UVRSR_OK)
|
||||||
|
return _UVRSR_FAILED;
|
||||||
|
}
|
||||||
|
if (representation == _UVRSD_VFPX)
|
||||||
|
++sp;
|
||||||
|
return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32,
|
||||||
|
&sp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_LIBUNWIND_ABORT("unsupported register class");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to find the start of the
|
||||||
|
/// function.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
uintptr_t result = 0;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||||
|
result = (uintptr_t)frameInfo.start_ip;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%llX",
|
||||||
|
static_cast<void *>(context), (long long)result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 if a foreign exception
|
||||||
|
// is caught.
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
|
||||||
|
static_cast<void *>(exception_object));
|
||||||
|
if (exception_object->exception_cleanup != NULL)
|
||||||
|
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||||
|
exception_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
__gnu_unwind_frame(_Unwind_Exception *exception_object,
|
||||||
|
struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
if (__unw_step(cursor) != UNW_STEP_SUCCESS)
|
||||||
|
return _URC_FAILURE;
|
||||||
|
return _URC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
|
@ -0,0 +1,50 @@
|
||||||
|
//===------------------------- Unwind-EHABI.hpp ---------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __UNWIND_EHABI_H__
|
||||||
|
#define __UNWIND_EHABI_H__
|
||||||
|
|
||||||
|
#include <__libunwind_config.h>
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unwind.h>
|
||||||
|
|
||||||
|
// Unable to unwind in the ARM index table (section 5 EHABI).
|
||||||
|
#define UNW_EXIDX_CANTUNWIND 0x1
|
||||||
|
|
||||||
|
static inline uint32_t signExtendPrel31(uint32_t data) {
|
||||||
|
return data | ((data & 0x40000000u) << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t readPrel31(const uint32_t *data) {
|
||||||
|
return (((uint32_t)(uintptr_t)data) + signExtendPrel31(*data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr0(
|
||||||
|
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||||
|
|
||||||
|
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(
|
||||||
|
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||||
|
|
||||||
|
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr2(
|
||||||
|
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
#endif // __UNWIND_EHABI_H__
|
|
@ -0,0 +1,501 @@
|
||||||
|
//===--------------------------- Unwind-seh.cpp ---------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Implements SEH-based Itanium C++ exceptions.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
|
||||||
|
|
||||||
|
#include <unwind.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <windef.h>
|
||||||
|
#include <excpt.h>
|
||||||
|
#include <winnt.h>
|
||||||
|
#include <ntstatus.h>
|
||||||
|
|
||||||
|
#include "libunwind_ext.h"
|
||||||
|
#include "UnwindCursor.hpp"
|
||||||
|
|
||||||
|
using namespace libunwind;
|
||||||
|
|
||||||
|
#define STATUS_USER_DEFINED (1u << 29)
|
||||||
|
|
||||||
|
#define STATUS_GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C')
|
||||||
|
|
||||||
|
#define MAKE_CUSTOM_STATUS(s, c) \
|
||||||
|
((NTSTATUS)(((s) << 30) | STATUS_USER_DEFINED | (c)))
|
||||||
|
#define MAKE_GCC_EXCEPTION(c) \
|
||||||
|
MAKE_CUSTOM_STATUS(STATUS_SEVERITY_SUCCESS, STATUS_GCC_MAGIC | ((c) << 24))
|
||||||
|
|
||||||
|
/// SEH exception raised by libunwind when the program calls
|
||||||
|
/// \c _Unwind_RaiseException.
|
||||||
|
#define STATUS_GCC_THROW MAKE_GCC_EXCEPTION(0) // 0x20474343
|
||||||
|
/// SEH exception raised by libunwind to initiate phase 2 of exception
|
||||||
|
/// handling.
|
||||||
|
#define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) // 0x21474343
|
||||||
|
|
||||||
|
/// Class of foreign exceptions based on unrecognized SEH exceptions.
|
||||||
|
static const uint64_t kSEHExceptionClass = 0x434C4E4753454800; // CLNGSEH\0
|
||||||
|
|
||||||
|
/// Exception cleanup routine used by \c _GCC_specific_handler to
|
||||||
|
/// free foreign exceptions.
|
||||||
|
static void seh_exc_cleanup(_Unwind_Reason_Code urc, _Unwind_Exception *exc) {
|
||||||
|
(void)urc;
|
||||||
|
if (exc->exception_class != kSEHExceptionClass)
|
||||||
|
_LIBUNWIND_ABORT("SEH cleanup called on non-SEH exception");
|
||||||
|
free(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx);
|
||||||
|
static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor);
|
||||||
|
static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor,
|
||||||
|
DISPATCHER_CONTEXT *disp);
|
||||||
|
|
||||||
|
/// Common implementation of SEH-style handler functions used by Itanium-
|
||||||
|
/// style frames. Depending on how and why it was called, it may do one of:
|
||||||
|
/// a) Delegate to the given Itanium-style personality function; or
|
||||||
|
/// b) Initiate a collided unwind to halt unwinding.
|
||||||
|
_LIBUNWIND_EXPORT EXCEPTION_DISPOSITION
|
||||||
|
_GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx,
|
||||||
|
DISPATCHER_CONTEXT *disp, __personality_routine pers) {
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
_Unwind_Exception *exc;
|
||||||
|
_Unwind_Action action;
|
||||||
|
struct _Unwind_Context *ctx = nullptr;
|
||||||
|
_Unwind_Reason_Code urc;
|
||||||
|
uintptr_t retval, target;
|
||||||
|
bool ours = false;
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)",
|
||||||
|
ms_exc->ExceptionCode, ms_exc->ExceptionFlags,
|
||||||
|
(void *)frame);
|
||||||
|
if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) {
|
||||||
|
if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) {
|
||||||
|
// Set up the upper return value (the lower one and the target PC
|
||||||
|
// were set in the call to RtlUnwindEx()) for the landing pad.
|
||||||
|
#ifdef __x86_64__
|
||||||
|
disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
|
||||||
|
#elif defined(__arm__)
|
||||||
|
disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3];
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// This is the collided unwind to the landing pad. Nothing to do.
|
||||||
|
return ExceptionContinueSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms_exc->ExceptionCode == STATUS_GCC_THROW) {
|
||||||
|
// This is (probably) a libunwind-controlled exception/unwind. Recover the
|
||||||
|
// parameters which we set below, and pass them to the personality function.
|
||||||
|
ours = true;
|
||||||
|
exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0];
|
||||||
|
if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) {
|
||||||
|
ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1];
|
||||||
|
action = (_Unwind_Action)ms_exc->ExceptionInformation[2];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Foreign exception.
|
||||||
|
exc = (_Unwind_Exception *)malloc(sizeof(_Unwind_Exception));
|
||||||
|
exc->exception_class = kSEHExceptionClass;
|
||||||
|
exc->exception_cleanup = seh_exc_cleanup;
|
||||||
|
memset(exc->private_, 0, sizeof(exc->private_));
|
||||||
|
}
|
||||||
|
if (!ctx) {
|
||||||
|
__unw_init_seh(&cursor, disp->ContextRecord);
|
||||||
|
__unw_seh_set_disp_ctx(&cursor, disp);
|
||||||
|
__unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1);
|
||||||
|
ctx = (struct _Unwind_Context *)&cursor;
|
||||||
|
|
||||||
|
if (!IS_UNWINDING(ms_exc->ExceptionFlags)) {
|
||||||
|
if (ours && ms_exc->NumberParameters > 1)
|
||||||
|
action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND);
|
||||||
|
else
|
||||||
|
action = _UA_SEARCH_PHASE;
|
||||||
|
} else {
|
||||||
|
if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame)
|
||||||
|
action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME);
|
||||||
|
else
|
||||||
|
action = _UA_CLEANUP_PHASE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality "
|
||||||
|
"function %p(1, %d, %llx, %p, %p)",
|
||||||
|
(void *)pers, action, exc->exception_class,
|
||||||
|
(void *)exc, (void *)ctx);
|
||||||
|
urc = pers(1, action, exc->exception_class, exc, ctx);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d", urc);
|
||||||
|
switch (urc) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
// If we're in phase 2, and the personality routine said to continue
|
||||||
|
// at the target frame, we're in real trouble.
|
||||||
|
if (action & _UA_HANDLER_FRAME)
|
||||||
|
_LIBUNWIND_ABORT("Personality continued unwind at the target frame!");
|
||||||
|
return ExceptionContinueSearch;
|
||||||
|
case _URC_HANDLER_FOUND:
|
||||||
|
// If we were called by __libunwind_seh_personality(), indicate that
|
||||||
|
// a handler was found; otherwise, initiate phase 2 by unwinding.
|
||||||
|
if (ours && ms_exc->NumberParameters > 1)
|
||||||
|
return 4 /* ExecptionExecuteHandler in mingw */;
|
||||||
|
// This should never happen in phase 2.
|
||||||
|
if (IS_UNWINDING(ms_exc->ExceptionFlags))
|
||||||
|
_LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!");
|
||||||
|
exc->private_[1] = (ULONG_PTR)frame;
|
||||||
|
if (ours) {
|
||||||
|
ms_exc->NumberParameters = 4;
|
||||||
|
ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame;
|
||||||
|
}
|
||||||
|
// FIXME: Indicate target frame in foreign case!
|
||||||
|
// phase 2: the clean up phase
|
||||||
|
RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable);
|
||||||
|
_LIBUNWIND_ABORT("RtlUnwindEx() failed");
|
||||||
|
case _URC_INSTALL_CONTEXT: {
|
||||||
|
// If we were called by __libunwind_seh_personality(), indicate that
|
||||||
|
// a handler was found; otherwise, it's time to initiate a collided
|
||||||
|
// unwind to the target.
|
||||||
|
if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1)
|
||||||
|
return 4 /* ExecptionExecuteHandler in mingw */;
|
||||||
|
// This should never happen in phase 1.
|
||||||
|
if (!IS_UNWINDING(ms_exc->ExceptionFlags))
|
||||||
|
_LIBUNWIND_ABORT("Personality installed context during phase 1!");
|
||||||
|
#ifdef __x86_64__
|
||||||
|
exc->private_[2] = disp->TargetIp;
|
||||||
|
__unw_get_reg(&cursor, UNW_X86_64_RAX, &retval);
|
||||||
|
__unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]);
|
||||||
|
#elif defined(__arm__)
|
||||||
|
exc->private_[2] = disp->TargetPc;
|
||||||
|
__unw_get_reg(&cursor, UNW_ARM_R0, &retval);
|
||||||
|
__unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]);
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
exc->private_[2] = disp->TargetPc;
|
||||||
|
__unw_get_reg(&cursor, UNW_ARM64_X0, &retval);
|
||||||
|
__unw_get_reg(&cursor, UNW_ARM64_X1, &exc->private_[3]);
|
||||||
|
#endif
|
||||||
|
__unw_get_reg(&cursor, UNW_REG_IP, &target);
|
||||||
|
ms_exc->ExceptionCode = STATUS_GCC_UNWIND;
|
||||||
|
#ifdef __x86_64__
|
||||||
|
ms_exc->ExceptionInformation[2] = disp->TargetIp;
|
||||||
|
#elif defined(__arm__) || defined(__aarch64__)
|
||||||
|
ms_exc->ExceptionInformation[2] = disp->TargetPc;
|
||||||
|
#endif
|
||||||
|
ms_exc->ExceptionInformation[3] = exc->private_[3];
|
||||||
|
// Give NTRTL some scratch space to keep track of the collided unwind.
|
||||||
|
// Don't use the one that was passed in; we don't want to overwrite the
|
||||||
|
// context in the DISPATCHER_CONTEXT.
|
||||||
|
CONTEXT new_ctx;
|
||||||
|
RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable);
|
||||||
|
_LIBUNWIND_ABORT("RtlUnwindEx() failed");
|
||||||
|
}
|
||||||
|
// Anything else indicates a serious problem.
|
||||||
|
default: return ExceptionContinueExecution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Personality function returned by \c __unw_get_proc_info() in SEH contexts.
|
||||||
|
/// This is a wrapper that calls the real SEH handler function, which in
|
||||||
|
/// turn (at least, for Itanium-style frames) calls the real Itanium
|
||||||
|
/// personality function (see \c _GCC_specific_handler()).
|
||||||
|
extern "C" _Unwind_Reason_Code
|
||||||
|
__libunwind_seh_personality(int version, _Unwind_Action state,
|
||||||
|
uint64_t klass, _Unwind_Exception *exc,
|
||||||
|
struct _Unwind_Context *context) {
|
||||||
|
(void)version;
|
||||||
|
(void)klass;
|
||||||
|
EXCEPTION_RECORD ms_exc;
|
||||||
|
bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE;
|
||||||
|
ms_exc.ExceptionCode = STATUS_GCC_THROW;
|
||||||
|
ms_exc.ExceptionFlags = 0;
|
||||||
|
ms_exc.NumberParameters = 3;
|
||||||
|
ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc;
|
||||||
|
ms_exc.ExceptionInformation[1] = (ULONG_PTR)context;
|
||||||
|
ms_exc.ExceptionInformation[2] = state;
|
||||||
|
DISPATCHER_CONTEXT *disp_ctx =
|
||||||
|
__unw_seh_get_disp_ctx((unw_cursor_t *)context);
|
||||||
|
EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc,
|
||||||
|
(PVOID)disp_ctx->EstablisherFrame,
|
||||||
|
disp_ctx->ContextRecord,
|
||||||
|
disp_ctx);
|
||||||
|
switch (ms_act) {
|
||||||
|
case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND;
|
||||||
|
case 4 /*ExceptionExecuteHandler*/:
|
||||||
|
return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND;
|
||||||
|
default:
|
||||||
|
return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase2_forced(unw_context_t *uc,
|
||||||
|
_Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||||
|
unw_cursor_t cursor2;
|
||||||
|
__unw_init_local(&cursor2, uc);
|
||||||
|
|
||||||
|
// Walk each frame until we reach where search phase said to stop
|
||||||
|
while (__unw_step(&cursor2) > 0) {
|
||||||
|
|
||||||
|
// Update info about this frame.
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step "
|
||||||
|
"failed => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tracing, print state information.
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionBuf[512];
|
||||||
|
const char *functionName = functionBuf;
|
||||||
|
unw_word_t offset;
|
||||||
|
if ((__unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf),
|
||||||
|
&offset) != UNW_ESUCCESS) ||
|
||||||
|
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||||
|
functionName = ".anonymous.";
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64
|
||||||
|
", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64,
|
||||||
|
(void *)exception_object, frameInfo.start_ip, functionName,
|
||||||
|
frameInfo.lsda, frameInfo.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call stop function at each frame.
|
||||||
|
_Unwind_Action action =
|
||||||
|
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||||
|
_Unwind_Reason_Code stopResult =
|
||||||
|
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(&cursor2), stop_parameter);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): stop function returned %d",
|
||||||
|
(void *)exception_object, stopResult);
|
||||||
|
if (stopResult != _URC_NO_REASON) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): stopped by stop function",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a personality routine, tell it we are unwinding.
|
||||||
|
if (frameInfo.handler != 0) {
|
||||||
|
__personality_routine p =
|
||||||
|
(__personality_routine)(intptr_t)(frameInfo.handler);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): calling personality function %p",
|
||||||
|
(void *)exception_object, (void *)(uintptr_t)p);
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(&cursor2));
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned "
|
||||||
|
"_URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
// Destructors called, continue unwinding
|
||||||
|
break;
|
||||||
|
case _URC_INSTALL_CONTEXT:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned "
|
||||||
|
"_URC_INSTALL_CONTEXT",
|
||||||
|
(void *)exception_object);
|
||||||
|
// We may get control back if landing pad calls _Unwind_Resume().
|
||||||
|
__unw_resume(&cursor2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Personality routine returned an unknown result code.
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned %d, "
|
||||||
|
"_URC_FATAL_PHASE2_ERROR",
|
||||||
|
(void *)exception_object, personalityResult);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call stop function one last time and tell it we've reached the end
|
||||||
|
// of the stack.
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
|
||||||
|
"function with _UA_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
_Unwind_Action lastAction =
|
||||||
|
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||||
|
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(&cursor2), stop_parameter);
|
||||||
|
|
||||||
|
// Clean up phase did not resume at the frame that the search phase said it
|
||||||
|
// would.
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by \c __cxa_throw(). Only returns if there is a fatal error.
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
|
||||||
|
// Mark that this is a non-forced unwind, so _Unwind_Resume()
|
||||||
|
// can do the right thing.
|
||||||
|
memset(exception_object->private_, 0, sizeof(exception_object->private_));
|
||||||
|
|
||||||
|
// phase 1: the search phase
|
||||||
|
// We'll let the system do that for us.
|
||||||
|
RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object);
|
||||||
|
|
||||||
|
// If we get here, either something went horribly wrong or we reached the
|
||||||
|
// top of the stack. Either way, let libc++abi call std::terminate().
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When \c _Unwind_RaiseException() is in phase2, it hands control
|
||||||
|
/// to the personality function at each frame. The personality
|
||||||
|
/// may force a jump to a landing pad in that function; the landing
|
||||||
|
/// pad code may then call \c _Unwind_Resume() to continue with the
|
||||||
|
/// unwinding. Note: the call to \c _Unwind_Resume() is from compiler
|
||||||
|
/// geneated user code. All other \c _Unwind_* routines are called
|
||||||
|
/// by the C++ runtime \c __cxa_* routines.
|
||||||
|
///
|
||||||
|
/// Note: re-throwing an exception (as opposed to continuing the unwind)
|
||||||
|
/// is implemented by having the code call \c __cxa_rethrow() which
|
||||||
|
/// in turn calls \c _Unwind_Resume_or_Rethrow().
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_Resume(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object);
|
||||||
|
|
||||||
|
if (exception_object->private_[0] != 0) {
|
||||||
|
unw_context_t uc;
|
||||||
|
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
unwind_phase2_forced(&uc, exception_object,
|
||||||
|
(_Unwind_Stop_Fn) exception_object->private_[0],
|
||||||
|
(void *)exception_object->private_[4]);
|
||||||
|
} else {
|
||||||
|
// Recover the parameters for the unwind from the exception object
|
||||||
|
// so we can start unwinding again.
|
||||||
|
EXCEPTION_RECORD ms_exc;
|
||||||
|
CONTEXT ms_ctx;
|
||||||
|
UNWIND_HISTORY_TABLE hist;
|
||||||
|
|
||||||
|
memset(&ms_exc, 0, sizeof(ms_exc));
|
||||||
|
memset(&hist, 0, sizeof(hist));
|
||||||
|
ms_exc.ExceptionCode = STATUS_GCC_THROW;
|
||||||
|
ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
||||||
|
ms_exc.NumberParameters = 4;
|
||||||
|
ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object;
|
||||||
|
ms_exc.ExceptionInformation[1] = exception_object->private_[1];
|
||||||
|
ms_exc.ExceptionInformation[2] = exception_object->private_[2];
|
||||||
|
ms_exc.ExceptionInformation[3] = exception_object->private_[3];
|
||||||
|
RtlUnwindEx((PVOID)exception_object->private_[1],
|
||||||
|
(PVOID)exception_object->private_[2], &ms_exc,
|
||||||
|
exception_object, &ms_ctx, &hist);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_Resume() can't return");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Not used by C++.
|
||||||
|
/// Unwinds stack, calling "stop" function at each frame.
|
||||||
|
/// Could be used to implement \c longjmp().
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",
|
||||||
|
(void *)exception_object, (void *)(uintptr_t)stop);
|
||||||
|
unw_context_t uc;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
|
||||||
|
// Mark that this is a forced unwind, so _Unwind_Resume() can do
|
||||||
|
// the right thing.
|
||||||
|
exception_object->private_[0] = (uintptr_t) stop;
|
||||||
|
exception_object->private_[4] = (uintptr_t) stop_parameter;
|
||||||
|
|
||||||
|
// do it
|
||||||
|
return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||||
|
uintptr_t result =
|
||||||
|
(uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData;
|
||||||
|
_LIBUNWIND_TRACE_API(
|
||||||
|
"_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to find the start of the
|
||||||
|
/// function.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||||
|
DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context);
|
||||||
|
uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) {
|
||||||
|
#ifdef _LIBUNWIND_TARGET_X86_64
|
||||||
|
new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor))
|
||||||
|
UnwindCursor<LocalAddressSpace, Registers_x86_64>(
|
||||||
|
context, LocalAddressSpace::sThisAddressSpace);
|
||||||
|
auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
|
||||||
|
co->setInfoBasedOnIPRegister();
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
#elif defined(_LIBUNWIND_TARGET_ARM)
|
||||||
|
new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor))
|
||||||
|
UnwindCursor<LocalAddressSpace, Registers_arm>(
|
||||||
|
context, LocalAddressSpace::sThisAddressSpace);
|
||||||
|
auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
|
||||||
|
co->setInfoBasedOnIPRegister();
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
#elif defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor))
|
||||||
|
UnwindCursor<LocalAddressSpace, Registers_arm64>(
|
||||||
|
context, LocalAddressSpace::sThisAddressSpace);
|
||||||
|
auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
|
||||||
|
co->setInfoBasedOnIPRegister();
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
#else
|
||||||
|
return UNW_EINVAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) {
|
||||||
|
#ifdef _LIBUNWIND_TARGET_X86_64
|
||||||
|
return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->getDispatcherContext();
|
||||||
|
#elif defined(_LIBUNWIND_TARGET_ARM)
|
||||||
|
return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->getDispatcherContext();
|
||||||
|
#elif defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->getDispatcherContext();
|
||||||
|
#else
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor,
|
||||||
|
DISPATCHER_CONTEXT *disp) {
|
||||||
|
#ifdef _LIBUNWIND_TARGET_X86_64
|
||||||
|
reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->setDispatcherContext(disp);
|
||||||
|
#elif defined(_LIBUNWIND_TARGET_ARM)
|
||||||
|
reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->setDispatcherContext(disp);
|
||||||
|
#elif defined(_LIBUNWIND_TARGET_AARCH64)
|
||||||
|
reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->setDispatcherContext(disp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
|
|
@ -0,0 +1,516 @@
|
||||||
|
//===--------------------------- Unwind-sjlj.c ----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Implements setjump-longjump based C++ exceptions
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include <unwind.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/// With SJLJ based exceptions, any function that has a catch clause or needs to
|
||||||
|
/// do any clean up when an exception propagates through it, needs to call
|
||||||
|
/// \c _Unwind_SjLj_Register at the start of the function and
|
||||||
|
/// \c _Unwind_SjLj_Unregister at the end. The register function is called with
|
||||||
|
/// the address of a block of memory in the function's stack frame. The runtime
|
||||||
|
/// keeps a linked list (stack) of these blocks - one per thread. The calling
|
||||||
|
/// function also sets the personality and lsda fields of the block.
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
||||||
|
|
||||||
|
struct _Unwind_FunctionContext {
|
||||||
|
// next function in stack of handlers
|
||||||
|
struct _Unwind_FunctionContext *prev;
|
||||||
|
|
||||||
|
// set by calling function before registering to be the landing pad
|
||||||
|
uint32_t resumeLocation;
|
||||||
|
|
||||||
|
// set by personality handler to be parameters passed to landing pad function
|
||||||
|
uint32_t resumeParameters[4];
|
||||||
|
|
||||||
|
// set by calling function before registering
|
||||||
|
__personality_routine personality; // arm offset=24
|
||||||
|
uintptr_t lsda; // arm offset=28
|
||||||
|
|
||||||
|
// variable length array, contains registers to restore
|
||||||
|
// 0 = r7, 1 = pc, 2 = sp
|
||||||
|
void *jbuf[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_HAS_NO_THREADS)
|
||||||
|
# define _LIBUNWIND_THREAD_LOCAL
|
||||||
|
#else
|
||||||
|
# if __STDC_VERSION__ >= 201112L
|
||||||
|
# define _LIBUNWIND_THREAD_LOCAL _Thread_local
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define _LIBUNWIND_THREAD_LOCAL __declspec(thread)
|
||||||
|
# elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
# define _LIBUNWIND_THREAD_LOCAL __thread
|
||||||
|
# else
|
||||||
|
# error Unable to create thread local storage
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(FOR_DYLD)
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <System/pthread_machdep.h>
|
||||||
|
#else
|
||||||
|
static _LIBUNWIND_THREAD_LOCAL struct _Unwind_FunctionContext *stack = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() {
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
return _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key);
|
||||||
|
#else
|
||||||
|
return stack;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc) {
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
_pthread_setspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key, fc);
|
||||||
|
#else
|
||||||
|
stack = fc;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// Called at start of each function that catches exceptions
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_SjLj_Register(struct _Unwind_FunctionContext *fc) {
|
||||||
|
fc->prev = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||||
|
__Unwind_SjLj_SetTopOfFunctionStack(fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called at end of each function that catches exceptions
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_SjLj_Unregister(struct _Unwind_FunctionContext *fc) {
|
||||||
|
__Unwind_SjLj_SetTopOfFunctionStack(fc->prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase1(struct _Unwind_Exception *exception_object) {
|
||||||
|
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1: initial function-context=%p",
|
||||||
|
(void *)c);
|
||||||
|
|
||||||
|
// walk each frame looking for a place to stop
|
||||||
|
for (bool handlerNotFound = true; handlerNotFound; c = c->prev) {
|
||||||
|
|
||||||
|
// check for no more frames
|
||||||
|
if (c == NULL) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): reached "
|
||||||
|
"bottom => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1: function-context=%p", (void *)c);
|
||||||
|
// if there is a personality routine, ask it if it will want to stop at this
|
||||||
|
// frame
|
||||||
|
if (c->personality != NULL) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): calling "
|
||||||
|
"personality function %p",
|
||||||
|
(void *)exception_object,
|
||||||
|
(void *)c->personality);
|
||||||
|
_Unwind_Reason_Code personalityResult = (*c->personality)(
|
||||||
|
1, _UA_SEARCH_PHASE, exception_object->exception_class,
|
||||||
|
exception_object, (struct _Unwind_Context *)c);
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_HANDLER_FOUND:
|
||||||
|
// found a catch clause or locals that need destructing in this frame
|
||||||
|
// stop search and remember function context
|
||||||
|
handlerNotFound = false;
|
||||||
|
exception_object->private_2 = (uintptr_t) c;
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): "
|
||||||
|
"_URC_HANDLER_FOUND",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): "
|
||||||
|
"_URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
// continue unwinding
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// something went wrong
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase2(struct _Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
|
||||||
|
// walk each frame until we reach where search phase said to stop
|
||||||
|
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||||
|
while (true) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2s(ex_ojb=%p): context=%p",
|
||||||
|
(void *)exception_object, (void *)c);
|
||||||
|
|
||||||
|
// check for no more frames
|
||||||
|
if (c == NULL) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): __unw_step() reached "
|
||||||
|
"bottom => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a personality routine, tell it we are unwinding
|
||||||
|
if (c->personality != NULL) {
|
||||||
|
_Unwind_Action action = _UA_CLEANUP_PHASE;
|
||||||
|
if ((uintptr_t) c == exception_object->private_2)
|
||||||
|
action = (_Unwind_Action)(
|
||||||
|
_UA_CLEANUP_PHASE |
|
||||||
|
_UA_HANDLER_FRAME); // tell personality this was the frame it marked
|
||||||
|
// in phase 1
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*c->personality)(1, action, exception_object->exception_class,
|
||||||
|
exception_object, (struct _Unwind_Context *)c);
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
// continue unwinding
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
if ((uintptr_t) c == exception_object->private_2) {
|
||||||
|
// phase 1 said we would stop at this frame, but we did not...
|
||||||
|
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||||
|
"stop here, but now if phase2 it did not stop here");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _URC_INSTALL_CONTEXT:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): "
|
||||||
|
"_URC_INSTALL_CONTEXT, will resume at "
|
||||||
|
"landing pad %p",
|
||||||
|
(void *)exception_object, c->jbuf[1]);
|
||||||
|
// personality routine says to transfer control to landing pad
|
||||||
|
// we may get control back if landing pad calls _Unwind_Resume()
|
||||||
|
__Unwind_SjLj_SetTopOfFunctionStack(c);
|
||||||
|
__builtin_longjmp(c->jbuf, 1);
|
||||||
|
// __unw_resume() only returns if there was an error
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
default:
|
||||||
|
// something went wrong
|
||||||
|
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||||
|
personalityResult);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = c->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up phase did not resume at the frame that the search phase said it
|
||||||
|
// would
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase2_forced(struct _Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||||
|
// walk each frame until we reach where search phase said to stop
|
||||||
|
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
// get next frame (skip over first which is _Unwind_RaiseException)
|
||||||
|
if (c == NULL) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): __unw_step() reached "
|
||||||
|
"bottom => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call stop function at each frame
|
||||||
|
_Unwind_Action action =
|
||||||
|
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||||
|
_Unwind_Reason_Code stopResult =
|
||||||
|
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)c, stop_parameter);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"stop function returned %d",
|
||||||
|
(void *)exception_object, stopResult);
|
||||||
|
if (stopResult != _URC_NO_REASON) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"stopped by stop function",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a personality routine, tell it we are unwinding
|
||||||
|
if (c->personality != NULL) {
|
||||||
|
__personality_routine p = (__personality_routine) c->personality;
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"calling personality function %p",
|
||||||
|
(void *)exception_object, (void *)p);
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)c);
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned _URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
// destructors called, continue unwinding
|
||||||
|
break;
|
||||||
|
case _URC_INSTALL_CONTEXT:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned _URC_INSTALL_CONTEXT",
|
||||||
|
(void *)exception_object);
|
||||||
|
// we may get control back if landing pad calls _Unwind_Resume()
|
||||||
|
__Unwind_SjLj_SetTopOfFunctionStack(c);
|
||||||
|
__builtin_longjmp(c->jbuf, 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// something went wrong
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned %d, "
|
||||||
|
"_URC_FATAL_PHASE2_ERROR",
|
||||||
|
(void *)exception_object, personalityResult);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = c->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call stop function one last time and tell it we've reached the end of the
|
||||||
|
// stack
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
|
||||||
|
"function with _UA_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
_Unwind_Action lastAction =
|
||||||
|
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||||
|
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)c, stop_parameter);
|
||||||
|
|
||||||
|
// clean up phase did not resume at the frame that the search phase said it
|
||||||
|
// would
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by __cxa_throw. Only returns if there is a fatal error
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_SjLj_RaiseException(struct _Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_SjLj_RaiseException(ex_obj=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
|
||||||
|
// mark that this is a non-forced unwind, so _Unwind_Resume() can do the right
|
||||||
|
// thing
|
||||||
|
exception_object->private_1 = 0;
|
||||||
|
exception_object->private_2 = 0;
|
||||||
|
|
||||||
|
// phase 1: the search phase
|
||||||
|
_Unwind_Reason_Code phase1 = unwind_phase1(exception_object);
|
||||||
|
if (phase1 != _URC_NO_REASON)
|
||||||
|
return phase1;
|
||||||
|
|
||||||
|
// phase 2: the clean up phase
|
||||||
|
return unwind_phase2(exception_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||||
|
/// to the personality function at each frame. The personality
|
||||||
|
/// may force a jump to a landing pad in that function, the landing
|
||||||
|
/// pad code may then call _Unwind_Resume() to continue with the
|
||||||
|
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||||
|
/// geneated user code. All other _Unwind_* routines are called
|
||||||
|
/// by the C++ runtime __cxa_* routines.
|
||||||
|
///
|
||||||
|
/// Re-throwing an exception is implemented by having the code call
|
||||||
|
/// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow()
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_SjLj_Resume(ex_obj=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
|
||||||
|
if (exception_object->private_1 != 0)
|
||||||
|
unwind_phase2_forced(exception_object,
|
||||||
|
(_Unwind_Stop_Fn) exception_object->private_1,
|
||||||
|
(void *)exception_object->private_2);
|
||||||
|
else
|
||||||
|
unwind_phase2(exception_object);
|
||||||
|
|
||||||
|
// clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_SjLj_Resume() can't return");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by __cxa_rethrow().
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), "
|
||||||
|
"private_1=%" PRIuPTR,
|
||||||
|
(void *)exception_object, exception_object->private_1);
|
||||||
|
// If this is non-forced and a stopping place was found, then this is a
|
||||||
|
// re-throw.
|
||||||
|
// Call _Unwind_RaiseException() as if this was a new exception.
|
||||||
|
if (exception_object->private_1 == 0) {
|
||||||
|
return _Unwind_SjLj_RaiseException(exception_object);
|
||||||
|
// should return if there is no catch clause, so that __cxa_rethrow can call
|
||||||
|
// std::terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call through to _Unwind_Resume() which distiguishes between forced and
|
||||||
|
// regular exceptions.
|
||||||
|
_Unwind_SjLj_Resume(exception_object);
|
||||||
|
_LIBUNWIND_ABORT("__Unwind_SjLj_Resume_or_Rethrow() called "
|
||||||
|
"_Unwind_SjLj_Resume() which unexpectedly returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) "
|
||||||
|
"=> 0x%" PRIuPTR,
|
||||||
|
(void *)context, ufc->lsda);
|
||||||
|
return ufc->lsda;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get register values.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context,
|
||||||
|
int index) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d)", (void *)context,
|
||||||
|
index);
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
return ufc->resumeParameters[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to alter register values.
|
||||||
|
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||||
|
uintptr_t new_value) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIuPTR
|
||||||
|
")",
|
||||||
|
(void *)context, index, new_value);
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
ufc->resumeParameters[index] = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIu32,
|
||||||
|
(void *)context, ufc->resumeLocation + 1);
|
||||||
|
return ufc->resumeLocation + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||||
|
/// ipBefore is a boolean that says if IP is already adjusted to be the call
|
||||||
|
/// site address. Normally IP is the return address.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||||
|
int *ipBefore) {
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
*ipBefore = 0;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIu32,
|
||||||
|
(void *)context, (void *)ipBefore,
|
||||||
|
ufc->resumeLocation + 1);
|
||||||
|
return ufc->resumeLocation + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to alter instruction pointer.
|
||||||
|
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
|
||||||
|
uintptr_t new_value) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIuPTR ")",
|
||||||
|
(void *)context, new_value);
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
ufc->resumeLocation = new_value - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to find the start of the
|
||||||
|
/// function.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||||
|
// Not supported or needed for sjlj based unwinding
|
||||||
|
(void)context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p)", (void *)context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 if a foreign exception
|
||||||
|
/// is caught.
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_DeleteException(struct _Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
if (exception_object->exception_cleanup != NULL)
|
||||||
|
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||||
|
exception_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get base address for data
|
||||||
|
/// relative encodings.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
||||||
|
// Not supported or needed for sjlj based unwinding
|
||||||
|
(void)context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context);
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get base address for text
|
||||||
|
/// relative encodings.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
||||||
|
// Not supported or needed for sjlj based unwinding
|
||||||
|
(void)context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context);
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler to get "Call Frame Area" for current frame.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p)", (void *)context);
|
||||||
|
if (context != NULL) {
|
||||||
|
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||||
|
// Setjmp/longjmp based exceptions don't have a true CFA.
|
||||||
|
// Instead, the SP in the jmpbuf is the closest approximation.
|
||||||
|
return (uintptr_t) ufc->jbuf[2];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,319 @@
|
||||||
|
//===--------------------- UnwindLevel1-gcc-ext.c -------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Implements gcc extensions to the C++ ABI Exception Handling Level 1.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "libunwind_ext.h"
|
||||||
|
#include "libunwind.h"
|
||||||
|
#include "Unwind-EHABI.h"
|
||||||
|
#include "unwind.h"
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
|
||||||
|
#define private_1 private_[0]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Called by __cxa_rethrow().
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) {
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld",
|
||||||
|
(void *)exception_object,
|
||||||
|
(long)exception_object->unwinder_cache.reserved1);
|
||||||
|
#else
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR,
|
||||||
|
(void *)exception_object,
|
||||||
|
(intptr_t)exception_object->private_1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
// _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
|
||||||
|
// which is in the same position as private_1 below.
|
||||||
|
return _Unwind_RaiseException(exception_object);
|
||||||
|
#else
|
||||||
|
// If this is non-forced and a stopping place was found, then this is a
|
||||||
|
// re-throw.
|
||||||
|
// Call _Unwind_RaiseException() as if this was a new exception
|
||||||
|
if (exception_object->private_1 == 0) {
|
||||||
|
return _Unwind_RaiseException(exception_object);
|
||||||
|
// Will return if there is no catch clause, so that __cxa_rethrow can call
|
||||||
|
// std::terminate().
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call through to _Unwind_Resume() which distiguishes between forced and
|
||||||
|
// regular exceptions.
|
||||||
|
_Unwind_Resume(exception_object);
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()"
|
||||||
|
" which unexpectedly returned");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get base address for data
|
||||||
|
/// relative encodings.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
||||||
|
(void)context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context);
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get base address for text
|
||||||
|
/// relative encodings.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
||||||
|
(void)context;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context);
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Scans unwind information to find the function that contains the
|
||||||
|
/// specified code address "pc".
|
||||||
|
_LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)", pc);
|
||||||
|
// This is slow, but works.
|
||||||
|
// We create an unwind cursor then alter the IP to be pc
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_proc_info_t info;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
__unw_init_local(&cursor, &uc);
|
||||||
|
__unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc);
|
||||||
|
if (__unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS)
|
||||||
|
return (void *)(intptr_t) info.start_ip;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk every frame and call trace function at each one. If trace function
|
||||||
|
/// returns anything other than _URC_NO_REASON, then walk is terminated.
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
unw_context_t uc;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
__unw_init_local(&cursor, &uc);
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)",
|
||||||
|
(void *)(uintptr_t)callback);
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
// Create a mock exception object for force unwinding.
|
||||||
|
_Unwind_Exception ex;
|
||||||
|
memset(&ex, '\0', sizeof(ex));
|
||||||
|
ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// walk each frame
|
||||||
|
while (true) {
|
||||||
|
_Unwind_Reason_Code result;
|
||||||
|
|
||||||
|
#if !defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
// ask libunwind to get next frame (skip over first frame which is
|
||||||
|
// _Unwind_Backtrace())
|
||||||
|
if (__unw_step(&cursor) <= 0) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached "
|
||||||
|
"bottom of stack, returning %d",
|
||||||
|
_URC_END_OF_STACK);
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Get the information for this frame.
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
if (__unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the pr_cache in the mock exception object.
|
||||||
|
const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info;
|
||||||
|
ex.pr_cache.fnstart = frameInfo.start_ip;
|
||||||
|
ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo;
|
||||||
|
ex.pr_cache.additional= frameInfo.flags;
|
||||||
|
|
||||||
|
struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor;
|
||||||
|
// Get and call the personality function to unwind the frame.
|
||||||
|
__personality_routine handler = (__personality_routine) frameInfo.handler;
|
||||||
|
if (handler == NULL) {
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) !=
|
||||||
|
_URC_CONTINUE_UNWIND) {
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
}
|
||||||
|
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionName[512];
|
||||||
|
unw_proc_info_t frame;
|
||||||
|
unw_word_t offset;
|
||||||
|
__unw_get_proc_name(&cursor, functionName, 512, &offset);
|
||||||
|
__unw_get_proc_info(&cursor, &frame);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
" _backtrace: start_ip=0x%" PRIxPTR ", func=%s, lsda=0x%" PRIxPTR ", context=%p",
|
||||||
|
frame.start_ip, functionName, frame.lsda,
|
||||||
|
(void *)&cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// call trace function with this frame
|
||||||
|
result = (*callback)((struct _Unwind_Context *)(&cursor), ref);
|
||||||
|
if (result != _URC_NO_REASON) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
" _backtrace: ended because callback returned %d", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Find DWARF unwind info for an address 'pc' in some function.
|
||||||
|
_LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc,
|
||||||
|
struct dwarf_eh_bases *bases) {
|
||||||
|
// This is slow, but works.
|
||||||
|
// We create an unwind cursor then alter the IP to be pc
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_proc_info_t info;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
__unw_init_local(&cursor, &uc);
|
||||||
|
__unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc);
|
||||||
|
__unw_get_proc_info(&cursor, &info);
|
||||||
|
bases->tbase = (uintptr_t)info.extra;
|
||||||
|
bases->dbase = 0; // dbase not used on Mac OS X
|
||||||
|
bases->func = (uintptr_t)info.start_ip;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p", pc,
|
||||||
|
(void *)(intptr_t) info.unwind_info);
|
||||||
|
return (void *)(intptr_t) info.unwind_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the CFA (call frame area, or stack pointer at start of function)
|
||||||
|
/// for the current context.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_word_t result;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_SP, &result);
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, result);
|
||||||
|
return (uintptr_t)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||||
|
/// ipBefore is a boolean that says if IP is already adjusted to be the call
|
||||||
|
/// site address. Normally IP is the return address.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||||
|
int *ipBefore) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)", (void *)context);
|
||||||
|
*ipBefore = 0;
|
||||||
|
return _Unwind_GetIP(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
|
||||||
|
/// Called by programs with dynamic code generators that want
|
||||||
|
/// to register a dynamically generated FDE.
|
||||||
|
/// This function has existed on Mac OS X since 10.4, but
|
||||||
|
/// was broken until 10.6.
|
||||||
|
_LIBUNWIND_EXPORT void __register_frame(const void *fde) {
|
||||||
|
_LIBUNWIND_TRACE_API("__register_frame(%p)", fde);
|
||||||
|
__unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by programs with dynamic code generators that want
|
||||||
|
/// to unregister a dynamically generated FDE.
|
||||||
|
/// This function has existed on Mac OS X since 10.4, but
|
||||||
|
/// was broken until 10.6.
|
||||||
|
_LIBUNWIND_EXPORT void __deregister_frame(const void *fde) {
|
||||||
|
_LIBUNWIND_TRACE_API("__deregister_frame(%p)", fde);
|
||||||
|
__unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The following register/deregister functions are gcc extensions.
|
||||||
|
// They have existed on Mac OS X, but have never worked because Mac OS X
|
||||||
|
// before 10.6 used keymgr to track known FDEs, but these functions
|
||||||
|
// never got updated to use keymgr.
|
||||||
|
// For now, we implement these as do-nothing functions to keep any existing
|
||||||
|
// applications working. We also add the not in 10.6 symbol so that nwe
|
||||||
|
// application won't be able to use them.
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_FRAME_APIS)
|
||||||
|
_LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob,
|
||||||
|
void *tb, void *db) {
|
||||||
|
(void)fde;
|
||||||
|
(void)ob;
|
||||||
|
(void)tb;
|
||||||
|
(void)db;
|
||||||
|
_LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)",
|
||||||
|
fde, ob, tb, db);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) {
|
||||||
|
(void)fde;
|
||||||
|
(void)ob;
|
||||||
|
_LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)", fde, ob);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde,
|
||||||
|
void *ob, void *tb,
|
||||||
|
void *db) {
|
||||||
|
(void)fde;
|
||||||
|
(void)ob;
|
||||||
|
(void)tb;
|
||||||
|
(void)db;
|
||||||
|
_LIBUNWIND_TRACE_API("__register_frame_info_table_bases"
|
||||||
|
"(%p,%p, %p, %p)", fde, ob, tb, db);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) {
|
||||||
|
(void)fde;
|
||||||
|
(void)ob;
|
||||||
|
_LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)", fde, ob);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void __register_frame_table(const void *fde) {
|
||||||
|
(void)fde;
|
||||||
|
_LIBUNWIND_TRACE_API("__register_frame_table(%p)", fde);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) {
|
||||||
|
(void)fde;
|
||||||
|
_LIBUNWIND_TRACE_API("__deregister_frame_info(%p)", fde);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) {
|
||||||
|
(void)fde;
|
||||||
|
_LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)", fde);
|
||||||
|
// do nothing, this function never worked in Mac OS X
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif // defined(_LIBUNWIND_SUPPORT_FRAME_APIS)
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
|
@ -0,0 +1,515 @@
|
||||||
|
//===------------------------- UnwindLevel1.c -----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Implements C++ ABI Exception Handling Level 1 as documented at:
|
||||||
|
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
|
||||||
|
// using libunwind
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// ARM EHABI does not specify _Unwind_{Get,Set}{GR,IP}(). Thus, we are
|
||||||
|
// defining inline functions to delegate the function calls to
|
||||||
|
// _Unwind_VRS_{Get,Set}(). However, some applications might declare the
|
||||||
|
// function protetype directly (instead of including <unwind.h>), thus we need
|
||||||
|
// to export these functions from libunwind.so as well.
|
||||||
|
#define _LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE 1
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "libunwind.h"
|
||||||
|
#include "libunwind_ext.h"
|
||||||
|
#include "unwind.h"
|
||||||
|
|
||||||
|
#if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
|
||||||
|
#ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
|
||||||
|
__unw_init_local(cursor, uc);
|
||||||
|
|
||||||
|
// Walk each frame looking for a place to stop.
|
||||||
|
bool handlerNotFound = true;
|
||||||
|
while (handlerNotFound) {
|
||||||
|
// Ask libunwind to get next frame (skip over first which is
|
||||||
|
// _Unwind_RaiseException).
|
||||||
|
int stepResult = __unw_step(cursor);
|
||||||
|
if (stepResult == 0) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): __unw_step() reached "
|
||||||
|
"bottom => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
} else if (stepResult < 0) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): __unw_step failed => "
|
||||||
|
"_URC_FATAL_PHASE1_ERROR",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if frame has code to run (has personality routine).
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
unw_word_t sp;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): __unw_get_proc_info "
|
||||||
|
"failed => _URC_FATAL_PHASE1_ERROR",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tracing, print state information.
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionBuf[512];
|
||||||
|
const char *functionName = functionBuf;
|
||||||
|
unw_word_t offset;
|
||||||
|
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||||
|
&offset) != UNW_ESUCCESS) ||
|
||||||
|
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||||
|
functionName = ".anonymous.";
|
||||||
|
unw_word_t pc;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR
|
||||||
|
", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "",
|
||||||
|
(void *)exception_object, pc, frameInfo.start_ip, functionName,
|
||||||
|
frameInfo.lsda, frameInfo.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a personality routine, ask it if it will want to stop at
|
||||||
|
// this frame.
|
||||||
|
if (frameInfo.handler != 0) {
|
||||||
|
__personality_routine p =
|
||||||
|
(__personality_routine)(uintptr_t)(frameInfo.handler);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): calling personality function %p",
|
||||||
|
(void *)exception_object, (void *)(uintptr_t)p);
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class,
|
||||||
|
exception_object, (struct _Unwind_Context *)(cursor));
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_HANDLER_FOUND:
|
||||||
|
// found a catch clause or locals that need destructing in this frame
|
||||||
|
// stop search and remember stack pointer at the frame
|
||||||
|
handlerNotFound = false;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||||
|
exception_object->private_2 = (uintptr_t)sp;
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
// continue unwinding
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// something went wrong
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
|
||||||
|
__unw_init_local(cursor, uc);
|
||||||
|
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
|
||||||
|
// Walk each frame until we reach where search phase said to stop.
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
// Ask libunwind to get next frame (skip over first which is
|
||||||
|
// _Unwind_RaiseException).
|
||||||
|
int stepResult = __unw_step(cursor);
|
||||||
|
if (stepResult == 0) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): __unw_step() reached "
|
||||||
|
"bottom => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_END_OF_STACK;
|
||||||
|
} else if (stepResult < 0) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): __unw_step failed => "
|
||||||
|
"_URC_FATAL_PHASE1_ERROR",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get info about this frame.
|
||||||
|
unw_word_t sp;
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): __unw_get_proc_info "
|
||||||
|
"failed => _URC_FATAL_PHASE1_ERROR",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tracing, print state information.
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionBuf[512];
|
||||||
|
const char *functionName = functionBuf;
|
||||||
|
unw_word_t offset;
|
||||||
|
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||||
|
&offset) != UNW_ESUCCESS) ||
|
||||||
|
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||||
|
functionName = ".anonymous.";
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR
|
||||||
|
", func=%s, sp=0x%" PRIxPTR ", lsda=0x%" PRIxPTR
|
||||||
|
", personality=0x%" PRIxPTR,
|
||||||
|
(void *)exception_object, frameInfo.start_ip,
|
||||||
|
functionName, sp, frameInfo.lsda,
|
||||||
|
frameInfo.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a personality routine, tell it we are unwinding.
|
||||||
|
if (frameInfo.handler != 0) {
|
||||||
|
__personality_routine p =
|
||||||
|
(__personality_routine)(uintptr_t)(frameInfo.handler);
|
||||||
|
_Unwind_Action action = _UA_CLEANUP_PHASE;
|
||||||
|
if (sp == exception_object->private_2) {
|
||||||
|
// Tell personality this was the frame it marked in phase 1.
|
||||||
|
action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME);
|
||||||
|
}
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(cursor));
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
// Continue unwinding
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
if (sp == exception_object->private_2) {
|
||||||
|
// Phase 1 said we would stop at this frame, but we did not...
|
||||||
|
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||||
|
"stop here, but now in phase2 it did not stop here");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _URC_INSTALL_CONTEXT:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT",
|
||||||
|
(void *)exception_object);
|
||||||
|
// Personality routine says to transfer control to landing pad.
|
||||||
|
// We may get control back if landing pad calls _Unwind_Resume().
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
unw_word_t pc;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||||
|
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering "
|
||||||
|
"user code with ip=0x%" PRIxPTR
|
||||||
|
", sp=0x%" PRIxPTR,
|
||||||
|
(void *)exception_object, pc, sp);
|
||||||
|
}
|
||||||
|
__unw_resume(cursor);
|
||||||
|
// __unw_resume() only returns if there was an error.
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
default:
|
||||||
|
// Personality routine returned an unknown result code.
|
||||||
|
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||||
|
personalityResult);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up phase did not resume at the frame that the search phase
|
||||||
|
// said it would...
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
|
||||||
|
_Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||||
|
__unw_init_local(cursor, uc);
|
||||||
|
|
||||||
|
// Walk each frame until we reach where search phase said to stop
|
||||||
|
while (__unw_step(cursor) > 0) {
|
||||||
|
|
||||||
|
// Update info about this frame.
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step "
|
||||||
|
"failed => _URC_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tracing, print state information.
|
||||||
|
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||||
|
char functionBuf[512];
|
||||||
|
const char *functionName = functionBuf;
|
||||||
|
unw_word_t offset;
|
||||||
|
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||||
|
&offset) != UNW_ESUCCESS) ||
|
||||||
|
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||||
|
functionName = ".anonymous.";
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR
|
||||||
|
", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR,
|
||||||
|
(void *)exception_object, frameInfo.start_ip, functionName,
|
||||||
|
frameInfo.lsda, frameInfo.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call stop function at each frame.
|
||||||
|
_Unwind_Action action =
|
||||||
|
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||||
|
_Unwind_Reason_Code stopResult =
|
||||||
|
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(cursor), stop_parameter);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): stop function returned %d",
|
||||||
|
(void *)exception_object, stopResult);
|
||||||
|
if (stopResult != _URC_NO_REASON) {
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): stopped by stop function",
|
||||||
|
(void *)exception_object);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a personality routine, tell it we are unwinding.
|
||||||
|
if (frameInfo.handler != 0) {
|
||||||
|
__personality_routine p =
|
||||||
|
(__personality_routine)(intptr_t)(frameInfo.handler);
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING(
|
||||||
|
"unwind_phase2_forced(ex_ojb=%p): calling personality function %p",
|
||||||
|
(void *)exception_object, (void *)(uintptr_t)p);
|
||||||
|
_Unwind_Reason_Code personalityResult =
|
||||||
|
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(cursor));
|
||||||
|
switch (personalityResult) {
|
||||||
|
case _URC_CONTINUE_UNWIND:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned "
|
||||||
|
"_URC_CONTINUE_UNWIND",
|
||||||
|
(void *)exception_object);
|
||||||
|
// Destructors called, continue unwinding
|
||||||
|
break;
|
||||||
|
case _URC_INSTALL_CONTEXT:
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned "
|
||||||
|
"_URC_INSTALL_CONTEXT",
|
||||||
|
(void *)exception_object);
|
||||||
|
// We may get control back if landing pad calls _Unwind_Resume().
|
||||||
|
__unw_resume(cursor);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Personality routine returned an unknown result code.
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||||
|
"personality returned %d, "
|
||||||
|
"_URC_FATAL_PHASE2_ERROR",
|
||||||
|
(void *)exception_object, personalityResult);
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call stop function one last time and tell it we've reached the end
|
||||||
|
// of the stack.
|
||||||
|
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
|
||||||
|
"function with _UA_END_OF_STACK",
|
||||||
|
(void *)exception_object);
|
||||||
|
_Unwind_Action lastAction =
|
||||||
|
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||||
|
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||||
|
(struct _Unwind_Context *)(cursor), stop_parameter);
|
||||||
|
|
||||||
|
// Clean up phase did not resume at the frame that the search phase said it
|
||||||
|
// would.
|
||||||
|
return _URC_FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by __cxa_throw. Only returns if there is a fatal error.
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
|
||||||
|
// Mark that this is a non-forced unwind, so _Unwind_Resume()
|
||||||
|
// can do the right thing.
|
||||||
|
exception_object->private_1 = 0;
|
||||||
|
exception_object->private_2 = 0;
|
||||||
|
|
||||||
|
// phase 1: the search phase
|
||||||
|
_Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object);
|
||||||
|
if (phase1 != _URC_NO_REASON)
|
||||||
|
return phase1;
|
||||||
|
|
||||||
|
// phase 2: the clean up phase
|
||||||
|
return unwind_phase2(&uc, &cursor, exception_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||||
|
/// to the personality function at each frame. The personality
|
||||||
|
/// may force a jump to a landing pad in that function, the landing
|
||||||
|
/// pad code may then call _Unwind_Resume() to continue with the
|
||||||
|
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||||
|
/// geneated user code. All other _Unwind_* routines are called
|
||||||
|
/// by the C++ runtime __cxa_* routines.
|
||||||
|
///
|
||||||
|
/// Note: re-throwing an exception (as opposed to continuing the unwind)
|
||||||
|
/// is implemented by having the code call __cxa_rethrow() which
|
||||||
|
/// in turn calls _Unwind_Resume_or_Rethrow().
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_Resume(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object);
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
|
||||||
|
if (exception_object->private_1 != 0)
|
||||||
|
unwind_phase2_forced(&uc, &cursor, exception_object,
|
||||||
|
(_Unwind_Stop_Fn) exception_object->private_1,
|
||||||
|
(void *)exception_object->private_2);
|
||||||
|
else
|
||||||
|
unwind_phase2(&uc, &cursor, exception_object);
|
||||||
|
|
||||||
|
// Clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||||
|
_LIBUNWIND_ABORT("_Unwind_Resume() can't return");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Not used by C++.
|
||||||
|
/// Unwinds stack, calling "stop" function at each frame.
|
||||||
|
/// Could be used to implement longjmp().
|
||||||
|
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||||
|
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||||
|
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",
|
||||||
|
(void *)exception_object, (void *)(uintptr_t)stop);
|
||||||
|
unw_context_t uc;
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
__unw_getcontext(&uc);
|
||||||
|
|
||||||
|
// Mark that this is a forced unwind, so _Unwind_Resume() can do
|
||||||
|
// the right thing.
|
||||||
|
exception_object->private_1 = (uintptr_t) stop;
|
||||||
|
exception_object->private_2 = (uintptr_t) stop_parameter;
|
||||||
|
|
||||||
|
// do it
|
||||||
|
return unwind_phase2_forced(&uc, &cursor, exception_object, stop, stop_parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
uintptr_t result = 0;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||||
|
result = (uintptr_t)frameInfo.lsda;
|
||||||
|
_LIBUNWIND_TRACE_API(
|
||||||
|
"_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, result);
|
||||||
|
if (result != 0) {
|
||||||
|
if (*((uint8_t *)result) != 0xFF)
|
||||||
|
_LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF",
|
||||||
|
result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to find the start of the
|
||||||
|
/// function.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_proc_info_t frameInfo;
|
||||||
|
uintptr_t result = 0;
|
||||||
|
if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||||
|
result = (uintptr_t)frameInfo.start_ip;
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !_LIBUNWIND_SUPPORT_SEH_UNWIND
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 if a foreign exception
|
||||||
|
// is caught.
|
||||||
|
_LIBUNWIND_EXPORT void
|
||||||
|
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
|
||||||
|
(void *)exception_object);
|
||||||
|
if (exception_object->exception_cleanup != NULL)
|
||||||
|
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||||
|
exception_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get register values.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t
|
||||||
|
_Unwind_GetGR(struct _Unwind_Context *context, int index) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_word_t result;
|
||||||
|
__unw_get_reg(cursor, index, &result);
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, index, result);
|
||||||
|
return (uintptr_t)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to alter register values.
|
||||||
|
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||||
|
uintptr_t value) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0" PRIxPTR
|
||||||
|
")",
|
||||||
|
(void *)context, index, value);
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
__unw_set_reg(cursor, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||||
|
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
unw_word_t result;
|
||||||
|
__unw_get_reg(cursor, UNW_REG_IP, &result);
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR,
|
||||||
|
(void *)context, result);
|
||||||
|
return (uintptr_t)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by personality handler during phase 2 to alter instruction pointer,
|
||||||
|
/// such as setting where the landing pad is, so _Unwind_Resume() will
|
||||||
|
/// start executing in the landing pad.
|
||||||
|
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
|
||||||
|
uintptr_t value) {
|
||||||
|
_LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0" PRIxPTR ")",
|
||||||
|
(void *)context, value);
|
||||||
|
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||||
|
__unw_set_reg(cursor, UNW_REG_IP, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,983 @@
|
||||||
|
//===------------------------ UnwindRegistersSave.S -----------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "assembly.h"
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
#if !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# On entry:
|
||||||
|
# + +
|
||||||
|
# +-----------------------+
|
||||||
|
# + thread_state pointer +
|
||||||
|
# +-----------------------+
|
||||||
|
# + return address +
|
||||||
|
# +-----------------------+ <-- SP
|
||||||
|
# + +
|
||||||
|
#
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
push %eax
|
||||||
|
movl 8(%esp), %eax
|
||||||
|
movl %ebx, 4(%eax)
|
||||||
|
movl %ecx, 8(%eax)
|
||||||
|
movl %edx, 12(%eax)
|
||||||
|
movl %edi, 16(%eax)
|
||||||
|
movl %esi, 20(%eax)
|
||||||
|
movl %ebp, 24(%eax)
|
||||||
|
movl %esp, %edx
|
||||||
|
addl $8, %edx
|
||||||
|
movl %edx, 28(%eax) # store what sp was at call site as esp
|
||||||
|
# skip ss
|
||||||
|
# skip eflags
|
||||||
|
movl 4(%esp), %edx
|
||||||
|
movl %edx, 40(%eax) # store return address as eip
|
||||||
|
# skip cs
|
||||||
|
# skip ds
|
||||||
|
# skip es
|
||||||
|
# skip fs
|
||||||
|
# skip gs
|
||||||
|
movl (%esp), %edx
|
||||||
|
movl %edx, (%eax) # store original eax
|
||||||
|
popl %eax
|
||||||
|
xorl %eax, %eax # return UNW_ESUCCESS
|
||||||
|
ret
|
||||||
|
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# On entry:
|
||||||
|
# thread_state pointer is in rdi
|
||||||
|
#
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#define PTR %rcx
|
||||||
|
#define TMP %rdx
|
||||||
|
#else
|
||||||
|
#define PTR %rdi
|
||||||
|
#define TMP %rsi
|
||||||
|
#endif
|
||||||
|
|
||||||
|
movq %rax, (PTR)
|
||||||
|
movq %rbx, 8(PTR)
|
||||||
|
movq %rcx, 16(PTR)
|
||||||
|
movq %rdx, 24(PTR)
|
||||||
|
movq %rdi, 32(PTR)
|
||||||
|
movq %rsi, 40(PTR)
|
||||||
|
movq %rbp, 48(PTR)
|
||||||
|
movq %rsp, 56(PTR)
|
||||||
|
addq $8, 56(PTR)
|
||||||
|
movq %r8, 64(PTR)
|
||||||
|
movq %r9, 72(PTR)
|
||||||
|
movq %r10, 80(PTR)
|
||||||
|
movq %r11, 88(PTR)
|
||||||
|
movq %r12, 96(PTR)
|
||||||
|
movq %r13,104(PTR)
|
||||||
|
movq %r14,112(PTR)
|
||||||
|
movq %r15,120(PTR)
|
||||||
|
movq (%rsp),TMP
|
||||||
|
movq TMP,128(PTR) # store return address as rip
|
||||||
|
# skip rflags
|
||||||
|
# skip cs
|
||||||
|
# skip fs
|
||||||
|
# skip gs
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
movdqu %xmm0,176(PTR)
|
||||||
|
movdqu %xmm1,192(PTR)
|
||||||
|
movdqu %xmm2,208(PTR)
|
||||||
|
movdqu %xmm3,224(PTR)
|
||||||
|
movdqu %xmm4,240(PTR)
|
||||||
|
movdqu %xmm5,256(PTR)
|
||||||
|
movdqu %xmm6,272(PTR)
|
||||||
|
movdqu %xmm7,288(PTR)
|
||||||
|
movdqu %xmm8,304(PTR)
|
||||||
|
movdqu %xmm9,320(PTR)
|
||||||
|
movdqu %xmm10,336(PTR)
|
||||||
|
movdqu %xmm11,352(PTR)
|
||||||
|
movdqu %xmm12,368(PTR)
|
||||||
|
movdqu %xmm13,384(PTR)
|
||||||
|
movdqu %xmm14,400(PTR)
|
||||||
|
movdqu %xmm15,416(PTR)
|
||||||
|
#endif
|
||||||
|
xorl %eax, %eax # return UNW_ESUCCESS
|
||||||
|
ret
|
||||||
|
|
||||||
|
#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# On entry:
|
||||||
|
# thread_state pointer is in a0 ($4)
|
||||||
|
#
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
.set push
|
||||||
|
.set noat
|
||||||
|
.set noreorder
|
||||||
|
.set nomacro
|
||||||
|
sw $1, (4 * 1)($4)
|
||||||
|
sw $2, (4 * 2)($4)
|
||||||
|
sw $3, (4 * 3)($4)
|
||||||
|
sw $4, (4 * 4)($4)
|
||||||
|
sw $5, (4 * 5)($4)
|
||||||
|
sw $6, (4 * 6)($4)
|
||||||
|
sw $7, (4 * 7)($4)
|
||||||
|
sw $8, (4 * 8)($4)
|
||||||
|
sw $9, (4 * 9)($4)
|
||||||
|
sw $10, (4 * 10)($4)
|
||||||
|
sw $11, (4 * 11)($4)
|
||||||
|
sw $12, (4 * 12)($4)
|
||||||
|
sw $13, (4 * 13)($4)
|
||||||
|
sw $14, (4 * 14)($4)
|
||||||
|
sw $15, (4 * 15)($4)
|
||||||
|
sw $16, (4 * 16)($4)
|
||||||
|
sw $17, (4 * 17)($4)
|
||||||
|
sw $18, (4 * 18)($4)
|
||||||
|
sw $19, (4 * 19)($4)
|
||||||
|
sw $20, (4 * 20)($4)
|
||||||
|
sw $21, (4 * 21)($4)
|
||||||
|
sw $22, (4 * 22)($4)
|
||||||
|
sw $23, (4 * 23)($4)
|
||||||
|
sw $24, (4 * 24)($4)
|
||||||
|
sw $25, (4 * 25)($4)
|
||||||
|
sw $26, (4 * 26)($4)
|
||||||
|
sw $27, (4 * 27)($4)
|
||||||
|
sw $28, (4 * 28)($4)
|
||||||
|
sw $29, (4 * 29)($4)
|
||||||
|
sw $30, (4 * 30)($4)
|
||||||
|
sw $31, (4 * 31)($4)
|
||||||
|
# Store return address to pc
|
||||||
|
sw $31, (4 * 32)($4)
|
||||||
|
# hi and lo
|
||||||
|
mfhi $8
|
||||||
|
sw $8, (4 * 33)($4)
|
||||||
|
mflo $8
|
||||||
|
sw $8, (4 * 34)($4)
|
||||||
|
#ifdef __mips_hard_float
|
||||||
|
#if __mips_fpr != 64
|
||||||
|
sdc1 $f0, (4 * 36 + 8 * 0)($4)
|
||||||
|
sdc1 $f2, (4 * 36 + 8 * 2)($4)
|
||||||
|
sdc1 $f4, (4 * 36 + 8 * 4)($4)
|
||||||
|
sdc1 $f6, (4 * 36 + 8 * 6)($4)
|
||||||
|
sdc1 $f8, (4 * 36 + 8 * 8)($4)
|
||||||
|
sdc1 $f10, (4 * 36 + 8 * 10)($4)
|
||||||
|
sdc1 $f12, (4 * 36 + 8 * 12)($4)
|
||||||
|
sdc1 $f14, (4 * 36 + 8 * 14)($4)
|
||||||
|
sdc1 $f16, (4 * 36 + 8 * 16)($4)
|
||||||
|
sdc1 $f18, (4 * 36 + 8 * 18)($4)
|
||||||
|
sdc1 $f20, (4 * 36 + 8 * 20)($4)
|
||||||
|
sdc1 $f22, (4 * 36 + 8 * 22)($4)
|
||||||
|
sdc1 $f24, (4 * 36 + 8 * 24)($4)
|
||||||
|
sdc1 $f26, (4 * 36 + 8 * 26)($4)
|
||||||
|
sdc1 $f28, (4 * 36 + 8 * 28)($4)
|
||||||
|
sdc1 $f30, (4 * 36 + 8 * 30)($4)
|
||||||
|
#else
|
||||||
|
sdc1 $f0, (4 * 36 + 8 * 0)($4)
|
||||||
|
sdc1 $f1, (4 * 36 + 8 * 1)($4)
|
||||||
|
sdc1 $f2, (4 * 36 + 8 * 2)($4)
|
||||||
|
sdc1 $f3, (4 * 36 + 8 * 3)($4)
|
||||||
|
sdc1 $f4, (4 * 36 + 8 * 4)($4)
|
||||||
|
sdc1 $f5, (4 * 36 + 8 * 5)($4)
|
||||||
|
sdc1 $f6, (4 * 36 + 8 * 6)($4)
|
||||||
|
sdc1 $f7, (4 * 36 + 8 * 7)($4)
|
||||||
|
sdc1 $f8, (4 * 36 + 8 * 8)($4)
|
||||||
|
sdc1 $f9, (4 * 36 + 8 * 9)($4)
|
||||||
|
sdc1 $f10, (4 * 36 + 8 * 10)($4)
|
||||||
|
sdc1 $f11, (4 * 36 + 8 * 11)($4)
|
||||||
|
sdc1 $f12, (4 * 36 + 8 * 12)($4)
|
||||||
|
sdc1 $f13, (4 * 36 + 8 * 13)($4)
|
||||||
|
sdc1 $f14, (4 * 36 + 8 * 14)($4)
|
||||||
|
sdc1 $f15, (4 * 36 + 8 * 15)($4)
|
||||||
|
sdc1 $f16, (4 * 36 + 8 * 16)($4)
|
||||||
|
sdc1 $f17, (4 * 36 + 8 * 17)($4)
|
||||||
|
sdc1 $f18, (4 * 36 + 8 * 18)($4)
|
||||||
|
sdc1 $f19, (4 * 36 + 8 * 19)($4)
|
||||||
|
sdc1 $f20, (4 * 36 + 8 * 20)($4)
|
||||||
|
sdc1 $f21, (4 * 36 + 8 * 21)($4)
|
||||||
|
sdc1 $f22, (4 * 36 + 8 * 22)($4)
|
||||||
|
sdc1 $f23, (4 * 36 + 8 * 23)($4)
|
||||||
|
sdc1 $f24, (4 * 36 + 8 * 24)($4)
|
||||||
|
sdc1 $f25, (4 * 36 + 8 * 25)($4)
|
||||||
|
sdc1 $f26, (4 * 36 + 8 * 26)($4)
|
||||||
|
sdc1 $f27, (4 * 36 + 8 * 27)($4)
|
||||||
|
sdc1 $f28, (4 * 36 + 8 * 28)($4)
|
||||||
|
sdc1 $f29, (4 * 36 + 8 * 29)($4)
|
||||||
|
sdc1 $f30, (4 * 36 + 8 * 30)($4)
|
||||||
|
sdc1 $f31, (4 * 36 + 8 * 31)($4)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
jr $31
|
||||||
|
# return UNW_ESUCCESS
|
||||||
|
or $2, $0, $0
|
||||||
|
.set pop
|
||||||
|
|
||||||
|
#elif defined(__mips64)
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# On entry:
|
||||||
|
# thread_state pointer is in a0 ($4)
|
||||||
|
#
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
.set push
|
||||||
|
.set noat
|
||||||
|
.set noreorder
|
||||||
|
.set nomacro
|
||||||
|
sd $1, (8 * 1)($4)
|
||||||
|
sd $2, (8 * 2)($4)
|
||||||
|
sd $3, (8 * 3)($4)
|
||||||
|
sd $4, (8 * 4)($4)
|
||||||
|
sd $5, (8 * 5)($4)
|
||||||
|
sd $6, (8 * 6)($4)
|
||||||
|
sd $7, (8 * 7)($4)
|
||||||
|
sd $8, (8 * 8)($4)
|
||||||
|
sd $9, (8 * 9)($4)
|
||||||
|
sd $10, (8 * 10)($4)
|
||||||
|
sd $11, (8 * 11)($4)
|
||||||
|
sd $12, (8 * 12)($4)
|
||||||
|
sd $13, (8 * 13)($4)
|
||||||
|
sd $14, (8 * 14)($4)
|
||||||
|
sd $15, (8 * 15)($4)
|
||||||
|
sd $16, (8 * 16)($4)
|
||||||
|
sd $17, (8 * 17)($4)
|
||||||
|
sd $18, (8 * 18)($4)
|
||||||
|
sd $19, (8 * 19)($4)
|
||||||
|
sd $20, (8 * 20)($4)
|
||||||
|
sd $21, (8 * 21)($4)
|
||||||
|
sd $22, (8 * 22)($4)
|
||||||
|
sd $23, (8 * 23)($4)
|
||||||
|
sd $24, (8 * 24)($4)
|
||||||
|
sd $25, (8 * 25)($4)
|
||||||
|
sd $26, (8 * 26)($4)
|
||||||
|
sd $27, (8 * 27)($4)
|
||||||
|
sd $28, (8 * 28)($4)
|
||||||
|
sd $29, (8 * 29)($4)
|
||||||
|
sd $30, (8 * 30)($4)
|
||||||
|
sd $31, (8 * 31)($4)
|
||||||
|
# Store return address to pc
|
||||||
|
sd $31, (8 * 32)($4)
|
||||||
|
# hi and lo
|
||||||
|
mfhi $8
|
||||||
|
sd $8, (8 * 33)($4)
|
||||||
|
mflo $8
|
||||||
|
sd $8, (8 * 34)($4)
|
||||||
|
#ifdef __mips_hard_float
|
||||||
|
sdc1 $f0, (8 * 35)($4)
|
||||||
|
sdc1 $f1, (8 * 36)($4)
|
||||||
|
sdc1 $f2, (8 * 37)($4)
|
||||||
|
sdc1 $f3, (8 * 38)($4)
|
||||||
|
sdc1 $f4, (8 * 39)($4)
|
||||||
|
sdc1 $f5, (8 * 40)($4)
|
||||||
|
sdc1 $f6, (8 * 41)($4)
|
||||||
|
sdc1 $f7, (8 * 42)($4)
|
||||||
|
sdc1 $f8, (8 * 43)($4)
|
||||||
|
sdc1 $f9, (8 * 44)($4)
|
||||||
|
sdc1 $f10, (8 * 45)($4)
|
||||||
|
sdc1 $f11, (8 * 46)($4)
|
||||||
|
sdc1 $f12, (8 * 47)($4)
|
||||||
|
sdc1 $f13, (8 * 48)($4)
|
||||||
|
sdc1 $f14, (8 * 49)($4)
|
||||||
|
sdc1 $f15, (8 * 50)($4)
|
||||||
|
sdc1 $f16, (8 * 51)($4)
|
||||||
|
sdc1 $f17, (8 * 52)($4)
|
||||||
|
sdc1 $f18, (8 * 53)($4)
|
||||||
|
sdc1 $f19, (8 * 54)($4)
|
||||||
|
sdc1 $f20, (8 * 55)($4)
|
||||||
|
sdc1 $f21, (8 * 56)($4)
|
||||||
|
sdc1 $f22, (8 * 57)($4)
|
||||||
|
sdc1 $f23, (8 * 58)($4)
|
||||||
|
sdc1 $f24, (8 * 59)($4)
|
||||||
|
sdc1 $f25, (8 * 60)($4)
|
||||||
|
sdc1 $f26, (8 * 61)($4)
|
||||||
|
sdc1 $f27, (8 * 62)($4)
|
||||||
|
sdc1 $f28, (8 * 63)($4)
|
||||||
|
sdc1 $f29, (8 * 64)($4)
|
||||||
|
sdc1 $f30, (8 * 65)($4)
|
||||||
|
sdc1 $f31, (8 * 66)($4)
|
||||||
|
#endif
|
||||||
|
jr $31
|
||||||
|
# return UNW_ESUCCESS
|
||||||
|
or $2, $0, $0
|
||||||
|
.set pop
|
||||||
|
|
||||||
|
# elif defined(__mips__)
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# Just trap for the time being.
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
teq $0, $0
|
||||||
|
|
||||||
|
#elif defined(__powerpc64__)
|
||||||
|
|
||||||
|
//
|
||||||
|
// extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
//
|
||||||
|
// On entry:
|
||||||
|
// thread_state pointer is in r3
|
||||||
|
//
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
|
||||||
|
// store register (GPR)
|
||||||
|
#define PPC64_STR(n) \
|
||||||
|
std %r##n, (8 * (n + 2))(%r3)
|
||||||
|
|
||||||
|
// save GPRs
|
||||||
|
PPC64_STR(0)
|
||||||
|
mflr %r0
|
||||||
|
std %r0, PPC64_OFFS_SRR0(%r3) // store lr as ssr0
|
||||||
|
PPC64_STR(1)
|
||||||
|
PPC64_STR(2)
|
||||||
|
PPC64_STR(3)
|
||||||
|
PPC64_STR(4)
|
||||||
|
PPC64_STR(5)
|
||||||
|
PPC64_STR(6)
|
||||||
|
PPC64_STR(7)
|
||||||
|
PPC64_STR(8)
|
||||||
|
PPC64_STR(9)
|
||||||
|
PPC64_STR(10)
|
||||||
|
PPC64_STR(11)
|
||||||
|
PPC64_STR(12)
|
||||||
|
PPC64_STR(13)
|
||||||
|
PPC64_STR(14)
|
||||||
|
PPC64_STR(15)
|
||||||
|
PPC64_STR(16)
|
||||||
|
PPC64_STR(17)
|
||||||
|
PPC64_STR(18)
|
||||||
|
PPC64_STR(19)
|
||||||
|
PPC64_STR(20)
|
||||||
|
PPC64_STR(21)
|
||||||
|
PPC64_STR(22)
|
||||||
|
PPC64_STR(23)
|
||||||
|
PPC64_STR(24)
|
||||||
|
PPC64_STR(25)
|
||||||
|
PPC64_STR(26)
|
||||||
|
PPC64_STR(27)
|
||||||
|
PPC64_STR(28)
|
||||||
|
PPC64_STR(29)
|
||||||
|
PPC64_STR(30)
|
||||||
|
PPC64_STR(31)
|
||||||
|
|
||||||
|
mfcr %r0
|
||||||
|
std %r0, PPC64_OFFS_CR(%r3)
|
||||||
|
mfxer %r0
|
||||||
|
std %r0, PPC64_OFFS_XER(%r3)
|
||||||
|
mflr %r0
|
||||||
|
std %r0, PPC64_OFFS_LR(%r3)
|
||||||
|
mfctr %r0
|
||||||
|
std %r0, PPC64_OFFS_CTR(%r3)
|
||||||
|
mfvrsave %r0
|
||||||
|
std %r0, PPC64_OFFS_VRSAVE(%r3)
|
||||||
|
|
||||||
|
#ifdef PPC64_HAS_VMX
|
||||||
|
// save VS registers
|
||||||
|
// (note that this also saves floating point registers and V registers,
|
||||||
|
// because part of VS is mapped to these registers)
|
||||||
|
|
||||||
|
addi %r4, %r3, PPC64_OFFS_FP
|
||||||
|
|
||||||
|
// store VS register
|
||||||
|
#define PPC64_STVS(n) \
|
||||||
|
stxvd2x %vs##n, 0, %r4 ;\
|
||||||
|
addi %r4, %r4, 16
|
||||||
|
|
||||||
|
PPC64_STVS(0)
|
||||||
|
PPC64_STVS(1)
|
||||||
|
PPC64_STVS(2)
|
||||||
|
PPC64_STVS(3)
|
||||||
|
PPC64_STVS(4)
|
||||||
|
PPC64_STVS(5)
|
||||||
|
PPC64_STVS(6)
|
||||||
|
PPC64_STVS(7)
|
||||||
|
PPC64_STVS(8)
|
||||||
|
PPC64_STVS(9)
|
||||||
|
PPC64_STVS(10)
|
||||||
|
PPC64_STVS(11)
|
||||||
|
PPC64_STVS(12)
|
||||||
|
PPC64_STVS(13)
|
||||||
|
PPC64_STVS(14)
|
||||||
|
PPC64_STVS(15)
|
||||||
|
PPC64_STVS(16)
|
||||||
|
PPC64_STVS(17)
|
||||||
|
PPC64_STVS(18)
|
||||||
|
PPC64_STVS(19)
|
||||||
|
PPC64_STVS(20)
|
||||||
|
PPC64_STVS(21)
|
||||||
|
PPC64_STVS(22)
|
||||||
|
PPC64_STVS(23)
|
||||||
|
PPC64_STVS(24)
|
||||||
|
PPC64_STVS(25)
|
||||||
|
PPC64_STVS(26)
|
||||||
|
PPC64_STVS(27)
|
||||||
|
PPC64_STVS(28)
|
||||||
|
PPC64_STVS(29)
|
||||||
|
PPC64_STVS(30)
|
||||||
|
PPC64_STVS(31)
|
||||||
|
PPC64_STVS(32)
|
||||||
|
PPC64_STVS(33)
|
||||||
|
PPC64_STVS(34)
|
||||||
|
PPC64_STVS(35)
|
||||||
|
PPC64_STVS(36)
|
||||||
|
PPC64_STVS(37)
|
||||||
|
PPC64_STVS(38)
|
||||||
|
PPC64_STVS(39)
|
||||||
|
PPC64_STVS(40)
|
||||||
|
PPC64_STVS(41)
|
||||||
|
PPC64_STVS(42)
|
||||||
|
PPC64_STVS(43)
|
||||||
|
PPC64_STVS(44)
|
||||||
|
PPC64_STVS(45)
|
||||||
|
PPC64_STVS(46)
|
||||||
|
PPC64_STVS(47)
|
||||||
|
PPC64_STVS(48)
|
||||||
|
PPC64_STVS(49)
|
||||||
|
PPC64_STVS(50)
|
||||||
|
PPC64_STVS(51)
|
||||||
|
PPC64_STVS(52)
|
||||||
|
PPC64_STVS(53)
|
||||||
|
PPC64_STVS(54)
|
||||||
|
PPC64_STVS(55)
|
||||||
|
PPC64_STVS(56)
|
||||||
|
PPC64_STVS(57)
|
||||||
|
PPC64_STVS(58)
|
||||||
|
PPC64_STVS(59)
|
||||||
|
PPC64_STVS(60)
|
||||||
|
PPC64_STVS(61)
|
||||||
|
PPC64_STVS(62)
|
||||||
|
PPC64_STVS(63)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// store FP register
|
||||||
|
#define PPC64_STF(n) \
|
||||||
|
stfd %f##n, (PPC64_OFFS_FP + n * 16)(%r3)
|
||||||
|
|
||||||
|
// save float registers
|
||||||
|
PPC64_STF(0)
|
||||||
|
PPC64_STF(1)
|
||||||
|
PPC64_STF(2)
|
||||||
|
PPC64_STF(3)
|
||||||
|
PPC64_STF(4)
|
||||||
|
PPC64_STF(5)
|
||||||
|
PPC64_STF(6)
|
||||||
|
PPC64_STF(7)
|
||||||
|
PPC64_STF(8)
|
||||||
|
PPC64_STF(9)
|
||||||
|
PPC64_STF(10)
|
||||||
|
PPC64_STF(11)
|
||||||
|
PPC64_STF(12)
|
||||||
|
PPC64_STF(13)
|
||||||
|
PPC64_STF(14)
|
||||||
|
PPC64_STF(15)
|
||||||
|
PPC64_STF(16)
|
||||||
|
PPC64_STF(17)
|
||||||
|
PPC64_STF(18)
|
||||||
|
PPC64_STF(19)
|
||||||
|
PPC64_STF(20)
|
||||||
|
PPC64_STF(21)
|
||||||
|
PPC64_STF(22)
|
||||||
|
PPC64_STF(23)
|
||||||
|
PPC64_STF(24)
|
||||||
|
PPC64_STF(25)
|
||||||
|
PPC64_STF(26)
|
||||||
|
PPC64_STF(27)
|
||||||
|
PPC64_STF(28)
|
||||||
|
PPC64_STF(29)
|
||||||
|
PPC64_STF(30)
|
||||||
|
PPC64_STF(31)
|
||||||
|
|
||||||
|
// save vector registers
|
||||||
|
|
||||||
|
// Use 16-bytes below the stack pointer as an
|
||||||
|
// aligned buffer to save each vector register.
|
||||||
|
// Note that the stack pointer is always 16-byte aligned.
|
||||||
|
subi %r4, %r1, 16
|
||||||
|
|
||||||
|
#define PPC64_STV_UNALIGNED(n) \
|
||||||
|
stvx %v##n, 0, %r4 ;\
|
||||||
|
ld %r5, 0(%r4) ;\
|
||||||
|
std %r5, (PPC64_OFFS_V + n * 16)(%r3) ;\
|
||||||
|
ld %r5, 8(%r4) ;\
|
||||||
|
std %r5, (PPC64_OFFS_V + n * 16 + 8)(%r3)
|
||||||
|
|
||||||
|
PPC64_STV_UNALIGNED(0)
|
||||||
|
PPC64_STV_UNALIGNED(1)
|
||||||
|
PPC64_STV_UNALIGNED(2)
|
||||||
|
PPC64_STV_UNALIGNED(3)
|
||||||
|
PPC64_STV_UNALIGNED(4)
|
||||||
|
PPC64_STV_UNALIGNED(5)
|
||||||
|
PPC64_STV_UNALIGNED(6)
|
||||||
|
PPC64_STV_UNALIGNED(7)
|
||||||
|
PPC64_STV_UNALIGNED(8)
|
||||||
|
PPC64_STV_UNALIGNED(9)
|
||||||
|
PPC64_STV_UNALIGNED(10)
|
||||||
|
PPC64_STV_UNALIGNED(11)
|
||||||
|
PPC64_STV_UNALIGNED(12)
|
||||||
|
PPC64_STV_UNALIGNED(13)
|
||||||
|
PPC64_STV_UNALIGNED(14)
|
||||||
|
PPC64_STV_UNALIGNED(15)
|
||||||
|
PPC64_STV_UNALIGNED(16)
|
||||||
|
PPC64_STV_UNALIGNED(17)
|
||||||
|
PPC64_STV_UNALIGNED(18)
|
||||||
|
PPC64_STV_UNALIGNED(19)
|
||||||
|
PPC64_STV_UNALIGNED(20)
|
||||||
|
PPC64_STV_UNALIGNED(21)
|
||||||
|
PPC64_STV_UNALIGNED(22)
|
||||||
|
PPC64_STV_UNALIGNED(23)
|
||||||
|
PPC64_STV_UNALIGNED(24)
|
||||||
|
PPC64_STV_UNALIGNED(25)
|
||||||
|
PPC64_STV_UNALIGNED(26)
|
||||||
|
PPC64_STV_UNALIGNED(27)
|
||||||
|
PPC64_STV_UNALIGNED(28)
|
||||||
|
PPC64_STV_UNALIGNED(29)
|
||||||
|
PPC64_STV_UNALIGNED(30)
|
||||||
|
PPC64_STV_UNALIGNED(31)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
li %r3, 0 // return UNW_ESUCCESS
|
||||||
|
blr
|
||||||
|
|
||||||
|
|
||||||
|
#elif defined(__ppc__)
|
||||||
|
|
||||||
|
//
|
||||||
|
// extern int unw_getcontext(unw_context_t* thread_state)
|
||||||
|
//
|
||||||
|
// On entry:
|
||||||
|
// thread_state pointer is in r3
|
||||||
|
//
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
stw %r0, 8(%r3)
|
||||||
|
mflr %r0
|
||||||
|
stw %r0, 0(%r3) // store lr as ssr0
|
||||||
|
stw %r1, 12(%r3)
|
||||||
|
stw %r2, 16(%r3)
|
||||||
|
stw %r3, 20(%r3)
|
||||||
|
stw %r4, 24(%r3)
|
||||||
|
stw %r5, 28(%r3)
|
||||||
|
stw %r6, 32(%r3)
|
||||||
|
stw %r7, 36(%r3)
|
||||||
|
stw %r8, 40(%r3)
|
||||||
|
stw %r9, 44(%r3)
|
||||||
|
stw %r10, 48(%r3)
|
||||||
|
stw %r11, 52(%r3)
|
||||||
|
stw %r12, 56(%r3)
|
||||||
|
stw %r13, 60(%r3)
|
||||||
|
stw %r14, 64(%r3)
|
||||||
|
stw %r15, 68(%r3)
|
||||||
|
stw %r16, 72(%r3)
|
||||||
|
stw %r17, 76(%r3)
|
||||||
|
stw %r18, 80(%r3)
|
||||||
|
stw %r19, 84(%r3)
|
||||||
|
stw %r20, 88(%r3)
|
||||||
|
stw %r21, 92(%r3)
|
||||||
|
stw %r22, 96(%r3)
|
||||||
|
stw %r23,100(%r3)
|
||||||
|
stw %r24,104(%r3)
|
||||||
|
stw %r25,108(%r3)
|
||||||
|
stw %r26,112(%r3)
|
||||||
|
stw %r27,116(%r3)
|
||||||
|
stw %r28,120(%r3)
|
||||||
|
stw %r29,124(%r3)
|
||||||
|
stw %r30,128(%r3)
|
||||||
|
stw %r31,132(%r3)
|
||||||
|
|
||||||
|
// save VRSave register
|
||||||
|
mfspr %r0, 256
|
||||||
|
stw %r0, 156(%r3)
|
||||||
|
// save CR registers
|
||||||
|
mfcr %r0
|
||||||
|
stw %r0, 136(%r3)
|
||||||
|
// save CTR register
|
||||||
|
mfctr %r0
|
||||||
|
stw %r0, 148(%r3)
|
||||||
|
|
||||||
|
// save float registers
|
||||||
|
stfd %f0, 160(%r3)
|
||||||
|
stfd %f1, 168(%r3)
|
||||||
|
stfd %f2, 176(%r3)
|
||||||
|
stfd %f3, 184(%r3)
|
||||||
|
stfd %f4, 192(%r3)
|
||||||
|
stfd %f5, 200(%r3)
|
||||||
|
stfd %f6, 208(%r3)
|
||||||
|
stfd %f7, 216(%r3)
|
||||||
|
stfd %f8, 224(%r3)
|
||||||
|
stfd %f9, 232(%r3)
|
||||||
|
stfd %f10,240(%r3)
|
||||||
|
stfd %f11,248(%r3)
|
||||||
|
stfd %f12,256(%r3)
|
||||||
|
stfd %f13,264(%r3)
|
||||||
|
stfd %f14,272(%r3)
|
||||||
|
stfd %f15,280(%r3)
|
||||||
|
stfd %f16,288(%r3)
|
||||||
|
stfd %f17,296(%r3)
|
||||||
|
stfd %f18,304(%r3)
|
||||||
|
stfd %f19,312(%r3)
|
||||||
|
stfd %f20,320(%r3)
|
||||||
|
stfd %f21,328(%r3)
|
||||||
|
stfd %f22,336(%r3)
|
||||||
|
stfd %f23,344(%r3)
|
||||||
|
stfd %f24,352(%r3)
|
||||||
|
stfd %f25,360(%r3)
|
||||||
|
stfd %f26,368(%r3)
|
||||||
|
stfd %f27,376(%r3)
|
||||||
|
stfd %f28,384(%r3)
|
||||||
|
stfd %f29,392(%r3)
|
||||||
|
stfd %f30,400(%r3)
|
||||||
|
stfd %f31,408(%r3)
|
||||||
|
|
||||||
|
|
||||||
|
// save vector registers
|
||||||
|
|
||||||
|
subi %r4, %r1, 16
|
||||||
|
rlwinm %r4, %r4, 0, 0, 27 // mask low 4-bits
|
||||||
|
// r4 is now a 16-byte aligned pointer into the red zone
|
||||||
|
|
||||||
|
#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \
|
||||||
|
stvx _vec, 0, %r4 SEPARATOR \
|
||||||
|
lwz %r5, 0(%r4) SEPARATOR \
|
||||||
|
stw %r5, _offset(%r3) SEPARATOR \
|
||||||
|
lwz %r5, 4(%r4) SEPARATOR \
|
||||||
|
stw %r5, _offset+4(%r3) SEPARATOR \
|
||||||
|
lwz %r5, 8(%r4) SEPARATOR \
|
||||||
|
stw %r5, _offset+8(%r3) SEPARATOR \
|
||||||
|
lwz %r5, 12(%r4) SEPARATOR \
|
||||||
|
stw %r5, _offset+12(%r3)
|
||||||
|
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v0, 424+0x000)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v1, 424+0x010)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v2, 424+0x020)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v3, 424+0x030)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v4, 424+0x040)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v5, 424+0x050)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v6, 424+0x060)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v7, 424+0x070)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v8, 424+0x080)
|
||||||
|
SAVE_VECTOR_UNALIGNED( %v9, 424+0x090)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v10, 424+0x0A0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v11, 424+0x0B0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v12, 424+0x0C0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v13, 424+0x0D0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v14, 424+0x0E0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v15, 424+0x0F0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v16, 424+0x100)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v17, 424+0x110)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v18, 424+0x120)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v19, 424+0x130)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v20, 424+0x140)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v21, 424+0x150)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v22, 424+0x160)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v23, 424+0x170)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v24, 424+0x180)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v25, 424+0x190)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v26, 424+0x1A0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v27, 424+0x1B0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v28, 424+0x1C0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v29, 424+0x1D0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v30, 424+0x1E0)
|
||||||
|
SAVE_VECTOR_UNALIGNED(%v31, 424+0x1F0)
|
||||||
|
|
||||||
|
li %r3, 0 // return UNW_ESUCCESS
|
||||||
|
blr
|
||||||
|
|
||||||
|
|
||||||
|
#elif defined(__arm64__) || defined(__aarch64__)
|
||||||
|
|
||||||
|
//
|
||||||
|
// extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
//
|
||||||
|
// On entry:
|
||||||
|
// thread_state pointer is in x0
|
||||||
|
//
|
||||||
|
.p2align 2
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
stp x0, x1, [x0, #0x000]
|
||||||
|
stp x2, x3, [x0, #0x010]
|
||||||
|
stp x4, x5, [x0, #0x020]
|
||||||
|
stp x6, x7, [x0, #0x030]
|
||||||
|
stp x8, x9, [x0, #0x040]
|
||||||
|
stp x10,x11, [x0, #0x050]
|
||||||
|
stp x12,x13, [x0, #0x060]
|
||||||
|
stp x14,x15, [x0, #0x070]
|
||||||
|
stp x16,x17, [x0, #0x080]
|
||||||
|
stp x18,x19, [x0, #0x090]
|
||||||
|
stp x20,x21, [x0, #0x0A0]
|
||||||
|
stp x22,x23, [x0, #0x0B0]
|
||||||
|
stp x24,x25, [x0, #0x0C0]
|
||||||
|
stp x26,x27, [x0, #0x0D0]
|
||||||
|
stp x28,x29, [x0, #0x0E0]
|
||||||
|
str x30, [x0, #0x0F0]
|
||||||
|
mov x1,sp
|
||||||
|
str x1, [x0, #0x0F8]
|
||||||
|
str x30, [x0, #0x100] // store return address as pc
|
||||||
|
// skip cpsr
|
||||||
|
stp d0, d1, [x0, #0x110]
|
||||||
|
stp d2, d3, [x0, #0x120]
|
||||||
|
stp d4, d5, [x0, #0x130]
|
||||||
|
stp d6, d7, [x0, #0x140]
|
||||||
|
stp d8, d9, [x0, #0x150]
|
||||||
|
stp d10,d11, [x0, #0x160]
|
||||||
|
stp d12,d13, [x0, #0x170]
|
||||||
|
stp d14,d15, [x0, #0x180]
|
||||||
|
stp d16,d17, [x0, #0x190]
|
||||||
|
stp d18,d19, [x0, #0x1A0]
|
||||||
|
stp d20,d21, [x0, #0x1B0]
|
||||||
|
stp d22,d23, [x0, #0x1C0]
|
||||||
|
stp d24,d25, [x0, #0x1D0]
|
||||||
|
stp d26,d27, [x0, #0x1E0]
|
||||||
|
stp d28,d29, [x0, #0x1F0]
|
||||||
|
str d30, [x0, #0x200]
|
||||||
|
str d31, [x0, #0x208]
|
||||||
|
mov x0, #0 // return UNW_ESUCCESS
|
||||||
|
ret
|
||||||
|
|
||||||
|
#elif defined(__arm__) && !defined(__APPLE__)
|
||||||
|
|
||||||
|
#if !defined(__ARM_ARCH_ISA_ARM)
|
||||||
|
#if (__ARM_ARCH_ISA_THUMB == 2)
|
||||||
|
.syntax unified
|
||||||
|
#endif
|
||||||
|
.thumb
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@
|
||||||
|
@ extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
@
|
||||||
|
@ On entry:
|
||||||
|
@ thread_state pointer is in r0
|
||||||
|
@
|
||||||
|
@ Per EHABI #4.7 this only saves the core integer registers.
|
||||||
|
@ EHABI #7.4.5 notes that in general all VRS registers should be restored
|
||||||
|
@ however this is very hard to do for VFP registers because it is unknown
|
||||||
|
@ to the library how many registers are implemented by the architecture.
|
||||||
|
@ Instead, VFP registers are demand saved by logic external to __unw_getcontext.
|
||||||
|
@
|
||||||
|
.p2align 2
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
#if !defined(__ARM_ARCH_ISA_ARM) && __ARM_ARCH_ISA_THUMB == 1
|
||||||
|
stm r0!, {r0-r7}
|
||||||
|
mov r1, r8
|
||||||
|
mov r2, r9
|
||||||
|
mov r3, r10
|
||||||
|
stm r0!, {r1-r3}
|
||||||
|
mov r1, r11
|
||||||
|
mov r2, sp
|
||||||
|
mov r3, lr
|
||||||
|
str r1, [r0, #0] @ r11
|
||||||
|
@ r12 does not need storing, it it the intra-procedure-call scratch register
|
||||||
|
str r2, [r0, #8] @ sp
|
||||||
|
str r3, [r0, #12] @ lr
|
||||||
|
str r3, [r0, #16] @ store return address as pc
|
||||||
|
@ T1 does not have a non-cpsr-clobbering register-zeroing instruction.
|
||||||
|
@ It is safe to use here though because we are about to return, and cpsr is
|
||||||
|
@ not expected to be preserved.
|
||||||
|
movs r0, #0 @ return UNW_ESUCCESS
|
||||||
|
#else
|
||||||
|
@ 32bit thumb-2 restrictions for stm:
|
||||||
|
@ . the sp (r13) cannot be in the list
|
||||||
|
@ . the pc (r15) cannot be in the list in an STM instruction
|
||||||
|
stm r0, {r0-r12}
|
||||||
|
str sp, [r0, #52]
|
||||||
|
str lr, [r0, #56]
|
||||||
|
str lr, [r0, #60] @ store return address as pc
|
||||||
|
mov r0, #0 @ return UNW_ESUCCESS
|
||||||
|
#endif
|
||||||
|
JMP(lr)
|
||||||
|
|
||||||
|
@
|
||||||
|
@ static void libunwind::Registers_arm::saveVFPWithFSTMD(unw_fpreg_t* values)
|
||||||
|
@
|
||||||
|
@ On entry:
|
||||||
|
@ values pointer is in r0
|
||||||
|
@
|
||||||
|
.p2align 2
|
||||||
|
#if defined(__ELF__)
|
||||||
|
.fpu vfpv3-d16
|
||||||
|
#endif
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMDEPv)
|
||||||
|
vstmia r0, {d0-d15}
|
||||||
|
JMP(lr)
|
||||||
|
|
||||||
|
@
|
||||||
|
@ static void libunwind::Registers_arm::saveVFPWithFSTMX(unw_fpreg_t* values)
|
||||||
|
@
|
||||||
|
@ On entry:
|
||||||
|
@ values pointer is in r0
|
||||||
|
@
|
||||||
|
.p2align 2
|
||||||
|
#if defined(__ELF__)
|
||||||
|
.fpu vfpv3-d16
|
||||||
|
#endif
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMXEPv)
|
||||||
|
vstmia r0, {d0-d15} @ fstmiax is deprecated in ARMv7+ and now behaves like vstmia
|
||||||
|
JMP(lr)
|
||||||
|
|
||||||
|
@
|
||||||
|
@ static void libunwind::Registers_arm::saveVFPv3(unw_fpreg_t* values)
|
||||||
|
@
|
||||||
|
@ On entry:
|
||||||
|
@ values pointer is in r0
|
||||||
|
@
|
||||||
|
.p2align 2
|
||||||
|
#if defined(__ELF__)
|
||||||
|
.fpu vfpv3
|
||||||
|
#endif
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm9saveVFPv3EPv)
|
||||||
|
@ VFP and iwMMX instructions are only available when compiling with the flags
|
||||||
|
@ that enable them. We do not want to do that in the library (because we do not
|
||||||
|
@ want the compiler to generate instructions that access those) but this is
|
||||||
|
@ only accessed if the personality routine needs these registers. Use of
|
||||||
|
@ these registers implies they are, actually, available on the target, so
|
||||||
|
@ it's ok to execute.
|
||||||
|
@ So, generate the instructions using the corresponding coprocessor mnemonic.
|
||||||
|
vstmia r0, {d16-d31}
|
||||||
|
JMP(lr)
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_WMMX)
|
||||||
|
|
||||||
|
@
|
||||||
|
@ static void libunwind::Registers_arm::saveiWMMX(unw_fpreg_t* values)
|
||||||
|
@
|
||||||
|
@ On entry:
|
||||||
|
@ values pointer is in r0
|
||||||
|
@
|
||||||
|
.p2align 2
|
||||||
|
#if defined(__ELF__)
|
||||||
|
.arch armv5te
|
||||||
|
#endif
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm9saveiWMMXEPv)
|
||||||
|
stcl p1, cr0, [r0], #8 @ wstrd wR0, [r0], #8
|
||||||
|
stcl p1, cr1, [r0], #8 @ wstrd wR1, [r0], #8
|
||||||
|
stcl p1, cr2, [r0], #8 @ wstrd wR2, [r0], #8
|
||||||
|
stcl p1, cr3, [r0], #8 @ wstrd wR3, [r0], #8
|
||||||
|
stcl p1, cr4, [r0], #8 @ wstrd wR4, [r0], #8
|
||||||
|
stcl p1, cr5, [r0], #8 @ wstrd wR5, [r0], #8
|
||||||
|
stcl p1, cr6, [r0], #8 @ wstrd wR6, [r0], #8
|
||||||
|
stcl p1, cr7, [r0], #8 @ wstrd wR7, [r0], #8
|
||||||
|
stcl p1, cr8, [r0], #8 @ wstrd wR8, [r0], #8
|
||||||
|
stcl p1, cr9, [r0], #8 @ wstrd wR9, [r0], #8
|
||||||
|
stcl p1, cr10, [r0], #8 @ wstrd wR10, [r0], #8
|
||||||
|
stcl p1, cr11, [r0], #8 @ wstrd wR11, [r0], #8
|
||||||
|
stcl p1, cr12, [r0], #8 @ wstrd wR12, [r0], #8
|
||||||
|
stcl p1, cr13, [r0], #8 @ wstrd wR13, [r0], #8
|
||||||
|
stcl p1, cr14, [r0], #8 @ wstrd wR14, [r0], #8
|
||||||
|
stcl p1, cr15, [r0], #8 @ wstrd wR15, [r0], #8
|
||||||
|
JMP(lr)
|
||||||
|
|
||||||
|
@
|
||||||
|
@ static void libunwind::Registers_arm::saveiWMMXControl(unw_uint32_t* values)
|
||||||
|
@
|
||||||
|
@ On entry:
|
||||||
|
@ values pointer is in r0
|
||||||
|
@
|
||||||
|
.p2align 2
|
||||||
|
#if defined(__ELF__)
|
||||||
|
.arch armv5te
|
||||||
|
#endif
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveiWMMXControlEPj)
|
||||||
|
stc2 p1, cr8, [r0], #4 @ wstrw wCGR0, [r0], #4
|
||||||
|
stc2 p1, cr9, [r0], #4 @ wstrw wCGR1, [r0], #4
|
||||||
|
stc2 p1, cr10, [r0], #4 @ wstrw wCGR2, [r0], #4
|
||||||
|
stc2 p1, cr11, [r0], #4 @ wstrw wCGR3, [r0], #4
|
||||||
|
JMP(lr)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(__or1k__)
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# On entry:
|
||||||
|
# thread_state pointer is in r3
|
||||||
|
#
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
l.sw 0(r3), r0
|
||||||
|
l.sw 4(r3), r1
|
||||||
|
l.sw 8(r3), r2
|
||||||
|
l.sw 12(r3), r3
|
||||||
|
l.sw 16(r3), r4
|
||||||
|
l.sw 20(r3), r5
|
||||||
|
l.sw 24(r3), r6
|
||||||
|
l.sw 28(r3), r7
|
||||||
|
l.sw 32(r3), r8
|
||||||
|
l.sw 36(r3), r9
|
||||||
|
l.sw 40(r3), r10
|
||||||
|
l.sw 44(r3), r11
|
||||||
|
l.sw 48(r3), r12
|
||||||
|
l.sw 52(r3), r13
|
||||||
|
l.sw 56(r3), r14
|
||||||
|
l.sw 60(r3), r15
|
||||||
|
l.sw 64(r3), r16
|
||||||
|
l.sw 68(r3), r17
|
||||||
|
l.sw 72(r3), r18
|
||||||
|
l.sw 76(r3), r19
|
||||||
|
l.sw 80(r3), r20
|
||||||
|
l.sw 84(r3), r21
|
||||||
|
l.sw 88(r3), r22
|
||||||
|
l.sw 92(r3), r23
|
||||||
|
l.sw 96(r3), r24
|
||||||
|
l.sw 100(r3), r25
|
||||||
|
l.sw 104(r3), r26
|
||||||
|
l.sw 108(r3), r27
|
||||||
|
l.sw 112(r3), r28
|
||||||
|
l.sw 116(r3), r29
|
||||||
|
l.sw 120(r3), r30
|
||||||
|
l.sw 124(r3), r31
|
||||||
|
# store ra to pc
|
||||||
|
l.sw 128(r3), r9
|
||||||
|
# zero epcr
|
||||||
|
l.sw 132(r3), r0
|
||||||
|
|
||||||
|
#elif defined(__sparc__)
|
||||||
|
|
||||||
|
#
|
||||||
|
# extern int __unw_getcontext(unw_context_t* thread_state)
|
||||||
|
#
|
||||||
|
# On entry:
|
||||||
|
# thread_state pointer is in o0
|
||||||
|
#
|
||||||
|
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
|
||||||
|
ta 3
|
||||||
|
add %o7, 8, %o7
|
||||||
|
std %g0, [%o0 + 0]
|
||||||
|
std %g2, [%o0 + 8]
|
||||||
|
std %g4, [%o0 + 16]
|
||||||
|
std %g6, [%o0 + 24]
|
||||||
|
std %o0, [%o0 + 32]
|
||||||
|
std %o2, [%o0 + 40]
|
||||||
|
std %o4, [%o0 + 48]
|
||||||
|
std %o6, [%o0 + 56]
|
||||||
|
std %l0, [%o0 + 64]
|
||||||
|
std %l2, [%o0 + 72]
|
||||||
|
std %l4, [%o0 + 80]
|
||||||
|
std %l6, [%o0 + 88]
|
||||||
|
std %i0, [%o0 + 96]
|
||||||
|
std %i2, [%o0 + 104]
|
||||||
|
std %i4, [%o0 + 112]
|
||||||
|
std %i6, [%o0 + 120]
|
||||||
|
jmp %o7
|
||||||
|
clr %o0 // return UNW_ESUCCESS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WEAK_ALIAS(__unw_getcontext, unw_getcontext)
|
||||||
|
|
||||||
|
#endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */
|
||||||
|
|
||||||
|
NO_EXEC_STACK_DIRECTIVE
|
|
@ -0,0 +1,183 @@
|
||||||
|
//===--------------------- Unwind_AppleExtras.cpp -------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "AddressSpace.hpp"
|
||||||
|
#include "DwarfParser.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
// private keymgr stuff
|
||||||
|
#define KEYMGR_GCC3_DW2_OBJ_LIST 302
|
||||||
|
extern "C" {
|
||||||
|
extern void _keymgr_set_and_unlock_processwide_ptr(int key, void *ptr);
|
||||||
|
extern void *_keymgr_get_and_lock_processwide_ptr(int key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// undocumented libgcc "struct object"
|
||||||
|
struct libgcc_object {
|
||||||
|
void *start;
|
||||||
|
void *unused1;
|
||||||
|
void *unused2;
|
||||||
|
void *fde;
|
||||||
|
unsigned long encoding;
|
||||||
|
void *fde_end;
|
||||||
|
libgcc_object *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// undocumented libgcc "struct km_object_info" referenced by
|
||||||
|
// KEYMGR_GCC3_DW2_OBJ_LIST
|
||||||
|
struct libgcc_object_info {
|
||||||
|
libgcc_object *seen_objects;
|
||||||
|
libgcc_object *unseen_objects;
|
||||||
|
unsigned spare[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// static linker symbols to prevent wrong two level namespace for _Unwind symbols
|
||||||
|
#if defined(__arm__)
|
||||||
|
#define NOT_HERE_BEFORE_5_0(sym) \
|
||||||
|
extern const char sym##_tmp30 __asm("$ld$hide$os3.0$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp30 = 0; \
|
||||||
|
extern const char sym##_tmp31 __asm("$ld$hide$os3.1$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp31 = 0; \
|
||||||
|
extern const char sym##_tmp32 __asm("$ld$hide$os3.2$_" #sym );\
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp32 = 0; \
|
||||||
|
extern const char sym##_tmp40 __asm("$ld$hide$os4.0$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp40 = 0; \
|
||||||
|
extern const char sym##_tmp41 __asm("$ld$hide$os4.1$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp41 = 0; \
|
||||||
|
extern const char sym##_tmp42 __asm("$ld$hide$os4.2$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp42 = 0; \
|
||||||
|
extern const char sym##_tmp43 __asm("$ld$hide$os4.3$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp43 = 0;
|
||||||
|
#elif defined(__arm64__)
|
||||||
|
#define NOT_HERE_BEFORE_10_6(sym)
|
||||||
|
#define NEVER_HERE(sym)
|
||||||
|
#else
|
||||||
|
#define NOT_HERE_BEFORE_10_6(sym) \
|
||||||
|
extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
|
||||||
|
extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp5 = 0;
|
||||||
|
#define NEVER_HERE(sym) \
|
||||||
|
extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
|
||||||
|
extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp5 = 0; \
|
||||||
|
extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \
|
||||||
|
__attribute__((visibility("default"))) const char sym##_tmp6 = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
||||||
|
|
||||||
|
//
|
||||||
|
// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in
|
||||||
|
// earlier versions
|
||||||
|
//
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_DeleteException)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetGR)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetIP)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_RaiseException)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_Resume)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_SetGR)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_SetIP)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_Backtrace)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetCFA)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow)
|
||||||
|
NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo)
|
||||||
|
NOT_HERE_BEFORE_10_6(__register_frame)
|
||||||
|
NOT_HERE_BEFORE_10_6(__deregister_frame)
|
||||||
|
|
||||||
|
//
|
||||||
|
// symbols in libSystem.dylib for compatibility, but we don't want any new code
|
||||||
|
// using them
|
||||||
|
//
|
||||||
|
NEVER_HERE(__register_frame_info_bases)
|
||||||
|
NEVER_HERE(__register_frame_info)
|
||||||
|
NEVER_HERE(__register_frame_info_table_bases)
|
||||||
|
NEVER_HERE(__register_frame_info_table)
|
||||||
|
NEVER_HERE(__register_frame_table)
|
||||||
|
NEVER_HERE(__deregister_frame_info)
|
||||||
|
NEVER_HERE(__deregister_frame_info_bases)
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
||||||
|
//
|
||||||
|
// symbols in libSystem.dylib in iOS 5.0 and later, but are in libgcc_s.dylib in
|
||||||
|
// earlier versions
|
||||||
|
//
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_GetLanguageSpecificData)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_GetRegionStart)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_GetIP)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SetGR)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SetIP)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_DeleteException)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Register)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_GetGR)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_GetIPInfo)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_GetCFA)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_RaiseException)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume_or_Rethrow)
|
||||||
|
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Unregister)
|
||||||
|
|
||||||
|
#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
||||||
|
|
||||||
|
|
||||||
|
namespace libunwind {
|
||||||
|
|
||||||
|
_LIBUNWIND_HIDDEN
|
||||||
|
bool checkKeyMgrRegisteredFDEs(uintptr_t pc, void *&fde) {
|
||||||
|
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||||
|
// lastly check for old style keymgr registration of dynamically generated
|
||||||
|
// FDEs acquire exclusive access to libgcc_object_info
|
||||||
|
libgcc_object_info *head = (libgcc_object_info *)
|
||||||
|
_keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
|
||||||
|
if (head != NULL) {
|
||||||
|
// look at each FDE in keymgr
|
||||||
|
for (libgcc_object *ob = head->unseen_objects; ob != NULL; ob = ob->next) {
|
||||||
|
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
|
||||||
|
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
|
||||||
|
const char *msg = CFI_Parser<LocalAddressSpace>::decodeFDE(
|
||||||
|
LocalAddressSpace::sThisAddressSpace,
|
||||||
|
(uintptr_t)ob->fde, &fdeInfo, &cieInfo);
|
||||||
|
if (msg == NULL) {
|
||||||
|
// Check if this FDE is for a function that includes the pc
|
||||||
|
if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) {
|
||||||
|
fde = (void*)fdeInfo.pcStart;
|
||||||
|
_keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST,
|
||||||
|
head);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// release libgcc_object_info
|
||||||
|
_keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head);
|
||||||
|
#else
|
||||||
|
(void)pc;
|
||||||
|
(void)fde;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/* ===-- assembly.h - libUnwind assembler support macros -------------------===
|
||||||
|
*
|
||||||
|
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
* See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
*
|
||||||
|
* ===----------------------------------------------------------------------===
|
||||||
|
*
|
||||||
|
* This file defines macros for use in libUnwind assembler source.
|
||||||
|
* This file is not part of the interface of this library.
|
||||||
|
*
|
||||||
|
* ===----------------------------------------------------------------------===
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UNWIND_ASSEMBLY_H
|
||||||
|
#define UNWIND_ASSEMBLY_H
|
||||||
|
|
||||||
|
#if defined(__powerpc64__)
|
||||||
|
#define SEPARATOR ;
|
||||||
|
#define PPC64_OFFS_SRR0 0
|
||||||
|
#define PPC64_OFFS_CR 272
|
||||||
|
#define PPC64_OFFS_XER 280
|
||||||
|
#define PPC64_OFFS_LR 288
|
||||||
|
#define PPC64_OFFS_CTR 296
|
||||||
|
#define PPC64_OFFS_VRSAVE 304
|
||||||
|
#define PPC64_OFFS_FP 312
|
||||||
|
#define PPC64_OFFS_V 824
|
||||||
|
#ifdef _ARCH_PWR8
|
||||||
|
#define PPC64_HAS_VMX
|
||||||
|
#endif
|
||||||
|
#elif defined(__arm64__)
|
||||||
|
#define SEPARATOR %%
|
||||||
|
#else
|
||||||
|
#define SEPARATOR ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1)
|
||||||
|
#define PPC64_OPD1 .section .opd,"aw",@progbits SEPARATOR
|
||||||
|
#define PPC64_OPD2 SEPARATOR \
|
||||||
|
.p2align 3 SEPARATOR \
|
||||||
|
.quad .Lfunc_begin0 SEPARATOR \
|
||||||
|
.quad .TOC.@tocbase SEPARATOR \
|
||||||
|
.quad 0 SEPARATOR \
|
||||||
|
.text SEPARATOR \
|
||||||
|
.Lfunc_begin0:
|
||||||
|
#else
|
||||||
|
#define PPC64_OPD1
|
||||||
|
#define PPC64_OPD2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GLUE2(a, b) a ## b
|
||||||
|
#define GLUE(a, b) GLUE2(a, b)
|
||||||
|
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
|
||||||
|
#define SYMBOL_IS_FUNC(name)
|
||||||
|
#define EXPORT_SYMBOL(name)
|
||||||
|
#define HIDDEN_SYMBOL(name) .private_extern name
|
||||||
|
#define WEAK_SYMBOL(name) .weak_reference name
|
||||||
|
#define WEAK_ALIAS(name, aliasname) \
|
||||||
|
.globl SYMBOL_NAME(aliasname) SEPARATOR \
|
||||||
|
WEAK_SYMBOL(aliasname) SEPARATOR \
|
||||||
|
SYMBOL_NAME(aliasname) = SYMBOL_NAME(name)
|
||||||
|
|
||||||
|
#define NO_EXEC_STACK_DIRECTIVE
|
||||||
|
|
||||||
|
#elif defined(__ELF__)
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
#define SYMBOL_IS_FUNC(name) .type name,%function
|
||||||
|
#else
|
||||||
|
#define SYMBOL_IS_FUNC(name) .type name,@function
|
||||||
|
#endif
|
||||||
|
#define EXPORT_SYMBOL(name)
|
||||||
|
#define HIDDEN_SYMBOL(name) .hidden name
|
||||||
|
#define WEAK_SYMBOL(name) .weak name
|
||||||
|
#define WEAK_ALIAS(name, aliasname) \
|
||||||
|
WEAK_SYMBOL(aliasname) SEPARATOR \
|
||||||
|
SYMBOL_NAME(aliasname) = SYMBOL_NAME(name)
|
||||||
|
|
||||||
|
#if defined(__GNU__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
|
||||||
|
defined(__linux__)
|
||||||
|
#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits
|
||||||
|
#else
|
||||||
|
#define NO_EXEC_STACK_DIRECTIVE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
#define SYMBOL_IS_FUNC(name) \
|
||||||
|
.def name SEPARATOR \
|
||||||
|
.scl 2 SEPARATOR \
|
||||||
|
.type 32 SEPARATOR \
|
||||||
|
.endef
|
||||||
|
#define EXPORT_SYMBOL2(name) \
|
||||||
|
.section .drectve,"yn" SEPARATOR \
|
||||||
|
.ascii "-export:", #name, "\0" SEPARATOR \
|
||||||
|
.text
|
||||||
|
#if defined(_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS)
|
||||||
|
#define EXPORT_SYMBOL(name)
|
||||||
|
#else
|
||||||
|
#define EXPORT_SYMBOL(name) EXPORT_SYMBOL2(name)
|
||||||
|
#endif
|
||||||
|
#define HIDDEN_SYMBOL(name)
|
||||||
|
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
#define WEAK_ALIAS(name, aliasname) \
|
||||||
|
.globl SYMBOL_NAME(aliasname) SEPARATOR \
|
||||||
|
EXPORT_SYMBOL(aliasname) SEPARATOR \
|
||||||
|
SYMBOL_NAME(aliasname) = SYMBOL_NAME(name)
|
||||||
|
#else
|
||||||
|
#define WEAK_ALIAS3(name, aliasname) \
|
||||||
|
.section .drectve,"yn" SEPARATOR \
|
||||||
|
.ascii "-alternatename:", #aliasname, "=", #name, "\0" SEPARATOR \
|
||||||
|
.text
|
||||||
|
#define WEAK_ALIAS2(name, aliasname) \
|
||||||
|
WEAK_ALIAS3(name, aliasname)
|
||||||
|
#define WEAK_ALIAS(name, aliasname) \
|
||||||
|
EXPORT_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \
|
||||||
|
WEAK_ALIAS2(SYMBOL_NAME(name), SYMBOL_NAME(aliasname))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NO_EXEC_STACK_DIRECTIVE
|
||||||
|
|
||||||
|
#elif defined(__sparc__)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error Unsupported target
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEFINE_LIBUNWIND_FUNCTION(name) \
|
||||||
|
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||||
|
HIDDEN_SYMBOL(SYMBOL_NAME(name)) SEPARATOR \
|
||||||
|
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||||
|
PPC64_OPD1 \
|
||||||
|
SYMBOL_NAME(name): \
|
||||||
|
PPC64_OPD2
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
#if !defined(__ARM_ARCH)
|
||||||
|
#define __ARM_ARCH 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5
|
||||||
|
#define ARM_HAS_BX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARM_HAS_BX
|
||||||
|
#define JMP(r) bx r
|
||||||
|
#else
|
||||||
|
#define JMP(r) mov pc, r
|
||||||
|
#endif
|
||||||
|
#endif /* __arm__ */
|
||||||
|
|
||||||
|
#endif /* UNWIND_ASSEMBLY_H */
|
|
@ -0,0 +1,211 @@
|
||||||
|
//===----------------------------- config.h -------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Defines macros used within libunwind project.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIBUNWIND_CONFIG_H
|
||||||
|
#define LIBUNWIND_CONFIG_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// Define static_assert() unless already defined by compiler.
|
||||||
|
#ifndef __has_feature
|
||||||
|
#define __has_feature(__x) 0
|
||||||
|
#endif
|
||||||
|
#if !(__has_feature(cxx_static_assert)) && !defined(static_assert)
|
||||||
|
#define static_assert(__b, __m) \
|
||||||
|
extern int compile_time_assert_failed[ ( __b ) ? 1 : -1 ] \
|
||||||
|
__attribute__( ( unused ) );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform specific configuration defines.
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#if defined(FOR_DYLD)
|
||||||
|
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND
|
||||||
|
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||||
|
#endif
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef __SEH__
|
||||||
|
#define _LIBUNWIND_SUPPORT_SEH_UNWIND 1
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if defined(__ARM_DWARF_EH__) || !defined(__arm__)
|
||||||
|
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||||
|
#define _LIBUNWIND_SUPPORT_DWARF_INDEX 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS)
|
||||||
|
#define _LIBUNWIND_EXPORT
|
||||||
|
#define _LIBUNWIND_HIDDEN
|
||||||
|
#else
|
||||||
|
#if !defined(__ELF__) && !defined(__MACH__)
|
||||||
|
#define _LIBUNWIND_EXPORT __declspec(dllexport)
|
||||||
|
#define _LIBUNWIND_HIDDEN
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
|
||||||
|
#define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STR(a) #a
|
||||||
|
#define XSTR(a) STR(a)
|
||||||
|
#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||||
|
__asm__(".globl " SYMBOL_NAME(aliasname)); \
|
||||||
|
__asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \
|
||||||
|
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
|
||||||
|
__attribute__((weak_import));
|
||||||
|
#elif defined(__ELF__)
|
||||||
|
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||||
|
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
|
||||||
|
__attribute__((weak, alias(#name)));
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||||
|
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
|
||||||
|
__attribute__((alias(#name)));
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||||
|
__pragma(comment(linker, "/alternatename:" SYMBOL_NAME(aliasname) "=" \
|
||||||
|
SYMBOL_NAME(name))) \
|
||||||
|
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error Unsupported target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__APPLE__) && defined(__arm__)) || defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
#define _LIBUNWIND_BUILD_SJLJ_APIS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__)
|
||||||
|
#define _LIBUNWIND_SUPPORT_FRAME_APIS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__) || \
|
||||||
|
defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__) || \
|
||||||
|
(!defined(__APPLE__) && defined(__arm__)) || \
|
||||||
|
(defined(__arm64__) || defined(__aarch64__)) || \
|
||||||
|
defined(__mips__)
|
||||||
|
#if !defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
||||||
|
#define _LIBUNWIND_BUILD_ZERO_COST_APIS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__powerpc64__) && defined(_ARCH_PWR8)
|
||||||
|
#define PPC64_HAS_VMX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||||
|
#define _LIBUNWIND_ABORT(msg) \
|
||||||
|
do { \
|
||||||
|
abort(); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_ABORT(msg) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "libunwind: %s %s:%d - %s\n", __func__, __FILE__, \
|
||||||
|
__LINE__, msg); \
|
||||||
|
fflush(stderr); \
|
||||||
|
abort(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||||
|
#define _LIBUNWIND_LOG0(msg)
|
||||||
|
#define _LIBUNWIND_LOG(msg, ...)
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_LOG0(msg) \
|
||||||
|
fprintf(stderr, "libunwind: " msg "\n")
|
||||||
|
#define _LIBUNWIND_LOG(msg, ...) \
|
||||||
|
fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(NDEBUG)
|
||||||
|
#define _LIBUNWIND_LOG_IF_FALSE(x) x
|
||||||
|
#else
|
||||||
|
#define _LIBUNWIND_LOG_IF_FALSE(x) \
|
||||||
|
do { \
|
||||||
|
bool _ret = x; \
|
||||||
|
if (!_ret) \
|
||||||
|
_LIBUNWIND_LOG("" #x " failed in %s", __FUNCTION__); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Macros that define away in non-Debug builds
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define _LIBUNWIND_DEBUG_LOG(msg, ...)
|
||||||
|
#define _LIBUNWIND_TRACE_API(msg, ...)
|
||||||
|
#define _LIBUNWIND_TRACING_UNWINDING (0)
|
||||||
|
#define _LIBUNWIND_TRACING_DWARF (0)
|
||||||
|
#define _LIBUNWIND_TRACE_UNWINDING(msg, ...)
|
||||||
|
#define _LIBUNWIND_TRACE_DWARF(...)
|
||||||
|
#else
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
extern bool logAPIs();
|
||||||
|
extern bool logUnwinding();
|
||||||
|
extern bool logDWARF();
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#define _LIBUNWIND_DEBUG_LOG(msg, ...) _LIBUNWIND_LOG(msg, __VA_ARGS__)
|
||||||
|
#define _LIBUNWIND_TRACE_API(msg, ...) \
|
||||||
|
do { \
|
||||||
|
if (logAPIs()) \
|
||||||
|
_LIBUNWIND_LOG(msg, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#define _LIBUNWIND_TRACING_UNWINDING logUnwinding()
|
||||||
|
#define _LIBUNWIND_TRACING_DWARF logDWARF()
|
||||||
|
#define _LIBUNWIND_TRACE_UNWINDING(msg, ...) \
|
||||||
|
do { \
|
||||||
|
if (logUnwinding()) \
|
||||||
|
_LIBUNWIND_LOG(msg, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#define _LIBUNWIND_TRACE_DWARF(...) \
|
||||||
|
do { \
|
||||||
|
if (logDWARF()) \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
// Used to fit UnwindCursor and Registers_xxx types against unw_context_t /
|
||||||
|
// unw_cursor_t sized memory blocks.
|
||||||
|
#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||||
|
# define COMP_OP ==
|
||||||
|
#else
|
||||||
|
# define COMP_OP <=
|
||||||
|
#endif
|
||||||
|
template <typename _Type, typename _Mem>
|
||||||
|
struct check_fit {
|
||||||
|
template <typename T>
|
||||||
|
struct blk_count {
|
||||||
|
static const size_t count =
|
||||||
|
(sizeof(T) + sizeof(uint64_t) - 1) / sizeof(uint64_t);
|
||||||
|
};
|
||||||
|
static const bool does_fit =
|
||||||
|
(blk_count<_Type>::count COMP_OP blk_count<_Mem>::count);
|
||||||
|
};
|
||||||
|
#undef COMP_OP
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // LIBUNWIND_CONFIG_H
|
|
@ -0,0 +1,239 @@
|
||||||
|
//===------------------------------- dwarf2.h -----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
These constants were taken from version 3 of the DWARF standard,
|
||||||
|
which is Copyright (c) 2005 Free Standards Group, and
|
||||||
|
Copyright (c) 1992, 1993 UNIX International, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DWARF2__
|
||||||
|
#define __DWARF2__
|
||||||
|
|
||||||
|
// DWARF unwind instructions
|
||||||
|
enum {
|
||||||
|
DW_CFA_nop = 0x0,
|
||||||
|
DW_CFA_set_loc = 0x1,
|
||||||
|
DW_CFA_advance_loc1 = 0x2,
|
||||||
|
DW_CFA_advance_loc2 = 0x3,
|
||||||
|
DW_CFA_advance_loc4 = 0x4,
|
||||||
|
DW_CFA_offset_extended = 0x5,
|
||||||
|
DW_CFA_restore_extended = 0x6,
|
||||||
|
DW_CFA_undefined = 0x7,
|
||||||
|
DW_CFA_same_value = 0x8,
|
||||||
|
DW_CFA_register = 0x9,
|
||||||
|
DW_CFA_remember_state = 0xA,
|
||||||
|
DW_CFA_restore_state = 0xB,
|
||||||
|
DW_CFA_def_cfa = 0xC,
|
||||||
|
DW_CFA_def_cfa_register = 0xD,
|
||||||
|
DW_CFA_def_cfa_offset = 0xE,
|
||||||
|
DW_CFA_def_cfa_expression = 0xF,
|
||||||
|
DW_CFA_expression = 0x10,
|
||||||
|
DW_CFA_offset_extended_sf = 0x11,
|
||||||
|
DW_CFA_def_cfa_sf = 0x12,
|
||||||
|
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||||
|
DW_CFA_val_offset = 0x14,
|
||||||
|
DW_CFA_val_offset_sf = 0x15,
|
||||||
|
DW_CFA_val_expression = 0x16,
|
||||||
|
DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
|
||||||
|
DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
|
||||||
|
DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
|
||||||
|
|
||||||
|
// GNU extensions
|
||||||
|
DW_CFA_GNU_window_save = 0x2D,
|
||||||
|
DW_CFA_GNU_args_size = 0x2E,
|
||||||
|
DW_CFA_GNU_negative_offset_extended = 0x2F,
|
||||||
|
|
||||||
|
// AARCH64 extensions
|
||||||
|
DW_CFA_AARCH64_negate_ra_state = 0x2D
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// FSF exception handling Pointer-Encoding constants
|
||||||
|
// Used in CFI augmentation by GCC
|
||||||
|
enum {
|
||||||
|
DW_EH_PE_ptr = 0x00,
|
||||||
|
DW_EH_PE_uleb128 = 0x01,
|
||||||
|
DW_EH_PE_udata2 = 0x02,
|
||||||
|
DW_EH_PE_udata4 = 0x03,
|
||||||
|
DW_EH_PE_udata8 = 0x04,
|
||||||
|
DW_EH_PE_signed = 0x08,
|
||||||
|
DW_EH_PE_sleb128 = 0x09,
|
||||||
|
DW_EH_PE_sdata2 = 0x0A,
|
||||||
|
DW_EH_PE_sdata4 = 0x0B,
|
||||||
|
DW_EH_PE_sdata8 = 0x0C,
|
||||||
|
DW_EH_PE_absptr = 0x00,
|
||||||
|
DW_EH_PE_pcrel = 0x10,
|
||||||
|
DW_EH_PE_textrel = 0x20,
|
||||||
|
DW_EH_PE_datarel = 0x30,
|
||||||
|
DW_EH_PE_funcrel = 0x40,
|
||||||
|
DW_EH_PE_aligned = 0x50,
|
||||||
|
DW_EH_PE_indirect = 0x80,
|
||||||
|
DW_EH_PE_omit = 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// DWARF expressions
|
||||||
|
enum {
|
||||||
|
DW_OP_addr = 0x03, // constant address (size target specific)
|
||||||
|
DW_OP_deref = 0x06,
|
||||||
|
DW_OP_const1u = 0x08, // 1-byte constant
|
||||||
|
DW_OP_const1s = 0x09, // 1-byte constant
|
||||||
|
DW_OP_const2u = 0x0A, // 2-byte constant
|
||||||
|
DW_OP_const2s = 0x0B, // 2-byte constant
|
||||||
|
DW_OP_const4u = 0x0C, // 4-byte constant
|
||||||
|
DW_OP_const4s = 0x0D, // 4-byte constant
|
||||||
|
DW_OP_const8u = 0x0E, // 8-byte constant
|
||||||
|
DW_OP_const8s = 0x0F, // 8-byte constant
|
||||||
|
DW_OP_constu = 0x10, // ULEB128 constant
|
||||||
|
DW_OP_consts = 0x11, // SLEB128 constant
|
||||||
|
DW_OP_dup = 0x12,
|
||||||
|
DW_OP_drop = 0x13,
|
||||||
|
DW_OP_over = 0x14,
|
||||||
|
DW_OP_pick = 0x15, // 1-byte stack index
|
||||||
|
DW_OP_swap = 0x16,
|
||||||
|
DW_OP_rot = 0x17,
|
||||||
|
DW_OP_xderef = 0x18,
|
||||||
|
DW_OP_abs = 0x19,
|
||||||
|
DW_OP_and = 0x1A,
|
||||||
|
DW_OP_div = 0x1B,
|
||||||
|
DW_OP_minus = 0x1C,
|
||||||
|
DW_OP_mod = 0x1D,
|
||||||
|
DW_OP_mul = 0x1E,
|
||||||
|
DW_OP_neg = 0x1F,
|
||||||
|
DW_OP_not = 0x20,
|
||||||
|
DW_OP_or = 0x21,
|
||||||
|
DW_OP_plus = 0x22,
|
||||||
|
DW_OP_plus_uconst = 0x23, // ULEB128 addend
|
||||||
|
DW_OP_shl = 0x24,
|
||||||
|
DW_OP_shr = 0x25,
|
||||||
|
DW_OP_shra = 0x26,
|
||||||
|
DW_OP_xor = 0x27,
|
||||||
|
DW_OP_skip = 0x2F, // signed 2-byte constant
|
||||||
|
DW_OP_bra = 0x28, // signed 2-byte constant
|
||||||
|
DW_OP_eq = 0x29,
|
||||||
|
DW_OP_ge = 0x2A,
|
||||||
|
DW_OP_gt = 0x2B,
|
||||||
|
DW_OP_le = 0x2C,
|
||||||
|
DW_OP_lt = 0x2D,
|
||||||
|
DW_OP_ne = 0x2E,
|
||||||
|
DW_OP_lit0 = 0x30, // Literal 0
|
||||||
|
DW_OP_lit1 = 0x31, // Literal 1
|
||||||
|
DW_OP_lit2 = 0x32, // Literal 2
|
||||||
|
DW_OP_lit3 = 0x33, // Literal 3
|
||||||
|
DW_OP_lit4 = 0x34, // Literal 4
|
||||||
|
DW_OP_lit5 = 0x35, // Literal 5
|
||||||
|
DW_OP_lit6 = 0x36, // Literal 6
|
||||||
|
DW_OP_lit7 = 0x37, // Literal 7
|
||||||
|
DW_OP_lit8 = 0x38, // Literal 8
|
||||||
|
DW_OP_lit9 = 0x39, // Literal 9
|
||||||
|
DW_OP_lit10 = 0x3A, // Literal 10
|
||||||
|
DW_OP_lit11 = 0x3B, // Literal 11
|
||||||
|
DW_OP_lit12 = 0x3C, // Literal 12
|
||||||
|
DW_OP_lit13 = 0x3D, // Literal 13
|
||||||
|
DW_OP_lit14 = 0x3E, // Literal 14
|
||||||
|
DW_OP_lit15 = 0x3F, // Literal 15
|
||||||
|
DW_OP_lit16 = 0x40, // Literal 16
|
||||||
|
DW_OP_lit17 = 0x41, // Literal 17
|
||||||
|
DW_OP_lit18 = 0x42, // Literal 18
|
||||||
|
DW_OP_lit19 = 0x43, // Literal 19
|
||||||
|
DW_OP_lit20 = 0x44, // Literal 20
|
||||||
|
DW_OP_lit21 = 0x45, // Literal 21
|
||||||
|
DW_OP_lit22 = 0x46, // Literal 22
|
||||||
|
DW_OP_lit23 = 0x47, // Literal 23
|
||||||
|
DW_OP_lit24 = 0x48, // Literal 24
|
||||||
|
DW_OP_lit25 = 0x49, // Literal 25
|
||||||
|
DW_OP_lit26 = 0x4A, // Literal 26
|
||||||
|
DW_OP_lit27 = 0x4B, // Literal 27
|
||||||
|
DW_OP_lit28 = 0x4C, // Literal 28
|
||||||
|
DW_OP_lit29 = 0x4D, // Literal 29
|
||||||
|
DW_OP_lit30 = 0x4E, // Literal 30
|
||||||
|
DW_OP_lit31 = 0x4F, // Literal 31
|
||||||
|
DW_OP_reg0 = 0x50, // Contents of reg0
|
||||||
|
DW_OP_reg1 = 0x51, // Contents of reg1
|
||||||
|
DW_OP_reg2 = 0x52, // Contents of reg2
|
||||||
|
DW_OP_reg3 = 0x53, // Contents of reg3
|
||||||
|
DW_OP_reg4 = 0x54, // Contents of reg4
|
||||||
|
DW_OP_reg5 = 0x55, // Contents of reg5
|
||||||
|
DW_OP_reg6 = 0x56, // Contents of reg6
|
||||||
|
DW_OP_reg7 = 0x57, // Contents of reg7
|
||||||
|
DW_OP_reg8 = 0x58, // Contents of reg8
|
||||||
|
DW_OP_reg9 = 0x59, // Contents of reg9
|
||||||
|
DW_OP_reg10 = 0x5A, // Contents of reg10
|
||||||
|
DW_OP_reg11 = 0x5B, // Contents of reg11
|
||||||
|
DW_OP_reg12 = 0x5C, // Contents of reg12
|
||||||
|
DW_OP_reg13 = 0x5D, // Contents of reg13
|
||||||
|
DW_OP_reg14 = 0x5E, // Contents of reg14
|
||||||
|
DW_OP_reg15 = 0x5F, // Contents of reg15
|
||||||
|
DW_OP_reg16 = 0x60, // Contents of reg16
|
||||||
|
DW_OP_reg17 = 0x61, // Contents of reg17
|
||||||
|
DW_OP_reg18 = 0x62, // Contents of reg18
|
||||||
|
DW_OP_reg19 = 0x63, // Contents of reg19
|
||||||
|
DW_OP_reg20 = 0x64, // Contents of reg20
|
||||||
|
DW_OP_reg21 = 0x65, // Contents of reg21
|
||||||
|
DW_OP_reg22 = 0x66, // Contents of reg22
|
||||||
|
DW_OP_reg23 = 0x67, // Contents of reg23
|
||||||
|
DW_OP_reg24 = 0x68, // Contents of reg24
|
||||||
|
DW_OP_reg25 = 0x69, // Contents of reg25
|
||||||
|
DW_OP_reg26 = 0x6A, // Contents of reg26
|
||||||
|
DW_OP_reg27 = 0x6B, // Contents of reg27
|
||||||
|
DW_OP_reg28 = 0x6C, // Contents of reg28
|
||||||
|
DW_OP_reg29 = 0x6D, // Contents of reg29
|
||||||
|
DW_OP_reg30 = 0x6E, // Contents of reg30
|
||||||
|
DW_OP_reg31 = 0x6F, // Contents of reg31
|
||||||
|
DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset
|
||||||
|
DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset
|
||||||
|
DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset
|
||||||
|
DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset
|
||||||
|
DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset
|
||||||
|
DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset
|
||||||
|
DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset
|
||||||
|
DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset
|
||||||
|
DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset
|
||||||
|
DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset
|
||||||
|
DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset
|
||||||
|
DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset
|
||||||
|
DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset
|
||||||
|
DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset
|
||||||
|
DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset
|
||||||
|
DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset
|
||||||
|
DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset
|
||||||
|
DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset
|
||||||
|
DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset
|
||||||
|
DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset
|
||||||
|
DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset
|
||||||
|
DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset
|
||||||
|
DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset
|
||||||
|
DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset
|
||||||
|
DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset
|
||||||
|
DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset
|
||||||
|
DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset
|
||||||
|
DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset
|
||||||
|
DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset
|
||||||
|
DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset
|
||||||
|
DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset
|
||||||
|
DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset
|
||||||
|
DW_OP_regx = 0x90, // ULEB128 register
|
||||||
|
DW_OP_fbreg = 0x91, // SLEB128 offset
|
||||||
|
DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset
|
||||||
|
DW_OP_piece = 0x93, // ULEB128 size of piece addressed
|
||||||
|
DW_OP_deref_size = 0x94, // 1-byte size of data retrieved
|
||||||
|
DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved
|
||||||
|
DW_OP_nop = 0x96,
|
||||||
|
DW_OP_push_object_addres = 0x97,
|
||||||
|
DW_OP_call2 = 0x98, // 2-byte offset of DIE
|
||||||
|
DW_OP_call4 = 0x99, // 4-byte offset of DIE
|
||||||
|
DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE
|
||||||
|
DW_OP_lo_user = 0xE0,
|
||||||
|
DW_OP_APPLE_uninit = 0xF0,
|
||||||
|
DW_OP_hi_user = 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,322 @@
|
||||||
|
//===--------------------------- libunwind.cpp ----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Implements unw_* functions from <libunwind.h>
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include <libunwind.h>
|
||||||
|
|
||||||
|
#include "libunwind_ext.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
#include "AddressSpace.hpp"
|
||||||
|
#include "UnwindCursor.hpp"
|
||||||
|
|
||||||
|
using namespace libunwind;
|
||||||
|
|
||||||
|
/// internal object to represent this processes address space
|
||||||
|
LocalAddressSpace LocalAddressSpace::sThisAddressSpace;
|
||||||
|
|
||||||
|
_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space =
|
||||||
|
(unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace;
|
||||||
|
|
||||||
|
/// Create a cursor of a thread in this process given 'context' recorded by
|
||||||
|
/// __unw_getcontext().
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor,
|
||||||
|
unw_context_t *context) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)",
|
||||||
|
static_cast<void *>(cursor),
|
||||||
|
static_cast<void *>(context));
|
||||||
|
#if defined(__i386__)
|
||||||
|
# define REGISTER_KIND Registers_x86
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
# define REGISTER_KIND Registers_x86_64
|
||||||
|
#elif defined(__powerpc64__)
|
||||||
|
# define REGISTER_KIND Registers_ppc64
|
||||||
|
#elif defined(__ppc__)
|
||||||
|
# define REGISTER_KIND Registers_ppc
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
# define REGISTER_KIND Registers_arm64
|
||||||
|
#elif defined(__arm__)
|
||||||
|
# define REGISTER_KIND Registers_arm
|
||||||
|
#elif defined(__or1k__)
|
||||||
|
# define REGISTER_KIND Registers_or1k
|
||||||
|
#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32
|
||||||
|
# define REGISTER_KIND Registers_mips_o32
|
||||||
|
#elif defined(__mips64)
|
||||||
|
# define REGISTER_KIND Registers_mips_newabi
|
||||||
|
#elif defined(__mips__)
|
||||||
|
# warning The MIPS architecture is not supported with this ABI and environment!
|
||||||
|
#elif defined(__sparc__)
|
||||||
|
# define REGISTER_KIND Registers_sparc
|
||||||
|
#else
|
||||||
|
# error Architecture not supported
|
||||||
|
#endif
|
||||||
|
// Use "placement new" to allocate UnwindCursor in the cursor buffer.
|
||||||
|
new (reinterpret_cast<UnwindCursor<LocalAddressSpace, REGISTER_KIND> *>(cursor))
|
||||||
|
UnwindCursor<LocalAddressSpace, REGISTER_KIND>(
|
||||||
|
context, LocalAddressSpace::sThisAddressSpace);
|
||||||
|
#undef REGISTER_KIND
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
co->setInfoBasedOnIPRegister();
|
||||||
|
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local)
|
||||||
|
|
||||||
|
/// Get value of specified register at cursor position in stack frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||||
|
unw_word_t *value) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_get_reg(cursor=%p, regNum=%d, &value=%p)",
|
||||||
|
static_cast<void *>(cursor), regNum,
|
||||||
|
static_cast<void *>(value));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
if (co->validReg(regNum)) {
|
||||||
|
*value = co->getReg(regNum);
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
}
|
||||||
|
return UNW_EBADREG;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg)
|
||||||
|
|
||||||
|
/// Set value of specified register at cursor position in stack frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||||
|
unw_word_t value) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_set_reg(cursor=%p, regNum=%d, value=0x%" PRIxPTR
|
||||||
|
")",
|
||||||
|
static_cast<void *>(cursor), regNum, value);
|
||||||
|
typedef LocalAddressSpace::pint_t pint_t;
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
if (co->validReg(regNum)) {
|
||||||
|
co->setReg(regNum, (pint_t)value);
|
||||||
|
// specical case altering IP to re-find info (being called by personality
|
||||||
|
// function)
|
||||||
|
if (regNum == UNW_REG_IP) {
|
||||||
|
unw_proc_info_t info;
|
||||||
|
// First, get the FDE for the old location and then update it.
|
||||||
|
co->getInfo(&info);
|
||||||
|
co->setInfoBasedOnIPRegister(false);
|
||||||
|
// If the original call expects stack adjustment, perform this now.
|
||||||
|
// Normal frame unwinding would have included the offset already in the
|
||||||
|
// CFA computation.
|
||||||
|
// Note: for PA-RISC and other platforms where the stack grows up,
|
||||||
|
// this should actually be - info.gp. LLVM doesn't currently support
|
||||||
|
// any such platforms and Clang doesn't export a macro for them.
|
||||||
|
if (info.gp)
|
||||||
|
co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp);
|
||||||
|
}
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
}
|
||||||
|
return UNW_EBADREG;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_set_reg, unw_set_reg)
|
||||||
|
|
||||||
|
/// Get value of specified float register at cursor position in stack frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||||
|
unw_fpreg_t *value) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)",
|
||||||
|
static_cast<void *>(cursor), regNum,
|
||||||
|
static_cast<void *>(value));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
if (co->validFloatReg(regNum)) {
|
||||||
|
*value = co->getFloatReg(regNum);
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
}
|
||||||
|
return UNW_EBADREG;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_get_fpreg, unw_get_fpreg)
|
||||||
|
|
||||||
|
/// Set value of specified float register at cursor position in stack frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||||
|
unw_fpreg_t value) {
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)",
|
||||||
|
static_cast<void *>(cursor), regNum, value);
|
||||||
|
#else
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)",
|
||||||
|
static_cast<void *>(cursor), regNum, value);
|
||||||
|
#endif
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
if (co->validFloatReg(regNum)) {
|
||||||
|
co->setFloatReg(regNum, value);
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
}
|
||||||
|
return UNW_EBADREG;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_set_fpreg, unw_set_fpreg)
|
||||||
|
|
||||||
|
/// Move cursor to next frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_step(cursor=%p)", static_cast<void *>(cursor));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
return co->step();
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step)
|
||||||
|
|
||||||
|
/// Get unwind info at cursor position in stack frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor,
|
||||||
|
unw_proc_info_t *info) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_get_proc_info(cursor=%p, &info=%p)",
|
||||||
|
static_cast<void *>(cursor), static_cast<void *>(info));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
co->getInfo(info);
|
||||||
|
if (info->end_ip == 0)
|
||||||
|
return UNW_ENOINFO;
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info)
|
||||||
|
|
||||||
|
/// Resume execution at cursor position (aka longjump).
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast<void *>(cursor));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
co->jumpto();
|
||||||
|
return UNW_EUNSPEC;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume)
|
||||||
|
|
||||||
|
/// Get name of function at cursor position in stack frame.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf,
|
||||||
|
size_t bufLen, unw_word_t *offset) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)",
|
||||||
|
static_cast<void *>(cursor), static_cast<void *>(buf),
|
||||||
|
static_cast<unsigned long>(bufLen));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
if (co->getFunctionName(buf, bufLen, offset))
|
||||||
|
return UNW_ESUCCESS;
|
||||||
|
return UNW_EUNSPEC;
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_name, unw_get_proc_name)
|
||||||
|
|
||||||
|
/// Checks if a register is a floating-point register.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_is_fpreg(unw_cursor_t *cursor,
|
||||||
|
unw_regnum_t regNum) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_is_fpreg(cursor=%p, regNum=%d)",
|
||||||
|
static_cast<void *>(cursor), regNum);
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
return co->validFloatReg(regNum);
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_is_fpreg, unw_is_fpreg)
|
||||||
|
|
||||||
|
/// Checks if a register is a floating-point register.
|
||||||
|
_LIBUNWIND_HIDDEN const char *__unw_regname(unw_cursor_t *cursor,
|
||||||
|
unw_regnum_t regNum) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_regname(cursor=%p, regNum=%d)",
|
||||||
|
static_cast<void *>(cursor), regNum);
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
return co->getRegisterName(regNum);
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_regname, unw_regname)
|
||||||
|
|
||||||
|
/// Checks if current frame is signal trampoline.
|
||||||
|
_LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_is_signal_frame(cursor=%p)",
|
||||||
|
static_cast<void *>(cursor));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
return co->isSignalFrame();
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame)
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD
|
||||||
|
_LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_get_fpreg_save_vfp_as_X(cursor=%p)",
|
||||||
|
static_cast<void *>(cursor));
|
||||||
|
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||||
|
return co->saveVFPAsX();
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_save_vfp_as_X, unw_save_vfp_as_X)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
/// SPI: walks cached DWARF entries
|
||||||
|
_LIBUNWIND_HIDDEN void __unw_iterate_dwarf_unwind_cache(void (*func)(
|
||||||
|
unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) {
|
||||||
|
_LIBUNWIND_TRACE_API("__unw_iterate_dwarf_unwind_cache(func=%p)",
|
||||||
|
reinterpret_cast<void *>(func));
|
||||||
|
DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);
|
||||||
|
}
|
||||||
|
_LIBUNWIND_WEAK_ALIAS(__unw_iterate_dwarf_unwind_cache,
|
||||||
|
unw_iterate_dwarf_unwind_cache)
|
||||||
|
|
||||||
|
/// IPI: for __register_frame()
|
||||||
|
void __unw_add_dynamic_fde(unw_word_t fde) {
|
||||||
|
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
|
||||||
|
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
|
||||||
|
const char *message = CFI_Parser<LocalAddressSpace>::decodeFDE(
|
||||||
|
LocalAddressSpace::sThisAddressSpace,
|
||||||
|
(LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo);
|
||||||
|
if (message == NULL) {
|
||||||
|
// dynamically registered FDEs don't have a mach_header group they are in.
|
||||||
|
// Use fde as mh_group
|
||||||
|
unw_word_t mh_group = fdeInfo.fdeStart;
|
||||||
|
DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,
|
||||||
|
fdeInfo.pcStart, fdeInfo.pcEnd,
|
||||||
|
fdeInfo.fdeStart);
|
||||||
|
} else {
|
||||||
|
_LIBUNWIND_DEBUG_LOG("__unw_add_dynamic_fde: bad fde: %s", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IPI: for __deregister_frame()
|
||||||
|
void __unw_remove_dynamic_fde(unw_word_t fde) {
|
||||||
|
// fde is own mh_group
|
||||||
|
DwarfFDECache<LocalAddressSpace>::removeAllIn((LocalAddressSpace::pint_t)fde);
|
||||||
|
}
|
||||||
|
#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||||
|
#endif // !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Add logging hooks in Debug builds only
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
_LIBUNWIND_HIDDEN
|
||||||
|
bool logAPIs() {
|
||||||
|
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||||
|
static bool checked = false;
|
||||||
|
static bool log = false;
|
||||||
|
if (!checked) {
|
||||||
|
log = (getenv("LIBUNWIND_PRINT_APIS") != NULL);
|
||||||
|
checked = true;
|
||||||
|
}
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_HIDDEN
|
||||||
|
bool logUnwinding() {
|
||||||
|
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||||
|
static bool checked = false;
|
||||||
|
static bool log = false;
|
||||||
|
if (!checked) {
|
||||||
|
log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL);
|
||||||
|
checked = true;
|
||||||
|
}
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LIBUNWIND_HIDDEN
|
||||||
|
bool logDWARF() {
|
||||||
|
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||||
|
static bool checked = false;
|
||||||
|
static bool log = false;
|
||||||
|
if (!checked) {
|
||||||
|
log = (getenv("LIBUNWIND_PRINT_DWARF") != NULL);
|
||||||
|
checked = true;
|
||||||
|
}
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
//===------------------------ libunwind_ext.h -----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Extensions to libunwind API.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef __LIBUNWIND_EXT__
|
||||||
|
#define __LIBUNWIND_EXT__
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <libunwind.h>
|
||||||
|
#include <unwind.h>
|
||||||
|
|
||||||
|
#define UNW_STEP_SUCCESS 1
|
||||||
|
#define UNW_STEP_END 0
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int __unw_getcontext(unw_context_t *);
|
||||||
|
extern int __unw_init_local(unw_cursor_t *, unw_context_t *);
|
||||||
|
extern int __unw_step(unw_cursor_t *);
|
||||||
|
extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *);
|
||||||
|
extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *);
|
||||||
|
extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t);
|
||||||
|
extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t);
|
||||||
|
extern int __unw_resume(unw_cursor_t *);
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
/* Save VFP registers in FSTMX format (instead of FSTMD). */
|
||||||
|
extern void __unw_save_vfp_as_X(unw_cursor_t *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const char *__unw_regname(unw_cursor_t *, unw_regnum_t);
|
||||||
|
extern int __unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *);
|
||||||
|
extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t);
|
||||||
|
extern int __unw_is_signal_frame(unw_cursor_t *);
|
||||||
|
extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *);
|
||||||
|
|
||||||
|
// SPI
|
||||||
|
extern void __unw_iterate_dwarf_unwind_cache(void (*func)(
|
||||||
|
unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh));
|
||||||
|
|
||||||
|
// IPI
|
||||||
|
extern void __unw_add_dynamic_fde(unw_word_t fde);
|
||||||
|
extern void __unw_remove_dynamic_fde(unw_word_t fde);
|
||||||
|
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*);
|
||||||
|
extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context,
|
||||||
|
const uint32_t *data,
|
||||||
|
size_t offset, size_t len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __LIBUNWIND_EXT__
|
|
@ -0,0 +1,35 @@
|
||||||
|
include(AddLLVM) # for add_lit_testsuite
|
||||||
|
macro(pythonize_bool var)
|
||||||
|
if (${var})
|
||||||
|
set(${var} True)
|
||||||
|
else()
|
||||||
|
set(${var} False)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
if (NOT DEFINED LIBCXX_ENABLE_SHARED)
|
||||||
|
set(LIBCXX_ENABLE_SHARED ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
pythonize_bool(LIBUNWIND_BUILD_32_BITS)
|
||||||
|
pythonize_bool(LIBCXX_ENABLE_SHARED)
|
||||||
|
pythonize_bool(LIBUNWIND_ENABLE_SHARED)
|
||||||
|
pythonize_bool(LIBUNWIND_ENABLE_THREADS)
|
||||||
|
pythonize_bool(LIBUNWIND_ENABLE_EXCEPTIONS)
|
||||||
|
pythonize_bool(LIBUNWIND_USE_COMPILER_RT)
|
||||||
|
pythonize_bool(LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY)
|
||||||
|
set(LIBUNWIND_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING
|
||||||
|
"TargetInfo to use when setting up test environment.")
|
||||||
|
set(LIBUNWIND_EXECUTOR "None" CACHE STRING
|
||||||
|
"Executor to use when running tests.")
|
||||||
|
|
||||||
|
set(AUTO_GEN_COMMENT "## Autogenerated by libunwind configuration.\n# Do not edit!")
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||||
|
@ONLY)
|
||||||
|
|
||||||
|
add_lit_testsuite(check-unwind "Running libunwind tests"
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
DEPENDS ${LIBUNWIND_TEST_DEPS}
|
||||||
|
)
|
|
@ -0,0 +1,28 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// The Itanium ABI requires that _Unwind_Exception objects are "double-word
|
||||||
|
// aligned".
|
||||||
|
|
||||||
|
#include <unwind.h>
|
||||||
|
|
||||||
|
// EHABI : 8-byte aligned
|
||||||
|
// itanium: largest supported alignment for the system
|
||||||
|
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||||
|
static_assert(alignof(_Unwind_Control_Block) == 8,
|
||||||
|
"_Unwind_Control_Block must be double-word aligned");
|
||||||
|
#else
|
||||||
|
struct MaxAligned {} __attribute__((__aligned__));
|
||||||
|
static_assert(alignof(_Unwind_Exception) == alignof(MaxAligned),
|
||||||
|
"_Unwind_Exception must be maximally aligned");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
#===----------------------------------------------------------------------===##
|
||||||
|
#
|
||||||
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
# See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
#
|
||||||
|
#===----------------------------------------------------------------------===##
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from libcxx.test.config import Configuration as LibcxxConfiguration
|
||||||
|
|
||||||
|
|
||||||
|
class Configuration(LibcxxConfiguration):
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
|
def __init__(self, lit_config, config):
|
||||||
|
super(Configuration, self).__init__(lit_config, config)
|
||||||
|
self.libunwind_src_root = None
|
||||||
|
self.libunwind_obj_root = None
|
||||||
|
self.abi_library_path = None
|
||||||
|
self.libcxx_src_root = None
|
||||||
|
|
||||||
|
def configure_src_root(self):
|
||||||
|
self.libunwind_src_root = (self.get_lit_conf('libunwind_src_root')
|
||||||
|
or os.path.dirname(self.config.test_source_root))
|
||||||
|
self.libcxx_src_root = (self.get_lit_conf('libcxx_src_root')
|
||||||
|
or os.path.join(self.libunwind_src_root, '..', 'libcxx'))
|
||||||
|
|
||||||
|
def configure_obj_root(self):
|
||||||
|
self.libunwind_obj_root = self.get_lit_conf('libunwind_obj_root')
|
||||||
|
super(Configuration, self).configure_obj_root()
|
||||||
|
|
||||||
|
def has_cpp_feature(self, feature, required_value):
|
||||||
|
return int(self.cxx.dumpMacros().get('__cpp_' + feature, 0)) >= required_value
|
||||||
|
|
||||||
|
def configure_features(self):
|
||||||
|
super(Configuration, self).configure_features()
|
||||||
|
if not self.get_lit_bool('enable_exceptions', True):
|
||||||
|
self.config.available_features.add('libcxxabi-no-exceptions')
|
||||||
|
|
||||||
|
def configure_compile_flags(self):
|
||||||
|
self.cxx.compile_flags += ['-DLIBUNWIND_NO_TIMER']
|
||||||
|
if not self.get_lit_bool('enable_exceptions', True):
|
||||||
|
self.cxx.compile_flags += ['-fno-exceptions', '-DLIBUNWIND_HAS_NO_EXCEPTIONS']
|
||||||
|
# Stack unwinding tests need unwinding tables and these are not
|
||||||
|
# generated by default on all Targets.
|
||||||
|
self.cxx.compile_flags += ['-funwind-tables']
|
||||||
|
if not self.get_lit_bool('enable_threads', True):
|
||||||
|
self.cxx.compile_flags += ['-D_LIBUNWIND_HAS_NO_THREADS']
|
||||||
|
self.config.available_features.add('libunwind-no-threads')
|
||||||
|
super(Configuration, self).configure_compile_flags()
|
||||||
|
|
||||||
|
def configure_compile_flags_header_includes(self):
|
||||||
|
self.configure_config_site_header()
|
||||||
|
|
||||||
|
libunwind_headers = self.get_lit_conf(
|
||||||
|
'libunwind_headers',
|
||||||
|
os.path.join(self.libunwind_src_root, 'include'))
|
||||||
|
if not os.path.isdir(libunwind_headers):
|
||||||
|
self.lit_config.fatal("libunwind_headers='%s' is not a directory."
|
||||||
|
% libunwind_headers)
|
||||||
|
self.cxx.compile_flags += ['-I' + libunwind_headers]
|
||||||
|
|
||||||
|
def configure_compile_flags_exceptions(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure_compile_flags_rtti(self):
|
||||||
|
pass
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include <libunwind.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void backtrace(int lower_bound) {
|
||||||
|
unw_context_t context;
|
||||||
|
unw_getcontext(&context);
|
||||||
|
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
unw_init_local(&cursor, &context);
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
do {
|
||||||
|
++n;
|
||||||
|
if (n > 100) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} while (unw_step(&cursor) > 0);
|
||||||
|
|
||||||
|
if (n < lower_bound) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test1(int i) {
|
||||||
|
backtrace(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test2(int i, int j) {
|
||||||
|
backtrace(i);
|
||||||
|
test1(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test3(int i, int j, int k) {
|
||||||
|
backtrace(i);
|
||||||
|
test2(j, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_no_info() {
|
||||||
|
unw_context_t context;
|
||||||
|
unw_getcontext(&context);
|
||||||
|
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
unw_init_local(&cursor, &context);
|
||||||
|
|
||||||
|
unw_proc_info_t info;
|
||||||
|
int ret = unw_get_proc_info(&cursor, &info);
|
||||||
|
if (ret != UNW_ESUCCESS)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
// Set the IP to an address clearly outside any function.
|
||||||
|
unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)0);
|
||||||
|
|
||||||
|
ret = unw_get_proc_info(&cursor, &info);
|
||||||
|
if (ret != UNW_ENOINFO)
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test1(1);
|
||||||
|
test2(1, 2);
|
||||||
|
test3(1, 2, 3);
|
||||||
|
test_no_info();
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unwind.h>
|
||||||
|
|
||||||
|
#define EXPECTED_NUM_FRAMES 50
|
||||||
|
#define NUM_FRAMES_UPPER_BOUND 100
|
||||||
|
|
||||||
|
_Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) {
|
||||||
|
(void)context;
|
||||||
|
int *i = (int *)cnt;
|
||||||
|
++*i;
|
||||||
|
if (*i > NUM_FRAMES_UPPER_BOUND) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_backtrace() {
|
||||||
|
int n = 0;
|
||||||
|
_Unwind_Backtrace(&callback, &n);
|
||||||
|
if (n < EXPECTED_NUM_FRAMES) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int test(int i) {
|
||||||
|
if (i == 0) {
|
||||||
|
test_backtrace();
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return i + test(i - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int total = test(50);
|
||||||
|
assert(total == 1275);
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79:
|
||||||
|
|
||||||
|
# Configuration file for the 'lit' test runner.
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import site
|
||||||
|
|
||||||
|
site.addsitedir(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
# Tell pylint that we know config and lit_config exist somewhere.
|
||||||
|
if 'PYLINT_IMPORT' in os.environ:
|
||||||
|
config = object()
|
||||||
|
lit_config = object()
|
||||||
|
|
||||||
|
# name: The name of this test suite.
|
||||||
|
config.name = 'libunwind'
|
||||||
|
|
||||||
|
# suffixes: A list of file extensions to treat as test files.
|
||||||
|
config.suffixes = ['.cpp', '.s']
|
||||||
|
|
||||||
|
# test_source_root: The root path where tests are located.
|
||||||
|
config.test_source_root = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
# needed to test libunwind with code that throws exceptions
|
||||||
|
config.enable_exceptions = True
|
||||||
|
|
||||||
|
# Infer the libcxx_test_source_root for configuration import.
|
||||||
|
# If libcxx_source_root isn't specified in the config, assume that the libcxx
|
||||||
|
# and libunwind source directories are sibling directories.
|
||||||
|
libcxx_src_root = getattr(config, 'libcxx_src_root', None)
|
||||||
|
if not libcxx_src_root:
|
||||||
|
libcxx_src_root = os.path.join(config.test_source_root, '../../libcxx')
|
||||||
|
libcxx_test_src_root = os.path.join(libcxx_src_root, 'utils')
|
||||||
|
if os.path.isfile(os.path.join(libcxx_test_src_root, 'libcxx', '__init__.py')):
|
||||||
|
site.addsitedir(libcxx_test_src_root)
|
||||||
|
else:
|
||||||
|
lit_config.fatal('Could not find libcxx test directory for test imports'
|
||||||
|
' in: %s' % libcxx_test_src_root)
|
||||||
|
|
||||||
|
# Infer the test_exec_root from the libcxx_object root.
|
||||||
|
obj_root = getattr(config, 'libunwind_obj_root', None)
|
||||||
|
|
||||||
|
# Check that the test exec root is known.
|
||||||
|
if obj_root is None:
|
||||||
|
import libcxx.test.config
|
||||||
|
libcxx.test.config.loadSiteConfig(
|
||||||
|
lit_config, config, 'libunwind_site_config', 'LIBUNWIND_SITE_CONFIG')
|
||||||
|
obj_root = getattr(config, 'libunwind_obj_root', None)
|
||||||
|
if obj_root is None:
|
||||||
|
import tempfile
|
||||||
|
obj_root = tempfile.mkdtemp(prefix='libunwind-testsuite-')
|
||||||
|
lit_config.warning('Creating temporary directory for object root: %s' %
|
||||||
|
obj_root)
|
||||||
|
|
||||||
|
config.test_exec_root = os.path.join(obj_root, 'test')
|
||||||
|
|
||||||
|
cfg_variant = getattr(config, 'configuration_variant', 'libunwind')
|
||||||
|
if cfg_variant:
|
||||||
|
lit_config.note('Using configuration variant: %s' % cfg_variant)
|
||||||
|
|
||||||
|
# Load the Configuration class from the module name <cfg_variant>.test.config.
|
||||||
|
config_module_name = '.'.join([cfg_variant, 'test', 'config'])
|
||||||
|
config_module = __import__(config_module_name, fromlist=['Configuration'])
|
||||||
|
|
||||||
|
configuration = config_module.Configuration(lit_config, config)
|
||||||
|
configuration.configure()
|
||||||
|
configuration.print_config_info()
|
||||||
|
config.test_format = configuration.get_test_format()
|
|
@ -0,0 +1,30 @@
|
||||||
|
@AUTO_GEN_COMMENT@
|
||||||
|
config.cxx_under_test = "@LIBUNWIND_COMPILER@"
|
||||||
|
config.project_obj_root = "@CMAKE_BINARY_DIR@"
|
||||||
|
config.libunwind_src_root = "@LIBUNWIND_SOURCE_DIR@"
|
||||||
|
config.libunwind_obj_root = "@LIBUNWIND_BINARY_DIR@"
|
||||||
|
config.abi_library_path = "@LIBUNWIND_LIBRARY_DIR@"
|
||||||
|
config.libcxx_src_root = "@LIBUNWIND_LIBCXX_PATH@"
|
||||||
|
config.libunwind_headers = "@LIBUNWIND_SOURCE_DIR@/include"
|
||||||
|
config.cxx_library_root = "@LIBUNWIND_LIBCXX_LIBRARY_PATH@"
|
||||||
|
config.llvm_unwinder = True
|
||||||
|
config.builtins_library = "@LIBUNWIND_BUILTINS_LIBRARY@"
|
||||||
|
config.enable_threads = @LIBUNWIND_ENABLE_THREADS@
|
||||||
|
config.use_sanitizer = "@LLVM_USE_SANITIZER@"
|
||||||
|
config.enable_32bit = @LIBUNWIND_BUILD_32_BITS@
|
||||||
|
config.target_info = "@LIBUNWIND_TARGET_INFO@"
|
||||||
|
config.test_linker_flags = "@LIBUNWIND_TEST_LINKER_FLAGS@"
|
||||||
|
config.test_compiler_flags = "@LIBUNWIND_TEST_COMPILER_FLAGS@"
|
||||||
|
config.executor = "@LIBUNWIND_EXECUTOR@"
|
||||||
|
config.libunwind_shared = @LIBUNWIND_ENABLE_SHARED@
|
||||||
|
config.enable_shared = @LIBCXX_ENABLE_SHARED@
|
||||||
|
config.enable_exceptions = @LIBUNWIND_ENABLE_EXCEPTIONS@
|
||||||
|
config.host_triple = "@LLVM_HOST_TRIPLE@"
|
||||||
|
config.target_triple = "@TARGET_TRIPLE@"
|
||||||
|
config.use_target = bool("@LIBUNWIND_TARGET_TRIPLE@")
|
||||||
|
config.sysroot = "@LIBUNWIND_SYSROOT@"
|
||||||
|
config.gcc_toolchain = "@LIBUNWIND_GCC_TOOLCHAIN@"
|
||||||
|
config.cxx_ext_threads = @LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY@
|
||||||
|
|
||||||
|
# Let the main config do the real work.
|
||||||
|
lit_config.load_config(config, "@LIBUNWIND_SOURCE_DIR@/test/lit.cfg")
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libunwind.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
unw_context_t context;
|
||||||
|
int ret = unw_getcontext(&context);
|
||||||
|
assert(ret == UNW_ESUCCESS);
|
||||||
|
}
|
Loading…
Reference in New Issue