1 | //===-- EmulateInstruction.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 "lldb/Core/EmulateInstruction.h" |
10 | |
11 | #include "lldb/Core/Address.h" |
12 | #include "lldb/Core/DumpRegisterValue.h" |
13 | #include "lldb/Core/PluginManager.h" |
14 | #include "lldb/Host/StreamFile.h" |
15 | #include "lldb/Symbol/UnwindPlan.h" |
16 | #include "lldb/Target/Process.h" |
17 | #include "lldb/Target/RegisterContext.h" |
18 | #include "lldb/Target/StackFrame.h" |
19 | #include "lldb/Utility/ConstString.h" |
20 | #include "lldb/Utility/DataExtractor.h" |
21 | #include "lldb/Utility/RegisterValue.h" |
22 | #include "lldb/Utility/Status.h" |
23 | #include "lldb/Utility/Stream.h" |
24 | #include "lldb/Utility/StreamString.h" |
25 | #include "lldb/lldb-forward.h" |
26 | #include "lldb/lldb-private-interfaces.h" |
27 | |
28 | #include "llvm/ADT/StringRef.h" |
29 | |
30 | #include <cstring> |
31 | #include <memory> |
32 | #include <optional> |
33 | |
34 | #include <cinttypes> |
35 | #include <cstdio> |
36 | |
37 | namespace lldb_private { |
38 | class Target; |
39 | } |
40 | |
41 | using namespace lldb; |
42 | using namespace lldb_private; |
43 | |
44 | EmulateInstruction * |
45 | EmulateInstruction::FindPlugin(const ArchSpec &arch, |
46 | InstructionType supported_inst_type, |
47 | const char *plugin_name) { |
48 | EmulateInstructionCreateInstance create_callback = nullptr; |
49 | if (plugin_name) { |
50 | create_callback = |
51 | PluginManager::GetEmulateInstructionCreateCallbackForPluginName( |
52 | name: plugin_name); |
53 | if (create_callback) { |
54 | EmulateInstruction *emulate_insn_ptr = |
55 | create_callback(arch, supported_inst_type); |
56 | if (emulate_insn_ptr) |
57 | return emulate_insn_ptr; |
58 | } |
59 | } else { |
60 | for (uint32_t idx = 0; |
61 | (create_callback = |
62 | PluginManager::GetEmulateInstructionCreateCallbackAtIndex(idx)) != |
63 | nullptr; |
64 | ++idx) { |
65 | EmulateInstruction *emulate_insn_ptr = |
66 | create_callback(arch, supported_inst_type); |
67 | if (emulate_insn_ptr) |
68 | return emulate_insn_ptr; |
69 | } |
70 | } |
71 | return nullptr; |
72 | } |
73 | |
74 | EmulateInstruction::EmulateInstruction(const ArchSpec &arch) : m_arch(arch) {} |
75 | |
76 | std::optional<RegisterValue> |
77 | EmulateInstruction::ReadRegister(const RegisterInfo ®_info) { |
78 | if (m_read_reg_callback == nullptr) |
79 | return {}; |
80 | |
81 | RegisterValue reg_value; |
82 | bool success = m_read_reg_callback(this, m_baton, ®_info, reg_value); |
83 | if (success) |
84 | return reg_value; |
85 | return {}; |
86 | } |
87 | |
88 | bool EmulateInstruction::ReadRegister(lldb::RegisterKind reg_kind, |
89 | uint32_t reg_num, |
90 | RegisterValue ®_value) { |
91 | std::optional<RegisterInfo> reg_info = GetRegisterInfo(reg_kind, reg_num); |
92 | if (!reg_info) |
93 | return false; |
94 | |
95 | std::optional<RegisterValue> value = ReadRegister(reg_info: *reg_info); |
96 | if (value) |
97 | reg_value = *value; |
98 | return value.has_value(); |
99 | } |
100 | |
101 | uint64_t EmulateInstruction::ReadRegisterUnsigned(lldb::RegisterKind reg_kind, |
102 | uint32_t reg_num, |
103 | uint64_t fail_value, |
104 | bool *success_ptr) { |
105 | RegisterValue reg_value; |
106 | if (ReadRegister(reg_kind, reg_num, reg_value)) |
107 | return reg_value.GetAsUInt64(fail_value, success_ptr); |
108 | if (success_ptr) |
109 | *success_ptr = false; |
110 | return fail_value; |
111 | } |
112 | |
113 | uint64_t EmulateInstruction::ReadRegisterUnsigned(const RegisterInfo ®_info, |
114 | uint64_t fail_value, |
115 | bool *success_ptr) { |
116 | std::optional<RegisterValue> reg_value = ReadRegister(reg_info); |
117 | if (!reg_value) { |
118 | if (success_ptr) |
119 | *success_ptr = false; |
120 | return fail_value; |
121 | } |
122 | |
123 | return reg_value->GetAsUInt64(fail_value, success_ptr); |
124 | } |
125 | |
126 | bool EmulateInstruction::WriteRegister(const Context &context, |
127 | const RegisterInfo ®_info, |
128 | const RegisterValue ®_value) { |
129 | if (m_write_reg_callback != nullptr) |
130 | return m_write_reg_callback(this, m_baton, context, ®_info, reg_value); |
131 | return false; |
132 | } |
133 | |
134 | bool EmulateInstruction::WriteRegister(const Context &context, |
135 | lldb::RegisterKind reg_kind, |
136 | uint32_t reg_num, |
137 | const RegisterValue ®_value) { |
138 | std::optional<RegisterInfo> reg_info = GetRegisterInfo(reg_kind, reg_num); |
139 | if (reg_info) |
140 | return WriteRegister(context, reg_info: *reg_info, reg_value); |
141 | return false; |
142 | } |
143 | |
144 | bool EmulateInstruction::WriteRegisterUnsigned(const Context &context, |
145 | lldb::RegisterKind reg_kind, |
146 | uint32_t reg_num, |
147 | uint64_t uint_value) { |
148 | std::optional<RegisterInfo> reg_info = GetRegisterInfo(reg_kind, reg_num); |
149 | if (reg_info) { |
150 | RegisterValue reg_value; |
151 | if (reg_value.SetUInt(uint: uint_value, byte_size: reg_info->byte_size)) |
152 | return WriteRegister(context, reg_info: *reg_info, reg_value); |
153 | } |
154 | return false; |
155 | } |
156 | |
157 | bool EmulateInstruction::WriteRegisterUnsigned(const Context &context, |
158 | const RegisterInfo ®_info, |
159 | uint64_t uint_value) { |
160 | RegisterValue reg_value; |
161 | if (reg_value.SetUInt(uint: uint_value, byte_size: reg_info.byte_size)) |
162 | return WriteRegister(context, reg_info, reg_value); |
163 | return false; |
164 | } |
165 | |
166 | size_t EmulateInstruction::ReadMemory(const Context &context, lldb::addr_t addr, |
167 | void *dst, size_t dst_len) { |
168 | if (m_read_mem_callback != nullptr) |
169 | return m_read_mem_callback(this, m_baton, context, addr, dst, dst_len) == |
170 | dst_len; |
171 | return false; |
172 | } |
173 | |
174 | uint64_t EmulateInstruction::ReadMemoryUnsigned(const Context &context, |
175 | lldb::addr_t addr, |
176 | size_t byte_size, |
177 | uint64_t fail_value, |
178 | bool *success_ptr) { |
179 | uint64_t uval64 = 0; |
180 | bool success = false; |
181 | if (byte_size <= 8) { |
182 | uint8_t buf[sizeof(uint64_t)]; |
183 | size_t bytes_read = |
184 | m_read_mem_callback(this, m_baton, context, addr, buf, byte_size); |
185 | if (bytes_read == byte_size) { |
186 | lldb::offset_t offset = 0; |
187 | DataExtractor data(buf, byte_size, GetByteOrder(), GetAddressByteSize()); |
188 | uval64 = data.GetMaxU64(offset_ptr: &offset, byte_size); |
189 | success = true; |
190 | } |
191 | } |
192 | |
193 | if (success_ptr) |
194 | *success_ptr = success; |
195 | |
196 | if (!success) |
197 | uval64 = fail_value; |
198 | return uval64; |
199 | } |
200 | |
201 | bool EmulateInstruction::WriteMemoryUnsigned(const Context &context, |
202 | lldb::addr_t addr, uint64_t uval, |
203 | size_t uval_byte_size) { |
204 | StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder()); |
205 | strm.PutMaxHex64(uvalue: uval, byte_size: uval_byte_size); |
206 | |
207 | size_t bytes_written = m_write_mem_callback( |
208 | this, m_baton, context, addr, strm.GetString().data(), uval_byte_size); |
209 | return (bytes_written == uval_byte_size); |
210 | } |
211 | |
212 | bool EmulateInstruction::WriteMemory(const Context &context, lldb::addr_t addr, |
213 | const void *src, size_t src_len) { |
214 | if (m_write_mem_callback != nullptr) |
215 | return m_write_mem_callback(this, m_baton, context, addr, src, src_len) == |
216 | src_len; |
217 | return false; |
218 | } |
219 | |
220 | void EmulateInstruction::SetBaton(void *baton) { m_baton = baton; } |
221 | |
222 | void EmulateInstruction::SetCallbacks( |
223 | ReadMemoryCallback read_mem_callback, |
224 | WriteMemoryCallback write_mem_callback, |
225 | ReadRegisterCallback read_reg_callback, |
226 | WriteRegisterCallback write_reg_callback) { |
227 | m_read_mem_callback = read_mem_callback; |
228 | m_write_mem_callback = write_mem_callback; |
229 | m_read_reg_callback = read_reg_callback; |
230 | m_write_reg_callback = write_reg_callback; |
231 | } |
232 | |
233 | void EmulateInstruction::SetReadMemCallback( |
234 | ReadMemoryCallback read_mem_callback) { |
235 | m_read_mem_callback = read_mem_callback; |
236 | } |
237 | |
238 | void EmulateInstruction::SetWriteMemCallback( |
239 | WriteMemoryCallback write_mem_callback) { |
240 | m_write_mem_callback = write_mem_callback; |
241 | } |
242 | |
243 | void EmulateInstruction::SetReadRegCallback( |
244 | ReadRegisterCallback read_reg_callback) { |
245 | m_read_reg_callback = read_reg_callback; |
246 | } |
247 | |
248 | void EmulateInstruction::SetWriteRegCallback( |
249 | WriteRegisterCallback write_reg_callback) { |
250 | m_write_reg_callback = write_reg_callback; |
251 | } |
252 | |
253 | // |
254 | // Read & Write Memory and Registers callback functions. |
255 | // |
256 | |
257 | size_t EmulateInstruction::ReadMemoryFrame(EmulateInstruction *instruction, |
258 | void *baton, const Context &context, |
259 | lldb::addr_t addr, void *dst, |
260 | size_t dst_len) { |
261 | if (baton == nullptr || dst == nullptr || dst_len == 0) |
262 | return 0; |
263 | |
264 | StackFrame *frame = (StackFrame *)baton; |
265 | |
266 | ProcessSP process_sp(frame->CalculateProcess()); |
267 | if (process_sp) { |
268 | Status error; |
269 | return process_sp->ReadMemory(vm_addr: addr, buf: dst, size: dst_len, error); |
270 | } |
271 | return 0; |
272 | } |
273 | |
274 | size_t EmulateInstruction::WriteMemoryFrame(EmulateInstruction *instruction, |
275 | void *baton, const Context &context, |
276 | lldb::addr_t addr, const void *src, |
277 | size_t src_len) { |
278 | if (baton == nullptr || src == nullptr || src_len == 0) |
279 | return 0; |
280 | |
281 | StackFrame *frame = (StackFrame *)baton; |
282 | |
283 | ProcessSP process_sp(frame->CalculateProcess()); |
284 | if (process_sp) { |
285 | Status error; |
286 | return process_sp->WriteMemory(vm_addr: addr, buf: src, size: src_len, error); |
287 | } |
288 | |
289 | return 0; |
290 | } |
291 | |
292 | bool EmulateInstruction::ReadRegisterFrame(EmulateInstruction *instruction, |
293 | void *baton, |
294 | const RegisterInfo *reg_info, |
295 | RegisterValue ®_value) { |
296 | if (baton == nullptr) |
297 | return false; |
298 | |
299 | StackFrame *frame = (StackFrame *)baton; |
300 | return frame->GetRegisterContext()->ReadRegister(reg_info, reg_value); |
301 | } |
302 | |
303 | bool EmulateInstruction::WriteRegisterFrame(EmulateInstruction *instruction, |
304 | void *baton, const Context &context, |
305 | const RegisterInfo *reg_info, |
306 | const RegisterValue ®_value) { |
307 | if (baton == nullptr) |
308 | return false; |
309 | |
310 | StackFrame *frame = (StackFrame *)baton; |
311 | return frame->GetRegisterContext()->WriteRegister(reg_info, reg_value); |
312 | } |
313 | |
314 | size_t EmulateInstruction::ReadMemoryDefault(EmulateInstruction *instruction, |
315 | void *baton, |
316 | const Context &context, |
317 | lldb::addr_t addr, void *dst, |
318 | size_t length) { |
319 | StreamFile strm(stdout, false); |
320 | strm.Printf(format: " Read from Memory (address = 0x%" PRIx64 ", length = %" PRIu64 |
321 | ", context = " , |
322 | addr, (uint64_t)length); |
323 | context.Dump(s&: strm, instruction); |
324 | strm.EOL(); |
325 | *((uint64_t *)dst) = 0xdeadbeef; |
326 | return length; |
327 | } |
328 | |
329 | size_t EmulateInstruction::WriteMemoryDefault(EmulateInstruction *instruction, |
330 | void *baton, |
331 | const Context &context, |
332 | lldb::addr_t addr, |
333 | const void *dst, size_t length) { |
334 | StreamFile strm(stdout, false); |
335 | strm.Printf(format: " Write to Memory (address = 0x%" PRIx64 ", length = %" PRIu64 |
336 | ", context = " , |
337 | addr, (uint64_t)length); |
338 | context.Dump(s&: strm, instruction); |
339 | strm.EOL(); |
340 | return length; |
341 | } |
342 | |
343 | bool EmulateInstruction::ReadRegisterDefault(EmulateInstruction *instruction, |
344 | void *baton, |
345 | const RegisterInfo *reg_info, |
346 | RegisterValue ®_value) { |
347 | StreamFile strm(stdout, false); |
348 | strm.Printf(format: " Read Register (%s)\n" , reg_info->name); |
349 | lldb::RegisterKind reg_kind; |
350 | uint32_t reg_num; |
351 | if (GetBestRegisterKindAndNumber(reg_info, reg_kind, reg_num)) |
352 | reg_value.SetUInt64(uint: (uint64_t)reg_kind << 24 | reg_num); |
353 | else |
354 | reg_value.SetUInt64(uint: 0); |
355 | |
356 | return true; |
357 | } |
358 | |
359 | bool EmulateInstruction::WriteRegisterDefault(EmulateInstruction *instruction, |
360 | void *baton, |
361 | const Context &context, |
362 | const RegisterInfo *reg_info, |
363 | const RegisterValue ®_value) { |
364 | StreamFile strm(stdout, false); |
365 | strm.Printf(format: " Write to Register (name = %s, value = " , reg_info->name); |
366 | DumpRegisterValue(reg_val: reg_value, s&: strm, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format: eFormatDefault); |
367 | strm.PutCString(cstr: ", context = " ); |
368 | context.Dump(s&: strm, instruction); |
369 | strm.EOL(); |
370 | return true; |
371 | } |
372 | |
373 | void EmulateInstruction::Context::Dump(Stream &strm, |
374 | EmulateInstruction *instruction) const { |
375 | switch (type) { |
376 | case eContextReadOpcode: |
377 | strm.PutCString(cstr: "reading opcode" ); |
378 | break; |
379 | |
380 | case eContextImmediate: |
381 | strm.PutCString(cstr: "immediate" ); |
382 | break; |
383 | |
384 | case eContextPushRegisterOnStack: |
385 | strm.PutCString(cstr: "push register" ); |
386 | break; |
387 | |
388 | case eContextPopRegisterOffStack: |
389 | strm.PutCString(cstr: "pop register" ); |
390 | break; |
391 | |
392 | case eContextAdjustStackPointer: |
393 | strm.PutCString(cstr: "adjust sp" ); |
394 | break; |
395 | |
396 | case eContextSetFramePointer: |
397 | strm.PutCString(cstr: "set frame pointer" ); |
398 | break; |
399 | |
400 | case eContextAdjustBaseRegister: |
401 | strm.PutCString(cstr: "adjusting (writing value back to) a base register" ); |
402 | break; |
403 | |
404 | case eContextRegisterPlusOffset: |
405 | strm.PutCString(cstr: "register + offset" ); |
406 | break; |
407 | |
408 | case eContextRegisterStore: |
409 | strm.PutCString(cstr: "store register" ); |
410 | break; |
411 | |
412 | case eContextRegisterLoad: |
413 | strm.PutCString(cstr: "load register" ); |
414 | break; |
415 | |
416 | case eContextRelativeBranchImmediate: |
417 | strm.PutCString(cstr: "relative branch immediate" ); |
418 | break; |
419 | |
420 | case eContextAbsoluteBranchRegister: |
421 | strm.PutCString(cstr: "absolute branch register" ); |
422 | break; |
423 | |
424 | case eContextSupervisorCall: |
425 | strm.PutCString(cstr: "supervisor call" ); |
426 | break; |
427 | |
428 | case eContextTableBranchReadMemory: |
429 | strm.PutCString(cstr: "table branch read memory" ); |
430 | break; |
431 | |
432 | case eContextWriteRegisterRandomBits: |
433 | strm.PutCString(cstr: "write random bits to a register" ); |
434 | break; |
435 | |
436 | case eContextWriteMemoryRandomBits: |
437 | strm.PutCString(cstr: "write random bits to a memory address" ); |
438 | break; |
439 | |
440 | case eContextArithmetic: |
441 | strm.PutCString(cstr: "arithmetic" ); |
442 | break; |
443 | |
444 | case eContextReturnFromException: |
445 | strm.PutCString(cstr: "return from exception" ); |
446 | break; |
447 | |
448 | default: |
449 | strm.PutCString(cstr: "unrecognized context." ); |
450 | break; |
451 | } |
452 | |
453 | switch (GetInfoType()) { |
454 | case eInfoTypeRegisterPlusOffset: |
455 | strm.Printf(format: " (reg_plus_offset = %s%+" PRId64 ")" , |
456 | info.RegisterPlusOffset.reg.name, |
457 | info.RegisterPlusOffset.signed_offset); |
458 | break; |
459 | |
460 | case eInfoTypeRegisterPlusIndirectOffset: |
461 | strm.Printf(format: " (reg_plus_reg = %s + %s)" , |
462 | info.RegisterPlusIndirectOffset.base_reg.name, |
463 | info.RegisterPlusIndirectOffset.offset_reg.name); |
464 | break; |
465 | |
466 | case eInfoTypeRegisterToRegisterPlusOffset: |
467 | strm.Printf(format: " (base_and_imm_offset = %s%+" PRId64 ", data_reg = %s)" , |
468 | info.RegisterToRegisterPlusOffset.base_reg.name, |
469 | info.RegisterToRegisterPlusOffset.offset, |
470 | info.RegisterToRegisterPlusOffset.data_reg.name); |
471 | break; |
472 | |
473 | case eInfoTypeRegisterToRegisterPlusIndirectOffset: |
474 | strm.Printf(format: " (base_and_reg_offset = %s + %s, data_reg = %s)" , |
475 | info.RegisterToRegisterPlusIndirectOffset.base_reg.name, |
476 | info.RegisterToRegisterPlusIndirectOffset.offset_reg.name, |
477 | info.RegisterToRegisterPlusIndirectOffset.data_reg.name); |
478 | break; |
479 | |
480 | case eInfoTypeRegisterRegisterOperands: |
481 | strm.Printf(format: " (register to register binary op: %s and %s)" , |
482 | info.RegisterRegisterOperands.operand1.name, |
483 | info.RegisterRegisterOperands.operand2.name); |
484 | break; |
485 | |
486 | case eInfoTypeOffset: |
487 | strm.Printf(format: " (signed_offset = %+" PRId64 ")" , info.signed_offset); |
488 | break; |
489 | |
490 | case eInfoTypeRegister: |
491 | strm.Printf(format: " (reg = %s)" , info.reg.name); |
492 | break; |
493 | |
494 | case eInfoTypeImmediate: |
495 | strm.Printf(format: " (unsigned_immediate = %" PRIu64 " (0x%16.16" PRIx64 "))" , |
496 | info.unsigned_immediate, info.unsigned_immediate); |
497 | break; |
498 | |
499 | case eInfoTypeImmediateSigned: |
500 | strm.Printf(format: " (signed_immediate = %+" PRId64 " (0x%16.16" PRIx64 "))" , |
501 | info.signed_immediate, info.signed_immediate); |
502 | break; |
503 | |
504 | case eInfoTypeAddress: |
505 | strm.Printf(format: " (address = 0x%" PRIx64 ")" , info.address); |
506 | break; |
507 | |
508 | case eInfoTypeISAAndImmediate: |
509 | strm.Printf(format: " (isa = %u, unsigned_immediate = %u (0x%8.8x))" , |
510 | info.ISAAndImmediate.isa, info.ISAAndImmediate.unsigned_data32, |
511 | info.ISAAndImmediate.unsigned_data32); |
512 | break; |
513 | |
514 | case eInfoTypeISAAndImmediateSigned: |
515 | strm.Printf(format: " (isa = %u, signed_immediate = %i (0x%8.8x))" , |
516 | info.ISAAndImmediateSigned.isa, |
517 | info.ISAAndImmediateSigned.signed_data32, |
518 | info.ISAAndImmediateSigned.signed_data32); |
519 | break; |
520 | |
521 | case eInfoTypeISA: |
522 | strm.Printf(format: " (isa = %u)" , info.isa); |
523 | break; |
524 | |
525 | case eInfoTypeNoArgs: |
526 | break; |
527 | } |
528 | } |
529 | |
530 | bool EmulateInstruction::SetInstruction(const Opcode &opcode, |
531 | const Address &inst_addr, |
532 | Target *target) { |
533 | m_opcode = opcode; |
534 | m_addr = LLDB_INVALID_ADDRESS; |
535 | if (inst_addr.IsValid()) { |
536 | if (target != nullptr) |
537 | m_addr = inst_addr.GetLoadAddress(target); |
538 | if (m_addr == LLDB_INVALID_ADDRESS) |
539 | m_addr = inst_addr.GetFileAddress(); |
540 | } |
541 | return true; |
542 | } |
543 | |
544 | bool EmulateInstruction::GetBestRegisterKindAndNumber( |
545 | const RegisterInfo *reg_info, lldb::RegisterKind ®_kind, |
546 | uint32_t ®_num) { |
547 | // Generic and DWARF should be the two most popular register kinds when |
548 | // emulating instructions since they are the most platform agnostic... |
549 | reg_num = reg_info->kinds[eRegisterKindGeneric]; |
550 | if (reg_num != LLDB_INVALID_REGNUM) { |
551 | reg_kind = eRegisterKindGeneric; |
552 | return true; |
553 | } |
554 | |
555 | reg_num = reg_info->kinds[eRegisterKindDWARF]; |
556 | if (reg_num != LLDB_INVALID_REGNUM) { |
557 | reg_kind = eRegisterKindDWARF; |
558 | return true; |
559 | } |
560 | |
561 | reg_num = reg_info->kinds[eRegisterKindLLDB]; |
562 | if (reg_num != LLDB_INVALID_REGNUM) { |
563 | reg_kind = eRegisterKindLLDB; |
564 | return true; |
565 | } |
566 | |
567 | reg_num = reg_info->kinds[eRegisterKindEHFrame]; |
568 | if (reg_num != LLDB_INVALID_REGNUM) { |
569 | reg_kind = eRegisterKindEHFrame; |
570 | return true; |
571 | } |
572 | |
573 | reg_num = reg_info->kinds[eRegisterKindProcessPlugin]; |
574 | if (reg_num != LLDB_INVALID_REGNUM) { |
575 | reg_kind = eRegisterKindProcessPlugin; |
576 | return true; |
577 | } |
578 | return false; |
579 | } |
580 | |
581 | uint32_t |
582 | EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx, |
583 | const RegisterInfo ®_info) { |
584 | lldb::RegisterKind reg_kind; |
585 | uint32_t reg_num; |
586 | if (reg_ctx && GetBestRegisterKindAndNumber(reg_info: ®_info, reg_kind, reg_num)) |
587 | return reg_ctx->ConvertRegisterKindToRegisterNumber(kind: reg_kind, num: reg_num); |
588 | return LLDB_INVALID_REGNUM; |
589 | } |
590 | |
591 | bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) { |
592 | unwind_plan.Clear(); |
593 | return false; |
594 | } |
595 | |