diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -287,6 +287,10 @@ static void encodePersonalities(const std::vector *> &cuPtrVector, std::vector &personalities) { + // Map of personality symbol name to (personality, personalityIndex). + DenseMap> nameToPersonality; + + const auto &entries = in.got->getEntries(); for (CompactUnwindEntry *cu : cuPtrVector) { if (cu->personality == 0) continue; @@ -296,13 +300,39 @@ if (it != personalities.end()) { personalityIndex = std::distance(personalities.begin(), it) + 1; } else { - personalities.push_back(cu->personality); - personalityIndex = personalities.size(); + const Symbol *sym = entries[cu->personality - 1]; // -1 because 1-based + auto it = nameToPersonality.find(sym->getName()); + + if (it == nameToPersonality.end()) { + // If we've never seen this symbol, then record it. + personalities.push_back(cu->personality); + personalityIndex = personalities.size(); + nameToPersonality[sym->getName()] = + std::pair(cu->personality, personalityIndex); + } else { + auto &pair = it->second; + const Symbol *prev_sym = entries[pair.first - 1]; + // Can't have both Defined with the same name and different GOT index. + assert(sym->kind() != Symbol::Defined || + prev_sym->kind() != Symbol::Defined()); + + // Prioritize Defined over others, and older entries over others (just + // to avoid constantly updating the nameToPersonality map) + if (sym->kind() != Symbol::Defined || + prev_sym->kind == Symbol::Defined) { + personalityIndex = pair.second; + } else { + personalities[pair.second] = cu->personality; + pair.first = cu->personality; + personalityIndex = pair.second; + } + } } cu->encoding |= personalityIndex << countTrailingZeros( static_cast(UNWIND_PERSONALITY_MASK)); } + if (personalities.size() > 3) error("too many personalities (" + std::to_string(personalities.size()) + ") for compact unwind to encode");