blob: 16f8d346696d0fbfd540e1daa46914d6965013b3 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012-2014 Google, Inc. All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2000-2001 Hewlett-Packard Company */
/*
* fragment.h - fragment_t structures
*/
#ifndef _FRAGMENT_H_
#define _FRAGMENT_H_ 1
#include "hashtable.h"
#include "translate.h"
/* Flags, stored in fragment_t->flags bitfield
*/
#define FRAG_IS_FUTURE 0x000001
#define FRAG_TRACE_LINKS_SHIFTED 0x000002
#define FRAG_IS_TRACE 0x000004
#define FRAG_IS_TRACE_HEAD 0x000008
#define FRAG_LINKED_OUTGOING 0x000010
#define FRAG_LINKED_INCOMING 0x000020
#define FRAG_CANNOT_DELETE 0x000040
#define FRAG_CANNOT_BE_TRACE 0x000080
/* Indicates an irregular fragment_t. In particular, there are no
* trailing linkstubs after this fragment_t struct. Note that other
* "fake fragment_t" flags should be set in combination with this one
* (FRAG_IS_{FUTURE,EXTRA_VMAREA*,EMPTY_SLOT}, FRAG_FCACHE_FREE_LIST).
*/
#define FRAG_FAKE 0x000100
/* this flag indicates fragment writes all 6 flags prior to reading */
#define FRAG_WRITES_EFLAGS_6 0x000200
#define FRAG_WRITES_EFLAGS_ARITH FRAG_WRITES_EFLAGS_6
/* this flag indicates fragment writes OF before reading it */
#define FRAG_WRITES_EFLAGS_OF 0x000400
/* This is not a fragment_t but an fcache free list entry.
* In current usage this is checked to see if the previous free list entry is
* a free list entry (see fcache.c's free_list_header_t.flags).
* This flag MUST be in the bottom 16 bits since free_list_header_t.flags
* is a ushort!
*/
#define FRAG_FCACHE_FREE_LIST 0x000800
#define FRAG_HAS_SYSCALL 0x001000
/* this flags indicates that a trace is being built from fragment_t->tag */
#define FRAG_TRACE_BUILDING 0x002000
/* used on future fragments, currently only read for adaptive working set
* also used for fragments to know whether they are on the deleted list
* (shared) or flush queue (private)
*/
#define FRAG_WAS_DELETED 0x004000
/* indicates frag is from a non-protected page and may be self-modifying */
#define FRAG_SELFMOD_SANDBOXED 0x008000
/* indicates whether frag contains an elided direct cti */
#define FRAG_HAS_DIRECT_CTI 0x010000
/* used by fcache to distinguish fragment_t from its own empty slot struct */
#define FRAG_IS_EMPTY_SLOT 0x020000
/* used by vmarea to distinguish fragment_t from its own multi unit struct */
#define FRAG_IS_EXTRA_VMAREA 0x040000
#define FRAG_IS_EXTRA_VMAREA_INIT 0x080000
#ifdef PROGRAM_SHEPHERDING
/* indicates from memory that wasn't part of code from image on disk */
# define FRAG_DYNGEN 0x100000
# ifdef DGC_DIAGNOSTICS
/* for now, only used to identify regions that fail our policies */
# define FRAG_DYNGEN_RESTRICTED 0x200000
# endif
#endif
#ifndef DGC_DIAGNOSTICS
/* i#107, for mangling mov_seg instruction,
* NOTE: mangle_app_seg cannot be used with DGC_DIAGNOSTICS.
*/
# define FRAG_HAS_MOV_SEG 0x200000
#endif
#if defined(X86) && defined(X64)
/* this fragment contains 32-bit code */
# define FRAG_32_BIT 0x400000
#elif defined(ARM) && !defined(X64)
/* this fragment contains Thumb code */
# define FRAG_THUMB 0x400000
#endif
#define FRAG_MUST_END_TRACE 0x800000
#define FRAG_SHARED 0x1000000
/* indicates a temporary private copy of a shared bb, used
* for trace building
*/
#define FRAG_TEMP_PRIVATE 0x2000000
#define FRAG_TRACE_OUTPUT 0x4000000
#define FRAG_CBR_FALLTHROUGH_SHORT 0x8000000
/* Indicates coarse-grain cache management, i.e., batch units with
* no individual fragment_t.
*/
#define FRAG_COARSE_GRAIN 0x10000000
/* Translation info was recorded at fragment emit time in a post-fragment_t field.
* This is NOT set for flushed fragments, which store their info in the in_xlate
* union instead and are marked FRAG_WAS_DELETED, though if both flags are set
* then the info is in the post-fragment_t field.
*/
#define FRAG_HAS_TRANSLATION_INFO 0x20000000
#ifdef X64
/* this fragment contains 64-bit code translated from 32-bit app code */
# define FRAG_X86_TO_X64 0x40000000
# ifdef SIDELINE
# error SIDELINE not compatible with X64
# endif
#elif defined(SIDELINE)
# define FRAG_DO_NOT_SIDELINE 0x40000000
#endif
/* This fragment immediately follows a free entry in the fcache */
#define FRAG_FOLLOWS_FREE_ENTRY 0x80000000
/* Flags that a future fragment can transfer to a real on taking its place:
* Naturally we don't want FRAG_IS_FUTURE or FRAG_WAS_DELETED.
* FRAG_SHARED has to already be on the real fragment.
* Do NOT take the FRAG_TEMP_PRIVATE flag that we put on futures as an
* optimization in case never used -- it will mess up which heap is used, etc.
* There aren't really any other flags added to future fragments.
* Even FRAG_IS_TRACE_HEAD is only used for marking shared secondary
* trace heads from private traces.
*/
#define FUTURE_FLAGS_TRANSFER (FRAG_IS_TRACE_HEAD)
/* only used for debugging */
#define FUTURE_FLAGS_ALLOWED (FUTURE_FLAGS_TRANSFER|FRAG_FAKE|FRAG_IS_FUTURE|\
FRAG_WAS_DELETED|FRAG_SHARED|FRAG_TEMP_PRIVATE)
#define FRAG_ISA_MODE(flags) \
IF_X86_ELSE(IF_X64_ELSE((FRAG_IS_32(flags) || FRAG_IS_X86_TO_X64(flags)) ? \
DR_ISA_IA32 : DR_ISA_AMD64, DR_ISA_IA32), \
IF_X64_ELSE(DR_ISA_ARM_A64, \
(TEST(FRAG_THUMB, (flags)) ? DR_ISA_ARM_THUMB : \
DR_ISA_ARM_A32)))
static inline uint
frag_flags_from_isa_mode(dr_isa_mode_t mode)
{
#ifdef X86
# ifdef X64
if (mode == DR_ISA_IA32)
return FRAG_32_BIT;
ASSERT(mode == DR_ISA_AMD64);
return 0;
# else
ASSERT(mode == DR_ISA_IA32);
return 0;
# endif
#elif defined(ARM)
# ifdef X64
ASSERT(mode == DR_ISA_ARM_A64);
return 0;
# else
if (mode == DR_ISA_ARM_THUMB)
return FRAG_THUMB;
ASSERT(mode == DR_ISA_ARM_A32);
return 0;
# endif
#endif
}
/* to save space size field is a ushort => maximum fragment size */
enum { MAX_FRAGMENT_SIZE = USHRT_MAX };
/* fragment structure used for basic blocks and traces
* this is the core structure shared by everything
* trace heads and traces extend it below
*/
struct _fragment_t {
/* WARNING: the tag offset is assumed to be 0 in arch/emit_utils.c
* Also, next and flags' offsets must match future_fragment_t's
* And flags' offset must match fcache.c's empty_slot_t as well as
* vmarea.c's multi_entry_t structs
*/
app_pc tag; /* non-zero fragment tag used for lookups */
/* Contains FRAG_ flags. Should only be modified for FRAG_SHARED fragments
* while holding the change_linking_lock.
*/
uint flags;
/* trace head counters are in separate hashtable since always private.
* FIXME: when all fragments are private, a separate table uses more memory
* than having a counter field for all fragments, including non-trace-heads
*/
/* size in bytes of the fragment (includes body and stubs, and for
* selfmod fragments also includes selfmod app code copy and size field)
*/
ushort size;
/* both of these fields are tiny -- padding shouldn't be more than a cache line
* size (32 P3, 64 P4), prefix should be even smaller.
* they combine with size to shrink fragment_t for us
* N.B.: byte is an unsigned char
*/
byte prefix_size; /* size of prefix, after which is non-ind. br. entry */
byte fcache_extra; /* padding to fit in fcache slot, also includes the header */
cache_pc start_pc; /* very top of fragment's code, equals
* entry point when indirect branch target */
union {
/* For a live fragment, we store a list of other fragments' exits that target
* this fragment (outgoing exit stubs are all allocated with fragment_t struct,
* use FRAGMENT_EXIT_STUBS() to access).
*/
linkstub_t *incoming_stubs;
/* For a pending-deletion fragment (marked with FRAG_WAS_DELETED),
* we store translation info.
*/
translation_info_t *translation_info;
} in_xlate;
fragment_t *next_vmarea; /* for chaining fragments in vmarea list */
fragment_t *prev_vmarea; /* for chaining fragments in vmarea list */
union {
fragment_t *also_vmarea; /* for chaining fragments across vmarea lists */
/* For lazily-deleted fragments, we store the flushtime here, as this
* field is no longer used once a fragment is not live.
*/
uint flushtime;
} also;
#ifdef DEBUG
int id; /* thread-shared-unique fragment identifier */
#endif
#ifdef CUSTOM_TRACES_RET_REMOVAL
int num_calls;
int num_rets;
#endif
}; /* fragment_t */
/* Shared fragments don't need some fields that private ones do, so we
* dynamically choose different structs. fragment_t is for shared only.
* Here we again use C awkwardness to have a subclass.
*/
typedef struct _private_fragment_t {
fragment_t f;
fragment_t *next_fcache; /* for chaining fragments in fcache unit */
fragment_t *prev_fcache; /* for chaining fragments in fcache unit */
} private_fragment_t;
/* Structure used for future fragments, separate to save memory.
* next and flags must be at same offset as for fragment_t, so that
* hashtable (next) and link.c (flags) can polymorphize fragment_t
* and future_fragment_t. The rule is enforced in fragment_init.
*/
struct _future_fragment_t {
app_pc tag; /* non-zero fragment tag used for lookups */
uint flags; /* contains FRAG_ flags */
linkstub_t *incoming_stubs; /* list of other fragments' exits that target
* this fragment */
};
typedef struct _trace_bb_info_t {
app_pc tag;
#if defined(RETURN_AFTER_CALL) || defined(RCT_IND_BRANCH)
/* PR 204770: holds # exits in the trace corresponding to that bb.
* Used to obtain a better RCT source address.
* We could recreate to obtain this, except for flushed fragments: but
* we do this frequently enough that it's simpler to store for all
* fragments.
*/
uint num_exits;
#endif
} trace_bb_info_t;
/* N.B.: if you add fields to trace_t, make sure to add them
* to fragment_copy_data_fields as well as fragment_create and fragment_free
*/
typedef struct _trace_only_t {
#ifdef PROFILE_RDTSC
uint64 count; /* number of executions of this fragment */
uint64 total_time; /* total time ever spent in this fragment */
#endif
#ifdef SIDELINE_COUNT_STUDY
linkcount_type_t count_old_pre;
linkcount_type_t count_old_post;
#endif
/* holds the tags (and other info) for all constituent basic blocks */
trace_bb_info_t *bbs;
uint num_bbs;
} trace_only_t;
/* trace extension of fragment_t */
struct _trace_t {
fragment_t f; /* shared fields */
trace_only_t t;
}; /* trace_t */
/* private version of trace_t */
typedef struct _private_trace_t {
private_fragment_t f;
trace_only_t t;
} private_trace_t;
/* convenient way to deal w/ trace fields: this returns trace_only_t* */
#define TRACE_FIELDS(f) (ASSERT(TEST(FRAG_IS_TRACE, (f)->flags)), \
(TEST(FRAG_SHARED, (f)->flags) ? \
&(((trace_t *)(f))->t) : &(((private_trace_t *)(f))->t)))
/* FIXME: Can be used to determine if a frag should have a prefix since currently
* all IB targets have the same prefix. Use a different macro if different frags
* have different prefixes, i.e., BBs vs. traces.
*/
/* Historically traces were the only IBL targeted fragments, we'd want to have BBs
* targeted too, yet not all of them should indeed be targeted.
* See case 147 about possible extensions for bb non-tracehead
* fragments.
*/
/* FIXME: case 147: private bb's would have a different prefix
* therefore should be taken out of here. Other than that there is no good reason
* not to be able to target them. case 5836 covers targeting private fragments
* when using thread-shared ibl tables.
* A good example is FRAG_SELFMOD_SANDBOXED bb's which are always private.
* However it's not very useful to go after the short lived FRAG_TEMP_PRIVATE
*/
/* can a frag w/the given flags be an IBL target? */
#define IS_IBL_TARGET(flags) \
(TEST(FRAG_IS_TRACE, (flags)) ? \
(TEST(FRAG_SHARED, (flags)) || !DYNAMO_OPTION(shared_trace_ibt_tables)) : \
(DYNAMO_OPTION(bb_ibl_targets) && \
(TEST(FRAG_SHARED, (flags)) || !DYNAMO_OPTION(shared_bb_ibt_tables))))
#define HASHTABLE_IBL_OFFSET(branch_type) \
(((branch_type) == IBL_INDCALL) ? DYNAMO_OPTION(ibl_indcall_hash_offset) : \
DYNAMO_OPTION(ibl_hash_func_offset))
#ifdef HASHTABLE_STATISTICS
/* Statistics written from the cache that must be allocated separately */
typedef struct _unprot_ht_statistics_t {
/* Statistics for App mode indirect branch lookups. Useful only for trace table. */
/* These should be accessible by indirect_branch_lookup emitted routines. */
/* Should have the form <ibl_routine>_stats, and are per hash table per routine (and per thread) */
/* FIXME: These are in the hash table itself for easier access when sharing IBL routines */
hashtable_statistics_t trace_ibl_stats[IBL_BRANCH_TYPE_END];
hashtable_statistics_t bb_ibl_stats[IBL_BRANCH_TYPE_END];
/* FIXME: this should really go to arch/arch.c instead of here */
# ifdef WINDOWS
hashtable_statistics_t shared_syscall_hit_stats; /* miss path is shared with trace_ibl */
# endif
} unprot_ht_statistics_t;
#endif /* HASHTABLE_STATISTICS */
/* Cached tag->start_pc table used by lookuptable in fragment_table_t as
* well as by ibl_table_t
*/
typedef struct _fragment_entry_t {
app_pc tag_fragment; /* non-zero fragment tag used for lookups */
cache_pc start_pc_fragment;/* very top of fragment's code from fragment_t */
} fragment_entry_t;
#define HASHLOOKUP_SENTINEL_START_PC ((cache_pc)PTR_UINT_1)
/* Flags stored in {fragment,ibl}_table_t->flags bitfield
*/
/* Indicates that fragment entries are shared between multiple tables in an
* inclusive hierarchical fashion, so only removal from the master table (which
* is not so marked) will result in fragment deletion. Used primarily for
* IBL targeted tables
*/
/* Updates to these flags should be reflected in
* arch/arch.c:table_flags_to_frag_flags() */
#define FRAG_TABLE_INCLUSIVE_HIERARCHY HASHTABLE_NOT_PRIMARY_STORAGE
/* Set for IBL targeted tables, used in conjuction with FRAG_INCLUSIVE_HIERARCHY */
#define FRAG_TABLE_IBL_TARGETED HASHTABLE_LOCKLESS_ACCESS
/* Set for IBL targeted tables, indicates that the table holds shared targets */
#define FRAG_TABLE_TARGET_SHARED HASHTABLE_ENTRY_SHARED
/* Indicates that the table is shared */
#define FRAG_TABLE_SHARED HASHTABLE_SHARED
/* is this table allocated in persistent memory? */
#define FRAG_TABLE_PERSISTENT HASHTABLE_PERSISTENT
/* Indicates that the table targets traces */
#define FRAG_TABLE_TRACE HASHTABLE_CUSTOM_FLAGS_START
/* hashtable of fragment_t* entries */
/* macros w/ name and types are duplicated in fragment.c -- keep in sync */
#define NAME_KEY fragment
#define ENTRY_TYPE fragment_t *
/* not defining HASHTABLE_USE_LOOKUPTABLE */
#define HASHTABLEX_HEADER 1
#define CUSTOM_FIELDS /* none */
#include "hashtablex.h"
#undef HASHTABLEX_HEADER
/* hashtable of fragment_entry_t entries for per-type ibl tables */
/* macros w/ name and types are duplicated in fragment.c -- keep in sync */
#define NAME_KEY ibl
#define ENTRY_TYPE fragment_entry_t
/* not defining HASHTABLE_USE_LOOKUPTABLE */
#ifdef HASHTABLE_STATISTICS
# define HASHTABLE_ENTRY_STATS 1
# define CUSTOM_FIELDS \
ibl_branch_type_t branch_type; \
/* stats written from the cache must be unprotected by allocating separately \
* FIXME: we could avoid this when protect_mask==0 by having a union here, \
* like we have with mcontext in the dcontext, but not worth the complexity \
* or space for debug-build-only stats \
*/ \
unprot_ht_statistics_t *unprot_stats;
#else
# define CUSTOM_FIELDS \
ibl_branch_type_t branch_type;
#endif /* HASHTABLE_STATISTICS */
#define HASHTABLEX_HEADER 1
#include "hashtablex.h"
#undef HASHTABLEX_HEADER
#if defined(RETURN_AFTER_CALL) || defined (RCT_IND_BRANCH)
/* 3 macros w/ name and types are duplicated in fragment.c -- keep in sync */
#define NAME_KEY app_pc
#define ENTRY_TYPE app_pc
/* not defining HASHTABLE_USE_LOOKUPTABLE */
#define CUSTOM_FIELDS /* none */
#define HASHTABLEX_HEADER 1
#include "hashtablex.h"
#undef HASHTABLEX_HEADER
#endif /* defined(RETURN_AFTER_CALL) || defined (RCT_IND_BRANCH) */
/* We keep basic blocks and traces in separate hashtables. This is to
* speed up indirect_branch_lookup that looks for traces only, but it
* means our lookup function has to look in both hashtables. This has
* no noticeable performance impact. A strategy of having an
* all-fragment hashtable and a trace-only hashtable that mirrors just
* the traces of the all-fragment hashtable performs similarly but is
* more complicated since fragments need two different next fields,
* plus it uses more memory because traces are in two hashtables
* simultaneously.
*
* FIXME: Shared bb IBL routines indirectly access only a few fields
* from each fragment_table_t which will touch a separate cache line for
* each. However, trace IBL routines don't indirect so I don't expect
* a performance hit of using the current struct layout.
* FIXME: The bb IBL routines however are shared and therefore
* indirect, so splitting the fragment_table_t in two compactable
* structures may be worth trying.
*/
typedef struct _per_thread_t {
ibl_table_t trace_ibt[IBL_BRANCH_TYPE_END]; /* trace IB targets */
ibl_table_t bb_ibt[IBL_BRANCH_TYPE_END]; /* bb IB targets */
fragment_table_t bb;
fragment_table_t trace;
fragment_table_t future;
#if defined(CLIENT_INTERFACE) && defined(CLIENT_SIDELINE)
mutex_t fragment_delete_mutex;
#endif
file_t tracefile;
/* used for unlinking other threads' caches for flushing */
bool could_be_linking; /* accessing link data structs? */
bool wait_for_unlink; /* should this thread wait at synch point? */
bool about_to_exit; /* is this thread about to exit, so no need to flush? */
bool flush_queue_nonempty; /* is this thread's deletion queue nonempty? */
event_t waiting_for_unlink; /* synch bet flusher and flushee */
event_t finished_with_unlink; /* ditto */
event_t finished_all_unlink;/* ditto */
/* this lock controls all 4 vars above, plus linking/unlinking shared_syscall,
* plus modifying queue of to-be-deleted thread-local vm regions
*/
mutex_t linking_lock;
bool soon_to_be_linking; /* tells flusher thread is at cache exit synch */
/* for shared_deletion protocol */
uint flushtime_last_update;
/* for syscalls_synch_flush, only used to cache whether a thread was at
* a syscall during early flushing stages for use in later stages.
* not used while not flushing.
*/
bool at_syscall_at_flush;
#ifdef PROFILE_LINKCOUNT
uint tracedump_num_below_threshold;
linkcount_type_t tracedump_count_below_threshold;
#endif
} per_thread_t;
#define FCACHE_ENTRY_PC(f) (f->start_pc + f->prefix_size)
#define FCACHE_PREFIX_ENTRY_PC(f) \
(f->start_pc + f->prefix_size - FRAGMENT_BASE_PREFIX_SIZE(f->flags))
#define FCACHE_IBT_ENTRY_PC(f) (f->start_pc)
/* translation info pointer can be at end of any struct, so rather than have
* 8 different structs we keep it out of the formal struct definitions
*/
#define FRAGMENT_STRUCT_SIZE(flags) \
((TEST(FRAG_IS_TRACE, (flags)) ? \
(TEST(FRAG_SHARED, (flags)) ? sizeof(trace_t) : sizeof(private_trace_t)) : \
(TEST(FRAG_SHARED, (flags)) ? sizeof(fragment_t) : sizeof(private_fragment_t))) \
+ (TEST(FRAG_HAS_TRANSLATION_INFO, flags) ? sizeof(translation_info_t*) : 0))
#define FRAGMENT_EXIT_STUBS(f) \
(TEST(FRAG_FAKE, (f)->flags) ? \
(ASSERT(false && "fake fragment_t has no exit stubs!"), (linkstub_t *)NULL) : \
((linkstub_t *)(((byte*)(f)) + FRAGMENT_STRUCT_SIZE((f)->flags))))
/* selfmod copy size is stored at very end of fragment space */
#define FRAGMENT_SELFMOD_COPY_SIZE(f) \
(ASSERT(TEST(FRAG_SELFMOD_SANDBOXED, (f)->flags)), \
(*((uint *)((f)->start_pc + (f)->size - sizeof(uint)))))
#define FRAGMENT_SELFMOD_COPY_CODE_SIZE(f) \
(FRAGMENT_SELFMOD_COPY_SIZE(f) - sizeof(uint))
#define FRAGMENT_SELFMOD_COPY_PC(f) \
(ASSERT(TEST(FRAG_SELFMOD_SANDBOXED, (f)->flags)), \
((f)->start_pc + (f)->size - FRAGMENT_SELFMOD_COPY_SIZE(f)))
#define FRAGMENT_TRANSLATION_INFO_ADDR(f) \
(TEST(FRAG_HAS_TRANSLATION_INFO, (f)->flags) ? \
((translation_info_t **)(((byte*)(f)) + FRAGMENT_STRUCT_SIZE((f)->flags) \
- sizeof(translation_info_t*))) : \
((INTERNAL_OPTION(safe_translate_flushed) && TEST(FRAG_WAS_DELETED, (f)->flags)) ? \
&((f)->in_xlate.translation_info) : NULL))
#define HAS_STORED_TRANSLATION_INFO(f) \
(TEST(FRAG_HAS_TRANSLATION_INFO, (f)->flags) || \
(INTERNAL_OPTION(safe_translate_flushed) && TEST(FRAG_WAS_DELETED, (f)->flags)))
#define FRAGMENT_TRANSLATION_INFO(f) \
(HAS_STORED_TRANSLATION_INFO(f) ? (*(FRAGMENT_TRANSLATION_INFO_ADDR(f))) : NULL)
/* Returns the end of the fragment body + any local stubs (excluding selfmod copy) */
cache_pc
fragment_stubs_end_pc(fragment_t *f);
/* Returns the end of the fragment body (excluding exit stubs and selfmod copy) */
cache_pc
fragment_body_end_pc(dcontext_t *dcontext, fragment_t *f);
bool
fragment_initialized(dcontext_t *dcontext);
void
fragment_init(void);
void
fragment_exit(void);
void
fragment_reset_init(void);
void
fragment_reset_free(void);
void
fragment_thread_init(dcontext_t *dcontext);
void
fragment_thread_exit(dcontext_t *dcontext);
/* re-initializes non-persistent memory */
void
fragment_thread_reset_init(dcontext_t *dcontext);
/* frees all non-persistent memory */
void
fragment_thread_reset_free(dcontext_t *dcontext);
#ifdef UNIX
void
fragment_fork_init(dcontext_t *dcontext);
#endif
#ifdef PROFILE_LINKCOUNT
linkcount_type_t
get_total_linkcount(fragment_t *f);
#endif
fragment_t *
fragment_create(dcontext_t *dcontext, app_pc tag,
int body_size, int direct_exits, int indirect_exits,
int exits_size, uint flags);
/* Creates a new fragment_t+linkstubs from the passed-in fragment and
* fills in linkstub_t and fragment_t fields, copying the fcache-related fields
* from the passed-in fragment (so be careful how the fields are used).
* Meant to be used to create a full fragment from a coarse-grain fragment.
* Caller is responsible for freeing via fragment_free().
*/
fragment_t *
fragment_recreate_with_linkstubs(dcontext_t *dcontext, fragment_t *f_src);
/* Frees the storage associated with f.
* Callers should use fragment_delete() instead of this routine, unless they
* obtained their fragment_t from fragment_recreate_with_linkstubs().
*/
void
fragment_free(dcontext_t *dcontext, fragment_t *f);
void
fragment_add(dcontext_t *dcontext, fragment_t *f);
void
fragment_copy_data_fields(dcontext_t *dcontext, fragment_t *f_src, fragment_t *f_dst);
/* options for fragment_delete actions param
* N.B.: these are NEGATIVE since callers care what's NOT done
*/
enum {
FRAGDEL_ALL = 0x000,
FRAGDEL_NO_OUTPUT = 0x001,
FRAGDEL_NO_UNLINK = 0x002,
FRAGDEL_NO_HTABLE = 0x004,
FRAGDEL_NO_FCACHE = 0x008,
FRAGDEL_NO_HEAP = 0x010,
FRAGDEL_NO_MONITOR = 0x020,
FRAGDEL_NO_VMAREA = 0x040,
FRAGDEL_NEED_CHLINK_LOCK = 0x080,
};
void
fragment_delete(dcontext_t *dcontext, fragment_t *f, uint actions);
void
fragment_record_translation_info(dcontext_t *dcontext, fragment_t *f, instrlist_t *ilist);
void
fragment_remove_shared_no_flush(dcontext_t *dcontext, fragment_t *f);
void
fragment_unlink_for_deletion(dcontext_t *dcontext, fragment_t *f);
bool
fragment_prepare_for_removal(dcontext_t *dcontext, fragment_t *f);
/* Removes f from any IBT tables it is in. */
void
fragment_remove_from_ibt_tables(dcontext_t *dcontext, fragment_t *f, bool from_shared);
uint
fragment_remove_all_ibl_in_region(dcontext_t *dcontext, app_pc start, app_pc end);
/* Removes f from any hashtables -- BB, trace, future -- and IBT tables
* it is in */
void
fragment_remove(dcontext_t *dcontext, fragment_t *f);
void
fragment_replace(dcontext_t *dcontext, fragment_t *f, fragment_t *new_f);
fragment_t *
fragment_lookup(dcontext_t *dcontext, app_pc tag);
fragment_t *
fragment_lookup_bb(dcontext_t *dcontext, app_pc tag);
fragment_t *
fragment_lookup_shared_bb(dcontext_t *dcontext, app_pc tag);
fragment_t *
fragment_lookup_trace(dcontext_t *dcontext, app_pc tag);
fragment_t *
fragment_lookup_same_sharing(dcontext_t *dcontext, app_pc tag, uint flags);
fragment_t *
fragment_pclookup(dcontext_t *dcontext, cache_pc pc, fragment_t *wrapper);
/* Performs a pclookup and if the result is a coarse-grain fragment, allocates
* a new fragment_t+linkstubs.
* Returns in alloc whether the returned fragment_t was allocated and needs to be
* freed by the caller via fragment_free().
* If no result is found, alloc is set to false.
*/
fragment_t *
fragment_pclookup_with_linkstubs(dcontext_t *dcontext, cache_pc pc,
/*OUT*/bool *alloc);
#ifdef DEBUG
fragment_t *
fragment_pclookup_by_htable(dcontext_t *dcontext, cache_pc pc, fragment_t *wrapper);
#endif
void
fragment_shift_fcache_pointers(dcontext_t *dcontext, fragment_t *f, ssize_t shift,
cache_pc start, cache_pc end, size_t old_size);
fragment_t *
fragment_add_ibl_target(dcontext_t *dcontext, app_pc tag, ibl_branch_type_t branch_type);
/* future fragments */
future_fragment_t *
fragment_create_and_add_future(dcontext_t *dcontext, app_pc tag, uint flags);
void
fragment_delete_future(dcontext_t *dcontext, future_fragment_t *fut);
future_fragment_t *
fragment_lookup_future(dcontext_t *dcontext, app_pc tag);
future_fragment_t *
fragment_lookup_private_future(dcontext_t *dcontext, app_pc tag);
#ifdef RETURN_AFTER_CALL
app_pc
fragment_after_call_lookup(dcontext_t *dcontext, app_pc tag);
void
fragment_add_after_call(dcontext_t *dcontext, app_pc tag);
void
fragment_flush_after_call(dcontext_t *dcontext, app_pc tag);
uint
invalidate_after_call_target_range(dcontext_t *dcontext,
app_pc text_start, app_pc text_end);
#endif /* RETURN_AFTER_CALL */
#if defined(RETURN_AFTER_CALL) || defined(RCT_IND_BRANCH)
/* case 9672: we split our RCT and RAC targets into per-module tables.
* To support sharing, we separate the persisted from the live.
* Typedef is in globals.h
*/
struct _rct_module_table_t {
app_pc_table_t *persisted_table;
app_pc_table_t *live_table;
/* Optimization: to avoid walking the table to find entries in a
* coarse unit's region we track the max and min entries in the
* live table. In the common case we can then do a straight copy
*/
app_pc live_min;
app_pc live_max;
};
# ifdef UNIX
extern rct_module_table_t rct_global_table;
# endif
void
rct_module_table_free(dcontext_t *dcontext, rct_module_table_t *permod, app_pc modpc);
bool
rct_module_table_set(dcontext_t *dcontext, app_pc modpc, app_pc_table_t *table,
rct_type_t which);
void
rct_module_table_persisted_invalidate(dcontext_t *dcontext, app_pc modpc);
bool
rct_module_persisted_table_exists(dcontext_t *dcontext, app_pc modpc,
rct_type_t which);
uint
rct_module_live_entries(dcontext_t *dcontext, app_pc modpc, rct_type_t which);
app_pc_table_t *
rct_module_table_copy(dcontext_t *dcontext, app_pc modpc, rct_type_t which,
app_pc limit_start, app_pc limit_end);
void
rct_table_free(dcontext_t *dcontext, app_pc_table_t *table, bool free_data);
app_pc_table_t *
rct_table_copy(dcontext_t *dcontext, app_pc_table_t *src);
app_pc_table_t *
rct_table_merge(dcontext_t *dcontext, app_pc_table_t *src1, app_pc_table_t *src2);
uint
rct_table_persist_size(dcontext_t *dcontext, app_pc_table_t *table);
bool
rct_table_persist(dcontext_t *dcontext, app_pc_table_t *table, file_t fd);
app_pc_table_t *
rct_table_resurrect(dcontext_t *dcontext, byte *mapped_table, rct_type_t which);
#endif /* RETURN_AFTER_CALL || RCT_IND_BRANCH */
#if defined(CLIENT_INTERFACE) && defined(CLIENT_SIDELINE)
/* synchronization routine for sideline thread */
void
fragment_get_fragment_delete_mutex(dcontext_t *dcontext);
void
fragment_release_fragment_delete_mutex(dcontext_t *dcontext);
#endif
/*******************************************************************************
* COARSE-GRAIN FRAGMENT HASHTABLE
*/
/* N.B.: if you change the coarse_table_t struct you must increase
* PERSISTENT_CACHE_VERSION!
*/
typedef struct _app_to_cache_t {
app_pc app;
/* cache is absolute pc for non-frozen units, but a relative offset for frozen.
* PR 203915: should we split into separate instantation to
* use a 32-bit offset for frozen on x64? For now not worth it.
*/
cache_pc cache;
} app_to_cache_t;
/* 3 macros w/ name and types are duplicated in fragment.c -- keep in sync */
#define NAME_KEY coarse
#define ENTRY_TYPE app_to_cache_t
/* not defining HASHTABLE_USE_LOOKUPTABLE */
#define CUSTOM_FIELDS \
ssize_t mod_shift;
#define HASHTABLEX_HEADER 1
#include "hashtablex.h"
#undef HASHTABLEX_HEADER
void
fragment_coarse_htable_create(coarse_info_t *info, uint init_capacity,
uint init_th_capacity);
void
fragment_coarse_htable_free(coarse_info_t *info);
/* Merges the main and th htables from info1 and info2 into new htables for dst.
* If !add_info2, makes room for but does not add entries from info2.
* If !add_th_htable, creates but does not add entries to dst->th_htable.
*/
void
fragment_coarse_htable_merge(dcontext_t *dcontext, coarse_info_t *dst,
coarse_info_t *info1, coarse_info_t *info2,
bool add_info2, bool add_th_htable);
uint
fragment_coarse_num_entries(coarse_info_t *info);
/* Add coarse fragment represented by wrapper f to its hashtable */
void
fragment_coarse_add(dcontext_t *dcontext, coarse_info_t *info,
app_pc tag, cache_pc cache);
void
fragment_coarse_th_add(dcontext_t *dcontext, coarse_info_t *info,
app_pc tag, cache_pc cache);
void
fragment_coarse_th_unlink_and_add(dcontext_t *dcontext, app_pc tag,
cache_pc stub_pc, cache_pc body_pc);
#ifdef DEBUG
/* only exported to allow an assert to avoid rank order issues in
* push_pending_freeze() */
void
coarse_body_from_htable_entry(dcontext_t *dcontext, coarse_info_t *info,
app_pc tag, cache_pc res,
cache_pc *stub_pc_out/*OUT*/,
cache_pc *body_pc_out/*OUT*/);
#endif
void
fragment_coarse_lookup_in_unit(dcontext_t *dcontext, coarse_info_t *info, app_pc tag,
cache_pc *stub_pc/*OUT*/, cache_pc *body_pc/*OUT*/);
cache_pc
fragment_coarse_lookup(dcontext_t *dcontext, app_pc tag);
void
fragment_coarse_wrapper(fragment_t *wrapper, app_pc tag, cache_pc body_pc);
fragment_t *
fragment_coarse_lookup_wrapper(dcontext_t *dcontext, app_pc tag, fragment_t *wrapper);
fragment_t *
fragment_lookup_fine_and_coarse(dcontext_t *dcontext, app_pc tag,
fragment_t *wrapper, linkstub_t *last_exit);
fragment_t *
fragment_lookup_fine_and_coarse_sharing(dcontext_t *dcontext, app_pc tag,
fragment_t *wrapper, linkstub_t *last_exit,
uint share_flags);
coarse_info_t *
get_fragment_coarse_info(fragment_t *f);
bool
coarse_is_trace_head_in_own_unit(dcontext_t *dcontext, app_pc tag, cache_pc stub,
cache_pc body_in, bool body_valid,
coarse_info_t *info_in);
bool
fragment_coarse_replace(dcontext_t *dcontext, coarse_info_t *info, app_pc tag,
cache_pc new_value);
/* Returns the tag for the coarse fragment whose body contains pc, as well as the
* body pc in the optional OUT param.
*/
app_pc
fragment_coarse_pclookup(dcontext_t *dcontext, coarse_info_t *info, cache_pc pc,
/*OUT*/cache_pc *body);
/* Creates a reverse lookup table. For a non-frozen unit, the caller should only
* do this while all threads are suspended, and should free the table before
* resuming other threads.
*/
void
fragment_coarse_create_entry_pclookup_table(dcontext_t *dcontext, coarse_info_t *info);
void
fragment_coarse_free_entry_pclookup_table(dcontext_t *dcontext, coarse_info_t *info);
/* Returns the tag for the coarse fragment whose body _begins at_ pc */
app_pc
fragment_coarse_entry_pclookup(dcontext_t *dcontext, coarse_info_t *info,
cache_pc pc);
void
fragment_coarse_unit_freeze(dcontext_t *dcontext,
coarse_freeze_info_t *freeze_info);
uint
fragment_coarse_htable_persist_size(dcontext_t *dcontext, coarse_info_t *info,
bool cache_table);
bool
fragment_coarse_htable_persist(dcontext_t *dcontext, coarse_info_t *info,
bool cache_table, file_t fd);
void
fragment_coarse_htable_resurrect(dcontext_t *dcontext, coarse_info_t *info,
bool cache_table, byte *mapped_table);
/*******************************************************************************/
void
fragment_output(dcontext_t *dcontext, fragment_t *f);
bool
fragment_overlaps(dcontext_t *dcontext, fragment_t *f,
byte *region_start, byte *region_end, bool page_only,
overlap_info_t *info_res, app_pc *bb_tag);
bool
is_self_flushing(void);
bool
is_self_allsynch_flushing(void);
bool
is_self_couldbelinking(void);
/* N.B.: can only call if target thread is suspended or waiting for flush */
bool
is_couldbelinking(dcontext_t *dcontext);
/* Returns false iff watch ends up being deleted */
bool
enter_nolinking(dcontext_t *dcontext, fragment_t *watch, bool temporary);
/* Returns false iff watch ends up being deleted */
bool
enter_couldbelinking(dcontext_t *dcontext, fragment_t *watch, bool temporary);
void
enter_threadexit(dcontext_t *dcontext);
uint
get_flushtime_last_update(dcontext_t *dcontext);
void
set_flushtime_last_update(dcontext_t *dcontext, uint val);
/* caller must hold shared_cache_flush_lock */
void
increment_global_flushtime(void);
void
set_at_syscall(dcontext_t *dcontext, bool val);
bool
get_at_syscall(dcontext_t *dcontext);
/****************************************************************************
* FLUSHING
*
* Typical use is to flush a memory region in
* flush_fragments_and_remove_region(). If custom executable areas editing
* is required, the memory-region-flushing pair
* flush_fragments_in_region_start() and flush_fragments_in_region_finish()
* should be used (they MUST be used together). If no executable area removal
* is needed use flush_fragments_from_region().
*
* Alternatively, the trio (also not usable individually)
* 1) flush_fragments_synch_unlink_priv
* 2) flush_fragments_unlink_shared
* 3) flush_fragments_end_synch
* provide flushing for non-memory regions via a list of fragments. The
* list should be chained by next_vmarea and vm_area_remove_fragment()
* should already have been called on each fragment in the list. This
* usage does not involve the executable_areas lock at all.
*
* The exec_invalid parameter must be set to indicate whether the
* executable area is being invalidated as well or this is just a capacity
* flush (or a flush to change instrumentation).
*
* Possibly the thread_initexit_lock (if there are fragments to
* flush), and always executable_areas lock for region flushing, ARE
* HELD IN BETWEEN the routines, and no thread is could_be_linking()
* in between.
* WARNING: case 8572: the caller owning the thread_initexit_lock
* is incompatible w/ suspend-the-world flushing!
*/
/* If size>0, returns whether there is an overlap; if not, no synch is done.
* If size==0, synch is always performed and true is always returned.
*/
bool
flush_fragments_synch_unlink_priv(dcontext_t *dcontext, app_pc base, size_t size,
bool own_initexit_lock, bool exec_invalid,
bool force_synchall _IF_DGCDIAG(app_pc written_pc));
void
flush_fragments_unlink_shared(dcontext_t *dcontext, app_pc base, size_t size,
fragment_t *list _IF_DGCDIAG(app_pc written_pc));
/* Invalidates (does not remove) shared fragment f from the private/shared
* ibl tables. Can only be called in flush stage 2.
*/
void
flush_invalidate_ibl_shared_target(dcontext_t *dcontext, fragment_t *f);
void
flush_fragments_end_synch(dcontext_t *dcontext, bool keep_initexit_lock);
void
flush_fragments_in_region_start(dcontext_t *dcontext, app_pc base, size_t size,
bool own_initexit_lock, bool free_futures,
bool exec_invalid, bool force_synchall
_IF_DGCDIAG(app_pc written_pc));
void
flush_fragments_in_region_finish(dcontext_t *dcontext, bool keep_initexit_lock);
void
flush_fragments_and_remove_region(dcontext_t *dcontext, app_pc base, size_t size,
bool own_initexit_lock, bool free_futures);
void
flush_fragments_from_region(dcontext_t *dcontext, app_pc base, size_t size,
bool force_synchall);
void
flush_fragments_custom_list(dcontext_t *dcontext, fragment_t *list,
bool own_initexit_lock, bool exec_invalid);
/* Invalidate all fragments in all caches. Currently executed
* fragments may be alive until they reach an exit. It should be used
* for correctness when finishing existing fragment execution is
* acceptable. There are no guarantees on actual deletion from this
* interface.
*/
void
invalidate_code_cache(void);
/* Flushes all areas stored in the vector toflush.
* Synch is up to caller, but as locks cannot be held when flushing,
* toflush needs to be thread-private.
*/
void
flush_vmvector_regions(dcontext_t *dcontext, vm_area_vector_t *toflush,
bool free_futures, bool exec_invalid);
/*
****************************************************************************/
/* locks must be exported for DEADLOCK_AVOIDANCE */
/* allows independent sequences of flushes and delayed deletions */
extern mutex_t shared_cache_flush_lock;
/* Global count of flushes, used as a timestamp for shared deletion.
* Writes to it are protected by shared_cache_flush_lock.
*/
extern uint flushtime_global;
#ifdef CLIENT_INTERFACE
extern mutex_t client_flush_request_lock;
extern client_flush_req_t *client_flush_requests;
#endif
#ifdef PROFILE_RDTSC
void
profile_fragment_enter(fragment_t *f, uint64 end_time);
void
profile_fragment_dispatch(dcontext_t *dcontext);
#endif
void
fragment_self_write(dcontext_t *dcontext);
#ifdef SHARING_STUDY
void
print_shared_stats(void);
#endif
#define PROTECT_FRAGMENT_ENABLED(flags) \
(TEST(FRAG_SHARED, (flags)) ? \
TEST(SELFPROT_GLOBAL, dynamo_options.protect_mask) : \
TEST(SELFPROT_LOCAL, dynamo_options.protect_mask))
#ifdef DEBUG
void study_all_hashtables(dcontext_t *dcontext);
#endif /* DEBUG */
#ifdef HASHTABLE_STATISTICS
/* in arch/interp.c */
int
append_ib_trace_last_ibl_exit_stat(dcontext_t *dcontext, instrlist_t *trace,
app_pc speculate_next_tag);
static INLINE_ONCE
hashtable_statistics_t*
get_ibl_per_type_statistics(dcontext_t *dcontext, ibl_branch_type_t branch_type)
{
per_thread_t *pt = (per_thread_t *) dcontext->fragment_field;
return &pt->trace_ibt[branch_type].unprot_stats->
trace_ibl_stats[branch_type];
}
#endif /* HASHTABLE_STATISTICS */
/* DR_API EXPORT TOFILE dr_tools.h */
/* DR_API EXPORT BEGIN */
/****************************************************************************
* BINARY TRACE DUMP FORMAT
*/
/**<pre>
* Binary trace dump format:
* the file starts with a tracedump_file_header_t
* then, for each trace:
struct _tracedump_trace_header
if num_bbs > 0 # tracedump_origins
foreach bb:
app_pc tag;
int bb_code_size;
byte code[bb_code_size];
endif
foreach exit:
struct _tracedump_stub_data
if linkcount_size > 0
linkcount_type_t count; # sizeof == linkcount_size
endif
if separate from body
(i.e., exit_stub < cache_start_pc || exit_stub >= cache_start_pc+code_size):
byte stub_code[15]; # all separate stubs are 15
endif
endfor
byte code[code_size];
* if the -tracedump_threshold option (deprecated) was specified:
int num_below_treshold
linkcount_type_t count_below_threshold
endif
</pre>
*/
typedef struct _tracedump_file_header_t {
int version; /**< The DynamoRIO version that created the file. */
bool x64; /**< Whether a 64-bit DynamoRIO library created the file. */
int linkcount_size; /**< Size of the linkcount (linkcounts are deprecated). */
} tracedump_file_header_t;
/** Header for an individual trace in a binary trace dump file. */
typedef struct _tracedump_trace_header_t {
int frag_id; /**< Identifier for the trace. */
app_pc tag; /**< Application address for start of trace. */
app_pc cache_start_pc; /**< Code cache address of start of trace. */
int entry_offs; /**< Offset into trace of normal entry. */
int num_exits; /**< Number of exits from the trace. */
int code_size; /**< Length of the trace in the code cache. */
uint num_bbs; /**< Number of constituent basic blocks making up the trace. */
bool x64; /**< Whether the trace contains 64-bit code. */
} tracedump_trace_header_t;
/** Size of tag + bb_code_size fields for each bb. */
#define BB_ORIGIN_HEADER_SIZE (sizeof(app_pc)+sizeof(int))
/**< tracedump_stub_data_t.stub_size will not exceed this value. */
#define SEPARATE_STUB_MAX_SIZE IF_X64_ELSE(23, 15)
/** The format of a stub in a trace dump file. */
typedef struct _tracedump_stub_data {
int cti_offs; /**< Offset from the start of the fragment. */
/* stub_pc is an absolute address, since can be separate from body. */
app_pc stub_pc; /**< Code cache address of the stub. */
app_pc target; /**< Target of the stub. */
bool linked; /**< Whether the stub is linked to its target. */
int stub_size; /**< Length of stub_code array */
/****** the rest of the fields are optional and may not be present! ******/
union {
uint count32; /**< 32-bit exit execution count. */
uint64 count64; /**< 64-bit exit execution count. */
} count; /**< Which field is present depends on the first entry in
* the file, which indicates the linkcount size. */
/** Code for exit stubs. Only present if:
* stub_pc < cache_start_pc ||
* stub_pc >= cache_start_pc+code_size).
* The actual size of the array varies and is indicated by the stub_size field.
*/
byte stub_code[1/*variable-sized*/];
} tracedump_stub_data_t;
/** The last offset into tracedump_stub_data_t of always-present fields. */
#define STUB_DATA_FIXED_SIZE (offsetof(tracedump_stub_data_t, count))
/****************************************************************************/
/* DR_API EXPORT END */
#endif /* _FRAGMENT_H_ */