diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Expression')
19 files changed, 18025 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Expression/ASTDumper.cpp b/contrib/llvm/tools/lldb/source/Expression/ASTDumper.cpp new file mode 100644 index 0000000..5210d14 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ASTDumper.cpp @@ -0,0 +1,132 @@ +//===-- ASTDumper.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Symbol/ClangASTType.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace lldb_private; + +ASTDumper::ASTDumper (clang::Decl *decl) +{ + clang::DeclContext *decl_ctx = llvm::dyn_cast<clang::DeclContext>(decl); + + bool has_external_lexical_storage; + bool has_external_visible_storage; + + if (decl_ctx) + { + has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage(); + has_external_visible_storage = decl_ctx->hasExternalVisibleStorage(); + decl_ctx->setHasExternalLexicalStorage(false); + decl_ctx->setHasExternalVisibleStorage(false); + } + + llvm::raw_string_ostream os(m_dump); + decl->print (os); + os.flush(); + + if (decl_ctx) + { + decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage); + decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage); + } +} + +ASTDumper::ASTDumper (clang::DeclContext *decl_ctx) +{ + bool has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage(); + bool has_external_visible_storage = decl_ctx->hasExternalVisibleStorage(); + + decl_ctx->setHasExternalLexicalStorage(false); + decl_ctx->setHasExternalVisibleStorage(false); + + if (clang::Decl *decl = llvm::dyn_cast<clang::Decl>(decl_ctx)) + { + llvm::raw_string_ostream os(m_dump); + decl->print (os); + os.flush(); + } + else + { + m_dump.assign("<DeclContext is not a Decl>"); + } + + decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage); + decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage); +} + +ASTDumper::ASTDumper (const clang::Type *type) +{ + m_dump = clang::QualType(type, 0).getAsString(); +} + +ASTDumper::ASTDumper (clang::QualType type) +{ + m_dump = type.getAsString(); +} + +ASTDumper::ASTDumper (lldb::clang_type_t type) +{ + m_dump = clang::QualType::getFromOpaquePtr(type).getAsString(); +} + +ASTDumper::ASTDumper (const ClangASTType &clang_type) +{ + m_dump = clang_type.GetQualType().getAsString(); +} + + +const char * +ASTDumper::GetCString() +{ + return m_dump.c_str(); +} + +void ASTDumper::ToSTDERR() +{ + fprintf(stderr, "%s\n", m_dump.c_str()); +} + +void ASTDumper::ToLog(Log *log, const char *prefix) +{ + size_t len = m_dump.length() + 1; + + char *alloc = (char*)malloc(len); + char *str = alloc; + + memcpy(str, m_dump.c_str(), len); + + char *end = NULL; + + end = strchr(str, '\n'); + + while (end) + { + *end = '\0'; + + log->Printf("%s%s", prefix, str); + + *end = '\n'; + + str = end + 1; + end = strchr(str, '\n'); + } + + log->Printf("%s%s", prefix, str); + + free(alloc); +} + +void ASTDumper::ToStream(lldb::StreamSP &stream) +{ + stream->PutCString(m_dump.c_str()); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ASTResultSynthesizer.cpp b/contrib/llvm/tools/lldb/source/Expression/ASTResultSynthesizer.cpp new file mode 100644 index 0000000..76c2577 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ASTResultSynthesizer.cpp @@ -0,0 +1,512 @@ +//===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "stdlib.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/ASTResultSynthesizer.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Target/Target.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough, + Target &target) : + m_ast_context (NULL), + m_passthrough (passthrough), + m_passthrough_sema (NULL), + m_target (target), + m_sema (NULL) +{ + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough); +} + +ASTResultSynthesizer::~ASTResultSynthesizer() +{ +} + +void +ASTResultSynthesizer::Initialize(ASTContext &Context) +{ + m_ast_context = &Context; + + if (m_passthrough) + m_passthrough->Initialize(Context); +} + +void +ASTResultSynthesizer::TransformTopLevelDecl(Decl* D) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (NamedDecl *named_decl = dyn_cast<NamedDecl>(D)) + { + if (log && log->GetVerbose()) + { + if (named_decl->getIdentifier()) + log->Printf("TransformTopLevelDecl(%s)", named_decl->getIdentifier()->getNameStart()); + else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) + log->Printf("TransformTopLevelDecl(%s)", method_decl->getSelector().getAsString().c_str()); + else + log->Printf("TransformTopLevelDecl(<complex>)"); + } + + } + + if (LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D)) + { + RecordDecl::decl_iterator decl_iterator; + + for (decl_iterator = linkage_spec_decl->decls_begin(); + decl_iterator != linkage_spec_decl->decls_end(); + ++decl_iterator) + { + TransformTopLevelDecl(*decl_iterator); + } + } + else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) + { + if (m_ast_context && + !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) + { + RecordPersistentTypes(method_decl); + SynthesizeObjCMethodResult(method_decl); + } + } + else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) + { + if (m_ast_context && + !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) + { + RecordPersistentTypes(function_decl); + SynthesizeFunctionResult(function_decl); + } + } +} + +bool +ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) +{ + DeclGroupRef::iterator decl_iterator; + + for (decl_iterator = D.begin(); + decl_iterator != D.end(); + ++decl_iterator) + { + Decl *decl = *decl_iterator; + + TransformTopLevelDecl(decl); + } + + if (m_passthrough) + return m_passthrough->HandleTopLevelDecl(D); + return true; +} + +bool +ASTResultSynthesizer::SynthesizeFunctionResult (FunctionDecl *FunDecl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_sema) + return false; + + FunctionDecl *function_decl = FunDecl; + + if (!function_decl) + return false; + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + function_decl->print(os); + + os.flush(); + + log->Printf ("Untransformed function AST:\n%s", s.c_str()); + } + + Stmt *function_body = function_decl->getBody(); + CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(function_body); + + bool ret = SynthesizeBodyResult (compound_stmt, + function_decl); + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + function_decl->print(os); + + os.flush(); + + log->Printf ("Transformed function AST:\n%s", s.c_str()); + } + + return ret; +} + +bool +ASTResultSynthesizer::SynthesizeObjCMethodResult (ObjCMethodDecl *MethodDecl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_sema) + return false; + + if (!MethodDecl) + return false; + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + MethodDecl->print(os); + + os.flush(); + + log->Printf ("Untransformed method AST:\n%s", s.c_str()); + } + + Stmt *method_body = MethodDecl->getBody(); + + if (!method_body) + return false; + + CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(method_body); + + bool ret = SynthesizeBodyResult (compound_stmt, + MethodDecl); + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream os(s); + + MethodDecl->print(os); + + os.flush(); + + log->Printf("Transformed method AST:\n%s", s.c_str()); + } + + return ret; +} + +bool +ASTResultSynthesizer::SynthesizeBodyResult (CompoundStmt *Body, + DeclContext *DC) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ASTContext &Ctx(*m_ast_context); + + if (!Body) + return false; + + if (Body->body_empty()) + return false; + + Stmt **last_stmt_ptr = Body->body_end() - 1; + Stmt *last_stmt = *last_stmt_ptr; + + while (dyn_cast<NullStmt>(last_stmt)) + { + if (last_stmt_ptr != Body->body_begin()) + { + last_stmt_ptr--; + last_stmt = *last_stmt_ptr; + } + else + { + return false; + } + } + + Expr *last_expr = dyn_cast<Expr>(last_stmt); + + if (!last_expr) + // No auxiliary variable necessary; expression returns void + return true; + + // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off if that's the + // case. + + do { + ImplicitCastExpr *implicit_cast = dyn_cast<ImplicitCastExpr>(last_expr); + + if (!implicit_cast) + break; + + if (implicit_cast->getCastKind() != CK_LValueToRValue) + break; + + last_expr = implicit_cast->getSubExpr(); + } while (0); + + // is_lvalue is used to record whether the expression returns an assignable Lvalue or an + // Rvalue. This is relevant because they are handled differently. + // + // For Lvalues + // + // - In AST result synthesis (here!) the expression E is transformed into an initialization + // T *$__lldb_expr_result_ptr = &E. + // + // - In structure allocation, a pointer-sized slot is allocated in the struct that is to be + // passed into the expression. + // + // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are redirected at + // an entry in the struct ($__lldb_arg) passed into the expression. (Other persistent + // variables are treated similarly, having been materialized as references, but in those + // cases the value of the reference itself is never modified.) + // + // - During materialization, $0 (the result persistent variable) is ignored. + // + // - During dematerialization, $0 is marked up as a load address with value equal to the + // contents of the structure entry. + // + // For Rvalues + // + // - In AST result synthesis the expression E is transformed into an initialization + // static T $__lldb_expr_result = E. + // + // - In structure allocation, a pointer-sized slot is allocated in the struct that is to be + // passed into the expression. + // + // - In IR transformations, an instruction is inserted at the beginning of the function to + // dereference the pointer resident in the slot. Reads and writes to $__lldb_expr_result + // are redirected at that dereferenced version. Guard variables for the static variable + // are excised. + // + // - During materialization, $0 (the result persistent variable) is populated with the location + // of a newly-allocated area of memory. + // + // - During dematerialization, $0 is ignored. + + bool is_lvalue = + (last_expr->getValueKind() == VK_LValue || last_expr->getValueKind() == VK_XValue) && + (last_expr->getObjectKind() == OK_Ordinary); + + QualType expr_qual_type = last_expr->getType(); + const clang::Type *expr_type = expr_qual_type.getTypePtr(); + + if (!expr_type) + return false; + + if (expr_type->isVoidType()) + return true; + + if (log) + { + std::string s = expr_qual_type.getAsString(); + + log->Printf("Last statement is an %s with type: %s", (is_lvalue ? "lvalue" : "rvalue"), s.c_str()); + } + + clang::VarDecl *result_decl = NULL; + + if (is_lvalue) + { + IdentifierInfo *result_ptr_id; + + if (expr_type->isFunctionType()) + result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should be treated like function pointers + else + result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr"); + + m_sema->RequireCompleteType(SourceLocation(), expr_qual_type, clang::diag::err_incomplete_type); + + QualType ptr_qual_type; + + if (expr_qual_type->getAs<ObjCObjectType>() != NULL) + ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type); + else + ptr_qual_type = Ctx.getPointerType(expr_qual_type); + + result_decl = VarDecl::Create(Ctx, + DC, + SourceLocation(), + SourceLocation(), + result_ptr_id, + ptr_qual_type, + NULL, + SC_Static); + + if (!result_decl) + return false; + + ExprResult address_of_expr = m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr); + + m_sema->AddInitializerToDecl(result_decl, address_of_expr.take(), true, false); + } + else + { + IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result"); + + result_decl = VarDecl::Create(Ctx, + DC, + SourceLocation(), + SourceLocation(), + &result_id, + expr_qual_type, + NULL, + SC_Static); + + if (!result_decl) + return false; + + m_sema->AddInitializerToDecl(result_decl, last_expr, true, false); + } + + DC->addDecl(result_decl); + + /////////////////////////////// + // call AddInitializerToDecl + // + + //m_sema->AddInitializerToDecl(result_decl, last_expr); + + ///////////////////////////////// + // call ConvertDeclToDeclGroup + // + + Sema::DeclGroupPtrTy result_decl_group_ptr; + + result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl); + + //////////////////////// + // call ActOnDeclStmt + // + + StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt(result_decl_group_ptr, + SourceLocation(), + SourceLocation())); + + //////////////////////////////////////////////// + // replace the old statement with the new one + // + + *last_stmt_ptr = reinterpret_cast<Stmt*>(result_initialization_stmt_result.take()); + + return true; +} + +void +ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) +{ + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void +ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) +{ + typedef DeclContext::specific_decl_iterator<TypeDecl> TypeDeclIterator; + + for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()), + e = TypeDeclIterator(FunDeclCtx->decls_end()); + i != e; + ++i) + { + MaybeRecordPersistentType(*i); + } +} + +void +ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) +{ + if (!D->getIdentifier()) + return; + + StringRef name = D->getName(); + + if (name.size() == 0 || name[0] != '$') + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ConstString name_cs(name.str().c_str()); + + if (log) + log->Printf ("Recording persistent type %s\n", name_cs.GetCString()); + + Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl(m_target.GetScratchClangASTContext()->getASTContext(), + m_ast_context, + D); + + if (TypeDecl *TypeDecl_scratch = dyn_cast<TypeDecl>(D_scratch)) + m_target.GetPersistentVariables().RegisterPersistentType(name_cs, TypeDecl_scratch); +} + +void +ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) +{ + if (m_passthrough) + m_passthrough->HandleTagDeclDefinition(D); +} + +void +ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) +{ + if (m_passthrough) + m_passthrough->CompleteTentativeDefinition(D); +} + +void +ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD, bool DefinitionRequired) +{ + if (m_passthrough) + m_passthrough->HandleVTable(RD, DefinitionRequired); +} + +void +ASTResultSynthesizer::PrintStats() +{ + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void +ASTResultSynthesizer::InitializeSema(Sema &S) +{ + m_sema = &S; + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void +ASTResultSynthesizer::ForgetSema() +{ + m_sema = NULL; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ASTStructExtractor.cpp b/contrib/llvm/tools/lldb/source/Expression/ASTStructExtractor.cpp new file mode 100644 index 0000000..d1f2192 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ASTStructExtractor.cpp @@ -0,0 +1,220 @@ +//===-- ASTStructExtractor.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "stdlib.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Stmt.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ASTStructExtractor.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTStructExtractor::ASTStructExtractor(ASTConsumer *passthrough, + const char *struct_name, + ClangFunction &function) : + m_ast_context (NULL), + m_passthrough (passthrough), + m_passthrough_sema (NULL), + m_sema (NULL), + m_action (NULL), + m_function (function), + m_struct_name (struct_name) +{ + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough); +} + +ASTStructExtractor::~ASTStructExtractor() +{ +} + +void +ASTStructExtractor::Initialize(ASTContext &Context) +{ + m_ast_context = &Context; + + if (m_passthrough) + m_passthrough->Initialize(Context); +} + +void +ASTStructExtractor::ExtractFromFunctionDecl(FunctionDecl *F) +{ + if (!F->hasBody()) + return; + + Stmt *body_stmt = F->getBody(); + CompoundStmt *body_compound_stmt = dyn_cast<CompoundStmt>(body_stmt); + + if (!body_compound_stmt) + return; // do we have to handle this? + + RecordDecl *struct_decl = NULL; + + StringRef desired_name(m_struct_name.c_str()); + + for (CompoundStmt::const_body_iterator bi = body_compound_stmt->body_begin(), be = body_compound_stmt->body_end(); + bi != be; + ++bi) + { + Stmt *curr_stmt = *bi; + DeclStmt *curr_decl_stmt = dyn_cast<DeclStmt>(curr_stmt); + if (!curr_decl_stmt) + continue; + DeclGroupRef decl_group = curr_decl_stmt->getDeclGroup(); + for (Decl *candidate_decl : decl_group) + { + RecordDecl *candidate_record_decl = dyn_cast<RecordDecl>(candidate_decl); + if (!candidate_record_decl) + continue; + if (candidate_record_decl->getName() == desired_name) + { + struct_decl = candidate_record_decl; + break; + } + } + if (struct_decl) + break; + } + + if (!struct_decl) + return; + + const ASTRecordLayout* struct_layout(&m_ast_context->getASTRecordLayout (struct_decl)); + + if (!struct_layout) + return; + + m_function.m_struct_size = struct_layout->getSize().getQuantity(); // TODO Store m_struct_size as CharUnits + m_function.m_return_offset = struct_layout->getFieldOffset(struct_layout->getFieldCount() - 1) / 8; + m_function.m_return_size = struct_layout->getDataSize().getQuantity() - m_function.m_return_offset; + + for (unsigned field_index = 0, num_fields = struct_layout->getFieldCount(); + field_index < num_fields; + ++field_index) + { + m_function.m_member_offsets.push_back(struct_layout->getFieldOffset(field_index) / 8); + } + + m_function.m_struct_valid = true; +} + +void +ASTStructExtractor::ExtractFromTopLevelDecl(Decl* D) +{ + LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D); + + if (linkage_spec_decl) + { + RecordDecl::decl_iterator decl_iterator; + + for (decl_iterator = linkage_spec_decl->decls_begin(); + decl_iterator != linkage_spec_decl->decls_end(); + ++decl_iterator) + { + ExtractFromTopLevelDecl(*decl_iterator); + } + } + + FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D); + + if (m_ast_context && + function_decl && + !m_function.m_wrapper_function_name.compare(function_decl->getNameAsString().c_str())) + { + ExtractFromFunctionDecl(function_decl); + } +} + +bool +ASTStructExtractor::HandleTopLevelDecl(DeclGroupRef D) +{ + DeclGroupRef::iterator decl_iterator; + + for (decl_iterator = D.begin(); + decl_iterator != D.end(); + ++decl_iterator) + { + Decl *decl = *decl_iterator; + + ExtractFromTopLevelDecl(decl); + } + + if (m_passthrough) + return m_passthrough->HandleTopLevelDecl(D); + return true; +} + +void +ASTStructExtractor::HandleTranslationUnit(ASTContext &Ctx) +{ + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void +ASTStructExtractor::HandleTagDeclDefinition(TagDecl *D) +{ + if (m_passthrough) + m_passthrough->HandleTagDeclDefinition(D); +} + +void +ASTStructExtractor::CompleteTentativeDefinition(VarDecl *D) +{ + if (m_passthrough) + m_passthrough->CompleteTentativeDefinition(D); +} + +void +ASTStructExtractor::HandleVTable(CXXRecordDecl *RD, bool DefinitionRequired) +{ + if (m_passthrough) + m_passthrough->HandleVTable(RD, DefinitionRequired); +} + +void +ASTStructExtractor::PrintStats() +{ + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void +ASTStructExtractor::InitializeSema(Sema &S) +{ + m_sema = &S; + m_action = reinterpret_cast<Action*>(m_sema); + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void +ASTStructExtractor::ForgetSema() +{ + m_sema = NULL; + m_action = NULL; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangASTSource.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangASTSource.cpp new file mode 100644 index 0000000..49513d7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangASTSource.cpp @@ -0,0 +1,1866 @@ +//===-- ClangASTSource.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace clang; +using namespace lldb_private; + +ClangASTSource::~ClangASTSource() +{ + m_ast_importer->ForgetDestination(m_ast_context); + + // We are in the process of destruction, don't create clang ast context on demand + // by passing false to Target::GetScratchClangASTContext(create_on_demand). + ClangASTContext *scratch_clang_ast_context = m_target->GetScratchClangASTContext(false); + + if (!scratch_clang_ast_context) + return; + + clang::ASTContext *scratch_ast_context = scratch_clang_ast_context->getASTContext(); + + if (!scratch_ast_context) + return; + + if (m_ast_context != scratch_ast_context) + m_ast_importer->ForgetSource(scratch_ast_context, m_ast_context); +} + +void +ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) +{ + if (!m_ast_context) + return; + + m_ast_context->getTranslationUnitDecl()->setHasExternalVisibleStorage(); + m_ast_context->getTranslationUnitDecl()->setHasExternalLexicalStorage(); +} + +// The core lookup interface. +bool +ClangASTSource::FindExternalVisibleDeclsByName +( + const DeclContext *decl_ctx, + DeclarationName clang_decl_name +) +{ + if (!m_ast_context) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + if (GetImportInProgress()) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + std::string decl_name (clang_decl_name.getAsString()); + +// if (m_decl_map.DoingASTImport ()) +// return DeclContext::lookup_result(); +// + switch (clang_decl_name.getNameKind()) { + // Normal identifiers. + case DeclarationName::Identifier: + { + clang::IdentifierInfo *identifier_info = clang_decl_name.getAsIdentifierInfo(); + + if (!identifier_info || + identifier_info->getBuiltinID() != 0) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + } + break; + + // Operator names. Not important for now. + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXLiteralOperatorName: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + // Using directives found in this context. + // Tell Sema we didn't find any or we'll end up getting asked a *lot*. + case DeclarationName::CXXUsingDirective: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + { + llvm::SmallVector<NamedDecl*, 1> method_decls; + + NameSearchContext method_search_context (*this, method_decls, clang_decl_name, decl_ctx); + + FindObjCMethodDecls(method_search_context); + + SetExternalVisibleDeclsForName (decl_ctx, clang_decl_name, method_decls); + return (method_decls.size() > 0); + } + // These aren't possible in the global context. + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + + if (!GetLookupsEnabled()) + { + // Wait until we see a '$' at the start of a name before we start doing + // any lookups so we can avoid lookup up all of the builtin types. + if (!decl_name.empty() && decl_name[0] == '$') + { + SetLookupsEnabled (true); + } + else + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + } + + ConstString const_decl_name(decl_name.c_str()); + + const char *uniqued_const_decl_name = const_decl_name.GetCString(); + if (m_active_lookups.find (uniqued_const_decl_name) != m_active_lookups.end()) + { + // We are currently looking up this name... + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + m_active_lookups.insert(uniqued_const_decl_name); +// static uint32_t g_depth = 0; +// ++g_depth; +// printf("[%5u] FindExternalVisibleDeclsByName() \"%s\"\n", g_depth, uniqued_const_decl_name); + llvm::SmallVector<NamedDecl*, 4> name_decls; + NameSearchContext name_search_context(*this, name_decls, clang_decl_name, decl_ctx); + FindExternalVisibleDecls(name_search_context); + SetExternalVisibleDeclsForName (decl_ctx, clang_decl_name, name_decls); +// --g_depth; + m_active_lookups.erase (uniqued_const_decl_name); + return (name_decls.size() != 0); +} + +void +ClangASTSource::CompleteType (TagDecl *tag_decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + log->Printf(" CompleteTagDecl[%u] on (ASTContext*)%p Completing (TagDecl*)%p named %s", + current_id, + m_ast_context, + tag_decl, + tag_decl->getName().str().c_str()); + + log->Printf(" CTD[%u] Before:", current_id); + ASTDumper dumper((Decl*)tag_decl); + dumper.ToLog(log, " [CTD] "); + } + + if (!m_ast_importer->CompleteTagDecl (tag_decl)) + { + // We couldn't complete the type. Maybe there's a definition + // somewhere else that can be completed. + + if (log) + log->Printf(" CTD[%u] Type could not be completed in the module in which it was first found.", current_id); + + bool found = false; + + DeclContext *decl_ctx = tag_decl->getDeclContext(); + + if (const NamespaceDecl *namespace_context = dyn_cast<NamespaceDecl>(decl_ctx)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CTD[%u] Inspecting namespace map %p (%d entries)", + current_id, + namespace_map.get(), + (int)namespace_map->size()); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); + i != e && !found; + ++i) + { + if (log) + log->Printf(" CTD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + TypeList types; + + SymbolContext null_sc; + ConstString name(tag_decl->getName().str().c_str()); + + i->first->FindTypesInNamespace(null_sc, name, &i->second, UINT32_MAX, types); + + for (uint32_t ti = 0, te = types.GetSize(); + ti != te && !found; + ++ti) + { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + ClangASTType clang_type (type->GetClangFullType()); + + if (!clang_type) + continue; + + const TagType *tag_type = clang_type.GetQualType()->getAs<TagType>(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = const_cast<TagDecl*>(tag_type->getDecl()); + + if (m_ast_importer->CompleteTagDeclWithOrigin (tag_decl, candidate_tag_decl)) + found = true; + } + } + } + else + { + TypeList types; + + SymbolContext null_sc; + ConstString name(tag_decl->getName().str().c_str()); + ClangNamespaceDecl namespace_decl; + + const ModuleList &module_list = m_target->GetImages(); + + bool exact_match = false; + module_list.FindTypes (null_sc, name, exact_match, UINT32_MAX, types); + + for (uint32_t ti = 0, te = types.GetSize(); + ti != te && !found; + ++ti) + { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + ClangASTType clang_type (type->GetClangFullType()); + + if (!clang_type) + continue; + + const TagType *tag_type = clang_type.GetQualType()->getAs<TagType>(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = const_cast<TagDecl*>(tag_type->getDecl()); + + if (m_ast_importer->CompleteTagDeclWithOrigin (tag_decl, candidate_tag_decl)) + found = true; + } + } + } + + if (log) + { + log->Printf(" [CTD] After:"); + ASTDumper dumper((Decl*)tag_decl); + dumper.ToLog(log, " [CTD] "); + } +} + +void +ClangASTSource::CompleteType (clang::ObjCInterfaceDecl *interface_decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + log->Printf(" [CompleteObjCInterfaceDecl] on (ASTContext*)%p Completing an ObjCInterfaceDecl named %s", m_ast_context, interface_decl->getName().str().c_str()); + log->Printf(" [COID] Before:"); + ASTDumper dumper((Decl*)interface_decl); + dumper.ToLog(log, " [COID] "); + } + + m_ast_importer->CompleteObjCInterfaceDecl (interface_decl); + + if (log) + { + log->Printf(" [COID] After:"); + ASTDumper dumper((Decl*)interface_decl); + dumper.ToLog(log, " [COID] "); + } +} + +clang::ObjCInterfaceDecl * +ClangASTSource::GetCompleteObjCInterface (clang::ObjCInterfaceDecl *interface_decl) +{ + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return NULL; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + return NULL; + + ConstString class_name(interface_decl->getNameAsString().c_str()); + + lldb::TypeSP complete_type_sp(language_runtime->LookupInCompleteClassCache(class_name)); + + if (!complete_type_sp) + return NULL; + + TypeFromUser complete_type = TypeFromUser(complete_type_sp->GetClangFullType()); + lldb::clang_type_t complete_opaque_type = complete_type.GetOpaqueQualType(); + + if (!complete_opaque_type) + return NULL; + + const clang::Type *complete_clang_type = QualType::getFromOpaquePtr(complete_opaque_type).getTypePtr(); + const ObjCInterfaceType *complete_interface_type = dyn_cast<ObjCInterfaceType>(complete_clang_type); + + if (!complete_interface_type) + return NULL; + + ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl()); + + return complete_iface_decl; +} + +clang::ExternalLoadResult +ClangASTSource::FindExternalLexicalDecls (const DeclContext *decl_context, + bool (*predicate)(Decl::Kind), + llvm::SmallVectorImpl<Decl*> &decls) +{ + ClangASTMetrics::RegisterLexicalQuery(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const Decl *context_decl = dyn_cast<Decl>(decl_context); + + if (!context_decl) + return ELR_Failure; + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context_decl)) + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in '%s' (%sDecl*)%p with %s predicate", + current_id, + m_ast_context, + context_named_decl->getNameAsString().c_str(), + context_decl->getDeclKindName(), + context_decl, + (predicate ? "non-null" : "null")); + else if(context_decl) + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in (%sDecl*)%p with %s predicate", + current_id, + m_ast_context, + context_decl->getDeclKindName(), + context_decl, + (predicate ? "non-null" : "null")); + else + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in a NULL context with %s predicate", + current_id, + m_ast_context, + (predicate ? "non-null" : "null")); + } + + Decl *original_decl = NULL; + ASTContext *original_ctx = NULL; + + if (!m_ast_importer->ResolveDeclOrigin(context_decl, &original_decl, &original_ctx)) + return ELR_Failure; + + if (log) + { + log->Printf(" FELD[%u] Original decl (ASTContext*)%p (Decl*)%p:", current_id, original_ctx, original_decl); + ASTDumper(original_decl).ToLog(log, " "); + } + + if (ObjCInterfaceDecl *original_iface_decl = dyn_cast<ObjCInterfaceDecl>(original_decl)) + { + ObjCInterfaceDecl *complete_iface_decl = GetCompleteObjCInterface(original_iface_decl); + + if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) + { + original_decl = complete_iface_decl; + original_ctx = &complete_iface_decl->getASTContext(); + + m_ast_importer->SetDeclOrigin(context_decl, original_iface_decl); + } + } + + if (TagDecl *original_tag_decl = dyn_cast<TagDecl>(original_decl)) + { + ExternalASTSource *external_source = original_ctx->getExternalSource(); + + if (external_source) + external_source->CompleteType (original_tag_decl); + } + + const DeclContext *original_decl_context = dyn_cast<DeclContext>(original_decl); + + if (!original_decl_context) + return ELR_Failure; + + for (TagDecl::decl_iterator iter = original_decl_context->decls_begin(); + iter != original_decl_context->decls_end(); + ++iter) + { + Decl *decl = *iter; + + if (!predicate || predicate(decl->getKind())) + { + if (log) + { + ASTDumper ast_dumper(decl); + if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context_decl)) + log->Printf(" FELD[%d] Adding [to %sDecl %s] lexical %sDecl %s", current_id, context_named_decl->getDeclKindName(), context_named_decl->getNameAsString().c_str(), decl->getDeclKindName(), ast_dumper.GetCString()); + else + log->Printf(" FELD[%d] Adding lexical %sDecl %s", current_id, decl->getDeclKindName(), ast_dumper.GetCString()); + } + + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, original_ctx, decl); + + if (!copied_decl) + continue; + + if (FieldDecl *copied_field = dyn_cast<FieldDecl>(copied_decl)) + { + QualType copied_field_type = copied_field->getType(); + + m_ast_importer->RequireCompleteType(copied_field_type); + } + + decls.push_back(copied_decl); + + DeclContext *decl_context_non_const = const_cast<DeclContext *>(decl_context); + + if (copied_decl->getDeclContext() != decl_context) + { + if (copied_decl->getDeclContext()->containsDecl(copied_decl)) + copied_decl->getDeclContext()->removeDecl(copied_decl); + copied_decl->setDeclContext(decl_context_non_const); + } + + if (!decl_context_non_const->containsDecl(copied_decl)) + decl_context_non_const->addDeclInternal(copied_decl); + } + } + + return ELR_AlreadyLoaded; +} + +void +ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context) +{ + assert (m_ast_context); + + ClangASTMetrics::RegisterVisibleQuery(); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (!context.m_decl_context) + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in a NULL DeclContext", current_id, m_ast_context, name.GetCString()); + else if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context.m_decl_context)) + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in '%s'", current_id, m_ast_context, name.GetCString(), context_named_decl->getNameAsString().c_str()); + else + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in a '%s'", current_id, m_ast_context, name.GetCString(), context.m_decl_context->getDeclKindName()); + } + + context.m_namespace_map.reset(new ClangASTImporter::NamespaceMap); + + if (const NamespaceDecl *namespace_context = dyn_cast<NamespaceDecl>(context.m_decl_context)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CAS::FEVD[%u] Inspecting namespace map %p (%d entries)", + current_id, + namespace_map.get(), + (int)namespace_map->size()); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); + i != e; + ++i) + { + if (log) + log->Printf(" CAS::FEVD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, + i->first, + i->second, + current_id); + } + } + else if (isa<ObjCInterfaceDecl>(context.m_decl_context)) + { + FindObjCPropertyAndIvarDecls(context); + } + else if (!isa<TranslationUnitDecl>(context.m_decl_context)) + { + // we shouldn't be getting FindExternalVisibleDecls calls for these + return; + } + else + { + ClangNamespaceDecl namespace_decl; + + if (log) + log->Printf(" CAS::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, + lldb::ModuleSP(), + namespace_decl, + current_id); + } + + if (!context.m_namespace_map->empty()) + { + if (log && log->GetVerbose()) + log->Printf(" CAS::FEVD[%u] Registering namespace map %p (%d entries)", + current_id, + context.m_namespace_map.get(), + (int)context.m_namespace_map->size()); + + NamespaceDecl *clang_namespace_decl = AddNamespace(context, context.m_namespace_map); + + if (clang_namespace_decl) + clang_namespace_decl->setHasExternalVisibleStorage(); + } +} + +void +ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module_sp, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id) +{ + assert (m_ast_context); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + SymbolContextList sc_list; + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + const char *name_unique_cstr = name.GetCString(); + + static ConstString id_name("id"); + static ConstString Class_name("Class"); + + if (name == id_name || name == Class_name) + return; + + if (name_unique_cstr == NULL) + return; + + // The ClangASTSource is not responsible for finding $-names. + if (name_unique_cstr[0] == '$') + return; + + if (module_sp && namespace_decl) + { + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + + if (symbol_vendor) + { + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &namespace_decl); + + if (found_namespace_decl) + { + context.m_namespace_map->push_back(std::pair<lldb::ModuleSP, ClangNamespaceDecl>(module_sp, found_namespace_decl)); + + if (log) + log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + } + else + { + const ModuleList &target_images = m_target->GetImages(); + Mutex::Locker modules_locker (target_images.GetMutex()); + + for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) + { + lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i); + + if (!image) + continue; + + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = image->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &namespace_decl); + + if (found_namespace_decl) + { + context.m_namespace_map->push_back(std::pair<lldb::ModuleSP, ClangNamespaceDecl>(image, found_namespace_decl)); + + if (log) + log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + image->GetFileSpec().GetFilename().GetCString()); + } + } + } + + do + { + TypeList types; + SymbolContext null_sc; + const bool exact_match = false; + + if (module_sp && namespace_decl) + module_sp->FindTypesInNamespace(null_sc, name, &namespace_decl, 1, types); + else + m_target->GetImages().FindTypes(null_sc, name, exact_match, 1, types); + + if (types.GetSize()) + { + lldb::TypeSP type_sp = types.GetTypeAtIndex(0); + + if (log) + { + const char *name_string = type_sp->GetName().GetCString(); + + log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\": %s", + current_id, + name.GetCString(), + (name_string ? name_string : "<anonymous>")); + } + + ClangASTType full_type = type_sp->GetClangFullType(); + + ClangASTType copied_clang_type (GuardedCopyType(full_type)); + + if (!copied_clang_type) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type", + current_id); + + break; + } + + context.AddTypeDecl(copied_clang_type); + } + else + { + do + { + // Couldn't find any types elsewhere. Try the Objective-C runtime if one exists. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + break; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector <ClangASTType> types; + + if (!type_vendor->FindTypes(name, + append, + max_matches, + types)) + break; + + if (log) + { + log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\" in the runtime", + current_id, + name.GetCString()); + } + + ClangASTType copied_clang_type (GuardedCopyType(types[0])); + + if (!copied_clang_type) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type from the runtime", + current_id); + + break; + } + + context.AddTypeDecl(copied_clang_type); + } + while(0); + } + + } while(0); +} + +template <class D> class TaggedASTDecl { +public: + TaggedASTDecl() : decl(NULL) { } + TaggedASTDecl(D *_decl) : decl(_decl) { } + bool IsValid() const { return (decl != NULL); } + bool IsInvalid() const { return !IsValid(); } + D *operator->() const { return decl; } + D *decl; +}; + +template <class D2, template <class D> class TD, class D1> +TD<D2> +DynCast(TD<D1> source) +{ + return TD<D2> (dyn_cast<D2>(source.decl)); +} + +template <class D = Decl> class DeclFromParser; +template <class D = Decl> class DeclFromUser; + +template <class D> class DeclFromParser : public TaggedASTDecl<D> { +public: + DeclFromParser() : TaggedASTDecl<D>() { } + DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) { } + + DeclFromUser<D> GetOrigin(ClangASTImporter *importer); +}; + +template <class D> class DeclFromUser : public TaggedASTDecl<D> { +public: + DeclFromUser() : TaggedASTDecl<D>() { } + DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) { } + + DeclFromParser<D> Import(ClangASTImporter *importer, ASTContext &dest_ctx); +}; + +template <class D> +DeclFromUser<D> +DeclFromParser<D>::GetOrigin(ClangASTImporter *importer) +{ + DeclFromUser <> origin_decl; + importer->ResolveDeclOrigin(this->decl, &origin_decl.decl, NULL); + if (origin_decl.IsInvalid()) + return DeclFromUser<D>(); + return DeclFromUser<D>(dyn_cast<D>(origin_decl.decl)); +} + +template <class D> +DeclFromParser<D> +DeclFromUser<D>::Import(ClangASTImporter *importer, ASTContext &dest_ctx) +{ + DeclFromParser <> parser_generic_decl(importer->CopyDecl(&dest_ctx, &this->decl->getASTContext(), this->decl)); + if (parser_generic_decl.IsInvalid()) + return DeclFromParser<D>(); + return DeclFromParser<D>(dyn_cast<D>(parser_generic_decl.decl)); +} + +static bool +FindObjCMethodDeclsWithOrigin (unsigned int current_id, + NameSearchContext &context, + ObjCInterfaceDecl *original_interface_decl, + clang::ASTContext *ast_context, + ClangASTImporter *ast_importer, + const char *log_info) +{ + const DeclarationName &decl_name(context.m_decl_name); + clang::ASTContext *original_ctx = &original_interface_decl->getASTContext(); + + Selector original_selector; + + if (decl_name.isObjCZeroArgSelector()) + { + IdentifierInfo *ident = &original_ctx->Idents.get(decl_name.getAsString()); + original_selector = original_ctx->Selectors.getSelector(0, &ident); + } + else if (decl_name.isObjCOneArgSelector()) + { + const std::string &decl_name_string = decl_name.getAsString(); + std::string decl_name_string_without_colon(decl_name_string.c_str(), decl_name_string.length() - 1); + IdentifierInfo *ident = &original_ctx->Idents.get(decl_name_string_without_colon.c_str()); + original_selector = original_ctx->Selectors.getSelector(1, &ident); + } + else + { + SmallVector<IdentifierInfo *, 4> idents; + + clang::Selector sel = decl_name.getObjCSelector(); + + unsigned num_args = sel.getNumArgs(); + + for (unsigned i = 0; + i != num_args; + ++i) + { + idents.push_back(&original_ctx->Idents.get(sel.getNameForSlot(i))); + } + + original_selector = original_ctx->Selectors.getSelector(num_args, idents.data()); + } + + DeclarationName original_decl_name(original_selector); + + ObjCInterfaceDecl::lookup_result result = original_interface_decl->lookup(original_decl_name); + + if (result.empty()) + return false; + + if (!result[0]) + return false; + + ObjCMethodDecl *result_method = dyn_cast<ObjCMethodDecl>(result[0]); + + if (!result_method) + return false; + + Decl *copied_decl = ast_importer->CopyDecl(ast_context, &result_method->getASTContext(), result_method); + + if (!copied_decl) + return false; + + ObjCMethodDecl *copied_method_decl = dyn_cast<ObjCMethodDecl>(copied_decl); + + if (!copied_method_decl) + return false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + ASTDumper dumper((Decl*)copied_method_decl); + log->Printf(" CAS::FOMD[%d] found (%s) %s", current_id, log_info, dumper.GetCString()); + } + + context.AddNamedDecl(copied_method_decl); + + return true; +} + +void +ClangASTSource::FindObjCMethodDecls (NameSearchContext &context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + const DeclarationName &decl_name(context.m_decl_name); + const DeclContext *decl_ctx(context.m_decl_context); + + const ObjCInterfaceDecl *interface_decl = dyn_cast<ObjCInterfaceDecl>(decl_ctx); + + if (!interface_decl) + return; + + do + { + Decl *original_decl = NULL; + ASTContext *original_ctx = NULL; + + m_ast_importer->ResolveDeclOrigin(interface_decl, &original_decl, &original_ctx); + + if (!original_decl) + break; + + ObjCInterfaceDecl *original_interface_decl = dyn_cast<ObjCInterfaceDecl>(original_decl); + + if (FindObjCMethodDeclsWithOrigin(current_id, + context, + original_interface_decl, + m_ast_context, + m_ast_importer, + "at origin")) + return; // found it, no need to look any further + } while (0); + + StreamString ss; + + if (decl_name.isObjCZeroArgSelector()) + { + ss.Printf("%s", decl_name.getAsString().c_str()); + } + else if (decl_name.isObjCOneArgSelector()) + { + ss.Printf("%s", decl_name.getAsString().c_str()); + } + else + { + clang::Selector sel = decl_name.getObjCSelector(); + + for (unsigned i = 0, e = sel.getNumArgs(); + i != e; + ++i) + { + llvm::StringRef r = sel.getNameForSlot(i); + ss.Printf("%s:", r.str().c_str()); + } + } + ss.Flush(); + + ConstString selector_name(ss.GetData()); + + if (log) + log->Printf("ClangASTSource::FindObjCMethodDecls[%d] on (ASTContext*)%p for selector [%s %s]", + current_id, + m_ast_context, + interface_decl->getNameAsString().c_str(), + selector_name.AsCString()); + SymbolContextList sc_list; + + const bool include_symbols = false; + const bool include_inlines = false; + const bool append = false; + + std::string interface_name = interface_decl->getNameAsString(); + + do + { + StreamString ms; + ms.Printf("-[%s %s]", interface_name.c_str(), selector_name.AsCString()); + ms.Flush(); + ConstString instance_method_name(ms.GetData()); + + m_target->GetImages().FindFunctions(instance_method_name, lldb::eFunctionNameTypeFull, include_symbols, include_inlines, append, sc_list); + + if (sc_list.GetSize()) + break; + + ms.Clear(); + ms.Printf("+[%s %s]", interface_name.c_str(), selector_name.AsCString()); + ms.Flush(); + ConstString class_method_name(ms.GetData()); + + m_target->GetImages().FindFunctions(class_method_name, lldb::eFunctionNameTypeFull, include_symbols, include_inlines, append, sc_list); + + if (sc_list.GetSize()) + break; + + // Fall back and check for methods in categories. If we find methods this way, we need to check that they're actually in + // categories on the desired class. + + SymbolContextList candidate_sc_list; + + m_target->GetImages().FindFunctions(selector_name, lldb::eFunctionNameTypeSelector, include_symbols, include_inlines, append, candidate_sc_list); + + for (uint32_t ci = 0, ce = candidate_sc_list.GetSize(); + ci != ce; + ++ci) + { + SymbolContext candidate_sc; + + if (!candidate_sc_list.GetContextAtIndex(ci, candidate_sc)) + continue; + + if (!candidate_sc.function) + continue; + + const char *candidate_name = candidate_sc.function->GetName().AsCString(); + + const char *cursor = candidate_name; + + if (*cursor != '+' && *cursor != '-') + continue; + + ++cursor; + + if (*cursor != '[') + continue; + + ++cursor; + + size_t interface_len = interface_name.length(); + + if (strncmp(cursor, interface_name.c_str(), interface_len)) + continue; + + cursor += interface_len; + + if (*cursor == ' ' || *cursor == '(') + sc_list.Append(candidate_sc); + } + } + while (0); + + if (sc_list.GetSize()) + { + // We found a good function symbol. Use that. + + for (uint32_t i = 0, e = sc_list.GetSize(); + i != e; + ++i) + { + SymbolContext sc; + + if (!sc_list.GetContextAtIndex(i, sc)) + continue; + + if (!sc.function) + continue; + + DeclContext *function_ctx = sc.function->GetClangDeclContext(); + + if (!function_ctx) + continue; + + ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(function_ctx); + + if (!method_decl) + continue; + + ObjCInterfaceDecl *found_interface_decl = method_decl->getClassInterface(); + + if (!found_interface_decl) + continue; + + if (found_interface_decl->getName() == interface_decl->getName()) + { + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, &method_decl->getASTContext(), method_decl); + + if (!copied_decl) + continue; + + ObjCMethodDecl *copied_method_decl = dyn_cast<ObjCMethodDecl>(copied_decl); + + if (!copied_method_decl) + continue; + + if (log) + { + ASTDumper dumper((Decl*)copied_method_decl); + log->Printf(" CAS::FOMD[%d] found (in symbols) %s", current_id, dumper.GetCString()); + } + + context.AddNamedDecl(copied_method_decl); + } + } + + return; + } + + // Try the debug information. + + do + { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(const_cast<ObjCInterfaceDecl*>(interface_decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried in this scenario. + + DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl(complete_interface_decl); + + if (complete_interface_decl == interface_decl) + break; // already checked this one + + if (log) + log->Printf("CAS::FOPD[%d] trying origin (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + complete_interface_decl, + &complete_iface_decl->getASTContext()); + + FindObjCMethodDeclsWithOrigin(current_id, + context, + complete_interface_decl, + m_ast_context, + m_ast_importer, + "in debug info"); + + return; + } + while (0); + + do + { + // Check the runtime only if the debug information didn't have a complete interface. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + break; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + ConstString interface_name(interface_decl->getNameAsString().c_str()); + bool append = false; + uint32_t max_matches = 1; + std::vector <ClangASTType> types; + + if (!type_vendor->FindTypes(interface_name, + append, + max_matches, + types)) + break; + + const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); + + const ObjCInterfaceType *runtime_interface_type = dyn_cast<ObjCInterfaceType>(runtime_clang_type); + + if (!runtime_interface_type) + break; + + ObjCInterfaceDecl *runtime_interface_decl = runtime_interface_type->getDecl(); + + FindObjCMethodDeclsWithOrigin(current_id, + context, + runtime_interface_decl, + m_ast_context, + m_ast_importer, + "in runtime"); + } + while(0); +} + +static bool +FindObjCPropertyAndIvarDeclsWithOrigin (unsigned int current_id, + NameSearchContext &context, + clang::ASTContext &ast_context, + ClangASTImporter *ast_importer, + DeclFromUser<const ObjCInterfaceDecl> &origin_iface_decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (origin_iface_decl.IsInvalid()) + return false; + + std::string name_str = context.m_decl_name.getAsString(); + StringRef name(name_str.c_str()); + IdentifierInfo &name_identifier(origin_iface_decl->getASTContext().Idents.get(name)); + + DeclFromUser<ObjCPropertyDecl> origin_property_decl(origin_iface_decl->FindPropertyDeclaration(&name_identifier)); + + bool found = false; + + if (origin_property_decl.IsValid()) + { + DeclFromParser<ObjCPropertyDecl> parser_property_decl(origin_property_decl.Import(ast_importer, ast_context)); + if (parser_property_decl.IsValid()) + { + if (log) + { + ASTDumper dumper((Decl*)parser_property_decl.decl); + log->Printf(" CAS::FOPD[%d] found %s", current_id, dumper.GetCString()); + } + + context.AddNamedDecl(parser_property_decl.decl); + found = true; + } + } + + DeclFromUser<ObjCIvarDecl> origin_ivar_decl(origin_iface_decl->getIvarDecl(&name_identifier)); + + if (origin_ivar_decl.IsValid()) + { + DeclFromParser<ObjCIvarDecl> parser_ivar_decl(origin_ivar_decl.Import(ast_importer, ast_context)); + if (parser_ivar_decl.IsValid()) + { + if (log) + { + ASTDumper dumper((Decl*)parser_ivar_decl.decl); + log->Printf(" CAS::FOPD[%d] found %s", current_id, dumper.GetCString()); + } + + context.AddNamedDecl(parser_ivar_decl.decl); + found = true; + } + } + + return found; +} + +void +ClangASTSource::FindObjCPropertyAndIvarDecls (NameSearchContext &context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + DeclFromParser<const ObjCInterfaceDecl> parser_iface_decl(cast<ObjCInterfaceDecl>(context.m_decl_context)); + DeclFromUser<const ObjCInterfaceDecl> origin_iface_decl(parser_iface_decl.GetOrigin(m_ast_importer)); + + ConstString class_name(parser_iface_decl->getNameAsString().c_str()); + + if (log) + log->Printf("ClangASTSource::FindObjCPropertyAndIvarDecls[%d] on (ASTContext*)%p for '%s.%s'", + current_id, + m_ast_context, + parser_iface_decl->getNameAsString().c_str(), + context.m_decl_name.getAsString().c_str()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + origin_iface_decl)) + return; + + if (log) + log->Printf("CAS::FOPD[%d] couldn't find the property on origin (ObjCInterfaceDecl*)%p/(ASTContext*)%p, searching elsewhere...", + current_id, + origin_iface_decl.decl, + &origin_iface_decl->getASTContext()); + + SymbolContext null_sc; + TypeList type_list; + + do + { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(const_cast<ObjCInterfaceDecl*>(parser_iface_decl.decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried in this scenario. + + DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl(complete_interface_decl); + + if (complete_iface_decl.decl == origin_iface_decl.decl) + break; // already checked this one + + if (log) + log->Printf("CAS::FOPD[%d] trying origin (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + complete_iface_decl.decl, + &complete_iface_decl->getASTContext()); + + FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + complete_iface_decl); + + return; + } + while(0); + + do + { + // Check the runtime only if the debug information didn't have a complete interface. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + return; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector <ClangASTType> types; + + if (!type_vendor->FindTypes(class_name, + append, + max_matches, + types)) + break; + + const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); + + const ObjCInterfaceType *runtime_interface_type = dyn_cast<ObjCInterfaceType>(runtime_clang_type); + + if (!runtime_interface_type) + break; + + DeclFromUser<const ObjCInterfaceDecl> runtime_iface_decl(runtime_interface_type->getDecl()); + + if (log) + log->Printf("CAS::FOPD[%d] trying runtime (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + runtime_iface_decl.decl, + &runtime_iface_decl->getASTContext()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + runtime_iface_decl)) + return; + } + while(0); +} + +typedef llvm::DenseMap <const FieldDecl *, uint64_t> FieldOffsetMap; +typedef llvm::DenseMap <const CXXRecordDecl *, CharUnits> BaseOffsetMap; + +template <class D, class O> +static bool +ImportOffsetMap (llvm::DenseMap <const D*, O> &destination_map, + llvm::DenseMap <const D*, O> &source_map, + ClangASTImporter *importer, + ASTContext &dest_ctx) +{ + typedef llvm::DenseMap <const D*, O> MapType; + + for (typename MapType::iterator fi = source_map.begin(), fe = source_map.end(); + fi != fe; + ++fi) + { + DeclFromUser <D> user_decl(const_cast<D*>(fi->first)); + DeclFromParser <D> parser_decl(user_decl.Import(importer, dest_ctx)); + if (parser_decl.IsInvalid()) + return false; + destination_map.insert(std::pair<const D *, O>(parser_decl.decl, fi->second)); + } + + return true; +} + +template <bool IsVirtual> bool ExtractBaseOffsets (const ASTRecordLayout &record_layout, + DeclFromUser<const CXXRecordDecl> &record, + BaseOffsetMap &base_offsets) +{ + for (CXXRecordDecl::base_class_const_iterator + bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()), + be = (IsVirtual ? record->vbases_end() : record->bases_end()); + bi != be; + ++bi) + { + if (!IsVirtual && bi->isVirtual()) + continue; + + const clang::Type *origin_base_type = bi->getType().getTypePtr(); + const clang::RecordType *origin_base_record_type = origin_base_type->getAs<RecordType>(); + + if (!origin_base_record_type) + return false; + + DeclFromUser <RecordDecl> origin_base_record(origin_base_record_type->getDecl()); + + if (origin_base_record.IsInvalid()) + return false; + + DeclFromUser <CXXRecordDecl> origin_base_cxx_record(DynCast<CXXRecordDecl>(origin_base_record)); + + if (origin_base_cxx_record.IsInvalid()) + return false; + + CharUnits base_offset; + + if (IsVirtual) + base_offset = record_layout.getVBaseClassOffset(origin_base_cxx_record.decl); + else + base_offset = record_layout.getBaseClassOffset(origin_base_cxx_record.decl); + + base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>(origin_base_cxx_record.decl, base_offset)); + } + + return true; +} + +bool +ClangASTSource::layoutRecordType(const RecordDecl *record, + uint64_t &size, + uint64_t &alignment, + FieldOffsetMap &field_offsets, + BaseOffsetMap &base_offsets, + BaseOffsetMap &virtual_base_offsets) +{ + ClangASTMetrics::RegisterRecordLayout(); + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + log->Printf("LayoutRecordType[%u] on (ASTContext*)%p for (RecordDecl*)%p [name = '%s']", + current_id, + m_ast_context, + record, + record->getNameAsString().c_str()); + } + + + DeclFromParser <const RecordDecl> parser_record(record); + DeclFromUser <const RecordDecl> origin_record(parser_record.GetOrigin(m_ast_importer)); + + if (origin_record.IsInvalid()) + return false; + + FieldOffsetMap origin_field_offsets; + BaseOffsetMap origin_base_offsets; + BaseOffsetMap origin_virtual_base_offsets; + + ClangASTContext::GetCompleteDecl(&origin_record->getASTContext(), const_cast<RecordDecl*>(origin_record.decl)); + + if (!origin_record.decl->getDefinition()) + return false; + + const ASTRecordLayout &record_layout(origin_record->getASTContext().getASTRecordLayout(origin_record.decl)); + + int field_idx = 0, field_count = record_layout.getFieldCount(); + + for (RecordDecl::field_iterator fi = origin_record->field_begin(), fe = origin_record->field_end(); + fi != fe; + ++fi) + { + if (field_idx >= field_count) + return false; // Layout didn't go well. Bail out. + + uint64_t field_offset = record_layout.getFieldOffset(field_idx); + + origin_field_offsets.insert(std::pair<const FieldDecl *, uint64_t>(*fi, field_offset)); + + field_idx++; + } + + ASTContext &parser_ast_context(record->getASTContext()); + + DeclFromUser <const CXXRecordDecl> origin_cxx_record(DynCast<const CXXRecordDecl>(origin_record)); + + if (origin_cxx_record.IsValid()) + { + if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record, origin_base_offsets) || + !ExtractBaseOffsets<true>(record_layout, origin_cxx_record, origin_virtual_base_offsets)) + return false; + } + + if (!ImportOffsetMap(field_offsets, origin_field_offsets, m_ast_importer, parser_ast_context) || + !ImportOffsetMap(base_offsets, origin_base_offsets, m_ast_importer, parser_ast_context) || + !ImportOffsetMap(virtual_base_offsets, origin_virtual_base_offsets, m_ast_importer, parser_ast_context)) + return false; + + size = record_layout.getSize().getQuantity() * m_ast_context->getCharWidth(); + alignment = record_layout.getAlignment().getQuantity() * m_ast_context->getCharWidth(); + + if (log) + { + log->Printf("LRT[%u] returned:", current_id); + log->Printf("LRT[%u] Original = (RecordDecl*)%p", current_id, origin_record.decl); + log->Printf("LRT[%u] Size = %" PRId64, current_id, size); + log->Printf("LRT[%u] Alignment = %" PRId64, current_id, alignment); + log->Printf("LRT[%u] Fields:", current_id); + for (RecordDecl::field_iterator fi = record->field_begin(), fe = record->field_end(); + fi != fe; + ++fi) + { + log->Printf("LRT[%u] (FieldDecl*)%p, Name = '%s', Offset = %" PRId64 " bits", + current_id, + *fi, + fi->getNameAsString().c_str(), + field_offsets[*fi]); + } + DeclFromParser <const CXXRecordDecl> parser_cxx_record = DynCast<const CXXRecordDecl>(parser_record); + if (parser_cxx_record.IsValid()) + { + log->Printf("LRT[%u] Bases:", current_id); + for (CXXRecordDecl::base_class_const_iterator bi = parser_cxx_record->bases_begin(), be = parser_cxx_record->bases_end(); + bi != be; + ++bi) + { + bool is_virtual = bi->isVirtual(); + + QualType base_type = bi->getType(); + const RecordType *base_record_type = base_type->getAs<RecordType>(); + DeclFromParser <RecordDecl> base_record(base_record_type->getDecl()); + DeclFromParser <CXXRecordDecl> base_cxx_record = DynCast<CXXRecordDecl>(base_record); + + log->Printf("LRT[%u] %s(CXXRecordDecl*)%p, Name = '%s', Offset = %" PRId64 " chars", + current_id, + (is_virtual ? "Virtual " : ""), + base_cxx_record.decl, + base_cxx_record.decl->getNameAsString().c_str(), + (is_virtual ? virtual_base_offsets[base_cxx_record.decl].getQuantity() : + base_offsets[base_cxx_record.decl].getQuantity())); + } + } + else + { + log->Printf("LRD[%u] Not a CXXRecord, so no bases", current_id); + } + } + + return true; +} + +void +ClangASTSource::CompleteNamespaceMap (ClangASTImporter::NamespaceMapSP &namespace_map, + const ConstString &name, + ClangASTImporter::NamespaceMapSP &parent_map) const +{ + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + if (parent_map && parent_map->size()) + log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for namespace %s in namespace %s", + current_id, + m_ast_context, + name.GetCString(), + parent_map->begin()->second.GetNamespaceDecl()->getDeclName().getAsString().c_str()); + else + log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for namespace %s", + current_id, + m_ast_context, + name.GetCString()); + } + + + if (parent_map) + { + for (ClangASTImporter::NamespaceMap::iterator i = parent_map->begin(), e = parent_map->end(); + i != e; + ++i) + { + ClangNamespaceDecl found_namespace_decl; + + lldb::ModuleSP module_sp = i->first; + ClangNamespaceDecl module_parent_namespace_decl = i->second; + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &module_parent_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair<lldb::ModuleSP, ClangNamespaceDecl>(module_sp, found_namespace_decl)); + + if (log) + log->Printf(" CMN[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + else + { + const ModuleList &target_images = m_target->GetImages(); + Mutex::Locker modules_locker(target_images.GetMutex()); + + ClangNamespaceDecl null_namespace_decl; + + for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) + { + lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i); + + if (!image) + continue; + + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = image->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &null_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair<lldb::ModuleSP, ClangNamespaceDecl>(image, found_namespace_decl)); + + if (log) + log->Printf(" CMN[%u] Found namespace %s in module %s", + current_id, + name.GetCString(), + image->GetFileSpec().GetFilename().GetCString()); + } + } +} + +NamespaceDecl * +ClangASTSource::AddNamespace (NameSearchContext &context, ClangASTImporter::NamespaceMapSP &namespace_decls) +{ + if (!namespace_decls) + return NULL; + + const ClangNamespaceDecl &namespace_decl = namespace_decls->begin()->second; + + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, namespace_decl.GetASTContext(), namespace_decl.GetNamespaceDecl()); + + if (!copied_decl) + return NULL; + + NamespaceDecl *copied_namespace_decl = dyn_cast<NamespaceDecl>(copied_decl); + + if (!copied_namespace_decl) + return NULL; + + context.m_decls.push_back(copied_namespace_decl); + + m_ast_importer->RegisterNamespaceMap(copied_namespace_decl, namespace_decls); + + return dyn_cast<NamespaceDecl>(copied_decl); +} + +ClangASTType +ClangASTSource::GuardedCopyType (const ClangASTType &src_type) +{ + ClangASTMetrics::RegisterLLDBImport(); + + SetImportInProgress(true); + + QualType copied_qual_type = m_ast_importer->CopyType (m_ast_context, src_type.GetASTContext(), src_type.GetQualType()); + + SetImportInProgress(false); + + if (copied_qual_type.getAsOpaquePtr() && copied_qual_type->getCanonicalTypeInternal().isNull()) + // this shouldn't happen, but we're hardening because the AST importer seems to be generating bad types + // on occasion. + return ClangASTType(); + + return ClangASTType(m_ast_context, copied_qual_type); +} + +clang::NamedDecl * +NameSearchContext::AddVarDecl(const ClangASTType &type) +{ + assert (type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return NULL; + + IdentifierInfo *ii = m_decl_name.getAsIdentifierInfo(); + + clang::ASTContext *ast = type.GetASTContext(); + + clang::NamedDecl *Decl = VarDecl::Create(*ast, + const_cast<DeclContext*>(m_decl_context), + SourceLocation(), + SourceLocation(), + ii, + type.GetQualType(), + 0, + SC_Static); + m_decls.push_back(Decl); + + return Decl; +} + +clang::NamedDecl * +NameSearchContext::AddFunDecl (const ClangASTType &type) +{ + assert (type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return NULL; + + if (m_function_types.count(type)) + return NULL; + + m_function_types.insert(type); + + QualType qual_type (type.GetQualType()); + + clang::ASTContext *ast = type.GetASTContext(); + + const bool isInlineSpecified = false; + const bool hasWrittenPrototype = true; + const bool isConstexprSpecified = false; + + clang::FunctionDecl *func_decl = FunctionDecl::Create (*ast, + const_cast<DeclContext*>(m_decl_context), + SourceLocation(), + SourceLocation(), + m_decl_name.getAsIdentifierInfo(), + qual_type, + NULL, + SC_Static, + isInlineSpecified, + hasWrittenPrototype, + isConstexprSpecified); + + // We have to do more than just synthesize the FunctionDecl. We have to + // synthesize ParmVarDecls for all of the FunctionDecl's arguments. To do + // this, we raid the function's FunctionProtoType for types. + + const FunctionProtoType *func_proto_type = qual_type.getTypePtr()->getAs<FunctionProtoType>(); + + if (func_proto_type) + { + unsigned NumArgs = func_proto_type->getNumArgs(); + unsigned ArgIndex; + + SmallVector<ParmVarDecl *, 5> parm_var_decls; + + for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) + { + QualType arg_qual_type (func_proto_type->getArgType(ArgIndex)); + + parm_var_decls.push_back(ParmVarDecl::Create (*ast, + const_cast<DeclContext*>(m_decl_context), + SourceLocation(), + SourceLocation(), + NULL, + arg_qual_type, + NULL, + SC_Static, + NULL)); + } + + func_decl->setParams(ArrayRef<ParmVarDecl*>(parm_var_decls)); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("Function type wasn't a FunctionProtoType"); + } + + m_decls.push_back(func_decl); + + return func_decl; +} + +clang::NamedDecl * +NameSearchContext::AddGenericFunDecl() +{ + FunctionProtoType::ExtProtoInfo proto_info; + + proto_info.Variadic = true; + + QualType generic_function_type(m_ast_source.m_ast_context->getFunctionType (m_ast_source.m_ast_context->UnknownAnyTy, // result + ArrayRef<QualType>(), // argument types + proto_info)); + + return AddFunDecl(ClangASTType (m_ast_source.m_ast_context, generic_function_type)); +} + +clang::NamedDecl * +NameSearchContext::AddTypeDecl(const ClangASTType &clang_type) +{ + if (clang_type) + { + QualType qual_type = clang_type.GetQualType(); + + if (const TypedefType *typedef_type = llvm::dyn_cast<TypedefType>(qual_type)) + { + TypedefNameDecl *typedef_name_decl = typedef_type->getDecl(); + + m_decls.push_back(typedef_name_decl); + + return (NamedDecl*)typedef_name_decl; + } + else if (const TagType *tag_type = qual_type->getAs<TagType>()) + { + TagDecl *tag_decl = tag_type->getDecl(); + + m_decls.push_back(tag_decl); + + return tag_decl; + } + else if (const ObjCObjectType *objc_object_type = qual_type->getAs<ObjCObjectType>()) + { + ObjCInterfaceDecl *interface_decl = objc_object_type->getInterface(); + + m_decls.push_back((NamedDecl*)interface_decl); + + return (NamedDecl*)interface_decl; + } + } + return NULL; +} + +void +NameSearchContext::AddLookupResult (clang::DeclContextLookupConstResult result) +{ + for (clang::NamedDecl *decl : result) + m_decls.push_back (decl); +} + +void +NameSearchContext::AddNamedDecl (clang::NamedDecl *decl) +{ + m_decls.push_back (decl); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangExpressionDeclMap.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionDeclMap.cpp new file mode 100644 index 0000000..072e781 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -0,0 +1,1956 @@ +//===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangExpressionDeclMap.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Decl.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +ClangExpressionDeclMap::ClangExpressionDeclMap (bool keep_result_in_memory, ExecutionContext &exe_ctx) : + ClangASTSource (exe_ctx.GetTargetSP()), + m_found_entities (), + m_struct_members (), + m_keep_result_in_memory (keep_result_in_memory), + m_parser_vars (), + m_struct_vars () +{ + EnableStructVars(); +} + +ClangExpressionDeclMap::~ClangExpressionDeclMap() +{ + // Note: The model is now that the parser's AST context and all associated + // data does not vanish until the expression has been executed. This means + // that valuable lookup data (like namespaces) doesn't vanish, but + + DidParse(); + DisableStructVars(); +} + +bool +ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx, + Materializer *materializer) +{ + ClangASTMetrics::ClearLocalCounters(); + + EnableParserVars(); + m_parser_vars->m_exe_ctx = exe_ctx; + + Target *target = exe_ctx.GetTargetPtr(); + if (exe_ctx.GetFramePtr()) + m_parser_vars->m_sym_ctx = exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything); + else if (exe_ctx.GetThreadPtr() && exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)) + m_parser_vars->m_sym_ctx = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext(lldb::eSymbolContextEverything); + else if (exe_ctx.GetProcessPtr()) + { + m_parser_vars->m_sym_ctx.Clear(true); + m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); + } + else if (target) + { + m_parser_vars->m_sym_ctx.Clear(true); + m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP(); + } + + if (target) + { + m_parser_vars->m_persistent_vars = &target->GetPersistentVariables(); + + if (!target->GetScratchClangASTContext()) + return false; + } + + m_parser_vars->m_target_info = GetTargetInfo(); + m_parser_vars->m_materializer = materializer; + + return true; +} + +void +ClangExpressionDeclMap::DidParse() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + ClangASTMetrics::DumpCounters(log); + + if (m_parser_vars.get()) + { + for (size_t entity_index = 0, num_entities = m_found_entities.GetSize(); + entity_index < num_entities; + ++entity_index) + { + ClangExpressionVariableSP var_sp(m_found_entities.GetVariableAtIndex(entity_index)); + if (var_sp) + var_sp->DisableParserVars(GetParserID()); + } + + for (size_t pvar_index = 0, num_pvars = m_parser_vars->m_persistent_vars->GetSize(); + pvar_index < num_pvars; + ++pvar_index) + { + ClangExpressionVariableSP pvar_sp(m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index)); + if (pvar_sp) + pvar_sp->DisableParserVars(GetParserID()); + } + + DisableParserVars(); + } +} + +// Interface for IRForTarget + +ClangExpressionDeclMap::TargetInfo +ClangExpressionDeclMap::GetTargetInfo() +{ + assert (m_parser_vars.get()); + + TargetInfo ret; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + + Process *process = exe_ctx.GetProcessPtr(); + if (process) + { + ret.byte_order = process->GetByteOrder(); + ret.address_byte_size = process->GetAddressByteSize(); + } + else + { + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + ret.byte_order = target->GetArchitecture().GetByteOrder(); + ret.address_byte_size = target->GetArchitecture().GetAddressByteSize(); + } + } + + return ret; +} + +bool +ClangExpressionDeclMap::AddPersistentVariable +( + const NamedDecl *decl, + const ConstString &name, + TypeFromParser parser_type, + bool is_result, + bool is_lvalue +) +{ + assert (m_parser_vars.get()); + + if (m_parser_vars->m_materializer && is_result) + { + Error err; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == NULL) + return false; + + ASTContext *context(target->GetScratchClangASTContext()->getASTContext()); + + TypeFromUser user_type(m_ast_importer->DeportType(context, + parser_type.GetASTContext(), + parser_type.GetOpaqueQualType()), + context); + + uint32_t offset = m_parser_vars->m_materializer->AddResultVariable(user_type, is_lvalue, m_keep_result_in_memory, err); + + ClangExpressionVariableSP var_sp = m_found_entities.CreateVariable(exe_ctx.GetBestExecutionContextScope(), + name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size); + + if (!var_sp) + return false; + + var_sp->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + var_sp->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID()); + + jit_vars->m_offset = offset; + + return true; + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == NULL) + return false; + + ASTContext *context(target->GetScratchClangASTContext()->getASTContext()); + + TypeFromUser user_type(m_ast_importer->DeportType(context, + parser_type.GetASTContext(), + parser_type.GetOpaqueQualType()), + context); + + if (!user_type.GetOpaqueQualType()) + { + if (log) + log->Printf("Persistent variable's type wasn't copied successfully"); + return false; + } + + if (!m_parser_vars->m_target_info.IsValid()) + return false; + + ClangExpressionVariableSP var_sp = m_parser_vars->m_persistent_vars->CreatePersistentVariable (exe_ctx.GetBestExecutionContextScope (), + name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size); + + if (!var_sp) + return false; + + var_sp->m_frozen_sp->SetHasCompleteType(); + + if (is_result) + var_sp->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + else + var_sp->m_flags |= ClangExpressionVariable::EVKeepInTarget; // explicitly-declared persistent variables should persist + + if (is_lvalue) + { + var_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference; + } + else + { + var_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + var_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + } + + if (m_keep_result_in_memory) + { + var_sp->m_flags |= ClangExpressionVariable::EVKeepInTarget; + } + + if (log) + log->Printf("Created persistent variable with flags 0x%hx", var_sp->m_flags); + + var_sp->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + return true; +} + +bool +ClangExpressionDeclMap::AddValueToStruct +( + const NamedDecl *decl, + const ConstString &name, + llvm::Value *value, + size_t size, + off_t alignment +) +{ + assert (m_struct_vars.get()); + assert (m_parser_vars.get()); + + bool is_persistent_variable = false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + m_struct_vars->m_struct_laid_out = false; + + if (m_struct_members.GetVariable(decl, GetParserID())) + return true; + + ClangExpressionVariableSP var_sp (m_found_entities.GetVariable(decl, GetParserID())); + + if (!var_sp) + { + var_sp = m_parser_vars->m_persistent_vars->GetVariable(decl, GetParserID()); + is_persistent_variable = true; + } + + if (!var_sp) + return false; + + if (log) + log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure", + decl, + name.GetCString(), + var_sp->GetName().GetCString()); + + // We know entity->m_parser_vars is valid because we used a parser variable + // to find it + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_llvm_value = value; + + if (ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID())) + { + // We already laid this out; do not touch + + if (log) + log->Printf("Already placed at 0x%llx", (unsigned long long)jit_vars->m_offset); + } + + var_sp->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID()); + + jit_vars->m_alignment = alignment; + jit_vars->m_size = size; + + m_struct_members.AddVariable(var_sp); + + if (m_parser_vars->m_materializer) + { + uint32_t offset = 0; + + Error err; + + if (is_persistent_variable) + { + offset = m_parser_vars->m_materializer->AddPersistentVariable(var_sp, err); + } + else + { + if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym) + offset = m_parser_vars->m_materializer->AddSymbol(*sym, err); + else if (const RegisterInfo *reg_info = var_sp->GetRegisterInfo()) + offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err); + else if (parser_vars->m_lldb_var) + offset = m_parser_vars->m_materializer->AddVariable(parser_vars->m_lldb_var, err); + } + + if (!err.Success()) + return false; + + if (log) + log->Printf("Placed at 0x%llx", (unsigned long long)offset); + + jit_vars->m_offset = offset; // TODO DoStructLayout() should not change this. + } + + return true; +} + +bool +ClangExpressionDeclMap::DoStructLayout () +{ + assert (m_struct_vars.get()); + + if (m_struct_vars->m_struct_laid_out) + return true; + + if (!m_parser_vars->m_materializer) + return false; + + m_struct_vars->m_struct_alignment = m_parser_vars->m_materializer->GetStructAlignment(); + m_struct_vars->m_struct_size = m_parser_vars->m_materializer->GetStructByteSize(); + m_struct_vars->m_struct_laid_out = true; + return true; +} + +bool ClangExpressionDeclMap::GetStructInfo +( + uint32_t &num_elements, + size_t &size, + off_t &alignment +) +{ + assert (m_struct_vars.get()); + + if (!m_struct_vars->m_struct_laid_out) + return false; + + num_elements = m_struct_members.GetSize(); + size = m_struct_vars->m_struct_size; + alignment = m_struct_vars->m_struct_alignment; + + return true; +} + +bool +ClangExpressionDeclMap::GetStructElement +( + const NamedDecl *&decl, + llvm::Value *&value, + off_t &offset, + ConstString &name, + uint32_t index +) +{ + assert (m_struct_vars.get()); + + if (!m_struct_vars->m_struct_laid_out) + return false; + + if (index >= m_struct_members.GetSize()) + return false; + + ClangExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index)); + + if (!member_sp) + return false; + + ClangExpressionVariable::ParserVars *parser_vars = member_sp->GetParserVars(GetParserID()); + ClangExpressionVariable::JITVars *jit_vars = member_sp->GetJITVars(GetParserID()); + + if (!parser_vars || + !jit_vars || + !member_sp->GetValueObject()) + return false; + + decl = parser_vars->m_named_decl; + value = parser_vars->m_llvm_value; + offset = jit_vars->m_offset; + name = member_sp->GetName(); + + return true; +} + +bool +ClangExpressionDeclMap::GetFunctionInfo +( + const NamedDecl *decl, + uint64_t &ptr +) +{ + ClangExpressionVariableSP entity_sp(m_found_entities.GetVariable(decl, GetParserID())); + + if (!entity_sp) + return false; + + // We know m_parser_vars is valid since we searched for the variable by + // its NamedDecl + + ClangExpressionVariable::ParserVars *parser_vars = entity_sp->GetParserVars(GetParserID()); + + ptr = parser_vars->m_lldb_value.GetScalar().ULongLong(); + + return true; +} + +static void +FindCodeSymbolInContext +( + const ConstString &name, + SymbolContext &sym_ctx, + SymbolContextList &sc_list +) +{ + SymbolContextList temp_sc_list; + if (sym_ctx.module_sp) + sym_ctx.module_sp->FindSymbolsWithNameAndType(name, eSymbolTypeAny, temp_sc_list); + + if (!sc_list.GetSize() && sym_ctx.target_sp) + sym_ctx.target_sp->GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, temp_sc_list); + + unsigned temp_sc_list_size = temp_sc_list.GetSize(); + for (unsigned i = 0; i < temp_sc_list_size; i++) + { + SymbolContext sym_ctx; + temp_sc_list.GetContextAtIndex(i, sym_ctx); + if (sym_ctx.symbol) + { + switch (sym_ctx.symbol->GetType()) + { + case eSymbolTypeCode: + case eSymbolTypeResolver: + sc_list.Append(sym_ctx); + break; + + default: + break; + } + } + } +} + +bool +ClangExpressionDeclMap::GetFunctionAddress +( + const ConstString &name, + uint64_t &func_addr +) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + // Back out in all cases where we're not fully initialized + if (target == NULL) + return false; + if (!m_parser_vars->m_sym_ctx.target_sp) + return false; + + SymbolContextList sc_list; + + FindCodeSymbolInContext(name, m_parser_vars->m_sym_ctx, sc_list); + + if (!sc_list.GetSize()) + { + // We occasionally get debug information in which a const function is reported + // as non-const, so the mangled name is wrong. This is a hack to compensate. + + if (!strncmp(name.GetCString(), "_ZN", 3) && + strncmp(name.GetCString(), "_ZNK", 4)) + { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(name.GetCString() + 3); + ConstString fixed_name(fixed_scratch.c_str()); + + if (log) + log->Printf("Failed to find symbols given non-const name %s; trying %s", name.GetCString(), fixed_name.GetCString()); + + FindCodeSymbolInContext(fixed_name, m_parser_vars->m_sym_ctx, sc_list); + } + } + + if (!sc_list.GetSize()) + return false; + + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(0, sym_ctx); + + const Address *func_so_addr = NULL; + bool is_indirect_function = false; + + if (sym_ctx.function) + func_so_addr = &sym_ctx.function->GetAddressRange().GetBaseAddress(); + else if (sym_ctx.symbol) { + func_so_addr = &sym_ctx.symbol->GetAddress(); + is_indirect_function = sym_ctx.symbol->IsIndirect(); + } else + return false; + + if (!func_so_addr || !func_so_addr->IsValid()) + return false; + + func_addr = func_so_addr->GetCallableLoadAddress (target, is_indirect_function); + + return true; +} + +addr_t +ClangExpressionDeclMap::GetSymbolAddress (Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type) +{ + SymbolContextList sc_list; + + target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list); + + const uint32_t num_matches = sc_list.GetSize(); + addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; + + for (uint32_t i=0; i<num_matches && (symbol_load_addr == 0 || symbol_load_addr == LLDB_INVALID_ADDRESS); i++) + { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(i, sym_ctx); + + const Address *sym_address = &sym_ctx.symbol->GetAddress(); + + if (!sym_address || !sym_address->IsValid()) + continue; + + if (sym_address) + { + switch (sym_ctx.symbol->GetType()) + { + case eSymbolTypeCode: + case eSymbolTypeTrampoline: + symbol_load_addr = sym_address->GetCallableLoadAddress (&target); + break; + + case eSymbolTypeResolver: + symbol_load_addr = sym_address->GetCallableLoadAddress (&target, true); + break; + + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeVariable: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeException: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeUndefined: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + symbol_load_addr = sym_address->GetLoadAddress (&target); + break; + } + } + } + + if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) + { + ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime(); + + if (runtime) + { + symbol_load_addr = runtime->LookupRuntimeSymbol(name); + } + } + + return symbol_load_addr; +} + +addr_t +ClangExpressionDeclMap::GetSymbolAddress (const ConstString &name, lldb::SymbolType symbol_type) +{ + assert (m_parser_vars.get()); + + if (!m_parser_vars->m_exe_ctx.GetTargetPtr()) + return false; + + return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), m_parser_vars->m_exe_ctx.GetProcessPtr(), name, symbol_type); +} + +const Symbol * +ClangExpressionDeclMap::FindGlobalDataSymbol (Target &target, + const ConstString &name) +{ + SymbolContextList sc_list; + + target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); + + const uint32_t matches = sc_list.GetSize(); + for (uint32_t i=0; i<matches; ++i) + { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(i, sym_ctx); + if (sym_ctx.symbol) + { + const Symbol *symbol = sym_ctx.symbol; + const Address *sym_address = &symbol->GetAddress(); + + if (sym_address && sym_address->IsValid()) + { + switch (symbol->GetType()) + { + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeAbsolute: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + if (symbol->GetDemangledNameIsSynthesized()) + { + // If the demangled name was synthesized, then don't use it + // for expressions. Only let the symbol match if the mangled + // named matches for these symbols. + if (symbol->GetMangled().GetMangledName() != name) + break; + } + return symbol; + + case eSymbolTypeCode: // We already lookup functions elsewhere + case eSymbolTypeVariable: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeTrampoline: + case eSymbolTypeInvalid: + case eSymbolTypeException: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeUndefined: + case eSymbolTypeResolver: + break; + } + } + } + } + + return NULL; +} + +lldb::VariableSP +ClangExpressionDeclMap::FindGlobalVariable +( + Target &target, + ModuleSP &module, + const ConstString &name, + ClangNamespaceDecl *namespace_decl, + TypeFromUser *type +) +{ + VariableList vars; + + if (module && namespace_decl) + module->FindGlobalVariables (name, namespace_decl, true, -1, vars); + else + target.GetImages().FindGlobalVariables(name, true, -1, vars); + + if (vars.GetSize()) + { + if (type) + { + for (size_t i = 0; i < vars.GetSize(); ++i) + { + VariableSP var_sp = vars.GetVariableAtIndex(i); + + if (ClangASTContext::AreTypesSame(*type, var_sp->GetType()->GetClangFullType())) + return var_sp; + } + } + else + { + return vars.GetVariableAtIndex(0); + } + } + + return VariableSP(); +} + +// Interface for ClangASTSource + +void +ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context) +{ + assert (m_ast_context); + + ClangASTMetrics::RegisterVisibleQuery(); + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (GetImportInProgress()) + { + if (log && log->GetVerbose()) + log->Printf("Ignoring a query during an import"); + return; + } + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (!context.m_decl_context) + log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for '%s' in a NULL DeclContext", current_id, name.GetCString()); + else if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context.m_decl_context)) + log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for '%s' in '%s'", current_id, name.GetCString(), context_named_decl->getNameAsString().c_str()); + else + log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for '%s' in a '%s'", current_id, name.GetCString(), context.m_decl_context->getDeclKindName()); + } + + if (const NamespaceDecl *namespace_context = dyn_cast<NamespaceDecl>(context.m_decl_context)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)", + current_id, + namespace_map.get(), + (int)namespace_map->size()); + + if (!namespace_map) + return; + + for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(), e = namespace_map->end(); + i != e; + ++i) + { + if (log) + log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, + i->first, + i->second, + current_id); + } + } + else if (isa<TranslationUnitDecl>(context.m_decl_context)) + { + ClangNamespaceDecl namespace_decl; + + if (log) + log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, + lldb::ModuleSP(), + namespace_decl, + current_id); + } + + if (!context.m_found.variable) + ClangASTSource::FindExternalVisibleDecls(context); +} + +void +ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module_sp, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id) +{ + assert (m_ast_context); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + SymbolContextList sc_list; + + const ConstString name(context.m_decl_name.getAsString().c_str()); + + const char *name_unique_cstr = name.GetCString(); + + if (name_unique_cstr == NULL) + return; + + static ConstString id_name("id"); + static ConstString Class_name("Class"); + + if (name == id_name || name == Class_name) + return; + + // Only look for functions by name out in our symbols if the function + // doesn't start with our phony prefix of '$' + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + if (name_unique_cstr[0] == '$' && !namespace_decl) + { + static ConstString g_lldb_class_name ("$__lldb_class"); + + if (name == g_lldb_class_name) + { + // Clang is looking for the type of "this" + + if (frame == NULL) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction); + + if (!sym_ctx.function) + return; + + // Get the block that defines the function + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + return; + + clang::CXXMethodDecl *method_decl = llvm::dyn_cast<clang::CXXMethodDecl>(decl_context); + + if (method_decl) + { + clang::CXXRecordDecl *class_decl = method_decl->getParent(); + + QualType class_qual_type(class_decl->getTypeForDecl(), 0); + + TypeFromUser class_user_type (class_qual_type.getAsOpaquePtr(), + &class_decl->getASTContext()); + + if (log) + { + ASTDumper ast_dumper(class_qual_type); + log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromParser class_type = CopyClassType(class_user_type, current_id); + + if (!class_type.IsValid()) + return; + + TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo(QualType::getFromOpaquePtr(class_type.GetOpaqueQualType())); + + if (!type_source_info) + return; + + TypedefDecl *typedef_decl = TypedefDecl::Create(*m_ast_context, + m_ast_context->getTranslationUnitDecl(), + SourceLocation(), + SourceLocation(), + context.m_decl_name.getAsIdentifierInfo(), + type_source_info); + + + if (!typedef_decl) + return; + + context.AddNamedDecl(typedef_decl); + + if (method_decl->isInstance()) + { + // self is a pointer to the object + + QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + } + else + { + // This branch will get hit if we are executing code in the context of a function that + // claims to have an object pointer (through DW_AT_object_pointer?) but is not formally a + // method of the class. In that case, just look up the "this" variable in the the current + // scope and use its type. + // FIXME: This code is formally correct, but clang doesn't currently emit DW_AT_object_pointer + // for C++ so it hasn't actually been tested. + + VariableList *vars = frame->GetVariableList(false); + + lldb::VariableSP this_var = vars->FindVariable(ConstString("this")); + + if (this_var && + this_var->IsInScope(frame) && + this_var->LocationIsValidForFrame (frame)) + { + Type *this_type = this_var->GetType(); + + if (!this_type) + return; + + ClangASTType pointee_type = this_type->GetClangForwardType().GetPointeeType(); + + if (pointee_type.IsValid()) + { + if (log) + { + ASTDumper ast_dumper(this_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type(pointee_type); + AddOneType(context, class_user_type, current_id); + + + TypeFromUser this_user_type(this_type->GetClangFullType()); + m_struct_vars->m_object_pointer_type = this_user_type; + return; + } + } + } + + return; + } + + static ConstString g_lldb_objc_class_name ("$__lldb_objc_class"); + if (name == g_lldb_objc_class_name) + { + // Clang is looking for the type of "*self" + + if (!frame) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction); + + if (!sym_ctx.function) + return; + + // Get the block that defines the function + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + return; + + clang::ObjCMethodDecl *method_decl = llvm::dyn_cast<clang::ObjCMethodDecl>(decl_context); + + if (method_decl) + { + ObjCInterfaceDecl* self_interface = method_decl->getClassInterface(); + + if (!self_interface) + return; + + const clang::Type *interface_type = self_interface->getTypeForDecl(); + + if (!interface_type) + return; // This is unlikely, but we have seen crashes where this occurred + + TypeFromUser class_user_type(QualType(interface_type, 0).getAsOpaquePtr(), + &method_decl->getASTContext()); + + if (log) + { + ASTDumper ast_dumper(interface_type); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + AddOneType(context, class_user_type, current_id); + + if (method_decl->isInstanceMethod()) + { + // self is a pointer to the object + + QualType class_pointer_type = method_decl->getASTContext().getObjCObjectPointerType(QualType(interface_type, 0)); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + else + { + // self is a Class pointer + QualType class_type = method_decl->getASTContext().getObjCClassType(); + + TypeFromUser self_user_type(class_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + + return; + } + else + { + // This branch will get hit if we are executing code in the context of a function that + // claims to have an object pointer (through DW_AT_object_pointer?) but is not formally a + // method of the class. In that case, just look up the "self" variable in the the current + // scope and use its type. + + VariableList *vars = frame->GetVariableList(false); + + lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); + + if (self_var && + self_var->IsInScope(frame) && + self_var->LocationIsValidForFrame (frame)) + { + Type *self_type = self_var->GetType(); + + if (!self_type) + return; + + ClangASTType self_clang_type = self_type->GetClangFullType(); + + if (self_clang_type.IsObjCClassType()) + { + return; + } + else if (self_clang_type.IsObjCObjectPointerType()) + { + self_clang_type = self_clang_type.GetPointeeType(); + + if (!self_clang_type) + return; + + if (log) + { + ASTDumper ast_dumper(self_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type (self_clang_type); + + AddOneType(context, class_user_type, current_id); + + TypeFromUser self_user_type(self_type->GetClangFullType()); + + m_struct_vars->m_object_pointer_type = self_user_type; + return; + } + } + } + + return; + } + + // any other $__lldb names should be weeded out now + if (!::strncmp(name_unique_cstr, "$__lldb", sizeof("$__lldb") - 1)) + return; + + do + { + if (!target) + break; + + ClangASTContext *scratch_clang_ast_context = target->GetScratchClangASTContext(); + + if (!scratch_clang_ast_context) + break; + + ASTContext *scratch_ast_context = scratch_clang_ast_context->getASTContext(); + + if (!scratch_ast_context) + break; + + TypeDecl *ptype_type_decl = m_parser_vars->m_persistent_vars->GetPersistentType(name); + + if (!ptype_type_decl) + break; + + Decl *parser_ptype_decl = m_ast_importer->CopyDecl(m_ast_context, scratch_ast_context, ptype_type_decl); + + if (!parser_ptype_decl) + break; + + TypeDecl *parser_ptype_type_decl = dyn_cast<TypeDecl>(parser_ptype_decl); + + if (!parser_ptype_type_decl) + break; + + if (log) + log->Printf(" CEDM::FEVD[%u] Found persistent type %s", current_id, name.GetCString()); + + context.AddNamedDecl(parser_ptype_type_decl); + } while (0); + + ClangExpressionVariableSP pvar_sp(m_parser_vars->m_persistent_vars->GetVariable(name)); + + if (pvar_sp) + { + AddOneVariable(context, pvar_sp, current_id); + return; + } + + const char *reg_name(&name.GetCString()[1]); + + if (m_parser_vars->m_exe_ctx.GetRegisterContext()) + { + const RegisterInfo *reg_info(m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName(reg_name)); + + if (reg_info) + { + if (log) + log->Printf(" CEDM::FEVD[%u] Found register %s", current_id, reg_info->name); + + AddOneRegister(context, reg_info, current_id); + } + } + } + else + { + ValueObjectSP valobj; + VariableSP var; + Error err; + + if (frame && !namespace_decl) + { + valobj = frame->GetValueForVariableExpressionPath(name_unique_cstr, + eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember || + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || + StackFrame::eExpressionPathOptionsNoFragileObjcIvar || + StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var, + err); + + // If we found a variable in scope, no need to pull up function names + if (err.Success() && var) + { + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + if (target) + { + var = FindGlobalVariable (*target, + module_sp, + name, + &namespace_decl, + NULL); + + if (var) + { + valobj = ValueObjectVariable::Create(target, var); + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + if (!context.m_found.variable) + { + const bool include_inlines = false; + const bool append = false; + + if (namespace_decl && module_sp) + { + const bool include_symbols = false; + + module_sp->FindFunctions(name, + &namespace_decl, + eFunctionNameTypeBase, + include_symbols, + include_inlines, + append, + sc_list); + } + else if (target && !namespace_decl) + { + const bool include_symbols = true; + + // TODO Fix FindFunctions so that it doesn't return + // instance methods for eFunctionNameTypeBase. + + target->GetImages().FindFunctions(name, + eFunctionNameTypeFull, + include_symbols, + include_inlines, + append, + sc_list); + } + + if (sc_list.GetSize()) + { + Symbol *generic_symbol = NULL; + Symbol *non_extern_symbol = NULL; + + for (uint32_t index = 0, num_indices = sc_list.GetSize(); + index < num_indices; + ++index) + { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + + if (sym_ctx.function) + { + clang::DeclContext *decl_ctx = sym_ctx.function->GetClangDeclContext(); + + if (!decl_ctx) + continue; + + // Filter out class/instance methods. + if (dyn_cast<clang::ObjCMethodDecl>(decl_ctx)) + continue; + if (dyn_cast<clang::CXXMethodDecl>(decl_ctx)) + continue; + + AddOneFunction(context, sym_ctx.function, NULL, current_id); + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } + else if (sym_ctx.symbol) + { + if (sym_ctx.symbol->IsExternal()) + generic_symbol = sym_ctx.symbol; + else + non_extern_symbol = sym_ctx.symbol; + } + } + + if (!context.m_found.function_with_type_info) + { + if (generic_symbol) + { + AddOneFunction (context, NULL, generic_symbol, current_id); + context.m_found.function = true; + } + else if (non_extern_symbol) + { + AddOneFunction (context, NULL, non_extern_symbol, current_id); + context.m_found.function = true; + } + } + } + + if (target && !context.m_found.variable && !namespace_decl) + { + // We couldn't find a non-symbol variable for this. Now we'll hunt for a generic + // data symbol, and -- if it is found -- treat it as a variable. + + const Symbol *data_symbol = FindGlobalDataSymbol(*target, name); + + if (data_symbol) + { + AddOneGenericVariable(context, *data_symbol, current_id); + context.m_found.variable = true; + } + } + } + } +} + +static clang_type_t +MaybePromoteToBlockPointerType +( + ASTContext *ast_context, + clang_type_t candidate_type +) +{ + if (!candidate_type) + return candidate_type; + + QualType candidate_qual_type = QualType::getFromOpaquePtr(candidate_type); + + const PointerType *candidate_pointer_type = dyn_cast<PointerType>(candidate_qual_type); + + if (!candidate_pointer_type) + return candidate_type; + + QualType pointee_qual_type = candidate_pointer_type->getPointeeType(); + + const RecordType *pointee_record_type = dyn_cast<RecordType>(pointee_qual_type); + + if (!pointee_record_type) + return candidate_type; + + RecordDecl *pointee_record_decl = pointee_record_type->getDecl(); + + if (!pointee_record_decl->isRecord()) + return candidate_type; + + if (!pointee_record_decl->getName().startswith(llvm::StringRef("__block_literal_"))) + return candidate_type; + + QualType generic_function_type = ast_context->getFunctionNoProtoType(ast_context->UnknownAnyTy); + QualType block_pointer_type = ast_context->getBlockPointerType(generic_function_type); + + return block_pointer_type.getAsOpaquePtr(); +} + +bool +ClangExpressionDeclMap::GetVariableValue (VariableSP &var, + lldb_private::Value &var_location, + TypeFromUser *user_type, + TypeFromParser *parser_type) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Type *var_type = var->GetType(); + + if (!var_type) + { + if (log) + log->PutCString("Skipped a definition because it has no type"); + return false; + } + + ClangASTType var_clang_type = var_type->GetClangFullType(); + + if (!var_clang_type) + { + if (log) + log->PutCString("Skipped a definition because it has no Clang type"); + return false; + } + + // commented out because of <rdar://problem/11024417> + ASTContext *ast = var_type->GetClangASTContext().getASTContext(); + + if (!ast) + { + if (log) + log->PutCString("There is no AST context for the current execution context"); + return false; + } + //var_clang_type = MaybePromoteToBlockPointerType (ast, var_clang_type); + + DWARFExpression &var_location_expr = var->LocationExpression(); + + lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS; + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + if (var_location_expr.IsLocationList()) + { + SymbolContext var_sc; + var->CalculateSymbolContext (&var_sc); + loclist_base_load_addr = var_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (target); + } + Error err; + + if (var->GetLocationIsConstantValueData()) + { + DataExtractor const_value_extractor; + + if (var_location_expr.GetExpressionData(const_value_extractor)) + { + var_location = Value(const_value_extractor.GetDataStart(), const_value_extractor.GetByteSize()); + var_location.SetValueType(Value::eValueTypeHostAddress); + } + else + { + if (log) + log->Printf("Error evaluating constant variable: %s", err.AsCString()); + return false; + } + } + + ClangASTType type_to_use = GuardedCopyType(var_clang_type); + + if (!type_to_use) + { + if (log) + log->Printf("Couldn't copy a variable's type into the parser's AST context"); + + return false; + } + + if (parser_type) + *parser_type = TypeFromParser(type_to_use); + + if (var_location.GetContextType() == Value::eContextTypeInvalid) + var_location.SetClangType(type_to_use); + + if (var_location.GetValueType() == Value::eValueTypeFileAddress) + { + SymbolContext var_sc; + var->CalculateSymbolContext(&var_sc); + + if (!var_sc.module_sp) + return false; + + Address so_addr(var_location.GetScalar().ULongLong(), var_sc.module_sp->GetSectionList()); + + lldb::addr_t load_addr = so_addr.GetLoadAddress(target); + + if (load_addr != LLDB_INVALID_ADDRESS) + { + var_location.GetScalar() = load_addr; + var_location.SetValueType(Value::eValueTypeLoadAddress); + } + } + + if (user_type) + *user_type = TypeFromUser(var_clang_type); + + return true; +} + +void +ClangExpressionDeclMap::AddOneVariable (NameSearchContext &context, VariableSP var, ValueObjectSP valobj, unsigned int current_id) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser ut; + TypeFromParser pt; + Value var_location; + + if (!GetVariableValue (var, var_location, &ut, &pt)) + return; + + clang::QualType parser_opaque_type = QualType::getFromOpaquePtr(pt.GetOpaqueQualType()); + + if (parser_opaque_type.isNull()) + return; + + if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) + { + if (const TagType *tag_type = dyn_cast<TagType>(parser_type)) + CompleteType(tag_type->getDecl()); + } + + + bool is_reference = pt.IsReferenceType(); + + NamedDecl *var_decl = NULL; + if (is_reference) + var_decl = context.AddVarDecl(pt); + else + var_decl = context.AddVarDecl(pt.GetLValueReferenceType()); + + std::string decl_name(context.m_decl_name.getAsString()); + ConstString entity_name(decl_name.c_str()); + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (valobj)); + + assert (entity.get()); + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + parser_vars->m_parser_type = pt; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_value = var_location; + parser_vars->m_lldb_var = var; + + if (is_reference) + entity->m_flags |= ClangExpressionVariable::EVTypeIsReference; + + if (log) + { + ASTDumper orig_dumper(ut.GetOpaqueQualType()); + ASTDumper ast_dumper(var_decl); + log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s (original %s)", current_id, decl_name.c_str(), ast_dumper.GetCString(), orig_dumper.GetCString()); + } +} + +void +ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, + ClangExpressionVariableSP &pvar_sp, + unsigned int current_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser user_type (pvar_sp->GetTypeFromUser()); + + TypeFromParser parser_type (GuardedCopyType(user_type)); + + if (!parser_type.GetOpaqueQualType()) + { + if (log) + log->Printf(" CEDM::FEVD[%u] Couldn't import type for pvar %s", current_id, pvar_sp->GetName().GetCString()); + return; + } + + NamedDecl *var_decl = context.AddVarDecl(parser_type.GetLValueReferenceType()); + + pvar_sp->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = pvar_sp->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_value.Clear(); + + if (log) + { + ASTDumper ast_dumper(var_decl); + log->Printf(" CEDM::FEVD[%u] Added pvar %s, returned %s", current_id, pvar_sp->GetName().GetCString(), ast_dumper.GetCString()); + } +} + +void +ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context, + const Symbol &symbol, + unsigned int current_id) +{ + assert(m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + if (target == NULL) + return; + + ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); + + TypeFromUser user_type (ClangASTContext::GetBasicType(scratch_ast_context, eBasicTypeVoid).GetPointerType().GetLValueReferenceType()); + TypeFromParser parser_type (ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid).GetPointerType().GetLValueReferenceType()); + NamedDecl *var_decl = context.AddVarDecl(parser_type); + + std::string decl_name(context.m_decl_name.getAsString()); + ConstString entity_name(decl_name.c_str()); + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope (), + entity_name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + const Address &symbol_address = symbol.GetAddress(); + lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target); + + //parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, user_type.GetOpaqueQualType()); + parser_vars->m_lldb_value.SetClangType(user_type); + parser_vars->m_lldb_value.GetScalar() = symbol_load_addr; + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); + + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_sym = &symbol; + + if (log) + { + ASTDumper ast_dumper(var_decl); + + log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s", current_id, decl_name.c_str(), ast_dumper.GetCString()); + } +} + +bool +ClangExpressionDeclMap::ResolveUnknownTypes() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); + + for (size_t index = 0, num_entities = m_found_entities.GetSize(); + index < num_entities; + ++index) + { + ClangExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index); + + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + if (entity->m_flags & ClangExpressionVariable::EVUnknownType) + { + const NamedDecl *named_decl = parser_vars->m_named_decl; + const VarDecl *var_decl = dyn_cast<VarDecl>(named_decl); + + if (!var_decl) + { + if (log) + log->Printf("Entity of unknown type does not have a VarDecl"); + return false; + } + + if (log) + { + ASTDumper ast_dumper(const_cast<VarDecl*>(var_decl)); + log->Printf("Variable of unknown type now has Decl %s", ast_dumper.GetCString()); + } + + QualType var_type = var_decl->getType(); + TypeFromParser parser_type(var_type.getAsOpaquePtr(), &var_decl->getASTContext()); + + lldb::clang_type_t copied_type = m_ast_importer->CopyType(scratch_ast_context, &var_decl->getASTContext(), var_type.getAsOpaquePtr()); + + if (!copied_type) + { + if (log) + log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't import the type for a variable"); + + return (bool) lldb::ClangExpressionVariableSP(); + } + + TypeFromUser user_type(copied_type, scratch_ast_context); + +// parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, user_type.GetOpaqueQualType()); + parser_vars->m_lldb_value.SetClangType(user_type); + parser_vars->m_parser_type = parser_type; + + entity->SetClangType(user_type); + + entity->m_flags &= ~(ClangExpressionVariable::EVUnknownType); + } + } + + return true; +} + +void +ClangExpressionDeclMap::AddOneRegister (NameSearchContext &context, + const RegisterInfo *reg_info, + unsigned int current_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ClangASTType clang_type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (m_ast_context, + reg_info->encoding, + reg_info->byte_size * 8); + + if (!clang_type) + { + if (log) + log->Printf(" Tried to add a type for %s, but couldn't get one", context.m_decl_name.getAsString().c_str()); + return; + } + + TypeFromParser parser_clang_type (clang_type); + + NamedDecl *var_decl = context.AddVarDecl(parser_clang_type); + + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName (ConstString (decl_name.c_str())); + entity->SetRegisterInfo (reg_info); + entity->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_clang_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + parser_vars->m_lldb_value.Clear(); + entity->m_flags |= ClangExpressionVariable::EVBareRegister; + + if (log) + { + ASTDumper ast_dumper(var_decl); + log->Printf(" CEDM::FEVD[%d] Added register %s, returned %s", current_id, context.m_decl_name.getAsString().c_str(), ast_dumper.GetCString()); + } +} + +void +ClangExpressionDeclMap::AddOneFunction (NameSearchContext &context, + Function* function, + Symbol* symbol, + unsigned int current_id) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + NamedDecl *function_decl = NULL; + const Address *fun_address = NULL; + ClangASTType function_clang_type; + + bool is_indirect_function = false; + + if (function) + { + Type *function_type = function->GetType(); + + if (!function_type) + { + if (log) + log->PutCString(" Skipped a function because it has no type"); + return; + } + + function_clang_type = function_type->GetClangFullType(); + + if (!function_clang_type) + { + if (log) + log->PutCString(" Skipped a function because it has no Clang type"); + return; + } + + fun_address = &function->GetAddressRange().GetBaseAddress(); + + ClangASTType copied_function_type = GuardedCopyType(function_clang_type); + if (copied_function_type) + { + function_decl = context.AddFunDecl(copied_function_type); + + if (!function_decl) + { + if (log) + { + log->Printf (" Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}", + function_type->GetName().GetCString(), + function_type->GetID()); + } + + return; + } + } + else + { + // We failed to copy the type we found + if (log) + { + log->Printf (" Failed to import the function type '%s' {0x%8.8" PRIx64 "} into the expression parser AST contenxt", + function_type->GetName().GetCString(), + function_type->GetID()); + } + + return; + } + } + else if (symbol) + { + fun_address = &symbol->GetAddress(); + function_decl = context.AddGenericFunDecl(); + is_indirect_function = symbol->IsIndirect(); + } + else + { + if (log) + log->PutCString(" AddOneFunction called with no function and no symbol"); + return; + } + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + lldb::addr_t load_addr = fun_address->GetCallableLoadAddress(target, is_indirect_function); + + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope (), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName(ConstString(decl_name.c_str())); + entity->SetClangType (function_clang_type); + entity->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + if (load_addr != LLDB_INVALID_ADDRESS) + { + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress); + parser_vars->m_lldb_value.GetScalar() = load_addr; + } + else + { + // We have to try finding a file address. + + lldb::addr_t file_addr = fun_address->GetFileAddress(); + + parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress); + parser_vars->m_lldb_value.GetScalar() = file_addr; + } + + + parser_vars->m_named_decl = function_decl; + parser_vars->m_llvm_value = NULL; + + if (log) + { + ASTDumper ast_dumper(function_decl); + + StreamString ss; + + fun_address->Dump(&ss, m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), Address::DumpStyleResolvedDescription); + + log->Printf(" CEDM::FEVD[%u] Found %s function %s (description %s), returned %s", + current_id, + (function ? "specific" : "generic"), + decl_name.c_str(), + ss.GetData(), + ast_dumper.GetCString()); + } +} + +TypeFromParser +ClangExpressionDeclMap::CopyClassType(TypeFromUser &ut, + unsigned int current_id) +{ + ClangASTType copied_clang_type = GuardedCopyType(ut); + + if (!copied_clang_type) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangExpressionDeclMap::CopyClassType - Couldn't import the type"); + + return TypeFromParser(); + } + + if (copied_clang_type.IsAggregateType() && copied_clang_type.GetCompleteType ()) + { + ClangASTType void_clang_type = ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid); + ClangASTType void_ptr_clang_type = void_clang_type.GetPointerType(); + + ClangASTType method_type = ClangASTContext::CreateFunctionType (m_ast_context, + void_clang_type, + &void_ptr_clang_type, + 1, + false, + copied_clang_type.GetTypeQualifiers()); + + const bool is_virtual = false; + const bool is_static = false; + const bool is_inline = false; + const bool is_explicit = false; + const bool is_attr_used = true; + const bool is_artificial = false; + + copied_clang_type.AddMethodToCXXRecordType ("$__lldb_expr", + method_type, + lldb::eAccessPublic, + is_virtual, + is_static, + is_inline, + is_explicit, + is_attr_used, + is_artificial); + } + + return TypeFromParser(copied_clang_type); +} + +void +ClangExpressionDeclMap::AddOneType(NameSearchContext &context, + TypeFromUser &ut, + unsigned int current_id) +{ + ClangASTType copied_clang_type = GuardedCopyType(ut); + + if (!copied_clang_type) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangExpressionDeclMap::AddOneType - Couldn't import the type"); + + return; + } + + context.AddTypeDecl(copied_clang_type); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp new file mode 100644 index 0000000..98c0bfd --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp @@ -0,0 +1,595 @@ +//===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Expression/ClangExpressionParser.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRDynamicChecks.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/Sema/SemaConsumer.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/TargetSelect.h" + +#if defined(__FreeBSD__) +#define USE_STANDARD_JIT +#endif + +#if defined (USE_STANDARD_JIT) +#include "llvm/ExecutionEngine/JIT.h" +#else +#include "llvm/ExecutionEngine/MCJIT.h" +#endif +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Signals.h" + +using namespace clang; +using namespace llvm; +using namespace lldb_private; + +//===----------------------------------------------------------------------===// +// Utility Methods for Clang +//===----------------------------------------------------------------------===// + +std::string GetBuiltinIncludePath(const char *Argv0) { + SmallString<128> P(llvm::sys::fs::getMainExecutable( + Argv0, (void *)(intptr_t) GetBuiltinIncludePath)); + + if (!P.empty()) { + llvm::sys::path::remove_filename(P); // Remove /clang from foo/bin/clang + llvm::sys::path::remove_filename(P); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + llvm::sys::path::append(P, "lib", "clang", CLANG_VERSION_STRING, + "include"); + } + + return P.str(); +} + + +//===----------------------------------------------------------------------===// +// Main driver for Clang +//===----------------------------------------------------------------------===// + +static void LLVMErrorHandler(void *UserData, const std::string &Message) { + DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + assert(0); +} + +static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { + using namespace clang::frontend; + + switch (CI.getFrontendOpts().ProgramAction) { + default: + llvm_unreachable("Invalid program action!"); + + case ASTDump: return new ASTDumpAction(); + case ASTPrint: return new ASTPrintAction(); + case ASTDumpXML: return new ASTDumpXMLAction(); + case ASTView: return new ASTViewAction(); + case DumpRawTokens: return new DumpRawTokensAction(); + case DumpTokens: return new DumpTokensAction(); + case EmitAssembly: return new EmitAssemblyAction(); + case EmitBC: return new EmitBCAction(); + case EmitHTML: return new HTMLPrintAction(); + case EmitLLVM: return new EmitLLVMAction(); + case EmitLLVMOnly: return new EmitLLVMOnlyAction(); + case EmitCodeGenOnly: return new EmitCodeGenOnlyAction(); + case EmitObj: return new EmitObjAction(); + case FixIt: return new FixItAction(); + case GeneratePCH: return new GeneratePCHAction(); + case GeneratePTH: return new GeneratePTHAction(); + case InitOnly: return new InitOnlyAction(); + case ParseSyntaxOnly: return new SyntaxOnlyAction(); + + case PluginAction: { + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == CI.getFrontendOpts().ActionName) { + llvm::OwningPtr<PluginASTAction> P(it->instantiate()); + if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs)) + return 0; + return P.take(); + } + } + + CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) + << CI.getFrontendOpts().ActionName; + return 0; + } + + case PrintDeclContext: return new DeclContextPrintAction(); + case PrintPreamble: return new PrintPreambleAction(); + case PrintPreprocessedInput: return new PrintPreprocessedAction(); + case RewriteMacros: return new RewriteMacrosAction(); + case RewriteObjC: return new RewriteObjCAction(); + case RewriteTest: return new RewriteTestAction(); + //case RunAnalysis: return new AnalysisAction(); + case RunPreprocessorOnly: return new PreprocessOnlyAction(); + } +} + +static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { + // Create the underlying action. + FrontendAction *Act = CreateFrontendBaseAction(CI); + if (!Act) + return 0; + + // If there are any AST files to merge, create a frontend action + // adaptor to perform the merge. + if (!CI.getFrontendOpts().ASTMergeFiles.empty()) + Act = new ASTMergeAction(Act, CI.getFrontendOpts().ASTMergeFiles); + + return Act; +} + +//===----------------------------------------------------------------------===// +// Implementation of ClangExpressionParser +//===----------------------------------------------------------------------===// + +ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, + ClangExpression &expr) : + m_expr (expr), + m_compiler (), + m_code_generator () +{ + // Initialize targets first, so that --version shows registered targets. + static struct InitializeLLVM { + InitializeLLVM() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + llvm::DisablePrettyStackTrace = true; + } + } InitializeLLVM; + + // 1. Create a new compiler instance. + m_compiler.reset(new CompilerInstance()); + + // 2. Install the target. + + lldb::TargetSP target_sp; + if (exe_scope) + target_sp = exe_scope->CalculateTarget(); + + // TODO: figure out what to really do when we don't have a valid target. + // Sometimes this will be ok to just use the host target triple (when we + // evaluate say "2+3", but other expressions like breakpoint conditions + // and other things that _are_ target specific really shouldn't just be + // using the host triple. This needs to be fixed in a better way. + if (target_sp && target_sp->GetArchitecture().IsValid()) + { + std::string triple = target_sp->GetArchitecture().GetTriple().str(); + + int dash_count = 0; + for (size_t i = 0; i < triple.size(); ++i) + { + if (triple[i] == '-') + dash_count++; + if (dash_count == 3) + { + triple.resize(i); + break; + } + } + + m_compiler->getTargetOpts().Triple = triple; + } + else + { + m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); + } + + if (target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86 || + target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86_64) + { + m_compiler->getTargetOpts().Features.push_back("+sse"); + m_compiler->getTargetOpts().Features.push_back("+sse2"); + } + + if (m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) + m_compiler->getTargetOpts().ABI = "apcs-gnu"; + + m_compiler->createDiagnostics(); + + // Create the target instance. + m_compiler->setTarget(TargetInfo::CreateTargetInfo(m_compiler->getDiagnostics(), + &m_compiler->getTargetOpts())); + + assert (m_compiler->hasTarget()); + + // 3. Set options. + + lldb::LanguageType language = expr.Language(); + + switch (language) + { + case lldb::eLanguageTypeC: + break; + case lldb::eLanguageTypeObjC: + m_compiler->getLangOpts().ObjC1 = true; + m_compiler->getLangOpts().ObjC2 = true; + break; + case lldb::eLanguageTypeC_plus_plus: + m_compiler->getLangOpts().CPlusPlus = true; + m_compiler->getLangOpts().CPlusPlus11 = true; + break; + case lldb::eLanguageTypeObjC_plus_plus: + default: + m_compiler->getLangOpts().ObjC1 = true; + m_compiler->getLangOpts().ObjC2 = true; + m_compiler->getLangOpts().CPlusPlus = true; + m_compiler->getLangOpts().CPlusPlus11 = true; + break; + } + + m_compiler->getLangOpts().Bool = true; + m_compiler->getLangOpts().WChar = true; + m_compiler->getLangOpts().Blocks = true; + m_compiler->getLangOpts().DebuggerSupport = true; // Features specifically for debugger clients + if (expr.DesiredResultType() == ClangExpression::eResultTypeId) + m_compiler->getLangOpts().DebuggerCastResultToId = true; + + // Spell checking is a nice feature, but it ends up completing a + // lot of types that we didn't strictly speaking need to complete. + // As a result, we spend a long time parsing and importing debug + // information. + m_compiler->getLangOpts().SpellChecking = false; + + lldb::ProcessSP process_sp; + if (exe_scope) + process_sp = exe_scope->CalculateProcess(); + + if (process_sp && m_compiler->getLangOpts().ObjC1) + { + if (process_sp->GetObjCLanguageRuntime()) + { + if (process_sp->GetObjCLanguageRuntime()->GetRuntimeVersion() == eAppleObjC_V2) + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7)); + else + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::FragileMacOSX, VersionTuple(10, 7)); + + if (process_sp->GetObjCLanguageRuntime()->HasNewLiteralsAndIndexing()) + m_compiler->getLangOpts().DebuggerObjCLiteral = true; + } + } + + m_compiler->getLangOpts().ThreadsafeStatics = false; + m_compiler->getLangOpts().AccessControl = false; // Debuggers get universal access + m_compiler->getLangOpts().DollarIdents = true; // $ indicates a persistent variable name + + // Set CodeGen options + m_compiler->getCodeGenOpts().EmitDeclMetadata = true; + m_compiler->getCodeGenOpts().InstrumentFunctions = false; + + // Disable some warnings. + m_compiler->getDiagnostics().setDiagnosticGroupMapping("unused-value", clang::diag::MAP_IGNORE, SourceLocation()); + m_compiler->getDiagnostics().setDiagnosticGroupMapping("odr", clang::diag::MAP_IGNORE, SourceLocation()); + + // Inform the target of the language options + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + m_compiler->getTarget().setForcedLangOptions(m_compiler->getLangOpts()); + + // 4. Set up the diagnostic buffer for reporting errors + + m_compiler->getDiagnostics().setClient(new clang::TextDiagnosticBuffer); + + // 5. Set up the source management objects inside the compiler + + clang::FileSystemOptions file_system_options; + m_file_manager.reset(new clang::FileManager(file_system_options)); + + if (!m_compiler->hasSourceManager()) + m_compiler->createSourceManager(*m_file_manager.get()); + + m_compiler->createFileManager(); + m_compiler->createPreprocessor(); + + // 6. Most of this we get from the CompilerInstance, but we + // also want to give the context an ExternalASTSource. + m_selector_table.reset(new SelectorTable()); + m_builtin_context.reset(new Builtin::Context()); + + std::unique_ptr<clang::ASTContext> ast_context(new ASTContext(m_compiler->getLangOpts(), + m_compiler->getSourceManager(), + &m_compiler->getTarget(), + m_compiler->getPreprocessor().getIdentifierTable(), + *m_selector_table.get(), + *m_builtin_context.get(), + 0)); + + ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); + + if (decl_map) + { + llvm::OwningPtr<clang::ExternalASTSource> ast_source(decl_map->CreateProxy()); + decl_map->InstallASTContext(ast_context.get()); + ast_context->setExternalSource(ast_source); + } + + m_compiler->setASTContext(ast_context.release()); + + std::string module_name("$__lldb_module"); + + m_llvm_context.reset(new LLVMContext()); + m_code_generator.reset(CreateLLVMCodeGen(m_compiler->getDiagnostics(), + module_name, + m_compiler->getCodeGenOpts(), + m_compiler->getTargetOpts(), + *m_llvm_context)); +} + +ClangExpressionParser::~ClangExpressionParser() +{ +} + +unsigned +ClangExpressionParser::Parse (Stream &stream) +{ + TextDiagnosticBuffer *diag_buf = static_cast<TextDiagnosticBuffer*>(m_compiler->getDiagnostics().getClient()); + + diag_buf->FlushDiagnostics (m_compiler->getDiagnostics()); + + MemoryBuffer *memory_buffer = MemoryBuffer::getMemBufferCopy(m_expr.Text(), __FUNCTION__); + m_compiler->getSourceManager().createMainFileIDForMemBuffer (memory_buffer); + + diag_buf->BeginSourceFile(m_compiler->getLangOpts(), &m_compiler->getPreprocessor()); + + ASTConsumer *ast_transformer = m_expr.ASTTransformer(m_code_generator.get()); + + if (ast_transformer) + ParseAST(m_compiler->getPreprocessor(), ast_transformer, m_compiler->getASTContext()); + else + ParseAST(m_compiler->getPreprocessor(), m_code_generator.get(), m_compiler->getASTContext()); + + diag_buf->EndSourceFile(); + + TextDiagnosticBuffer::const_iterator diag_iterator; + + int num_errors = 0; + + for (diag_iterator = diag_buf->warn_begin(); + diag_iterator != diag_buf->warn_end(); + ++diag_iterator) + stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); + + num_errors = 0; + + for (diag_iterator = diag_buf->err_begin(); + diag_iterator != diag_buf->err_end(); + ++diag_iterator) + { + num_errors++; + stream.Printf("error: %s\n", (*diag_iterator).second.c_str()); + } + + for (diag_iterator = diag_buf->note_begin(); + diag_iterator != diag_buf->note_end(); + ++diag_iterator) + stream.Printf("note: %s\n", (*diag_iterator).second.c_str()); + + if (!num_errors) + { + if (m_expr.DeclMap() && !m_expr.DeclMap()->ResolveUnknownTypes()) + { + stream.Printf("error: Couldn't infer the type of a variable\n"); + num_errors++; + } + } + + return num_errors; +} + +static bool FindFunctionInModule (ConstString &mangled_name, + llvm::Module *module, + const char *orig_name) +{ + for (llvm::Module::iterator fi = module->getFunctionList().begin(), fe = module->getFunctionList().end(); + fi != fe; + ++fi) + { + if (fi->getName().str().find(orig_name) != std::string::npos) + { + mangled_name.SetCString(fi->getName().str().c_str()); + return true; + } + } + + return false; +} + +Error +ClangExpressionParser::PrepareForExecution (lldb::addr_t &func_addr, + lldb::addr_t &func_end, + std::unique_ptr<IRExecutionUnit> &execution_unit_ap, + ExecutionContext &exe_ctx, + bool &can_interpret, + ExecutionPolicy execution_policy) +{ + func_addr = LLDB_INVALID_ADDRESS; + func_end = LLDB_INVALID_ADDRESS; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + std::unique_ptr<llvm::ExecutionEngine> execution_engine_ap; + + Error err; + + std::unique_ptr<llvm::Module> module_ap (m_code_generator->ReleaseModule()); + + if (!module_ap.get()) + { + err.SetErrorToGenericError(); + err.SetErrorString("IR doesn't contain a module"); + return err; + } + + // Find the actual name of the function (it's often mangled somehow) + + ConstString function_name; + + if (!FindFunctionInModule(function_name, module_ap.get(), m_expr.FunctionName())) + { + err.SetErrorToGenericError(); + err.SetErrorStringWithFormat("Couldn't find %s() in the module", m_expr.FunctionName()); + return err; + } + else + { + if (log) + log->Printf("Found function %s for %s", function_name.AsCString(), m_expr.FunctionName()); + } + + m_execution_unit.reset(new IRExecutionUnit(m_llvm_context, // handed off here + module_ap, // handed off here + function_name, + exe_ctx.GetTargetSP(), + m_compiler->getTargetOpts().Features)); + + ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); // result can be NULL + + if (decl_map) + { + Stream *error_stream = NULL; + Target *target = exe_ctx.GetTargetPtr(); + if (target) + error_stream = &target->GetDebugger().GetErrorStream(); + + IRForTarget ir_for_target(decl_map, + m_expr.NeedsVariableResolution(), + *m_execution_unit, + error_stream, + function_name.AsCString()); + + bool ir_can_run = ir_for_target.runOnModule(*m_execution_unit->GetModule()); + + Error interpret_error; + + can_interpret = IRInterpreter::CanInterpret(*m_execution_unit->GetModule(), *m_execution_unit->GetFunction(), interpret_error); + + Process *process = exe_ctx.GetProcessPtr(); + + if (!ir_can_run) + { + err.SetErrorString("The expression could not be prepared to run in the target"); + return err; + } + + if (!can_interpret && execution_policy == eExecutionPolicyNever) + { + err.SetErrorStringWithFormat("Can't run the expression locally: %s", interpret_error.AsCString()); + return err; + } + + if (!process && execution_policy == eExecutionPolicyAlways) + { + err.SetErrorString("Expression needed to run in the target, but the target can't be run"); + return err; + } + + if (execution_policy == eExecutionPolicyAlways || !can_interpret) + { + if (m_expr.NeedsValidation() && process) + { + if (!process->GetDynamicCheckers()) + { + DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); + + StreamString install_errors; + + if (!dynamic_checkers->Install(install_errors, exe_ctx)) + { + if (install_errors.GetString().empty()) + err.SetErrorString ("couldn't install checkers, unknown error"); + else + err.SetErrorString (install_errors.GetString().c_str()); + + return err; + } + + process->SetDynamicCheckers(dynamic_checkers); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Finished installing dynamic checkers =="); + } + + IRDynamicChecks ir_dynamic_checks(*process->GetDynamicCheckers(), function_name.AsCString()); + + if (!ir_dynamic_checks.runOnModule(*m_execution_unit->GetModule())) + { + err.SetErrorToGenericError(); + err.SetErrorString("Couldn't add dynamic checks to the expression"); + return err; + } + } + + m_execution_unit->GetRunnableInfo(err, func_addr, func_end); + } + } + else + { + m_execution_unit->GetRunnableInfo(err, func_addr, func_end); + } + + execution_unit_ap.reset (m_execution_unit.release()); + + return err; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangExpressionVariable.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionVariable.cpp new file mode 100644 index 0000000..0d355ce --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangExpressionVariable.cpp @@ -0,0 +1,136 @@ +//===-- ClangExpressionVariable.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangExpressionVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "clang/AST/ASTContext.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb_private; +using namespace clang; + +ClangExpressionVariable::ClangExpressionVariable(ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size) : + m_parser_vars(), + m_jit_vars (), + m_flags (EVNone), + m_frozen_sp (ValueObjectConstResult::Create (exe_scope, byte_order, addr_byte_size)) +{ +} + +ClangExpressionVariable::ClangExpressionVariable (const lldb::ValueObjectSP &valobj_sp) : + m_parser_vars(), + m_jit_vars (), + m_flags (EVNone), + m_frozen_sp (valobj_sp) +{ +} + +//---------------------------------------------------------------------- +/// Return the variable's size in bytes +//---------------------------------------------------------------------- +size_t +ClangExpressionVariable::GetByteSize () +{ + return m_frozen_sp->GetByteSize(); +} + +const ConstString & +ClangExpressionVariable::GetName () +{ + return m_frozen_sp->GetName(); +} + +lldb::ValueObjectSP +ClangExpressionVariable::GetValueObject() +{ + return m_frozen_sp; +} + +RegisterInfo * +ClangExpressionVariable::GetRegisterInfo() +{ + return m_frozen_sp->GetValue().GetRegisterInfo(); +} + +void +ClangExpressionVariable::SetRegisterInfo (const RegisterInfo *reg_info) +{ + return m_frozen_sp->GetValue().SetContext (Value::eContextTypeRegisterInfo, const_cast<RegisterInfo *>(reg_info)); +} + +ClangASTType +ClangExpressionVariable::GetClangType() +{ + return m_frozen_sp->GetClangType(); +} + +void +ClangExpressionVariable::SetClangType(const ClangASTType &clang_type) +{ + m_frozen_sp->GetValue().SetClangType(clang_type); +} + + +TypeFromUser +ClangExpressionVariable::GetTypeFromUser() +{ + TypeFromUser tfu (m_frozen_sp->GetClangType()); + return tfu; +} + +uint8_t * +ClangExpressionVariable::GetValueBytes() +{ + const size_t byte_size = m_frozen_sp->GetByteSize(); + if (byte_size > 0) + { + if (m_frozen_sp->GetDataExtractor().GetByteSize() < byte_size) + { + m_frozen_sp->GetValue().ResizeData(byte_size); + m_frozen_sp->GetValue().GetData (m_frozen_sp->GetDataExtractor()); + } + return const_cast<uint8_t *>(m_frozen_sp->GetDataExtractor().GetDataStart()); + } + return NULL; +} + +void +ClangExpressionVariable::SetName (const ConstString &name) +{ + m_frozen_sp->SetName (name); +} + +void +ClangExpressionVariable::ValueUpdated () +{ + m_frozen_sp->ValueUpdated (); +} + +void +ClangExpressionVariable::TransferAddress (bool force) +{ + if (m_live_sp.get() == NULL) + return; + + if (m_frozen_sp.get() == NULL) + return; + + if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS)) + m_frozen_sp->SetLiveAddress(m_live_sp->GetLiveAddress()); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangFunction.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangFunction.cpp new file mode 100644 index 0000000..177138d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangFunction.cpp @@ -0,0 +1,633 @@ +//===-- ClangFunction.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 +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/Module.h" + +// Project includes +#include "lldb/Expression/ASTStructExtractor.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/State.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.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/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ClangFunction constructor +//---------------------------------------------------------------------- +ClangFunction::ClangFunction +( + ExecutionContextScope &exe_scope, + const ClangASTType &return_type, + const Address& functionAddress, + const ValueList &arg_value_list +) : + m_function_ptr (NULL), + m_function_addr (functionAddress), + m_function_return_type(return_type), + m_wrapper_function_name ("__lldb_caller_function"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_wrapper_args_addrs (), + m_arg_values (arg_value_list), + m_compiled (false), + m_JITted (false) +{ + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a ClangFunction without a process. + assert (m_jit_process_wp.lock()); +} + +ClangFunction::ClangFunction +( + ExecutionContextScope &exe_scope, + Function &function, + ClangASTContext *ast_context, + const ValueList &arg_value_list +) : + m_function_ptr (&function), + m_function_addr (), + m_function_return_type (), + m_clang_ast_context (ast_context), + m_wrapper_function_name ("__lldb_function_caller"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_wrapper_args_addrs (), + m_arg_values (arg_value_list), + m_compiled (false), + m_JITted (false) +{ + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a ClangFunction without a process. + assert (m_jit_process_wp.lock()); + + m_function_addr = m_function_ptr->GetAddressRange().GetBaseAddress(); + m_function_return_type = m_function_ptr->GetClangType().GetFunctionReturnType(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangFunction::~ClangFunction() +{ +} + +unsigned +ClangFunction::CompileFunction (Stream &errors) +{ + if (m_compiled) + return 0; + + // FIXME: How does clang tell us there's no return value? We need to handle that case. + unsigned num_errors = 0; + + std::string return_type_str (m_function_return_type.GetTypeName()); + + // Cons up the function we're going to wrap our call in, then compile it... + // We declare the function "extern "C"" because the compiler might be in C++ + // mode which would mangle the name and then we couldn't find it again... + m_wrapper_function_text.clear(); + m_wrapper_function_text.append ("extern \"C\" void "); + m_wrapper_function_text.append (m_wrapper_function_name); + m_wrapper_function_text.append (" (void *input)\n{\n struct "); + m_wrapper_function_text.append (m_wrapper_struct_name); + m_wrapper_function_text.append (" \n {\n"); + m_wrapper_function_text.append (" "); + m_wrapper_function_text.append (return_type_str); + m_wrapper_function_text.append (" (*fn_ptr) ("); + + // Get the number of arguments. If we have a function type and it is prototyped, + // trust that, otherwise use the values we were given. + + // FIXME: This will need to be extended to handle Variadic functions. We'll need + // to pull the defined arguments out of the function, then add the types from the + // arguments list for the variable arguments. + + uint32_t num_args = UINT32_MAX; + bool trust_function = false; + // GetArgumentCount returns -1 for an unprototyped function. + ClangASTType function_clang_type; + if (m_function_ptr) + { + function_clang_type = m_function_ptr->GetClangType(); + if (function_clang_type) + { + int num_func_args = function_clang_type.GetFunctionArgumentCount(); + if (num_func_args >= 0) + { + trust_function = true; + num_args = num_func_args; + } + } + } + + if (num_args == UINT32_MAX) + num_args = m_arg_values.GetSize(); + + std::string args_buffer; // This one stores the definition of all the args in "struct caller". + std::string args_list_buffer; // This one stores the argument list called from the structure. + for (size_t i = 0; i < num_args; i++) + { + std::string type_name; + + if (trust_function) + { + type_name = function_clang_type.GetFunctionArgumentTypeAtIndex(i).GetTypeName(); + } + else + { + ClangASTType clang_qual_type = m_arg_values.GetValueAtIndex(i)->GetClangType (); + if (clang_qual_type) + { + type_name = clang_qual_type.GetTypeName(); + } + else + { + errors.Printf("Could not determine type of input value %lu.", i); + return 1; + } + } + + m_wrapper_function_text.append (type_name); + if (i < num_args - 1) + m_wrapper_function_text.append (", "); + + char arg_buf[32]; + args_buffer.append (" "); + args_buffer.append (type_name); + snprintf(arg_buf, 31, "arg_%" PRIu64, (uint64_t)i); + args_buffer.push_back (' '); + args_buffer.append (arg_buf); + args_buffer.append (";\n"); + + args_list_buffer.append ("__lldb_fn_data->"); + args_list_buffer.append (arg_buf); + if (i < num_args - 1) + args_list_buffer.append (", "); + + } + m_wrapper_function_text.append (");\n"); // Close off the function calling prototype. + + m_wrapper_function_text.append (args_buffer); + + m_wrapper_function_text.append (" "); + m_wrapper_function_text.append (return_type_str); + m_wrapper_function_text.append (" return_value;"); + m_wrapper_function_text.append ("\n };\n struct "); + m_wrapper_function_text.append (m_wrapper_struct_name); + m_wrapper_function_text.append ("* __lldb_fn_data = (struct "); + m_wrapper_function_text.append (m_wrapper_struct_name); + m_wrapper_function_text.append (" *) input;\n"); + + m_wrapper_function_text.append (" __lldb_fn_data->return_value = __lldb_fn_data->fn_ptr ("); + m_wrapper_function_text.append (args_list_buffer); + m_wrapper_function_text.append (");\n}\n"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + if (log) + log->Printf ("Expression: \n\n%s\n\n", m_wrapper_function_text.c_str()); + + // Okay, now compile this expression + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + if (jit_process_sp) + { + m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this)); + + num_errors = m_parser->Parse (errors); + } + else + { + errors.Printf("no process - unable to inject function"); + num_errors = 1; + } + + m_compiled = (num_errors == 0); + + if (!m_compiled) + return num_errors; + + return num_errors; +} + +bool +ClangFunction::WriteFunctionWrapper (ExecutionContext &exe_ctx, Stream &errors) +{ + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) + return false; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + if (!m_compiled) + return false; + + if (m_JITted) + return true; + + bool can_interpret = false; // should stay that way + + Error jit_error (m_parser->PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + can_interpret, + eExecutionPolicyAlways)); + + if (!jit_error.Success()) + return false; + + if (process && m_jit_start_addr) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + + m_JITted = true; + + return true; +} + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, Stream &errors) +{ + return WriteFunctionArguments(exe_ctx, args_addr_ref, m_function_addr, m_arg_values, errors); +} + +// FIXME: Assure that the ValueList we were passed in is consistent with the one that defined this function. + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Address function_address, + ValueList &arg_values, + Stream &errors) +{ + // All the information to reconstruct the struct is provided by the + // StructExtractor. + if (!m_struct_valid) + { + errors.Printf("Argument information was not correctly parsed, so the function cannot be called."); + return false; + } + + Error error; + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL) + return return_value; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + if (args_addr_ref == LLDB_INVALID_ADDRESS) + { + args_addr_ref = process->AllocateMemory(m_struct_size, lldb::ePermissionsReadable|lldb::ePermissionsWritable, error); + if (args_addr_ref == LLDB_INVALID_ADDRESS) + return false; + m_wrapper_args_addrs.push_back (args_addr_ref); + } + else + { + // Make sure this is an address that we've already handed out. + if (find (m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(), args_addr_ref) == m_wrapper_args_addrs.end()) + { + return false; + } + } + + // TODO: verify fun_addr needs to be a callable address + Scalar fun_addr (function_address.GetCallableLoadAddress(exe_ctx.GetTargetPtr())); + uint64_t first_offset = m_member_offsets[0]; + process->WriteScalarToMemory(args_addr_ref + first_offset, fun_addr, process->GetAddressByteSize(), error); + + // FIXME: We will need to extend this for Variadic functions. + + Error value_error; + + size_t num_args = arg_values.GetSize(); + if (num_args != m_arg_values.GetSize()) + { + errors.Printf ("Wrong number of arguments - was: %lu should be: %lu", num_args, m_arg_values.GetSize()); + return false; + } + + for (size_t i = 0; i < num_args; i++) + { + // FIXME: We should sanity check sizes. + + uint64_t offset = m_member_offsets[i+1]; // Clang sizes are in bytes. + Value *arg_value = arg_values.GetValueAtIndex(i); + + // FIXME: For now just do scalars: + + // Special case: if it's a pointer, don't do anything (the ABI supports passing cstrings) + + if (arg_value->GetValueType() == Value::eValueTypeHostAddress && + arg_value->GetContextType() == Value::eContextTypeInvalid && + arg_value->GetClangType().IsPointerType()) + continue; + + const Scalar &arg_scalar = arg_value->ResolveValue(&exe_ctx); + + if (!process->WriteScalarToMemory(args_addr_ref + offset, arg_scalar, arg_scalar.GetByteSize(), error)) + return false; + } + + return true; +} + +bool +ClangFunction::InsertFunction (ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, Stream &errors) +{ + using namespace clang; + + if (CompileFunction(errors) != 0) + return false; + if (!WriteFunctionWrapper(exe_ctx, errors)) + return false; + if (!WriteFunctionArguments(exe_ctx, args_addr_ref, errors)) + return false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("Call Address: 0x%" PRIx64 " Struct Address: 0x%" PRIx64 ".\n", m_jit_start_addr, args_addr_ref); + + return true; +} + +ThreadPlan * +ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t func_addr, + lldb::addr_t &args_addr, + Stream &errors, + bool stop_others, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg, + lldb::addr_t *cmd_arg) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("-- [ClangFunction::GetThreadPlanToCallFunction] Creating thread plan to call function --"); + + // FIXME: Use the errors Stream for better error reporting. + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == NULL) + { + errors.Printf("Can't call a function without a valid thread."); + return NULL; + } + + // Okay, now run the function: + + Address wrapper_address (func_addr); + ThreadPlan *new_plan = new ThreadPlanCallFunction (*thread, + wrapper_address, + ClangASTType(), + args_addr, + stop_others, + unwind_on_error, + ignore_breakpoints, + this_arg, + cmd_arg); + new_plan->SetIsMasterPlan(true); + new_plan->SetOkayToDiscard (false); + return new_plan; +} + +bool +ClangFunction::FetchFunctionResults (ExecutionContext &exe_ctx, lldb::addr_t args_addr, Value &ret_value) +{ + // Read the return value - it is the last field in the struct: + // FIXME: How does clang tell us there's no return value? We need to handle that case. + // FIXME: Create our ThreadPlanCallFunction with the return ClangASTType, and then use GetReturnValueObject + // to fetch the value. That way we can fetch any values we need. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("-- [ClangFunction::FetchFunctionResults] Fetching function results --"); + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL) + return false; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + Error error; + ret_value.GetScalar() = process->ReadUnsignedIntegerFromMemory (args_addr + m_return_offset, m_return_size, 0, error); + + if (error.Fail()) + return false; + + ret_value.SetClangType(m_function_return_type); + ret_value.SetValueType(Value::eValueTypeScalar); + return true; +} + +void +ClangFunction::DeallocateFunctionResults (ExecutionContext &exe_ctx, lldb::addr_t args_addr) +{ + std::list<lldb::addr_t>::iterator pos; + pos = std::find(m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(), args_addr); + if (pos != m_wrapper_args_addrs.end()) + m_wrapper_args_addrs.erase(pos); + + exe_ctx.GetProcessRef().DeallocateMemory(args_addr); +} + +ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, Value &results) +{ + return ExecuteFunction (exe_ctx, errors, 1000, true, results); +} + +ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, bool stop_others, Value &results) +{ + const bool try_all_threads = false; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, 0UL, try_all_threads, + unwind_on_error, ignore_breakpoints, results); +} + +ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exe_ctx, + Stream &errors, + uint32_t timeout_usec, + bool try_all_threads, + Value &results) +{ + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, timeout_usec, + try_all_threads, unwind_on_error, ignore_breakpoints, results); +} + +// This is the static function +ExecutionResults +ClangFunction::ExecuteFunction ( + ExecutionContext &exe_ctx, + lldb::addr_t function_address, + lldb::addr_t &void_arg, + bool stop_others, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors, + lldb::addr_t *this_arg) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("== [ClangFunction::ExecuteFunction] Executing function =="); + + lldb::ThreadPlanSP call_plan_sp (ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + function_address, + void_arg, + errors, + stop_others, + unwind_on_error, + ignore_breakpoints, + this_arg)); + if (!call_plan_sp) + return eExecutionSetupError; + + // <rdar://problem/12027563> we need to make sure we record the fact that we are running an expression here + // otherwise this fact will fail to be recorded when fetching an Objective-C object description + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + ExecutionResults results = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, call_plan_sp, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + errors); + + if (log) + { + if (results != eExecutionCompleted) + { + log->Printf("== [ClangFunction::ExecuteFunction] Execution completed abnormally =="); + } + else + { + log->Printf("== [ClangFunction::ExecuteFunction] Execution completed normally =="); + } + } + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + return results; +} + +ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exe_ctx, + lldb::addr_t *args_addr_ptr, + Stream &errors, + bool stop_others, + uint32_t timeout_usec, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + Value &results) +{ + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + lldb::addr_t args_addr; + + if (args_addr_ptr != NULL) + args_addr = *args_addr_ptr; + else + args_addr = LLDB_INVALID_ADDRESS; + + if (CompileFunction(errors) != 0) + return eExecutionSetupError; + + if (args_addr == LLDB_INVALID_ADDRESS) + { + if (!InsertFunction(exe_ctx, args_addr, errors)) + return eExecutionSetupError; + } + + return_value = ClangFunction::ExecuteFunction (exe_ctx, + m_jit_start_addr, + args_addr, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + errors); + + if (args_addr_ptr != NULL) + *args_addr_ptr = args_addr; + + if (return_value != eExecutionCompleted) + return return_value; + + FetchFunctionResults(exe_ctx, args_addr, results); + + if (args_addr_ptr == NULL) + DeallocateFunctionResults(exe_ctx, args_addr); + + return eExecutionCompleted; +} + +clang::ASTConsumer * +ClangFunction::ASTTransformer (clang::ASTConsumer *passthrough) +{ + return new ASTStructExtractor(passthrough, m_wrapper_struct_name.c_str(), *this); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangPersistentVariables.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangPersistentVariables.cpp new file mode 100644 index 0000000..db062d2 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangPersistentVariables.cpp @@ -0,0 +1,89 @@ +//===-- ClangPersistentVariables.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" + +#include "llvm/ADT/StringMap.h" + +using namespace lldb; +using namespace lldb_private; + +ClangPersistentVariables::ClangPersistentVariables () : + ClangExpressionVariableList(), + m_next_persistent_variable_id (0) +{ +} + +ClangExpressionVariableSP +ClangPersistentVariables::CreatePersistentVariable (const lldb::ValueObjectSP &valobj_sp) +{ + ClangExpressionVariableSP var_sp (CreateVariable(valobj_sp)); + return var_sp; +} + +ClangExpressionVariableSP +ClangPersistentVariables::CreatePersistentVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size) +{ + ClangExpressionVariableSP var_sp (GetVariable(name)); + + if (!var_sp) + var_sp = CreateVariable(exe_scope, name, user_type, byte_order, addr_byte_size); + + return var_sp; +} + +void +ClangPersistentVariables::RemovePersistentVariable (lldb::ClangExpressionVariableSP variable) +{ + RemoveVariable(variable); + + const char *name = variable->GetName().AsCString(); + + if (*name != '$') + return; + name++; + + if (strtoul(name, NULL, 0) == m_next_persistent_variable_id - 1) + m_next_persistent_variable_id--; +} + +ConstString +ClangPersistentVariables::GetNextPersistentVariableName () +{ + char name_cstr[256]; + ::snprintf (name_cstr, sizeof(name_cstr), "$%u", m_next_persistent_variable_id++); + ConstString name(name_cstr); + return name; +} + +void +ClangPersistentVariables::RegisterPersistentType (const ConstString &name, + clang::TypeDecl *type_decl) +{ + m_persistent_types.insert(std::pair<const char*, clang::TypeDecl*>(name.GetCString(), type_decl)); +} + +clang::TypeDecl * +ClangPersistentVariables::GetPersistentType (const ConstString &name) +{ + PersistentTypeMap::const_iterator i = m_persistent_types.find(name.GetCString()); + + if (i == m_persistent_types.end()) + return NULL; + else + return i->second; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangUserExpression.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangUserExpression.cpp new file mode 100644 index 0000000..7f09f6f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangUserExpression.cpp @@ -0,0 +1,1091 @@ +//===-- ClangUserExpression.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 +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +// C++ Includes +#include <cstdlib> +#include <string> +#include <map> + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/ASTResultSynthesizer.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallUserExpression.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + +using namespace lldb_private; + +ClangUserExpression::ClangUserExpression (const char *expr, + const char *expr_prefix, + lldb::LanguageType language, + ResultType desired_type) : + ClangExpression (), + m_stack_frame_bottom (LLDB_INVALID_ADDRESS), + m_stack_frame_top (LLDB_INVALID_ADDRESS), + m_expr_text (expr), + m_expr_prefix (expr_prefix ? expr_prefix : ""), + m_language (language), + m_transformed_text (), + m_desired_type (desired_type), + m_enforce_valid_object (true), + m_cplusplus (false), + m_objectivec (false), + m_static_method(false), + m_needs_object_ptr (false), + m_const_object (false), + m_target (NULL), + m_can_interpret (false), + m_materialized_address (LLDB_INVALID_ADDRESS) +{ + switch (m_language) + { + case lldb::eLanguageTypeC_plus_plus: + m_allow_cxx = true; + break; + case lldb::eLanguageTypeObjC: + m_allow_objc = true; + break; + case lldb::eLanguageTypeObjC_plus_plus: + default: + m_allow_cxx = true; + m_allow_objc = true; + break; + } +} + +ClangUserExpression::~ClangUserExpression () +{ +} + +clang::ASTConsumer * +ClangUserExpression::ASTTransformer (clang::ASTConsumer *passthrough) +{ + ClangASTContext *clang_ast_context = m_target->GetScratchClangASTContext(); + + if (!clang_ast_context) + return NULL; + + if (!m_result_synthesizer.get()) + m_result_synthesizer.reset(new ASTResultSynthesizer(passthrough, + *m_target)); + + return m_result_synthesizer.get(); +} + +void +ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &err) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangUserExpression::ScanContext()"); + + m_target = exe_ctx.GetTargetPtr(); + + if (!(m_allow_cxx || m_allow_objc)) + { + if (log) + log->Printf(" [CUE::SC] Settings inhibit C++ and Objective-C"); + return; + } + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == NULL) + { + if (log) + log->Printf(" [CUE::SC] Null stack frame"); + return; + } + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); + + if (!sym_ctx.function) + { + if (log) + log->Printf(" [CUE::SC] Null function"); + return; + } + + // Find the block that defines the function represented by "sym_ctx" + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + { + if (log) + log->Printf(" [CUE::SC] Null function block"); + return; + } + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + { + if (log) + log->Printf(" [CUE::SC] Null decl context"); + return; + } + + if (clang::CXXMethodDecl *method_decl = llvm::dyn_cast<clang::CXXMethodDecl>(decl_context)) + { + if (m_allow_cxx && method_decl->isInstance()) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *thisErrorString = "Stopped in a C++ method, but 'this' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(thisErrorString); + return; + } + + lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this"))); + + if (!this_var_sp || + !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(thisErrorString); + return; + } + } + + m_cplusplus = true; + m_needs_object_ptr = true; + } + } + else if (clang::ObjCMethodDecl *method_decl = llvm::dyn_cast<clang::ObjCMethodDecl>(decl_context)) + { + if (m_allow_objc) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *selfErrorString = "Stopped in an Objective-C method, but 'self' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(selfErrorString); + return; + } + + lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self")); + + if (!self_variable_sp || + !self_variable_sp->IsInScope(frame) || + !self_variable_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(selfErrorString); + return; + } + } + + m_objectivec = true; + m_needs_object_ptr = true; + + if (!method_decl->isInstanceMethod()) + m_static_method = true; + } + } + else if (clang::FunctionDecl *function_decl = llvm::dyn_cast<clang::FunctionDecl>(decl_context)) + { + // We might also have a function that said in the debug information that it captured an + // object pointer. The best way to deal with getting to the ivars at present it by pretending + // that this is a method of a class in whatever runtime the debug info says the object pointer + // belongs to. Do that here. + + ClangASTMetadata *metadata = ClangASTContext::GetMetadata (&decl_context->getParentASTContext(), function_decl); + if (metadata && metadata->HasObjectPtr()) + { + lldb::LanguageType language = metadata->GetObjectPtrLanguage(); + if (language == lldb::eLanguageTypeC_plus_plus) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *thisErrorString = "Stopped in a context claiming to capture a C++ object pointer, but 'this' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(thisErrorString); + return; + } + + lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this"))); + + if (!this_var_sp || + !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(thisErrorString); + return; + } + } + + m_cplusplus = true; + m_needs_object_ptr = true; + } + else if (language == lldb::eLanguageTypeObjC) + { + if (m_enforce_valid_object) + { + lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true)); + + const char *selfErrorString = "Stopped in a context claiming to capture an Objective-C object pointer, but 'self' isn't available; pretending we are in a generic context"; + + if (!variable_list_sp) + { + err.SetErrorString(selfErrorString); + return; + } + + lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self")); + + if (!self_variable_sp || + !self_variable_sp->IsInScope(frame) || + !self_variable_sp->LocationIsValidForFrame (frame)) + { + err.SetErrorString(selfErrorString); + return; + } + + Type *self_type = self_variable_sp->GetType(); + + if (!self_type) + { + err.SetErrorString(selfErrorString); + return; + } + + ClangASTType self_clang_type = self_type->GetClangForwardType(); + + if (!self_clang_type) + { + err.SetErrorString(selfErrorString); + return; + } + + if (self_clang_type.IsObjCClassType()) + { + return; + } + else if (self_clang_type.IsObjCObjectPointerType()) + { + m_objectivec = true; + m_needs_object_ptr = true; + } + else + { + err.SetErrorString(selfErrorString); + return; + } + } + else + { + m_objectivec = true; + m_needs_object_ptr = true; + } + } + } + } +} + +void +ClangUserExpression::InstallContext (ExecutionContext &exe_ctx) +{ + m_process_wp = exe_ctx.GetProcessSP(); + + lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP(); + + if (frame_sp) + m_address = frame_sp->GetFrameCodeAddress(); +} + +bool +ClangUserExpression::LockAndCheckContext (ExecutionContext &exe_ctx, + lldb::TargetSP &target_sp, + lldb::ProcessSP &process_sp, + lldb::StackFrameSP &frame_sp) +{ + lldb::ProcessSP expected_process_sp = m_process_wp.lock(); + process_sp = exe_ctx.GetProcessSP(); + + if (process_sp != expected_process_sp) + return false; + + process_sp = exe_ctx.GetProcessSP(); + target_sp = exe_ctx.GetTargetSP(); + frame_sp = exe_ctx.GetFrameSP(); + + if (m_address.IsValid()) + { + if (!frame_sp) + return false; + else + return (0 == Address::CompareLoadAddress(m_address, frame_sp->GetFrameCodeAddress(), target_sp.get())); + } + + return true; +} + +bool +ClangUserExpression::MatchesContext (ExecutionContext &exe_ctx) +{ + lldb::TargetSP target_sp; + lldb::ProcessSP process_sp; + lldb::StackFrameSP frame_sp; + + return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp); +} + +// This is a really nasty hack, meant to fix Objective-C expressions of the form +// (int)[myArray count]. Right now, because the type information for count is +// not available, [myArray count] returns id, which can't be directly cast to +// int without causing a clang error. +static void +ApplyObjcCastHack(std::string &expr) +{ +#define OBJC_CAST_HACK_FROM "(int)[" +#define OBJC_CAST_HACK_TO "(int)(long long)[" + + size_t from_offset; + + while ((from_offset = expr.find(OBJC_CAST_HACK_FROM)) != expr.npos) + expr.replace(from_offset, sizeof(OBJC_CAST_HACK_FROM) - 1, OBJC_CAST_HACK_TO); + +#undef OBJC_CAST_HACK_TO +#undef OBJC_CAST_HACK_FROM +} + +// Another hack, meant to allow use of unichar despite it not being available in +// the type information. Although we could special-case it in type lookup, +// hopefully we'll figure out a way to #include the same environment as is +// present in the original source file rather than try to hack specific type +// definitions in as needed. +static void +ApplyUnicharHack(std::string &expr) +{ +#define UNICHAR_HACK_FROM "unichar" +#define UNICHAR_HACK_TO "unsigned short" + + size_t from_offset; + + while ((from_offset = expr.find(UNICHAR_HACK_FROM)) != expr.npos) + expr.replace(from_offset, sizeof(UNICHAR_HACK_FROM) - 1, UNICHAR_HACK_TO); + +#undef UNICHAR_HACK_TO +#undef UNICHAR_HACK_FROM +} + +bool +ClangUserExpression::Parse (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Error err; + + InstallContext(exe_ctx); + + ScanContext(exe_ctx, err); + + if (!err.Success()) + { + error_stream.Printf("warning: %s\n", err.AsCString()); + } + + StreamString m_transformed_stream; + + //////////////////////////////////// + // Generate the expression + // + + ApplyObjcCastHack(m_expr_text); + //ApplyUnicharHack(m_expr_text); + + std::unique_ptr<ExpressionSourceCode> source_code (ExpressionSourceCode::CreateWrapped(m_expr_prefix.c_str(), m_expr_text.c_str())); + + lldb::LanguageType lang_type; + + if (m_cplusplus) + lang_type = lldb::eLanguageTypeC_plus_plus; + else if(m_objectivec) + lang_type = lldb::eLanguageTypeObjC; + else + lang_type = lldb::eLanguageTypeC; + + if (!source_code->GetText(m_transformed_text, lang_type, m_const_object, m_static_method)) + { + error_stream.PutCString ("error: couldn't construct expression body"); + return false; + } + + if (log) + log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str()); + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + { + error_stream.PutCString ("error: invalid target\n"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + m_materializer_ap.reset(new Materializer()); + + m_expr_decl_map.reset(new ClangExpressionDeclMap(keep_result_in_memory, exe_ctx)); + + class OnExit + { + public: + typedef std::function <void (void)> Callback; + + OnExit (Callback const &callback) : + m_callback(callback) + { + } + + ~OnExit () + { + m_callback(); + } + private: + Callback m_callback; + }; + + OnExit on_exit([this]() { m_expr_decl_map.reset(); }); + + if (!m_expr_decl_map->WillParse(exe_ctx, m_materializer_ap.get())) + { + error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + ExecutionContextScope *exe_scope = process; + + if (!exe_scope) + exe_scope = exe_ctx.GetTargetPtr(); + + ClangExpressionParser parser(exe_scope, *this); + + unsigned num_errors = parser.Parse (error_stream); + + if (num_errors) + { + error_stream.Printf ("error: %d errors parsing expression\n", num_errors); + + m_expr_decl_map->DidParse(); + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Prepare the output of the parser for execution, evaluating it statically if possible + // + + Error jit_error = parser.PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + m_can_interpret, + execution_policy); + + if (jit_error.Success()) + { + if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + return true; + } + else + { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + error_stream.Printf ("error: %s\n", error_cstr); + else + error_stream.Printf ("error: expression can't be interpreted or run\n"); + return false; + } +} + +static lldb::addr_t +GetObjectPointer (lldb::StackFrameSP frame_sp, + ConstString &object_name, + Error &err) +{ + err.Clear(); + + if (!frame_sp) + { + err.SetErrorStringWithFormat("Couldn't load '%s' because the context is incomplete", object_name.AsCString()); + return LLDB_INVALID_ADDRESS; + } + + lldb::VariableSP var_sp; + lldb::ValueObjectSP valobj_sp; + + valobj_sp = frame_sp->GetValueForVariableExpressionPath(object_name.AsCString(), + lldb::eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember || + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || + StackFrame::eExpressionPathOptionsNoFragileObjcIvar || + StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var_sp, + err); + + if (!err.Success()) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + if (ret == LLDB_INVALID_ADDRESS) + { + err.SetErrorStringWithFormat("Couldn't load '%s' because its value couldn't be evaluated", object_name.AsCString()); + return LLDB_INVALID_ADDRESS; + } + + return ret; +} + +bool +ClangUserExpression::PrepareToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::addr_t &struct_address, + lldb::addr_t &object_ptr, + lldb::addr_t &cmd_ptr) +{ + lldb::TargetSP target; + lldb::ProcessSP process; + lldb::StackFrameSP frame; + + if (!LockAndCheckContext(exe_ctx, + target, + process, + frame)) + { + error_stream.Printf("The context has changed before we could JIT the expression!\n"); + return false; + } + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) + { + if (m_needs_object_ptr) + { + ConstString object_name; + + if (m_cplusplus) + { + object_name.SetCString("this"); + } + else if (m_objectivec) + { + object_name.SetCString("self"); + } + else + { + error_stream.Printf("Need object pointer but don't know the language\n"); + return false; + } + + Error object_ptr_error; + + object_ptr = GetObjectPointer(frame, object_name, object_ptr_error); + + if (!object_ptr_error.Success()) + { + error_stream.Printf("warning: couldn't get required object pointer (substituting NULL): %s\n", object_ptr_error.AsCString()); + object_ptr = 0; + } + + if (m_objectivec) + { + ConstString cmd_name("_cmd"); + + cmd_ptr = GetObjectPointer(frame, cmd_name, object_ptr_error); + + if (!object_ptr_error.Success()) + { + error_stream.Printf("warning: couldn't get cmd pointer (substituting NULL): %s\n", object_ptr_error.AsCString()); + cmd_ptr = 0; + } + } + } + + if (m_materialized_address == LLDB_INVALID_ADDRESS) + { + Error alloc_error; + + IRMemoryMap::AllocationPolicy policy = m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly : IRMemoryMap::eAllocationPolicyMirror; + + m_materialized_address = m_execution_unit_ap->Malloc(m_materializer_ap->GetStructByteSize(), + m_materializer_ap->GetStructAlignment(), + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + policy, + alloc_error); + + if (!alloc_error.Success()) + { + error_stream.Printf("Couldn't allocate space for materialized struct: %s\n", alloc_error.AsCString()); + return false; + } + } + + struct_address = m_materialized_address; + + if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) + { + Error alloc_error; + + const size_t stack_frame_size = 512 * 1024; + + m_stack_frame_bottom = m_execution_unit_ap->Malloc(stack_frame_size, + 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyHostOnly, + alloc_error); + + m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; + + if (!alloc_error.Success()) + { + error_stream.Printf("Couldn't allocate space for the stack frame: %s\n", alloc_error.AsCString()); + return false; + } + } + + Error materialize_error; + + m_dematerializer_sp = m_materializer_ap->Materialize(frame, *m_execution_unit_ap, struct_address, materialize_error); + + if (!materialize_error.Success()) + { + error_stream.Printf("Couldn't materialize struct: %s\n", materialize_error.AsCString()); + return false; + } + } + return true; +} + +ThreadPlan * +ClangUserExpression::GetThreadPlanToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx) +{ + lldb::addr_t struct_address; + + lldb::addr_t object_ptr = 0; + lldb::addr_t cmd_ptr = 0; + + PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr, cmd_ptr); + + // FIXME: This should really return a ThreadPlanCallUserExpression, in order to make sure that we don't release the + // ClangUserExpression resources before the thread plan finishes execution in the target. But because we are + // forcing unwind_on_error to be true here, in practical terms that can't happen. + + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = false; + return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + m_jit_start_addr, + struct_address, + error_stream, + stop_others, + unwind_on_error, + ignore_breakpoints, + (m_needs_object_ptr ? &object_ptr : NULL), + (m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL); +} + +bool +ClangUserExpression::FinalizeJITExecution (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::ClangExpressionVariableSP &result, + lldb::addr_t function_stack_bottom, + lldb::addr_t function_stack_top) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("-- [ClangUserExpression::FinalizeJITExecution] Dematerializing after execution --"); + + if (!m_dematerializer_sp) + { + error_stream.Printf ("Couldn't apply expression side effects : no dematerializer is present"); + return false; + } + + Error dematerialize_error; + + m_dematerializer_sp->Dematerialize(dematerialize_error, result, function_stack_bottom, function_stack_top); + + if (!dematerialize_error.Success()) + { + error_stream.Printf ("Couldn't apply expression side effects : %s\n", dematerialize_error.AsCString("unknown error")); + return false; + } + + if (result) + result->TransferAddress(); + + m_dematerializer_sp.reset(); + + return true; +} + +ExecutionResults +ClangUserExpression::Execute (Stream &error_stream, + ExecutionContext &exe_ctx, + bool unwind_on_error, + bool ignore_breakpoints, + ClangUserExpression::ClangUserExpressionSP &shared_ptr_to_me, + lldb::ClangExpressionVariableSP &result, + bool run_others, + uint32_t timeout_usec) +{ + // The expression log is quite verbose, and if you're just tracking the execution of the + // expression, it's quite convenient to have these logs come out with the STEP log as well. + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) + { + lldb::addr_t struct_address = LLDB_INVALID_ADDRESS; + + lldb::addr_t object_ptr = 0; + lldb::addr_t cmd_ptr = 0; + + if (!PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr, cmd_ptr)) + { + error_stream.Printf("Errored out in %s, couldn't PrepareToExecuteJITExpression", __FUNCTION__); + return eExecutionSetupError; + } + + lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS; + lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS; + + if (m_can_interpret) + { + llvm::Module *module = m_execution_unit_ap->GetModule(); + llvm::Function *function = m_execution_unit_ap->GetFunction(); + + if (!module || !function) + { + error_stream.Printf("Supposed to interpret, but nothing is there"); + return eExecutionSetupError; + } + + Error interpreter_error; + + llvm::SmallVector <lldb::addr_t, 3> args; + + if (m_needs_object_ptr) + { + args.push_back(object_ptr); + + if (m_objectivec) + args.push_back(cmd_ptr); + } + + args.push_back(struct_address); + + function_stack_bottom = m_stack_frame_bottom; + function_stack_top = m_stack_frame_top; + + IRInterpreter::Interpret (*module, + *function, + args, + *m_execution_unit_ap.get(), + interpreter_error, + function_stack_bottom, + function_stack_top); + + if (!interpreter_error.Success()) + { + error_stream.Printf("Supposed to interpret, but failed: %s", interpreter_error.AsCString()); + return eExecutionDiscarded; + } + } + else + { + const bool stop_others = true; + const bool try_all_threads = run_others; + + Address wrapper_address (m_jit_start_addr); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(), + wrapper_address, + struct_address, + stop_others, + unwind_on_error, + ignore_breakpoints, + (m_needs_object_ptr ? &object_ptr : NULL), + ((m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL), + shared_ptr_to_me)); + + if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream)) + return eExecutionSetupError; + + lldb::addr_t function_stack_pointer = static_cast<ThreadPlanCallFunction *>(call_plan_sp.get())->GetFunctionStackPointer(); + + function_stack_bottom = function_stack_pointer - Host::GetPageSize(); + function_stack_top = function_stack_pointer; + + if (log) + log->Printf("-- [ClangUserExpression::Execute] Execution of expression begins --"); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + ExecutionResults execution_result = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, + call_plan_sp, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + error_stream); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + if (log) + log->Printf("-- [ClangUserExpression::Execute] Execution of expression completed --"); + + if (execution_result == eExecutionInterrupted || execution_result == eExecutionHitBreakpoint) + { + const char *error_desc = NULL; + + if (call_plan_sp) + { + lldb::StopInfoSP real_stop_info_sp = call_plan_sp->GetRealStopInfo(); + if (real_stop_info_sp) + error_desc = real_stop_info_sp->GetDescription(); + } + if (error_desc) + error_stream.Printf ("Execution was interrupted, reason: %s.", error_desc); + else + error_stream.Printf ("Execution was interrupted."); + + if ((execution_result == eExecutionInterrupted && unwind_on_error) + || (execution_result == eExecutionHitBreakpoint && ignore_breakpoints)) + error_stream.Printf ("\nThe process has been returned to the state before expression evaluation."); + else + error_stream.Printf ("\nThe process has been left at the point where it was interrupted, use \"thread return -x\" to return to the state before expression evaluation."); + + return execution_result; + } + else if (execution_result != eExecutionCompleted) + { + error_stream.Printf ("Couldn't execute function; result was %s\n", Process::ExecutionResultAsCString (execution_result)); + return execution_result; + } + } + + if (FinalizeJITExecution (error_stream, exe_ctx, result, function_stack_bottom, function_stack_top)) + { + return eExecutionCompleted; + } + else + { + return eExecutionSetupError; + } + } + else + { + error_stream.Printf("Expression can't be run, because there is no JIT compiled function"); + return eExecutionSetupError; + } +} + +ExecutionResults +ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + bool run_others, + uint32_t timeout_usec) +{ + Error error; + return EvaluateWithError (exe_ctx, + execution_policy, + language, + desired_type, + unwind_on_error, + ignore_breakpoints, + expr_cstr, + expr_prefix, + result_valobj_sp, + error, + run_others, + timeout_usec); +} + +ExecutionResults +ClangUserExpression::EvaluateWithError (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + Error &error, + bool run_others, + uint32_t timeout_usec) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + ExecutionResults execution_results = eExecutionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL || process->GetState() != lldb::eStateStopped) + { + if (execution_policy == eExecutionPolicyAlways) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Expression may not run, but is not constant =="); + + error.SetErrorString ("expression needed to run but couldn't"); + + return execution_results; + } + } + + if (process == NULL || !process->CanJIT()) + execution_policy = eExecutionPolicyNever; + + ClangUserExpressionSP user_expression_sp (new ClangUserExpression (expr_cstr, expr_prefix, language, desired_type)); + + StreamString error_stream; + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Parsing expression %s ==", expr_cstr); + + const bool keep_expression_in_memory = true; + + if (!user_expression_sp->Parse (error_stream, exe_ctx, execution_policy, keep_expression_in_memory)) + { + if (error_stream.GetString().empty()) + error.SetErrorString ("expression failed to parse, unknown error"); + else + error.SetErrorString (error_stream.GetString().c_str()); + } + else + { + lldb::ClangExpressionVariableSP expr_result; + + if (execution_policy == eExecutionPolicyNever && + !user_expression_sp->CanInterpret()) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Expression may not run, but is not constant =="); + + if (error_stream.GetString().empty()) + error.SetErrorString ("expression needed to run but couldn't"); + } + else + { + error_stream.GetString().clear(); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Executing expression =="); + + execution_results = user_expression_sp->Execute (error_stream, + exe_ctx, + unwind_on_error, + ignore_breakpoints, + user_expression_sp, + expr_result, + run_others, + timeout_usec); + + if (execution_results != eExecutionCompleted) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed abnormally =="); + + if (error_stream.GetString().empty()) + error.SetErrorString ("expression failed to execute, unknown error"); + else + error.SetErrorString (error_stream.GetString().c_str()); + } + else + { + if (expr_result) + { + result_valobj_sp = expr_result->GetValueObject(); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed normally with result %s ==", result_valobj_sp->GetValueAsCString()); + } + else + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed normally with no result =="); + + error.SetError(ClangUserExpression::kNoResult, lldb::eErrorTypeGeneric); + } + } + } + } + + if (result_valobj_sp.get() == NULL) + result_valobj_sp = ValueObjectConstResult::Create (NULL, error); + + return execution_results; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/ClangUtilityFunction.cpp b/contrib/llvm/tools/lldb/source/Expression/ClangUtilityFunction.cpp new file mode 100644 index 0000000..c911c27 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ClangUtilityFunction.cpp @@ -0,0 +1,169 @@ +//===-- ClangUserExpression.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 +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +// C++ Includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangUtilityFunction.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +//------------------------------------------------------------------ +/// Constructor +/// +/// @param[in] text +/// The text of the function. Must be a full translation unit. +/// +/// @param[in] name +/// The name of the function, as used in the text. +//------------------------------------------------------------------ +ClangUtilityFunction::ClangUtilityFunction (const char *text, + const char *name) : + ClangExpression (), + m_function_text (ExpressionSourceCode::g_expression_prefix), + m_function_name (name) +{ + if (text && text[0]) + m_function_text.append (text); +} + +ClangUtilityFunction::~ClangUtilityFunction () +{ +} + +//------------------------------------------------------------------ +/// Install the utility function into a process +/// +/// @param[in] error_stream +/// A stream to print parse errors and warnings to. +/// +/// @param[in] exe_ctx +/// The execution context to install the utility function to. +/// +/// @return +/// True on success (no errors); false otherwise. +//------------------------------------------------------------------ +bool +ClangUtilityFunction::Install (Stream &error_stream, + ExecutionContext &exe_ctx) +{ + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) + { + error_stream.PutCString("error: already installed\n"); + return false; + } + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + { + error_stream.PutCString ("error: invalid target\n"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) + { + error_stream.PutCString ("error: invalid process\n"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + bool keep_result_in_memory = false; + + m_expr_decl_map.reset(new ClangExpressionDeclMap(keep_result_in_memory, exe_ctx)); + + if (!m_expr_decl_map->WillParse(exe_ctx, NULL)) + { + error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n"); + return false; + } + + ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this); + + unsigned num_errors = parser.Parse (error_stream); + + if (num_errors) + { + error_stream.Printf ("error: %d errors parsing expression\n", num_errors); + + m_expr_decl_map.reset(); + + return false; + } + + ////////////////////////////////// + // JIT the output of the parser + // + + bool can_interpret = false; // should stay that way + + Error jit_error = parser.PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + can_interpret, + eExecutionPolicyAlways); + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + +#if 0 + // jingham: look here + StreamFile logfile ("/tmp/exprs.txt", "a"); + logfile.Printf ("0x%16.16" PRIx64 ": func = %s, source =\n%s\n", + m_jit_start_addr, + m_function_name.c_str(), + m_function_text.c_str()); +#endif + + m_expr_decl_map->DidParse(); + + m_expr_decl_map.reset(); + + if (jit_error.Success()) + { + return true; + } + else + { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + error_stream.Printf ("error: %s\n", error_cstr); + else + error_stream.Printf ("error: expression can't be interpreted or run\n"); + return false; + } +} + + diff --git a/contrib/llvm/tools/lldb/source/Expression/DWARFExpression.cpp b/contrib/llvm/tools/lldb/source/Expression/DWARFExpression.cpp new file mode 100644 index 0000000..e2ae19e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/DWARFExpression.cpp @@ -0,0 +1,2691 @@ +//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DWARFExpression.h" + +#include <vector> + +#include "lldb/Core/DataEncoder.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/VMRange.h" + +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionVariable.h" + +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackID.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +DW_OP_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x03: return "DW_OP_addr"; + case 0x06: return "DW_OP_deref"; + case 0x08: return "DW_OP_const1u"; + case 0x09: return "DW_OP_const1s"; + case 0x0a: return "DW_OP_const2u"; + case 0x0b: return "DW_OP_const2s"; + case 0x0c: return "DW_OP_const4u"; + case 0x0d: return "DW_OP_const4s"; + case 0x0e: return "DW_OP_const8u"; + case 0x0f: return "DW_OP_const8s"; + case 0x10: return "DW_OP_constu"; + case 0x11: return "DW_OP_consts"; + case 0x12: return "DW_OP_dup"; + case 0x13: return "DW_OP_drop"; + case 0x14: return "DW_OP_over"; + case 0x15: return "DW_OP_pick"; + case 0x16: return "DW_OP_swap"; + case 0x17: return "DW_OP_rot"; + case 0x18: return "DW_OP_xderef"; + case 0x19: return "DW_OP_abs"; + case 0x1a: return "DW_OP_and"; + case 0x1b: return "DW_OP_div"; + case 0x1c: return "DW_OP_minus"; + case 0x1d: return "DW_OP_mod"; + case 0x1e: return "DW_OP_mul"; + case 0x1f: return "DW_OP_neg"; + case 0x20: return "DW_OP_not"; + case 0x21: return "DW_OP_or"; + case 0x22: return "DW_OP_plus"; + case 0x23: return "DW_OP_plus_uconst"; + case 0x24: return "DW_OP_shl"; + case 0x25: return "DW_OP_shr"; + case 0x26: return "DW_OP_shra"; + case 0x27: return "DW_OP_xor"; + case 0x2f: return "DW_OP_skip"; + case 0x28: return "DW_OP_bra"; + case 0x29: return "DW_OP_eq"; + case 0x2a: return "DW_OP_ge"; + case 0x2b: return "DW_OP_gt"; + case 0x2c: return "DW_OP_le"; + case 0x2d: return "DW_OP_lt"; + case 0x2e: return "DW_OP_ne"; + case 0x30: return "DW_OP_lit0"; + case 0x31: return "DW_OP_lit1"; + case 0x32: return "DW_OP_lit2"; + case 0x33: return "DW_OP_lit3"; + case 0x34: return "DW_OP_lit4"; + case 0x35: return "DW_OP_lit5"; + case 0x36: return "DW_OP_lit6"; + case 0x37: return "DW_OP_lit7"; + case 0x38: return "DW_OP_lit8"; + case 0x39: return "DW_OP_lit9"; + case 0x3a: return "DW_OP_lit10"; + case 0x3b: return "DW_OP_lit11"; + case 0x3c: return "DW_OP_lit12"; + case 0x3d: return "DW_OP_lit13"; + case 0x3e: return "DW_OP_lit14"; + case 0x3f: return "DW_OP_lit15"; + case 0x40: return "DW_OP_lit16"; + case 0x41: return "DW_OP_lit17"; + case 0x42: return "DW_OP_lit18"; + case 0x43: return "DW_OP_lit19"; + case 0x44: return "DW_OP_lit20"; + case 0x45: return "DW_OP_lit21"; + case 0x46: return "DW_OP_lit22"; + case 0x47: return "DW_OP_lit23"; + case 0x48: return "DW_OP_lit24"; + case 0x49: return "DW_OP_lit25"; + case 0x4a: return "DW_OP_lit26"; + case 0x4b: return "DW_OP_lit27"; + case 0x4c: return "DW_OP_lit28"; + case 0x4d: return "DW_OP_lit29"; + case 0x4e: return "DW_OP_lit30"; + case 0x4f: return "DW_OP_lit31"; + case 0x50: return "DW_OP_reg0"; + case 0x51: return "DW_OP_reg1"; + case 0x52: return "DW_OP_reg2"; + case 0x53: return "DW_OP_reg3"; + case 0x54: return "DW_OP_reg4"; + case 0x55: return "DW_OP_reg5"; + case 0x56: return "DW_OP_reg6"; + case 0x57: return "DW_OP_reg7"; + case 0x58: return "DW_OP_reg8"; + case 0x59: return "DW_OP_reg9"; + case 0x5a: return "DW_OP_reg10"; + case 0x5b: return "DW_OP_reg11"; + case 0x5c: return "DW_OP_reg12"; + case 0x5d: return "DW_OP_reg13"; + case 0x5e: return "DW_OP_reg14"; + case 0x5f: return "DW_OP_reg15"; + case 0x60: return "DW_OP_reg16"; + case 0x61: return "DW_OP_reg17"; + case 0x62: return "DW_OP_reg18"; + case 0x63: return "DW_OP_reg19"; + case 0x64: return "DW_OP_reg20"; + case 0x65: return "DW_OP_reg21"; + case 0x66: return "DW_OP_reg22"; + case 0x67: return "DW_OP_reg23"; + case 0x68: return "DW_OP_reg24"; + case 0x69: return "DW_OP_reg25"; + case 0x6a: return "DW_OP_reg26"; + case 0x6b: return "DW_OP_reg27"; + case 0x6c: return "DW_OP_reg28"; + case 0x6d: return "DW_OP_reg29"; + case 0x6e: return "DW_OP_reg30"; + case 0x6f: return "DW_OP_reg31"; + case 0x70: return "DW_OP_breg0"; + case 0x71: return "DW_OP_breg1"; + case 0x72: return "DW_OP_breg2"; + case 0x73: return "DW_OP_breg3"; + case 0x74: return "DW_OP_breg4"; + case 0x75: return "DW_OP_breg5"; + case 0x76: return "DW_OP_breg6"; + case 0x77: return "DW_OP_breg7"; + case 0x78: return "DW_OP_breg8"; + case 0x79: return "DW_OP_breg9"; + case 0x7a: return "DW_OP_breg10"; + case 0x7b: return "DW_OP_breg11"; + case 0x7c: return "DW_OP_breg12"; + case 0x7d: return "DW_OP_breg13"; + case 0x7e: return "DW_OP_breg14"; + case 0x7f: return "DW_OP_breg15"; + case 0x80: return "DW_OP_breg16"; + case 0x81: return "DW_OP_breg17"; + case 0x82: return "DW_OP_breg18"; + case 0x83: return "DW_OP_breg19"; + case 0x84: return "DW_OP_breg20"; + case 0x85: return "DW_OP_breg21"; + case 0x86: return "DW_OP_breg22"; + case 0x87: return "DW_OP_breg23"; + case 0x88: return "DW_OP_breg24"; + case 0x89: return "DW_OP_breg25"; + case 0x8a: return "DW_OP_breg26"; + case 0x8b: return "DW_OP_breg27"; + case 0x8c: return "DW_OP_breg28"; + case 0x8d: return "DW_OP_breg29"; + case 0x8e: return "DW_OP_breg30"; + case 0x8f: return "DW_OP_breg31"; + case 0x90: return "DW_OP_regx"; + case 0x91: return "DW_OP_fbreg"; + case 0x92: return "DW_OP_bregx"; + case 0x93: return "DW_OP_piece"; + case 0x94: return "DW_OP_deref_size"; + case 0x95: return "DW_OP_xderef_size"; + case 0x96: return "DW_OP_nop"; + case 0x97: return "DW_OP_push_object_address"; + case 0x98: return "DW_OP_call2"; + case 0x99: return "DW_OP_call4"; + case 0x9a: return "DW_OP_call_ref"; +// case DW_OP_APPLE_array_ref: return "DW_OP_APPLE_array_ref"; +// case DW_OP_APPLE_extern: return "DW_OP_APPLE_extern"; + case DW_OP_APPLE_uninit: return "DW_OP_APPLE_uninit"; +// case DW_OP_APPLE_assign: return "DW_OP_APPLE_assign"; +// case DW_OP_APPLE_address_of: return "DW_OP_APPLE_address_of"; +// case DW_OP_APPLE_value_of: return "DW_OP_APPLE_value_of"; +// case DW_OP_APPLE_deref_type: return "DW_OP_APPLE_deref_type"; +// case DW_OP_APPLE_expr_local: return "DW_OP_APPLE_expr_local"; +// case DW_OP_APPLE_constf: return "DW_OP_APPLE_constf"; +// case DW_OP_APPLE_scalar_cast: return "DW_OP_APPLE_scalar_cast"; +// case DW_OP_APPLE_clang_cast: return "DW_OP_APPLE_clang_cast"; +// case DW_OP_APPLE_clear: return "DW_OP_APPLE_clear"; +// case DW_OP_APPLE_error: return "DW_OP_APPLE_error"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + + +//---------------------------------------------------------------------- +// DWARFExpression constructor +//---------------------------------------------------------------------- +DWARFExpression::DWARFExpression() : + m_data(), + m_reg_kind (eRegisterKindDWARF), + m_loclist_slide (LLDB_INVALID_ADDRESS) +{ +} + +DWARFExpression::DWARFExpression(const DWARFExpression& rhs) : + m_data(rhs.m_data), + m_reg_kind (rhs.m_reg_kind), + m_loclist_slide(rhs.m_loclist_slide) +{ +} + + +DWARFExpression::DWARFExpression(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) : + m_data(data, data_offset, data_length), + m_reg_kind (eRegisterKindDWARF), + m_loclist_slide(LLDB_INVALID_ADDRESS) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DWARFExpression::~DWARFExpression() +{ +} + + +bool +DWARFExpression::IsValid() const +{ + return m_data.GetByteSize() > 0; +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data) +{ + m_data = data; +} + +void +DWARFExpression::CopyOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +{ + const uint8_t *bytes = data.PeekData(data_offset, data_length); + if (bytes) + { + m_data.SetData(DataBufferSP(new DataBufferHeap(bytes, data_length))); + m_data.SetByteOrder(data.GetByteOrder()); + m_data.SetAddressByteSize(data.GetAddressByteSize()); + } +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +{ + m_data.SetData(data, data_offset, data_length); +} + +void +DWARFExpression::DumpLocation (Stream *s, lldb::offset_t offset, lldb::offset_t length, lldb::DescriptionLevel level, ABI *abi) const +{ + if (!m_data.ValidOffsetForDataOfSize(offset, length)) + return; + const lldb::offset_t start_offset = offset; + const lldb::offset_t end_offset = offset + length; + while (m_data.ValidOffset(offset) && offset < end_offset) + { + const lldb::offset_t op_offset = offset; + const uint8_t op = m_data.GetU8(&offset); + + switch (level) + { + default: + break; + + case lldb::eDescriptionLevelBrief: + if (offset > start_offset) + s->PutChar(' '); + break; + + case lldb::eDescriptionLevelFull: + case lldb::eDescriptionLevelVerbose: + if (offset > start_offset) + s->EOL(); + s->Indent(); + if (level == lldb::eDescriptionLevelFull) + break; + // Fall through for verbose and print offset and DW_OP prefix.. + s->Printf("0x%8.8" PRIx64 ": %s", op_offset, op >= DW_OP_APPLE_uninit ? "DW_OP_APPLE_" : "DW_OP_"); + break; + } + + switch (op) + { + case DW_OP_addr: *s << "DW_OP_addr(" << m_data.GetAddress(&offset) << ") "; break; // 0x03 1 address + case DW_OP_deref: *s << "DW_OP_deref"; break; // 0x06 + case DW_OP_const1u: s->Printf("DW_OP_const1u(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x08 1 1-byte constant + case DW_OP_const1s: s->Printf("DW_OP_const1s(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x09 1 1-byte constant + case DW_OP_const2u: s->Printf("DW_OP_const2u(0x%4.4x) ", m_data.GetU16(&offset)); break; // 0x0a 1 2-byte constant + case DW_OP_const2s: s->Printf("DW_OP_const2s(0x%4.4x) ", m_data.GetU16(&offset)); break; // 0x0b 1 2-byte constant + case DW_OP_const4u: s->Printf("DW_OP_const4u(0x%8.8x) ", m_data.GetU32(&offset)); break; // 0x0c 1 4-byte constant + case DW_OP_const4s: s->Printf("DW_OP_const4s(0x%8.8x) ", m_data.GetU32(&offset)); break; // 0x0d 1 4-byte constant + case DW_OP_const8u: s->Printf("DW_OP_const8u(0x%16.16" PRIx64 ") ", m_data.GetU64(&offset)); break; // 0x0e 1 8-byte constant + case DW_OP_const8s: s->Printf("DW_OP_const8s(0x%16.16" PRIx64 ") ", m_data.GetU64(&offset)); break; // 0x0f 1 8-byte constant + case DW_OP_constu: s->Printf("DW_OP_constu(0x%" PRIx64 ") ", m_data.GetULEB128(&offset)); break; // 0x10 1 ULEB128 constant + case DW_OP_consts: s->Printf("DW_OP_consts(0x%" PRId64 ") ", m_data.GetSLEB128(&offset)); break; // 0x11 1 SLEB128 constant + case DW_OP_dup: s->PutCString("DW_OP_dup"); break; // 0x12 + case DW_OP_drop: s->PutCString("DW_OP_drop"); break; // 0x13 + case DW_OP_over: s->PutCString("DW_OP_over"); break; // 0x14 + case DW_OP_pick: s->Printf("DW_OP_pick(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x15 1 1-byte stack index + case DW_OP_swap: s->PutCString("DW_OP_swap"); break; // 0x16 + case DW_OP_rot: s->PutCString("DW_OP_rot"); break; // 0x17 + case DW_OP_xderef: s->PutCString("DW_OP_xderef"); break; // 0x18 + case DW_OP_abs: s->PutCString("DW_OP_abs"); break; // 0x19 + case DW_OP_and: s->PutCString("DW_OP_and"); break; // 0x1a + case DW_OP_div: s->PutCString("DW_OP_div"); break; // 0x1b + case DW_OP_minus: s->PutCString("DW_OP_minus"); break; // 0x1c + case DW_OP_mod: s->PutCString("DW_OP_mod"); break; // 0x1d + case DW_OP_mul: s->PutCString("DW_OP_mul"); break; // 0x1e + case DW_OP_neg: s->PutCString("DW_OP_neg"); break; // 0x1f + case DW_OP_not: s->PutCString("DW_OP_not"); break; // 0x20 + case DW_OP_or: s->PutCString("DW_OP_or"); break; // 0x21 + case DW_OP_plus: s->PutCString("DW_OP_plus"); break; // 0x22 + case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend + s->Printf("DW_OP_plus_uconst(0x%" PRIx64 ") ", m_data.GetULEB128(&offset)); + break; + + case DW_OP_shl: s->PutCString("DW_OP_shl"); break; // 0x24 + case DW_OP_shr: s->PutCString("DW_OP_shr"); break; // 0x25 + case DW_OP_shra: s->PutCString("DW_OP_shra"); break; // 0x26 + case DW_OP_xor: s->PutCString("DW_OP_xor"); break; // 0x27 + case DW_OP_skip: s->Printf("DW_OP_skip(0x%4.4x)", m_data.GetU16(&offset)); break; // 0x2f 1 signed 2-byte constant + case DW_OP_bra: s->Printf("DW_OP_bra(0x%4.4x)", m_data.GetU16(&offset)); break; // 0x28 1 signed 2-byte constant + case DW_OP_eq: s->PutCString("DW_OP_eq"); break; // 0x29 + case DW_OP_ge: s->PutCString("DW_OP_ge"); break; // 0x2a + case DW_OP_gt: s->PutCString("DW_OP_gt"); break; // 0x2b + case DW_OP_le: s->PutCString("DW_OP_le"); break; // 0x2c + case DW_OP_lt: s->PutCString("DW_OP_lt"); break; // 0x2d + case DW_OP_ne: s->PutCString("DW_OP_ne"); break; // 0x2e + + case DW_OP_lit0: // 0x30 + case DW_OP_lit1: // 0x31 + case DW_OP_lit2: // 0x32 + case DW_OP_lit3: // 0x33 + case DW_OP_lit4: // 0x34 + case DW_OP_lit5: // 0x35 + case DW_OP_lit6: // 0x36 + case DW_OP_lit7: // 0x37 + case DW_OP_lit8: // 0x38 + case DW_OP_lit9: // 0x39 + case DW_OP_lit10: // 0x3A + case DW_OP_lit11: // 0x3B + case DW_OP_lit12: // 0x3C + case DW_OP_lit13: // 0x3D + case DW_OP_lit14: // 0x3E + case DW_OP_lit15: // 0x3F + case DW_OP_lit16: // 0x40 + case DW_OP_lit17: // 0x41 + case DW_OP_lit18: // 0x42 + case DW_OP_lit19: // 0x43 + case DW_OP_lit20: // 0x44 + case DW_OP_lit21: // 0x45 + case DW_OP_lit22: // 0x46 + case DW_OP_lit23: // 0x47 + case DW_OP_lit24: // 0x48 + case DW_OP_lit25: // 0x49 + case DW_OP_lit26: // 0x4A + case DW_OP_lit27: // 0x4B + case DW_OP_lit28: // 0x4C + case DW_OP_lit29: // 0x4D + case DW_OP_lit30: // 0x4E + case DW_OP_lit31: s->Printf("DW_OP_lit%i", op - DW_OP_lit0); break; // 0x4f + + case DW_OP_reg0: // 0x50 + case DW_OP_reg1: // 0x51 + case DW_OP_reg2: // 0x52 + case DW_OP_reg3: // 0x53 + case DW_OP_reg4: // 0x54 + case DW_OP_reg5: // 0x55 + case DW_OP_reg6: // 0x56 + case DW_OP_reg7: // 0x57 + case DW_OP_reg8: // 0x58 + case DW_OP_reg9: // 0x59 + case DW_OP_reg10: // 0x5A + case DW_OP_reg11: // 0x5B + case DW_OP_reg12: // 0x5C + case DW_OP_reg13: // 0x5D + case DW_OP_reg14: // 0x5E + case DW_OP_reg15: // 0x5F + case DW_OP_reg16: // 0x60 + case DW_OP_reg17: // 0x61 + case DW_OP_reg18: // 0x62 + case DW_OP_reg19: // 0x63 + case DW_OP_reg20: // 0x64 + case DW_OP_reg21: // 0x65 + case DW_OP_reg22: // 0x66 + case DW_OP_reg23: // 0x67 + case DW_OP_reg24: // 0x68 + case DW_OP_reg25: // 0x69 + case DW_OP_reg26: // 0x6A + case DW_OP_reg27: // 0x6B + case DW_OP_reg28: // 0x6C + case DW_OP_reg29: // 0x6D + case DW_OP_reg30: // 0x6E + case DW_OP_reg31: // 0x6F + { + uint32_t reg_num = op - DW_OP_reg0; + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->PutCString (reg_info.name); + break; + } + else if (reg_info.alt_name) + { + s->PutCString (reg_info.alt_name); + break; + } + } + } + s->Printf("DW_OP_reg%u", reg_num); break; + } + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + { + uint32_t reg_num = op - DW_OP_breg0; + int64_t reg_offset = m_data.GetSLEB128(&offset); + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset); + break; + } + else if (reg_info.alt_name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset); + break; + } + } + } + s->Printf("DW_OP_breg%i(0x%" PRIx64 ")", reg_num, reg_offset); + } + break; + + case DW_OP_regx: // 0x90 1 ULEB128 register + { + uint32_t reg_num = m_data.GetULEB128(&offset); + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->PutCString (reg_info.name); + break; + } + else if (reg_info.alt_name) + { + s->PutCString (reg_info.alt_name); + break; + } + } + } + s->Printf("DW_OP_regx(%" PRIu32 ")", reg_num); break; + } + break; + case DW_OP_fbreg: // 0x91 1 SLEB128 offset + s->Printf("DW_OP_fbreg(%" PRIi64 ")",m_data.GetSLEB128(&offset)); + break; + case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset + { + uint32_t reg_num = m_data.GetULEB128(&offset); + int64_t reg_offset = m_data.GetSLEB128(&offset); + if (abi) + { + RegisterInfo reg_info; + if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) + { + if (reg_info.name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset); + break; + } + else if (reg_info.alt_name) + { + s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset); + break; + } + } + } + s->Printf("DW_OP_bregx(reg=%" PRIu32 ",offset=%" PRIi64 ")", reg_num, reg_offset); + } + break; + case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed + s->Printf("DW_OP_piece(0x%" PRIx64 ")", m_data.GetULEB128(&offset)); + break; + case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved + s->Printf("DW_OP_deref_size(0x%2.2x)", m_data.GetU8(&offset)); + break; + case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved + s->Printf("DW_OP_xderef_size(0x%2.2x)", m_data.GetU8(&offset)); + break; + case DW_OP_nop: s->PutCString("DW_OP_nop"); break; // 0x96 + case DW_OP_push_object_address: s->PutCString("DW_OP_push_object_address"); break; // 0x97 DWARF3 + case DW_OP_call2: // 0x98 DWARF3 1 2-byte offset of DIE + s->Printf("DW_OP_call2(0x%4.4x)", m_data.GetU16(&offset)); + break; + case DW_OP_call4: // 0x99 DWARF3 1 4-byte offset of DIE + s->Printf("DW_OP_call4(0x%8.8x)", m_data.GetU32(&offset)); + break; + case DW_OP_call_ref: // 0x9a DWARF3 1 4- or 8-byte offset of DIE + s->Printf("DW_OP_call_ref(0x%8.8" PRIx64 ")", m_data.GetAddress(&offset)); + break; +// case DW_OP_form_tls_address: s << "form_tls_address"; break; // 0x9b DWARF3 +// case DW_OP_call_frame_cfa: s << "call_frame_cfa"; break; // 0x9c DWARF3 +// case DW_OP_bit_piece: // 0x9d DWARF3 2 +// s->Printf("DW_OP_bit_piece(0x%x, 0x%x)", m_data.GetULEB128(&offset), m_data.GetULEB128(&offset)); +// break; +// case DW_OP_lo_user: s->PutCString("DW_OP_lo_user"); break; // 0xe0 +// case DW_OP_hi_user: s->PutCString("DW_OP_hi_user"); break; // 0xff +// case DW_OP_APPLE_extern: +// s->Printf("DW_OP_APPLE_extern(%" PRIu64 ")", m_data.GetULEB128(&offset)); +// break; +// case DW_OP_APPLE_array_ref: +// s->PutCString("DW_OP_APPLE_array_ref"); +// break; + case DW_OP_APPLE_uninit: + s->PutCString("DW_OP_APPLE_uninit"); // 0xF0 + break; +// case DW_OP_APPLE_assign: // 0xF1 - pops value off and assigns it to second item on stack (2nd item must have assignable context) +// s->PutCString("DW_OP_APPLE_assign"); +// break; +// case DW_OP_APPLE_address_of: // 0xF2 - gets the address of the top stack item (top item must be a variable, or have value_type that is an address already) +// s->PutCString("DW_OP_APPLE_address_of"); +// break; +// case DW_OP_APPLE_value_of: // 0xF3 - pops the value off the stack and pushes the value of that object (top item must be a variable, or expression local) +// s->PutCString("DW_OP_APPLE_value_of"); +// break; +// case DW_OP_APPLE_deref_type: // 0xF4 - gets the address of the top stack item (top item must be a variable, or a clang type) +// s->PutCString("DW_OP_APPLE_deref_type"); +// break; +// case DW_OP_APPLE_expr_local: // 0xF5 - ULEB128 expression local index +// s->Printf("DW_OP_APPLE_expr_local(%" PRIu64 ")", m_data.GetULEB128(&offset)); +// break; +// case DW_OP_APPLE_constf: // 0xF6 - 1 byte float size, followed by constant float data +// { +// uint8_t float_length = m_data.GetU8(&offset); +// s->Printf("DW_OP_APPLE_constf(<%u> ", float_length); +// m_data.Dump(s, offset, eFormatHex, float_length, 1, UINT32_MAX, DW_INVALID_ADDRESS, 0, 0); +// s->PutChar(')'); +// // Consume the float data +// m_data.GetData(&offset, float_length); +// } +// break; +// case DW_OP_APPLE_scalar_cast: +// s->Printf("DW_OP_APPLE_scalar_cast(%s)", Scalar::GetValueTypeAsCString ((Scalar::Type)m_data.GetU8(&offset))); +// break; +// case DW_OP_APPLE_clang_cast: +// { +// clang::Type *clang_type = (clang::Type *)m_data.GetMaxU64(&offset, sizeof(void*)); +// s->Printf("DW_OP_APPLE_clang_cast(%p)", clang_type); +// } +// break; +// case DW_OP_APPLE_clear: +// s->PutCString("DW_OP_APPLE_clear"); +// break; +// case DW_OP_APPLE_error: // 0xFF - Stops expression evaluation and returns an error (no args) +// s->PutCString("DW_OP_APPLE_error"); +// break; + } + } +} + +void +DWARFExpression::SetLocationListSlide (addr_t slide) +{ + m_loclist_slide = slide; +} + +int +DWARFExpression::GetRegisterKind () +{ + return m_reg_kind; +} + +void +DWARFExpression::SetRegisterKind (RegisterKind reg_kind) +{ + m_reg_kind = reg_kind; +} + +bool +DWARFExpression::IsLocationList() const +{ + return m_loclist_slide != LLDB_INVALID_ADDRESS; +} + +void +DWARFExpression::GetDescription (Stream *s, lldb::DescriptionLevel level, addr_t location_list_base_addr, ABI *abi) const +{ + if (IsLocationList()) + { + // We have a location list + lldb::offset_t offset = 0; + uint32_t count = 0; + addr_t curr_base_addr = location_list_base_addr; + while (m_data.ValidOffset(offset)) + { + lldb::addr_t begin_addr_offset = m_data.GetAddress(&offset); + lldb::addr_t end_addr_offset = m_data.GetAddress(&offset); + if (begin_addr_offset < end_addr_offset) + { + if (count > 0) + s->PutCString(", "); + VMRange addr_range(curr_base_addr + begin_addr_offset, curr_base_addr + end_addr_offset); + addr_range.Dump(s, 0, 8); + s->PutChar('{'); + lldb::offset_t location_length = m_data.GetU16(&offset); + DumpLocation (s, offset, location_length, level, abi); + s->PutChar('}'); + offset += location_length; + } + else if (begin_addr_offset == 0 && end_addr_offset == 0) + { + // The end of the location list is marked by both the start and end offset being zero + break; + } + else + { + if ((m_data.GetAddressByteSize() == 4 && (begin_addr_offset == UINT32_MAX)) || + (m_data.GetAddressByteSize() == 8 && (begin_addr_offset == UINT64_MAX))) + { + curr_base_addr = end_addr_offset + location_list_base_addr; + // We have a new base address + if (count > 0) + s->PutCString(", "); + *s << "base_addr = " << end_addr_offset; + } + } + + count++; + } + } + else + { + // We have a normal location that contains DW_OP location opcodes + DumpLocation (s, 0, m_data.GetByteSize(), level, abi); + } +} + +static bool +ReadRegisterValueAsScalar +( + RegisterContext *reg_ctx, + uint32_t reg_kind, + uint32_t reg_num, + Error *error_ptr, + Value &value +) +{ + if (reg_ctx == NULL) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); + } + else + { + uint32_t native_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + if (native_reg == LLDB_INVALID_REGNUM) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Unable to convert register kind=%u reg_num=%u to a native register number.\n", reg_kind, reg_num); + } + else + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(native_reg); + RegisterValue reg_value; + if (reg_ctx->ReadRegister (reg_info, reg_value)) + { + if (reg_value.GetScalarValue(value.GetScalar())) + { + value.SetValueType (Value::eValueTypeScalar); + value.SetContext (Value::eContextTypeRegisterInfo, + const_cast<RegisterInfo *>(reg_info)); + if (error_ptr) + error_ptr->Clear(); + return true; + } + else + { + // If we get this error, then we need to implement a value + // buffer in the dwarf expression evaluation function... + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("register %s can't be converted to a scalar value", + reg_info->name); + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("register %s is not available", reg_info->name); + } + } + } + return false; +} + +//bool +//DWARFExpression::LocationListContainsLoadAddress (Process* process, const Address &addr) const +//{ +// return LocationListContainsLoadAddress(process, addr.GetLoadAddress(process)); +//} +// +//bool +//DWARFExpression::LocationListContainsLoadAddress (Process* process, addr_t load_addr) const +//{ +// if (load_addr == LLDB_INVALID_ADDRESS) +// return false; +// +// if (IsLocationList()) +// { +// lldb::offset_t offset = 0; +// +// addr_t loc_list_base_addr = m_loclist_slide.GetLoadAddress(process); +// +// if (loc_list_base_addr == LLDB_INVALID_ADDRESS) +// return false; +// +// while (m_data.ValidOffset(offset)) +// { +// // We need to figure out what the value is for the location. +// addr_t lo_pc = m_data.GetAddress(&offset); +// addr_t hi_pc = m_data.GetAddress(&offset); +// if (lo_pc == 0 && hi_pc == 0) +// break; +// else +// { +// lo_pc += loc_list_base_addr; +// hi_pc += loc_list_base_addr; +// +// if (lo_pc <= load_addr && load_addr < hi_pc) +// return true; +// +// offset += m_data.GetU16(&offset); +// } +// } +// } +// return false; +//} + +static offset_t +GetOpcodeDataSize (const DataExtractor &data, const lldb::offset_t data_offset, const uint8_t op) +{ + lldb::offset_t offset = data_offset; + switch (op) + { + case DW_OP_addr: + case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3) + return data.GetAddressByteSize(); + + // Opcodes with no arguments + case DW_OP_deref: // 0x06 + case DW_OP_dup: // 0x12 + case DW_OP_drop: // 0x13 + case DW_OP_over: // 0x14 + case DW_OP_swap: // 0x16 + case DW_OP_rot: // 0x17 + case DW_OP_xderef: // 0x18 + case DW_OP_abs: // 0x19 + case DW_OP_and: // 0x1a + case DW_OP_div: // 0x1b + case DW_OP_minus: // 0x1c + case DW_OP_mod: // 0x1d + case DW_OP_mul: // 0x1e + case DW_OP_neg: // 0x1f + case DW_OP_not: // 0x20 + case DW_OP_or: // 0x21 + case DW_OP_plus: // 0x22 + case DW_OP_shl: // 0x24 + case DW_OP_shr: // 0x25 + case DW_OP_shra: // 0x26 + case DW_OP_xor: // 0x27 + case DW_OP_eq: // 0x29 + case DW_OP_ge: // 0x2a + case DW_OP_gt: // 0x2b + case DW_OP_le: // 0x2c + case DW_OP_lt: // 0x2d + case DW_OP_ne: // 0x2e + case DW_OP_lit0: // 0x30 + case DW_OP_lit1: // 0x31 + case DW_OP_lit2: // 0x32 + case DW_OP_lit3: // 0x33 + case DW_OP_lit4: // 0x34 + case DW_OP_lit5: // 0x35 + case DW_OP_lit6: // 0x36 + case DW_OP_lit7: // 0x37 + case DW_OP_lit8: // 0x38 + case DW_OP_lit9: // 0x39 + case DW_OP_lit10: // 0x3A + case DW_OP_lit11: // 0x3B + case DW_OP_lit12: // 0x3C + case DW_OP_lit13: // 0x3D + case DW_OP_lit14: // 0x3E + case DW_OP_lit15: // 0x3F + case DW_OP_lit16: // 0x40 + case DW_OP_lit17: // 0x41 + case DW_OP_lit18: // 0x42 + case DW_OP_lit19: // 0x43 + case DW_OP_lit20: // 0x44 + case DW_OP_lit21: // 0x45 + case DW_OP_lit22: // 0x46 + case DW_OP_lit23: // 0x47 + case DW_OP_lit24: // 0x48 + case DW_OP_lit25: // 0x49 + case DW_OP_lit26: // 0x4A + case DW_OP_lit27: // 0x4B + case DW_OP_lit28: // 0x4C + case DW_OP_lit29: // 0x4D + case DW_OP_lit30: // 0x4E + case DW_OP_lit31: // 0x4f + case DW_OP_reg0: // 0x50 + case DW_OP_reg1: // 0x51 + case DW_OP_reg2: // 0x52 + case DW_OP_reg3: // 0x53 + case DW_OP_reg4: // 0x54 + case DW_OP_reg5: // 0x55 + case DW_OP_reg6: // 0x56 + case DW_OP_reg7: // 0x57 + case DW_OP_reg8: // 0x58 + case DW_OP_reg9: // 0x59 + case DW_OP_reg10: // 0x5A + case DW_OP_reg11: // 0x5B + case DW_OP_reg12: // 0x5C + case DW_OP_reg13: // 0x5D + case DW_OP_reg14: // 0x5E + case DW_OP_reg15: // 0x5F + case DW_OP_reg16: // 0x60 + case DW_OP_reg17: // 0x61 + case DW_OP_reg18: // 0x62 + case DW_OP_reg19: // 0x63 + case DW_OP_reg20: // 0x64 + case DW_OP_reg21: // 0x65 + case DW_OP_reg22: // 0x66 + case DW_OP_reg23: // 0x67 + case DW_OP_reg24: // 0x68 + case DW_OP_reg25: // 0x69 + case DW_OP_reg26: // 0x6A + case DW_OP_reg27: // 0x6B + case DW_OP_reg28: // 0x6C + case DW_OP_reg29: // 0x6D + case DW_OP_reg30: // 0x6E + case DW_OP_reg31: // 0x6F + case DW_OP_nop: // 0x96 + case DW_OP_push_object_address: // 0x97 DWARF3 + case DW_OP_form_tls_address: // 0x9b DWARF3 + case DW_OP_call_frame_cfa: // 0x9c DWARF3 + case DW_OP_stack_value: // 0x9f DWARF4 + return 0; + + // Opcodes with a single 1 byte arguments + case DW_OP_const1u: // 0x08 1 1-byte constant + case DW_OP_const1s: // 0x09 1 1-byte constant + case DW_OP_pick: // 0x15 1 1-byte stack index + case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved + case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved + return 1; + + // Opcodes with a single 2 byte arguments + case DW_OP_const2u: // 0x0a 1 2-byte constant + case DW_OP_const2s: // 0x0b 1 2-byte constant + case DW_OP_skip: // 0x2f 1 signed 2-byte constant + case DW_OP_bra: // 0x28 1 signed 2-byte constant + case DW_OP_call2: // 0x98 1 2-byte offset of DIE (DWARF3) + return 2; + + // Opcodes with a single 4 byte arguments + case DW_OP_const4u: // 0x0c 1 4-byte constant + case DW_OP_const4s: // 0x0d 1 4-byte constant + case DW_OP_call4: // 0x99 1 4-byte offset of DIE (DWARF3) + return 4; + + // Opcodes with a single 8 byte arguments + case DW_OP_const8u: // 0x0e 1 8-byte constant + case DW_OP_const8s: // 0x0f 1 8-byte constant + return 8; + + // All opcodes that have a single ULEB (signed or unsigned) argument + case DW_OP_constu: // 0x10 1 ULEB128 constant + case DW_OP_consts: // 0x11 1 SLEB128 constant + case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend + case DW_OP_breg0: // 0x70 1 ULEB128 register + case DW_OP_breg1: // 0x71 1 ULEB128 register + case DW_OP_breg2: // 0x72 1 ULEB128 register + case DW_OP_breg3: // 0x73 1 ULEB128 register + case DW_OP_breg4: // 0x74 1 ULEB128 register + case DW_OP_breg5: // 0x75 1 ULEB128 register + case DW_OP_breg6: // 0x76 1 ULEB128 register + case DW_OP_breg7: // 0x77 1 ULEB128 register + case DW_OP_breg8: // 0x78 1 ULEB128 register + case DW_OP_breg9: // 0x79 1 ULEB128 register + case DW_OP_breg10: // 0x7a 1 ULEB128 register + case DW_OP_breg11: // 0x7b 1 ULEB128 register + case DW_OP_breg12: // 0x7c 1 ULEB128 register + case DW_OP_breg13: // 0x7d 1 ULEB128 register + case DW_OP_breg14: // 0x7e 1 ULEB128 register + case DW_OP_breg15: // 0x7f 1 ULEB128 register + case DW_OP_breg16: // 0x80 1 ULEB128 register + case DW_OP_breg17: // 0x81 1 ULEB128 register + case DW_OP_breg18: // 0x82 1 ULEB128 register + case DW_OP_breg19: // 0x83 1 ULEB128 register + case DW_OP_breg20: // 0x84 1 ULEB128 register + case DW_OP_breg21: // 0x85 1 ULEB128 register + case DW_OP_breg22: // 0x86 1 ULEB128 register + case DW_OP_breg23: // 0x87 1 ULEB128 register + case DW_OP_breg24: // 0x88 1 ULEB128 register + case DW_OP_breg25: // 0x89 1 ULEB128 register + case DW_OP_breg26: // 0x8a 1 ULEB128 register + case DW_OP_breg27: // 0x8b 1 ULEB128 register + case DW_OP_breg28: // 0x8c 1 ULEB128 register + case DW_OP_breg29: // 0x8d 1 ULEB128 register + case DW_OP_breg30: // 0x8e 1 ULEB128 register + case DW_OP_breg31: // 0x8f 1 ULEB128 register + case DW_OP_regx: // 0x90 1 ULEB128 register + case DW_OP_fbreg: // 0x91 1 SLEB128 offset + case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed + data.Skip_LEB128(&offset); + return offset - data_offset; + + // All opcodes that have a 2 ULEB (signed or unsigned) arguments + case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset + case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); + data.Skip_LEB128(&offset); + data.Skip_LEB128(&offset); + return offset - data_offset; + + case DW_OP_implicit_value: // 0x9e ULEB128 size followed by block of that size (DWARF4) + { + uint64_t block_len = data.Skip_LEB128(&offset); + offset += block_len; + return offset - data_offset; + } + + default: + break; + } + return LLDB_INVALID_OFFSET; +} + +lldb::addr_t +DWARFExpression::GetLocation_DW_OP_addr (uint32_t op_addr_idx, bool &error) const +{ + error = false; + if (IsLocationList()) + return LLDB_INVALID_ADDRESS; + lldb::offset_t offset = 0; + uint32_t curr_op_addr_idx = 0; + while (m_data.ValidOffset(offset)) + { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) + { + const lldb::addr_t op_file_addr = m_data.GetAddress(&offset); + if (curr_op_addr_idx == op_addr_idx) + return op_file_addr; + else + ++curr_op_addr_idx; + } + else + { + const offset_t op_arg_size = GetOpcodeDataSize (m_data, offset, op); + if (op_arg_size == LLDB_INVALID_OFFSET) + { + error = true; + break; + } + offset += op_arg_size; + } + } + return LLDB_INVALID_ADDRESS; +} + +bool +DWARFExpression::Update_DW_OP_addr (lldb::addr_t file_addr) +{ + if (IsLocationList()) + return false; + lldb::offset_t offset = 0; + while (m_data.ValidOffset(offset)) + { + const uint8_t op = m_data.GetU8(&offset); + + if (op == DW_OP_addr) + { + const uint32_t addr_byte_size = m_data.GetAddressByteSize(); + // We have to make a copy of the data as we don't know if this + // data is from a read only memory mapped buffer, so we duplicate + // all of the data first, then modify it, and if all goes well, + // we then replace the data for this expression + + // So first we copy the data into a heap buffer + std::unique_ptr<DataBufferHeap> head_data_ap (new DataBufferHeap (m_data.GetDataStart(), + m_data.GetByteSize())); + + // Make en encoder so we can write the address into the buffer using + // the correct byte order (endianness) + DataEncoder encoder (head_data_ap->GetBytes(), + head_data_ap->GetByteSize(), + m_data.GetByteOrder(), + addr_byte_size); + + // Replace the address in the new buffer + if (encoder.PutMaxU64 (offset, addr_byte_size, file_addr) == UINT32_MAX) + return false; + + // All went well, so now we can reset the data using a shared + // pointer to the heap data so "m_data" will now correctly + // manage the heap data. + m_data.SetData (DataBufferSP (head_data_ap.release())); + return true; + } + else + { + const offset_t op_arg_size = GetOpcodeDataSize (m_data, offset, op); + if (op_arg_size == LLDB_INVALID_OFFSET) + break; + offset += op_arg_size; + } + } + return false; +} + +bool +DWARFExpression::LocationListContainsAddress (lldb::addr_t loclist_base_addr, lldb::addr_t addr) const +{ + if (addr == LLDB_INVALID_ADDRESS) + return false; + + if (IsLocationList()) + { + lldb::offset_t offset = 0; + + if (loclist_base_addr == LLDB_INVALID_ADDRESS) + return false; + + while (m_data.ValidOffset(offset)) + { + // We need to figure out what the value is for the location. + addr_t lo_pc = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + break; + else + { + lo_pc += loclist_base_addr - m_loclist_slide; + hi_pc += loclist_base_addr - m_loclist_slide; + + if (lo_pc <= addr && addr < hi_pc) + return true; + + offset += m_data.GetU16(&offset); + } + } + } + return false; +} + +bool +DWARFExpression::GetLocation (addr_t base_addr, addr_t pc, lldb::offset_t &offset, lldb::offset_t &length) +{ + offset = 0; + if (!IsLocationList()) + { + length = m_data.GetByteSize(); + return true; + } + + if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) + { + addr_t curr_base_addr = base_addr; + + while (m_data.ValidOffset(offset)) + { + // We need to figure out what the value is for the location. + addr_t lo_pc = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + { + break; + } + else + { + lo_pc += curr_base_addr - m_loclist_slide; + hi_pc += curr_base_addr - m_loclist_slide; + + length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) + return true; + + offset += length; + } + } + } + offset = LLDB_INVALID_OFFSET; + length = 0; + return false; +} + +bool +DWARFExpression::DumpLocationForAddress (Stream *s, + lldb::DescriptionLevel level, + addr_t base_addr, + addr_t address, + ABI *abi) +{ + lldb::offset_t offset = 0; + lldb::offset_t length = 0; + + if (GetLocation (base_addr, address, offset, length)) + { + if (length > 0) + { + DumpLocation(s, offset, length, level, abi); + return true; + } + } + return false; +} + +bool +DWARFExpression::Evaluate +( + ExecutionContextScope *exe_scope, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + ExecutionContext exe_ctx (exe_scope); + return Evaluate(&exe_ctx, expr_locals, decl_map, NULL, loclist_base_load_addr, initial_value_ptr, result, error_ptr); +} + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + if (IsLocationList()) + { + lldb::offset_t offset = 0; + addr_t pc; + StackFrame *frame = NULL; + if (reg_ctx) + pc = reg_ctx->GetPC(); + else + { + frame = exe_ctx->GetFramePtr(); + if (!frame) + return false; + RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); + if (!reg_ctx_sp) + return false; + pc = reg_ctx_sp->GetPC(); + } + + if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) + { + if (pc == LLDB_INVALID_ADDRESS) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid PC in frame."); + return false; + } + + addr_t curr_loclist_base_load_addr = loclist_base_load_addr; + + while (m_data.ValidOffset(offset)) + { + // We need to figure out what the value is for the location. + addr_t lo_pc = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + { + break; + } + else + { + lo_pc += curr_loclist_base_load_addr - m_loclist_slide; + hi_pc += curr_loclist_base_load_addr - m_loclist_slide; + + uint16_t length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) + { + return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, m_data, offset, length, m_reg_kind, initial_value_ptr, result, error_ptr); + } + offset += length; + } + } + } + if (error_ptr) + error_ptr->SetErrorString ("variable not available"); + return false; + } + + // Not a location list, just a single expression. + return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, m_data, 0, m_data.GetByteSize(), m_reg_kind, initial_value_ptr, result, error_ptr); +} + + + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + const DataExtractor& opcodes, + const lldb::offset_t opcodes_offset, + const lldb::offset_t opcodes_length, + const uint32_t reg_kind, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) +{ + + if (opcodes_length == 0) + { + if (error_ptr) + error_ptr->SetErrorString ("no location, value may have been optimized out"); + return false; + } + std::vector<Value> stack; + + Process *process = NULL; + StackFrame *frame = NULL; + + if (exe_ctx) + { + process = exe_ctx->GetProcessPtr(); + frame = exe_ctx->GetFramePtr(); + } + if (reg_ctx == NULL && frame) + reg_ctx = frame->GetRegisterContext().get(); + + if (initial_value_ptr) + stack.push_back(*initial_value_ptr); + + lldb::offset_t offset = opcodes_offset; + const lldb::offset_t end_offset = opcodes_offset + opcodes_length; + Value tmp; + uint32_t reg_num; + + // Make sure all of the data is available in opcodes. + if (!opcodes.ValidOffsetForDataOfSize(opcodes_offset, opcodes_length)) + { + if (error_ptr) + error_ptr->SetErrorString ("invalid offset and/or length for opcodes buffer."); + return false; + } + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + + while (opcodes.ValidOffset(offset) && offset < end_offset) + { + const lldb::offset_t op_offset = offset; + const uint8_t op = opcodes.GetU8(&offset); + + if (log && log->GetVerbose()) + { + size_t count = stack.size(); + log->Printf("Stack before operation has %lu values:", count); + for (size_t i=0; i<count; ++i) + { + StreamString new_value; + new_value.Printf("[%" PRIu64 "]", (uint64_t)i); + stack[i].Dump(&new_value); + log->Printf(" %s", new_value.GetData()); + } + log->Printf("0x%8.8" PRIx64 ": %s", op_offset, DW_OP_value_to_name(op)); + } + switch (op) + { + //---------------------------------------------------------------------- + // The DW_OP_addr operation has a single operand that encodes a machine + // address and whose size is the size of an address on the target machine. + //---------------------------------------------------------------------- + case DW_OP_addr: + stack.push_back(Scalar(opcodes.GetAddress(&offset))); + stack.back().SetValueType (Value::eValueTypeFileAddress); + break; + + //---------------------------------------------------------------------- + // The DW_OP_addr_sect_offset4 is used for any location expressions in + // shared libraries that have a location like: + // DW_OP_addr(0x1000) + // If this address resides in a shared library, then this virtual + // address won't make sense when it is evaluated in the context of a + // running process where shared libraries have been slid. To account for + // this, this new address type where we can store the section pointer + // and a 4 byte offset. + //---------------------------------------------------------------------- +// case DW_OP_addr_sect_offset4: +// { +// result_type = eResultTypeFileAddress; +// lldb::Section *sect = (lldb::Section *)opcodes.GetMaxU64(&offset, sizeof(void *)); +// lldb::addr_t sect_offset = opcodes.GetU32(&offset); +// +// Address so_addr (sect, sect_offset); +// lldb::addr_t load_addr = so_addr.GetLoadAddress(); +// if (load_addr != LLDB_INVALID_ADDRESS) +// { +// // We successfully resolve a file address to a load +// // address. +// stack.push_back(load_addr); +// break; +// } +// else +// { +// // We were able +// if (error_ptr) +// error_ptr->SetErrorStringWithFormat ("Section %s in %s is not currently loaded.\n", sect->GetName().AsCString(), sect->GetModule()->GetFileSpec().GetFilename().AsCString()); +// return false; +// } +// } +// break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_deref + // OPERANDS: none + // DESCRIPTION: Pops the top stack entry and treats it as an address. + // The value retrieved from that address is pushed. The size of the + // data retrieved from the dereferenced address is the size of an + // address on the target machine. + //---------------------------------------------------------------------- + case DW_OP_deref: + { + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) + { + case Value::eValueTypeHostAddress: + { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy (&ptr, src, sizeof(void *)); + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } + break; + case Value::eValueTypeLoadAddress: + if (exe_ctx) + { + if (process) + { + lldb::addr_t pointer_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; + uint32_t addr_size = process->GetAddressByteSize(); + Error error; + if (process->ReadMemory(pointer_addr, &addr_bytes, addr_size, error) == addr_size) + { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), process->GetByteOrder(), addr_size); + lldb::offset_t addr_data_offset = 0; + stack.back().GetScalar() = addr_data.GetPointer(&addr_data_offset); + stack.back().ClearContext(); + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Failed to dereference pointer from 0x%" PRIx64 " for DW_OP_deref: %s\n", + pointer_addr, + error.AsCString()); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL process for DW_OP_deref.\n"); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_deref_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top + // stack entry and treats it as an address. The value retrieved from that + // address is pushed. In the DW_OP_deref_size operation, however, the + // size in bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended + // to the size of an address on the target machine before being pushed + // on the expression stack. + //---------------------------------------------------------------------- + case DW_OP_deref_size: + { + uint8_t size = opcodes.GetU8(&offset); + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) + { + case Value::eValueTypeHostAddress: + { + void *src = (void *)stack.back().GetScalar().ULongLong(); + intptr_t ptr; + ::memcpy (&ptr, src, sizeof(void *)); + // I can't decide whether the size operand should apply to the bytes in their + // lldb-host endianness or the target endianness.. I doubt this'll ever come up + // but I'll opt for assuming big endian regardless. + switch (size) + { + case 1: ptr = ptr & 0xff; break; + case 2: ptr = ptr & 0xffff; break; + case 3: ptr = ptr & 0xffffff; break; + case 4: ptr = ptr & 0xffffffff; break; + // the casts are added to work around the case where intptr_t is a 32 bit quantity; + // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this program. + case 5: ptr = (intptr_t) ptr & 0xffffffffffULL; break; + case 6: ptr = (intptr_t) ptr & 0xffffffffffffULL; break; + case 7: ptr = (intptr_t) ptr & 0xffffffffffffffULL; break; + default: break; + } + stack.back().GetScalar() = ptr; + stack.back().ClearContext(); + } + break; + case Value::eValueTypeLoadAddress: + if (exe_ctx) + { + if (process) + { + lldb::addr_t pointer_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; + Error error; + if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == size) + { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), process->GetByteOrder(), size); + lldb::offset_t addr_data_offset = 0; + switch (size) + { + case 1: stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); break; + case 2: stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); break; + case 4: stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); break; + case 8: stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); break; + default: stack.back().GetScalar() = addr_data.GetPointer(&addr_data_offset); + } + stack.back().ClearContext(); + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Failed to dereference pointer from 0x%" PRIx64 " for DW_OP_deref: %s\n", + pointer_addr, + error.AsCString()); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL process for DW_OP_deref.\n"); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_xderef_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at + // the top of the stack is treated as an address. The second stack + // entry is treated as an "address space identifier" for those + // architectures that support multiple address spaces. The top two + // stack elements are popped, a data item is retrieved through an + // implementation-defined address calculation and pushed as the new + // stack top. In the DW_OP_xderef_size operation, however, the size in + // bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended + // to the size of an address on the target machine before being pushed + // on the expression stack. + //---------------------------------------------------------------------- + case DW_OP_xderef_size: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); + return false; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_xderef + // OPERANDS: none + // DESCRIPTION: Provides an extended dereference mechanism. The entry at + // the top of the stack is treated as an address. The second stack entry + // is treated as an "address space identifier" for those architectures + // that support multiple address spaces. The top two stack elements are + // popped, a data item is retrieved through an implementation-defined + // address calculation and pushed as the new stack top. The size of the + // data retrieved from the dereferenced address is the size of an address + // on the target machine. + //---------------------------------------------------------------------- + case DW_OP_xderef: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); + return false; + + //---------------------------------------------------------------------- + // All DW_OP_constXXX opcodes have a single operand as noted below: + // + // Opcode Operand 1 + // --------------- ---------------------------------------------------- + // DW_OP_const1u 1-byte unsigned integer constant + // DW_OP_const1s 1-byte signed integer constant + // DW_OP_const2u 2-byte unsigned integer constant + // DW_OP_const2s 2-byte signed integer constant + // DW_OP_const4u 4-byte unsigned integer constant + // DW_OP_const4s 4-byte signed integer constant + // DW_OP_const8u 8-byte unsigned integer constant + // DW_OP_const8s 8-byte signed integer constant + // DW_OP_constu unsigned LEB128 integer constant + // DW_OP_consts signed LEB128 integer constant + //---------------------------------------------------------------------- + case DW_OP_const1u : stack.push_back(Scalar(( uint8_t)opcodes.GetU8 (&offset))); break; + case DW_OP_const1s : stack.push_back(Scalar(( int8_t)opcodes.GetU8 (&offset))); break; + case DW_OP_const2u : stack.push_back(Scalar((uint16_t)opcodes.GetU16 (&offset))); break; + case DW_OP_const2s : stack.push_back(Scalar(( int16_t)opcodes.GetU16 (&offset))); break; + case DW_OP_const4u : stack.push_back(Scalar((uint32_t)opcodes.GetU32 (&offset))); break; + case DW_OP_const4s : stack.push_back(Scalar(( int32_t)opcodes.GetU32 (&offset))); break; + case DW_OP_const8u : stack.push_back(Scalar((uint64_t)opcodes.GetU64 (&offset))); break; + case DW_OP_const8s : stack.push_back(Scalar(( int64_t)opcodes.GetU64 (&offset))); break; + case DW_OP_constu : stack.push_back(Scalar(opcodes.GetULEB128 (&offset))); break; + case DW_OP_consts : stack.push_back(Scalar(opcodes.GetSLEB128 (&offset))); break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_dup + // OPERANDS: none + // DESCRIPTION: duplicates the value at the top of the stack + //---------------------------------------------------------------------- + case DW_OP_dup: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); + return false; + } + else + stack.push_back(stack.back()); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_drop + // OPERANDS: none + // DESCRIPTION: pops the value at the top of the stack + //---------------------------------------------------------------------- + case DW_OP_drop: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); + return false; + } + else + stack.pop_back(); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_over + // OPERANDS: none + // DESCRIPTION: Duplicates the entry currently second in the stack at + // the top of the stack. + //---------------------------------------------------------------------- + case DW_OP_over: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_over."); + return false; + } + else + stack.push_back(stack[stack.size() - 2]); + break; + + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_pick + // OPERANDS: uint8_t index into the current stack + // DESCRIPTION: The stack entry with the specified index (0 through 255, + // inclusive) is pushed on the stack + //---------------------------------------------------------------------- + case DW_OP_pick: + { + uint8_t pick_idx = opcodes.GetU8(&offset); + if (pick_idx < stack.size()) + stack.push_back(stack[pick_idx]); + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Index %u out of range for DW_OP_pick.\n", pick_idx); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_swap + // OPERANDS: none + // DESCRIPTION: swaps the top two stack entries. The entry at the top + // of the stack becomes the second stack entry, and the second entry + // becomes the top of the stack + //---------------------------------------------------------------------- + case DW_OP_swap: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_swap."); + return false; + } + else + { + tmp = stack.back(); + stack.back() = stack[stack.size() - 2]; + stack[stack.size() - 2] = tmp; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_rot + // OPERANDS: none + // DESCRIPTION: Rotates the first three stack entries. The entry at + // the top of the stack becomes the third stack entry, the second + // entry becomes the top of the stack, and the third entry becomes + // the second entry. + //---------------------------------------------------------------------- + case DW_OP_rot: + if (stack.size() < 3) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 3 items for DW_OP_rot."); + return false; + } + else + { + size_t last_idx = stack.size() - 1; + Value old_top = stack[last_idx]; + stack[last_idx] = stack[last_idx - 1]; + stack[last_idx - 1] = stack[last_idx - 2]; + stack[last_idx - 2] = old_top; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_abs + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, interprets it as a signed + // value and pushes its absolute value. If the absolute value can not be + // represented, the result is undefined. + //---------------------------------------------------------------------- + case DW_OP_abs: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_abs."); + return false; + } + else if (stack.back().ResolveValue(exe_ctx).AbsoluteValue() == false) + { + if (error_ptr) + error_ptr->SetErrorString("Failed to take the absolute value of the first stack item."); + return false; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_and + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, performs a bitwise and + // operation on the two, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_and: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_and."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_div + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, divides the former second + // entry by the former top of the stack using signed division, and + // pushes the result. + //---------------------------------------------------------------------- + case DW_OP_div: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_div."); + return false; + } + else + { + tmp = stack.back(); + if (tmp.ResolveValue(exe_ctx).IsZero()) + { + if (error_ptr) + error_ptr->SetErrorString("Divide by zero."); + return false; + } + else + { + stack.pop_back(); + stack.back() = stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx); + if (!stack.back().ResolveValue(exe_ctx).IsValid()) + { + if (error_ptr) + error_ptr->SetErrorString("Divide failed."); + return false; + } + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_minus + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, subtracts the former top + // of the stack from the former second entry, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_minus: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_minus."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_mod + // OPERANDS: none + // DESCRIPTION: pops the top two stack values and pushes the result of + // the calculation: former second stack entry modulo the former top of + // the stack. + //---------------------------------------------------------------------- + case DW_OP_mod: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_mod."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx); + } + break; + + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_mul + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, multiplies them + // together, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_mul: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_mul."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_neg + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its negation. + //---------------------------------------------------------------------- + case DW_OP_neg: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_neg."); + return false; + } + else + { + if (stack.back().ResolveValue(exe_ctx).UnaryNegate() == false) + { + if (error_ptr) + error_ptr->SetErrorString("Unary negate failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_not + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, and pushes its bitwise + // complement + //---------------------------------------------------------------------- + case DW_OP_not: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_not."); + return false; + } + else + { + if (stack.back().ResolveValue(exe_ctx).OnesComplement() == false) + { + if (error_ptr) + error_ptr->SetErrorString("Logical NOT failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_or + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs a bitwise or + // operation on the two, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_or: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_or."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_plus + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, adds them together, and + // pushes the result. + //---------------------------------------------------------------------- + case DW_OP_plus: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_plus."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) + tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_plus_uconst + // OPERANDS: none + // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 + // constant operand and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_plus_uconst: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_plus_uconst."); + return false; + } + else + { + const uint64_t uconst_value = opcodes.GetULEB128(&offset); + // Implicit conversion from a UINT to a Scalar... + stack.back().ResolveValue(exe_ctx) += uconst_value; + if (!stack.back().ResolveValue(exe_ctx).IsValid()) + { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_plus_uconst failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_shl + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former + // second entry left by the number of bits specified by the former top + // of the stack, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_shl: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_shl."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_shr + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right logically (filling with zero bits) by the number of bits + // specified by the former top of the stack, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_shr: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_shr."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + if (stack.back().ResolveValue(exe_ctx).ShiftRightLogical(tmp.ResolveValue(exe_ctx)) == false) + { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_shr failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_shra + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, shifts the former second + // entry right arithmetically (divide the magnitude by 2, keep the same + // sign for the result) by the number of bits specified by the former + // top of the stack, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_shra: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_shra."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_xor + // OPERANDS: none + // DESCRIPTION: pops the top two stack entries, performs the bitwise + // exclusive-or operation on the two, and pushes the result. + //---------------------------------------------------------------------- + case DW_OP_xor: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_xor."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx); + } + break; + + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_skip + // OPERANDS: int16_t + // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte + // signed integer constant. The 2-byte constant is the number of bytes + // of the DWARF expression to skip forward or backward from the current + // operation, beginning after the 2-byte constant. + //---------------------------------------------------------------------- + case DW_OP_skip: + { + int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); + lldb::offset_t new_offset = offset + skip_offset; + if (new_offset >= opcodes_offset && new_offset < end_offset) + offset = new_offset; + else + { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_bra + // OPERANDS: int16_t + // DESCRIPTION: A conditional branch. Its single operand is a 2-byte + // signed integer constant. This operation pops the top of stack. If + // the value popped is not the constant 0, the 2-byte constant operand + // is the number of bytes of the DWARF expression to skip forward or + // backward from the current operation, beginning after the 2-byte + // constant. + //---------------------------------------------------------------------- + case DW_OP_bra: + { + tmp = stack.back(); + stack.pop_back(); + int16_t bra_offset = (int16_t)opcodes.GetU16(&offset); + Scalar zero(0); + if (tmp.ResolveValue(exe_ctx) != zero) + { + lldb::offset_t new_offset = offset + bra_offset; + if (new_offset >= opcodes_offset && new_offset < end_offset) + offset = new_offset; + else + { + if (error_ptr) + error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); + return false; + } + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_eq + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // equals (==) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_eq: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_eq."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_ge + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than or equal to (>=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_ge: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_ge."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_gt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // greater than (>) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_gt: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_gt."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_le + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than or equal to (<=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_le: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_le."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_lt + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // less than (<) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_lt: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_lt."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_ne + // OPERANDS: none + // DESCRIPTION: pops the top two stack values, compares using the + // not equal (!=) operator. + // STACK RESULT: push the constant value 1 onto the stack if the result + // of the operation is true or the constant value 0 if the result of the + // operation is false. + //---------------------------------------------------------------------- + case DW_OP_ne: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_ne."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + stack.back().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_litn + // OPERANDS: none + // DESCRIPTION: encode the unsigned literal values from 0 through 31. + // STACK RESULT: push the unsigned literal constant value onto the top + // of the stack. + //---------------------------------------------------------------------- + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + stack.push_back(Scalar(op - DW_OP_lit0)); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_regN + // OPERANDS: none + // DESCRIPTION: Push the value in register n on the top of the stack. + //---------------------------------------------------------------------- + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + { + reg_num = op - DW_OP_reg0; + + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } + break; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_regx + // OPERANDS: + // ULEB128 literal operand that encodes the register. + // DESCRIPTION: Push the value in register on the top of the stack. + //---------------------------------------------------------------------- + case DW_OP_regx: + { + reg_num = opcodes.GetULEB128(&offset); + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + stack.push_back(tmp); + else + return false; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_bregN + // OPERANDS: + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + //---------------------------------------------------------------------- + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + { + reg_num = op - DW_OP_breg0; + + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + break; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_bregx + // OPERANDS: 2 + // ULEB128 literal operand that encodes the register. + // SLEB128 offset from register N + // DESCRIPTION: Value is in memory at the address specified by register + // N plus an offset. + //---------------------------------------------------------------------- + case DW_OP_bregx: + { + reg_num = opcodes.GetULEB128(&offset); + + if (ReadRegisterValueAsScalar (reg_ctx, reg_kind, reg_num, error_ptr, tmp)) + { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; + tmp.ClearContext(); + stack.push_back(tmp); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + break; + + case DW_OP_fbreg: + if (exe_ctx) + { + if (frame) + { + Scalar value; + if (frame->GetFrameBaseValue(value, error_ptr)) + { + int64_t fbreg_offset = opcodes.GetSLEB128(&offset); + value += fbreg_offset; + stack.push_back(value); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("Invalid stack frame in context for DW_OP_fbreg opcode."); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_fbreg.\n"); + return false; + } + + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_nop + // OPERANDS: none + // DESCRIPTION: A place holder. It has no effect on the location stack + // or any of its values. + //---------------------------------------------------------------------- + case DW_OP_nop: + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_piece + // OPERANDS: 1 + // ULEB128: byte size of the piece + // DESCRIPTION: The operand describes the size in bytes of the piece of + // the object referenced by the DWARF expression whose result is at the + // top of the stack. If the piece is located in a register, but does not + // occupy the entire register, the placement of the piece within that + // register is defined by the ABI. + // + // Many compilers store a single variable in sets of registers, or store + // a variable partially in memory and partially in registers. + // DW_OP_piece provides a way of describing how large a part of a + // variable a particular DWARF expression refers to. + //---------------------------------------------------------------------- + case DW_OP_piece: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_piece."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_push_object_address + // OPERANDS: none + // DESCRIPTION: Pushes the address of the object currently being + // evaluated as part of evaluation of a user presented expression. + // This object may correspond to an independent variable described by + // its own DIE or it may be a component of an array, structure, or class + // whose address has been dynamically determined by an earlier step + // during user expression evaluation. + //---------------------------------------------------------------------- + case DW_OP_push_object_address: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_push_object_address."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_call2 + // OPERANDS: + // uint16_t compile unit relative offset of a DIE + // DESCRIPTION: Performs subroutine calls during evaluation + // of a DWARF expression. The operand is the 2-byte unsigned offset + // of a debugging information entry in the current compilation unit. + // + // Operand interpretation is exactly like that for DW_FORM_ref2. + // + // This operation transfers control of DWARF expression evaluation + // to the DW_AT_location attribute of the referenced DIE. If there is + // no such attribute, then there is no effect. Execution of the DWARF + // expression of a DW_AT_location attribute may add to and/or remove from + // values on the stack. Execution returns to the point following the call + // when the end of the attribute is reached. Values on the stack at the + // time of the call may be used as parameters by the called expression + // and values left on the stack by the called expression may be used as + // return values by prior agreement between the calling and called + // expressions. + //---------------------------------------------------------------------- + case DW_OP_call2: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_call2."); + return false; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_call4 + // OPERANDS: 1 + // uint32_t compile unit relative offset of a DIE + // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF + // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset + // of a debugging information entry in the current compilation unit. + // + // Operand interpretation DW_OP_call4 is exactly like that for + // DW_FORM_ref4. + // + // This operation transfers control of DWARF expression evaluation + // to the DW_AT_location attribute of the referenced DIE. If there is + // no such attribute, then there is no effect. Execution of the DWARF + // expression of a DW_AT_location attribute may add to and/or remove from + // values on the stack. Execution returns to the point following the call + // when the end of the attribute is reached. Values on the stack at the + // time of the call may be used as parameters by the called expression + // and values left on the stack by the called expression may be used as + // return values by prior agreement between the calling and called + // expressions. + //---------------------------------------------------------------------- + case DW_OP_call4: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_call4."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_stack_value + // OPERANDS: None + // DESCRIPTION: Specifies that the object does not exist in memory but + // rather is a constant value. The value from the top of the stack is + // the value to be used. This is the actual object value and not the + // location. + //---------------------------------------------------------------------- + case DW_OP_stack_value: + stack.back().SetValueType(Value::eValueTypeScalar); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_call_frame_cfa + // OPERANDS: None + // DESCRIPTION: Specifies a DWARF expression that pushes the value of + // the canonical frame address consistent with the call frame information + // located in .debug_frame (or in the FDEs of the eh_frame section). + //---------------------------------------------------------------------- + case DW_OP_call_frame_cfa: + if (frame) + { + // Note that we don't have to parse FDEs because this DWARF expression + // is commonly evaluated with a valid stack frame. + StackID id = frame->GetStackID(); + addr_t cfa = id.GetCallFrameAddress(); + if (cfa != LLDB_INVALID_ADDRESS) + { + stack.push_back(Scalar(cfa)); + stack.back().SetValueType (Value::eValueTypeHostAddress); + } + else + if (error_ptr) + error_ptr->SetErrorString ("Stack frame does not include a canonical frame address for DW_OP_call_frame_cfa opcode."); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("Invalid stack frame in context for DW_OP_call_frame_cfa opcode."); + return false; + } + break; + default: + if (log) + log->Printf("Unhandled opcode %s in DWARFExpression.", DW_OP_value_to_name(op)); + break; + } + } + + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString ("Stack empty after evaluation."); + return false; + } + else if (log && log->GetVerbose()) + { + size_t count = stack.size(); + log->Printf("Stack after operation has %lu values:", count); + for (size_t i=0; i<count; ++i) + { + StreamString new_value; + new_value.Printf("[%" PRIu64 "]", (uint64_t)i); + stack[i].Dump(&new_value); + log->Printf(" %s", new_value.GetData()); + } + } + + result = stack.back(); + return true; // Return true on success +} + diff --git a/contrib/llvm/tools/lldb/source/Expression/ExpressionSourceCode.cpp b/contrib/llvm/tools/lldb/source/Expression/ExpressionSourceCode.cpp new file mode 100644 index 0000000..aef3b9e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/ExpressionSourceCode.cpp @@ -0,0 +1,142 @@ +//===-- ExpressionSourceCode.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ExpressionSourceCode.h" + +#include "lldb/Core/StreamString.h" + +using namespace lldb_private; + +const char * +ExpressionSourceCode::g_expression_prefix = R"( +#undef NULL +#undef Nil +#undef nil +#undef YES +#undef NO +#define NULL (__null) +#define Nil (__null) +#define nil (__null) +#define YES ((BOOL)1) +#define NO ((BOOL)0) +typedef signed char BOOL; +typedef signed __INT8_TYPE__ int8_t; +typedef unsigned __INT8_TYPE__ uint8_t; +typedef signed __INT16_TYPE__ int16_t; +typedef unsigned __INT16_TYPE__ uint16_t; +typedef signed __INT32_TYPE__ int32_t; +typedef unsigned __INT32_TYPE__ uint32_t; +typedef signed __INT64_TYPE__ int64_t; +typedef unsigned __INT64_TYPE__ uint64_t; +typedef signed __INTPTR_TYPE__ intptr_t; +typedef unsigned __INTPTR_TYPE__ uintptr_t; +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef unsigned short unichar; +)"; + + +bool ExpressionSourceCode::GetText (std::string &text, lldb::LanguageType wrapping_language, bool const_object, bool static_method) const +{ + if (m_wrap) + { + switch (wrapping_language) + { + default: + return false; + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeObjC: + break; + } + + StreamString wrap_stream; + + switch (wrapping_language) + { + default: + break; + case lldb::eLanguageTypeC: + wrap_stream.Printf("%s \n" + "%s \n" + "void \n" + "%s(void *$__lldb_arg) \n" + "{ \n" + " %s; \n" + "} \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_body.c_str()); + break; + case lldb::eLanguageTypeC_plus_plus: + wrap_stream.Printf("%s \n" + "%s \n" + "void \n" + "$__lldb_class::%s(void *$__lldb_arg) %s\n" + "{ \n" + " %s; \n" + "} \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + (const_object ? "const" : ""), + m_body.c_str()); + break; + case lldb::eLanguageTypeObjC: + if (static_method) + { + wrap_stream.Printf("%s \n" + "%s \n" + "@interface $__lldb_objc_class ($__lldb_category) \n" + "+(void)%s:(void *)$__lldb_arg; \n" + "@end \n" + "@implementation $__lldb_objc_class ($__lldb_category) \n" + "+(void)%s:(void *)$__lldb_arg \n" + "{ \n" + " %s; \n" + "} \n" + "@end \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_name.c_str(), + m_body.c_str()); + } + else + { + wrap_stream.Printf("%s \n" + "%s \n" + "@interface $__lldb_objc_class ($__lldb_category) \n" + "-(void)%s:(void *)$__lldb_arg; \n" + "@end \n" + "@implementation $__lldb_objc_class ($__lldb_category) \n" + "-(void)%s:(void *)$__lldb_arg \n" + "{ \n" + " %s; \n" + "} \n" + "@end \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_name.c_str(), + m_body.c_str()); + } + break; + } + + text = wrap_stream.GetString(); + } + else + { + text.append(m_body); + } + + return true; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRDynamicChecks.cpp b/contrib/llvm/tools/lldb/source/Expression/IRDynamicChecks.cpp new file mode 100644 index 0000000..4030f14 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRDynamicChecks.cpp @@ -0,0 +1,659 @@ +//===-- IRDynamicChecks.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRDynamicChecks.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ClangUtilityFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.h" + +using namespace llvm; +using namespace lldb_private; + +static char ID; + +#define VALID_POINTER_CHECK_NAME "$__lldb_valid_pointer_check" +#define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check" + +static const char g_valid_pointer_check_text[] = +"extern \"C\" void\n" +"$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n" +"{\n" +" unsigned char $__lldb_local_val = *$__lldb_arg_ptr;\n" +"}"; + +DynamicCheckerFunctions::DynamicCheckerFunctions () +{ +} + +DynamicCheckerFunctions::~DynamicCheckerFunctions () +{ +} + +bool +DynamicCheckerFunctions::Install(Stream &error_stream, + ExecutionContext &exe_ctx) +{ + m_valid_pointer_check.reset(new ClangUtilityFunction(g_valid_pointer_check_text, + VALID_POINTER_CHECK_NAME)); + if (!m_valid_pointer_check->Install(error_stream, exe_ctx)) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process) + { + ObjCLanguageRuntime *objc_language_runtime = process->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker(VALID_OBJC_OBJECT_CHECK_NAME)); + + if (!m_objc_object_check->Install(error_stream, exe_ctx)) + return false; + } + } + + return true; +} + +bool +DynamicCheckerFunctions::DoCheckersExplainStop (lldb::addr_t addr, Stream &message) +{ + // FIXME: We have to get the checkers to know why they scotched the call in more detail, + // so we can print a better message here. + if (m_valid_pointer_check.get() != NULL && m_valid_pointer_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid pointer."); + return true; + } + else if (m_objc_object_check.get() != NULL && m_objc_object_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid ObjC Object or send it an unrecognized selector"); + return true; + } + return false; +} + + +static std::string +PrintValue(llvm::Value *V, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + V->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +//---------------------------------------------------------------------- +/// @class Instrumenter IRDynamicChecks.cpp +/// @brief Finds and instruments individual LLVM IR instructions +/// +/// When instrumenting LLVM IR, it is frequently desirable to first search +/// for instructions, and then later modify them. This way iterators +/// remain intact, and multiple passes can look at the same code base without +/// treading on each other's toes. +/// +/// The Instrumenter class implements this functionality. A client first +/// calls Inspect on a function, which populates a list of instructions to +/// be instrumented. Then, later, when all passes' Inspect functions have +/// been called, the client calls Instrument, which adds the desired +/// instrumentation. +/// +/// A subclass of Instrumenter must override InstrumentInstruction, which +/// is responsible for adding whatever instrumentation is necessary. +/// +/// A subclass of Instrumenter may override: +/// +/// - InspectInstruction [default: does nothing] +/// +/// - InspectBasicBlock [default: iterates through the instructions in a +/// basic block calling InspectInstruction] +/// +/// - InspectFunction [default: iterates through the basic blocks in a +/// function calling InspectBasicBlock] +//---------------------------------------------------------------------- +class Instrumenter { +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] module + /// The module being instrumented. + //------------------------------------------------------------------ + Instrumenter (llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + m_module(module), + m_checker_functions(checker_functions), + m_i8ptr_ty(NULL) + { + } + + virtual~Instrumenter () + { + } + + //------------------------------------------------------------------ + /// Inspect a function to find instructions to instrument + /// + /// @param[in] function + /// The function to inspect. + /// + /// @return + /// True on success; false on error. + //------------------------------------------------------------------ + bool Inspect (llvm::Function &function) + { + return InspectFunction(function); + } + + //------------------------------------------------------------------ + /// Instrument all the instructions found by Inspect() + /// + /// @return + /// True on success; false on error. + //------------------------------------------------------------------ + bool Instrument () + { + for (InstIterator ii = m_to_instrument.begin(), last_ii = m_to_instrument.end(); + ii != last_ii; + ++ii) + { + if (!InstrumentInstruction(*ii)) + return false; + } + + return true; + } +protected: + //------------------------------------------------------------------ + /// Add instrumentation to a single instruction + /// + /// @param[in] inst + /// The instruction to be instrumented. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0; + + //------------------------------------------------------------------ + /// Register a single instruction to be instrumented + /// + /// @param[in] inst + /// The instruction to be instrumented. + //------------------------------------------------------------------ + void RegisterInstruction(llvm::Instruction &i) + { + m_to_instrument.push_back(&i); + } + + //------------------------------------------------------------------ + /// Determine whether a single instruction is interesting to + /// instrument, and, if so, call RegisterInstruction + /// + /// @param[in] i + /// The instruction to be inspected. + /// + /// @return + /// False if there was an error scanning; true otherwise. + //------------------------------------------------------------------ + virtual bool InspectInstruction(llvm::Instruction &i) + { + return true; + } + + //------------------------------------------------------------------ + /// Scan a basic block to see if any instructions are interesting + /// + /// @param[in] bb + /// The basic block to be inspected. + /// + /// @return + /// False if there was an error scanning; true otherwise. + //------------------------------------------------------------------ + virtual bool InspectBasicBlock(llvm::BasicBlock &bb) + { + for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end(); + ii != last_ii; + ++ii) + { + if (!InspectInstruction(*ii)) + return false; + } + + return true; + } + + //------------------------------------------------------------------ + /// Scan a function to see if any instructions are interesting + /// + /// @param[in] f + /// The function to be inspected. + /// + /// @return + /// False if there was an error scanning; true otherwise. + //------------------------------------------------------------------ + virtual bool InspectFunction(llvm::Function &f) + { + for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end(); + bbi != last_bbi; + ++bbi) + { + if (!InspectBasicBlock(*bbi)) + return false; + } + + return true; + } + + //------------------------------------------------------------------ + /// Build a function pointer for a function with signature + /// void (*)(uint8_t*) with a given address + /// + /// @param[in] start_address + /// The address of the function. + /// + /// @return + /// The function pointer, for use in a CallInst. + //------------------------------------------------------------------ + llvm::Value *BuildPointerValidatorFunc(lldb::addr_t start_address) + { + IntegerType *intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + (m_module.getPointerSize() == llvm::Module::Pointer64) ? 64 : 32); + + llvm::Type *param_array[1]; + + param_array[0] = const_cast<llvm::PointerType*>(GetI8PtrTy()); + + ArrayRef<llvm::Type*> params(param_array, 1); + + FunctionType *fun_ty = FunctionType::get(llvm::Type::getVoidTy(m_module.getContext()), params, true); + PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, start_address, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); + } + + //------------------------------------------------------------------ + /// Build a function pointer for a function with signature + /// void (*)(uint8_t*, uint8_t*) with a given address + /// + /// @param[in] start_address + /// The address of the function. + /// + /// @return + /// The function pointer, for use in a CallInst. + //------------------------------------------------------------------ + llvm::Value *BuildObjectCheckerFunc(lldb::addr_t start_address) + { + IntegerType *intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + (m_module.getPointerSize() == llvm::Module::Pointer64) ? 64 : 32); + + llvm::Type *param_array[2]; + + param_array[0] = const_cast<llvm::PointerType*>(GetI8PtrTy()); + param_array[1] = const_cast<llvm::PointerType*>(GetI8PtrTy()); + + ArrayRef<llvm::Type*> params(param_array, 2); + + FunctionType *fun_ty = FunctionType::get(llvm::Type::getVoidTy(m_module.getContext()), params, true); + PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, start_address, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); + } + + PointerType *GetI8PtrTy() + { + if (!m_i8ptr_ty) + m_i8ptr_ty = llvm::Type::getInt8PtrTy(m_module.getContext()); + + return m_i8ptr_ty; + } + + typedef std::vector <llvm::Instruction *> InstVector; + typedef InstVector::iterator InstIterator; + + InstVector m_to_instrument; ///< List of instructions the inspector found + llvm::Module &m_module; ///< The module which is being instrumented + DynamicCheckerFunctions &m_checker_functions; ///< The dynamic checker functions for the process +private: + PointerType *m_i8ptr_ty; +}; + +class ValidPointerChecker : public Instrumenter +{ +public: + ValidPointerChecker (llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + Instrumenter(module, checker_functions), + m_valid_pointer_check_func(NULL) + { + } + + virtual ~ValidPointerChecker () + { + } +private: + bool InstrumentInstruction(llvm::Instruction *inst) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("Instrumenting load/store instruction: %s\n", + PrintValue(inst).c_str()); + + if (!m_valid_pointer_check_func) + m_valid_pointer_check_func = BuildPointerValidatorFunc(m_checker_functions.m_valid_pointer_check->StartAddress()); + + llvm::Value *dereferenced_ptr = NULL; + + if (llvm::LoadInst *li = dyn_cast<llvm::LoadInst> (inst)) + dereferenced_ptr = li->getPointerOperand(); + else if (llvm::StoreInst *si = dyn_cast<llvm::StoreInst> (inst)) + dereferenced_ptr = si->getPointerOperand(); + else + return false; + + // Insert an instruction to cast the loaded value to int8_t* + + BitCastInst *bit_cast = new BitCastInst(dereferenced_ptr, + GetI8PtrTy(), + "", + inst); + + // Insert an instruction to call the helper with the result + + llvm::Value *arg_array[1]; + + arg_array[0] = bit_cast; + + llvm::ArrayRef<llvm::Value *> args(arg_array, 1); + + CallInst::Create(m_valid_pointer_check_func, + args, + "", + inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) + { + if (dyn_cast<llvm::LoadInst> (&i) || + dyn_cast<llvm::StoreInst> (&i)) + RegisterInstruction(i); + + return true; + } + + llvm::Value *m_valid_pointer_check_func; +}; + +class ObjcObjectChecker : public Instrumenter +{ +public: + ObjcObjectChecker(llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + Instrumenter(module, checker_functions), + m_objc_object_check_func(NULL) + { + } + + virtual + ~ObjcObjectChecker () + { + } + + enum msgSend_type + { + eMsgSend = 0, + eMsgSendSuper, + eMsgSendSuper_stret, + eMsgSend_fpret, + eMsgSend_stret + }; + + std::map <llvm::Instruction *, msgSend_type> msgSend_types; + +private: + bool InstrumentInstruction(llvm::Instruction *inst) + { + CallInst *call_inst = dyn_cast<CallInst>(inst); + + if (!call_inst) + return false; // call_inst really shouldn't be NULL, because otherwise InspectInstruction wouldn't have registered it + + if (!m_objc_object_check_func) + m_objc_object_check_func = BuildObjectCheckerFunc(m_checker_functions.m_objc_object_check->StartAddress()); + + // id objc_msgSend(id theReceiver, SEL theSelector, ...) + + llvm::Value *target_object; + llvm::Value *selector; + + switch (msgSend_types[inst]) + { + case eMsgSend: + case eMsgSend_fpret: + target_object = call_inst->getArgOperand(0); + selector = call_inst->getArgOperand(1); + break; + case eMsgSend_stret: + target_object = call_inst->getArgOperand(1); + selector = call_inst->getArgOperand(2); + break; + case eMsgSendSuper: + case eMsgSendSuper_stret: + return true; + } + + // These objects should always be valid according to Sean Calannan + assert (target_object); + assert (selector); + + // Insert an instruction to cast the receiver id to int8_t* + + BitCastInst *bit_cast = new BitCastInst(target_object, + GetI8PtrTy(), + "", + inst); + + // Insert an instruction to call the helper with the result + + llvm::Value *arg_array[2]; + + arg_array[0] = bit_cast; + arg_array[1] = selector; + + ArrayRef<llvm::Value*> args(arg_array, 2); + + CallInst::Create(m_objc_object_check_func, + args, + "", + inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + CallInst *call_inst = dyn_cast<CallInst>(&i); + + if (call_inst) + { + // This metadata is set by IRForTarget::MaybeHandleCall(). + + MDNode *metadata = call_inst->getMetadata("lldb.call.realName"); + + if (!metadata) + return true; + + if (metadata->getNumOperands() != 1) + { + if (log) + log->Printf("Function call metadata has %d operands for [%p] %s", metadata->getNumOperands(), call_inst, PrintValue(call_inst).c_str()); + return false; + } + + MDString *real_name = dyn_cast<MDString>(metadata->getOperand(0)); + + if (!real_name) + { + if (log) + log->Printf("Function call metadata is not an MDString for [%p] %s", call_inst, PrintValue(call_inst).c_str()); + return false; + } + + std::string name_str = real_name->getString(); + const char* name_cstr = name_str.c_str(); + + if (log) + log->Printf("Found call to %s: %s\n", name_cstr, PrintValue(call_inst).c_str()); + + if (name_str.find("objc_msgSend") == std::string::npos) + return true; + + if (!strcmp(name_cstr, "objc_msgSend")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSend_stret")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend_stret; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSend_fpret")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSend_fpret; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSendSuper")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSendSuper; + return true; + } + + if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) + { + RegisterInstruction(i); + msgSend_types[&i] = eMsgSendSuper_stret; + return true; + } + + if (log) + log->Printf("Function name '%s' contains 'objc_msgSend' but is not handled", name_str.c_str()); + + return true; + } + + return true; + } + + llvm::Value *m_objc_object_check_func; +}; + +IRDynamicChecks::IRDynamicChecks(DynamicCheckerFunctions &checker_functions, + const char *func_name) : + ModulePass(ID), + m_func_name(func_name), + m_checker_functions(checker_functions) +{ +} + +IRDynamicChecks::~IRDynamicChecks() +{ +} + +bool +IRDynamicChecks::runOnModule(llvm::Module &M) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + llvm::Function* function = M.getFunction(StringRef(m_func_name.c_str())); + + if (!function) + { + if (log) + log->Printf("Couldn't find %s() in the module", m_func_name.c_str()); + + return false; + } + + if (m_checker_functions.m_valid_pointer_check.get()) + { + ValidPointerChecker vpc(M, m_checker_functions); + + if (!vpc.Inspect(*function)) + return false; + + if (!vpc.Instrument()) + return false; + } + + if (m_checker_functions.m_objc_object_check.get()) + { + ObjcObjectChecker ooc(M, m_checker_functions); + + if (!ooc.Inspect(*function)) + return false; + + if (!ooc.Instrument()) + return false; + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + M.print(oss, NULL); + + oss.flush(); + + log->Printf ("Module after dynamic checks: \n%s", s.c_str()); + } + + return true; +} + +void +IRDynamicChecks::assignPassManager(PMStack &PMS, + PassManagerType T) +{ +} + +PassManagerType +IRDynamicChecks::getPotentialPassManagerType() const +{ + return PMT_ModulePassManager; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp b/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp new file mode 100644 index 0000000..16ef6e5 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp @@ -0,0 +1,704 @@ +//===-- IRExecutionUnit.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 +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/SourceMgr.h" +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +IRExecutionUnit::IRExecutionUnit (std::unique_ptr<llvm::LLVMContext> &context_ap, + std::unique_ptr<llvm::Module> &module_ap, + ConstString &name, + const lldb::TargetSP &target_sp, + std::vector<std::string> &cpu_features) : + IRMemoryMap(target_sp), + m_context_ap(context_ap.release()), + m_module_ap(module_ap.release()), + m_module(m_module_ap.get()), + m_cpu_features(cpu_features), + m_name(name), + m_did_jit(false), + m_function_load_addr(LLDB_INVALID_ADDRESS), + m_function_end_load_addr(LLDB_INVALID_ADDRESS) +{ +} + +lldb::addr_t +IRExecutionUnit::WriteNow (const uint8_t *bytes, + size_t size, + Error &error) +{ + lldb::addr_t allocation_process_addr = Malloc (size, + 8, + lldb::ePermissionsWritable | lldb::ePermissionsReadable, + eAllocationPolicyMirror, + error); + + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + + WriteMemory(allocation_process_addr, bytes, size, error); + + if (!error.Success()) + { + Error err; + Free (allocation_process_addr, err); + + return LLDB_INVALID_ADDRESS; + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + DataBufferHeap my_buffer(size, 0); + Error err; + ReadMemory(my_buffer.GetBytes(), allocation_process_addr, size, err); + + if (err.Success()) + { + DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(), lldb::eByteOrderBig, 8); + + StreamString ss; + + my_extractor.Dump(&ss, 0, lldb::eFormatBytesWithASCII, 1, my_buffer.GetByteSize(), 32, allocation_process_addr, 0, 0); + + log->PutCString(ss.GetData()); + } + } + + return allocation_process_addr; +} + +void +IRExecutionUnit::FreeNow (lldb::addr_t allocation) +{ + if (allocation == LLDB_INVALID_ADDRESS) + return; + + Error err; + + Free(allocation, err); +} + +Error +IRExecutionUnit::DisassembleFunction (Stream &stream, + lldb::ProcessSP &process_wp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ExecutionContext exe_ctx(process_wp); + + Error ret; + + ret.Clear(); + + lldb::addr_t func_local_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t func_remote_addr = LLDB_INVALID_ADDRESS; + + for (JittedFunction &function : m_jitted_functions) + { + if (strstr(function.m_name.c_str(), m_name.AsCString())) + { + func_local_addr = function.m_local_addr; + func_remote_addr = function.m_remote_addr; + } + } + + if (func_local_addr == LLDB_INVALID_ADDRESS) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't find function %s for disassembly", m_name.AsCString()); + return ret; + } + + if (log) + log->Printf("Found function, has local address 0x%" PRIx64 " and remote address 0x%" PRIx64, (uint64_t)func_local_addr, (uint64_t)func_remote_addr); + + std::pair <lldb::addr_t, lldb::addr_t> func_range; + + func_range = GetRemoteRangeForLocal(func_local_addr); + + if (func_range.first == 0 && func_range.second == 0) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't find code range for function %s", m_name.AsCString()); + return ret; + } + + if (log) + log->Printf("Function's code range is [0x%" PRIx64 "+0x%" PRIx64 "]", func_range.first, func_range.second); + + Target *target = exe_ctx.GetTargetPtr(); + if (!target) + { + ret.SetErrorToGenericError(); + ret.SetErrorString("Couldn't find the target"); + return ret; + } + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(func_range.second, 0)); + + Process *process = exe_ctx.GetProcessPtr(); + Error err; + process->ReadMemory(func_remote_addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), err); + + if (!err.Success()) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Couldn't read from process: %s", err.AsCString("unknown error")); + return ret; + } + + ArchSpec arch(target->GetArchitecture()); + + const char *plugin_name = NULL; + const char *flavor_string = NULL; + lldb::DisassemblerSP disassembler_sp = Disassembler::FindPlugin(arch, flavor_string, plugin_name); + + if (!disassembler_sp) + { + ret.SetErrorToGenericError(); + ret.SetErrorStringWithFormat("Unable to find disassembler plug-in for %s architecture.", arch.GetArchitectureName()); + return ret; + } + + if (!process) + { + ret.SetErrorToGenericError(); + ret.SetErrorString("Couldn't find the process"); + return ret; + } + + DataExtractor extractor(buffer_sp, + process->GetByteOrder(), + target->GetArchitecture().GetAddressByteSize()); + + if (log) + { + log->Printf("Function data has contents:"); + extractor.PutToLog (log, + 0, + extractor.GetByteSize(), + func_remote_addr, + 16, + DataExtractor::TypeUInt8); + } + + disassembler_sp->DecodeInstructions (Address (func_remote_addr), extractor, 0, UINT32_MAX, false, false); + + InstructionList &instruction_list = disassembler_sp->GetInstructionList(); + const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize(); + + for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize(); + instruction_index < num_instructions; + ++instruction_index) + { + Instruction *instruction = instruction_list.GetInstructionAtIndex(instruction_index).get(); + instruction->Dump (&stream, + max_opcode_byte_size, + true, + true, + &exe_ctx); + stream.PutChar('\n'); + } + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disassembler_sp->GetInstructionList().Clear(); + return ret; +} + +static void ReportInlineAsmError(const llvm::SMDiagnostic &diagnostic, void *Context, unsigned LocCookie) +{ + Error *err = static_cast<Error*>(Context); + + if (err && err->Success()) + { + err->SetErrorToGenericError(); + err->SetErrorStringWithFormat("Inline assembly error: %s", diagnostic.getMessage().str().c_str()); + } +} + +void +IRExecutionUnit::GetRunnableInfo(Error &error, + lldb::addr_t &func_addr, + lldb::addr_t &func_end) +{ + lldb::ProcessSP process_sp(GetProcessWP().lock()); + + func_addr = LLDB_INVALID_ADDRESS; + func_end = LLDB_INVALID_ADDRESS; + + if (!process_sp) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write the JIT compiled code into the process because the process is invalid"); + return; + } + + if (m_did_jit) + { + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; + + return; + }; + + m_did_jit = true; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + std::string error_string; + + if (log) + { + std::string s; + llvm::raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf ("Module being sent to JIT: \n%s", s.c_str()); + } + + llvm::Triple triple(m_module->getTargetTriple()); + llvm::Function *function = m_module->getFunction (m_name.AsCString()); + llvm::Reloc::Model relocModel; + llvm::CodeModel::Model codeModel; + + if (triple.isOSBinFormatELF()) + { + relocModel = llvm::Reloc::Static; + // This will be small for 32-bit and large for 64-bit. + codeModel = llvm::CodeModel::JITDefault; + } + else + { + relocModel = llvm::Reloc::PIC_; + codeModel = llvm::CodeModel::Small; + } + + m_module_ap->getContext().setInlineAsmDiagnosticHandler(ReportInlineAsmError, &error); + + llvm::EngineBuilder builder(m_module_ap.get()); + + builder.setEngineKind(llvm::EngineKind::JIT) + .setErrorStr(&error_string) + .setRelocationModel(relocModel) + .setJITMemoryManager(new MemoryManager(*this)) + .setOptLevel(llvm::CodeGenOpt::Less) + .setAllocateGVsWithCode(true) + .setCodeModel(codeModel) + .setUseMCJIT(true); + + llvm::StringRef mArch; + llvm::StringRef mCPU; + llvm::SmallVector<std::string, 0> mAttrs; + + for (std::string &feature : m_cpu_features) + mAttrs.push_back(feature); + + llvm::TargetMachine *target_machine = builder.selectTarget(triple, + mArch, + mCPU, + mAttrs); + + m_execution_engine_ap.reset(builder.create(target_machine)); + + if (!m_execution_engine_ap.get()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't JIT the function: %s", error_string.c_str()); + return; + } + else + { + m_module_ap.release(); // ownership was transferred + } + + m_execution_engine_ap->DisableLazyCompilation(); + + // We don't actually need the function pointer here, this just forces it to get resolved. + + void *fun_ptr = m_execution_engine_ap->getPointerToFunction(function); + + if (!error.Success()) + { + // We got an error through our callback! + return; + } + + if (!function) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't find '%s' in the JITted module", m_name.AsCString()); + return; + } + + if (!fun_ptr) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("'%s' was in the JITted module but wasn't lowered", m_name.AsCString()); + return; + } + + m_jitted_functions.push_back (JittedFunction(m_name.AsCString(), (lldb::addr_t)fun_ptr)); + + CommitAllocations(process_sp); + ReportAllocations(*m_execution_engine_ap); + WriteData(process_sp); + + for (JittedFunction &jitted_function : m_jitted_functions) + { + jitted_function.m_remote_addr = GetRemoteAddressForLocal (jitted_function.m_local_addr); + + if (!jitted_function.m_name.compare(m_name.AsCString())) + { + AddrRange func_range = GetRemoteRangeForLocal(jitted_function.m_local_addr); + m_function_end_load_addr = func_range.first + func_range.second; + m_function_load_addr = jitted_function.m_remote_addr; + } + } + + if (log) + { + log->Printf("Code can be run in the target."); + + StreamString disassembly_stream; + + Error err = DisassembleFunction(disassembly_stream, process_sp); + + if (!err.Success()) + { + log->Printf("Couldn't disassemble function : %s", err.AsCString("unknown error")); + } + else + { + log->Printf("Function disassembly:\n%s", disassembly_stream.GetData()); + } + } + + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; + + return; +} + +IRExecutionUnit::~IRExecutionUnit () +{ + m_module_ap.reset(); + m_execution_engine_ap.reset(); + m_context_ap.reset(); +} + +IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) : + m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()), + m_parent (parent) +{ +} + +void +IRExecutionUnit::MemoryManager::setMemoryWritable () +{ + m_default_mm_ap->setMemoryWritable(); +} + +void +IRExecutionUnit::MemoryManager::setMemoryExecutable () +{ + m_default_mm_ap->setMemoryExecutable(); +} + + +uint8_t * +IRExecutionUnit::MemoryManager::startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize) +{ + return m_default_mm_ap->startFunctionBody(F, ActualSize); +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateStub(const llvm::GlobalValue* F, + unsigned StubSize, + unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + StubSize, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateStub (F=%p, StubSize=%u, Alignment=%u) = %p", + F, StubSize, Alignment, return_value); + } + + return return_value; +} + +void +IRExecutionUnit::MemoryManager::endFunctionBody(const llvm::Function *F, + uint8_t *FunctionStart, + uint8_t *FunctionEnd) +{ + m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd); +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateSpace(Size=%" PRIu64 ", Alignment=%u) = %p", + (uint64_t)Size, Alignment, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateCodeSection(Size, Alignment, SectionID); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsExecutable, + Size, + Alignment, + SectionID)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateDataSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID, + bool IsReadOnly) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateDataSection(Size, Alignment, SectionID, IsReadOnly); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment, + SectionID)); + if (log) + { + log->Printf("IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateGlobal(uintptr_t Size, + unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateGlobal(Size=0x%" PRIx64 ", Alignment=%u) = %p", + (uint64_t)Size, Alignment, return_value); + } + + return return_value; +} + +void +IRExecutionUnit::MemoryManager::deallocateFunctionBody(void *Body) +{ + m_default_mm_ap->deallocateFunctionBody(Body); +} + +lldb::addr_t +IRExecutionUnit::GetRemoteAddressForLocal (lldb::addr_t local_address) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (AllocationRecord &record : m_records) + { + if (local_address >= record.m_host_address && + local_address < record.m_host_address + record.m_size) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + lldb::addr_t ret = record.m_process_address + (local_address - record.m_host_address); + + if (log) + { + log->Printf("IRExecutionUnit::GetRemoteAddressForLocal() found 0x%" PRIx64 " in [0x%" PRIx64 "..0x%" PRIx64 "], and returned 0x%" PRIx64 " from [0x%" PRIx64 "..0x%" PRIx64 "].", + local_address, + (uint64_t)record.m_host_address, + (uint64_t)record.m_host_address + (uint64_t)record.m_size, + ret, + record.m_process_address, + record.m_process_address + record.m_size); + } + + return ret; + } + } + + return LLDB_INVALID_ADDRESS; +} + +IRExecutionUnit::AddrRange +IRExecutionUnit::GetRemoteRangeForLocal (lldb::addr_t local_address) +{ + for (AllocationRecord &record : m_records) + { + if (local_address >= record.m_host_address && + local_address < record.m_host_address + record.m_size) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return AddrRange(0, 0); + + return AddrRange(record.m_process_address, record.m_size); + } + } + + return AddrRange (0, 0); +} + +bool +IRExecutionUnit::CommitAllocations (lldb::ProcessSP &process_sp) +{ + bool ret = true; + + lldb_private::Error err; + + for (AllocationRecord &record : m_records) + { + if (record.m_process_address != LLDB_INVALID_ADDRESS) + continue; + + + record.m_process_address = Malloc(record.m_size, + record.m_alignment, + record.m_permissions, + eAllocationPolicyProcessOnly, + err); + + if (!err.Success()) + { + ret = false; + break; + } + } + + if (!ret) + { + for (AllocationRecord &record : m_records) + { + if (record.m_process_address != LLDB_INVALID_ADDRESS) + { + Free(record.m_process_address, err); + record.m_process_address = LLDB_INVALID_ADDRESS; + } + } + } + + return ret; +} + +void +IRExecutionUnit::ReportAllocations (llvm::ExecutionEngine &engine) +{ + for (AllocationRecord &record : m_records) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + continue; + + if (record.m_section_id == eSectionIDInvalid) + continue; + + engine.mapSectionAddress((void*)record.m_host_address, record.m_process_address); + } + + // Trigger re-application of relocations. + engine.finalizeObject(); +} + +bool +IRExecutionUnit::WriteData (lldb::ProcessSP &process_sp) +{ + for (AllocationRecord &record : m_records) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Error err; + + WriteMemory (record.m_process_address, (uint8_t*)record.m_host_address, record.m_size, err); + } + + return true; +} + +void +IRExecutionUnit::AllocationRecord::dump (Log *log) +{ + if (!log) + return; + + log->Printf("[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d)", + (unsigned long long)m_host_address, + (unsigned long long)m_size, + (unsigned long long)m_process_address, + (unsigned)m_alignment, + (unsigned)m_section_id); +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp b/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp new file mode 100644 index 0000000..cac3fdf --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp @@ -0,0 +1,2879 @@ +//===-- IRForTarget.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRForTarget.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/IR/ValueSymbolTable.h" + +#include "clang/AST/ASTContext.h" + +#include "lldb/Core/dwarf.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" + +#include <map> + +using namespace llvm; + +static char ID; + +IRForTarget::StaticDataAllocator::StaticDataAllocator(lldb_private::IRExecutionUnit &execution_unit) : + m_execution_unit(execution_unit), + m_stream_string(lldb_private::Stream::eBinary, execution_unit.GetAddressByteSize(), execution_unit.GetByteOrder()), + m_allocation(LLDB_INVALID_ADDRESS) +{ +} + +IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker) : + m_maker(maker), + m_values() +{ +} + +IRForTarget::FunctionValueCache::~FunctionValueCache() +{ +} + +llvm::Value *IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) +{ + if (!m_values.count(function)) + { + llvm::Value *ret = m_maker(function); + m_values[function] = ret; + return ret; + } + return m_values[function]; +} + +lldb::addr_t IRForTarget::StaticDataAllocator::Allocate() +{ + lldb_private::Error err; + + if (m_allocation != LLDB_INVALID_ADDRESS) + { + m_execution_unit.FreeNow(m_allocation); + m_allocation = LLDB_INVALID_ADDRESS; + } + + m_allocation = m_execution_unit.WriteNow((const uint8_t*)m_stream_string.GetData(), m_stream_string.GetSize(), err); + + return m_allocation; +} + +static llvm::Value *FindEntryInstruction (llvm::Function *function) +{ + if (function->empty()) + return NULL; + + return function->getEntryBlock().getFirstNonPHIOrDbg(); +} + +IRForTarget::IRForTarget (lldb_private::ClangExpressionDeclMap *decl_map, + bool resolve_vars, + lldb_private::IRExecutionUnit &execution_unit, + lldb_private::Stream *error_stream, + const char *func_name) : + ModulePass(ID), + m_resolve_vars(resolve_vars), + m_func_name(func_name), + m_module(NULL), + m_decl_map(decl_map), + m_data_allocator(execution_unit), + m_CFStringCreateWithBytes(NULL), + m_sel_registerName(NULL), + m_error_stream(error_stream), + m_result_store(NULL), + m_result_is_pointer(false), + m_reloc_placeholder(NULL), + m_entry_instruction_finder (FindEntryInstruction) +{ +} + +/* Handy utility functions used at several places in the code */ + +static std::string +PrintValue(const Value *value, bool truncate = false) +{ + std::string s; + if (value) + { + raw_string_ostream rso(s); + value->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + } + return s; +} + +static std::string +PrintType(const llvm::Type *type, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + type->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +IRForTarget::~IRForTarget() +{ +} + +bool +IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) +{ + llvm_function.setLinkage(GlobalValue::ExternalLinkage); + + std::string name = llvm_function.getName().str(); + + return true; +} + +bool +IRForTarget::GetFunctionAddress (llvm::Function *fun, + uint64_t &fun_addr, + lldb_private::ConstString &name, + Constant **&value_ptr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + fun_addr = LLDB_INVALID_ADDRESS; + name.Clear(); + value_ptr = NULL; + + if (fun->isIntrinsic()) + { + Intrinsic::ID intrinsic_id = (Intrinsic::ID)fun->getIntrinsicID(); + + switch (intrinsic_id) + { + default: + if (log) + log->Printf("Unresolved intrinsic \"%s\"", Intrinsic::getName(intrinsic_id).c_str()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Call to unhandled compiler intrinsic '%s'\n", Intrinsic::getName(intrinsic_id).c_str()); + + return false; + case Intrinsic::memcpy: + { + static lldb_private::ConstString g_memcpy_str ("memcpy"); + name = g_memcpy_str; + } + break; + case Intrinsic::memset: + { + static lldb_private::ConstString g_memset_str ("memset"); + name = g_memset_str; + } + break; + } + + if (log && name) + log->Printf("Resolved intrinsic name \"%s\"", name.GetCString()); + } + else + { + name.SetCStringWithLength (fun->getName().data(), fun->getName().size()); + } + + // Find the address of the function. + + clang::NamedDecl *fun_decl = DeclForGlobal (fun); + + if (fun_decl) + { + if (!m_decl_map->GetFunctionInfo (fun_decl, fun_addr)) + { + lldb_private::ConstString altnernate_name; + bool found_it = m_decl_map->GetFunctionAddress (name, fun_addr); + if (!found_it) + { + // Check for an alternate mangling for "std::basic_string<char>" + // that is part of the itanium C++ name mangling scheme + const char *name_cstr = name.GetCString(); + if (name_cstr && strncmp(name_cstr, "_ZNKSbIcE", strlen("_ZNKSbIcE")) == 0) + { + std::string alternate_mangling("_ZNKSs"); + alternate_mangling.append (name_cstr + strlen("_ZNKSbIcE")); + altnernate_name.SetCString(alternate_mangling.c_str()); + found_it = m_decl_map->GetFunctionAddress (altnernate_name, fun_addr); + } + } + + if (!found_it) + { + lldb_private::Mangled mangled_name(name); + lldb_private::Mangled alt_mangled_name(altnernate_name); + if (log) + { + if (alt_mangled_name) + log->Printf("Function \"%s\" (alternate name \"%s\") has no address", + mangled_name.GetName().GetCString(), + alt_mangled_name.GetName().GetCString()); + else + log->Printf("Function \"%s\" had no address", + mangled_name.GetName().GetCString()); + } + + if (m_error_stream) + { + if (alt_mangled_name) + m_error_stream->Printf("error: call to a function '%s' (alternate name '%s') that is not present in the target\n", + mangled_name.GetName().GetCString(), + alt_mangled_name.GetName().GetCString()); + else if (mangled_name.GetMangledName()) + m_error_stream->Printf("error: call to a function '%s' ('%s') that is not present in the target\n", + mangled_name.GetName().GetCString(), + mangled_name.GetMangledName().GetCString()); + else + m_error_stream->Printf("error: call to a function '%s' that is not present in the target\n", + mangled_name.GetName().GetCString()); + } + return false; + } + } + } + else + { + if (!m_decl_map->GetFunctionAddress (name, fun_addr)) + { + if (log) + log->Printf ("Metadataless function \"%s\" had no address", name.GetCString()); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Call to a symbol-only function '%s' that is not present in the target\n", name.GetCString()); + + return false; + } + } + + if (log) + log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), fun_addr); + + return true; +} + +llvm::Constant * +IRForTarget::BuildFunctionPointer (llvm::Type *type, + uint64_t ptr) +{ + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + PointerType *fun_ptr_ty = PointerType::getUnqual(type); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, ptr, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); +} + +void +IRForTarget::RegisterFunctionMetadata(LLVMContext &context, + llvm::Value *function_ptr, + const char *name) +{ + for (Value::use_iterator i = function_ptr->use_begin(), e = function_ptr->use_end(); + i != e; + ++i) + { + Value *user = *i; + + if (Instruction *user_inst = dyn_cast<Instruction>(user)) + { + MDString* md_name = MDString::get(context, StringRef(name)); + + MDNode *metadata = MDNode::get(context, md_name); + + user_inst->setMetadata("lldb.call.realName", metadata); + } + else + { + RegisterFunctionMetadata (context, user, name); + } + } +} + +bool +IRForTarget::ResolveFunctionPointers(llvm::Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (llvm::Module::iterator fi = llvm_module.begin(); + fi != llvm_module.end(); + ++fi) + { + Function *fun = fi; + + bool is_decl = fun->isDeclaration(); + + if (log) + log->Printf("Examining %s function %s", (is_decl ? "declaration" : "non-declaration"), fun->getName().str().c_str()); + + if (!is_decl) + continue; + + if (fun->hasNUses(0)) + continue; // ignore + + uint64_t addr = LLDB_INVALID_ADDRESS; + lldb_private::ConstString name; + Constant **value_ptr = NULL; + + if (!GetFunctionAddress(fun, + addr, + name, + value_ptr)) + return false; // GetFunctionAddress reports its own errors + + Constant *value = BuildFunctionPointer(fun->getFunctionType(), addr); + + RegisterFunctionMetadata (llvm_module.getContext(), fun, name.AsCString()); + + if (value_ptr) + *value_ptr = value; + + // If we are replacing a function with the nobuiltin attribute, it may + // be called with the builtin attribute on call sites. Remove any such + // attributes since it's illegal to have a builtin call to something + // other than a nobuiltin function. + if (fun->hasFnAttribute(Attribute::NoBuiltin)) { + Attribute builtin = Attribute::get(fun->getContext(), Attribute::Builtin); + + for (auto u = fun->use_begin(), e = fun->use_end(); u != e; ++u) { + if (auto call = dyn_cast<CallInst>(*u)) { + call->removeAttribute(AttributeSet::FunctionIndex, builtin); + } + } + } + + fun->replaceAllUsesWith(value); + } + + return true; +} + + +clang::NamedDecl * +IRForTarget::DeclForGlobal (const GlobalValue *global_val, Module *module) +{ + NamedMDNode *named_metadata = module->getNamedMetadata("clang.global.decl.ptrs"); + + if (!named_metadata) + return NULL; + + unsigned num_nodes = named_metadata->getNumOperands(); + unsigned node_index; + + for (node_index = 0; + node_index < num_nodes; + ++node_index) + { + MDNode *metadata_node = named_metadata->getOperand(node_index); + + if (!metadata_node) + return NULL; + + if (metadata_node->getNumOperands() != 2) + continue; + + if (metadata_node->getOperand(0) != global_val) + continue; + + ConstantInt *constant_int = dyn_cast<ConstantInt>(metadata_node->getOperand(1)); + + if (!constant_int) + return NULL; + + uintptr_t ptr = constant_int->getZExtValue(); + + return reinterpret_cast<clang::NamedDecl *>(ptr); + } + + return NULL; +} + +clang::NamedDecl * +IRForTarget::DeclForGlobal (GlobalValue *global_val) +{ + return DeclForGlobal(global_val, m_module); +} + +bool +IRForTarget::CreateResultVariable (llvm::Function &llvm_function) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_resolve_vars) + return true; + + // Find the result variable. If it doesn't exist, we can give up right here. + + ValueSymbolTable& value_symbol_table = m_module->getValueSymbolTable(); + + std::string result_name_str; + const char *result_name = NULL; + + for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); + vi != ve; + ++vi) + { + result_name_str = vi->first().str(); + const char *value_name = result_name_str.c_str(); + + if (strstr(value_name, "$__lldb_expr_result_ptr") && + strncmp(value_name, "_ZGV", 4)) + { + result_name = value_name; + m_result_is_pointer = true; + break; + } + + if (strstr(value_name, "$__lldb_expr_result") && + strncmp(value_name, "_ZGV", 4)) + { + result_name = value_name; + m_result_is_pointer = false; + break; + } + } + + if (!result_name) + { + if (log) + log->PutCString("Couldn't find result variable"); + + return true; + } + + if (log) + log->Printf("Result name: \"%s\"", result_name); + + Value *result_value = m_module->getNamedValue(result_name); + + if (!result_value) + { + if (log) + log->PutCString("Result variable had no data"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable's name (%s) exists, but not its definition\n", result_name); + + return false; + } + + if (log) + log->Printf("Found result in the IR: \"%s\"", PrintValue(result_value, false).c_str()); + + GlobalVariable *result_global = dyn_cast<GlobalVariable>(result_value); + + if (!result_global) + { + if (log) + log->PutCString("Result variable isn't a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) is defined, but is not a global variable\n", result_name); + + return false; + } + + clang::NamedDecl *result_decl = DeclForGlobal (result_global); + if (!result_decl) + { + if (log) + log->PutCString("Result variable doesn't have a corresponding Decl"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) does not have a corresponding Clang entity\n", result_name); + + return false; + } + + if (log) + { + std::string decl_desc_str; + raw_string_ostream decl_desc_stream(decl_desc_str); + result_decl->print(decl_desc_stream); + decl_desc_stream.flush(); + + log->Printf("Found result decl: \"%s\"", decl_desc_str.c_str()); + } + + clang::VarDecl *result_var = dyn_cast<clang::VarDecl>(result_decl); + if (!result_var) + { + if (log) + log->PutCString("Result variable Decl isn't a VarDecl"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s)'s corresponding Clang entity isn't a variable\n", result_name); + + return false; + } + + // Get the next available result name from m_decl_map and create the persistent + // variable for it + + // If the result is an Lvalue, it is emitted as a pointer; see + // ASTResultSynthesizer::SynthesizeBodyResult. + if (m_result_is_pointer) + { + clang::QualType pointer_qual_type = result_var->getType(); + const clang::Type *pointer_type = pointer_qual_type.getTypePtr(); + + const clang::PointerType *pointer_pointertype = pointer_type->getAs<clang::PointerType>(); + const clang::ObjCObjectPointerType *pointer_objcobjpointertype = pointer_type->getAs<clang::ObjCObjectPointerType>(); + + if (pointer_pointertype) + { + clang::QualType element_qual_type = pointer_pointertype->getPointeeType(); + + m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), + &result_decl->getASTContext()); + } + else if (pointer_objcobjpointertype) + { + clang::QualType element_qual_type = clang::QualType(pointer_objcobjpointertype->getObjectType(), 0); + + m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), + &result_decl->getASTContext()); + } + else + { + if (log) + log->PutCString("Expected result to have pointer type, but it did not"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Lvalue result (%s) is not a pointer variable\n", result_name); + + return false; + } + } + else + { + m_result_type = lldb_private::TypeFromParser(result_var->getType().getAsOpaquePtr(), + &result_decl->getASTContext()); + } + + if (m_result_type.GetBitSize() == 0) + { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + if (log) + log->Printf("Result type has size 0"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Size of result type '%s' couldn't be determined\n", + type_desc_stream.GetData()); + return false; + } + + if (log) + { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + log->Printf("Result decl type: \"%s\"", type_desc_stream.GetData()); + } + + m_result_name = lldb_private::ConstString("$RESULT_NAME"); + + if (log) + log->Printf("Creating a new result global: \"%s\" with size 0x%" PRIx64, + m_result_name.GetCString(), + m_result_type.GetByteSize()); + + // Construct a new result global and set up its metadata + + GlobalVariable *new_result_global = new GlobalVariable((*m_module), + result_global->getType()->getElementType(), + false, /* not constant */ + GlobalValue::ExternalLinkage, + NULL, /* no initializer */ + m_result_name.GetCString ()); + + // It's too late in compilation to create a new VarDecl for this, but we don't + // need to. We point the metadata at the old VarDecl. This creates an odd + // anomaly: a variable with a Value whose name is something like $0 and a + // Decl whose name is $__lldb_expr_result. This condition is handled in + // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is + // fixed up. + + ConstantInt *new_constant_int = ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()), + reinterpret_cast<uint64_t>(result_decl), + false); + + llvm::Value* values[2]; + values[0] = new_result_global; + values[1] = new_constant_int; + + ArrayRef<Value*> value_ref(values, 2); + + MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); + NamedMDNode *named_metadata = m_module->getNamedMetadata("clang.global.decl.ptrs"); + named_metadata->addOperand(persistent_global_md); + + if (log) + log->Printf("Replacing \"%s\" with \"%s\"", + PrintValue(result_global).c_str(), + PrintValue(new_result_global).c_str()); + + if (result_global->hasNUses(0)) + { + // We need to synthesize a store for this variable, because otherwise + // there's nothing to put into its equivalent persistent variable. + + BasicBlock &entry_block(llvm_function.getEntryBlock()); + Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg()); + + if (!first_entry_instruction) + return false; + + if (!result_global->hasInitializer()) + { + if (log) + log->Printf("Couldn't find initializer for unused variable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) has no writes and no initializer\n", result_name); + + return false; + } + + Constant *initializer = result_global->getInitializer(); + + StoreInst *synthesized_store = new StoreInst(initializer, + new_result_global, + first_entry_instruction); + + if (log) + log->Printf("Synthesized result store \"%s\"\n", PrintValue(synthesized_store).c_str()); + } + else + { + result_global->replaceAllUsesWith(new_result_global); + } + + if (!m_decl_map->AddPersistentVariable(result_decl, + m_result_name, + m_result_type, + true, + m_result_is_pointer)) + return false; + + result_global->eraseFromParent(); + + return true; +} + +#if 0 +static void DebugUsers(Log *log, Value *value, uint8_t depth) +{ + if (!depth) + return; + + depth--; + + if (log) + log->Printf(" <Begin %d users>", value->getNumUses()); + + for (Value::use_iterator ui = value->use_begin(), ue = value->use_end(); + ui != ue; + ++ui) + { + if (log) + log->Printf(" <Use %p> %s", *ui, PrintValue(*ui).c_str()); + DebugUsers(log, *ui, depth); + } + + if (log) + log->Printf(" <End uses>"); +} +#endif + +bool +IRForTarget::RewriteObjCConstString (llvm::GlobalVariable *ns_str, + llvm::GlobalVariable *cstr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Type *ns_str_ty = ns_str->getType(); + + Type *i8_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() + == Module::Pointer64) ? 64 : 32); + Type *i32_ty = Type::getInt32Ty(m_module->getContext()); + Type *i8_ty = Type::getInt8Ty(m_module->getContext()); + + if (!m_CFStringCreateWithBytes) + { + lldb::addr_t CFStringCreateWithBytes_addr; + + static lldb_private::ConstString g_CFStringCreateWithBytes_str ("CFStringCreateWithBytes"); + + if (!m_decl_map->GetFunctionAddress (g_CFStringCreateWithBytes_str, CFStringCreateWithBytes_addr)) + { + if (log) + log->PutCString("Couldn't find CFStringCreateWithBytes in the target"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Rewriting an Objective-C constant string requires CFStringCreateWithBytes\n"); + + return false; + } + + if (log) + log->Printf("Found CFStringCreateWithBytes at 0x%" PRIx64, CFStringCreateWithBytes_addr); + + // Build the function type: + // + // CFStringRef CFStringCreateWithBytes ( + // CFAllocatorRef alloc, + // const UInt8 *bytes, + // CFIndex numBytes, + // CFStringEncoding encoding, + // Boolean isExternalRepresentation + // ); + // + // We make the following substitutions: + // + // CFStringRef -> i8* + // CFAllocatorRef -> i8* + // UInt8 * -> i8* + // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its pointer size for now) + // CFStringEncoding -> i32 + // Boolean -> i8 + + Type *arg_type_array[5]; + + arg_type_array[0] = i8_ptr_ty; + arg_type_array[1] = i8_ptr_ty; + arg_type_array[2] = intptr_ty; + arg_type_array[3] = i32_ty; + arg_type_array[4] = i8_ty; + + ArrayRef<Type *> CFSCWB_arg_types(arg_type_array, 5); + + llvm::Type *CFSCWB_ty = FunctionType::get(ns_str_ty, CFSCWB_arg_types, false); + + // Build the constant containing the pointer to the function + PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty); + Constant *CFSCWB_addr_int = ConstantInt::get(intptr_ty, CFStringCreateWithBytes_addr, false); + m_CFStringCreateWithBytes = ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty); + } + + ConstantDataSequential *string_array = NULL; + + if (cstr) + string_array = dyn_cast<ConstantDataSequential>(cstr->getInitializer()); + + Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty); + Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty) : Constant::getNullValue(i8_ptr_ty); + Constant *numBytes_arg = ConstantInt::get(intptr_ty, cstr ? string_array->getNumElements() - 1 : 0, false); + Constant *encoding_arg = ConstantInt::get(i32_ty, 0x0600, false); /* 0x0600 is kCFStringEncodingASCII */ + Constant *isExternal_arg = ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */ + + Value *argument_array[5]; + + argument_array[0] = alloc_arg; + argument_array[1] = bytes_arg; + argument_array[2] = numBytes_arg; + argument_array[3] = encoding_arg; + argument_array[4] = isExternal_arg; + + ArrayRef <Value *> CFSCWB_arguments(argument_array, 5); + + FunctionValueCache CFSCWB_Caller ([this, &CFSCWB_arguments] (llvm::Function *function)->llvm::Value * { + return CallInst::Create(m_CFStringCreateWithBytes, + CFSCWB_arguments, + "CFStringCreateWithBytes", + llvm::cast<Instruction>(m_entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(ns_str, CFSCWB_Caller, m_entry_instruction_finder)) + { + if (log) + log->PutCString("Couldn't replace the NSString with the result of the call"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't replace an Objective-C constant string with a dynamic string\n"); + + return false; + } + + ns_str->eraseFromParent(); + + return true; +} + +bool +IRForTarget::RewriteObjCConstStrings() +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ValueSymbolTable& value_symbol_table = m_module->getValueSymbolTable(); + + for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); + vi != ve; + ++vi) + { + std::string value_name = vi->first().str(); + const char *value_name_cstr = value_name.c_str(); + + if (strstr(value_name_cstr, "_unnamed_cfstring_")) + { + Value *nsstring_value = vi->second; + + GlobalVariable *nsstring_global = dyn_cast<GlobalVariable>(nsstring_value); + + if (!nsstring_global) + { + if (log) + log->PutCString("NSString variable is not a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string is not a global variable\n"); + + return false; + } + + if (!nsstring_global->hasInitializer()) + { + if (log) + log->PutCString("NSString variable does not have an initializer"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string does not have an initializer\n"); + + return false; + } + + ConstantStruct *nsstring_struct = dyn_cast<ConstantStruct>(nsstring_global->getInitializer()); + + if (!nsstring_struct) + { + if (log) + log->PutCString("NSString variable's initializer is not a ConstantStruct"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string is not a structure constant\n"); + + return false; + } + + // We expect the following structure: + // + // struct { + // int *isa; + // int flags; + // char *str; + // long length; + // }; + + if (nsstring_struct->getNumOperands() != 4) + { + if (log) + log->Printf("NSString variable's initializer structure has an unexpected number of members. Should be 4, is %d", nsstring_struct->getNumOperands()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: The struct for an Objective-C constant string is not as expected\n"); + + return false; + } + + Constant *nsstring_member = nsstring_struct->getOperand(2); + + if (!nsstring_member) + { + if (log) + log->PutCString("NSString initializer's str element was empty"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string does not have a string initializer\n"); + + return false; + } + + ConstantExpr *nsstring_expr = dyn_cast<ConstantExpr>(nsstring_member); + + if (!nsstring_expr) + { + if (log) + log->PutCString("NSString initializer's str element is not a ConstantExpr"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not constant\n"); + + return false; + } + + if (nsstring_expr->getOpcode() != Instruction::GetElementPtr) + { + if (log) + log->Printf("NSString initializer's str element is not a GetElementPtr expression, it's a %s", nsstring_expr->getOpcodeName()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not an array\n"); + + return false; + } + + Constant *nsstring_cstr = nsstring_expr->getOperand(0); + + GlobalVariable *cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr); + + if (!cstr_global) + { + if (log) + log->PutCString("NSString initializer's str element is not a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a global\n"); + + return false; + } + + if (!cstr_global->hasInitializer()) + { + if (log) + log->PutCString("NSString initializer's str element does not have an initializer"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to initialized data\n"); + + return false; + } + + /* + if (!cstr_array) + { + if (log) + log->PutCString("NSString initializer's str element is not a ConstantArray"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to an array\n"); + + return false; + } + + if (!cstr_array->isCString()) + { + if (log) + log->PutCString("NSString initializer's str element is not a C string array"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a C string\n"); + + return false; + } + */ + + ConstantDataArray *cstr_array = dyn_cast<ConstantDataArray>(cstr_global->getInitializer()); + + if (log) + { + if (cstr_array) + log->Printf("Found NSString constant %s, which contains \"%s\"", value_name_cstr, cstr_array->getAsString().str().c_str()); + else + log->Printf("Found NSString constant %s, which contains \"\"", value_name_cstr); + } + + if (!cstr_array) + cstr_global = NULL; + + if (!RewriteObjCConstString(nsstring_global, cstr_global)) + { + if (log) + log->PutCString("Error rewriting the constant string"); + + // We don't print an error message here because RewriteObjCConstString has done so for us. + + return false; + } + } + } + + for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); + vi != ve; + ++vi) + { + std::string value_name = vi->first().str(); + const char *value_name_cstr = value_name.c_str(); + + if (!strcmp(value_name_cstr, "__CFConstantStringClassReference")) + { + GlobalVariable *gv = dyn_cast<GlobalVariable>(vi->second); + + if (!gv) + { + if (log) + log->PutCString("__CFConstantStringClassReference is not a global variable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Found a CFConstantStringClassReference, but it is not a global object\n"); + + return false; + } + + gv->eraseFromParent(); + + break; + } + } + + return true; +} + +static bool IsObjCSelectorRef (Value *value) +{ + GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value); + + if (!global_variable || !global_variable->hasName() || !global_variable->getName().startswith("\01L_OBJC_SELECTOR_REFERENCES_")) + return false; + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::RewriteObjCSelector (Instruction* selector_load) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + LoadInst *load = dyn_cast<LoadInst>(selector_load); + + if (!load) + return false; + + // Unpack the message name from the selector. In LLVM IR, an objc_msgSend gets represented as + // + // %tmp = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" ; <i8*> + // %call = call i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; <i8*> + // + // where %obj is the object pointer and %tmp is the selector. + // + // @"\01L_OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_". + // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string. + + // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr) and get the string from its target + + GlobalVariable *_objc_selector_references_ = dyn_cast<GlobalVariable>(load->getPointerOperand()); + + if (!_objc_selector_references_ || !_objc_selector_references_->hasInitializer()) + return false; + + Constant *osr_initializer = _objc_selector_references_->getInitializer(); + + ConstantExpr *osr_initializer_expr = dyn_cast<ConstantExpr>(osr_initializer); + + if (!osr_initializer_expr || osr_initializer_expr->getOpcode() != Instruction::GetElementPtr) + return false; + + Value *osr_initializer_base = osr_initializer_expr->getOperand(0); + + if (!osr_initializer_base) + return false; + + // Find the string's initializer (a ConstantArray) and get the string from it + + GlobalVariable *_objc_meth_var_name_ = dyn_cast<GlobalVariable>(osr_initializer_base); + + if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer()) + return false; + + Constant *omvn_initializer = _objc_meth_var_name_->getInitializer(); + + ConstantDataArray *omvn_initializer_array = dyn_cast<ConstantDataArray>(omvn_initializer); + + if (!omvn_initializer_array->isString()) + return false; + + std::string omvn_initializer_string = omvn_initializer_array->getAsString(); + + if (log) + log->Printf("Found Objective-C selector reference \"%s\"", omvn_initializer_string.c_str()); + + // Construct a call to sel_registerName + + if (!m_sel_registerName) + { + lldb::addr_t sel_registerName_addr; + + static lldb_private::ConstString g_sel_registerName_str ("sel_registerName"); + if (!m_decl_map->GetFunctionAddress (g_sel_registerName_str, sel_registerName_addr)) + return false; + + if (log) + log->Printf("Found sel_registerName at 0x%" PRIx64, sel_registerName_addr); + + // Build the function type: struct objc_selector *sel_registerName(uint8_t*) + + // The below code would be "more correct," but in actuality what's required is uint8_t* + //Type *sel_type = StructType::get(m_module->getContext()); + //Type *sel_ptr_type = PointerType::getUnqual(sel_type); + Type *sel_ptr_type = Type::getInt8PtrTy(m_module->getContext()); + + Type *type_array[1]; + + type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext()); + + ArrayRef<Type *> srN_arg_types(type_array, 1); + + llvm::Type *srN_type = FunctionType::get(sel_ptr_type, srN_arg_types, false); + + // Build the constant containing the pointer to the function + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type); + Constant *srN_addr_int = ConstantInt::get(intptr_ty, sel_registerName_addr, false); + m_sel_registerName = ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty); + } + + Value *argument_array[1]; + + Constant *omvn_pointer = ConstantExpr::getBitCast(_objc_meth_var_name_, Type::getInt8PtrTy(m_module->getContext())); + + argument_array[0] = omvn_pointer; + + ArrayRef<Value *> srN_arguments(argument_array, 1); + + CallInst *srN_call = CallInst::Create(m_sel_registerName, + srN_arguments, + "sel_registerName", + selector_load); + + // Replace the load with the call in all users + + selector_load->replaceAllUsesWith(srN_call); + + selector_load->eraseFromParent(); + + return true; +} + +bool +IRForTarget::RewriteObjCSelectors (BasicBlock &basic_block) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + BasicBlock::iterator ii; + + typedef SmallVector <Instruction*, 2> InstrList; + typedef InstrList::iterator InstrIterator; + + InstrList selector_loads; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + if (LoadInst *load = dyn_cast<LoadInst>(&inst)) + if (IsObjCSelectorRef(load->getPointerOperand())) + selector_loads.push_back(&inst); + } + + InstrIterator iter; + + for (iter = selector_loads.begin(); + iter != selector_loads.end(); + ++iter) + { + if (!RewriteObjCSelector(*iter)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't change a static reference to an Objective-C selector to a dynamic reference\n"); + + if (log) + log->PutCString("Couldn't rewrite a reference to an Objective-C selector"); + + return false; + } + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::RewritePersistentAlloc (llvm::Instruction *persistent_alloc) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + AllocaInst *alloc = dyn_cast<AllocaInst>(persistent_alloc); + + MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr"); + + if (!alloc_md || !alloc_md->getNumOperands()) + return false; + + ConstantInt *constant_int = dyn_cast<ConstantInt>(alloc_md->getOperand(0)); + + if (!constant_int) + return false; + + // We attempt to register this as a new persistent variable with the DeclMap. + + uintptr_t ptr = constant_int->getZExtValue(); + + clang::VarDecl *decl = reinterpret_cast<clang::VarDecl *>(ptr); + + lldb_private::TypeFromParser result_decl_type (decl->getType().getAsOpaquePtr(), + &decl->getASTContext()); + + StringRef decl_name (decl->getName()); + lldb_private::ConstString persistent_variable_name (decl_name.data(), decl_name.size()); + if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name, result_decl_type, false, false)) + return false; + + GlobalVariable *persistent_global = new GlobalVariable((*m_module), + alloc->getType(), + false, /* not constant */ + GlobalValue::ExternalLinkage, + NULL, /* no initializer */ + alloc->getName().str().c_str()); + + // What we're going to do here is make believe this was a regular old external + // variable. That means we need to make the metadata valid. + + NamedMDNode *named_metadata = m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs"); + + llvm::Value* values[2]; + values[0] = persistent_global; + values[1] = constant_int; + + ArrayRef<llvm::Value*> value_ref(values, 2); + + MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); + named_metadata->addOperand(persistent_global_md); + + // Now, since the variable is a pointer variable, we will drop in a load of that + // pointer variable. + + LoadInst *persistent_load = new LoadInst (persistent_global, "", alloc); + + if (log) + log->Printf("Replacing \"%s\" with \"%s\"", + PrintValue(alloc).c_str(), + PrintValue(persistent_load).c_str()); + + alloc->replaceAllUsesWith(persistent_load); + alloc->eraseFromParent(); + + return true; +} + +bool +IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) +{ + if (!m_resolve_vars) + return true; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + BasicBlock::iterator ii; + + typedef SmallVector <Instruction*, 2> InstrList; + typedef InstrList::iterator InstrIterator; + + InstrList pvar_allocs; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + if (AllocaInst *alloc = dyn_cast<AllocaInst>(&inst)) + { + llvm::StringRef alloc_name = alloc->getName(); + + if (alloc_name.startswith("$") && + !alloc_name.startswith("$__lldb")) + { + if (alloc_name.find_first_of("0123456789") == 1) + { + if (log) + log->Printf("Rejecting a numeric persistent variable."); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Names starting with $0, $1, ... are reserved for use as result names\n"); + + return false; + } + + pvar_allocs.push_back(alloc); + } + } + } + + InstrIterator iter; + + for (iter = pvar_allocs.begin(); + iter != pvar_allocs.end(); + ++iter) + { + if (!RewritePersistentAlloc(*iter)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite the creation of a persistent variable\n"); + + if (log) + log->PutCString("Couldn't rewrite the creation of a persistent variable"); + + return false; + } + } + + return true; +} + +bool +IRForTarget::MaterializeInitializer (uint8_t *data, Constant *initializer) +{ + if (!initializer) + return true; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log && log->GetVerbose()) + log->Printf(" MaterializeInitializer(%p, %s)", data, PrintValue(initializer).c_str()); + + Type *initializer_type = initializer->getType(); + + if (ConstantInt *int_initializer = dyn_cast<ConstantInt>(initializer)) + { + memcpy (data, int_initializer->getValue().getRawData(), m_target_data->getTypeStoreSize(initializer_type)); + return true; + } + else if (ConstantDataArray *array_initializer = dyn_cast<ConstantDataArray>(initializer)) + { + if (array_initializer->isString()) + { + std::string array_initializer_string = array_initializer->getAsString(); + memcpy (data, array_initializer_string.c_str(), m_target_data->getTypeStoreSize(initializer_type)); + } + else + { + ArrayType *array_initializer_type = array_initializer->getType(); + Type *array_element_type = array_initializer_type->getElementType(); + + size_t element_size = m_target_data->getTypeAllocSize(array_element_type); + + for (unsigned i = 0; i < array_initializer->getNumOperands(); ++i) + { + Value *operand_value = array_initializer->getOperand(i); + Constant *operand_constant = dyn_cast<Constant>(operand_value); + + if (!operand_constant) + return false; + + if (!MaterializeInitializer(data + (i * element_size), operand_constant)) + return false; + } + } + return true; + } + else if (ConstantStruct *struct_initializer = dyn_cast<ConstantStruct>(initializer)) + { + StructType *struct_initializer_type = struct_initializer->getType(); + const StructLayout *struct_layout = m_target_data->getStructLayout(struct_initializer_type); + + for (unsigned i = 0; + i < struct_initializer->getNumOperands(); + ++i) + { + if (!MaterializeInitializer(data + struct_layout->getElementOffset(i), struct_initializer->getOperand(i))) + return false; + } + return true; + } + else if (isa<ConstantAggregateZero>(initializer)) + { + memset(data, 0, m_target_data->getTypeStoreSize(initializer_type)); + return true; + } + return false; +} + +bool +IRForTarget::MaterializeInternalVariable (GlobalVariable *global_variable) +{ + if (GlobalVariable::isExternalLinkage(global_variable->getLinkage())) + return false; + + if (global_variable == m_reloc_placeholder) + return true; + + uint64_t offset = m_data_allocator.GetStream().GetSize(); + + llvm::Type *variable_type = global_variable->getType(); + + Constant *initializer = global_variable->getInitializer(); + + llvm::Type *initializer_type = initializer->getType(); + + size_t size = m_target_data->getTypeAllocSize(initializer_type); + size_t align = m_target_data->getPrefTypeAlignment(initializer_type); + + const size_t mask = (align - 1); + uint64_t aligned_offset = (offset + mask) & ~mask; + m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); + offset = aligned_offset; + + lldb_private::DataBufferHeap data(size, '\0'); + + if (initializer) + if (!MaterializeInitializer(data.GetBytes(), initializer)) + return false; + + m_data_allocator.GetStream().Write(data.GetBytes(), data.GetByteSize()); + + Constant *new_pointer = BuildRelocation(variable_type, offset); + + global_variable->replaceAllUsesWith(new_pointer); + + global_variable->eraseFromParent(); + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::MaybeHandleVariable (Value *llvm_value_ptr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("MaybeHandleVariable (%s)", PrintValue(llvm_value_ptr).c_str()); + + if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(llvm_value_ptr)) + { + switch (constant_expr->getOpcode()) + { + default: + break; + case Instruction::GetElementPtr: + case Instruction::BitCast: + Value *s = constant_expr->getOperand(0); + if (!MaybeHandleVariable(s)) + return false; + } + } + else if (GlobalVariable *global_variable = dyn_cast<GlobalVariable>(llvm_value_ptr)) + { + if (!GlobalValue::isExternalLinkage(global_variable->getLinkage())) + return MaterializeInternalVariable(global_variable); + + clang::NamedDecl *named_decl = DeclForGlobal(global_variable); + + if (!named_decl) + { + if (IsObjCSelectorRef(llvm_value_ptr)) + return true; + + if (!global_variable->hasExternalLinkage()) + return true; + + if (log) + log->Printf("Found global variable \"%s\" without metadata", global_variable->getName().str().c_str()); + + return false; + } + + std::string name (named_decl->getName().str()); + + clang::ValueDecl *value_decl = dyn_cast<clang::ValueDecl>(named_decl); + if (value_decl == NULL) + return false; + + lldb_private::ClangASTType clang_type(&value_decl->getASTContext(), value_decl->getType()); + + const Type *value_type = NULL; + + if (name[0] == '$') + { + // The $__lldb_expr_result name indicates the the return value has allocated as + // a static variable. Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, + // accesses to this static variable need to be redirected to the result of dereferencing + // a pointer that is passed in as one of the arguments. + // + // Consequently, when reporting the size of the type, we report a pointer type pointing + // to the type of $__lldb_expr_result, not the type itself. + // + // We also do this for any user-declared persistent variables. + clang_type = clang_type.GetPointerType(); + value_type = PointerType::get(global_variable->getType(), 0); + } + else + { + value_type = global_variable->getType(); + } + + const uint64_t value_size = clang_type.GetByteSize(); + off_t value_alignment = (clang_type.GetTypeBitAlign() + 7ull) / 8ull; + + if (log) + { + log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64 ", align %" PRId64 "]", + name.c_str(), + clang_type.GetQualType().getAsString().c_str(), + PrintType(value_type).c_str(), + value_size, + value_alignment); + } + + + if (named_decl && !m_decl_map->AddValueToStruct(named_decl, + lldb_private::ConstString (name.c_str()), + llvm_value_ptr, + value_size, + value_alignment)) + { + if (!global_variable->hasExternalLinkage()) + return true; + else if (HandleSymbol (global_variable)) + return true; + else + return false; + } + } + else if (dyn_cast<llvm::Function>(llvm_value_ptr)) + { + if (log) + log->Printf("Function pointers aren't handled right now"); + + return false; + } + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::HandleSymbol (Value *symbol) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + lldb_private::ConstString name(symbol->getName().str().c_str()); + + lldb::addr_t symbol_addr = m_decl_map->GetSymbolAddress (name, lldb::eSymbolTypeAny); + + if (symbol_addr == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("Symbol \"%s\" had no address", name.GetCString()); + + return false; + } + + if (log) + log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), symbol_addr); + + Type *symbol_type = symbol->getType(); + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + Constant *symbol_addr_int = ConstantInt::get(intptr_ty, symbol_addr, false); + + Value *symbol_addr_ptr = ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type); + + if (log) + log->Printf("Replacing %s with %s", PrintValue(symbol).c_str(), PrintValue(symbol_addr_ptr).c_str()); + + symbol->replaceAllUsesWith(symbol_addr_ptr); + + return true; +} + +bool +IRForTarget::MaybeHandleCallArguments (CallInst *Old) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("MaybeHandleCallArguments(%s)", PrintValue(Old).c_str()); + + for (unsigned op_index = 0, num_ops = Old->getNumArgOperands(); + op_index < num_ops; + ++op_index) + if (!MaybeHandleVariable(Old->getArgOperand(op_index))) // conservatively believe that this is a store + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call.\n"); + + return false; + } + + return true; +} + +bool +IRForTarget::HandleObjCClass(Value *classlist_reference) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + GlobalVariable *global_variable = dyn_cast<GlobalVariable>(classlist_reference); + + if (!global_variable) + return false; + + Constant *initializer = global_variable->getInitializer(); + + if (!initializer) + return false; + + if (!initializer->hasName()) + return false; + + StringRef name(initializer->getName()); + lldb_private::ConstString name_cstr(name.str().c_str()); + lldb::addr_t class_ptr = m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass); + + if (log) + log->Printf("Found reference to Objective-C class %s (0x%llx)", name_cstr.AsCString(), (unsigned long long)class_ptr); + + if (class_ptr == LLDB_INVALID_ADDRESS) + return false; + + if (global_variable->use_begin() == global_variable->use_end()) + return false; + + SmallVector<LoadInst *, 2> load_instructions; + + for (Value::use_iterator i = global_variable->use_begin(), e = global_variable->use_end(); + i != e; + ++i) + { + if (LoadInst *load_instruction = dyn_cast<LoadInst>(*i)) + load_instructions.push_back(load_instruction); + } + + if (load_instructions.empty()) + return false; + + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() + == Module::Pointer64) ? 64 : 32); + + Constant *class_addr = ConstantInt::get(intptr_ty, (uint64_t)class_ptr); + + for (LoadInst *load_instruction : load_instructions) + { + Constant *class_bitcast = ConstantExpr::getIntToPtr(class_addr, load_instruction->getType()); + + load_instruction->replaceAllUsesWith(class_bitcast); + + load_instruction->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::RemoveCXAAtExit (BasicBlock &basic_block) +{ + BasicBlock::iterator ii; + + std::vector<CallInst *> calls_to_remove; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + CallInst *call = dyn_cast<CallInst>(&inst); + + // MaybeHandleCallArguments handles error reporting; we are silent here + if (!call) + continue; + + bool remove = false; + + llvm::Function *func = call->getCalledFunction(); + + if (func && func->getName() == "__cxa_atexit") + remove = true; + + llvm::Value *val = call->getCalledValue(); + + if (val && val->getName() == "__cxa_atexit") + remove = true; + + if (remove) + calls_to_remove.push_back(call); + } + + for (std::vector<CallInst *>::iterator ci = calls_to_remove.begin(), ce = calls_to_remove.end(); + ci != ce; + ++ci) + { + (*ci)->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::ResolveCalls(BasicBlock &basic_block) +{ + ///////////////////////////////////////////////////////////////////////// + // Prepare the current basic block for execution in the remote process + // + + BasicBlock::iterator ii; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + CallInst *call = dyn_cast<CallInst>(&inst); + + // MaybeHandleCallArguments handles error reporting; we are silent here + if (call && !MaybeHandleCallArguments(call)) + return false; + } + + return true; +} + +bool +IRForTarget::ResolveExternals (Function &llvm_function) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (Module::global_iterator global = m_module->global_begin(), end = m_module->global_end(); + global != end; + ++global) + { + if (!global) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: global variable is NULL"); + + return false; + } + + std::string global_name = (*global).getName().str(); + + if (log) + log->Printf("Examining %s, DeclForGlobalValue returns %p", + global_name.c_str(), + DeclForGlobal(global)); + + if (global_name.find("OBJC_IVAR") == 0) + { + if (!HandleSymbol(global)) + { + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't find Objective-C indirect ivar symbol %s\n", global_name.c_str()); + + return false; + } + } + else if (global_name.find("OBJC_CLASSLIST_REFERENCES_$") != global_name.npos) + { + if (!HandleObjCClass(global)) + { + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call\n"); + + return false; + } + } + else if (global_name.find("OBJC_CLASSLIST_SUP_REFS_$") != global_name.npos) + { + if (!HandleObjCClass(global)) + { + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call\n"); + + return false; + } + } + else if (DeclForGlobal(global)) + { + if (!MaybeHandleVariable (global)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite external variable %s\n", global_name.c_str()); + + return false; + } + } + } + + return true; +} + +bool +IRForTarget::ReplaceStrings () +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + typedef std::map <GlobalVariable *, size_t> OffsetsTy; + + OffsetsTy offsets; + + for (Module::global_iterator gi = m_module->global_begin(), ge = m_module->global_end(); + gi != ge; + ++gi) + { + GlobalVariable *gv = gi; + + if (!gv->hasInitializer()) + continue; + + Constant *gc = gv->getInitializer(); + + std::string str; + + if (gc->isNullValue()) + { + Type *gc_type = gc->getType(); + + ArrayType *gc_array_type = dyn_cast<ArrayType>(gc_type); + + if (!gc_array_type) + continue; + + Type *gc_element_type = gc_array_type->getElementType(); + + IntegerType *gc_integer_type = dyn_cast<IntegerType>(gc_element_type); + + if (gc_integer_type->getBitWidth() != 8) + continue; + + str = ""; + } + else + { + ConstantDataArray *gc_array = dyn_cast<ConstantDataArray>(gc); + + if (!gc_array) + continue; + + if (!gc_array->isCString()) + continue; + + if (log) + log->Printf("Found a GlobalVariable with string initializer %s", PrintValue(gc).c_str()); + + str = gc_array->getAsString(); + } + + offsets[gv] = m_data_allocator.GetStream().GetSize(); + + m_data_allocator.GetStream().Write(str.c_str(), str.length() + 1); + } + + Type *char_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); + + for (OffsetsTy::iterator oi = offsets.begin(), oe = offsets.end(); + oi != oe; + ++oi) + { + GlobalVariable *gv = oi->first; + size_t offset = oi->second; + + Constant *new_initializer = BuildRelocation(char_ptr_ty, offset); + + if (log) + log->Printf("Replacing GV %s with %s", PrintValue(gv).c_str(), PrintValue(new_initializer).c_str()); + + for (GlobalVariable::use_iterator ui = gv->use_begin(), ue = gv->use_end(); + ui != ue; + ++ui) + { + if (log) + log->Printf("Found use %s", PrintValue(*ui).c_str()); + + ConstantExpr *const_expr = dyn_cast<ConstantExpr>(*ui); + StoreInst *store_inst = dyn_cast<StoreInst>(*ui); + + if (const_expr) + { + if (const_expr->getOpcode() != Instruction::GetElementPtr) + { + if (log) + log->Printf("Use (%s) of string variable is not a GetElementPtr constant", PrintValue(const_expr).c_str()); + + return false; + } + + Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, const_expr->getOperand(0)->getType()); + Constant *new_gep = const_expr->getWithOperandReplaced(0, bit_cast); + + const_expr->replaceAllUsesWith(new_gep); + } + else if (store_inst) + { + Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, store_inst->getValueOperand()->getType()); + + store_inst->setOperand(0, bit_cast); + } + else + { + if (log) + log->Printf("Use (%s) of string variable is neither a constant nor a store", PrintValue(const_expr).c_str()); + + return false; + } + } + + gv->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::ReplaceStaticLiterals (llvm::BasicBlock &basic_block) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + typedef SmallVector <Value*, 2> ConstantList; + typedef SmallVector <llvm::Instruction*, 2> UserList; + typedef ConstantList::iterator ConstantIterator; + typedef UserList::iterator UserIterator; + + ConstantList static_constants; + UserList static_users; + + for (BasicBlock::iterator ii = basic_block.begin(), ie = basic_block.end(); + ii != ie; + ++ii) + { + llvm::Instruction &inst = *ii; + + for (Instruction::op_iterator oi = inst.op_begin(), oe = inst.op_end(); + oi != oe; + ++oi) + { + Value *operand_val = oi->get(); + + ConstantFP *operand_constant_fp = dyn_cast<ConstantFP>(operand_val); + + if (operand_constant_fp/* && operand_constant_fp->getType()->isX86_FP80Ty()*/) + { + static_constants.push_back(operand_val); + static_users.push_back(ii); + } + } + } + + ConstantIterator constant_iter; + UserIterator user_iter; + + for (constant_iter = static_constants.begin(), user_iter = static_users.begin(); + constant_iter != static_constants.end(); + ++constant_iter, ++user_iter) + { + Value *operand_val = *constant_iter; + llvm::Instruction *inst = *user_iter; + + ConstantFP *operand_constant_fp = dyn_cast<ConstantFP>(operand_val); + + if (operand_constant_fp) + { + Type *operand_type = operand_constant_fp->getType(); + + APFloat operand_apfloat = operand_constant_fp->getValueAPF(); + APInt operand_apint = operand_apfloat.bitcastToAPInt(); + + const uint8_t* operand_raw_data = (const uint8_t*)operand_apint.getRawData(); + size_t operand_data_size = operand_apint.getBitWidth() / 8; + + if (log) + { + std::string s; + raw_string_ostream ss(s); + for (size_t index = 0; + index < operand_data_size; + ++index) + { + ss << (uint32_t)operand_raw_data[index]; + ss << " "; + } + ss.flush(); + + log->Printf("Found ConstantFP with size %lu and raw data %s", operand_data_size, s.c_str()); + } + + lldb_private::DataBufferHeap data(operand_data_size, 0); + + if (lldb::endian::InlHostByteOrder() != m_data_allocator.GetStream().GetByteOrder()) + { + uint8_t *data_bytes = data.GetBytes(); + + for (size_t index = 0; + index < operand_data_size; + ++index) + { + data_bytes[index] = operand_raw_data[operand_data_size - (1 + index)]; + } + } + else + { + memcpy(data.GetBytes(), operand_raw_data, operand_data_size); + } + + uint64_t offset = m_data_allocator.GetStream().GetSize(); + + size_t align = m_target_data->getPrefTypeAlignment(operand_type); + + const size_t mask = (align - 1); + uint64_t aligned_offset = (offset + mask) & ~mask; + m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); + offset = aligned_offset; + + m_data_allocator.GetStream().Write(data.GetBytes(), operand_data_size); + + llvm::Type *fp_ptr_ty = operand_constant_fp->getType()->getPointerTo(); + + Constant *new_pointer = BuildRelocation(fp_ptr_ty, aligned_offset); + + llvm::LoadInst *fp_load = new llvm::LoadInst(new_pointer, "fp_load", inst); + + operand_constant_fp->replaceAllUsesWith(fp_load); + } + } + + return true; +} + +static bool isGuardVariableRef(Value *V) +{ + Constant *Old = NULL; + + if (!(Old = dyn_cast<Constant>(V))) + return false; + + ConstantExpr *CE = NULL; + + if ((CE = dyn_cast<ConstantExpr>(V))) + { + if (CE->getOpcode() != Instruction::BitCast) + return false; + + Old = CE->getOperand(0); + } + + GlobalVariable *GV = dyn_cast<GlobalVariable>(Old); + + if (!GV || !GV->hasName() || !GV->getName().startswith("_ZGV")) + return false; + + return true; +} + +void +IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction* guard_load) +{ + Constant* zero(ConstantInt::get(Type::getInt8Ty(m_module->getContext()), 0, true)); + + Value::use_iterator ui; + + for (ui = guard_load->use_begin(); + ui != guard_load->use_end(); + ++ui) + { + if (isa<Constant>(*ui)) + { + // do nothing for the moment + } + else + { + ui->replaceUsesOfWith(guard_load, zero); + } + } + + guard_load->eraseFromParent(); +} + +static void ExciseGuardStore(Instruction* guard_store) +{ + guard_store->eraseFromParent(); +} + +bool +IRForTarget::RemoveGuards(BasicBlock &basic_block) +{ + /////////////////////////////////////////////////////// + // Eliminate any reference to guard variables found. + // + + BasicBlock::iterator ii; + + typedef SmallVector <Instruction*, 2> InstrList; + typedef InstrList::iterator InstrIterator; + + InstrList guard_loads; + InstrList guard_stores; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + if (LoadInst *load = dyn_cast<LoadInst>(&inst)) + if (isGuardVariableRef(load->getPointerOperand())) + guard_loads.push_back(&inst); + + if (StoreInst *store = dyn_cast<StoreInst>(&inst)) + if (isGuardVariableRef(store->getPointerOperand())) + guard_stores.push_back(&inst); + } + + InstrIterator iter; + + for (iter = guard_loads.begin(); + iter != guard_loads.end(); + ++iter) + TurnGuardLoadIntoZero(*iter); + + for (iter = guard_stores.begin(); + iter != guard_stores.end(); + ++iter) + ExciseGuardStore(*iter); + + return true; +} + +// This function does not report errors; its callers are responsible. +bool +IRForTarget::UnfoldConstant(Constant *old_constant, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Value::use_iterator ui; + + SmallVector<User*, 16> users; + + // We do this because the use list might change, invalidating our iterator. + // Much better to keep a work list ourselves. + for (ui = old_constant->use_begin(); + ui != old_constant->use_end(); + ++ui) + users.push_back(*ui); + + for (size_t i = 0; + i < users.size(); + ++i) + { + User *user = users[i]; + + if (Constant *constant = dyn_cast<Constant>(user)) + { + // synthesize a new non-constant equivalent of the constant + + if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) + { + switch (constant_expr->getOpcode()) + { + default: + if (log) + log->Printf("Unhandled constant expression type: \"%s\"", PrintValue(constant_expr).c_str()); + return false; + case Instruction::BitCast: + { + FunctionValueCache bit_cast_maker ([&value_maker, &entry_instruction_finder, old_constant, constant_expr] (llvm::Function *function)->llvm::Value* { + // UnaryExpr + // OperandList[0] is value + + if (constant_expr->getOperand(0) != old_constant) + return constant_expr; + + return new BitCastInst(value_maker.GetValue(function), + constant_expr->getType(), + "", + llvm::cast<Instruction>(entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, bit_cast_maker, entry_instruction_finder)) + return false; + } + break; + case Instruction::GetElementPtr: + { + // GetElementPtrConstantExpr + // OperandList[0] is base + // OperandList[1]... are indices + + FunctionValueCache get_element_pointer_maker ([&value_maker, &entry_instruction_finder, old_constant, constant_expr] (llvm::Function *function)->llvm::Value* { + Value *ptr = constant_expr->getOperand(0); + + if (ptr == old_constant) + ptr = value_maker.GetValue(function); + + std::vector<Value*> index_vector; + + unsigned operand_index; + unsigned num_operands = constant_expr->getNumOperands(); + + for (operand_index = 1; + operand_index < num_operands; + ++operand_index) + { + Value *operand = constant_expr->getOperand(operand_index); + + if (operand == old_constant) + operand = value_maker.GetValue(function); + + index_vector.push_back(operand); + } + + ArrayRef <Value*> indices(index_vector); + + return GetElementPtrInst::Create(ptr, indices, "", llvm::cast<Instruction>(entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, get_element_pointer_maker, entry_instruction_finder)) + return false; + } + break; + } + } + else + { + if (log) + log->Printf("Unhandled constant type: \"%s\"", PrintValue(constant).c_str()); + return false; + } + } + else + { + if (Instruction *inst = llvm::dyn_cast<Instruction>(user)) + { + inst->replaceUsesOfWith(old_constant, value_maker.GetValue(inst->getParent()->getParent())); + } + else + { + if (log) + log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(user).c_str()); + return false; + } + } + } + + if (!isa<GlobalValue>(old_constant)) + { + old_constant->destroyConstant(); + } + + return true; +} + +bool +IRForTarget::ReplaceVariables (Function &llvm_function) +{ + if (!m_resolve_vars) + return true; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + m_decl_map->DoStructLayout(); + + if (log) + log->Printf("Element arrangement:"); + + uint32_t num_elements; + uint32_t element_index; + + size_t size; + off_t alignment; + + if (!m_decl_map->GetStructInfo (num_elements, size, alignment)) + return false; + + Function::arg_iterator iter(llvm_function.getArgumentList().begin()); + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes no arguments (should take at least a struct pointer)"); + + return false; + } + + Argument *argument = iter; + + if (argument->getName().equals("this")) + { + ++iter; + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'this' argument (should take a struct pointer too)"); + + return false; + } + + argument = iter; + } + else if (argument->getName().equals("self")) + { + ++iter; + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'self' argument (should take '_cmd' and a struct pointer too)"); + + return false; + } + + if (!iter->getName().equals("_cmd")) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes '%s' after 'self' argument (should take '_cmd')", iter->getName().str().c_str()); + + return false; + } + + ++iter; + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'self' and '_cmd' arguments (should take a struct pointer too)"); + + return false; + } + + argument = iter; + } + + if (!argument->getName().equals("$__lldb_arg")) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes an argument named '%s' instead of the struct pointer", argument->getName().str().c_str()); + + return false; + } + + if (log) + log->Printf("Arg: \"%s\"", PrintValue(argument).c_str()); + + BasicBlock &entry_block(llvm_function.getEntryBlock()); + Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg()); + + if (!FirstEntryInstruction) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find the first instruction in the wrapper for use in rewriting"); + + return false; + } + + LLVMContext &context(m_module->getContext()); + IntegerType *offset_type(Type::getInt32Ty(context)); + + if (!offset_type) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't produce an offset type"); + + return false; + } + + for (element_index = 0; element_index < num_elements; ++element_index) + { + const clang::NamedDecl *decl = NULL; + Value *value = NULL; + off_t offset; + lldb_private::ConstString name; + + if (!m_decl_map->GetStructElement (decl, value, offset, name, element_index)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Structure information is incomplete"); + + return false; + } + + if (log) + log->Printf(" \"%s\" (\"%s\") placed at %" PRId64, + name.GetCString(), + decl->getNameAsString().c_str(), + offset); + + if (value) + { + if (log) + log->Printf(" Replacing [%s]", PrintValue(value).c_str()); + + FunctionValueCache body_result_maker ([this, name, offset_type, offset, argument, value] (llvm::Function *function)->llvm::Value * { + // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, in cases where the result + // variable is an rvalue, we have to synthesize a dereference of the appropriate structure + // entry in order to produce the static variable that the AST thinks it is accessing. + + llvm::Instruction *entry_instruction = llvm::cast<Instruction>(m_entry_instruction_finder.GetValue(function)); + + ConstantInt *offset_int(ConstantInt::get(offset_type, offset, true)); + GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(argument, + offset_int, + "", + entry_instruction); + + if (name == m_result_name && !m_result_is_pointer) + { + BitCastInst *bit_cast = new BitCastInst(get_element_ptr, + value->getType()->getPointerTo(), + "", + entry_instruction); + + LoadInst *load = new LoadInst(bit_cast, "", entry_instruction); + + return load; + } + else + { + BitCastInst *bit_cast = new BitCastInst(get_element_ptr, value->getType(), "", entry_instruction); + + return bit_cast; + } + }); + + if (Constant *constant = dyn_cast<Constant>(value)) + { + UnfoldConstant(constant, body_result_maker, m_entry_instruction_finder); + } + else if (Instruction *instruction = dyn_cast<Instruction>(value)) + { + value->replaceAllUsesWith(body_result_maker.GetValue(instruction->getParent()->getParent())); + } + else + { + if (log) + log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(value).c_str()); + return false; + } + + if (GlobalVariable *var = dyn_cast<GlobalVariable>(value)) + var->eraseFromParent(); + } + } + + if (log) + log->Printf("Total structure [align %" PRId64 ", size %lu]", alignment, size); + + return true; +} + +llvm::Constant * +IRForTarget::BuildRelocation(llvm::Type *type, uint64_t offset) +{ + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + llvm::Constant *offset_int = ConstantInt::get(intptr_ty, offset); + + llvm::Constant *offset_array[1]; + + offset_array[0] = offset_int; + + llvm::ArrayRef<llvm::Constant *> offsets(offset_array, 1); + + llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(m_reloc_placeholder, offsets); + llvm::Constant *reloc_getbitcast = ConstantExpr::getBitCast(reloc_getelementptr, type); + + return reloc_getbitcast; +} + +bool +IRForTarget::CompleteDataAllocation () +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_data_allocator.GetStream().GetSize()) + return true; + + lldb::addr_t allocation = m_data_allocator.Allocate(); + + if (log) + { + if (allocation) + log->Printf("Allocated static data at 0x%llx", (unsigned long long)allocation); + else + log->Printf("Failed to allocate static data"); + } + + if (!allocation || allocation == LLDB_INVALID_ADDRESS) + return false; + + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + Constant *relocated_addr = ConstantInt::get(intptr_ty, (uint64_t)allocation); + Constant *relocated_bitcast = ConstantExpr::getIntToPtr(relocated_addr, llvm::Type::getInt8PtrTy(m_module->getContext())); + + m_reloc_placeholder->replaceAllUsesWith(relocated_bitcast); + + m_reloc_placeholder->eraseFromParent(); + + return true; +} + +bool +IRForTarget::StripAllGVs (Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + std::vector<GlobalVariable *> global_vars; + std::set<GlobalVariable *>erased_vars; + + bool erased = true; + + while (erased) + { + erased = false; + + for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); + gi != ge; + ++gi) + { + GlobalVariable *global_var = dyn_cast<GlobalVariable>(gi); + + global_var->removeDeadConstantUsers(); + + if (global_var->use_empty()) + { + if (log) + log->Printf("Did remove %s", + PrintValue(global_var).c_str()); + global_var->eraseFromParent(); + erased = true; + break; + } + } + } + + for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); + gi != ge; + ++gi) + { + GlobalVariable *global_var = dyn_cast<GlobalVariable>(gi); + + GlobalValue::use_iterator ui = global_var->use_begin(); + + if (log) + log->Printf("Couldn't remove %s because of %s", + PrintValue(global_var).c_str(), + PrintValue(*ui).c_str()); + } + + return true; +} + +bool +IRForTarget::runOnModule (Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + m_module = &llvm_module; + m_target_data.reset(new DataLayout(m_module)); + + if (log) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module as passed in to IRForTarget: \n\"%s\"", s.c_str()); + } + + Function* main_function = m_module->getFunction(StringRef(m_func_name.c_str())); + + if (!main_function) + { + if (log) + log->Printf("Couldn't find \"%s()\" in the module", m_func_name.c_str()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find wrapper '%s' in the module", m_func_name.c_str()); + + return false; + } + + if (!FixFunctionLinkage (*main_function)) + { + if (log) + log->Printf("Couldn't fix the linkage for the function"); + + return false; + } + + llvm::Type *intptr_ty = Type::getInt8Ty(m_module->getContext()); + + m_reloc_placeholder = new llvm::GlobalVariable((*m_module), + intptr_ty, + false /* IsConstant */, + GlobalVariable::InternalLinkage, + Constant::getNullValue(intptr_ty), + "reloc_placeholder", + NULL /* InsertBefore */, + GlobalVariable::NotThreadLocal /* ThreadLocal */, + 0 /* AddressSpace */); + + //////////////////////////////////////////////////////////// + // Replace $__lldb_expr_result with a persistent variable + // + + if (!CreateResultVariable(*main_function)) + { + if (log) + log->Printf("CreateResultVariable() failed"); + + // CreateResultVariable() reports its own errors, so we don't do so here + + return false; + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module after creating the result variable: \n\"%s\"", s.c_str()); + } + + for (Module::iterator fi = m_module->begin(), fe = m_module->end(); + fi != fe; + ++fi) + { + llvm::Function *function = fi; + + if (function->begin() == function->end()) + continue; + + Function::iterator bbi; + + for (bbi = function->begin(); + bbi != function->end(); + ++bbi) + { + if (!RemoveGuards(*bbi)) + { + if (log) + log->Printf("RemoveGuards() failed"); + + // RemoveGuards() reports its own errors, so we don't do so here + + return false; + } + + if (!RewritePersistentAllocs(*bbi)) + { + if (log) + log->Printf("RewritePersistentAllocs() failed"); + + // RewritePersistentAllocs() reports its own errors, so we don't do so here + + return false; + } + + if (!RemoveCXAAtExit(*bbi)) + { + if (log) + log->Printf("RemoveCXAAtExit() failed"); + + // RemoveCXAAtExit() reports its own errors, so we don't do so here + + return false; + } + } + } + + /////////////////////////////////////////////////////////////////////////////// + // Fix all Objective-C constant strings to use NSStringWithCString:encoding: + // + + if (!RewriteObjCConstStrings()) + { + if (log) + log->Printf("RewriteObjCConstStrings() failed"); + + // RewriteObjCConstStrings() reports its own errors, so we don't do so here + + return false; + } + + /////////////////////////////// + // Resolve function pointers + // + + if (!ResolveFunctionPointers(llvm_module)) + { + if (log) + log->Printf("ResolveFunctionPointers() failed"); + + // ResolveFunctionPointers() reports its own errors, so we don't do so here + + return false; + } + + for (Module::iterator fi = m_module->begin(), fe = m_module->end(); + fi != fe; + ++fi) + { + llvm::Function *function = fi; + + for (llvm::Function::iterator bbi = function->begin(), bbe = function->end(); + bbi != bbe; + ++bbi) + { + if (!RewriteObjCSelectors(*bbi)) + { + if (log) + log->Printf("RewriteObjCSelectors() failed"); + + // RewriteObjCSelectors() reports its own errors, so we don't do so here + + return false; + } + } + } + + for (Module::iterator fi = m_module->begin(), fe = m_module->end(); + fi != fe; + ++fi) + { + llvm::Function *function = fi; + + for (llvm::Function::iterator bbi = function->begin(), bbe = function->end(); + bbi != bbe; + ++bbi) + { + if (!ResolveCalls(*bbi)) + { + if (log) + log->Printf("ResolveCalls() failed"); + + // ResolveCalls() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceStaticLiterals(*bbi)) + { + if (log) + log->Printf("ReplaceStaticLiterals() failed"); + + return false; + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Run function-level passes that only make sense on the main function + // + + if (!ResolveExternals(*main_function)) + { + if (log) + log->Printf("ResolveExternals() failed"); + + // ResolveExternals() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceVariables(*main_function)) + { + if (log) + log->Printf("ReplaceVariables() failed"); + + // ReplaceVariables() reports its own errors, so we don't do so here + + return false; + } + + if (!ReplaceStrings()) + { + if (log) + log->Printf("ReplaceStrings() failed"); + + return false; + } + + if (!CompleteDataAllocation()) + { + if (log) + log->Printf("CompleteDataAllocation() failed"); + + return false; + } + + if (!StripAllGVs(llvm_module)) + { + if (log) + log->Printf("StripAllGVs() failed"); + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module after preparing for execution: \n\"%s\"", s.c_str()); + } + + return true; +} + +void +IRForTarget::assignPassManager (PMStack &pass_mgr_stack, PassManagerType pass_mgr_type) +{ +} + +PassManagerType +IRForTarget::getPotentialPassManagerType() const +{ + return PMT_ModulePassManager; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRInterpreter.cpp b/contrib/llvm/tools/lldb/source/Expression/IRInterpreter.cpp new file mode 100644 index 0000000..dcbf584 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRInterpreter.cpp @@ -0,0 +1,1379 @@ +//===-- IRInterpreter.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Expression/IRInterpreter.h" + +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" + +#include <map> + +using namespace llvm; + +static std::string +PrintValue(const Value *value, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + value->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + + size_t offset; + while ((offset = s.find('\n')) != s.npos) + s.erase(offset, 1); + while (s[0] == ' ' || s[0] == '\t') + s.erase(0, 1); + + return s; +} + +static std::string +PrintType(const Type *type, bool truncate = false) +{ + std::string s; + raw_string_ostream rso(s); + type->print(rso); + rso.flush(); + if (truncate) + s.resize(s.length() - 1); + return s; +} + +class InterpreterStackFrame +{ +public: + typedef std::map <const Value*, lldb::addr_t> ValueMap; + + ValueMap m_values; + DataLayout &m_target_data; + lldb_private::IRMemoryMap &m_memory_map; + const BasicBlock *m_bb; + BasicBlock::const_iterator m_ii; + BasicBlock::const_iterator m_ie; + + lldb::addr_t m_frame_process_address; + size_t m_frame_size; + lldb::addr_t m_stack_pointer; + + lldb::ByteOrder m_byte_order; + size_t m_addr_byte_size; + + InterpreterStackFrame (DataLayout &target_data, + lldb_private::IRMemoryMap &memory_map, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) : + m_target_data (target_data), + m_memory_map (memory_map) + { + m_byte_order = (target_data.isLittleEndian() ? lldb::eByteOrderLittle : lldb::eByteOrderBig); + m_addr_byte_size = (target_data.getPointerSize(0)); + + m_frame_process_address = stack_frame_bottom; + m_frame_size = stack_frame_top - stack_frame_bottom; + m_stack_pointer = stack_frame_top; + } + + ~InterpreterStackFrame () + { + } + + void Jump (const BasicBlock *bb) + { + m_bb = bb; + m_ii = m_bb->begin(); + m_ie = m_bb->end(); + } + + std::string SummarizeValue (const Value *value) + { + lldb_private::StreamString ss; + + ss.Printf("%s", PrintValue(value).c_str()); + + ValueMap::iterator i = m_values.find(value); + + if (i != m_values.end()) + { + lldb::addr_t addr = i->second; + + ss.Printf(" 0x%llx", (unsigned long long)addr); + } + + return ss.GetString(); + } + + bool AssignToMatchType (lldb_private::Scalar &scalar, uint64_t u64value, Type *type) + { + size_t type_size = m_target_data.getTypeStoreSize(type); + + switch (type_size) + { + case 1: + scalar = (uint8_t)u64value; + break; + case 2: + scalar = (uint16_t)u64value; + break; + case 4: + scalar = (uint32_t)u64value; + break; + case 8: + scalar = (uint64_t)u64value; + break; + default: + return false; + } + + return true; + } + + bool EvaluateValue (lldb_private::Scalar &scalar, const Value *value, Module &module) + { + const Constant *constant = dyn_cast<Constant>(value); + + if (constant) + { + APInt value_apint; + + if (!ResolveConstantValue(value_apint, constant)) + return false; + + return AssignToMatchType(scalar, value_apint.getLimitedValue(), value->getType()); + } + else + { + lldb::addr_t process_address = ResolveValue(value, module); + size_t value_size = m_target_data.getTypeStoreSize(value->getType()); + + lldb_private::DataExtractor value_extractor; + lldb_private::Error extract_error; + + m_memory_map.GetMemoryData(value_extractor, process_address, value_size, extract_error); + + if (!extract_error.Success()) + return false; + + lldb::offset_t offset = 0; + if (value_size == 1 || value_size == 2 || value_size == 4 || value_size == 8) + { + uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size); + return AssignToMatchType(scalar, u64value, value->getType()); + } + } + + return false; + } + + bool AssignValue (const Value *value, lldb_private::Scalar &scalar, Module &module) + { + lldb::addr_t process_address = ResolveValue (value, module); + + if (process_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Scalar cast_scalar; + + if (!AssignToMatchType(cast_scalar, scalar.GetRawBits64(0), value->getType())) + return false; + + size_t value_byte_size = m_target_data.getTypeStoreSize(value->getType()); + + lldb_private::DataBufferHeap buf(value_byte_size, 0); + + lldb_private::Error get_data_error; + + if (!cast_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), m_byte_order, get_data_error)) + return false; + + lldb_private::Error write_error; + + m_memory_map.WriteMemory(process_address, buf.GetBytes(), buf.GetByteSize(), write_error); + + return write_error.Success(); + } + + bool ResolveConstantValue (APInt &value, const Constant *constant) + { + switch (constant->getValueID()) + { + default: + break; + case Value::ConstantIntVal: + if (const ConstantInt *constant_int = dyn_cast<ConstantInt>(constant)) + { + value = constant_int->getValue(); + return true; + } + break; + case Value::ConstantFPVal: + if (const ConstantFP *constant_fp = dyn_cast<ConstantFP>(constant)) + { + value = constant_fp->getValueAPF().bitcastToAPInt(); + return true; + } + break; + case Value::ConstantExprVal: + if (const ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) + { + switch (constant_expr->getOpcode()) + { + default: + return false; + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::BitCast: + return ResolveConstantValue(value, constant_expr->getOperand(0)); + case Instruction::GetElementPtr: + { + ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin(); + ConstantExpr::const_op_iterator op_end = constant_expr->op_end(); + + Constant *base = dyn_cast<Constant>(*op_cursor); + + if (!base) + return false; + + if (!ResolveConstantValue(value, base)) + return false; + + op_cursor++; + + if (op_cursor == op_end) + return true; // no offset to apply! + + SmallVector <Value *, 8> indices (op_cursor, op_end); + + uint64_t offset = m_target_data.getIndexedOffset(base->getType(), indices); + + const bool is_signed = true; + value += APInt(value.getBitWidth(), offset, is_signed); + + return true; + } + } + } + break; + case Value::ConstantPointerNullVal: + if (isa<ConstantPointerNull>(constant)) + { + value = APInt(m_target_data.getPointerSizeInBits(), 0); + return true; + } + break; + } + return false; + } + + bool MakeArgument(const Argument *value, uint64_t address) + { + lldb::addr_t data_address = Malloc(value->getType()); + + if (data_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Error write_error; + + m_memory_map.WritePointerToMemory(data_address, address, write_error); + + if (!write_error.Success()) + { + lldb_private::Error free_error; + m_memory_map.Free(data_address, free_error); + return false; + } + + m_values[value] = data_address; + + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + log->Printf("Made an allocation for argument %s", PrintValue(value).c_str()); + log->Printf(" Data region : %llx", (unsigned long long)address); + log->Printf(" Ref region : %llx", (unsigned long long)data_address); + } + + return true; + } + + bool ResolveConstant (lldb::addr_t process_address, const Constant *constant) + { + APInt resolved_value; + + if (!ResolveConstantValue(resolved_value, constant)) + return false; + + const uint64_t *raw_data = resolved_value.getRawData(); + + size_t constant_size = m_target_data.getTypeStoreSize(constant->getType()); + + lldb_private::Error write_error; + + m_memory_map.WriteMemory(process_address, (uint8_t*)raw_data, constant_size, write_error); + + return write_error.Success(); + } + + lldb::addr_t Malloc (size_t size, uint8_t byte_alignment) + { + lldb::addr_t ret = m_stack_pointer; + + ret -= size; + ret -= (ret % byte_alignment); + + if (ret < m_frame_process_address) + return LLDB_INVALID_ADDRESS; + + m_stack_pointer = ret; + return ret; + } + + lldb::addr_t MallocPointer () + { + return Malloc(m_target_data.getPointerSize(), m_target_data.getPointerPrefAlignment()); + } + + lldb::addr_t Malloc (llvm::Type *type) + { + lldb_private::Error alloc_error; + + return Malloc(m_target_data.getTypeAllocSize(type), m_target_data.getPrefTypeAlignment(type)); + } + + std::string PrintData (lldb::addr_t addr, llvm::Type *type) + { + size_t length = m_target_data.getTypeStoreSize(type); + + lldb_private::DataBufferHeap buf(length, 0); + + lldb_private::Error read_error; + + m_memory_map.ReadMemory(buf.GetBytes(), addr, length, read_error); + + if (!read_error.Success()) + return std::string("<couldn't read data>"); + + lldb_private::StreamString ss; + + for (size_t i = 0; i < length; i++) + { + if ((!(i & 0xf)) && i) + ss.Printf("%02hhx - ", buf.GetBytes()[i]); + else + ss.Printf("%02hhx ", buf.GetBytes()[i]); + } + + return ss.GetString(); + } + + lldb::addr_t ResolveValue (const Value *value, Module &module) + { + ValueMap::iterator i = m_values.find(value); + + if (i != m_values.end()) + return i->second; + + // Fall back and allocate space [allocation type Alloca] + + lldb::addr_t data_address = Malloc(value->getType()); + + if (const Constant *constant = dyn_cast<Constant>(value)) + { + if (!ResolveConstant (data_address, constant)) + { + lldb_private::Error free_error; + m_memory_map.Free(data_address, free_error); + return LLDB_INVALID_ADDRESS; + } + } + + m_values[value] = data_address; + return data_address; + } +}; + +static const char *unsupported_opcode_error = "Interpreter doesn't handle one of the expression's opcodes"; +static const char *unsupported_operand_error = "Interpreter doesn't handle one of the expression's operands"; +//static const char *interpreter_initialization_error = "Interpreter couldn't be initialized"; +static const char *interpreter_internal_error = "Interpreter encountered an internal error"; +static const char *bad_value_error = "Interpreter couldn't resolve a value during execution"; +static const char *memory_allocation_error = "Interpreter couldn't allocate memory"; +static const char *memory_write_error = "Interpreter couldn't write to memory"; +static const char *memory_read_error = "Interpreter couldn't read from memory"; +static const char *infinite_loop_error = "Interpreter ran for too many cycles"; +//static const char *bad_result_error = "Result of expression is in bad memory"; + +bool +IRInterpreter::CanInterpret (llvm::Module &module, + llvm::Function &function, + lldb_private::Error &error) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + bool saw_function_with_body = false; + + for (Module::iterator fi = module.begin(), fe = module.end(); + fi != fe; + ++fi) + { + if (fi->begin() != fi->end()) + { + if (saw_function_with_body) + return false; + saw_function_with_body = true; + } + } + + for (Function::iterator bbi = function.begin(), bbe = function.end(); + bbi != bbe; + ++bbi) + { + for (BasicBlock::iterator ii = bbi->begin(), ie = bbi->end(); + ii != ie; + ++ii) + { + switch (ii->getOpcode()) + { + default: + { + if (log) + log->Printf("Unsupported instruction: %s", PrintValue(ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + case Instruction::Add: + case Instruction::Alloca: + case Instruction::BitCast: + case Instruction::Br: + case Instruction::GetElementPtr: + break; + case Instruction::ICmp: + { + ICmpInst *icmp_inst = dyn_cast<ICmpInst>(ii); + + if (!icmp_inst) + { + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + switch (icmp_inst->getPredicate()) + { + default: + { + if (log) + log->Printf("Unsupported ICmp predicate: %s", PrintValue(ii).c_str()); + + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + case CmpInst::ICMP_EQ: + case CmpInst::ICMP_NE: + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_ULT: + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_SLE: + break; + } + } + break; + case Instruction::And: + case Instruction::AShr: + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::Load: + case Instruction::LShr: + case Instruction::Mul: + case Instruction::Or: + case Instruction::Ret: + case Instruction::SDiv: + case Instruction::SExt: + case Instruction::Shl: + case Instruction::SRem: + case Instruction::Store: + case Instruction::Sub: + case Instruction::UDiv: + case Instruction::URem: + case Instruction::Xor: + case Instruction::ZExt: + break; + } + + for (int oi = 0, oe = ii->getNumOperands(); + oi != oe; + ++oi) + { + Value *operand = ii->getOperand(oi); + Type *operand_type = operand->getType(); + + switch (operand_type->getTypeID()) + { + default: + break; + case Type::VectorTyID: + { + if (log) + log->Printf("Unsupported operand type: %s", PrintType(operand_type).c_str()); + error.SetErrorString(unsupported_operand_error); + return false; + } + } + } + } + + } + + return true;} + +bool +IRInterpreter::Interpret (llvm::Module &module, + llvm::Function &function, + llvm::ArrayRef<lldb::addr_t> args, + lldb_private::IRMemoryMap &memory_map, + lldb_private::Error &error, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + std::string s; + raw_string_ostream oss(s); + + module.print(oss, NULL); + + oss.flush(); + + log->Printf("Module as passed in to IRInterpreter::Interpret: \n\"%s\"", s.c_str()); + } + + DataLayout data_layout(&module); + + InterpreterStackFrame frame(data_layout, memory_map, stack_frame_bottom, stack_frame_top); + + if (frame.m_frame_process_address == LLDB_INVALID_ADDRESS) + { + error.SetErrorString("Couldn't allocate stack frame"); + } + + int arg_index = 0; + + for (llvm::Function::arg_iterator ai = function.arg_begin(), ae = function.arg_end(); + ai != ae; + ++ai, ++arg_index) + { + if (args.size() < arg_index) + { + error.SetErrorString ("Not enough arguments passed in to function"); + return false; + } + + lldb::addr_t ptr = args[arg_index]; + + frame.MakeArgument(ai, ptr); + } + + uint32_t num_insts = 0; + + frame.Jump(function.begin()); + + while (frame.m_ii != frame.m_ie && (++num_insts < 4096)) + { + const Instruction *inst = frame.m_ii; + + if (log) + log->Printf("Interpreting %s", PrintValue(inst).c_str()); + + switch (inst->getOpcode()) + { + default: + break; + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::SDiv: + case Instruction::UDiv: + case Instruction::SRem: + case Instruction::URem: + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + { + const BinaryOperator *bin_op = dyn_cast<BinaryOperator>(inst); + + if (!bin_op) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BinaryOperator", inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *lhs = inst->getOperand(0); + Value *rhs = inst->getOperand(1); + + lldb_private::Scalar L; + lldb_private::Scalar R; + + if (!frame.EvaluateValue(L, lhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!frame.EvaluateValue(R, rhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb_private::Scalar result; + + switch (inst->getOpcode()) + { + default: + break; + case Instruction::Add: + result = L + R; + break; + case Instruction::Mul: + result = L * R; + break; + case Instruction::Sub: + result = L - R; + break; + case Instruction::SDiv: + L.MakeSigned(); + R.MakeSigned(); + result = L / R; + break; + case Instruction::UDiv: + result = L.GetRawBits64(0) / R.GetRawBits64(1); + break; + case Instruction::SRem: + L.MakeSigned(); + R.MakeSigned(); + result = L % R; + break; + case Instruction::URem: + result = L.GetRawBits64(0) % R.GetRawBits64(1); + break; + case Instruction::Shl: + result = L << R; + break; + case Instruction::AShr: + result = L >> R; + break; + case Instruction::LShr: + result = L; + result.ShiftRightLogical(R); + break; + case Instruction::And: + result = L & R; + break; + case Instruction::Or: + result = L | R; + break; + case Instruction::Xor: + result = L ^ R; + break; + } + + frame.AssignValue(inst, result, module); + + if (log) + { + log->Printf("Interpreted a %s", inst->getOpcodeName()); + log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str()); + log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::Alloca: + { + const AllocaInst *alloca_inst = dyn_cast<AllocaInst>(inst); + + if (!alloca_inst) + { + if (log) + log->Printf("getOpcode() returns Alloca, but instruction is not an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + if (alloca_inst->isArrayAllocation()) + { + if (log) + log->Printf("AllocaInsts are not handled if isArrayAllocation() is true"); + error.SetErrorToGenericError(); + error.SetErrorString(unsupported_opcode_error); + return false; + } + + // The semantics of Alloca are: + // Create a region R of virtual memory of type T, backed by a data buffer + // Create a region P of virtual memory of type T*, backed by a data buffer + // Write the virtual address of R into P + + Type *T = alloca_inst->getAllocatedType(); + Type *Tptr = alloca_inst->getType(); + + lldb::addr_t R = frame.Malloc(T); + + if (R == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("Couldn't allocate memory for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_allocation_error); + return false; + } + + lldb::addr_t P = frame.Malloc(Tptr); + + if (P == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("Couldn't allocate the result pointer for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_allocation_error); + return false; + } + + lldb_private::Error write_error; + + memory_map.WritePointerToMemory(P, R, write_error); + + if (!write_error.Success()) + { + if (log) + log->Printf("Couldn't write the result pointer for an AllocaInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + lldb_private::Error free_error; + memory_map.Free(P, free_error); + memory_map.Free(R, free_error); + return false; + } + + frame.m_values[alloca_inst] = P; + + if (log) + { + log->Printf("Interpreted an AllocaInst"); + log->Printf(" R : 0x%" PRIx64, R); + log->Printf(" P : 0x%" PRIx64, P); + } + } + break; + case Instruction::BitCast: + case Instruction::ZExt: + { + const CastInst *cast_inst = dyn_cast<CastInst>(inst); + + if (!cast_inst) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, S, module); + } + break; + case Instruction::SExt: + { + const CastInst *cast_inst = dyn_cast<CastInst>(inst); + + if (!cast_inst) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + S.MakeSigned(); + + lldb_private::Scalar S_signextend(S.SLongLong()); + + frame.AssignValue(inst, S_signextend, module); + } + break; + case Instruction::Br: + { + const BranchInst *br_inst = dyn_cast<BranchInst>(inst); + + if (!br_inst) + { + if (log) + log->Printf("getOpcode() returns Br, but instruction is not a BranchInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + if (br_inst->isConditional()) + { + Value *condition = br_inst->getCondition(); + + lldb_private::Scalar C; + + if (!frame.EvaluateValue(C, condition, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(condition).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (C.GetRawBits64(0)) + frame.Jump(br_inst->getSuccessor(0)); + else + frame.Jump(br_inst->getSuccessor(1)); + + if (log) + { + log->Printf("Interpreted a BrInst with a condition"); + log->Printf(" cond : %s", frame.SummarizeValue(condition).c_str()); + } + } + else + { + frame.Jump(br_inst->getSuccessor(0)); + + if (log) + { + log->Printf("Interpreted a BrInst with no condition"); + } + } + } + continue; + case Instruction::GetElementPtr: + { + const GetElementPtrInst *gep_inst = dyn_cast<GetElementPtrInst>(inst); + + if (!gep_inst) + { + if (log) + log->Printf("getOpcode() returns GetElementPtr, but instruction is not a GetElementPtrInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + const Value *pointer_operand = gep_inst->getPointerOperand(); + Type *pointer_type = pointer_operand->getType(); + + lldb_private::Scalar P; + + if (!frame.EvaluateValue(P, pointer_operand, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(pointer_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + typedef SmallVector <Value *, 8> IndexVector; + typedef IndexVector::iterator IndexIterator; + + SmallVector <Value *, 8> indices (gep_inst->idx_begin(), + gep_inst->idx_end()); + + SmallVector <Value *, 8> const_indices; + + for (IndexIterator ii = indices.begin(), ie = indices.end(); + ii != ie; + ++ii) + { + ConstantInt *constant_index = dyn_cast<ConstantInt>(*ii); + + if (!constant_index) + { + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, *ii, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(*ii).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (log) + log->Printf("Evaluated constant index %s as %llu", PrintValue(*ii).c_str(), I.ULongLong(LLDB_INVALID_ADDRESS)); + + constant_index = cast<ConstantInt>(ConstantInt::get((*ii)->getType(), I.ULongLong(LLDB_INVALID_ADDRESS))); + } + + const_indices.push_back(constant_index); + } + + uint64_t offset = data_layout.getIndexedOffset(pointer_type, const_indices); + + lldb_private::Scalar Poffset = P + offset; + + frame.AssignValue(inst, Poffset, module); + + if (log) + { + log->Printf("Interpreted a GetElementPtrInst"); + log->Printf(" P : %s", frame.SummarizeValue(pointer_operand).c_str()); + log->Printf(" Poffset : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::ICmp: + { + const ICmpInst *icmp_inst = dyn_cast<ICmpInst>(inst); + + if (!icmp_inst) + { + if (log) + log->Printf("getOpcode() returns ICmp, but instruction is not an ICmpInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + CmpInst::Predicate predicate = icmp_inst->getPredicate(); + + Value *lhs = inst->getOperand(0); + Value *rhs = inst->getOperand(1); + + lldb_private::Scalar L; + lldb_private::Scalar R; + + if (!frame.EvaluateValue(L, lhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (!frame.EvaluateValue(R, rhs, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb_private::Scalar result; + + switch (predicate) + { + default: + return false; + case CmpInst::ICMP_EQ: + result = (L == R); + break; + case CmpInst::ICMP_NE: + result = (L != R); + break; + case CmpInst::ICMP_UGT: + result = (L.GetRawBits64(0) > R.GetRawBits64(0)); + break; + case CmpInst::ICMP_UGE: + result = (L.GetRawBits64(0) >= R.GetRawBits64(0)); + break; + case CmpInst::ICMP_ULT: + result = (L.GetRawBits64(0) < R.GetRawBits64(0)); + break; + case CmpInst::ICMP_ULE: + result = (L.GetRawBits64(0) <= R.GetRawBits64(0)); + break; + case CmpInst::ICMP_SGT: + L.MakeSigned(); + R.MakeSigned(); + result = (L > R); + break; + case CmpInst::ICMP_SGE: + L.MakeSigned(); + R.MakeSigned(); + result = (L >= R); + break; + case CmpInst::ICMP_SLT: + L.MakeSigned(); + R.MakeSigned(); + result = (L < R); + break; + case CmpInst::ICMP_SLE: + L.MakeSigned(); + R.MakeSigned(); + result = (L <= R); + break; + } + + frame.AssignValue(inst, result, module); + + if (log) + { + log->Printf("Interpreted an ICmpInst"); + log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str()); + log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::IntToPtr: + { + const IntToPtrInst *int_to_ptr_inst = dyn_cast<IntToPtrInst>(inst); + + if (!int_to_ptr_inst) + { + if (log) + log->Printf("getOpcode() returns IntToPtr, but instruction is not an IntToPtrInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *src_operand = int_to_ptr_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) + { + log->Printf("Interpreted an IntToPtr"); + log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::PtrToInt: + { + const PtrToIntInst *ptr_to_int_inst = dyn_cast<PtrToIntInst>(inst); + + if (!ptr_to_int_inst) + { + if (log) + log->Printf("getOpcode() returns PtrToInt, but instruction is not an PtrToIntInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *src_operand = ptr_to_int_inst->getOperand(0); + + lldb_private::Scalar I; + + if (!frame.EvaluateValue(I, src_operand, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + frame.AssignValue(inst, I, module); + + if (log) + { + log->Printf("Interpreted a PtrToInt"); + log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); + log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); + } + } + break; + case Instruction::Load: + { + const LoadInst *load_inst = dyn_cast<LoadInst>(inst); + + if (!load_inst) + { + if (log) + log->Printf("getOpcode() returns Load, but instruction is not a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + // The semantics of Load are: + // Create a region D that will contain the loaded data + // Resolve the region P containing a pointer + // Dereference P to get the region R that the data should be loaded from + // Transfer a unit of type type(D) from R to D + + const Value *pointer_operand = load_inst->getPointerOperand(); + + Type *pointer_ty = pointer_operand->getType(); + PointerType *pointer_ptr_ty = dyn_cast<PointerType>(pointer_ty); + if (!pointer_ptr_ty) + { + if (log) + log->Printf("getPointerOperand()->getType() is not a PointerType"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + Type *target_ty = pointer_ptr_ty->getElementType(); + + lldb::addr_t D = frame.ResolveValue(load_inst, module); + lldb::addr_t P = frame.ResolveValue(pointer_operand, module); + + if (D == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("LoadInst's value doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (P == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("LoadInst's pointer doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb::addr_t R; + lldb_private::Error read_error; + memory_map.ReadPointerFromMemory(&R, P, read_error); + + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read the address to be loaded for a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + size_t target_size = data_layout.getTypeStoreSize(target_ty); + lldb_private::DataBufferHeap buffer(target_size, 0); + + read_error.Clear(); + memory_map.ReadMemory(buffer.GetBytes(), R, buffer.GetByteSize(), read_error); + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read from a region on behalf of a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + lldb_private::Error write_error; + memory_map.WriteMemory(D, buffer.GetBytes(), buffer.GetByteSize(), write_error); + if (!write_error.Success()) + { + if (log) + log->Printf("Couldn't write to a region on behalf of a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + if (log) + { + log->Printf("Interpreted a LoadInst"); + log->Printf(" P : 0x%" PRIx64, P); + log->Printf(" R : 0x%" PRIx64, R); + log->Printf(" D : 0x%" PRIx64, D); + } + } + break; + case Instruction::Ret: + { + return true; + } + case Instruction::Store: + { + const StoreInst *store_inst = dyn_cast<StoreInst>(inst); + + if (!store_inst) + { + if (log) + log->Printf("getOpcode() returns Store, but instruction is not a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + // The semantics of Store are: + // Resolve the region D containing the data to be stored + // Resolve the region P containing a pointer + // Dereference P to get the region R that the data should be stored in + // Transfer a unit of type type(D) from D to R + + const Value *value_operand = store_inst->getValueOperand(); + const Value *pointer_operand = store_inst->getPointerOperand(); + + Type *pointer_ty = pointer_operand->getType(); + PointerType *pointer_ptr_ty = dyn_cast<PointerType>(pointer_ty); + if (!pointer_ptr_ty) + return false; + Type *target_ty = pointer_ptr_ty->getElementType(); + + lldb::addr_t D = frame.ResolveValue(value_operand, module); + lldb::addr_t P = frame.ResolveValue(pointer_operand, module); + + if (D == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("StoreInst's value doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + if (P == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf("StoreInst's pointer doesn't resolve to anything"); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + lldb::addr_t R; + lldb_private::Error read_error; + memory_map.ReadPointerFromMemory(&R, P, read_error); + + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read the address to be loaded for a LoadInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + size_t target_size = data_layout.getTypeStoreSize(target_ty); + lldb_private::DataBufferHeap buffer(target_size, 0); + + read_error.Clear(); + memory_map.ReadMemory(buffer.GetBytes(), D, buffer.GetByteSize(), read_error); + if (!read_error.Success()) + { + if (log) + log->Printf("Couldn't read from a region on behalf of a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_read_error); + return false; + } + + lldb_private::Error write_error; + memory_map.WriteMemory(R, buffer.GetBytes(), buffer.GetByteSize(), write_error); + if (!write_error.Success()) + { + if (log) + log->Printf("Couldn't write to a region on behalf of a StoreInst"); + error.SetErrorToGenericError(); + error.SetErrorString(memory_write_error); + return false; + } + + if (log) + { + log->Printf("Interpreted a StoreInst"); + log->Printf(" D : 0x%" PRIx64, D); + log->Printf(" P : 0x%" PRIx64, P); + log->Printf(" R : 0x%" PRIx64, R); + } + } + break; + } + + ++frame.m_ii; + } + + if (num_insts >= 4096) + { + error.SetErrorToGenericError(); + error.SetErrorString(infinite_loop_error); + return false; + } + + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Expression/IRMemoryMap.cpp b/contrib/llvm/tools/lldb/source/Expression/IRMemoryMap.cpp new file mode 100644 index 0000000..ef362ba --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/IRMemoryMap.cpp @@ -0,0 +1,758 @@ +//===-- IRMemoryMap.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +IRMemoryMap::IRMemoryMap (lldb::TargetSP target_sp) : + m_target_wp(target_sp) +{ + if (target_sp) + m_process_wp = target_sp->GetProcessSP(); +} + +IRMemoryMap::~IRMemoryMap () +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + { + AllocationMap::iterator iter; + + Error err; + + while ((iter = m_allocations.begin()) != m_allocations.end()) + { + err.Clear(); + if (iter->second.m_leak) + m_allocations.erase(iter); + else + Free(iter->first, err); + } + } +} + +lldb::addr_t +IRMemoryMap::FindSpace (size_t size) +{ + lldb::TargetSP target_sp = m_target_wp.lock(); + lldb::ProcessSP process_sp = m_process_wp.lock(); + + lldb::addr_t ret = LLDB_INVALID_ADDRESS; + + if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) + { + Error alloc_error; + + ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable | lldb::ePermissionsWritable, alloc_error); + + if (!alloc_error.Success()) + return LLDB_INVALID_ADDRESS; + else + return ret; + } + + for (int iterations = 0; iterations < 16; ++iterations) + { + lldb::addr_t candidate = LLDB_INVALID_ADDRESS; + + switch (target_sp->GetArchitecture().GetAddressByteSize()) + { + case 4: + { + uint32_t random_data = random(); + candidate = random_data; + candidate &= ~0xfffull; + break; + } + case 8: + { + uint32_t random_low = random(); + uint32_t random_high = random(); + candidate = random_high; + candidate <<= 32ull; + candidate |= random_low; + candidate &= ~0xfffull; + break; + } + } + + if (IntersectsAllocation(candidate, size)) + continue; + + ret = candidate; + + return ret; + } + + return ret; +} + +IRMemoryMap::AllocationMap::iterator +IRMemoryMap::FindAllocation (lldb::addr_t addr, size_t size) +{ + if (addr == LLDB_INVALID_ADDRESS) + return m_allocations.end(); + + AllocationMap::iterator iter = m_allocations.lower_bound (addr); + + if (iter == m_allocations.end() || + iter->first > addr) + { + if (iter == m_allocations.begin()) + return m_allocations.end(); + iter--; + } + + if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size) + return iter; + + return m_allocations.end(); +} + +bool +IRMemoryMap::IntersectsAllocation (lldb::addr_t addr, size_t size) +{ + if (addr == LLDB_INVALID_ADDRESS) + return false; + + AllocationMap::iterator iter = m_allocations.lower_bound (addr); + + if (iter == m_allocations.end() || + iter->first > addr) + { + if (iter == m_allocations.begin()) + return false; + + iter--; + } + + while (iter != m_allocations.end() && iter->second.m_process_alloc < addr + size) + { + if (iter->second.m_process_start + iter->second.m_size > addr) + return true; + + ++iter; + } + + return false; +} + +lldb::ByteOrder +IRMemoryMap::GetByteOrder() +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp->GetByteOrder(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp->GetArchitecture().GetByteOrder(); + + return lldb::eByteOrderInvalid; +} + +uint32_t +IRMemoryMap::GetAddressByteSize() +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp->GetAddressByteSize(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp->GetArchitecture().GetAddressByteSize(); + + return UINT32_MAX; +} + +ExecutionContextScope * +IRMemoryMap::GetBestExecutionContextScope() +{ + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + return process_sp.get(); + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + return target_sp.get(); + + return NULL; +} + +IRMemoryMap::Allocation::Allocation (lldb::addr_t process_alloc, + lldb::addr_t process_start, + size_t size, + uint32_t permissions, + uint8_t alignment, + AllocationPolicy policy) : + m_process_alloc (process_alloc), + m_process_start (process_start), + m_size (size), + m_permissions (permissions), + m_alignment (alignment), + m_policy (policy), + m_leak (false) +{ + switch (policy) + { + default: + assert (0 && "We cannot reach this!"); + case eAllocationPolicyHostOnly: + m_data.SetByteSize(size); + memset(m_data.GetBytes(), 0, size); + break; + case eAllocationPolicyProcessOnly: + break; + case eAllocationPolicyMirror: + m_data.SetByteSize(size); + memset(m_data.GetBytes(), 0, size); + break; + } +} + +lldb::addr_t +IRMemoryMap::Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error) +{ + error.Clear(); + + lldb::ProcessSP process_sp; + lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS; + lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS; + + size_t alignment_mask = alignment - 1; + size_t allocation_size; + + if (size == 0) + allocation_size = alignment; + else + allocation_size = (size & alignment_mask) ? ((size + alignment) & (~alignment_mask)) : size; + + switch (policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: invalid allocation policy"); + return LLDB_INVALID_ADDRESS; + case eAllocationPolicyHostOnly: + allocation_address = FindSpace(allocation_size); + if (allocation_address == LLDB_INVALID_ADDRESS) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: address space is full"); + return LLDB_INVALID_ADDRESS; + } + break; + case eAllocationPolicyMirror: + process_sp = m_process_wp.lock(); + if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) + { + allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } + else + { + policy = eAllocationPolicyHostOnly; + allocation_address = FindSpace(allocation_size); + if (allocation_address == LLDB_INVALID_ADDRESS) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: address space is full"); + return LLDB_INVALID_ADDRESS; + } + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) + { + if (process_sp->CanJIT() && process_sp->IsAlive()) + { + allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: process doesn't support allocating memory"); + return LLDB_INVALID_ADDRESS; + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't malloc: process doesn't exist, and this memory must be in the process"); + return LLDB_INVALID_ADDRESS; + } + break; + } + + + lldb::addr_t mask = alignment - 1; + aligned_address = (allocation_address + mask) & (~mask); + + m_allocations[aligned_address] = Allocation(allocation_address, + aligned_address, + allocation_size, + permissions, + alignment, + policy); + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + const char * policy_string; + + switch (policy) + { + default: + policy_string = "<invalid policy>"; + break; + case eAllocationPolicyHostOnly: + policy_string = "eAllocationPolicyHostOnly"; + break; + case eAllocationPolicyProcessOnly: + policy_string = "eAllocationPolicyProcessOnly"; + break; + case eAllocationPolicyMirror: + policy_string = "eAllocationPolicyMirror"; + break; + } + + log->Printf("IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", %s) -> 0x%" PRIx64, + (uint64_t)allocation_size, + (uint64_t)alignment, + (uint64_t)permissions, + policy_string, + aligned_address); + } + + return aligned_address; +} + +void +IRMemoryMap::Leak (lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't leak: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + allocation.m_leak = true; +} + +void +IRMemoryMap::Free (lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't free: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + switch (allocation.m_policy) + { + default: + case eAllocationPolicyHostOnly: + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) + { + if (process_sp->CanJIT() && process_sp->IsAlive()) + process_sp->DeallocateMemory(allocation.m_process_alloc); // FindSpace allocated this for real + } + + break; + } + case eAllocationPolicyMirror: + case eAllocationPolicyProcessOnly: + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + if (process_sp) + process_sp->DeallocateMemory(allocation.m_process_alloc); + } + } + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, + iter->second.m_process_start, + iter->second.m_process_start + iter->second.m_size); + } + + m_allocations.erase(iter); +} + +void +IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + { + process_sp->WriteMemory(process_address, bytes, size, error); + return; + } + + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: no allocation contains the target range and the process doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + uint64_t offset = process_address - allocation.m_process_start; + + lldb::ProcessSP process_sp; + + switch (allocation.m_policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: invalid allocation policy"); + return; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: data buffer is empty"); + return; + } + ::memcpy (allocation.m_data.GetBytes() + offset, bytes, size); + break; + case eAllocationPolicyMirror: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't write: data buffer is empty"); + return; + } + ::memcpy (allocation.m_data.GetBytes() + offset, bytes, size); + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->WriteMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->WriteMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + } + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, + (uint64_t)bytes, + (uint64_t)size, + (uint64_t)allocation.m_process_start, + (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size); + } +} + +void +IRMemoryMap::WriteScalarToMemory (lldb::addr_t process_address, Scalar &scalar, size_t size, Error &error) +{ + error.Clear(); + + if (size == UINT32_MAX) + size = scalar.GetByteSize(); + + if (size > 0) + { + uint8_t buf[32]; + const size_t mem_size = scalar.GetAsMemoryData (buf, size, GetByteOrder(), error); + if (mem_size > 0) + { + return WriteMemory(process_address, buf, mem_size, error); + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't write scalar: failed to get scalar as memory data"); + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't write scalar: its size was zero"); + } + return; +} + +void +IRMemoryMap::WritePointerToMemory (lldb::addr_t process_address, lldb::addr_t address, Error &error) +{ + error.Clear(); + + Scalar scalar(address); + + WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error); +} + +void +IRMemoryMap::ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (process_sp) + { + process_sp->ReadMemory(process_address, bytes, size, error); + return; + } + + lldb::TargetSP target_sp = m_target_wp.lock(); + + if (target_sp) + { + Address absolute_address(process_address); + target_sp->ReadMemory(absolute_address, false, bytes, size, error); + return; + } + + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: no allocation contains the target range, and neither the process nor the target exist"); + return; + } + + Allocation &allocation = iter->second; + + uint64_t offset = process_address - allocation.m_process_start; + + lldb::ProcessSP process_sp; + + switch (allocation.m_policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: invalid allocation policy"); + return; + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data buffer is empty"); + return; + } + ::memcpy (bytes, allocation.m_data.GetBytes() + offset, size); + break; + case eAllocationPolicyMirror: + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->ReadMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + else + { + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't read: data buffer is empty"); + return; + } + ::memcpy (bytes, allocation.m_data.GetBytes() + offset, size); + } + break; + case eAllocationPolicyProcessOnly: + process_sp = m_process_wp.lock(); + if (process_sp) + { + process_sp->ReadMemory(process_address, bytes, size, error); + if (!error.Success()) + return; + } + break; + } + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")", + (uint64_t)process_address, + (uint64_t)bytes, + (uint64_t)size, + (uint64_t)allocation.m_process_start, + (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size); + } +} + +void +IRMemoryMap::ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error) +{ + error.Clear(); + + if (size > 0) + { + DataBufferHeap buf(size, 0); + ReadMemory(buf.GetBytes(), process_address, size, error); + + if (!error.Success()) + return; + + DataExtractor extractor(buf.GetBytes(), buf.GetByteSize(), GetByteOrder(), GetAddressByteSize()); + + lldb::offset_t offset = 0; + + switch (size) + { + default: + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size); + return; + case 1: scalar = extractor.GetU8(&offset); break; + case 2: scalar = extractor.GetU16(&offset); break; + case 4: scalar = extractor.GetU32(&offset); break; + case 8: scalar = extractor.GetU64(&offset); break; + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't read scalar: its size was zero"); + } + return; +} + +void +IRMemoryMap::ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + Scalar pointer_scalar; + ReadScalarFromMemory(pointer_scalar, process_address, GetAddressByteSize(), error); + + if (!error.Success()) + return; + + *address = pointer_scalar.ULongLong(); + + return; +} + +void +IRMemoryMap::GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error) +{ + error.Clear(); + + if (size > 0) + { + AllocationMap::iterator iter = FindAllocation(process_address, size); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 ")", process_address, process_address + size); + return; + } + + Allocation &allocation = iter->second; + + switch (allocation.m_policy) + { + default: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: invalid allocation policy"); + return; + case eAllocationPolicyProcessOnly: + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: memory is only in the target"); + return; + case eAllocationPolicyMirror: + { + lldb::ProcessSP process_sp = m_process_wp.lock(); + + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: data buffer is empty"); + return; + } + if (process_sp) + { + process_sp->ReadMemory(allocation.m_process_start, allocation.m_data.GetBytes(), allocation.m_data.GetByteSize(), error); + if (!error.Success()) + return; + uint64_t offset = process_address - allocation.m_process_start; + extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize()); + return; + } + } + case eAllocationPolicyHostOnly: + if (!allocation.m_data.GetByteSize()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't get memory data: data buffer is empty"); + return; + } + uint64_t offset = process_address - allocation.m_process_start; + extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize()); + return; + } + } + else + { + error.SetErrorToGenericError(); + error.SetErrorString ("Couldn't get memory data: its size was zero"); + return; + } +} + + diff --git a/contrib/llvm/tools/lldb/source/Expression/Materializer.cpp b/contrib/llvm/tools/lldb/source/Expression/Materializer.cpp new file mode 100644 index 0000000..8a1900e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Expression/Materializer.cpp @@ -0,0 +1,1414 @@ +//===-- Materializer.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +uint32_t +Materializer::AddStructMember (Entity &entity) +{ + uint32_t size = entity.GetSize(); + uint32_t alignment = entity.GetAlignment(); + + uint32_t ret; + + if (m_current_offset == 0) + m_struct_alignment = alignment; + + if (m_current_offset % alignment) + m_current_offset += (alignment - (m_current_offset % alignment)); + + ret = m_current_offset; + + m_current_offset += size; + + return ret; +} + +void +Materializer::Entity::SetSizeAndAlignmentFromType (ClangASTType &type) +{ + m_size = type.GetByteSize(); + + uint32_t bit_alignment = type.GetTypeBitAlign(); + + if (bit_alignment % 8) + { + bit_alignment += 8; + bit_alignment &= ~((uint32_t)0x111u); + } + + m_alignment = bit_alignment / 8; +} + +class EntityPersistentVariable : public Materializer::Entity +{ +public: + EntityPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp) : + Entity(), + m_persistent_variable_sp(persistent_variable_sp) + { + // Hard-coding to maximum size of a pointer since persistent variables are materialized by reference + m_size = 8; + m_alignment = 8; + } + + void MakeAllocation (IRMemoryMap &map, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + // Allocate a spare memory area to store the persistent variable's contents. + + Error allocate_error; + + lldb::addr_t mem = map.Malloc(m_persistent_variable_sp->GetByteSize(), + 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, + allocate_error); + + if (!allocate_error.Success()) + { + err.SetErrorStringWithFormat("couldn't allocate a memory area to store %s: %s", m_persistent_variable_sp->GetName().GetCString(), allocate_error.AsCString()); + return; + } + + if (log) + log->Printf("Allocated %s (0x%" PRIx64 ") sucessfully", m_persistent_variable_sp->GetName().GetCString(), mem); + + // Put the location of the spare memory into the live data of the ValueObject. + + m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create (map.GetBestExecutionContextScope(), + m_persistent_variable_sp->GetTypeFromUser(), + m_persistent_variable_sp->GetName(), + mem, + eAddressTypeLoad, + m_persistent_variable_sp->GetByteSize()); + + // Clear the flag if the variable will never be deallocated. + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget) + { + Error leak_error; + map.Leak(mem, leak_error); + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVNeedsAllocation; + } + + // Write the contents of the variable to the area. + + Error write_error; + + map.WriteMemory (mem, + m_persistent_variable_sp->GetValueBytes(), + m_persistent_variable_sp->GetByteSize(), + write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat ("couldn't write %s to the target: %s", m_persistent_variable_sp->GetName().AsCString(), + write_error.AsCString()); + return; + } + } + + void DestroyAllocation (IRMemoryMap &map, Error &err) + { + Error deallocate_error; + + map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue().GetScalar().ULongLong(), deallocate_error); + + m_persistent_variable_sp->m_live_sp.reset(); + + if (!deallocate_error.Success()) + { + err.SetErrorStringWithFormat ("couldn't deallocate memory for %s: %s", m_persistent_variable_sp->GetName().GetCString(), deallocate_error.AsCString()); + } + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityPersistentVariable::Materialize [address = 0x%" PRIx64 ", m_name = %s, m_flags = 0x%hx]", + (uint64_t)load_addr, + m_persistent_variable_sp->GetName().AsCString(), + m_persistent_variable_sp->m_flags); + } + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsAllocation) + { + MakeAllocation(map, err); + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + + if (!err.Success()) + return; + } + + if ((m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference && m_persistent_variable_sp->m_live_sp) || + m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsLLDBAllocated) + { + Error write_error; + + map.WriteScalarToMemory(load_addr, + m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(), + map.GetAddressByteSize(), + write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the location of %s to memory: %s", m_persistent_variable_sp->GetName().AsCString(), write_error.AsCString()); + } + } + else + { + err.SetErrorStringWithFormat("no materialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); + return; + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityPersistentVariable::Dematerialize [address = 0x%" PRIx64 ", m_name = %s, m_flags = 0x%hx]", + (uint64_t)process_address + m_offset, + m_persistent_variable_sp->GetName().AsCString(), + m_persistent_variable_sp->m_flags); + } + + if ((m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsLLDBAllocated) || + (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference)) + { + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference && + !m_persistent_variable_sp->m_live_sp) + { + // If the reference comes from the program, then the ClangExpressionVariable's + // live variable data hasn't been set up yet. Do this now. + + lldb::addr_t location; + Error read_error; + + map.ReadPointerFromMemory(&location, load_addr, read_error); + + if (!read_error.Success()) + { + err.SetErrorStringWithFormat("couldn't read the address of program-allocated variable %s: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); + return; + } + + m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create (map.GetBestExecutionContextScope (), + m_persistent_variable_sp->GetTypeFromUser(), + m_persistent_variable_sp->GetName(), + location, + eAddressTypeLoad, + m_persistent_variable_sp->GetByteSize()); + + if (frame_top != LLDB_INVALID_ADDRESS && + frame_bottom != LLDB_INVALID_ADDRESS && + location >= frame_bottom && + location <= frame_top) + { + // If the variable is resident in the stack frame created by the expression, + // then it cannot be relied upon to stay around. We treat it as needing + // reallocation. + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVIsProgramReference; + } + } + + lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue().GetScalar().ULongLong(); + + if (!m_persistent_variable_sp->m_live_sp) + { + err.SetErrorStringWithFormat("couldn't find the memory area used to store %s", m_persistent_variable_sp->GetName().GetCString()); + return; + } + + if (m_persistent_variable_sp->m_live_sp->GetValue().GetValueAddressType() != eAddressTypeLoad) + { + err.SetErrorStringWithFormat("the address of the memory area for %s is in an incorrect format", m_persistent_variable_sp->GetName().GetCString()); + return; + } + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsFreezeDry || + m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget) + { + if (log) + log->Printf("Dematerializing %s from 0x%" PRIx64 " (size = %llu)", m_persistent_variable_sp->GetName().GetCString(), (uint64_t)mem, (unsigned long long)m_persistent_variable_sp->GetByteSize()); + + // Read the contents of the spare memory area + + m_persistent_variable_sp->ValueUpdated (); + + Error read_error; + + map.ReadMemory(m_persistent_variable_sp->GetValueBytes(), + mem, + m_persistent_variable_sp->GetByteSize(), + read_error); + + if (!read_error.Success()) + { + err.SetErrorStringWithFormat ("couldn't read the contents of %s from memory: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); + return; + } + + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVNeedsFreezeDry; + } + } + else + { + err.SetErrorStringWithFormat("no dematerialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); + return; + } + + lldb::ProcessSP process_sp = map.GetBestExecutionContextScope()->CalculateProcess(); + if (!process_sp || + !process_sp->CanJIT()) + { + // Allocations are not persistent so persistent variables cannot stay materialized. + + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + else if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsAllocation && + !(m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget)) + { + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n", load_addr, m_persistent_variable_sp->GetName().AsCString()); + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + { + dump_stream.Printf("Target:\n"); + + lldb::addr_t target_address; + + map.ReadPointerFromMemory (&target_address, load_addr, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataBufferHeap data (m_persistent_variable_sp->GetByteSize(), 0); + + map.ReadMemory(data.GetBytes(), target_address, m_persistent_variable_sp->GetByteSize(), err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, target_address); + + dump_stream.PutChar('\n'); + } + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + lldb::ClangExpressionVariableSP m_persistent_variable_sp; +}; + +uint32_t +Materializer::AddPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityPersistentVariable (persistent_variable_sp)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityVariable : public Materializer::Entity +{ +public: + EntityVariable (lldb::VariableSP &variable_sp) : + Entity(), + m_variable_sp(variable_sp), + m_is_reference(false), + m_temporary_allocation(LLDB_INVALID_ADDRESS), + m_temporary_allocation_size(0) + { + // Hard-coding to maximum size of a pointer since all variables are materialized by reference + m_size = 8; + m_alignment = 8; + m_is_reference = m_variable_sp->GetType()->GetClangForwardType().IsReferenceType(); + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + if (log) + { + log->Printf("EntityVariable::Materialize [address = 0x%" PRIx64 ", m_variable_sp = %s]", + (uint64_t)load_addr, + m_variable_sp->GetName().AsCString()); + } + + ExecutionContextScope *scope = frame_sp.get(); + + if (!scope) + scope = map.GetBestExecutionContextScope(); + + lldb::ValueObjectSP valobj_sp = ValueObjectVariable::Create(scope, m_variable_sp); + + if (!valobj_sp) + { + err.SetErrorStringWithFormat("couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); + return; + } + + if (m_is_reference) + { + DataExtractor valobj_extractor; + valobj_sp->GetData(valobj_extractor); + lldb::offset_t offset = 0; + lldb::addr_t reference_addr = valobj_extractor.GetAddress(&offset); + + Error write_error; + map.WritePointerToMemory(load_addr, reference_addr, write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the contents of reference variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + return; + } + } + else + { + Error get_address_error; + lldb::ValueObjectSP addr_of_valobj_sp = valobj_sp->AddressOf(get_address_error); + if (get_address_error.Success()) + { + DataExtractor valobj_extractor; + addr_of_valobj_sp->GetData(valobj_extractor); + lldb::offset_t offset = 0; + lldb::addr_t addr_of_valobj_addr = valobj_extractor.GetAddress(&offset); + + Error write_error; + map.WritePointerToMemory(load_addr, addr_of_valobj_addr, write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + return; + } + } + else + { + DataExtractor data; + valobj_sp->GetData(data); + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + err.SetErrorStringWithFormat("trying to create a temporary region for %s but one exists", m_variable_sp->GetName().AsCString()); + return; + } + + if (data.GetByteSize() != m_variable_sp->GetType()->GetByteSize()) + { + if (data.GetByteSize() == 0 && m_variable_sp->LocationExpression().IsValid() == false) + { + err.SetErrorStringWithFormat("the variable '%s' has no location, it may have been optimized out", m_variable_sp->GetName().AsCString()); + } + else + { + err.SetErrorStringWithFormat("size of variable %s disagrees with the ValueObject's size", m_variable_sp->GetName().AsCString()); + } + return; + } + + size_t bit_align = m_variable_sp->GetType()->GetClangLayoutType().GetTypeBitAlign(); + size_t byte_align = (bit_align + 7) / 8; + + Error alloc_error; + + m_temporary_allocation = map.Malloc(data.GetByteSize(), byte_align, lldb::ePermissionsReadable | lldb::ePermissionsWritable, IRMemoryMap::eAllocationPolicyMirror, alloc_error); + m_temporary_allocation_size = data.GetByteSize(); + + if (!alloc_error.Success()) + { + err.SetErrorStringWithFormat("couldn't allocate a temporary region for %s: %s", m_variable_sp->GetName().AsCString(), alloc_error.AsCString()); + return; + } + + Error write_error; + + map.WriteMemory(m_temporary_allocation, data.GetDataStart(), data.GetByteSize(), write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write to the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + return; + } + + Error pointer_write_error; + + map.WritePointerToMemory(load_addr, m_temporary_allocation, pointer_write_error); + + if (!pointer_write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), pointer_write_error.AsCString()); + } + } + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + if (log) + { + log->Printf("EntityVariable::Dematerialize [address = 0x%" PRIx64 ", m_variable_sp = %s]", + (uint64_t)load_addr, + m_variable_sp->GetName().AsCString()); + } + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + ExecutionContextScope *scope = frame_sp.get(); + + if (!scope) + scope = map.GetBestExecutionContextScope(); + + lldb::ValueObjectSP valobj_sp = ValueObjectVariable::Create(scope, m_variable_sp); + + if (!valobj_sp) + { + err.SetErrorStringWithFormat("couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); + return; + } + + lldb_private::DataExtractor data; + + Error extract_error; + + map.GetMemoryData(data, m_temporary_allocation, valobj_sp->GetByteSize(), extract_error); + + if (!extract_error.Success()) + { + err.SetErrorStringWithFormat("couldn't get the data for variable %s", m_variable_sp->GetName().AsCString()); + return; + } + + Error set_error; + + valobj_sp->SetData(data, set_error); + + if (!set_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the new contents of %s back into the variable", m_variable_sp->GetName().AsCString()); + return; + } + + Error free_error; + + map.Free(m_temporary_allocation, free_error); + + if (!free_error.Success()) + { + err.SetErrorStringWithFormat("couldn't free the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), free_error.AsCString()); + return; + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", load_addr); + + Error err; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + lldb::offset_t offset; + + ptr = extractor.GetPointer(&offset); + + dump_stream.PutChar('\n'); + } + } + + if (m_temporary_allocation == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf("Points to process memory:\n"); + } + else + { + dump_stream.Printf("Temporary allocation:\n"); + } + + if (ptr == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf(" <could not be be found>\n"); + } + else + { + DataBufferHeap data (m_temporary_allocation_size, 0); + + map.ReadMemory(data.GetBytes(), m_temporary_allocation, m_temporary_allocation_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + + map.Free(m_temporary_allocation, free_error); + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + + } +private: + lldb::VariableSP m_variable_sp; + bool m_is_reference; + lldb::addr_t m_temporary_allocation; + size_t m_temporary_allocation_size; +}; + +uint32_t +Materializer::AddVariable (lldb::VariableSP &variable_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityVariable (variable_sp)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityResultVariable : public Materializer::Entity +{ +public: + EntityResultVariable (const TypeFromUser &type, bool is_program_reference, bool keep_in_memory) : + Entity(), + m_type(type), + m_is_program_reference(is_program_reference), + m_keep_in_memory(keep_in_memory), + m_temporary_allocation(LLDB_INVALID_ADDRESS), + m_temporary_allocation_size(0) + { + // Hard-coding to maximum size of a pointer since all results are materialized by reference + m_size = 8; + m_alignment = 8; + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + if (!m_is_program_reference) + { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + err.SetErrorString("Trying to create a temporary region for the result but one exists"); + return; + } + + const lldb::addr_t load_addr = process_address + m_offset; + + size_t byte_size = m_type.GetByteSize(); + size_t bit_align = m_type.GetTypeBitAlign(); + size_t byte_align = (bit_align + 7) / 8; + + Error alloc_error; + + m_temporary_allocation = map.Malloc(byte_size, byte_align, lldb::ePermissionsReadable | lldb::ePermissionsWritable, IRMemoryMap::eAllocationPolicyMirror, alloc_error); + m_temporary_allocation_size = byte_size; + + if (!alloc_error.Success()) + { + err.SetErrorStringWithFormat("couldn't allocate a temporary region for the result: %s", alloc_error.AsCString()); + return; + } + + Error pointer_write_error; + + map.WritePointerToMemory(load_addr, m_temporary_allocation, pointer_write_error); + + if (!pointer_write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of the temporary region for the result: %s", pointer_write_error.AsCString()); + } + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + err.SetErrorString("Tried to detmaterialize a result variable with the normal Dematerialize method"); + } + + void Dematerialize (lldb::ClangExpressionVariableSP &result_variable_sp, + lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + err.Clear(); + + ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope(); + + if (!exe_scope) + { + err.SetErrorString("Couldn't dematerialize a result variable: invalid execution context scope"); + return; + } + + lldb::addr_t address; + Error read_error; + const lldb::addr_t load_addr = process_address + m_offset; + + map.ReadPointerFromMemory (&address, load_addr, read_error); + + if (!read_error.Success()) + { + err.SetErrorString("Couldn't dematerialize a result variable: couldn't read its address"); + return; + } + + lldb::TargetSP target_sp = exe_scope->CalculateTarget(); + + if (!target_sp) + { + err.SetErrorString("Couldn't dematerialize a result variable: no target"); + return; + } + + ConstString name = target_sp->GetPersistentVariables().GetNextPersistentVariableName(); + + lldb::ClangExpressionVariableSP ret; + + ret = target_sp->GetPersistentVariables().CreateVariable(exe_scope, + name, + m_type, + map.GetByteOrder(), + map.GetAddressByteSize()); + + if (!ret) + { + err.SetErrorStringWithFormat("couldn't dematerialize a result variable: failed to make persistent variable %s", name.AsCString()); + return; + } + + lldb::ProcessSP process_sp = map.GetBestExecutionContextScope()->CalculateProcess(); + + bool can_persist = (m_is_program_reference && process_sp && process_sp->CanJIT() && !(address >= frame_bottom && address < frame_top)); + + if (can_persist && m_keep_in_memory) + { + ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, + m_type, + name, + address, + eAddressTypeLoad, + ret->GetByteSize()); + } + + ret->ValueUpdated(); + + const size_t pvar_byte_size = ret->GetByteSize(); + uint8_t *pvar_data = ret->GetValueBytes(); + + map.ReadMemory(pvar_data, address, pvar_byte_size, read_error); + + if (!read_error.Success()) + { + err.SetErrorString("Couldn't dematerialize a result variable: couldn't read its memory"); + return; + } + + result_variable_sp = ret; + + if (!can_persist || !m_keep_in_memory) + { + ret->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + map.Free(m_temporary_allocation, free_error); + } + } + else + { + ret->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr); + + Error err; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + lldb::offset_t offset; + + ptr = extractor.GetPointer(&offset); + + dump_stream.PutChar('\n'); + } + } + + if (m_temporary_allocation == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf("Points to process memory:\n"); + } + else + { + dump_stream.Printf("Temporary allocation:\n"); + } + + if (ptr == LLDB_INVALID_ADDRESS) + { + dump_stream.Printf(" <could not be be found>\n"); + } + else + { + DataBufferHeap data (m_temporary_allocation_size, 0); + + map.ReadMemory(data.GetBytes(), m_temporary_allocation, m_temporary_allocation_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + + map.Free(m_temporary_allocation, free_error); + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } +private: + TypeFromUser m_type; + bool m_is_program_reference; + bool m_keep_in_memory; + + lldb::addr_t m_temporary_allocation; + size_t m_temporary_allocation_size; +}; + +uint32_t +Materializer::AddResultVariable (const TypeFromUser &type, bool is_program_reference, bool keep_in_memory, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityResultVariable (type, is_program_reference, keep_in_memory)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + m_result_entity = iter->get(); + return ret; +} + +class EntitySymbol : public Materializer::Entity +{ +public: + EntitySymbol (const Symbol &symbol) : + Entity(), + m_symbol(symbol) + { + // Hard-coding to maximum size of a symbol + m_size = 8; + m_alignment = 8; + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntitySymbol::Materialize [address = 0x%" PRIx64 ", m_symbol = %s]", + (uint64_t)load_addr, + m_symbol.GetName().AsCString()); + } + + Address &sym_address = m_symbol.GetAddress(); + + ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope(); + + lldb::TargetSP target_sp; + + if (exe_scope) + target_sp = map.GetBestExecutionContextScope()->CalculateTarget(); + + if (!target_sp) + { + err.SetErrorStringWithFormat("couldn't resolve symbol %s because there is no target", m_symbol.GetName().AsCString()); + return; + } + + lldb::addr_t resolved_address = sym_address.GetLoadAddress(target_sp.get()); + + if (resolved_address == LLDB_INVALID_ADDRESS) + resolved_address = sym_address.GetFileAddress(); + + Error pointer_write_error; + + map.WritePointerToMemory(load_addr, resolved_address, pointer_write_error); + + if (!pointer_write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the address of symbol %s: %s", m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); + return; + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntitySymbol::Dematerialize [address = 0x%" PRIx64 ", m_symbol = %s]", + (uint64_t)load_addr, + m_symbol.GetName().AsCString()); + } + + // no work needs to be done + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error err; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntitySymbol (%s)\n", load_addr, m_symbol.GetName().AsCString()); + + { + dump_stream.Printf("Pointer:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + Symbol m_symbol; +}; + +uint32_t +Materializer::AddSymbol (const Symbol &symbol_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntitySymbol (symbol_sp)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +class EntityRegister : public Materializer::Entity +{ +public: + EntityRegister (const RegisterInfo ®ister_info) : + Entity(), + m_register_info(register_info) + { + // Hard-coding alignment conservatively + m_size = m_register_info.byte_size; + m_alignment = m_register_info.byte_size; + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityRegister::Materialize [address = 0x%" PRIx64 ", m_register_info = %s]", + (uint64_t)load_addr, + m_register_info.name); + } + + RegisterValue reg_value; + + if (!frame_sp.get()) + { + err.SetErrorStringWithFormat("couldn't materialize register %s without a stack frame", m_register_info.name); + return; + } + + lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); + + if (!reg_context_sp->ReadRegister(&m_register_info, reg_value)) + { + err.SetErrorStringWithFormat("couldn't read the value of register %s", m_register_info.name); + return; + } + + DataExtractor register_data; + + if (!reg_value.GetData(register_data)) + { + err.SetErrorStringWithFormat("couldn't get the data for register %s", m_register_info.name); + return; + } + + if (register_data.GetByteSize() != m_register_info.byte_size) + { + err.SetErrorStringWithFormat("data for register %s had size %llu but we expected %llu", m_register_info.name, (unsigned long long)register_data.GetByteSize(), (unsigned long long)m_register_info.byte_size); + return; + } + + m_register_contents.reset(new DataBufferHeap(register_data.GetDataStart(), register_data.GetByteSize())); + + Error write_error; + + map.WriteMemory(load_addr, register_data.GetDataStart(), register_data.GetByteSize(), write_error); + + if (!write_error.Success()) + { + err.SetErrorStringWithFormat("couldn't write the contents of register %s: %s", m_register_info.name, write_error.AsCString()); + return; + } + } + + void Dematerialize (lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const lldb::addr_t load_addr = process_address + m_offset; + + if (log) + { + log->Printf("EntityRegister::Dematerialize [address = 0x%" PRIx64 ", m_register_info = %s]", + (uint64_t)load_addr, + m_register_info.name); + } + + Error extract_error; + + DataExtractor register_data; + + if (!frame_sp.get()) + { + err.SetErrorStringWithFormat("couldn't dematerialize register %s without a stack frame", m_register_info.name); + return; + } + + lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); + + map.GetMemoryData(register_data, load_addr, m_register_info.byte_size, extract_error); + + if (!extract_error.Success()) + { + err.SetErrorStringWithFormat("couldn't get the data for register %s: %s", m_register_info.name, extract_error.AsCString()); + return; + } + + if (!memcmp(register_data.GetDataStart(), m_register_contents->GetBytes(), register_data.GetByteSize())) + { + // No write required, and in particular we avoid errors if the register wasn't writable + + m_register_contents.reset(); + return; + } + + m_register_contents.reset(); + + RegisterValue register_value (const_cast<uint8_t*>(register_data.GetDataStart()), register_data.GetByteSize(), register_data.GetByteOrder()); + + if (!reg_context_sp->WriteRegister(&m_register_info, register_value)) + { + err.SetErrorStringWithFormat("couldn't write the value of register %s", m_register_info.name); + return; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error err; + + const lldb::addr_t load_addr = process_address + m_offset; + + + dump_stream.Printf("0x%" PRIx64 ": EntityRegister (%s)\n", load_addr, m_register_info.name); + + { + dump_stream.Printf("Value:\n"); + + DataBufferHeap data (m_size, 0); + + map.ReadMemory(data.GetBytes(), load_addr, m_size, err); + + if (!err.Success()) + { + dump_stream.Printf(" <could not be read>\n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + RegisterInfo m_register_info; + lldb::DataBufferSP m_register_contents; +}; + +uint32_t +Materializer::AddRegister (const RegisterInfo ®ister_info, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityRegister (register_info)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + return ret; +} + +Materializer::Materializer () : + m_dematerializer_wp(), + m_result_entity(NULL), + m_current_offset(0), + m_struct_alignment(8) +{ +} + +Materializer::~Materializer () +{ + DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); + + if (dematerializer_sp) + dematerializer_sp->Wipe(); +} + +Materializer::DematerializerSP +Materializer::Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &error) +{ + ExecutionContextScope *exe_scope = frame_sp.get(); + + if (!exe_scope) + exe_scope = map.GetBestExecutionContextScope(); + + DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); + + if (dematerializer_sp) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't materialize: already materialized"); + } + + DematerializerSP ret(new Dematerializer(*this, frame_sp, map, process_address)); + + if (!exe_scope) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't materialize: target doesn't exist"); + } + + for (EntityUP &entity_up : m_entities) + { + entity_up->Materialize(frame_sp, map, process_address, error); + + if (!error.Success()) + return DematerializerSP(); + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("Materializer::Materialize (frame_sp = %p, process_address = 0x%" PRIx64 ") materialized:", frame_sp.get(), process_address); + for (EntityUP &entity_up : m_entities) + entity_up->DumpToLog(map, process_address, log); + } + + m_dematerializer_wp = ret; + + return ret; +} + +void +Materializer::Dematerializer::Dematerialize (Error &error, lldb::ClangExpressionVariableSP &result_sp, lldb::addr_t frame_bottom, lldb::addr_t frame_top) +{ + lldb::StackFrameSP frame_sp; + + lldb::ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + frame_sp = thread_sp->GetFrameWithStackID(m_stack_id); + + ExecutionContextScope *exe_scope = m_map->GetBestExecutionContextScope(); + + if (!IsValid()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't dematerialize: invalid dematerializer"); + } + + if (!exe_scope) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't dematerialize: target is gone"); + } + else + { + if (Log *log =lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + log->Printf("Materializer::Dematerialize (frame_sp = %p, process_address = 0x%" PRIx64 ") about to dematerialize:", frame_sp.get(), m_process_address); + for (EntityUP &entity_up : m_materializer->m_entities) + entity_up->DumpToLog(*m_map, m_process_address, log); + } + + for (EntityUP &entity_up : m_materializer->m_entities) + { + if (entity_up.get() == m_materializer->m_result_entity) + { + static_cast<EntityResultVariable*>(m_materializer->m_result_entity)->Dematerialize (result_sp, frame_sp, *m_map, m_process_address, frame_top, frame_bottom, error); + } + else + { + entity_up->Dematerialize (frame_sp, *m_map, m_process_address, frame_top, frame_bottom, error); + } + + if (!error.Success()) + break; + } + } + + Wipe(); +} + +void +Materializer::Dematerializer::Wipe () +{ + if (!IsValid()) + return; + + for (EntityUP &entity_up : m_materializer->m_entities) + { + entity_up->Wipe (*m_map, m_process_address); + } + + m_materializer = NULL; + m_map = NULL; + m_process_address = LLDB_INVALID_ADDRESS; +} |