[Windows] Enable crash reporter on MinGW builds, use libbacktrace to generate stack trace from DWARF symbols.
This commit is contained in:
parent
7529c0bec5
commit
c1b34672f9
|
@ -293,6 +293,11 @@ Comment: jpeg-compressor
|
|||
Copyright: 2012, Rich Geldreich
|
||||
License: public-domain or Apache-2.0
|
||||
|
||||
Files: ./thirdparty/libbacktrace/
|
||||
Comment: libbacktrace
|
||||
Copyright: 2012-2021, Free Software Foundation, Inc.
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: ./thirdparty/libktx/
|
||||
Comment: KTX
|
||||
Copyright: 2013-2020, Mark Callow
|
||||
|
|
|
@ -14,6 +14,8 @@ SConscript("coreaudio/SCsub")
|
|||
SConscript("pulseaudio/SCsub")
|
||||
if env["platform"] == "windows":
|
||||
SConscript("wasapi/SCsub")
|
||||
if not env.msvc:
|
||||
SConscript("backtrace/SCsub")
|
||||
if env["xaudio2"]:
|
||||
SConscript("xaudio2/SCsub")
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env_backtrace = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
thirdparty_dir = "#thirdparty/libbacktrace/"
|
||||
thirdparty_sources = [
|
||||
"atomic.c",
|
||||
"dwarf.c",
|
||||
"fileline.c",
|
||||
"posix.c",
|
||||
"print.c",
|
||||
"sort.c",
|
||||
"state.c",
|
||||
"backtrace.c",
|
||||
"simple.c",
|
||||
"pecoff.c",
|
||||
"read.c",
|
||||
"alloc.c",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_backtrace.Prepend(CPPPATH=[thirdparty_dir])
|
||||
|
||||
env_thirdparty = env_backtrace.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
|
||||
env.drivers_sources += thirdparty_obj
|
||||
|
||||
# Godot source files
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_backtrace.add_source_files(driver_obj, "*.cpp")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
# Needed to force rebuilding the driver files when the thirdparty library is updated.
|
||||
env.Depends(driver_obj, thirdparty_obj)
|
|
@ -10,7 +10,6 @@ sources = []
|
|||
|
||||
common_win = [
|
||||
"godot_windows.cpp",
|
||||
"crash_handler_windows.cpp",
|
||||
"os_windows.cpp",
|
||||
"display_server_windows.cpp",
|
||||
"key_mapping_windows.cpp",
|
||||
|
@ -25,6 +24,11 @@ common_win = [
|
|||
"rendering_context_driver_vulkan_windows.cpp",
|
||||
]
|
||||
|
||||
if env.msvc:
|
||||
common_win += ["crash_handler_windows_seh.cpp"]
|
||||
else:
|
||||
common_win += ["crash_handler_windows_signal.cpp"]
|
||||
|
||||
common_win_wrap = [
|
||||
"console_wrapper_windows.cpp",
|
||||
]
|
||||
|
|
|
@ -35,12 +35,15 @@
|
|||
#include <windows.h>
|
||||
|
||||
// Crash handler exception only enabled with MSVC
|
||||
#if defined(DEBUG_ENABLED) && defined(_MSC_VER)
|
||||
#if defined(DEBUG_ENABLED)
|
||||
#define CRASH_HANDLER_EXCEPTION 1
|
||||
|
||||
#ifdef _MSC_VER
|
||||
extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
class CrashHandler {
|
||||
bool disabled;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**************************************************************************/
|
||||
/* crash_handler_windows.cpp */
|
||||
/* crash_handler_windows_seh.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
|
@ -0,0 +1,205 @@
|
|||
/**************************************************************************/
|
||||
/* crash_handler_windows_signal.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "crash_handler_windows.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/version.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#ifdef CRASH_HANDLER_EXCEPTION
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <signal.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <psapi.h>
|
||||
|
||||
#include "thirdparty/libbacktrace/backtrace.h"
|
||||
|
||||
struct CrashHandlerData {
|
||||
int64_t index = 0;
|
||||
backtrace_state *state = nullptr;
|
||||
int64_t offset = 0;
|
||||
};
|
||||
|
||||
int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
|
||||
CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
|
||||
if (!function) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char fname[1024];
|
||||
snprintf(fname, 1024, "%s", function);
|
||||
|
||||
if (function[0] == '_') {
|
||||
int status;
|
||||
char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);
|
||||
|
||||
if (status == 0 && demangled) {
|
||||
snprintf(fname, 1024, "%s", demangled);
|
||||
}
|
||||
|
||||
if (demangled) {
|
||||
free(demangled);
|
||||
}
|
||||
}
|
||||
|
||||
print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void error_callback(void *data, const char *msg, int errnum) {
|
||||
CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
|
||||
if (ch_data->index == 0) {
|
||||
print_error(vformat("Error(%d): %s", errnum, String::utf8(msg)));
|
||||
} else {
|
||||
print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg)));
|
||||
}
|
||||
}
|
||||
|
||||
int trace_callback(void *data, uintptr_t pc) {
|
||||
CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
|
||||
backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t get_image_base(const String &p_path) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
f->seek(0x3c);
|
||||
uint32_t pe_pos = f->get_32();
|
||||
|
||||
f->seek(pe_pos);
|
||||
uint32_t magic = f->get_32();
|
||||
if (magic != 0x00004550) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int64_t opt_header_pos = f->get_position() + 0x14;
|
||||
f->seek(opt_header_pos);
|
||||
|
||||
uint16_t opt_header_magic = f->get_16();
|
||||
if (opt_header_magic == 0x10B) {
|
||||
f->seek(opt_header_pos + 0x1C);
|
||||
return f->get_32();
|
||||
} else if (opt_header_magic == 0x20B) {
|
||||
f->seek(opt_header_pos + 0x18);
|
||||
return f->get_64();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern void CrashHandlerException(int signal) {
|
||||
CrashHandlerData data;
|
||||
|
||||
if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String msg;
|
||||
const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
|
||||
if (proj_settings) {
|
||||
msg = proj_settings->get("debug/settings/crash_handler/message");
|
||||
}
|
||||
|
||||
// Tell MainLoop about the crash. This can be handled by users too in Node.
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
|
||||
}
|
||||
|
||||
print_error("\n================================================================");
|
||||
print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, signal));
|
||||
|
||||
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
|
||||
if (String(VERSION_HASH).is_empty()) {
|
||||
print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
|
||||
} else {
|
||||
print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH));
|
||||
}
|
||||
print_error(vformat("Dumping the backtrace. %s", msg));
|
||||
|
||||
String _execpath = OS::get_singleton()->get_executable_path();
|
||||
|
||||
// Load process and image info to determine ASLR addresses offset.
|
||||
MODULEINFO mi;
|
||||
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi));
|
||||
int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll);
|
||||
int64_t image_file_base = get_image_base(_execpath);
|
||||
data.offset = image_mem_base - image_file_base;
|
||||
|
||||
data.state = backtrace_create_state(_execpath.utf8().get_data(), 0, &error_callback, reinterpret_cast<void *>(&data));
|
||||
if (data.state != nullptr) {
|
||||
data.index = 1;
|
||||
backtrace_simple(data.state, 1, &trace_callback, &error_callback, reinterpret_cast<void *>(&data));
|
||||
}
|
||||
|
||||
print_error("-- END OF BACKTRACE --");
|
||||
print_error("================================================================");
|
||||
}
|
||||
#endif
|
||||
|
||||
CrashHandler::CrashHandler() {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
CrashHandler::~CrashHandler() {
|
||||
}
|
||||
|
||||
void CrashHandler::disable() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CRASH_HANDLER_EXCEPTION)
|
||||
signal(SIGSEGV, nullptr);
|
||||
signal(SIGFPE, nullptr);
|
||||
signal(SIGILL, nullptr);
|
||||
#endif
|
||||
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
void CrashHandler::initialize() {
|
||||
#if defined(CRASH_HANDLER_EXCEPTION)
|
||||
signal(SIGSEGV, CrashHandlerException);
|
||||
signal(SIGFPE, CrashHandlerException);
|
||||
signal(SIGILL, CrashHandlerException);
|
||||
#endif
|
||||
}
|
|
@ -557,6 +557,9 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
|
|||
LIBS += ["dxgi", "d3d9", "d3d11"]
|
||||
env.Prepend(CPPPATH=["#thirdparty/angle/include"])
|
||||
|
||||
if env["target"] in ["editor", "template_debug"]:
|
||||
LIBS += ["psapi", "dbghelp"]
|
||||
|
||||
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
|
||||
|
||||
if vcvars_msvc_config:
|
||||
|
|
|
@ -215,7 +215,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
// _argc and _argv are ignored
|
||||
// we are going to use the WideChar version of them instead
|
||||
#ifdef CRASH_HANDLER_EXCEPTION
|
||||
#if defined(CRASH_HANDLER_EXCEPTION) && defined(_MSC_VER)
|
||||
__try {
|
||||
return _main();
|
||||
} __except (CrashHandlerException(GetExceptionInformation())) {
|
||||
|
|
|
@ -428,6 +428,18 @@ Files extracted from upstream source:
|
|||
- `jpge*.{c,h}`
|
||||
|
||||
|
||||
## libbacktrace
|
||||
|
||||
- Upstream: https://github.com/ianlancetaylor/libbacktrace
|
||||
- Version: git (4d2dd0b172f2c9192f83ba93425f868f2a13c553, 2022)
|
||||
- License: BSD-3-Clause
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
||||
- `*.{c,h}` files for Windows platform
|
||||
- `LICENSE`
|
||||
|
||||
|
||||
## libktx
|
||||
|
||||
- Upstream: https://github.com/KhronosGroup/KTX-Software
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Copyright (C) 2012-2016 Free Software Foundation, Inc.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
|
||||
# (1) Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
|
||||
# (2) Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
|
||||
# (3) The name of the author may not be used to
|
||||
# endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,167 @@
|
|||
/* alloc.c -- Memory allocation without mmap.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* Allocation routines to use on systems that do not support anonymous
|
||||
mmap. This implementation just uses malloc, which means that the
|
||||
backtrace functions may not be safely invoked from a signal
|
||||
handler. */
|
||||
|
||||
/* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't
|
||||
report an error. */
|
||||
|
||||
void *
|
||||
backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
size_t size, backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = malloc (size);
|
||||
if (ret == NULL)
|
||||
{
|
||||
if (error_callback)
|
||||
error_callback (data, "malloc", errno);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Free memory. */
|
||||
|
||||
void
|
||||
backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
void *p, size_t size ATTRIBUTE_UNUSED,
|
||||
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
|
||||
void *data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
free (p);
|
||||
}
|
||||
|
||||
/* Grow VEC by SIZE bytes. */
|
||||
|
||||
void *
|
||||
backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
size_t size, backtrace_error_callback error_callback,
|
||||
void *data, struct backtrace_vector *vec)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (size > vec->alc)
|
||||
{
|
||||
size_t alc;
|
||||
void *base;
|
||||
|
||||
if (vec->size == 0)
|
||||
alc = 32 * size;
|
||||
else if (vec->size >= 4096)
|
||||
alc = vec->size + 4096;
|
||||
else
|
||||
alc = 2 * vec->size;
|
||||
|
||||
if (alc < vec->size + size)
|
||||
alc = vec->size + size;
|
||||
|
||||
base = realloc (vec->base, alc);
|
||||
if (base == NULL)
|
||||
{
|
||||
error_callback (data, "realloc", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vec->base = base;
|
||||
vec->alc = alc - vec->size;
|
||||
}
|
||||
|
||||
ret = (char *) vec->base + vec->size;
|
||||
vec->size += size;
|
||||
vec->alc -= size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Finish the current allocation on VEC. */
|
||||
|
||||
void *
|
||||
backtrace_vector_finish (struct backtrace_state *state,
|
||||
struct backtrace_vector *vec,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
/* With this allocator we call realloc in backtrace_vector_grow,
|
||||
which means we can't easily reuse the memory here. So just
|
||||
release it. */
|
||||
if (!backtrace_vector_release (state, vec, error_callback, data))
|
||||
return NULL;
|
||||
ret = vec->base;
|
||||
vec->base = NULL;
|
||||
vec->size = 0;
|
||||
vec->alc = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Release any extra space allocated for VEC. */
|
||||
|
||||
int
|
||||
backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
struct backtrace_vector *vec,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
vec->alc = 0;
|
||||
|
||||
if (vec->size == 0)
|
||||
{
|
||||
/* As of C17, realloc with size 0 is marked as an obsolescent feature, use
|
||||
free instead. */
|
||||
free (vec->base);
|
||||
vec->base = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
vec->base = realloc (vec->base, vec->size);
|
||||
if (vec->base == NULL)
|
||||
{
|
||||
error_callback (data, "realloc", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/* atomic.c -- Support for atomic functions if not present.
|
||||
Copyright (C) 2013-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "backtrace-supported.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* This file holds implementations of the atomic functions that are
|
||||
used if the host compiler has the sync functions but not the atomic
|
||||
functions, as is true of versions of GCC before 4.7. */
|
||||
|
||||
#if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS)
|
||||
|
||||
/* Do an atomic load of a pointer. */
|
||||
|
||||
void *
|
||||
backtrace_atomic_load_pointer (void *arg)
|
||||
{
|
||||
void **pp;
|
||||
void *p;
|
||||
|
||||
pp = (void **) arg;
|
||||
p = *pp;
|
||||
while (!__sync_bool_compare_and_swap (pp, p, p))
|
||||
p = *pp;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Do an atomic load of an int. */
|
||||
|
||||
int
|
||||
backtrace_atomic_load_int (int *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = *p;
|
||||
while (!__sync_bool_compare_and_swap (p, i, i))
|
||||
i = *p;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Do an atomic store of a pointer. */
|
||||
|
||||
void
|
||||
backtrace_atomic_store_pointer (void *arg, void *p)
|
||||
{
|
||||
void **pp;
|
||||
void *old;
|
||||
|
||||
pp = (void **) arg;
|
||||
old = *pp;
|
||||
while (!__sync_bool_compare_and_swap (pp, old, p))
|
||||
old = *pp;
|
||||
}
|
||||
|
||||
/* Do an atomic store of a size_t value. */
|
||||
|
||||
void
|
||||
backtrace_atomic_store_size_t (size_t *p, size_t v)
|
||||
{
|
||||
size_t old;
|
||||
|
||||
old = *p;
|
||||
while (!__sync_bool_compare_and_swap (p, old, v))
|
||||
old = *p;
|
||||
}
|
||||
|
||||
/* Do an atomic store of a int value. */
|
||||
|
||||
void
|
||||
backtrace_atomic_store_int (int *p, int v)
|
||||
{
|
||||
size_t old;
|
||||
|
||||
old = *p;
|
||||
while (!__sync_bool_compare_and_swap (p, old, v))
|
||||
old = *p;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
/* backtrace-supported.h.in -- Whether stack backtrace is supported.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
/* The file backtrace-supported.h.in is used by configure to generate
|
||||
the file backtrace-supported.h. The file backtrace-supported.h may
|
||||
be #include'd to see whether the backtrace library will be able to
|
||||
get a backtrace and produce symbolic information. */
|
||||
|
||||
|
||||
/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
|
||||
should work, 0 if it will not. Libraries may #include this to make
|
||||
other arrangements. */
|
||||
|
||||
#define BACKTRACE_SUPPORTED 1
|
||||
|
||||
/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
|
||||
library will call malloc as it works, 0 if it will call mmap
|
||||
instead. This may be used to determine whether it is safe to call
|
||||
the backtrace functions from a signal handler. In general this
|
||||
only applies to calls like backtrace and backtrace_pcinfo. It does
|
||||
not apply to backtrace_simple, which never calls malloc. It does
|
||||
not apply to backtrace_print, which always calls fprintf and
|
||||
therefore malloc. */
|
||||
|
||||
#define BACKTRACE_USES_MALLOC 1
|
||||
|
||||
/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
|
||||
library is configured with threading support, 0 if not. If this is
|
||||
0, the threaded parameter to backtrace_create_state must be passed
|
||||
as 0. */
|
||||
|
||||
#define BACKTRACE_SUPPORTS_THREADS 1
|
||||
|
||||
/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
|
||||
will work for variables. It will always work for functions. */
|
||||
|
||||
#define BACKTRACE_SUPPORTS_DATA 0
|
|
@ -0,0 +1,129 @@
|
|||
/* backtrace.c -- Entry point for stack backtrace library.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "unwind.h"
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* The main backtrace_full routine. */
|
||||
|
||||
/* Data passed through _Unwind_Backtrace. */
|
||||
|
||||
struct backtrace_data
|
||||
{
|
||||
/* Number of frames to skip. */
|
||||
int skip;
|
||||
/* Library state. */
|
||||
struct backtrace_state *state;
|
||||
/* Callback routine. */
|
||||
backtrace_full_callback callback;
|
||||
/* Error callback routine. */
|
||||
backtrace_error_callback error_callback;
|
||||
/* Data to pass to callback routines. */
|
||||
void *data;
|
||||
/* Value to return from backtrace_full. */
|
||||
int ret;
|
||||
/* Whether there is any memory available. */
|
||||
int can_alloc;
|
||||
};
|
||||
|
||||
/* Unwind library callback routine. This is passed to
|
||||
_Unwind_Backtrace. */
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind (struct _Unwind_Context *context, void *vdata)
|
||||
{
|
||||
struct backtrace_data *bdata = (struct backtrace_data *) vdata;
|
||||
uintptr_t pc;
|
||||
int ip_before_insn = 0;
|
||||
|
||||
#ifdef HAVE_GETIPINFO
|
||||
pc = _Unwind_GetIPInfo (context, &ip_before_insn);
|
||||
#else
|
||||
pc = _Unwind_GetIP (context);
|
||||
#endif
|
||||
|
||||
if (bdata->skip > 0)
|
||||
{
|
||||
--bdata->skip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
if (!ip_before_insn)
|
||||
--pc;
|
||||
|
||||
if (!bdata->can_alloc)
|
||||
bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
|
||||
else
|
||||
bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
|
||||
bdata->error_callback, bdata->data);
|
||||
if (bdata->ret != 0)
|
||||
return _URC_END_OF_STACK;
|
||||
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
/* Get a stack backtrace. */
|
||||
|
||||
int __attribute__((noinline))
|
||||
backtrace_full (struct backtrace_state *state, int skip,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
struct backtrace_data bdata;
|
||||
void *p;
|
||||
|
||||
bdata.skip = skip + 1;
|
||||
bdata.state = state;
|
||||
bdata.callback = callback;
|
||||
bdata.error_callback = error_callback;
|
||||
bdata.data = data;
|
||||
bdata.ret = 0;
|
||||
|
||||
/* If we can't allocate any memory at all, don't try to produce
|
||||
file/line information. */
|
||||
p = backtrace_alloc (state, 4096, NULL, NULL);
|
||||
if (p == NULL)
|
||||
bdata.can_alloc = 0;
|
||||
else
|
||||
{
|
||||
backtrace_free (state, p, 4096, NULL, NULL);
|
||||
bdata.can_alloc = 1;
|
||||
}
|
||||
|
||||
_Unwind_Backtrace (unwind, &bdata);
|
||||
return bdata.ret;
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/* backtrace.h -- Public header file for stack backtrace library.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#ifndef BACKTRACE_H
|
||||
#define BACKTRACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The backtrace state. This struct is intentionally not defined in
|
||||
the public interface. */
|
||||
|
||||
struct backtrace_state;
|
||||
|
||||
/* The type of the error callback argument to backtrace functions.
|
||||
This function, if not NULL, will be called for certain error cases.
|
||||
The DATA argument is passed to the function that calls this one.
|
||||
The MSG argument is an error message. The ERRNUM argument, if
|
||||
greater than 0, holds an errno value. The MSG buffer may become
|
||||
invalid after this function returns.
|
||||
|
||||
As a special case, the ERRNUM argument will be passed as -1 if no
|
||||
debug info can be found for the executable, or if the debug info
|
||||
exists but has an unsupported version, but the function requires
|
||||
debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in
|
||||
this case will be something along the lines of "no debug info".
|
||||
Similarly, ERRNUM will be passed as -1 if there is no symbol table,
|
||||
but the function requires a symbol table (e.g., backtrace_syminfo).
|
||||
This may be used as a signal that some other approach should be
|
||||
tried. */
|
||||
|
||||
typedef void (*backtrace_error_callback) (void *data, const char *msg,
|
||||
int errnum);
|
||||
|
||||
/* Create state information for the backtrace routines. This must be
|
||||
called before any of the other routines, and its return value must
|
||||
be passed to all of the other routines. FILENAME is the path name
|
||||
of the executable file; if it is NULL the library will try
|
||||
system-specific path names. If not NULL, FILENAME must point to a
|
||||
permanent buffer. If THREADED is non-zero the state may be
|
||||
accessed by multiple threads simultaneously, and the library will
|
||||
use appropriate atomic operations. If THREADED is zero the state
|
||||
may only be accessed by one thread at a time. This returns a state
|
||||
pointer on success, NULL on error. If an error occurs, this will
|
||||
call the ERROR_CALLBACK routine.
|
||||
|
||||
Calling this function allocates resources that cannot be freed.
|
||||
There is no backtrace_free_state function. The state is used to
|
||||
cache information that is expensive to recompute. Programs are
|
||||
expected to call this function at most once and to save the return
|
||||
value for all later calls to backtrace functions. */
|
||||
|
||||
extern struct backtrace_state *backtrace_create_state (
|
||||
const char *filename, int threaded,
|
||||
backtrace_error_callback error_callback, void *data);
|
||||
|
||||
/* The type of the callback argument to the backtrace_full function.
|
||||
DATA is the argument passed to backtrace_full. PC is the program
|
||||
counter. FILENAME is the name of the file containing PC, or NULL
|
||||
if not available. LINENO is the line number in FILENAME containing
|
||||
PC, or 0 if not available. FUNCTION is the name of the function
|
||||
containing PC, or NULL if not available. This should return 0 to
|
||||
continuing tracing. The FILENAME and FUNCTION buffers may become
|
||||
invalid after this function returns. */
|
||||
|
||||
typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
|
||||
const char *filename, int lineno,
|
||||
const char *function);
|
||||
|
||||
/* Get a full stack backtrace. SKIP is the number of frames to skip;
|
||||
passing 0 will start the trace with the function calling
|
||||
backtrace_full. DATA is passed to the callback routine. If any
|
||||
call to CALLBACK returns a non-zero value, the stack backtrace
|
||||
stops, and backtrace returns that value; this may be used to limit
|
||||
the number of stack frames desired. If all calls to CALLBACK
|
||||
return 0, backtrace returns 0. The backtrace_full function will
|
||||
make at least one call to either CALLBACK or ERROR_CALLBACK. This
|
||||
function requires debug info for the executable. */
|
||||
|
||||
extern int backtrace_full (struct backtrace_state *state, int skip,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* The type of the callback argument to the backtrace_simple function.
|
||||
DATA is the argument passed to simple_backtrace. PC is the program
|
||||
counter. This should return 0 to continue tracing. */
|
||||
|
||||
typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
|
||||
|
||||
/* Get a simple backtrace. SKIP is the number of frames to skip, as
|
||||
in backtrace. DATA is passed to the callback routine. If any call
|
||||
to CALLBACK returns a non-zero value, the stack backtrace stops,
|
||||
and backtrace_simple returns that value. Otherwise
|
||||
backtrace_simple returns 0. The backtrace_simple function will
|
||||
make at least one call to either CALLBACK or ERROR_CALLBACK. This
|
||||
function does not require any debug info for the executable. */
|
||||
|
||||
extern int backtrace_simple (struct backtrace_state *state, int skip,
|
||||
backtrace_simple_callback callback,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* Print the current backtrace in a user readable format to a FILE.
|
||||
SKIP is the number of frames to skip, as in backtrace_full. Any
|
||||
error messages are printed to stderr. This function requires debug
|
||||
info for the executable. */
|
||||
|
||||
extern void backtrace_print (struct backtrace_state *state, int skip, FILE *);
|
||||
|
||||
/* Given PC, a program counter in the current program, call the
|
||||
callback function with filename, line number, and function name
|
||||
information. This will normally call the callback function exactly
|
||||
once. However, if the PC happens to describe an inlined call, and
|
||||
the debugging information contains the necessary information, then
|
||||
this may call the callback function multiple times. This will make
|
||||
at least one call to either CALLBACK or ERROR_CALLBACK. This
|
||||
returns the first non-zero value returned by CALLBACK, or 0. */
|
||||
|
||||
extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* The type of the callback argument to backtrace_syminfo. DATA and
|
||||
PC are the arguments passed to backtrace_syminfo. SYMNAME is the
|
||||
name of the symbol for the corresponding code. SYMVAL is the
|
||||
value and SYMSIZE is the size of the symbol. SYMNAME will be NULL
|
||||
if no error occurred but the symbol could not be found. */
|
||||
|
||||
typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
|
||||
const char *symname,
|
||||
uintptr_t symval,
|
||||
uintptr_t symsize);
|
||||
|
||||
/* Given ADDR, an address or program counter in the current program,
|
||||
call the callback information with the symbol name and value
|
||||
describing the function or variable in which ADDR may be found.
|
||||
This will call either CALLBACK or ERROR_CALLBACK exactly once.
|
||||
This returns 1 on success, 0 on failure. This function requires
|
||||
the symbol table but does not require the debug info. Note that if
|
||||
the symbol table is present but ADDR could not be found in the
|
||||
table, CALLBACK will be called with a NULL SYMNAME argument.
|
||||
Returns 1 on success, 0 on error. */
|
||||
|
||||
extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
backtrace_syminfo_callback callback,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End extern "C". */
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,170 @@
|
|||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* ELF size: 32 or 64 */
|
||||
#define BACKTRACE_ELF_SIZE unused
|
||||
|
||||
/* XCOFF size: 32 or 64 */
|
||||
#define BACKTRACE_XCOFF_SIZE unused
|
||||
|
||||
/* Define to 1 if you have the __atomic functions */
|
||||
#define HAVE_ATOMIC_FUNCTIONS 1
|
||||
|
||||
/* Define to 1 if you have the `clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define to 1 if you have the declaration of `getpagesize', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_GETPAGESIZE 0
|
||||
|
||||
/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
|
||||
don't. */
|
||||
#define HAVE_DECL_STRNLEN 1
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
/* #undef HAVE_DLFCN_H */
|
||||
|
||||
/* Define if dl_iterate_phdr is available. */
|
||||
/* #undef HAVE_DL_ITERATE_PHDR */
|
||||
|
||||
/* Define to 1 if you have the fcntl function */
|
||||
/* #undef HAVE_FCNTL */
|
||||
|
||||
/* Define if getexecname is available. */
|
||||
/* #undef HAVE_GETEXECNAME */
|
||||
|
||||
/* Define if _Unwind_GetIPInfo is available. */
|
||||
#define HAVE_GETIPINFO 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have KERN_PROC and KERN_PROC_PATHNAME in <sys/sysctl.h>.
|
||||
*/
|
||||
/* #undef HAVE_KERN_PROC */
|
||||
|
||||
/* Define to 1 if you have KERN_PROCARGS and KERN_PROC_PATHNAME in
|
||||
<sys/sysctl.h>. */
|
||||
/* #undef HAVE_KERN_PROC_ARGS */
|
||||
|
||||
/* Define if -llzma is available. */
|
||||
#define HAVE_LIBLZMA 1
|
||||
|
||||
/* Define to 1 if you have the <link.h> header file. */
|
||||
/* #undef HAVE_LINK_H */
|
||||
|
||||
/* Define if AIX loadquery is available. */
|
||||
/* #undef HAVE_LOADQUERY */
|
||||
|
||||
/* Define to 1 if you have the `lstat' function. */
|
||||
/* #undef HAVE_LSTAT */
|
||||
|
||||
/* Define to 1 if you have the <mach-o/dyld.h> header file. */
|
||||
/* #undef HAVE_MACH_O_DYLD_H */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the `readlink' function. */
|
||||
/* #undef HAVE_READLINK */
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the __sync functions */
|
||||
#define HAVE_SYNC_FUNCTIONS 1
|
||||
|
||||
/* Define to 1 if you have the <sys/ldr.h> header file. */
|
||||
/* #undef HAVE_SYS_LDR_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/mman.h> header file. */
|
||||
/* #undef HAVE_SYS_MMAN_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define if -lz is available. */
|
||||
#define HAVE_ZLIB 1
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "package-unused"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "package-unused version-unused"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "libbacktrace"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "version-unused"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# define _ALL_SOURCE 1
|
||||
#endif
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
/* Enable threading extensions on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# define _POSIX_PTHREAD_SEMANTICS 1
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# define _TANDEM_SOURCE 1
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# define __EXTENSIONS__ 1
|
||||
#endif
|
||||
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
/* #undef _LARGE_FILES */
|
||||
|
||||
/* Define to 1 if on MINIX. */
|
||||
/* #undef _MINIX */
|
||||
|
||||
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||
this defined. */
|
||||
/* #undef _POSIX_1_SOURCE */
|
||||
|
||||
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||
/* #undef _POSIX_SOURCE */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,346 @@
|
|||
/* fileline.c -- Get file and line number information in a backtrace.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MACH_O_DYLD_H
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
#ifndef HAVE_GETEXECNAME
|
||||
#define getexecname() NULL
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC)
|
||||
|
||||
#define sysctl_exec_name1(state, error_callback, data) NULL
|
||||
#define sysctl_exec_name2(state, error_callback, data) NULL
|
||||
|
||||
#else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */
|
||||
|
||||
static char *
|
||||
sysctl_exec_name (struct backtrace_state *state,
|
||||
int mib0, int mib1, int mib2, int mib3,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
int mib[4];
|
||||
size_t len;
|
||||
char *name;
|
||||
size_t rlen;
|
||||
|
||||
mib[0] = mib0;
|
||||
mib[1] = mib1;
|
||||
mib[2] = mib2;
|
||||
mib[3] = mib3;
|
||||
|
||||
if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0)
|
||||
return NULL;
|
||||
name = (char *) backtrace_alloc (state, len, error_callback, data);
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
rlen = len;
|
||||
if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0)
|
||||
{
|
||||
backtrace_free (state, name, len, error_callback, data);
|
||||
return NULL;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
#ifdef HAVE_KERN_PROC_ARGS
|
||||
|
||||
static char *
|
||||
sysctl_exec_name1 (struct backtrace_state *state,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
/* This variant is used on NetBSD. */
|
||||
return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1,
|
||||
KERN_PROC_PATHNAME, error_callback, data);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define sysctl_exec_name1(state, error_callback, data) NULL
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KERN_PROC
|
||||
|
||||
static char *
|
||||
sysctl_exec_name2 (struct backtrace_state *state,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
/* This variant is used on FreeBSD. */
|
||||
return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
|
||||
error_callback, data);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define sysctl_exec_name2(state, error_callback, data) NULL
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */
|
||||
|
||||
#ifdef HAVE_MACH_O_DYLD_H
|
||||
|
||||
static char *
|
||||
macho_get_executable_path (struct backtrace_state *state,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
uint32_t len;
|
||||
char *name;
|
||||
|
||||
len = 0;
|
||||
if (_NSGetExecutablePath (NULL, &len) == 0)
|
||||
return NULL;
|
||||
name = (char *) backtrace_alloc (state, len, error_callback, data);
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
if (_NSGetExecutablePath (name, &len) != 0)
|
||||
{
|
||||
backtrace_free (state, name, len, error_callback, data);
|
||||
return NULL;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
#else /* !defined (HAVE_MACH_O_DYLD_H) */
|
||||
|
||||
#define macho_get_executable_path(state, error_callback, data) NULL
|
||||
|
||||
#endif /* !defined (HAVE_MACH_O_DYLD_H) */
|
||||
|
||||
/* Initialize the fileline information from the executable. Returns 1
|
||||
on success, 0 on failure. */
|
||||
|
||||
static int
|
||||
fileline_initialize (struct backtrace_state *state,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
int failed;
|
||||
fileline fileline_fn;
|
||||
int pass;
|
||||
int called_error_callback;
|
||||
int descriptor;
|
||||
const char *filename;
|
||||
char buf[64];
|
||||
|
||||
if (!state->threaded)
|
||||
failed = state->fileline_initialization_failed;
|
||||
else
|
||||
failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
|
||||
|
||||
if (failed)
|
||||
{
|
||||
error_callback (data, "failed to read executable information", -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!state->threaded)
|
||||
fileline_fn = state->fileline_fn;
|
||||
else
|
||||
fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
|
||||
if (fileline_fn != NULL)
|
||||
return 1;
|
||||
|
||||
/* We have not initialized the information. Do it now. */
|
||||
|
||||
descriptor = -1;
|
||||
called_error_callback = 0;
|
||||
for (pass = 0; pass < 8; ++pass)
|
||||
{
|
||||
int does_not_exist;
|
||||
|
||||
switch (pass)
|
||||
{
|
||||
case 0:
|
||||
filename = state->filename;
|
||||
break;
|
||||
case 1:
|
||||
filename = getexecname ();
|
||||
break;
|
||||
case 2:
|
||||
filename = "/proc/self/exe";
|
||||
break;
|
||||
case 3:
|
||||
filename = "/proc/curproc/file";
|
||||
break;
|
||||
case 4:
|
||||
snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
|
||||
(long) getpid ());
|
||||
filename = buf;
|
||||
break;
|
||||
case 5:
|
||||
filename = sysctl_exec_name1 (state, error_callback, data);
|
||||
break;
|
||||
case 6:
|
||||
filename = sysctl_exec_name2 (state, error_callback, data);
|
||||
break;
|
||||
case 7:
|
||||
filename = macho_get_executable_path (state, error_callback, data);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (filename == NULL)
|
||||
continue;
|
||||
|
||||
descriptor = backtrace_open (filename, error_callback, data,
|
||||
&does_not_exist);
|
||||
if (descriptor < 0 && !does_not_exist)
|
||||
{
|
||||
called_error_callback = 1;
|
||||
break;
|
||||
}
|
||||
if (descriptor >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (descriptor < 0)
|
||||
{
|
||||
if (!called_error_callback)
|
||||
{
|
||||
if (state->filename != NULL)
|
||||
error_callback (data, state->filename, ENOENT);
|
||||
else
|
||||
error_callback (data,
|
||||
"libbacktrace could not find executable to open",
|
||||
0);
|
||||
}
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
if (!failed)
|
||||
{
|
||||
if (!backtrace_initialize (state, filename, descriptor, error_callback,
|
||||
data, &fileline_fn))
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
if (failed)
|
||||
{
|
||||
if (!state->threaded)
|
||||
state->fileline_initialization_failed = 1;
|
||||
else
|
||||
backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!state->threaded)
|
||||
state->fileline_fn = fileline_fn;
|
||||
else
|
||||
{
|
||||
backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
|
||||
|
||||
/* Note that if two threads initialize at once, one of the data
|
||||
sets may be leaked. */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Given a PC, find the file name, line number, and function name. */
|
||||
|
||||
int
|
||||
backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
if (!fileline_initialize (state, error_callback, data))
|
||||
return 0;
|
||||
|
||||
if (state->fileline_initialization_failed)
|
||||
return 0;
|
||||
|
||||
return state->fileline_fn (state, pc, callback, error_callback, data);
|
||||
}
|
||||
|
||||
/* Given a PC, find the symbol for it, and its value. */
|
||||
|
||||
int
|
||||
backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_syminfo_callback callback,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
if (!fileline_initialize (state, error_callback, data))
|
||||
return 0;
|
||||
|
||||
if (state->fileline_initialization_failed)
|
||||
return 0;
|
||||
|
||||
state->syminfo_fn (state, pc, callback, error_callback, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* A backtrace_syminfo_callback that can call into a
|
||||
backtrace_full_callback, used when we have a symbol table but no
|
||||
debug info. */
|
||||
|
||||
void
|
||||
backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
|
||||
const char *symname,
|
||||
uintptr_t symval ATTRIBUTE_UNUSED,
|
||||
uintptr_t symsize ATTRIBUTE_UNUSED)
|
||||
{
|
||||
struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
|
||||
|
||||
bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname);
|
||||
}
|
||||
|
||||
/* An error callback that corresponds to
|
||||
backtrace_syminfo_to_full_callback. */
|
||||
|
||||
void
|
||||
backtrace_syminfo_to_full_error_callback (void *data, const char *msg,
|
||||
int errnum)
|
||||
{
|
||||
struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
|
||||
|
||||
bdata->full_error_callback (bdata->full_data, msg, errnum);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* btest.c -- Filename header for libbacktrace library
|
||||
Copyright (C) 2012-2018 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#ifndef GCC_VERSION
|
||||
# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
|
||||
#endif
|
||||
|
||||
#if (GCC_VERSION < 2007)
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#ifndef ATTRIBUTE_UNUSED
|
||||
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__)
|
||||
# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
|
||||
# define HAS_DRIVE_SPEC(f) ((f)[0] != '\0' && (f)[1] == ':')
|
||||
# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC(f))
|
||||
#else
|
||||
# define IS_DIR_SEPARATOR(c) ((c) == '/')
|
||||
# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]))
|
||||
#endif
|
|
@ -0,0 +1,380 @@
|
|||
/* internal.h -- Internal header file for stack backtrace library.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#ifndef BACKTRACE_INTERNAL_H
|
||||
#define BACKTRACE_INTERNAL_H
|
||||
|
||||
/* We assume that <sys/types.h> and "backtrace.h" have already been
|
||||
included. */
|
||||
|
||||
#ifndef GCC_VERSION
|
||||
# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
|
||||
#endif
|
||||
|
||||
#if (GCC_VERSION < 2007)
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#ifndef ATTRIBUTE_UNUSED
|
||||
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
#ifndef ATTRIBUTE_MALLOC
|
||||
# if (GCC_VERSION >= 2096)
|
||||
# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
|
||||
# else
|
||||
# define ATTRIBUTE_MALLOC
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ATTRIBUTE_FALLTHROUGH
|
||||
# if (GCC_VERSION >= 7000)
|
||||
# define ATTRIBUTE_FALLTHROUGH __attribute__ ((__fallthrough__))
|
||||
# else
|
||||
# define ATTRIBUTE_FALLTHROUGH
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SYNC_FUNCTIONS
|
||||
|
||||
/* Define out the sync functions. These should never be called if
|
||||
they are not available. */
|
||||
|
||||
#define __sync_bool_compare_and_swap(A, B, C) (abort(), 1)
|
||||
#define __sync_lock_test_and_set(A, B) (abort(), 0)
|
||||
#define __sync_lock_release(A) abort()
|
||||
|
||||
#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
|
||||
|
||||
#ifdef HAVE_ATOMIC_FUNCTIONS
|
||||
|
||||
/* We have the atomic builtin functions. */
|
||||
|
||||
#define backtrace_atomic_load_pointer(p) \
|
||||
__atomic_load_n ((p), __ATOMIC_ACQUIRE)
|
||||
#define backtrace_atomic_load_int(p) \
|
||||
__atomic_load_n ((p), __ATOMIC_ACQUIRE)
|
||||
#define backtrace_atomic_store_pointer(p, v) \
|
||||
__atomic_store_n ((p), (v), __ATOMIC_RELEASE)
|
||||
#define backtrace_atomic_store_size_t(p, v) \
|
||||
__atomic_store_n ((p), (v), __ATOMIC_RELEASE)
|
||||
#define backtrace_atomic_store_int(p, v) \
|
||||
__atomic_store_n ((p), (v), __ATOMIC_RELEASE)
|
||||
|
||||
#else /* !defined (HAVE_ATOMIC_FUNCTIONS) */
|
||||
#ifdef HAVE_SYNC_FUNCTIONS
|
||||
|
||||
/* We have the sync functions but not the atomic functions. Define
|
||||
the atomic ones in terms of the sync ones. */
|
||||
|
||||
extern void *backtrace_atomic_load_pointer (void *);
|
||||
extern int backtrace_atomic_load_int (int *);
|
||||
extern void backtrace_atomic_store_pointer (void *, void *);
|
||||
extern void backtrace_atomic_store_size_t (size_t *, size_t);
|
||||
extern void backtrace_atomic_store_int (int *, int);
|
||||
|
||||
#else /* !defined (HAVE_SYNC_FUNCTIONS) */
|
||||
|
||||
/* We have neither the sync nor the atomic functions. These will
|
||||
never be called. */
|
||||
|
||||
#define backtrace_atomic_load_pointer(p) (abort(), (void *) NULL)
|
||||
#define backtrace_atomic_load_int(p) (abort(), 0)
|
||||
#define backtrace_atomic_store_pointer(p, v) abort()
|
||||
#define backtrace_atomic_store_size_t(p, v) abort()
|
||||
#define backtrace_atomic_store_int(p, v) abort()
|
||||
|
||||
#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
|
||||
#endif /* !defined (HAVE_ATOMIC_FUNCTIONS) */
|
||||
|
||||
/* The type of the function that collects file/line information. This
|
||||
is like backtrace_pcinfo. */
|
||||
|
||||
typedef int (*fileline) (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_full_callback callback,
|
||||
backtrace_error_callback error_callback, void *data);
|
||||
|
||||
/* The type of the function that collects symbol information. This is
|
||||
like backtrace_syminfo. */
|
||||
|
||||
typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc,
|
||||
backtrace_syminfo_callback callback,
|
||||
backtrace_error_callback error_callback, void *data);
|
||||
|
||||
/* What the backtrace state pointer points to. */
|
||||
|
||||
struct backtrace_state
|
||||
{
|
||||
/* The name of the executable. */
|
||||
const char *filename;
|
||||
/* Non-zero if threaded. */
|
||||
int threaded;
|
||||
/* The master lock for fileline_fn, fileline_data, syminfo_fn,
|
||||
syminfo_data, fileline_initialization_failed and everything the
|
||||
data pointers point to. */
|
||||
void *lock;
|
||||
/* The function that returns file/line information. */
|
||||
fileline fileline_fn;
|
||||
/* The data to pass to FILELINE_FN. */
|
||||
void *fileline_data;
|
||||
/* The function that returns symbol information. */
|
||||
syminfo syminfo_fn;
|
||||
/* The data to pass to SYMINFO_FN. */
|
||||
void *syminfo_data;
|
||||
/* Whether initializing the file/line information failed. */
|
||||
int fileline_initialization_failed;
|
||||
/* The lock for the freelist. */
|
||||
int lock_alloc;
|
||||
/* The freelist when using mmap. */
|
||||
struct backtrace_freelist_struct *freelist;
|
||||
};
|
||||
|
||||
/* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST
|
||||
is not NULL, *DOES_NOT_EXIST will be set to 0 normally and set to 1
|
||||
if the file does not exist. If the file does not exist and
|
||||
DOES_NOT_EXIST is not NULL, the function will return -1 and will
|
||||
not call ERROR_CALLBACK. On other errors, or if DOES_NOT_EXIST is
|
||||
NULL, the function will call ERROR_CALLBACK before returning. */
|
||||
extern int backtrace_open (const char *filename,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data,
|
||||
int *does_not_exist);
|
||||
|
||||
/* A view of the contents of a file. This supports mmap when
|
||||
available. A view will remain in memory even after backtrace_close
|
||||
is called on the file descriptor from which the view was
|
||||
obtained. */
|
||||
|
||||
struct backtrace_view
|
||||
{
|
||||
/* The data that the caller requested. */
|
||||
const void *data;
|
||||
/* The base of the view. */
|
||||
void *base;
|
||||
/* The total length of the view. */
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. Store the
|
||||
result in *VIEW. Returns 1 on success, 0 on error. */
|
||||
extern int backtrace_get_view (struct backtrace_state *state, int descriptor,
|
||||
off_t offset, uint64_t size,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data, struct backtrace_view *view);
|
||||
|
||||
/* Release a view created by backtrace_get_view. */
|
||||
extern void backtrace_release_view (struct backtrace_state *state,
|
||||
struct backtrace_view *view,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* Close a file opened by backtrace_open. Returns 1 on success, 0 on
|
||||
error. */
|
||||
|
||||
extern int backtrace_close (int descriptor,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* Sort without using memory. */
|
||||
|
||||
extern void backtrace_qsort (void *base, size_t count, size_t size,
|
||||
int (*compar) (const void *, const void *));
|
||||
|
||||
/* Allocate memory. This is like malloc. If ERROR_CALLBACK is NULL,
|
||||
this does not report an error, it just returns NULL. */
|
||||
|
||||
extern void *backtrace_alloc (struct backtrace_state *state, size_t size,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data) ATTRIBUTE_MALLOC;
|
||||
|
||||
/* Free memory allocated by backtrace_alloc. If ERROR_CALLBACK is
|
||||
NULL, this does not report an error. */
|
||||
|
||||
extern void backtrace_free (struct backtrace_state *state, void *mem,
|
||||
size_t size,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* A growable vector of some struct. This is used for more efficient
|
||||
allocation when we don't know the final size of some group of data
|
||||
that we want to represent as an array. */
|
||||
|
||||
struct backtrace_vector
|
||||
{
|
||||
/* The base of the vector. */
|
||||
void *base;
|
||||
/* The number of bytes in the vector. */
|
||||
size_t size;
|
||||
/* The number of bytes available at the current allocation. */
|
||||
size_t alc;
|
||||
};
|
||||
|
||||
/* Grow VEC by SIZE bytes. Return a pointer to the newly allocated
|
||||
bytes. Note that this may move the entire vector to a new memory
|
||||
location. Returns NULL on failure. */
|
||||
|
||||
extern void *backtrace_vector_grow (struct backtrace_state *state, size_t size,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data,
|
||||
struct backtrace_vector *vec);
|
||||
|
||||
/* Finish the current allocation on VEC. Prepare to start a new
|
||||
allocation. The finished allocation will never be freed. Returns
|
||||
a pointer to the base of the finished entries, or NULL on
|
||||
failure. */
|
||||
|
||||
extern void* backtrace_vector_finish (struct backtrace_state *state,
|
||||
struct backtrace_vector *vec,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* Release any extra space allocated for VEC. This may change
|
||||
VEC->base. Returns 1 on success, 0 on failure. */
|
||||
|
||||
extern int backtrace_vector_release (struct backtrace_state *state,
|
||||
struct backtrace_vector *vec,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data);
|
||||
|
||||
/* Free the space managed by VEC. This will reset VEC. */
|
||||
|
||||
static inline void
|
||||
backtrace_vector_free (struct backtrace_state *state,
|
||||
struct backtrace_vector *vec,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
vec->alc += vec->size;
|
||||
vec->size = 0;
|
||||
backtrace_vector_release (state, vec, error_callback, data);
|
||||
}
|
||||
|
||||
/* Read initial debug data from a descriptor, and set the
|
||||
fileline_data, syminfo_fn, and syminfo_data fields of STATE.
|
||||
Return the fileln_fn field in *FILELN_FN--this is done this way so
|
||||
that the synchronization code is only implemented once. This is
|
||||
called after the descriptor has first been opened. It will close
|
||||
the descriptor if it is no longer needed. Returns 1 on success, 0
|
||||
on error. There will be multiple implementations of this function,
|
||||
for different file formats. Each system will compile the
|
||||
appropriate one. */
|
||||
|
||||
extern int backtrace_initialize (struct backtrace_state *state,
|
||||
const char *filename,
|
||||
int descriptor,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data,
|
||||
fileline *fileline_fn);
|
||||
|
||||
/* An enum for the DWARF sections we care about. */
|
||||
|
||||
enum dwarf_section
|
||||
{
|
||||
DEBUG_INFO,
|
||||
DEBUG_LINE,
|
||||
DEBUG_ABBREV,
|
||||
DEBUG_RANGES,
|
||||
DEBUG_STR,
|
||||
DEBUG_ADDR,
|
||||
DEBUG_STR_OFFSETS,
|
||||
DEBUG_LINE_STR,
|
||||
DEBUG_RNGLISTS,
|
||||
|
||||
DEBUG_MAX
|
||||
};
|
||||
|
||||
/* Data for the DWARF sections we care about. */
|
||||
|
||||
struct dwarf_sections
|
||||
{
|
||||
const unsigned char *data[DEBUG_MAX];
|
||||
size_t size[DEBUG_MAX];
|
||||
};
|
||||
|
||||
/* DWARF data read from a file, used for .gnu_debugaltlink. */
|
||||
|
||||
struct dwarf_data;
|
||||
|
||||
/* Add file/line information for a DWARF module. */
|
||||
|
||||
extern int backtrace_dwarf_add (struct backtrace_state *state,
|
||||
uintptr_t base_address,
|
||||
const struct dwarf_sections *dwarf_sections,
|
||||
int is_bigendian,
|
||||
struct dwarf_data *fileline_altlink,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data, fileline *fileline_fn,
|
||||
struct dwarf_data **fileline_entry);
|
||||
|
||||
/* A data structure to pass to backtrace_syminfo_to_full. */
|
||||
|
||||
struct backtrace_call_full
|
||||
{
|
||||
backtrace_full_callback full_callback;
|
||||
backtrace_error_callback full_error_callback;
|
||||
void *full_data;
|
||||
int ret;
|
||||
};
|
||||
|
||||
/* A backtrace_syminfo_callback that can call into a
|
||||
backtrace_full_callback, used when we have a symbol table but no
|
||||
debug info. */
|
||||
|
||||
extern void backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
|
||||
const char *symname,
|
||||
uintptr_t symval,
|
||||
uintptr_t symsize);
|
||||
|
||||
/* An error callback that corresponds to
|
||||
backtrace_syminfo_to_full_callback. */
|
||||
|
||||
extern void backtrace_syminfo_to_full_error_callback (void *, const char *,
|
||||
int);
|
||||
|
||||
/* A test-only hook for elf_uncompress_zdebug. */
|
||||
|
||||
extern int backtrace_uncompress_zdebug (struct backtrace_state *,
|
||||
const unsigned char *compressed,
|
||||
size_t compressed_size,
|
||||
backtrace_error_callback, void *data,
|
||||
unsigned char **uncompressed,
|
||||
size_t *uncompressed_size);
|
||||
|
||||
/* A test-only hook for elf_uncompress_lzma. */
|
||||
|
||||
extern int backtrace_uncompress_lzma (struct backtrace_state *,
|
||||
const unsigned char *compressed,
|
||||
size_t compressed_size,
|
||||
backtrace_error_callback, void *data,
|
||||
unsigned char **uncompressed,
|
||||
size_t *uncompressed_size);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,935 @@
|
|||
/* pecoff.c -- Get debug data from a PE/COFFF file for backtraces.
|
||||
Copyright (C) 2015-2021 Free Software Foundation, Inc.
|
||||
Adapted from elf.c by Tristan Gingold, AdaCore.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* Coff file header. */
|
||||
|
||||
typedef struct {
|
||||
uint16_t machine;
|
||||
uint16_t number_of_sections;
|
||||
uint32_t time_date_stamp;
|
||||
uint32_t pointer_to_symbol_table;
|
||||
uint32_t number_of_symbols;
|
||||
uint16_t size_of_optional_header;
|
||||
uint16_t characteristics;
|
||||
} b_coff_file_header;
|
||||
|
||||
/* Coff optional header. */
|
||||
|
||||
typedef struct {
|
||||
uint16_t magic;
|
||||
uint8_t major_linker_version;
|
||||
uint8_t minor_linker_version;
|
||||
uint32_t size_of_code;
|
||||
uint32_t size_of_initialized_data;
|
||||
uint32_t size_of_uninitialized_data;
|
||||
uint32_t address_of_entry_point;
|
||||
uint32_t base_of_code;
|
||||
union {
|
||||
struct {
|
||||
uint32_t base_of_data;
|
||||
uint32_t image_base;
|
||||
} pe;
|
||||
struct {
|
||||
uint64_t image_base;
|
||||
} pep;
|
||||
} u;
|
||||
} b_coff_optional_header;
|
||||
|
||||
/* Values of magic in optional header. */
|
||||
|
||||
#define PE_MAGIC 0x10b /* PE32 executable. */
|
||||
#define PEP_MAGIC 0x20b /* PE32+ executable (for 64bit targets). */
|
||||
|
||||
/* Coff section header. */
|
||||
|
||||
typedef struct {
|
||||
char name[8];
|
||||
uint32_t virtual_size;
|
||||
uint32_t virtual_address;
|
||||
uint32_t size_of_raw_data;
|
||||
uint32_t pointer_to_raw_data;
|
||||
uint32_t pointer_to_relocations;
|
||||
uint32_t pointer_to_line_numbers;
|
||||
uint16_t number_of_relocations;
|
||||
uint16_t number_of_line_numbers;
|
||||
uint32_t characteristics;
|
||||
} b_coff_section_header;
|
||||
|
||||
/* Coff symbol name. */
|
||||
|
||||
typedef union {
|
||||
char short_name[8];
|
||||
struct {
|
||||
unsigned char zeroes[4];
|
||||
unsigned char off[4];
|
||||
} long_name;
|
||||
} b_coff_name;
|
||||
|
||||
/* Coff symbol (external representation which is unaligned). */
|
||||
|
||||
typedef struct {
|
||||
b_coff_name name;
|
||||
unsigned char value[4];
|
||||
unsigned char section_number[2];
|
||||
unsigned char type[2];
|
||||
unsigned char storage_class;
|
||||
unsigned char number_of_aux_symbols;
|
||||
} b_coff_external_symbol;
|
||||
|
||||
/* Symbol types. */
|
||||
|
||||
#define N_TBSHFT 4 /* Shift for the derived type. */
|
||||
#define IMAGE_SYM_DTYPE_FUNCTION 2 /* Function derived type. */
|
||||
|
||||
/* Size of a coff symbol. */
|
||||
|
||||
#define SYM_SZ 18
|
||||
|
||||
/* Coff symbol, internal representation (aligned). */
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
uint32_t value;
|
||||
int16_t sec;
|
||||
uint16_t type;
|
||||
uint16_t sc;
|
||||
} b_coff_internal_symbol;
|
||||
|
||||
/* Names of sections, indexed by enum dwarf_section in internal.h. */
|
||||
|
||||
static const char * const debug_section_names[DEBUG_MAX] =
|
||||
{
|
||||
".debug_info",
|
||||
".debug_line",
|
||||
".debug_abbrev",
|
||||
".debug_ranges",
|
||||
".debug_str",
|
||||
".debug_addr",
|
||||
".debug_str_offsets",
|
||||
".debug_line_str",
|
||||
".debug_rnglists"
|
||||
};
|
||||
|
||||
/* Information we gather for the sections we care about. */
|
||||
|
||||
struct debug_section_info
|
||||
{
|
||||
/* Section file offset. */
|
||||
off_t offset;
|
||||
/* Section size. */
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* Information we keep for an coff symbol. */
|
||||
|
||||
struct coff_symbol
|
||||
{
|
||||
/* The name of the symbol. */
|
||||
const char *name;
|
||||
/* The address of the symbol. */
|
||||
uintptr_t address;
|
||||
};
|
||||
|
||||
/* Information to pass to coff_syminfo. */
|
||||
|
||||
struct coff_syminfo_data
|
||||
{
|
||||
/* Symbols for the next module. */
|
||||
struct coff_syminfo_data *next;
|
||||
/* The COFF symbols, sorted by address. */
|
||||
struct coff_symbol *symbols;
|
||||
/* The number of symbols. */
|
||||
size_t count;
|
||||
};
|
||||
|
||||
/* A dummy callback function used when we can't find any debug info. */
|
||||
|
||||
static int
|
||||
coff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
uintptr_t pc ATTRIBUTE_UNUSED,
|
||||
backtrace_full_callback callback ATTRIBUTE_UNUSED,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
error_callback (data, "no debug info in PE/COFF executable", -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A dummy callback function used when we can't find a symbol
|
||||
table. */
|
||||
|
||||
static void
|
||||
coff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||
uintptr_t addr ATTRIBUTE_UNUSED,
|
||||
backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
error_callback (data, "no symbol table in PE/COFF executable", -1);
|
||||
}
|
||||
|
||||
/* Read a potentially unaligned 4 byte word at P, using native endianness. */
|
||||
|
||||
static uint32_t
|
||||
coff_read4 (const unsigned char *p)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
memcpy (&res, p, 4);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Read a potentially unaligned 2 byte word at P, using native endianness.
|
||||
All 2 byte word in symbols are always aligned, but for coherency all
|
||||
fields are declared as char arrays. */
|
||||
|
||||
static uint16_t
|
||||
coff_read2 (const unsigned char *p)
|
||||
{
|
||||
uint16_t res;
|
||||
|
||||
memcpy (&res, p, sizeof (res));
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Return the length (without the trailing 0) of a COFF short name. */
|
||||
|
||||
static size_t
|
||||
coff_short_name_len (const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
if (name[i] == 0)
|
||||
return i;
|
||||
return 8;
|
||||
}
|
||||
|
||||
/* Return true iff COFF short name CNAME is the same as NAME (a NUL-terminated
|
||||
string). */
|
||||
|
||||
static int
|
||||
coff_short_name_eq (const char *name, const char *cname)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if (name[i] != cname[i])
|
||||
return 0;
|
||||
if (name[i] == 0)
|
||||
return 1;
|
||||
}
|
||||
return name[8] == 0;
|
||||
}
|
||||
|
||||
/* Return true iff NAME is the same as string at offset OFF. */
|
||||
|
||||
static int
|
||||
coff_long_name_eq (const char *name, unsigned int off,
|
||||
struct backtrace_view *str_view)
|
||||
{
|
||||
if (off >= str_view->len)
|
||||
return 0;
|
||||
return strcmp (name, (const char *)str_view->data + off) == 0;
|
||||
}
|
||||
|
||||
/* Compare struct coff_symbol for qsort. */
|
||||
|
||||
static int
|
||||
coff_symbol_compare (const void *v1, const void *v2)
|
||||
{
|
||||
const struct coff_symbol *e1 = (const struct coff_symbol *) v1;
|
||||
const struct coff_symbol *e2 = (const struct coff_symbol *) v2;
|
||||
|
||||
if (e1->address < e2->address)
|
||||
return -1;
|
||||
else if (e1->address > e2->address)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert SYM to internal (and aligned) format ISYM, using string table
|
||||
from STRTAB and STRTAB_SIZE, and number of sections SECTS_NUM.
|
||||
Return -1 in case of error (invalid section number or string index). */
|
||||
|
||||
static int
|
||||
coff_expand_symbol (b_coff_internal_symbol *isym,
|
||||
const b_coff_external_symbol *sym,
|
||||
uint16_t sects_num,
|
||||
const unsigned char *strtab, size_t strtab_size)
|
||||
{
|
||||
isym->type = coff_read2 (sym->type);
|
||||
isym->sec = coff_read2 (sym->section_number);
|
||||
isym->sc = sym->storage_class;
|
||||
|
||||
if (isym->sec > 0 && (uint16_t) isym->sec > sects_num)
|
||||
return -1;
|
||||
if (sym->name.short_name[0] != 0)
|
||||
isym->name = sym->name.short_name;
|
||||
else
|
||||
{
|
||||
uint32_t off = coff_read4 (sym->name.long_name.off);
|
||||
|
||||
if (off >= strtab_size)
|
||||
return -1;
|
||||
isym->name = (const char *) strtab + off;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return true iff SYM is a defined symbol for a function. Data symbols
|
||||
aren't considered because they aren't easily identified (same type as
|
||||
section names, presence of symbols defined by the linker script). */
|
||||
|
||||
static int
|
||||
coff_is_function_symbol (const b_coff_internal_symbol *isym)
|
||||
{
|
||||
return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION
|
||||
&& isym->sec > 0;
|
||||
}
|
||||
|
||||
/* Initialize the symbol table info for coff_syminfo. */
|
||||
|
||||
static int
|
||||
coff_initialize_syminfo (struct backtrace_state *state,
|
||||
uintptr_t base_address, int is_64,
|
||||
const b_coff_section_header *sects, size_t sects_num,
|
||||
const b_coff_external_symbol *syms, size_t syms_size,
|
||||
const unsigned char *strtab, size_t strtab_size,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data, struct coff_syminfo_data *sdata)
|
||||
{
|
||||
size_t syms_count;
|
||||
char *coff_symstr;
|
||||
size_t coff_symstr_len;
|
||||
size_t coff_symbol_count;
|
||||
size_t coff_symbol_size;
|
||||
struct coff_symbol *coff_symbols;
|
||||
struct coff_symbol *coff_sym;
|
||||
char *coff_str;
|
||||
size_t i;
|
||||
|
||||
syms_count = syms_size / SYM_SZ;
|
||||
|
||||
/* We only care about function symbols. Count them. Also count size of
|
||||
strings for in-symbol names. */
|
||||
coff_symbol_count = 0;
|
||||
coff_symstr_len = 0;
|
||||
for (i = 0; i < syms_count; ++i)
|
||||
{
|
||||
const b_coff_external_symbol *asym = &syms[i];
|
||||
b_coff_internal_symbol isym;
|
||||
|
||||
if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size) < 0)
|
||||
{
|
||||
error_callback (data, "invalid section or offset in coff symbol", 0);
|
||||
return 0;
|
||||
}
|
||||
if (coff_is_function_symbol (&isym))
|
||||
{
|
||||
++coff_symbol_count;
|
||||
if (asym->name.short_name[0] != 0)
|
||||
coff_symstr_len += coff_short_name_len (asym->name.short_name) + 1;
|
||||
}
|
||||
|
||||
i += asym->number_of_aux_symbols;
|
||||
}
|
||||
|
||||
coff_symbol_size = (coff_symbol_count + 1) * sizeof (struct coff_symbol);
|
||||
coff_symbols = ((struct coff_symbol *)
|
||||
backtrace_alloc (state, coff_symbol_size, error_callback,
|
||||
data));
|
||||
if (coff_symbols == NULL)
|
||||
return 0;
|
||||
|
||||
/* Allocate memory for symbols strings. */
|
||||
if (coff_symstr_len > 0)
|
||||
{
|
||||
coff_symstr = ((char *)
|
||||
backtrace_alloc (state, coff_symstr_len, error_callback,
|
||||
data));
|
||||
if (coff_symstr == NULL)
|
||||
{
|
||||
backtrace_free (state, coff_symbols, coff_symbol_size,
|
||||
error_callback, data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
coff_symstr = NULL;
|
||||
|
||||
/* Copy symbols. */
|
||||
coff_sym = coff_symbols;
|
||||
coff_str = coff_symstr;
|
||||
for (i = 0; i < syms_count; ++i)
|
||||
{
|
||||
const b_coff_external_symbol *asym = &syms[i];
|
||||
b_coff_internal_symbol isym;
|
||||
|
||||
if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size))
|
||||
{
|
||||
/* Should not fail, as it was already tested in the previous
|
||||
loop. */
|
||||
abort ();
|
||||
}
|
||||
if (coff_is_function_symbol (&isym))
|
||||
{
|
||||
const char *name;
|
||||
int16_t secnum;
|
||||
|
||||
if (asym->name.short_name[0] != 0)
|
||||
{
|
||||
size_t len = coff_short_name_len (isym.name);
|
||||
name = coff_str;
|
||||
memcpy (coff_str, isym.name, len);
|
||||
coff_str[len] = 0;
|
||||
coff_str += len + 1;
|
||||
}
|
||||
else
|
||||
name = isym.name;
|
||||
|
||||
if (!is_64)
|
||||
{
|
||||
/* Strip leading '_'. */
|
||||
if (name[0] == '_')
|
||||
name++;
|
||||
}
|
||||
|
||||
/* Symbol value is section relative, so we need to read the address
|
||||
of its section. */
|
||||
secnum = coff_read2 (asym->section_number);
|
||||
|
||||
coff_sym->name = name;
|
||||
coff_sym->address = (coff_read4 (asym->value)
|
||||
+ sects[secnum - 1].virtual_address
|
||||
+ base_address);
|
||||
coff_sym++;
|
||||
}
|
||||
|
||||
i += asym->number_of_aux_symbols;
|
||||
}
|
||||
|
||||
/* End of symbols marker. */
|
||||
coff_sym->name = NULL;
|
||||
coff_sym->address = -1;
|
||||
|
||||
backtrace_qsort (coff_symbols, coff_symbol_count,
|
||||
sizeof (struct coff_symbol), coff_symbol_compare);
|
||||
|
||||
sdata->next = NULL;
|
||||
sdata->symbols = coff_symbols;
|
||||
sdata->count = coff_symbol_count;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Add EDATA to the list in STATE. */
|
||||
|
||||
static void
|
||||
coff_add_syminfo_data (struct backtrace_state *state,
|
||||
struct coff_syminfo_data *sdata)
|
||||
{
|
||||
if (!state->threaded)
|
||||
{
|
||||
struct coff_syminfo_data **pp;
|
||||
|
||||
for (pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
|
||||
*pp != NULL;
|
||||
pp = &(*pp)->next)
|
||||
;
|
||||
*pp = sdata;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
struct coff_syminfo_data **pp;
|
||||
|
||||
pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct coff_syminfo_data *p;
|
||||
|
||||
p = backtrace_atomic_load_pointer (pp);
|
||||
|
||||
if (p == NULL)
|
||||
break;
|
||||
|
||||
pp = &p->next;
|
||||
}
|
||||
|
||||
if (__sync_bool_compare_and_swap (pp, NULL, sdata))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare an ADDR against an elf_symbol for bsearch. We allocate one
|
||||
extra entry in the array so that this can look safely at the next
|
||||
entry. */
|
||||
|
||||
static int
|
||||
coff_symbol_search (const void *vkey, const void *ventry)
|
||||
{
|
||||
const uintptr_t *key = (const uintptr_t *) vkey;
|
||||
const struct coff_symbol *entry = (const struct coff_symbol *) ventry;
|
||||
uintptr_t addr;
|
||||
|
||||
addr = *key;
|
||||
if (addr < entry->address)
|
||||
return -1;
|
||||
else if (addr >= entry[1].address)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the symbol name and value for an ADDR. */
|
||||
|
||||
static void
|
||||
coff_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||
backtrace_syminfo_callback callback,
|
||||
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
|
||||
void *data)
|
||||
{
|
||||
struct coff_syminfo_data *sdata;
|
||||
struct coff_symbol *sym = NULL;
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
for (sdata = (struct coff_syminfo_data *) state->syminfo_data;
|
||||
sdata != NULL;
|
||||
sdata = sdata->next)
|
||||
{
|
||||
sym = ((struct coff_symbol *)
|
||||
bsearch (&addr, sdata->symbols, sdata->count,
|
||||
sizeof (struct coff_symbol), coff_symbol_search));
|
||||
if (sym != NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct coff_syminfo_data **pp;
|
||||
|
||||
pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
|
||||
while (1)
|
||||
{
|
||||
sdata = backtrace_atomic_load_pointer (pp);
|
||||
if (sdata == NULL)
|
||||
break;
|
||||
|
||||
sym = ((struct coff_symbol *)
|
||||
bsearch (&addr, sdata->symbols, sdata->count,
|
||||
sizeof (struct coff_symbol), coff_symbol_search));
|
||||
if (sym != NULL)
|
||||
break;
|
||||
|
||||
pp = &sdata->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (sym == NULL)
|
||||
callback (data, addr, NULL, 0, 0);
|
||||
else
|
||||
callback (data, addr, sym->name, sym->address, 0);
|
||||
}
|
||||
|
||||
/* Add the backtrace data for one PE/COFF file. Returns 1 on success,
|
||||
0 on failure (in both cases descriptor is closed). */
|
||||
|
||||
static int
|
||||
coff_add (struct backtrace_state *state, int descriptor,
|
||||
backtrace_error_callback error_callback, void *data,
|
||||
fileline *fileline_fn, int *found_sym, int *found_dwarf)
|
||||
{
|
||||
struct backtrace_view fhdr_view;
|
||||
off_t fhdr_off;
|
||||
int magic_ok;
|
||||
b_coff_file_header fhdr;
|
||||
off_t opt_sects_off;
|
||||
size_t opt_sects_size;
|
||||
unsigned int sects_num;
|
||||
struct backtrace_view sects_view;
|
||||
int sects_view_valid;
|
||||
const b_coff_optional_header *opt_hdr;
|
||||
const b_coff_section_header *sects;
|
||||
struct backtrace_view str_view;
|
||||
int str_view_valid;
|
||||
size_t str_size;
|
||||
off_t str_off;
|
||||
struct backtrace_view syms_view;
|
||||
off_t syms_off;
|
||||
size_t syms_size;
|
||||
int syms_view_valid;
|
||||
unsigned int syms_num;
|
||||
unsigned int i;
|
||||
struct debug_section_info sections[DEBUG_MAX];
|
||||
off_t min_offset;
|
||||
off_t max_offset;
|
||||
struct backtrace_view debug_view;
|
||||
int debug_view_valid;
|
||||
int is_64;
|
||||
uintptr_t image_base;
|
||||
struct dwarf_sections dwarf_sections;
|
||||
|
||||
*found_sym = 0;
|
||||
*found_dwarf = 0;
|
||||
|
||||
sects_view_valid = 0;
|
||||
syms_view_valid = 0;
|
||||
str_view_valid = 0;
|
||||
debug_view_valid = 0;
|
||||
|
||||
/* Map the MS-DOS stub (if any) and extract file header offset. */
|
||||
if (!backtrace_get_view (state, descriptor, 0, 0x40, error_callback,
|
||||
data, &fhdr_view))
|
||||
goto fail;
|
||||
|
||||
{
|
||||
const unsigned char *vptr = fhdr_view.data;
|
||||
|
||||
if (vptr[0] == 'M' && vptr[1] == 'Z')
|
||||
fhdr_off = coff_read4 (vptr + 0x3c);
|
||||
else
|
||||
fhdr_off = 0;
|
||||
}
|
||||
|
||||
backtrace_release_view (state, &fhdr_view, error_callback, data);
|
||||
|
||||
/* Map the coff file header. */
|
||||
if (!backtrace_get_view (state, descriptor, fhdr_off,
|
||||
sizeof (b_coff_file_header) + 4,
|
||||
error_callback, data, &fhdr_view))
|
||||
goto fail;
|
||||
|
||||
if (fhdr_off != 0)
|
||||
{
|
||||
const char *magic = (const char *) fhdr_view.data;
|
||||
magic_ok = memcmp (magic, "PE\0", 4) == 0;
|
||||
fhdr_off += 4;
|
||||
|
||||
memcpy (&fhdr, fhdr_view.data + 4, sizeof fhdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (&fhdr, fhdr_view.data, sizeof fhdr);
|
||||
/* TODO: test fhdr.machine for coff but non-PE platforms. */
|
||||
magic_ok = 0;
|
||||
}
|
||||
backtrace_release_view (state, &fhdr_view, error_callback, data);
|
||||
|
||||
if (!magic_ok)
|
||||
{
|
||||
error_callback (data, "executable file is not COFF", 0);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sects_num = fhdr.number_of_sections;
|
||||
syms_num = fhdr.number_of_symbols;
|
||||
|
||||
opt_sects_off = fhdr_off + sizeof (fhdr);
|
||||
opt_sects_size = (fhdr.size_of_optional_header
|
||||
+ sects_num * sizeof (b_coff_section_header));
|
||||
|
||||
/* To translate PC to file/line when using DWARF, we need to find
|
||||
the .debug_info and .debug_line sections. */
|
||||
|
||||
/* Read the optional header and the section headers. */
|
||||
|
||||
if (!backtrace_get_view (state, descriptor, opt_sects_off, opt_sects_size,
|
||||
error_callback, data, §s_view))
|
||||
goto fail;
|
||||
sects_view_valid = 1;
|
||||
opt_hdr = (const b_coff_optional_header *) sects_view.data;
|
||||
sects = (const b_coff_section_header *)
|
||||
(sects_view.data + fhdr.size_of_optional_header);
|
||||
|
||||
is_64 = 0;
|
||||
if (fhdr.size_of_optional_header > sizeof (*opt_hdr))
|
||||
{
|
||||
if (opt_hdr->magic == PE_MAGIC)
|
||||
image_base = opt_hdr->u.pe.image_base;
|
||||
else if (opt_hdr->magic == PEP_MAGIC)
|
||||
{
|
||||
image_base = opt_hdr->u.pep.image_base;
|
||||
is_64 = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
error_callback (data, "bad magic in PE optional header", 0);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
image_base = 0;
|
||||
|
||||
/* Read the symbol table and the string table. */
|
||||
|
||||
if (fhdr.pointer_to_symbol_table == 0)
|
||||
{
|
||||
/* No symbol table, no string table. */
|
||||
str_off = 0;
|
||||
str_size = 0;
|
||||
syms_num = 0;
|
||||
syms_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Symbol table is followed by the string table. The string table
|
||||
starts with its length (on 4 bytes).
|
||||
Map the symbol table and the length of the string table. */
|
||||
syms_off = fhdr.pointer_to_symbol_table;
|
||||
syms_size = syms_num * SYM_SZ;
|
||||
|
||||
if (!backtrace_get_view (state, descriptor, syms_off, syms_size + 4,
|
||||
error_callback, data, &syms_view))
|
||||
goto fail;
|
||||
syms_view_valid = 1;
|
||||
|
||||
str_size = coff_read4 (syms_view.data + syms_size);
|
||||
|
||||
str_off = syms_off + syms_size;
|
||||
|
||||
if (str_size > 4)
|
||||
{
|
||||
/* Map string table (including the length word). */
|
||||
|
||||
if (!backtrace_get_view (state, descriptor, str_off, str_size,
|
||||
error_callback, data, &str_view))
|
||||
goto fail;
|
||||
str_view_valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
memset (sections, 0, sizeof sections);
|
||||
|
||||
/* Look for the symbol table. */
|
||||
for (i = 0; i < sects_num; ++i)
|
||||
{
|
||||
const b_coff_section_header *s = sects + i;
|
||||
unsigned int str_off;
|
||||
int j;
|
||||
|
||||
if (s->name[0] == '/')
|
||||
{
|
||||
/* Extended section name. */
|
||||
str_off = atoi (s->name + 1);
|
||||
}
|
||||
else
|
||||
str_off = 0;
|
||||
|
||||
for (j = 0; j < (int) DEBUG_MAX; ++j)
|
||||
{
|
||||
const char *dbg_name = debug_section_names[j];
|
||||
int match;
|
||||
|
||||
if (str_off != 0)
|
||||
match = coff_long_name_eq (dbg_name, str_off, &str_view);
|
||||
else
|
||||
match = coff_short_name_eq (dbg_name, s->name);
|
||||
if (match)
|
||||
{
|
||||
sections[j].offset = s->pointer_to_raw_data;
|
||||
sections[j].size = s->virtual_size <= s->size_of_raw_data ?
|
||||
s->virtual_size : s->size_of_raw_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (syms_num != 0)
|
||||
{
|
||||
struct coff_syminfo_data *sdata;
|
||||
|
||||
sdata = ((struct coff_syminfo_data *)
|
||||
backtrace_alloc (state, sizeof *sdata, error_callback, data));
|
||||
if (sdata == NULL)
|
||||
goto fail;
|
||||
|
||||
if (!coff_initialize_syminfo (state, image_base, is_64,
|
||||
sects, sects_num,
|
||||
syms_view.data, syms_size,
|
||||
str_view.data, str_size,
|
||||
error_callback, data, sdata))
|
||||
{
|
||||
backtrace_free (state, sdata, sizeof *sdata, error_callback, data);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*found_sym = 1;
|
||||
|
||||
coff_add_syminfo_data (state, sdata);
|
||||
}
|
||||
|
||||
backtrace_release_view (state, §s_view, error_callback, data);
|
||||
sects_view_valid = 0;
|
||||
if (syms_view_valid)
|
||||
{
|
||||
backtrace_release_view (state, &syms_view, error_callback, data);
|
||||
syms_view_valid = 0;
|
||||
}
|
||||
|
||||
/* Read all the debug sections in a single view, since they are
|
||||
probably adjacent in the file. We never release this view. */
|
||||
|
||||
min_offset = 0;
|
||||
max_offset = 0;
|
||||
for (i = 0; i < (int) DEBUG_MAX; ++i)
|
||||
{
|
||||
off_t end;
|
||||
|
||||
if (sections[i].size == 0)
|
||||
continue;
|
||||
if (min_offset == 0 || sections[i].offset < min_offset)
|
||||
min_offset = sections[i].offset;
|
||||
end = sections[i].offset + sections[i].size;
|
||||
if (end > max_offset)
|
||||
max_offset = end;
|
||||
}
|
||||
if (min_offset == 0 || max_offset == 0)
|
||||
{
|
||||
if (!backtrace_close (descriptor, error_callback, data))
|
||||
goto fail;
|
||||
*fileline_fn = coff_nodebug;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!backtrace_get_view (state, descriptor, min_offset,
|
||||
max_offset - min_offset,
|
||||
error_callback, data, &debug_view))
|
||||
goto fail;
|
||||
debug_view_valid = 1;
|
||||
|
||||
/* We've read all we need from the executable. */
|
||||
if (!backtrace_close (descriptor, error_callback, data))
|
||||
goto fail;
|
||||
descriptor = -1;
|
||||
|
||||
for (i = 0; i < (int) DEBUG_MAX; ++i)
|
||||
{
|
||||
size_t size = sections[i].size;
|
||||
dwarf_sections.size[i] = size;
|
||||
if (size == 0)
|
||||
dwarf_sections.data[i] = NULL;
|
||||
else
|
||||
dwarf_sections.data[i] = ((const unsigned char *) debug_view.data
|
||||
+ (sections[i].offset - min_offset));
|
||||
}
|
||||
|
||||
if (!backtrace_dwarf_add (state, /* base_address */ 0, &dwarf_sections,
|
||||
0, /* FIXME: is_bigendian */
|
||||
NULL, /* altlink */
|
||||
error_callback, data, fileline_fn,
|
||||
NULL /* returned fileline_entry */))
|
||||
goto fail;
|
||||
|
||||
*found_dwarf = 1;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
if (sects_view_valid)
|
||||
backtrace_release_view (state, §s_view, error_callback, data);
|
||||
if (str_view_valid)
|
||||
backtrace_release_view (state, &str_view, error_callback, data);
|
||||
if (syms_view_valid)
|
||||
backtrace_release_view (state, &syms_view, error_callback, data);
|
||||
if (debug_view_valid)
|
||||
backtrace_release_view (state, &debug_view, error_callback, data);
|
||||
if (descriptor != -1)
|
||||
backtrace_close (descriptor, error_callback, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize the backtrace data we need from an ELF executable. At
|
||||
the ELF level, all we need to do is find the debug info
|
||||
sections. */
|
||||
|
||||
int
|
||||
backtrace_initialize (struct backtrace_state *state,
|
||||
const char *filename ATTRIBUTE_UNUSED, int descriptor,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data, fileline *fileline_fn)
|
||||
{
|
||||
int ret;
|
||||
int found_sym;
|
||||
int found_dwarf;
|
||||
fileline coff_fileline_fn;
|
||||
|
||||
ret = coff_add (state, descriptor, error_callback, data,
|
||||
&coff_fileline_fn, &found_sym, &found_dwarf);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
if (found_sym)
|
||||
state->syminfo_fn = coff_syminfo;
|
||||
else if (state->syminfo_fn == NULL)
|
||||
state->syminfo_fn = coff_nosyms;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (found_sym)
|
||||
backtrace_atomic_store_pointer (&state->syminfo_fn, coff_syminfo);
|
||||
else
|
||||
(void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
|
||||
coff_nosyms);
|
||||
}
|
||||
|
||||
if (!state->threaded)
|
||||
{
|
||||
if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug)
|
||||
*fileline_fn = coff_fileline_fn;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileline current_fn;
|
||||
|
||||
current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
|
||||
if (current_fn == NULL || current_fn == coff_nodebug)
|
||||
*fileline_fn = coff_fileline_fn;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/* posix.c -- POSIX file I/O routines for the backtrace library.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
#ifndef FD_CLOEXEC
|
||||
#define FD_CLOEXEC 1
|
||||
#endif
|
||||
|
||||
/* Open a file for reading. */
|
||||
|
||||
int
|
||||
backtrace_open (const char *filename, backtrace_error_callback error_callback,
|
||||
void *data, int *does_not_exist)
|
||||
{
|
||||
int descriptor;
|
||||
|
||||
if (does_not_exist != NULL)
|
||||
*does_not_exist = 0;
|
||||
|
||||
descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC));
|
||||
if (descriptor < 0)
|
||||
{
|
||||
/* If DOES_NOT_EXIST is not NULL, then don't call ERROR_CALLBACK
|
||||
if the file does not exist. We treat lacking permission to
|
||||
open the file as the file not existing; this case arises when
|
||||
running the libgo syscall package tests as root. */
|
||||
if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES))
|
||||
*does_not_exist = 1;
|
||||
else
|
||||
error_callback (data, filename, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_FCNTL
|
||||
/* Set FD_CLOEXEC just in case the kernel does not support
|
||||
O_CLOEXEC. It doesn't matter if this fails for some reason.
|
||||
FIXME: At some point it should be safe to only do this if
|
||||
O_CLOEXEC == 0. */
|
||||
fcntl (descriptor, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/* Close DESCRIPTOR. */
|
||||
|
||||
int
|
||||
backtrace_close (int descriptor, backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
if (close (descriptor) < 0)
|
||||
{
|
||||
error_callback (data, "close", errno);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/* print.c -- Print the current backtrace.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* Passed to callbacks. */
|
||||
|
||||
struct print_data
|
||||
{
|
||||
struct backtrace_state *state;
|
||||
FILE *f;
|
||||
};
|
||||
|
||||
/* Print one level of a backtrace. */
|
||||
|
||||
static int
|
||||
print_callback (void *data, uintptr_t pc, const char *filename, int lineno,
|
||||
const char *function)
|
||||
{
|
||||
struct print_data *pdata = (struct print_data *) data;
|
||||
|
||||
fprintf (pdata->f, "0x%lx %s\n\t%s:%d\n",
|
||||
(unsigned long) pc,
|
||||
function == NULL ? "???" : function,
|
||||
filename == NULL ? "???" : filename,
|
||||
lineno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print errors to stderr. */
|
||||
|
||||
static void
|
||||
error_callback (void *data, const char *msg, int errnum)
|
||||
{
|
||||
struct print_data *pdata = (struct print_data *) data;
|
||||
|
||||
if (pdata->state->filename != NULL)
|
||||
fprintf (stderr, "%s: ", pdata->state->filename);
|
||||
fprintf (stderr, "libbacktrace: %s", msg);
|
||||
if (errnum > 0)
|
||||
fprintf (stderr, ": %s", strerror (errnum));
|
||||
fputc ('\n', stderr);
|
||||
}
|
||||
|
||||
/* Print a backtrace. */
|
||||
|
||||
void __attribute__((noinline))
|
||||
backtrace_print (struct backtrace_state *state, int skip, FILE *f)
|
||||
{
|
||||
struct print_data data;
|
||||
|
||||
data.state = state;
|
||||
data.f = f;
|
||||
backtrace_full (state, skip + 1, print_callback, error_callback,
|
||||
(void *) &data);
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* read.c -- File views without mmap.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* This file implements file views when mmap is not available. */
|
||||
|
||||
/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */
|
||||
|
||||
int
|
||||
backtrace_get_view (struct backtrace_state *state, int descriptor,
|
||||
off_t offset, uint64_t size,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data, struct backtrace_view *view)
|
||||
{
|
||||
uint64_t got;
|
||||
ssize_t r;
|
||||
|
||||
if ((uint64_t) (size_t) size != size)
|
||||
{
|
||||
error_callback (data, "file size too large", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek (descriptor, offset, SEEK_SET) < 0)
|
||||
{
|
||||
error_callback (data, "lseek", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
view->base = backtrace_alloc (state, size, error_callback, data);
|
||||
if (view->base == NULL)
|
||||
return 0;
|
||||
view->data = view->base;
|
||||
view->len = size;
|
||||
|
||||
got = 0;
|
||||
while (got < size)
|
||||
{
|
||||
r = read (descriptor, view->base, size - got);
|
||||
if (r < 0)
|
||||
{
|
||||
error_callback (data, "read", errno);
|
||||
free (view->base);
|
||||
return 0;
|
||||
}
|
||||
if (r == 0)
|
||||
break;
|
||||
got += (uint64_t) r;
|
||||
}
|
||||
|
||||
if (got < size)
|
||||
{
|
||||
error_callback (data, "file too short", 0);
|
||||
free (view->base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Release a view read by backtrace_get_view. */
|
||||
|
||||
void
|
||||
backtrace_release_view (struct backtrace_state *state,
|
||||
struct backtrace_view *view,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
backtrace_free (state, view->base, view->len, error_callback, data);
|
||||
view->data = NULL;
|
||||
view->base = NULL;
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* simple.c -- The backtrace_simple function.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "unwind.h"
|
||||
#include "backtrace.h"
|
||||
|
||||
/* The simple_backtrace routine. */
|
||||
|
||||
/* Data passed through _Unwind_Backtrace. */
|
||||
|
||||
struct backtrace_simple_data
|
||||
{
|
||||
/* Number of frames to skip. */
|
||||
int skip;
|
||||
/* Library state. */
|
||||
struct backtrace_state *state;
|
||||
/* Callback routine. */
|
||||
backtrace_simple_callback callback;
|
||||
/* Error callback routine. */
|
||||
backtrace_error_callback error_callback;
|
||||
/* Data to pass to callback routine. */
|
||||
void *data;
|
||||
/* Value to return from backtrace. */
|
||||
int ret;
|
||||
};
|
||||
|
||||
/* Unwind library callback routine. This is passed to
|
||||
_Unwind_Backtrace. */
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
simple_unwind (struct _Unwind_Context *context, void *vdata)
|
||||
{
|
||||
struct backtrace_simple_data *bdata = (struct backtrace_simple_data *) vdata;
|
||||
uintptr_t pc;
|
||||
int ip_before_insn = 0;
|
||||
|
||||
#ifdef HAVE_GETIPINFO
|
||||
pc = _Unwind_GetIPInfo (context, &ip_before_insn);
|
||||
#else
|
||||
pc = _Unwind_GetIP (context);
|
||||
#endif
|
||||
|
||||
if (bdata->skip > 0)
|
||||
{
|
||||
--bdata->skip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
if (!ip_before_insn)
|
||||
--pc;
|
||||
|
||||
bdata->ret = bdata->callback (bdata->data, pc);
|
||||
|
||||
if (bdata->ret != 0)
|
||||
return _URC_END_OF_STACK;
|
||||
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
/* Get a simple stack backtrace. */
|
||||
|
||||
int __attribute__((noinline))
|
||||
backtrace_simple (struct backtrace_state *state, int skip,
|
||||
backtrace_simple_callback callback,
|
||||
backtrace_error_callback error_callback, void *data)
|
||||
{
|
||||
struct backtrace_simple_data bdata;
|
||||
|
||||
bdata.skip = skip + 1;
|
||||
bdata.state = state;
|
||||
bdata.callback = callback;
|
||||
bdata.error_callback = error_callback;
|
||||
bdata.data = data;
|
||||
bdata.ret = 0;
|
||||
_Unwind_Backtrace (simple_unwind, &bdata);
|
||||
return bdata.ret;
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* sort.c -- Sort without allocating memory
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* The GNU glibc version of qsort allocates memory, which we must not
|
||||
do if we are invoked by a signal handler. So provide our own
|
||||
sort. */
|
||||
|
||||
static void
|
||||
swap (char *a, char *b, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++, a++, b++)
|
||||
{
|
||||
char t;
|
||||
|
||||
t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
backtrace_qsort (void *basearg, size_t count, size_t size,
|
||||
int (*compar) (const void *, const void *))
|
||||
{
|
||||
char *base = (char *) basearg;
|
||||
size_t i;
|
||||
size_t mid;
|
||||
|
||||
tail_recurse:
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
/* The symbol table and DWARF tables, which is all we use this
|
||||
routine for, tend to be roughly sorted. Pick the middle element
|
||||
in the array as our pivot point, so that we are more likely to
|
||||
cut the array in half for each recursion step. */
|
||||
swap (base, base + (count / 2) * size, size);
|
||||
|
||||
mid = 0;
|
||||
for (i = 1; i < count; i++)
|
||||
{
|
||||
if ((*compar) (base, base + i * size) > 0)
|
||||
{
|
||||
++mid;
|
||||
if (i != mid)
|
||||
swap (base + mid * size, base + i * size, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (mid > 0)
|
||||
swap (base, base + mid * size, size);
|
||||
|
||||
/* Recurse with the smaller array, loop with the larger one. That
|
||||
ensures that our maximum stack depth is log count. */
|
||||
if (2 * mid < count)
|
||||
{
|
||||
backtrace_qsort (base, mid, size, compar);
|
||||
base += (mid + 1) * size;
|
||||
count -= mid + 1;
|
||||
goto tail_recurse;
|
||||
}
|
||||
else
|
||||
{
|
||||
backtrace_qsort (base + (mid + 1) * size, count - (mid + 1),
|
||||
size, compar);
|
||||
count = mid;
|
||||
goto tail_recurse;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* state.c -- Create the backtrace state.
|
||||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Google.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
(2) Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
(3) The name of the author may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "backtrace-supported.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* Create the backtrace state. This will then be passed to all the
|
||||
other routines. */
|
||||
|
||||
struct backtrace_state *
|
||||
backtrace_create_state (const char *filename, int threaded,
|
||||
backtrace_error_callback error_callback,
|
||||
void *data)
|
||||
{
|
||||
struct backtrace_state init_state;
|
||||
struct backtrace_state *state;
|
||||
|
||||
#ifndef HAVE_SYNC_FUNCTIONS
|
||||
if (threaded)
|
||||
{
|
||||
error_callback (data, "backtrace library does not support threads", 0);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset (&init_state, 0, sizeof init_state);
|
||||
init_state.filename = filename;
|
||||
init_state.threaded = threaded;
|
||||
|
||||
state = ((struct backtrace_state *)
|
||||
backtrace_alloc (&init_state, sizeof *state, error_callback, data));
|
||||
if (state == NULL)
|
||||
return NULL;
|
||||
*state = init_state;
|
||||
|
||||
return state;
|
||||
}
|
Loading…
Reference in New Issue