ValueObjectSP ABISysV_i386::GetReturnValueObjectImpl (Thread &thread, CompilerType &return_clang_type) const { ValueObjectSP return_valobj_sp; if (!return_clang_type) return return_valobj_sp; ExecutionContext exe_ctx (thread.shared_from_this()); return_valobj_sp = GetReturnValueObjectSimple(thread, return_clang_type); if (return_valobj_sp) return return_valobj_sp; RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); if (!reg_ctx_sp) return return_valobj_sp; if (return_clang_type.IsAggregateType()) { unsigned eax_id = reg_ctx_sp->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; lldb::addr_t storage_addr = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); return_valobj_sp = ValueObjectMemory::Create (&thread, "", Address (storage_addr, nullptr), return_clang_type); } return return_valobj_sp; }
ValueObjectSP ABISysV_s390x::GetReturnValueObjectImpl( Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; if (!return_compiler_type) return return_valobj_sp; ExecutionContext exe_ctx(thread.shared_from_this()); return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type); if (return_valobj_sp) return return_valobj_sp; RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); if (!reg_ctx_sp) return return_valobj_sp; if (return_compiler_type.IsAggregateType()) { // FIXME: This is just taking a guess, r2 may very well no longer hold the // return storage location. // If we are going to do this right, when we make a new frame we should // check to see if it uses a memory return, and if we are at the first // instruction and if so stash away the return location. Then we would // only return the memory return value if we know it is valid. unsigned r2_id = reg_ctx_sp->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB]; lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0); return_valobj_sp = ValueObjectMemory::Create( &thread, "", Address(storage_addr, nullptr), return_compiler_type); } return return_valobj_sp; }
uint32_t GoASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, bool omit_empty_base_classes) { if (!type || !GetCompleteType(type)) return 0; GoType *t = static_cast<GoType *>(type); if (t->GetGoKind() == GoType::KIND_PTR) { CompilerType elem = t->GetElementType(); if (elem.IsAggregateType()) return elem.GetNumChildren(omit_empty_base_classes); return 1; } else if (GoArray *array = t->GetArray()) { return array->GetLength(); } else if (t->IsTypedef()) { return t->GetElementType().GetNumChildren(omit_empty_base_classes); } return GetNumFields(type); }
ValueObjectSP ABISysV_mips::GetReturnValueObjectImpl (Thread &thread, CompilerType &return_clang_type) const { ValueObjectSP return_valobj_sp; Value value; if (!return_clang_type) return return_valobj_sp; ExecutionContext exe_ctx (thread.shared_from_this()); if (exe_ctx.GetTargetPtr() == NULL || exe_ctx.GetProcessPtr() == NULL) return return_valobj_sp; value.SetCompilerType(return_clang_type); RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return return_valobj_sp; bool is_signed = false; bool is_complex = false; uint32_t count = 0; // In MIPS register "r2" (v0) holds the integer function return values const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoByName("r2", 0); size_t bit_width = return_clang_type.GetBitSize(&thread); if (return_clang_type.IsIntegerType (is_signed)) { switch (bit_width) { default: return return_valobj_sp; case 64: { const RegisterInfo *r3_reg_info = reg_ctx->GetRegisterInfoByName("r3", 0); uint64_t raw_value; raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX; raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0) & UINT32_MAX)) << 32; if (is_signed) value.GetScalar() = (int64_t)raw_value; else value.GetScalar() = (uint64_t)raw_value; } break; case 32: if (is_signed) value.GetScalar() = (int32_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX); else value.GetScalar() = (uint32_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX); break; case 16: if (is_signed) value.GetScalar() = (int16_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX); else value.GetScalar() = (uint16_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX); break; case 8: if (is_signed) value.GetScalar() = (int8_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX); else value.GetScalar() = (uint8_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX); break; } } else if (return_clang_type.IsPointerType ()) { uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX; value.GetScalar() = ptr; } else if (return_clang_type.IsAggregateType ()) { // Structure/Vector is always passed in memory and pointer to that memory is passed in r2. uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("r2", 0), 0); // We have got the address. Create a memory object out of it return_valobj_sp = ValueObjectMemory::Create (&thread, "", Address (mem_address, NULL), return_clang_type); return return_valobj_sp; } else if (return_clang_type.IsFloatingPointType (count, is_complex)) { const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0); const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0); if (count == 1 && !is_complex) { switch (bit_width) { default: return return_valobj_sp; case 64: { static_assert(sizeof(double) == sizeof(uint64_t), ""); uint64_t raw_value; raw_value = reg_ctx->ReadRegisterAsUnsigned(f0_info, 0) & UINT32_MAX; raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(f1_info, 0) & UINT32_MAX)) << 32; value.GetScalar() = *reinterpret_cast<double*>(&raw_value); break; } case 32: { static_assert(sizeof(float) == sizeof(uint32_t), ""); uint32_t raw_value = reg_ctx->ReadRegisterAsUnsigned(f0_info, 0) & UINT32_MAX; value.GetScalar() = *reinterpret_cast<float*>(&raw_value); break; } } } else { // not handled yet return return_valobj_sp; } } else { // not handled yet return return_valobj_sp; } // If we get here, we have a valid Value, so make our ValueObject out of it: return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); return return_valobj_sp; }
ValueObjectSP ABISysV_ppc64::GetReturnValueObjectImpl( Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; if (!return_compiler_type) return return_valobj_sp; ExecutionContext exe_ctx(thread.shared_from_this()); return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type); if (return_valobj_sp) return return_valobj_sp; RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); if (!reg_ctx_sp) return return_valobj_sp; const size_t bit_width = return_compiler_type.GetBitSize(&thread); if (return_compiler_type.IsAggregateType()) { Target *target = exe_ctx.GetTargetPtr(); bool is_memory = true; if (bit_width <= 128) { ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder(); DataBufferSP data_sp(new DataBufferHeap(16, 0)); DataExtractor return_ext(data_sp, target_byte_order, target->GetArchitecture().GetAddressByteSize()); const RegisterInfo *r3_info = reg_ctx_sp->GetRegisterInfoByName("r3", 0); const RegisterInfo *rdx_info = reg_ctx_sp->GetRegisterInfoByName("rdx", 0); RegisterValue r3_value, rdx_value; reg_ctx_sp->ReadRegister(r3_info, r3_value); reg_ctx_sp->ReadRegister(rdx_info, rdx_value); DataExtractor r3_data, rdx_data; r3_value.GetData(r3_data); rdx_value.GetData(rdx_data); uint32_t fp_bytes = 0; // Tracks how much of the xmm registers we've consumed so far uint32_t integer_bytes = 0; // Tracks how much of the r3/rds registers we've consumed so far const uint32_t num_children = return_compiler_type.GetNumFields(); // Since we are in the small struct regime, assume we are not in memory. is_memory = false; for (uint32_t idx = 0; idx < num_children; idx++) { std::string name; uint64_t field_bit_offset = 0; bool is_signed; bool is_complex; uint32_t count; CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex( idx, name, &field_bit_offset, nullptr, nullptr); const size_t field_bit_width = field_compiler_type.GetBitSize(&thread); // If there are any unaligned fields, this is stored in memory. if (field_bit_offset % field_bit_width != 0) { is_memory = true; break; } uint32_t field_byte_width = field_bit_width / 8; uint32_t field_byte_offset = field_bit_offset / 8; DataExtractor *copy_from_extractor = nullptr; uint32_t copy_from_offset = 0; if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) || field_compiler_type.IsPointerType()) { if (integer_bytes < 8) { if (integer_bytes + field_byte_width <= 8) { // This is in RAX, copy from register to our result structure: copy_from_extractor = &r3_data; copy_from_offset = integer_bytes; integer_bytes += field_byte_width; } else { // The next field wouldn't fit in the remaining space, so we // pushed it to rdx. copy_from_extractor = &rdx_data; copy_from_offset = 0; integer_bytes = 8 + field_byte_width; } } else if (integer_bytes + field_byte_width <= 16) { copy_from_extractor = &rdx_data; copy_from_offset = integer_bytes - 8; integer_bytes += field_byte_width; } else { // The last field didn't fit. I can't see how that would happen w/o // the overall size being // greater than 16 bytes. For now, return a nullptr return value // object. return return_valobj_sp; } } else if (field_compiler_type.IsFloatingPointType(count, is_complex)) { // Structs with long doubles are always passed in memory. if (field_bit_width == 128) { is_memory = true; break; } else if (field_bit_width == 64) { copy_from_offset = 0; fp_bytes += field_byte_width; } else if (field_bit_width == 32) { // This one is kind of complicated. If we are in an "eightbyte" // with another float, we'll // be stuffed into an xmm register with it. If we are in an // "eightbyte" with one or more ints, // then we will be stuffed into the appropriate GPR with them. bool in_gpr; if (field_byte_offset % 8 == 0) { // We are at the beginning of one of the eightbytes, so check the // next element (if any) if (idx == num_children - 1) in_gpr = false; else { uint64_t next_field_bit_offset = 0; CompilerType next_field_compiler_type = return_compiler_type.GetFieldAtIndex(idx + 1, name, &next_field_bit_offset, nullptr, nullptr); if (next_field_compiler_type.IsIntegerOrEnumerationType( is_signed)) in_gpr = true; else { copy_from_offset = 0; in_gpr = false; } } } else if (field_byte_offset % 4 == 0) { // We are inside of an eightbyte, so see if the field before us is // floating point: // This could happen if somebody put padding in the structure. if (idx == 0) in_gpr = false; else { uint64_t prev_field_bit_offset = 0; CompilerType prev_field_compiler_type = return_compiler_type.GetFieldAtIndex(idx - 1, name, &prev_field_bit_offset, nullptr, nullptr); if (prev_field_compiler_type.IsIntegerOrEnumerationType( is_signed)) in_gpr = true; else { copy_from_offset = 4; in_gpr = false; } } } else { is_memory = true; continue; } // Okay, we've figured out whether we are in GPR or XMM, now figure // out which one. if (in_gpr) { if (integer_bytes < 8) { // This is in RAX, copy from register to our result structure: copy_from_extractor = &r3_data; copy_from_offset = integer_bytes; integer_bytes += field_byte_width; } else { copy_from_extractor = &rdx_data; copy_from_offset = integer_bytes - 8; integer_bytes += field_byte_width; } } else { fp_bytes += field_byte_width; } } } // These two tests are just sanity checks. If I somehow get the // type calculation wrong above it is better to just return nothing // than to assert or crash. if (!copy_from_extractor) return return_valobj_sp; if (copy_from_offset + field_byte_width > copy_from_extractor->GetByteSize()) return return_valobj_sp; copy_from_extractor->CopyByteOrderedData( copy_from_offset, field_byte_width, data_sp->GetBytes() + field_byte_offset, field_byte_width, target_byte_order); } if (!is_memory) { // The result is in our data buffer. Let's make a variable object out // of it: return_valobj_sp = ValueObjectConstResult::Create( &thread, return_compiler_type, ConstString(""), return_ext); } } // FIXME: This is just taking a guess, r3 may very well no longer hold the // return storage location. // If we are going to do this right, when we make a new frame we should // check to see if it uses a memory // return, and if we are at the first instruction and if so stash away the // return location. Then we would // only return the memory return value if we know it is valid. if (is_memory) { unsigned r3_id = reg_ctx_sp->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB]; lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0); return_valobj_sp = ValueObjectMemory::Create( &thread, "", Address(storage_addr, nullptr), return_compiler_type); } } return return_valobj_sp; }
CompilerType GoASTContext::GetChildCompilerTypeAtIndex(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, bool ignore_array_bounds, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, bool &child_is_deref_of_parent, ValueObject *valobj, uint64_t &language_flags) { child_name.clear(); child_byte_size = 0; child_byte_offset = 0; child_bitfield_bit_size = 0; child_bitfield_bit_offset = 0; child_is_base_class = false; child_is_deref_of_parent = false; language_flags = 0; if (!type || !GetCompleteType(type)) return CompilerType(); GoType *t = static_cast<GoType *>(type); if (t->GetStruct()) { uint64_t bit_offset; CompilerType ret = GetFieldAtIndex(type, idx, child_name, &bit_offset, nullptr, nullptr); child_byte_size = ret.GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr); child_byte_offset = bit_offset / 8; return ret; } else if (t->GetGoKind() == GoType::KIND_PTR) { CompilerType pointee = t->GetElementType(); if (!pointee.IsValid() || pointee.IsVoidType()) return CompilerType(); if (transparent_pointers && pointee.IsAggregateType()) { bool tmp_child_is_deref_of_parent = false; return pointee.GetChildCompilerTypeAtIndex(exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, tmp_child_is_deref_of_parent, valobj, language_flags); } else { child_is_deref_of_parent = true; const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; if (parent_name) { child_name.assign(1, '*'); child_name += parent_name; } // We have a pointer to an simple type if (idx == 0 && pointee.GetCompleteType()) { child_byte_size = pointee.GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee; } } } else if (GoArray *a = t->GetArray()) { if (ignore_array_bounds || idx < a->GetLength()) { CompilerType element_type = a->GetElementType(); if (element_type.GetCompleteType()) { char element_name[64]; ::snprintf(element_name, sizeof(element_name), "[%zu]", idx); child_name.assign(element_name); child_byte_size = element_type.GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; return element_type; } } } else if (t->IsTypedef()) { return t->GetElementType().GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } return CompilerType(); }