Skip to content

Commit 686d003

Browse files
alx32Alex B
authored and
Alex B
committed
[lld-macho] Fix category merging category map non-determinism
1 parent 89e0557 commit 686d003

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
@@ -354,10 +354,13 @@ class ObjcCategoryMerger {
354354
ConcatInputSection *catListIsec;
355355
ConcatInputSection *catBodyIsec;
356356
uint32_t offCatListIsec = 0;
357+
uint32_t inputIndex = 0;
357358

358359
bool wasMerged = false;
359360
};
360361

362+
typedef std::vector<InfoInputCategory> CategoryGroup;
363+
361364
// To write new (merged) categories or classes, we will try make limited
362365
// assumptions about the alignment and the sections the various class/category
363366
// info are stored in and . So we'll just reuse the same sections and
@@ -429,8 +432,7 @@ class ObjcCategoryMerger {
429432

430433
private:
431434
void collectAndValidateCategoriesData();
432-
void
433-
mergeCategoriesIntoSingleCategory(std::vector<InfoInputCategory> &categories);
435+
void mergeCategoriesIntoSingleCategory(CategoryGroup &categories);
434436

435437
void eraseISec(ConcatInputSection *isec);
436438
void removeRefsToErasedIsecs();
@@ -490,8 +492,8 @@ class ObjcCategoryMerger {
490492

491493
InfoCategoryWriter infoCategoryWriter;
492494
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;
495497
// Set for tracking InputSection erased via eraseISec
496498
DenseSet<InputSection *> erasedIsecs;
497499

@@ -1061,6 +1063,12 @@ void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
10611063
}
10621064

10631065
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;
10641072
for (InputSection *sec : allInputSections) {
10651073
if (sec->getName() != section_names::objcCatList)
10661074
continue;
@@ -1090,12 +1098,25 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
10901098
tryGetSymbolAtIsecOffset(catBodyIsec, catLayout.klassOffset);
10911099
assert(classSym && "Category does not have a valid base class");
10921100

1093-
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
1101+
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off,
1102+
inputCategoryIndex++};
10941103
categoryMap[classSym].push_back(catInputInfo);
10951104

10961105
collectCategoryWriterInfoFromCategory(catInputInfo);
10971106
}
10981107
}
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+
});
10991120
}
11001121

11011122
// In the input we have multiple __objc_catlist InputSection, each of which may
@@ -1173,8 +1194,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11731194
// Map of InputSection to a set of offsets of the categories that were merged
11741195
std::map<ConcatInputSection *, std::set<uint64_t>> catListToErasedOffsets;
11751196

1176-
for (auto &mapEntry : categoryMap) {
1177-
for (InfoInputCategory &catInfo : mapEntry.second) {
1197+
for (auto &catGroup : categoryGroups) {
1198+
for (InfoInputCategory &catInfo : catGroup) {
11781199
if (catInfo.wasMerged) {
11791200
eraseISec(catInfo.catListIsec);
11801201
catListToErasedOffsets[catInfo.catListIsec].insert(
@@ -1189,8 +1210,8 @@ void ObjcCategoryMerger::eraseMergedCategories() {
11891210
generateCatListForNonErasedCategories(catListToErasedOffsets);
11901211

11911212
// 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) {
11941215
if (!catInfo.wasMerged)
11951216
continue;
11961217

@@ -1241,10 +1262,10 @@ void ObjcCategoryMerger::removeRefsToErasedIsecs() {
12411262
void ObjcCategoryMerger::doMerge() {
12421263
collectAndValidateCategoriesData();
12431264

1244-
for (auto &entry : categoryMap)
1245-
if (entry.second.size() > 1)
1265+
for (auto &catGroup : categoryGroups)
1266+
if (catGroup.size() > 1)
12461267
// Merge all categories into a new, single category
1247-
mergeCategoriesIntoSingleCategory(entry.second);
1268+
mergeCategoriesIntoSingleCategory(catGroup);
12481269

12491270
// Erase all categories that were merged
12501271
eraseMergedCategories();

0 commit comments

Comments
 (0)