Skip to content

Commit 8add44d

Browse files
committed
[lld-macho] Fix category merging category map non-determinism
1 parent aae3835 commit 8add44d

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

lld/MachO/ObjC.cpp

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,13 @@ class ObjcCategoryMerger {
341341
ConcatInputSection *catListIsec;
342342
ConcatInputSection *catBodyIsec;
343343
uint32_t offCatListIsec = 0;
344+
uint32_t inputIndex = 0;
344345

345346
bool wasMerged = false;
346347
};
347348

349+
typedef std::vector<InfoInputCategory> CategoryGroup;
350+
348351
// To write new (merged) categories or classes, we will try make limited
349352
// assumptions about the alignment and the sections the various class/category
350353
// info are stored in and . So we'll just reuse the same sections and
@@ -416,8 +419,7 @@ class ObjcCategoryMerger {
416419

417420
private:
418421
void collectAndValidateCategoriesData();
419-
void
420-
mergeCategoriesIntoSingleCategory(std::vector<InfoInputCategory> &categories);
422+
void mergeCategoriesIntoSingleCategory(CategoryGroup &categories);
421423

422424
void eraseISec(ConcatInputSection *isec);
423425
void eraseMergedCategories();
@@ -476,8 +478,8 @@ class ObjcCategoryMerger {
476478

477479
InfoCategoryWriter infoCategoryWriter;
478480
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;
481483

482484
// Normally, the binary data comes from the input files, but since we're
483485
// generating binary data ourselves, we use the below array to store it in.
@@ -1043,6 +1045,12 @@ void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
10431045
}
10441046

10451047
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;
10461054
for (InputSection *sec : allInputSections) {
10471055
if (sec->getName() != section_names::objcCatList)
10481056
continue;
@@ -1072,12 +1080,25 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
10721080
tryGetSymbolAtIsecOffset(catBodyIsec, catLayout.klassOffset);
10731081
assert(classSym && "Category does not have a valid base class");
10741082

1075-
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
1083+
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off,
1084+
inputCategoryIndex++};
10761085
categoryMap[classSym].push_back(catInputInfo);
10771086

10781087
collectCategoryWriterInfoFromCategory(catInputInfo);
10791088
}
10801089
}
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+
});
10811102
}
10821103

10831104
// In the input we have multiple __objc_catlist InputSection, each of which may
@@ -1153,8 +1174,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11531174
// Map of InputSection to a set of offsets of the categories that were merged
11541175
std::map<ConcatInputSection *, std::set<uint64_t>> catListToErasedOffsets;
11551176

1156-
for (auto &mapEntry : categoryMap) {
1157-
for (InfoInputCategory &catInfo : mapEntry.second) {
1177+
for (auto &catGroup : categoryGroups) {
1178+
for (InfoInputCategory &catInfo : catGroup) {
11581179
if (catInfo.wasMerged) {
11591180
eraseISec(catInfo.catListIsec);
11601181
catListToErasedOffsets[catInfo.catListIsec].insert(
@@ -1169,8 +1190,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11691190
generateCatListForNonErasedCategories(catListToErasedOffsets);
11701191

11711192
// 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) {
11741195
if (!catInfo.wasMerged)
11751196
continue;
11761197

@@ -1193,10 +1214,10 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11931214
void ObjcCategoryMerger::doMerge() {
11941215
collectAndValidateCategoriesData();
11951216

1196-
for (auto &entry : categoryMap)
1197-
if (entry.second.size() > 1)
1217+
for (auto &catGroup : categoryGroups)
1218+
if (catGroup.size() > 1)
11981219
// Merge all categories into a new, single category
1199-
mergeCategoriesIntoSingleCategory(entry.second);
1220+
mergeCategoriesIntoSingleCategory(catGroup);
12001221

12011222
// Erase all categories that were merged
12021223
eraseMergedCategories();

0 commit comments

Comments
 (0)