@@ -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,8 @@ 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
+ // Info for all input categories, grouped by base class
482
+ std::vector<CategoryGroup> categoryGroups ;
481
483
482
484
// Normally, the binary data comes from the input files, but since we're
483
485
// generating binary data ourselves, we use the below array to store it in.
@@ -1043,6 +1045,12 @@ void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
1043
1045
}
1044
1046
1045
1047
void ObjcCategoryMerger::collectAndValidateCategoriesData () {
1048
+ // Make category merging deterministic by using a counter for found categories
1049
+ uint32_t inputCategoryIndex = 0 ;
1050
+ // Map of base class Symbol to list of InfoInputCategory's for it. We use this
1051
+ // for fast lookup of categories to their base class. Later this info will be
1052
+ // moved into 'categoryGroups' member.
1053
+ DenseMap<const Symbol *, std::vector<InfoInputCategory>> categoryMap;
1046
1054
for (InputSection *sec : allInputSections) {
1047
1055
if (sec->getName () != section_names::objcCatList)
1048
1056
continue ;
@@ -1072,12 +1080,25 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
1072
1080
tryGetSymbolAtIsecOffset (catBodyIsec, catLayout.klassOffset );
1073
1081
assert (classSym && " Category does not have a valid base class" );
1074
1082
1075
- InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
1083
+ InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off,
1084
+ inputCategoryIndex++};
1076
1085
categoryMap[classSym].push_back (catInputInfo);
1077
1086
1078
1087
collectCategoryWriterInfoFromCategory (catInputInfo);
1079
1088
}
1080
1089
}
1090
+
1091
+ // Move categoryMap into categoryGroups and sort by the first category's
1092
+ // inputIndex. This way we can be sure that category merging will be
1093
+ // deterministic across linker runs.
1094
+ categoryGroups.reserve (categoryMap.size ());
1095
+ for (auto &mapEntry : categoryMap)
1096
+ categoryGroups.push_back (mapEntry.second );
1097
+
1098
+ std::sort (categoryGroups.begin (), categoryGroups.end (),
1099
+ [](const CategoryGroup &a, const CategoryGroup &b) {
1100
+ return a[0 ].inputIndex < b[0 ].inputIndex ;
1101
+ });
1081
1102
}
1082
1103
1083
1104
// In the input we have multiple __objc_catlist InputSection, each of which may
@@ -1153,8 +1174,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1153
1174
// Map of InputSection to a set of offsets of the categories that were merged
1154
1175
std::map<ConcatInputSection *, std::set<uint64_t >> catListToErasedOffsets;
1155
1176
1156
- for (auto &mapEntry : categoryMap ) {
1157
- for (InfoInputCategory &catInfo : mapEntry. second ) {
1177
+ for (auto &catGroup : categoryGroups ) {
1178
+ for (InfoInputCategory &catInfo : catGroup ) {
1158
1179
if (catInfo.wasMerged ) {
1159
1180
eraseISec (catInfo.catListIsec );
1160
1181
catListToErasedOffsets[catInfo.catListIsec ].insert (
@@ -1169,8 +1190,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1169
1190
generateCatListForNonErasedCategories (catListToErasedOffsets);
1170
1191
1171
1192
// Erase the old method lists & names of the categories that were merged
1172
- for (auto &mapEntry : categoryMap ) {
1173
- for (InfoInputCategory &catInfo : mapEntry. second ) {
1193
+ for (auto &catgroup : categoryGroups ) {
1194
+ for (InfoInputCategory &catInfo : catgroup ) {
1174
1195
if (!catInfo.wasMerged )
1175
1196
continue ;
1176
1197
@@ -1193,10 +1214,10 @@ void ObjcCategoryMerger::eraseMergedCategories() {
1193
1214
void ObjcCategoryMerger::doMerge () {
1194
1215
collectAndValidateCategoriesData ();
1195
1216
1196
- for (auto &entry : categoryMap )
1197
- if (entry. second .size () > 1 )
1217
+ for (auto &catGroup : categoryGroups )
1218
+ if (catGroup .size () > 1 )
1198
1219
// Merge all categories into a new, single category
1199
- mergeCategoriesIntoSingleCategory (entry. second );
1220
+ mergeCategoriesIntoSingleCategory (catGroup );
1200
1221
1201
1222
// Erase all categories that were merged
1202
1223
eraseMergedCategories ();
0 commit comments