|
29 | 29 | #include "llvm/ADT/DenseMap.h"
|
30 | 30 | #include "../CompatibilityOverride/CompatibilityOverride.h"
|
31 | 31 | #include "ImageInspection.h"
|
| 32 | +#include "ImageInspectionCommon.h" |
32 | 33 | #include "Private.h"
|
| 34 | +#include "TypeDiscovery.h" |
33 | 35 |
|
34 | 36 | #include <vector>
|
35 | 37 |
|
| 38 | +#if defined(__MACH__) |
| 39 | +#include <dlfcn.h> |
| 40 | +#include <mach-o/dyld.h> |
| 41 | +#include <mach-o/getsect.h> |
| 42 | +#include <mach-o/loader.h> |
| 43 | +#endif |
| 44 | + |
36 | 45 | #if __has_include(<mach-o/dyld_priv.h>)
|
37 | 46 | #include <mach-o/dyld_priv.h>
|
38 | 47 | #define DYLD_EXPECTED_SWIFT_OPTIMIZATIONS_VERSION 1u
|
@@ -1271,5 +1280,265 @@ const Metadata *swift::findConformingSuperclass(
|
1271 | 1280 | return conformingType;
|
1272 | 1281 | }
|
1273 | 1282 |
|
| 1283 | +/// Walk the conformances in a \c ConformanceSection instance, check if they |
| 1284 | +/// specify conformance to all the specified protocols, and call \a body for the |
| 1285 | +/// types that match. |
| 1286 | +SWIFT_CC(swift) |
| 1287 | +static void _enumerateConformingTypesFromSections( |
| 1288 | + const ConformanceSection *sections, |
| 1289 | + size_t count, |
| 1290 | + const llvm::ArrayRef<ProtocolDescriptorRef>& protocols, |
| 1291 | + TypeEnumerationFunction body, |
| 1292 | + void *bodyContext, |
| 1293 | + bool *stop, |
| 1294 | + SWIFT_CONTEXT void *context, |
| 1295 | + SWIFT_ERROR_RESULT SwiftError **error) { |
| 1296 | + |
| 1297 | + for (size_t i = 0; i < count; i++) { |
| 1298 | + const auto& section = sections[i]; |
| 1299 | + for (const auto& record : section) { |
| 1300 | + auto protocolDescriptor = record->getProtocol(); |
| 1301 | + auto metadata = record->getCanonicalTypeMetadata(); |
| 1302 | + if (!protocolDescriptor || !metadata) { |
| 1303 | + // This record does not contain the info we're interested in. |
| 1304 | + continue; |
| 1305 | + } |
| 1306 | + |
| 1307 | + bool conforms = true; { |
| 1308 | + for (const auto& protocol : protocols) { |
| 1309 | + const auto& swiftProtocol = protocol.getSwiftProtocol(); |
| 1310 | + if (!swift_conformsToProtocol(metadata, swiftProtocol)) { |
| 1311 | + // This type does not conform to all the specified protocol(s). |
| 1312 | + conforms = false; |
| 1313 | + break; |
| 1314 | + } |
| 1315 | + } |
| 1316 | + } |
| 1317 | + if (!conforms) { |
| 1318 | + continue; |
| 1319 | + } |
| 1320 | + |
| 1321 | + body(metadata, stop, bodyContext, error); |
| 1322 | + if (*stop || *error) { |
| 1323 | + return; |
| 1324 | + } |
| 1325 | + } |
| 1326 | + } |
| 1327 | +} |
| 1328 | + |
| 1329 | +#if defined(__MACH__) |
| 1330 | +/// Get the conformance section of a given Mach-O image. |
| 1331 | +/// |
| 1332 | +/// \param imageAddress The address of a Mach header or of a handle returned |
| 1333 | +/// from \c dlopen(). |
| 1334 | +/// \param maybeDlopenHandle Whether or not \a imageAddress might be a |
| 1335 | +/// \c dlopen() handle. If it is, an additional call to dyld is made to get |
| 1336 | +/// its corresponding Mach header. |
| 1337 | +/// |
| 1338 | +/// If the image does not have a conformance section, returns \c llvm::None. |
| 1339 | +static llvm::Optional<ConformanceSection> |
| 1340 | +_getConformanceSectionFromMachImage(const void *imageAddress, |
| 1341 | + bool maybeDlopenHandle) { |
| 1342 | +#ifdef __LP64__ |
| 1343 | + typedef const mach_header_64 *MachHeaderAddress; |
| 1344 | +#else |
| 1345 | + typedef const mach_header *MachHeaderAddress; |
| 1346 | +#endif |
| 1347 | + auto machHeader = reinterpret_cast<MachHeaderAddress>(imageAddress); |
| 1348 | + |
| 1349 | + if (maybeDlopenHandle) { |
| 1350 | + // The input pointer might be a dlopen() handle instead of a Mach header. |
| 1351 | + // Try to get the corresponding Mach header. |
| 1352 | + auto dyldAddress = dlsym(const_cast<void *>(imageAddress), "__dso_handle"); |
| 1353 | + if (dyldAddress) { |
| 1354 | + machHeader = reinterpret_cast<MachHeaderAddress>(dyldAddress); |
| 1355 | + } |
| 1356 | + |
| 1357 | + // If dyld failed to get a Mach header from the image address, it might mean |
| 1358 | + // it's already a Mach header or that it could not resolve the Mac header. |
| 1359 | + // getsectiondata() does not check that the header passed to it is valid, so |
| 1360 | + // perform a basic validation before passing along the pointer we have. |
| 1361 | + if (machHeader->magic != MH_MAGIC && machHeader->magic != MH_MAGIC_64) { |
| 1362 | + return llvm::None; |
| 1363 | + } |
| 1364 | + } |
| 1365 | + |
| 1366 | + unsigned long sectionSize = 0; |
| 1367 | + const auto& sectionData = getsectiondata(machHeader, |
| 1368 | + SEG_TEXT, |
| 1369 | + MachOProtocolConformancesSection, |
| 1370 | + §ionSize); |
| 1371 | + if (sectionData) { |
| 1372 | + return ConformanceSection(sectionData, sectionSize); |
| 1373 | + } |
| 1374 | + |
| 1375 | + return llvm::None; |
| 1376 | +} |
| 1377 | +#endif |
| 1378 | + |
| 1379 | +SWIFT_CC(swift) |
| 1380 | +static void _enumerateConformingTypesFromImage( |
| 1381 | + const void *imageAddress, |
| 1382 | + const llvm::ArrayRef<ProtocolDescriptorRef>& protocols, |
| 1383 | + TypeEnumerationFunction body, |
| 1384 | + void *bodyContext, |
| 1385 | + SWIFT_CONTEXT void *context, |
| 1386 | + SWIFT_ERROR_RESULT SwiftError **error) { |
| 1387 | + |
| 1388 | + bool stop = false; |
| 1389 | + *error = nullptr; |
| 1390 | + |
| 1391 | +#if defined(__MACH__) |
| 1392 | + // This platform has dyld and we can use it instead of walking the section |
| 1393 | + // table. Takes care of the nuances of DYLD shared cache support. |
| 1394 | + auto section = _getConformanceSectionFromMachImage(imageAddress, true); |
| 1395 | + if (section) { |
| 1396 | + _enumerateConformingTypesFromSections(section.getPointer(), 1, |
| 1397 | + protocols, body, bodyContext, |
| 1398 | + &stop, context, error); |
| 1399 | + } |
| 1400 | + |
| 1401 | +#else |
| 1402 | + // This platform doesn't have dyld, so walk all sections in the conformances |
| 1403 | + // table and try to find one that matches the input image address. |
| 1404 | + auto snapshot = Conformances.get().SectionsToScan.snapshot(); |
| 1405 | + |
| 1406 | + // FIXME: This operation is somewhat costly and could probably be optimized on a per-platform basis. |
| 1407 | + for (const auto& section : snapshot) { |
| 1408 | + // Check if this section came from the image of interest. |
| 1409 | + SymbolInfo info; |
| 1410 | + if (!lookupSymbol(section.begin(), &info)) { |
| 1411 | + continue; |
| 1412 | + } |
| 1413 | + |
| 1414 | + if (info.baseAddress == imageAddress) { |
| 1415 | + _enumerateConformingTypesFromSections(§ion, 1, |
| 1416 | + protocols, body, bodyContext, |
| 1417 | + &stop, context, error); |
| 1418 | + return; |
| 1419 | + } |
| 1420 | + } |
| 1421 | +#endif |
| 1422 | +} |
| 1423 | + |
| 1424 | +SWIFT_CC(swift) |
| 1425 | +static void _enumerateConformingTypesFromAllImages( |
| 1426 | + const llvm::ArrayRef<ProtocolDescriptorRef>& protocols, |
| 1427 | + TypeEnumerationFunction body, |
| 1428 | + void *bodyContext, |
| 1429 | + SWIFT_CONTEXT void *context, |
| 1430 | + SWIFT_ERROR_RESULT SwiftError **error) { |
| 1431 | + |
| 1432 | + bool stop = false; |
| 1433 | + *error = nullptr; |
| 1434 | + |
| 1435 | +#if defined(__MACH__) |
| 1436 | + // This platform has the dyld shared cache and that means that the |
| 1437 | + // conformances table won't contain conformance sections that are otherwise |
| 1438 | + // held there. Enumerate all libraries known to dyld. |
| 1439 | + // |
| 1440 | + // There are a couple of race conditions here: |
| 1441 | + // 1. If images are added or removed during enumeration, we may end up |
| 1442 | + // missing types that we should be enumerating. |
| 1443 | + // 2. If an image containing a conforming ObjC class is loaded while this |
| 1444 | + // function is enumerating, the function may enumerate an Objective-C class |
| 1445 | + // that has been incompletely initialized. |
| 1446 | + // |
| 1447 | + // Adding and removing images are rare events that generally don't happen once |
| 1448 | + // user code starts executing, but callers may wish to take care not to use |
| 1449 | + // this function if the image list may be changed by another thread or by the |
| 1450 | + // body closure. |
| 1451 | + auto count = _dyld_image_count(); |
| 1452 | + for (uint32_t i = 0; i < count; i++) { |
| 1453 | + const auto& imageAddress = _dyld_get_image_header(i); |
| 1454 | + if (!imageAddress) { |
| 1455 | + continue; |
| 1456 | + } |
| 1457 | + |
| 1458 | + auto section = _getConformanceSectionFromMachImage(imageAddress, false); |
| 1459 | + if (section) { |
| 1460 | + _enumerateConformingTypesFromSections(section.getPointer(), 1, |
| 1461 | + protocols, body, bodyContext, |
| 1462 | + &stop, context, error); |
| 1463 | + if (stop || *error) { |
| 1464 | + return; |
| 1465 | + } |
| 1466 | + } |
| 1467 | + } |
| 1468 | + |
| 1469 | +#else |
| 1470 | + // This platform doesn't have the dyld shared cache, so the conformances |
| 1471 | + // table should be complete and can be used directly. |
| 1472 | + auto snapshot = Conformances.get().SectionsToScan.snapshot(); |
| 1473 | + |
| 1474 | + // Walk all sections. |
| 1475 | + _enumerateConformingTypesFromSections(snapshot.begin(), snapshot.count(), |
| 1476 | + protocols, body, bodyContext, |
| 1477 | + &stop, context, error); |
| 1478 | +#endif |
| 1479 | +} |
| 1480 | + |
| 1481 | +SWIFT_CC(swift) |
| 1482 | +void swift::swift_enumerateConformingTypesFromImage( |
| 1483 | + const void *imageAddress, |
| 1484 | + const Metadata *protocolMetadata, |
| 1485 | + TypeEnumerationFunction body, |
| 1486 | + void *bodyContext, |
| 1487 | + SWIFT_CONTEXT void *context, |
| 1488 | + SWIFT_ERROR_RESULT SwiftError **error) { |
| 1489 | + |
| 1490 | + // Find the protocol(s) requested by the caller. |
| 1491 | + auto existentialType = dyn_cast<ExistentialTypeMetadata>(protocolMetadata); |
| 1492 | + if (!existentialType) { |
| 1493 | + auto typeName = swift_getTypeName(protocolMetadata, true); |
| 1494 | + swift::fatalError( |
| 1495 | + 0, |
| 1496 | + "_enumerateTypes(fromImageAt:conformingTo:_:) can only test for " |
| 1497 | + "conformance to a protocol, but %.*s is not a protocol.", |
| 1498 | + (int)typeName.length, typeName.data); |
| 1499 | + } |
| 1500 | + const auto& protocols = existentialType->getProtocols(); |
| 1501 | + |
| 1502 | + if (protocols.empty()) { |
| 1503 | + swift::fatalError( |
| 1504 | + 0, |
| 1505 | + "Any is not a protocol and _enumerateTypes(fromImageAt:conformingTo:_:) " |
| 1506 | + "cannot test for conformance to it."); |
| 1507 | + } |
| 1508 | + |
| 1509 | +#if SWIFT_OBJC_INTEROP |
| 1510 | + // Check for any Objective-C protocols in the input. We do not currently |
| 1511 | + // support searching for Objective-C protocol conformances because they do not |
| 1512 | + // produce conformance records in the compiled binary. In the future, if we |
| 1513 | + // detect any such protocols, we can use objc_copyClassList() to add to the |
| 1514 | + // enumerated set (taking care not to enumerate a class twice if it conforms |
| 1515 | + // to an Objective-C protocol *and* a Swift protocol.) |
| 1516 | + for (const auto& protocol : protocols) { |
| 1517 | + if (protocol.isObjC()) { |
| 1518 | + swift::fatalError( |
| 1519 | + 0, |
| 1520 | + "_enumerateTypes(fromImageAt:conformingTo:_:) does not currently " |
| 1521 | + "support Objective-C protocols such as %s.", |
| 1522 | + protocol.getName()); |
| 1523 | + } |
| 1524 | + } |
| 1525 | +#endif |
| 1526 | + |
| 1527 | +#if defined(__WASM__) |
| 1528 | + // Swift+WASM does not support dynamic linking and #dsohandle is 0, so |
| 1529 | + // ignore the passed image address and iterate over any/all available |
| 1530 | + // sections. Presumably only one will be available. |
| 1531 | + imageAddress = nullptr; |
| 1532 | +#endif |
| 1533 | + |
| 1534 | + if (imageAddress) { |
| 1535 | + _enumerateConformingTypesFromImage(imageAddress, protocols, body, |
| 1536 | + bodyContext, context, error); |
| 1537 | + } else { |
| 1538 | + _enumerateConformingTypesFromAllImages(protocols, body, bodyContext, |
| 1539 | + context, error); |
| 1540 | + } |
| 1541 | +} |
| 1542 | + |
1274 | 1543 | #define OVERRIDE_PROTOCOLCONFORMANCE COMPATIBILITY_OVERRIDE
|
1275 | 1544 | #include COMPATIBILITY_OVERRIDE_INCLUDE_PATH
|
0 commit comments