diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 56229334f9a44..ed0aacb8ae755 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -295,6 +295,9 @@ struct Config { bool useAndroidRelrTags = false; bool warnBackrefs; llvm::SmallVector warnBackrefsExclude; + bool warnMismatchSectionsInComdatGroups; + llvm::SmallVector + warnMismatchSectionsInComdatGroupsExclude; bool warnCommon; bool warnMissingEntry; bool warnSymbolOrdering; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 6bef09eeca015..0042ad555d439 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1437,6 +1437,9 @@ static void readConfigs(opt::InputArgList &args) { OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false); config->warnBackrefs = args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); + config->warnMismatchSectionsInComdatGroups = + args.hasFlag(OPT_warn_mismatch_sections_in_comdat_groups, + OPT_no_warn_mismatch_sections_in_comdat_groups, false); config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); config->warnSymbolOrdering = args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); @@ -1709,6 +1712,17 @@ static void readConfigs(opt::InputArgList &args) { pattern); } + for (opt::Arg *arg : + args.filtered(OPT_warn_mismatch_sections_in_comdat_groups_exclude)) { + StringRef pattern(arg->getValue()); + if (Expected pat = GlobPattern::create(pattern)) + config->warnMismatchSectionsInComdatGroupsExclude.push_back( + std::move(*pat)); + else + error(arg->getSpelling() + ": " + toString(pat.takeError()) + ": " + + pattern); + } + // For -no-pie and -pie, --export-dynamic-symbol specifies defined symbols // which should be exported. For -shared, references to matched non-local // STV_DEFAULT symbols are not bound to definitions within the shared object, diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 06a3d565deb76..0cde39ef1fad6 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -561,6 +561,84 @@ uint32_t ObjFile::getSectionIndex(const Elf_Sym &sym) const { this); } +template +void ObjFile::checkComdatGroups(bool recordedNewSection, + const InputFile *otherFile, + ArrayRef sectionsInGroup, + StringRef signature) { + ArrayRef objSections = this->getELFShdrs(); + object::ELFFile obj = this->getObj(); + StringRef shstrtab = CHECK(obj.getSectionStringTable(objSections), this); + const auto &exclude = config->warnMismatchSectionsInComdatGroupsExclude; + + // First check for a given comdat group, we have all the necessary sections. + if (recordedNewSection) { + // The signature was recorded - we need to save the section names for this + // group. + for (uint32_t secIdx : sectionsInGroup) { + const Elf_Shdr §ion = objSections[secIdx]; + StringRef sectionName = + CHECK(obj.getSectionName(section, shstrtab), this); + symtab.comdatGroupSectionNames[CachedHashStringRef(signature)].insert( + sectionName); + } + } else { + // The signature was not recorded - this comdat group has a set of section + // names associated with it. Check the sets match. + const llvm::StringSet<> &cachedSectionNames = + symtab.comdatGroupSectionNames.at(CachedHashStringRef(signature)); + size_t cachedSize = cachedSectionNames.size(); + size_t foundSize = sectionsInGroup.size(); + if (cachedSize != foundSize) { + warn("comdat group with signature '" + signature + "' in '" + + otherFile->getName() + "' has " + Twine(cachedSize) + + " section(s) while group in '" + this->getName() + "' has " + + Twine(foundSize) + " section(s)"); + return; + } + + // Group sizes match, but may have different section names. + for (uint32_t secIdx : sectionsInGroup) { + const Elf_Shdr §ion = objSections[secIdx]; + StringRef sectionName = + CHECK(obj.getSectionName(section, shstrtab), this); + if (!cachedSectionNames.contains(sectionName)) { + warn("comdat group with signature '" + signature + "' in '" + + otherFile->getName() + "' does not have section '" + sectionName + + "' which is part of comdat group in '" + this->getName() + "'"); + return; + } + } + } + + // Get all sections part of the group seen here. + for (uint32_t secIdx : sectionsInGroup) { + const Elf_Shdr §ion = objSections[secIdx]; + ArrayRef contents = + CHECK(obj.template getSectionContentsAsArray(section), this); + StringRef sectionName = CHECK(obj.getSectionName(section, shstrtab), this); + + auto pair = symtab.comdatGroupSectionContents.try_emplace( + CachedHashStringRef(sectionName), contents); + bool added = pair.second; + ArrayRef cachedContents = pair.first->second; + + bool ignoreSection = + std::any_of(exclude.begin(), exclude.end(), + [§ionName](const llvm::GlobPattern &pat) { + return pat.match(sectionName); + }); + + if (!added && !ignoreSection && this != otherFile && + !contents.equals(cachedContents)) { + warn("section '" + sectionName + "' for comdat group with signature '" + + signature + "' has different contents between '" + this->getName() + + "' and '" + otherFile->getName() + "'"); + return; + } + } +} + template void ObjFile::parse(bool ignoreComdats) { object::ELFFile obj = this->getObj(); // Read a section table. justSymbols is usually false. @@ -646,10 +724,15 @@ template void ObjFile::parse(bool ignoreComdats) { if (flag && flag != GRP_COMDAT) fatal(toString(this) + ": unsupported SHT_GROUP format"); - bool keepGroup = - (flag & GRP_COMDAT) == 0 || ignoreComdats || - symtab.comdatGroups.try_emplace(CachedHashStringRef(signature), this) - .second; + bool keepGroup = (flag & GRP_COMDAT) == 0 || ignoreComdats; + if (!keepGroup) { + auto pair = + symtab.comdatGroups.try_emplace(CachedHashStringRef(signature), this); + keepGroup = pair.second; + if (config->warnMismatchSectionsInComdatGroups) + checkComdatGroups(pair.second, pair.first->second, entries.slice(1), + signature); + } if (keepGroup) { if (config->relocatable) this->sections[i] = createInputSection( diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index ab98d78fcf145..a2ad394d4adf1 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -304,6 +304,10 @@ template class ObjFile : public ELFFileBase { bool shouldMerge(const Elf_Shdr &sec, StringRef name); + void checkComdatGroups(bool recordedNewSection, const InputFile *otherFile, + ArrayRef sectionsInGroup, + StringRef signature); + // Each ELF symbol contains a section index which the symbol belongs to. // However, because the number of bits dedicated for that is limited, a // symbol can directly point to a section only when the section index is diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index c2c9cabc92a4d..4fd6ac3735e51 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -510,6 +510,15 @@ defm warn_backrefs_exclude "which should be ignored for --warn-backrefs.">, MetaVarName<"">; +defm warn_mismatch_sections_in_comdat_groups: BB<"warn-mismatch-sections-in-comdat-groups", + "Warn about discarded sections in a comdat group that have different contents from a retained section in a comdat group with the same key", + "Do not warn about discarded sections in a comdat group that have different contents from a retained section in a comdat group with the same key">; + +defm warn_mismatch_sections_in_comdat_groups_exclude + : EEq<"warn-mismatch-sections-in-comdat-groups-exclude", + "Glob describing sections to ignore checking for --warn-mismatch-sections-in-comdat-groups.">, + MetaVarName<"">; + defm warn_common: B<"warn-common", "Warn about duplicate common symbols", "Do not warn about duplicate common symbols (default)">; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index 37e31d2372963..1b26bae9c8f81 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -65,6 +65,16 @@ class SymbolTable { // is used to uniquify them. llvm::DenseMap comdatGroups; + // Map from a section name for a comdat section to its contents. These + // sections are the ones not discarded. + llvm::DenseMap> + comdatGroupSectionContents; + + // Map from a comdat group signature to the names of sections part of the + // group. + llvm::DenseMap> + comdatGroupSectionNames; + // The Map of __acle_se_, pairs found in the input objects. // Key is the name. llvm::SmallMapVector cmseSymMap; diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/Inputs/func.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/Inputs/func.s new file mode 100644 index 0000000000000..767d52f0f47a8 --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/Inputs/func.s @@ -0,0 +1,4 @@ + .global func + .section .data.func,"awG",@progbits,func,comdat +func: + .word 7 diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-group-sizes.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-group-sizes.s new file mode 100644 index 0000000000000..2f39b5ff7e937 --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-group-sizes.s @@ -0,0 +1,17 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/func.s -o %t0.o +// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --warn-mismatch-sections-in-comdat-groups 2>&1 | FileCheck %s +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --warn-mismatch-sections-in-comdat-groups 2>&1 | FileCheck %s --check-prefix=REVERSE + +// CHECK: warning: comdat group with signature 'func' in '{{.*}}0.o' has 1 section(s) while group in '{{.*}}1.o' has 2 section(s) +// REVERSE: warning: comdat group with signature 'func' in '{{.*}}1.o' has 2 section(s) while group in '{{.*}}0.o' has 1 section(s) + + .global func + .section .data.func,"awG",@progbits,func,comdat +func: + .word 7 + + .global func2 + .section .data.func2,"awG",@progbits,func,comdat +func2: + .word 7 diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-section-contents.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-section-contents.s new file mode 100644 index 0000000000000..2313921021ecc --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-section-contents.s @@ -0,0 +1,12 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/func.s -o %t0.o +// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --warn-mismatch-sections-in-comdat-groups 2>&1 | FileCheck %s +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --warn-mismatch-sections-in-comdat-groups 2>&1 | FileCheck %s --check-prefix=REVERSE + +// CHECK: warning: section '.data.func' for comdat group with signature 'func' has different contents between '{{.*}}1.o' and '{{.*}}0.o' +// REVERSE: warning: section '.data.func' for comdat group with signature 'func' has different contents between '{{.*}}0.o' and '{{.*}}1.o' + + .global func + .section .data.func,"awG",@progbits,func,comdat +func: + .word 6 diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-section-names.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-section-names.s new file mode 100644 index 0000000000000..d21c33354dc9a --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/different-section-names.s @@ -0,0 +1,12 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/func.s -o %t0.o +// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --warn-mismatch-sections-in-comdat-groups 2>&1 | FileCheck %s +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --warn-mismatch-sections-in-comdat-groups 2>&1 | FileCheck %s --check-prefix=REVERSE + +// CHECK: warning: comdat group with signature 'func' in '{{.*}}0.o' does not have section '.data.func2' which is part of comdat group in '{{.*}}1.o' +// REVERSE: warning: comdat group with signature 'func' in '{{.*}}1.o' does not have section '.data.func' which is part of comdat group in '{{.*}}0.o' + + .global func + .section .data.func2,"awG",@progbits,func,comdat +func: + .word 7 diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/disabled-warning.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/disabled-warning.s new file mode 100644 index 0000000000000..09ebdc798ef69 --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/disabled-warning.s @@ -0,0 +1,13 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/func.s -o %t0.o +// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --fatal-warnings +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --fatal-warnings +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --fatal-warnings --no-warn-mismatch-sections-in-comdat-groups +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --fatal-warnings --no-warn-mismatch-sections-in-comdat-groups + +// No error since the warning is disabled, despite the mismatch. + + .global func + .section .data.func,"awG",@progbits,func,comdat +func: + .word 6 diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/ignore-section.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/ignore-section.s new file mode 100644 index 0000000000000..a5a59f447799d --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/ignore-section.s @@ -0,0 +1,9 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/func.s -o %t0.o +// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --fatal-warnings --warn-mismatch-sections-in-comdat-groups --warn-mismatch-sections-in-comdat-groups-exclude=.data.f* +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --fatal-warnings --warn-mismatch-sections-in-comdat-groups --warn-mismatch-sections-in-comdat-groups-exclude=.data.f* + + .global func + .section .data.func,"awG",@progbits,func,comdat +func: + .word 6 diff --git a/lld/test/ELF/mismatch-sections-in-comdat-group-warning/no-warning.s b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/no-warning.s new file mode 100644 index 0000000000000..f80ea070e358a --- /dev/null +++ b/lld/test/ELF/mismatch-sections-in-comdat-group-warning/no-warning.s @@ -0,0 +1,11 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/func.s -o %t0.o +// RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t1.o +// RUN: ld.lld -shared %t0.o %t1.o -o /dev/null --fatal-warnings --warn-mismatch-sections-in-comdat-groups +// RUN: ld.lld -shared %t1.o %t0.o -o /dev/null --fatal-warnings --warn-mismatch-sections-in-comdat-groups + +// No error since the sections in the comdat groups for `func` in both object files are the same. + + .global func + .section .data.func,"awG",@progbits,func,comdat +func: + .word 7