diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1618,9 +1618,10 @@ } static bool compareByFilePosition(InputSection *a, InputSection *b) { - InputSection *la = a->getLinkOrderDep(); - InputSection *lb = b->getLinkOrderDep(); - // SHF_LINK_ORDER sections with non-zero sh_link are ordered before others. + InputSection *la = a->flags & SHF_LINK_ORDER ? a->getLinkOrderDep() : nullptr; + InputSection *lb = b->flags & SHF_LINK_ORDER ? b->getLinkOrderDep() : nullptr; + // SHF_LINK_ORDER sections with non-zero sh_link are ordered before + // non-SHF_LINK_ORDER sections and SHF_LINK_ORDER sections with zero sh_link. if (!la || !lb) return la && !lb; OutputSection *aOut = la->getParent(); @@ -1642,44 +1643,34 @@ sec->type == SHT_ARM_EXIDX) continue; - // Link order may be distributed across several InputSectionDescriptions - // but sort must consider them all at once. + // Link order may be distributed across several InputSectionDescriptions. + // Sorting is performed separately. std::vector scriptSections; std::vector sections; - bool started = false, stopped = false; for (BaseCommand *base : sec->sectionCommands) { - if (auto *isd = dyn_cast(base)) { - for (InputSection *&isec : isd->sections) { - if (!(isec->flags & SHF_LINK_ORDER)) { - if (started) - stopped = true; - } else if (stopped) { - error(toString(isec) + ": SHF_LINK_ORDER sections in " + sec->name + - " are not contiguous"); - } else { - started = true; - - scriptSections.push_back(&isec); - sections.push_back(isec); - - InputSection *link = isec->getLinkOrderDep(); - if (link && !link->getParent()) - error(toString(isec) + ": sh_link points to discarded section " + - toString(link)); - } + auto *isd = dyn_cast(base); + if (!isd) + continue; + bool hasLinkOrder = false; + scriptSections.clear(); + sections.clear(); + for (InputSection *&isec : isd->sections) { + if (isec->flags & SHF_LINK_ORDER) { + InputSection *link = isec->getLinkOrderDep(); + if (link && !link->getParent()) + error(toString(isec) + ": sh_link points to discarded section " + + toString(link)); + hasLinkOrder = true; } - } else if (started) { - stopped = true; + scriptSections.push_back(&isec); + sections.push_back(isec); + } + if (hasLinkOrder && errorCount() == 0) { + llvm::stable_sort(sections, compareByFilePosition); + for (int i = 0, n = sections.size(); i != n; ++i) + *scriptSections[i] = sections[i]; } } - - if (errorCount()) - continue; - - llvm::stable_sort(sections, compareByFilePosition); - - for (int i = 0, n = sections.size(); i < n; ++i) - *scriptSections[i] = sections[i]; } } diff --git a/lld/test/ELF/linkerscript/linkorder.s b/lld/test/ELF/linkerscript/linkorder.s --- a/lld/test/ELF/linkerscript/linkorder.s +++ b/lld/test/ELF/linkerscript/linkorder.s @@ -2,7 +2,7 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o ## Contiguous SHF_LINK_ORDER sections. -# RUN: echo 'SECTIONS { .rodata : {BYTE(0) *(.rodata*) BYTE(3)} \ +# RUN: echo 'SECTIONS { .rodata : {BYTE(0) *(.rodata*) BYTE(4)} \ # RUN: .text : {*(.text.bar) *(.text.foo)} }' > %t.lds # RUN: ld.lld -T %t.lds %t.o -o %t # RUN: llvm-readelf -S -x .rodata -x .text %t | FileCheck %s @@ -10,11 +10,11 @@ # CHECK: [ 1] .rodata {{.*}} AL 3 # CHECK: [ 3] .text {{.*}} AX 0 # CHECK: Hex dump of section '.rodata': -# CHECK-NEXT: 00020103 +# CHECK-NEXT: 00030102 04 # CHECK: Hex dump of section '.text': # CHECK-NEXT: 0201 -# RUN: echo 'SECTIONS { .rodata : {BYTE(0) *(.rodata*) BYTE(3)} \ +# RUN: echo 'SECTIONS { .rodata : {BYTE(0) *(.rodata*) BYTE(4)} \ # RUN: .text : {*(.text.foo) *(.text.bar)} }' > %t1.lds # RUN: ld.lld -T %t1.lds %t.o -o %t1 # RUN: llvm-readelf -S -x .rodata -x .text %t1 | FileCheck --check-prefix=CHECK1 %s @@ -22,34 +22,46 @@ # CHECK1: [ 1] .rodata {{.*}} AL 3 # CHECK1: [ 3] .text {{.*}} AX 0 # CHECK1: Hex dump of section '.rodata': -# CHECK1-NEXT: 00010203 +# CHECK1-NEXT: 00010302 04 # CHECK1: Hex dump of section '.text': # CHECK1-NEXT: 0102 ## Adjacent input sections descriptions are contiguous. -## Orphan section .text.bar precedes .text.foo, so swap the order of .rodata.* +## Orphan section .text.bar precedes .text.foo. However, don't swap the order of .rodata.* +## because they are in different InputSectionDescriptions. # RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) *(.rodata.bar)} }' > %t2.lds # RUN: ld.lld -T %t2.lds %t.o -o %t2 # RUN: llvm-readelf -S -x .rodata %t2 | FileCheck --check-prefix=CHECK2 %s -# CHECK2: [ 1] .rodata {{.*}} AL 4 +# CHECK2: [ 1] .rodata {{.*}} AL 5 # CHECK2: [ 4] .text.bar {{.*}} AX 0 +# CHECK2-NEXT: [ 5] .text.foo {{.*}} AX 0 # CHECK2: Hex dump of section '.rodata': -# CHECK2-NEXT: 0201 +# CHECK2-NEXT: 010302 ## Non-contiguous SHF_LINK_ORDER sections, separated by a BYTE. -# RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) BYTE(0) *(.rodata.bar)} }' > %terr1.lds -# RUN: not ld.lld -T %terr1.lds %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s +# RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) BYTE(0) *(.rodata.bar)} }' > %t3.lds +# RUN: ld.lld -T %t3.lds %t.o -o %t3 +# RUN: llvm-readelf -S -x .rodata %t3 | FileCheck --check-prefix=CHECK3 %s + +# CHECK3: [ 1] .rodata {{.*}} AL 5 +# CHECK3: [ 4] .text.bar {{.*}} AX 0 +# CHECK3: [ 5] .text.foo {{.*}} AX 0 +# CHECK3: Hex dump of section '.rodata': +# CHECK3-NEXT: 01000302 ## Non-contiguous SHF_LINK_ORDER sections, separated by a non-SHF_LINK_ORDER section. -# RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) *(.text) *(.rodata.bar)} }' > %terr2.lds -# RUN: not ld.lld -T %terr2.lds %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s +# RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) *(.text) *(.rodata.bar)} }' > %t4.lds +# RUN: ld.lld -T %t4.lds %t.o -o %t4 +# RUN: llvm-readelf -x .rodata %t4 | FileCheck --check-prefix=CHECK4 %s -## Non-contiguous SHF_LINK_ORDER sections, separated by a symbol assignment. -# RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) a = .; *(.rodata.bar)} }' > %terr3.lds -# RUN: not ld.lld -T %terr3.lds %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s +# CHECK4: Hex dump of section '.rodata': +# CHECK4-NEXT: 01cccccc 0302 -# ERR: error: {{.*}}.o:(.rodata.bar): SHF_LINK_ORDER sections in .rodata are not contiguous +## Non-contiguous SHF_LINK_ORDER sections, separated by a symbol assignment. +# RUN: echo 'SECTIONS { .rodata : {*(.rodata.foo) a = .; *(.rodata.bar)} }' > %t5.lds +# RUN: ld.lld -T %t5.lds %t.o -o %t5 +# RUN: llvm-readelf -S -x .rodata %t5 | FileCheck --check-prefix=CHECK2 %s .global _start _start: @@ -63,5 +75,9 @@ .byte 1 .section .rodata.foo,"ao",@progbits,.text.foo .byte 1 -.section .rodata.bar,"ao",@progbits,.text.bar +## If the two .rodata.bar sections are in the same InputSectionDescription, +## 03 (sh_link!=0) will be ordered before 02 (sh_link=0). +.section .rodata.bar,"a",@progbits .byte 2 +.section .rodata.bar,"ao",@progbits,.text.bar +.byte 3 diff --git a/lld/test/ELF/linkorder-mixed.s b/lld/test/ELF/linkorder-mixed.s --- a/lld/test/ELF/linkorder-mixed.s +++ b/lld/test/ELF/linkorder-mixed.s @@ -14,9 +14,17 @@ # CHECK: Hex dump of section '.linkorder': # CHECK-NEXT: [[#%x,ADDR:]] 01020003 -## TODO Allow non-contiguous SHF_LINK_ORDER sections in an output section. # RUN: llvm-mc --filetype=obj -triple=x86_64 --defsym EXTRA=1 %s -o %t.o -# RUN: not ld.lld %t.o -o /dev/null +# RUN: ld.lld %t.o -o %t1 +# RUN: llvm-readelf -S -x .linkorder %t1 | FileCheck %s --check-prefix=CHECK1 + +# CHECK1: [Nr] Name {{.*}} Size ES Flg Lk Inf +# CHECK1-NEXT: [ 0] {{.*}} +# CHECK1-NEXT: [ 1] .linkorder {{.*}} 000005 00 AL 2 0 +# CHECK1-NEXT: [ 2] .text {{.*}} + +# CHECK1: Hex dump of section '.linkorder': +# CHECK1-NEXT: [[#%x,ADDR:]] 01020004 03 .section .text,"ax",@progbits,unique,0 .Ltext0: