xref: /llvm-project/clang/lib/InstallAPI/DylibVerifier.cpp (revision f2794ccede6d32a6b5ef7a376ced420331e2be27)
1 #include "clang/InstallAPI/DylibVerifier.h"
2 #include "clang/InstallAPI/FrontendRecords.h"
3 #include "llvm/Demangle/Demangle.h"
4 
5 using namespace llvm::MachO;
6 
7 namespace clang {
8 namespace installapi {
9 
10 /// Metadata stored about a mapping of a declaration to a symbol.
11 struct DylibVerifier::SymbolContext {
12   // Name to use for printing in diagnostics.
13   std::string PrettyPrintName{""};
14 
15   // Name to use for all querying and verification
16   // purposes.
17   std::string SymbolName{""};
18 
19   // Kind to map symbol type against record.
20   EncodeKind Kind = EncodeKind::GlobalSymbol;
21 
22   // Frontend Attributes tied to the AST.
23   const FrontendAttrs *FA = nullptr;
24 
25   // The ObjCInterface symbol type, if applicable.
26   ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None;
27 };
28 
29 static std::string
30 getAnnotatedName(const Record *R, EncodeKind Kind, StringRef Name,
31                  bool ValidSourceLoc = true,
32                  ObjCIFSymbolKind ObjCIF = ObjCIFSymbolKind::None) {
33   assert(!Name.empty() && "Need symbol name for printing");
34 
35   std::string Annotation;
36   if (R->isWeakDefined())
37     Annotation += "(weak-def) ";
38   if (R->isWeakReferenced())
39     Annotation += "(weak-ref) ";
40   if (R->isThreadLocalValue())
41     Annotation += "(tlv) ";
42 
43   // Check if symbol represents only part of a @interface declaration.
44   const bool IsAnnotatedObjCClass = ((ObjCIF != ObjCIFSymbolKind::None) &&
45                                      (ObjCIF <= ObjCIFSymbolKind::EHType));
46 
47   if (IsAnnotatedObjCClass) {
48     if (ObjCIF == ObjCIFSymbolKind::EHType)
49       Annotation += "Exception Type of ";
50     if (ObjCIF == ObjCIFSymbolKind::MetaClass)
51       Annotation += "Metaclass of ";
52     if (ObjCIF == ObjCIFSymbolKind::Class)
53       Annotation += "Class of ";
54   }
55 
56   // Only print symbol type prefix or leading "_" if there is no source location
57   // tied to it. This can only ever happen when the location has to come from
58   // debug info.
59   if (ValidSourceLoc) {
60     if ((Kind == EncodeKind::GlobalSymbol) && Name.starts_with("_"))
61       return Annotation + Name.drop_front(1).str();
62     return Annotation + Name.str();
63   }
64 
65   if (IsAnnotatedObjCClass)
66     return Annotation + Name.str();
67 
68   switch (Kind) {
69   case EncodeKind::GlobalSymbol:
70     return Annotation + Name.str();
71   case EncodeKind::ObjectiveCInstanceVariable:
72     return Annotation + "(ObjC IVar) " + Name.str();
73   case EncodeKind::ObjectiveCClass:
74     return Annotation + "(ObjC Class) " + Name.str();
75   case EncodeKind::ObjectiveCClassEHType:
76     return Annotation + "(ObjC Class EH) " + Name.str();
77   }
78 
79   llvm_unreachable("unexpected case for EncodeKind");
80 }
81 
82 static std::string demangle(StringRef Name) {
83   // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'.
84   if (!(Name.starts_with("_Z") || Name.starts_with("___Z")))
85     return Name.str();
86   char *Result = llvm::itaniumDemangle(Name.data());
87   if (!Result)
88     return Name.str();
89 
90   std::string Demangled(Result);
91   free(Result);
92   return Demangled;
93 }
94 
95 static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev,
96                                           const DylibVerifier::Result Curr) {
97   if (Prev == Curr)
98     return Prev;
99 
100   // Never update from invalid or noverify state.
101   if ((Prev == DylibVerifier::Result::Invalid) ||
102       (Prev == DylibVerifier::Result::NoVerify))
103     return Prev;
104 
105   // Don't let an ignored verification remove a valid one.
106   if (Prev == DylibVerifier::Result::Valid &&
107       Curr == DylibVerifier::Result::Ignore)
108     return Prev;
109 
110   return Curr;
111 }
112 
113 void DylibVerifier::updateState(Result State) {
114   Ctx.FrontendState = updateResult(Ctx.FrontendState, State);
115 }
116 
117 void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx,
118                               TargetList &&Targets) {
119   if (Targets.empty())
120     Targets = {Ctx.Target};
121 
122   Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets);
123 }
124 
125 DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
126                                                 SymbolContext &SymCtx) {
127   R->setVerify();
128   if (!canVerify()) {
129     // Accumulate symbols when not in verifying against dylib.
130     if (R->isExported() && !SymCtx.FA->Avail.isUnconditionallyUnavailable() &&
131         !SymCtx.FA->Avail.isObsoleted()) {
132       addSymbol(R, SymCtx);
133     }
134     return Ctx.FrontendState;
135   }
136   return Ctx.FrontendState;
137 }
138 
139 bool DylibVerifier::canVerify() {
140   return Ctx.FrontendState != Result::NoVerify;
141 }
142 
143 void DylibVerifier::setTarget(const Target &T) {
144   Ctx.Target = T;
145   Ctx.DiscoveredFirstError = false;
146   updateState(Dylib.empty() ? Result::NoVerify : Result::Ignore);
147 }
148 
149 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R,
150                                             const FrontendAttrs *FA,
151                                             const StringRef SuperClass) {
152   if (R->isVerified())
153     return getState();
154 
155   std::string FullName =
156       ObjCIVarRecord::createScopedName(SuperClass, R->getName());
157   SymbolContext SymCtx{
158       getAnnotatedName(R, EncodeKind::ObjectiveCInstanceVariable,
159                        Demangle ? demangle(FullName) : FullName),
160       FullName, EncodeKind::ObjectiveCInstanceVariable, FA};
161   return verifyImpl(R, SymCtx);
162 }
163 
164 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) {
165   ObjCIFSymbolKind Result = ObjCIFSymbolKind::None;
166   if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown)
167     Result |= ObjCIFSymbolKind::Class;
168   if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) !=
169       RecordLinkage::Unknown)
170     Result |= ObjCIFSymbolKind::MetaClass;
171   if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) !=
172       RecordLinkage::Unknown)
173     Result |= ObjCIFSymbolKind::EHType;
174   return Result;
175 }
176 
177 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R,
178                                             const FrontendAttrs *FA) {
179   if (R->isVerified())
180     return getState();
181   SymbolContext SymCtx;
182   SymCtx.SymbolName = R->getName();
183   SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R);
184 
185   std::string DisplayName =
186       Demangle ? demangle(SymCtx.SymbolName) : SymCtx.SymbolName;
187   SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType
188                                            : EncodeKind::ObjectiveCClass;
189   SymCtx.PrettyPrintName = getAnnotatedName(R, SymCtx.Kind, DisplayName);
190   SymCtx.FA = FA;
191 
192   return verifyImpl(R, SymCtx);
193 }
194 
195 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R,
196                                             const FrontendAttrs *FA) {
197   if (R->isVerified())
198     return getState();
199 
200   // Global classifications could be obfusciated with `asm`.
201   SimpleSymbol Sym = parseSymbol(R->getName());
202   SymbolContext SymCtx;
203   SymCtx.SymbolName = Sym.Name;
204   SymCtx.PrettyPrintName =
205       getAnnotatedName(R, Sym.Kind, Demangle ? demangle(Sym.Name) : Sym.Name);
206   SymCtx.Kind = Sym.Kind;
207   SymCtx.FA = FA;
208   return verifyImpl(R, SymCtx);
209 }
210 
211 } // namespace installapi
212 } // namespace clang
213