@@ -379,12 +379,21 @@ class ObjcCategoryMerger {
379
379
InfoWriteSection catPtrListInfo;
380
380
};
381
381
382
- // Information about a pointer list in the original categories (method lists,
383
- // protocol lists, etc)
382
+ // Information about a pointer list in the original categories or class (method
383
+ // lists, protocol lists, etc)
384
384
struct PointerListInfo {
385
+ PointerListInfo () = default ;
386
+ PointerListInfo (const PointerListInfo &) = default ;
385
387
PointerListInfo (const char *_categoryPrefix, uint32_t _pointersPerStruct)
386
388
: categoryPrefix(_categoryPrefix),
387
389
pointersPerStruct (_pointersPerStruct) {}
390
+
391
+ inline bool operator ==(const PointerListInfo &cmp) {
392
+ return pointersPerStruct == cmp.pointersPerStruct &&
393
+ structSize == cmp.structSize && structCount == cmp.structCount &&
394
+ allPtrs == cmp.allPtrs ;
395
+ }
396
+
388
397
const char *categoryPrefix;
389
398
390
399
uint32_t pointersPerStruct = 0 ;
@@ -395,9 +404,9 @@ class ObjcCategoryMerger {
395
404
std::vector<Symbol *> allPtrs;
396
405
};
397
406
398
- // Full information about all the categories that extend a class. This will
399
- // include all the additional methods, protocols, and properties that are
400
- // contained in all the categories that extend a particular class.
407
+ // Full information describing an ObjC class . This will include all the
408
+ // additional methods, protocols, and properties that are contained in the
409
+ // class and all the categories that extend a particular class.
401
410
struct ClassExtensionInfo {
402
411
ClassExtensionInfo (CategoryLayout &_catLayout) : catLayout(_catLayout){};
403
412
@@ -449,16 +458,19 @@ class ObjcCategoryMerger {
449
458
void parseProtocolListInfo (const ConcatInputSection *isec, uint32_t secOffset,
450
459
PointerListInfo &ptrList);
451
460
461
+ PointerListInfo parseProtocolListInfo (const ConcatInputSection *isec,
462
+ uint32_t secOffset);
463
+
452
464
void parsePointerListInfo (const ConcatInputSection *isec, uint32_t secOffset,
453
465
PointerListInfo &ptrList);
454
466
455
467
void emitAndLinkPointerList (Defined *parentSym, uint32_t linkAtOffset,
456
468
const ClassExtensionInfo &extInfo,
457
469
const PointerListInfo &ptrList);
458
470
459
- void emitAndLinkProtocolList (Defined *parentSym, uint32_t linkAtOffset,
460
- const ClassExtensionInfo &extInfo,
461
- const PointerListInfo &ptrList);
471
+ Defined * emitAndLinkProtocolList (Defined *parentSym, uint32_t linkAtOffset,
472
+ const ClassExtensionInfo &extInfo,
473
+ const PointerListInfo &ptrList);
462
474
463
475
Defined *emitCategory (const ClassExtensionInfo &extInfo);
464
476
Defined *emitCatListEntrySec (const std::string &forCategoryName,
@@ -474,6 +486,10 @@ class ObjcCategoryMerger {
474
486
uint32_t offset);
475
487
Defined *tryGetDefinedAtIsecOffset (const ConcatInputSection *isec,
476
488
uint32_t offset);
489
+ Defined *getClassRo (const Defined *classSym, bool getMetaRo);
490
+ void mergeCategoriesIntoBaseClass (const Defined *baseClass,
491
+ std::vector<InfoInputCategory> &categories);
492
+ void eraseSymbolAtIsecOffset (ConcatInputSection *isec, uint32_t offset);
477
493
void tryEraseDefinedAtIsecOffset (const ConcatInputSection *isec,
478
494
uint32_t offset);
479
495
@@ -552,6 +568,29 @@ ObjcCategoryMerger::tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,
552
568
return dyn_cast_or_null<Defined>(sym);
553
569
}
554
570
571
+ // Get the class's ro_data symbol. If getMetaRo is true, then we will return
572
+ // the meta-class's ro_data symbol. Otherwise, we will return the class
573
+ // (instance) ro_data symbol.
574
+ Defined *ObjcCategoryMerger::getClassRo (const Defined *classSym,
575
+ bool getMetaRo) {
576
+ ConcatInputSection *isec = dyn_cast<ConcatInputSection>(classSym->isec ());
577
+ if (!isec)
578
+ return nullptr ;
579
+
580
+ if (!getMetaRo)
581
+ return tryGetDefinedAtIsecOffset (isec, classLayout.roDataOffset +
582
+ classSym->value );
583
+
584
+ Defined *metaClass = tryGetDefinedAtIsecOffset (
585
+ isec, classLayout.metaClassOffset + classSym->value );
586
+ if (!metaClass)
587
+ return nullptr ;
588
+
589
+ return tryGetDefinedAtIsecOffset (
590
+ dyn_cast<ConcatInputSection>(metaClass->isec ()),
591
+ classLayout.roDataOffset );
592
+ }
593
+
555
594
// Given an ConcatInputSection or CStringInputSection and an offset, if there is
556
595
// a symbol(Defined) at that offset, then erase the symbol (mark it not live)
557
596
void ObjcCategoryMerger::tryEraseDefinedAtIsecOffset (
@@ -663,6 +702,15 @@ void ObjcCategoryMerger::parseProtocolListInfo(const ConcatInputSection *isec,
663
702
" Protocol list end offset does not match expected size" );
664
703
}
665
704
705
+ // Parse a protocol list and return the PointerListInfo for it
706
+ ObjcCategoryMerger::PointerListInfo
707
+ ObjcCategoryMerger::parseProtocolListInfo (const ConcatInputSection *isec,
708
+ uint32_t secOffset) {
709
+ PointerListInfo ptrList;
710
+ parseProtocolListInfo (isec, secOffset, ptrList);
711
+ return ptrList;
712
+ }
713
+
666
714
// Parse a pointer list that might be linked to ConcatInputSection at a given
667
715
// offset. This can be used for instance methods, class methods, instance props
668
716
// and class props since they have the same format.
@@ -769,11 +817,11 @@ void ObjcCategoryMerger::parseCatInfoToExtInfo(const InfoInputCategory &catInfo,
769
817
770
818
// Generate a protocol list (including header) and link it into the parent at
771
819
// the specified offset.
772
- void ObjcCategoryMerger::emitAndLinkProtocolList (
820
+ Defined * ObjcCategoryMerger::emitAndLinkProtocolList (
773
821
Defined *parentSym, uint32_t linkAtOffset,
774
822
const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {
775
823
if (ptrList.allPtrs .empty ())
776
- return ;
824
+ return nullptr ;
777
825
778
826
assert (ptrList.allPtrs .size () == ptrList.structCount );
779
827
@@ -820,6 +868,8 @@ void ObjcCategoryMerger::emitAndLinkProtocolList(
820
868
infoCategoryWriter.catPtrListInfo .relocTemplate );
821
869
offset += target->wordSize ;
822
870
}
871
+
872
+ return ptrListSym;
823
873
}
824
874
825
875
// Generate a pointer list (including header) and link it into the parent at the
@@ -1265,10 +1315,15 @@ void ObjcCategoryMerger::removeRefsToErasedIsecs() {
1265
1315
void ObjcCategoryMerger::doMerge () {
1266
1316
collectAndValidateCategoriesData ();
1267
1317
1268
- for (auto &entry : categoryMap)
1269
- if (entry.second .size () > 1 )
1318
+ for (auto &[baseClass, catInfos] : categoryMap) {
1319
+ if (auto *baseClassDef = dyn_cast<Defined>(baseClass)) {
1320
+ // Merge all categories into the base class
1321
+ mergeCategoriesIntoBaseClass (baseClassDef, catInfos);
1322
+ } else if (catInfos.size () > 1 ) {
1270
1323
// Merge all categories into a new, single category
1271
- mergeCategoriesIntoSingleCategory (entry.second );
1324
+ mergeCategoriesIntoSingleCategory (catInfos);
1325
+ }
1326
+ }
1272
1327
1273
1328
// Erase all categories that were merged
1274
1329
eraseMergedCategories ();
@@ -1302,3 +1357,101 @@ void objc::mergeCategories() {
1302
1357
}
1303
1358
1304
1359
void objc::doCleanup () { ObjcCategoryMerger::doCleanup (); }
1360
+
1361
+ void ObjcCategoryMerger::mergeCategoriesIntoBaseClass (
1362
+ const Defined *baseClass, std::vector<InfoInputCategory> &categories) {
1363
+ assert (categories.size () >= 1 && " Expected at least one category to merge" );
1364
+
1365
+ // Collect all the info from the categories
1366
+ ClassExtensionInfo extInfo (catLayout);
1367
+ for (auto &catInfo : categories) {
1368
+ parseCatInfoToExtInfo (catInfo, extInfo);
1369
+ }
1370
+
1371
+ // Get metadata for the base class
1372
+ Defined *metaRo = getClassRo (baseClass, /* getMetaRo=*/ true );
1373
+ ConcatInputSection *metaIsec = dyn_cast<ConcatInputSection>(metaRo->isec ());
1374
+ Defined *classRo = getClassRo (baseClass, /* getMetaRo=*/ false );
1375
+ ConcatInputSection *classIsec = dyn_cast<ConcatInputSection>(classRo->isec ());
1376
+
1377
+ // Now collect the info from the base class from the various lists in the
1378
+ // class metadata
1379
+
1380
+ // Protocol lists are a special case - the same protocol list is in classRo
1381
+ // and metaRo, so we only need to parse it once
1382
+ parseProtocolListInfo (classIsec, roClassLayout.baseProtocolsOffset ,
1383
+ extInfo.protocols );
1384
+
1385
+ // Check that the classRo and metaRo protocol lists are identical
1386
+ assert (
1387
+ parseProtocolListInfo (classIsec, roClassLayout.baseProtocolsOffset ) ==
1388
+ parseProtocolListInfo (metaIsec, roClassLayout.baseProtocolsOffset ) &&
1389
+ " Category merger expects classRo and metaRo to have the same protocol "
1390
+ " list" );
1391
+
1392
+ parsePointerListInfo (metaIsec, roClassLayout.baseMethodsOffset ,
1393
+ extInfo.classMethods );
1394
+ parsePointerListInfo (classIsec, roClassLayout.baseMethodsOffset ,
1395
+ extInfo.instanceMethods );
1396
+
1397
+ parsePointerListInfo (metaIsec, roClassLayout.basePropertiesOffset ,
1398
+ extInfo.classProps );
1399
+ parsePointerListInfo (classIsec, roClassLayout.basePropertiesOffset ,
1400
+ extInfo.instanceProps );
1401
+
1402
+ // Erase the old lists - these will be generated and replaced
1403
+ eraseSymbolAtIsecOffset (metaIsec, roClassLayout.baseMethodsOffset );
1404
+ eraseSymbolAtIsecOffset (metaIsec, roClassLayout.baseProtocolsOffset );
1405
+ eraseSymbolAtIsecOffset (metaIsec, roClassLayout.basePropertiesOffset );
1406
+ eraseSymbolAtIsecOffset (classIsec, roClassLayout.baseMethodsOffset );
1407
+ eraseSymbolAtIsecOffset (classIsec, roClassLayout.baseProtocolsOffset );
1408
+ eraseSymbolAtIsecOffset (classIsec, roClassLayout.basePropertiesOffset );
1409
+
1410
+ // Emit the newly merged lists - first into the meta RO then into the class RO
1411
+ // First we emit and link the protocol list into the meta RO. Then we link it
1412
+ // in the classRo as well (they're supposed to be identical)
1413
+ if (Defined *protoListSym =
1414
+ emitAndLinkProtocolList (metaRo, roClassLayout.baseProtocolsOffset ,
1415
+ extInfo, extInfo.protocols )) {
1416
+ createSymbolReference (classRo, protoListSym,
1417
+ roClassLayout.baseProtocolsOffset ,
1418
+ infoCategoryWriter.catBodyInfo .relocTemplate );
1419
+ }
1420
+
1421
+ emitAndLinkPointerList (metaRo, roClassLayout.baseMethodsOffset , extInfo,
1422
+ extInfo.classMethods );
1423
+ emitAndLinkPointerList (classRo, roClassLayout.baseMethodsOffset , extInfo,
1424
+ extInfo.instanceMethods );
1425
+
1426
+ emitAndLinkPointerList (metaRo, roClassLayout.basePropertiesOffset , extInfo,
1427
+ extInfo.classProps );
1428
+
1429
+ emitAndLinkPointerList (classRo, roClassLayout.basePropertiesOffset , extInfo,
1430
+ extInfo.instanceProps );
1431
+
1432
+ // Mark all the categories as merged - this will be used to erase them later
1433
+ for (auto &catInfo : categories)
1434
+ catInfo.wasMerged = true ;
1435
+ }
1436
+
1437
+ // Erase the symbol at a given offset in an InputSection
1438
+ void ObjcCategoryMerger::eraseSymbolAtIsecOffset (ConcatInputSection *isec,
1439
+ uint32_t offset) {
1440
+ Defined *sym = tryGetDefinedAtIsecOffset (isec, offset);
1441
+ if (!sym)
1442
+ return ;
1443
+
1444
+ // Remove the symbol from isec->symbols
1445
+ assert (isa<Defined>(sym) && " Can only erase a Defined" );
1446
+ llvm::erase (isec->symbols , sym);
1447
+
1448
+ // Remove the relocs that refer to this symbol
1449
+ auto removeAtOff = [offset](Reloc const &r) { return r.offset == offset; };
1450
+ llvm::erase_if (isec->relocs , removeAtOff);
1451
+
1452
+ // Now, if the symbol fully occupies a ConcatInputSection, we can also erase
1453
+ // the whole ConcatInputSection
1454
+ if (ConcatInputSection *cisec = dyn_cast<ConcatInputSection>(sym->isec ()))
1455
+ if (cisec->data .size () == sym->size )
1456
+ eraseISec (cisec);
1457
+ }
0 commit comments