#include "clang/InstallAPI/Visitor.h"
#include "clang/AST/Availability.h"
#include "clang/AST/ParentMapContext.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Linkage.h"
#include "clang/InstallAPI/DylibVerifier.h"
#include "clang/InstallAPI/FrontendRecords.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Mangler.h"
using namespace llvm;
using namespace llvm::MachO;
namespace {
enum class CXXLinkage {
ExternalLinkage,
LinkOnceODRLinkage,
WeakODRLinkage,
PrivateLinkage,
};
}
namespace clang::installapi {
static bool isExported(const NamedDecl *D) {
auto LV = D->getLinkageAndVisibility();
return isExternallyVisible(LV.getLinkage()) &&
(LV.getVisibility() == DefaultVisibility);
}
static bool isInlined(const FunctionDecl *D) {
bool HasInlineAttribute = false;
bool NoCXXAttr =
(!D->getASTContext().getLangOpts().CPlusPlus &&
!D->getASTContext().getTargetInfo().getCXXABI().isMicrosoft() &&
!D->hasAttr<DLLExportAttr>());
for (const auto *RD : D->redecls()) {
if (!RD->isInlined())
continue;
HasInlineAttribute = true;
if (!(NoCXXAttr || RD->hasAttr<GNUInlineAttr>()))
continue;
if (RD->doesThisDeclarationHaveABody() &&
RD->isInlineDefinitionExternallyVisible())
return false;
}
if (!HasInlineAttribute)
return false;
return true;
}
static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal = false) {
SymbolFlags Result = SymbolFlags::None;
if (WeakDef)
Result |= SymbolFlags::WeakDefined;
if (ThreadLocal)
Result |= SymbolFlags::ThreadLocalValue;
return Result;
}
void InstallAPIVisitor::HandleTranslationUnit(ASTContext &ASTCtx) {
if (ASTCtx.getDiagnostics().hasErrorOccurred())
return;
auto *D = ASTCtx.getTranslationUnitDecl();
TraverseDecl(D);
}
std::string InstallAPIVisitor::getMangledName(const NamedDecl *D) const {
SmallString<256> Name;
if (MC->shouldMangleDeclName(D)) {
raw_svector_ostream NStream(Name);
MC->mangleName(D, NStream);
} else
Name += D->getNameAsString();
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getBackendMangledName(Twine Name) const {
SmallString<256> FinalName;
Mangler::getNameWithPrefix(FinalName, Name, DataLayout(Layout));
return std::string(FinalName);
}
std::optional<HeaderType>
InstallAPIVisitor::getAccessForDecl(const NamedDecl *D) const {
SourceLocation Loc = D->getLocation();
if (Loc.isInvalid())
return std::nullopt;
auto FileLoc = SrcMgr.getFileLoc(Loc);
FileID ID = SrcMgr.getFileID(FileLoc);
if (ID.isInvalid())
return std::nullopt;
const FileEntry *FE = SrcMgr.getFileEntryForID(ID);
if (!FE)
return std::nullopt;
auto Header = Ctx.findAndRecordFile(FE, PP);
if (!Header.has_value())
return std::nullopt;
HeaderType Access = Header.value();
assert(Access != HeaderType::Unknown && "unexpected access level for global");
return Access;
}
static bool hasObjCExceptionAttribute(const ObjCInterfaceDecl *D) {
for (; D != nullptr; D = D->getSuperClass())
if (D->hasAttr<ObjCExceptionAttr>())
return true;
return false;
}
void InstallAPIVisitor::recordObjCInstanceVariables(
const ASTContext &ASTCtx, ObjCContainerRecord *Record, StringRef SuperClass,
const llvm::iterator_range<
DeclContext::specific_decl_iterator<ObjCIvarDecl>>
Ivars) {
RecordLinkage Linkage = RecordLinkage::Exported;
const RecordLinkage ContainerLinkage = Record->getLinkage();
if (ASTCtx.getLangOpts().ObjCRuntime.isFragile())
Linkage = RecordLinkage::Unknown;
else if (ContainerLinkage != RecordLinkage::Unknown)
Linkage = ContainerLinkage;
for (const auto *IV : Ivars) {
auto Access = getAccessForDecl(IV);
if (!Access)
continue;
StringRef Name = IV->getName();
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(IV);
auto AC = IV->getCanonicalAccessControl();
auto [ObjCIVR, FA] =
Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
Ctx.Verifier->verify(ObjCIVR, FA, SuperClass);
}
}
bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
if (!D->isThisDeclarationADefinition())
return true;
auto Access = getAccessForDecl(D);
if (!Access)
return true;
StringRef Name = D->getObjCRuntimeNameAsString();
const RecordLinkage Linkage =
isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
const bool IsEHType =
(!D->getASTContext().getLangOpts().ObjCRuntime.isFragile() &&
hasObjCExceptionAttribute(D));
auto [Class, FA] =
Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
Ctx.Verifier->verify(Class, FA);
StringRef SuperClassName;
if (const auto *SuperClass = D->getSuperClass())
SuperClassName = SuperClass->getObjCRuntimeNameAsString();
recordObjCInstanceVariables(D->getASTContext(), Class, Class->getName(),
D->ivars());
return true;
}
bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
StringRef CategoryName = D->getName();
auto Access = getAccessForDecl(D);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
const StringRef InterfaceName = InterfaceD->getName();
ObjCCategoryRecord *CategoryRecord =
Ctx.Slice->addObjCCategory(InterfaceName, CategoryName, Avail, D, *Access)
.first;
recordObjCInstanceVariables(D->getASTContext(), CategoryRecord, InterfaceName,
D->ivars());
return true;
}
bool InstallAPIVisitor::VisitVarDecl(const VarDecl *D) {
if (isa<ParmVarDecl>(D))
return true;
if (D->getDeclContext()->isRecord())
return true;
if (!D->isDefinedOutsideFunctionOrMethod())
return true;
if (D->getASTContext().getTemplateOrSpecializationInfo(D) &&
D->getTemplateSpecializationKind() == TSK_Undeclared)
return true;
auto Access = getAccessForDecl(D);
if (!Access)
return true;
const RecordLinkage Linkage =
isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
const bool WeakDef = D->hasAttr<WeakAttr>();
const bool ThreadLocal = D->getTLSKind() != VarDecl::TLS_None;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
auto [GR, FA] = Ctx.Slice->addGlobal(getMangledName(D), Linkage,
GlobalRecord::Kind::Variable, Avail, D,
*Access, getFlags(WeakDef, ThreadLocal));
Ctx.Verifier->verify(GR, FA);
return true;
}
bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(D)) {
if (M->getParent()->getDescribedClassTemplate() != nullptr)
return true;
for (const DynTypedNode &P : D->getASTContext().getParents(*M)) {
if (P.get<CXXRecordDecl>())
return true;
}
if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M))
return true;
}
switch (D->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
case FunctionDecl::TK_DependentNonTemplate:
break;
case FunctionDecl::TK_MemberSpecialization:
case FunctionDecl::TK_FunctionTemplateSpecialization:
if (auto *TempInfo = D->getTemplateSpecializationInfo()) {
if (!TempInfo->isExplicitInstantiationOrSpecialization())
return true;
}
break;
case FunctionDecl::TK_FunctionTemplate:
case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
return true;
}
auto Access = getAccessForDecl(D);
if (!Access)
return true;
auto Name = getMangledName(D);
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
const bool ExplicitInstantiation = D->getTemplateSpecializationKind() ==
TSK_ExplicitInstantiationDeclaration;
const bool WeakDef = ExplicitInstantiation || D->hasAttr<WeakAttr>();
const bool Inlined = isInlined(D);
const RecordLinkage Linkage = (Inlined || !isExported(D))
? RecordLinkage::Internal
: RecordLinkage::Exported;
auto [GR, FA] =
Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef), Inlined);
Ctx.Verifier->verify(GR, FA);
return true;
}
static bool hasVTable(const CXXRecordDecl *D) {
if (!D->hasDefinition() || !D->isDynamicClass())
return false;
assert(D->isExternallyVisible() && "Should be externally visible");
assert(D->isCompleteDefinition() && "Only works on complete definitions");
const CXXMethodDecl *KeyFunctionD =
D->getASTContext().getCurrentKeyFunction(D);
if (KeyFunctionD) {
switch (KeyFunctionD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
case TSK_ExplicitInstantiationDefinition:
return true;
case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable(
"Unexpected TemplateSpecializationKind for key function");
}
} else if (D->isAbstract()) {
return false;
}
switch (D->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
return false;
case TSK_ExplicitInstantiationDeclaration:
case TSK_ExplicitInstantiationDefinition:
return true;
}
llvm_unreachable("Invalid TemplateSpecializationKind!");
}
static CXXLinkage getVTableLinkage(const CXXRecordDecl *D) {
assert((D->hasDefinition() && D->isDynamicClass()) && "Record has no vtable");
assert(D->isExternallyVisible() && "Record should be externally visible");
if (D->getVisibility() == HiddenVisibility)
return CXXLinkage::PrivateLinkage;
const CXXMethodDecl *KeyFunctionD =
D->getASTContext().getCurrentKeyFunction(D);
if (KeyFunctionD) {
switch (KeyFunctionD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
if (isInlined(KeyFunctionD))
return CXXLinkage::LinkOnceODRLinkage;
return CXXLinkage::ExternalLinkage;
case TSK_ImplicitInstantiation:
llvm_unreachable("No external vtable for implicit instantiations");
case TSK_ExplicitInstantiationDefinition:
return CXXLinkage::WeakODRLinkage;
case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable(
"Unexpected TemplateSpecializationKind for key function");
}
}
switch (D->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
return CXXLinkage::LinkOnceODRLinkage;
case TSK_ExplicitInstantiationDeclaration:
case TSK_ExplicitInstantiationDefinition:
return CXXLinkage::WeakODRLinkage;
}
llvm_unreachable("Invalid TemplateSpecializationKind!");
}
static bool isRTTIWeakDef(const CXXRecordDecl *D) {
if (D->hasAttr<WeakAttr>())
return true;
if (D->isAbstract() && D->getASTContext().getCurrentKeyFunction(D) == nullptr)
return true;
if (D->isDynamicClass())
return getVTableLinkage(D) != CXXLinkage::ExternalLinkage;
return false;
}
static bool hasRTTI(const CXXRecordDecl *D) {
if (!D->getASTContext().getLangOpts().RTTI)
return false;
if (!D->hasDefinition())
return false;
if (!D->isDynamicClass())
return false;
if (isRTTIWeakDef(D))
return false;
return true;
}
std::string
InstallAPIVisitor::getMangledCXXRTTIName(const CXXRecordDecl *D) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
MC->mangleCXXRTTIName(QualType(D->getTypeForDecl(), 0), NameStream);
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getMangledCXXRTTI(const CXXRecordDecl *D) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
MC->mangleCXXRTTI(QualType(D->getTypeForDecl(), 0), NameStream);
return getBackendMangledName(Name);
}
std::string
InstallAPIVisitor::getMangledCXXVTableName(const CXXRecordDecl *D) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
MC->mangleCXXVTable(D, NameStream);
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getMangledCXXThunk(
const GlobalDecl &D, const ThunkInfo &Thunk, bool ElideOverrideInfo) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
const auto *Method = cast<CXXMethodDecl>(D.getDecl());
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method))
MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk, ElideOverrideInfo,
NameStream);
else
MC->mangleThunk(Method, Thunk, ElideOverrideInfo, NameStream);
return getBackendMangledName(Name);
}
std::string InstallAPIVisitor::getMangledCtorDtor(const CXXMethodDecl *D,
int Type) const {
SmallString<256> Name;
raw_svector_ostream NameStream(Name);
GlobalDecl GD;
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
GD = GlobalDecl(Ctor, CXXCtorType(Type));
else {
const auto *Dtor = cast<CXXDestructorDecl>(D);
GD = GlobalDecl(Dtor, CXXDtorType(Type));
}
MC->mangleName(GD, NameStream);
return getBackendMangledName(Name);
}
void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
const AvailabilityInfo &Avail,
const HeaderType Access,
bool EmittedVTable) {
if (hasVTable(D)) {
EmittedVTable = true;
const CXXLinkage VTableLinkage = getVTableLinkage(D);
if (VTableLinkage == CXXLinkage::ExternalLinkage ||
VTableLinkage == CXXLinkage::WeakODRLinkage) {
const std::string Name = getMangledCXXVTableName(D);
const bool WeakDef = VTableLinkage == CXXLinkage::WeakODRLinkage;
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail,
D, Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
if (!D->getDescribedClassTemplate() && !D->isInvalidDecl()) {
VTableContextBase *VTable = D->getASTContext().getVTableContext();
auto AddThunk = [&](GlobalDecl GD) {
const ItaniumVTableContext::ThunkInfoVectorTy *Thunks =
VTable->getThunkInfo(GD);
if (!Thunks)
return;
for (const auto &Thunk : *Thunks) {
const std::string Name =
getMangledCXXThunk(GD, Thunk, true);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function,
Avail, GD.getDecl(), Access);
Ctx.Verifier->verify(GR, FA);
}
};
for (const auto *Method : D->methods()) {
if (isa<CXXConstructorDecl>(Method) || !Method->isVirtual())
continue;
if (auto Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
if (Dtor->isDefaulted())
continue;
AddThunk({Dtor, Dtor_Deleting});
AddThunk({Dtor, Dtor_Complete});
} else
AddThunk(Method);
}
}
}
}
if (!EmittedVTable)
return;
if (hasRTTI(D)) {
std::string Name = getMangledCXXRTTI(D);
auto [GR, FA] =
Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail, D, Access);
Ctx.Verifier->verify(GR, FA);
Name = getMangledCXXRTTIName(D);
auto [NamedGR, NamedFA] =
Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail, D, Access);
Ctx.Verifier->verify(NamedGR, NamedFA);
}
for (const auto &It : D->bases()) {
const CXXRecordDecl *Base =
cast<CXXRecordDecl>(It.getType()->castAs<RecordType>()->getDecl());
const auto BaseAccess = getAccessForDecl(Base);
if (!BaseAccess)
continue;
const AvailabilityInfo BaseAvail = AvailabilityInfo::createFromDecl(Base);
emitVTableSymbols(Base, BaseAvail, *BaseAccess, true);
}
}
bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
if (!D->isCompleteDefinition())
return true;
if (D->getDescribedClassTemplate() != nullptr)
return true;
if (isa<ClassTemplatePartialSpecializationDecl>(D))
return true;
auto Access = getAccessForDecl(D);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
if (isExported(D))
emitVTableSymbols(D, Avail, *Access);
TemplateSpecializationKind ClassSK = TSK_Undeclared;
bool KeepInlineAsWeak = false;
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
ClassSK = Templ->getTemplateSpecializationKind();
if (ClassSK == TSK_ExplicitInstantiationDeclaration)
KeepInlineAsWeak = true;
}
for (const auto *M : D->methods()) {
bool WeakDef = false;
if (isInlined(M)) {
if (!KeepInlineAsWeak)
continue;
WeakDef = true;
}
if (!isExported(M))
continue;
switch (M->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
break;
case TSK_ImplicitInstantiation:
continue;
case TSK_ExplicitInstantiationDeclaration:
if (ClassSK == TSK_ExplicitInstantiationDeclaration)
WeakDef = true;
break;
case TSK_ExplicitInstantiationDefinition:
WeakDef = true;
break;
}
if (!M->isUserProvided())
continue;
if (M->isDeleted())
continue;
const auto Access = getAccessForDecl(M);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(M);
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(M)) {
if (Ctor->isDefaulted())
continue;
std::string Name = getMangledCtorDtor(M, Ctor_Base);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
if (!D->isAbstract()) {
std::string Name = getMangledCtorDtor(M, Ctor_Complete);
auto [GR, FA] = Ctx.Slice->addGlobal(
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
}
continue;
}
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(M)) {
if (Dtor->isDefaulted())
continue;
std::string Name = getMangledCtorDtor(M, Dtor_Base);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
Name = getMangledCtorDtor(M, Dtor_Complete);
auto [CompleteGR, CompleteFA] = Ctx.Slice->addGlobal(
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail, D,
*Access, getFlags(WeakDef));
Ctx.Verifier->verify(CompleteGR, CompleteFA);
if (Dtor->isVirtual()) {
Name = getMangledCtorDtor(M, Dtor_Deleting);
auto [VirtualGR, VirtualFA] = Ctx.Slice->addGlobal(
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
D, *Access, getFlags(WeakDef));
Ctx.Verifier->verify(VirtualGR, VirtualFA);
}
continue;
}
if (M->isPureVirtual())
continue;
std::string Name = getMangledName(M);
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Function, Avail, M,
*Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
}
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
if (!Templ->isExplicitInstantiationOrSpecialization())
return true;
}
using var_iter = CXXRecordDecl::specific_decl_iterator<VarDecl>;
using var_range = iterator_range<var_iter>;
for (const auto *Var : var_range(D->decls())) {
if (Var->isStaticDataMember() && Var->hasInit())
continue;
if (!isExported(Var))
continue;
const std::string Name = getMangledName(Var);
const auto Access = getAccessForDecl(Var);
if (!Access)
return true;
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(Var);
const bool WeakDef = Var->hasAttr<WeakAttr>() || KeepInlineAsWeak;
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
GlobalRecord::Kind::Variable, Avail, D,
*Access, getFlags(WeakDef));
Ctx.Verifier->verify(GR, FA);
}
return true;
}
}