@@ -341,10 +341,13 @@ class ObjcCategoryMerger {
341
341
ConcatInputSection *catListIsec;
342
342
ConcatInputSection *catBodyIsec;
343
343
uint32_t offCatListIsec = 0 ;
344
+ uint32_t inputIndex = 0 ;
344
345
345
346
bool wasMerged = false ;
346
347
};
347
348
349
+ typedef std::vector<InfoInputCategory> CategoryGroup;
350
+
348
351
// To write new (merged) categories or classes, we will try make limited
349
352
// assumptions about the alignment and the sections the various class/category
350
353
// info are stored in and . So we'll just reuse the same sections and
@@ -416,8 +419,7 @@ class ObjcCategoryMerger {
416
419
417
420
private:
418
421
void collectAndValidateCategoriesData ();
419
- void
420
- mergeCategoriesIntoSingleCategory (std::vector<InfoInputCategory> &categories);
422
+ void mergeCategoriesIntoSingleCategory (CategoryGroup &categories);
421
423
422
424
void eraseISec (ConcatInputSection *isec);
423
425
void eraseMergedCategories ();
@@ -476,8 +478,9 @@ class ObjcCategoryMerger {
476
478
477
479
InfoCategoryWriter infoCategoryWriter;
478
480
std::vector<ConcatInputSection *> &allInputSections;
479
- // Map of base class Symbol to list of InfoInputCategory's for it
480
- DenseMap<const Symbol *, std::vector<InfoInputCategory>> categoryMap;
481
+ // We'll use this to keep track of which category groups (categories with the
482
+ // same base class)
483
+ std::vector<CategoryGroup> categoryGroups;
481
484
482
485
// Normally, the binary data comes from the input files, but since we're
483
486
// generating binary data ourselves, we use the below array to store it in.
@@ -1043,6 +1046,14 @@ void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
1043
1046
}
1044
1047
1045
1048
void ObjcCategoryMerger::collectAndValidateCategoriesData () {
1049
+
1050
+ // A counter for the cateogries found in the input, used to make category
1051
+ // merging deterministic.
1052
+ uint32_t inputCategoryIndex = 0 ;
1053
+ // Map of base class Symbol to list of InfoInputCategory's for it. We use this
1054
+ // for fast lookup of categories to their base class. Later this info will be
1055
+ // moved into categoryGroups.
1056
+ DenseMap<const Symbol *, std::vector<InfoInputCategory>> categoryMap;
1046
1057
for (InputSection *sec : allInputSections) {
1047
1058
if (sec->getName () != section_names::objcCatList)
1048
1059
continue ;
@@ -1072,12 +1083,25 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
1072
1083
tryGetSymbolAtIsecOffset (catBodyIsec, catLayout.klassOffset );
1073
1084
assert (classSym && " Category does not have a valid base class" );
1074
1085
1075
- InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
1086
+ InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off,
1087
+ inputCategoryIndex++};
1076
1088
categoryMap[classSym].push_back (catInputInfo);
1077
1089
1078
1090
collectCategoryWriterInfoFromCategory (catInputInfo);
1079
1091
}
1080
1092
}
1093
+
1094
+ // Move categoryMap into categoryGroups and sort by the first category's
1095
+ // inputIndex. This way we can be sure that category merging will be
1096
+ // deterministic across linker runs.
1097
+ categoryGroups.reserve (categoryMap.size ());
1098
+ for (auto &mapEntry : categoryMap)
1099
+ categoryGroups.push_back (mapEntry.second );
1100
+
1101
+ std::sort (categoryGroups.begin (), categoryGroups.end (),
1102
+ [](const CategoryGroup &a, const CategoryGroup &b) {
1103
+ return a[0 ].inputIndex < b[0 ].inputIndex ;
1104
+ });
1081
1105
}
1082
1106
1083
1107
// In the input we have multiple __objc_catlist InputSection, each of which may
@@ -1153,8 +1177,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1153
1177
// Map of InputSection to a set of offsets of the categories that were merged
1154
1178
std::map<ConcatInputSection *, std::set<uint64_t >> catListToErasedOffsets;
1155
1179
1156
- for (auto &mapEntry : categoryMap ) {
1157
- for (InfoInputCategory &catInfo : mapEntry. second ) {
1180
+ for (auto &catGroup : categoryGroups ) {
1181
+ for (InfoInputCategory &catInfo : catGroup ) {
1158
1182
if (catInfo.wasMerged ) {
1159
1183
eraseISec (catInfo.catListIsec );
1160
1184
catListToErasedOffsets[catInfo.catListIsec ].insert (
@@ -1169,8 +1193,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1169
1193
generateCatListForNonErasedCategories (catListToErasedOffsets);
1170
1194
1171
1195
// Erase the old method lists & names of the categories that were merged
1172
- for (auto &mapEntry : categoryMap ) {
1173
- for (InfoInputCategory &catInfo : mapEntry. second ) {
1196
+ for (auto &catgroup : categoryGroups ) {
1197
+ for (InfoInputCategory &catInfo : catgroup ) {
1174
1198
if (!catInfo.wasMerged )
1175
1199
continue ;
1176
1200
@@ -1193,10 +1217,10 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1193
1217
void ObjcCategoryMerger::doMerge () {
1194
1218
collectAndValidateCategoriesData ();
1195
1219
1196
- for (auto &entry : categoryMap )
1197
- if (entry. second .size () > 1 )
1220
+ for (auto &catGroup : categoryGroups )
1221
+ if (catGroup .size () > 1 )
1198
1222
// Merge all categories into a new, single category
1199
- mergeCategoriesIntoSingleCategory (entry. second );
1223
+ mergeCategoriesIntoSingleCategory (catGroup );
1200
1224
1201
1225
// Erase all categories that were merged
1202
1226
eraseMergedCategories ();
0 commit comments