/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ABI/X86/ABIMacOSX_i386.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ABIMacOSX_i386.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 "ABIMacOSX_i386.h" |
10 | | |
11 | | #include <optional> |
12 | | #include <vector> |
13 | | |
14 | | #include "llvm/ADT/STLExtras.h" |
15 | | #include "llvm/TargetParser/Triple.h" |
16 | | |
17 | | #include "lldb/Core/Module.h" |
18 | | #include "lldb/Core/PluginManager.h" |
19 | | #include "lldb/Core/ValueObjectConstResult.h" |
20 | | #include "lldb/Symbol/UnwindPlan.h" |
21 | | #include "lldb/Target/Process.h" |
22 | | #include "lldb/Target/RegisterContext.h" |
23 | | #include "lldb/Target/Target.h" |
24 | | #include "lldb/Target/Thread.h" |
25 | | #include "lldb/Utility/ConstString.h" |
26 | | #include "lldb/Utility/RegisterValue.h" |
27 | | #include "lldb/Utility/Scalar.h" |
28 | | #include "lldb/Utility/Status.h" |
29 | | |
30 | | using namespace lldb; |
31 | | using namespace lldb_private; |
32 | | |
33 | | LLDB_PLUGIN_DEFINE(ABIMacOSX_i386) |
34 | | |
35 | | enum { |
36 | | dwarf_eax = 0, |
37 | | dwarf_ecx, |
38 | | dwarf_edx, |
39 | | dwarf_ebx, |
40 | | dwarf_esp, |
41 | | dwarf_ebp, |
42 | | dwarf_esi, |
43 | | dwarf_edi, |
44 | | dwarf_eip, |
45 | | }; |
46 | | |
47 | 0 | size_t ABIMacOSX_i386::GetRedZoneSize() const { return 0; } |
48 | | |
49 | | // Static Functions |
50 | | |
51 | | ABISP |
52 | 4.27k | ABIMacOSX_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) { |
53 | 4.27k | if ((arch.GetTriple().getArch() == llvm::Triple::x86) && |
54 | 4.27k | (34 arch.GetTriple().isMacOSX()34 || arch.GetTriple().isiOS()34 || |
55 | 34 | arch.GetTriple().isWatchOS())) { |
56 | 0 | return ABISP( |
57 | 0 | new ABIMacOSX_i386(std::move(process_sp), MakeMCRegisterInfo(arch))); |
58 | 0 | } |
59 | 4.27k | return ABISP(); |
60 | 4.27k | } |
61 | | |
62 | | bool ABIMacOSX_i386::PrepareTrivialCall(Thread &thread, addr_t sp, |
63 | | addr_t func_addr, addr_t return_addr, |
64 | 0 | llvm::ArrayRef<addr_t> args) const { |
65 | 0 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
66 | 0 | if (!reg_ctx) |
67 | 0 | return false; |
68 | 0 | uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber( |
69 | 0 | eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
70 | 0 | uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber( |
71 | 0 | eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); |
72 | | |
73 | | // When writing a register value down to memory, the register info used to |
74 | | // write memory just needs to have the correct size of a 32 bit register, the |
75 | | // actual register it pertains to is not important, just the size needs to be |
76 | | // correct. Here we use "eax"... |
77 | 0 | const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax"); |
78 | 0 | if (!reg_info_32) |
79 | 0 | return false; // TODO this should actually never happen |
80 | | |
81 | | // Make room for the argument(s) on the stack |
82 | | |
83 | 0 | Status error; |
84 | 0 | RegisterValue reg_value; |
85 | | |
86 | | // Write any arguments onto the stack |
87 | 0 | sp -= 4 * args.size(); |
88 | | |
89 | | // Align the SP |
90 | 0 | sp &= ~(16ull - 1ull); // 16-byte alignment |
91 | |
|
92 | 0 | addr_t arg_pos = sp; |
93 | |
|
94 | 0 | for (addr_t arg : args) { |
95 | 0 | reg_value.SetUInt32(arg); |
96 | 0 | error = reg_ctx->WriteRegisterValueToMemory( |
97 | 0 | reg_info_32, arg_pos, reg_info_32->byte_size, reg_value); |
98 | 0 | if (error.Fail()) |
99 | 0 | return false; |
100 | 0 | arg_pos += 4; |
101 | 0 | } |
102 | | |
103 | | // The return address is pushed onto the stack (yes after we just set the |
104 | | // alignment above!). |
105 | 0 | sp -= 4; |
106 | 0 | reg_value.SetUInt32(return_addr); |
107 | 0 | error = reg_ctx->WriteRegisterValueToMemory( |
108 | 0 | reg_info_32, sp, reg_info_32->byte_size, reg_value); |
109 | 0 | if (error.Fail()) |
110 | 0 | return false; |
111 | | |
112 | | // %esp is set to the actual stack value. |
113 | | |
114 | 0 | if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp)) |
115 | 0 | return false; |
116 | | |
117 | | // %eip is set to the address of the called function. |
118 | | |
119 | 0 | if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr)) |
120 | 0 | return false; |
121 | | |
122 | 0 | return true; |
123 | 0 | } |
124 | | |
125 | | static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width, |
126 | | bool is_signed, Process *process, |
127 | 0 | addr_t ¤t_stack_argument) { |
128 | |
|
129 | 0 | uint32_t byte_size = (bit_width + (8 - 1)) / 8; |
130 | 0 | Status error; |
131 | 0 | if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, |
132 | 0 | is_signed, scalar, error)) { |
133 | 0 | current_stack_argument += byte_size; |
134 | 0 | return true; |
135 | 0 | } |
136 | 0 | return false; |
137 | 0 | } |
138 | | |
139 | | bool ABIMacOSX_i386::GetArgumentValues(Thread &thread, |
140 | 0 | ValueList &values) const { |
141 | 0 | unsigned int num_values = values.GetSize(); |
142 | 0 | unsigned int value_index; |
143 | | |
144 | | // Get the pointer to the first stack argument so we have a place to start |
145 | | // when reading data |
146 | |
|
147 | 0 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
148 | |
|
149 | 0 | if (!reg_ctx) |
150 | 0 | return false; |
151 | | |
152 | 0 | addr_t sp = reg_ctx->GetSP(0); |
153 | |
|
154 | 0 | if (!sp) |
155 | 0 | return false; |
156 | | |
157 | 0 | addr_t current_stack_argument = sp + 4; // jump over return address |
158 | |
|
159 | 0 | for (value_index = 0; value_index < num_values; ++value_index) { |
160 | 0 | Value *value = values.GetValueAtIndex(value_index); |
161 | |
|
162 | 0 | if (!value) |
163 | 0 | return false; |
164 | | |
165 | | // We currently only support extracting values with Clang QualTypes. Do we |
166 | | // care about others? |
167 | 0 | CompilerType compiler_type(value->GetCompilerType()); |
168 | 0 | std::optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread); |
169 | 0 | if (bit_size) { |
170 | 0 | bool is_signed; |
171 | 0 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) |
172 | 0 | ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, |
173 | 0 | thread.GetProcess().get(), current_stack_argument); |
174 | 0 | else if (compiler_type.IsPointerType()) |
175 | 0 | ReadIntegerArgument(value->GetScalar(), *bit_size, false, |
176 | 0 | thread.GetProcess().get(), current_stack_argument); |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | 0 | return true; |
181 | 0 | } |
182 | | |
183 | | Status ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, |
184 | 0 | lldb::ValueObjectSP &new_value_sp) { |
185 | 0 | Status error; |
186 | 0 | if (!new_value_sp) { |
187 | 0 | error.SetErrorString("Empty value object for return value."); |
188 | 0 | return error; |
189 | 0 | } |
190 | | |
191 | 0 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
192 | 0 | if (!compiler_type) { |
193 | 0 | error.SetErrorString("Null clang type for return value."); |
194 | 0 | return error; |
195 | 0 | } |
196 | | |
197 | 0 | Thread *thread = frame_sp->GetThread().get(); |
198 | |
|
199 | 0 | bool is_signed; |
200 | 0 | uint32_t count; |
201 | 0 | bool is_complex; |
202 | |
|
203 | 0 | RegisterContext *reg_ctx = thread->GetRegisterContext().get(); |
204 | |
|
205 | 0 | bool set_it_simple = false; |
206 | 0 | if (compiler_type.IsIntegerOrEnumerationType(is_signed) || |
207 | 0 | compiler_type.IsPointerType()) { |
208 | 0 | DataExtractor data; |
209 | 0 | Status data_error; |
210 | 0 | size_t num_bytes = new_value_sp->GetData(data, data_error); |
211 | 0 | if (data_error.Fail()) { |
212 | 0 | error.SetErrorStringWithFormat( |
213 | 0 | "Couldn't convert return value to raw data: %s", |
214 | 0 | data_error.AsCString()); |
215 | 0 | return error; |
216 | 0 | } |
217 | 0 | lldb::offset_t offset = 0; |
218 | 0 | if (num_bytes <= 8) { |
219 | 0 | const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0); |
220 | 0 | if (num_bytes <= 4) { |
221 | 0 | uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); |
222 | |
|
223 | 0 | if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value)) |
224 | 0 | set_it_simple = true; |
225 | 0 | } else { |
226 | 0 | uint32_t raw_value = data.GetMaxU32(&offset, 4); |
227 | |
|
228 | 0 | if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value)) { |
229 | 0 | const RegisterInfo *edx_info = |
230 | 0 | reg_ctx->GetRegisterInfoByName("edx", 0); |
231 | 0 | uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset); |
232 | |
|
233 | 0 | if (reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value)) |
234 | 0 | set_it_simple = true; |
235 | 0 | } |
236 | 0 | } |
237 | 0 | } else { |
238 | 0 | error.SetErrorString("We don't support returning longer than 64 bit " |
239 | 0 | "integer values at present."); |
240 | 0 | } |
241 | 0 | } else if (compiler_type.IsFloatingPointType(count, is_complex)) { |
242 | 0 | if (is_complex) |
243 | 0 | error.SetErrorString( |
244 | 0 | "We don't support returning complex values at present"); |
245 | 0 | else |
246 | 0 | error.SetErrorString( |
247 | 0 | "We don't support returning float values at present"); |
248 | 0 | } |
249 | | |
250 | 0 | if (!set_it_simple) |
251 | 0 | error.SetErrorString( |
252 | 0 | "We only support setting simple integer return types at present."); |
253 | |
|
254 | 0 | return error; |
255 | 0 | } |
256 | | |
257 | | ValueObjectSP |
258 | | ABIMacOSX_i386::GetReturnValueObjectImpl(Thread &thread, |
259 | 0 | CompilerType &compiler_type) const { |
260 | 0 | Value value; |
261 | 0 | ValueObjectSP return_valobj_sp; |
262 | |
|
263 | 0 | if (!compiler_type) |
264 | 0 | return return_valobj_sp; |
265 | | |
266 | | // value.SetContext (Value::eContextTypeClangType, |
267 | | // compiler_type.GetOpaqueQualType()); |
268 | 0 | value.SetCompilerType(compiler_type); |
269 | |
|
270 | 0 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
271 | 0 | if (!reg_ctx) |
272 | 0 | return return_valobj_sp; |
273 | | |
274 | 0 | bool is_signed; |
275 | |
|
276 | 0 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { |
277 | 0 | std::optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread); |
278 | 0 | if (!bit_width) |
279 | 0 | return return_valobj_sp; |
280 | 0 | unsigned eax_id = |
281 | 0 | reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; |
282 | 0 | unsigned edx_id = |
283 | 0 | reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB]; |
284 | |
|
285 | 0 | switch (*bit_width) { |
286 | 0 | default: |
287 | 0 | case 128: |
288 | | // Scalar can't hold 128-bit literals, so we don't handle this |
289 | 0 | return return_valobj_sp; |
290 | 0 | case 64: |
291 | 0 | uint64_t raw_value; |
292 | 0 | raw_value = |
293 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
294 | 0 | 0xffffffff; |
295 | 0 | raw_value |= |
296 | 0 | (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) & |
297 | 0 | 0xffffffff) |
298 | 0 | << 32; |
299 | 0 | if (is_signed) |
300 | 0 | value.GetScalar() = (int64_t)raw_value; |
301 | 0 | else |
302 | 0 | value.GetScalar() = (uint64_t)raw_value; |
303 | 0 | break; |
304 | 0 | case 32: |
305 | 0 | if (is_signed) |
306 | 0 | value.GetScalar() = (int32_t)( |
307 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
308 | 0 | 0xffffffff); |
309 | 0 | else |
310 | 0 | value.GetScalar() = (uint32_t)( |
311 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
312 | 0 | 0xffffffff); |
313 | 0 | break; |
314 | 0 | case 16: |
315 | 0 | if (is_signed) |
316 | 0 | value.GetScalar() = (int16_t)( |
317 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
318 | 0 | 0xffff); |
319 | 0 | else |
320 | 0 | value.GetScalar() = (uint16_t)( |
321 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
322 | 0 | 0xffff); |
323 | 0 | break; |
324 | 0 | case 8: |
325 | 0 | if (is_signed) |
326 | 0 | value.GetScalar() = (int8_t)( |
327 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
328 | 0 | 0xff); |
329 | 0 | else |
330 | 0 | value.GetScalar() = (uint8_t)( |
331 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
332 | 0 | 0xff); |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | } else if (compiler_type.IsPointerType()) { |
336 | 0 | unsigned eax_id = |
337 | 0 | reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; |
338 | 0 | uint32_t ptr = |
339 | 0 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
340 | 0 | 0xffffffff; |
341 | 0 | value.GetScalar() = ptr; |
342 | 0 | } else { |
343 | | // not handled yet |
344 | 0 | return return_valobj_sp; |
345 | 0 | } |
346 | | |
347 | | // If we get here, we have a valid Value, so make our ValueObject out of it: |
348 | | |
349 | 0 | return_valobj_sp = ValueObjectConstResult::Create( |
350 | 0 | thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
351 | 0 | return return_valobj_sp; |
352 | 0 | } |
353 | | |
354 | | // This defines the CFA as esp+4 |
355 | | // the saved pc is at CFA-4 (i.e. esp+0) |
356 | | // The saved esp is CFA+0 |
357 | | |
358 | 0 | bool ABIMacOSX_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { |
359 | 0 | unwind_plan.Clear(); |
360 | 0 | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
361 | |
|
362 | 0 | uint32_t sp_reg_num = dwarf_esp; |
363 | 0 | uint32_t pc_reg_num = dwarf_eip; |
364 | |
|
365 | 0 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
366 | 0 | row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4); |
367 | 0 | row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false); |
368 | 0 | row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); |
369 | 0 | unwind_plan.AppendRow(row); |
370 | 0 | unwind_plan.SetSourceName("i386 at-func-entry default"); |
371 | 0 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
372 | 0 | return true; |
373 | 0 | } |
374 | | |
375 | | // This defines the CFA as ebp+8 |
376 | | // The saved pc is at CFA-4 (i.e. ebp+4) |
377 | | // The saved ebp is at CFA-8 (i.e. ebp+0) |
378 | | // The saved esp is CFA+0 |
379 | | |
380 | 0 | bool ABIMacOSX_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { |
381 | 0 | unwind_plan.Clear(); |
382 | 0 | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
383 | |
|
384 | 0 | uint32_t fp_reg_num = dwarf_ebp; |
385 | 0 | uint32_t sp_reg_num = dwarf_esp; |
386 | 0 | uint32_t pc_reg_num = dwarf_eip; |
387 | |
|
388 | 0 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
389 | 0 | const int32_t ptr_size = 4; |
390 | |
|
391 | 0 | row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size); |
392 | 0 | row->SetOffset(0); |
393 | 0 | row->SetUnspecifiedRegistersAreUndefined(true); |
394 | |
|
395 | 0 | row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); |
396 | 0 | row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); |
397 | 0 | row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); |
398 | |
|
399 | 0 | unwind_plan.AppendRow(row); |
400 | 0 | unwind_plan.SetSourceName("i386 default unwind plan"); |
401 | 0 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
402 | 0 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
403 | 0 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
404 | 0 | return true; |
405 | 0 | } |
406 | | |
407 | 0 | bool ABIMacOSX_i386::RegisterIsVolatile(const RegisterInfo *reg_info) { |
408 | 0 | return !RegisterIsCalleeSaved(reg_info); |
409 | 0 | } |
410 | | |
411 | | // v. |
412 | | // http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130 |
413 | | // -IA- |
414 | | // 32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4 |
415 | | // |
416 | | // This document ("OS X ABI Function Call Guide", chapter "IA-32 Function |
417 | | // Calling Conventions") says that the following registers on i386 are |
418 | | // preserved aka non-volatile aka callee-saved: |
419 | | // |
420 | | // ebx, ebp, esi, edi, esp |
421 | | |
422 | 0 | bool ABIMacOSX_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
423 | 0 | if (reg_info) { |
424 | | // Saved registers are ebx, ebp, esi, edi, esp, eip |
425 | 0 | const char *name = reg_info->name; |
426 | 0 | if (name[0] == 'e') { |
427 | 0 | switch (name[1]) { |
428 | 0 | case 'b': |
429 | 0 | if (name[2] == 'x' || name[2] == 'p') |
430 | 0 | return name[3] == '\0'; |
431 | 0 | break; |
432 | 0 | case 'd': |
433 | 0 | if (name[2] == 'i') |
434 | 0 | return name[3] == '\0'; |
435 | 0 | break; |
436 | 0 | case 'i': |
437 | 0 | if (name[2] == 'p') |
438 | 0 | return name[3] == '\0'; |
439 | 0 | break; |
440 | 0 | case 's': |
441 | 0 | if (name[2] == 'i' || name[2] == 'p') |
442 | 0 | return name[3] == '\0'; |
443 | 0 | break; |
444 | 0 | } |
445 | 0 | } |
446 | 0 | if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp |
447 | 0 | return true; |
448 | 0 | if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp |
449 | 0 | return true; |
450 | 0 | if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc |
451 | 0 | return true; |
452 | 0 | } |
453 | 0 | return false; |
454 | 0 | } |
455 | | |
456 | 3.91k | void ABIMacOSX_i386::Initialize() { |
457 | 3.91k | PluginManager::RegisterPlugin( |
458 | 3.91k | GetPluginNameStatic(), "Mac OS X ABI for i386 targets", CreateInstance); |
459 | 3.91k | } |
460 | | |
461 | 3.90k | void ABIMacOSX_i386::Terminate() { |
462 | 3.90k | PluginManager::UnregisterPlugin(CreateInstance); |
463 | 3.90k | } |