Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -279,10 +279,12 @@ typedef typename llvm::object::ELFFile::uintX_t uintX_t; OutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags); void addSection(InputSectionBase *C) override; - void sortByPriority(); + void sortInitFini(); + void sortCtorsDtors(); void writeTo(uint8_t *Buf) override; private: + void reassignOffsets(); std::vector *> Sections; }; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -763,10 +763,25 @@ return V; } +// This function is called after we sort input sections +// to update their offsets. +template void OutputSection::reassignOffsets() { + uintX_t Off = 0; + for (InputSection *S : Sections) { + Off = alignTo(Off, S->getAlign()); + S->OutSecOff = Off; + Off += S->getSize(); + } + this->Header.sh_size = Off; +} + // Sorts input sections by section name suffixes, so that .foo.N comes // before .foo.M if N < M. Used to sort .{init,fini}_array.N sections. +// We want to keep the original order if the priorities are the same +// because the compiler keeps the original initialization order in a +// translation unit and we need to respect that. // For more detail, read the section of the GCC's manual about init_priority. -template void OutputSection::sortByPriority() { +template void OutputSection::sortInitFini() { // Sort sections by priority. typedef std::pair *> Pair; auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; }; @@ -778,15 +793,59 @@ Sections.clear(); for (Pair &P : V) Sections.push_back(P.second); + reassignOffsets(); +} - // Reassign section addresses. - uintX_t Off = 0; - for (InputSection *S : Sections) { - Off = alignTo(Off, S->getAlign()); - S->OutSecOff = Off; - Off += S->getSize(); - } - this->Header.sh_size = Off; +// Returns true if S matches /crtbegin.?\.o$/. +static bool isCrtbegin(StringRef S) { + if (S.endswith("crtbegin.o")) + return true; + // Drop ?.o from end and check if it ends with "crtbegin". + if (!S.endswith(".o") || S.size() < 3) + return false; + return S.drop_back(3).endswith("crtbegin"); +} + +// .ctors and .dtors are sorted by this priority from highest to lowest. +// +// 1. The section was contained in a CRT file. (crtbegin contains +// some sentinel value in its .ctors and .dtors so that the runtime +// can find the beginning of the sections.) +// +// 2. The section has an optional priority value in the form of ".ctors.N" +// or ".dtors.N" where N is a number. Unlike .{init,fini}_array, +// they are compared as string rather than number. +// +// 3. The section is just ".ctors" or ".dtors". +// +// In an ideal world, we don't need this function because .init_array and +// .ctors are duplicate features (and .init_array is newer.) However, there +// are too many real-world use cases of .ctors, so we had no choice to +// support that with this rather ad-hoc semantics. +template +static bool compCtors(const InputSection *A, + const InputSection *B) { + bool CrtA = isCrtbegin(A->getFile()->getName()); + bool CrtB = isCrtbegin(B->getFile()->getName()); + if (CrtA != CrtB) + return CrtA; + StringRef X = A->getSectionName(); + StringRef Y = B->getSectionName(); + assert(X.startswith(".ctors") || X.startswith(".dtors")); + assert(Y.startswith(".ctors") || Y.startswith(".dtors")); + X = X.substr(6); + Y = Y.substr(6); + if (X.empty() || Y.empty()) + return Y.empty(); + return X < Y; +} + +// Sorts input sections by the special rules for .ctors and .dtors. +// Unfortunately the rules are different from the one for .{init,fini}_array. +// Read the comment above. +template void OutputSection::sortCtorsDtors() { + std::stable_sort(Sections.begin(), Sections.end(), compCtors); + reassignOffsets(); } // Returns a VA which a relocatin RI refers to. Used only for local symbols. Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -911,9 +911,15 @@ // Sort input sections by section name suffixes for // __attribute__((init_priority(N))). -template static void sortByPriority(OutputSectionBase *S) { +template static void sortInitFini(OutputSectionBase *S) { if (S) - reinterpret_cast *>(S)->sortByPriority(); + reinterpret_cast *>(S)->sortInitFini(); +} + +// Sort input sections by the special rule for .ctors and .dtors. +template static void sortCtorsDtors(OutputSectionBase *S) { + if (S) + reinterpret_cast *>(S)->sortCtorsDtors(); } // Create output section objects and add them to OutputSections. @@ -964,10 +970,10 @@ Factory.lookup(".fini_array", SHT_FINI_ARRAY, SHF_WRITE | SHF_ALLOC); // Sort section contents for __attribute__((init_priority(N)). - sortByPriority(Out::Dynamic->InitArraySec); - sortByPriority(Out::Dynamic->FiniArraySec); - sortByPriority(Factory.lookup(".ctors", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC)); - sortByPriority(Factory.lookup(".dtors", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC)); + sortInitFini(Out::Dynamic->InitArraySec); + sortInitFini(Out::Dynamic->FiniArraySec); + sortCtorsDtors(Factory.lookup(".ctors", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC)); + sortCtorsDtors(Factory.lookup(".dtors", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC)); // The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop // symbols for sections, so that the runtime can get the start and end Index: test/ELF/Inputs/ctors_dtors_priority.s =================================================================== --- /dev/null +++ test/ELF/Inputs/ctors_dtors_priority.s @@ -0,0 +1,5 @@ +.section .ctors, "aw", @progbits + .byte 0xAA + +.section .dtors, "aw", @progbits + .byte 0xBB Index: test/ELF/ctors_dtors_priority.s =================================================================== --- test/ELF/ctors_dtors_priority.s +++ test/ELF/ctors_dtors_priority.s @@ -1,5 +1,8 @@ -// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t -// RUN: ld.lld %t -o %t.exe +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1 +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +// RUN: %p/Inputs/ctors_dtors_priority.s -o %t2 +// RUN: cp %t2 %t-crtbegin.o +// RUN: ld.lld %t1 %t2 %t-crtbegin.o -o %t.exe // RUN: llvm-objdump -s %t.exe | FileCheck %s // REQUIRES: x86 @@ -12,7 +15,7 @@ .byte 1 .section .ctors.100, "aw", @progbits .long 2 -.section .ctors.5, "aw", @progbits +.section .ctors.005, "aw", @progbits .byte 3 .section .ctors, "aw", @progbits .byte 4 @@ -24,7 +27,7 @@ .byte 0x11 .section .dtors.100, "aw", @progbits .long 0x12 -.section .dtors.5, "aw", @progbits +.section .dtors.005, "aw", @progbits .byte 0x13 .section .dtors, "aw", @progbits .byte 0x14 @@ -32,6 +35,6 @@ .byte 0x15 // CHECK: Contents of section .ctors: -// CHECK-NEXT: 03020000 00000000 010405 +// CHECK-NEXT: aa030200 0000aa00 010405 // CHECK: Contents of section .dtors: -// CHECK-NEXT: 13120000 00000000 111415 +// CHECK-NEXT: bb131200 0000bb00 111415