diff --git a/include/klee/Module/KType.h b/include/klee/Module/KType.h new file mode 100644 index 0000000000..3f22b07dc6 --- /dev/null +++ b/include/klee/Module/KType.h @@ -0,0 +1,84 @@ +#ifndef KLEE_KTYPE_H +#define KLEE_KTYPE_H + +#include +#include +namespace llvm { +class Type; +class raw_ostream; +} // namespace llvm + +namespace klee { +class Expr; +class TypeManager; + +template class ref; + +enum TypeSystemKind { LLVM }; +class KType { + friend TypeManager; + +protected: + /** + * Represents type of used TypeManager. Required + * for llvm RTTI. + */ + TypeSystemKind typeSystemKind; + + /** + * Wrapped type. + */ + llvm::Type *type; + + /** + * Owning type manager system. Note, that only it can + * create instances of KTypes. + */ + TypeManager *parent; + + /** + * Innner types. Maps type to their offsets in current + * type. Should contain type itself and + * all types, that can be found in that object. + * For example, if object of type A contains object + * of type B, then all types in B can be accessed via A. + */ + std::unordered_map> innerTypes; + + KType(llvm::Type *, TypeManager *); + + /** + * Object cannot been created within class, defferent + * from TypeManager, as it is important to have only + * one instance for every llvm::Type. + */ + KType(const KType &) = delete; + KType &operator=(const KType &) = delete; + KType(KType &&) = delete; + KType &operator=(KType &&) = delete; + +public: + /** + * Method to check if 2 types are compatible. + */ + virtual bool isAccessableFrom(KType *accessingType) const; + + /** + * Handler for possible memory access. By default does nothing. + */ + virtual void handleMemoryAccess(KType *, ref, ref, bool isWrite); + + /** + * Returns the stored raw llvm type. + */ + llvm::Type *getRawType() const; + + TypeSystemKind getTypeSystemKind() const; + + virtual void print(llvm::raw_ostream &) const; + + virtual ~KType() = default; +}; +} // namespace klee + +#endif diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 6c0eb8da95..7a7761e994 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -25,6 +25,7 @@ klee_add_component(kleeCore SpecialFunctionHandler.cpp StatsTracker.cpp TimingSolver.cpp + TypeManager.cpp UserSearcher.cpp ) diff --git a/lib/Core/TypeManager.cpp b/lib/Core/TypeManager.cpp new file mode 100644 index 0000000000..9ff4d6a5fc --- /dev/null +++ b/lib/Core/TypeManager.cpp @@ -0,0 +1,178 @@ +#include "TypeManager.h" + +#include "klee/ADT/Ref.h" +#include "klee/Expr/Expr.h" +#include "klee/Expr/ExprHashMap.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include "klee/Module/KType.h" + +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Casting.h" + +#include +#include +#include + +using namespace klee; + +/** + * Initializes type system with raw llvm types. + */ +TypeManager::TypeManager(KModule *parent) : parent(parent) {} + +/** + * Computes KType for given type, and cache it, if it was not + * inititalized before. So, any two calls with the same argument + * will return same KType's. + */ +KType *TypeManager::getWrappedType(llvm::Type *type) { + if (typesMap.count(type) == 0) { + types.emplace_back(new KType(type, this)); + typesMap.emplace(type, types.back().get()); + } + return typesMap[type]; +} + +KType *TypeManager::getUnknownType() { return getWrappedType(nullptr); } + +KType *TypeManager::handleAlloc(ref size) { return getUnknownType(); } + +KType *TypeManager::handleRealloc(KType *type, ref) { return type; } + +/** + * Performs initialization for struct types, including inner types. + * Note, that initialization for structs differs from initialization + * for other types, as types from structs can create cyclic dependencies, + * and that is why it cannot be done in constructor. + */ +void TypeManager::initTypesFromStructs() { + /* + * To collect information about all inner types + * we will topologically sort dependencies between structures + * (e.g. if struct A contains class B, we will make edge from A to B) + * and pull types to top. + */ + + std::vector collectedStructTypes = + parent->module->getIdentifiedStructTypes(); + for (auto &structType : collectedStructTypes) { + getWrappedType(structType); + } + + for (auto &typesToOffsets : typesMap) { + if (llvm::isa(typesToOffsets.first)) { + collectedStructTypes.emplace_back( + llvm::cast(typesToOffsets.first)); + } + } + + std::vector sortedStructTypesGraph; + std::unordered_set visitedStructTypesGraph; + + std::function dfs = [this, &sortedStructTypesGraph, + &visitedStructTypesGraph, + &dfs](llvm::StructType *type) { + visitedStructTypesGraph.insert(type); + + for (auto typeTo : type->elements()) { + getWrappedType(typeTo); + if (visitedStructTypesGraph.count(typeTo) == 0 && typeTo->isStructTy()) { + dfs(llvm::cast(typeTo)); + } + } + + sortedStructTypesGraph.push_back(type); + }; + + for (auto &structType : collectedStructTypes) { + dfs(structType); + } + + for (auto structType : sortedStructTypesGraph) { + if (structType->isOpaque()) { + continue; + } + + /* Here we make initializaion for inner types of given structure type */ + const llvm::StructLayout *structLayout = + parent->targetData->getStructLayout(structType); + for (unsigned idx = 0; idx < structType->getNumElements(); ++idx) { + uint64_t offset = structLayout->getElementOffset(idx); + llvm::Type *rawElementType = structType->getElementType(idx); + typesMap[structType]->innerTypes[typesMap[rawElementType]].push_back( + offset); + + /* Provide initialization from types in inner class */ + for (auto &innerStructMemberTypesToOffsets : + typesMap[rawElementType]->innerTypes) { + KType *innerStructMemberType = innerStructMemberTypesToOffsets.first; + const std::vector &innerTypeOffsets = + innerStructMemberTypesToOffsets.second; + + /* Add offsets from inner class */ + for (uint64_t innerTypeOffset : innerTypeOffsets) { + typesMap[structType]->innerTypes[innerStructMemberType].emplace_back( + offset + innerTypeOffset); + } + } + } + } +} + +/** + * Performs type system initialization for global objects. + */ +void TypeManager::initTypesFromGlobals() { + for (auto &global : parent->module->getGlobalList()) { + getWrappedType(global.getType()); + } +} + +/** + * Performs type system initialization for all instructions in + * this module. Takes into consideration return and argument types. + */ +void TypeManager::initTypesFromInstructions() { + for (auto &function : *(parent->module)) { + auto kf = parent->functionMap[&function]; + + for (auto &BasicBlock : function) { + unsigned numInstructions = kf->blockMap[&BasicBlock]->numInstructions; + KBlock *kb = kf->blockMap[&BasicBlock]; + + for (unsigned i = 0; i < numInstructions; ++i) { + llvm::Instruction *inst = kb->instructions[i]->inst; + + /* Register return type */ + getWrappedType(inst->getType()); + + /* Register types for arguments */ + for (auto opb = inst->op_begin(), ope = inst->op_end(); opb != ope; + ++opb) { + getWrappedType((*opb)->getType()); + } + } + } + } +} + +void TypeManager::onFinishInitModule() {} + +/** + * Method to initialize all types in given module. + * Note, that it cannot be called in costructor + * as implementation of getWrappedType can be different + * for high-level languages. Note, that struct types is + * called last, as it is required to know about all + * structure types in code. + */ +void TypeManager::initModule() { + initTypesFromGlobals(); + initTypesFromInstructions(); + initTypesFromStructs(); + onFinishInitModule(); +} diff --git a/lib/Core/TypeManager.h b/lib/Core/TypeManager.h new file mode 100644 index 0000000000..a6bed0bbb9 --- /dev/null +++ b/lib/Core/TypeManager.h @@ -0,0 +1,65 @@ +#ifndef KLEE_TYPEMANAGER_H +#define KLEE_TYPEMANAGER_H + +#include +#include +#include + +namespace llvm { +class Type; +class Function; +} // namespace llvm + +namespace klee { + +class Expr; +class KType; +class KModule; +struct KInstruction; + +template class ref; +template class ExprHashMap; + +/** + * Default class for managing type system. + * Works with *raw* llvm types. By extending this + * class you can add more rules to type system. + */ +class TypeManager { +private: + void initTypesFromGlobals(); + void initTypesFromStructs(); + void initTypesFromInstructions(); + +protected: + KModule *parent; + std::vector> types; + std::unordered_map typesMap; + + /** + * Make specified post initialization in initModule(). Note, that + * it is intentionally separated from initModule, as initModule + * order of function calls in it important. By default do nothing. + */ + virtual void onFinishInitModule(); + +public: + TypeManager(KModule *); + + /** + * Initializes type system for current module. + */ + void initModule(); + + virtual KType *getWrappedType(llvm::Type *); + KType *getUnknownType(); + + virtual KType *handleAlloc(ref size); + virtual KType *handleRealloc(KType *, ref); + + virtual ~TypeManager() = default; +}; + +} /*namespace klee*/ + +#endif /* KLEE_TYPEMANAGER_H */ diff --git a/lib/Module/CMakeLists.txt b/lib/Module/CMakeLists.txt index 15a38ce144..9d2dd68a69 100644 --- a/lib/Module/CMakeLists.txt +++ b/lib/Module/CMakeLists.txt @@ -14,6 +14,7 @@ set(KLEE_MODULE_COMPONENT_SRCS IntrinsicCleaner.cpp KInstruction.cpp KModule.cpp + KType.cpp LowerSwitch.cpp ModuleUtil.cpp Optimize.cpp diff --git a/lib/Module/KType.cpp b/lib/Module/KType.cpp new file mode 100644 index 0000000000..1e65d4994e --- /dev/null +++ b/lib/Module/KType.cpp @@ -0,0 +1,39 @@ +#include "klee/Module/KType.h" + +#include "klee/ADT/Ref.h" +#include "klee/Expr/Expr.h" +#include "klee/Module/KModule.h" + +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +using namespace klee; +using namespace llvm; + +KType::KType(llvm::Type *type, TypeManager *parent) + : type(type), parent(parent) { + typeSystemKind = TypeSystemKind::LLVM; + /* Type itself can be reached at offset 0 */ + innerTypes[this].emplace_back(0); +} + +bool KType::isAccessableFrom(KType *accessingType) const { return true; } + +llvm::Type *KType::getRawType() const { return type; } + +TypeSystemKind KType::getTypeSystemKind() const { + return typeSystemKind; +} + +void KType::handleMemoryAccess(KType *, ref, ref, bool isWrite) {} + +void KType::print(llvm::raw_ostream &os) const { + if (type == nullptr) { + os << "nullptr"; + } else { + type->print(os); + } +}