blob: 536fbc79a129bcdea23a37813c5db00c647d6a74 [file] [edit]
/* **********************************************************
* Copyright (c) 2010-2025 Google, Inc. All rights reserved.
* Copyright (c) 2007-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/* Dr. Memory: the memory debugger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License, and no later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _UTILS_H_
#define _UTILS_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
#ifdef WINDOWS
/* DRi#1424: avoid pulling in features from recent versions to keep compatibility */
# ifndef _WIN32_WINNT
# ifdef X64
# define _WIN32_WINNT _WIN32_WINNT_WIN2003
# else
# define _WIN32_WINNT _WIN32_WINNT_WINXP
# endif
# endif
#endif
#include "hashtable.h"
#include "dr_config.h" /* for DR_MAX_OPTIONS_LENGTH */
#include "dr_tools.h"
#include "drmgr.h"
#include "drsyms.h"
#include "drsyscall.h"
#include <limits.h>
#ifdef WINDOWS
# define IF_WINDOWS(x) x
# define IF_WINDOWS_(x) x,
# define _IF_WINDOWS(x) , x
# define IF_WINDOWS_ELSE(x,y) x
# define IF_UNIX(x)
# define IF_UNIX_ELSE(x,y) y
# define IF_LINUX(x)
# define IF_LINUX_ELSE(x,y) y
# define IF_UNIX_(x)
#else
# define IF_WINDOWS(x)
# define IF_WINDOWS_(x)
# define _IF_WINDOWS(x)
# define IF_WINDOWS_ELSE(x,y) y
# define IF_UNIX(x) x
# define IF_UNIX_ELSE(x,y) x
# define IF_UNIX_(x) x,
# ifdef LINUX
# define IF_LINUX(x) x
# define IF_LINUX_ELSE(x,y) x
# define IF_LINUX_(x) x,
# else
# define IF_LINUX(x)
# define IF_LINUX_ELSE(x,y) y
# define IF_LINUX_(x)
# endif
#endif
#ifdef MACOS
# define IF_MACOS(x) x
# define IF_MACOS_ELSE(x,y) x
# define IF_MACOS_(x) x,
#else
# define IF_MACOS(x)
# define IF_MACOS_ELSE(x,y) y
# define IF_MACOS_(x)
#endif
#ifdef ANDROID
# define IF_ANDROID(x) x
# define IF_ANDROID_ELSE(x,y) x
#else
# define IF_ANDROID(x)
# define IF_ANDROID_ELSE(x,y) y
#endif
#ifndef IF_X64
# ifdef X64
# define IF_X64(x) x
# else
# define IF_X64(x)
# endif
#endif
#ifndef IF_NOT_X64
# ifdef X64
# define IF_NOT_X64(x)
# else
# define IF_NOT_X64(x) x
# endif
#endif
#ifndef IF_NOT_X64_
# ifdef X64
# define IF_NOT_X64_(x)
# else
# define IF_NOT_X64_(x) x,
# endif
#endif
#ifdef X86
# ifndef IF_X86
# define IF_X86(x) x
# define IF_X86_ELSE(x, y) x
# endif
# ifdef X64
# ifndef IF_X86_32
# define IF_X86_32(x)
# endif
# define IF_X86_32_(x)
# else
# ifndef IF_X86_32
# define IF_X86_32(x) x
# endif
# define IF_X86_32_(x) x,
# endif
#else
# ifndef IF_X86
# define IF_X86(x)
# define IF_X86_ELSE(x, y) y
# endif
# define IF_X86_32_(x)
#endif
#ifndef IF_ARM
# ifdef ARM
# define IF_ARM(x) x
# define IF_ARM_ELSE(x, y) x
# else
# define IF_ARM(x)
# define IF_ARM_ELSE(x, y) y
# endif
#endif
#ifdef DEBUG
# define IF_DEBUG(x) x
# define IF_DEBUG_ELSE(x,y) x
# define _IF_DEBUG(x) , x
#else
# define IF_DEBUG(x)
# define IF_DEBUG_ELSE(x,y) y
# define _IF_DEBUG(x)
#endif
#ifdef VMX86_SERVER
# define IF_VMX86(x) x
#else
# define IF_VMX86(x)
#endif
#ifdef TOOL_DR_MEMORY
# define IF_DRMEM(x) x
# define IF_DRHEAP(x)
# define IF_DRMEM_ELSE(x, y) x
#else
# define IF_DRMEM(x)
# define IF_DRHEAP(x) x
# define IF_DRMEM_ELSE(x, y) y
#endif
#define ALIGNED(x, alignment) ((((ptr_uint_t)x) & ((alignment)-1)) == 0)
#define ALIGN_BACKWARD(x, alignment) (((ptr_uint_t)x) & (~((alignment)-1)))
#define ALIGN_FORWARD(x, alignment) \
((((ptr_uint_t)x) + ((alignment)-1)) & (~((alignment)-1)))
#define ALIGN_MOD(addr, size, alignment) \
((((ptr_uint_t)addr)+(size)-1) & ((alignment)-1))
#define CROSSES_ALIGNMENT(addr, size, alignment) \
(ALIGN_MOD(addr, size, alignment) < (size)-1)
#ifndef TESTANY
# define TEST(mask, var) (((mask) & (var)) != 0)
# define TESTANY TEST
# define TESTALL(mask, var) (((mask) & (var)) == (mask))
# define TESTONE(mask, var) test_one_bit_set((mask) & (var))
#endif
#define IS_POWER_OF_2(x) ((x) != 0 && ((x) & ((x)-1)) == 0)
#define EXPANDSTR(x) #x
#define STRINGIFY(x) EXPANDSTR(x)
#ifdef WINDOWS
# define inline __inline
# define INLINE_FORCED __forceinline
/* Use special C99 operator _Pragma to generate a pragma from a macro */
# ifndef ACTUAL_PRAGMA
# if _MSC_VER <= 1200 /* XXX: __pragma may work w/ vc6: then don't need #if */
# define ACTUAL_PRAGMA(p) _Pragma ( #p )
# else
# define ACTUAL_PRAGMA(p) __pragma ( p )
# endif
# endif
# define DO_NOT_OPTIMIZE ACTUAL_PRAGMA( optimize("g", off) )
# define END_DO_NOT_OPTIMIZE ACTUAL_PRAGMA( optimize("g", on) )
/* START_PACKED_STRUCTURE can't be used after typedef (b/c MSVC compiler
* has bug where it accepts #pragma but not __pragma there) and thus the
* struct have to be typedef-ed in two steps.
* see example struct _packed_frame_t at common/callstack.c
*/
# ifndef START_PACKED_STRUCTURE
# define START_PACKED_STRUCTURE ACTUAL_PRAGMA( pack(push,1) )
# define END_PACKED_STRUCTURE ACTUAL_PRAGMA( pack(pop) )
# endif
#else /* UNIX */
# define inline __inline__
# define INLINE_FORCED inline
# if 0 /* only available in gcc 4.4+ so not using: XXX: add HAVE_OPTIMIZE_ATTRIBUTE */
# define DO_NOT_OPTIMIZE __attribute__((optimize("O0")))
# else
# define DO_NOT_OPTIMIZE /* nothing */
# endif
# define END_DO_NOT_OPTIMIZE /* nothing */
# ifndef START_PACKED_STRUCTURE
# define START_PACKED_STRUCTURE /* nothing */
# define END_PACKED_STRUCTURE __attribute__ ((__packed__))
# endif
#endif
#define INLINE_ONCE inline
#ifdef UNIX
# define DIRSEP '/'
# define ALT_DIRSEP '/'
# define NL "\n"
#else
/* We can pick which we want for usability: the auto-launch of notepad
* converts to backslash regardless (i#1123). Backslash works in all
* Windows apps, while forward works in most and in cygwin (though
* still not first-class there as it has a drive letter) but not in
* File Open dialogs or older notepad Save As (or as executable path
* when launching). We could consider making this a runtime option
* or auto-picked if in cygwin but for now we're going with backslash.
*/
# define DIRSEP '\\'
# define ALT_DIRSEP '/'
# define NL "\r\n"
#endif
/* Names meant for use in text_matches_pattern() where wildcards are supported */
#define DYNAMORIO_LIBNAME IF_WINDOWS_ELSE("dynamorio.dll", \
IF_MACOS_ELSE("libdynamorio.*dylib*", "libdynamorio.so*"))
/* CLIENT_LIBNAME is assumed to be set as a preprocessor define */
#define CLIENT_LIBNAME_BASE STRINGIFY(CLIENT_LIBNAME)
#define DRMEMORY_LIBNAME \
IF_WINDOWS_ELSE(CLIENT_LIBNAME_BASE ".dll", "lib" CLIENT_LIBNAME_BASE \
IF_MACOS_ELSE("*.dylib*", ".so*"))
#define MAX_INSTR_SIZE 17
#define sscanf DO_NOT_USE_sscanf_directly_see_issue_344
#define INVALID_THREAD_ID 0
#define ASSERT_NOT_IMPLEMENTED() ASSERT(false, "Not Yet Implemented")
#define ASSERT_NOT_REACHED() ASSERT(false, "Shouldn't be reached")
#define CHECK_TRUNCATE_RANGE_uint(val) ((val) >= 0 && (val) <= UINT_MAX)
#define CHECK_TRUNCATE_RANGE_int(val) ((val) <= INT_MAX && ((int64)(val)) >= INT_MIN)
#ifdef WINDOWS
# define CHECK_TRUNCATE_RANGE_ULONG(val) CHECK_TRUNCATE_RANGE_uint(val)
#endif
#define CHECK_TRUNCATE_RANGE_ushort(val) ((val) >= 0 && (val) <= USHRT_MAX)
#define ASSERT_TRUNCATE_TYPE(var, type) ASSERT(sizeof(var) == sizeof(type), \
"mismatch "#var" and "#type)
/* check no precision lose on typecast from val to var.
* var = (type) val; should always be preceded by a call to ASSERT_TRUNCATE
*/
#define ASSERT_TRUNCATE(var, type, val) do { \
ASSERT_TRUNCATE_TYPE(var, type); \
ASSERT(CHECK_TRUNCATE_RANGE_##type(val), \
"truncating value to ("#type")"#var); \
} while (0)
#ifdef X86
# define MC_RET_REG(mc) (mc)->xax
# define MC_FP_REG(mc) (mc)->xbp
# define MC_SP_REG(mc) (mc)->xsp
#elif defined(ARM)
# define MC_RET_REG(mc) (mc)->r0
# define MC_FP_REG(mc) (mc)->r11
# define MC_SP_REG(mc) (mc)->sp
#endif
#define DR_REG_PTR_RETURN IF_X86_ELSE(DR_REG_XAX, DR_REG_R0)
/* globals that affect NOTIFY* and *LOG* macros */
extern bool op_print_stderr;
extern int op_verbose_level;
extern bool op_pause_at_assert;
extern bool op_pause_via_loop;
extern bool op_ignore_asserts;
extern uint op_prefix_style;
extern file_t f_global;
extern int reported_disk_error;
#ifdef TOOL_DR_MEMORY
extern file_t f_results;
#endif
extern bool op_use_symcache;
/* Workarounds for i#261 where DR can't write to cmd console.
*
* 1) Use private kernel32!WriteFile to write to the
* console. This could be unsafe to do in the middle of app operations but our
* notifications are usually at startup and shutdown only. Writing to the
* console involves sending a message to csrss which we hope we can treat like a
* system call wrt atomicity so long as the message crafting via private
* kernel32.dll is safe.
*
* 2) For drag-and-drop where the cmd window will disappear, we use
* msgboxes (rather than "wait for keypress") for error messages,
* which are good to highlight anyway (and thus we do not try to
* distinguish existing cmd from new cmd: but we do not use msgbox for
* rxvt or xterm or other shells since we assume that's a savvy user).
* We only pop up msgbox for NOTIFY_ERROR() to avoid too many
* msgboxes, esp for things like options list (for that we'd want to
* bundle into one buffer anyway: but that exceeds dr_messagebox's
* buffer size).
*/
#ifdef WIN32
# define IN_CMD (dr_using_console())
# define USE_MSGBOX (op_print_stderr && IN_CMD)
#else
/* For non-USE_DRSYMS Windows we just don't support cmd: unfortunately this
* includes cygwin in cmd. With PR 561181 we'll get cygwin into USE_DRSYMS.
*/
# define USE_MSGBOX (false)
#endif
/* dr_fprintf() now prints to the console after dr_enable_console_printing() */
#define PRINT_CONSOLE(...) dr_fprintf(STDERR, __VA_ARGS__)
/* for notifying user
* XXX: should add messagebox, controlled by option
*/
enum {
PREFIX_STYLE_DEAULT,
PREFIX_STYLE_NONE,
PREFIX_STYLE_BLANK,
};
#ifdef TOOL_DR_MEMORY
# define PREFIX_DEFAULT_MAIN_THREAD "~~Dr.M~~ "
#else
# define PREFIX_DEFAULT_MAIN_THREAD "~~Dr.H~~ "
#endif
#define PREFIX_BLANK " "
void
print_prefix_to_buffer(char *buf, size_t bufsz, size_t *sofar);
void
print_prefix_to_console(void);
#define NOTIFY(...) do { \
ELOG(0, __VA_ARGS__); \
if (op_print_stderr) { \
print_prefix_to_console(); \
PRINT_CONSOLE(__VA_ARGS__); \
} \
} while (0)
#define NOTIFY_ERROR(...) do { \
NOTIFY(__VA_ARGS__); \
IF_DRMEM(ELOGF(0, f_results, __VA_ARGS__)); \
IF_WINDOWS({if (USE_MSGBOX) dr_messagebox(__VA_ARGS__);}) \
} while (0)
#define NOTIFY_COND(cond, f, ...) do { \
ELOGF(0, f, __VA_ARGS__); \
if ((cond) && op_print_stderr) { \
print_prefix_to_console(); \
PRINT_CONSOLE(__VA_ARGS__); \
} \
} while (0)
#define NOTIFY_VERBOSE(level, ...) do { \
ELOG(0, __VA_ARGS__); \
if (op_verbose_level >= level && op_print_stderr) { \
print_prefix_to_console(); \
PRINT_CONSOLE(__VA_ARGS__); \
} \
} while (0)
#define NOTIFY_NO_PREFIX(...) do { \
ELOG(0, __VA_ARGS__); \
if (op_print_stderr) { \
PRINT_CONSOLE(__VA_ARGS__); \
} \
} while (0)
/***************************************************************************
* for warning/error reporting to logfile
*/
/* Per-thread data shared across callbacks and all modules */
typedef struct _tls_util_t {
file_t f; /* logfile */
} tls_util_t;
extern int tls_idx_util;
#define LOGFILE(pt) ((pt) == NULL ? f_global : (pt)->f)
#define PT_GET(dc) \
(((dc) == NULL) ? NULL : (tls_util_t *)drmgr_get_tls_field(dc, tls_idx_util))
#define LOGFILE_GET(dc) LOGFILE(PT_GET(dc))
#define PT_LOOKUP() PT_GET(dr_get_current_drcontext())
#define LOGFILE_LOOKUP() LOGFILE(PT_LOOKUP())
/* we require a ,fmt arg but C99 requires one+ argument which we just strip */
#define ELOGF(level, f, ...) do { \
if (op_verbose_level >= (level) && (f) != INVALID_FILE) {\
if (dr_fprintf(f, __VA_ARGS__) < 0) \
REPORT_DISK_ERROR(); \
} \
} while (0)
#define ELOGPT(level, pt, ...) \
ELOGF(level, LOGFILE(pt), __VA_ARGS__)
#define ELOG(level, ...) do { \
if (op_verbose_level >= (level)) { /* avoid unnec PT_GET */ \
ELOGPT(level, PT_LOOKUP(), __VA_ARGS__); \
} \
} while (0)
/* DR's fprintf has a size limit */
#define ELOG_LARGE_F(level, f, s) do { \
if (op_verbose_level >= (level) && (f) != INVALID_FILE) \
dr_write_file(f, s, strlen(s)); \
} while (0)
#define ELOG_LARGE_PT(level, pt, s) \
ELOG_LARGE_F(level, LOGFILE(pt), s)
#define ELOG_LARGE(level, s) do { \
if (op_verbose_level >= (level)) { /* avoid unnec PT_GET */ \
ELOG_LARGE_PT(level, PT_LOOKUP(), s); \
} \
} while (0)
#define WARN(...) ELOGF(0, f_global, __VA_ARGS__)
#define REPORT_DISK_ERROR() do { \
/* this implements a DO_ONCE with multiple instantiations */ \
int report_count = dr_atomic_add32_return_sum(&reported_disk_error, 1); \
if (report_count == 1) { \
if (op_print_stderr) { \
print_prefix_to_console(); \
PRINT_CONSOLE("WARNING: Unable to write to the disk. "\
"Ensure that you have enough space and permissions.\n"); \
} \
if (USE_MSGBOX) { \
IF_WINDOWS(dr_messagebox("Unable to write to the disk. "\
"Ensure that you have enough space and permissions.\n")); \
} \
} \
} while(0)
/* PR 427074: asserts should go to the log and not just stderr.
* Since we don't have a vsnprintf() (i#168) we can't make this an
* expression (w/o duplicating NOTIFY as an expression) but we only
* use it as a statement anyway.
*/
#ifdef DEBUG
# define ASSERT(x, msg) do { \
if (!(x)) { \
NOTIFY_ERROR("ASSERT FAILURE (thread " TIDFMT "): %s:%d: %s (%s)" NL, \
(dr_get_current_drcontext() == NULL ? 0 : \
dr_get_thread_id(dr_get_current_drcontext())),\
__FILE__, __LINE__, #x, msg); \
if (!op_ignore_asserts) drmemory_abort(); \
} \
} while (0)
# define ASSERT_NOT_TESTED(msg) do { \
static int assert_not_tested_printed = 0; \
if (!assert_not_tested_printed) { \
/* This singleton-like implementation has a data race. \
* However, in the worst case this will result in printing the message \
* twice which isn't a big deal \
*/ \
assert_not_tested_printed = 1; \
NOTIFY("Not tested - %s @%s:%d" NL, msg, __FILE__, __LINE__); \
} \
} while (0)
#else
# define ASSERT(x, msg) /* nothing */
# define ASSERT_NOT_TESTED(msg) /* nothing */
#endif
#ifdef BUILD_UNIT_TESTS
/* Don't use ASSERT as it will crash trying to get TLS fields.
* We should move these to a shared header once we had tests elsewhere.
*/
# define EXPECT(x) do { \
if (!(x)) { \
dr_fprintf(STDERR, "CHECK FAILED: %s:%d: %s", __FILE__, __LINE__, #x); \
drmemory_abort(); \
} \
} while (0)
#endif
#ifdef DEBUG
# define LOGF ELOGF
# define LOGPT ELOGPT
# define LOG ELOG
# define LOG_LARGE_F ELOG_LARGE_F
# define LOG_LARGE_PT ELOG_LARGE_PT
# define LOG_LARGE ELOG_LARGE
# define DOLOG(level, stmt) do { \
if (op_verbose_level >= (level)) \
stmt \
} while (0)
# define DODEBUG(stmt) do { \
stmt \
} while (0)
#else
# define LOGF(level, pt, fmt, ...) /* nothing */
# define LOGPT(level, pt, fmt, ...) /* nothing */
# define LOG(level, fmt, ...) /* nothing */
# define LOG_LARGE_F(level, pt, fmt, ...) /* nothing */
# define LOG_LARGE_PT(level, pt, fmt, ...) /* nothing */
# define LOG_LARGE(level, fmt, ...) /* nothing */
# define DOLOG(level, stmt) /* nothing */
# define DODEBUG(stmt) /* nothing */
#endif
/* For printing to a buffer.
* Usage: have a size_t variable "sofar" that counts the chars used so far.
* We take in "len" to avoid repeated locals, which some compilers won't
* combine (grrr: xref some PR).
* If we had i#168 dr_vsnprintf this wouldn't have to be a macro.
*/
#define BUFPRINT_NO_ASSERT(buf, bufsz, sofar, len, ...) do { \
len = dr_snprintf((buf)+(sofar), (bufsz)-(sofar), __VA_ARGS__); \
sofar += (len == -1 ? ((bufsz)-(sofar)) : (len < 0 ? 0 : len)); \
/* be paranoid: though usually many calls in a row and could delay until end */ \
(buf)[(bufsz)-1] = '\0'; \
} while (0)
#define BUFPRINT(buf, bufsz, sofar, len, ...) do { \
BUFPRINT_NO_ASSERT(buf, bufsz, sofar, len, __VA_ARGS__); \
ASSERT((bufsz) > (sofar), "buffer size miscalculation"); \
} while (0)
/* Buffered file write marcos, to improve performance. See PR 551841. */
#define FLUSH_BUFFER(fd, buf, sofar) do { \
if ((sofar) > 0) \
dr_write_file(fd, buf, sofar); \
(sofar) = 0; \
} while (0)
#define BUFFERED_WRITE(fd, buf, bufsz, sofar, len, ...) do { \
int old_sofar = sofar; \
BUFPRINT_NO_ASSERT(buf, bufsz, sofar, len, __VA_ARGS__); \
\
/* If the buffer overflows, \
* flush the buffer to the file and reprint to the buffer. \
* We must treat the buffer length being hit exactly as an overflow \
* b/c the NULL already clobbered our data. \
*/ \
if ((sofar) >= (bufsz)) { \
ASSERT((bufsz) > old_sofar, "unexpected overflow"); \
(buf)[old_sofar] = '\0'; /* not needed, strictly speaking; be safe */ \
(sofar) = old_sofar; \
FLUSH_BUFFER(fd, buf, sofar); /* pass sofar to get it zeroed */ \
BUFPRINT_NO_ASSERT(buf, bufsz, sofar, len, __VA_ARGS__); \
ASSERT((bufsz) > (sofar), "single write can't overflow buffer"); \
} \
} while (0)
#ifdef STATISTICS
# define STATS_INC(stat) ATOMIC_INC32(stat)
# define STATS_DEC(stat) ATOMIC_DEC32(stat)
# define STATS_ADD(stat, val) ATOMIC_ADD32(stat, val)
# define STATS_PEAK(stat) do { \
uint stats_peak_local_val_ = stat; \
if (stats_peak_local_val_ > peak_##stat) \
peak_##stat = stats_peak_local_val_; \
} while (0)
# define DOSTATS(x) x
# define _IF_STATS(x) , x
#else
# define STATS_INC(stat) /* nothing */
# define STATS_DEC(stat) /* nothing */
# define STATS_ADD(stat, val) /* nothing */
# define STATS_PEAK(stat) /* nothing */
# define DOSTATS(x) /* nothing */
# define _IF_STATS(x) /* nothing */
#endif
#define PRE instrlist_meta_preinsert
#define PREXL8 instrlist_preinsert
#define POST instrlist_meta_postinsert
#define POSTXL8 instrlist_postinsert
#define BUFFER_SIZE_BYTES(buf) sizeof(buf)
#define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof((buf)[0]))
#define BUFFER_LAST_ELEMENT(buf) (buf)[BUFFER_SIZE_ELEMENTS(buf) - 1]
#define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0
#define DWORD2BYTE(v, n) (((v) & (0xff << 8*(n))) >> 8*(n))
#ifdef X64
# define POINTER_MAX ULLONG_MAX
# define XSP_SZ 8
#else
# define POINTER_MAX UINT_MAX
# define XSP_SZ 4
#endif
/* C standard has pointer overflow as undefined so cast to unsigned (i#302) */
#define POINTER_OVERFLOW_ON_ADD(ptr, add) \
(((ptr_uint_t)(ptr)) + (add) < ((ptr_uint_t)(ptr)))
#define POINTER_UNDERFLOW_ON_SUB(ptr, sub) \
(((ptr_uint_t)(ptr)) - (sub) > ((ptr_uint_t)(ptr)))
#ifdef UNIX
# ifdef X64
# define ASM_XAX "rax"
# define ASM_XDX "rdx"
# define ASM_XSP "rsp"
# define ASM_SEG "gs"
# define ASM_SYSARG1 "rdi"
# define ASM_SYSARG2 "rsi"
# define ASM_SYSARG3 "rdx"
# define ASM_SYSARG4 "r10"
# define ASM_SYSARG5 "r8"
# define ASM_SYSARG6 "r9"
/* int 0x80 returns different value from syscall in x64.
* For example, brk(0) returns 0xfffffffffffffff2 using int 0x80
*/
# define ASM_SYSCALL "syscall"
# else
# define ASM_XAX "eax"
# define ASM_XDX "edx"
# define ASM_XSP "esp"
# define ASM_SEG "fs"
# define ASM_SYSARG1 "ebx"
# define ASM_SYSARG2 "ecx"
# define ASM_SYSARG3 "edx"
# define ASM_SYSARG4 "esi"
# define ASM_SYSARG5 "edi"
# define ASM_SYSARG6 "ebp"
# define ASM_SYSCALL "int $0x80"
# endif
#endif
#ifdef UNIX
# ifdef X86
# define ATOMIC_INC32(x) __asm__ __volatile__("lock incl %0" : "=m" (x) : : "memory")
# define ATOMIC_DEC32(x) __asm__ __volatile__("lock decl %0" : "=m" (x) : : "memory")
# define ATOMIC_ADD32(x, val) \
__asm__ __volatile__("lock addl %1, %0" : "=m" (x) : "r" (val) : "memory")
static inline int
atomic_add32_return_sum(volatile int *x, int val)
{
int cur;
__asm__ __volatile__("lock xaddl %1, %0" : "=m" (*x), "=r" (cur)
: "1" (val) : "memory");
return (cur + val);
}
# elif defined(ARM)
/* XXX: should DR export these for us? */
# define ATOMIC_INC32(x) \
__asm__ __volatile__( \
"1: ldrex r2, %0 \n\t" \
" add r2, r2, #1 \n\t" \
" strex r3, r2, %0 \n\t" \
" cmp r3, #0 \n\t" \
" bne 1b \n\t" \
" cmp r2, #0" /* for possible SET_FLAG use */ \
: "=Q" (x) /* no offset for ARM mode */ \
: : "cc", "memory", "r2", "r3")
# define ATOMIC_DEC32(x) \
__asm__ __volatile__( \
"1: ldrex r2, %0 \n\t" \
" sub r2, r2, #1 \n\t" \
" strex r3, r2, %0 \n\t" \
" cmp r3, #0 \n\t" \
" bne 1b \n\t" \
" cmp r2, #0" /* for possible SET_FLAG use */ \
: "=Q" (x) /* no offset for ARM mode */ \
: : "cc", "memory", "r2", "r3")
# define ATOMIC_ADD32(x, val) \
__asm__ __volatile__( \
"1: ldrex r2, %0 \n\t" \
" add r2, r2, %1 \n\t" \
" strex r3, r2, %0 \n\t" \
" cmp r3, #0 \n\t" \
" bne 1b \n\t" \
" cmp r2, #0" /* for possible SET_FLAG use */ \
: "=Q" (x) /* no offset for ARM mode */ \
: "r" (val) \
: "cc", "memory", "r2", "r3")
# define ATOMIC_ADD_EXCHANGE32(x, val, result) \
__asm__ __volatile__( \
"1: ldrex r2, %0 \n\t" \
" add r2, r2, %2 \n\t" \
" strex r3, r2, %0 \n\t" \
" cmp r3, #0 \n\t" \
" bne 1b \n\t" \
" sub r2, r2, %2 \n\t" \
" str r2, %1" \
: "=Q" (*x), "=m" (result) \
: "r" (val) \
: "cc", "memory", "r2", "r3")
static inline int
atomic_add32_return_sum(volatile int *x, int val)
{
int temp;
ATOMIC_ADD_EXCHANGE32(x, val, temp);
return (temp + val);
}
# endif
#else
# define ATOMIC_INC32(x) _InterlockedIncrement((volatile LONG *)&(x))
# define ATOMIC_DEC32(x) _InterlockedDecrement((volatile LONG *)&(x))
# define ATOMIC_ADD32(x, val) _InterlockedExchangeAdd((volatile LONG *)&(x), val)
static inline int
atomic_add32_return_sum(volatile int *x, int val)
{
return (ATOMIC_ADD32(*x, val) + val);
}
#endif
/* racy: should be used only for diagnostics */
#define DO_ONCE(stmt) { \
static int do_once = 0; \
if (!do_once) { \
do_once = 1; \
stmt; \
} \
}
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
/* IS_ASCII excludes null char */
#define IS_ASCII(c) ((byte)(c) < 0x80 && (byte)(c) != 0)
#define IS_WCHAR_AT(ptr) \
(IS_ASCII(*((byte*)(ptr))) && *(((byte*)(ptr))+1) == 0)
#define IS_WCHARx2_AT(ptr) \
(IS_WCHAR_AT(ptr) && IS_WCHAR_AT(((wchar_t*)(ptr))+1))
/***************************************************************************
* UTILITY ROUTINES
*/
#ifdef WINDOWS
# define strcasecmp _stricmp
#endif
#ifdef memset
# undef memset
#endif
void *
memset(void *dst, int val, size_t size);
void
wait_for_user(const char *message);
void
drmemory_abort(void);
bool
safe_read(void *base, size_t size, void *out_buf);
/* if returns false, calls instr_free() on inst first */
bool
safe_decode(void *drcontext, app_pc pc, instr_t *inst, app_pc *next_pc /*OPTIONAL OUT*/);
#ifdef STATISTICS
extern uint symbol_lookups;
extern uint symbol_searches;
extern uint symbol_lookup_cache_hits;
extern uint symbol_search_cache_hits;
extern uint symbol_address_lookups;
#endif
bool
lookup_has_fast_search(const module_data_t *mod);
app_pc
lookup_symbol(const module_data_t *mod, const char *symname);
app_pc
lookup_internal_symbol(const module_data_t *mod, const char *symname);
/* Iterates over symbols matching modname!sym_pattern until
* callback returns false.
* N.B.: if you add a call to this routine, or modify an existing call,
* bump SYMCACHE_VERSION and add symcache checks.
*/
bool
lookup_all_symbols(const module_data_t *mod, const char *sym_pattern, bool full,
drsym_enumerate_ex_cb callback, void *data);
bool
module_has_debug_info(const module_data_t *mod);
#ifdef DEBUG
void
print_mcontext(file_t f, dr_mcontext_t *mc);
#endif
#ifdef WINDOWS
# ifdef DEBUG
/* check that peb isolation is consistently applied (xref i#324) */
bool
using_private_peb(void);
# endif
HANDLE
get_private_heap_handle(void);
#endif /* WINDOWS */
void
hashtable_delete_with_stats(hashtable_t *table, const char *name);
#ifdef STATISTICS
void
hashtable_cluster_stats(hashtable_t *table, const char *name);
#endif
bool
unsigned_multiply_will_overflow(size_t m, size_t n);
void
crash_process(void);
static inline bool
test_one_bit_set(uint x)
{
return (x > 0) && (x & (x-1)) == 0;
}
static inline generic_func_t
cast_to_func(void *p)
{
#ifdef WINDOWS
# pragma warning(push)
# pragma warning(disable : 4055)
#endif
return (generic_func_t) p;
#ifdef WINDOWS
# pragma warning(pop)
#endif
}
/***************************************************************************
* WINDOWS SYSCALLS
*/
#ifdef WINDOWS
# include "windefs.h"
/* i#1908: we support loading numbers from a file */
# define SYSNUM_FILE IF_X64_ELSE("syscalls_x64.txt", "syscalls_x86.txt")
# define SYSNUM_FILE_WOW64 "syscalls_wow64.txt"
# ifndef STATUS_INVALID_KERNEL_INFO_VERSION
# define STATUS_INVALID_KERNEL_INFO_VERSION 0xc000a004
# endif
TEB *
get_TEB(void);
TEB *
get_TEB_from_handle(HANDLE h);
thread_id_t
get_tid_from_handle(HANDLE h);
TEB *
get_TEB_from_tid(thread_id_t tid);
void
set_app_error_code(void *drcontext, uint val);
PEB *
get_app_PEB(void);
HANDLE
get_process_heap_handle(void);
bool
is_current_process(HANDLE h);
bool
is_wow64_process(void);
const wchar_t *
get_app_commandline(void);
/* returns just the primary number */
int
sysnum_from_name(const char *name);
bool
get_sysnum(const char *name, drsys_sysnum_t *var, bool ok_to_fail);
bool
running_on_Win7_or_later(void);
bool
running_on_Win7SP1_or_later(void);
bool
running_on_Vista_or_later(void);
dr_os_version_t
get_windows_version(void);
void
get_windows_version_string(char *buf DR_PARAM_OUT, size_t bufsz);
app_pc
get_highest_user_address(void);
/* set *base to preferred value, or NULL for none */
bool
virtual_alloc(void **base, size_t size, uint memtype, uint prot);
bool
virtual_free(void *base);
bool
module_imports_from_msvc(const module_data_t *mod);
#endif /* WINDOWS */
reg_t
syscall_get_param(void *drcontext, uint num);
/***************************************************************************
* HEAP WITH STATS
*/
/* PR 423757: heap accounting */
typedef enum {
HEAPSTAT_SHADOW,
HEAPSTAT_PERBB,
#ifdef TOOL_DR_HEAPSTAT
HEAPSTAT_SNAPSHOT,
HEAPSTAT_STALENESS,
#endif
HEAPSTAT_CALLSTACK,
HEAPSTAT_HASHTABLE,
HEAPSTAT_GENCODE,
HEAPSTAT_RBTREE,
HEAPSTAT_REPORT,
HEAPSTAT_WRAP,
HEAPSTAT_MISC,
/* when you add here, add to heapstat_names in utils.c */
HEAPSTAT_NUMTYPES,
} heapstat_t;
/* wrappers around dr_ versions to track heap accounting stats */
void *
global_alloc(size_t size, heapstat_t type);
void
global_free(void *p, size_t size, heapstat_t type);
void *
thread_alloc(void *drcontext, size_t size, heapstat_t type);
void
thread_free(void *drcontext, void *p, size_t size, heapstat_t type);
void *
nonheap_alloc(size_t size, uint prot, heapstat_t type);
void
nonheap_free(void *p, size_t size, heapstat_t type);
void
heap_dump_stats(file_t f);
#define dr_global_alloc DO_NOT_USE_use_global_alloc
#define dr_global_free DO_NOT_USE_use_global_free
#define dr_thread_alloc DO_NOT_USE_use_thread_alloc
#define dr_thread_free DO_NOT_USE_use_thread_free
#define dr_nonheap_alloc DO_NOT_USE_use_nonheap_alloc
#define dr_nonheap_free DO_NOT_USE_use_nonheap_free
char *
drmem_strdup(const char *src, heapstat_t type);
/* note: Guarantees that even if src overflows max, the allocated buffer will be
* large enough for max characters plus the null-terminator.
*/
char *
drmem_strndup(const char *src, size_t max, heapstat_t type);
/***************************************************************************
* STRINGS
*/
#define END_MARKER "\terror end" NL
/* DR_MAX_OPTIONS_LENGTH is the maximum client options string length that DR
* will give us. by making each individual option buffer this long, we won't
* have truncation issues.
*/
#define MAX_OPTION_LEN DR_MAX_OPTIONS_LENGTH
#if !defined(MACOS) && !defined(ANDROID) && !defined(NOLINK_STRCASESTR)
char *
strcasestr(const char *text, const char *pattern);
#endif
char *
strnchr(const char *str, int find, size_t max);
#define FILESYS_CASELESS IF_WINDOWS_ELSE(true, false)
bool
text_matches_pattern(const char *text, const char *pattern, bool ignore_case);
/* patterns is a null-separated, double-null-terminated list of strings */
bool
text_matches_any_pattern(const char *text, const char *patterns, bool ignore_case);
/* patterns is a null-separated, double-null-terminated list of strings */
const char *
text_contains_any_string(const char *text, const char *patterns, bool ignore_case,
const char **matched);
/* For parsing an mmapped file into lines: returns the start of the next line.
* Optionally returns the start of this line after skipping whitespace (if skip_ws)
* in "sol" and the end of the line (prior to any whitespace, if skip_ws) in "eol".
*/
const char *
find_next_line(const char *start, const char *eof, const char **sol DR_PARAM_OUT,
const char **eol DR_PARAM_OUT, bool skip_ws);
/***************************************************************************
* REGISTER CONVERSION UTILITIES
*/
reg_id_t
reg_ptrsz_to_32(reg_id_t reg);
reg_id_t
reg_ptrsz_to_16(reg_id_t reg);
reg_id_t
reg_ptrsz_to_8(reg_id_t reg);
#ifdef X86
reg_id_t
reg_ptrsz_to_8h(reg_id_t reg);
#endif
reg_id_t
reg_to_size(reg_id_t reg, opnd_size_t size);
/***************************************************************************
* APP INSTRS
*/
instr_t *
instr_get_prev_app_instr(instr_t *instr);
instr_t *
instr_get_next_app_instr(instr_t *instr);
instr_t *
instrlist_first_app_instr(instrlist_t *ilist);
instr_t *
instrlist_last_app_instr(instrlist_t *ilist);
/***************************************************************************
* HASHTABLE
*
* hashtable was moved and generalized so we need to initialize it
*/
/* must be called before drmgr or drwrap */
void
utils_early_init(void);
void
utils_init(void);
void
utils_exit(void);
void
utils_thread_init(void *drcontext);
void
utils_thread_exit(void *drcontext);
void
utils_thread_set_file(void *drcontext, file_t f);
#ifdef __cplusplus
}
#endif
#endif /* _UTILS_H_ */