| // Copyright 2018 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/developer/debug/zxdb/expr/resolve_ptr_ref.h" | |
| #include "src/developer/debug/zxdb/common/err.h" | |
| #include "src/developer/debug/zxdb/expr/eval_context.h" | |
| #include "src/developer/debug/zxdb/expr/expr_value.h" | |
| #include "src/developer/debug/zxdb/expr/resolve_base.h" | |
| #include "src/developer/debug/zxdb/symbols/arch.h" | |
| #include "src/developer/debug/zxdb/symbols/modified_type.h" | |
| #include "src/developer/debug/zxdb/symbols/symbol_data_provider.h" | |
| #include "src/developer/debug/zxdb/symbols/type.h" | |
| #include "src/lib/fxl/strings/string_printf.h" | |
| namespace zxdb { | |
| namespace { | |
| // Extracts the value from the ExprValue, assuming it's a pointer. If not, | |
| // return the error, otherwise fill in *pointer_value. | |
| Err GetPointerValue(const ExprValue& value, TargetPointer* pointer_value) { | |
| Err err = value.EnsureSizeIs(kTargetPointerSize); | |
| if (err.has_error()) | |
| return err; | |
| *pointer_value = value.GetAs<TargetPointer>(); | |
| return Err(); | |
| } | |
| // Backend for the higher-level variant of ResolvePointer that does not handle upcasting to derived | |
| // types. | |
| void DoResolvePointer(const fxl::RefPtr<EvalContext>& eval_context, const ExprValue& pointer, | |
| EvalCallback cb) { | |
| fxl::RefPtr<Type> pointed_to; | |
| if (Err err = GetPointedToType(eval_context, pointer.type(), &pointed_to); err.has_error()) | |
| return cb(err); | |
| TargetPointer pointer_value = 0; | |
| if (Err err = GetPointerValue(pointer, &pointer_value); err.has_error()) | |
| return cb(err); | |
| // Forward to low-level pointer resolution. | |
| ResolvePointer(std::move(eval_context), pointer_value, std::move(pointed_to), std::move(cb)); | |
| } | |
| // Backend for EnsureResolveReference that does not handle upcasting to derived types. | |
| void DoEnsureResolveReference(const fxl::RefPtr<EvalContext>& eval_context, ExprValue value, | |
| EvalCallback cb) { | |
| Type* type = value.type(); | |
| if (!type) { | |
| // Untyped input, pass the value forward and let the callback handle the problem. | |
| return cb(std::move(value)); | |
| } | |
| // The computed type will have the same const, etc. on it as the original, so we need to | |
| // make it concrete. | |
| fxl::RefPtr<Type> concrete = eval_context->GetConcreteType(type); | |
| if (concrete->tag() != DwarfTag::kReferenceType && | |
| concrete->tag() != DwarfTag::kRvalueReferenceType) { | |
| // Not a reference, nothing to do. | |
| return cb(std::move(value)); | |
| } | |
| // The symbol provider should have created the right object type. | |
| const ModifiedType* reference = concrete->AsModifiedType(); | |
| FX_DCHECK(reference); | |
| const Type* underlying_type = reference->modified().Get()->AsType(); | |
| TargetPointer pointer_value = 0; | |
| if (Err err = GetPointerValue(value, &pointer_value); err.has_error()) { | |
| cb(err); | |
| } else { | |
| ResolvePointer(eval_context, pointer_value, RefPtrTo(underlying_type), std::move(cb)); | |
| } | |
| } | |
| } // namespace | |
| void ResolvePointer(const fxl::RefPtr<EvalContext>& eval_context, uint64_t address, | |
| fxl::RefPtr<Type> type, EvalCallback cb) { | |
| // We need to be careful to construct the return type with the original type given since it may | |
| // have const qualifiers, etc., but to use the concrete one (no const, with forward-definitions | |
| // resolved) for size computation. | |
| fxl::RefPtr<Type> concrete = eval_context->GetConcreteType(type.get()); | |
| if (!concrete) | |
| return cb(Err("Missing pointer type.")); | |
| uint32_t type_size = concrete->byte_size(); | |
| eval_context->GetDataProvider()->GetMemoryAsync( | |
| address, type_size, | |
| [type = std::move(type), address, type_size, cb = std::move(cb)]( | |
| const Err& err, std::vector<uint8_t> data) mutable { | |
| // Watch out, "type" may be non-concrete (we need to preserve "const", etc.). Use | |
| // "type_size" for the concrete size. | |
| if (err.has_error()) { | |
| cb(err); | |
| } else if (data.size() != type_size) { | |
| // Short read, memory is invalid. | |
| cb(Err(fxl::StringPrintf("Invalid pointer 0x%" PRIx64, address))); | |
| } else { | |
| cb(ExprValue(std::move(type), std::move(data), ExprValueSource(address))); | |
| } | |
| }); | |
| } | |
| void ResolvePointer(const fxl::RefPtr<EvalContext>& eval_context, const ExprValue& pointer, | |
| EvalCallback cb) { | |
| if (eval_context->ShouldPromoteToDerived()) { | |
| // Check to see if this is a pointer to a base class that we can convert to a derived class. | |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOnly, pointer, | |
| [eval_context, cb = std::move(cb)](ErrOrValue result) mutable { | |
| if (result.has_error()) | |
| cb(result); | |
| else | |
| DoResolvePointer(eval_context, result.take_value(), std::move(cb)); | |
| }); | |
| } else { | |
| // No magic base-class resolution is required, just check the reference. | |
| DoResolvePointer(eval_context, pointer, std::move(cb)); | |
| } | |
| } | |
| void EnsureResolveReference(const fxl::RefPtr<EvalContext>& eval_context, ExprValue value, | |
| EvalCallback cb) { | |
| if (eval_context->ShouldPromoteToDerived()) { | |
| // Check to see if this is a reference to a base class that we can convert to a derived class. | |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kRefOnly, std::move(value), | |
| [eval_context, cb = std::move(cb)](ErrOrValue result) mutable { | |
| if (result.has_error()) { | |
| cb(result); | |
| } else { | |
| DoEnsureResolveReference(eval_context, result.take_value(), | |
| std::move(cb)); | |
| } | |
| }); | |
| } else { | |
| // No magic base-class resolution is required, just check the reference. | |
| DoEnsureResolveReference(eval_context, std::move(value), std::move(cb)); | |
| } | |
| } | |
| Err GetPointedToType(const fxl::RefPtr<EvalContext>& eval_context, const Type* input, | |
| fxl::RefPtr<Type>* pointed_to) { | |
| if (!input) | |
| return Err("No type information."); | |
| // Convert to a pointer. GetConcreteType() here is more theoretical since current C compilers | |
| // won't forward-declare pointer types. But it's nice to be sure and this will also strip | |
| // CV-qualifiers which we do need. | |
| fxl::RefPtr<Type> input_concrete = eval_context->GetConcreteType(input); | |
| const ModifiedType* mod_type = input_concrete->AsModifiedType(); | |
| if (!mod_type || mod_type->tag() != DwarfTag::kPointerType) { | |
| return Err(fxl::StringPrintf("Attempting to dereference '%s' which is not a pointer.", | |
| input->GetFullName().c_str())); | |
| } | |
| *pointed_to = fxl::RefPtr<Type>(const_cast<Type*>(mod_type->modified().Get()->AsType())); | |
| if (!*pointed_to) | |
| return Err("Can not dereference a pointer to 'void'."); | |
| return Err(); | |
| } | |
| } // namespace zxdb |