construct/ircd/backtrace.cc

109 lines
2.5 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#include <RB_INC_EXECINFO_H
#include <RB_INC_SYS_MMAN_H
#if defined(__GNUC__) && defined(HAVE_EXECINFO_H)
#define IRCD_BACKTRACE_SUPPORT
#endif
#if defined(IRCD_BACKTRACE_SUPPORT) && defined(HAVE_SYS_MMAN_H) && !defined(__clang__)
#define IRCD_BACKTRACE_GLIBC_WORKAROUND
#endif
#if defined(IRCD_BACKTRACE_GLIBC_WORKAROUND)
// The problem here is that backtrace(3) performs checks against this value
// which is not correct for our own allocated `ircd::ctx` stacks. To workaround
// this we neutralize the value before calling backtrace(3) and restore it
// afterward. While this symbol is extern accessible, the page its on is
// write-protected at some point, so we have to re-allow writing to it here.
//
// sysdeps/i386/backtrace.c | elf/dl-support.c
// > This is a global variable set at program start time. It marks the
// > highest used stack address.
//
extern void *
__libc_stack_end;
void
__attribute__((constructor))
ircd_backtrace_allow_libc_fix()
{
static const auto &prot
{
PROT_READ | PROT_WRITE
};
const auto &addr
{
uintptr_t(&__libc_stack_end) & ~(getpagesize() - 1UL)
};
ircd::syscall(::mprotect, (void *)addr, sizeof(__libc_stack_end), prot);
}
#endif defined(IRCD_BACKTRACE_GLIBC_WORKAROUND)
//
// backtrace::backtrace
//
namespace ircd
{
static thread_local std::array<const void *, 512> backtrace_buffer;
}
ircd::backtrace::backtrace()
:backtrace
{
backtrace_buffer.data(),
backtrace_buffer.size()
}
{
}
ircd::backtrace::backtrace(const mutable_buffer &buf)
:backtrace
{
reinterpret_cast<const void **>
(
const_cast<const char **>
(
std::addressof(data(buf))
)
),
buffer::size(buf) / sizeof(void *)
}
{
}
ircd::backtrace::backtrace(const void **const &array,
const size_t &count)
:array
{
array
}
,count
{
0UL
}
{
#if defined(IRCD_BACKTRACE_GLIBC_WORKAROUND)
const scope_restore stack_check_workaround
{
__libc_stack_end, reinterpret_cast<void *>(uintptr_t(-1UL))
};
#endif
#if defined(IRCD_BACKTRACE_SUPPORT)
this->count = ::backtrace(const_cast<void **>(this->array), count);
#endif
}