* Copyright (c) 2012-2019 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.
*/
* perscache.h - persistent code cache exports
*/
#ifndef _PERSCACHE_H_
#define _PERSCACHE_H_ 1
#include "module_shared.h"
* COARSE-GRAIN UNITS
*/
* 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 d_r_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;
* we only need when persisting.
*/
bool has_persist_info : 1;
* Non-in-use units inherit this from their sources, but do not
* change the status on deletion.
*/
bool primary_for_module : 1;
bool stubs_readonly : 1;
#ifdef DEBUG
* We only use this flag to get around lock ordering issues (case 11064).
*/
bool is_local : 1;
#endif
void *cache;
* FIXME case 8628: split these into a body table and a stub table for
* non-frozen units.
*/
void *htable;
void *th_htable;
void *pclookup_last_htable;
void *stubs;
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;
* Since htable entries are offsets we need to expose the cache and stub pcs
*/
cache_pc cache_start_pc;
cache_pc cache_end_pc;
cache_pc stubs_start_pc;
cache_pc stubs_end_pc;
* 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;
void *pclookup_htable;
uint flags;
cache_pc mmap_pc;
* 1) [mmap_pc,mmap_pc+mmap_ro_size)
* 2) [mmap_pc+mmap_ro_size,mmap_pc+mmap_size)
*/
size_t mmap_ro_size;
file_t fd;
* 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;
* entries. We only do once, storing here to enforce that.
*/
uint ibl_pending_used;
#endif
#ifdef HOT_PATCHING_INTERFACE
* 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;
#endif
uint stubs_write_count;
* 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;
* #trace heads, etc.
*/
* Fields below this point are preserved across a coarse_unit_reset_free(),
* while those above are cleared.
*/
* the incoming list */
mutex_t lock;
* lock while changing target linked unit locks (case 9809) */
mutex_t incoming_lock;
* source modules
*/
app_pc base_pc;
app_pc end_pc;
#ifdef DEBUG
const char *module;
#endif
* so we're comparing the in-memory image at a consistent point.
*/
module_digest_t module_md5;
app_pc persist_base;
ssize_t mod_shift;
};
#if defined(X86) && defined(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);
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
*/
typedef struct _pending_freeze_t {
bool entrance_stub;
bool trace_head;
app_pc tag;
cache_pc cur_pc;
cache_pc link_cti_opnd;
bool elide_ubr;
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
size_t app_code_size;
uint num_cbr;
uint num_jmp;
uint num_call;
uint num_indbr;
uint num_elisions;
uint added_fallthrough;
uint added_indbr_mangle;
uint added_indbr_stub;
uint added_jecxz_mangle;
#endif
};
void
perscache_init(void);
void
perscache_fast_exit(void);
void
perscache_slow_exit(void);
bool
perscache_dirname(char *directory , 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
*/
enum {
PERSISTENT_CACHE_MAGIC = 0x244f4952,
PERSISTENT_CACHE_VERSION = 10,
};
enum {
PERSCACHE_X86_32 = 0x00000001,
PERSCACHE_X86_64 = 0x00000002,
* -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,
PERSCACHE_ELIDED_UBR = 0x00000008,
PERSCACHE_SUPPORT_RAC = 0x00000010,
PERSCACHE_SUPPORT_RCT = 0x00000020,
PERSCACHE_ENTIRE_MODULE_RCT = 0x00000040,
PERSCACHE_SUPPORT_TRACES = 0x00000080,
PERSCACHE_MAP_RW_SEPARATE = 0x00000100,
PERSCACHE_EXEMPTION_OPTIONS = 0x00000200,
* 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,
};
enum {
PERSCACHE_MODULE_MD5_SHORT = 0x00000001,
PERSCACHE_MODULE_MD5_COMPLETE = 0x00000002,
PERSCACHE_GENFILE_MD5_SHORT = 0x00000004,
PERSCACHE_GENFILE_MD5_COMPLETE = 0x00000008,
* In 4.4 this will be stored at 1st execution, not load time (case 10601)
*/
PERSCACHE_MODULE_MD5_AT_LOAD = 0x00000010,
};
* 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;
uint checksum;
uint timestamp;
* want the same header size for both 32 and 64 bit
*/
uint64 image_size;
uint64 code_size;
uint64 file_version;
* process control, aslr?
*/
* 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;
* to exclude itself from the calc and to provide assurance the end
* of the file was written.
*/
typedef struct _persisted_footer_t {
* 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;
uint magic;
} persisted_footer_t;
* 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 {
uint magic;
uint version;
* 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;
* Uses the PERSCACHE_ flags.
*/
uint flags;
uint build_number;
persisted_module_info_t modinfo;
size_t start_offs;
size_t end_offs;
uint tls_offs_base;
* order, to allow for expansion */
size_t instrument_rw_len;
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;
size_t cache_len;
size_t post_cache_pad_len;
* end of actual instrs in the cache */
size_t pad_len;
size_t instrument_rx_len;
size_t view_pad_len;
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;
#endif
size_t instrument_ro_len;
size_t option_string_len;
* header_len indicates the start of the data section
*/
#ifdef HOT_PATCHING_INTERFACE
* the same vulns are active in the current run (case 9969).
*/
#endif
* 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.
*/
* 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.
*/
* 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.
*/
* 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 */
* 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);
bool
coarse_unit_check_persist_space(file_t fd_in , size_t size_needed);
void
mark_module_exempted(app_pc pc);
#endif