/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ABI/X86/ABISysV_x86_64.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ABISysV_x86_64.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 "ABISysV_x86_64.h" |
10 | | |
11 | | #include "llvm/ADT/STLExtras.h" |
12 | | #include "llvm/ADT/StringSwitch.h" |
13 | | #include "llvm/TargetParser/Triple.h" |
14 | | |
15 | | #include "lldb/Core/Module.h" |
16 | | #include "lldb/Core/PluginManager.h" |
17 | | #include "lldb/Core/Value.h" |
18 | | #include "lldb/Core/ValueObjectConstResult.h" |
19 | | #include "lldb/Core/ValueObjectMemory.h" |
20 | | #include "lldb/Core/ValueObjectRegister.h" |
21 | | #include "lldb/Symbol/UnwindPlan.h" |
22 | | #include "lldb/Target/Process.h" |
23 | | #include "lldb/Target/RegisterContext.h" |
24 | | #include "lldb/Target/StackFrame.h" |
25 | | #include "lldb/Target/Target.h" |
26 | | #include "lldb/Target/Thread.h" |
27 | | #include "lldb/Utility/ConstString.h" |
28 | | #include "lldb/Utility/DataExtractor.h" |
29 | | #include "lldb/Utility/LLDBLog.h" |
30 | | #include "lldb/Utility/Log.h" |
31 | | #include "lldb/Utility/RegisterValue.h" |
32 | | #include "lldb/Utility/Status.h" |
33 | | |
34 | | #include <optional> |
35 | | #include <vector> |
36 | | |
37 | | using namespace lldb; |
38 | | using namespace lldb_private; |
39 | | |
40 | | LLDB_PLUGIN_DEFINE(ABISysV_x86_64) |
41 | | |
42 | | enum dwarf_regnums { |
43 | | dwarf_rax = 0, |
44 | | dwarf_rdx, |
45 | | dwarf_rcx, |
46 | | dwarf_rbx, |
47 | | dwarf_rsi, |
48 | | dwarf_rdi, |
49 | | dwarf_rbp, |
50 | | dwarf_rsp, |
51 | | dwarf_r8, |
52 | | dwarf_r9, |
53 | | dwarf_r10, |
54 | | dwarf_r11, |
55 | | dwarf_r12, |
56 | | dwarf_r13, |
57 | | dwarf_r14, |
58 | | dwarf_r15, |
59 | | dwarf_rip, |
60 | | }; |
61 | | |
62 | 4 | bool ABISysV_x86_64::GetPointerReturnRegister(const char *&name) { |
63 | 4 | name = "rax"; |
64 | 4 | return true; |
65 | 4 | } |
66 | | |
67 | 3.40k | size_t ABISysV_x86_64::GetRedZoneSize() const { return 128; } |
68 | | |
69 | | // Static Functions |
70 | | |
71 | | ABISP |
72 | 4.63k | ABISysV_x86_64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) { |
73 | 4.63k | const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch(); |
74 | 4.63k | const llvm::Triple::OSType os_type = arch.GetTriple().getOS(); |
75 | 4.63k | const llvm::Triple::EnvironmentType os_env = |
76 | 4.63k | arch.GetTriple().getEnvironment(); |
77 | 4.63k | if (arch_type == llvm::Triple::x86_64) { |
78 | 4.54k | switch(os_type) { |
79 | 8 | case llvm::Triple::OSType::IOS: |
80 | 8 | case llvm::Triple::OSType::TvOS: |
81 | 10 | case llvm::Triple::OSType::WatchOS: |
82 | 10 | switch (os_env) { |
83 | 6 | case llvm::Triple::EnvironmentType::MacABI: |
84 | 10 | case llvm::Triple::EnvironmentType::Simulator: |
85 | 10 | case llvm::Triple::EnvironmentType::UnknownEnvironment: |
86 | | // UnknownEnvironment is needed for older compilers that don't |
87 | | // support the simulator environment. |
88 | 10 | return ABISP(new ABISysV_x86_64(std::move(process_sp), |
89 | 10 | MakeMCRegisterInfo(arch))); |
90 | 0 | default: |
91 | 0 | return ABISP(); |
92 | 10 | } |
93 | 0 | case llvm::Triple::OSType::Darwin: |
94 | 4 | case llvm::Triple::OSType::FreeBSD: |
95 | 55 | case llvm::Triple::OSType::Linux: |
96 | 4.35k | case llvm::Triple::OSType::MacOSX: |
97 | 4.36k | case llvm::Triple::OSType::NetBSD: |
98 | 4.36k | case llvm::Triple::OSType::Solaris: |
99 | 4.52k | case llvm::Triple::OSType::UnknownOS: |
100 | 4.52k | return ABISP( |
101 | 4.52k | new ABISysV_x86_64(std::move(process_sp), MakeMCRegisterInfo(arch))); |
102 | 4 | default: |
103 | 4 | return ABISP(); |
104 | 4.54k | } |
105 | 4.54k | } |
106 | 93 | return ABISP(); |
107 | 4.63k | } |
108 | | |
109 | | bool ABISysV_x86_64::PrepareTrivialCall(Thread &thread, addr_t sp, |
110 | | addr_t func_addr, addr_t return_addr, |
111 | 3.39k | llvm::ArrayRef<addr_t> args) const { |
112 | 3.39k | Log *log = GetLog(LLDBLog::Expressions); |
113 | | |
114 | 3.39k | if (log) { |
115 | 8 | StreamString s; |
116 | 8 | s.Printf("ABISysV_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64 |
117 | 8 | ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 |
118 | 8 | ", return_addr = 0x%" PRIx64, |
119 | 8 | thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, |
120 | 8 | (uint64_t)return_addr); |
121 | | |
122 | 16 | for (size_t i = 0; i < args.size(); ++i8 ) |
123 | 8 | s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1), |
124 | 8 | args[i]); |
125 | 8 | s.PutCString(")"); |
126 | 8 | log->PutString(s.GetString()); |
127 | 8 | } |
128 | | |
129 | 3.39k | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
130 | 3.39k | if (!reg_ctx) |
131 | 0 | return false; |
132 | | |
133 | 3.39k | const RegisterInfo *reg_info = nullptr; |
134 | | |
135 | 3.39k | if (args.size() > 6) // TODO handle more than 6 arguments |
136 | 0 | return false; |
137 | | |
138 | 6.86k | for (size_t i = 0; 3.39k i < args.size(); ++i3.46k ) { |
139 | 3.46k | reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, |
140 | 3.46k | LLDB_REGNUM_GENERIC_ARG1 + i); |
141 | 3.46k | LLDB_LOGF(log, "About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s", |
142 | 3.46k | static_cast<uint64_t>(i + 1), args[i], reg_info->name); |
143 | 3.46k | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) |
144 | 0 | return false; |
145 | 3.46k | } |
146 | | |
147 | | // First, align the SP |
148 | | |
149 | 3.39k | LLDB_LOGF(log, "16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, |
150 | 3.39k | (uint64_t)sp, (uint64_t)(sp & ~0xfull)); |
151 | | |
152 | 3.39k | sp &= ~(0xfull); // 16-byte alignment |
153 | | |
154 | 3.39k | sp -= 8; |
155 | | |
156 | 3.39k | Status error; |
157 | 3.39k | const RegisterInfo *pc_reg_info = |
158 | 3.39k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
159 | 3.39k | const RegisterInfo *sp_reg_info = |
160 | 3.39k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); |
161 | 3.39k | ProcessSP process_sp(thread.GetProcess()); |
162 | | |
163 | 3.39k | RegisterValue reg_value; |
164 | 3.39k | LLDB_LOGF(log, |
165 | 3.39k | "Pushing the return address onto the stack: 0x%" PRIx64 |
166 | 3.39k | ": 0x%" PRIx64, |
167 | 3.39k | (uint64_t)sp, (uint64_t)return_addr); |
168 | | |
169 | | // Save return address onto the stack |
170 | 3.39k | if (!process_sp->WritePointerToMemory(sp, return_addr, error)) |
171 | 0 | return false; |
172 | | |
173 | | // %rsp is set to the actual stack value. |
174 | | |
175 | 3.39k | LLDB_LOGF(log, "Writing SP: 0x%" PRIx64, (uint64_t)sp); |
176 | | |
177 | 3.39k | if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp)) |
178 | 0 | return false; |
179 | | |
180 | | // %rip is set to the address of the called function. |
181 | | |
182 | 3.39k | LLDB_LOGF(log, "Writing IP: 0x%" PRIx64, (uint64_t)func_addr); |
183 | | |
184 | 3.39k | if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr)) |
185 | 0 | return false; |
186 | | |
187 | 3.39k | return true; |
188 | 3.39k | } |
189 | | |
190 | | static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width, |
191 | | bool is_signed, Thread &thread, |
192 | | uint32_t *argument_register_ids, |
193 | | unsigned int ¤t_argument_register, |
194 | 6.60k | addr_t ¤t_stack_argument) { |
195 | 6.60k | if (bit_width > 64) |
196 | 0 | return false; // Scalar can't hold large integer arguments |
197 | | |
198 | 6.60k | if (current_argument_register < 6) { |
199 | 6.60k | scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned( |
200 | 6.60k | argument_register_ids[current_argument_register], 0); |
201 | 6.60k | current_argument_register++; |
202 | 6.60k | if (is_signed) |
203 | 0 | scalar.SignExtend(bit_width); |
204 | 6.60k | } else { |
205 | 0 | uint32_t byte_size = (bit_width + (8 - 1)) / 8; |
206 | 0 | Status error; |
207 | 0 | if (thread.GetProcess()->ReadScalarIntegerFromMemory( |
208 | 0 | current_stack_argument, byte_size, is_signed, scalar, error)) { |
209 | 0 | current_stack_argument += byte_size; |
210 | 0 | return true; |
211 | 0 | } |
212 | 0 | return false; |
213 | 0 | } |
214 | 6.60k | return true; |
215 | 6.60k | } |
216 | | |
217 | | bool ABISysV_x86_64::GetArgumentValues(Thread &thread, |
218 | 2.21k | ValueList &values) const { |
219 | 2.21k | unsigned int num_values = values.GetSize(); |
220 | 2.21k | unsigned int value_index; |
221 | | |
222 | | // Extract the register context so we can read arguments from registers |
223 | | |
224 | 2.21k | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
225 | | |
226 | 2.21k | if (!reg_ctx) |
227 | 0 | return false; |
228 | | |
229 | | // Get the pointer to the first stack argument so we have a place to start |
230 | | // when reading data |
231 | | |
232 | 2.21k | addr_t sp = reg_ctx->GetSP(0); |
233 | | |
234 | 2.21k | if (!sp) |
235 | 0 | return false; |
236 | | |
237 | 2.21k | addr_t current_stack_argument = sp + 8; // jump over return address |
238 | | |
239 | 2.21k | uint32_t argument_register_ids[6]; |
240 | | |
241 | 2.21k | argument_register_ids[0] = |
242 | 2.21k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1) |
243 | 2.21k | ->kinds[eRegisterKindLLDB]; |
244 | 2.21k | argument_register_ids[1] = |
245 | 2.21k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2) |
246 | 2.21k | ->kinds[eRegisterKindLLDB]; |
247 | 2.21k | argument_register_ids[2] = |
248 | 2.21k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3) |
249 | 2.21k | ->kinds[eRegisterKindLLDB]; |
250 | 2.21k | argument_register_ids[3] = |
251 | 2.21k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4) |
252 | 2.21k | ->kinds[eRegisterKindLLDB]; |
253 | 2.21k | argument_register_ids[4] = |
254 | 2.21k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5) |
255 | 2.21k | ->kinds[eRegisterKindLLDB]; |
256 | 2.21k | argument_register_ids[5] = |
257 | 2.21k | reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6) |
258 | 2.21k | ->kinds[eRegisterKindLLDB]; |
259 | | |
260 | 2.21k | unsigned int current_argument_register = 0; |
261 | | |
262 | 8.82k | for (value_index = 0; value_index < num_values; ++value_index6.60k ) { |
263 | 6.60k | Value *value = values.GetValueAtIndex(value_index); |
264 | | |
265 | 6.60k | if (!value) |
266 | 0 | return false; |
267 | | |
268 | | // We currently only support extracting values with Clang QualTypes. Do we |
269 | | // care about others? |
270 | 6.60k | CompilerType compiler_type = value->GetCompilerType(); |
271 | 6.60k | std::optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread); |
272 | 6.60k | if (!bit_size) |
273 | 0 | return false; |
274 | 6.60k | bool is_signed; |
275 | | |
276 | 6.60k | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { |
277 | 4.35k | ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread, |
278 | 4.35k | argument_register_ids, current_argument_register, |
279 | 4.35k | current_stack_argument); |
280 | 4.35k | } else if (2.25k compiler_type.IsPointerType()2.25k ) { |
281 | 2.25k | ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread, |
282 | 2.25k | argument_register_ids, current_argument_register, |
283 | 2.25k | current_stack_argument); |
284 | 2.25k | } |
285 | 6.60k | } |
286 | | |
287 | 2.21k | return true; |
288 | 2.21k | } |
289 | | |
290 | | Status ABISysV_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, |
291 | 0 | lldb::ValueObjectSP &new_value_sp) { |
292 | 0 | Status error; |
293 | 0 | if (!new_value_sp) { |
294 | 0 | error.SetErrorString("Empty value object for return value."); |
295 | 0 | return error; |
296 | 0 | } |
297 | | |
298 | 0 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
299 | 0 | if (!compiler_type) { |
300 | 0 | error.SetErrorString("Null clang type for return value."); |
301 | 0 | return error; |
302 | 0 | } |
303 | | |
304 | 0 | Thread *thread = frame_sp->GetThread().get(); |
305 | |
|
306 | 0 | bool is_signed; |
307 | 0 | uint32_t count; |
308 | 0 | bool is_complex; |
309 | |
|
310 | 0 | RegisterContext *reg_ctx = thread->GetRegisterContext().get(); |
311 | |
|
312 | 0 | bool set_it_simple = false; |
313 | 0 | if (compiler_type.IsIntegerOrEnumerationType(is_signed) || |
314 | 0 | compiler_type.IsPointerType()) { |
315 | 0 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0); |
316 | |
|
317 | 0 | DataExtractor data; |
318 | 0 | Status data_error; |
319 | 0 | size_t num_bytes = new_value_sp->GetData(data, data_error); |
320 | 0 | if (data_error.Fail()) { |
321 | 0 | error.SetErrorStringWithFormat( |
322 | 0 | "Couldn't convert return value to raw data: %s", |
323 | 0 | data_error.AsCString()); |
324 | 0 | return error; |
325 | 0 | } |
326 | 0 | lldb::offset_t offset = 0; |
327 | 0 | if (num_bytes <= 8) { |
328 | 0 | uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); |
329 | |
|
330 | 0 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value)) |
331 | 0 | set_it_simple = true; |
332 | 0 | } else { |
333 | 0 | error.SetErrorString("We don't support returning longer than 64 bit " |
334 | 0 | "integer values at present."); |
335 | 0 | } |
336 | 0 | } else if (compiler_type.IsFloatingPointType(count, is_complex)) { |
337 | 0 | if (is_complex) |
338 | 0 | error.SetErrorString( |
339 | 0 | "We don't support returning complex values at present"); |
340 | 0 | else { |
341 | 0 | std::optional<uint64_t> bit_width = |
342 | 0 | compiler_type.GetBitSize(frame_sp.get()); |
343 | 0 | if (!bit_width) { |
344 | 0 | error.SetErrorString("can't get type size"); |
345 | 0 | return error; |
346 | 0 | } |
347 | 0 | if (*bit_width <= 64) { |
348 | 0 | const RegisterInfo *xmm0_info = |
349 | 0 | reg_ctx->GetRegisterInfoByName("xmm0", 0); |
350 | 0 | RegisterValue xmm0_value; |
351 | 0 | DataExtractor data; |
352 | 0 | Status data_error; |
353 | 0 | size_t num_bytes = new_value_sp->GetData(data, data_error); |
354 | 0 | if (data_error.Fail()) { |
355 | 0 | error.SetErrorStringWithFormat( |
356 | 0 | "Couldn't convert return value to raw data: %s", |
357 | 0 | data_error.AsCString()); |
358 | 0 | return error; |
359 | 0 | } |
360 | | |
361 | 0 | unsigned char buffer[16]; |
362 | 0 | ByteOrder byte_order = data.GetByteOrder(); |
363 | |
|
364 | 0 | data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order); |
365 | 0 | xmm0_value.SetBytes(buffer, 16, byte_order); |
366 | 0 | reg_ctx->WriteRegister(xmm0_info, xmm0_value); |
367 | 0 | set_it_simple = true; |
368 | 0 | } else { |
369 | | // FIXME - don't know how to do 80 bit long doubles yet. |
370 | 0 | error.SetErrorString( |
371 | 0 | "We don't support returning float values > 64 bits at present"); |
372 | 0 | } |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | 0 | if (!set_it_simple) { |
377 | | // Okay we've got a structure or something that doesn't fit in a simple |
378 | | // register. We should figure out where it really goes, but we don't |
379 | | // support this yet. |
380 | 0 | error.SetErrorString("We only support setting simple integer and float " |
381 | 0 | "return types at present."); |
382 | 0 | } |
383 | |
|
384 | 0 | return error; |
385 | 0 | } |
386 | | |
387 | | ValueObjectSP ABISysV_x86_64::GetReturnValueObjectSimple( |
388 | 156 | Thread &thread, CompilerType &return_compiler_type) const { |
389 | 156 | ValueObjectSP return_valobj_sp; |
390 | 156 | Value value; |
391 | | |
392 | 156 | if (!return_compiler_type) |
393 | 0 | return return_valobj_sp; |
394 | | |
395 | | // value.SetContext (Value::eContextTypeClangType, return_value_type); |
396 | 156 | value.SetCompilerType(return_compiler_type); |
397 | | |
398 | 156 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
399 | 156 | if (!reg_ctx) |
400 | 0 | return return_valobj_sp; |
401 | | |
402 | 156 | const uint32_t type_flags = return_compiler_type.GetTypeInfo(); |
403 | 156 | if (type_flags & eTypeIsScalar) { |
404 | 42 | value.SetValueType(Value::ValueType::Scalar); |
405 | | |
406 | 42 | bool success = false; |
407 | 42 | if (type_flags & eTypeIsInteger) { |
408 | | // Extract the register context so we can read arguments from registers |
409 | | |
410 | 36 | std::optional<uint64_t> byte_size = |
411 | 36 | return_compiler_type.GetByteSize(&thread); |
412 | 36 | if (!byte_size) |
413 | 0 | return return_valobj_sp; |
414 | 36 | uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned( |
415 | 36 | reg_ctx->GetRegisterInfoByName("rax", 0), 0); |
416 | 36 | const bool is_signed = (type_flags & eTypeIsSigned) != 0; |
417 | 36 | switch (*byte_size) { |
418 | 0 | default: |
419 | 0 | break; |
420 | | |
421 | 0 | case sizeof(uint64_t): |
422 | 0 | if (is_signed) |
423 | 0 | value.GetScalar() = (int64_t)(raw_value); |
424 | 0 | else |
425 | 0 | value.GetScalar() = (uint64_t)(raw_value); |
426 | 0 | success = true; |
427 | 0 | break; |
428 | | |
429 | 36 | case sizeof(uint32_t): |
430 | 36 | if (is_signed) |
431 | 36 | value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); |
432 | 0 | else |
433 | 0 | value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); |
434 | 36 | success = true; |
435 | 36 | break; |
436 | | |
437 | 0 | case sizeof(uint16_t): |
438 | 0 | if (is_signed) |
439 | 0 | value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); |
440 | 0 | else |
441 | 0 | value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); |
442 | 0 | success = true; |
443 | 0 | break; |
444 | | |
445 | 0 | case sizeof(uint8_t): |
446 | 0 | if (is_signed) |
447 | 0 | value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); |
448 | 0 | else |
449 | 0 | value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); |
450 | 0 | success = true; |
451 | 0 | break; |
452 | 36 | } |
453 | 36 | } else if (6 type_flags & eTypeIsFloat6 ) { |
454 | 6 | if (type_flags & eTypeIsComplex) { |
455 | | // Don't handle complex yet. |
456 | 6 | } else { |
457 | 6 | std::optional<uint64_t> byte_size = |
458 | 6 | return_compiler_type.GetByteSize(&thread); |
459 | 6 | if (byte_size && *byte_size <= sizeof(long double)) { |
460 | 6 | const RegisterInfo *xmm0_info = |
461 | 6 | reg_ctx->GetRegisterInfoByName("xmm0", 0); |
462 | 6 | RegisterValue xmm0_value; |
463 | 6 | if (reg_ctx->ReadRegister(xmm0_info, xmm0_value)) { |
464 | 6 | DataExtractor data; |
465 | 6 | if (xmm0_value.GetData(data)) { |
466 | 6 | lldb::offset_t offset = 0; |
467 | 6 | if (*byte_size == sizeof(float)) { |
468 | 2 | value.GetScalar() = (float)data.GetFloat(&offset); |
469 | 2 | success = true; |
470 | 4 | } else if (*byte_size == sizeof(double)) { |
471 | 4 | value.GetScalar() = (double)data.GetDouble(&offset); |
472 | 4 | success = true; |
473 | 4 | } else if (0 *byte_size == sizeof(long double)0 ) { |
474 | | // Don't handle long double since that can be encoded as 80 bit |
475 | | // floats... |
476 | 0 | } |
477 | 6 | } |
478 | 6 | } |
479 | 6 | } |
480 | 6 | } |
481 | 6 | } |
482 | | |
483 | 42 | if (success) |
484 | 42 | return_valobj_sp = ValueObjectConstResult::Create( |
485 | 42 | thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
486 | 114 | } else if (type_flags & eTypeIsPointer) { |
487 | 32 | unsigned rax_id = |
488 | 32 | reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; |
489 | 32 | value.GetScalar() = |
490 | 32 | (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, |
491 | 32 | 0); |
492 | 32 | value.SetValueType(Value::ValueType::Scalar); |
493 | 32 | return_valobj_sp = ValueObjectConstResult::Create( |
494 | 32 | thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
495 | 82 | } else if (type_flags & eTypeIsVector) { |
496 | 12 | std::optional<uint64_t> byte_size = |
497 | 12 | return_compiler_type.GetByteSize(&thread); |
498 | 12 | if (byte_size && *byte_size > 0) { |
499 | 12 | const RegisterInfo *altivec_reg = |
500 | 12 | reg_ctx->GetRegisterInfoByName("xmm0", 0); |
501 | 12 | if (altivec_reg == nullptr) |
502 | 0 | altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0); |
503 | | |
504 | 12 | if (altivec_reg) { |
505 | 12 | if (*byte_size <= altivec_reg->byte_size) { |
506 | 8 | ProcessSP process_sp(thread.GetProcess()); |
507 | 8 | if (process_sp) { |
508 | 8 | std::unique_ptr<DataBufferHeap> heap_data_up( |
509 | 8 | new DataBufferHeap(*byte_size, 0)); |
510 | 8 | const ByteOrder byte_order = process_sp->GetByteOrder(); |
511 | 8 | RegisterValue reg_value; |
512 | 8 | if (reg_ctx->ReadRegister(altivec_reg, reg_value)) { |
513 | 8 | Status error; |
514 | 8 | if (reg_value.GetAsMemoryData( |
515 | 8 | *altivec_reg, heap_data_up->GetBytes(), |
516 | 8 | heap_data_up->GetByteSize(), byte_order, error)) { |
517 | 8 | DataExtractor data(DataBufferSP(heap_data_up.release()), |
518 | 8 | byte_order, |
519 | 8 | process_sp->GetTarget() |
520 | 8 | .GetArchitecture() |
521 | 8 | .GetAddressByteSize()); |
522 | 8 | return_valobj_sp = ValueObjectConstResult::Create( |
523 | 8 | &thread, return_compiler_type, ConstString(""), data); |
524 | 8 | } |
525 | 8 | } |
526 | 8 | } |
527 | 8 | } else if (4 *byte_size <= altivec_reg->byte_size * 24 ) { |
528 | 4 | const RegisterInfo *altivec_reg2 = |
529 | 4 | reg_ctx->GetRegisterInfoByName("xmm1", 0); |
530 | 4 | if (altivec_reg2) { |
531 | 4 | ProcessSP process_sp(thread.GetProcess()); |
532 | 4 | if (process_sp) { |
533 | 4 | std::unique_ptr<DataBufferHeap> heap_data_up( |
534 | 4 | new DataBufferHeap(*byte_size, 0)); |
535 | 4 | const ByteOrder byte_order = process_sp->GetByteOrder(); |
536 | 4 | RegisterValue reg_value; |
537 | 4 | RegisterValue reg_value2; |
538 | 4 | if (reg_ctx->ReadRegister(altivec_reg, reg_value) && |
539 | 4 | reg_ctx->ReadRegister(altivec_reg2, reg_value2)) { |
540 | | |
541 | 4 | Status error; |
542 | 4 | if (reg_value.GetAsMemoryData( |
543 | 4 | *altivec_reg, heap_data_up->GetBytes(), |
544 | 4 | altivec_reg->byte_size, byte_order, error) && |
545 | 4 | reg_value2.GetAsMemoryData( |
546 | 4 | *altivec_reg2, |
547 | 4 | heap_data_up->GetBytes() + altivec_reg->byte_size, |
548 | 4 | heap_data_up->GetByteSize() - altivec_reg->byte_size, |
549 | 4 | byte_order, error)) { |
550 | 4 | DataExtractor data(DataBufferSP(heap_data_up.release()), |
551 | 4 | byte_order, |
552 | 4 | process_sp->GetTarget() |
553 | 4 | .GetArchitecture() |
554 | 4 | .GetAddressByteSize()); |
555 | 4 | return_valobj_sp = ValueObjectConstResult::Create( |
556 | 4 | &thread, return_compiler_type, ConstString(""), data); |
557 | 4 | } |
558 | 4 | } |
559 | 4 | } |
560 | 4 | } |
561 | 4 | } |
562 | 12 | } |
563 | 12 | } |
564 | 12 | } |
565 | | |
566 | 156 | return return_valobj_sp; |
567 | 156 | } |
568 | | |
569 | | // The compiler will flatten the nested aggregate type into single |
570 | | // layer and push the value to stack |
571 | | // This helper function will flatten an aggregate type |
572 | | // and return true if it can be returned in register(s) by value |
573 | | // return false if the aggregate is in memory |
574 | | static bool FlattenAggregateType( |
575 | | Thread &thread, ExecutionContext &exe_ctx, |
576 | | CompilerType &return_compiler_type, |
577 | | uint32_t data_byte_offset, |
578 | | std::vector<uint32_t> &aggregate_field_offsets, |
579 | 69 | std::vector<CompilerType> &aggregate_compiler_types) { |
580 | | |
581 | 69 | const uint32_t num_children = return_compiler_type.GetNumFields(); |
582 | 208 | for (uint32_t idx = 0; idx < num_children; ++idx139 ) { |
583 | 139 | std::string name; |
584 | 139 | bool is_signed; |
585 | 139 | uint32_t count; |
586 | 139 | bool is_complex; |
587 | | |
588 | 139 | uint64_t field_bit_offset = 0; |
589 | 139 | CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex( |
590 | 139 | idx, name, &field_bit_offset, nullptr, nullptr); |
591 | 139 | std::optional<uint64_t> field_bit_width = |
592 | 139 | field_compiler_type.GetBitSize(&thread); |
593 | | |
594 | | // if we don't know the size of the field (e.g. invalid type), exit |
595 | 139 | if (!field_bit_width || *field_bit_width == 0) { |
596 | 0 | return false; |
597 | 0 | } |
598 | | |
599 | 139 | uint32_t field_byte_offset = field_bit_offset / 8 + data_byte_offset; |
600 | | |
601 | 139 | const uint32_t field_type_flags = field_compiler_type.GetTypeInfo(); |
602 | 139 | if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) || |
603 | 139 | field_compiler_type.IsPointerType()76 || |
604 | 139 | field_compiler_type.IsFloatingPointType(count, is_complex)66 ) { |
605 | 117 | aggregate_field_offsets.push_back(field_byte_offset); |
606 | 117 | aggregate_compiler_types.push_back(field_compiler_type); |
607 | 117 | } else if (22 field_type_flags & eTypeHasChildren22 ) { |
608 | 22 | if (!FlattenAggregateType(thread, exe_ctx, field_compiler_type, |
609 | 22 | field_byte_offset, aggregate_field_offsets, |
610 | 22 | aggregate_compiler_types)) { |
611 | 0 | return false; |
612 | 0 | } |
613 | 22 | } |
614 | 139 | } |
615 | 69 | return true; |
616 | 69 | } |
617 | | |
618 | | ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl( |
619 | 156 | Thread &thread, CompilerType &return_compiler_type) const { |
620 | 156 | ValueObjectSP return_valobj_sp; |
621 | | |
622 | 156 | if (!return_compiler_type) |
623 | 0 | return return_valobj_sp; |
624 | | |
625 | 156 | ExecutionContext exe_ctx(thread.shared_from_this()); |
626 | 156 | return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type); |
627 | 156 | if (return_valobj_sp) |
628 | 86 | return return_valobj_sp; |
629 | | |
630 | 70 | RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); |
631 | 70 | if (!reg_ctx_sp) |
632 | 0 | return return_valobj_sp; |
633 | | |
634 | 70 | std::optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread); |
635 | 70 | if (!bit_width) |
636 | 14 | return return_valobj_sp; |
637 | 56 | if (return_compiler_type.IsAggregateType()) { |
638 | 56 | Target *target = exe_ctx.GetTargetPtr(); |
639 | 56 | bool is_memory = true; |
640 | 56 | std::vector<uint32_t> aggregate_field_offsets; |
641 | 56 | std::vector<CompilerType> aggregate_compiler_types; |
642 | 56 | auto ts = return_compiler_type.GetTypeSystem(); |
643 | 56 | if (ts && ts->CanPassInRegisters(return_compiler_type) && |
644 | 56 | *bit_width <= 12853 && |
645 | 56 | FlattenAggregateType(thread, exe_ctx, return_compiler_type, 0, |
646 | 47 | aggregate_field_offsets, |
647 | 47 | aggregate_compiler_types)) { |
648 | 47 | ByteOrder byte_order = target->GetArchitecture().GetByteOrder(); |
649 | 47 | WritableDataBufferSP data_sp(new DataBufferHeap(16, 0)); |
650 | 47 | DataExtractor return_ext(data_sp, byte_order, |
651 | 47 | target->GetArchitecture().GetAddressByteSize()); |
652 | | |
653 | 47 | const RegisterInfo *rax_info = |
654 | 47 | reg_ctx_sp->GetRegisterInfoByName("rax", 0); |
655 | 47 | const RegisterInfo *rdx_info = |
656 | 47 | reg_ctx_sp->GetRegisterInfoByName("rdx", 0); |
657 | 47 | const RegisterInfo *xmm0_info = |
658 | 47 | reg_ctx_sp->GetRegisterInfoByName("xmm0", 0); |
659 | 47 | const RegisterInfo *xmm1_info = |
660 | 47 | reg_ctx_sp->GetRegisterInfoByName("xmm1", 0); |
661 | | |
662 | 47 | RegisterValue rax_value, rdx_value, xmm0_value, xmm1_value; |
663 | 47 | reg_ctx_sp->ReadRegister(rax_info, rax_value); |
664 | 47 | reg_ctx_sp->ReadRegister(rdx_info, rdx_value); |
665 | 47 | reg_ctx_sp->ReadRegister(xmm0_info, xmm0_value); |
666 | 47 | reg_ctx_sp->ReadRegister(xmm1_info, xmm1_value); |
667 | | |
668 | 47 | DataExtractor rax_data, rdx_data, xmm0_data, xmm1_data; |
669 | | |
670 | 47 | rax_value.GetData(rax_data); |
671 | 47 | rdx_value.GetData(rdx_data); |
672 | 47 | xmm0_value.GetData(xmm0_data); |
673 | 47 | xmm1_value.GetData(xmm1_data); |
674 | | |
675 | 47 | uint32_t fp_bytes = |
676 | 47 | 0; // Tracks how much of the xmm registers we've consumed so far |
677 | 47 | uint32_t integer_bytes = |
678 | 47 | 0; // Tracks how much of the rax/rds registers we've consumed so far |
679 | | |
680 | | // in case of the returned type is a subclass of non-abstract-base class |
681 | | // it will have a padding to skip the base content |
682 | 47 | if (aggregate_field_offsets.size()) { |
683 | 47 | fp_bytes = aggregate_field_offsets[0]; |
684 | 47 | integer_bytes = aggregate_field_offsets[0]; |
685 | 47 | } |
686 | | |
687 | 47 | const uint32_t num_children = aggregate_compiler_types.size(); |
688 | | |
689 | | // Since we are in the small struct regime, assume we are not in memory. |
690 | 47 | is_memory = false; |
691 | 164 | for (uint32_t idx = 0; idx < num_children; idx++117 ) { |
692 | 117 | bool is_signed; |
693 | 117 | uint32_t count; |
694 | 117 | bool is_complex; |
695 | | |
696 | 117 | CompilerType field_compiler_type = aggregate_compiler_types[idx]; |
697 | 117 | uint32_t field_byte_width = (uint32_t) (*field_compiler_type.GetByteSize(&thread)); |
698 | 117 | uint32_t field_byte_offset = aggregate_field_offsets[idx]; |
699 | | |
700 | 117 | uint32_t field_bit_width = field_byte_width * 8; |
701 | | |
702 | 117 | DataExtractor *copy_from_extractor = nullptr; |
703 | 117 | uint32_t copy_from_offset = 0; |
704 | | |
705 | 117 | if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) || |
706 | 117 | field_compiler_type.IsPointerType()54 ) { |
707 | 73 | if (integer_bytes < 8) { |
708 | 63 | if (integer_bytes + field_byte_width <= 8) { |
709 | | // This is in RAX, copy from register to our result structure: |
710 | 59 | copy_from_extractor = &rax_data; |
711 | 59 | copy_from_offset = integer_bytes; |
712 | 59 | integer_bytes += field_byte_width; |
713 | 59 | } else { |
714 | | // The next field wouldn't fit in the remaining space, so we |
715 | | // pushed it to rdx. |
716 | 4 | copy_from_extractor = &rdx_data; |
717 | 4 | copy_from_offset = 0; |
718 | 4 | integer_bytes = 8 + field_byte_width; |
719 | 4 | } |
720 | 63 | } else if (10 integer_bytes + field_byte_width <= 1610 ) { |
721 | 10 | copy_from_extractor = &rdx_data; |
722 | 10 | copy_from_offset = integer_bytes - 8; |
723 | 10 | integer_bytes += field_byte_width; |
724 | 10 | } else { |
725 | | // The last field didn't fit. I can't see how that would happen |
726 | | // w/o the overall size being greater than 16 bytes. For now, |
727 | | // return a nullptr return value object. |
728 | 0 | return return_valobj_sp; |
729 | 0 | } |
730 | 73 | } else if (44 field_compiler_type.IsFloatingPointType(count, is_complex)44 ) { |
731 | | // Structs with long doubles are always passed in memory. |
732 | 44 | if (field_bit_width == 128) { |
733 | 0 | is_memory = true; |
734 | 0 | break; |
735 | 44 | } else if (field_bit_width == 64) { |
736 | | // These have to be in a single xmm register. |
737 | 10 | if (fp_bytes == 0) |
738 | 8 | copy_from_extractor = &xmm0_data; |
739 | 2 | else |
740 | 2 | copy_from_extractor = &xmm1_data; |
741 | | |
742 | 10 | copy_from_offset = 0; |
743 | 10 | fp_bytes += field_byte_width; |
744 | 34 | } else if (field_bit_width == 32) { |
745 | | // This one is kind of complicated. If we are in an "eightbyte" |
746 | | // with another float, we'll be stuffed into an xmm register with |
747 | | // it. If we are in an "eightbyte" with one or more ints, then we |
748 | | // will be stuffed into the appropriate GPR with them. |
749 | 34 | bool in_gpr; |
750 | 34 | if (field_byte_offset % 8 == 0) { |
751 | | // We are at the beginning of one of the eightbytes, so check the |
752 | | // next element (if any) |
753 | 22 | if (idx == num_children - 1) { |
754 | 4 | in_gpr = false; |
755 | 18 | } else { |
756 | 18 | CompilerType next_field_compiler_type = |
757 | 18 | aggregate_compiler_types[idx + 1]; |
758 | 18 | if (next_field_compiler_type.IsIntegerOrEnumerationType( |
759 | 18 | is_signed)) { |
760 | 6 | in_gpr = true; |
761 | 12 | } else { |
762 | 12 | copy_from_offset = 0; |
763 | 12 | in_gpr = false; |
764 | 12 | } |
765 | 18 | } |
766 | 22 | } else if (12 field_byte_offset % 4 == 012 ) { |
767 | | // We are inside of an eightbyte, so see if the field before us |
768 | | // is floating point: This could happen if somebody put padding |
769 | | // in the structure. |
770 | 12 | if (idx == 0) { |
771 | 0 | in_gpr = false; |
772 | 12 | } else { |
773 | 12 | CompilerType prev_field_compiler_type = |
774 | 12 | aggregate_compiler_types[idx - 1]; |
775 | 12 | if (prev_field_compiler_type.IsIntegerOrEnumerationType( |
776 | 12 | is_signed)) { |
777 | 2 | in_gpr = true; |
778 | 10 | } else { |
779 | 10 | copy_from_offset = 4; |
780 | 10 | in_gpr = false; |
781 | 10 | } |
782 | 12 | } |
783 | 12 | } else { |
784 | 0 | is_memory = true; |
785 | 0 | continue; |
786 | 0 | } |
787 | | |
788 | | // Okay, we've figured out whether we are in GPR or XMM, now figure |
789 | | // out which one. |
790 | 34 | if (in_gpr) { |
791 | 8 | if (integer_bytes < 8) { |
792 | | // This is in RAX, copy from register to our result structure: |
793 | 8 | copy_from_extractor = &rax_data; |
794 | 8 | copy_from_offset = integer_bytes; |
795 | 8 | integer_bytes += field_byte_width; |
796 | 8 | } else { |
797 | 0 | copy_from_extractor = &rdx_data; |
798 | 0 | copy_from_offset = integer_bytes - 8; |
799 | 0 | integer_bytes += field_byte_width; |
800 | 0 | } |
801 | 26 | } else { |
802 | 26 | if (fp_bytes < 8) |
803 | 18 | copy_from_extractor = &xmm0_data; |
804 | 8 | else |
805 | 8 | copy_from_extractor = &xmm1_data; |
806 | | |
807 | 26 | fp_bytes += field_byte_width; |
808 | 26 | } |
809 | 34 | } |
810 | 44 | } |
811 | | // These two tests are just sanity checks. If I somehow get the type |
812 | | // calculation wrong above it is better to just return nothing than to |
813 | | // assert or crash. |
814 | 117 | if (!copy_from_extractor) |
815 | 0 | return return_valobj_sp; |
816 | 117 | if (copy_from_offset + field_byte_width > |
817 | 117 | copy_from_extractor->GetByteSize()) |
818 | 0 | return return_valobj_sp; |
819 | 117 | copy_from_extractor->CopyByteOrderedData( |
820 | 117 | copy_from_offset, field_byte_width, |
821 | 117 | data_sp->GetBytes() + field_byte_offset, field_byte_width, |
822 | 117 | byte_order); |
823 | 117 | } |
824 | 47 | if (!is_memory) { |
825 | | // The result is in our data buffer. Let's make a variable object out |
826 | | // of it: |
827 | 47 | return_valobj_sp = ValueObjectConstResult::Create( |
828 | 47 | &thread, return_compiler_type, ConstString(""), return_ext); |
829 | 47 | } |
830 | 47 | } |
831 | | |
832 | | // FIXME: This is just taking a guess, rax may very well no longer hold the |
833 | | // return storage location. |
834 | | // If we are going to do this right, when we make a new frame we should |
835 | | // check to see if it uses a memory return, and if we are at the first |
836 | | // instruction and if so stash away the return location. Then we would |
837 | | // only return the memory return value if we know it is valid. |
838 | | |
839 | 56 | if (is_memory) { |
840 | 9 | unsigned rax_id = |
841 | 9 | reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; |
842 | 9 | lldb::addr_t storage_addr = |
843 | 9 | (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, |
844 | 9 | 0); |
845 | 9 | return_valobj_sp = ValueObjectMemory::Create( |
846 | 9 | &thread, "", Address(storage_addr, nullptr), return_compiler_type); |
847 | 9 | } |
848 | 56 | } |
849 | | |
850 | 56 | return return_valobj_sp; |
851 | 56 | } |
852 | | |
853 | | // This defines the CFA as rsp+8 |
854 | | // the saved pc is at CFA-8 (i.e. rsp+0) |
855 | | // The saved rsp is CFA+0 |
856 | | |
857 | 8.82k | bool ABISysV_x86_64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { |
858 | 8.82k | unwind_plan.Clear(); |
859 | 8.82k | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
860 | | |
861 | 8.82k | uint32_t sp_reg_num = dwarf_rsp; |
862 | 8.82k | uint32_t pc_reg_num = dwarf_rip; |
863 | | |
864 | 8.82k | UnwindPlan::RowSP row(new UnwindPlan::Row); |
865 | 8.82k | row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 8); |
866 | 8.82k | row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false); |
867 | 8.82k | row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); |
868 | 8.82k | unwind_plan.AppendRow(row); |
869 | 8.82k | unwind_plan.SetSourceName("x86_64 at-func-entry default"); |
870 | 8.82k | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
871 | 8.82k | return true; |
872 | 8.82k | } |
873 | | |
874 | | // This defines the CFA as rbp+16 |
875 | | // The saved pc is at CFA-8 (i.e. rbp+8) |
876 | | // The saved rbp is at CFA-16 (i.e. rbp+0) |
877 | | // The saved rsp is CFA+0 |
878 | | |
879 | 51.8k | bool ABISysV_x86_64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { |
880 | 51.8k | unwind_plan.Clear(); |
881 | 51.8k | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
882 | | |
883 | 51.8k | uint32_t fp_reg_num = dwarf_rbp; |
884 | 51.8k | uint32_t sp_reg_num = dwarf_rsp; |
885 | 51.8k | uint32_t pc_reg_num = dwarf_rip; |
886 | | |
887 | 51.8k | UnwindPlan::RowSP row(new UnwindPlan::Row); |
888 | | |
889 | 51.8k | const int32_t ptr_size = 8; |
890 | 51.8k | row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp, 2 * ptr_size); |
891 | 51.8k | row->SetOffset(0); |
892 | 51.8k | row->SetUnspecifiedRegistersAreUndefined(true); |
893 | | |
894 | 51.8k | row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); |
895 | 51.8k | row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); |
896 | 51.8k | row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); |
897 | | |
898 | 51.8k | unwind_plan.AppendRow(row); |
899 | 51.8k | unwind_plan.SetSourceName("x86_64 default unwind plan"); |
900 | 51.8k | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
901 | 51.8k | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
902 | 51.8k | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
903 | 51.8k | return true; |
904 | 51.8k | } |
905 | | |
906 | 8.61k | bool ABISysV_x86_64::RegisterIsVolatile(const RegisterInfo *reg_info) { |
907 | 8.61k | return !RegisterIsCalleeSaved(reg_info); |
908 | 8.61k | } |
909 | | |
910 | | // See "Register Usage" in the |
911 | | // "System V Application Binary Interface" |
912 | | // "AMD64 Architecture Processor Supplement" (or "x86-64(tm) Architecture |
913 | | // Processor Supplement" in earlier revisions) (this doc is also commonly |
914 | | // referred to as the x86-64/AMD64 psABI) Edited by Michael Matz, Jan Hubicka, |
915 | | // Andreas Jaeger, and Mark Mitchell current version is 0.99.6 released |
916 | | // 2012-07-02 at http://refspecs.linuxfoundation.org/elf/x86-64-abi-0.99.pdf |
917 | | // It's being revised & updated at https://github.com/hjl-tools/x86-psABI/ |
918 | | |
919 | 8.61k | bool ABISysV_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
920 | 8.61k | if (!reg_info) |
921 | 0 | return false; |
922 | 8.61k | assert(reg_info->name != nullptr && "unnamed register?"); |
923 | 8.61k | std::string Name = std::string(reg_info->name); |
924 | 8.61k | bool IsCalleeSaved = |
925 | 8.61k | llvm::StringSwitch<bool>(Name) |
926 | 8.61k | .Cases("r12", "r13", "r14", "r15", "rbp", "ebp", "rbx", "ebx", true) |
927 | 8.61k | .Cases("rip", "eip", "rsp", "esp", "sp", "fp", "pc", true) |
928 | 8.61k | .Default(false); |
929 | 8.61k | return IsCalleeSaved; |
930 | 8.61k | } |
931 | | |
932 | 247k | uint32_t ABISysV_x86_64::GetGenericNum(llvm::StringRef name) { |
933 | 247k | return llvm::StringSwitch<uint32_t>(name) |
934 | 247k | .Case("rip", LLDB_REGNUM_GENERIC_PC) |
935 | 247k | .Case("rsp", LLDB_REGNUM_GENERIC_SP) |
936 | 247k | .Case("rbp", LLDB_REGNUM_GENERIC_FP) |
937 | 247k | .Case("rflags", LLDB_REGNUM_GENERIC_FLAGS) |
938 | | // gdbserver uses eflags |
939 | 247k | .Case("eflags", LLDB_REGNUM_GENERIC_FLAGS) |
940 | 247k | .Case("rdi", LLDB_REGNUM_GENERIC_ARG1) |
941 | 247k | .Case("rsi", LLDB_REGNUM_GENERIC_ARG2) |
942 | 247k | .Case("rdx", LLDB_REGNUM_GENERIC_ARG3) |
943 | 247k | .Case("rcx", LLDB_REGNUM_GENERIC_ARG4) |
944 | 247k | .Case("r8", LLDB_REGNUM_GENERIC_ARG5) |
945 | 247k | .Case("r9", LLDB_REGNUM_GENERIC_ARG6) |
946 | 247k | .Default(LLDB_INVALID_REGNUM); |
947 | 247k | } |
948 | | |
949 | 3.92k | void ABISysV_x86_64::Initialize() { |
950 | 3.92k | PluginManager::RegisterPlugin( |
951 | 3.92k | GetPluginNameStatic(), "System V ABI for x86_64 targets", CreateInstance); |
952 | 3.92k | } |
953 | | |
954 | 3.92k | void ABISysV_x86_64::Terminate() { |
955 | 3.92k | PluginManager::UnregisterPlugin(CreateInstance); |
956 | 3.92k | } |