blob: 2b9cc677e8e7baa259546988e18fbe06b4e0e4f4 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012 Google, Inc. All rights reserved.
* Copyright (c) 2006-2008 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) 2006-2007 Determina Corp. */
/*
* perscache.h - persistent code cache exports
*/
#ifndef _PERSCACHE_H_
#define _PERSCACHE_H_ 1
#include "module_shared.h" /* for module_digest_t */
/***************************************************************************
* COARSE-GRAIN UNITS
*/
/* Information kept per coarse-grain region.
* FIXME: for sharing we want to keep htable, stubs, and incoming
* per unit, not per cache. That will require changing fcache_unit_t and
* having a way to map from app pc to unit. For this first step toward
* sharing we stick with a per-cache scheme with the multiple units
* internal to the cache simply being ignored at this level.
* Cache consistency needs to treat this as a unit in any case.
*
* Synchronization model: the struct lock controls writes to the
* direct fields. The cache, htable, th_htable, and stubs fields are
* all assumed to only be written at init time, and thus internal
* changes to the objects do not require the struct lock.
* The struct lock is used at init time and for later writes to incoming
* and frozen.
* Destruction is assumed to involve all-thread-synch and so reads of
* fields do not require the struct lock. Just like reset, we rely on
* all-thread-synch plus redirection to dispatch (rather than resuming
* at suspended location, which we only do for native threads) to
* allow us to free these shared structures that are read w/o locks.
* FIXME: what about where we lack setcontext permission
*/
struct _coarse_info_t {
bool frozen:1;
bool persisted:1;
bool in_use:1; /* are we using this unit officially? */
/* Flag to indicate whether we've calculated the rac/rct/hotp info that
* we only need when persisting.
*/
bool has_persist_info:1;
/* Case 9653: only the 1st coarse unit in a module's +x region(s) is persisted
* Non-in-use units inherit this from their sources, but do not
* change the status on deletion.
*/
bool primary_for_module:1;
/* case 10525 where we keep the stubs read-only */
bool stubs_readonly:1;
#ifdef DEBUG
/* A local info pointer has not escaped to any other thread.
* We only use this flag to get around lock ordering issues (case 11064).
*/
bool is_local:1; /* no lock needed since only known to this thread */
#endif
void *cache; /* opaque type internal to fcache.c */
/* For frozen units, htable holds body pcs, while th_htable holds stub pcs.
* FIXME case 8628: split these into a body table and a stub table for
* non-frozen units.
*/
void *htable; /* opaque htable mapping app pc -> stub/cache entry point */
void *th_htable; /* opaque htable mapping trace head app pc -> cache entry point */
/* cache pclookups to avoid htable walk (i#658) */
void *pclookup_last_htable; /* opaque htable caching recent non-entry pclookups */
void *stubs; /* opaque special heap */
cache_pc fcache_return_prefix;
cache_pc trace_head_return_prefix;
cache_pc ibl_ret_prefix;
cache_pc ibl_call_prefix;
cache_pc ibl_jmp_prefix;
coarse_incoming_t *incoming;
/* These fields are non-NULL only for frozen units.
* Since htable entries are offsets we need to expose the cache and stub pcs
*/
cache_pc cache_start_pc;
cache_pc cache_end_pc; /* last instr, not end of allocation */
cache_pc stubs_start_pc; /* this is post-prefixes */
cache_pc stubs_end_pc; /* may not fill out full mmap_size if overestimate */
/* if not persisted, this is the bounds of the region shared by
* the frozen cache and stubs, assumed to start at cache_start_pc;
* if persisted, this is the bounds of the entire mmapped file.
*/
size_t mmap_size;
/* Performance optimization for frozen units (critical for trace building) */
void *pclookup_htable; /* opaque htable mapping cache entry point -> app pc */
/* end frozen-only fields */
/* Fields for persisted units */
uint flags; /* corresponds to PERSCACHE_ flags for persisted files */
cache_pc mmap_pc; /* start of persisted mmapped file; size is mmap_size */
/* if this is >0, we mapped the file in two different views:
* 1) [mmap_pc,mmap_pc+mmap_ro_size)
* 2) [mmap_pc+mmap_ro_size,mmap_pc+mmap_size)
*/
size_t mmap_ro_size;
/* case 9925: we may want to keep the file handle open for the duration */
file_t fd;
/* If we merged with a persisted file, we store the original size so
* we can avoid re-merging with that on-disk file.
*/
size_t persisted_source_mmap_size;
#if defined(RETURN_AFTER_CALL) || defined(RCT_IND_BRANCH)
app_pc_table_t *rct_table;
app_pc_table_t *rac_table;
/* Case 9639: fill ibl tables from persisted rac/rct table
* entries. We only do once, storing here to enforce that.
*/
uint ibl_pending_used; /* bitmask from (1<<(IBL_<type>-1)) so we use each once */
#endif
#ifdef HOT_PATCHING_INTERFACE
/* Case 9995: array of hotp patch points matched at persist time, which
* means they should have been made fine-grained and NOT be present
* in the pcache.
* Used for persisted units only; for this-run units it would only make
* sense to use this if hotp did not ignore idempotent ppoint changes
* at nudge time, which may be the case today but better to fix hotp.
*/
app_rva_t *hotp_ppoint_vec;
uint hotp_ppoint_vec_num; /* number of elements in array */
#endif
/* case 10525: leave stubs as writable if written too many times */
uint stubs_write_count;
/* case 9521: we can have a second unit in the same region for new,
* non-frozen coarse code if the primary unit is frozen.
* Presumably frozen unit is larger so we put it first.
*/
struct _coarse_info_t *non_frozen;
/* FIXME: add debug-build stats on #fragments, #stubs,
* #trace heads, etc.
*/
/***************************************************
* Fields below this point are preserved across a coarse_unit_reset_free(),
* while those above are cleared.
*/
/* Controls access to directly changing the fields of the struct, except
* the incoming list */
mutex_t lock;
/* Controls the incoming list; separated to allow holding the src main
* lock while changing target linked unit locks (case 9809) */
mutex_t incoming_lock;
/* FIXME: once we persist/share we will need official labels to identify
* source modules
*/
app_pc base_pc; /* base of vm area this unit covers */
app_pc end_pc; /* end of vm area this unit covers */
#ifdef DEBUG
const char *module;
#endif
/* MD5 of the module, used only for persisting but we calculate at load time
* so we're comparing the in-memory image at a consistent point.
*/
module_digest_t module_md5;
/* persisted base */
app_pc persist_base;
/* persisted base minus cur base */
ssize_t mod_shift;
/* Only add a field here if it should be preserved across coarse_unit_reset_free */
}; /* typedef as "coarse_info_t" is in globals.h */
#ifdef X64
# define COARSE_32_FLAG(info) (TEST(PERSCACHE_X86_32, (info)->flags) ? FRAG_32_BIT : 0)
#else
# define COARSE_32_FLAG(info) 0
#endif
coarse_info_t *
coarse_unit_create(app_pc base_pc, app_pc end_pc, module_digest_t *digest,
bool for_execution);
void
coarse_unit_init(coarse_info_t *info, void *cache);
/* If caller holds change_linking_lock and info->lock, have_locks should be true */
void
coarse_unit_reset_free(dcontext_t *dcontext, coarse_info_t *info,
bool have_locks, bool unlink, bool abdicate_primary);
void
coarse_unit_free(dcontext_t *dcontext, coarse_info_t *info);
void
coarse_unit_mark_in_use(coarse_info_t *info);
/***************************************************************************
* FROZEN UNITS
*/
/* For storing information needed during the freezing process */
typedef struct _pending_freeze_t {
bool entrance_stub;
bool trace_head;
app_pc tag;
cache_pc cur_pc;
cache_pc link_cti_opnd; /* 4-byte pc-relative opnd to re-target */
bool elide_ubr; /* whether to elide the link, if that's an option */
struct _pending_freeze_t *next;
} pending_freeze_t;
struct _coarse_freeze_info_t {
coarse_info_t *src_info;
coarse_info_t *dst_info;
cache_pc cache_start_pc;
cache_pc cache_cur_pc;
cache_pc stubs_start_pc;
cache_pc stubs_cur_pc;
bool unlink;
pending_freeze_t *pending;
#ifdef DEBUG
/* statistics on frozen code expansion from original app code */
size_t app_code_size;
uint num_cbr;
uint num_jmp;
uint num_call;
uint num_indbr;
/* removed code: */
uint num_elisions;
/* trying to enumerate all the added code: */
uint added_fallthrough;
uint added_indbr_mangle; /* all but the stub and the (removable) jmp to stub */
uint added_indbr_stub; /* stub plus the jmp to it */
uint added_jecxz_mangle;
#endif
}; /* typedef as "coarse_freeze_info_t" is in globals.h */
void
perscache_init(void);
void
perscache_fast_exit(void);
void
perscache_slow_exit(void);
bool
perscache_dirname(char *directory /* OUT */, uint directory_len);
void
coarse_units_freeze_all(bool in_place);
coarse_info_t *
coarse_unit_freeze(dcontext_t *dcontext, coarse_info_t *info, bool in_place);
void
transfer_coarse_stub(dcontext_t *dcontext, coarse_freeze_info_t *freeze_info,
cache_pc stub, bool trace_head, bool replace_outgoing);
void
transfer_coarse_stub_fix_trace_head(dcontext_t *dcontext,
coarse_freeze_info_t *freeze_info,
cache_pc stub);
void
transfer_coarse_fragment(dcontext_t *dcontext, coarse_freeze_info_t *freeze_info,
cache_pc body);
coarse_info_t *
coarse_unit_merge(dcontext_t *dcontext, coarse_info_t *info1, coarse_info_t *info2,
bool in_place);
/***************************************************************************
* PERSISTENT CODE CACHE
*/
/* For storing frozen caches on disk */
enum {
PERSISTENT_CACHE_MAGIC = 0x244f4952, /* RIO$ */
PERSISTENT_CACHE_VERSION = 10,
};
/* Global flags we need to process if present in a persisted cache */
enum {
/* Identify underlying architecture */
PERSCACHE_X86_32 = 0x00000001,
PERSCACHE_X86_64 = 0x00000002,
/* FIXME: should we add cache line info? Currently we have no
* -pad_jmps for coarse bbs, coarse bbs are aligned to 1, our only
* hotpatched jmps are in stubs which are 16-byte-aligned and 15 bytes
* long, and we have -tls_align 1, so we have no cache line
* dependences.
*/
PERSCACHE_SEEN_BORLAND_SEH = 0x00000004,
/* Does cache contain elided ubrs? */
PERSCACHE_ELIDED_UBR = 0x00000008,
/* Does cache contain return-after-call or RCT entries? */
PERSCACHE_SUPPORT_RAC = 0x00000010,
PERSCACHE_SUPPORT_RCT = 0x00000020,
/* Does cache contain persisted RCT for entire module? */
PERSCACHE_ENTIRE_MODULE_RCT = 0x00000040,
/* Does cache support trace building? */
PERSCACHE_SUPPORT_TRACES = 0x00000080,
/* Does cache support separately mapping the writable portion? */
PERSCACHE_MAP_RW_SEPARATE = 0x00000100,
/* Case 9799: local exemption options are part of option string */
PERSCACHE_EXEMPTION_OPTIONS = 0x00000200,
/* Used only in coarse_info_t, not in coarse_persisted_info_t.
* We load and use persisted RCT tables prior to full code consistency checks;
* plus, we may continue using RCT tables after code consistency checks fail.
* Thus, the code being valid is separate from the pcache file being loaded.
* Xref case 10601.
*/
PERSCACHE_CODE_INVALID = 0x00000400,
};
/* Consistency and security checking options */
enum {
/* Checks on the app module */
PERSCACHE_MODULE_MD5_SHORT = 0x00000001, /* header + footer */
PERSCACHE_MODULE_MD5_COMPLETE = 0x00000002, /* entire code region */
/* Checks on our own generated file */
PERSCACHE_GENFILE_MD5_SHORT = 0x00000004, /* header */
PERSCACHE_GENFILE_MD5_COMPLETE = 0x00000008, /* entire file */
/* When to calculate gen-side module md5's
* In 4.4 this will be stored at 1st execution, not load time (case 10601)
*/
PERSCACHE_MODULE_MD5_AT_LOAD = 0x00000010, /* else, at persist time */
};
/* FIXME: share with hotp_module_sig_t in hotpatch.c
* Xref case 9543 on combining all these module structs.
* N.B.: the precise layout of the fields here is relied upon
* in persist_modinfo_cmp()
*/
typedef struct _persisted_module_info_t {
app_pc base; /* base of module at persist time */
uint checksum;
uint timestamp;
/* Ordinarily we'd use size_t for image_size and code_size, but we
* want the same header size for both 32 and 64 bit
*/
uint64 image_size;
uint64 code_size; /* sum of sizes of executable sections in module */
uint64 file_version;
/* FIXME case 10087: move to module list and share w/ module-level
* process control, aslr?
*/
/* FIXME: granularity: may want to support loading small pieces of the
* cache+stubs at a time, to reduce cache capacity and consistency checks
* and RAC security loosening. Should then have separate md5's.
*/
module_digest_t module_md5;
} persisted_module_info_t;
/* We put an MD5 of our generated file at the end, to make it easier
* to exclude itself from the calc and to provide assurance the end
* of the file was written.
*/
typedef struct _persisted_footer_t {
/* Self-consistency: MD5 of the header or of the whole file.
* We store them separately, even though file is superset, to allow
* checking just the header even if we generated the whole.
*/
module_digest_t self_md5; /* should we take module out of type name? */
uint magic;
} persisted_footer_t;
/* The layout of a frozen coarse unit for persisting to disk.
* For the htables, cache, and stubs, we store the raw data here and when
* reading back in we generate separate header structs for each.
*
* The layout is split into two pieces: the header and the data. For
* each section of data (stubs, cache, htable) we store a length in
* the header. The lengths are stored in reverse order, allowing for
* adding new sections (to the read-only-not-executable side at least)
* that are ignored by older versions of the code.
*
* FIXME: we have our own format here which we keep page-aligned on disk and set
* memory privileges properly ourselves upon loading in. We could instead use
* an image format (PE/ELF): with PE the kernel will set up the privileges for
* us, though with ELF it's the user-mode loader that does that. Our own format
* lets us map sub-views of the file, though currently only the small header
* data is not needed as a map. An image format will make it easier to use
* tools like debuggers with our caches. It also has an impact on sharing and
* copy-on-write. But, the kernel may make assumptions about images that don't
* apply to our files.
*/
typedef struct _coarse_persisted_info_t {
/* Read-only **************************************/
/* For verification */
uint magic;
uint version;
/* The lengths of the two divisions in the file.
* We limit entire file to 2GB on x64, but use size_t as
* the natural size for all our length computations.
*/
size_t header_len;
size_t data_len;
/* Unit-wide flags for state such as whether we've seen Borland SEH.
* Uses the PERSCACHE_ flags.
*/
uint flags;
/* For diagnostics */
uint build_number;
/* Consistency with original file */
persisted_module_info_t modinfo;
/* the address range that this cache covers, offset from module_base */
size_t start_offs;
size_t end_offs;
/* We require a match here; alternative is to put all uses in relocs */
uint tls_offs_base; /* could be ushort */
/* Now we store the lengths of each data section, in reverse
* order, to allow for expansion */
/* +rw data sections */
size_t instrument_rw_len;
/* +rwx data sections */
size_t stubs_len;
size_t ibl_jmp_prefix_len;
size_t ibl_call_prefix_len;
size_t ibl_ret_prefix_len;
size_t trace_head_return_prefix_len;
size_t fcache_return_prefix_len;
/* +rx data sections */
size_t cache_len;
size_t post_cache_pad_len; /* included in cache_len, this lets us know the
* end of actual instrs in the cache */
size_t pad_len; /* padding to get +rx onto new page */
size_t instrument_rx_len;
size_t view_pad_len; /* padding to get cache|stubs on map boundary */
/* +r data sections */
size_t stub_htable_len;
size_t cache_htable_len;
size_t rct_htable_len;
size_t rac_htable_len;
size_t reloc_len;
#ifdef HOT_PATCHING_INTERFACE
size_t hotp_patch_list_len; /* in bytes, like all the other *_len fields */
#endif
size_t instrument_ro_len;
/* Case 9799: pcache-affecting options that differ from default values */
size_t option_string_len;
/* Add length of new +r data section here (header grows downward)
* header_len indicates the start of the data section
*/
/* Data sections ****************************************/
/* option_string is the 1st data section; it may be padded to a 4-byte alignment */
/* Add new data section here (data grows upward across versions) */
/* Client data */
#ifdef HOT_PATCHING_INTERFACE
/* Hotp patch points matched at persist time to avoid flushing if
* the same vulns are active in the current run (case 9969).
*/
#endif
/* Relocations
* FIXME case 9581 NYI: other than app code relocs, all
* we add w/ coarse bbs are "call->push immed" manglings. Once have traces,
* also stay-on-trace cmp. No off-fragment jmps are currently allowed
* except for fcache/trace-head return and ibl, which are indirected.
* For app relocs we can either store in our own format at freeze time
* or do all processing at load time.
* FIXME: our non-entry pclookup is slow -- how slow will applying relocs be?
* FIXME case 9649: We could make our own call->push manglings
* PIC using pc-relative addressing on x86-64.
*/
/* RAC and RCT tables -- RCT only for Borland SEH
* FIXME case 8648: instead keep as flag hidden in msb of main htable?
* won't work for persisting the entire RCT tables, but will for Borland
* and RAC where all targets are present in cache.
* FIXME case 9777: we're loosening security by allowing ret to target any
* after-call executed in any prior run of this app or whatever
* app is producing, instead of just this run.
*/
/* Hashtables
* Up to the table what to persist: current plan has the struct and the table
* here, and on load we make a copy of the struct so that the lock can be
* writable.
*/
/* Padding to get +x section on new page */
/* Client +x gencode */
/****** Begin offset-sensitive sequence */
/* We have one single contiguous piece of memory containing our cache and
* stubs which must be kept together, as they contain relative jmps to each
* other, including to the prefixes at the top of the stubs.
*
* --------------------------------------
* executable below here
*
* code cache
*
* read-only above here
* --------------------------------------
* writable below here
* FIXME case 9650: we can make the prefixes read-only after
* loading if we put them on their own page
*
* fcache_return_prefix
* trace_head_return_prefix
* ibl_ret_prefix
* ibl_call_prefix
* ibl_jmp_prefix
* stubs
*
****** End offset-sensitive sequence */
/* Client writable data */
/* persisted_footer_t here */
/* We place a guard page here at load time if we fill out the allocation
* region. We don't need one in front since we have read-only pages there.
*/
} coarse_persisted_info_t;
bool
coarse_unit_persist(dcontext_t *dcontext, coarse_info_t *info);
coarse_info_t *
coarse_unit_load(dcontext_t *dcontext, app_pc start, app_pc end,
bool for_execution);
bool
exists_coarse_ibl_pending_table(dcontext_t *dcontext,
coarse_info_t *info, ibl_branch_type_t branch_type);
/* Checks for enough space on the volume where persisted caches are stored */
bool
coarse_unit_check_persist_space(file_t fd_in/*OPTIONAL*/, size_t size_needed);
/* If pc is in a module, marks that module as exempted (case 9799) */
void
mark_module_exempted(app_pc pc);
#endif /* _PERSCACHE_H_ */