Coverage Report

Created: 2023-09-12 09:32

/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 &current_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
}