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

Side by Side Diff: third_party/tcmalloc/chromium/src/windows/preamble_patcher_with_stub.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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* Copyright (c) 2007, Google Inc. 1 /* Copyright (c) 2007, Google Inc.
2 * All rights reserved. 2 * All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 11 matching lines...) Expand all
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * 29 *
30 * --- 30 * ---
31 * Author: Joi Sigurdsson 31 * Author: Joi Sigurdsson
32 * Author: Scott Francis
33 * 32 *
34 * Implementation of PreamblePatcher 33 * Implementation of PreamblePatcher
35 */ 34 */
36 35
37 #include "preamble_patcher.h" 36 #include "preamble_patcher.h"
38 37
39 #include "mini_disassembler.h" 38 #include "mini_disassembler.h"
40 39
41 // Definitions of assembly statements we need 40 // Definitions of assembly statements we need
42 #define ASM_JMP32REL 0xE9 41 #define ASM_JMP32REL 0xE9
43 #define ASM_INT3 0xCC 42 #define ASM_INT3 0xCC
44 #define ASM_NOP 0x90
45 // X64 opcodes
46 #define ASM_MOVRAX_IMM 0xB8
47 #define ASM_REXW 0x48
48 #define ASM_JMP 0xFF
49 #define ASM_JMP_RAX 0xE0
50 #define ASM_PUSH 0x68
51 #define ASM_RET 0xC3
52 43
53 namespace sidestep { 44 namespace sidestep {
54 45
55 SideStepError PreamblePatcher::RawPatchWithStub( 46 SideStepError PreamblePatcher::RawPatchWithStub(
56 void* target_function, 47 void* target_function,
57 void* replacement_function, 48 void *replacement_function,
58 unsigned char* preamble_stub, 49 unsigned char* preamble_stub,
59 unsigned long stub_size, 50 unsigned long stub_size,
60 unsigned long* bytes_needed) { 51 unsigned long* bytes_needed) {
61 if ((NULL == target_function) || 52 if ((NULL == target_function) ||
62 (NULL == replacement_function) || 53 (NULL == replacement_function) ||
63 (NULL == preamble_stub)) { 54 (NULL == preamble_stub)) {
64 SIDESTEP_ASSERT(false && 55 SIDESTEP_ASSERT(false &&
65 "Invalid parameters - either pTargetFunction or " 56 "Invalid parameters - either pTargetFunction or "
66 "pReplacementFunction or pPreambleStub were NULL."); 57 "pReplacementFunction or pPreambleStub were NULL.");
67 return SIDESTEP_INVALID_PARAMETER; 58 return SIDESTEP_INVALID_PARAMETER;
68 } 59 }
69 60
70 // TODO(V7:joi) Siggi and I just had a discussion and decided that both 61 // TODO(V7:joi) Siggi and I just had a discussion and decided that both
71 // patching and unpatching are actually unsafe. We also discussed a 62 // patching and unpatching are actually unsafe. We also discussed a
72 // method of making it safe, which is to freeze all other threads in the 63 // method of making it safe, which is to freeze all other threads in the
73 // process, check their thread context to see if their eip is currently 64 // process, check their thread context to see if their eip is currently
74 // inside the block of instructions we need to copy to the stub, and if so 65 // inside the block of instructions we need to copy to the stub, and if so
75 // wait a bit and try again, then unfreeze all threads once we've patched. 66 // wait a bit and try again, then unfreeze all threads once we've patched.
76 // Not implementing this for now since we're only using SideStep for unit 67 // Not implementing this for now since we're only using SideStep for unit
77 // testing, but if we ever use it for production code this is what we 68 // testing, but if we ever use it for production code this is what we
78 // should do. 69 // should do.
79 // 70 //
80 // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using 71 // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using
81 // FPU instructions, and on newer processors we could use cmpxchg8b or 72 // FPU instructions, and on newer processors we could use cmpxchg8b or
82 // cmpxchg16b. So it might be possible to do the patching/unpatching 73 // cmpxchg16b. So it might be possible to do the patching/unpatching
83 // atomically and avoid having to freeze other threads. Note though, that 74 // atomically and avoid having to freeze other threads. Note though, that
84 // doing it atomically does not help if one of the other threads happens 75 // doing it atomically does not help if one of the other threads happens
85 // to have its eip in the middle of the bytes you change while you change 76 // to have its eip in the middle of the bytes you change while you change
86 // them. 77 // them.
87 unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
88 unsigned int required_trampoline_bytes = 0;
89 const unsigned int kRequiredStubJumpBytes = 5;
90 const unsigned int kRequiredTargetPatchBytes = 5;
91 78
92 // Initialize the stub with INT3's just in case. 79 // First, deal with a special case that we see with functions that
93 if (stub_size) { 80 // point into an IAT table (including functions linked statically
94 memset(preamble_stub, 0xcc, stub_size); 81 // into the application): these function already starts with
82 // ASM_JMP32REL. For instance, malloc() might be implemented as a
83 // JMP to __malloc(). In that case, we replace the destination of
84 // the JMP (__malloc), rather than the JMP itself (malloc). This
85 // way we get the correct behavior no matter how malloc gets called.
86 void *new_target = ResolveTarget(target_function);
87 if (new_target != target_function) { // we're in the IAT case
88 // I'd like to just say "target = new_target", but I can't,
89 // because the new target will need to have its protections set.
90 return RawPatchWithStubAndProtections(new_target, replacement_function,
91 preamble_stub, stub_size,
92 bytes_needed);
95 } 93 }
96 if (kIs64BitBinary) { 94 unsigned char* target = reinterpret_cast<unsigned char*>(new_target);
97 // In 64-bit mode JMP instructions are always relative to RIP. If the
98 // replacement - target offset is > 2GB, we can't JMP to the replacement
99 // function. In this case, we're going to use a trampoline - that is,
100 // we're going to do a relative jump to a small chunk of code in the stub
101 // that will then do the absolute jump to the replacement function. By
102 // doing this, we only need to patch 5 bytes in the target function, as
103 // opposed to patching 12 bytes if we were to do an absolute jump.
104 //
105 // Note that the first byte of the trampoline is a NOP instruction. This
106 // is used as a trampoline signature that will be detected when unpatching
107 // the function.
108 //
109 // jmp <trampoline>
110 //
111 // trampoline:
112 // nop
113 // mov rax, <replacement_function>
114 // jmp rax
115 //
116 __int64 replacement_target_offset = reinterpret_cast<__int64>(
117 replacement_function) - reinterpret_cast<__int64>(target) - 5;
118 if (replacement_target_offset > INT_MAX
119 || replacement_target_offset < INT_MIN) {
120 // The stub needs to be within 2GB of the target for the trampoline to
121 // work!
122 __int64 trampoline_offset = reinterpret_cast<__int64>(preamble_stub)
123 - reinterpret_cast<__int64>(target) - 5;
124 if (trampoline_offset > INT_MAX || trampoline_offset < INT_MIN) {
125 // We're screwed.
126 SIDESTEP_ASSERT(false
127 && "Preamble stub is too far from target to patch.");
128 return SIDESTEP_UNEXPECTED;
129 }
130 required_trampoline_bytes = 13;
131 }
132 }
133 95
134 // Let's disassemble the preamble of the target function to see if we can 96 // Let's disassemble the preamble of the target function to see if we can
135 // patch, and to see how much of the preamble we need to take. We need 5 97 // patch, and to see how much of the preamble we need to take. We need 5
136 // bytes for our jmp instruction, so let's find the minimum number of 98 // bytes for our jmp instruction, so let's find the minimum number of
137 // instructions to get 5 bytes. 99 // instructions to get 5 bytes.
138 MiniDisassembler disassembler; 100 MiniDisassembler disassembler;
139 unsigned int preamble_bytes = 0; 101 unsigned int preamble_bytes = 0;
140 unsigned int stub_bytes = 0; 102 while (preamble_bytes < 5) {
141 while (preamble_bytes < kRequiredTargetPatchBytes) {
142 unsigned int cur_bytes = 0;
143 InstructionType instruction_type = 103 InstructionType instruction_type =
144 disassembler.Disassemble(target + preamble_bytes, cur_bytes); 104 disassembler.Disassemble(target + preamble_bytes, preamble_bytes);
145 if (IT_JUMP == instruction_type) { 105 if (IT_JUMP == instruction_type) {
146 unsigned int jump_bytes = 0; 106 SIDESTEP_ASSERT(false &&
147 SideStepError jump_ret = SIDESTEP_JUMP_INSTRUCTION; 107 "Unable to patch because there is a jump instruction "
148 if (IsShortConditionalJump(target + preamble_bytes, cur_bytes)) { 108 "in the first 5 bytes.");
149 jump_ret = PatchShortConditionalJump(target + preamble_bytes, cur_bytes, 109 return SIDESTEP_JUMP_INSTRUCTION;
150 preamble_stub + stub_bytes,
151 &jump_bytes,
152 stub_size - stub_bytes);
153 } else if (IsNearConditionalJump(target + preamble_bytes, cur_bytes) ||
154 IsNearRelativeJump(target + preamble_bytes, cur_bytes) ||
155 IsNearAbsoluteCall(target + preamble_bytes, cur_bytes) ||
156 IsNearRelativeCall(target + preamble_bytes, cur_bytes)) {
157 jump_ret = PatchNearJumpOrCall(target + preamble_bytes, cur_bytes,
158 preamble_stub + stub_bytes, &jump_bytes,
159 stub_size - stub_bytes);
160 }
161 if (jump_ret != SIDESTEP_SUCCESS) {
162 SIDESTEP_ASSERT(false &&
163 "Unable to patch because there is an unhandled branch "
164 "instruction in the initial preamble bytes.");
165 return SIDESTEP_JUMP_INSTRUCTION;
166 }
167 stub_bytes += jump_bytes;
168 } else if (IT_RETURN == instruction_type) { 110 } else if (IT_RETURN == instruction_type) {
169 SIDESTEP_ASSERT(false && 111 SIDESTEP_ASSERT(false &&
170 "Unable to patch because function is too short"); 112 "Unable to patch because function is too short");
171 return SIDESTEP_FUNCTION_TOO_SMALL; 113 return SIDESTEP_FUNCTION_TOO_SMALL;
172 } else if (IT_GENERIC == instruction_type) { 114 } else if (IT_GENERIC != instruction_type) {
173 if (IsMovWithDisplacement(target + preamble_bytes, cur_bytes)) {
174 unsigned int mov_bytes = 0;
175 if (PatchMovWithDisplacement(target + preamble_bytes, cur_bytes,
176 preamble_stub + stub_bytes, &mov_bytes,
177 stub_size - stub_bytes)
178 != SIDESTEP_SUCCESS) {
179 return SIDESTEP_UNSUPPORTED_INSTRUCTION;
180 }
181 stub_bytes += mov_bytes;
182 } else {
183 memcpy(reinterpret_cast<void*>(preamble_stub + stub_bytes),
184 reinterpret_cast<void*>(target + preamble_bytes), cur_bytes);
185 stub_bytes += cur_bytes;
186 }
187 } else {
188 SIDESTEP_ASSERT(false && 115 SIDESTEP_ASSERT(false &&
189 "Disassembler encountered unsupported instruction " 116 "Disassembler encountered unsupported instruction "
190 "(either unused or unknown"); 117 "(either unused or unknown");
191 return SIDESTEP_UNSUPPORTED_INSTRUCTION; 118 return SIDESTEP_UNSUPPORTED_INSTRUCTION;
192 } 119 }
193 preamble_bytes += cur_bytes;
194 } 120 }
195 121
196 if (NULL != bytes_needed) 122 if (NULL != bytes_needed)
197 *bytes_needed = stub_bytes + kRequiredStubJumpBytes 123 *bytes_needed = preamble_bytes + 5;
198 + required_trampoline_bytes;
199 124
200 // Inv: cbPreamble is the number of bytes (at least 5) that we need to take 125 // Inv: cbPreamble is the number of bytes (at least 5) that we need to take
201 // from the preamble to have whole instructions that are 5 bytes or more 126 // from the preamble to have whole instructions that are 5 bytes or more
202 // in size total. The size of the stub required is cbPreamble + 127 // in size total. The size of the stub required is cbPreamble + size of
203 // kRequiredStubJumpBytes (5) + required_trampoline_bytes (0 or 13) 128 // jmp (5)
204 if (stub_bytes + kRequiredStubJumpBytes + required_trampoline_bytes 129 if (preamble_bytes + 5 > stub_size) {
205 > stub_size) {
206 SIDESTEP_ASSERT(false); 130 SIDESTEP_ASSERT(false);
207 return SIDESTEP_INSUFFICIENT_BUFFER; 131 return SIDESTEP_INSUFFICIENT_BUFFER;
208 } 132 }
209 133
134 // First, copy the preamble that we will overwrite.
135 memcpy(reinterpret_cast<void*>(preamble_stub),
136 reinterpret_cast<void*>(target), preamble_bytes);
137
210 // Now, make a jmp instruction to the rest of the target function (minus the 138 // Now, make a jmp instruction to the rest of the target function (minus the
211 // preamble bytes we moved into the stub) and copy it into our preamble-stub. 139 // preamble bytes we moved into the stub) and copy it into our preamble-stub.
212 // find address to jump to, relative to next address after jmp instruction 140 // find address to jump to, relative to next address after jmp instruction
213 #ifdef _MSC_VER 141 #ifdef _MSC_VER
214 #pragma warning(push) 142 #pragma warning(push)
215 #pragma warning(disable:4244) 143 #pragma warning(disable:4244)
216 #endif 144 #endif
217 int relative_offset_to_target_rest 145 int relative_offset_to_target_rest
218 = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) - 146 = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
219 (preamble_stub + stub_bytes + kRequiredStubJumpBytes)); 147 (preamble_stub + preamble_bytes + 5));
220 #ifdef _MSC_VER 148 #ifdef _MSC_VER
221 #pragma warning(pop) 149 #pragma warning(pop)
222 #endif 150 #endif
223 // jmp (Jump near, relative, displacement relative to next instruction) 151 // jmp (Jump near, relative, displacement relative to next instruction)
224 preamble_stub[stub_bytes] = ASM_JMP32REL; 152 preamble_stub[preamble_bytes] = ASM_JMP32REL;
225 // copy the address 153 // copy the address
226 memcpy(reinterpret_cast<void*>(preamble_stub + stub_bytes + 1), 154 memcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1),
227 reinterpret_cast<void*>(&relative_offset_to_target_rest), 4); 155 reinterpret_cast<void*>(&relative_offset_to_target_rest), 4);
228 156
229 if (kIs64BitBinary && required_trampoline_bytes != 0) {
230 // Construct the trampoline
231 unsigned int trampoline_pos = stub_bytes + kRequiredStubJumpBytes;
232 preamble_stub[trampoline_pos] = ASM_NOP;
233 preamble_stub[trampoline_pos + 1] = ASM_REXW;
234 preamble_stub[trampoline_pos + 2] = ASM_MOVRAX_IMM;
235 memcpy(reinterpret_cast<void*>(preamble_stub + trampoline_pos + 3),
236 reinterpret_cast<void*>(&replacement_function),
237 sizeof(void *));
238 preamble_stub[trampoline_pos + 11] = ASM_JMP;
239 preamble_stub[trampoline_pos + 12] = ASM_JMP_RAX;
240
241 // Now update replacement_function to point to the trampoline
242 replacement_function = preamble_stub + trampoline_pos;
243 }
244
245 // Inv: preamble_stub points to assembly code that will execute the 157 // Inv: preamble_stub points to assembly code that will execute the
246 // original function by first executing the first cbPreamble bytes of the 158 // original function by first executing the first cbPreamble bytes of the
247 // preamble, then jumping to the rest of the function. 159 // preamble, then jumping to the rest of the function.
248 160
249 // Overwrite the first 5 bytes of the target function with a jump to our 161 // Overwrite the first 5 bytes of the target function with a jump to our
250 // replacement function. 162 // replacement function.
251 // (Jump near, relative, displacement relative to next instruction) 163 // (Jump near, relative, displacement relative to next instruction)
252 target[0] = ASM_JMP32REL; 164 target[0] = ASM_JMP32REL;
253 165
254 // Find offset from instruction after jmp, to the replacement function. 166 // Find offset from instruction after jmp, to the replacement function.
255 #ifdef _MSC_VER 167 #ifdef _MSC_VER
256 #pragma warning(push) 168 #pragma warning(push)
257 #pragma warning(disable:4244) 169 #pragma warning(disable:4244)
258 #endif 170 #endif
259 int offset_to_replacement_function = 171 int offset_to_replacement_function =
260 reinterpret_cast<unsigned char*>(replacement_function) - 172 reinterpret_cast<unsigned char*>(replacement_function) -
261 reinterpret_cast<unsigned char*>(target) - 5; 173 reinterpret_cast<unsigned char*>(target) - 5;
262 #ifdef _MSC_VER 174 #ifdef _MSC_VER
263 #pragma warning(pop) 175 #pragma warning(pop)
264 #endif 176 #endif
265 // complete the jmp instruction 177 // complete the jmp instruction
266 memcpy(reinterpret_cast<void*>(target + 1), 178 memcpy(reinterpret_cast<void*>(target + 1),
267 reinterpret_cast<void*>(&offset_to_replacement_function), 4); 179 reinterpret_cast<void*>(&offset_to_replacement_function), 4);
268
269 // Set any remaining bytes that were moved to the preamble-stub to INT3 so 180 // Set any remaining bytes that were moved to the preamble-stub to INT3 so
270 // as not to cause confusion (otherwise you might see some strange 181 // as not to cause confusion (otherwise you might see some strange
271 // instructions if you look at the disassembly, or even invalid 182 // instructions if you look at the disassembly, or even invalid
272 // instructions). Also, by doing this, we will break into the debugger if 183 // instructions). Also, by doing this, we will break into the debugger if
273 // some code calls into this portion of the code. If this happens, it 184 // some code calls into this portion of the code. If this happens, it
274 // means that this function cannot be patched using this patcher without 185 // means that this function cannot be patched using this patcher without
275 // further thought. 186 // further thought.
276 if (preamble_bytes > kRequiredTargetPatchBytes) { 187 if (preamble_bytes > 5) {
277 memset(reinterpret_cast<void*>(target + kRequiredTargetPatchBytes), 188 memset(reinterpret_cast<void*>(target + 5), ASM_INT3, preamble_bytes - 5);
278 ASM_INT3, preamble_bytes - kRequiredTargetPatchBytes);
279 } 189 }
280 190
281 // Inv: The memory pointed to by target_function now points to a relative 191 // Inv: The memory pointed to by target_function now points to a relative
282 // jump instruction that jumps over to the preamble_stub. The preamble 192 // jump instruction that jumps over to the preamble_stub. The preamble
283 // stub contains the first stub_size bytes of the original target 193 // stub contains the first stub_size bytes of the original target
284 // function's preamble code, followed by a relative jump back to the next 194 // function's preamble code, followed by a relative jump back to the next
285 // instruction after the first cbPreamble bytes. 195 // instruction after the first cbPreamble bytes.
286 // 196
287 // In 64-bit mode the memory pointed to by target_function *may* point to a
288 // relative jump instruction that jumps to a trampoline which will then
289 // perform an absolute jump to the replacement function. The preamble stub
290 // still contains the original target function's preamble code, followed by a
291 // jump back to the instructions after the first preamble bytes.
292 //
293 return SIDESTEP_SUCCESS; 197 return SIDESTEP_SUCCESS;
294 } 198 }
295 199
296 }; // namespace sidestep 200 }; // namespace sidestep
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698