1 #include "clang/InstallAPI/DylibVerifier.h" 2 #include "clang/InstallAPI/FrontendRecords.h" 3 #include "clang/InstallAPI/InstallAPIDiagnostic.h" 4 #include "llvm/Demangle/Demangle.h" 5 6 using namespace llvm::MachO; 7 8 namespace clang { 9 namespace installapi { 10 11 /// Metadata stored about a mapping of a declaration to a symbol. 12 struct DylibVerifier::SymbolContext { 13 // Name to use for all querying and verification 14 // purposes. 15 std::string SymbolName{""}; 16 17 // Kind to map symbol type against record. 18 EncodeKind Kind = EncodeKind::GlobalSymbol; 19 20 // Frontend Attributes tied to the AST. 21 const FrontendAttrs *FA = nullptr; 22 23 // The ObjCInterface symbol type, if applicable. 24 ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None; 25 26 // Whether Decl is inlined. 27 bool Inlined = false; 28 }; 29 30 static bool isCppMangled(StringRef Name) { 31 // InstallAPI currently only supports itanium manglings. 32 return (Name.starts_with("_Z") || Name.starts_with("__Z") || 33 Name.starts_with("___Z")); 34 } 35 36 static std::string demangle(StringRef Name) { 37 // InstallAPI currently only supports itanium manglings. 38 if (!isCppMangled(Name)) 39 return Name.str(); 40 char *Result = llvm::itaniumDemangle(Name); 41 if (!Result) 42 return Name.str(); 43 44 std::string Demangled(Result); 45 free(Result); 46 return Demangled; 47 } 48 49 std::string DylibVerifier::getAnnotatedName(const Record *R, 50 SymbolContext &SymCtx, 51 bool ValidSourceLoc) { 52 assert(!SymCtx.SymbolName.empty() && "Expected symbol name"); 53 54 const StringRef SymbolName = SymCtx.SymbolName; 55 std::string PrettyName = 56 (Demangle && (SymCtx.Kind == EncodeKind::GlobalSymbol)) 57 ? demangle(SymbolName) 58 : SymbolName.str(); 59 60 std::string Annotation; 61 if (R->isWeakDefined()) 62 Annotation += "(weak-def) "; 63 if (R->isWeakReferenced()) 64 Annotation += "(weak-ref) "; 65 if (R->isThreadLocalValue()) 66 Annotation += "(tlv) "; 67 68 // Check if symbol represents only part of a @interface declaration. 69 const bool IsAnnotatedObjCClass = 70 ((SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) && 71 (SymCtx.ObjCIFKind <= ObjCIFSymbolKind::EHType)); 72 73 if (IsAnnotatedObjCClass) { 74 if (SymCtx.ObjCIFKind == ObjCIFSymbolKind::EHType) 75 Annotation += "Exception Type of "; 76 if (SymCtx.ObjCIFKind == ObjCIFSymbolKind::MetaClass) 77 Annotation += "Metaclass of "; 78 if (SymCtx.ObjCIFKind == ObjCIFSymbolKind::Class) 79 Annotation += "Class of "; 80 } 81 82 // Only print symbol type prefix or leading "_" if there is no source location 83 // tied to it. This can only ever happen when the location has to come from 84 // debug info. 85 if (ValidSourceLoc) { 86 StringRef PrettyNameRef(PrettyName); 87 if ((SymCtx.Kind == EncodeKind::GlobalSymbol) && 88 !isCppMangled(SymbolName) && PrettyNameRef.starts_with("_")) 89 return Annotation + PrettyNameRef.drop_front(1).str(); 90 return Annotation + PrettyName; 91 } 92 93 if (IsAnnotatedObjCClass) 94 return Annotation + PrettyName; 95 96 switch (SymCtx.Kind) { 97 case EncodeKind::GlobalSymbol: 98 return Annotation + PrettyName; 99 case EncodeKind::ObjectiveCInstanceVariable: 100 return Annotation + "(ObjC IVar) " + PrettyName; 101 case EncodeKind::ObjectiveCClass: 102 return Annotation + "(ObjC Class) " + PrettyName; 103 case EncodeKind::ObjectiveCClassEHType: 104 return Annotation + "(ObjC Class EH) " + PrettyName; 105 } 106 107 llvm_unreachable("unexpected case for EncodeKind"); 108 } 109 110 static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev, 111 const DylibVerifier::Result Curr) { 112 if (Prev == Curr) 113 return Prev; 114 115 // Never update from invalid or noverify state. 116 if ((Prev == DylibVerifier::Result::Invalid) || 117 (Prev == DylibVerifier::Result::NoVerify)) 118 return Prev; 119 120 // Don't let an ignored verification remove a valid one. 121 if (Prev == DylibVerifier::Result::Valid && 122 Curr == DylibVerifier::Result::Ignore) 123 return Prev; 124 125 return Curr; 126 } 127 // __private_extern__ is a deprecated specifier that clang does not 128 // respect in all contexts, it should just be considered hidden for InstallAPI. 129 static bool shouldIgnorePrivateExternAttr(const Decl *D) { 130 if (const FunctionDecl *FD = cast<FunctionDecl>(D)) 131 return FD->getStorageClass() == StorageClass::SC_PrivateExtern; 132 if (const VarDecl *VD = cast<VarDecl>(D)) 133 return VD->getStorageClass() == StorageClass::SC_PrivateExtern; 134 135 return false; 136 } 137 138 Record *findRecordFromSlice(const RecordsSlice *Slice, StringRef Name, 139 EncodeKind Kind) { 140 switch (Kind) { 141 case EncodeKind::GlobalSymbol: 142 return Slice->findGlobal(Name); 143 case EncodeKind::ObjectiveCInstanceVariable: 144 return Slice->findObjCIVar(Name.contains('.'), Name); 145 case EncodeKind::ObjectiveCClass: 146 case EncodeKind::ObjectiveCClassEHType: 147 return Slice->findObjCInterface(Name); 148 } 149 llvm_unreachable("unexpected end when finding record"); 150 } 151 152 void DylibVerifier::updateState(Result State) { 153 Ctx.FrontendState = updateResult(Ctx.FrontendState, State); 154 } 155 156 void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx, 157 TargetList &&Targets) { 158 if (Targets.empty()) 159 Targets = {Ctx.Target}; 160 161 Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets); 162 } 163 164 bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx, 165 const Record *DR) { 166 return SymCtx.FA->Avail.isObsoleted(); 167 } 168 169 bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R, 170 SymbolContext &SymCtx, 171 const ObjCInterfaceRecord *DR) { 172 const bool IsDeclVersionComplete = 173 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::Class) == 174 ObjCIFSymbolKind::Class) && 175 ((SymCtx.ObjCIFKind & ObjCIFSymbolKind::MetaClass) == 176 ObjCIFSymbolKind::MetaClass); 177 178 const bool IsDylibVersionComplete = DR->isCompleteInterface(); 179 180 // The common case, a complete ObjCInterface. 181 if (IsDeclVersionComplete && IsDylibVersionComplete) 182 return true; 183 184 auto PrintDiagnostic = [&](auto SymLinkage, const Record *Record, 185 StringRef SymName, bool PrintAsWarning = false) { 186 if (SymLinkage == RecordLinkage::Unknown) 187 Ctx.emitDiag([&]() { 188 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 189 PrintAsWarning ? diag::warn_library_missing_symbol 190 : diag::err_library_missing_symbol) 191 << SymName; 192 }); 193 else 194 Ctx.emitDiag([&]() { 195 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 196 PrintAsWarning ? diag::warn_library_hidden_symbol 197 : diag::err_library_hidden_symbol) 198 << SymName; 199 }); 200 }; 201 202 if (IsDeclVersionComplete) { 203 // The decl represents a complete ObjCInterface, but the symbols in the 204 // dylib do not. Determine which symbol is missing. To keep older projects 205 // building, treat this as a warning. 206 if (!DR->isExportedSymbol(ObjCIFSymbolKind::Class)) { 207 SymCtx.ObjCIFKind = ObjCIFSymbolKind::Class; 208 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::Class), R, 209 getAnnotatedName(R, SymCtx), 210 /*PrintAsWarning=*/true); 211 } 212 if (!DR->isExportedSymbol(ObjCIFSymbolKind::MetaClass)) { 213 SymCtx.ObjCIFKind = ObjCIFSymbolKind::MetaClass; 214 PrintDiagnostic(DR->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass), R, 215 getAnnotatedName(R, SymCtx), 216 /*PrintAsWarning=*/true); 217 } 218 return true; 219 } 220 221 if (DR->isExportedSymbol(SymCtx.ObjCIFKind)) { 222 if (!IsDylibVersionComplete) { 223 // Both the declaration and dylib have a non-complete interface. 224 SymCtx.Kind = EncodeKind::GlobalSymbol; 225 SymCtx.SymbolName = R->getName(); 226 } 227 return true; 228 } 229 230 // At this point that means there was not a matching class symbol 231 // to represent the one discovered as a declaration. 232 PrintDiagnostic(DR->getLinkageForSymbol(SymCtx.ObjCIFKind), R, 233 SymCtx.SymbolName); 234 return false; 235 } 236 237 DylibVerifier::Result DylibVerifier::compareVisibility(const Record *R, 238 SymbolContext &SymCtx, 239 const Record *DR) { 240 241 if (R->isExported()) { 242 if (!DR) { 243 Ctx.emitDiag([&]() { 244 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 245 diag::err_library_missing_symbol) 246 << getAnnotatedName(R, SymCtx); 247 }); 248 return Result::Invalid; 249 } 250 if (DR->isInternal()) { 251 Ctx.emitDiag([&]() { 252 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 253 diag::err_library_hidden_symbol) 254 << getAnnotatedName(R, SymCtx); 255 }); 256 return Result::Invalid; 257 } 258 } 259 260 // Emit a diagnostic for hidden declarations with external symbols, except 261 // when theres an inlined attribute. 262 if ((R->isInternal() && !SymCtx.Inlined) && DR && DR->isExported()) { 263 264 if (Mode == VerificationMode::ErrorsOnly) 265 return Result::Ignore; 266 267 if (shouldIgnorePrivateExternAttr(SymCtx.FA->D)) 268 return Result::Ignore; 269 270 unsigned ID; 271 Result Outcome; 272 if (Mode == VerificationMode::ErrorsAndWarnings) { 273 ID = diag::warn_header_hidden_symbol; 274 Outcome = Result::Ignore; 275 } else { 276 ID = diag::err_header_hidden_symbol; 277 Outcome = Result::Invalid; 278 } 279 Ctx.emitDiag([&]() { 280 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), ID) 281 << getAnnotatedName(R, SymCtx); 282 }); 283 return Outcome; 284 } 285 286 if (R->isInternal()) 287 return Result::Ignore; 288 289 return Result::Valid; 290 } 291 292 DylibVerifier::Result DylibVerifier::compareAvailability(const Record *R, 293 SymbolContext &SymCtx, 294 const Record *DR) { 295 if (!SymCtx.FA->Avail.isUnavailable()) 296 return Result::Valid; 297 298 const bool IsDeclAvailable = SymCtx.FA->Avail.isUnavailable(); 299 300 switch (Mode) { 301 case VerificationMode::ErrorsAndWarnings: 302 Ctx.emitDiag([&]() { 303 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 304 diag::warn_header_availability_mismatch) 305 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 306 }); 307 return Result::Ignore; 308 case VerificationMode::Pedantic: 309 Ctx.emitDiag([&]() { 310 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 311 diag::err_header_availability_mismatch) 312 << getAnnotatedName(R, SymCtx) << IsDeclAvailable << IsDeclAvailable; 313 }); 314 return Result::Invalid; 315 case VerificationMode::ErrorsOnly: 316 return Result::Ignore; 317 case VerificationMode::Invalid: 318 llvm_unreachable("Unexpected verification mode symbol verification"); 319 } 320 llvm_unreachable("Unexpected verification mode symbol verification"); 321 } 322 323 bool DylibVerifier::compareSymbolFlags(const Record *R, SymbolContext &SymCtx, 324 const Record *DR) { 325 if (DR->isThreadLocalValue() && !R->isThreadLocalValue()) { 326 Ctx.emitDiag([&]() { 327 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 328 diag::err_dylib_symbol_flags_mismatch) 329 << getAnnotatedName(DR, SymCtx) << DR->isThreadLocalValue(); 330 }); 331 return false; 332 } 333 if (!DR->isThreadLocalValue() && R->isThreadLocalValue()) { 334 Ctx.emitDiag([&]() { 335 SymCtx.FA->D->getLocation(), 336 Ctx.Diag->Report(diag::err_header_symbol_flags_mismatch) 337 << getAnnotatedName(DR, SymCtx) << R->isThreadLocalValue(); 338 }); 339 return false; 340 } 341 342 if (DR->isWeakDefined() && !R->isWeakDefined()) { 343 Ctx.emitDiag([&]() { 344 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 345 diag::err_dylib_symbol_flags_mismatch) 346 << getAnnotatedName(DR, SymCtx) << R->isWeakDefined(); 347 }); 348 return false; 349 } 350 if (!DR->isWeakDefined() && R->isWeakDefined()) { 351 Ctx.emitDiag([&]() { 352 Ctx.Diag->Report(SymCtx.FA->D->getLocation(), 353 diag::err_header_symbol_flags_mismatch) 354 << getAnnotatedName(R, SymCtx) << R->isWeakDefined(); 355 }); 356 return false; 357 } 358 359 return true; 360 } 361 362 DylibVerifier::Result DylibVerifier::verifyImpl(Record *R, 363 SymbolContext &SymCtx) { 364 R->setVerify(); 365 if (!canVerify()) { 366 // Accumulate symbols when not in verifying against dylib. 367 if (R->isExported() && !SymCtx.FA->Avail.isUnavailable() && 368 !SymCtx.FA->Avail.isObsoleted()) { 369 addSymbol(R, SymCtx); 370 } 371 return Ctx.FrontendState; 372 } 373 374 Record *DR = 375 findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind); 376 if (DR) 377 DR->setVerify(); 378 379 if (shouldIgnoreObsolete(R, SymCtx, DR)) { 380 updateState(Result::Ignore); 381 return Ctx.FrontendState; 382 } 383 384 // Unavailable declarations don't need matching symbols. 385 if (SymCtx.FA->Avail.isUnavailable() && (!DR || DR->isInternal())) { 386 updateState(Result::Valid); 387 return Ctx.FrontendState; 388 } 389 390 Result VisibilityCheck = compareVisibility(R, SymCtx, DR); 391 if (VisibilityCheck != Result::Valid) { 392 updateState(VisibilityCheck); 393 return Ctx.FrontendState; 394 } 395 396 // All missing symbol cases to diagnose have been handled now. 397 if (!DR) { 398 updateState(Result::Ignore); 399 return Ctx.FrontendState; 400 } 401 402 // Check for mismatching ObjC interfaces. 403 if (SymCtx.ObjCIFKind != ObjCIFSymbolKind::None) { 404 if (!compareObjCInterfaceSymbols( 405 R, SymCtx, Ctx.DylibSlice->findObjCInterface(DR->getName()))) { 406 updateState(Result::Invalid); 407 return Ctx.FrontendState; 408 } 409 } 410 411 Result AvailabilityCheck = compareAvailability(R, SymCtx, DR); 412 if (AvailabilityCheck != Result::Valid) { 413 updateState(AvailabilityCheck); 414 return Ctx.FrontendState; 415 } 416 417 if (!compareSymbolFlags(R, SymCtx, DR)) { 418 updateState(Result::Invalid); 419 return Ctx.FrontendState; 420 } 421 422 addSymbol(R, SymCtx); 423 updateState(Result::Valid); 424 return Ctx.FrontendState; 425 } 426 427 bool DylibVerifier::canVerify() { 428 return Ctx.FrontendState != Result::NoVerify; 429 } 430 431 void DylibVerifier::assignSlice(const Target &T) { 432 assert(T == Ctx.Target && "Active targets should match."); 433 if (Dylib.empty()) 434 return; 435 436 // Note: there are no reexport slices with binaries, as opposed to TBD files, 437 // so it can be assumed that the target match is the active top-level library. 438 auto It = find_if( 439 Dylib, [&T](const auto &Slice) { return T == Slice->getTarget(); }); 440 441 assert(It != Dylib.end() && "Target slice should always exist."); 442 Ctx.DylibSlice = It->get(); 443 } 444 445 void DylibVerifier::setTarget(const Target &T) { 446 Ctx.Target = T; 447 Ctx.DiscoveredFirstError = false; 448 if (Dylib.empty()) { 449 updateState(Result::NoVerify); 450 return; 451 } 452 updateState(Result::Ignore); 453 assignSlice(T); 454 } 455 456 DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R, 457 const FrontendAttrs *FA, 458 const StringRef SuperClass) { 459 if (R->isVerified()) 460 return getState(); 461 462 std::string FullName = 463 ObjCIVarRecord::createScopedName(SuperClass, R->getName()); 464 SymbolContext SymCtx{FullName, EncodeKind::ObjectiveCInstanceVariable, FA}; 465 return verifyImpl(R, SymCtx); 466 } 467 468 static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) { 469 ObjCIFSymbolKind Result = ObjCIFSymbolKind::None; 470 if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown) 471 Result |= ObjCIFSymbolKind::Class; 472 if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) != 473 RecordLinkage::Unknown) 474 Result |= ObjCIFSymbolKind::MetaClass; 475 if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) != 476 RecordLinkage::Unknown) 477 Result |= ObjCIFSymbolKind::EHType; 478 return Result; 479 } 480 481 DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R, 482 const FrontendAttrs *FA) { 483 if (R->isVerified()) 484 return getState(); 485 SymbolContext SymCtx; 486 SymCtx.SymbolName = R->getName(); 487 SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R); 488 489 SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType 490 : EncodeKind::ObjectiveCClass; 491 SymCtx.FA = FA; 492 493 return verifyImpl(R, SymCtx); 494 } 495 496 DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R, 497 const FrontendAttrs *FA) { 498 if (R->isVerified()) 499 return getState(); 500 501 // Global classifications could be obfusciated with `asm`. 502 SimpleSymbol Sym = parseSymbol(R->getName()); 503 SymbolContext SymCtx; 504 SymCtx.SymbolName = Sym.Name; 505 SymCtx.Kind = Sym.Kind; 506 SymCtx.FA = FA; 507 SymCtx.Inlined = R->isInlined(); 508 return verifyImpl(R, SymCtx); 509 } 510 511 void DylibVerifier::VerifierContext::emitDiag( 512 llvm::function_ref<void()> Report) { 513 if (!DiscoveredFirstError) { 514 Diag->Report(diag::warn_target) 515 << (PrintArch ? getArchitectureName(Target.Arch) 516 : getTargetTripleName(Target)); 517 DiscoveredFirstError = true; 518 } 519 520 Report(); 521 } 522 523 } // namespace installapi 524 } // namespace clang 525