Skip to content

Commit 2ac1d78

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

File tree

1 file changed

+36
-12
lines changed

1 file changed

+36
-12
lines changed

lld/MachO/ObjC.cpp

Lines changed: 36 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,9 @@ 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+
// We'll use this to keep track of which category groups (categories with the
482+
// same base class)
483+
std::vector<CategoryGroup> categoryGroups;
481484

482485
// Normally, the binary data comes from the input files, but since we're
483486
// generating binary data ourselves, we use the below array to store it in.
@@ -1043,6 +1046,14 @@ void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
10431046
}
10441047

10451048
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;
10461057
for (InputSection *sec : allInputSections) {
10471058
if (sec->getName() != section_names::objcCatList)
10481059
continue;
@@ -1072,12 +1083,25 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
10721083
tryGetSymbolAtIsecOffset(catBodyIsec, catLayout.klassOffset);
10731084
assert(classSym && "Category does not have a valid base class");
10741085

1075-
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
1086+
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off,
1087+
inputCategoryIndex++};
10761088
categoryMap[classSym].push_back(catInputInfo);
10771089

10781090
collectCategoryWriterInfoFromCategory(catInputInfo);
10791091
}
10801092
}
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+
});
10811105
}
10821106

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

1156-
for (auto &mapEntry : categoryMap) {
1157-
for (InfoInputCategory &catInfo : mapEntry.second) {
1180+
for (auto &catGroup : categoryGroups) {
1181+
for (InfoInputCategory &catInfo : catGroup) {
11581182
if (catInfo.wasMerged) {
11591183
eraseISec(catInfo.catListIsec);
11601184
catListToErasedOffsets[catInfo.catListIsec].insert(
@@ -1169,8 +1193,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11691193
generateCatListForNonErasedCategories(catListToErasedOffsets);
11701194

11711195
// 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) {
11741198
if (!catInfo.wasMerged)
11751199
continue;
11761200

@@ -1193,10 +1217,10 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11931217
void ObjcCategoryMerger::doMerge() {
11941218
collectAndValidateCategoriesData();
11951219

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

12011225
// Erase all categories that were merged
12021226
eraseMergedCategories();

0 commit comments

Comments
 (0)