@@ -354,10 +354,13 @@ class ObjcCategoryMerger {
354
354
ConcatInputSection *catListIsec;
355
355
ConcatInputSection *catBodyIsec;
356
356
uint32_t offCatListIsec = 0 ;
357
+ uint32_t inputIndex = 0 ;
357
358
358
359
bool wasMerged = false ;
359
360
};
360
361
362
+ typedef std::vector<InfoInputCategory> CategoryGroup;
363
+
361
364
// To write new (merged) categories or classes, we will try make limited
362
365
// assumptions about the alignment and the sections the various class/category
363
366
// info are stored in and . So we'll just reuse the same sections and
@@ -429,8 +432,7 @@ class ObjcCategoryMerger {
429
432
430
433
private:
431
434
void collectAndValidateCategoriesData ();
432
- void
433
- mergeCategoriesIntoSingleCategory (std::vector<InfoInputCategory> &categories);
435
+ void mergeCategoriesIntoSingleCategory (CategoryGroup &categories);
434
436
435
437
void eraseISec (ConcatInputSection *isec);
436
438
void removeRefsToErasedIsecs ();
@@ -490,8 +492,8 @@ class ObjcCategoryMerger {
490
492
491
493
InfoCategoryWriter infoCategoryWriter;
492
494
std::vector<ConcatInputSection *> &allInputSections;
493
- // Map of base class Symbol to list of InfoInputCategory's for it
494
- DenseMap< const Symbol *, std::vector<InfoInputCategory>> categoryMap ;
495
+ // Info for all input categories, grouped by base class
496
+ std::vector<CategoryGroup> categoryGroups ;
495
497
// Set for tracking InputSection erased via eraseISec
496
498
DenseSet<InputSection *> erasedIsecs;
497
499
@@ -1061,6 +1063,12 @@ void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
1061
1063
}
1062
1064
1063
1065
void ObjcCategoryMerger::collectAndValidateCategoriesData () {
1066
+ // Make category merging deterministic by using a counter for found categories
1067
+ uint32_t inputCategoryIndex = 0 ;
1068
+ // Map of base class Symbol to list of InfoInputCategory's for it. We use this
1069
+ // for fast lookup of categories to their base class. Later this info will be
1070
+ // moved into 'categoryGroups' member.
1071
+ DenseMap<const Symbol *, std::vector<InfoInputCategory>> categoryMap;
1064
1072
for (InputSection *sec : allInputSections) {
1065
1073
if (sec->getName () != section_names::objcCatList)
1066
1074
continue ;
@@ -1090,12 +1098,25 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
1090
1098
tryGetSymbolAtIsecOffset (catBodyIsec, catLayout.klassOffset );
1091
1099
assert (classSym && " Category does not have a valid base class" );
1092
1100
1093
- InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
1101
+ InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off,
1102
+ inputCategoryIndex++};
1094
1103
categoryMap[classSym].push_back (catInputInfo);
1095
1104
1096
1105
collectCategoryWriterInfoFromCategory (catInputInfo);
1097
1106
}
1098
1107
}
1108
+
1109
+ // Move categoryMap into categoryGroups and sort by the first category's
1110
+ // inputIndex. This way we can be sure that category merging will be
1111
+ // deterministic across linker runs.
1112
+ categoryGroups.reserve (categoryMap.size ());
1113
+ for (auto &mapEntry : categoryMap)
1114
+ categoryGroups.push_back (mapEntry.second );
1115
+
1116
+ std::sort (categoryGroups.begin (), categoryGroups.end (),
1117
+ [](const CategoryGroup &a, const CategoryGroup &b) {
1118
+ return a[0 ].inputIndex < b[0 ].inputIndex ;
1119
+ });
1099
1120
}
1100
1121
1101
1122
// In the input we have multiple __objc_catlist InputSection, each of which may
@@ -1173,8 +1194,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1173
1194
// Map of InputSection to a set of offsets of the categories that were merged
1174
1195
std::map<ConcatInputSection *, std::set<uint64_t >> catListToErasedOffsets;
1175
1196
1176
- for (auto &mapEntry : categoryMap ) {
1177
- for (InfoInputCategory &catInfo : mapEntry. second ) {
1197
+ for (auto &catGroup : categoryGroups ) {
1198
+ for (InfoInputCategory &catInfo : catGroup ) {
1178
1199
if (catInfo.wasMerged ) {
1179
1200
eraseISec (catInfo.catListIsec );
1180
1201
catListToErasedOffsets[catInfo.catListIsec ].insert (
@@ -1189,8 +1210,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1189
1210
generateCatListForNonErasedCategories (catListToErasedOffsets);
1190
1211
1191
1212
// Erase the old method lists & names of the categories that were merged
1192
- for (auto &mapEntry : categoryMap ) {
1193
- for (InfoInputCategory &catInfo : mapEntry. second ) {
1213
+ for (auto &catgroup : categoryGroups ) {
1214
+ for (InfoInputCategory &catInfo : catgroup ) {
1194
1215
if (!catInfo.wasMerged )
1195
1216
continue ;
1196
1217
@@ -1241,10 +1262,10 @@ void ObjcCategoryMerger::removeRefsToErasedIsecs() {
1241
1262
void ObjcCategoryMerger::doMerge () {
1242
1263
collectAndValidateCategoriesData ();
1243
1264
1244
- for (auto &entry : categoryMap )
1245
- if (entry. second .size () > 1 )
1265
+ for (auto &catGroup : categoryGroups )
1266
+ if (catGroup .size () > 1 )
1246
1267
// Merge all categories into a new, single category
1247
- mergeCategoriesIntoSingleCategory (entry. second );
1268
+ mergeCategoriesIntoSingleCategory (catGroup );
1248
1269
1249
1270
// Erase all categories that were merged
1250
1271
eraseMergedCategories ();
0 commit comments