Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1545,16 +1545,33 @@ // The ELF spec requires that all local symbols precede global symbols, so we // sort symbol entries in this function. (For .dynsym, we don't do that because // symbols for dynamic linking are inherently all globals.) +// +// Aside from above, we put local symbols in groups starting with the STT_FILE +// symbol. That is convenient for purpose of identifying where are local symbols +// coming from. void SymbolTableBaseSection::postThunkContents() { if (this->Type == SHT_DYNSYM) return; - // move all local symbols before global symbols. - auto It = std::stable_partition( + + // Move all local symbols before global symbols. + auto E = std::stable_partition( Symbols.begin(), Symbols.end(), [](const SymbolTableEntry &S) { return S.Sym->isLocal() || S.Sym->computeBinding() == STB_LOCAL; }); - size_t NumLocals = It - Symbols.begin(); + size_t NumLocals = E - Symbols.begin(); getParent()->Info = NumLocals + 1; + + // Assign the growing unique ID for each local symbol's file. + DenseMap FileIDs; + for (auto I = Symbols.begin(); I != E; ++I) + FileIDs.insert({I->Sym->File, FileIDs.size()}); + + // Sort the local symbols to group them by file. We do not need to care about + // the STT_FILE symbols, they are already naturally placed first in each group. + std::stable_sort(Symbols.begin(), E, + [&](const SymbolTableEntry &L, const SymbolTableEntry &R) { + return FileIDs[L.Sym->File] < FileIDs[R.Sym->File]; + }); } void SymbolTableBaseSection::addSymbol(Symbol *B) { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1663,15 +1663,15 @@ } while (Changed); } + // createThunks may have added local symbols to the static symbol table + applySynthetic({InX::SymTab}, + [](SyntheticSection *SS) { SS->postThunkContents(); }); + // Fill other section headers. The dynamic table is finalized // at the end because some tags like RELSZ depend on result // of finalizing other sections. for (OutputSection *Sec : OutputSections) Sec->finalize(); - - // createThunks may have added local symbols to the static symbol table - applySynthetic({InX::SymTab}, - [](SyntheticSection *SS) { SS->postThunkContents(); }); } // The linker is expected to define SECNAME_start and SECNAME_end Index: test/ELF/local-symbols-order.s =================================================================== --- test/ELF/local-symbols-order.s +++ test/ELF/local-symbols-order.s @@ -7,15 +7,16 @@ # RUN: ld.lld -o %t %t1.o %t2.o # RUN: llvm-readobj -symbols -elf-output-style=GNU %t | FileCheck %s -## Show the order of the local symbols produced currently by LLD. +## Check we sort local symbols to match the +## following order: file1, local1, hidden1, file2, local2, hidden2 ... # CHECK: Num: Value Size Type Bind Vis Ndx Name # CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND # CHECK-NEXT: 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS file1 # CHECK-NEXT: 2: 0000000000201000 0 NOTYPE LOCAL DEFAULT 1 foo1 -# CHECK-NEXT: 3: 0000000000000000 0 FILE LOCAL DEFAULT ABS file2 -# CHECK-NEXT: 4: 0000000000201000 0 NOTYPE LOCAL DEFAULT 1 foo2 -# CHECK-NEXT: 5: 0000000000201000 0 NOTYPE LOCAL HIDDEN 1 bar1 +# CHECK-NEXT: 3: 0000000000201000 0 NOTYPE LOCAL HIDDEN 1 bar1 +# CHECK-NEXT: 4: 0000000000000000 0 FILE LOCAL DEFAULT ABS file2 +# CHECK-NEXT: 5: 0000000000201000 0 NOTYPE LOCAL DEFAULT 1 foo2 # CHECK-NEXT: 6: 0000000000201000 0 NOTYPE LOCAL HIDDEN 1 bar2 foo1: Index: test/ELF/relocatable-comdat-multiple.s =================================================================== --- test/ELF/relocatable-comdat-multiple.s +++ test/ELF/relocatable-comdat-multiple.s @@ -21,7 +21,7 @@ # CHECK-NEXT: Name: .group # CHECK-NEXT: Index: 5 # CHECK-NEXT: Link: 8 -# CHECK-NEXT: Info: 2 +# CHECK-NEXT: Info: 6 # CHECK-NEXT: Type: COMDAT # CHECK-NEXT: Signature: bbb # CHECK-NEXT: Section(s) in group [