| Index: third_party/tcmalloc/chromium/src/windows/preamble_patcher.h
|
| diff --git a/third_party/tcmalloc/chromium/src/windows/preamble_patcher.h b/third_party/tcmalloc/chromium/src/windows/preamble_patcher.h
|
| index 0028e4e09628d288080c6c2de82474055e22eb48..4fdb7d001655a02953ea8f0f821a5c5ab2f7794e 100644
|
| --- a/third_party/tcmalloc/chromium/src/windows/preamble_patcher.h
|
| +++ b/third_party/tcmalloc/chromium/src/windows/preamble_patcher.h
|
| @@ -29,6 +29,7 @@
|
| *
|
| * ---
|
| * Author: Joi Sigurdsson
|
| + * Author: Scott Francis
|
| *
|
| * Definition of PreamblePatcher
|
| */
|
| @@ -36,6 +37,7 @@
|
| #ifndef GOOGLE_PERFTOOLS_PREAMBLE_PATCHER_H_
|
| #define GOOGLE_PERFTOOLS_PREAMBLE_PATCHER_H_
|
|
|
| +#include "config.h"
|
| #include <windows.h>
|
|
|
| // compatibility shim
|
| @@ -47,7 +49,25 @@
|
| // bytes of the function. Considering the worst case scenario, we need 4
|
| // bytes + the max instruction size + 5 more bytes for our jump back to
|
| // the original code. With that in mind, 32 is a good number :)
|
| +#ifdef _M_X64
|
| +// In 64-bit mode we may need more room. In 64-bit mode all jumps must be
|
| +// within +/-2GB of RIP. Because of this limitation we may need to use a
|
| +// trampoline to jump to the replacement function if it is further than 2GB
|
| +// away from the target. The trampoline is 14 bytes.
|
| +//
|
| +// So 4 bytes + max instruction size (17 bytes) + 5 bytes to jump back to the
|
| +// original code + trampoline size. 64 bytes is a nice number :-)
|
| +#define MAX_PREAMBLE_STUB_SIZE (64)
|
| +#else
|
| #define MAX_PREAMBLE_STUB_SIZE (32)
|
| +#endif
|
| +
|
| +// Determines if this is a 64-bit binary.
|
| +#ifdef _M_X64
|
| +static const bool kIs64BitBinary = true;
|
| +#else
|
| +static const bool kIs64BitBinary = false;
|
| +#endif
|
|
|
| namespace sidestep {
|
|
|
| @@ -68,6 +88,8 @@ enum SideStepError {
|
| #define SIDESTEP_TO_HRESULT(error) \
|
| MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, error)
|
|
|
| +class DeleteUnsignedCharArray;
|
| +
|
| // Implements a patching mechanism that overwrites the first few bytes of
|
| // a function preamble with a jump to our hook function, which is then
|
| // able to call the original function via a specially-made preamble-stub
|
| @@ -126,7 +148,7 @@ enum SideStepError {
|
| // reuse the result of calling the function with a given parameter, which
|
| // may mean if you patch the function in between your patch will never get
|
| // invoked. See preamble_patcher_test.cc for an example.
|
| -class PreamblePatcher {
|
| +class PERFTOOLS_DLL_DECL PreamblePatcher {
|
| public:
|
|
|
| // This is a typesafe version of RawPatch(), identical in all other
|
| @@ -287,7 +309,55 @@ class PreamblePatcher {
|
| return (T)ResolveTargetImpl((unsigned char*)target_function, NULL);
|
| }
|
|
|
| + // Allocates a block of memory of size MAX_PREAMBLE_STUB_SIZE that is as
|
| + // close (within 2GB) as possible to target. This is done to ensure that
|
| + // we can perform a relative jump from target to a trampoline if the
|
| + // replacement function is > +-2GB from target. This means that we only need
|
| + // to patch 5 bytes in the target function.
|
| + //
|
| + // @param target Pointer to target function.
|
| + //
|
| + // @return Returns a block of memory of size MAX_PREAMBLE_STUB_SIZE that can
|
| + // be used to store a function preamble block.
|
| + static unsigned char* AllocPreambleBlockNear(void* target);
|
| +
|
| + // Frees a block allocated by AllocPreambleBlockNear.
|
| + //
|
| + // @param block Block that was returned by AllocPreambleBlockNear.
|
| + static void FreePreambleBlock(unsigned char* block);
|
| +
|
| private:
|
| + friend class DeleteUnsignedCharArray;
|
| +
|
| + // Used to store data allocated for preamble stubs
|
| + struct PreamblePage {
|
| + unsigned int magic_;
|
| + PreamblePage* next_;
|
| + // This member points to a linked list of free blocks within the page
|
| + // or NULL if at the end
|
| + void* free_;
|
| + };
|
| +
|
| + // In 64-bit mode, the replacement function must be within 2GB of the original
|
| + // target in order to only require 5 bytes for the function patch. To meet
|
| + // this requirement we're creating an allocator within this class to
|
| + // allocate blocks that are within 2GB of a given target. This member is the
|
| + // head of a linked list of pages used to allocate blocks that are within
|
| + // 2GB of the target.
|
| + static PreamblePage* preamble_pages_;
|
| +
|
| + // Page granularity
|
| + static long granularity_;
|
| +
|
| + // Page size
|
| + static long pagesize_;
|
| +
|
| + // Determines if the patcher has been initialized.
|
| + static bool initialized_;
|
| +
|
| + // Used to initialize static members.
|
| + static void Initialize();
|
| +
|
| // Patches a function by overwriting its first few bytes with
|
| // a jump to a different function. This is similar to the RawPatch
|
| // function except that it uses the stub allocated by the caller
|
| @@ -318,7 +388,7 @@ class PreamblePatcher {
|
| // @return An error code indicating the result of patching.
|
| static SideStepError RawPatchWithStubAndProtections(
|
| void* target_function,
|
| - void *replacement_function,
|
| + void* replacement_function,
|
| unsigned char* preamble_stub,
|
| unsigned long stub_size,
|
| unsigned long* bytes_needed);
|
| @@ -348,7 +418,7 @@ class PreamblePatcher {
|
| //
|
| // @return An error code indicating the result of patching.
|
| static SideStepError RawPatchWithStub(void* target_function,
|
| - void *replacement_function,
|
| + void* replacement_function,
|
| unsigned char* preamble_stub,
|
| unsigned long stub_size,
|
| unsigned long* bytes_needed);
|
| @@ -365,12 +435,175 @@ class PreamblePatcher {
|
| // target_function, we get to the address stop, we return
|
| // immediately, the address that jumps to stop_before.
|
| //
|
| + // @param stop_before_trampoline When following JMP instructions from
|
| + // target_function, stop before a trampoline is detected. See comment in
|
| + // PreamblePatcher::RawPatchWithStub for more information. This parameter
|
| + // has no effect in 32-bit mode.
|
| + //
|
| // @return Either target_function (the input parameter), or if
|
| // target_function's body consists entirely of a JMP instruction,
|
| // the address it JMPs to (or more precisely, the address at the end
|
| // of a chain of JMPs).
|
| static void* ResolveTargetImpl(unsigned char* target_function,
|
| - unsigned char* stop_before);
|
| + unsigned char* stop_before,
|
| + bool stop_before_trampoline = false);
|
| +
|
| + // Helper routine that attempts to allocate a page as close (within 2GB)
|
| + // as possible to target.
|
| + //
|
| + // @param target Pointer to target function.
|
| + //
|
| + // @return Returns an address that is within 2GB of target.
|
| + static void* AllocPageNear(void* target);
|
| +
|
| + // Helper routine that determines if a target instruction is a short
|
| + // conditional jump.
|
| + //
|
| + // @param target Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction in bytes.
|
| + //
|
| + // @return Returns true if the instruction is a short conditional jump.
|
| + static bool IsShortConditionalJump(unsigned char* target,
|
| + unsigned int instruction_size);
|
| +
|
| + // Helper routine that determines if a target instruction is a near
|
| + // conditional jump.
|
| + //
|
| + // @param target Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction in bytes.
|
| + //
|
| + // @return Returns true if the instruction is a near conditional jump.
|
| + static bool IsNearConditionalJump(unsigned char* target,
|
| + unsigned int instruction_size);
|
| +
|
| + // Helper routine that determines if a target instruction is a near
|
| + // relative jump.
|
| + //
|
| + // @param target Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction in bytes.
|
| + //
|
| + // @return Returns true if the instruction is a near absolute jump.
|
| + static bool IsNearRelativeJump(unsigned char* target,
|
| + unsigned int instruction_size);
|
| +
|
| + // Helper routine that determines if a target instruction is a near
|
| + // absolute call.
|
| + //
|
| + // @param target Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction in bytes.
|
| + //
|
| + // @return Returns true if the instruction is a near absolute call.
|
| + static bool IsNearAbsoluteCall(unsigned char* target,
|
| + unsigned int instruction_size);
|
| +
|
| + // Helper routine that determines if a target instruction is a near
|
| + // absolute call.
|
| + //
|
| + // @param target Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction in bytes.
|
| + //
|
| + // @return Returns true if the instruction is a near absolute call.
|
| + static bool IsNearRelativeCall(unsigned char* target,
|
| + unsigned int instruction_size);
|
| +
|
| + // Helper routine that determines if a target instruction is a 64-bit MOV
|
| + // that uses a RIP-relative displacement.
|
| + //
|
| + // @param target Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction in bytes.
|
| + //
|
| + // @return Returns true if the instruction is a MOV with displacement.
|
| + static bool IsMovWithDisplacement(unsigned char* target,
|
| + unsigned int instruction_size);
|
| +
|
| + // Helper routine that converts a short conditional jump instruction
|
| + // to a near conditional jump in a target buffer. Note that the target
|
| + // buffer must be within 2GB of the source for the near jump to work.
|
| + //
|
| + // A short conditional jump instruction is in the format:
|
| + // 7x xx = Jcc rel8off
|
| + //
|
| + // @param source Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction.
|
| + //
|
| + // @param target Target buffer to write the new instruction.
|
| + //
|
| + // @param target_bytes Pointer to a buffer that contains the size
|
| + // of the target instruction, in bytes.
|
| + //
|
| + // @param target_size Size of the target buffer.
|
| + //
|
| + // @return Returns SIDESTEP_SUCCESS if successful, otherwise an error.
|
| + static SideStepError PatchShortConditionalJump(unsigned char* source,
|
| + unsigned int instruction_size,
|
| + unsigned char* target,
|
| + unsigned int* target_bytes,
|
| + unsigned int target_size);
|
| +
|
| + // Helper routine that converts an instruction that will convert various
|
| + // jump-like instructions to corresponding instructions in the target buffer.
|
| + // What this routine does is fix up the relative offsets contained in jump
|
| + // instructions to point back to the original target routine. Like with
|
| + // PatchShortConditionalJump, the target buffer must be within 2GB of the
|
| + // source.
|
| + //
|
| + // We currently handle the following instructions:
|
| + //
|
| + // E9 xx xx xx xx = JMP rel32off
|
| + // 0F 8x xx xx xx xx = Jcc rel32off
|
| + // FF /2 xx xx xx xx = CALL reg/mem32/mem64
|
| + // E8 xx xx xx xx = CALL rel32off
|
| + //
|
| + // It should not be hard to update this function to support other
|
| + // instructions that jump to relative targets.
|
| + //
|
| + // @param source Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction.
|
| + //
|
| + // @param target Target buffer to write the new instruction.
|
| + //
|
| + // @param target_bytes Pointer to a buffer that contains the size
|
| + // of the target instruction, in bytes.
|
| + //
|
| + // @param target_size Size of the target buffer.
|
| + //
|
| + // @return Returns SIDESTEP_SUCCESS if successful, otherwise an error.
|
| + static SideStepError PatchNearJumpOrCall(unsigned char* source,
|
| + unsigned int instruction_size,
|
| + unsigned char* target,
|
| + unsigned int* target_bytes,
|
| + unsigned int target_size);
|
| +
|
| + // Helper routine that patches a 64-bit MOV instruction with a RIP-relative
|
| + // displacement. The target buffer must be within 2GB of the source.
|
| + //
|
| + // 48 8B 0D XX XX XX XX = MOV rel32off
|
| + //
|
| + // @param source Pointer to instruction.
|
| + //
|
| + // @param instruction_size Size of the instruction.
|
| + //
|
| + // @param target Target buffer to write the new instruction.
|
| + //
|
| + // @param target_bytes Pointer to a buffer that contains the size
|
| + // of the target instruction, in bytes.
|
| + //
|
| + // @param target_size Size of the target buffer.
|
| + //
|
| + // @return Returns SIDESTEP_SUCCESS if successful, otherwise an error.
|
| + static SideStepError PatchMovWithDisplacement(unsigned char* source,
|
| + unsigned int instruction_size,
|
| + unsigned char* target,
|
| + unsigned int* target_bytes,
|
| + unsigned int target_size);
|
| };
|
|
|
| }; // namespace sidestep
|
|
|