d07bf508a9
The Omnitrace program is being renamed. Full name: "ROCm Systems Profiler" Package name: "rocprofiler-systems" Binary / Library names: "rocprof-sys-*" --------- Co-authored-by: Xuan Chen <xuchen@amd.com> Signed-off-by: David Galiffi <David.Galiffi@amd.com>
730 řádky
20 KiB
C
730 řádky
20 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: RTlinux.c,v 1.54 2008/04/11 23:30:44 legendre Exp $
|
|
* RTlinux.c: mutatee-side library function specific to Linux
|
|
************************************************************************/
|
|
|
|
#include "h/dyninstAPI_RT.h"
|
|
#include "src/RTcommon.h"
|
|
#include "src/RTthread.h"
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <link.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/types.h>
|
|
|
|
/* FreeBSD libc has stubs so a static version shouldn't need libpthreads */
|
|
#include <pthread.h>
|
|
|
|
extern double DYNINSTstaticHeap_512K_lowmemHeap_1[];
|
|
extern double DYNINSTstaticHeap_16M_anyHeap_1[];
|
|
extern unsigned long sizeOfLowMemHeap1;
|
|
extern unsigned long sizeOfAnyHeap1;
|
|
|
|
static struct trap_mapping_header*
|
|
getStaticTrapMap(unsigned long addr);
|
|
|
|
/** RT lib initialization **/
|
|
|
|
void
|
|
mark_heaps_exec()
|
|
{
|
|
RTprintf("*** Initializing dyninstAPI runtime.\n");
|
|
|
|
/* Grab the page size, to align the heap pointer. */
|
|
long int pageSize = sysconf(_SC_PAGESIZE);
|
|
if(pageSize == 0 || pageSize == -1)
|
|
{
|
|
fprintf(stderr, "*** Failed to obtain page size, guessing 16K.\n");
|
|
perror("mark_heaps_exec");
|
|
pageSize = 1024 * 16;
|
|
} /* end pageSize initialization */
|
|
|
|
/* Align the heap pointer. */
|
|
unsigned long int alignedHeapPointer =
|
|
(unsigned long int) DYNINSTstaticHeap_16M_anyHeap_1;
|
|
alignedHeapPointer = (alignedHeapPointer) & ~(pageSize - 1);
|
|
unsigned long int adjustedSize = (unsigned long int) DYNINSTstaticHeap_16M_anyHeap_1 -
|
|
alignedHeapPointer + sizeOfAnyHeap1;
|
|
|
|
/* Make the heap's page executable. */
|
|
int result = mprotect((void*) alignedHeapPointer, (size_t) adjustedSize,
|
|
PROT_READ | PROT_WRITE | PROT_EXEC);
|
|
if(result != 0)
|
|
{
|
|
fprintf(stderr,
|
|
"%s[%d]: Couldn't make DYNINSTstaticHeap_16M_anyHeap_1 executable!\n",
|
|
__FILE__, __LINE__);
|
|
perror("mark_heaps_exec");
|
|
}
|
|
RTprintf("*** Marked memory from 0x%lx to 0x%lx executable.\n", alignedHeapPointer,
|
|
alignedHeapPointer + adjustedSize);
|
|
|
|
/* Mark _both_ heaps executable. */
|
|
alignedHeapPointer = (unsigned long int) DYNINSTstaticHeap_512K_lowmemHeap_1;
|
|
alignedHeapPointer = (alignedHeapPointer) & ~(pageSize - 1);
|
|
adjustedSize = (unsigned long int) DYNINSTstaticHeap_512K_lowmemHeap_1 -
|
|
alignedHeapPointer + sizeOfLowMemHeap1;
|
|
|
|
/* Make the heap's page executable. */
|
|
result = mprotect((void*) alignedHeapPointer, (size_t) adjustedSize,
|
|
PROT_READ | PROT_WRITE | PROT_EXEC);
|
|
if(result != 0)
|
|
{
|
|
fprintf(stderr,
|
|
"%s[%d]: Couldn't make DYNINSTstaticHeap_512K_lowmemHeap_1 executable!\n",
|
|
__FILE__, __LINE__);
|
|
perror("mark_heaps_exec");
|
|
}
|
|
RTprintf("*** Marked memory from 0x%lx to 0x%lx executable.\n", alignedHeapPointer,
|
|
alignedHeapPointer + adjustedSize);
|
|
} /* end mark_heaps_exec() */
|
|
|
|
#if defined(cap_binary_rewriter) && !defined(DYNINST_RT_STATIC_LIB)
|
|
/* For a static binary, all global constructors are combined in an undefined
|
|
* order. Also, DYNINSTBaseInit must be run after all global constructors have
|
|
* been run. Since the order of global constructors is undefined, DYNINSTBaseInit
|
|
* cannot be run as a constructor in static binaries. Instead, it is run from a
|
|
* special constructor handler that processes all the global constructors in
|
|
* the binary. Leaving this code in would create a global constructor for the
|
|
* function runDYNINSTBaseInit(). See DYNINSTglobal_ctors_handler.
|
|
*/
|
|
extern void
|
|
DYNINSTBaseInit();
|
|
void
|
|
runDYNINSTBaseInit() __attribute__((constructor));
|
|
void
|
|
runDYNINSTBaseInit()
|
|
{
|
|
DYNINSTBaseInit();
|
|
}
|
|
#endif
|
|
|
|
/** Dynamic instrumentation support **/
|
|
|
|
static int
|
|
tkill(pid_t pid, long lwp, int sig)
|
|
{
|
|
static int has_tkill = 1;
|
|
int result = 0;
|
|
|
|
if(has_tkill)
|
|
{
|
|
result = syscall(SYS_thr_kill2, pid, lwp, sig);
|
|
if(0 != result && ENOSYS == errno)
|
|
{
|
|
has_tkill = 0;
|
|
}
|
|
}
|
|
|
|
if(!has_tkill)
|
|
{
|
|
result = kill(pid, sig);
|
|
}
|
|
|
|
return (result == 0);
|
|
}
|
|
|
|
void
|
|
DYNINSTbreakPoint()
|
|
{
|
|
if(DYNINSTstaticMode) return;
|
|
|
|
DYNINST_break_point_event = 1;
|
|
while(DYNINST_break_point_event)
|
|
{
|
|
tkill(getpid(), dyn_lwp_self(), DYNINST_BREAKPOINT_SIGNUM);
|
|
}
|
|
/* Mutator resets to 0 */
|
|
}
|
|
|
|
static int failed_breakpoint = 0;
|
|
void
|
|
uncaught_breakpoint(int sig)
|
|
{
|
|
failed_breakpoint = 1;
|
|
}
|
|
|
|
void
|
|
DYNINSTsafeBreakPoint()
|
|
{
|
|
if(DYNINSTstaticMode) return;
|
|
|
|
DYNINST_break_point_event = 1;
|
|
while(DYNINST_break_point_event)
|
|
{
|
|
tkill(getpid(), dyn_lwp_self(), SIGSTOP);
|
|
}
|
|
/* Mutator resets to 0 */
|
|
|
|
#if 0
|
|
if( DYNINSTstaticMode ) return;
|
|
DYNINST_break_point_event = 2;
|
|
sigset_t emptyset;
|
|
sigemptyset(&emptyset);
|
|
|
|
// There is a bug with attaching to a stopped process on FreeBSD This
|
|
// achieves the same result as long as Dyninst attaches to the process when
|
|
// it is in sigsuspend
|
|
while( DYNINST_break_point_event ) {
|
|
sigsuspend(&emptyset);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !defined(DYNINST_RT_STATIC_LIB)
|
|
static int
|
|
get_dlopen_error()
|
|
{
|
|
const char* err_str;
|
|
err_str = dlerror();
|
|
if(err_str)
|
|
{
|
|
strncpy(gLoadLibraryErrorString, err_str, (size_t) ERROR_STRING_LENGTH);
|
|
return 1;
|
|
}
|
|
|
|
sprintf(gLoadLibraryErrorString, "unknown error withe dlopen");
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DYNINSTloadLibrary(char* libname)
|
|
{
|
|
void* res;
|
|
gLoadLibraryErrorString[0] = '\0';
|
|
res = dlopen(libname, RTLD_NOW | RTLD_GLOBAL);
|
|
if(res) return 1;
|
|
|
|
get_dlopen_error();
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/** threading support **/
|
|
|
|
int
|
|
dyn_lwp_self()
|
|
{
|
|
static int gettid_not_valid = 0;
|
|
int result;
|
|
|
|
if(gettid_not_valid) return getpid();
|
|
|
|
long lwp_id;
|
|
result = syscall(SYS_thr_self, &lwp_id);
|
|
if(result && errno == ENOSYS)
|
|
{
|
|
gettid_not_valid = 1;
|
|
return getpid();
|
|
}
|
|
|
|
return lwp_id;
|
|
}
|
|
|
|
int
|
|
dyn_pid_self()
|
|
{
|
|
return getpid();
|
|
}
|
|
|
|
dyntid_t (*DYNINST_pthread_self)(void);
|
|
|
|
dyntid_t
|
|
dyn_pthread_self()
|
|
{
|
|
dyntid_t me;
|
|
if(DYNINSTstaticMode)
|
|
{
|
|
return (dyntid_t) pthread_self();
|
|
}
|
|
if(!DYNINST_pthread_self)
|
|
{
|
|
return (dyntid_t) DYNINST_SINGLETHREADED;
|
|
}
|
|
me = (*DYNINST_pthread_self)();
|
|
return (dyntid_t) me;
|
|
}
|
|
|
|
int
|
|
DYNINST_am_initial_thread(dyntid_t tid)
|
|
{
|
|
/*
|
|
* LWPs and PIDs are in different namespaces on FreeBSD.
|
|
*
|
|
* I don't really know a good way to determine this without
|
|
* doing an expensive sysctl.
|
|
*
|
|
* Luckily, this function isn't used anymore
|
|
*/
|
|
assert(!"This function is unimplemented on FreeBSD");
|
|
return 0;
|
|
}
|
|
|
|
/** trap based instrumentation **/
|
|
|
|
#if defined(cap_mutatee_traps)
|
|
|
|
# include <ucontext.h>
|
|
|
|
# if defined(arch_x86) || defined(MUTATEE_32)
|
|
# define UC_PC(x) x->uc_mcontext.mc_eip
|
|
# elif defined(arch_x86_64)
|
|
# define UC_PC(x) x->uc_mcontext.mc_rip
|
|
# endif // UC_PC
|
|
|
|
extern unsigned long dyninstTrapTableUsed;
|
|
extern unsigned long dyninstTrapTableVersion;
|
|
extern trapMapping_t* dyninstTrapTable;
|
|
extern unsigned long dyninstTrapTableIsSorted;
|
|
|
|
/**
|
|
* This comment is now obsolete, left for historic purposes
|
|
*
|
|
* Called by the SIGTRAP handler, dyninstTrapHandler. This function is
|
|
* closly intwined with dyninstTrapHandler, don't modify one without
|
|
* understanding the other.
|
|
*
|
|
* This function sets up the calling context that was passed to the
|
|
* SIGTRAP handler so that control will be redirected to our instrumentation
|
|
* when we do the setcontext call.
|
|
*
|
|
* There are a couple things that make this more difficult than it should be:
|
|
* 1. The OS provided calling context is similar to the GLIBC calling context,
|
|
* but not compatible. We'll create a new GLIBC compatible context and
|
|
* copy the possibly stomped registers from the OS context into it. The
|
|
* incompatiblities seem to deal with FP and other special purpose registers.
|
|
* 2. setcontext doesn't restore the flags register. Thus dyninstTrapHandler
|
|
* will save the flags register first thing and pass us its value in the
|
|
* flags parameter. We'll then push the instrumentation entry and flags
|
|
* onto the context's stack. Instead of transfering control straight to the
|
|
* instrumentation, we'll actually go back to dyninstTrapHandler, which will
|
|
* do a popf/ret to restore flags and go to instrumentation. The 'retPoint'
|
|
* parameter is the address in dyninstTrapHandler the popf/ret can be found.
|
|
**/
|
|
void
|
|
dyninstTrapHandler(int sig, siginfo_t* sg, ucontext_t* context)
|
|
{
|
|
void* orig_ip;
|
|
void* trap_to;
|
|
|
|
orig_ip = UC_PC(context);
|
|
assert(orig_ip);
|
|
|
|
// Find the new IP we're going to and substitute. Leave everything else untouched
|
|
if(DYNINSTstaticMode)
|
|
{
|
|
unsigned long zero = 0;
|
|
unsigned long one = 1;
|
|
struct trap_mapping_header* hdr = getStaticTrapMap((unsigned long) orig_ip);
|
|
if(!hdr) return;
|
|
|
|
assert(hdr);
|
|
trapMapping_t* mapping = &(hdr->traps[0]);
|
|
trap_to = dyninstTrapTranslate(orig_ip, (unsigned long*) &hdr->num_entries, &zero,
|
|
(volatile trapMapping_t**) &mapping, &one);
|
|
}
|
|
else
|
|
{
|
|
trap_to = dyninstTrapTranslate(
|
|
orig_ip, &dyninstTrapTableUsed, &dyninstTrapTableVersion,
|
|
(volatile trapMapping_t**) &dyninstTrapTable, &dyninstTrapTableIsSorted);
|
|
}
|
|
UC_PC(context) = (long) trap_to;
|
|
}
|
|
|
|
# if defined(cap_binary_rewriter)
|
|
|
|
# define NUM_LIBRARIES 512 // Important, max number of rewritten libraries
|
|
|
|
# define WORD_SIZE (8 * sizeof(unsigned))
|
|
# define NUM_LIBRARIES_BITMASK_SIZE (1 + NUM_LIBRARIES / WORD_SIZE)
|
|
struct trap_mapping_header* all_headers[NUM_LIBRARIES];
|
|
|
|
static unsigned all_headers_current[NUM_LIBRARIES_BITMASK_SIZE];
|
|
static unsigned all_headers_last[NUM_LIBRARIES_BITMASK_SIZE];
|
|
|
|
# if !defined(arch_x86_64) || defined(MUTATEE_32)
|
|
typedef Elf32_Dyn ElfX_Dyn;
|
|
typedef Elf32_Ehdr ElfX_Ehdr;
|
|
# else
|
|
typedef Elf64_Dyn ElfX_Dyn;
|
|
typedef Elf64_Ehdr ElfX_Ehdr;
|
|
# endif
|
|
|
|
static int
|
|
parse_libs();
|
|
static int
|
|
parse_link_map(struct link_map* l);
|
|
static void
|
|
clear_unloaded_libs();
|
|
|
|
static void
|
|
set_bit(unsigned* bit_mask, int bit, char value);
|
|
static void
|
|
clear_bitmask(unsigned* bit_mask);
|
|
static unsigned
|
|
get_next_free_bitmask(unsigned* bit_mask, int last_pos);
|
|
static unsigned
|
|
get_next_set_bitmask(unsigned* bit_mask, int last_pos);
|
|
|
|
static tc_lock_t trap_mapping_lock;
|
|
|
|
static struct trap_mapping_header*
|
|
getStaticTrapMap(unsigned long addr)
|
|
{
|
|
struct trap_mapping_header* header;
|
|
int i;
|
|
|
|
tc_lock_lock(&trap_mapping_lock);
|
|
parse_libs();
|
|
|
|
i = -1;
|
|
for(;;)
|
|
{
|
|
i = get_next_set_bitmask(all_headers_current, i);
|
|
assert(i >= 0 && i <= NUM_LIBRARIES);
|
|
if(i == NUM_LIBRARIES)
|
|
{
|
|
header = NULL;
|
|
goto done;
|
|
}
|
|
header = all_headers[i];
|
|
if(addr >= header->low_entry && addr <= header->high_entry)
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
tc_lock_unlock(&trap_mapping_lock);
|
|
return header;
|
|
}
|
|
|
|
static struct link_map*
|
|
getLinkMap()
|
|
{
|
|
struct link_map* map = NULL;
|
|
# if !defined(DYNINST_RT_STATIC_LIB)
|
|
if(dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Rewind the current link map pointer to find the
|
|
// start of the list
|
|
struct link_map* last_map;
|
|
while(map != NULL)
|
|
{
|
|
last_map = map;
|
|
map = map->l_prev;
|
|
}
|
|
|
|
map = last_map;
|
|
# endif
|
|
return map;
|
|
}
|
|
|
|
static int
|
|
parse_libs()
|
|
{
|
|
struct link_map* l_current;
|
|
|
|
l_current = getLinkMap();
|
|
if(!l_current) return -1;
|
|
|
|
clear_bitmask(all_headers_current);
|
|
while(l_current)
|
|
{
|
|
parse_link_map(l_current);
|
|
l_current = l_current->l_next;
|
|
}
|
|
clear_unloaded_libs();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// parse_link_map return values
|
|
# define PARSED 0
|
|
# define NOT_REWRITTEN 1
|
|
# define ALREADY_PARSED 2
|
|
# define ERROR_INTERNAL -1
|
|
# define ERROR_FULL -2
|
|
static int
|
|
parse_link_map(struct link_map* l)
|
|
{
|
|
ElfX_Dyn* dynamic_ptr;
|
|
struct trap_mapping_header* header;
|
|
unsigned int i, new_pos;
|
|
|
|
dynamic_ptr = (ElfX_Dyn*) l->l_ld;
|
|
if(!dynamic_ptr) return -1;
|
|
|
|
assert(sizeof(dynamic_ptr->d_un.d_ptr) == sizeof(void*));
|
|
for(; dynamic_ptr->d_tag != DT_NULL && dynamic_ptr->d_tag != DT_DYNINST;
|
|
dynamic_ptr++)
|
|
;
|
|
if(dynamic_ptr->d_tag == DT_NULL)
|
|
{
|
|
return NOT_REWRITTEN;
|
|
}
|
|
|
|
header = (struct trap_mapping_header*) (dynamic_ptr->d_un.d_val + l->l_addr);
|
|
|
|
caddr_t libAddr = l->l_addr;
|
|
|
|
// Executables have an implicit zero load address but the library load address
|
|
// may be non-zero
|
|
if(((ElfX_Ehdr*) libAddr)->e_type == ET_EXEC)
|
|
{
|
|
libAddr = 0;
|
|
}
|
|
else if(((ElfX_Ehdr*) libAddr)->e_type == ET_DYN)
|
|
{
|
|
// Account for library_adjust mechanism which is used for shared libraries
|
|
// on FreeBSD
|
|
libAddr += getpagesize();
|
|
}
|
|
|
|
header = (struct trap_mapping_header*) (dynamic_ptr->d_un.d_val + libAddr);
|
|
|
|
if(header->signature != TRAP_HEADER_SIG) return ERROR_INTERNAL;
|
|
if(header->pos != -1)
|
|
{
|
|
set_bit(all_headers_current, header->pos, 1);
|
|
assert(all_headers[header->pos] == header);
|
|
return ALREADY_PARSED;
|
|
}
|
|
|
|
for(i = 0; i < header->num_entries; i++)
|
|
{
|
|
header->traps[i].source =
|
|
(void*) (((unsigned long) header->traps[i].source) + libAddr);
|
|
header->traps[i].target =
|
|
(void*) (((unsigned long) header->traps[i].target) + libAddr);
|
|
if(!header->low_entry ||
|
|
header->low_entry > (unsigned long) header->traps[i].source)
|
|
header->low_entry = (unsigned long) header->traps[i].source;
|
|
if(!header->high_entry ||
|
|
header->high_entry < (unsigned long) header->traps[i].source)
|
|
header->high_entry = (unsigned long) header->traps[i].source;
|
|
}
|
|
|
|
new_pos = get_next_free_bitmask(all_headers_last, -1);
|
|
assert(new_pos >= 0 && new_pos < NUM_LIBRARIES);
|
|
if(new_pos == NUM_LIBRARIES) return ERROR_FULL;
|
|
|
|
header->pos = new_pos;
|
|
all_headers[new_pos] = header;
|
|
set_bit(all_headers_current, new_pos, 1);
|
|
set_bit(all_headers_last, new_pos, 1);
|
|
|
|
return PARSED;
|
|
}
|
|
|
|
static void
|
|
clear_unloaded_libs()
|
|
{
|
|
unsigned i;
|
|
for(i = 0; i < NUM_LIBRARIES_BITMASK_SIZE; i++)
|
|
{
|
|
all_headers_last[i] = all_headers_current[i];
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_bit(unsigned* bit_mask, int bit, char value)
|
|
{
|
|
assert(bit < NUM_LIBRARIES);
|
|
unsigned* word = bit_mask + bit / WORD_SIZE;
|
|
unsigned shift = bit % WORD_SIZE;
|
|
if(value)
|
|
{
|
|
*word |= (1 << shift);
|
|
}
|
|
else
|
|
{
|
|
*word &= ~(1 << shift);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear_bitmask(unsigned* bit_mask)
|
|
{
|
|
unsigned i;
|
|
for(i = 0; i < NUM_LIBRARIES_BITMASK_SIZE; i++)
|
|
{
|
|
bit_mask[i] = 0;
|
|
}
|
|
}
|
|
|
|
static unsigned
|
|
get_next_free_bitmask(unsigned* bit_mask, int last_pos)
|
|
{
|
|
unsigned i, j;
|
|
j = last_pos + 1;
|
|
i = j / WORD_SIZE;
|
|
for(; j < NUM_LIBRARIES; i++)
|
|
{
|
|
if(bit_mask[i] == (unsigned) -1)
|
|
{
|
|
j += WORD_SIZE;
|
|
continue;
|
|
}
|
|
for(;;)
|
|
{
|
|
if(!((1 << (j % WORD_SIZE) & bit_mask[i])))
|
|
{
|
|
return j;
|
|
}
|
|
j++;
|
|
if(j % WORD_SIZE == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return NUM_LIBRARIES;
|
|
}
|
|
|
|
static unsigned
|
|
get_next_set_bitmask(unsigned* bit_mask, int last_pos)
|
|
{
|
|
unsigned i, j;
|
|
j = last_pos + 1;
|
|
i = j / WORD_SIZE;
|
|
for(; j < NUM_LIBRARIES; i++)
|
|
{
|
|
if(bit_mask[i] == (unsigned) 0)
|
|
{
|
|
j += WORD_SIZE;
|
|
continue;
|
|
}
|
|
for(;;)
|
|
{
|
|
if((1 << (j % WORD_SIZE) & bit_mask[i]))
|
|
{
|
|
return j;
|
|
}
|
|
j++;
|
|
if(j % WORD_SIZE == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return NUM_LIBRARIES;
|
|
}
|
|
|
|
# endif
|
|
|
|
#endif /* cap_mutatee_traps */
|
|
|
|
/*
|
|
* Note: this program is for historical purposes only, we use libthread_db
|
|
* now to get thread information.
|
|
*
|
|
* A program to determine the offsets of certain thread structures on FreeBSD
|
|
*
|
|
* This program should be compiled with the headers from the libthr library from
|
|
* /usr/src. This can be installed using sysinstall. The following arguments
|
|
* should be added to the compile once these headers are installed.
|
|
*
|
|
* -I/usr/src/lib/libthr/arch/amd64/include -I/usr/src/lib/libthr/thread
|
|
*
|
|
* Change amd64 to what ever is appropriate.
|
|
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "thr_private.h"
|
|
|
|
pthread_attr_t attr;
|
|
|
|
void *foo(void *f) {
|
|
unsigned long stack_addr;
|
|
void *(*start_func)(void *);
|
|
unsigned long tid;
|
|
|
|
// Get all the values
|
|
syscall(SYS_thr_self, &tid);
|
|
|
|
start_func = foo;
|
|
|
|
asm("mov %%rbp,%0" : "=r" (stack_addr));
|
|
|
|
pthread_t threadSelf = pthread_self();
|
|
|
|
printf("TID: %u == %u\n", tid, threadSelf->tid);
|
|
printf("STACK: 0x%lx == 0x%lx\n", stack_addr, threadSelf->attr.stackaddr_attr +
|
|
threadSelf->attr.stacksize_attr); printf("START: 0x%lx == 0x%lx\n", (unsigned
|
|
long)start_func, (unsigned long)threadSelf->start_routine);
|
|
|
|
unsigned char *ptr = (unsigned char *)threadSelf;
|
|
unsigned long tidVal = *((unsigned long *)(ptr + offsetof(struct pthread, tid)));
|
|
unsigned long stackAddrVal = *((unsigned long *)(ptr + offsetof(struct pthread, attr)
|
|
+ offsetof(struct pthread_attr, stackaddr_attr))); unsigned long stackSizeVal =
|
|
*((unsigned long *)(ptr + offsetof(struct pthread, attr) + offsetof(struct pthread_attr,
|
|
stacksize_attr))); unsigned long startFuncVal = *((unsigned long *)(ptr + offsetof(struct
|
|
pthread, start_routine)));
|
|
|
|
printf("TID = %u, offset = %u\n", tidVal, offsetof(struct pthread, tid));
|
|
printf("STACK = 0x%lx, offset = %u\n", stackAddrVal, offsetof(struct pthread, attr) +
|
|
offsetof(struct pthread_attr, stackaddr_attr)); printf("SIZE = 0x%lx, offset = %u\n",
|
|
stackSizeVal, offsetof(struct pthread, attr) + offsetof(struct pthread_attr,
|
|
stacksize_attr)); printf("START = 0x%lx, offset = %u\n", startFuncVal, offsetof(struct
|
|
pthread, start_routine));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
pthread_t t;
|
|
void *result;
|
|
pthread_attr_init(&attr);
|
|
pthread_create(&t, &attr, foo, NULL);
|
|
pthread_join(t, &result);
|
|
|
|
return 0;
|
|
}
|
|
*/
|