| 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 | |