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