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 | size_t ABIMacOSX_i386::GetRedZoneSize() const { return 0; } |
48 | |
49 | // Static Functions |
50 | |
51 | ABISP |
52 | ABIMacOSX_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) { |
53 | if ((arch.GetTriple().getArch() == llvm::Triple::x86) && |
54 | (arch.GetTriple().isMacOSX() || arch.GetTriple().isiOS() || |
55 | arch.GetTriple().isWatchOS())) { |
56 | return ABISP( |
57 | new ABIMacOSX_i386(std::move(process_sp), MakeMCRegisterInfo(arch))); |
58 | } |
59 | return ABISP(); |
60 | } |
61 | |
62 | bool ABIMacOSX_i386::PrepareTrivialCall(Thread &thread, addr_t sp, |
63 | addr_t func_addr, addr_t return_addr, |
64 | llvm::ArrayRef<addr_t> args) const { |
65 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
66 | if (!reg_ctx) |
67 | return false; |
68 | uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber( |
69 | kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
70 | uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber( |
71 | kind: 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 | const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName(reg_name: "eax" ); |
78 | if (!reg_info_32) |
79 | return false; // TODO this should actually never happen |
80 | |
81 | // Make room for the argument(s) on the stack |
82 | |
83 | Status error; |
84 | RegisterValue reg_value; |
85 | |
86 | // Write any arguments onto the stack |
87 | sp -= 4 * args.size(); |
88 | |
89 | // Align the SP |
90 | sp &= ~(16ull - 1ull); // 16-byte alignment |
91 | |
92 | addr_t arg_pos = sp; |
93 | |
94 | for (addr_t arg : args) { |
95 | reg_value.SetUInt32(uint: arg); |
96 | error = reg_ctx->WriteRegisterValueToMemory( |
97 | reg_info: reg_info_32, dst_addr: arg_pos, dst_len: reg_info_32->byte_size, reg_value); |
98 | if (error.Fail()) |
99 | return false; |
100 | arg_pos += 4; |
101 | } |
102 | |
103 | // The return address is pushed onto the stack (yes after we just set the |
104 | // alignment above!). |
105 | sp -= 4; |
106 | reg_value.SetUInt32(uint: return_addr); |
107 | error = reg_ctx->WriteRegisterValueToMemory( |
108 | reg_info: reg_info_32, dst_addr: sp, dst_len: reg_info_32->byte_size, reg_value); |
109 | if (error.Fail()) |
110 | return false; |
111 | |
112 | // %esp is set to the actual stack value. |
113 | |
114 | if (!reg_ctx->WriteRegisterFromUnsigned(reg: sp_reg_num, uval: sp)) |
115 | return false; |
116 | |
117 | // %eip is set to the address of the called function. |
118 | |
119 | if (!reg_ctx->WriteRegisterFromUnsigned(reg: pc_reg_num, uval: func_addr)) |
120 | return false; |
121 | |
122 | return true; |
123 | } |
124 | |
125 | static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width, |
126 | bool is_signed, Process *process, |
127 | addr_t ¤t_stack_argument) { |
128 | |
129 | uint32_t byte_size = (bit_width + (8 - 1)) / 8; |
130 | Status error; |
131 | if (process->ReadScalarIntegerFromMemory(addr: current_stack_argument, byte_size, |
132 | is_signed, scalar, error)) { |
133 | current_stack_argument += byte_size; |
134 | return true; |
135 | } |
136 | return false; |
137 | } |
138 | |
139 | bool ABIMacOSX_i386::GetArgumentValues(Thread &thread, |
140 | ValueList &values) const { |
141 | unsigned int num_values = values.GetSize(); |
142 | 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 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
148 | |
149 | if (!reg_ctx) |
150 | return false; |
151 | |
152 | addr_t sp = reg_ctx->GetSP(fail_value: 0); |
153 | |
154 | if (!sp) |
155 | return false; |
156 | |
157 | addr_t current_stack_argument = sp + 4; // jump over return address |
158 | |
159 | for (value_index = 0; value_index < num_values; ++value_index) { |
160 | Value *value = values.GetValueAtIndex(idx: value_index); |
161 | |
162 | if (!value) |
163 | return false; |
164 | |
165 | // We currently only support extracting values with Clang QualTypes. Do we |
166 | // care about others? |
167 | CompilerType compiler_type(value->GetCompilerType()); |
168 | std::optional<uint64_t> bit_size = compiler_type.GetBitSize(exe_scope: &thread); |
169 | if (bit_size) { |
170 | bool is_signed; |
171 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) |
172 | ReadIntegerArgument(scalar&: value->GetScalar(), bit_width: *bit_size, is_signed, |
173 | process: thread.GetProcess().get(), current_stack_argument); |
174 | else if (compiler_type.IsPointerType()) |
175 | ReadIntegerArgument(scalar&: value->GetScalar(), bit_width: *bit_size, is_signed: false, |
176 | process: thread.GetProcess().get(), current_stack_argument); |
177 | } |
178 | } |
179 | |
180 | return true; |
181 | } |
182 | |
183 | Status ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, |
184 | lldb::ValueObjectSP &new_value_sp) { |
185 | Status error; |
186 | if (!new_value_sp) { |
187 | error.SetErrorString("Empty value object for return value." ); |
188 | return error; |
189 | } |
190 | |
191 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
192 | if (!compiler_type) { |
193 | error.SetErrorString("Null clang type for return value." ); |
194 | return error; |
195 | } |
196 | |
197 | Thread *thread = frame_sp->GetThread().get(); |
198 | |
199 | bool is_signed; |
200 | uint32_t count; |
201 | bool is_complex; |
202 | |
203 | RegisterContext *reg_ctx = thread->GetRegisterContext().get(); |
204 | |
205 | bool set_it_simple = false; |
206 | if (compiler_type.IsIntegerOrEnumerationType(is_signed) || |
207 | compiler_type.IsPointerType()) { |
208 | DataExtractor data; |
209 | Status data_error; |
210 | size_t num_bytes = new_value_sp->GetData(data, error&: data_error); |
211 | if (data_error.Fail()) { |
212 | error.SetErrorStringWithFormat( |
213 | "Couldn't convert return value to raw data: %s" , |
214 | data_error.AsCString()); |
215 | return error; |
216 | } |
217 | lldb::offset_t offset = 0; |
218 | if (num_bytes <= 8) { |
219 | const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName(reg_name: "eax" , start_idx: 0); |
220 | if (num_bytes <= 4) { |
221 | uint32_t raw_value = data.GetMaxU32(offset_ptr: &offset, byte_size: num_bytes); |
222 | |
223 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info: eax_info, uval: raw_value)) |
224 | set_it_simple = true; |
225 | } else { |
226 | uint32_t raw_value = data.GetMaxU32(offset_ptr: &offset, byte_size: 4); |
227 | |
228 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info: eax_info, uval: raw_value)) { |
229 | const RegisterInfo *edx_info = |
230 | reg_ctx->GetRegisterInfoByName(reg_name: "edx" , start_idx: 0); |
231 | uint32_t raw_value = data.GetMaxU32(offset_ptr: &offset, byte_size: num_bytes - offset); |
232 | |
233 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info: edx_info, uval: raw_value)) |
234 | set_it_simple = true; |
235 | } |
236 | } |
237 | } else { |
238 | error.SetErrorString("We don't support returning longer than 64 bit " |
239 | "integer values at present." ); |
240 | } |
241 | } else if (compiler_type.IsFloatingPointType(count, is_complex)) { |
242 | if (is_complex) |
243 | error.SetErrorString( |
244 | "We don't support returning complex values at present" ); |
245 | else |
246 | error.SetErrorString( |
247 | "We don't support returning float values at present" ); |
248 | } |
249 | |
250 | if (!set_it_simple) |
251 | error.SetErrorString( |
252 | "We only support setting simple integer return types at present." ); |
253 | |
254 | return error; |
255 | } |
256 | |
257 | ValueObjectSP |
258 | ABIMacOSX_i386::GetReturnValueObjectImpl(Thread &thread, |
259 | CompilerType &compiler_type) const { |
260 | Value value; |
261 | ValueObjectSP return_valobj_sp; |
262 | |
263 | if (!compiler_type) |
264 | return return_valobj_sp; |
265 | |
266 | // value.SetContext (Value::eContextTypeClangType, |
267 | // compiler_type.GetOpaqueQualType()); |
268 | value.SetCompilerType(compiler_type); |
269 | |
270 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
271 | if (!reg_ctx) |
272 | return return_valobj_sp; |
273 | |
274 | bool is_signed; |
275 | |
276 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { |
277 | std::optional<uint64_t> bit_width = compiler_type.GetBitSize(exe_scope: &thread); |
278 | if (!bit_width) |
279 | return return_valobj_sp; |
280 | unsigned eax_id = |
281 | reg_ctx->GetRegisterInfoByName(reg_name: "eax" , start_idx: 0)->kinds[eRegisterKindLLDB]; |
282 | unsigned edx_id = |
283 | reg_ctx->GetRegisterInfoByName(reg_name: "edx" , start_idx: 0)->kinds[eRegisterKindLLDB]; |
284 | |
285 | switch (*bit_width) { |
286 | default: |
287 | case 128: |
288 | // Scalar can't hold 128-bit literals, so we don't handle this |
289 | return return_valobj_sp; |
290 | case 64: |
291 | uint64_t raw_value; |
292 | raw_value = |
293 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
294 | 0xffffffff; |
295 | raw_value |= |
296 | (thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: edx_id, fail_value: 0) & |
297 | 0xffffffff) |
298 | << 32; |
299 | if (is_signed) |
300 | value.GetScalar() = (int64_t)raw_value; |
301 | else |
302 | value.GetScalar() = (uint64_t)raw_value; |
303 | break; |
304 | case 32: |
305 | if (is_signed) |
306 | value.GetScalar() = (int32_t)( |
307 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
308 | 0xffffffff); |
309 | else |
310 | value.GetScalar() = (uint32_t)( |
311 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
312 | 0xffffffff); |
313 | break; |
314 | case 16: |
315 | if (is_signed) |
316 | value.GetScalar() = (int16_t)( |
317 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
318 | 0xffff); |
319 | else |
320 | value.GetScalar() = (uint16_t)( |
321 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
322 | 0xffff); |
323 | break; |
324 | case 8: |
325 | if (is_signed) |
326 | value.GetScalar() = (int8_t)( |
327 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
328 | 0xff); |
329 | else |
330 | value.GetScalar() = (uint8_t)( |
331 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
332 | 0xff); |
333 | break; |
334 | } |
335 | } else if (compiler_type.IsPointerType()) { |
336 | unsigned eax_id = |
337 | reg_ctx->GetRegisterInfoByName(reg_name: "eax" , start_idx: 0)->kinds[eRegisterKindLLDB]; |
338 | uint32_t ptr = |
339 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
340 | 0xffffffff; |
341 | value.GetScalar() = ptr; |
342 | } else { |
343 | // not handled yet |
344 | return return_valobj_sp; |
345 | } |
346 | |
347 | // If we get here, we have a valid Value, so make our ValueObject out of it: |
348 | |
349 | return_valobj_sp = ValueObjectConstResult::Create( |
350 | exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), value, name: ConstString("" )); |
351 | return return_valobj_sp; |
352 | } |
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 | bool ABIMacOSX_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { |
359 | unwind_plan.Clear(); |
360 | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
361 | |
362 | uint32_t sp_reg_num = dwarf_esp; |
363 | uint32_t pc_reg_num = dwarf_eip; |
364 | |
365 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
366 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: sp_reg_num, offset: 4); |
367 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: pc_reg_num, offset: -4, can_replace: false); |
368 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: sp_reg_num, offset: 0, can_replace: true); |
369 | unwind_plan.AppendRow(row_sp: row); |
370 | unwind_plan.SetSourceName("i386 at-func-entry default" ); |
371 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
372 | return true; |
373 | } |
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 | bool ABIMacOSX_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { |
381 | unwind_plan.Clear(); |
382 | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
383 | |
384 | uint32_t fp_reg_num = dwarf_ebp; |
385 | uint32_t sp_reg_num = dwarf_esp; |
386 | uint32_t pc_reg_num = dwarf_eip; |
387 | |
388 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
389 | const int32_t ptr_size = 4; |
390 | |
391 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: fp_reg_num, offset: 2 * ptr_size); |
392 | row->SetOffset(0); |
393 | row->SetUnspecifiedRegistersAreUndefined(true); |
394 | |
395 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: fp_reg_num, offset: ptr_size * -2, can_replace: true); |
396 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: pc_reg_num, offset: ptr_size * -1, can_replace: true); |
397 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: sp_reg_num, offset: 0, can_replace: true); |
398 | |
399 | unwind_plan.AppendRow(row_sp: row); |
400 | unwind_plan.SetSourceName("i386 default unwind plan" ); |
401 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
402 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
403 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
404 | return true; |
405 | } |
406 | |
407 | bool ABIMacOSX_i386::RegisterIsVolatile(const RegisterInfo *reg_info) { |
408 | return !RegisterIsCalleeSaved(reg_info); |
409 | } |
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 | bool ABIMacOSX_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
423 | if (reg_info) { |
424 | // Saved registers are ebx, ebp, esi, edi, esp, eip |
425 | const char *name = reg_info->name; |
426 | if (name[0] == 'e') { |
427 | switch (name[1]) { |
428 | case 'b': |
429 | if (name[2] == 'x' || name[2] == 'p') |
430 | return name[3] == '\0'; |
431 | break; |
432 | case 'd': |
433 | if (name[2] == 'i') |
434 | return name[3] == '\0'; |
435 | break; |
436 | case 'i': |
437 | if (name[2] == 'p') |
438 | return name[3] == '\0'; |
439 | break; |
440 | case 's': |
441 | if (name[2] == 'i' || name[2] == 'p') |
442 | return name[3] == '\0'; |
443 | break; |
444 | } |
445 | } |
446 | if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp |
447 | return true; |
448 | if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp |
449 | return true; |
450 | if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc |
451 | return true; |
452 | } |
453 | return false; |
454 | } |
455 | |
456 | void ABIMacOSX_i386::Initialize() { |
457 | PluginManager::RegisterPlugin( |
458 | name: GetPluginNameStatic(), description: "Mac OS X ABI for i386 targets" , create_callback: CreateInstance); |
459 | } |
460 | |
461 | void ABIMacOSX_i386::Terminate() { |
462 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
463 | } |
464 | |