1 | //===-- x86AssemblyInspectionEngine.cpp -----------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "x86AssemblyInspectionEngine.h" |
10 | |
11 | #include <memory> |
12 | |
13 | #include "llvm-c/Disassembler.h" |
14 | |
15 | #include "lldb/Core/Address.h" |
16 | #include "lldb/Symbol/UnwindPlan.h" |
17 | #include "lldb/Target/RegisterContext.h" |
18 | #include "lldb/Target/UnwindAssembly.h" |
19 | |
20 | using namespace lldb_private; |
21 | using namespace lldb; |
22 | |
23 | x86AssemblyInspectionEngine::x86AssemblyInspectionEngine(const ArchSpec &arch) |
24 | : m_cur_insn(nullptr), m_machine_ip_regnum(LLDB_INVALID_REGNUM), |
25 | m_machine_sp_regnum(LLDB_INVALID_REGNUM), |
26 | m_machine_fp_regnum(LLDB_INVALID_REGNUM), |
27 | m_machine_alt_fp_regnum(LLDB_INVALID_REGNUM), |
28 | m_lldb_ip_regnum(LLDB_INVALID_REGNUM), |
29 | m_lldb_sp_regnum(LLDB_INVALID_REGNUM), |
30 | m_lldb_fp_regnum(LLDB_INVALID_REGNUM), |
31 | m_lldb_alt_fp_regnum(LLDB_INVALID_REGNUM), m_reg_map(), m_arch(arch), |
32 | m_cpu(k_cpu_unspecified), m_wordsize(-1), |
33 | m_register_map_initialized(false), m_disasm_context() { |
34 | m_disasm_context = |
35 | ::LLVMCreateDisasm(TripleName: arch.GetTriple().getTriple().c_str(), DisInfo: nullptr, |
36 | /*TagType=*/1, GetOpInfo: nullptr, SymbolLookUp: nullptr); |
37 | } |
38 | |
39 | x86AssemblyInspectionEngine::~x86AssemblyInspectionEngine() { |
40 | ::LLVMDisasmDispose(DC: m_disasm_context); |
41 | } |
42 | |
43 | void x86AssemblyInspectionEngine::Initialize(RegisterContextSP ®_ctx) { |
44 | m_cpu = k_cpu_unspecified; |
45 | m_wordsize = -1; |
46 | m_register_map_initialized = false; |
47 | |
48 | const llvm::Triple::ArchType cpu = m_arch.GetMachine(); |
49 | if (cpu == llvm::Triple::x86) |
50 | m_cpu = k_i386; |
51 | else if (cpu == llvm::Triple::x86_64) |
52 | m_cpu = k_x86_64; |
53 | |
54 | if (m_cpu == k_cpu_unspecified) |
55 | return; |
56 | |
57 | if (reg_ctx.get() == nullptr) |
58 | return; |
59 | |
60 | if (m_cpu == k_i386) { |
61 | m_machine_ip_regnum = k_machine_eip; |
62 | m_machine_sp_regnum = k_machine_esp; |
63 | m_machine_fp_regnum = k_machine_ebp; |
64 | m_machine_alt_fp_regnum = k_machine_ebx; |
65 | m_wordsize = 4; |
66 | |
67 | struct lldb_reg_info reginfo; |
68 | reginfo.name = "eax" ; |
69 | m_reg_map[k_machine_eax] = reginfo; |
70 | reginfo.name = "edx" ; |
71 | m_reg_map[k_machine_edx] = reginfo; |
72 | reginfo.name = "esp" ; |
73 | m_reg_map[k_machine_esp] = reginfo; |
74 | reginfo.name = "esi" ; |
75 | m_reg_map[k_machine_esi] = reginfo; |
76 | reginfo.name = "eip" ; |
77 | m_reg_map[k_machine_eip] = reginfo; |
78 | reginfo.name = "ecx" ; |
79 | m_reg_map[k_machine_ecx] = reginfo; |
80 | reginfo.name = "ebx" ; |
81 | m_reg_map[k_machine_ebx] = reginfo; |
82 | reginfo.name = "ebp" ; |
83 | m_reg_map[k_machine_ebp] = reginfo; |
84 | reginfo.name = "edi" ; |
85 | m_reg_map[k_machine_edi] = reginfo; |
86 | } else { |
87 | m_machine_ip_regnum = k_machine_rip; |
88 | m_machine_sp_regnum = k_machine_rsp; |
89 | m_machine_fp_regnum = k_machine_rbp; |
90 | m_machine_alt_fp_regnum = k_machine_rbx; |
91 | m_wordsize = 8; |
92 | |
93 | struct lldb_reg_info reginfo; |
94 | reginfo.name = "rax" ; |
95 | m_reg_map[k_machine_rax] = reginfo; |
96 | reginfo.name = "rdx" ; |
97 | m_reg_map[k_machine_rdx] = reginfo; |
98 | reginfo.name = "rsp" ; |
99 | m_reg_map[k_machine_rsp] = reginfo; |
100 | reginfo.name = "rsi" ; |
101 | m_reg_map[k_machine_rsi] = reginfo; |
102 | reginfo.name = "r8" ; |
103 | m_reg_map[k_machine_r8] = reginfo; |
104 | reginfo.name = "r10" ; |
105 | m_reg_map[k_machine_r10] = reginfo; |
106 | reginfo.name = "r12" ; |
107 | m_reg_map[k_machine_r12] = reginfo; |
108 | reginfo.name = "r14" ; |
109 | m_reg_map[k_machine_r14] = reginfo; |
110 | reginfo.name = "rip" ; |
111 | m_reg_map[k_machine_rip] = reginfo; |
112 | reginfo.name = "rcx" ; |
113 | m_reg_map[k_machine_rcx] = reginfo; |
114 | reginfo.name = "rbx" ; |
115 | m_reg_map[k_machine_rbx] = reginfo; |
116 | reginfo.name = "rbp" ; |
117 | m_reg_map[k_machine_rbp] = reginfo; |
118 | reginfo.name = "rdi" ; |
119 | m_reg_map[k_machine_rdi] = reginfo; |
120 | reginfo.name = "r9" ; |
121 | m_reg_map[k_machine_r9] = reginfo; |
122 | reginfo.name = "r11" ; |
123 | m_reg_map[k_machine_r11] = reginfo; |
124 | reginfo.name = "r13" ; |
125 | m_reg_map[k_machine_r13] = reginfo; |
126 | reginfo.name = "r15" ; |
127 | m_reg_map[k_machine_r15] = reginfo; |
128 | } |
129 | |
130 | for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin(); |
131 | it != m_reg_map.end(); ++it) { |
132 | const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName(reg_name: it->second.name); |
133 | if (ri) |
134 | it->second.lldb_regnum = ri->kinds[eRegisterKindLLDB]; |
135 | } |
136 | |
137 | uint32_t lldb_regno; |
138 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_sp_regnum, lldb_regno)) |
139 | m_lldb_sp_regnum = lldb_regno; |
140 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_fp_regnum, lldb_regno)) |
141 | m_lldb_fp_regnum = lldb_regno; |
142 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_alt_fp_regnum, lldb_regno)) |
143 | m_lldb_alt_fp_regnum = lldb_regno; |
144 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_ip_regnum, lldb_regno)) |
145 | m_lldb_ip_regnum = lldb_regno; |
146 | |
147 | m_register_map_initialized = true; |
148 | } |
149 | |
150 | void x86AssemblyInspectionEngine::Initialize( |
151 | std::vector<lldb_reg_info> ®_info) { |
152 | m_cpu = k_cpu_unspecified; |
153 | m_wordsize = -1; |
154 | m_register_map_initialized = false; |
155 | |
156 | const llvm::Triple::ArchType cpu = m_arch.GetMachine(); |
157 | if (cpu == llvm::Triple::x86) |
158 | m_cpu = k_i386; |
159 | else if (cpu == llvm::Triple::x86_64) |
160 | m_cpu = k_x86_64; |
161 | |
162 | if (m_cpu == k_cpu_unspecified) |
163 | return; |
164 | |
165 | if (m_cpu == k_i386) { |
166 | m_machine_ip_regnum = k_machine_eip; |
167 | m_machine_sp_regnum = k_machine_esp; |
168 | m_machine_fp_regnum = k_machine_ebp; |
169 | m_machine_alt_fp_regnum = k_machine_ebx; |
170 | m_wordsize = 4; |
171 | |
172 | struct lldb_reg_info reginfo; |
173 | reginfo.name = "eax" ; |
174 | m_reg_map[k_machine_eax] = reginfo; |
175 | reginfo.name = "edx" ; |
176 | m_reg_map[k_machine_edx] = reginfo; |
177 | reginfo.name = "esp" ; |
178 | m_reg_map[k_machine_esp] = reginfo; |
179 | reginfo.name = "esi" ; |
180 | m_reg_map[k_machine_esi] = reginfo; |
181 | reginfo.name = "eip" ; |
182 | m_reg_map[k_machine_eip] = reginfo; |
183 | reginfo.name = "ecx" ; |
184 | m_reg_map[k_machine_ecx] = reginfo; |
185 | reginfo.name = "ebx" ; |
186 | m_reg_map[k_machine_ebx] = reginfo; |
187 | reginfo.name = "ebp" ; |
188 | m_reg_map[k_machine_ebp] = reginfo; |
189 | reginfo.name = "edi" ; |
190 | m_reg_map[k_machine_edi] = reginfo; |
191 | } else { |
192 | m_machine_ip_regnum = k_machine_rip; |
193 | m_machine_sp_regnum = k_machine_rsp; |
194 | m_machine_fp_regnum = k_machine_rbp; |
195 | m_machine_alt_fp_regnum = k_machine_rbx; |
196 | m_wordsize = 8; |
197 | |
198 | struct lldb_reg_info reginfo; |
199 | reginfo.name = "rax" ; |
200 | m_reg_map[k_machine_rax] = reginfo; |
201 | reginfo.name = "rdx" ; |
202 | m_reg_map[k_machine_rdx] = reginfo; |
203 | reginfo.name = "rsp" ; |
204 | m_reg_map[k_machine_rsp] = reginfo; |
205 | reginfo.name = "rsi" ; |
206 | m_reg_map[k_machine_rsi] = reginfo; |
207 | reginfo.name = "r8" ; |
208 | m_reg_map[k_machine_r8] = reginfo; |
209 | reginfo.name = "r10" ; |
210 | m_reg_map[k_machine_r10] = reginfo; |
211 | reginfo.name = "r12" ; |
212 | m_reg_map[k_machine_r12] = reginfo; |
213 | reginfo.name = "r14" ; |
214 | m_reg_map[k_machine_r14] = reginfo; |
215 | reginfo.name = "rip" ; |
216 | m_reg_map[k_machine_rip] = reginfo; |
217 | reginfo.name = "rcx" ; |
218 | m_reg_map[k_machine_rcx] = reginfo; |
219 | reginfo.name = "rbx" ; |
220 | m_reg_map[k_machine_rbx] = reginfo; |
221 | reginfo.name = "rbp" ; |
222 | m_reg_map[k_machine_rbp] = reginfo; |
223 | reginfo.name = "rdi" ; |
224 | m_reg_map[k_machine_rdi] = reginfo; |
225 | reginfo.name = "r9" ; |
226 | m_reg_map[k_machine_r9] = reginfo; |
227 | reginfo.name = "r11" ; |
228 | m_reg_map[k_machine_r11] = reginfo; |
229 | reginfo.name = "r13" ; |
230 | m_reg_map[k_machine_r13] = reginfo; |
231 | reginfo.name = "r15" ; |
232 | m_reg_map[k_machine_r15] = reginfo; |
233 | } |
234 | |
235 | for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin(); |
236 | it != m_reg_map.end(); ++it) { |
237 | for (size_t i = 0; i < reg_info.size(); ++i) { |
238 | if (::strcmp(s1: reg_info[i].name, s2: it->second.name) == 0) { |
239 | it->second.lldb_regnum = reg_info[i].lldb_regnum; |
240 | break; |
241 | } |
242 | } |
243 | } |
244 | |
245 | uint32_t lldb_regno; |
246 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_sp_regnum, lldb_regno)) |
247 | m_lldb_sp_regnum = lldb_regno; |
248 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_fp_regnum, lldb_regno)) |
249 | m_lldb_fp_regnum = lldb_regno; |
250 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_alt_fp_regnum, lldb_regno)) |
251 | m_lldb_alt_fp_regnum = lldb_regno; |
252 | if (machine_regno_to_lldb_regno(machine_regno: m_machine_ip_regnum, lldb_regno)) |
253 | m_lldb_ip_regnum = lldb_regno; |
254 | |
255 | m_register_map_initialized = true; |
256 | } |
257 | |
258 | // This function expects an x86 native register number (i.e. the bits stripped |
259 | // out of the actual instruction), not an lldb register number. |
260 | // |
261 | // FIXME: This is ABI dependent, it shouldn't be hardcoded here. |
262 | |
263 | bool x86AssemblyInspectionEngine::nonvolatile_reg_p(int machine_regno) { |
264 | if (m_cpu == k_i386) { |
265 | switch (machine_regno) { |
266 | case k_machine_ebx: |
267 | case k_machine_ebp: // not actually a nonvolatile but often treated as such |
268 | // by convention |
269 | case k_machine_esi: |
270 | case k_machine_edi: |
271 | case k_machine_esp: |
272 | return true; |
273 | default: |
274 | return false; |
275 | } |
276 | } |
277 | if (m_cpu == k_x86_64) { |
278 | switch (machine_regno) { |
279 | case k_machine_rbx: |
280 | case k_machine_rsp: |
281 | case k_machine_rbp: // not actually a nonvolatile but often treated as such |
282 | // by convention |
283 | case k_machine_r12: |
284 | case k_machine_r13: |
285 | case k_machine_r14: |
286 | case k_machine_r15: |
287 | return true; |
288 | default: |
289 | return false; |
290 | } |
291 | } |
292 | return false; |
293 | } |
294 | |
295 | // Macro to detect if this is a REX mode prefix byte. |
296 | #define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48) |
297 | |
298 | // The high bit which should be added to the source register number (the "R" |
299 | // bit) |
300 | #define REX_W_SRCREG(opcode) (((opcode)&0x4) >> 2) |
301 | |
302 | // The high bit which should be added to the destination register number (the |
303 | // "B" bit) |
304 | #define REX_W_DSTREG(opcode) ((opcode)&0x1) |
305 | |
306 | // pushq %rbp [0x55] |
307 | bool x86AssemblyInspectionEngine::push_rbp_pattern_p() { |
308 | uint8_t *p = m_cur_insn; |
309 | return *p == 0x55; |
310 | } |
311 | |
312 | // pushq $0 ; the first instruction in start() [0x6a 0x00] |
313 | bool x86AssemblyInspectionEngine::push_0_pattern_p() { |
314 | uint8_t *p = m_cur_insn; |
315 | return *p == 0x6a && *(p + 1) == 0x0; |
316 | } |
317 | |
318 | // pushq $0 |
319 | // pushl $0 |
320 | bool x86AssemblyInspectionEngine::push_imm_pattern_p() { |
321 | uint8_t *p = m_cur_insn; |
322 | return *p == 0x68 || *p == 0x6a; |
323 | } |
324 | |
325 | // pushl imm8(%esp) |
326 | // |
327 | // e.g. 0xff 0x74 0x24 0x20 - 'pushl 0x20(%esp)' (same byte pattern for 'pushq |
328 | // 0x20(%rsp)' in an x86_64 program) |
329 | // |
330 | // 0xff (with opcode bits '6' in next byte, PUSH r/m32) 0x74 (ModR/M byte with |
331 | // three bits used to specify the opcode) |
332 | // mod == b01, opcode == b110, R/M == b100 |
333 | // "+disp8" |
334 | // 0x24 (SIB byte - scaled index = 0, r32 == esp) 0x20 imm8 value |
335 | |
336 | bool x86AssemblyInspectionEngine::push_extended_pattern_p() { |
337 | if (*m_cur_insn == 0xff) { |
338 | // Get the 3 opcode bits from the ModR/M byte |
339 | uint8_t opcode = (*(m_cur_insn + 1) >> 3) & 7; |
340 | if (opcode == 6) { |
341 | // I'm only looking for 0xff /6 here - I |
342 | // don't really care what value is being pushed, just that we're pushing |
343 | // a 32/64 bit value on to the stack is enough. |
344 | return true; |
345 | } |
346 | } |
347 | return false; |
348 | } |
349 | |
350 | // instructions only valid in 32-bit mode: |
351 | // 0x0e - push cs |
352 | // 0x16 - push ss |
353 | // 0x1e - push ds |
354 | // 0x06 - push es |
355 | bool x86AssemblyInspectionEngine::push_misc_reg_p() { |
356 | uint8_t p = *m_cur_insn; |
357 | if (m_wordsize == 4) { |
358 | if (p == 0x0e || p == 0x16 || p == 0x1e || p == 0x06) |
359 | return true; |
360 | } |
361 | return false; |
362 | } |
363 | |
364 | // pushq %rbx |
365 | // pushl %ebx |
366 | bool x86AssemblyInspectionEngine::push_reg_p(int ®no) { |
367 | uint8_t *p = m_cur_insn; |
368 | int regno_prefix_bit = 0; |
369 | // If we have a rex prefix byte, check to see if a B bit is set |
370 | if (m_wordsize == 8 && (*p & 0xfe) == 0x40) { |
371 | regno_prefix_bit = (*p & 1) << 3; |
372 | p++; |
373 | } |
374 | if (*p >= 0x50 && *p <= 0x57) { |
375 | regno = (*p - 0x50) | regno_prefix_bit; |
376 | return true; |
377 | } |
378 | return false; |
379 | } |
380 | |
381 | // movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] movl %esp, %ebp [0x8b |
382 | // 0xec] or [0x89 0xe5] |
383 | bool x86AssemblyInspectionEngine::mov_rsp_rbp_pattern_p() { |
384 | uint8_t *p = m_cur_insn; |
385 | if (m_wordsize == 8 && *p == 0x48) |
386 | p++; |
387 | if (*(p) == 0x8b && *(p + 1) == 0xec) |
388 | return true; |
389 | if (*(p) == 0x89 && *(p + 1) == 0xe5) |
390 | return true; |
391 | return false; |
392 | } |
393 | |
394 | // movq %rsp, %rbx [0x48 0x8b 0xdc] or [0x48 0x89 0xe3] |
395 | // movl %esp, %ebx [0x8b 0xdc] or [0x89 0xe3] |
396 | bool x86AssemblyInspectionEngine::mov_rsp_rbx_pattern_p() { |
397 | uint8_t *p = m_cur_insn; |
398 | if (m_wordsize == 8 && *p == 0x48) |
399 | p++; |
400 | if (*(p) == 0x8b && *(p + 1) == 0xdc) |
401 | return true; |
402 | if (*(p) == 0x89 && *(p + 1) == 0xe3) |
403 | return true; |
404 | return false; |
405 | } |
406 | |
407 | // movq %rbp, %rsp [0x48 0x8b 0xe5] or [0x48 0x89 0xec] |
408 | // movl %ebp, %esp [0x8b 0xe5] or [0x89 0xec] |
409 | bool x86AssemblyInspectionEngine::mov_rbp_rsp_pattern_p() { |
410 | uint8_t *p = m_cur_insn; |
411 | if (m_wordsize == 8 && *p == 0x48) |
412 | p++; |
413 | if (*(p) == 0x8b && *(p + 1) == 0xe5) |
414 | return true; |
415 | if (*(p) == 0x89 && *(p + 1) == 0xec) |
416 | return true; |
417 | return false; |
418 | } |
419 | |
420 | // movq %rbx, %rsp [0x48 0x8b 0xe3] or [0x48 0x89 0xdc] |
421 | // movl %ebx, %esp [0x8b 0xe3] or [0x89 0xdc] |
422 | bool x86AssemblyInspectionEngine::mov_rbx_rsp_pattern_p() { |
423 | uint8_t *p = m_cur_insn; |
424 | if (m_wordsize == 8 && *p == 0x48) |
425 | p++; |
426 | if (*(p) == 0x8b && *(p + 1) == 0xe3) |
427 | return true; |
428 | if (*(p) == 0x89 && *(p + 1) == 0xdc) |
429 | return true; |
430 | return false; |
431 | } |
432 | |
433 | // subq $0x20, %rsp |
434 | bool x86AssemblyInspectionEngine::sub_rsp_pattern_p(int &amount) { |
435 | uint8_t *p = m_cur_insn; |
436 | if (m_wordsize == 8 && *p == 0x48) |
437 | p++; |
438 | // 8-bit immediate operand |
439 | if (*p == 0x83 && *(p + 1) == 0xec) { |
440 | amount = (int8_t) * (p + 2); |
441 | return true; |
442 | } |
443 | // 32-bit immediate operand |
444 | if (*p == 0x81 && *(p + 1) == 0xec) { |
445 | amount = (int32_t)extract_4(b: p + 2); |
446 | return true; |
447 | } |
448 | return false; |
449 | } |
450 | |
451 | // addq $0x20, %rsp |
452 | bool x86AssemblyInspectionEngine::add_rsp_pattern_p(int &amount) { |
453 | uint8_t *p = m_cur_insn; |
454 | if (m_wordsize == 8 && *p == 0x48) |
455 | p++; |
456 | // 8-bit immediate operand |
457 | if (*p == 0x83 && *(p + 1) == 0xc4) { |
458 | amount = (int8_t) * (p + 2); |
459 | return true; |
460 | } |
461 | // 32-bit immediate operand |
462 | if (*p == 0x81 && *(p + 1) == 0xc4) { |
463 | amount = (int32_t)extract_4(b: p + 2); |
464 | return true; |
465 | } |
466 | return false; |
467 | } |
468 | |
469 | // lea esp, [esp - 0x28] |
470 | // lea esp, [esp + 0x28] |
471 | bool x86AssemblyInspectionEngine::lea_rsp_pattern_p(int &amount) { |
472 | uint8_t *p = m_cur_insn; |
473 | if (m_wordsize == 8 && *p == 0x48) |
474 | p++; |
475 | |
476 | // Check opcode |
477 | if (*p != 0x8d) |
478 | return false; |
479 | |
480 | // 8 bit displacement |
481 | if (*(p + 1) == 0x64 && (*(p + 2) & 0x3f) == 0x24) { |
482 | amount = (int8_t) * (p + 3); |
483 | return true; |
484 | } |
485 | |
486 | // 32 bit displacement |
487 | if (*(p + 1) == 0xa4 && (*(p + 2) & 0x3f) == 0x24) { |
488 | amount = (int32_t)extract_4(b: p + 3); |
489 | return true; |
490 | } |
491 | |
492 | return false; |
493 | } |
494 | |
495 | // lea -0x28(%ebp), %esp |
496 | // (32-bit and 64-bit variants, 8-bit and 32-bit displacement) |
497 | bool x86AssemblyInspectionEngine::lea_rbp_rsp_pattern_p(int &amount) { |
498 | uint8_t *p = m_cur_insn; |
499 | if (m_wordsize == 8 && *p == 0x48) |
500 | p++; |
501 | |
502 | // Check opcode |
503 | if (*p != 0x8d) |
504 | return false; |
505 | ++p; |
506 | |
507 | // 8 bit displacement |
508 | if (*p == 0x65) { |
509 | amount = (int8_t)p[1]; |
510 | return true; |
511 | } |
512 | |
513 | // 32 bit displacement |
514 | if (*p == 0xa5) { |
515 | amount = (int32_t)extract_4(b: p + 1); |
516 | return true; |
517 | } |
518 | |
519 | return false; |
520 | } |
521 | |
522 | // lea -0x28(%ebx), %esp |
523 | // (32-bit and 64-bit variants, 8-bit and 32-bit displacement) |
524 | bool x86AssemblyInspectionEngine::lea_rbx_rsp_pattern_p(int &amount) { |
525 | uint8_t *p = m_cur_insn; |
526 | if (m_wordsize == 8 && *p == 0x48) |
527 | p++; |
528 | |
529 | // Check opcode |
530 | if (*p != 0x8d) |
531 | return false; |
532 | ++p; |
533 | |
534 | // 8 bit displacement |
535 | if (*p == 0x63) { |
536 | amount = (int8_t)p[1]; |
537 | return true; |
538 | } |
539 | |
540 | // 32 bit displacement |
541 | if (*p == 0xa3) { |
542 | amount = (int32_t)extract_4(b: p + 1); |
543 | return true; |
544 | } |
545 | |
546 | return false; |
547 | } |
548 | |
549 | // and -0xfffffff0, %esp |
550 | // (32-bit and 64-bit variants, 8-bit and 32-bit displacement) |
551 | bool x86AssemblyInspectionEngine::and_rsp_pattern_p() { |
552 | uint8_t *p = m_cur_insn; |
553 | if (m_wordsize == 8 && *p == 0x48) |
554 | p++; |
555 | |
556 | if (*p != 0x81 && *p != 0x83) |
557 | return false; |
558 | |
559 | return *++p == 0xe4; |
560 | } |
561 | |
562 | // popq %rbx |
563 | // popl %ebx |
564 | bool x86AssemblyInspectionEngine::pop_reg_p(int ®no) { |
565 | uint8_t *p = m_cur_insn; |
566 | int regno_prefix_bit = 0; |
567 | // If we have a rex prefix byte, check to see if a B bit is set |
568 | if (m_wordsize == 8 && (*p & 0xfe) == 0x40) { |
569 | regno_prefix_bit = (*p & 1) << 3; |
570 | p++; |
571 | } |
572 | if (*p >= 0x58 && *p <= 0x5f) { |
573 | regno = (*p - 0x58) | regno_prefix_bit; |
574 | return true; |
575 | } |
576 | return false; |
577 | } |
578 | |
579 | // popq %rbp [0x5d] |
580 | // popl %ebp [0x5d] |
581 | bool x86AssemblyInspectionEngine::pop_rbp_pattern_p() { |
582 | uint8_t *p = m_cur_insn; |
583 | return (*p == 0x5d); |
584 | } |
585 | |
586 | // instructions valid only in 32-bit mode: |
587 | // 0x1f - pop ds |
588 | // 0x07 - pop es |
589 | // 0x17 - pop ss |
590 | bool x86AssemblyInspectionEngine::pop_misc_reg_p() { |
591 | uint8_t p = *m_cur_insn; |
592 | if (m_wordsize == 4) { |
593 | if (p == 0x1f || p == 0x07 || p == 0x17) |
594 | return true; |
595 | } |
596 | return false; |
597 | } |
598 | |
599 | // leave [0xc9] |
600 | bool x86AssemblyInspectionEngine::leave_pattern_p() { |
601 | uint8_t *p = m_cur_insn; |
602 | return (*p == 0xc9); |
603 | } |
604 | |
605 | // call $0 [0xe8 0x0 0x0 0x0 0x0] |
606 | bool x86AssemblyInspectionEngine::call_next_insn_pattern_p() { |
607 | uint8_t *p = m_cur_insn; |
608 | return (*p == 0xe8) && (*(p + 1) == 0x0) && (*(p + 2) == 0x0) && |
609 | (*(p + 3) == 0x0) && (*(p + 4) == 0x0); |
610 | } |
611 | |
612 | // Look for an instruction sequence storing a nonvolatile register on to the |
613 | // stack frame. |
614 | |
615 | // movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0] |
616 | // movl %eax, -0xc(%ebp) [0x89 0x45 0xf4] |
617 | |
618 | // The offset value returned in rbp_offset will be positive -- but it must be |
619 | // subtraced from the frame base register to get the actual location. The |
620 | // positive value returned for the offset is a convention used elsewhere for |
621 | // CFA offsets et al. |
622 | |
623 | bool x86AssemblyInspectionEngine::mov_reg_to_local_stack_frame_p( |
624 | int ®no, int &rbp_offset) { |
625 | uint8_t *p = m_cur_insn; |
626 | int src_reg_prefix_bit = 0; |
627 | int target_reg_prefix_bit = 0; |
628 | |
629 | if (m_wordsize == 8 && REX_W_PREFIX_P(*p)) { |
630 | src_reg_prefix_bit = REX_W_SRCREG(*p) << 3; |
631 | target_reg_prefix_bit = REX_W_DSTREG(*p) << 3; |
632 | if (target_reg_prefix_bit == 1) { |
633 | // rbp/ebp don't need a prefix bit - we know this isn't the reg we care |
634 | // about. |
635 | return false; |
636 | } |
637 | p++; |
638 | } |
639 | |
640 | if (*p == 0x89) { |
641 | /* Mask off the 3-5 bits which indicate the destination register |
642 | if this is a ModR/M byte. */ |
643 | int opcode_destreg_masked_out = *(p + 1) & (~0x38); |
644 | |
645 | /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101 |
646 | and three bits between them, e.g. 01nnn101 |
647 | We're looking for a destination of ebp-disp8 or ebp-disp32. */ |
648 | int immsize; |
649 | if (opcode_destreg_masked_out == 0x45) |
650 | immsize = 2; |
651 | else if (opcode_destreg_masked_out == 0x85) |
652 | immsize = 4; |
653 | else |
654 | return false; |
655 | |
656 | int offset = 0; |
657 | if (immsize == 2) |
658 | offset = (int8_t) * (p + 2); |
659 | if (immsize == 4) |
660 | offset = (uint32_t)extract_4(b: p + 2); |
661 | if (offset > 0) |
662 | return false; |
663 | |
664 | regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit; |
665 | rbp_offset = offset > 0 ? offset : -offset; |
666 | return true; |
667 | } |
668 | return false; |
669 | } |
670 | |
671 | // Returns true if this is a jmp instruction where we can't |
672 | // know the destination address statically. |
673 | // |
674 | // ff e0 jmpq *%rax |
675 | // ff e1 jmpq *%rcx |
676 | // ff 60 28 jmpq *0x28(%rax) |
677 | // ff 60 60 jmpq *0x60(%rax) |
678 | bool x86AssemblyInspectionEngine::jmp_to_reg_p() { |
679 | if (*m_cur_insn != 0xff) |
680 | return false; |
681 | |
682 | // The second byte is a ModR/M /4 byte, strip off the registers |
683 | uint8_t second_byte_sans_reg = *(m_cur_insn + 1) & ~7; |
684 | |
685 | // [reg] |
686 | if (second_byte_sans_reg == 0x20) |
687 | return true; |
688 | |
689 | // [reg]+disp8 |
690 | if (second_byte_sans_reg == 0x60) |
691 | return true; |
692 | |
693 | // [reg]+disp32 |
694 | if (second_byte_sans_reg == 0xa0) |
695 | return true; |
696 | |
697 | // reg |
698 | if (second_byte_sans_reg == 0xe0) |
699 | return true; |
700 | |
701 | return false; |
702 | } |
703 | |
704 | // Detect branches to fixed pc-relative offsets. |
705 | // Returns the offset from the address of the next instruction |
706 | // that may be branch/jumped to. |
707 | // |
708 | // Cannot determine the offset of a JMP that jumps to the address in |
709 | // a register ("jmpq *%rax") or offset from a register value |
710 | // ("jmpq *0x28(%rax)"), this method will return false on those |
711 | // instructions. |
712 | // |
713 | // These instructions all end in either a relative 8/16/32 bit value |
714 | // depending on the instruction and the current execution mode of the |
715 | // inferior process. Once we know the size of the opcode instruction, |
716 | // we can use the total instruction length to determine the size of |
717 | // the relative offset without having to compute it correctly. |
718 | |
719 | bool x86AssemblyInspectionEngine::pc_rel_branch_or_jump_p ( |
720 | const int instruction_length, int &offset) |
721 | { |
722 | int opcode_size = 0; |
723 | |
724 | uint8_t b1 = m_cur_insn[0]; |
725 | |
726 | switch (b1) { |
727 | case 0x77: // JA/JNBE rel8 |
728 | case 0x73: // JAE/JNB/JNC rel8 |
729 | case 0x72: // JB/JC/JNAE rel8 |
730 | case 0x76: // JBE/JNA rel8 |
731 | case 0xe3: // JCXZ/JECXZ/JRCXZ rel8 |
732 | case 0x74: // JE/JZ rel8 |
733 | case 0x7f: // JG/JNLE rel8 |
734 | case 0x7d: // JGE/JNL rel8 |
735 | case 0x7c: // JL/JNGE rel8 |
736 | case 0x7e: // JNG/JLE rel8 |
737 | case 0x71: // JNO rel8 |
738 | case 0x7b: // JNP/JPO rel8 |
739 | case 0x79: // JNS rel8 |
740 | case 0x75: // JNE/JNZ rel8 |
741 | case 0x70: // JO rel8 |
742 | case 0x7a: // JP/JPE rel8 |
743 | case 0x78: // JS rel8 |
744 | case 0xeb: // JMP rel8 |
745 | case 0xe9: // JMP rel16/rel32 |
746 | opcode_size = 1; |
747 | break; |
748 | default: |
749 | break; |
750 | } |
751 | if (b1 == 0x0f && opcode_size == 0) { |
752 | uint8_t b2 = m_cur_insn[1]; |
753 | switch (b2) { |
754 | case 0x87: // JA/JNBE rel16/rel32 |
755 | case 0x86: // JBE/JNA rel16/rel32 |
756 | case 0x84: // JE/JZ rel16/rel32 |
757 | case 0x8f: // JG/JNLE rel16/rel32 |
758 | case 0x8d: // JNL/JGE rel16/rel32 |
759 | case 0x8e: // JLE rel16/rel32 |
760 | case 0x82: // JB/JC/JNAE rel16/rel32 |
761 | case 0x83: // JAE/JNB/JNC rel16/rel32 |
762 | case 0x85: // JNE/JNZ rel16/rel32 |
763 | case 0x8c: // JL/JNGE rel16/rel32 |
764 | case 0x81: // JNO rel16/rel32 |
765 | case 0x8b: // JNP/JPO rel16/rel32 |
766 | case 0x89: // JNS rel16/rel32 |
767 | case 0x80: // JO rel16/rel32 |
768 | case 0x8a: // JP rel16/rel32 |
769 | case 0x88: // JS rel16/rel32 |
770 | opcode_size = 2; |
771 | break; |
772 | default: |
773 | break; |
774 | } |
775 | } |
776 | |
777 | if (opcode_size == 0) |
778 | return false; |
779 | |
780 | offset = 0; |
781 | if (instruction_length - opcode_size == 1) { |
782 | int8_t rel8 = (int8_t) *(m_cur_insn + opcode_size); |
783 | offset = rel8; |
784 | } else if (instruction_length - opcode_size == 2) { |
785 | int16_t rel16 = extract_2_signed (b: m_cur_insn + opcode_size); |
786 | offset = rel16; |
787 | } else if (instruction_length - opcode_size == 4) { |
788 | int32_t rel32 = extract_4_signed (b: m_cur_insn + opcode_size); |
789 | offset = rel32; |
790 | } else { |
791 | return false; |
792 | } |
793 | return true; |
794 | } |
795 | |
796 | // Returns true if this instruction is a intra-function branch or jump - |
797 | // a branch/jump within the bounds of this same function. |
798 | // Cannot predict where a jump through a register value ("jmpq *%rax") |
799 | // will go, so it will return false on that instruction. |
800 | bool x86AssemblyInspectionEngine::local_branch_p ( |
801 | const addr_t current_func_text_offset, |
802 | const AddressRange &func_range, |
803 | const int instruction_length, |
804 | addr_t &target_insn_offset) { |
805 | int offset; |
806 | if (pc_rel_branch_or_jump_p (instruction_length, offset) && offset != 0) { |
807 | addr_t next_pc_value = current_func_text_offset + instruction_length; |
808 | if (offset < 0 && addr_t(-offset) > current_func_text_offset) { |
809 | // Branch target is before the start of this function |
810 | return false; |
811 | } |
812 | if (offset + next_pc_value > func_range.GetByteSize()) { |
813 | // Branch targets outside this function's bounds |
814 | return false; |
815 | } |
816 | // This instruction branches to target_insn_offset (byte offset into the function) |
817 | target_insn_offset = next_pc_value + offset; |
818 | return true; |
819 | } |
820 | return false; |
821 | } |
822 | |
823 | // Returns true if this instruction is a inter-function branch or jump - a |
824 | // branch/jump to another function. |
825 | // Cannot predict where a jump through a register value ("jmpq *%rax") |
826 | // will go, so it will return false on that instruction. |
827 | bool x86AssemblyInspectionEngine::non_local_branch_p ( |
828 | const addr_t current_func_text_offset, |
829 | const AddressRange &func_range, |
830 | const int instruction_length) { |
831 | int offset; |
832 | addr_t target_insn_offset; |
833 | if (pc_rel_branch_or_jump_p (instruction_length, offset)) { |
834 | return !local_branch_p(current_func_text_offset,func_range,instruction_length,target_insn_offset); |
835 | } |
836 | return false; |
837 | } |
838 | |
839 | // ret [0xc3] or [0xcb] or [0xc2 imm16] or [0xca imm16] |
840 | bool x86AssemblyInspectionEngine::ret_pattern_p() { |
841 | uint8_t *p = m_cur_insn; |
842 | return *p == 0xc3 || *p == 0xc2 || *p == 0xca || *p == 0xcb; |
843 | } |
844 | |
845 | uint16_t x86AssemblyInspectionEngine::(uint8_t *b) { |
846 | uint16_t v = 0; |
847 | for (int i = 1; i >= 0; i--) |
848 | v = (v << 8) | b[i]; |
849 | return v; |
850 | } |
851 | |
852 | int16_t x86AssemblyInspectionEngine::(uint8_t *b) { |
853 | int16_t v = 0; |
854 | for (int i = 1; i >= 0; i--) |
855 | v = (v << 8) | b[i]; |
856 | return v; |
857 | } |
858 | |
859 | uint32_t x86AssemblyInspectionEngine::(uint8_t *b) { |
860 | uint32_t v = 0; |
861 | for (int i = 3; i >= 0; i--) |
862 | v = (v << 8) | b[i]; |
863 | return v; |
864 | } |
865 | |
866 | int32_t x86AssemblyInspectionEngine::(uint8_t *b) { |
867 | int32_t v = 0; |
868 | for (int i = 3; i >= 0; i--) |
869 | v = (v << 8) | b[i]; |
870 | return v; |
871 | } |
872 | |
873 | |
874 | bool x86AssemblyInspectionEngine::instruction_length(uint8_t *insn_p, |
875 | int &length, |
876 | uint32_t buffer_remaining_bytes) { |
877 | |
878 | uint32_t max_op_byte_size = std::min(a: buffer_remaining_bytes, b: m_arch.GetMaximumOpcodeByteSize()); |
879 | llvm::SmallVector<uint8_t, 32> opcode_data; |
880 | opcode_data.resize(N: max_op_byte_size); |
881 | |
882 | char out_string[512]; |
883 | const size_t inst_size = |
884 | ::LLVMDisasmInstruction(DC: m_disasm_context, Bytes: insn_p, BytesSize: max_op_byte_size, PC: 0, |
885 | OutString: out_string, OutStringSize: sizeof(out_string)); |
886 | |
887 | length = inst_size; |
888 | return true; |
889 | } |
890 | |
891 | bool x86AssemblyInspectionEngine::machine_regno_to_lldb_regno( |
892 | int machine_regno, uint32_t &lldb_regno) { |
893 | MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.find(x: machine_regno); |
894 | if (it != m_reg_map.end()) { |
895 | lldb_regno = it->second.lldb_regnum; |
896 | return true; |
897 | } |
898 | return false; |
899 | } |
900 | |
901 | bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly( |
902 | uint8_t *data, size_t size, AddressRange &func_range, |
903 | UnwindPlan &unwind_plan) { |
904 | unwind_plan.Clear(); |
905 | |
906 | if (data == nullptr || size == 0) |
907 | return false; |
908 | |
909 | if (!m_register_map_initialized) |
910 | return false; |
911 | |
912 | if (m_disasm_context == nullptr) |
913 | return false; |
914 | |
915 | addr_t current_func_text_offset = 0; |
916 | int current_sp_bytes_offset_from_fa = 0; |
917 | bool is_aligned = false; |
918 | UnwindPlan::Row::RegisterLocation initial_regloc; |
919 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
920 | |
921 | unwind_plan.SetPlanValidAddressRange(func_range); |
922 | unwind_plan.SetRegisterKind(eRegisterKindLLDB); |
923 | |
924 | // At the start of the function, find the CFA by adding wordsize to the SP |
925 | // register |
926 | row->SetOffset(current_func_text_offset); |
927 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: m_lldb_sp_regnum, offset: m_wordsize); |
928 | |
929 | // caller's stack pointer value before the call insn is the CFA address |
930 | initial_regloc.SetIsCFAPlusOffset(0); |
931 | row->SetRegisterInfo(reg_num: m_lldb_sp_regnum, register_location: initial_regloc); |
932 | |
933 | // saved instruction pointer can be found at CFA - wordsize. |
934 | current_sp_bytes_offset_from_fa = m_wordsize; |
935 | initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa); |
936 | row->SetRegisterInfo(reg_num: m_lldb_ip_regnum, register_location: initial_regloc); |
937 | |
938 | unwind_plan.AppendRow(row_sp: row); |
939 | |
940 | // Allocate a new Row, populate it with the existing Row contents. |
941 | UnwindPlan::Row *newrow = new UnwindPlan::Row; |
942 | *newrow = *row.get(); |
943 | row.reset(p: newrow); |
944 | |
945 | // Track which registers have been saved so far in the prologue. If we see |
946 | // another push of that register, it's not part of the prologue. The register |
947 | // numbers used here are the machine register #'s (i386_register_numbers, |
948 | // x86_64_register_numbers). |
949 | std::vector<bool> saved_registers(32, false); |
950 | |
951 | // Once the prologue has completed we'll save a copy of the unwind |
952 | // instructions If there is an epilogue in the middle of the function, after |
953 | // that epilogue we'll reinstate the unwind setup -- we assume that some code |
954 | // path jumps over the mid-function epilogue |
955 | |
956 | UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI |
957 | int prologue_completed_sp_bytes_offset_from_cfa = 0; // The sp value before the |
958 | // epilogue started executed |
959 | bool prologue_completed_is_aligned = false; |
960 | std::vector<bool> prologue_completed_saved_registers; |
961 | |
962 | while (current_func_text_offset < size) { |
963 | int stack_offset, insn_len; |
964 | int machine_regno; // register numbers masked directly out of instructions |
965 | uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB |
966 | // numbering scheme |
967 | |
968 | bool in_epilogue = false; // we're in the middle of an epilogue sequence |
969 | bool row_updated = false; // The UnwindPlan::Row 'row' has been updated |
970 | |
971 | m_cur_insn = data + current_func_text_offset; |
972 | if (!instruction_length(insn_p: m_cur_insn, length&: insn_len, buffer_remaining_bytes: size - current_func_text_offset) |
973 | || insn_len == 0 |
974 | || insn_len > kMaxInstructionByteSize) { |
975 | // An unrecognized/junk instruction |
976 | break; |
977 | } |
978 | |
979 | auto &cfa_value = row->GetCFAValue(); |
980 | auto &afa_value = row->GetAFAValue(); |
981 | auto fa_value_ptr = is_aligned ? &afa_value : &cfa_value; |
982 | |
983 | if (mov_rsp_rbp_pattern_p()) { |
984 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
985 | fa_value_ptr->SetIsRegisterPlusOffset( |
986 | reg_num: m_lldb_fp_regnum, offset: fa_value_ptr->GetOffset()); |
987 | row_updated = true; |
988 | } |
989 | } |
990 | |
991 | else if (mov_rsp_rbx_pattern_p()) { |
992 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
993 | fa_value_ptr->SetIsRegisterPlusOffset( |
994 | reg_num: m_lldb_alt_fp_regnum, offset: fa_value_ptr->GetOffset()); |
995 | row_updated = true; |
996 | } |
997 | } |
998 | |
999 | else if (and_rsp_pattern_p()) { |
1000 | current_sp_bytes_offset_from_fa = 0; |
1001 | afa_value.SetIsRegisterPlusOffset( |
1002 | reg_num: m_lldb_sp_regnum, offset: current_sp_bytes_offset_from_fa); |
1003 | fa_value_ptr = &afa_value; |
1004 | is_aligned = true; |
1005 | row_updated = true; |
1006 | } |
1007 | |
1008 | else if (mov_rbp_rsp_pattern_p()) { |
1009 | if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) |
1010 | { |
1011 | is_aligned = false; |
1012 | fa_value_ptr = &cfa_value; |
1013 | afa_value.SetUnspecified(); |
1014 | row_updated = true; |
1015 | } |
1016 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) |
1017 | current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset(); |
1018 | } |
1019 | |
1020 | else if (mov_rbx_rsp_pattern_p()) { |
1021 | if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum) |
1022 | { |
1023 | is_aligned = false; |
1024 | fa_value_ptr = &cfa_value; |
1025 | afa_value.SetUnspecified(); |
1026 | row_updated = true; |
1027 | } |
1028 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum) |
1029 | current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset(); |
1030 | } |
1031 | |
1032 | // This is the start() function (or a pthread equivalent), it starts with a |
1033 | // pushl $0x0 which puts the saved pc value of 0 on the stack. In this |
1034 | // case we want to pretend we didn't see a stack movement at all -- |
1035 | // normally the saved pc value is already on the stack by the time the |
1036 | // function starts executing. |
1037 | else if (push_0_pattern_p()) { |
1038 | } |
1039 | |
1040 | else if (push_reg_p(regno&: machine_regno)) { |
1041 | current_sp_bytes_offset_from_fa += m_wordsize; |
1042 | // the PUSH instruction has moved the stack pointer - if the FA is set |
1043 | // in terms of the stack pointer, we need to add a new row of |
1044 | // instructions. |
1045 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1046 | fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); |
1047 | row_updated = true; |
1048 | } |
1049 | // record where non-volatile (callee-saved, spilled) registers are saved |
1050 | // on the stack |
1051 | if (nonvolatile_reg_p(machine_regno) && |
1052 | machine_regno_to_lldb_regno(machine_regno, lldb_regno) && |
1053 | !saved_registers[machine_regno]) { |
1054 | UnwindPlan::Row::RegisterLocation regloc; |
1055 | if (is_aligned) |
1056 | regloc.SetAtAFAPlusOffset(-current_sp_bytes_offset_from_fa); |
1057 | else |
1058 | regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa); |
1059 | row->SetRegisterInfo(reg_num: lldb_regno, register_location: regloc); |
1060 | saved_registers[machine_regno] = true; |
1061 | row_updated = true; |
1062 | } |
1063 | } |
1064 | |
1065 | else if (pop_reg_p(regno&: machine_regno)) { |
1066 | current_sp_bytes_offset_from_fa -= m_wordsize; |
1067 | |
1068 | if (nonvolatile_reg_p(machine_regno) && |
1069 | machine_regno_to_lldb_regno(machine_regno, lldb_regno) && |
1070 | saved_registers[machine_regno]) { |
1071 | saved_registers[machine_regno] = false; |
1072 | row->RemoveRegisterInfo(reg_num: lldb_regno); |
1073 | |
1074 | if (lldb_regno == fa_value_ptr->GetRegisterNumber()) { |
1075 | fa_value_ptr->SetIsRegisterPlusOffset( |
1076 | reg_num: m_lldb_sp_regnum, offset: fa_value_ptr->GetOffset()); |
1077 | } |
1078 | |
1079 | in_epilogue = true; |
1080 | row_updated = true; |
1081 | } |
1082 | |
1083 | // the POP instruction has moved the stack pointer - if the FA is set in |
1084 | // terms of the stack pointer, we need to add a new row of instructions. |
1085 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1086 | fa_value_ptr->SetIsRegisterPlusOffset( |
1087 | reg_num: m_lldb_sp_regnum, offset: current_sp_bytes_offset_from_fa); |
1088 | row_updated = true; |
1089 | } |
1090 | } |
1091 | |
1092 | else if (pop_misc_reg_p()) { |
1093 | current_sp_bytes_offset_from_fa -= m_wordsize; |
1094 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1095 | fa_value_ptr->SetIsRegisterPlusOffset( |
1096 | reg_num: m_lldb_sp_regnum, offset: current_sp_bytes_offset_from_fa); |
1097 | row_updated = true; |
1098 | } |
1099 | } |
1100 | |
1101 | // The LEAVE instruction moves the value from rbp into rsp and pops a value |
1102 | // off the stack into rbp (restoring the caller's rbp value). It is the |
1103 | // opposite of ENTER, or 'push rbp, mov rsp rbp'. |
1104 | else if (leave_pattern_p()) { |
1105 | if (saved_registers[m_machine_fp_regnum]) { |
1106 | saved_registers[m_machine_fp_regnum] = false; |
1107 | row->RemoveRegisterInfo(reg_num: m_lldb_fp_regnum); |
1108 | |
1109 | row_updated = true; |
1110 | } |
1111 | |
1112 | if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) |
1113 | { |
1114 | is_aligned = false; |
1115 | fa_value_ptr = &cfa_value; |
1116 | afa_value.SetUnspecified(); |
1117 | row_updated = true; |
1118 | } |
1119 | |
1120 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) |
1121 | { |
1122 | fa_value_ptr->SetIsRegisterPlusOffset( |
1123 | reg_num: m_lldb_sp_regnum, offset: fa_value_ptr->GetOffset()); |
1124 | |
1125 | current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset(); |
1126 | } |
1127 | |
1128 | current_sp_bytes_offset_from_fa -= m_wordsize; |
1129 | |
1130 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1131 | fa_value_ptr->SetIsRegisterPlusOffset( |
1132 | reg_num: m_lldb_sp_regnum, offset: current_sp_bytes_offset_from_fa); |
1133 | row_updated = true; |
1134 | } |
1135 | |
1136 | in_epilogue = true; |
1137 | } |
1138 | |
1139 | else if (mov_reg_to_local_stack_frame_p(regno&: machine_regno, rbp_offset&: stack_offset) && |
1140 | nonvolatile_reg_p(machine_regno) && |
1141 | machine_regno_to_lldb_regno(machine_regno, lldb_regno) && |
1142 | !saved_registers[machine_regno]) { |
1143 | saved_registers[machine_regno] = true; |
1144 | |
1145 | UnwindPlan::Row::RegisterLocation regloc; |
1146 | |
1147 | // stack_offset for 'movq %r15, -80(%rbp)' will be 80. In the Row, we |
1148 | // want to express this as the offset from the FA. If the frame base is |
1149 | // rbp (like the above instruction), the FA offset for rbp is probably |
1150 | // 16. So we want to say that the value is stored at the FA address - |
1151 | // 96. |
1152 | if (is_aligned) |
1153 | regloc.SetAtAFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset())); |
1154 | else |
1155 | regloc.SetAtCFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset())); |
1156 | |
1157 | row->SetRegisterInfo(reg_num: lldb_regno, register_location: regloc); |
1158 | |
1159 | row_updated = true; |
1160 | } |
1161 | |
1162 | else if (sub_rsp_pattern_p(amount&: stack_offset)) { |
1163 | current_sp_bytes_offset_from_fa += stack_offset; |
1164 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1165 | fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); |
1166 | row_updated = true; |
1167 | } |
1168 | } |
1169 | |
1170 | else if (add_rsp_pattern_p(amount&: stack_offset)) { |
1171 | current_sp_bytes_offset_from_fa -= stack_offset; |
1172 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1173 | fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); |
1174 | row_updated = true; |
1175 | } |
1176 | in_epilogue = true; |
1177 | } |
1178 | |
1179 | else if (push_extended_pattern_p() || push_imm_pattern_p() || |
1180 | push_misc_reg_p()) { |
1181 | current_sp_bytes_offset_from_fa += m_wordsize; |
1182 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1183 | fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); |
1184 | row_updated = true; |
1185 | } |
1186 | } |
1187 | |
1188 | else if (lea_rsp_pattern_p(amount&: stack_offset)) { |
1189 | current_sp_bytes_offset_from_fa -= stack_offset; |
1190 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1191 | fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); |
1192 | row_updated = true; |
1193 | } |
1194 | if (stack_offset > 0) |
1195 | in_epilogue = true; |
1196 | } |
1197 | |
1198 | else if (lea_rbp_rsp_pattern_p(amount&: stack_offset)) { |
1199 | if (is_aligned && |
1200 | cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) { |
1201 | is_aligned = false; |
1202 | fa_value_ptr = &cfa_value; |
1203 | afa_value.SetUnspecified(); |
1204 | row_updated = true; |
1205 | } |
1206 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) { |
1207 | current_sp_bytes_offset_from_fa = |
1208 | fa_value_ptr->GetOffset() - stack_offset; |
1209 | } |
1210 | } |
1211 | |
1212 | else if (lea_rbx_rsp_pattern_p(amount&: stack_offset)) { |
1213 | if (is_aligned && |
1214 | cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum) { |
1215 | is_aligned = false; |
1216 | fa_value_ptr = &cfa_value; |
1217 | afa_value.SetUnspecified(); |
1218 | row_updated = true; |
1219 | } |
1220 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum) { |
1221 | current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset() - stack_offset; |
1222 | } |
1223 | } |
1224 | |
1225 | else if (prologue_completed_row.get() && |
1226 | (ret_pattern_p() || |
1227 | non_local_branch_p (current_func_text_offset, func_range, instruction_length: insn_len) || |
1228 | jmp_to_reg_p())) { |
1229 | // Check if the current instruction is the end of an epilogue sequence, |
1230 | // and if so, re-instate the prologue-completed unwind state. |
1231 | |
1232 | // The current instruction is a branch/jump outside this function, |
1233 | // a ret, or a jump through a register value which we cannot |
1234 | // determine the effcts of. Verify that the stack frame state |
1235 | // has been unwound to the same as it was at function entry to avoid |
1236 | // mis-identifying a JMP instruction as an epilogue. |
1237 | UnwindPlan::Row::RegisterLocation sp, pc; |
1238 | if (row->GetRegisterInfo(reg_num: m_lldb_sp_regnum, register_location&: sp) && |
1239 | row->GetRegisterInfo(reg_num: m_lldb_ip_regnum, register_location&: pc)) { |
1240 | // Any ret instruction variant is definitely indicative of an |
1241 | // epilogue; for other insn patterns verify that we're back to |
1242 | // the original unwind state. |
1243 | if (ret_pattern_p() || |
1244 | (sp.IsCFAPlusOffset() && sp.GetOffset() == 0 && |
1245 | pc.IsAtCFAPlusOffset() && pc.GetOffset() == -m_wordsize)) { |
1246 | // Reinstate the saved prologue setup for any instructions that come |
1247 | // after the epilogue |
1248 | |
1249 | UnwindPlan::Row *newrow = new UnwindPlan::Row; |
1250 | *newrow = *prologue_completed_row.get(); |
1251 | row.reset(p: newrow); |
1252 | current_sp_bytes_offset_from_fa = |
1253 | prologue_completed_sp_bytes_offset_from_cfa; |
1254 | is_aligned = prologue_completed_is_aligned; |
1255 | |
1256 | saved_registers.clear(); |
1257 | saved_registers.resize(new_size: prologue_completed_saved_registers.size(), x: false); |
1258 | for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i) { |
1259 | saved_registers[i] = prologue_completed_saved_registers[i]; |
1260 | } |
1261 | |
1262 | in_epilogue = true; |
1263 | row_updated = true; |
1264 | } |
1265 | } |
1266 | } |
1267 | |
1268 | // call next instruction |
1269 | // call 0 |
1270 | // => pop %ebx |
1271 | // This is used in i386 programs to get the PIC base address for finding |
1272 | // global data |
1273 | else if (call_next_insn_pattern_p()) { |
1274 | current_sp_bytes_offset_from_fa += m_wordsize; |
1275 | if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) { |
1276 | fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa); |
1277 | row_updated = true; |
1278 | } |
1279 | } |
1280 | |
1281 | if (row_updated) { |
1282 | if (current_func_text_offset + insn_len < size) { |
1283 | row->SetOffset(current_func_text_offset + insn_len); |
1284 | unwind_plan.AppendRow(row_sp: row); |
1285 | // Allocate a new Row, populate it with the existing Row contents. |
1286 | newrow = new UnwindPlan::Row; |
1287 | *newrow = *row.get(); |
1288 | row.reset(p: newrow); |
1289 | } |
1290 | } |
1291 | |
1292 | if (!in_epilogue && row_updated) { |
1293 | // If we're not in an epilogue sequence, save the updated Row |
1294 | UnwindPlan::Row *newrow = new UnwindPlan::Row; |
1295 | *newrow = *row.get(); |
1296 | prologue_completed_row.reset(p: newrow); |
1297 | |
1298 | prologue_completed_saved_registers.clear(); |
1299 | prologue_completed_saved_registers.resize(new_size: saved_registers.size(), x: false); |
1300 | for (size_t i = 0; i < saved_registers.size(); ++i) { |
1301 | prologue_completed_saved_registers[i] = saved_registers[i]; |
1302 | } |
1303 | } |
1304 | |
1305 | // We may change the sp value without adding a new Row necessarily -- keep |
1306 | // track of it either way. |
1307 | if (!in_epilogue) { |
1308 | prologue_completed_sp_bytes_offset_from_cfa = |
1309 | current_sp_bytes_offset_from_fa; |
1310 | prologue_completed_is_aligned = is_aligned; |
1311 | } |
1312 | |
1313 | m_cur_insn = m_cur_insn + insn_len; |
1314 | current_func_text_offset += insn_len; |
1315 | } |
1316 | |
1317 | unwind_plan.SetSourceName("assembly insn profiling" ); |
1318 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
1319 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); |
1320 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
1321 | |
1322 | return true; |
1323 | } |
1324 | |
1325 | bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite( |
1326 | uint8_t *data, size_t size, AddressRange &func_range, |
1327 | UnwindPlan &unwind_plan, RegisterContextSP ®_ctx) { |
1328 | Address addr_start = func_range.GetBaseAddress(); |
1329 | if (!addr_start.IsValid()) |
1330 | return false; |
1331 | |
1332 | // We either need a live RegisterContext, or we need the UnwindPlan to |
1333 | // already be in the lldb register numbering scheme. |
1334 | if (reg_ctx.get() == nullptr && |
1335 | unwind_plan.GetRegisterKind() != eRegisterKindLLDB) |
1336 | return false; |
1337 | |
1338 | // Is original unwind_plan valid? |
1339 | // unwind_plan should have at least one row which is ABI-default (CFA |
1340 | // register is sp), and another row in mid-function. |
1341 | if (unwind_plan.GetRowCount() < 2) |
1342 | return false; |
1343 | |
1344 | UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex(idx: 0); |
1345 | if (first_row->GetOffset() != 0) |
1346 | return false; |
1347 | uint32_t cfa_reg = first_row->GetCFAValue().GetRegisterNumber(); |
1348 | if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) { |
1349 | cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( |
1350 | kind: unwind_plan.GetRegisterKind(), |
1351 | num: first_row->GetCFAValue().GetRegisterNumber()); |
1352 | } |
1353 | if (cfa_reg != m_lldb_sp_regnum || |
1354 | first_row->GetCFAValue().GetOffset() != m_wordsize) |
1355 | return false; |
1356 | |
1357 | UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset(offset: -1); |
1358 | |
1359 | size_t offset = 0; |
1360 | int row_id = 1; |
1361 | bool unwind_plan_updated = false; |
1362 | UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row)); |
1363 | |
1364 | // After a mid-function epilogue we will need to re-insert the original |
1365 | // unwind rules so unwinds work for the remainder of the function. These |
1366 | // aren't common with clang/gcc on x86 but it is possible. |
1367 | bool reinstate_unwind_state = false; |
1368 | |
1369 | while (offset < size) { |
1370 | m_cur_insn = data + offset; |
1371 | int insn_len; |
1372 | if (!instruction_length(insn_p: m_cur_insn, length&: insn_len, buffer_remaining_bytes: size - offset) || |
1373 | insn_len == 0 || insn_len > kMaxInstructionByteSize) { |
1374 | // An unrecognized/junk instruction. |
1375 | break; |
1376 | } |
1377 | |
1378 | // Advance offsets. |
1379 | offset += insn_len; |
1380 | |
1381 | // offset is pointing beyond the bounds of the function; stop looping. |
1382 | if (offset >= size) |
1383 | continue; |
1384 | |
1385 | if (reinstate_unwind_state) { |
1386 | UnwindPlan::RowSP new_row(new UnwindPlan::Row()); |
1387 | *new_row = *original_last_row; |
1388 | new_row->SetOffset(offset); |
1389 | unwind_plan.AppendRow(row_sp: new_row); |
1390 | row = std::make_shared<UnwindPlan::Row>(); |
1391 | *row = *new_row; |
1392 | reinstate_unwind_state = false; |
1393 | unwind_plan_updated = true; |
1394 | continue; |
1395 | } |
1396 | |
1397 | // If we already have one row for this instruction, we can continue. |
1398 | while (row_id < unwind_plan.GetRowCount() && |
1399 | unwind_plan.GetRowAtIndex(idx: row_id)->GetOffset() <= offset) { |
1400 | row_id++; |
1401 | } |
1402 | UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex(idx: row_id - 1); |
1403 | if (original_row->GetOffset() == offset) { |
1404 | *row = *original_row; |
1405 | continue; |
1406 | } |
1407 | |
1408 | if (row_id == 0) { |
1409 | // If we are here, compiler didn't generate CFI for prologue. This won't |
1410 | // happen to GCC or clang. In this case, bail out directly. |
1411 | return false; |
1412 | } |
1413 | |
1414 | // Inspect the instruction to check if we need a new row for it. |
1415 | cfa_reg = row->GetCFAValue().GetRegisterNumber(); |
1416 | if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) { |
1417 | cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( |
1418 | kind: unwind_plan.GetRegisterKind(), |
1419 | num: row->GetCFAValue().GetRegisterNumber()); |
1420 | } |
1421 | if (cfa_reg == m_lldb_sp_regnum) { |
1422 | // CFA register is sp. |
1423 | |
1424 | // call next instruction |
1425 | // call 0 |
1426 | // => pop %ebx |
1427 | if (call_next_insn_pattern_p()) { |
1428 | row->SetOffset(offset); |
1429 | row->GetCFAValue().IncOffset(delta: m_wordsize); |
1430 | |
1431 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1432 | unwind_plan.InsertRow(row_sp: new_row); |
1433 | unwind_plan_updated = true; |
1434 | continue; |
1435 | } |
1436 | |
1437 | // push/pop register |
1438 | int regno; |
1439 | if (push_reg_p(regno)) { |
1440 | row->SetOffset(offset); |
1441 | row->GetCFAValue().IncOffset(delta: m_wordsize); |
1442 | |
1443 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1444 | unwind_plan.InsertRow(row_sp: new_row); |
1445 | unwind_plan_updated = true; |
1446 | continue; |
1447 | } |
1448 | if (pop_reg_p(regno)) { |
1449 | // Technically, this might be a nonvolatile register recover in |
1450 | // epilogue. We should reset RegisterInfo for the register. But in |
1451 | // practice, previous rule for the register is still valid... So we |
1452 | // ignore this case. |
1453 | |
1454 | row->SetOffset(offset); |
1455 | row->GetCFAValue().IncOffset(delta: -m_wordsize); |
1456 | |
1457 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1458 | unwind_plan.InsertRow(row_sp: new_row); |
1459 | unwind_plan_updated = true; |
1460 | continue; |
1461 | } |
1462 | |
1463 | if (pop_misc_reg_p()) { |
1464 | row->SetOffset(offset); |
1465 | row->GetCFAValue().IncOffset(delta: -m_wordsize); |
1466 | |
1467 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1468 | unwind_plan.InsertRow(row_sp: new_row); |
1469 | unwind_plan_updated = true; |
1470 | continue; |
1471 | } |
1472 | |
1473 | // push imm |
1474 | if (push_imm_pattern_p()) { |
1475 | row->SetOffset(offset); |
1476 | row->GetCFAValue().IncOffset(delta: m_wordsize); |
1477 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1478 | unwind_plan.InsertRow(row_sp: new_row); |
1479 | unwind_plan_updated = true; |
1480 | continue; |
1481 | } |
1482 | |
1483 | // push extended |
1484 | if (push_extended_pattern_p() || push_misc_reg_p()) { |
1485 | row->SetOffset(offset); |
1486 | row->GetCFAValue().IncOffset(delta: m_wordsize); |
1487 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1488 | unwind_plan.InsertRow(row_sp: new_row); |
1489 | unwind_plan_updated = true; |
1490 | continue; |
1491 | } |
1492 | |
1493 | // add/sub %rsp/%esp |
1494 | int amount; |
1495 | if (add_rsp_pattern_p(amount)) { |
1496 | row->SetOffset(offset); |
1497 | row->GetCFAValue().IncOffset(delta: -amount); |
1498 | |
1499 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1500 | unwind_plan.InsertRow(row_sp: new_row); |
1501 | unwind_plan_updated = true; |
1502 | continue; |
1503 | } |
1504 | if (sub_rsp_pattern_p(amount)) { |
1505 | row->SetOffset(offset); |
1506 | row->GetCFAValue().IncOffset(delta: amount); |
1507 | |
1508 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1509 | unwind_plan.InsertRow(row_sp: new_row); |
1510 | unwind_plan_updated = true; |
1511 | continue; |
1512 | } |
1513 | |
1514 | // lea %rsp, [%rsp + $offset] |
1515 | if (lea_rsp_pattern_p(amount)) { |
1516 | row->SetOffset(offset); |
1517 | row->GetCFAValue().IncOffset(delta: -amount); |
1518 | |
1519 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1520 | unwind_plan.InsertRow(row_sp: new_row); |
1521 | unwind_plan_updated = true; |
1522 | continue; |
1523 | } |
1524 | |
1525 | if (ret_pattern_p()) { |
1526 | reinstate_unwind_state = true; |
1527 | continue; |
1528 | } |
1529 | } else if (cfa_reg == m_lldb_fp_regnum) { |
1530 | // CFA register is fp. |
1531 | |
1532 | // The only case we care about is epilogue: |
1533 | // [0x5d] pop %rbp/%ebp |
1534 | // => [0xc3] ret |
1535 | if (pop_rbp_pattern_p() || leave_pattern_p()) { |
1536 | m_cur_insn++; |
1537 | if (ret_pattern_p()) { |
1538 | row->SetOffset(offset); |
1539 | row->GetCFAValue().SetIsRegisterPlusOffset( |
1540 | reg_num: first_row->GetCFAValue().GetRegisterNumber(), offset: m_wordsize); |
1541 | |
1542 | UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); |
1543 | unwind_plan.InsertRow(row_sp: new_row); |
1544 | unwind_plan_updated = true; |
1545 | reinstate_unwind_state = true; |
1546 | continue; |
1547 | } |
1548 | } |
1549 | } else { |
1550 | // CFA register is not sp or fp. |
1551 | |
1552 | // This must be hand-written assembly. |
1553 | // Just trust eh_frame and assume we have finished. |
1554 | break; |
1555 | } |
1556 | } |
1557 | |
1558 | unwind_plan.SetPlanValidAddressRange(func_range); |
1559 | if (unwind_plan_updated) { |
1560 | std::string unwind_plan_source(unwind_plan.GetSourceName().AsCString()); |
1561 | unwind_plan_source += " plus augmentation from assembly parsing" ; |
1562 | unwind_plan.SetSourceName(unwind_plan_source.c_str()); |
1563 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
1564 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); |
1565 | } |
1566 | return true; |
1567 | } |
1568 | |
1569 | bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction( |
1570 | uint8_t *data, size_t size, size_t &offset) { |
1571 | offset = 0; |
1572 | |
1573 | if (!m_register_map_initialized) |
1574 | return false; |
1575 | |
1576 | if (m_disasm_context == nullptr) |
1577 | return false; |
1578 | |
1579 | while (offset < size) { |
1580 | int regno; |
1581 | int insn_len; |
1582 | int scratch; |
1583 | |
1584 | m_cur_insn = data + offset; |
1585 | if (!instruction_length(insn_p: m_cur_insn, length&: insn_len, buffer_remaining_bytes: size - offset) |
1586 | || insn_len > kMaxInstructionByteSize |
1587 | || insn_len == 0) { |
1588 | // An error parsing the instruction, i.e. probably data/garbage - stop |
1589 | // scanning |
1590 | break; |
1591 | } |
1592 | |
1593 | if (push_rbp_pattern_p() || mov_rsp_rbp_pattern_p() || |
1594 | sub_rsp_pattern_p(amount&: scratch) || push_reg_p(regno) || |
1595 | mov_reg_to_local_stack_frame_p(regno, rbp_offset&: scratch) || |
1596 | (lea_rsp_pattern_p(amount&: scratch) && offset == 0)) { |
1597 | offset += insn_len; |
1598 | continue; |
1599 | } |
1600 | // |
1601 | // Unknown non-prologue instruction - stop scanning |
1602 | break; |
1603 | } |
1604 | |
1605 | return true; |
1606 | } |
1607 | |