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
30using namespace lldb;
31using namespace lldb_private;
32
33LLDB_PLUGIN_DEFINE(ABIMacOSX_i386)
34
35enum {
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
47size_t ABIMacOSX_i386::GetRedZoneSize() const { return 0; }
48
49// Static Functions
50
51ABISP
52ABIMacOSX_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
62bool 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
125static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
126 bool is_signed, Process *process,
127 addr_t &current_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
139bool 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
184Status 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
259ValueObjectSP
260ABIMacOSX_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
361UnwindPlanSP 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
382UnwindPlanSP 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
406bool 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
421bool 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
455void ABIMacOSX_i386::Initialize() {
456 PluginManager::RegisterPlugin(
457 name: GetPluginNameStatic(), description: "Mac OS X ABI for i386 targets", create_callback: CreateInstance);
458}
459
460void ABIMacOSX_i386::Terminate() {
461 PluginManager::UnregisterPlugin(create_callback: CreateInstance);
462}
463

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of lldb/source/Plugins/ABI/X86/ABIMacOSX_i386.cpp