2275 lines
70 KiB
C++
2275 lines
70 KiB
C++
//===-- 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 "IRForTarget.h"
|
|
|
|
#include "ClangExpressionDeclMap.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/LegacyPassManager.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/ValueSymbolTable.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Transforms/IPO.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "lldb/Core/Scalar.h"
|
|
#include "lldb/Core/dwarf.h"
|
|
#include "lldb/Expression/IRExecutionUnit.h"
|
|
#include "lldb/Expression/IRInterpreter.h"
|
|
#include "lldb/Symbol/ClangASTContext.h"
|
|
#include "lldb/Symbol/ClangUtil.h"
|
|
#include "lldb/Symbol/CompilerType.h"
|
|
#include "lldb/Utility/ConstString.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/Utility/Endian.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
|
|
#include <map>
|
|
|
|
using namespace llvm;
|
|
|
|
static char ID;
|
|
|
|
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];
|
|
}
|
|
|
|
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_CFStringCreateWithBytes(NULL),
|
|
m_sel_registerName(NULL), m_objc_getClass(NULL), m_intptr_ty(NULL),
|
|
m_error_stream(error_stream),
|
|
m_execution_unit(execution_unit), 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);
|
|
|
|
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) {
|
|
llvm::MDNode *metadata_node =
|
|
dyn_cast<llvm::MDNode>(named_metadata->getOperand(node_index));
|
|
if (!metadata_node)
|
|
return NULL;
|
|
|
|
if (metadata_node->getNumOperands() != 2)
|
|
continue;
|
|
|
|
if (mdconst::dyn_extract_or_null<GlobalValue>(
|
|
metadata_node->getOperand(0)) != global_val)
|
|
continue;
|
|
|
|
ConstantInt *constant_int =
|
|
mdconst::dyn_extract<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");
|
|
|
|
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");
|
|
|
|
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");
|
|
|
|
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");
|
|
|
|
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(),
|
|
lldb_private::ClangASTContext::GetASTContext(
|
|
&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(),
|
|
lldb_private::ClangASTContext::GetASTContext(
|
|
&result_decl->getASTContext()));
|
|
} else {
|
|
if (log)
|
|
log->PutCString("Expected result to have pointer type, but it did not");
|
|
|
|
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(),
|
|
lldb_private::ClangASTContext::GetASTContext(
|
|
&result_decl->getASTContext()));
|
|
}
|
|
|
|
lldb::TargetSP target_sp(m_execution_unit.GetTarget());
|
|
lldb_private::ExecutionContext exe_ctx(target_sp, true);
|
|
if (m_result_type.GetBitSize(exe_ctx.GetBestExecutionContextScope()) == 0) {
|
|
lldb_private::StreamString type_desc_stream;
|
|
m_result_type.DumpTypeDescription(&type_desc_stream);
|
|
|
|
if (log)
|
|
log->Printf("Result type has size 0");
|
|
|
|
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(nullptr));
|
|
|
|
// 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::Metadata *values[2];
|
|
values[0] = ConstantAsMetadata::get(new_result_global);
|
|
values[1] = ConstantAsMetadata::get(new_constant_int);
|
|
|
|
ArrayRef<Metadata *> 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->use_empty()) {
|
|
// 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");
|
|
|
|
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;
|
|
}
|
|
|
|
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());
|
|
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");
|
|
|
|
CFStringCreateWithBytes_addr =
|
|
m_execution_unit.FindSymbol(g_CFStringCreateWithBytes_str);
|
|
if (CFStringCreateWithBytes_addr == LLDB_INVALID_ADDRESS) {
|
|
if (log)
|
|
log->PutCString("Couldn't find CFStringCreateWithBytes in the target");
|
|
|
|
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] = m_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(m_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(
|
|
m_intptr_ty, cstr ? (string_array->getNumElements() - 1) * string_array->getElementByteSize() : 0, false);
|
|
int encoding_flags = 0;
|
|
switch (cstr ? string_array->getElementByteSize() : 1) {
|
|
case 1:
|
|
encoding_flags = 0x08000100; /* 0x08000100 is kCFStringEncodingUTF8 */
|
|
break;
|
|
case 2:
|
|
encoding_flags = 0x0100; /* 0x0100 is kCFStringEncodingUTF16 */
|
|
break;
|
|
case 4:
|
|
encoding_flags = 0x0c000100; /* 0x0c000100 is kCFStringEncodingUTF32 */
|
|
break;
|
|
default:
|
|
encoding_flags = 0x0600; /* fall back to 0x0600, kCFStringEncodingASCII */
|
|
LLDB_LOG(log, "Encountered an Objective-C constant string with unusual "
|
|
"element size {0}",
|
|
string_array->getElementByteSize());
|
|
}
|
|
Constant *encoding_arg = ConstantInt::get(i32_ty, encoding_flags, false);
|
|
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, nullptr, CFSCWB_Caller, m_entry_instruction_finder,
|
|
m_error_stream)) {
|
|
if (log)
|
|
log->PutCString(
|
|
"Couldn't replace the NSString with the result of the call");
|
|
|
|
m_error_stream.Printf("error [IRForTarget internal]: 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");
|
|
|
|
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");
|
|
|
|
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");
|
|
|
|
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());
|
|
|
|
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");
|
|
|
|
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");
|
|
|
|
m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
|
|
"constant string's string initializer is not "
|
|
"constant\n");
|
|
|
|
return false;
|
|
}
|
|
|
|
GlobalVariable *cstr_global = nullptr;
|
|
|
|
if (nsstring_expr->getOpcode() == Instruction::GetElementPtr) {
|
|
Constant *nsstring_cstr = nsstring_expr->getOperand(0);
|
|
cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
|
|
} else if (nsstring_expr->getOpcode() == Instruction::BitCast) {
|
|
Constant *nsstring_cstr = nsstring_expr->getOperand(0);
|
|
cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
|
|
}
|
|
|
|
if (!cstr_global) {
|
|
if (log)
|
|
log->PutCString(
|
|
"NSString initializer's str element is not a GlobalVariable");
|
|
|
|
m_error_stream.Printf("Internal error [IRForTarget]: Unhandled"
|
|
"constant string initializer\n");
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!cstr_global->hasInitializer()) {
|
|
if (log)
|
|
log->PutCString("NSString initializer's str element does not have an "
|
|
"initializer");
|
|
|
|
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");
|
|
|
|
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("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** @"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.
|
|
//
|
|
// @"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");
|
|
sel_registerName_addr = m_execution_unit.FindSymbol(g_sel_registerName_str);
|
|
if (sel_registerName_addr == LLDB_INVALID_ADDRESS)
|
|
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
|
|
PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type);
|
|
Constant *srN_addr_int =
|
|
ConstantInt::get(m_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)) {
|
|
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;
|
|
}
|
|
|
|
static bool IsObjCClassReference(Value *value) {
|
|
GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value);
|
|
|
|
if (!global_variable || !global_variable->hasName() ||
|
|
!global_variable->getName().startswith("OBJC_CLASS_REFERENCES_"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// This function does not report errors; its callers are responsible.
|
|
bool IRForTarget::RewriteObjCClassReference(Instruction *class_load) {
|
|
lldb_private::Log *log(
|
|
lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
|
|
|
|
LoadInst *load = dyn_cast<LoadInst>(class_load);
|
|
|
|
if (!load)
|
|
return false;
|
|
|
|
// Unpack the class name from the reference. In LLVM IR, a reference to an
|
|
// Objective-C class gets represented as
|
|
//
|
|
// %tmp = load %struct._objc_class*,
|
|
// %struct._objc_class** @OBJC_CLASS_REFERENCES_, align 4
|
|
//
|
|
// @"OBJC_CLASS_REFERENCES_ is a bitcast of a character array called
|
|
// @OBJC_CLASS_NAME_.
|
|
// @OBJC_CLASS_NAME contains the string.
|
|
|
|
// Find the pointer's initializer (a ConstantExpr with opcode BitCast)
|
|
// and get the string from its target
|
|
|
|
GlobalVariable *_objc_class_references_ =
|
|
dyn_cast<GlobalVariable>(load->getPointerOperand());
|
|
|
|
if (!_objc_class_references_ ||
|
|
!_objc_class_references_->hasInitializer())
|
|
return false;
|
|
|
|
Constant *ocr_initializer = _objc_class_references_->getInitializer();
|
|
|
|
ConstantExpr *ocr_initializer_expr = dyn_cast<ConstantExpr>(ocr_initializer);
|
|
|
|
if (!ocr_initializer_expr ||
|
|
ocr_initializer_expr->getOpcode() != Instruction::BitCast)
|
|
return false;
|
|
|
|
Value *ocr_initializer_base = ocr_initializer_expr->getOperand(0);
|
|
|
|
if (!ocr_initializer_base)
|
|
return false;
|
|
|
|
// Find the string's initializer (a ConstantArray) and get the string from it
|
|
|
|
GlobalVariable *_objc_class_name_ =
|
|
dyn_cast<GlobalVariable>(ocr_initializer_base);
|
|
|
|
if (!_objc_class_name_ || !_objc_class_name_->hasInitializer())
|
|
return false;
|
|
|
|
Constant *ocn_initializer = _objc_class_name_->getInitializer();
|
|
|
|
ConstantDataArray *ocn_initializer_array =
|
|
dyn_cast<ConstantDataArray>(ocn_initializer);
|
|
|
|
if (!ocn_initializer_array->isString())
|
|
return false;
|
|
|
|
std::string ocn_initializer_string = ocn_initializer_array->getAsString();
|
|
|
|
if (log)
|
|
log->Printf("Found Objective-C class reference \"%s\"",
|
|
ocn_initializer_string.c_str());
|
|
|
|
// Construct a call to objc_getClass
|
|
|
|
if (!m_objc_getClass) {
|
|
lldb::addr_t objc_getClass_addr;
|
|
|
|
static lldb_private::ConstString g_objc_getClass_str("objc_getClass");
|
|
objc_getClass_addr = m_execution_unit.FindSymbol(g_objc_getClass_str);
|
|
if (objc_getClass_addr == LLDB_INVALID_ADDRESS)
|
|
return false;
|
|
|
|
if (log)
|
|
log->Printf("Found objc_getClass at 0x%" PRIx64,
|
|
objc_getClass_addr);
|
|
|
|
// Build the function type: %struct._objc_class *objc_getClass(i8*)
|
|
|
|
Type *class_type = load->getType();
|
|
Type *type_array[1];
|
|
type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext());
|
|
|
|
ArrayRef<Type *> ogC_arg_types(type_array, 1);
|
|
|
|
llvm::Type *ogC_type =
|
|
FunctionType::get(class_type, ogC_arg_types, false);
|
|
|
|
// Build the constant containing the pointer to the function
|
|
PointerType *ogC_ptr_ty = PointerType::getUnqual(ogC_type);
|
|
Constant *ogC_addr_int =
|
|
ConstantInt::get(m_intptr_ty, objc_getClass_addr, false);
|
|
m_objc_getClass = ConstantExpr::getIntToPtr(ogC_addr_int, ogC_ptr_ty);
|
|
}
|
|
|
|
Value *argument_array[1];
|
|
|
|
Constant *ocn_pointer = ConstantExpr::getBitCast(
|
|
_objc_class_name_, Type::getInt8PtrTy(m_module->getContext()));
|
|
|
|
argument_array[0] = ocn_pointer;
|
|
|
|
ArrayRef<Value *> ogC_arguments(argument_array, 1);
|
|
|
|
CallInst *ogC_call = CallInst::Create(m_objc_getClass, ogC_arguments,
|
|
"objc_getClass", class_load);
|
|
|
|
// Replace the load with the call in all users
|
|
|
|
class_load->replaceAllUsesWith(ogC_call);
|
|
|
|
class_load->eraseFromParent();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IRForTarget::RewriteObjCClassReferences(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 class_loads;
|
|
|
|
for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
|
|
Instruction &inst = *ii;
|
|
|
|
if (LoadInst *load = dyn_cast<LoadInst>(&inst))
|
|
if (IsObjCClassReference(load->getPointerOperand()))
|
|
class_loads.push_back(&inst);
|
|
}
|
|
|
|
InstrIterator iter;
|
|
|
|
for (iter = class_loads.begin(); iter != class_loads.end(); ++iter) {
|
|
if (!RewriteObjCClassReference(*iter)) {
|
|
m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a "
|
|
"static reference to an Objective-C class to a "
|
|
"dynamic reference\n");
|
|
|
|
if (log)
|
|
log->PutCString(
|
|
"Couldn't rewrite a reference to an Objective-C class");
|
|
|
|
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 =
|
|
mdconst::dyn_extract<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(),
|
|
lldb_private::ClangASTContext::GetASTContext(&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());
|
|
|
|
// 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::Metadata *values[2];
|
|
values[0] = ConstantAsMetadata::get(persistent_global);
|
|
values[1] = ConstantAsMetadata::get(constant_int);
|
|
|
|
ArrayRef<llvm::Metadata *> 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.");
|
|
|
|
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)) {
|
|
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)", (void *)data,
|
|
PrintValue(initializer).c_str());
|
|
|
|
Type *initializer_type = initializer->getType();
|
|
|
|
if (ConstantInt *int_initializer = dyn_cast<ConstantInt>(initializer)) {
|
|
size_t constant_size = m_target_data->getTypeStoreSize(initializer_type);
|
|
lldb_private::Scalar scalar = int_initializer->getValue().zextOrTrunc(
|
|
llvm::NextPowerOf2(constant_size) * 8);
|
|
|
|
lldb_private::Status get_data_error;
|
|
if (!scalar.GetAsMemoryData(data, constant_size,
|
|
lldb_private::endian::InlHostByteOrder(),
|
|
get_data_error))
|
|
return false;
|
|
|
|
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;
|
|
}
|
|
|
|
// 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 true;
|
|
|
|
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::CompilerType compiler_type(&value_decl->getASTContext(),
|
|
value_decl->getType());
|
|
|
|
const Type *value_type = NULL;
|
|
|
|
if (name[0] == '$') {
|
|
// The $__lldb_expr_result name indicates 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.
|
|
compiler_type = compiler_type.GetPointerType();
|
|
value_type = PointerType::get(global_variable->getType(), 0);
|
|
} else {
|
|
value_type = global_variable->getType();
|
|
}
|
|
|
|
const uint64_t value_size = compiler_type.GetByteSize(nullptr);
|
|
lldb::offset_t value_alignment =
|
|
(compiler_type.GetTypeBitAlign() + 7ull) / 8ull;
|
|
|
|
if (log) {
|
|
log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64
|
|
", align %" PRIu64 "]",
|
|
name.c_str(),
|
|
lldb_private::ClangUtil::GetQualType(compiler_type)
|
|
.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
|
|
return true;
|
|
}
|
|
} 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();
|
|
|
|
Constant *symbol_addr_int = ConstantInt::get(m_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
|
|
{
|
|
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_empty())
|
|
return false;
|
|
|
|
SmallVector<LoadInst *, 2> load_instructions;
|
|
|
|
for (llvm::User *u : global_variable->users()) {
|
|
if (LoadInst *load_instruction = dyn_cast<LoadInst>(u))
|
|
load_instructions.push_back(load_instruction);
|
|
}
|
|
|
|
if (load_instructions.empty())
|
|
return false;
|
|
|
|
Constant *class_addr = ConstantInt::get(m_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 (GlobalVariable &global_var : m_module->globals()) {
|
|
std::string global_name = global_var.getName().str();
|
|
|
|
if (log)
|
|
log->Printf("Examining %s, DeclForGlobalValue returns %p",
|
|
global_name.c_str(),
|
|
static_cast<void *>(DeclForGlobal(&global_var)));
|
|
|
|
if (global_name.find("OBJC_IVAR") == 0) {
|
|
if (!HandleSymbol(&global_var)) {
|
|
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_var)) {
|
|
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_var)) {
|
|
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_var)) {
|
|
if (!MaybeHandleVariable(&global_var)) {
|
|
m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
|
|
"external variable %s\n",
|
|
global_name.c_str());
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
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") && // Itanium ABI guard variable
|
|
!GV->getName().endswith("@4IA"))) // Microsoft ABI guard variable
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction *guard_load) {
|
|
Constant *zero(Constant::getNullValue(guard_load->getType()));
|
|
guard_load->replaceAllUsesWith(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,
|
|
llvm::Function *llvm_function,
|
|
FunctionValueCache &value_maker,
|
|
FunctionValueCache &entry_instruction_finder,
|
|
lldb_private::Stream &error_stream) {
|
|
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 (llvm::User *u : old_constant->users())
|
|
users.push_back(u);
|
|
|
|
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:
|
|
error_stream.Printf("error [IRForTarget internal]: 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, llvm_function, bit_cast_maker,
|
|
entry_instruction_finder, error_stream))
|
|
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(
|
|
nullptr, ptr, indices, "",
|
|
llvm::cast<Instruction>(
|
|
entry_instruction_finder.GetValue(function)));
|
|
});
|
|
|
|
if (!UnfoldConstant(constant_expr, llvm_function,
|
|
get_element_pointer_maker,
|
|
entry_instruction_finder, error_stream))
|
|
return false;
|
|
} break;
|
|
}
|
|
} else {
|
|
error_stream.Printf(
|
|
"error [IRForTarget internal]: Unhandled constant type: \"%s\"",
|
|
PrintValue(constant).c_str());
|
|
return false;
|
|
}
|
|
} else {
|
|
if (Instruction *inst = llvm::dyn_cast<Instruction>(user)) {
|
|
if (llvm_function && inst->getParent()->getParent() != llvm_function) {
|
|
error_stream.PutCString("error: Capturing non-local variables in "
|
|
"expressions is unsupported.\n");
|
|
return false;
|
|
}
|
|
inst->replaceUsesOfWith(
|
|
old_constant, value_maker.GetValue(inst->getParent()->getParent()));
|
|
} else {
|
|
error_stream.Printf(
|
|
"error [IRForTarget internal]: 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;
|
|
lldb::offset_t alignment;
|
|
|
|
if (!m_decl_map->GetStructInfo(num_elements, size, alignment))
|
|
return false;
|
|
|
|
Function::arg_iterator iter(llvm_function.arg_begin());
|
|
|
|
if (iter == llvm_function.arg_end()) {
|
|
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.arg_end()) {
|
|
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.arg_end()) {
|
|
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")) {
|
|
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.arg_end()) {
|
|
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")) {
|
|
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) {
|
|
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) {
|
|
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;
|
|
lldb::offset_t offset;
|
|
lldb_private::ConstString name;
|
|
|
|
if (!m_decl_map->GetStructElement(decl, value, offset, name,
|
|
element_index)) {
|
|
m_error_stream.Printf(
|
|
"Internal error [IRForTarget]: Structure information is incomplete");
|
|
|
|
return false;
|
|
}
|
|
|
|
if (log)
|
|
log->Printf(" \"%s\" (\"%s\") placed at %" PRIu64, 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(
|
|
nullptr, 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)) {
|
|
if (!UnfoldConstant(constant, &llvm_function, body_result_maker,
|
|
m_entry_instruction_finder, m_error_stream)) {
|
|
return false;
|
|
}
|
|
} else if (Instruction *instruction = dyn_cast<Instruction>(value)) {
|
|
if (instruction->getParent()->getParent() != &llvm_function) {
|
|
m_error_stream.PutCString("error: Capturing non-local variables in "
|
|
"expressions is unsupported.\n");
|
|
return false;
|
|
}
|
|
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 %" PRIu64 "]",
|
|
(int64_t)alignment, (uint64_t)size);
|
|
|
|
return true;
|
|
}
|
|
|
|
llvm::Constant *IRForTarget::BuildRelocation(llvm::Type *type,
|
|
uint64_t offset) {
|
|
llvm::Constant *offset_int = ConstantInt::get(m_intptr_ty, offset);
|
|
|
|
llvm::Constant *offset_array[1];
|
|
|
|
offset_array[0] = offset_int;
|
|
|
|
llvm::ArrayRef<llvm::Constant *> offsets(offset_array, 1);
|
|
llvm::Type *char_type = llvm::Type::getInt8Ty(m_module->getContext());
|
|
llvm::Type *char_pointer_type = char_type->getPointerTo();
|
|
|
|
llvm::Constant *reloc_placeholder_bitcast =
|
|
ConstantExpr::getBitCast(m_reloc_placeholder, char_pointer_type);
|
|
llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(
|
|
char_type, reloc_placeholder_bitcast, offsets);
|
|
llvm::Constant *reloc_bitcast =
|
|
ConstantExpr::getBitCast(reloc_getelementptr, type);
|
|
|
|
return reloc_bitcast;
|
|
}
|
|
|
|
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));
|
|
m_intptr_ty = llvm::Type::getIntNTy(m_module->getContext(),
|
|
m_target_data->getPointerSizeInBits());
|
|
|
|
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 *const main_function =
|
|
m_func_name.IsEmpty() ? nullptr
|
|
: m_module->getFunction(m_func_name.GetStringRef());
|
|
|
|
if (!m_func_name.IsEmpty() && !main_function) {
|
|
if (log)
|
|
log->Printf("Couldn't find \"%s()\" in the module",
|
|
m_func_name.AsCString());
|
|
|
|
m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find wrapper "
|
|
"'%s' in the module",
|
|
m_func_name.AsCString());
|
|
|
|
return false;
|
|
}
|
|
|
|
if (main_function) {
|
|
if (!FixFunctionLinkage(*main_function)) {
|
|
if (log)
|
|
log->Printf("Couldn't fix the linkage for the function");
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
llvm::Type *int8_ty = Type::getInt8Ty(m_module->getContext());
|
|
|
|
m_reloc_placeholder = new llvm::GlobalVariable(
|
|
(*m_module), int8_ty, false /* IsConstant */,
|
|
GlobalVariable::InternalLinkage, Constant::getNullValue(int8_ty),
|
|
"reloc_placeholder", NULL /* InsertBefore */,
|
|
GlobalVariable::NotThreadLocal /* ThreadLocal */, 0 /* AddressSpace */);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Replace $__lldb_expr_result with a persistent variable
|
|
//
|
|
|
|
if (main_function) {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (!RewriteObjCClassReferences(*bbi)) {
|
|
if (log)
|
|
log->Printf("RewriteObjCClassReferences() failed");
|
|
|
|
// RewriteObjCClasses() 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Run function-level passes that only make sense on the main function
|
|
//
|
|
|
|
if (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 (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;
|
|
}
|