Index: lld/trunk/ELF/Arch/PPC64.cpp =================================================================== --- lld/trunk/ELF/Arch/PPC64.cpp +++ lld/trunk/ELF/Arch/PPC64.cpp @@ -98,6 +98,15 @@ return 0; } +bool elf::isPPC64SmallCodeModelReloc(RelType Type) { + // List is not yet complete, at the very least the got based tls related + // relocations need to be added, and we need to determine how the section + // sorting interacts with the thread pointer and dynamic thread pointer + // relative tls relocations. + return Type == R_PPC64_GOT16 || Type == R_PPC64_TOC16 || + Type == R_PPC64_TOC16_DS; +} + namespace { class PPC64 final : public TargetInfo { public: Index: lld/trunk/ELF/InputFiles.h =================================================================== --- lld/trunk/ELF/InputFiles.h +++ lld/trunk/ELF/InputFiles.h @@ -113,6 +113,16 @@ // True if this is an argument for --just-symbols. Usually false. bool JustSymbols = false; + // On PPC64 we need to keep track of which files contain small code model + // relocations. To minimize the chance of a relocation overflow files that do + // contain small code model relocations should have their .toc sections sorted + // closer to the .got section than files that do not contain any small code + // model relocations. Thats because the toc-pointer is defined to point at + // .got + 0x8000 and the instructions used with small code model relocations + // support immediates in the range [-0x8000, 0x7FFC], making the addressable + // range relative to the toc pointer [.got, .got + 0xFFFC]. + bool PPC64SmallCodeModelRelocs = false; + // GroupId is used for --warn-backrefs which is an optional error // checking feature. All files within the same --{start,end}-group or // --{start,end}-lib get the same group ID. Otherwise, each file gets a new Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -1000,6 +1000,9 @@ if (isRelExprOneOf(Expr)) return; + if (Config->EMachine == EM_PPC64 && isPPC64SmallCodeModelReloc(Type)) + Sec.File->PPC64SmallCodeModelRelocs = true; + // Strenghten or relax relocations. // // GNU ifunc symbols must be accessed via PLT because their addresses Index: lld/trunk/ELF/Target.h =================================================================== --- lld/trunk/ELF/Target.h +++ lld/trunk/ELF/Target.h @@ -176,6 +176,8 @@ // to the local entry-point. unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther); +bool isPPC64SmallCodeModelReloc(RelType Type); + uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t Expr); Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -1250,6 +1250,24 @@ if (Name == ".init" || Name == ".fini") return; + // .toc is allocated just after .got and is accessed using GOT-relative + // relocations. Object files compiled with small code model have an + // addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations. + // To reduce the risk of relocation overflow, .toc contents are sorted so that + // sections having smaller relocation offsets are at beginning of .toc + if (Config->EMachine == EM_PPC64 && Name == ".toc") { + if (Script->HasSectionsCommand) + return; + assert(Sec->SectionCommands.size() == 1); + auto *ISD = cast(Sec->SectionCommands[0]); + std::stable_sort(ISD->Sections.begin(), ISD->Sections.end(), + [](const InputSection *A, const InputSection *B) -> bool { + return A->File->PPC64SmallCodeModelRelocs && + !B->File->PPC64SmallCodeModelRelocs; + }); + return; + } + // Sort input sections by priority using the list provided // by --symbol-ordering-file. if (!Order.empty()) Index: lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input2.s =================================================================== --- lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input2.s +++ lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input2.s @@ -0,0 +1,23 @@ + .text + + .global set + .type set,@function +set: +.Lgep: + addis 2, 12, .TOC.-.Lgep@ha + addi 2, 2, .TOC.-.Lgep@l +.Llep: + .localentry set, .Llep-.Lgep + addis 5, 2, .LC0@toc@ha + addis 6, 2, .LC1@toc@ha + ld 5, .LC0@toc@l(5) + ld 6, .LC1@toc@l(6) + stw 3, 0(5) + stw 4, 0(6) + blr + + .section .toc,"aw",@progbits +.LC0: + .tc c[TC],c +.LC1: + .tc d[TC],d Index: lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input3.s =================================================================== --- lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input3.s +++ lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input3.s @@ -0,0 +1,41 @@ + .text + .global getA + .type getA,@function +getA: +.LgepA: + addis 2, 12, .TOC.-.LgepA@ha + addi 2, 2, .TOC.-.LgepA@l +.LlepA: + .localentry getA, .LlepA-.LgepA + ld 3, .LC0@toc(2) + lwa 3, 0(3) + blr + + .global getB + .type getB,@function +getB: +.LgepB: + addis 2, 12, .TOC.-.LgepB@ha + addi 2, 2, .TOC.-.LgepB@l +.LlepB: + .localentry getB, .LlepB-.LgepB + ld 3, .LC1@toc(2) + lwa 3, 0(3) + blr + + .section .toc,"aw",@progbits +.LC0: + .tc a[TC],a +.LConst1: + .quad 0xa +.LC1: + .tc b[TC],b +.Lconst2: + .quad 0xaabbccddeeff + + .type b,@object + .data + .global b +b: + .long 22 + .size b, 4 Index: lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input4.s =================================================================== --- lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input4.s +++ lld/trunk/test/ELF/Inputs/ppc64-sort-small-cm-relocs-input4.s @@ -0,0 +1,18 @@ + .text + .global getRodata + .type getRodata,@function +getRodata: +.Lgep: + addis 2, 12, .TOC.-.Lgep@ha + addi 2, 2, .TOC.-.Lgep@l +.Llep: + .localentry getRodata, .Llep-.Lgep + lwa 3, .LC0@toc(2) + blr + + .section .rodata,"aMS",@progbits,8 + .quad _start + + .section .toc,"aw",@progbits +.LC0: + .tc .rodata[TC], .rodata Index: lld/trunk/test/ELF/ppc64-sort-small-cm-relocs.s =================================================================== --- lld/trunk/test/ELF/ppc64-sort-small-cm-relocs.s +++ lld/trunk/test/ELF/ppc64-sort-small-cm-relocs.s @@ -0,0 +1,108 @@ +# REQUIRES: ppc +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-sort-small-cm-relocs-input2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-sort-small-cm-relocs-input3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-sort-small-cm-relocs-input4.s -o %t4.o + +# RUN: ld.lld %t1.o %t2.o %t3.o %t4.o -o %t -Map=%t.map +# RUN: FileCheck %s < %t.map + +# Test an alternate link order. +# RUN: ld.lld %t2.o %t3.o %t4.o %t1.o -o %t -Map=%t.map +# RUN: FileCheck %s -check-prefix=ALTERNATE < %t.map + +# If a linker script has a sections command then allow that to override the +# default sorting behavior. +# RUN: echo "SECTIONS { \ +# RUN: .toc : { \ +# RUN: */ppc64-sort-small-cm-relocs.s.tmp4.o(.toc*) \ +# RUN: */ppc64-sort-small-cm-relocs.s.tmp1.o(.toc*) \ +# RUN: *(.toc*) \ +# RUN: } \ +# RUN: } " > %t.script +# RUN: ld.lld %t1.o %t2.o %t3.o %t4.o -o %t -script %t.script -Map=%t.map +# RUN: FileCheck %s -check-prefix=SEC-CMD < %t.map + +# RUN: echo "SECTIONS { .text : {*(.text*)} } " > %t.script +# RUN: ld.lld %t1.o %t2.o %t3.o %t4.o -o %t -script %t.script -Map=%t.map +# RUN: FileCheck %s -check-prefix=SEC-CMD2 < %t.map + +# Default sort if the linker script does not have a sections command. +# RUN: echo "" > %t.script +# RUN: ld.lld %t1.o %t2.o %t3.o %t4.o -o %t -script %t.script -Map=%t.map +# RUN: FileCheck %s -check-prefix=NOSEC < %t.map + .text + + .global _start + .type _start,@function +_start: + li 3, 55 + blr + + .type a,@object + .data + .global a +a: + .long 10 + .size a, 4 + + .type c,@object + .data + .global c +c: + .long 55 + .size c, 4 + + .type d,@object + .global d +d: + .long 33 + .size d, 4 + + # .toc section contains only some constants. + .section .toc,"aw",@progbits + .quad 0xa1a1a1a1a1a1a1a1 + .quad 0xb2b2b2b2b2b2b2b2 + +# Input files tmp3.o and tmp4.o contain small code model relocs. + +# CHECK: .got +# CHECK-NEXT: :(.got) +# CHECK-NEXT: .toc +# CHECK-NEXT: {{.*}}3.o:(.toc) +# CHECK-NEXT: {{.*}}4.o:(.toc) +# CHECK-NEXT: {{.*}}1.o:(.toc) +# CHECK-NEXT: {{.*}}2.o:(.toc) + +# ALTERNATE: .got +# ALTERNATE-NEXT: :(.got) +# ALTERNATE-NEXT: .toc +# ALTERNATE-NEXT: {{.*}}3.o:(.toc) +# ALTERNATE-NEXT: {{.*}}4.o:(.toc) +# ALTERNATE-NEXT: {{.*}}2.o:(.toc) +# ALTERNATE-NEXT: {{.*}}1.o:(.toc) + +# SEC-CMD: .got +# SEC-CMD-NEXT: :(.got) +# SEC-CMD-NEXT: .toc +# SEC-CMD-NEXT: {{.*}}4.o:(.toc) +# SEC-CMD-NEXT: {{.*}}1.o:(.toc) +# SEC-CMD-NEXT: {{.*}}2.o:(.toc) +# SEC-CMD-NEXT: {{.*}}3.o:(.toc) + +# SEC-CMD2: .got +# SEC-CMD2-NEXT: :(.got) +# SEC-CMD2-NEXT: .toc +# SEC-CMD2-NEXT: {{.*}}1.o:(.toc) +# SEC-CMD2-NEXT: {{.*}}2.o:(.toc) +# SEC-CMD2-NEXT: {{.*}}3.o:(.toc) +# SEC-CMD2-NEXT: {{.*}}4.o:(.toc) + +# NOSEC: .got +# NOSEC-NEXT: :(.got) +# NOSEC-NEXT: .toc +# NOSEC-NEXT: {{.*}}3.o:(.toc) +# NOSEC-NEXT: {{.*}}4.o:(.toc) +# NOSEC-NEXT: {{.*}}1.o:(.toc) +# NOSEC-NEXT: {{.*}}2.o:(.toc) +