diff --git a/api/docs/intro.dox b/api/docs/intro.dox index 4ddf11dea3f..67710fb6a1e 100644 --- a/api/docs/intro.dox +++ b/api/docs/intro.dox @@ -1,5 +1,5 @@ /* ****************************************************************************** - * Copyright (c) 2010-2019 Google, Inc. All rights reserved. + * Copyright (c) 2010-2020 Google, Inc. All rights reserved. * Copyright (c) 2011 Massachusetts Institute of Technology All rights reserved. * Copyright (c) 2007-2010 VMware, Inc. All rights reserved. * ******************************************************************************/ @@ -2078,19 +2078,6 @@ When using DynamoRIO's CMake support, use the configure_DynamoRIO_decoder() function to set up include directories and to link with \p drdecode. The next section describes how to link with the DynamoRIO shared library. -\subsection sec_relativize Re-Relativization of Jumps and Calls - -When encoding a relative jump or call to a different location than it was -decoded from while in standalone mode, a re-encode must be forced in order -to work around an issue where DynamoRIO does not re-relativize the target: - -\code -instr_set_raw_bits_valid(instr, false) -\endcode - -When not in standalone mode, all branches are mangled and thus this is -never an issue. This should be fixed in a future release. - \section sec_standalone_shared DynamoRIO Shared Library Issues diff --git a/api/docs/release.dox b/api/docs/release.dox index 6e8a1e58507..ec5c9140d80 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -175,6 +175,12 @@ compatibility changes: - Changed the #PFX format string specifier to use %p rather than %x internally. - DR no longer forwards _snprintf, _snwprintf, _vsnprintf, sprintf, or sscanf to ntdll. Clients should use the dr_-prefixed versions of these functions. + - PC-relative control transfer instructions are now auto-re-relativized by the + general decoder and encoder. This affects clients and standalone tools that use + decode_from_copy() or instr_encode_to_copy() or instrlist_encode_to_copy(). + Previously, re-relativization for instruction references only happened when an + instruction was re-encoded. This auto-PC-relativization can be avoided by calling + instr_set_rip_rel_valid() and setting the validity of the PC-relative data to false. Further non-compatibility-affecting changes include: @@ -223,6 +229,8 @@ Further non-compatibility-affecting changes include: - Added drmgr_register_low_on_memory_event(), drmgr_unregister_low_on_memory_event() and their variants so that drmgr can support low-on-memory events. - Added drmgr_is_first_nonlabel_instr() and instrlist_first_nonlabel(). + - Added decode_sizeof_ex() and instr_get_rel_data_or_instr_target() handling + relative instruction references. **************************************************
diff --git a/core/arch/aarch64/codec.c b/core/arch/aarch64/codec.c index 6abf17f4e63..4318ec55372 100644 --- a/core/arch/aarch64/codec.c +++ b/core/arch/aarch64/codec.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2017 Google, Inc. All rights reserved. + * Copyright (c) 2017-2020 Google, Inc. All rights reserved. * Copyright (c) 2016 ARM Limited. All rights reserved. * **********************************************************/ @@ -3161,6 +3161,7 @@ decode_common(dcontext_t *dcontext, byte *pc, byte *orig_pc, instr_t *instr) if (orig_pc != pc) { /* We do not want to copy when encoding and condone an invalid * relative target. + * TODO i#4016: Add re-relativization support without having to re-encode. */ instr_set_raw_bits_valid(instr, false); instr_set_translation(instr, orig_pc); diff --git a/core/arch/aarch64/encode.c b/core/arch/aarch64/encode.c index f706ba5cc54..adeed88be98 100644 --- a/core/arch/aarch64/encode.c +++ b/core/arch/aarch64/encode.c @@ -1,4 +1,5 @@ /* ********************************************************** + * Copyright (c) 2020 Google, Inc. All rights reserved. * Copyright (c) 2016 ARM Limited. All rights reserved. * **********************************************************/ @@ -193,7 +194,7 @@ byte * copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst_pc, byte *final_pc) { - /* FIXME i#1569: re-relativizing is NYI */ + /* TODO i#4016: re-relativizing is NYI */ /* OP_ldstex is always relocatable. */ ASSERT(instr_raw_bits_valid(instr) || instr_get_opcode(instr) == OP_ldstex); memcpy(dst_pc, instr->bytes, instr->length); diff --git a/core/arch/arm/decode.c b/core/arch/arm/decode.c index bdb0f4c566d..1dcebec88ca 100644 --- a/core/arch/arm/decode.c +++ b/core/arch/arm/decode.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2014-2019 Google, Inc. All rights reserved. + * Copyright (c) 2014-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -2500,6 +2500,7 @@ decode_common(dcontext_t *dcontext, byte *pc, byte *orig_pc, instr_t *instr) if (orig_pc != pc) { /* We do not want to copy when encoding and condone an invalid * relative target + * TODO i#4016: Add re-relativization support without having to re-encode. */ instr_set_raw_bits_valid(instr, false); instr_set_translation(instr, orig_pc); diff --git a/core/arch/arm/encode.c b/core/arch/arm/encode.c index 039e67e2516..a4a31610c68 100644 --- a/core/arch/arm/encode.c +++ b/core/arch/arm/encode.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2014-2015 Google, Inc. All rights reserved. + * Copyright (c) 2014-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -3099,7 +3099,7 @@ byte * copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst_pc, byte *final_pc) { - /* FIXME i#1551: re-relativizing is NYI */ + /* TODO i#4016: re-relativizing is NYI */ ASSERT(instr_raw_bits_valid(instr)); memcpy(dst_pc, instr->bytes, instr->length); return dst_pc + instr->length; diff --git a/core/arch/decode_fast.h b/core/arch/decode_fast.h index de8b64eeb90..319e4f01064 100644 --- a/core/arch/decode_fast.h +++ b/core/arch/decode_fast.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2015-2019 Google, Inc. All rights reserved. + * Copyright (c) 2015-2020 Google, Inc. All rights reserved. * Copyright (c) 2001-2009 VMware, Inc. All rights reserved. * **********************************************************/ @@ -46,15 +46,37 @@ DR_API * Decodes only enough of the instruction at address \p pc to determine its size. * Returns that size. * If \p num_prefixes is non-NULL, returns the number of prefix bytes. - * If \p rip_rel_pos is non-NULL, returns the offset into the instruction - * of a rip-relative addressing displacement (for data only: ignores - * control-transfer relative addressing), or 0 if none. + * + * On x86, if \p rip_rel_pos is non-NULL, returns the offset into the instruction of a + * rip-relative addressing displacement (for data only: ignores control-transfer + * relative addressing; use decode_sizeof_ex() for that), or 0 if none. + * The \p rip_rel_pos parameter is only implemented for x86, where the displacement + * is always 4 bytes in size. + * * May return 0 size for certain invalid instructions. */ int decode_sizeof(dcontext_t *dcontext, byte *pc, int *num_prefixes _IF_X86_64(uint *rip_rel_pos)); +#ifdef X86 +DR_API +/** + * Decodes only enough of the instruction at address \p pc to determine its size. + * Returns that size. + * If \p num_prefixes is non-NULL, returns the number of prefix bytes. + * + * On x86, if \p rip_rel_pos is non-NULL, returns the offset into the instruction of a + * rip-relative addressing displacement for data or control-transfer relative + * addressing, or 0 if none. This is only implemented for x86, where the displacement + * is always 4 bytes for data but can be 1 byte for control. + * + * May return 0 size for certain invalid instructions. + */ +int +decode_sizeof_ex(dcontext_t *dcontext, byte *pc, int *num_prefixes, uint *rip_rel_pos); +#endif + DR_API /** * Decodes only enough of the instruction at address \p pc to determine its size. @@ -99,7 +121,7 @@ DR_UNS_EXCEPT_TESTS_API * Does NOT fill in any other prefix flags unless this is a cti instr * and the flags affect the instr. * - * For x64, calls instr_set_rip_rel_pos(). Thus, if the raw bytes are + * For x86, calls instr_set_rip_rel_pos(). Thus, if the raw bytes are * not modified prior to encode time, any rip-relative offset will be * automatically re-relativized (though encoding will fail if the new * encode location cannot reach the original target). diff --git a/core/arch/instr.h b/core/arch/instr.h index 38b4f30c354..1d6e9aafb9c 100644 --- a/core/arch/instr.h +++ b/core/arch/instr.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2019 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -460,7 +460,7 @@ struct _instr_t { uint opcode; -# ifdef X86_64 +# ifdef X86 /* PR 251479: offset into instr's raw bytes of rip-relative 4-byte displacement */ byte rip_rel_pos; # endif @@ -1870,12 +1870,24 @@ bool instr_is_xsave(instr_t *instr); #endif +DR_API +/** + * If any of \p instr's operands is a rip-relative data or instruction + * memory reference, returns the address that reference targets. Else + * returns false. For instruction references, only PC operands are + * considered: not instruction pointer operands. + * + * \note Currently this is only implemented for x86. + */ +bool +instr_get_rel_data_or_instr_target(instr_t *instr, /*OUT*/ app_pc *target); + /* DR_API EXPORT BEGIN */ #if defined(X64) || defined(ARM) /* DR_API EXPORT END */ DR_API /** - * Returns true iff any of \p instr's operands is a rip-relative memory reference. + * Returns true iff any of \p instr's operands is a rip-relative data memory reference. * * \note For 64-bit DR builds only. */ @@ -1884,7 +1896,7 @@ instr_has_rel_addr_reference(instr_t *instr); DR_API /** - * If any of \p instr's operands is a rip-relative memory reference, returns the + * If any of \p instr's operands is a rip-relative data memory reference, returns the * address that reference targets. Else returns false. * * \note For 64-bit DR builds only. @@ -1894,7 +1906,7 @@ instr_get_rel_addr_target(instr_t *instr, /*OUT*/ app_pc *target); DR_API /** - * If any of \p instr's destination operands is a rip-relative memory + * If any of \p instr's destination operands is a rip-relative data memory * reference, returns the operand position. If there is no such * destination operand, returns -1. * @@ -1917,7 +1929,7 @@ instr_get_rel_addr_src_idx(instr_t *instr); #endif /* X64 || ARM */ /* DR_API EXPORT END */ -#ifdef X86_64 +#ifdef X86 /* We're not exposing the low-level rip_rel_pos routines directly to clients, * who should only use this level 1-3 feature via decode_cti + encode. */ @@ -1949,7 +1961,7 @@ instr_get_rip_rel_pos(instr_t *instr); */ void instr_set_rip_rel_pos(instr_t *instr, uint pos); -#endif /* X64 */ +#endif /* X86 */ /* not exported: for PR 267260 */ bool @@ -3007,7 +3019,7 @@ instr_create_save_to_reg(dcontext_t *dcontext, reg_id_t reg1, reg_id_t reg2); instr_t * instr_create_restore_from_reg(dcontext_t *dcontext, reg_id_t reg1, reg_id_t reg2); -#ifdef X64 +#ifdef X86_64 byte * instr_raw_is_rip_rel_lea(byte *pc, byte *read_end); #endif diff --git a/core/arch/instr_shared.c b/core/arch/instr_shared.c index 018b153c6d2..3774b8ebd1c 100644 --- a/core/arch/instr_shared.c +++ b/core/arch/instr_shared.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2019 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -257,7 +257,7 @@ instr_reuse(dcontext_t *dcontext, instr_t *instr) bool alloc = false; bool mangle = instr_is_app(instr); dr_isa_mode_t isa_mode = instr_get_isa_mode(instr); -#ifdef X86_64 +#ifdef X86 uint rip_rel_pos = instr_rip_rel_valid(instr) ? instr->rip_rel_pos : 0; #endif instr_t *next = instr->next; @@ -288,7 +288,7 @@ instr_reuse(dcontext_t *dcontext, instr_t *instr) } /* preserve across the up-decode */ instr_set_isa_mode(instr, isa_mode); -#ifdef X86_64 +#ifdef X86 if (rip_rel_pos > 0) instr_set_rip_rel_pos(instr, rip_rel_pos); #endif @@ -365,7 +365,7 @@ private_instr_encode(dcontext_t *dcontext, instr_t *instr, bool always_cache) ((valid_to_cache && instr_is_app(instr)) || always_cache /*caller will use then invalidate*/)) { bool valid = instr_operands_valid(instr); -#ifdef X86_64 +#ifdef X86 /* we can't call instr_rip_rel_valid() b/c the raw bytes are not yet * set up: we rely on instr_encode() setting instr->rip_rel_pos and * the valid flag, even though raw bytes weren't there at the time. @@ -384,7 +384,7 @@ private_instr_encode(dcontext_t *dcontext, instr_t *instr, bool always_cache) */ tmp = instr->bytes; instr->bytes = buf; -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, rip_rel_valid); #endif copy_and_re_relativize_raw_instr(dcontext, instr, tmp, tmp); @@ -1031,7 +1031,7 @@ instr_set_raw_bits(instr_t *instr, byte *addr, uint length) instr->flags |= INSTR_RAW_BITS_VALID; instr->bytes = addr; instr->length = length; -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); /* relies on original raw bits */ #endif } @@ -1045,7 +1045,7 @@ instr_shift_raw_bits(instr_t *instr, ssize_t offs) { if ((instr->flags & INSTR_RAW_BITS_VALID) != 0) instr->bytes += offs; -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); /* relies on original raw bits */ #endif } @@ -1065,7 +1065,7 @@ instr_set_raw_bits_valid(instr_t *instr, bool valid) * addresses for exception/signal handlers * Also do not de-allocate allocated bits */ -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); #endif } @@ -1113,7 +1113,7 @@ instr_allocate_raw_bits(dcontext_t *dcontext, instr_t *instr, uint num_bytes) instr->flags |= INSTR_RAW_BITS_ALLOCATED; instr->flags &= ~INSTR_OPERANDS_VALID; instr->flags &= ~INSTR_EFLAGS_VALID; -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); /* relies on original raw bits */ #endif } @@ -1208,7 +1208,7 @@ instr_set_raw_byte(instr_t *instr, uint pos, byte val) CLIENT_ASSERT(pos >= 0 && pos < instr->length && instr->bytes != NULL, "instr_set_raw_byte: ordinal invalid, or no raw bits"); instr->bytes[pos] = (byte)val; -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); /* relies on original raw bits */ #endif } @@ -1225,7 +1225,7 @@ instr_set_raw_bytes(instr_t *instr, byte *start, uint num_bytes) CLIENT_ASSERT(num_bytes <= instr->length && instr->bytes != NULL, "instr_set_raw_bytes: ordinal invalid, or no raw bits"); memcpy(instr->bytes, start, num_bytes); -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); /* relies on original raw bits */ #endif } @@ -1242,7 +1242,7 @@ instr_set_raw_word(instr_t *instr, uint pos, uint word) CLIENT_ASSERT(pos >= 0 && pos + 3 < instr->length && instr->bytes != NULL, "instr_set_raw_word: ordinal invalid, or no raw bits"); *((uint *)(instr->bytes + pos)) = word; -#ifdef X86_64 +#ifdef X86 instr_set_rip_rel_valid(instr, false); /* relies on original raw bits */ #endif } @@ -1525,7 +1525,7 @@ instr_decode_opcode(dcontext_t *dcontext, instr_t *instr) if (!instr_opcode_valid(instr)) { byte *next_pc; DEBUG_EXT_DECLARE(int old_len = instr->length;) -#ifdef X86_64 +#ifdef X86 bool rip_rel_valid = instr_rip_rel_valid(instr); #endif /* decode_opcode() will use the dcontext mode, but we want the instr mode */ @@ -1536,7 +1536,7 @@ instr_decode_opcode(dcontext_t *dcontext, instr_t *instr) instr_reuse(dcontext, instr); next_pc = decode_opcode(dcontext, instr->bytes, instr); dr_set_isa_mode(dcontext, old_mode, NULL); -#ifdef X86_64 +#ifdef X86 /* decode_opcode sets raw bits which invalidates rip_rel, but * it should still be valid on an up-decode of the opcode */ if (rip_rel_valid) @@ -1558,7 +1558,7 @@ instr_decode(dcontext_t *dcontext, instr_t *instr) if (!instr_operands_valid(instr)) { byte *next_pc; DEBUG_EXT_DECLARE(int old_len = instr->length;) -#ifdef X86_64 +#ifdef X86 bool rip_rel_valid = instr_rip_rel_valid(instr); #endif /* decode() will use the current dcontext mode, but we want the instr mode */ @@ -1572,7 +1572,7 @@ instr_decode(dcontext_t *dcontext, instr_t *instr) instr_set_translation(instr, instr_get_raw_bits(instr)); #endif dr_set_isa_mode(dcontext, old_mode, NULL); -#ifdef X86_64 +#ifdef X86 /* decode sets raw bits which invalidates rip_rel, but * it should still be valid on an up-decode */ if (rip_rel_valid) @@ -2037,7 +2037,6 @@ instr_is_xsave(instr_t *instr) } #endif /* X86 */ -#if defined(X64) || defined(ARM) /* PR 251479: support general re-relativization. If INSTR_RIP_REL_VALID is set and * the raw bits are valid, instr->rip_rel_pos is assumed to hold the offset into the * instr of a 32-bit rip-relative displacement, which is used to re-relativize during @@ -2050,14 +2049,14 @@ instr_is_xsave(instr_t *instr) * raw bits: we can't rely just on the raw bits invalidation. * There can only be one rip-relative operand per instruction. */ -/* FIXME i#1551: for ARM we don't have a large displacement on every reference. +/* TODO i#4016: for AArchXX we don't have a large displacement on every reference. * Some have no disp at all, others have just 12 bits or smaller. * We need to come up with a strategy for handling encode-time re-relativization. * Xref copy_and_re_relativize_raw_instr(). * For now, we do use some of these routines, but none that use the rip_rel_pos. */ -# ifdef X86_64 +#ifdef X86 bool instr_rip_rel_valid(instr_t *instr) { @@ -2087,28 +2086,54 @@ instr_set_rip_rel_pos(instr_t *instr, uint pos) instr->rip_rel_pos = (byte)pos; instr_set_rip_rel_valid(instr, true); } -# endif /* X86_64 */ +#endif /* X86 */ bool -instr_get_rel_addr_target(instr_t *instr, app_pc *target) +instr_get_rel_target(instr_t *instr, /*OUT*/ app_pc *target, bool data_only) { - int i; - opnd_t curop; if (!instr_valid(instr)) return false; -# ifdef X86_64 + + /* For PC operands we have to look at the high-level *before* rip_rel_pos, to + * support decode_from_copy(). As documented, we ignore instr_t targets. + */ + if (!data_only && instr_operands_valid(instr) && instr_num_srcs(instr) > 0 && + opnd_is_pc(instr_get_src(instr, 0))) { + *target = opnd_get_pc(instr_get_src(instr, 0)); + return true; + } + +#ifdef X86 /* PR 251479: we support rip-rel info in level 1 instrs */ if (instr_rip_rel_valid(instr)) { - if (instr_get_rip_rel_pos(instr) > 0) { + int rip_rel_pos = instr_get_rip_rel_pos(instr); + if (rip_rel_pos > 0) { + if (data_only && + /* Invariant: no instruction has 2 rip-rel immeds. */ + ((instr_is_cti(instr) && !instr_is_mbr(instr)) || + instr_get_opcode(instr) == OP_xbegin)) + return false; if (target != NULL) { - *target = instr->bytes + instr->length + - *((int *)(instr->bytes + instr_get_rip_rel_pos(instr))); + /* We only support non-4-byte rip-rel disps for 1-byte instr-final + * (jcc_short). + */ + if (rip_rel_pos + 1 == instr->length) { + *target = instr->bytes + instr->length + + *((char *)(instr->bytes + rip_rel_pos)); + } else { + ASSERT(rip_rel_pos + 4 <= instr->length); + *target = instr->bytes + instr->length + + *((int *)(instr->bytes + rip_rel_pos)); + } } return true; } else return false; } -# endif +#endif +#if defined(X64) || defined(ARM) + int i; + opnd_t curop; /* else go to level 3 operands */ for (i = 0; i < instr_num_dsts(instr); i++) { curop = instr_get_dst(instr, i); @@ -2156,9 +2181,23 @@ instr_get_rel_addr_target(instr_t *instr, app_pc *target) } }); } +#endif return false; } +bool +instr_get_rel_data_or_instr_target(instr_t *instr, /*OUT*/ app_pc *target) +{ + return instr_get_rel_target(instr, target, false /*all*/); +} + +#if defined(X64) || defined(ARM) +bool +instr_get_rel_addr_target(instr_t *instr, /*OUT*/ app_pc *target) +{ + return instr_get_rel_target(instr, target, true /*data-only*/); +} + bool instr_has_rel_addr_reference(instr_t *instr) { diff --git a/core/arch/mangle_shared.c b/core/arch/mangle_shared.c index 5c7d3ed14c8..ba136938201 100644 --- a/core/arch/mangle_shared.c +++ b/core/arch/mangle_shared.c @@ -1,5 +1,5 @@ /* ****************************************************************************** - * Copyright (c) 2010-2019 Google, Inc. All rights reserved. + * Copyright (c) 2010-2020 Google, Inc. All rights reserved. * Copyright (c) 2010 Massachusetts Institute of Technology All rights reserved. * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. * ******************************************************************************/ @@ -1101,6 +1101,12 @@ mangle_rseq_insert_native_sequence(dcontext_t *dcontext, instrlist_t *ilist, opnd_is_pc(instr_get_target(copy))) { app_pc tgt = opnd_get_pc(instr_get_target(copy)); if (tgt >= start && tgt < end) { + /* We do not want to use the absolute PC and re-relativize: we want + * to use the same relative offset. (An alternative would be to + * convert the PC operand to an instr_t pointer but that would take + * extra passes.) + */ + instr_set_rip_rel_valid(copy, false); PRE(ilist, insert_at, copy); continue; } diff --git a/core/arch/x86/decode.c b/core/arch/x86/decode.c index e7d5c9f2da2..f3bb565475f 100644 --- a/core/arch/x86/decode.c +++ b/core/arch/x86/decode.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2019 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -500,6 +500,7 @@ read_operand(byte *pc, decode_info_t *di, byte optype, opnd_size_t opsize) } case TYPE_J: { byte *end_pc; + di->disp_abs = pc; /* For re-relativization support. */ pc = read_immed(pc, di, opsize, &val); if (di->orig_pc != di->start_pc) { CLIENT_ASSERT(di->start_pc != NULL, @@ -608,7 +609,10 @@ read_modrm(byte *pc, decode_info_t *di) /* 4-byte disp */ di->has_disp = true; di->disp = *((int *)pc); - IF_X64(di->disp_abs = pc); /* used to set instr->rip_rel_pos */ +#ifdef X64 + if (X64_MODE(di) && di->mod == 0 && di->rm == 5) + di->disp_abs = pc; /* Used to set instr->rip_rel_pos. */ +#endif pc += 4; } else if (di->mod == 1) { /* 1-byte disp */ @@ -955,6 +959,7 @@ read_instruction(byte *pc, byte *orig_pc, const instr_info_t **ret_info, di->repne_prefix = false; di->vex_encoded = false; di->evex_encoded = false; + di->disp_abs = 0; /* FIXME: set data and addr sizes to current mode * for now I assume always 32-bit mode (or 64 for X64_MODE(di))! */ @@ -2444,12 +2449,10 @@ decode_opcode(dcontext_t *dcontext, byte *pc, instr_t *instr) const instr_info_t *info; decode_info_t di; int sz; -#ifdef X64 /* PR 251479: we need to know about all rip-relative addresses. * Since change/setting raw bits invalidates, we must set this * on every return. */ uint rip_rel_pos; -#endif IF_X64(di.x86_mode = instr_get_x86_mode(instr)); /* when pass true to read_instruction it doesn't decode immeds, * so have to call decode_next_pc, but that ends up being faster @@ -2458,7 +2461,7 @@ decode_opcode(dcontext_t *dcontext, byte *pc, instr_t *instr) read_instruction(pc, pc, &info, &di, true /* just opcode */ _IF_DEBUG(!TEST(INSTR_IGNORE_INVALID, instr->flags))); - sz = decode_sizeof(dcontext, pc, NULL _IF_X64(&rip_rel_pos)); + sz = decode_sizeof_ex(dcontext, pc, NULL, &rip_rel_pos); IF_X64(instr_set_x86_mode(instr, get_x86_mode(dcontext))); instr_set_opcode(instr, info->type); /* read_instruction sets opcode to OP_INVALID for illegal instr. @@ -2477,7 +2480,7 @@ decode_opcode(dcontext_t *dcontext, byte *pc, instr_t *instr) /* raw bits are valid though and crucial for encoding */ instr_set_raw_bits(instr, pc, sz); /* must set rip_rel_pos after setting raw bits */ - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return pc + sz; } @@ -2654,27 +2657,18 @@ decode_common(dcontext_t *dcontext, byte *pc, byte *orig_pc, instr_t *instr) * in other situations does not result in #UD so we ignore. */ - if (orig_pc != pc) { - /* We do not want to copy when encoding and condone an invalid - * relative target - */ - instr_set_raw_bits_valid(instr, false); - instr_set_translation(instr, orig_pc); - } else { - /* we set raw bits AFTER setting all srcs and dsts b/c setting - * a src or dst marks instr as having invalid raw bits - */ - IF_X64(ASSERT(CHECK_TRUNCATE_TYPE_uint(next_pc - pc))); - instr_set_raw_bits(instr, pc, (uint)(next_pc - pc)); -#ifdef X64 - if (X64_MODE(&di) && TEST(HAS_MODRM, info->flags) && di.mod == 0 && di.rm == 5) { - CLIENT_ASSERT(di.disp_abs > di.start_pc, "decode: internal rip-rel error"); - CLIENT_ASSERT(CHECK_TRUNCATE_TYPE_int(di.disp_abs - di.start_pc), - "decode: internal rip-rel error"); - /* must do this AFTER setting raw bits to avoid being invalidated */ - instr_set_rip_rel_pos(instr, (int)(di.disp_abs - di.start_pc)); - } -#endif + instr_set_translation(instr, orig_pc); + /* We set raw bits AFTER setting all srcs and dsts b/c setting + * a src or dst marks instr as having invalid raw bits. + */ + IF_X64(ASSERT(CHECK_TRUNCATE_TYPE_uint(next_pc - pc))); + instr_set_raw_bits(instr, pc, (uint)(next_pc - pc)); + if (di.disp_abs > di.start_pc) { + CLIENT_ASSERT(di.disp_abs > di.start_pc, "decode: internal rip-rel error"); + CLIENT_ASSERT(CHECK_TRUNCATE_TYPE_int(di.disp_abs - di.start_pc), + "decode: internal rip-rel error"); + /* We must do this AFTER setting raw bits to avoid being invalidated. */ + instr_set_rip_rel_pos(instr, (int)(di.disp_abs - di.start_pc)); } return next_pc; diff --git a/core/arch/x86/decode_fast.c b/core/arch/x86/decode_fast.c index 488f71792c8..ad131ced2a3 100644 --- a/core/arch/x86/decode_fast.c +++ b/core/arch/x86/decode_fast.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2014 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2001-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -225,16 +225,15 @@ static const char x64_adjustment[256] = { /* Prototypes for the functions that calculate the variable * part of the x86 instruction length. */ static int -sizeof_modrm(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)); +sizeof_modrm(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc); static int -sizeof_fp_op(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)); +sizeof_fp_op(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc); static int -sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)); +sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc); static int -sizeof_3byte_38(dcontext_t *dcontext, byte *pc, bool addr16, - bool vex _IF_X64(byte **rip_rel_pc)); +sizeof_3byte_38(dcontext_t *dcontext, byte *pc, bool addr16, bool vex, byte **rip_rel_pc); static int -sizeof_3byte_3a(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)); +sizeof_3byte_3a(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc); enum { VARLEN_NONE, @@ -243,36 +242,40 @@ enum { VARLEN_ESCAPE, /* 2-byte opcodes */ VARLEN_3BYTE_38_ESCAPE, /* 3-byte opcodes 0f 38 */ VARLEN_3BYTE_3A_ESCAPE, /* 3-byte opcodes 0f 3a */ + VARLEN_RIP_REL_1BYTE, /* Ends in a 1-byte rip-rel immediate. */ + VARLEN_RIP_REL_4BYTE, /* Ends in a 4-byte rip-rel immediate. */ }; /* Some macros to make the following table look better. */ #define m VARLEN_MODRM #define f VARLEN_FP_OP #define e VARLEN_ESCAPE +#define r1 VARLEN_RIP_REL_1BYTE +#define r4 VARLEN_RIP_REL_4BYTE /* Data table indicating what function to use to calculate the variable part of the x86 instruction. This table is indexed by the primary opcode. */ static const byte variable_length[256] = { - m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, e, /* 0 */ - m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, 0, /* 1 */ - m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, 0, /* 2 */ - m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, 0, /* 3 */ - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ - 0, 0, m, m, 0, 0, 0, 0, 0, m, 0, m, 0, 0, 0, 0, /* 6 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */ - - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 8 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B */ - - m, m, 0, 0, m, m, m, m, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ - m, m, m, m, 0, 0, 0, 0, f, f, f, f, f, f, f, f, /* D */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E */ - 0, 0, 0, 0, 0, 0, m, m, 0, 0, 0, 0, 0, 0, m, m /* F */ + m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, e, /* 0 */ + m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, 0, /* 1 */ + m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, 0, /* 2 */ + m, m, m, m, 0, 0, 0, 0, m, m, m, m, 0, 0, 0, 0, /* 3 */ + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ + 0, 0, m, m, 0, 0, 0, 0, 0, m, 0, m, 0, 0, 0, 0, /* 6 */ + r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, r1, /* 7 */ + + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 8 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B */ + + m, m, 0, 0, m, m, m, m, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ + m, m, m, m, 0, 0, 0, 0, f, f, f, f, f, f, f, f, /* D */ + r1, r1, r1, r1, 0, 0, 0, 0, r4, r4, 0, r1, 0, 0, 0, 0, /* E */ + 0, 0, 0, 0, 0, 0, m, m, 0, 0, 0, 0, 0, 0, m, m /* F */ }; /* eliminate the macros */ @@ -319,25 +322,25 @@ static const byte escape_fixed_length[256] = { the variable part of the escaped x86 instruction. This table is indexed by the 2nd opcode byte. */ static const byte escape_variable_length[256] = { - m, m, m, m, 0, 0, 0, 0, 0, 0, 0, 0, 0, m, 0, m, /* 0 */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 1 */ - m, m, m, m, 0, 0, 0, 0, m, m, m, m, m, m, m, m, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, e1, 0, e2, 0, 0, 0, 0, 0, /* 3 */ - - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 4 */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 5 */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 6 */ - m, m, m, m, m, m, m, 0, m, m, m, m, m, m, m, m, /* 7 */ - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 9 */ - 0, 0, 0, m, m, m, 0, 0, 0, 0, 0, m, m, m, m, m, /* A */ - m, m, m, m, m, m, m, m, m, 0, m, m, m, m, m, m, /* B */ - - m, m, m, m, m, m, m, m, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* D */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* E */ - m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, 0 /* F */ + m, m, m, m, 0, 0, 0, 0, 0, 0, 0, 0, 0, m, 0, m, /* 0 */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 1 */ + m, m, m, m, 0, 0, 0, 0, m, m, m, m, m, m, m, m, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, e1, 0, e2, 0, 0, 0, 0, 0, /* 3 */ + + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 4 */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 5 */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 6 */ + m, m, m, m, m, m, m, 0, m, m, m, m, m, m, m, m, /* 7 */ + + r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, r4, /* 8 */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* 9 */ + 0, 0, 0, m, m, m, 0, 0, 0, 0, 0, m, m, m, m, m, /* A */ + m, m, m, m, m, m, m, m, m, 0, m, m, m, m, m, m, /* B */ + + m, m, m, m, m, m, m, m, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* D */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, /* E */ + m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, 0 /* F */ }; /* eliminate the macros */ @@ -462,8 +465,8 @@ static const byte xop_a_extra[256] = { * May return 0 size for certain invalid instructions */ int -decode_sizeof(dcontext_t *dcontext, byte *start_pc, - int *num_prefixes _IF_X64(uint *rip_rel_pos)) +decode_sizeof_ex(dcontext_t *dcontext, byte *start_pc, int *num_prefixes, + uint *rip_rel_pos) { byte *pc = start_pc; uint opc = (uint)*pc; @@ -476,9 +479,7 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, bool rep_prefix = false; bool evex_prefix = false; byte reg_opcode; /* reg_opcode field of modrm byte */ -#ifdef X64 byte *rip_rel_pc = NULL; -#endif /* Check for prefix byte(s) */ while (found_prefix) { @@ -570,15 +571,14 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, bool implied_escape = (!vex3 && !evex_prefix) || ((vex3 || evex_prefix) && (vex_mm == 1)); if (implied_escape) { - sz += sizeof_escape(dcontext, pc, addr16 _IF_X64(&rip_rel_pc)); + sz += sizeof_escape(dcontext, pc, addr16, &rip_rel_pc); goto decode_sizeof_done; } else if (vex_mm == 2) { - sz += sizeof_3byte_38(dcontext, pc - 1, addr16, - true _IF_X64(&rip_rel_pc)); + sz += + sizeof_3byte_38(dcontext, pc - 1, addr16, true, &rip_rel_pc); goto decode_sizeof_done; } else if (vex_mm == 3) { - sz += sizeof_3byte_3a(dcontext, pc - 1, - addr16 _IF_X64(&rip_rel_pc)); + sz += sizeof_3byte_3a(dcontext, pc - 1, addr16, &rip_rel_pc); goto decode_sizeof_done; } } else @@ -599,7 +599,7 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, if (num_prefixes != NULL) *num_prefixes = sz; /* all have modrm */ - sz += sizeof_modrm(dcontext, pc + 1, addr16 _IF_X64(&rip_rel_pc)); + sz += sizeof_modrm(dcontext, pc + 1, addr16, &rip_rel_pc); if (map_select == 0x8) { /* these always have an immediate byte */ sz += 1; @@ -660,9 +660,9 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, */ if (varlen == VARLEN_MODRM) - sz += sizeof_modrm(dcontext, pc + 1, addr16 _IF_X64(&rip_rel_pc)); + sz += sizeof_modrm(dcontext, pc + 1, addr16, &rip_rel_pc); else if (varlen == VARLEN_ESCAPE) { - sz += sizeof_escape(dcontext, pc + 1, addr16 _IF_X64(&rip_rel_pc)); + sz += sizeof_escape(dcontext, pc + 1, addr16, &rip_rel_pc); /* special case: Intel and AMD added size-differing prefix-dependent instrs! */ if (*(pc + 1) == 0x78) { /* XXX: if have rex.w prefix we clear word_operands: is that legal combo? */ @@ -671,9 +671,13 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, sz += 2; } /* else, vmread, w/ no immeds */ } - } else if (varlen == VARLEN_FP_OP) - sz += sizeof_fp_op(dcontext, pc + 1, addr16 _IF_X64(&rip_rel_pc)); - else + } else if (varlen == VARLEN_FP_OP) { + sz += sizeof_fp_op(dcontext, pc + 1, addr16, &rip_rel_pc); + } else if (varlen == VARLEN_RIP_REL_1BYTE) { + rip_rel_pc = start_pc + sz - 1; + } else if (varlen == VARLEN_RIP_REL_4BYTE) { + rip_rel_pc = start_pc + sz - 4; + } else CLIENT_ASSERT(varlen == VARLEN_NONE, "internal decoding error"); /* special case that doesn't fit the mold (of course one had to exist) */ @@ -686,9 +690,11 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, else sz += 4; /* TEST El,il -- add size of immediate */ } + /* Another special case: xbegin. */ + if (opc == 0xc7 && *(pc + 1) == 0xf8) + rip_rel_pc = start_pc + sz - 4; decode_sizeof_done: -#ifdef X64 if (rip_rel_pos != NULL) { if (rip_rel_pc != NULL) { CLIENT_ASSERT(X64_MODE_DC(dcontext), @@ -699,14 +705,23 @@ decode_sizeof(dcontext_t *dcontext, byte *start_pc, } else *rip_rel_pos = 0; } -#endif return sz; } +int +decode_sizeof(dcontext_t *dcontext, byte *start_pc, + int *num_prefixes _IF_X64(uint *rip_rel_pos)) +{ +#ifdef X64 + return decode_sizeof_ex(dcontext, start_pc, num_prefixes, rip_rel_pos); +#else + return decode_sizeof_ex(dcontext, start_pc, num_prefixes, NULL); +#endif +} + static int -sizeof_3byte_38(dcontext_t *dcontext, byte *pc, bool addr16, - bool vex _IF_X64(byte **rip_rel_pc)) +sizeof_3byte_38(dcontext_t *dcontext, byte *pc, bool addr16, bool vex, byte **rip_rel_pc) { int sz = 1; /* opcode past 0x0f 0x38 */ uint opc = *(++pc); @@ -715,18 +730,18 @@ sizeof_3byte_38(dcontext_t *dcontext, byte *pc, bool addr16, * use the threebyte_38_fixed_length[opc] entry and assume 1 */ if (vex) sz += threebyte_38_vex_extra[opc]; - sz += sizeof_modrm(dcontext, pc + 1, addr16 _IF_X64(rip_rel_pc)); + sz += sizeof_modrm(dcontext, pc + 1, addr16, rip_rel_pc); return sz; } static int -sizeof_3byte_3a(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)) +sizeof_3byte_3a(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc) { pc++; /* so far all 0f 3a 3-byte instrs have modrm bytes and 1-byte immeds */ /* to be robust for future additions we don't actually * use the threebyte_3a_fixed_length[opc] entry and assume 1 */ - return 1 + sizeof_modrm(dcontext, pc + 1, addr16 _IF_X64(rip_rel_pc)) + 1; + return 1 + sizeof_modrm(dcontext, pc + 1, addr16, rip_rel_pc) + 1; } /* Two-byte opcode map (Tables A-4 and A-5). You use this routine @@ -736,7 +751,7 @@ sizeof_3byte_3a(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_r * May return 0 size for certain invalid instructions. */ static int -sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)) +sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc) { uint opc = (uint)*pc; int sz = escape_fixed_length[opc]; @@ -747,11 +762,15 @@ sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel */ if (varlen == VARLEN_MODRM) - return sz + sizeof_modrm(dcontext, pc + 1, addr16 _IF_X64(rip_rel_pc)); + return sz + sizeof_modrm(dcontext, pc + 1, addr16, rip_rel_pc); else if (varlen == VARLEN_3BYTE_38_ESCAPE) { - return sz + sizeof_3byte_38(dcontext, pc, addr16, false _IF_X64(rip_rel_pc)); + return sz + sizeof_3byte_38(dcontext, pc, addr16, false, rip_rel_pc); } else if (varlen == VARLEN_3BYTE_3A_ESCAPE) { - return sz + sizeof_3byte_3a(dcontext, pc, addr16 _IF_X64(rip_rel_pc)); + return sz + sizeof_3byte_3a(dcontext, pc, addr16, rip_rel_pc); + } else if (varlen == VARLEN_RIP_REL_1BYTE) { + *rip_rel_pc = pc + sz - 1; + } else if (varlen == VARLEN_RIP_REL_4BYTE) { + *rip_rel_pc = pc + sz - 4; } else CLIENT_ASSERT(varlen == VARLEN_NONE, "internal decoding error"); @@ -773,7 +792,7 @@ sizeof_escape(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel * where (*) is 6 if base==5 and 2 otherwise. */ static int -sizeof_modrm(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)) +sizeof_modrm(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc) { int l = 0; /* return value for sizeof(eAddr) */ @@ -834,13 +853,13 @@ sizeof_modrm(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_ * to determine the number of extra bytes in the entire * instruction. */ static int -sizeof_fp_op(dcontext_t *dcontext, byte *pc, bool addr16 _IF_X64(byte **rip_rel_pc)) +sizeof_fp_op(dcontext_t *dcontext, byte *pc, bool addr16, byte **rip_rel_pc) { if (*pc > 0xbf) return 1; /* entire ModR/M byte is an opcode extension */ /* fp opcode in reg/opcode field */ - return sizeof_modrm(dcontext, pc, addr16 _IF_X64(rip_rel_pc)); + return sizeof_modrm(dcontext, pc, addr16, rip_rel_pc); } /* Table indicating "interesting" instructions, i.e., ones we @@ -1305,13 +1324,12 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) int eflags; int i; byte modrm = 0; /* used only for EFLAGS_6_SPECIAL */ -#ifdef X64 /* PR 251479: we need to know about all rip-relative addresses. * Since change/setting raw bits invalidates, we must set this - * on every return. */ + * on every return. + */ uint rip_rel_pos; -#endif - int sz = decode_sizeof(dcontext, pc, &prefixes _IF_X64(&rip_rel_pos)); + int sz = decode_sizeof_ex(dcontext, pc, &prefixes, &rip_rel_pos); if (sz == 0) { /* invalid instruction! */ instr_set_opcode(instr, OP_INVALID); @@ -1434,7 +1452,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) /* assumption: opcode already OP_UNDECODED */ /* assumption: operands are already marked invalid (instr was reset) */ instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (start_pc + sz); } @@ -1461,7 +1479,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) /* don't bother to set dsts/srcs */ instr_set_operands_valid(instr, false); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (start_pc + sz); } #endif @@ -1474,7 +1492,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_num_opnds(dcontext, instr, 0, 1); instr_set_target(instr, opnd_create_pc(tgt)); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 2); } @@ -1490,7 +1508,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_target(instr, opnd_create_pc(tgt)); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 2); } @@ -1507,7 +1525,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) REG_XSP, REG_NULL, 0, 0, resolve_variable_size_dc(dcontext, 0, OPSZ_call, false))); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 5); } @@ -1518,7 +1536,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_num_opnds(dcontext, instr, 0, 1); instr_set_target(instr, opnd_create_pc(tgt)); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 5); } @@ -1535,7 +1553,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_target(instr, opnd_create_pc(tgt)); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 6); } @@ -1574,18 +1592,18 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) resolve_variable_size_dc( dcontext, 0, nibble1 == 2 ? OPSZ_ret : OPSZ_REXVARSTACK, false))); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 3); case 3: /* ret w/ no immed */ instr_set_opcode(instr, OP_ret); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); /* we don't set any operands and leave to an up-decode for that */ return (pc + 1); case 0xb: /* far ret w/ no immed */ instr_set_opcode(instr, OP_ret_far); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); /* we don't set any operands and leave to an up-decode for that */ return (pc + 1); } @@ -1623,7 +1641,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_src(instr, 1, opnd_create_reg(REG_XCX)); instr_set_target(instr, opnd_create_pc(tgt)); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 2); } /* otherwise it wasn't a funny 8-bit cbr so continue */ @@ -1647,7 +1665,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_src(instr, 0, opnd_create_immed_int((char)byte1, OPSZ_1)); instr_set_src(instr, 1, opnd_create_reg(REG_XSP)); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 2); } /* sys{enter,exit,call,ret} */ @@ -1670,14 +1688,14 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) instr_set_num_opnds(dcontext, instr, 0, 0); } instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 2); } /* iret */ if (byte0 == 0xcf) { instr_set_opcode(instr, OP_iret); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 1); } /* popf */ @@ -1698,7 +1716,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) stack_sized_reg, REG_NULL, 0, 0, resolve_variable_size_dc(dcontext, prefixes, OPSZ_VARSTACK, false))); instr_set_dst(instr, 0, opnd_create_reg(stack_sized_reg)); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (pc + 1); } @@ -1707,7 +1725,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) if (INTERNAL_OPTION(mangle_app_seg) && (byte0 == 0x8c || byte0 == 0x8e)) { instr_set_opcode(instr, OP_mov_seg); instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); return (start_pc + sz); } #endif @@ -1725,7 +1743,7 @@ decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) /* all non-pc-relative instructions */ /* assumption: opcode already OP_UNDECODED */ instr_set_raw_bits(instr, start_pc, sz); - IF_X64(instr_set_rip_rel_pos(instr, rip_rel_pos)); + instr_set_rip_rel_pos(instr, rip_rel_pos); /* assumption: operands are already marked invalid (instr was reset) */ return (start_pc + sz); } diff --git a/core/arch/x86/decode_private.h b/core/arch/x86/decode_private.h index 9677d7b7cfa..e3cf62072db 100644 --- a/core/arch/x86/decode_private.h +++ b/core/arch/x86/decode_private.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2019 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -325,8 +325,9 @@ struct _decode_info_t { byte *start_pc; byte *final_pc; uint len; - /* This field is only used when encoding rip-relative data refs. - * To save space we could make it a union with disp. + /* This field is only used when encoding rip-relative data refs, and for + * re-relativizing level 1-3 relative jumps. To save space we could make it a + * union with disp. */ byte *disp_abs; #ifdef X64 diff --git a/core/arch/x86/encode.c b/core/arch/x86/encode.c index 5c18c511291..b1117828b18 100644 --- a/core/arch/x86/encode.c +++ b/core/arch/x86/encode.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2019 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2001-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -2663,10 +2663,6 @@ copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst { byte *orig_dst_pc = dst_pc; ASSERT(instr_raw_bits_valid(instr)); - /* FIXME i#731: if want to support ctis as well, need - * instr->rip_rel_disp_sz and need to set both for non-x64 as well - * in decode_sizeof(): or only in decode_cti()? - */ /* For PR 251646 we have special support for mangled jecxz/loop* */ if (instr_is_cti_short_rewrite(instr, NULL)) { app_pc target; @@ -2681,7 +2677,6 @@ copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst } *((int *)dst_pc) = (int)(target - (final_pc + instr->length)); } -#ifdef X64 /* We test the flag directly to support cases where the raw bits are * being set by private_instr_encode() */ else if (instr_rip_rel_valid(instr) && instr_get_rip_rel_pos(instr) > 0) { @@ -2690,9 +2685,9 @@ copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst ptr_int_t new_offs; bool addr32 = false; uint rip_rel_pos = instr_get_rip_rel_pos(instr); /* disp offs within instr */ - DEBUG_DECLARE(bool ok;) ASSERT(!instr_is_level_0(instr)); - DEBUG_DECLARE(ok =) instr_get_rel_addr_target(instr, &target); + DEBUG_DECLARE(bool ok;) + DEBUG_DECLARE(ok =) instr_get_rel_data_or_instr_target(instr, &target); ASSERT(ok); new_offs = target - (final_pc + instr->length); /* PR 253327: we don't record whether addr32 so we have to deduce it now */ @@ -2700,7 +2695,7 @@ copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst int num_prefixes; int i; IF_X64(bool old_mode = set_x86_mode(dcontext, instr_get_x86_mode(instr));) - decode_sizeof(dcontext, instr->bytes, &num_prefixes, NULL); + decode_sizeof(dcontext, instr->bytes, &num_prefixes _IF_X64(NULL)); IF_X64(set_x86_mode(dcontext, old_mode)); for (i = 0; i < num_prefixes; i++) { if (*(instr->bytes + i) == ADDR_PREFIX_OPCODE) { @@ -2722,14 +2717,20 @@ copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, byte *dst } memcpy(dst_pc, instr->bytes, rip_rel_pos); dst_pc += rip_rel_pos; - *((int *)dst_pc) = (int)new_offs; - if (rip_rel_pos + 4U < instr->length) { - /* suffix byte */ - memcpy(dst_pc + 4, instr->bytes + rip_rel_pos + 4, - instr->length - (rip_rel_pos + 4)); + /* We only support non-4-byte rip-rel disps for 1-byte instr-final (jcc_short). */ + if (rip_rel_pos + 1 == instr->length) { + ASSERT(CHECK_TRUNCATE_TYPE_sbyte(new_offs)); + *((char *)dst_pc) = (char)new_offs; + } else { + ASSERT(rip_rel_pos + 4 <= instr->length); + *((int *)dst_pc) = (int)new_offs; + if (rip_rel_pos + 4U < instr->length) { + /* suffix byte */ + memcpy(dst_pc + 4, instr->bytes + rip_rel_pos + 4, + instr->length - (rip_rel_pos + 4)); + } } } else -#endif memcpy(dst_pc, instr->bytes, instr->length); return orig_dst_pc + instr->length; } @@ -3106,7 +3107,6 @@ instr_encode_arch(dcontext_t *dcontext, instr_t *instr, byte *copy_pc, byte *fin } if (disp_relativize_at != NULL) { - CLIENT_ASSERT(X64_MODE(&di), "encode error: no rip-relative in x86 mode!"); if (check_reachable && !CHECK_TRUNCATE_TYPE_int(di.disp_abs - (field_ptr - copy_pc + final_pc)) && /* PR 253327: we auto-add addr prefix for out-of-reach low tgt */ @@ -3121,7 +3121,7 @@ instr_encode_arch(dcontext_t *dcontext, instr_t *instr, byte *copy_pc, byte *fin * private_instr_encode()), set rip_rel_pos */ CLIENT_ASSERT(CHECK_TRUNCATE_TYPE_byte(disp_relativize_at - di.start_pc), "internal encode error: rip-relative instr pos too large"); - IF_X64(instr_set_rip_rel_pos(instr, (byte)(disp_relativize_at - di.start_pc))); + instr_set_rip_rel_pos(instr, (byte)(disp_relativize_at - di.start_pc)); } #if DEBUG_DISABLE /* turn back on if want to debug */ diff --git a/suite/tests/api/ir_x86.c b/suite/tests/api/ir_x86.c index 3bb9222c63d..7266ecf039d 100644 --- a/suite/tests/api/ir_x86.c +++ b/suite/tests/api/ir_x86.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2019 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * Copyright (c) 2007-2008 VMware, Inc. All rights reserved. * **********************************************************/ @@ -2168,6 +2168,97 @@ test_reg_exact_reads(void *dc) instr_destroy(dc, instr); } +static void +test_re_relativization_disp32_opc16(void *dcontext, byte opc1, byte opc2) +{ + byte buf_dec_enc[] = { opc1, opc2, + /* disp32 of 0 which targets the next PC. */ + 0x00, 0x00, 0x00, 0x00, + /* We encode here. */ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; + instr_t instr; + instr_init(dcontext, &instr); + byte *pc = decode_from_copy(dcontext, buf_dec_enc, buf_dec_enc + 1, &instr); + ASSERT(pc != NULL); + ASSERT(instr_raw_bits_valid(&instr)); /* i#731. */ + ASSERT(opnd_get_pc(instr_get_src(&instr, 0)) == buf_dec_enc + 7); + pc = instr_encode(dcontext, &instr, buf_dec_enc + 6); + ASSERT(pc != NULL); + instr_reset(dcontext, &instr); + pc = decode(dcontext, buf_dec_enc + 6, &instr); + ASSERT(pc != NULL); + ASSERT(opnd_get_pc(instr_get_src(&instr, 0)) == buf_dec_enc + 7); + instr_free(dcontext, &instr); +} + +static void +test_re_relativization_disp8_opc8(void *dcontext, byte opc) +{ + byte buf_dec_enc[] = { opc, + /* disp8 of 0 which targets the next PC. */ + 0x00, + /* We encode here. */ 0x90, 0x90 }; + instr_t instr; + instr_init(dcontext, &instr); + byte *pc = decode_from_copy(dcontext, buf_dec_enc, buf_dec_enc + 1, &instr); + ASSERT(pc != NULL); + ASSERT(instr_raw_bits_valid(&instr)); /* i#731. */ + ASSERT(opnd_get_pc(instr_get_src(&instr, 0)) == buf_dec_enc + 3); + pc = instr_encode(dcontext, &instr, buf_dec_enc + 2); + ASSERT(pc != NULL); + instr_reset(dcontext, &instr); + pc = decode(dcontext, buf_dec_enc + 2, &instr); + ASSERT(pc != NULL); + ASSERT(opnd_get_pc(instr_get_src(&instr, 0)) == buf_dec_enc + 3); + instr_free(dcontext, &instr); +} + +/* XXX: Have DR export its raw opcodes, which overlap this list. */ +enum { + RAW_OPCODE_jmp_short = 0xeb, + RAW_OPCODE_jcc_short_start = 0x70, + RAW_OPCODE_jcc_short_end = 0x7f, + RAW_OPCODE_jcc_byte1 = 0x0f, + RAW_OPCODE_jcc_byte2_start = 0x80, + RAW_OPCODE_jcc_byte2_end = 0x8f, + RAW_OPCODE_loop_start = 0xe0, + RAW_OPCODE_loop_end = 0xe3, + RAW_OPCODE_xbegin_byte1 = 0xc7, + RAW_OPCODE_xbegin_byte2 = 0xf8, +}; + +static void +test_re_relativization(void *dcontext) +{ + instr_t instr; + instr_init(dcontext, &instr); + byte *pc; + + /* Test the i#4017 2-byte nop where re-encoding results in a 1-byte length. */ + const byte buf_nop2[] = { 0x66, 0x90 }; + instr_reset(dcontext, &instr); + pc = decode_from_copy(dcontext, (byte *)buf_nop2, (byte *)buf_nop2 + 1, &instr); + ASSERT(pc != NULL); + ASSERT(instr_length(dcontext, &instr) == sizeof(buf_nop2)); + + /* Test i#731 on short jumps. */ + test_re_relativization_disp8_opc8(dcontext, RAW_OPCODE_jmp_short); + test_re_relativization_disp8_opc8(dcontext, RAW_OPCODE_loop_start); + test_re_relativization_disp8_opc8(dcontext, RAW_OPCODE_loop_end); + test_re_relativization_disp8_opc8(dcontext, RAW_OPCODE_jcc_short_start); + test_re_relativization_disp8_opc8(dcontext, RAW_OPCODE_jcc_short_end); + + /* Test xbegin. */ + test_re_relativization_disp32_opc16(dcontext, RAW_OPCODE_xbegin_byte1, + RAW_OPCODE_xbegin_byte2); + /* Test jcc. */ + test_re_relativization_disp32_opc16(dcontext, RAW_OPCODE_jcc_byte1, + RAW_OPCODE_jcc_byte2_start); + test_re_relativization_disp32_opc16(dcontext, RAW_OPCODE_jcc_byte1, + RAW_OPCODE_jcc_byte2_end); + + instr_free(dcontext, &instr); +} + int main(int argc, char *argv[]) { @@ -2237,6 +2328,8 @@ main(int argc, char *argv[]) test_reg_exact_reads(dcontext); + test_re_relativization(dcontext); + #ifndef STANDALONE_DECODER /* speed up compilation */ test_all_opcodes_2_avx512_vex(dcontext); test_all_opcodes_3_avx512_vex(dcontext);