From 5967c973238605101632e320ea1f7990a6cd9b27 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Mon, 19 Dec 2016 21:21:07 +0000 Subject: [PATCH] Fix corner cases of setting the section address. This handles all the corner cases if setting a section address: - If the address is too low, we cannot allocate the program headers. - If the load address is lowered, we have to do that before finalize This also shares some code with the linker script since it was already hitting similar cases. This is used by the freebsd boot loader. It is not clear if we need to support this with a non binary output, but it is not as bad as I was expecting. llvm-svn: 290136 --- lld/ELF/LinkerScript.cpp | 13 +---- lld/ELF/Writer.cpp | 86 +++++++++++++++++++++++++++------ lld/ELF/Writer.h | 4 ++ lld/test/ELF/ttext-tdata-tbss.s | 63 ++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 lld/test/ELF/ttext-tdata-tbss.s diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index b99ddd7dfd68..53daaa6743d9 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -748,19 +748,10 @@ void LinkerScript::assignAddresses(std::vector &Phdrs) { } uintX_t HeaderSize = getHeaderSize(); - auto FirstPTLoad = - std::find_if(Phdrs.begin(), Phdrs.end(), - [](const PhdrEntry &E) { return E.p_type == PT_LOAD; }); - if (FirstPTLoad == Phdrs.end()) - return; - // If the linker script doesn't have PHDRS, add ElfHeader and ProgramHeaders // now that we know we have space. - if (HeaderSize <= MinVA && !hasPhdrsCommands()) { - FirstPTLoad->First = Out::ElfHeader; - if (!FirstPTLoad->Last) - FirstPTLoad->Last = Out::ProgramHeaders; - } + if (HeaderSize <= MinVA && !hasPhdrsCommands()) + allocateHeaders(Phdrs, *OutputSections); // ELF and Program headers need to be right before the first section in // memory. Set their addresses accordingly. diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index be3a070d082d..9dfb907c3c11 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -89,6 +89,7 @@ private: uintX_t FileSize; uintX_t SectionHeaderOff; + bool AllocateHeader = true; }; } // anonymous namespace @@ -192,15 +193,6 @@ template void Writer::run() { if (Config->Relocatable) { assignFileOffsets(); } else { - // Binary output does not have PHDRS. - if (!Config->OFormatBinary) { - Phdrs = Script::X->hasPhdrsCommands() - ? Script::X->createPhdrs() - : createPhdrs(); - addPtArmExid(Phdrs); - fixHeaders(); - } - if (ScriptConfig->HasSections) { Script::X->assignAddresses(Phdrs); } else { @@ -533,6 +525,17 @@ static bool compareSectionsNonScript(const OutputSectionBase *A, if (!AIsAlloc) return false; + // We want to put section specified by -T option first, so we + // can start assigning VA starting from them later. + auto AAddrSetI = Config->SectionStartMap.find(A->getName()); + auto BAddrSetI = Config->SectionStartMap.find(B->getName()); + bool AHasAddrSet = AAddrSetI != Config->SectionStartMap.end(); + bool BHasAddrSet = BAddrSetI != Config->SectionStartMap.end(); + if (AHasAddrSet != BHasAddrSet) + return AHasAddrSet; + if (AHasAddrSet) + return AAddrSetI->second < BAddrSetI->second; + // We want the read only sections first so that they go in the PT_LOAD // covering the program headers at the start of the file. bool AIsWritable = A->Flags & SHF_WRITE; @@ -1038,6 +1041,16 @@ template void Writer::finalizeSections() { Sec->ShName = In::ShStrTab->addString(Sec->getName()); } + // Binary and relocatable output does not have PHDRS. + // The headers have to be created before finalize as that can influence the + // image base and the dynamic section on mips includes the image base. + if (!Config->Relocatable && !Config->OFormatBinary) { + Phdrs = Script::X->hasPhdrsCommands() ? Script::X->createPhdrs() + : createPhdrs(); + addPtArmExid(Phdrs); + fixHeaders(); + } + // Fill other section headers. The dynamic table is finalized // at the end because some tags like RELSZ depend on result // of finalizing other sections. @@ -1159,10 +1172,6 @@ template std::vector Writer::createPhdrs() { // Add the first PT_LOAD segment for regular output sections. uintX_t Flags = computeFlags(PF_R); PhdrEntry *Load = AddHdr(PT_LOAD, Flags); - if (!ScriptConfig->HasSections) { - Load->add(Out::ElfHeader); - Load->add(Out::ProgramHeaders); - } PhdrEntry TlsHdr(PT_TLS, PF_R); PhdrEntry RelRo(PT_GNU_RELRO, PF_R); @@ -1270,7 +1279,7 @@ void Writer::addPtArmExid(std::vector &Phdrs) { // have to be page aligned so that the dynamic linker can set the permissions. template void Writer::fixSectionAlignments() { for (const PhdrEntry &P : Phdrs) - if (P.p_type == PT_LOAD) + if (P.p_type == PT_LOAD && P.First) P.First->PageAlign = true; for (const PhdrEntry &P : Phdrs) { @@ -1288,6 +1297,23 @@ template void Writer::fixSectionAlignments() { } } +template +void elf::allocateHeaders(MutableArrayRef Phdrs, + ArrayRef OutputSections) { + auto FirstPTLoad = + std::find_if(Phdrs.begin(), Phdrs.end(), + [](const PhdrEntry &E) { return E.p_type == PT_LOAD; }); + if (FirstPTLoad == Phdrs.end()) + return; + if (FirstPTLoad->First) + for (OutputSectionBase *Sec : OutputSections) + if (Sec->FirstInPtLoad == FirstPTLoad->First) + Sec->FirstInPtLoad = Out::ElfHeader; + FirstPTLoad->First = Out::ElfHeader; + if (!FirstPTLoad->Last) + FirstPTLoad->Last = Out::ProgramHeaders; +} + // We should set file offsets and VAs for elf header and program headers // sections. These are special, we do not include them into output sections // list, but have them to simplify the code. @@ -1296,6 +1322,25 @@ template void Writer::fixHeaders() { // If the script has SECTIONS, assignAddresses will compute the values. if (ScriptConfig->HasSections) return; + + uintX_t HeaderSize = getHeaderSize(); + // When -T
option is specified, lower the base to make room for those + // sections. + if (!Config->SectionStartMap.empty()) { + uint64_t Min = -1; + for (const auto &P : Config->SectionStartMap) + Min = std::min(Min, P.second); + if (HeaderSize < Min) + Min -= HeaderSize; + else + AllocateHeader = false; + if (Min < Config->ImageBase) + Config->ImageBase = alignDown(Min, Config->MaxPageSize); + } + + if (AllocateHeader) + allocateHeaders(Phdrs, OutputSections); + uintX_t BaseVA = Config->ImageBase; Out::ElfHeader->Addr = BaseVA; Out::ProgramHeaders->Addr = BaseVA + Out::ElfHeader->Size; @@ -1303,7 +1348,9 @@ template void Writer::fixHeaders() { // Assign VAs (addresses at run-time) to output sections. template void Writer::assignAddresses() { - uintX_t VA = Config->ImageBase + getHeaderSize(); + uintX_t VA = Config->ImageBase; + if (AllocateHeader) + VA += getHeaderSize(); uintX_t ThreadBssOffset = 0; for (OutputSectionBase *Sec : OutputSections) { uintX_t Alignment = Sec->Addralign; @@ -1682,6 +1729,15 @@ template void elf::writeResult(); template void elf::writeResult(); template void elf::writeResult(); +template void elf::allocateHeaders(MutableArrayRef, + ArrayRef); +template void elf::allocateHeaders(MutableArrayRef, + ArrayRef); +template void elf::allocateHeaders(MutableArrayRef, + ArrayRef); +template void elf::allocateHeaders(MutableArrayRef, + ArrayRef); + template bool elf::isRelroSection(const OutputSectionBase *); template bool elf::isRelroSection(const OutputSectionBase *); template bool elf::isRelroSection(const OutputSectionBase *); diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index 94d707ff4c57..96dd70b109d5 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -10,6 +10,7 @@ #ifndef LLD_ELF_WRITER_H #define LLD_ELF_WRITER_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include #include @@ -48,6 +49,9 @@ struct PhdrEntry { llvm::StringRef getOutputSectionName(llvm::StringRef Name); +template +void allocateHeaders(llvm::MutableArrayRef, + llvm::ArrayRef); template void reportDiscarded(InputSectionBase *IS); template uint32_t getMipsEFlags(); diff --git a/lld/test/ELF/ttext-tdata-tbss.s b/lld/test/ELF/ttext-tdata-tbss.s new file mode 100644 index 000000000000..c31c56e75692 --- /dev/null +++ b/lld/test/ELF/ttext-tdata-tbss.s @@ -0,0 +1,63 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o + +## Show what regular output gives to us. +# RUN: ld.lld %t.o -o %t1 +# RUN: llvm-readobj --elf-output-style=GNU -l -s %t1 | FileCheck %s +# CHECK: .rodata PROGBITS 0000000000200158 000158 000008 +# CHECK-NEXT: .text PROGBITS 0000000000201000 001000 000001 +# CHECK-NEXT: .aw PROGBITS 0000000000202000 002000 000008 +# CHECK-NEXT: .data PROGBITS 0000000000202008 002008 000008 +# CHECK-NEXT: .bss NOBITS 0000000000202010 002010 000008 +# CHECK: PHDR +# CHECK-NEXT: LOAD 0x000000 0x0000000000200000 + +## With .text at 0 there is no space to allocate the headers. +# RUN: ld.lld -Ttext 0x0 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t2 +# RUN: llvm-readobj --elf-output-style=GNU -l -s %t2 | FileCheck %s --check-prefix=USER1 +# USER1: .text PROGBITS 0000000000000000 001000 000001 +# USER1-NEXT: .data PROGBITS 0000000000004000 002000 000008 +# USER1-NEXT: .bss NOBITS 0000000000008000 002008 000008 +# USER1-NEXT: .rodata PROGBITS 0000000000009000 003000 000008 +# USER1-NEXT: .aw PROGBITS 000000000000a000 004000 000008 +# USER1: PHDR +# USER1-NEXT: LOAD 0x001000 0x0000000000000000 + +## With .text at 0x1000 there is space to allocate the headers. +# RUN: ld.lld -Ttext 0x1000 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t3 +# RUN: llvm-readobj --elf-output-style=GNU -l -s %t3 | FileCheck %s --check-prefix=USER2 +# USER2: .text PROGBITS 0000000000001000 001000 000001 +# USER2-NEXT: .data PROGBITS 0000000000004000 002000 000008 +# USER2-NEXT: .bss NOBITS 0000000000008000 002008 000008 +# USER2-NEXT: .rodata PROGBITS 0000000000009000 003000 000008 +# USER2-NEXT: .aw PROGBITS 000000000000a000 004000 000008 +# USER2: PHDR +# USER2-NEXT: LOAD 0x000000 0x0000000000000000 + +## With .text well above 200000 we don't need to change the image base +# RUN: ld.lld -Ttext 0x201000 %t.o -o %t4 +# RUN: llvm-readobj --elf-output-style=GNU -l -s %t4 | FileCheck %s --check-prefix=USER3 +# USER3: .text PROGBITS 0000000000201000 001000 000001 +# USER3-NEX: .rodata PROGBITS 0000000000202000 002000 000008 +# USER3-NEX: .aw PROGBITS 0000000000203000 003000 000008 +# USER3-NEX: .data PROGBITS 0000000000203008 003008 000008 +# USER3-NEX: .bss NOBITS 0000000000203010 003010 000008 +# USER3: PHDR +# USER3-NEXT: LOAD 0x000000 0x0000000000200000 + +.text +.globl _start +_start: + nop + +.section .rodata,"a" + .quad 0 + +.section .aw,"aw" + .quad 0 + +.section .data,"aw" + .quad 0 + +.section .bss,"",@nobits + .quad 0