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/Symbol/UnwindPlan.h" |
20 | #include "lldb/Target/Process.h" |
21 | #include "lldb/Target/RegisterContext.h" |
22 | #include "lldb/Target/Target.h" |
23 | #include "lldb/Target/Thread.h" |
24 | #include "lldb/Utility/ConstString.h" |
25 | #include "lldb/Utility/RegisterValue.h" |
26 | #include "lldb/Utility/Scalar.h" |
27 | #include "lldb/Utility/Status.h" |
28 | #include "lldb/ValueObject/ValueObjectConstResult.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 = |
169 | llvm::expectedToOptional(E: compiler_type.GetBitSize(exe_scope: &thread)); |
170 | if (bit_size) { |
171 | bool is_signed; |
172 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) |
173 | ReadIntegerArgument(scalar&: value->GetScalar(), bit_width: *bit_size, is_signed, |
174 | process: thread.GetProcess().get(), current_stack_argument); |
175 | else if (compiler_type.IsPointerType()) |
176 | ReadIntegerArgument(scalar&: value->GetScalar(), bit_width: *bit_size, is_signed: false, |
177 | process: thread.GetProcess().get(), current_stack_argument); |
178 | } |
179 | } |
180 | |
181 | return true; |
182 | } |
183 | |
184 | Status ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, |
185 | lldb::ValueObjectSP &new_value_sp) { |
186 | Status error; |
187 | if (!new_value_sp) { |
188 | error = Status::FromErrorString(str: "Empty value object for return value." ); |
189 | return error; |
190 | } |
191 | |
192 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
193 | if (!compiler_type) { |
194 | error = Status::FromErrorString(str: "Null clang type for return value." ); |
195 | return error; |
196 | } |
197 | |
198 | Thread *thread = frame_sp->GetThread().get(); |
199 | |
200 | bool is_signed; |
201 | uint32_t count; |
202 | bool is_complex; |
203 | |
204 | RegisterContext *reg_ctx = thread->GetRegisterContext().get(); |
205 | |
206 | bool set_it_simple = false; |
207 | if (compiler_type.IsIntegerOrEnumerationType(is_signed) || |
208 | compiler_type.IsPointerType()) { |
209 | DataExtractor data; |
210 | Status data_error; |
211 | size_t num_bytes = new_value_sp->GetData(data, error&: data_error); |
212 | if (data_error.Fail()) { |
213 | error = Status::FromErrorStringWithFormat( |
214 | format: "Couldn't convert return value to raw data: %s" , |
215 | data_error.AsCString()); |
216 | return error; |
217 | } |
218 | lldb::offset_t offset = 0; |
219 | if (num_bytes <= 8) { |
220 | const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName(reg_name: "eax" , start_idx: 0); |
221 | if (num_bytes <= 4) { |
222 | uint32_t raw_value = data.GetMaxU32(offset_ptr: &offset, byte_size: num_bytes); |
223 | |
224 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info: eax_info, uval: raw_value)) |
225 | set_it_simple = true; |
226 | } else { |
227 | uint32_t raw_value = data.GetMaxU32(offset_ptr: &offset, byte_size: 4); |
228 | |
229 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info: eax_info, uval: raw_value)) { |
230 | const RegisterInfo *edx_info = |
231 | reg_ctx->GetRegisterInfoByName(reg_name: "edx" , start_idx: 0); |
232 | uint32_t raw_value = data.GetMaxU32(offset_ptr: &offset, byte_size: num_bytes - offset); |
233 | |
234 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info: edx_info, uval: raw_value)) |
235 | set_it_simple = true; |
236 | } |
237 | } |
238 | } else { |
239 | error = Status::FromErrorString( |
240 | str: "We don't support returning longer than 64 bit " |
241 | "integer values at present." ); |
242 | } |
243 | } else if (compiler_type.IsFloatingPointType(count, is_complex)) { |
244 | if (is_complex) |
245 | error = Status::FromErrorString( |
246 | str: "We don't support returning complex values at present" ); |
247 | else |
248 | error = Status::FromErrorString( |
249 | str: "We don't support returning float values at present" ); |
250 | } |
251 | |
252 | if (!set_it_simple) |
253 | error = Status::FromErrorString( |
254 | str: "We only support setting simple integer return types at present." ); |
255 | |
256 | return error; |
257 | } |
258 | |
259 | ValueObjectSP |
260 | ABIMacOSX_i386::GetReturnValueObjectImpl(Thread &thread, |
261 | CompilerType &compiler_type) const { |
262 | Value value; |
263 | ValueObjectSP return_valobj_sp; |
264 | |
265 | if (!compiler_type) |
266 | return return_valobj_sp; |
267 | |
268 | // value.SetContext (Value::eContextTypeClangType, |
269 | // compiler_type.GetOpaqueQualType()); |
270 | value.SetCompilerType(compiler_type); |
271 | |
272 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
273 | if (!reg_ctx) |
274 | return return_valobj_sp; |
275 | |
276 | bool is_signed; |
277 | |
278 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { |
279 | std::optional<uint64_t> bit_width = |
280 | llvm::expectedToOptional(E: compiler_type.GetBitSize(exe_scope: &thread)); |
281 | if (!bit_width) |
282 | return return_valobj_sp; |
283 | unsigned eax_id = |
284 | reg_ctx->GetRegisterInfoByName(reg_name: "eax" , start_idx: 0)->kinds[eRegisterKindLLDB]; |
285 | unsigned edx_id = |
286 | reg_ctx->GetRegisterInfoByName(reg_name: "edx" , start_idx: 0)->kinds[eRegisterKindLLDB]; |
287 | |
288 | switch (*bit_width) { |
289 | default: |
290 | case 128: |
291 | // Scalar can't hold 128-bit literals, so we don't handle this |
292 | return return_valobj_sp; |
293 | case 64: |
294 | uint64_t raw_value; |
295 | raw_value = |
296 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
297 | 0xffffffff; |
298 | raw_value |= |
299 | (thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: edx_id, fail_value: 0) & |
300 | 0xffffffff) |
301 | << 32; |
302 | if (is_signed) |
303 | value.GetScalar() = (int64_t)raw_value; |
304 | else |
305 | value.GetScalar() = (uint64_t)raw_value; |
306 | break; |
307 | case 32: |
308 | if (is_signed) |
309 | value.GetScalar() = (int32_t)( |
310 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
311 | 0xffffffff); |
312 | else |
313 | value.GetScalar() = (uint32_t)( |
314 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
315 | 0xffffffff); |
316 | break; |
317 | case 16: |
318 | if (is_signed) |
319 | value.GetScalar() = (int16_t)( |
320 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
321 | 0xffff); |
322 | else |
323 | value.GetScalar() = (uint16_t)( |
324 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
325 | 0xffff); |
326 | break; |
327 | case 8: |
328 | if (is_signed) |
329 | value.GetScalar() = (int8_t)( |
330 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
331 | 0xff); |
332 | else |
333 | value.GetScalar() = (uint8_t)( |
334 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
335 | 0xff); |
336 | break; |
337 | } |
338 | } else if (compiler_type.IsPointerType()) { |
339 | unsigned eax_id = |
340 | reg_ctx->GetRegisterInfoByName(reg_name: "eax" , start_idx: 0)->kinds[eRegisterKindLLDB]; |
341 | uint32_t ptr = |
342 | thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg: eax_id, fail_value: 0) & |
343 | 0xffffffff; |
344 | value.GetScalar() = ptr; |
345 | } else { |
346 | // not handled yet |
347 | return return_valobj_sp; |
348 | } |
349 | |
350 | // If we get here, we have a valid Value, so make our ValueObject out of it: |
351 | |
352 | return_valobj_sp = ValueObjectConstResult::Create( |
353 | exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), value, name: ConstString("" )); |
354 | return return_valobj_sp; |
355 | } |
356 | |
357 | // This defines the CFA as esp+4 |
358 | // the saved pc is at CFA-4 (i.e. esp+0) |
359 | // The saved esp is CFA+0 |
360 | |
361 | UnwindPlanSP ABIMacOSX_i386::CreateFunctionEntryUnwindPlan() { |
362 | uint32_t sp_reg_num = dwarf_esp; |
363 | uint32_t pc_reg_num = dwarf_eip; |
364 | |
365 | UnwindPlan::Row 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 | |
370 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
371 | plan_sp->AppendRow(row: std::move(row)); |
372 | plan_sp->SetSourceName("i386 at-func-entry default" ); |
373 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
374 | return plan_sp; |
375 | } |
376 | |
377 | // This defines the CFA as ebp+8 |
378 | // The saved pc is at CFA-4 (i.e. ebp+4) |
379 | // The saved ebp is at CFA-8 (i.e. ebp+0) |
380 | // The saved esp is CFA+0 |
381 | |
382 | UnwindPlanSP ABIMacOSX_i386::CreateDefaultUnwindPlan() { |
383 | uint32_t fp_reg_num = dwarf_ebp; |
384 | uint32_t sp_reg_num = dwarf_esp; |
385 | uint32_t pc_reg_num = dwarf_eip; |
386 | |
387 | UnwindPlan::Row row; |
388 | const int32_t ptr_size = 4; |
389 | |
390 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: fp_reg_num, offset: 2 * ptr_size); |
391 | row.SetUnspecifiedRegistersAreUndefined(true); |
392 | |
393 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: fp_reg_num, offset: ptr_size * -2, can_replace: true); |
394 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: pc_reg_num, offset: ptr_size * -1, can_replace: true); |
395 | row.SetRegisterLocationToIsCFAPlusOffset(reg_num: sp_reg_num, offset: 0, can_replace: true); |
396 | |
397 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
398 | plan_sp->AppendRow(row: std::move(row)); |
399 | plan_sp->SetSourceName("i386 default unwind plan" ); |
400 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
401 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
402 | plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); |
403 | return plan_sp; |
404 | } |
405 | |
406 | bool ABIMacOSX_i386::RegisterIsVolatile(const RegisterInfo *reg_info) { |
407 | return !RegisterIsCalleeSaved(reg_info); |
408 | } |
409 | |
410 | // v. |
411 | // http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130 |
412 | // -IA- |
413 | // 32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4 |
414 | // |
415 | // This document ("OS X ABI Function Call Guide", chapter "IA-32 Function |
416 | // Calling Conventions") says that the following registers on i386 are |
417 | // preserved aka non-volatile aka callee-saved: |
418 | // |
419 | // ebx, ebp, esi, edi, esp |
420 | |
421 | bool ABIMacOSX_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
422 | if (reg_info) { |
423 | // Saved registers are ebx, ebp, esi, edi, esp, eip |
424 | const char *name = reg_info->name; |
425 | if (name[0] == 'e') { |
426 | switch (name[1]) { |
427 | case 'b': |
428 | if (name[2] == 'x' || name[2] == 'p') |
429 | return name[3] == '\0'; |
430 | break; |
431 | case 'd': |
432 | if (name[2] == 'i') |
433 | return name[3] == '\0'; |
434 | break; |
435 | case 'i': |
436 | if (name[2] == 'p') |
437 | return name[3] == '\0'; |
438 | break; |
439 | case 's': |
440 | if (name[2] == 'i' || name[2] == 'p') |
441 | return name[3] == '\0'; |
442 | break; |
443 | } |
444 | } |
445 | if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp |
446 | return true; |
447 | if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp |
448 | return true; |
449 | if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc |
450 | return true; |
451 | } |
452 | return false; |
453 | } |
454 | |
455 | void ABIMacOSX_i386::Initialize() { |
456 | PluginManager::RegisterPlugin( |
457 | name: GetPluginNameStatic(), description: "Mac OS X ABI for i386 targets" , create_callback: CreateInstance); |
458 | } |
459 | |
460 | void ABIMacOSX_i386::Terminate() { |
461 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
462 | } |
463 | |