2012-08-28 22:27:06 +08:00
|
|
|
//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2012-08-28 22:27:06 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file is shared between AddressSanitizer and ThreadSanitizer
|
|
|
|
// run-time libraries.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SANITIZER_STACKTRACE_H
|
|
|
|
#define SANITIZER_STACKTRACE_H
|
|
|
|
|
2012-08-28 22:48:28 +08:00
|
|
|
#include "sanitizer_internal_defs.h"
|
2012-08-28 22:27:06 +08:00
|
|
|
|
|
|
|
namespace __sanitizer {
|
|
|
|
|
2015-01-22 21:33:16 +08:00
|
|
|
static const u32 kStackTraceMax = 256;
|
2012-08-28 22:27:06 +08:00
|
|
|
|
[Sanitizers] Disable SANITIZER_CAN_FAST_UNWIND on all SPARC targets
While testing my to-be-submitted Solaris sanitizer support on gcc mainline, I ran into
an issue on Solaris/SPARC (sparc-sun-solaris2.11). Initially libasan.so failed to link:
Undefined first referenced
symbol in file
__sanitizer::BufferedStackTrace::FastUnwindStack(unsigned long, unsigned long, unsigned long, unsigned long, unsigned int) /var/gcc/gcc-9.0.0-20181024/11.5-gcc-gas/sparc-sun-solaris2.11/./libsanitizer/asan/.libs/libasan.so
This happens because SANITIZER_CAN_FAST_UNWIND is enabled on non-Linux
SPARC targets (cf. sanitizer_stacktrace.h), but the guard around the SPARCv8-only
definition in sanitizer_stacktrace_sparc.cc only works with clang:
clang predefines __sparcv8__ on non-Solaris, and __sparcv8 only on Solaris
gcc predefines __sparcv8 on Solaris, but __sparc_v8__ on non-Solaris
The attached patch allows for all three variants.
However, disabling SANITIZER_CAN_FAST_UNWIND on all SPARC targets
fixes a couple of testsuite failures in the Solaris asan testsuite, so for now it's better
to keep it disabled everywhere.
This allowed the libsanitizer build to complete and gave reasonable (though slightly
worse than on Solaris/x86) testsuite results.
Differential Revision: https://reviews.llvm.org/D54099
llvm-svn: 346155
2018-11-06 03:22:54 +08:00
|
|
|
#if defined(__sparc__) || (SANITIZER_LINUX && defined(__mips__))
|
2013-11-07 14:33:06 +08:00
|
|
|
# define SANITIZER_CAN_FAST_UNWIND 0
|
|
|
|
#elif SANITIZER_WINDOWS
|
|
|
|
# define SANITIZER_CAN_FAST_UNWIND 0
|
2018-03-03 20:12:03 +08:00
|
|
|
#elif SANITIZER_OPENBSD
|
|
|
|
# define SANITIZER_CAN_FAST_UNWIND 0
|
2013-05-08 20:45:55 +08:00
|
|
|
#else
|
2013-11-07 14:33:06 +08:00
|
|
|
# define SANITIZER_CAN_FAST_UNWIND 1
|
2013-05-08 20:45:55 +08:00
|
|
|
#endif
|
|
|
|
|
2014-11-10 23:22:04 +08:00
|
|
|
// Fast unwind is the only option on Mac for now; we will need to
|
|
|
|
// revisit this macro when slow unwind works on Mac, see
|
2015-12-05 01:50:03 +08:00
|
|
|
// https://github.com/google/sanitizers/issues/137
|
2018-05-18 08:43:54 +08:00
|
|
|
#if SANITIZER_MAC || SANITIZER_OPENBSD || SANITIZER_RTEMS
|
2014-11-10 23:22:04 +08:00
|
|
|
# define SANITIZER_CAN_SLOW_UNWIND 0
|
|
|
|
#else
|
|
|
|
# define SANITIZER_CAN_SLOW_UNWIND 1
|
|
|
|
#endif
|
|
|
|
|
2012-08-28 22:27:06 +08:00
|
|
|
struct StackTrace {
|
2014-10-26 11:35:14 +08:00
|
|
|
const uptr *trace;
|
2015-01-22 21:33:16 +08:00
|
|
|
u32 size;
|
|
|
|
u32 tag;
|
2013-10-12 20:40:47 +08:00
|
|
|
|
2015-01-22 21:33:16 +08:00
|
|
|
static const int TAG_UNKNOWN = 0;
|
|
|
|
static const int TAG_ALLOC = 1;
|
|
|
|
static const int TAG_DEALLOC = 2;
|
|
|
|
static const int TAG_CUSTOM = 100; // Tool specific tags start here.
|
|
|
|
|
|
|
|
StackTrace() : trace(nullptr), size(0), tag(0) {}
|
|
|
|
StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {}
|
|
|
|
StackTrace(const uptr *trace, u32 size, u32 tag)
|
|
|
|
: trace(trace), size(size), tag(tag) {}
|
2012-08-28 22:27:06 +08:00
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
// Prints a symbolized stacktrace, followed by an empty line.
|
|
|
|
void Print() const;
|
2012-08-28 22:27:06 +08:00
|
|
|
|
2013-11-07 15:28:33 +08:00
|
|
|
static bool WillUseFastUnwind(bool request_fast_unwind) {
|
|
|
|
if (!SANITIZER_CAN_FAST_UNWIND)
|
|
|
|
return false;
|
2014-11-10 23:22:04 +08:00
|
|
|
else if (!SANITIZER_CAN_SLOW_UNWIND)
|
2013-11-07 15:28:33 +08:00
|
|
|
return true;
|
|
|
|
return request_fast_unwind;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uptr GetCurrentPc();
|
2014-12-20 09:45:28 +08:00
|
|
|
static inline uptr GetPreviousInstructionPc(uptr pc);
|
2014-11-20 05:42:33 +08:00
|
|
|
static uptr GetNextInstructionPc(uptr pc);
|
2014-10-26 11:35:14 +08:00
|
|
|
typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
|
|
|
|
int out_size);
|
|
|
|
};
|
|
|
|
|
2014-12-20 09:45:28 +08:00
|
|
|
// Performance-critical, must be in the header.
|
|
|
|
ALWAYS_INLINE
|
|
|
|
uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
|
|
|
|
#if defined(__arm__)
|
2018-05-23 17:18:10 +08:00
|
|
|
// T32 (Thumb) branch instructions might be 16 or 32 bit long,
|
|
|
|
// so we return (pc-2) in that case in order to be safe.
|
|
|
|
// For A32 mode we return (pc-4) because all instructions are 32 bit long.
|
|
|
|
return (pc - 3) & (~1);
|
|
|
|
#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
|
2014-12-20 09:45:28 +08:00
|
|
|
// PCs are always 4 byte aligned.
|
|
|
|
return pc - 4;
|
|
|
|
#elif defined(__sparc__) || defined(__mips__)
|
|
|
|
return pc - 8;
|
|
|
|
#else
|
|
|
|
return pc - 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
// StackTrace that owns the buffer used to store the addresses.
|
|
|
|
struct BufferedStackTrace : public StackTrace {
|
|
|
|
uptr trace_buffer[kStackTraceMax];
|
|
|
|
uptr top_frame_bp; // Optional bp of a top frame.
|
|
|
|
|
|
|
|
BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
|
|
|
|
|
2014-11-04 06:23:44 +08:00
|
|
|
void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
|
2015-01-22 21:33:16 +08:00
|
|
|
void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
|
2014-10-26 11:35:14 +08:00
|
|
|
uptr stack_bottom, bool request_fast_unwind);
|
2013-11-07 15:28:33 +08:00
|
|
|
|
2017-09-14 11:06:35 +08:00
|
|
|
void Reset() {
|
|
|
|
*static_cast<StackTrace *>(this) = StackTrace(trace_buffer, 0);
|
|
|
|
top_frame_bp = 0;
|
|
|
|
}
|
|
|
|
|
2013-11-07 15:28:33 +08:00
|
|
|
private:
|
2019-02-23 06:03:09 +08:00
|
|
|
void UnwindFast(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
|
|
|
|
u32 max_depth);
|
|
|
|
void UnwindSlow(uptr pc, u32 max_depth);
|
|
|
|
void UnwindSlow(uptr pc, void *context, u32 max_depth);
|
2013-12-10 03:52:39 +08:00
|
|
|
void PopStackFrames(uptr count);
|
2013-11-15 18:57:56 +08:00
|
|
|
uptr LocatePcInTrace(uptr pc);
|
2014-11-04 06:23:44 +08:00
|
|
|
|
2017-09-14 11:06:35 +08:00
|
|
|
BufferedStackTrace(const BufferedStackTrace &) = delete;
|
|
|
|
void operator=(const BufferedStackTrace &) = delete;
|
2012-08-28 22:27:06 +08:00
|
|
|
};
|
|
|
|
|
2016-05-18 14:09:26 +08:00
|
|
|
// Check if given pointer points into allocated stack area.
|
|
|
|
static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
|
|
|
|
return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
|
|
|
|
}
|
|
|
|
|
2012-08-28 22:27:06 +08:00
|
|
|
} // namespace __sanitizer
|
|
|
|
|
|
|
|
// Use this macro if you want to print stack trace with the caller
|
|
|
|
// of the current function in the top frame.
|
|
|
|
#define GET_CALLER_PC_BP_SP \
|
|
|
|
uptr bp = GET_CURRENT_FRAME(); \
|
|
|
|
uptr pc = GET_CALLER_PC(); \
|
|
|
|
uptr local_stack; \
|
|
|
|
uptr sp = (uptr)&local_stack
|
|
|
|
|
2014-07-30 07:22:41 +08:00
|
|
|
#define GET_CALLER_PC_BP \
|
|
|
|
uptr bp = GET_CURRENT_FRAME(); \
|
|
|
|
uptr pc = GET_CALLER_PC();
|
|
|
|
|
2012-08-28 22:27:06 +08:00
|
|
|
// Use this macro if you want to print stack trace with the current
|
|
|
|
// function in the top frame.
|
|
|
|
#define GET_CURRENT_PC_BP_SP \
|
|
|
|
uptr bp = GET_CURRENT_FRAME(); \
|
|
|
|
uptr pc = StackTrace::GetCurrentPc(); \
|
|
|
|
uptr local_stack; \
|
|
|
|
uptr sp = (uptr)&local_stack
|
|
|
|
|
|
|
|
|
|
|
|
#endif // SANITIZER_STACKTRACE_H
|