Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,335 @@
//===-- AppleObjCClassDescriptorV2.h ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_AppleObjCClassDescriptorV2_h_
#define liblldb_AppleObjCClassDescriptorV2_h_
// C Includes
// C++ Includes
#include <mutex>
// Other libraries and framework includes
// Project includes
#include "AppleObjCRuntimeV2.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
public:
friend class lldb_private::AppleObjCRuntimeV2;
~ClassDescriptorV2() override = default;
ConstString GetClassName() override;
ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override;
ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override;
bool IsValid() override {
return true; // any Objective-C v2 runtime class descriptor we vend is valid
}
// a custom descriptor is used for tagged pointers
bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
uint64_t *value_bits = nullptr,
uint64_t *payload = nullptr) override {
return false;
}
uint64_t GetInstanceSize() override;
ObjCLanguageRuntime::ObjCISA GetISA() override { return m_objc_class_ptr; }
bool Describe(
std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
std::function<bool(const char *, const char *)> const
&instance_method_func,
std::function<bool(const char *, const char *)> const &class_method_func,
std::function<bool(const char *, const char *, lldb::addr_t,
uint64_t)> const &ivar_func) const override;
size_t GetNumIVars() override {
GetIVarInformation();
return m_ivars_storage.size();
}
iVarDescriptor GetIVarAtIndex(size_t idx) override {
if (idx >= GetNumIVars())
return iVarDescriptor();
return m_ivars_storage[idx];
}
protected:
void GetIVarInformation();
private:
static const uint32_t RW_REALIZED = (1 << 31);
struct objc_class_t {
ObjCLanguageRuntime::ObjCISA m_isa; // The class's metaclass.
ObjCLanguageRuntime::ObjCISA m_superclass;
lldb::addr_t m_cache_ptr;
lldb::addr_t m_vtable_ptr;
lldb::addr_t m_data_ptr;
uint8_t m_flags;
objc_class_t()
: m_isa(0), m_superclass(0), m_cache_ptr(0), m_vtable_ptr(0),
m_data_ptr(0), m_flags(0) {}
void Clear() {
m_isa = 0;
m_superclass = 0;
m_cache_ptr = 0;
m_vtable_ptr = 0;
m_data_ptr = 0;
m_flags = 0;
}
bool Read(Process *process, lldb::addr_t addr);
};
struct class_ro_t {
uint32_t m_flags;
uint32_t m_instanceStart;
uint32_t m_instanceSize;
uint32_t m_reserved;
lldb::addr_t m_ivarLayout_ptr;
lldb::addr_t m_name_ptr;
lldb::addr_t m_baseMethods_ptr;
lldb::addr_t m_baseProtocols_ptr;
lldb::addr_t m_ivars_ptr;
lldb::addr_t m_weakIvarLayout_ptr;
lldb::addr_t m_baseProperties_ptr;
std::string m_name;
bool Read(Process *process, lldb::addr_t addr);
};
struct class_rw_t {
uint32_t m_flags;
uint32_t m_version;
lldb::addr_t m_ro_ptr;
union {
lldb::addr_t m_method_list_ptr;
lldb::addr_t m_method_lists_ptr;
};
lldb::addr_t m_properties_ptr;
lldb::addr_t m_protocols_ptr;
ObjCLanguageRuntime::ObjCISA m_firstSubclass;
ObjCLanguageRuntime::ObjCISA m_nextSiblingClass;
bool Read(Process *process, lldb::addr_t addr);
};
struct method_list_t {
uint32_t m_entsize;
uint32_t m_count;
lldb::addr_t m_first_ptr;
bool Read(Process *process, lldb::addr_t addr);
};
struct method_t {
lldb::addr_t m_name_ptr;
lldb::addr_t m_types_ptr;
lldb::addr_t m_imp_ptr;
std::string m_name;
std::string m_types;
static size_t GetSize(Process *process) {
size_t ptr_size = process->GetAddressByteSize();
return ptr_size // SEL name;
+ ptr_size // const char *types;
+ ptr_size; // IMP imp;
}
bool Read(Process *process, lldb::addr_t addr);
};
struct ivar_list_t {
uint32_t m_entsize;
uint32_t m_count;
lldb::addr_t m_first_ptr;
bool Read(Process *process, lldb::addr_t addr);
};
struct ivar_t {
lldb::addr_t m_offset_ptr;
lldb::addr_t m_name_ptr;
lldb::addr_t m_type_ptr;
uint32_t m_alignment;
uint32_t m_size;
std::string m_name;
std::string m_type;
static size_t GetSize(Process *process) {
size_t ptr_size = process->GetAddressByteSize();
return ptr_size // uintptr_t *offset;
+ ptr_size // const char *name;
+ ptr_size // const char *type;
+ sizeof(uint32_t) // uint32_t alignment;
+ sizeof(uint32_t); // uint32_t size;
}
bool Read(Process *process, lldb::addr_t addr);
};
class iVarsStorage {
public:
iVarsStorage();
size_t size();
iVarDescriptor &operator[](size_t idx);
void fill(AppleObjCRuntimeV2 &runtime, ClassDescriptorV2 &descriptor);
private:
bool m_filled;
std::vector<iVarDescriptor> m_ivars;
std::recursive_mutex m_mutex;
};
// The constructor should only be invoked by the runtime as it builds its
// caches
// or populates them. A ClassDescriptorV2 should only ever exist in a cache.
ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
ObjCLanguageRuntime::ObjCISA isa, const char *name)
: m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
m_ivars_storage() {}
bool Read_objc_class(Process *process,
std::unique_ptr<objc_class_t> &objc_class) const;
bool Read_class_row(Process *process, const objc_class_t &objc_class,
std::unique_ptr<class_ro_t> &class_ro,
std::unique_ptr<class_rw_t> &class_rw) const;
AppleObjCRuntimeV2
&m_runtime; // The runtime, so we can read information lazily.
lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t. (I.e.,
// objects of this class type have this as
// their ISA)
ConstString m_name; // May be NULL
iVarsStorage m_ivars_storage;
};
// tagged pointer descriptor
class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor {
public:
ClassDescriptorV2Tagged(ConstString class_name, uint64_t payload) {
m_name = class_name;
if (!m_name) {
m_valid = false;
return;
}
m_valid = true;
m_payload = payload;
m_info_bits = (m_payload & 0xF0ULL) >> 4;
m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8;
}
ClassDescriptorV2Tagged(
ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp,
uint64_t payload) {
if (!actual_class_sp) {
m_valid = false;
return;
}
m_name = actual_class_sp->GetClassName();
if (!m_name) {
m_valid = false;
return;
}
m_valid = true;
m_payload = payload;
m_info_bits = (m_payload & 0x0FULL);
m_value_bits = (m_payload & ~0x0FULL) >> 4;
}
~ClassDescriptorV2Tagged() override = default;
ConstString GetClassName() override { return m_name; }
ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override {
// tagged pointers can represent a class that has a superclass, but since
// that information is not
// stored in the object itself, we would have to query the runtime to
// discover the hierarchy
// for the time being, we skip this step in the interest of static discovery
return ObjCLanguageRuntime::ClassDescriptorSP();
}
ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override {
return ObjCLanguageRuntime::ClassDescriptorSP();
}
bool IsValid() override { return m_valid; }
bool IsKVO() override {
return false; // tagged pointers are not KVO'ed
}
bool IsCFType() override {
return false; // tagged pointers are not CF objects
}
bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
uint64_t *value_bits = nullptr,
uint64_t *payload = nullptr) override {
if (info_bits)
*info_bits = GetInfoBits();
if (value_bits)
*value_bits = GetValueBits();
if (payload)
*payload = GetPayload();
return true;
}
uint64_t GetInstanceSize() override {
return (IsValid() ? m_pointer_size : 0);
}
ObjCLanguageRuntime::ObjCISA GetISA() override {
return 0; // tagged pointers have no ISA
}
// these calls are not part of any formal tagged pointers specification
virtual uint64_t GetValueBits() { return (IsValid() ? m_value_bits : 0); }
virtual uint64_t GetInfoBits() { return (IsValid() ? m_info_bits : 0); }
virtual uint64_t GetPayload() { return (IsValid() ? m_payload : 0); }
private:
ConstString m_name;
uint8_t m_pointer_size;
bool m_valid;
uint64_t m_info_bits;
uint64_t m_value_bits;
uint64_t m_payload;
};
} // namespace lldb_private
#endif // liblldb_AppleObjCClassDescriptorV2_h_

View File

@@ -0,0 +1,55 @@
//===-- AppleObjCDeclVendor.h -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_AppleObjCDeclVendor_h_
#define liblldb_AppleObjCDeclVendor_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/DeclVendor.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
class AppleObjCExternalASTSource;
class AppleObjCDeclVendor : public DeclVendor {
public:
AppleObjCDeclVendor(ObjCLanguageRuntime &runtime);
uint32_t FindDecls(const ConstString &name, bool append, uint32_t max_matches,
std::vector<clang::NamedDecl *> &decls) override;
clang::ExternalASTMerger::ImporterSource GetImporterSource() override;
friend class AppleObjCExternalASTSource;
private:
clang::ObjCInterfaceDecl *GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa);
bool FinishDecl(clang::ObjCInterfaceDecl *decl);
ObjCLanguageRuntime &m_runtime;
ClangASTContext m_ast_ctx;
ObjCLanguageRuntime::EncodingToTypeSP m_type_realizer_sp;
AppleObjCExternalASTSource *m_external_source;
typedef llvm::DenseMap<ObjCLanguageRuntime::ObjCISA,
clang::ObjCInterfaceDecl *>
ISAToInterfaceMap;
ISAToInterfaceMap m_isa_to_interface;
};
} // namespace lldb_private
#endif // liblldb_AppleObjCDeclVendor_h_

View File

@@ -0,0 +1,489 @@
//===-- AppleObjCRuntime.cpp -------------------------------------*- C++
//-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "AppleObjCRuntime.h"
#include "AppleObjCTrampolineHandler.h"
#include "clang/AST/Type.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/FunctionCaller.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StreamString.h"
#include <vector>
using namespace lldb;
using namespace lldb_private;
static constexpr std::chrono::seconds g_po_function_timeout(15);
AppleObjCRuntime::~AppleObjCRuntime() {}
AppleObjCRuntime::AppleObjCRuntime(Process *process)
: ObjCLanguageRuntime(process), m_read_objc_library(false),
m_objc_trampoline_handler_ap(), m_Foundation_major() {
ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
}
bool AppleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) {
CompilerType compiler_type(valobj.GetCompilerType());
bool is_signed;
// ObjC objects can only be pointers (or numbers that actually represents
// pointers
// but haven't been typecast, because reasons..)
if (!compiler_type.IsIntegerType(is_signed) && !compiler_type.IsPointerType())
return false;
// Make the argument list: we pass one arg, the address of our pointer, to the
// print function.
Value val;
if (!valobj.ResolveValue(val.GetScalar()))
return false;
// Value Objects may not have a process in their ExecutionContextRef. But we
// need to have one
// in the ref we pass down to eventually call description. Get it from the
// target if it isn't
// present.
ExecutionContext exe_ctx;
if (valobj.GetProcessSP()) {
exe_ctx = ExecutionContext(valobj.GetExecutionContextRef());
} else {
exe_ctx.SetContext(valobj.GetTargetSP(), true);
if (!exe_ctx.HasProcessScope())
return false;
}
return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope());
}
bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value,
ExecutionContextScope *exe_scope) {
if (!m_read_objc_library)
return false;
ExecutionContext exe_ctx;
exe_scope->CalculateExecutionContext(exe_ctx);
Process *process = exe_ctx.GetProcessPtr();
if (!process)
return false;
// We need other parts of the exe_ctx, but the processes have to match.
assert(m_process == process);
// Get the function address for the print function.
const Address *function_address = GetPrintForDebuggerAddr();
if (!function_address)
return false;
Target *target = exe_ctx.GetTargetPtr();
CompilerType compiler_type = value.GetCompilerType();
if (compiler_type) {
if (!ClangASTContext::IsObjCObjectPointerType(compiler_type)) {
strm.Printf("Value doesn't point to an ObjC object.\n");
return false;
}
} else {
// If it is not a pointer, see if we can make it into a pointer.
ClangASTContext *ast_context = target->GetScratchClangASTContext();
CompilerType opaque_type = ast_context->GetBasicType(eBasicTypeObjCID);
if (!opaque_type)
opaque_type = ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
// value.SetContext(Value::eContextTypeClangType, opaque_type_ptr);
value.SetCompilerType(opaque_type);
}
ValueList arg_value_list;
arg_value_list.PushValue(value);
// This is the return value:
ClangASTContext *ast_context = target->GetScratchClangASTContext();
CompilerType return_compiler_type = ast_context->GetCStringType(true);
Value ret;
// ret.SetContext(Value::eContextTypeClangType, return_compiler_type);
ret.SetCompilerType(return_compiler_type);
if (exe_ctx.GetFramePtr() == NULL) {
Thread *thread = exe_ctx.GetThreadPtr();
if (thread == NULL) {
exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread());
thread = exe_ctx.GetThreadPtr();
}
if (thread) {
exe_ctx.SetFrameSP(thread->GetSelectedFrame());
}
}
// Now we're ready to call the function:
DiagnosticManager diagnostics;
lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS;
if (!m_print_object_caller_up) {
Status error;
m_print_object_caller_up.reset(
exe_scope->CalculateTarget()->GetFunctionCallerForLanguage(
eLanguageTypeObjC, return_compiler_type, *function_address,
arg_value_list, "objc-object-description", error));
if (error.Fail()) {
m_print_object_caller_up.reset();
strm.Printf("Could not get function runner to call print for debugger "
"function: %s.",
error.AsCString());
return false;
}
m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr,
diagnostics);
} else {
m_print_object_caller_up->WriteFunctionArguments(
exe_ctx, wrapper_struct_addr, arg_value_list, diagnostics);
}
EvaluateExpressionOptions options;
options.SetUnwindOnError(true);
options.SetTryAllThreads(true);
options.SetStopOthers(true);
options.SetIgnoreBreakpoints(true);
options.SetTimeout(g_po_function_timeout);
ExpressionResults results = m_print_object_caller_up->ExecuteFunction(
exe_ctx, &wrapper_struct_addr, options, diagnostics, ret);
if (results != eExpressionCompleted) {
strm.Printf("Error evaluating Print Object function: %d.\n", results);
return false;
}
addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
char buf[512];
size_t cstr_len = 0;
size_t full_buffer_len = sizeof(buf) - 1;
size_t curr_len = full_buffer_len;
while (curr_len == full_buffer_len) {
Status error;
curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf,
sizeof(buf), error);
strm.Write(buf, curr_len);
cstr_len += curr_len;
}
return cstr_len > 0;
}
lldb::ModuleSP AppleObjCRuntime::GetObjCModule() {
ModuleSP module_sp(m_objc_module_wp.lock());
if (module_sp)
return module_sp;
Process *process = GetProcess();
if (process) {
const ModuleList &modules = process->GetTarget().GetImages();
for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
module_sp = modules.GetModuleAtIndex(idx);
if (AppleObjCRuntime::AppleIsModuleObjCLibrary(module_sp)) {
m_objc_module_wp = module_sp;
return module_sp;
}
}
}
return ModuleSP();
}
Address *AppleObjCRuntime::GetPrintForDebuggerAddr() {
if (!m_PrintForDebugger_addr.get()) {
const ModuleList &modules = m_process->GetTarget().GetImages();
SymbolContextList contexts;
SymbolContext context;
if ((!modules.FindSymbolsWithNameAndType(ConstString("_NSPrintForDebugger"),
eSymbolTypeCode, contexts)) &&
(!modules.FindSymbolsWithNameAndType(ConstString("_CFPrintForDebugger"),
eSymbolTypeCode, contexts)))
return NULL;
contexts.GetContextAtIndex(0, context);
m_PrintForDebugger_addr.reset(new Address(context.symbol->GetAddress()));
}
return m_PrintForDebugger_addr.get();
}
bool AppleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
return in_value.GetCompilerType().IsPossibleDynamicType(
NULL,
false, // do not check C++
true); // check ObjC
}
bool AppleObjCRuntime::GetDynamicTypeAndAddress(
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name, Address &address,
Value::ValueType &value_type) {
return false;
}
TypeAndOrName
AppleObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
ValueObject &static_value) {
CompilerType static_type(static_value.GetCompilerType());
Flags static_type_flags(static_type.GetTypeInfo());
TypeAndOrName ret(type_and_or_name);
if (type_and_or_name.HasType()) {
// The type will always be the type of the dynamic object. If our parent's
// type was a pointer,
// then our type should be a pointer to the type of the dynamic object. If
// a reference, then the original type
// should be okay...
CompilerType orig_type = type_and_or_name.GetCompilerType();
CompilerType corrected_type = orig_type;
if (static_type_flags.AllSet(eTypeIsPointer))
corrected_type = orig_type.GetPointerType();
ret.SetCompilerType(corrected_type);
} else {
// If we are here we need to adjust our dynamic type name to include the
// correct & or * symbol
std::string corrected_name(type_and_or_name.GetName().GetCString());
if (static_type_flags.AllSet(eTypeIsPointer))
corrected_name.append(" *");
// the parent type should be a correctly pointer'ed or referenc'ed type
ret.SetCompilerType(static_type);
ret.SetName(corrected_name.c_str());
}
return ret;
}
bool AppleObjCRuntime::AppleIsModuleObjCLibrary(const ModuleSP &module_sp) {
if (module_sp) {
const FileSpec &module_file_spec = module_sp->GetFileSpec();
static ConstString ObjCName("libobjc.A.dylib");
if (module_file_spec) {
if (module_file_spec.GetFilename() == ObjCName)
return true;
}
}
return false;
}
// we use the version of Foundation to make assumptions about the ObjC runtime
// on a target
uint32_t AppleObjCRuntime::GetFoundationVersion() {
if (!m_Foundation_major.hasValue()) {
const ModuleList &modules = m_process->GetTarget().GetImages();
uint32_t major = UINT32_MAX;
for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx);
if (!module_sp)
continue;
if (strcmp(module_sp->GetFileSpec().GetFilename().AsCString(""),
"Foundation") == 0) {
module_sp->GetVersion(&major, 1);
m_Foundation_major = major;
return major;
}
}
return LLDB_INVALID_MODULE_VERSION;
} else
return m_Foundation_major.getValue();
}
void AppleObjCRuntime::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
lldb::addr_t &cf_false) {
cf_true = cf_false = LLDB_INVALID_ADDRESS;
}
bool AppleObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
return AppleIsModuleObjCLibrary(module_sp);
}
bool AppleObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
// Maybe check here and if we have a handler already, and the UUID of this
// module is the same as the one in the
// current module, then we don't have to reread it?
m_objc_trampoline_handler_ap.reset(
new AppleObjCTrampolineHandler(m_process->shared_from_this(), module_sp));
if (m_objc_trampoline_handler_ap.get() != NULL) {
m_read_objc_library = true;
return true;
} else
return false;
}
ThreadPlanSP AppleObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
bool stop_others) {
ThreadPlanSP thread_plan_sp;
if (m_objc_trampoline_handler_ap.get())
thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan(
thread, stop_others);
return thread_plan_sp;
}
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
ObjCLanguageRuntime::ObjCRuntimeVersions
AppleObjCRuntime::GetObjCVersion(Process *process, ModuleSP &objc_module_sp) {
if (!process)
return ObjCRuntimeVersions::eObjC_VersionUnknown;
Target &target = process->GetTarget();
if (target.GetArchitecture().GetTriple().getVendor() !=
llvm::Triple::VendorType::Apple)
return ObjCRuntimeVersions::eObjC_VersionUnknown;
const ModuleList &target_modules = target.GetImages();
std::lock_guard<std::recursive_mutex> gaurd(target_modules.GetMutex());
size_t num_images = target_modules.GetSize();
for (size_t i = 0; i < num_images; i++) {
ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked(i);
// One tricky bit here is that we might get called as part of the initial
// module loading, but
// before all the pre-run libraries get winnowed from the module list. So
// there might actually
// be an old and incorrect ObjC library sitting around in the list, and we
// don't want to look at that.
// That's why we call IsLoadedInTarget.
if (AppleIsModuleObjCLibrary(module_sp) &&
module_sp->IsLoadedInTarget(&target)) {
objc_module_sp = module_sp;
ObjectFile *ofile = module_sp->GetObjectFile();
if (!ofile)
return ObjCRuntimeVersions::eObjC_VersionUnknown;
SectionList *sections = module_sp->GetSectionList();
if (!sections)
return ObjCRuntimeVersions::eObjC_VersionUnknown;
SectionSP v1_telltale_section_sp =
sections->FindSectionByName(ConstString("__OBJC"));
if (v1_telltale_section_sp) {
return ObjCRuntimeVersions::eAppleObjC_V1;
}
return ObjCRuntimeVersions::eAppleObjC_V2;
}
}
return ObjCRuntimeVersions::eObjC_VersionUnknown;
}
void AppleObjCRuntime::SetExceptionBreakpoints() {
const bool catch_bp = false;
const bool throw_bp = true;
const bool is_internal = true;
if (!m_objc_exception_bp_sp) {
m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint(
m_process->GetTarget(), GetLanguageType(), catch_bp, throw_bp,
is_internal);
if (m_objc_exception_bp_sp)
m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception");
} else
m_objc_exception_bp_sp->SetEnabled(true);
}
void AppleObjCRuntime::ClearExceptionBreakpoints() {
if (!m_process)
return;
if (m_objc_exception_bp_sp.get()) {
m_objc_exception_bp_sp->SetEnabled(false);
}
}
bool AppleObjCRuntime::ExceptionBreakpointsAreSet() {
return m_objc_exception_bp_sp && m_objc_exception_bp_sp->IsEnabled();
}
bool AppleObjCRuntime::ExceptionBreakpointsExplainStop(
lldb::StopInfoSP stop_reason) {
if (!m_process)
return false;
if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint)
return false;
uint64_t break_site_id = stop_reason->GetValue();
return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(
break_site_id, m_objc_exception_bp_sp->GetID());
}
bool AppleObjCRuntime::CalculateHasNewLiteralsAndIndexing() {
if (!m_process)
return false;
Target &target(m_process->GetTarget());
static ConstString s_method_signature(
"-[NSDictionary objectForKeyedSubscript:]");
static ConstString s_arclite_method_signature(
"__arclite_objectForKeyedSubscript");
SymbolContextList sc_list;
if (target.GetImages().FindSymbolsWithNameAndType(s_method_signature,
eSymbolTypeCode, sc_list) ||
target.GetImages().FindSymbolsWithNameAndType(s_arclite_method_signature,
eSymbolTypeCode, sc_list))
return true;
else
return false;
}
lldb::SearchFilterSP AppleObjCRuntime::CreateExceptionSearchFilter() {
Target &target = m_process->GetTarget();
if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) {
FileSpecList filter_modules;
filter_modules.Append(FileSpec("libobjc.A.dylib", false));
return target.GetSearchFilterForModuleList(&filter_modules);
} else {
return LanguageRuntime::CreateExceptionSearchFilter();
}
}
void AppleObjCRuntime::ReadObjCLibraryIfNeeded(const ModuleList &module_list) {
if (!HasReadObjCLibrary()) {
std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
size_t num_modules = module_list.GetSize();
for (size_t i = 0; i < num_modules; i++) {
auto mod = module_list.GetModuleAtIndex(i);
if (IsModuleObjCLibrary(mod)) {
ReadObjCLibrary(mod);
break;
}
}
}
}
void AppleObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
ReadObjCLibraryIfNeeded(module_list);
}

View File

@@ -0,0 +1,129 @@
//===-- AppleObjCRuntime.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_AppleObjCRuntime_h_
#define liblldb_AppleObjCRuntime_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
#include "llvm/ADT/Optional.h"
// Project includes
#include "AppleObjCTrampolineHandler.h"
#include "AppleThreadPlanStepThroughObjCTrampoline.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
class AppleObjCRuntime : public lldb_private::ObjCLanguageRuntime {
public:
~AppleObjCRuntime() override;
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
// Note there is no CreateInstance, Initialize & Terminate functions here,
// because
// you can't make an instance of this generic runtime.
static bool classof(const ObjCLanguageRuntime *runtime) {
switch (runtime->GetRuntimeVersion()) {
case ObjCRuntimeVersions::eAppleObjC_V1:
case ObjCRuntimeVersions::eAppleObjC_V2:
return true;
default:
return false;
}
}
// These are generic runtime functions:
bool GetObjectDescription(Stream &str, Value &value,
ExecutionContextScope *exe_scope) override;
bool GetObjectDescription(Stream &str, ValueObject &object) override;
bool CouldHaveDynamicValue(ValueObject &in_value) override;
bool GetDynamicTypeAndAddress(ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name,
Address &address,
Value::ValueType &value_type) override;
TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
ValueObject &static_value) override;
// These are the ObjC specific functions.
bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) override;
bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) override;
bool HasReadObjCLibrary() override { return m_read_objc_library; }
lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
bool stop_others) override;
// Get the "libobjc.A.dylib" module from the current target if we can find
// it, also cache it once it is found to ensure quick lookups.
lldb::ModuleSP GetObjCModule();
// Sync up with the target
void ModulesDidLoad(const ModuleList &module_list) override;
void SetExceptionBreakpoints() override;
void ClearExceptionBreakpoints() override;
bool ExceptionBreakpointsAreSet() override;
bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override;
lldb::SearchFilterSP CreateExceptionSearchFilter() override;
uint32_t GetFoundationVersion();
virtual void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
lldb::addr_t &cf_false);
virtual bool IsTaggedPointer (lldb::addr_t addr) { return false; }
protected:
// Call CreateInstance instead.
AppleObjCRuntime(Process *process);
bool CalculateHasNewLiteralsAndIndexing() override;
static bool AppleIsModuleObjCLibrary(const lldb::ModuleSP &module_sp);
static ObjCRuntimeVersions GetObjCVersion(Process *process,
lldb::ModuleSP &objc_module_sp);
void ReadObjCLibraryIfNeeded(const ModuleList &module_list);
Address *GetPrintForDebuggerAddr();
std::unique_ptr<Address> m_PrintForDebugger_addr;
bool m_read_objc_library;
std::unique_ptr<lldb_private::AppleObjCTrampolineHandler>
m_objc_trampoline_handler_ap;
lldb::BreakpointSP m_objc_exception_bp_sp;
lldb::ModuleWP m_objc_module_wp;
std::unique_ptr<FunctionCaller> m_print_object_caller_up;
llvm::Optional<uint32_t> m_Foundation_major;
};
} // namespace lldb_private
#endif // liblldb_AppleObjCRuntime_h_

View File

@@ -0,0 +1,441 @@
//===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++
//-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "AppleObjCRuntimeV1.h"
#include "AppleObjCDeclVendor.h"
#include "AppleObjCTrampolineHandler.h"
#include "clang/AST/Type.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Expression/FunctionCaller.h"
#include "lldb/Expression/UtilityFunction.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StreamString.h"
#include <vector>
using namespace lldb;
using namespace lldb_private;
AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)
: AppleObjCRuntime(process), m_hash_signature(),
m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {}
// for V1 runtime we just try to return a class name as that is the minimum
// level of support
// required for the data formatters to work
bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name, Address &address,
Value::ValueType &value_type) {
class_type_or_name.Clear();
value_type = Value::ValueType::eValueTypeScalar;
if (CouldHaveDynamicValue(in_value)) {
auto class_descriptor(GetClassDescriptor(in_value));
if (class_descriptor && class_descriptor->IsValid() &&
class_descriptor->GetClassName()) {
const addr_t object_ptr = in_value.GetPointerValue();
address.SetRawAddress(object_ptr);
class_type_or_name.SetName(class_descriptor->GetClassName());
}
}
return class_type_or_name.IsEmpty() == false;
}
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
lldb_private::LanguageRuntime *
AppleObjCRuntimeV1::CreateInstance(Process *process,
lldb::LanguageType language) {
// FIXME: This should be a MacOS or iOS process, and we need to look for the
// OBJC section to make
// sure we aren't using the V1 runtime.
if (language == eLanguageTypeObjC) {
ModuleSP objc_module_sp;
if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
ObjCRuntimeVersions::eAppleObjC_V1)
return new AppleObjCRuntimeV1(process);
else
return NULL;
} else
return NULL;
}
void AppleObjCRuntimeV1::Initialize() {
PluginManager::RegisterPlugin(
GetPluginNameStatic(), "Apple Objective C Language Runtime - Version 1",
CreateInstance);
}
void AppleObjCRuntimeV1::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}
lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() {
static ConstString g_name("apple-objc-v1");
return g_name;
}
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
ConstString AppleObjCRuntimeV1::GetPluginName() {
return GetPluginNameStatic();
}
uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; }
BreakpointResolverSP
AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp,
bool throw_bp) {
BreakpointResolverSP resolver_sp;
if (throw_bp)
resolver_sp.reset(new BreakpointResolverName(
bkpt, "objc_exception_throw", eFunctionNameTypeBase,
eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo));
// FIXME: don't do catch yet.
return resolver_sp;
}
struct BufStruct {
char contents[2048];
};
UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) {
std::unique_ptr<BufStruct> buf(new BufStruct);
int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents),
"struct __objc_class "
" \n"
"{ "
" \n"
" struct __objc_class *isa; "
" \n"
" struct __objc_class *super_class; "
" \n"
" const char *name; "
" \n"
" // rest of struct elided because unused "
" \n"
"}; "
" \n"
" "
" \n"
"struct __objc_object "
" \n"
"{ "
" \n"
" struct __objc_class *isa; "
" \n"
"}; "
" \n"
" "
" \n"
"extern \"C\" void "
" \n"
"%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
" \n"
"{ "
" \n"
" struct __objc_object *obj = (struct "
"__objc_object*)$__lldb_arg_obj; \n"
" if ($__lldb_arg_obj == (void *)0) "
" \n"
" return; // nil is ok "
" (int)strlen(obj->isa->name); "
" \n"
"} "
" \n",
name);
assert(strformatsize < (int)sizeof(buf->contents));
(void)strformatsize;
Status error;
return GetTargetRef().GetUtilityFunctionForLanguage(
buf->contents, eLanguageTypeObjC, name, error);
}
AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
ValueObject &isa_pointer) {
Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
}
AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
ObjCISA isa, lldb::ProcessSP process_sp) {
Initialize(isa, process_sp);
}
void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(
ObjCISA isa, lldb::ProcessSP process_sp) {
if (!isa || !process_sp) {
m_valid = false;
return;
}
m_valid = true;
Status error;
m_isa = process_sp->ReadPointerFromMemory(isa, error);
if (error.Fail()) {
m_valid = false;
return;
}
uint32_t ptr_size = process_sp->GetAddressByteSize();
if (!IsPointerValid(m_isa, ptr_size)) {
m_valid = false;
return;
}
m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);
if (error.Fail()) {
m_valid = false;
return;
}
if (!IsPointerValid(m_parent_isa, ptr_size, true)) {
m_valid = false;
return;
}
lldb::addr_t name_ptr =
process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);
if (error.Fail()) {
m_valid = false;
return;
}
lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
size_t count = process_sp->ReadCStringFromMemory(
name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);
if (error.Fail()) {
m_valid = false;
return;
}
if (count)
m_name = ConstString((char *)buffer_sp->GetBytes());
else
m_name = ConstString();
m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(
m_isa + 5 * ptr_size, ptr_size, 0, error);
if (error.Fail()) {
m_valid = false;
return;
}
m_process_wp = lldb::ProcessWP(process_sp);
}
AppleObjCRuntime::ClassDescriptorSP
AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {
if (!m_valid)
return AppleObjCRuntime::ClassDescriptorSP();
ProcessSP process_sp = m_process_wp.lock();
if (!process_sp)
return AppleObjCRuntime::ClassDescriptorSP();
return ObjCLanguageRuntime::ClassDescriptorSP(
new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));
}
AppleObjCRuntime::ClassDescriptorSP
AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
return ClassDescriptorSP();
}
bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(
std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
std::function<bool(const char *, const char *)> const &instance_method_func,
std::function<bool(const char *, const char *)> const &class_method_func,
std::function<bool(const char *, const char *, lldb::addr_t,
uint64_t)> const &ivar_func) const {
return false;
}
lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() {
if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
ModuleSP objc_module_sp(GetObjCModule());
if (!objc_module_sp)
return LLDB_INVALID_ADDRESS;
static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
g_objc_debug_class_hash, lldb::eSymbolTypeData);
if (symbol && symbol->ValueIsAddress()) {
Process *process = GetProcess();
if (process) {
lldb::addr_t objc_debug_class_hash_addr =
symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {
Status error;
lldb::addr_t objc_debug_class_hash_ptr =
process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
if (objc_debug_class_hash_ptr != 0 &&
objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {
m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
}
}
}
}
}
return m_isa_hash_table_ptr;
}
void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {
// TODO: implement HashTableSignature...
Process *process = GetProcess();
if (process) {
// Update the process stop ID that indicates the last time we updated the
// map, whether it was successful or not.
m_isa_to_descriptor_stop_id = process->GetStopID();
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
ProcessSP process_sp = process->shared_from_this();
ModuleSP objc_module_sp(GetObjCModule());
if (!objc_module_sp)
return;
uint32_t isa_count = 0;
lldb::addr_t hash_table_ptr = GetISAHashTablePointer();
if (hash_table_ptr != LLDB_INVALID_ADDRESS) {
// Read the NXHashTable struct:
//
// typedef struct {
// const NXHashTablePrototype *prototype;
// unsigned count;
// unsigned nbBuckets;
// void *buckets;
// const void *info;
// } NXHashTable;
Status error;
DataBufferHeap buffer(1024, 0);
if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==
20) {
const uint32_t addr_size = m_process->GetAddressByteSize();
const ByteOrder byte_order = m_process->GetByteOrder();
DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,
addr_size);
lldb::offset_t offset = addr_size; // Skip prototype
const uint32_t count = data.GetU32(&offset);
const uint32_t num_buckets = data.GetU32(&offset);
const addr_t buckets_ptr = data.GetPointer(&offset);
if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {
m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);
const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
buffer.SetByteSize(data_size);
if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,
error) == data_size) {
data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
offset = 0;
for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;
++bucket_idx) {
const uint32_t bucket_isa_count = data.GetU32(&offset);
const lldb::addr_t bucket_data = data.GetU32(&offset);
if (bucket_isa_count == 0)
continue;
isa_count += bucket_isa_count;
ObjCISA isa;
if (bucket_isa_count == 1) {
// When we only have one entry in the bucket, the bucket data is
// the "isa"
isa = bucket_data;
if (isa) {
if (!ISAIsCached(isa)) {
ClassDescriptorSP descriptor_sp(
new ClassDescriptorV1(isa, process_sp));
if (log && log->GetVerbose())
log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
" from _objc_debug_class_hash to "
"isa->descriptor cache",
isa);
AddClass(isa, descriptor_sp);
}
}
} else {
// When we have more than one entry in the bucket, the bucket
// data is a pointer
// to an array of "isa" values
addr_t isa_addr = bucket_data;
for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count;
++isa_idx, isa_addr += addr_size) {
isa = m_process->ReadPointerFromMemory(isa_addr, error);
if (isa && isa != LLDB_INVALID_ADDRESS) {
if (!ISAIsCached(isa)) {
ClassDescriptorSP descriptor_sp(
new ClassDescriptorV1(isa, process_sp));
if (log && log->GetVerbose())
log->Printf(
"AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
" from _objc_debug_class_hash to isa->descriptor "
"cache",
isa);
AddClass(isa, descriptor_sp);
}
}
}
}
}
}
}
}
}
} else {
m_isa_to_descriptor_stop_id = UINT32_MAX;
}
}
DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {
return nullptr;
}

View File

@@ -0,0 +1,161 @@
//===-- AppleObjCRuntimeV1.h ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_AppleObjCRuntimeV1_h_
#define liblldb_AppleObjCRuntimeV1_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "AppleObjCRuntime.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
class AppleObjCRuntimeV1 : public AppleObjCRuntime {
public:
~AppleObjCRuntimeV1() override = default;
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
static void Initialize();
static void Terminate();
static lldb_private::LanguageRuntime *
CreateInstance(Process *process, lldb::LanguageType language);
static lldb_private::ConstString GetPluginNameStatic();
static bool classof(const ObjCLanguageRuntime *runtime) {
switch (runtime->GetRuntimeVersion()) {
case ObjCRuntimeVersions::eAppleObjC_V1:
return true;
default:
return false;
}
}
class ClassDescriptorV1 : public ObjCLanguageRuntime::ClassDescriptor {
public:
ClassDescriptorV1(ValueObject &isa_pointer);
ClassDescriptorV1(ObjCISA isa, lldb::ProcessSP process_sp);
~ClassDescriptorV1() override = default;
ConstString GetClassName() override { return m_name; }
ClassDescriptorSP GetSuperclass() override;
ClassDescriptorSP GetMetaclass() const override;
bool IsValid() override { return m_valid; }
// v1 does not support tagged pointers
bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
uint64_t *value_bits = nullptr,
uint64_t *payload = nullptr) override {
return false;
}
uint64_t GetInstanceSize() override { return m_instance_size; }
ObjCISA GetISA() override { return m_isa; }
bool
Describe(std::function<void(ObjCLanguageRuntime::ObjCISA)> const
&superclass_func,
std::function<bool(const char *, const char *)> const
&instance_method_func,
std::function<bool(const char *, const char *)> const
&class_method_func,
std::function<bool(const char *, const char *, lldb::addr_t,
uint64_t)> const &ivar_func) const override;
protected:
void Initialize(ObjCISA isa, lldb::ProcessSP process_sp);
private:
ConstString m_name;
ObjCISA m_isa;
ObjCISA m_parent_isa;
bool m_valid;
lldb::ProcessWP m_process_wp;
uint64_t m_instance_size;
};
// These are generic runtime functions:
bool GetDynamicTypeAndAddress(ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name,
Address &address,
Value::ValueType &value_type) override;
UtilityFunction *CreateObjectChecker(const char *) override;
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
ConstString GetPluginName() override;
uint32_t GetPluginVersion() override;
ObjCRuntimeVersions GetRuntimeVersion() const override {
return ObjCRuntimeVersions::eAppleObjC_V1;
}
void UpdateISAToDescriptorMapIfNeeded() override;
DeclVendor *GetDeclVendor() override;
protected:
lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
bool catch_bp,
bool throw_bp) override;
class HashTableSignature {
public:
HashTableSignature()
: m_count(0), m_num_buckets(0), m_buckets_ptr(LLDB_INVALID_ADDRESS) {}
bool NeedsUpdate(uint32_t count, uint32_t num_buckets,
lldb::addr_t buckets_ptr) {
return m_count != count || m_num_buckets != num_buckets ||
m_buckets_ptr != buckets_ptr;
}
void UpdateSignature(uint32_t count, uint32_t num_buckets,
lldb::addr_t buckets_ptr) {
m_count = count;
m_num_buckets = num_buckets;
m_buckets_ptr = buckets_ptr;
}
protected:
uint32_t m_count;
uint32_t m_num_buckets;
lldb::addr_t m_buckets_ptr;
};
lldb::addr_t GetISAHashTablePointer();
HashTableSignature m_hash_signature;
lldb::addr_t m_isa_hash_table_ptr;
std::unique_ptr<DeclVendor> m_decl_vendor_ap;
private:
AppleObjCRuntimeV1(Process *process);
};
} // namespace lldb_private
#endif // liblldb_AppleObjCRuntimeV1_h_

View File

@@ -0,0 +1,346 @@
//===-- AppleObjCRuntimeV2.h ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_AppleObjCRuntimeV2_h_
#define liblldb_AppleObjCRuntimeV2_h_
// C Includes
// C++ Includes
#include <map>
#include <memory>
#include <mutex>
// Other libraries and framework includes
// Project includes
#include "AppleObjCRuntime.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/lldb-private.h"
class RemoteNXMapTable;
namespace lldb_private {
class AppleObjCRuntimeV2 : public AppleObjCRuntime {
public:
~AppleObjCRuntimeV2() override = default;
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
static void Initialize();
static void Terminate();
static lldb_private::LanguageRuntime *
CreateInstance(Process *process, lldb::LanguageType language);
static lldb_private::ConstString GetPluginNameStatic();
static bool classof(const ObjCLanguageRuntime *runtime) {
switch (runtime->GetRuntimeVersion()) {
case ObjCRuntimeVersions::eAppleObjC_V2:
return true;
default:
return false;
}
}
// These are generic runtime functions:
bool GetDynamicTypeAndAddress(ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name,
Address &address,
Value::ValueType &value_type) override;
UtilityFunction *CreateObjectChecker(const char *) override;
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
ConstString GetPluginName() override;
uint32_t GetPluginVersion() override;
ObjCRuntimeVersions GetRuntimeVersion() const override {
return ObjCRuntimeVersions::eAppleObjC_V2;
}
size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
const char *ivar_name) override;
void UpdateISAToDescriptorMapIfNeeded() override;
ConstString GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) override;
ClassDescriptorSP GetClassDescriptor(ValueObject &in_value) override;
ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override;
DeclVendor *GetDeclVendor() override;
lldb::addr_t LookupRuntimeSymbol(const ConstString &name) override;
EncodingToTypeSP GetEncodingToType() override;
bool IsTaggedPointer(lldb::addr_t ptr) override;
TaggedPointerVendor *GetTaggedPointerVendor() override {
return m_tagged_pointer_vendor_ap.get();
}
void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
lldb::addr_t &cf_false) override;
// none of these are valid ISAs - we use them to infer the type
// of tagged pointers - if we have something meaningful to say
// we report an actual type - otherwise, we just say tagged
// there is no connection between the values here and the tagged pointers map
static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA = 1;
static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSAtom = 2;
static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSNumber = 3;
static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDateTS = 4;
static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSManagedObject =
5;
static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDate = 6;
protected:
lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
bool catch_bp,
bool throw_bp) override;
private:
class HashTableSignature {
public:
HashTableSignature();
bool NeedsUpdate(Process *process, AppleObjCRuntimeV2 *runtime,
RemoteNXMapTable &hash_table);
void UpdateSignature(const RemoteNXMapTable &hash_table);
protected:
uint32_t m_count;
uint32_t m_num_buckets;
lldb::addr_t m_buckets_ptr;
};
class NonPointerISACache {
public:
static NonPointerISACache *
CreateInstance(AppleObjCRuntimeV2 &runtime,
const lldb::ModuleSP &objc_module_sp);
ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(ObjCISA isa);
private:
NonPointerISACache(AppleObjCRuntimeV2 &runtime,
const lldb::ModuleSP &objc_module_sp,
uint64_t objc_debug_isa_class_mask,
uint64_t objc_debug_isa_magic_mask,
uint64_t objc_debug_isa_magic_value,
uint64_t objc_debug_indexed_isa_magic_mask,
uint64_t objc_debug_indexed_isa_magic_value,
uint64_t objc_debug_indexed_isa_index_mask,
uint64_t objc_debug_indexed_isa_index_shift,
lldb::addr_t objc_indexed_classes);
bool EvaluateNonPointerISA(ObjCISA isa, ObjCISA &ret_isa);
AppleObjCRuntimeV2 &m_runtime;
std::map<ObjCISA, ObjCLanguageRuntime::ClassDescriptorSP> m_cache;
lldb::ModuleWP m_objc_module_wp;
uint64_t m_objc_debug_isa_class_mask;
uint64_t m_objc_debug_isa_magic_mask;
uint64_t m_objc_debug_isa_magic_value;
uint64_t m_objc_debug_indexed_isa_magic_mask;
uint64_t m_objc_debug_indexed_isa_magic_value;
uint64_t m_objc_debug_indexed_isa_index_mask;
uint64_t m_objc_debug_indexed_isa_index_shift;
lldb::addr_t m_objc_indexed_classes;
std::vector<lldb::addr_t> m_indexed_isa_cache;
friend class AppleObjCRuntimeV2;
DISALLOW_COPY_AND_ASSIGN(NonPointerISACache);
};
class TaggedPointerVendorV2
: public ObjCLanguageRuntime::TaggedPointerVendor {
public:
~TaggedPointerVendorV2() override = default;
static TaggedPointerVendorV2 *
CreateInstance(AppleObjCRuntimeV2 &runtime,
const lldb::ModuleSP &objc_module_sp);
protected:
AppleObjCRuntimeV2 &m_runtime;
TaggedPointerVendorV2(AppleObjCRuntimeV2 &runtime)
: TaggedPointerVendor(), m_runtime(runtime) {}
private:
DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorV2);
};
class TaggedPointerVendorRuntimeAssisted : public TaggedPointerVendorV2 {
public:
bool IsPossibleTaggedPointer(lldb::addr_t ptr) override;
ObjCLanguageRuntime::ClassDescriptorSP
GetClassDescriptor(lldb::addr_t ptr) override;
protected:
TaggedPointerVendorRuntimeAssisted(
AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
uint32_t objc_debug_taggedpointer_slot_shift,
uint32_t objc_debug_taggedpointer_slot_mask,
uint32_t objc_debug_taggedpointer_payload_lshift,
uint32_t objc_debug_taggedpointer_payload_rshift,
lldb::addr_t objc_debug_taggedpointer_classes);
typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache;
typedef Cache::iterator CacheIterator;
Cache m_cache;
uint64_t m_objc_debug_taggedpointer_mask;
uint32_t m_objc_debug_taggedpointer_slot_shift;
uint32_t m_objc_debug_taggedpointer_slot_mask;
uint32_t m_objc_debug_taggedpointer_payload_lshift;
uint32_t m_objc_debug_taggedpointer_payload_rshift;
lldb::addr_t m_objc_debug_taggedpointer_classes;
friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorRuntimeAssisted);
};
class TaggedPointerVendorExtended
: public TaggedPointerVendorRuntimeAssisted {
public:
ObjCLanguageRuntime::ClassDescriptorSP
GetClassDescriptor(lldb::addr_t ptr) override;
protected:
TaggedPointerVendorExtended(
AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
uint64_t objc_debug_taggedpointer_ext_mask,
uint32_t objc_debug_taggedpointer_slot_shift,
uint32_t objc_debug_taggedpointer_ext_slot_shift,
uint32_t objc_debug_taggedpointer_slot_mask,
uint32_t objc_debug_taggedpointer_ext_slot_mask,
uint32_t objc_debug_taggedpointer_payload_lshift,
uint32_t objc_debug_taggedpointer_payload_rshift,
uint32_t objc_debug_taggedpointer_ext_payload_lshift,
uint32_t objc_debug_taggedpointer_ext_payload_rshift,
lldb::addr_t objc_debug_taggedpointer_classes,
lldb::addr_t objc_debug_taggedpointer_ext_classes);
bool IsPossibleExtendedTaggedPointer(lldb::addr_t ptr);
typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache;
typedef Cache::iterator CacheIterator;
Cache m_ext_cache;
uint64_t m_objc_debug_taggedpointer_ext_mask;
uint32_t m_objc_debug_taggedpointer_ext_slot_shift;
uint32_t m_objc_debug_taggedpointer_ext_slot_mask;
uint32_t m_objc_debug_taggedpointer_ext_payload_lshift;
uint32_t m_objc_debug_taggedpointer_ext_payload_rshift;
lldb::addr_t m_objc_debug_taggedpointer_ext_classes;
friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorExtended);
};
class TaggedPointerVendorLegacy : public TaggedPointerVendorV2 {
public:
bool IsPossibleTaggedPointer(lldb::addr_t ptr) override;
ObjCLanguageRuntime::ClassDescriptorSP
GetClassDescriptor(lldb::addr_t ptr) override;
protected:
TaggedPointerVendorLegacy(AppleObjCRuntimeV2 &runtime)
: TaggedPointerVendorV2(runtime) {}
friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorLegacy);
};
struct DescriptorMapUpdateResult {
bool m_update_ran;
uint32_t m_num_found;
DescriptorMapUpdateResult(bool ran, uint32_t found) {
m_update_ran = ran;
m_num_found = found;
}
static DescriptorMapUpdateResult Fail() { return {false, 0}; }
static DescriptorMapUpdateResult Success(uint32_t found) {
return {true, found};
}
};
AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
ObjCISA GetPointerISA(ObjCISA isa);
lldb::addr_t GetISAHashTablePointer();
bool UpdateISAToDescriptorMapFromMemory(RemoteNXMapTable &hash_table);
DescriptorMapUpdateResult
UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
uint32_t num_class_infos);
DescriptorMapUpdateResult UpdateISAToDescriptorMapSharedCache();
enum class SharedCacheWarningReason {
eExpressionExecutionFailure,
eNotEnoughClassesRead
};
void WarnIfNoClassesCached(SharedCacheWarningReason reason);
lldb::addr_t GetSharedCacheReadOnlyAddress();
bool GetCFBooleanValuesIfNeeded();
friend class ClassDescriptorV2;
std::unique_ptr<UtilityFunction> m_get_class_info_code;
lldb::addr_t m_get_class_info_args;
std::mutex m_get_class_info_args_mutex;
std::unique_ptr<UtilityFunction> m_get_shared_cache_class_info_code;
lldb::addr_t m_get_shared_cache_class_info_args;
std::mutex m_get_shared_cache_class_info_args_mutex;
std::unique_ptr<DeclVendor> m_decl_vendor_ap;
lldb::addr_t m_isa_hash_table_ptr;
HashTableSignature m_hash_signature;
bool m_has_object_getClass;
bool m_loaded_objc_opt;
std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_ap;
std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_ap;
EncodingToTypeSP m_encoding_to_type_sp;
bool m_noclasses_warning_emitted;
llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
};
} // namespace lldb_private
#endif // liblldb_AppleObjCRuntimeV2_h_

View File

@@ -0,0 +1,162 @@
//===-- AppleObjCTrampolineHandler.h ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef lldb_AppleObjCTrampolineHandler_h_
#define lldb_AppleObjCTrampolineHandler_h_
// C Includes
// C++ Includes
#include <map>
#include <mutex>
#include <vector>
// Other libraries and framework includes
// Project includes
#include "lldb/Expression/UtilityFunction.h"
#include "lldb/lldb-public.h"
namespace lldb_private {
class AppleObjCTrampolineHandler {
public:
AppleObjCTrampolineHandler(const lldb::ProcessSP &process_sp,
const lldb::ModuleSP &objc_module_sp);
~AppleObjCTrampolineHandler();
lldb::ThreadPlanSP GetStepThroughDispatchPlan(Thread &thread,
bool stop_others);
FunctionCaller *GetLookupImplementationFunctionCaller();
bool AddrIsMsgForward(lldb::addr_t addr) const {
return (addr == m_msg_forward_addr || addr == m_msg_forward_stret_addr);
}
struct DispatchFunction {
public:
typedef enum { eFixUpNone, eFixUpFixed, eFixUpToFix } FixUpState;
const char *name;
bool stret_return;
bool is_super;
bool is_super2;
FixUpState fixedup;
};
lldb::addr_t SetupDispatchFunction(Thread &thread,
ValueList &dispatch_values);
private:
static const char *g_lookup_implementation_function_name;
static const char *g_lookup_implementation_with_stret_function_code;
static const char *g_lookup_implementation_no_stret_function_code;
class AppleObjCVTables {
public:
// These come from objc-gdb.h.
enum VTableFlags {
eOBJC_TRAMPOLINE_MESSAGE = (1 << 0), // trampoline acts like objc_msgSend
eOBJC_TRAMPOLINE_STRET = (1 << 1), // trampoline is struct-returning
eOBJC_TRAMPOLINE_VTABLE = (1 << 2) // trampoline is vtable dispatcher
};
private:
struct VTableDescriptor {
VTableDescriptor(uint32_t in_flags, lldb::addr_t in_code_start)
: flags(in_flags), code_start(in_code_start) {}
uint32_t flags;
lldb::addr_t code_start;
};
class VTableRegion {
public:
VTableRegion()
: m_valid(false), m_owner(NULL), m_header_addr(LLDB_INVALID_ADDRESS),
m_code_start_addr(0), m_code_end_addr(0), m_next_region(0) {}
VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr);
void SetUpRegion();
lldb::addr_t GetNextRegionAddr() { return m_next_region; }
lldb::addr_t GetCodeStart() { return m_code_start_addr; }
lldb::addr_t GetCodeEnd() { return m_code_end_addr; }
uint32_t GetFlagsForVTableAtAddress(lldb::addr_t address) { return 0; }
bool IsValid() { return m_valid; }
bool AddressInRegion(lldb::addr_t addr, uint32_t &flags);
void Dump(Stream &s);
public:
bool m_valid;
AppleObjCVTables *m_owner;
lldb::addr_t m_header_addr;
lldb::addr_t m_code_start_addr;
lldb::addr_t m_code_end_addr;
std::vector<VTableDescriptor> m_descriptors;
lldb::addr_t m_next_region;
};
public:
AppleObjCVTables(const lldb::ProcessSP &process_sp,
const lldb::ModuleSP &objc_module_sp);
~AppleObjCVTables();
bool InitializeVTableSymbols();
static bool RefreshTrampolines(void *baton,
StoppointCallbackContext *context,
lldb::user_id_t break_id,
lldb::user_id_t break_loc_id);
bool ReadRegions();
bool ReadRegions(lldb::addr_t region_addr);
bool IsAddressInVTables(lldb::addr_t addr, uint32_t &flags);
lldb::ProcessSP GetProcessSP() { return m_process_wp.lock(); }
private:
lldb::ProcessWP m_process_wp;
typedef std::vector<VTableRegion> region_collection;
lldb::addr_t m_trampoline_header;
lldb::break_id_t m_trampolines_changed_bp_id;
region_collection m_regions;
lldb::ModuleSP m_objc_module_sp;
};
static const DispatchFunction g_dispatch_functions[];
typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch
// fn address to the index in
// g_dispatch_functions
MsgsendMap m_msgSend_map;
lldb::ProcessWP m_process_wp;
lldb::ModuleSP m_objc_module_sp;
const char *m_lookup_implementation_function_code;
std::unique_ptr<UtilityFunction> m_impl_code;
std::mutex m_impl_function_mutex;
lldb::addr_t m_impl_fn_addr;
lldb::addr_t m_impl_stret_fn_addr;
lldb::addr_t m_msg_forward_addr;
lldb::addr_t m_msg_forward_stret_addr;
std::unique_ptr<AppleObjCVTables> m_vtables_ap;
};
} // namespace lldb_private
#endif // lldb_AppleObjCTrampolineHandler_h_

View File

@@ -0,0 +1,396 @@
//===-- AppleObjCTypeEncodingParser.cpp -------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "AppleObjCTypeEncodingParser.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/ClangUtil.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/StringLexer.h"
#include <vector>
using namespace lldb_private;
using namespace lldb_utility;
AppleObjCTypeEncodingParser::AppleObjCTypeEncodingParser(
ObjCLanguageRuntime &runtime)
: ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) {
if (!m_scratch_ast_ctx_ap)
m_scratch_ast_ctx_ap.reset(new ClangASTContext(runtime.GetProcess()
->GetTarget()
.GetArchitecture()
.GetTriple()
.str()
.c_str()));
}
std::string
AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
StreamString buffer;
while (type.HasAtLeast(1) && type.Peek() != '=')
buffer.Printf("%c", type.Next());
return buffer.GetString();
}
std::string
AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer &type) {
StreamString buffer;
while (type.HasAtLeast(1) && type.Peek() != '"')
buffer.Printf("%c", type.Next());
StringLexer::Character next = type.Next();
UNUSED_IF_ASSERT_DISABLED(next);
assert(next == '"');
return buffer.GetString();
}
uint32_t
AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
uint32_t total = 0;
while (type.HasAtLeast(1) && isdigit(type.Peek()))
total = 10 * total + (type.Next() - '0');
return total;
}
// as an extension to the published grammar recent runtimes emit structs like
// this:
// "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
AppleObjCTypeEncodingParser::StructElement::StructElement()
: name(""), type(clang::QualType()), bitfield(0) {}
AppleObjCTypeEncodingParser::StructElement
AppleObjCTypeEncodingParser::ReadStructElement(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression) {
StructElement retval;
if (type.NextIf('"'))
retval.name = ReadQuotedString(type);
if (!type.NextIf('"'))
return retval;
uint32_t bitfield_size = 0;
retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
retval.bitfield = bitfield_size;
return retval;
}
clang::QualType
AppleObjCTypeEncodingParser::BuildStruct(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression) {
return BuildAggregate(ast_ctx, type, for_expression, '{', '}',
clang::TTK_Struct);
}
clang::QualType
AppleObjCTypeEncodingParser::BuildUnion(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression) {
return BuildAggregate(ast_ctx, type, for_expression, '(', ')',
clang::TTK_Union);
}
clang::QualType AppleObjCTypeEncodingParser::BuildAggregate(
clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
bool for_expression, char opener, char closer, uint32_t kind) {
if (!type.NextIf(opener))
return clang::QualType();
std::string name(ReadStructName(type));
// We do not handle templated classes/structs at the moment.
// If the name has a < in it, we are going to abandon this.
// We're still obliged to parse it, so we just set a flag that
// means "Don't actually build anything."
const bool is_templated = name.find('<') != std::string::npos;
if (!type.NextIf('='))
return clang::QualType();
bool in_union = true;
std::vector<StructElement> elements;
while (in_union && type.HasAtLeast(1)) {
if (type.NextIf(closer)) {
in_union = false;
break;
} else {
auto element = ReadStructElement(ast_ctx, type, for_expression);
if (element.type.isNull())
break;
else
elements.push_back(element);
}
}
if (in_union)
return clang::QualType();
if (is_templated)
return clang::QualType(); // This is where we bail out. Sorry!
ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
if (!lldb_ctx)
return clang::QualType();
CompilerType union_type(lldb_ctx->CreateRecordType(
nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
if (union_type) {
ClangASTContext::StartTagDeclarationDefinition(union_type);
unsigned int count = 0;
for (auto element : elements) {
if (element.name.empty()) {
StreamString elem_name;
elem_name.Printf("__unnamed_%u", count);
element.name = elem_name.GetString();
}
ClangASTContext::AddFieldToRecordType(
union_type, element.name.c_str(),
CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
element.bitfield);
++count;
}
ClangASTContext::CompleteTagDeclarationDefinition(union_type);
}
return ClangUtil::GetQualType(union_type);
}
clang::QualType
AppleObjCTypeEncodingParser::BuildArray(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression) {
if (!type.NextIf('['))
return clang::QualType();
uint32_t size = ReadNumber(type);
clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
if (!type.NextIf(']'))
return clang::QualType();
ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
if (!lldb_ctx)
return clang::QualType();
CompilerType array_type(lldb_ctx->CreateArrayType(
CompilerType(&ast_ctx, element_type), size, false));
return ClangUtil::GetQualType(array_type);
}
// the runtime can emit these in the form of @"SomeType", giving more specifics
// this would be interesting for expression parser interop, but since we
// actually try
// to avoid exposing the ivar info to the expression evaluator, consume but
// ignore the type info
// and always return an 'id'; if anything, dynamic typing will resolve things
// for us anyway
clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType(
clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
bool for_expression) {
if (!type.NextIf('@'))
return clang::QualType();
std::string name;
if (type.NextIf('"')) {
// We have to be careful here. We're used to seeing
// @"NSString"
// but in records it is possible that the string following an @ is the name
// of the next field and @ means "id".
// This is the case if anything unquoted except for "}", the end of the
// type, or another name follows the quoted string.
//
// E.g.
// - @"NSString"@ means "id, followed by a field named NSString of type id"
// - @"NSString"} means "a pointer to NSString and the end of the struct"
// - @"NSString""nextField" means "a pointer to NSString and a field named
// nextField"
// - @"NSString" followed by the end of the string means "a pointer to
// NSString"
//
// As a result, the rule is: If we see @ followed by a quoted string, we
// peek.
// - If we see }, ), ], the end of the string, or a quote ("), the quoted
// string is a class name.
// - If we see anything else, the quoted string is a field name and we push
// it back onto type.
name = ReadQuotedString(type);
if (type.HasAtLeast(1)) {
switch (type.Peek()) {
default:
// roll back
type.PutBack(name.length() +
2); // undo our consumption of the string and of the quotes
name.clear();
break;
case '}':
case ')':
case ']':
case '"':
// the quoted string is a class name – see the rule
break;
}
} else {
// the quoted string is a class name – see the rule
}
}
if (for_expression && !name.empty()) {
size_t less_than_pos = name.find('<');
if (less_than_pos != std::string::npos) {
if (less_than_pos == 0)
return ast_ctx.getObjCIdType();
else
name.erase(less_than_pos);
}
DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
if (!decl_vendor)
return clang::QualType();
const bool append = false;
const uint32_t max_matches = 1;
std::vector<clang::NamedDecl *> decls;
uint32_t num_types =
decl_vendor->FindDecls(ConstString(name), append, max_matches, decls);
// The user can forward-declare something that has no definition. The runtime
// doesn't prohibit this at all.
// This is a rare and very weird case. We keep this assert in debug builds so
// we catch other weird cases.
#ifdef LLDB_CONFIGURATION_DEBUG
assert(num_types);
#else
if (!num_types)
return ast_ctx.getObjCIdType();
#endif
return ClangUtil::GetQualType(
ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType());
} else {
// We're going to resolve this dynamically anyway, so just smile and wave.
return ast_ctx.getObjCIdType();
}
}
clang::QualType
AppleObjCTypeEncodingParser::BuildType(clang::ASTContext &ast_ctx,
StringLexer &type, bool for_expression,
uint32_t *bitfield_bit_size) {
if (!type.HasAtLeast(1))
return clang::QualType();
switch (type.Peek()) {
default:
break;
case '{':
return BuildStruct(ast_ctx, type, for_expression);
case '[':
return BuildArray(ast_ctx, type, for_expression);
case '(':
return BuildUnion(ast_ctx, type, for_expression);
case '@':
return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
}
switch (type.Next()) {
default:
type.PutBack(1);
return clang::QualType();
case 'c':
return ast_ctx.CharTy;
case 'i':
return ast_ctx.IntTy;
case 's':
return ast_ctx.ShortTy;
case 'l':
return ast_ctx.getIntTypeForBitwidth(32, true);
// this used to be done like this:
// ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
// if (!lldb_ctx)
// return clang::QualType();
// return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
// which uses one of the constants if one is available, but we don't think all
// this work is necessary.
case 'q':
return ast_ctx.LongLongTy;
case 'C':
return ast_ctx.UnsignedCharTy;
case 'I':
return ast_ctx.UnsignedIntTy;
case 'S':
return ast_ctx.UnsignedShortTy;
case 'L':
return ast_ctx.getIntTypeForBitwidth(32, false);
// see note for 'l'
case 'Q':
return ast_ctx.UnsignedLongLongTy;
case 'f':
return ast_ctx.FloatTy;
case 'd':
return ast_ctx.DoubleTy;
case 'B':
return ast_ctx.BoolTy;
case 'v':
return ast_ctx.VoidTy;
case '*':
return ast_ctx.getPointerType(ast_ctx.CharTy);
case '#':
return ast_ctx.getObjCClassType();
case ':':
return ast_ctx.getObjCSelType();
case 'b': {
uint32_t size = ReadNumber(type);
if (bitfield_bit_size) {
*bitfield_bit_size = size;
return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
} else
return clang::QualType();
}
case 'r': {
clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
if (target_type.isNull())
return clang::QualType();
else if (target_type == ast_ctx.UnknownAnyTy)
return ast_ctx.UnknownAnyTy;
else
return ast_ctx.getConstType(target_type);
}
case '^': {
if (!for_expression && type.NextIf('?')) {
// if we are not supporting the concept of unknownAny, but what is being
// created here is an unknownAny*, then
// we can just get away with a void*
// this is theoretically wrong (in the same sense as 'theoretically
// nothing exists') but is way better than outright failure
// in many practical cases
return ast_ctx.VoidPtrTy;
} else {
clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
if (target_type.isNull())
return clang::QualType();
else if (target_type == ast_ctx.UnknownAnyTy)
return ast_ctx.UnknownAnyTy;
else
return ast_ctx.getPointerType(target_type);
}
}
case '?':
return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
}
}
CompilerType AppleObjCTypeEncodingParser::RealizeType(
clang::ASTContext &ast_ctx, const char *name, bool for_expression) {
if (name && name[0]) {
StringLexer lexer(name);
clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
return CompilerType(&ast_ctx, qual_type);
}
return CompilerType();
}

View File

@@ -0,0 +1,87 @@
//===-- AppleObjCTypeEncodingParser.h ---------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_AppleObjCTypeEncodingParser_h_
#define liblldb_AppleObjCTypeEncodingParser_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
#include "clang/AST/ASTContext.h"
// Project includes
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/lldb-private.h"
namespace lldb_utility {
class StringLexer;
}
namespace lldb_private {
class AppleObjCTypeEncodingParser : public ObjCLanguageRuntime::EncodingToType {
public:
AppleObjCTypeEncodingParser(ObjCLanguageRuntime &runtime);
~AppleObjCTypeEncodingParser() override = default;
CompilerType RealizeType(clang::ASTContext &ast_ctx, const char *name,
bool for_expression) override;
private:
struct StructElement {
std::string name;
clang::QualType type;
uint32_t bitfield;
StructElement();
~StructElement() = default;
};
clang::QualType BuildType(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression,
uint32_t *bitfield_bit_size = nullptr);
clang::QualType BuildStruct(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression);
clang::QualType BuildAggregate(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression, char opener, char closer,
uint32_t kind);
clang::QualType BuildUnion(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression);
clang::QualType BuildArray(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression);
std::string ReadStructName(lldb_utility::StringLexer &type);
StructElement ReadStructElement(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression);
clang::QualType BuildObjCObjectPointerType(clang::ASTContext &ast_ctx,
lldb_utility::StringLexer &type,
bool for_expression);
uint32_t ReadNumber(lldb_utility::StringLexer &type);
std::string ReadQuotedString(lldb_utility::StringLexer &type);
ObjCLanguageRuntime &m_runtime;
};
} // namespace lldb_private
#endif // liblldb_AppleObjCTypeEncodingParser_h_

View File

@@ -0,0 +1,213 @@
//===-- AppleThreadPlanStepThroughObjCTrampoline.cpp
//--------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "AppleThreadPlanStepThroughObjCTrampoline.h"
#include "AppleObjCTrampolineHandler.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/FunctionCaller.h"
#include "lldb/Expression/UtilityFunction.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlanRunToAddress.h"
#include "lldb/Target/ThreadPlanStepOut.h"
#include "lldb/Utility/Log.h"
using namespace lldb;
using namespace lldb_private;
//----------------------------------------------------------------------
// ThreadPlanStepThroughObjCTrampoline constructor
//----------------------------------------------------------------------
AppleThreadPlanStepThroughObjCTrampoline::
AppleThreadPlanStepThroughObjCTrampoline(
Thread &thread, AppleObjCTrampolineHandler *trampoline_handler,
ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
bool stop_others)
: ThreadPlan(ThreadPlan::eKindGeneric,
"MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion,
eVoteNoOpinion),
m_trampoline_handler(trampoline_handler),
m_args_addr(LLDB_INVALID_ADDRESS), m_input_values(input_values),
m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(NULL),
m_stop_others(stop_others) {}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
AppleThreadPlanStepThroughObjCTrampoline::
~AppleThreadPlanStepThroughObjCTrampoline() {}
void AppleThreadPlanStepThroughObjCTrampoline::DidPush() {
// Setting up the memory space for the called function text might require
// allocations,
// i.e. a nested function call. This needs to be done as a PreResumeAction.
m_thread.GetProcess()->AddPreResumeAction(PreResumeInitializeFunctionCaller,
(void *)this);
}
bool AppleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller() {
if (!m_func_sp) {
DiagnosticManager diagnostics;
m_args_addr =
m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values);
if (m_args_addr == LLDB_INVALID_ADDRESS) {
return false;
}
m_impl_function =
m_trampoline_handler->GetLookupImplementationFunctionCaller();
ExecutionContext exc_ctx;
EvaluateExpressionOptions options;
options.SetUnwindOnError(true);
options.SetIgnoreBreakpoints(true);
options.SetStopOthers(m_stop_others);
m_thread.CalculateExecutionContext(exc_ctx);
m_func_sp = m_impl_function->GetThreadPlanToCallFunction(
exc_ctx, m_args_addr, options, diagnostics);
m_func_sp->SetOkayToDiscard(true);
m_thread.QueueThreadPlan(m_func_sp, false);
}
return true;
}
bool AppleThreadPlanStepThroughObjCTrampoline::
PreResumeInitializeFunctionCaller(void *void_myself) {
AppleThreadPlanStepThroughObjCTrampoline *myself =
static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself);
return myself->InitializeFunctionCaller();
}
void AppleThreadPlanStepThroughObjCTrampoline::GetDescription(
Stream *s, lldb::DescriptionLevel level) {
if (level == lldb::eDescriptionLevelBrief)
s->Printf("Step through ObjC trampoline");
else {
s->Printf("Stepping to implementation of ObjC method - obj: 0x%llx, isa: "
"0x%" PRIx64 ", sel: 0x%" PRIx64,
m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
m_isa_addr, m_sel_addr);
}
}
bool AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan(Stream *error) {
return true;
}
bool AppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop(
Event *event_ptr) {
// If we get asked to explain the stop it will be because something went
// wrong (like the implementation for selector function crashed... We're
// going
// to figure out what to do about that, so we do explain the stop.
return true;
}
lldb::StateType AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState() {
return eStateRunning;
}
bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) {
// First stage: we are still handling the "call a function to get the target
// of the dispatch"
if (m_func_sp) {
if (!m_func_sp->IsPlanComplete()) {
return false;
} else {
if (!m_func_sp->PlanSucceeded()) {
SetPlanComplete(false);
return true;
}
m_func_sp.reset();
}
}
// Second stage, if all went well with the function calling, then fetch the
// target address, and
// queue up a "run to that address" plan.
if (!m_run_to_sp) {
Value target_addr_value;
ExecutionContext exc_ctx;
m_thread.CalculateExecutionContext(exc_ctx);
m_impl_function->FetchFunctionResults(exc_ctx, m_args_addr,
target_addr_value);
m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr);
lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong();
Address target_so_addr;
target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr());
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
if (target_addr == 0) {
if (log)
log->Printf("Got target implementation of 0x0, stopping.");
SetPlanComplete();
return true;
}
if (m_trampoline_handler->AddrIsMsgForward(target_addr)) {
if (log)
log->Printf(
"Implementation lookup returned msgForward function: 0x%" PRIx64
", stopping.",
target_addr);
SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(
eSymbolContextEverything);
const bool abort_other_plans = false;
const bool first_insn = true;
const uint32_t frame_idx = 0;
m_run_to_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop(
abort_other_plans, &sc, first_insn, m_stop_others, eVoteNoOpinion,
eVoteNoOpinion, frame_idx);
m_run_to_sp->SetPrivate(true);
return false;
}
if (log)
log->Printf("Running to ObjC method implementation: 0x%" PRIx64,
target_addr);
ObjCLanguageRuntime *objc_runtime =
GetThread().GetProcess()->GetObjCLanguageRuntime();
assert(objc_runtime != NULL);
objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr);
if (log)
log->Printf("Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64
"} = addr=0x%" PRIx64 " to cache.",
m_isa_addr, m_sel_addr, target_addr);
// Extract the target address from the value:
m_run_to_sp.reset(
new ThreadPlanRunToAddress(m_thread, target_so_addr, m_stop_others));
m_thread.QueueThreadPlan(m_run_to_sp, false);
m_run_to_sp->SetPrivate(true);
return false;
} else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) {
// Third stage, work the run to target plan.
SetPlanComplete();
return true;
}
return false;
}
// The base class MischiefManaged does some cleanup - so you have to call it
// in your MischiefManaged derived class.
bool AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged() {
if (IsPlanComplete())
return true;
else
return false;
}
bool AppleThreadPlanStepThroughObjCTrampoline::WillStop() { return true; }

View File

@@ -0,0 +1,81 @@
//===-- AppleThreadPlanStepThroughObjCTrampoline.h --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
#define lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "AppleObjCTrampolineHandler.h"
#include "lldb/Core/Value.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-types.h"
namespace lldb_private {
class AppleThreadPlanStepThroughObjCTrampoline : public ThreadPlan {
public:
AppleThreadPlanStepThroughObjCTrampoline(
Thread &thread, AppleObjCTrampolineHandler *trampoline_handler,
ValueList &values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
bool stop_others);
~AppleThreadPlanStepThroughObjCTrampoline() override;
static bool PreResumeInitializeFunctionCaller(void *myself);
void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
bool ValidatePlan(Stream *error) override;
lldb::StateType GetPlanRunState() override;
bool ShouldStop(Event *event_ptr) override;
bool StopOthers() override { return m_stop_others; }
// The base class MischiefManaged does some cleanup - so you have to call it
// in your MischiefManaged derived class.
bool MischiefManaged() override;
void DidPush() override;
bool WillStop() override;
protected:
bool DoPlanExplainsStop(Event *event_ptr) override;
private:
bool InitializeFunctionCaller();
AppleObjCTrampolineHandler *m_trampoline_handler; // FIXME - ensure this
// doesn't go away on us?
// SP maybe?
lldb::addr_t m_args_addr; // Stores the address for our step through function
// result structure.
// lldb::addr_t m_object_addr; // This is only for Description.
ValueList m_input_values;
lldb::addr_t m_isa_addr; // isa_addr and sel_addr are the keys we will use to
// cache the implementation.
lldb::addr_t m_sel_addr;
lldb::ThreadPlanSP m_func_sp; // This is the function call plan. We fill it
// at start, then set it
// to NULL when this plan is done. That way we know to go to:
lldb::ThreadPlanSP m_run_to_sp; // The plan that runs to the target.
FunctionCaller *m_impl_function; // This is a pointer to a impl function that
// is owned by the client that pushes this plan.
bool m_stop_others;
};
} // namespace lldb_private
#endif // lldb_AppleThreadPlanStepThroughObjCTrampoline_h_

View File

@@ -0,0 +1,24 @@
add_lldb_library(lldbPluginAppleObjCRuntime PLUGIN
AppleObjCRuntime.cpp
AppleObjCRuntimeV1.cpp
AppleObjCRuntimeV2.cpp
AppleObjCTrampolineHandler.cpp
AppleObjCDeclVendor.cpp
AppleThreadPlanStepThroughObjCTrampoline.cpp
AppleObjCClassDescriptorV2.cpp
AppleObjCTypeEncodingParser.cpp
LINK_LIBS
clangAST
lldbBreakpoint
lldbCore
lldbExpression
lldbHost
lldbInterpreter
lldbSymbol
lldbTarget
lldbUtility
lldbPluginExpressionParserClang
LINK_COMPONENTS
Support
)

View File

@@ -0,0 +1 @@
add_subdirectory(AppleObjCRuntime)