Files
revng-revng/lib/Decompiler/IRASTTypeTranslation.cpp
T
2021-02-04 14:33:41 +01:00

462 lines
17 KiB
C++

//
// Copyright rev.ng Srls. See LICENSE.md for details.
//
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/Casting.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
#include "revng/Support/Assert.h"
#include "revng-c/Decompiler/DLALayouts.h"
#include "IRASTTypeTranslation.h"
#include "DLATypeSystem.h"
#include "Mangling.h"
std::string
DeclCreator::getUniqueTypeNameForDecl(const llvm::Value *NamingValue) const {
std::string Result;
if (NamingValue) {
if (NamingValue->hasName()) {
if (auto *F = llvm::dyn_cast<llvm::Function>(NamingValue)) {
Result = makeCIdentifier(F->getName().str()) + std::string("_ret_type");
} else if (auto *Arg = llvm::dyn_cast<llvm::Argument>(NamingValue)) {
const llvm::Function *F = Arg->getParent();
Result = makeCIdentifier(F->getName().str()) + "_arg_"
+ makeCIdentifier(NamingValue->getName().str())
+ std::string("_type");
}
}
if (Result.empty()) {
const llvm::Type *VTy = NamingValue->getType();
if (const auto *StructTy = llvm::dyn_cast<llvm::StructType>(VTy))
Result = makeCIdentifier(StructTy->getName());
}
}
if (Result.empty())
Result = std::string("type_") + std::to_string(Types.size());
return Result;
}
DeclCreator::TypeDeclOrQualType
DeclCreator::getOrCreateBoolType(clang::ASTContext &ASTCtx,
const llvm::Type *Ty) {
// First, look it up, to see if we've already computed it.
llvm::Optional<TypeDeclOrQualType> Result = lookupType(Ty);
if (Result.hasValue())
return Result.getValue();
// Otherwise, create it right now.
clang::TranslationUnitDecl *TUDecl = ASTCtx.getTranslationUnitDecl();
clang::IdentifierInfo *BoolId = &ASTCtx.Idents.get("bool");
clang::DeclarationName BoolTypeName(BoolId);
// First, lookup for a typedef called 'bool', if we find it we're done
auto LookupResult = TUDecl->lookup(BoolTypeName);
revng_assert(LookupResult.empty() or LookupResult.size() == 1);
clang::QualType BoolType;
if (not LookupResult.empty()) {
auto *BoolTypedef = llvm::cast<clang::TypedefDecl>(LookupResult.front());
BoolType = ASTCtx.getTypedefType(BoolTypedef);
} else {
// If we haven't found the typedef, create it ourselves
//
// C99 actually defines '_Bool', while 'bool' is a MACRO, which expands
// to '_Bool'. We cheat by injecting a 'typedef _Bool bool;' in the
// translation unit we're handling, that has already been preprocessed
clang::CanQualType BoolQType = ASTCtx.BoolTy;
revng_assert(not BoolQType.isNull());
using TSInfo = clang::TypeSourceInfo;
TSInfo *BoolTypeInfo = ASTCtx.getTrivialTypeSourceInfo(BoolQType);
auto *BoolTypedef = clang::TypedefDecl::Create(ASTCtx,
TUDecl,
{},
{},
BoolId,
BoolTypeInfo);
TUDecl->addDecl(BoolTypedef);
BoolType = ASTCtx.getTypedefType(BoolTypedef);
}
// Add it to the map.
insertTypeMapping</* NullableTy */ true>(Ty, BoolType);
return BoolType;
}
DeclCreator::TypeDeclOrQualType
DeclCreator::getOrCreateType(const llvm::Type *Ty,
const llvm::Value *NamingValue,
clang::ASTContext &ASTCtx,
clang::DeclContext &DeclCtx) {
// First, look it up, to see if we've already computed it.
llvm::Optional<TypeDeclOrQualType> CachedQTy = lookupType(Ty);
if (CachedQTy.hasValue())
return CachedQTy.getValue();
llvm::Optional<TypeDeclOrQualType> Result = llvm::None;
switch (Ty->getTypeID()) {
case llvm::Type::TypeID::IntegerTyID: {
auto *LLVMIntType = llvm::cast<llvm::IntegerType>(Ty);
unsigned BitWidth = LLVMIntType->getBitWidth();
revng_assert(BitWidth == 1 or BitWidth == 8 or BitWidth == 16
or BitWidth == 32 or BitWidth == 64 or BitWidth == 128);
clang::TranslationUnitDecl *TUDecl = ASTCtx.getTranslationUnitDecl();
revng_assert(TUDecl->isLookupContext());
switch (BitWidth) {
case 1: {
Result = getOrCreateBoolType(ASTCtx, Ty);
} break;
case 16: {
const std::string UInt16Name = "uint16_t";
clang::IdentifierInfo &Id = ASTCtx.Idents.get(UInt16Name);
clang::DeclarationName TypeName(&Id);
for (clang::Decl *D : TUDecl->lookup(TypeName)) {
if (auto *Typedef = llvm::dyn_cast<clang::TypedefDecl>(D)) {
revng_assert(not Result.hasValue());
Result = ASTCtx.getTypedefType(Typedef);
}
}
revng_assert(Result.hasValue(),
"'uint16_t' type not found!\n"
"This should not happen since we '#include <stdint.h>'\n"
"Please make sure you have installed the header\n");
} break;
case 32: {
const std::string UInt32Name = "uint32_t";
clang::IdentifierInfo &Id = ASTCtx.Idents.get(UInt32Name);
clang::DeclarationName TypeName(&Id);
for (clang::Decl *D : TUDecl->lookup(TypeName)) {
if (auto *Typedef = llvm::dyn_cast<clang::TypedefDecl>(D)) {
revng_assert(not Result.hasValue());
Result = ASTCtx.getTypedefType(Typedef);
}
}
revng_assert(Result.hasValue(),
"'uint32_t' type not found!\n"
"This should not happen since we '#include <stdint.h>'\n"
"Please make sure you have installed the header\n");
} break;
case 64: {
const std::string UInt64Name = "uint64_t";
clang::IdentifierInfo &Id = ASTCtx.Idents.get(UInt64Name);
clang::DeclarationName TypeName(&Id);
for (clang::Decl *D : TUDecl->lookup(TypeName)) {
if (auto *Typedef = llvm::dyn_cast<clang::TypedefDecl>(D)) {
revng_assert(not Result.hasValue());
Result = ASTCtx.getTypedefType(Typedef);
}
}
revng_assert(Result.hasValue(),
"'uint64_t' type not found!\n"
"This should not happen since we '#include <stdint.h>'\n"
"Please make sure you have installed the header\n");
} break;
case 8:
// use char for strict aliasing reasons
[[fallthrough]];
case 128:
// use builtin clang types (__int128_t and __uint128_t), because C99 does
// not require to have 128 bit integer types
Result = ASTCtx.getIntTypeForBitwidth(BitWidth, /* Signed */ false);
break;
default:
revng_abort("unexpected integer size");
}
} break;
case llvm::Type::TypeID::PointerTyID: {
const llvm::PointerType *PtrTy = llvm::cast<llvm::PointerType>(Ty);
const llvm::Type *PointeeTy = PtrTy->getElementType();
TypeDeclOrQualType PointeeType = getOrCreateType(PointeeTy,
nullptr,
ASTCtx,
DeclCtx);
Result = ASTCtx.getPointerType(getQualType(PointeeType));
} break;
case llvm::Type::TypeID::VoidTyID:
case llvm::Type::TypeID::MetadataTyID: {
Result = ASTCtx.VoidTy;
} break;
case llvm::Type::TypeID::StructTyID: {
std::string TypeName = getUniqueTypeNameForDecl(NamingValue);
clang::IdentifierInfo &TypeId = ASTCtx.Idents.get(TypeName);
auto *Struct = clang::RecordDecl::Create(ASTCtx,
clang::TTK_Struct,
&DeclCtx,
clang::SourceLocation{},
clang::SourceLocation{},
&TypeId,
nullptr);
Struct->startDefinition();
const auto *StructTy = llvm::cast<llvm::StructType>(Ty);
for (auto &Group : llvm::enumerate(StructTy->elements())) {
llvm::Type *FieldTy = Group.value();
// HACK: Handle the type of the `@env` global variable, which we simply
// cast to a `void` `nullptr` type.
if (FieldTy->getTypeID() == llvm::Type::TypeID::ArrayTyID) {
Result = ASTCtx.VoidTy;
break;
}
TypeDeclOrQualType FTy = getOrCreateType(Group.value(),
nullptr,
ASTCtx,
DeclCtx);
clang::QualType QFieldTy = DeclCreator::getQualType(FTy);
clang::TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(QFieldTy);
const std::string FieldName = std::string("field_")
+ std::to_string(Group.index());
clang::IdentifierInfo &FieldId = ASTCtx.Idents.get(FieldName);
auto *Field = clang::FieldDecl::Create(ASTCtx,
Struct,
clang::SourceLocation{},
clang::SourceLocation{},
&FieldId,
QFieldTy,
TI,
nullptr,
/*Mutable*/ false,
clang::ICIS_NoInit);
Struct->addDecl(Field);
}
Struct->completeDefinition();
Result = Struct;
} break;
case llvm::Type::TypeID::ArrayTyID:
case llvm::Type::TypeID::FunctionTyID:
case llvm::Type::TypeID::VectorTyID:
case llvm::Type::TypeID::LabelTyID:
case llvm::Type::TypeID::X86_MMXTyID:
case llvm::Type::TypeID::TokenTyID:
case llvm::Type::TypeID::HalfTyID:
case llvm::Type::TypeID::FloatTyID:
case llvm::Type::TypeID::DoubleTyID:
case llvm::Type::TypeID::X86_FP80TyID:
case llvm::Type::TypeID::FP128TyID:
case llvm::Type::TypeID::PPC_FP128TyID:
revng_abort("unsupported type");
}
revng_assert(Result.hasValue());
revng_assert(not DeclCreator::getQualType(Result.getValue()).isNull());
// Then insert it in the type mapping and return it.
TypeDeclOrQualType ResultVal = Result.getValue();
insertTypeMapping(Ty, ResultVal);
return ResultVal;
}
llvm::SmallVector<const dla::Layout *, 16>
DeclCreator::getPointedLayouts(const llvm::Value *V) const {
revng_assert(V);
llvm::SmallVector<const dla::Layout *, 16> Result;
if (not ValueLayouts)
return Result;
// If we have a ValueLayouts map, it means that the DLA analysis was
// executed. We want to customize V's type in C according to the
// layouts computed by the DLA.
auto FItBegin = ValueLayouts->lower_bound(dla::LayoutTypePtr(V, 0));
auto FItEnd = ValueLayouts->upper_bound(dla::LayoutTypePtr(V));
// If there is a range of ValueLayouts that are indexed by V, we have type
// information coming from the DLA about where V points.
if (FItBegin != FItEnd) {
if (std::next(FItBegin) == FItEnd
and FItBegin->first.fieldNum() == dla::LayoutTypePtr::fieldNumNone) {
// V has a scalar type, which is a pointer to the pointed Layout.
Result.push_back(FItBegin->second);
} else {
// If we find more than one entry in ValueLayouts associated with V, it
// means that V either has a struct type, or it is a function that
// returns a struct type.
// Each entry in ValueLayouts corresponds to a field of a struct.
// The field of a struct has a pointer type and points to a Layout that
// is described by the mapped value in ValueLayouts.
llvm::Type *VTy = V->getType();
if (const auto *Fun = dyn_cast<llvm::Function>(V))
VTy = Fun->getReturnType();
auto NumFields = llvm::cast<llvm::StructType>(VTy)->getNumElements();
revng_assert(NumFields != dla::LayoutTypePtr::fieldNumNone);
for (decltype(NumFields) FieldId = 0; FieldId < NumFields; ++FieldId) {
auto CurrId = FItBegin->first.fieldNum();
if (CurrId == FieldId) {
Result.push_back(FItBegin->second);
++FItBegin;
} else {
Result.push_back(nullptr);
}
}
}
}
return Result;
}
llvm::Optional<DeclCreator::TypeDeclOrQualType>
DeclCreator::getOrCreateDLAType(const llvm::Value *V,
clang::ASTContext &ASTCtx,
clang::DeclContext &DeclCtx) {
revng_assert(V);
// First, look it up, to see if we've already computed it.
llvm::Optional<TypeDeclOrQualType> Result = lookupType(V);
if (Result.hasValue())
return Result.getValue();
if (ValueLayouts) {
auto PointedLayouts = getPointedLayouts(V);
// If there is a range of ValueLayouts that are indexed by V, we have type
// information coming from the DLA about where V points.
if (not PointedLayouts.empty()) {
llvm::LLVMContext &LLVMCtx = V->getContext();
if (PointedLayouts.size() == 1) {
// V has a scalar type, which is a pointer to the PointedLayout.
const dla::Layout *PointedLayout = PointedLayouts.front();
revng_assert(PointedLayout);
TypeDeclOrQualType LTy = getOrCreateTypeFromLayout(PointedLayout,
ASTCtx,
LLVMCtx);
Result = ASTCtx.getPointerType(DeclCreator::getQualType(LTy));
} else {
// If we find more than one entry in ValueLayouts associated with V, it
// means that V ha either a struct type, or it is a function that
// returns a struct type.
// Each entry in ValueLayouts corresponds to a field of a struct.
// The field of a struct has a pointer type and points to a Layout that
// is described by the mapped value in ValueLayouts.
// Create the struct
std::string TypeName = getUniqueTypeNameForDecl(nullptr);
clang::IdentifierInfo &TypeId = ASTCtx.Idents.get(TypeName);
auto *Struct = clang::RecordDecl::Create(ASTCtx,
clang::TTK_Struct,
&DeclCtx,
clang::SourceLocation{},
clang::SourceLocation{},
&TypeId,
nullptr);
// Define the fields.
Struct->startDefinition();
for (const auto &Group : llvm::enumerate(PointedLayouts)) {
// Create the type decl for the PointerLayout.
TypeDeclOrQualType LayoutTy;
if (const dla::Layout *PointedLayout = Group.value()) {
LayoutTy = getOrCreateTypeFromLayout(PointedLayout,
ASTCtx,
LLVMCtx);
} else {
// If we don't have information about the type of this field from
// the DLA we just say it's a pointer to void.
LayoutTy = ASTCtx.VoidTy;
}
clang::QualType LayoutQualTy = DeclCreator::getQualType(LayoutTy);
clang::QualType FieldPtrTy = ASTCtx.getPointerType(LayoutQualTy);
clang::TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(FieldPtrTy);
const std::string FieldName = std::string("field_")
+ std::to_string(Group.index());
clang::IdentifierInfo &FieldId = ASTCtx.Idents.get(FieldName);
auto *Field = clang::FieldDecl::Create(ASTCtx,
Struct,
clang::SourceLocation{},
clang::SourceLocation{},
&FieldId,
FieldPtrTy,
TI,
nullptr,
/*Mutable*/ false,
clang::ICIS_NoInit);
Struct->addDecl(Field);
}
Struct->completeDefinition();
Result = Struct;
}
}
}
if (Result.hasValue())
insertTypeMapping(V, Result.getValue());
return Result;
}
DeclCreator::TypeDeclOrQualType
DeclCreator::getOrCreateType(const llvm::Value *V,
clang::ASTContext &ASTCtx,
clang::DeclContext &DeclCtx) {
revng_assert(V);
llvm::Optional<TypeDeclOrQualType> Result = getOrCreateDLAType(V,
ASTCtx,
DeclCtx);
if (Result.hasValue())
return Result.getValue();
const auto *VType = V->getType();
if (const auto *F = dyn_cast<llvm::Function>(V)) {
VType = F->getReturnType();
} else if (const auto *G = dyn_cast<llvm::GlobalVariable>(V)) {
const auto *GlobalPtrTy = llvm::cast<llvm::PointerType>(G->getType());
VType = GlobalPtrTy->getElementType();
}
return getOrCreateType(VType, V, ASTCtx, DeclCtx);
}