Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Unified Diff: third_party/tcmalloc/chromium/src/windows/preamble_patcher.cc

Issue 9667026: Revert 126020 - Experiment for updating the tcmalloc chromium branch to r144 (gperftools 2.0). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/tcmalloc/chromium/src/windows/preamble_patcher.cc
===================================================================
--- third_party/tcmalloc/chromium/src/windows/preamble_patcher.cc (revision 126022)
+++ third_party/tcmalloc/chromium/src/windows/preamble_patcher.cc (working copy)
@@ -29,7 +29,6 @@
*
* ---
* Author: Joi Sigurdsson
- * Author: Scott Francis
*
* Implementation of PreamblePatcher
*/
@@ -47,42 +46,18 @@
#define ASM_JMP32ABS_0 0xFF
#define ASM_JMP32ABS_1 0x25
#define ASM_JMP8REL 0xEB
-#define ASM_JCC32REL_0 0x0F
-#define ASM_JCC32REL_1_MASK 0x80
-#define ASM_NOP 0x90
-// X64 opcodes
-#define ASM_REXW 0x48
-#define ASM_MOVRAX_IMM 0xB8
-#define ASM_JMP 0xFF
-#define ASM_JMP_RAX 0xE0
namespace sidestep {
-PreamblePatcher::PreamblePage* PreamblePatcher::preamble_pages_ = NULL;
-long PreamblePatcher::granularity_ = 0;
-long PreamblePatcher::pagesize_ = 0;
-bool PreamblePatcher::initialized_ = false;
-
-static const unsigned int kPreamblePageMagic = 0x4347414D; // "MAGC"
-
// Handle a special case that we see with functions that point into an
// IAT table (including functions linked statically into the
// application): these function already starts with ASM_JMP32*. For
// instance, malloc() might be implemented as a JMP to __malloc().
// This function follows the initial JMPs for us, until we get to the
// place where the actual code is defined. If we get to STOP_BEFORE,
-// we return the address before stop_before. The stop_before_trampoline
-// flag is used in 64-bit mode. If true, we will return the address
-// before a trampoline is detected. Trampolines are defined as:
-//
-// nop
-// mov rax, <replacement_function>
-// jmp rax
-//
-// See PreamblePatcher::RawPatchWithStub for more information.
+// we return the address before stop_before.
void* PreamblePatcher::ResolveTargetImpl(unsigned char* target,
- unsigned char* stop_before,
- bool stop_before_trampoline) {
+ unsigned char* stop_before) {
if (target == NULL)
return NULL;
while (1) {
@@ -106,26 +81,15 @@
// Visual studio seems to sometimes do it this way instead of the
// previous way. Not sure what the rules are, but it was happening
// with operator new in some binaries.
- void** new_target_v;
- if (kIs64BitBinary) {
- // In 64-bit mode JMPs are RIP-relative, not absolute
- int target_offset;
- memcpy(reinterpret_cast<void*>(&target_offset),
- reinterpret_cast<void*>(target + 2), 4);
- new_target_v = reinterpret_cast<void**>(target + target_offset + 6);
- } else {
- SIDESTEP_ASSERT(sizeof(new_target) == 4);
- memcpy(&new_target_v, reinterpret_cast<void*>(target + 2), 4);
- }
+ void **new_target_v;
+ SIDESTEP_ASSERT(sizeof(new_target) == 4);
+ memcpy(&new_target_v, reinterpret_cast<void*>(target + 2), 4);
new_target = reinterpret_cast<unsigned char*>(*new_target_v);
} else {
break;
}
if (new_target == stop_before)
break;
- if (stop_before_trampoline && *new_target == ASM_NOP
- && new_target[1] == ASM_REXW && new_target[2] == ASM_MOVRAX_IMM)
- break;
target = new_target;
}
return target;
@@ -139,7 +103,7 @@
~DeleteUnsignedCharArray() {
if (array_) {
- PreamblePatcher::FreePreambleBlock(array_);
+ delete [] array_;
}
}
@@ -227,23 +191,9 @@
return SIDESTEP_INVALID_PARAMETER;
}
- BOOL succeeded = FALSE;
-
- // First, deal with a special case that we see with functions that
- // point into an IAT table (including functions linked statically
- // into the application): these function already starts with
- // ASM_JMP32REL. For instance, malloc() might be implemented as a
- // JMP to __malloc(). In that case, we replace the destination of
- // the JMP (__malloc), rather than the JMP itself (malloc). This
- // way we get the correct behavior no matter how malloc gets called.
- void* new_target = ResolveTarget(target_function);
- if (new_target != target_function) {
- target_function = new_target;
- }
-
- // In 64-bit mode, preamble_stub must be within 2GB of target function
- // so that if target contains a jump, we can translate it.
- unsigned char* preamble_stub = AllocPreambleBlockNear(target_function);
+ // @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at
+ // this size
+ unsigned char* preamble_stub = new unsigned char[MAX_PREAMBLE_STUB_SIZE];
if (!preamble_stub) {
SIDESTEP_ASSERT(false && "Unable to allocate preamble-stub.");
return SIDESTEP_INSUFFICIENT_BUFFER;
@@ -252,6 +202,19 @@
// Frees the array at end of scope.
DeleteUnsignedCharArray guard_preamble_stub(preamble_stub);
+ // Change the protection of the newly allocated preamble stub to
+ // PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data
+ // Execution Prevention) which will cause an exception if code is executed
+ // from a page on which you do not have read access.
+ DWORD old_stub_protect = 0;
+ BOOL succeeded = ::VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE,
+ PAGE_EXECUTE_READWRITE, &old_stub_protect);
+ if (!succeeded) {
+ SIDESTEP_ASSERT(false &&
+ "Failed to make page preamble stub read-write-execute.");
+ return SIDESTEP_ACCESS_DENIED;
+ }
+
SideStepError error_code = RawPatchWithStubAndProtections(
target_function, replacement_function, preamble_stub,
MAX_PREAMBLE_STUB_SIZE, NULL);
@@ -297,6 +260,23 @@
return SIDESTEP_INVALID_PARAMETER;
}
+ // We disassemble the preamble of the _stub_ to see how many bytes we
+ // originally copied to the stub.
+ MiniDisassembler disassembler;
+ unsigned int preamble_bytes = 0;
+ while (preamble_bytes < 5) {
+ InstructionType instruction_type =
+ disassembler.Disassemble(
+ reinterpret_cast<unsigned char*>(original_function_stub) +
+ preamble_bytes,
+ preamble_bytes);
+ if (IT_GENERIC != instruction_type) {
+ SIDESTEP_ASSERT(false &&
+ "Should only have generic instructions in stub!!");
+ return SIDESTEP_UNSUPPORTED_INSTRUCTION;
+ }
+ }
+
// Before unpatching, target_function should be a JMP to
// replacement_function. If it's not, then either it's an error, or
// we're falling into the case where the original instruction was a
@@ -306,8 +286,7 @@
unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
target = reinterpret_cast<unsigned char*>(
ResolveTargetImpl(
- target, reinterpret_cast<unsigned char*>(replacement_function),
- true));
+ target, reinterpret_cast<unsigned char*>(replacement_function)));
// We should end at the function we patched. When we patch, we insert
// a ASM_JMP32REL instruction, so look for that as a sanity check.
if (target[0] != ASM_JMP32REL) {
@@ -316,13 +295,11 @@
return SIDESTEP_INVALID_PARAMETER;
}
- const unsigned int kRequiredTargetPatchBytes = 5;
-
// We need to be able to write to a process-local copy of the first
- // kRequiredTargetPatchBytes bytes of target_function
+ // MAX_PREAMBLE_STUB_SIZE bytes of target_function
DWORD old_target_function_protect = 0;
- BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
- kRequiredTargetPatchBytes,
+ BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
+ MAX_PREAMBLE_STUB_SIZE,
PAGE_EXECUTE_READWRITE,
&old_target_function_protect);
if (!succeeded) {
@@ -331,67 +308,20 @@
return SIDESTEP_ACCESS_DENIED;
}
- unsigned char* preamble_stub = reinterpret_cast<unsigned char*>(
- original_function_stub);
+ // Replace the first few bytes of the original function with the bytes we
+ // previously moved to the preamble stub.
+ memcpy(reinterpret_cast<void*>(target),
+ original_function_stub, preamble_bytes);
- // Disassemble the preamble of stub and copy the bytes back to target.
- // If we've done any conditional jumps in the preamble we need to convert
- // them back to the orignal REL8 jumps in the target.
- MiniDisassembler disassembler;
- unsigned int preamble_bytes = 0;
- unsigned int target_bytes = 0;
- while (target_bytes < kRequiredTargetPatchBytes) {
- unsigned int cur_bytes = 0;
- InstructionType instruction_type =
- disassembler.Disassemble(preamble_stub + preamble_bytes, cur_bytes);
- if (IT_JUMP == instruction_type) {
- unsigned int jump_bytes = 0;
- SideStepError jump_ret = SIDESTEP_JUMP_INSTRUCTION;
- if (IsNearConditionalJump(preamble_stub + preamble_bytes, cur_bytes) ||
- IsNearRelativeJump(preamble_stub + preamble_bytes, cur_bytes) ||
- IsNearAbsoluteCall(preamble_stub + preamble_bytes, cur_bytes) ||
- IsNearRelativeCall(preamble_stub + preamble_bytes, cur_bytes)) {
- jump_ret = PatchNearJumpOrCall(preamble_stub + preamble_bytes,
- cur_bytes, target + target_bytes,
- &jump_bytes, MAX_PREAMBLE_STUB_SIZE);
- }
- if (jump_ret == SIDESTEP_JUMP_INSTRUCTION) {
- SIDESTEP_ASSERT(false &&
- "Found unsupported jump instruction in stub!!");
- return SIDESTEP_UNSUPPORTED_INSTRUCTION;
- }
- target_bytes += jump_bytes;
- } else if (IT_GENERIC == instruction_type) {
- if (IsMovWithDisplacement(preamble_stub + preamble_bytes, cur_bytes)) {
- unsigned int mov_bytes = 0;
- if (PatchMovWithDisplacement(preamble_stub + preamble_bytes, cur_bytes,
- target + target_bytes, &mov_bytes,
- MAX_PREAMBLE_STUB_SIZE)
- != SIDESTEP_SUCCESS) {
- SIDESTEP_ASSERT(false &&
- "Found unsupported generic instruction in stub!!");
- return SIDESTEP_UNSUPPORTED_INSTRUCTION;
- }
- } else {
- memcpy(reinterpret_cast<void*>(target + target_bytes),
- reinterpret_cast<void*>(reinterpret_cast<unsigned char*>(
- original_function_stub) + preamble_bytes), cur_bytes);
- target_bytes += cur_bytes;
- }
- } else {
- SIDESTEP_ASSERT(false &&
- "Found unsupported instruction in stub!!");
- return SIDESTEP_UNSUPPORTED_INSTRUCTION;
- }
- preamble_bytes += cur_bytes;
- }
+ // Stub is now useless so delete it.
+ // [csilvers: Commented out for perftools because it causes big problems
+ // when we're unpatching malloc. We just let this live on as a leak.]
+ //delete [] reinterpret_cast<unsigned char*>(original_function_stub);
- FreePreambleBlock(reinterpret_cast<unsigned char*>(original_function_stub));
-
- // Restore the protection of the first kRequiredTargetPatchBytes bytes of
+ // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of
// target to what they were before we started goofing around.
succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
- kRequiredTargetPatchBytes,
+ MAX_PREAMBLE_STUB_SIZE,
old_target_function_protect,
&old_target_function_protect);
@@ -411,274 +341,4 @@
return SIDESTEP_SUCCESS;
}
-void PreamblePatcher::Initialize() {
- if (!initialized_) {
- SYSTEM_INFO si = { 0 };
- ::GetSystemInfo(&si);
- granularity_ = si.dwAllocationGranularity;
- pagesize_ = si.dwPageSize;
- initialized_ = true;
- }
-}
-
-unsigned char* PreamblePatcher::AllocPreambleBlockNear(void* target) {
- PreamblePage* preamble_page = preamble_pages_;
- while (preamble_page != NULL) {
- if (preamble_page->free_ != NULL) {
- __int64 val = reinterpret_cast<__int64>(preamble_page) -
- reinterpret_cast<__int64>(target);
- if ((val > 0 && val + pagesize_ <= INT_MAX) ||
- (val < 0 && val >= INT_MIN)) {
- break;
- }
- }
- preamble_page = preamble_page->next_;
- }
-
- // The free_ member of the page is used to store the next available block
- // of memory to use or NULL if there are no chunks available, in which case
- // we'll allocate a new page.
- if (preamble_page == NULL || preamble_page->free_ == NULL) {
- // Create a new preamble page and initialize the free list
- preamble_page = reinterpret_cast<PreamblePage*>(AllocPageNear(target));
- SIDESTEP_ASSERT(preamble_page != NULL && "Could not allocate page!");
- void** pp = &preamble_page->free_;
- unsigned char* ptr = reinterpret_cast<unsigned char*>(preamble_page) +
- MAX_PREAMBLE_STUB_SIZE;
- unsigned char* limit = reinterpret_cast<unsigned char*>(preamble_page) +
- pagesize_;
- while (ptr < limit) {
- *pp = ptr;
- pp = reinterpret_cast<void**>(ptr);
- ptr += MAX_PREAMBLE_STUB_SIZE;
- }
- *pp = NULL;
- // Insert the new page into the list
- preamble_page->magic_ = kPreamblePageMagic;
- preamble_page->next_ = preamble_pages_;
- preamble_pages_ = preamble_page;
- }
- unsigned char* ret = reinterpret_cast<unsigned char*>(preamble_page->free_);
- preamble_page->free_ = *(reinterpret_cast<void**>(preamble_page->free_));
- return ret;
-}
-
-void PreamblePatcher::FreePreambleBlock(unsigned char* block) {
- SIDESTEP_ASSERT(block != NULL);
- SIDESTEP_ASSERT(granularity_ != 0);
- uintptr_t ptr = reinterpret_cast<uintptr_t>(block);
- ptr -= ptr & (granularity_ - 1);
- PreamblePage* preamble_page = reinterpret_cast<PreamblePage*>(ptr);
- SIDESTEP_ASSERT(preamble_page->magic_ == kPreamblePageMagic);
- *(reinterpret_cast<void**>(block)) = preamble_page->free_;
- preamble_page->free_ = block;
-}
-
-void* PreamblePatcher::AllocPageNear(void* target) {
- MEMORY_BASIC_INFORMATION mbi = { 0 };
- if (!::VirtualQuery(target, &mbi, sizeof(mbi))) {
- SIDESTEP_ASSERT(false && "VirtualQuery failed on target address");
- return 0;
- }
- if (initialized_ == false) {
- PreamblePatcher::Initialize();
- SIDESTEP_ASSERT(initialized_);
- }
- void* pv = NULL;
- unsigned char* allocation_base = reinterpret_cast<unsigned char*>(
- mbi.AllocationBase);
- __int64 i = 1;
- bool high_target = reinterpret_cast<__int64>(target) > UINT_MAX;
- while (pv == NULL) {
- __int64 val = reinterpret_cast<__int64>(allocation_base) -
- (i * granularity_);
- if (high_target &&
- reinterpret_cast<__int64>(target) - val > INT_MAX) {
- // We're further than 2GB from the target
- break;
- } else if (val <= NULL) {
- // Less than 0
- break;
- }
- pv = ::VirtualAlloc(reinterpret_cast<void*>(allocation_base -
- (i++ * granularity_)),
- pagesize_, MEM_COMMIT | MEM_RESERVE,
- PAGE_EXECUTE_READWRITE);
- }
-
- // We couldn't allocate low, try to allocate high
- if (pv == NULL) {
- i = 1;
- // Round up to the next multiple of page granularity
- allocation_base = reinterpret_cast<unsigned char*>(
- (reinterpret_cast<__int64>(target) &
- (~(granularity_ - 1))) + granularity_);
- while (pv == NULL) {
- __int64 val = reinterpret_cast<__int64>(allocation_base) +
- (i * granularity_) - reinterpret_cast<__int64>(target);
- if (val > INT_MAX || val < 0) {
- // We're too far or we overflowed
- break;
- }
- pv = ::VirtualAlloc(reinterpret_cast<void*>(allocation_base +
- (i++ * granularity_)),
- pagesize_, MEM_COMMIT | MEM_RESERVE,
- PAGE_EXECUTE_READWRITE);
- }
- }
- return pv;
-}
-
-bool PreamblePatcher::IsShortConditionalJump(
- unsigned char* target,
- unsigned int instruction_size) {
- return (*(target) & 0x70) == 0x70 && instruction_size == 2;
-}
-
-bool PreamblePatcher::IsNearConditionalJump(
- unsigned char* target,
- unsigned int instruction_size) {
- return *(target) == 0xf && (*(target + 1) & 0x80) == 0x80 &&
- instruction_size == 6;
-}
-
-bool PreamblePatcher::IsNearRelativeJump(
- unsigned char* target,
- unsigned int instruction_size) {
- return *(target) == 0xe9 && instruction_size == 5;
-}
-
-bool PreamblePatcher::IsNearAbsoluteCall(
- unsigned char* target,
- unsigned int instruction_size) {
- return *(target) == 0xff && (*(target + 1) & 0x10) == 0x10 &&
- instruction_size == 6;
-}
-
-bool PreamblePatcher::IsNearRelativeCall(
- unsigned char* target,
- unsigned int instruction_size) {
- return *(target) == 0xe8 && instruction_size == 5;
-}
-
-bool PreamblePatcher::IsMovWithDisplacement(
- unsigned char* target,
- unsigned int instruction_size) {
- // In this case, the ModRM byte's mod field will be 0 and r/m will be 101b (5)
- return instruction_size == 7 && *target == 0x48 && *(target + 1) == 0x8b &&
- (*(target + 2) >> 6) == 0 && (*(target + 2) & 0x7) == 5;
-}
-
-SideStepError PreamblePatcher::PatchShortConditionalJump(
- unsigned char* source,
- unsigned int instruction_size,
- unsigned char* target,
- unsigned int* target_bytes,
- unsigned int target_size) {
- unsigned char* original_jump_dest = (source + 2) + source[1];
- unsigned char* stub_jump_from = target + 6;
- __int64 fixup_jump_offset = original_jump_dest - stub_jump_from;
- if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) {
- SIDESTEP_ASSERT(false &&
- "Unable to fix up short jump because target"
- " is too far away.");
- return SIDESTEP_JUMP_INSTRUCTION;
- }
-
- *target_bytes = 6;
- if (target_size > *target_bytes) {
- // Convert the short jump to a near jump.
- //
- // 0f 8x xx xx xx xx = Jcc rel32off
- unsigned short jmpcode = ((0x80 | (source[0] & 0xf)) << 8) | 0x0f;
- memcpy(reinterpret_cast<void*>(target),
- reinterpret_cast<void*>(&jmpcode), 2);
- memcpy(reinterpret_cast<void*>(target + 2),
- reinterpret_cast<void*>(&fixup_jump_offset), 4);
- }
-
- return SIDESTEP_SUCCESS;
-}
-
-SideStepError PreamblePatcher::PatchNearJumpOrCall(
- unsigned char* source,
- unsigned int instruction_size,
- unsigned char* target,
- unsigned int* target_bytes,
- unsigned int target_size) {
- SIDESTEP_ASSERT(instruction_size == 5 || instruction_size == 6);
- unsigned int jmp_offset_in_instruction = instruction_size == 5 ? 1 : 2;
- unsigned char* original_jump_dest = reinterpret_cast<unsigned char *>(
- reinterpret_cast<__int64>(source + instruction_size) +
- *(reinterpret_cast<int*>(source + jmp_offset_in_instruction)));
- unsigned char* stub_jump_from = target + instruction_size;
- __int64 fixup_jump_offset = original_jump_dest - stub_jump_from;
- if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) {
- SIDESTEP_ASSERT(false &&
- "Unable to fix up near jump because target"
- " is too far away.");
- return SIDESTEP_JUMP_INSTRUCTION;
- }
-
- if ((fixup_jump_offset < SCHAR_MAX && fixup_jump_offset > SCHAR_MIN)) {
- *target_bytes = 2;
- if (target_size > *target_bytes) {
- // If the new offset is in range, use a short jump instead of a near jump.
- if (source[0] == ASM_JCC32REL_0 &&
- (source[1] & ASM_JCC32REL_1_MASK) == ASM_JCC32REL_1_MASK) {
- unsigned short jmpcode = (static_cast<unsigned char>(
- fixup_jump_offset) << 8) | (0x70 | (source[1] & 0xf));
- memcpy(reinterpret_cast<void*>(target),
- reinterpret_cast<void*>(&jmpcode),
- 2);
- } else {
- target[0] = ASM_JMP8REL;
- target[1] = static_cast<unsigned char>(fixup_jump_offset);
- }
- }
- } else {
- *target_bytes = instruction_size;
- if (target_size > *target_bytes) {
- memcpy(reinterpret_cast<void*>(target),
- reinterpret_cast<void*>(source),
- jmp_offset_in_instruction);
- memcpy(reinterpret_cast<void*>(target + jmp_offset_in_instruction),
- reinterpret_cast<void*>(&fixup_jump_offset),
- 4);
- }
- }
-
- return SIDESTEP_SUCCESS;
-}
-
-SideStepError PreamblePatcher::PatchMovWithDisplacement(
- unsigned char* source,
- unsigned int instruction_size,
- unsigned char* target,
- unsigned int* target_bytes,
- unsigned int target_size) {
- SIDESTEP_ASSERT(instruction_size == 7);
- const int mov_offset_in_instruction = 3; // 0x48 0x8b 0x0d <offset>
- unsigned char* original_mov_dest = reinterpret_cast<unsigned char*>(
- reinterpret_cast<__int64>(source + instruction_size) +
- *(reinterpret_cast<int*>(source + mov_offset_in_instruction)));
- unsigned char* stub_mov_from = target + instruction_size;
- __int64 fixup_mov_offset = original_mov_dest - stub_mov_from;
- if (fixup_mov_offset > INT_MAX || fixup_mov_offset < INT_MIN) {
- SIDESTEP_ASSERT(false &&
- "Unable to fix up near MOV because target is too far away.");
- return SIDESTEP_UNEXPECTED;
- }
- *target_bytes = instruction_size;
- if (target_size > *target_bytes) {
- memcpy(reinterpret_cast<void*>(target),
- reinterpret_cast<void*>(source),
- mov_offset_in_instruction);
- memcpy(reinterpret_cast<void*>(target + mov_offset_in_instruction),
- reinterpret_cast<void*>(&fixup_mov_offset),
- 4);
- }
- return SIDESTEP_SUCCESS;
-}
-
}; // namespace sidestep

Powered by Google App Engine
This is Rietveld 408576698