Files
revng-revng/lib/Decompiler/TypeDeclCreationAction.cpp
T
2021-02-02 11:23:53 +01:00

350 lines
14 KiB
C++

//
// Copyright rev.ng Srls. See LICENSE.md for details.
//
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
#include "revng/Support/Assert.h"
#include "revng-c/Decompiler/DLALayouts.h"
#include "DecompilationHelpers.h"
#include "IRASTTypeTranslation.h"
#include "Mangling.h"
namespace clang {
class TranslationUnitDecl;
} // end namespace clang
using clang::QualType;
QualType
DeclCreator::getOrCreateQualTypeFromLayout(const dla::Layout *L,
clang::ASTContext &ClangCtx,
llvm::LLVMContext &LLVMCtx) {
revng_assert(L);
if (auto It = LayoutQualTypes.find(L); It != LayoutQualTypes.end())
return It->second;
QualType Result = ClangCtx.VoidTy;
using LayoutKind = dla::Layout::LayoutKind;
revng_assert(L->size());
// Build the type decl for the C type associated to layout.
switch (L->getKind()) {
case LayoutKind::Padding: {
QualType CharTy = ClangCtx.CharTy;
if (L->size() > 1) {
auto ByteSize = L->size();
llvm::APInt ArraySize(64 /* bits */, ByteSize);
auto ArraySizeKind = clang::ArrayType::ArraySizeModifier::Normal;
Result = ClangCtx.getConstantArrayType(CharTy,
ArraySize,
nullptr,
ArraySizeKind,
0);
} else {
Result = CharTy;
}
} break;
case LayoutKind::Base: {
auto *Base = llvm::cast<dla::BaseLayout>(L);
auto ByteSize = Base->size();
revng_assert(ByteSize);
bool IsPowerOf2 = (ByteSize & (ByteSize - 1)) == 0;
revng_assert(IsPowerOf2);
revng_assert(ByteSize <= 16);
auto *IntTy = llvm::IntegerType::get(LLVMCtx, ByteSize * 8);
Result = getOrCreateQualType(IntTy,
nullptr,
ClangCtx,
*ClangCtx.getTranslationUnitDecl());
} break;
case LayoutKind::Array: {
auto *Array = llvm::cast<dla::ArrayLayout>(L);
dla::Layout *ElementLayout = Array->getElem();
QualType ElemQualTy = getOrCreateQualTypeFromLayout(ElementLayout,
ClangCtx,
LLVMCtx);
auto ArraySizeKind = clang::ArrayType::ArraySizeModifier::Normal;
if (Array->hasKnownLength()) {
llvm::APInt ArraySize(64 /* bits */, Array->length());
Result = ClangCtx.getConstantArrayType(ElemQualTy,
ArraySize,
nullptr,
ArraySizeKind,
0);
} else {
Result = ClangCtx.getIncompleteArrayType(ElemQualTy, ArraySizeKind, 0);
}
} break;
case LayoutKind::Struct: {
auto *Struct = llvm::cast<dla::StructLayout>(L);
std::string TypeName = getUniqueTypeNameForDecl();
clang::IdentifierInfo &TypeId = ClangCtx.Idents.get(TypeName);
clang::TranslationUnitDecl &TUDecl = *ClangCtx.getTranslationUnitDecl();
auto *StructDecl = clang::RecordDecl::Create(ClangCtx,
clang::TTK_Struct,
&TUDecl,
clang::SourceLocation{},
clang::SourceLocation{},
&TypeId,
nullptr);
TypeDecls.push_back(StructDecl);
StructDecl->startDefinition();
for (const auto &Group : llvm::enumerate(Struct->fields())) {
// Create the type decl for the PointerLayout.
const dla::Layout *PointedLayout = Group.value();
QualType FieldTy = getOrCreateQualTypeFromLayout(PointedLayout,
ClangCtx,
LLVMCtx);
clang::TypeSourceInfo *TI = ClangCtx.CreateTypeSourceInfo(FieldTy);
const std::string FieldName = std::string("field_")
+ std::to_string(Group.index());
clang::IdentifierInfo &FieldId = ClangCtx.Idents.get(FieldName);
auto *Field = clang::FieldDecl::Create(ClangCtx,
StructDecl,
clang::SourceLocation{},
clang::SourceLocation{},
&FieldId,
FieldTy,
TI,
nullptr,
/*Mutable*/ false,
clang::ICIS_NoInit);
FieldDecls[StructDecl].push_back(Field);
StructDecl->addDecl(Field);
}
StructDecl->completeDefinition();
Result = QualType(StructDecl->getTypeForDecl(), 0);
} break;
case LayoutKind::Union: {
auto *Union = llvm::cast<dla::UnionLayout>(L);
std::string TypeName = getUniqueTypeNameForDecl();
clang::IdentifierInfo &TypeId = ClangCtx.Idents.get(TypeName);
clang::TranslationUnitDecl &TUDecl = *ClangCtx.getTranslationUnitDecl();
auto *UnionDecl = clang::RecordDecl::Create(ClangCtx,
clang::TTK_Union,
&TUDecl,
clang::SourceLocation{},
clang::SourceLocation{},
&TypeId,
nullptr);
TypeDecls.push_back(UnionDecl);
UnionDecl->startDefinition();
for (const auto &Group : llvm::enumerate(Union->elements())) {
// Create the type decl for the PointerLayout.
const dla::Layout *PointedLayout = Group.value();
QualType FieldTy = getOrCreateQualTypeFromLayout(PointedLayout,
ClangCtx,
LLVMCtx);
clang::TypeSourceInfo *TI = ClangCtx.CreateTypeSourceInfo(FieldTy);
const std::string FieldName = std::string("field_")
+ std::to_string(Group.index());
clang::IdentifierInfo &FieldId = ClangCtx.Idents.get(FieldName);
auto *Field = clang::FieldDecl::Create(ClangCtx,
UnionDecl,
clang::SourceLocation{},
clang::SourceLocation{},
&FieldId,
FieldTy,
TI,
nullptr,
/*Mutable*/ false,
clang::ICIS_NoInit);
FieldDecls[UnionDecl].push_back(Field);
UnionDecl->addDecl(Field);
}
UnionDecl->completeDefinition();
Result = QualType(UnionDecl->getTypeForDecl(), 0);
} break;
default:
revng_unreachable();
}
const auto &[_, New] = LayoutQualTypes.insert({ L, Result });
revng_assert(New);
return Result;
}
QualType DeclCreator::getOrCreateFunctionRetType(llvm::Function *F,
clang::ASTContext &ASTCtx,
clang::DeclContext &DeclCtx) {
QualType Result;
// If we have a ValueLayouts map, it means that the DLA analysis was
// executed. We want to customize F's return type in C according to the
// layouts computed by the DLA.
if (nullptr != ValueLayouts) {
if (auto It = FunctionRetTypes.find(F); It != FunctionRetTypes.end())
return It->second;
auto FItBegin = ValueLayouts->lower_bound(dla::LayoutTypePtr(F, 0));
auto FItEnd = ValueLayouts->upper_bound(dla::LayoutTypePtr(F));
// If there is a range of ValueLayouts that are indexed by F, they
// represent the return value of F.
if (FItBegin != FItEnd) {
llvm::LLVMContext &LLVMCtx = F->getContext();
if (std::next(FItBegin) == FItEnd) {
// F returns a scalar type, which is a pointer to the PointedLayout.
dla::Layout *PointedLayout = FItBegin->second;
revng_assert(PointedLayout);
QualType LayoutTy = getOrCreateQualTypeFromLayout(PointedLayout,
ASTCtx,
LLVMCtx);
Result = ASTCtx.getPointerType(LayoutTy);
} else {
revng_assert(F->hasName());
std::string TypeName = makeCIdentifier(F->getName()) + "_return_t";
clang::IdentifierInfo &TypeId = ASTCtx.Idents.get(TypeName);
auto *Struct = clang::RecordDecl::Create(ASTCtx,
clang::TTK_Struct,
&DeclCtx,
clang::SourceLocation{},
clang::SourceLocation{},
&TypeId,
nullptr);
TypeDecls.push_back(Struct);
Struct->startDefinition();
auto LayoutMapRange = llvm::make_range(FItBegin, FItEnd);
for (const auto &Group : llvm::enumerate(LayoutMapRange)) {
// Create the type decl for the PointerLayout.
dla::Layout *PointedLayout = Group.value().second;
QualType LayoutTy = getOrCreateQualTypeFromLayout(PointedLayout,
ASTCtx,
LLVMCtx);
QualType FieldPtrTy = ASTCtx.getPointerType(LayoutTy);
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);
FieldDecls[Struct].push_back(Field);
Struct->addDecl(Field);
}
Struct->completeDefinition();
Result = QualType(Struct->getTypeForDecl(), 0);
}
const auto &[_, New] = FunctionRetTypes.insert({ F, Result });
revng_assert(New);
return Result;
}
}
const llvm::FunctionType *FType = F->getFunctionType();
auto *RetType = FType->getReturnType();
Result = getOrCreateQualType(RetType, F, ASTCtx, DeclCtx);
const auto &[_, New] = FunctionRetTypes.insert({ F, Result });
revng_assert(New);
return Result;
}
QualType DeclCreator::getOrCreateArgumentType(llvm::Argument *A,
clang::ASTContext &ASTCtx,
clang::DeclContext &DeclCtx) {
// For now we don't handle function prototypes with struct arguments.
// In principle, we expect to never need it, because in assembly arguments
// are passed to functions by means of registers, that in the decompiled C
// code will become scalar type.
revng_assert(not isa<llvm::StructType>(A->getType()));
revng_assert(not A->getType()->isVoidTy());
QualType Result;
// If we have a ValueLayouts map, it means that the DLA analysis was
// executed. We want to customize A's return type in C according to the
// layouts computed by the DLA.
if (nullptr != ValueLayouts) {
if (auto It = ArgumentTypes.find(A); It != ArgumentTypes.end()) {
Result = It->second;
return Result;
}
if (auto It = ValueLayouts->find(dla::LayoutTypePtr(A));
It != ValueLayouts->end()) {
revng_assert(It->first.fieldNum() == dla::LayoutTypePtr::fieldNumNone);
llvm::LLVMContext &LLVMCtx = A->getContext();
// A must be a scalar type, which is a pointer to the PointedLayout.
dla::Layout *PointedLayout = It->second;
revng_assert(PointedLayout);
QualType LayoutTy = getOrCreateQualTypeFromLayout(PointedLayout,
ASTCtx,
LLVMCtx);
Result = ASTCtx.getPointerType(LayoutTy);
const auto &[_, New] = ArgumentTypes.insert({ A, Result });
revng_assert(New);
return Result;
}
}
Result = getOrCreateQualType(A->getType(), A, ASTCtx, DeclCtx);
const auto &[_, New] = ArgumentTypes.insert({ A, Result });
revng_assert(New);
return Result;
}
void DeclCreator::createTypeDeclsForFunctionPrototype(clang::ASTContext &Ctx,
llvm::Function *F) {
clang::TranslationUnitDecl &TUDecl = *Ctx.getTranslationUnitDecl();
// Create return type
getOrCreateFunctionRetType(F, Ctx, TUDecl);
// Create argument types
for (llvm::Argument &A : F->args())
getOrCreateArgumentType(&A, Ctx, TUDecl);
}