8ad58c5d28
* Build omnitrace-rt library
- Explicitly build dyninstAPI_RT as omnitrace-rt so that the SONAME in the ELF is omnitrace-rt instead of dyninstAPI_RT
- Create symbolic link lib/omnitrace/libdyninstAPI_RT.so which points to lib/libomnitrace-rt.so
- Simplify build tree location of libomnitrace-rt.so since it is ../lib from the bin directory even in the build tree
- Update dyninst submodule with minor tweaks to dyninstAPI_RT/CMakeLists.txt
* Update source/lib/omnitrace-rt/cmake/platform.cmake
* Use ftpmirror.gnu.org instead of ftp.gnu.org
- in timemory and dyninst submodules
- minor .clang-tidy tweak
[ROCm/rocprofiler-systems commit: 0cf017251e]
815 라인
24 KiB
C
815 라인
24 KiB
C
/*
|
|
* See the dyninst/COPYRIGHT file for copyright information.
|
|
*
|
|
* We provide the Paradyn Tools (below described as "Paradyn")
|
|
* on an AS IS basis, and do not warrant its validity or performance.
|
|
* We reserve the right to update, modify, or discontinue this
|
|
* software at any time. We shall have no obligation to supply such
|
|
* updates or modifications or any other form of support to you.
|
|
*
|
|
* By your use of Paradyn, you understand and agree that we (or any
|
|
* other person or entity with proprietary rights in Paradyn) are
|
|
* under no obligation to provide either maintenance services,
|
|
* update services, notices of latent defects, or correction of
|
|
* defects for Paradyn.
|
|
*
|
|
* 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; either
|
|
* version 2.1 of the License, or (at your option) any 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
|
|
* Lesser 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
|
|
*/
|
|
|
|
/* $Id: RTcommon.c,v 1.78 2008/04/15 16:43:44 roundy Exp $ */
|
|
|
|
#include "RTcommon.h"
|
|
#include "RTthread.h"
|
|
#include "h/dyninstAPI_RT.h"
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
unsigned int DYNINSTobsCostLow;
|
|
DLLEXPORT unsigned int DYNINSThasInitialized = 0;
|
|
struct DYNINST_bootstrapStruct DYNINST_bootstrap_info;
|
|
char gLoadLibraryErrorString[ERROR_STRING_LENGTH];
|
|
int DYNINSTdebugRTlib = 0;
|
|
|
|
DLLEXPORT int DYNINSTstaticMode = 1;
|
|
|
|
/**
|
|
* Allocate the Dyninst heaps
|
|
*
|
|
* The IA-64 requires that instruction be 16-byte aligned, so we have to
|
|
* align the heaps if we want to use them for inferior RPCs.
|
|
**/
|
|
#define HEAP_TYPE double
|
|
#define ALIGN_ATTRIB
|
|
|
|
#if defined(os_linux) || defined(os_freebsd)
|
|
# define HEAP_LOCAL extern
|
|
#else
|
|
# define HEAP_LOCAL DLLEXPORT
|
|
#endif
|
|
|
|
HEAP_LOCAL HEAP_TYPE
|
|
DYNINSTglobalData[SYN_INST_BUF_SIZE / sizeof(HEAP_TYPE)] ALIGN_ATTRIB;
|
|
|
|
#if !defined(target_smallmem)
|
|
HEAP_LOCAL HEAP_TYPE
|
|
DYNINSTstaticHeap_512K_lowmemHeap_1[512 * 1024 / sizeof(HEAP_TYPE)] ALIGN_ATTRIB;
|
|
HEAP_LOCAL HEAP_TYPE
|
|
DYNINSTstaticHeap_16M_anyHeap_1[16 * 1024 * 1024 / sizeof(HEAP_TYPE)] ALIGN_ATTRIB;
|
|
/* These are necessary due to silly C-style 'extern'/linking conventions. */
|
|
const unsigned long sizeOfLowMemHeap1 = sizeof(DYNINSTstaticHeap_512K_lowmemHeap_1);
|
|
const unsigned long sizeOfAnyHeap1 = sizeof(DYNINSTstaticHeap_16M_anyHeap_1);
|
|
#else
|
|
HEAP_LOCAL HEAP_TYPE
|
|
DYNINSTstaticHeap_8K_lowmemHeap_1[8 * 1024 / sizeof(HEAP_TYPE)] ALIGN_ATTRIB;
|
|
HEAP_LOCAL HEAP_TYPE
|
|
DYNINSTstaticHeap_32K_anyHeap_1[32 * 1024 / sizeof(HEAP_TYPE)] ALIGN_ATTRIB;
|
|
/* These are necessary due to silly C-style 'extern'/linking conventions. */
|
|
const unsigned long sizeOfLowMemHeap1 = sizeof(DYNINSTstaticHeap_8K_lowmemHeap_1);
|
|
const unsigned long sizeOfAnyHeap1 = sizeof(DYNINSTstaticHeap_32K_anyHeap_1);
|
|
#endif
|
|
|
|
/**
|
|
* One some platforms we can tell when a fork or exec is occurring through
|
|
* system-provided events. On others we do it ourselves. Enumerated type
|
|
* defined in header file
|
|
**/
|
|
DLLEXPORT DYNINST_synch_event_t DYNINST_synch_event_id = DSE_undefined;
|
|
DLLEXPORT void* DYNINST_synch_event_arg1;
|
|
/* Code to read args 2,3 would have to be moved from dyninstAPI's*/
|
|
/* process::handleStopThread to signalgenerator::decodeRTSignal in*/
|
|
/* order for other signals to make use of them, as currently only the*/
|
|
/* stopThread event needs makes use of them. */
|
|
DLLEXPORT void* DYNINST_synch_event_arg2 = NULL; /* not read in dyninst's decodeRTSignal*/
|
|
DLLEXPORT void* DYNINST_synch_event_arg3 = NULL; /* not read in dyninst's decodeRTSignal*/
|
|
DLLEXPORT int DYNINST_break_point_event = 0;
|
|
|
|
/**
|
|
* These variables are used to pass arguments into DYNINSTinit
|
|
* when it is called as an _init function
|
|
**/
|
|
int libdyninstAPI_RT_init_localCause = -1;
|
|
int libdyninstAPI_RT_init_localPid = -1;
|
|
int libdyninstAPI_RT_init_maxthreads = -1;
|
|
int libdyninstAPI_RT_init_debug_flag = 0;
|
|
|
|
int DYNINST_mutatorPid;
|
|
int DYNINSTdebugPrintRT = 0;
|
|
int isMutatedExec = 0;
|
|
|
|
// stopThread cache variables
|
|
char cacheLRUflags[TARGET_CACHE_WIDTH];
|
|
DLLEXPORT void* DYNINST_target_cache[TARGET_CACHE_WIDTH][TARGET_CACHE_WAYS];
|
|
FILE* stOut;
|
|
int fakeTickCount;
|
|
|
|
#ifdef _MSC_VER
|
|
# define TLS_VAR __declspec(thread)
|
|
#else
|
|
// Note, the initial-exec model gives us static TLS which can be accessed
|
|
// directly, unlike dynamic TLS that calls __tls_get_addr(). Such calls risk
|
|
// recursing back to us if they're also instrumented, ad infinitum. Static TLS
|
|
// must be used very sparingly though, because it is a limited resource.
|
|
// *** This case is very special -- do not use IE in general libraries! ***
|
|
|
|
# if defined(DYNINST_RT_STATIC_LIB)
|
|
# define TLS_VAR __thread __attribute__((tls_model("local-exec")))
|
|
# else
|
|
# define TLS_VAR __thread __attribute__((tls_model("initial-exec")))
|
|
# endif
|
|
#endif
|
|
|
|
// It's tempting to make this a char, but glibc < 2.17 hits a bug:
|
|
// https://sourceware.org/bugzilla/show_bug.cgi?id=14898
|
|
static TLS_VAR short DYNINST_tls_tramp_guard = 1;
|
|
|
|
DLLEXPORT int
|
|
DYNINST_lock_tramp_guard()
|
|
{
|
|
if(!DYNINST_tls_tramp_guard) return 0;
|
|
DYNINST_tls_tramp_guard = 0;
|
|
return 1;
|
|
}
|
|
DLLEXPORT void
|
|
DYNINST_unlock_tramp_guard()
|
|
{
|
|
DYNINST_tls_tramp_guard = 1;
|
|
}
|
|
|
|
DECLARE_DYNINST_LOCK(DYNINST_trace_lock);
|
|
|
|
/**
|
|
* Init the FPU. We've seen bugs with Linux (e.g., Redhat 6.2 stock kernel on
|
|
* PIIIs) where processes started by Paradyn started with FPU uninitialized.
|
|
* DYNINSTdummydouble is global so the compiler won't optimize away FP code
|
|
* in initFPU
|
|
**/
|
|
double DYNINSTdummydouble = 4321.71;
|
|
static void
|
|
initFPU()
|
|
{
|
|
double x = 17.1234;
|
|
DYNINSTdummydouble *= x;
|
|
}
|
|
|
|
/**
|
|
* This function is called in both static and dynamic rewriting, on
|
|
* all platforms that support binary rewriting, but before DYNINSTinit
|
|
**/
|
|
void
|
|
DYNINSTBaseInit()
|
|
{
|
|
#if defined(cap_mutatee_traps)
|
|
DYNINSTinitializeTrapHandler();
|
|
#endif
|
|
DYNINST_unlock_tramp_guard();
|
|
DYNINSThasInitialized = 1;
|
|
|
|
RTuntranslatedEntryCounter = 0;
|
|
}
|
|
|
|
/**
|
|
* The Dyninst API arranges for this function to be called at the entry to
|
|
* main() via libdyninstAPI_RT_init (defined in RTposix.c and RTwinnt.c).
|
|
* libdyninstAPI_RT_init is called by one of the following methods:
|
|
* GCC: link with gcc -shared, and use __attribute__((constructor));
|
|
* Linux: ld with -init libdyninstAPI_RT_init
|
|
* gcc with -Wl,-init -Wl,...
|
|
* Windows: called from DllMain, which exists in lieu of libdyninstAPI_RT_init
|
|
*
|
|
* This is only called in the Dynamic instrumentation case. Static
|
|
* libraries don't call this.
|
|
**/
|
|
void
|
|
DYNINSTinit()
|
|
{
|
|
rtdebug_printf("%s[%d]: DYNINSTinit: welcome to DYNINSTinit()\n", __FILE__,
|
|
__LINE__);
|
|
initFPU();
|
|
mark_heaps_exec();
|
|
|
|
tc_lock_init(&DYNINST_trace_lock);
|
|
DYNINSThasInitialized = 1;
|
|
rtdebug_printf("%s[%d]: welcome to DYNINSTinit\n", __FILE__, __LINE__);
|
|
|
|
/* sanity check */
|
|
assert(sizeof(int64_t) == 8);
|
|
assert(sizeof(int32_t) == 4);
|
|
|
|
/* defensive stuff */
|
|
memset(DYNINST_target_cache, 0,
|
|
sizeof(void*) * TARGET_CACHE_WIDTH * TARGET_CACHE_WAYS);
|
|
memset(cacheLRUflags, 1, sizeof(char) * TARGET_CACHE_WIDTH);
|
|
// stOut = fopen("rtdump.txt","w");
|
|
|
|
rtdebug_printf("%s[%d]: leaving DYNINSTinit\n", __FILE__, __LINE__);
|
|
fakeTickCount = 0;
|
|
/* Memory emulation */
|
|
}
|
|
|
|
/**
|
|
* Does what it's called. Used by the paradyn daemon as a default in certain
|
|
* cases (MT in particular)
|
|
**/
|
|
int
|
|
DYNINSTreturnZero()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Used to by dyninst breakpoint snippet */
|
|
void
|
|
DYNINST_snippetBreakpoint()
|
|
{
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_snippetBreakpoint;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
}
|
|
|
|
/* Used to instrument (and report) the entry of fork */
|
|
DLLEXPORT void
|
|
DYNINST_instForkEntry()
|
|
{
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_forkEntry;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
}
|
|
|
|
/* Used to instrument (and report) the exit of fork */
|
|
/* We use the safe breakpoint on the child side of fork
|
|
as we may not be attached at that point. The parent
|
|
side uses the normal version. */
|
|
DLLEXPORT void
|
|
DYNINST_instForkExit(void* arg1)
|
|
{
|
|
//#warning "This function is not implemented for AARCH64 yet!"
|
|
#if !defined(arch_aarch64)
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_forkExit;
|
|
DYNINST_synch_event_arg1 = arg1;
|
|
/* Stop ourselves */
|
|
if((long int) arg1 == 0)
|
|
{
|
|
/* Child... */
|
|
DYNINSTsafeBreakPoint();
|
|
}
|
|
else
|
|
{
|
|
DYNINSTbreakPoint();
|
|
}
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
#else
|
|
// Silence compiler warnings
|
|
(void) arg1;
|
|
assert(0);
|
|
#endif
|
|
}
|
|
|
|
/* Used to instrument (and report) the entry of exec */
|
|
DLLEXPORT void
|
|
DYNINST_instExecEntry(void* arg1)
|
|
{
|
|
//#warning "This function is not implemented for AARCH64 yet!"
|
|
#if !defined(arch_aarch64)
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_execEntry;
|
|
DYNINST_synch_event_arg1 = arg1;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
#endif
|
|
// Silence compiler warnings
|
|
(void) arg1;
|
|
}
|
|
|
|
/* Used to instrument (and report) the exit of exec */
|
|
DLLEXPORT void
|
|
DYNINST_instExecExit(void* arg1)
|
|
{
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_execExit;
|
|
DYNINST_synch_event_arg1 = arg1;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
}
|
|
|
|
/* Used to instrument (and report) the entry of exit */
|
|
DLLEXPORT void
|
|
DYNINST_instExitEntry(void* arg1)
|
|
{
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_exitEntry;
|
|
DYNINST_synch_event_arg1 = arg1;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
}
|
|
|
|
/* Used to instrument (and report) the entry of exit */
|
|
DLLEXPORT void
|
|
DYNINST_instLoadLibrary(void* arg1)
|
|
{
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_loadLibrary;
|
|
DYNINST_synch_event_arg1 = arg1;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
}
|
|
|
|
/* Used to instrument (and report) the entry of exit */
|
|
DLLEXPORT void
|
|
DYNINST_instLwpExit(void)
|
|
{
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_lwpExit;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
}
|
|
|
|
// implementation of an N=2 way associative cache to keep access time low
|
|
// width = 32
|
|
// the cache contains valid addresses
|
|
// add to the cache when an instrumented instruction misses in the cache
|
|
// update flags for the cache when an instrumented instruction hits in the cache
|
|
// instrumentation will take the form of a call to cache check
|
|
RT_Boolean
|
|
cacheLookup(void* calculation)
|
|
{
|
|
int index = ((unsigned long) calculation) % TARGET_CACHE_WIDTH;
|
|
if(DYNINST_target_cache[index][0] == calculation)
|
|
{
|
|
cacheLRUflags[index] = 0;
|
|
return RT_TRUE;
|
|
}
|
|
else if(DYNINST_target_cache[index][1] == calculation)
|
|
{
|
|
cacheLRUflags[index] = 1;
|
|
return RT_TRUE;
|
|
}
|
|
else
|
|
{ // miss
|
|
if(cacheLRUflags[index] == 0)
|
|
{
|
|
DYNINST_target_cache[index][1] = calculation;
|
|
cacheLRUflags[index] = 1;
|
|
}
|
|
else
|
|
{
|
|
DYNINST_target_cache[index][0] = calculation;
|
|
cacheLRUflags[index] = 0;
|
|
}
|
|
return RT_FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Receives two snippets as arguments and stops the mutatee
|
|
* while the mutator reads the arguments, saved to
|
|
* DYNINST_synch_event... global variables.
|
|
*
|
|
* if flag useCache==1, does a cache lookup and stops only
|
|
* if there is a cache miss
|
|
*
|
|
* The flags are:
|
|
* bit 0: useCache
|
|
* bit 1: true if interpAsTarget
|
|
* bit 2: true if interpAsReturnAddr
|
|
**/
|
|
//#define STACKDUMP
|
|
void
|
|
DYNINST_stopThread(void* pointAddr, void* callBackID, void* flags, void* calculation)
|
|
{
|
|
static int reentrant = 0;
|
|
|
|
RT_Boolean isInCache = RT_FALSE;
|
|
|
|
if(reentrant == 1)
|
|
{
|
|
return;
|
|
}
|
|
reentrant = 1;
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
rtdebug_printf("RT_st: pt[%lx] flags[%lx] calc[%lx] ", (unsigned long) pointAddr,
|
|
(unsigned long) flags, (unsigned long) calculation);
|
|
|
|
#if 0 && defined STACKDUMP
|
|
//if (0 && ((unsigned long)calculation == 0x9746a3 ||
|
|
// (unsigned long)calculation == 0x77dd761b))
|
|
//{
|
|
fprintf(stOut,"RT_st: %lx(%lx)\n", (long)pointAddr,&calculation);
|
|
fprintf(stOut,"at instr w/ targ=%lx\n",(long)calculation);
|
|
for (bidx=0; bidx < 0x100; bidx+=4) {
|
|
fprintf(stOut,"0x%x: ", (int)stackBase+bidx);
|
|
fprintf(stOut,"%02hhx", stackBase[bidx]);
|
|
fprintf(stOut,"%02hhx", stackBase[bidx+1]);
|
|
fprintf(stOut,"%02hhx", stackBase[bidx+2]);
|
|
fprintf(stOut,"%02hhx", stackBase[bidx+3]);
|
|
fprintf(stOut,"\n");
|
|
}
|
|
//}
|
|
// fsg: read from 40a4aa, how did it become 40a380?
|
|
#endif
|
|
|
|
if((((long) flags) & 0x04))
|
|
{
|
|
rtdebug_printf("ret-addr stopThread yields %lx", (unsigned long) calculation);
|
|
// fprintf(stderr,"[$0x%lx]\n", (long)calculation);
|
|
}
|
|
|
|
if(0 != (((long) flags) & 0x03))
|
|
{
|
|
// do the lookup if the useCache bit is set, or if it represents
|
|
// the address of real code, so that we add the address to the cache
|
|
// even if we will stop the thread if there's a cache hit
|
|
isInCache = cacheLookup(calculation);
|
|
}
|
|
|
|
// if the cache flag bit is not set, or if we get a cache miss,
|
|
// stop the thread so that we can call back to the mutatee
|
|
if(0 == (((long) flags) & 0x01) || !isInCache)
|
|
{
|
|
/* Set vars for Dyninst to read */
|
|
DYNINST_synch_event_id = DSE_stopThread;
|
|
DYNINST_synch_event_arg1 = pointAddr;
|
|
DYNINST_synch_event_arg2 = callBackID;
|
|
DYNINST_synch_event_arg3 = calculation;
|
|
|
|
// encode interp as target or as return addr by making
|
|
// callback ID negative
|
|
if(0 != (((long) flags) & 0x06))
|
|
{
|
|
DYNINST_synch_event_arg2 = (void*) (-1 * (long) DYNINST_synch_event_arg2);
|
|
}
|
|
|
|
rtdebug_printf("stopping! isInCache=%d\n", isInCache);
|
|
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
DYNINST_synch_event_arg2 = NULL;
|
|
DYNINST_synch_event_arg3 = NULL;
|
|
}
|
|
fflush(stOut);
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
reentrant = 0;
|
|
return;
|
|
}
|
|
|
|
// zeroes out the useCache flag if the call is interprocedural
|
|
void
|
|
DYNINST_stopInterProc(void* pointAddr, void* callBackID, void* flags, void* calculation,
|
|
void* objStart, void* objEnd)
|
|
{
|
|
#if defined STACKDUMP
|
|
fprintf(stOut, "RT_sip: calc=%lx objStart=%lx objEnd=%lx\n", calculation, objStart,
|
|
objEnd);
|
|
fflush(stOut);
|
|
#endif
|
|
if(calculation < objStart || calculation >= objEnd)
|
|
{
|
|
flags = (void*) ((long) (((int) ((long) flags)) & 0xfffffffe));
|
|
}
|
|
DYNINST_stopThread(pointAddr, callBackID, flags, calculation);
|
|
}
|
|
|
|
// boundsArray is a sequence of (blockStart,blockEnd) pairs
|
|
DLLEXPORT RT_Boolean
|
|
DYNINST_boundsCheck(void** boundsArray_, void* arrayLen_, void* writeTarget_)
|
|
{
|
|
RT_Boolean callStopThread = RT_FALSE;
|
|
const unsigned long writeTarget = (unsigned long) writeTarget_;
|
|
const long arrayLen = (long) arrayLen_;
|
|
unsigned long* boundsArray = (unsigned long*) boundsArray_;
|
|
// set idx to halfway into the array, ensuring we use a blockStart address
|
|
int idx = (int) arrayLen / 4 * 2;
|
|
int lowIdx = 0;
|
|
int highIdx = (int) arrayLen;
|
|
// fprintf(stderr,"D_bc@%p: boundsArray=%p target=%lx idx=%d arrayLen=%d [%d]\n",
|
|
// (void*)DYNINST_boundsCheck, boundsArray_, writeTarget_, idx, arrayLen, __LINE__);
|
|
// rtdebug_printf("D_bc@%p: boundsArray=%p target=%lx idx=%d arrayLen=%d [%d]\n",
|
|
// (void*)DYNINST_boundsCheck, boundsArray_, writeTarget_, idx, arrayLen, __LINE__);
|
|
if((unsigned long) boundsArray < 0x10000000)
|
|
{
|
|
printf("D_bc: boundsArray_ = %lx, returning false\n",
|
|
(unsigned long) boundsArray);
|
|
return RT_FALSE;
|
|
}
|
|
while(lowIdx < highIdx)
|
|
{
|
|
if(idx > arrayLen || idx < 0)
|
|
rtdebug_printf("ERROR: out of bounds idx=%d, arrayLen = %ld [%d]\n", idx,
|
|
arrayLen, __LINE__);
|
|
rtdebug_printf("D_bc: low=%d high=%d arr[%d]=%lx [%d]\n", lowIdx, highIdx, idx,
|
|
boundsArray[idx], __LINE__);
|
|
if(writeTarget < boundsArray[idx])
|
|
{
|
|
rtdebug_printf("D_bc: [%d]\n", __LINE__);
|
|
highIdx = idx;
|
|
idx = (highIdx - lowIdx) / 4 * 2 + lowIdx;
|
|
}
|
|
else if(boundsArray[idx + 1] <= writeTarget)
|
|
{
|
|
rtdebug_printf("D_bc: [%d]\n", __LINE__);
|
|
lowIdx = idx + 2;
|
|
idx = (highIdx - lowIdx) / 4 * 2 + lowIdx;
|
|
}
|
|
else
|
|
{
|
|
rtdebug_printf("D_bc: callST=true [%d]\n", __LINE__);
|
|
callStopThread = RT_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
rtdebug_printf("D_bc: boundsArray=%p ret=%d [%d]\n", (void*) boundsArray,
|
|
callStopThread, __LINE__);
|
|
return callStopThread;
|
|
}
|
|
|
|
/**
|
|
* Used to report addresses of functions called at dynamic call sites
|
|
**/
|
|
DLLEXPORT int
|
|
DYNINSTasyncDynFuncCall(void* call_target, void* call_addr)
|
|
{
|
|
if(DYNINSTstaticMode) return 0;
|
|
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_dynFuncCall;
|
|
DYNINST_synch_event_arg1 = call_target;
|
|
DYNINST_synch_event_arg2 = call_addr;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
DYNINST_synch_event_arg2 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DYNINSTuserMessage(void* msg, unsigned int msg_size)
|
|
{
|
|
unsigned long msg_size_long = (unsigned long) msg_size;
|
|
if(DYNINSTstaticMode)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
tc_lock_lock(&DYNINST_trace_lock);
|
|
|
|
/* Set the state so the mutator knows what's up */
|
|
DYNINST_synch_event_id = DSE_userMessage;
|
|
DYNINST_synch_event_arg1 = msg;
|
|
DYNINST_synch_event_arg2 = (void*) msg_size_long;
|
|
/* Stop ourselves */
|
|
DYNINSTbreakPoint();
|
|
/* Once the stop completes, clean up */
|
|
DYNINST_synch_event_id = DSE_undefined;
|
|
DYNINST_synch_event_arg1 = NULL;
|
|
DYNINST_synch_event_arg2 = NULL;
|
|
|
|
tc_lock_unlock(&DYNINST_trace_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
tc_lock_init(tc_lock_t* t)
|
|
{
|
|
t->mutex = 0;
|
|
t->tid = (dyntid_t) DYNINST_INITIAL_LOCK_PID;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
tc_lock_unlock(tc_lock_t* t)
|
|
{
|
|
t->tid = (dyntid_t) DYNINST_INITIAL_LOCK_PID;
|
|
t->mutex = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
tc_lock_destroy(tc_lock_t* t)
|
|
{
|
|
t->tid = (dyntid_t) DYNINST_INITIAL_LOCK_PID;
|
|
t->mutex = 0;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
dyninst_init_lock(dyninst_lock_t* lock)
|
|
{
|
|
lock->tid = (dyntid_t) DYNINST_INITIAL_LOCK_PID;
|
|
lock->mutex = 0;
|
|
}
|
|
|
|
void
|
|
dyninst_free_lock(dyninst_lock_t* lock)
|
|
{
|
|
(void) lock; /* unused parameter */
|
|
}
|
|
|
|
int
|
|
dyninst_lock(dyninst_lock_t* lock)
|
|
{
|
|
return tc_lock_lock(lock);
|
|
}
|
|
|
|
void
|
|
dyninst_unlock(dyninst_lock_t* lock)
|
|
{
|
|
tc_lock_unlock(lock);
|
|
}
|
|
|
|
int
|
|
rtdebug_printf(const char* format, ...)
|
|
{
|
|
int ret;
|
|
va_list va;
|
|
if(!DYNINSTdebugRTlib) return 0;
|
|
if(NULL == format) return DYNINST_PRINTF_ERRVAL;
|
|
|
|
fprintf(stderr, "[RTLIB]");
|
|
va_start(va, format);
|
|
ret = vfprintf(stderr, format, va);
|
|
va_end(va);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// clang-format off
|
|
#ifndef CASE_RETURN_STR
|
|
# define CASE_RETURN_STR(x) \
|
|
case x: return #x
|
|
#endif
|
|
// clang-format on
|
|
|
|
const char*
|
|
asyncEventType2str(rtBPatch_asyncEventType t)
|
|
{
|
|
switch(t)
|
|
{
|
|
CASE_RETURN_STR(rtBPatch_nullEvent);
|
|
CASE_RETURN_STR(rtBPatch_newConnectionEvent);
|
|
CASE_RETURN_STR(rtBPatch_internalShutDownEvent);
|
|
CASE_RETURN_STR(rtBPatch_threadCreateEvent);
|
|
CASE_RETURN_STR(rtBPatch_threadDestroyEvent);
|
|
CASE_RETURN_STR(rtBPatch_dynamicCallEvent);
|
|
CASE_RETURN_STR(rtBPatch_userEvent);
|
|
default: return "bad_async_event_type";
|
|
}
|
|
}
|
|
|
|
DLLEXPORT volatile unsigned long dyninstTrapTableUsed;
|
|
DLLEXPORT volatile unsigned long dyninstTrapTableVersion;
|
|
DLLEXPORT volatile trapMapping_t* dyninstTrapTable;
|
|
DLLEXPORT volatile unsigned long dyninstTrapTableIsSorted;
|
|
|
|
void*
|
|
dyninstTrapTranslate(void* source, volatile unsigned long* table_used,
|
|
volatile unsigned long* table_version,
|
|
volatile trapMapping_t** trap_table,
|
|
volatile unsigned long* is_sorted)
|
|
{
|
|
volatile unsigned local_version;
|
|
unsigned i;
|
|
void* target;
|
|
|
|
do
|
|
{
|
|
local_version = *table_version;
|
|
target = NULL;
|
|
|
|
if(*is_sorted)
|
|
{
|
|
unsigned min = 0;
|
|
unsigned mid = 0;
|
|
unsigned max = *table_used;
|
|
unsigned prev = max + 1;
|
|
|
|
for(;;)
|
|
{
|
|
mid = (min + max) / 2;
|
|
if(mid == prev)
|
|
{
|
|
fprintf(stderr,
|
|
"ERROR: dyninstTrapTranslate couldn't find "
|
|
"entry for %p: min=%x mid=%x max=%x prev=%x\n",
|
|
source, min, mid, max, prev);
|
|
break;
|
|
}
|
|
prev = mid;
|
|
|
|
if((*trap_table)[mid].source < source)
|
|
min = mid;
|
|
else if((*trap_table)[mid].source > source)
|
|
max = mid;
|
|
else
|
|
{
|
|
target = (*trap_table)[mid].target;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ /*!dyninstTrapTableIsSorted*/
|
|
for(i = 0; i < *table_used; i++)
|
|
{
|
|
if((*trap_table)[i].source == source)
|
|
{
|
|
target = (*trap_table)[i].target;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while(local_version != *table_version);
|
|
|
|
return target;
|
|
}
|
|
|
|
DLLEXPORT void
|
|
DYNINSTtrapFunction()
|
|
{
|
|
__asm__ __volatile__("nop\n" :::);
|
|
}
|