[elf2] Combine adjacent compatible OutputSections in PT_LOADs.

llvm-svn: 247925
This commit is contained in:
Michael J. Spencer 2015-09-17 19:58:07 +00:00
parent d6e597c2be
commit 2f0082424f
5 changed files with 153 additions and 79 deletions

View File

@ -446,6 +446,57 @@ private:
SymbolTable &SymTab; SymbolTable &SymTab;
}; };
static uint32_t convertSectionFlagsToPHDRFlags(uint64_t Flags) {
uint32_t Ret = PF_R;
if (Flags & SHF_WRITE)
Ret |= PF_W;
if (Flags & SHF_EXECINSTR)
Ret |= PF_X;
return Ret;
}
template <bool Is64Bits>
class ProgramHeader {
public:
typedef typename std::conditional<Is64Bits, uint64_t, uint32_t>::type uintX_t;
typedef
typename std::conditional<Is64Bits, Elf64_Phdr, Elf32_Phdr>::type HeaderT;
ProgramHeader(uintX_t p_type, uintX_t p_flags) {
std::memset(&Header, 0, sizeof(HeaderT));
Header.p_type = p_type;
Header.p_flags = p_flags;
Header.p_align = PageSize;
}
void setValuesFromSection(OutputSectionBase<Is64Bits> &Sec) {
Header.p_flags = convertSectionFlagsToPHDRFlags(Sec.getFlags());
Header.p_offset = Sec.getFileOff();
Header.p_vaddr = Sec.getVA();
Header.p_paddr = Header.p_vaddr;
Header.p_filesz = Sec.getSize();
Header.p_memsz = Header.p_filesz;
Header.p_align = Sec.getAlign();
}
template <endianness E>
void writeHeaderTo(typename ELFFile<ELFType<E, Is64Bits>>::Elf_Phdr *PHDR) {
PHDR->p_type = Header.p_type;
PHDR->p_flags = Header.p_flags;
PHDR->p_offset = Header.p_offset;
PHDR->p_vaddr = Header.p_vaddr;
PHDR->p_paddr = Header.p_paddr;
PHDR->p_filesz = Header.p_filesz;
PHDR->p_memsz = Header.p_memsz;
PHDR->p_align = Header.p_align;
}
HeaderT Header;
bool Closed = false;
};
// The writer writes a SymbolTable result to a file. // The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer { template <class ELFT> class Writer {
public: public:
@ -487,14 +538,20 @@ private:
unsigned getVAStart() const { return Config->Shared ? 0 : VAStart; } unsigned getVAStart() const { return Config->Shared ? 0 : VAStart; }
std::unique_ptr<llvm::FileOutputBuffer> Buffer; std::unique_ptr<llvm::FileOutputBuffer> Buffer;
llvm::SpecificBumpPtrAllocator<OutputSection<ELFT>> CAlloc; llvm::SpecificBumpPtrAllocator<OutputSection<ELFT>> CAlloc;
std::vector<OutputSectionBase<ELFT::Is64Bits> *> OutputSections; std::vector<OutputSectionBase<ELFT::Is64Bits> *> OutputSections;
unsigned getNumSections() const { return OutputSections.size() + 1; } unsigned getNumSections() const { return OutputSections.size() + 1; }
llvm::BumpPtrAllocator PAlloc;
std::vector<ProgramHeader<ELFT::Is64Bits> *> PHDRs;
ProgramHeader<ELFT::Is64Bits> FileHeaderPHDR{PT_LOAD, PF_R};
ProgramHeader<ELFT::Is64Bits> InterpPHDR{PT_INTERP, 0};
ProgramHeader<ELFT::Is64Bits> DynamicPHDR{PT_DYNAMIC, 0};
uintX_t FileSize; uintX_t FileSize;
uintX_t ProgramHeaderOff; uintX_t ProgramHeaderOff;
uintX_t SectionHeaderOff; uintX_t SectionHeaderOff;
unsigned NumPhdrs;
StringTableSection<ELFT::Is64Bits> StrTabSec = { /*dynamic=*/false }; StringTableSection<ELFT::Is64Bits> StrTabSec = { /*dynamic=*/false };
StringTableSection<ELFT::Is64Bits> DynStrSec = { /*dynamic=*/true }; StringTableSection<ELFT::Is64Bits> DynStrSec = { /*dynamic=*/true };
@ -950,12 +1007,13 @@ template <class ELFT> void Writer<ELFT>::createSections() {
template <class ELFT> template <class ELFT>
static bool outputSectionHasPHDR(OutputSectionBase<ELFT::Is64Bits> *Sec) { static bool outputSectionHasPHDR(OutputSectionBase<ELFT::Is64Bits> *Sec) {
return (Sec->getSize() != 0) && (Sec->getFlags() & SHF_ALLOC); return Sec->getFlags() & SHF_ALLOC;
} }
// Visits all sections to assign incremental, non-overlapping RVAs and // Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets. // file offsets.
template <class ELFT> void Writer<ELFT>::assignAddresses() { template <class ELFT> void Writer<ELFT>::assignAddresses() {
assert(!OutputSections.empty() && "No output sections to layout!");
uintX_t VA = getVAStart(); uintX_t VA = getVAStart();
uintX_t FileOff = 0; uintX_t FileOff = 0;
@ -967,26 +1025,41 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
FileOff = RoundUpToAlignment(FileOff, PageSize); FileOff = RoundUpToAlignment(FileOff, PageSize);
VA = RoundUpToAlignment(VA, PageSize); VA = RoundUpToAlignment(VA, PageSize);
NumPhdrs = 0;
// Add a PHDR for PT_INTERP.
if (needsInterpSection()) if (needsInterpSection())
++NumPhdrs; PHDRs.push_back(&InterpPHDR);
// Add a PHDR for the elf header and program headers. Some dynamic linkers ProgramHeader<ELFT::Is64Bits> *LastPHDR = &FileHeaderPHDR;
// (musl at least) require them to be covered by a PT_LOAD. // Create a PHDR for the file header.
++NumPhdrs; PHDRs.push_back(&FileHeaderPHDR);
FileHeaderPHDR.Header.p_vaddr = getVAStart();
FileHeaderPHDR.Header.p_paddr = getVAStart();
FileHeaderPHDR.Header.p_align = PageSize;
for (OutputSectionBase<ELFT::Is64Bits> *Sec : OutputSections) { for (OutputSectionBase<ELFT::Is64Bits> *Sec : OutputSections) {
StrTabSec.add(Sec->getName()); StrTabSec.add(Sec->getName());
Sec->finalize(); Sec->finalize();
// Since each output section gets its own PHDR, align each output section to if (Sec->getSize()) {
// a page. uintX_t Flags = convertSectionFlagsToPHDRFlags(Sec->getFlags());
if (outputSectionHasPHDR<ELFT>(Sec)) { if (LastPHDR->Header.p_flags != Flags ||
++NumPhdrs; !outputSectionHasPHDR<ELFT>(Sec)) {
VA = RoundUpToAlignment(VA, PageSize); // Flags changed. End current PHDR and potentially create a new one.
FileOff = RoundUpToAlignment(FileOff, PageSize); if (!LastPHDR->Closed) {
LastPHDR->Header.p_filesz = FileOff - LastPHDR->Header.p_offset;
LastPHDR->Header.p_memsz = VA - LastPHDR->Header.p_vaddr;
LastPHDR->Closed = true;
}
if (outputSectionHasPHDR<ELFT>(Sec)) {
LastPHDR = new (PAlloc) ProgramHeader<ELFT::Is64Bits>(PT_LOAD, Flags);
PHDRs.push_back(LastPHDR);
VA = RoundUpToAlignment(VA, PageSize);
FileOff = RoundUpToAlignment(FileOff, PageSize);
LastPHDR->Header.p_offset = FileOff;
LastPHDR->Header.p_vaddr = VA;
LastPHDR->Header.p_paddr = VA;
}
}
} }
uintX_t Align = Sec->getAlign(); uintX_t Align = Sec->getAlign();
@ -1004,7 +1077,7 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
// Add a PHDR for the dynamic table. // Add a PHDR for the dynamic table.
if (needsDynamicSections()) if (needsDynamicSections())
++NumPhdrs; PHDRs.push_back(&DynamicPHDR);
FileOff += OffsetToAlignment(FileOff, ELFT::Is64Bits ? 8 : 4); FileOff += OffsetToAlignment(FileOff, ELFT::Is64Bits ? 8 : 4);
@ -1014,29 +1087,6 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
FileSize = FileOff; FileSize = FileOff;
} }
static uint32_t convertSectionFlagsToPHDRFlags(uint64_t Flags) {
uint32_t Ret = PF_R;
if (Flags & SHF_WRITE)
Ret |= PF_W;
if (Flags & SHF_EXECINSTR)
Ret |= PF_X;
return Ret;
}
template <class ELFT>
static void setValuesFromSection(typename ELFFile<ELFT>::Elf_Phdr &P,
OutputSectionBase<ELFT::Is64Bits> &S) {
P.p_flags = convertSectionFlagsToPHDRFlags(S.getFlags());
P.p_offset = S.getFileOff();
P.p_vaddr = S.getVA();
P.p_paddr = P.p_vaddr;
P.p_filesz = S.getSize();
P.p_memsz = P.p_filesz;
P.p_align = S.getAlign();
}
template <class ELFT> void Writer<ELFT>::writeHeader() { template <class ELFT> void Writer<ELFT>::writeHeader() {
uint8_t *Buf = Buffer->getBufferStart(); uint8_t *Buf = Buffer->getBufferStart();
auto *EHdr = reinterpret_cast<Elf_Ehdr *>(Buf); auto *EHdr = reinterpret_cast<Elf_Ehdr *>(Buf);
@ -1065,46 +1115,24 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
EHdr->e_shoff = SectionHeaderOff; EHdr->e_shoff = SectionHeaderOff;
EHdr->e_ehsize = sizeof(Elf_Ehdr); EHdr->e_ehsize = sizeof(Elf_Ehdr);
EHdr->e_phentsize = sizeof(Elf_Phdr); EHdr->e_phentsize = sizeof(Elf_Phdr);
EHdr->e_phnum = NumPhdrs; EHdr->e_phnum = PHDRs.size();
EHdr->e_shentsize = sizeof(Elf_Shdr); EHdr->e_shentsize = sizeof(Elf_Shdr);
EHdr->e_shnum = getNumSections(); EHdr->e_shnum = getNumSections();
EHdr->e_shstrndx = StrTabSec.getSectionIndex(); EHdr->e_shstrndx = StrTabSec.getSectionIndex();
// If nothing was merged into the file header PT_LOAD, set the size correctly.
if (FileHeaderPHDR.Header.p_filesz == PageSize)
FileHeaderPHDR.Header.p_filesz = FileHeaderPHDR.Header.p_memsz =
sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * PHDRs.size();
if (needsInterpSection())
InterpPHDR.setValuesFromSection(InterpSec);
if (needsDynamicSections())
DynamicPHDR.setValuesFromSection(DynamicSec);
auto PHdrs = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff); auto PHdrs = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
if (needsInterpSection()) { for (ProgramHeader<ELFT::Is64Bits> *PHDR : PHDRs)
PHdrs->p_type = PT_INTERP; PHDR->template writeHeaderTo<ELFT::TargetEndianness>(PHdrs++);
setValuesFromSection<ELFT>(*PHdrs, InterpSec);
++PHdrs;
}
PHdrs->p_type = PT_LOAD;
PHdrs->p_flags = PF_R;
PHdrs->p_offset = 0;
PHdrs->p_vaddr = getVAStart();
PHdrs->p_paddr = PHdrs->p_vaddr;
PHdrs->p_filesz = ProgramHeaderOff + NumPhdrs * sizeof(Elf_Phdr);
PHdrs->p_memsz = PHdrs->p_filesz;
PHdrs->p_align = PageSize;
++PHdrs;
for (OutputSectionBase<ELFT::Is64Bits> *Sec : OutputSections) {
if (!outputSectionHasPHDR<ELFT>(Sec))
continue;
PHdrs->p_type = PT_LOAD;
PHdrs->p_flags = convertSectionFlagsToPHDRFlags(Sec->getFlags());
PHdrs->p_offset = Sec->getFileOff();
PHdrs->p_vaddr = Sec->getVA();
PHdrs->p_paddr = PHdrs->p_vaddr;
PHdrs->p_filesz = Sec->getType() == SHT_NOBITS ? 0 : Sec->getSize();
PHdrs->p_memsz = Sec->getSize();
PHdrs->p_align = PageSize;
++PHdrs;
}
if (needsDynamicSections()) {
PHdrs->p_type = PT_DYNAMIC;
setValuesFromSection<ELFT>(*PHdrs, DynamicSec);
}
auto SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff); auto SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff);
// First entry is null. // First entry is null.

View File

@ -22,7 +22,7 @@
// CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ] // CHECK-NEXT: ]
// CHECK-NEXT: Address: [[RELAADDR:.*]] // CHECK-NEXT: Address: [[RELAADDR:.*]]
// CHECK-NEXT: Offset: 0x6000 // CHECK-NEXT: Offset: 0x4058
// CHECK-NEXT: Size: [[RELASIZE:.*]] // CHECK-NEXT: Size: [[RELASIZE:.*]]
// CHECK-NEXT: Link: 4 // CHECK-NEXT: Link: 4
// CHECK-NEXT: Info: 0 // CHECK-NEXT: Info: 0

View File

@ -0,0 +1,46 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: lld -flavor gnu2 %t -o %t2
# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
# REQUIRES: x86
# Check that different output sections with the same flags are merged into a
# single Read/Write PT_LOAD.
.section .r,"a"
.globl _start
_start:
.quad 0
.section .a,"aw"
.quad 1
.section .b,"aw"
.quad 2
# CHECK: ProgramHeaders [
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_LOAD
# CHECK-NEXT: Offset: 0x0
# CHECK-NEXT: VirtualAddress:
# CHECK-NEXT: PhysicalAddress:
# CHECK-NEXT: FileSize:
# CHECK-NEXT: MemSize:
# CHECK-NEXT: Flags [
# CHECK-NEXT: PF_R
# CHECK-NEXT: ]
# CHECK-NEXT: Alignment:
# CHECK-NEXT: }
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_LOAD
# CHECK-NEXT: Offset:
# CHECK-NEXT: VirtualAddress:
# CHECK-NEXT: PhysicalAddress:
# CHECK-NEXT: FileSize: 16
# CHECK-NEXT: MemSize: 16
# CHECK-NEXT: Flags [
# CHECK-NEXT: PF_R
# CHECK-NEXT: PF_W
# CHECK-NEXT: ]
# CHECK-NEXT: Alignment:
# CHECK-NEXT: }
# CHECK-NEXT: ]

View File

@ -33,7 +33,7 @@ R_X86_64_32:
// constants in hex. // constants in hex.
// CHECK: Disassembly of section .text2: // CHECK: Disassembly of section .text2:
// CHECK-NEXT: R_X86_64_32: // CHECK-NEXT: R_X86_64_32:
// CHECK-NEXT: 12000: {{.*}} movl $73728, %edx // CHECK-NEXT: 1100c: {{.*}} movl $69644, %edx
.section .R_X86_64_32S,"ax",@progbits .section .R_X86_64_32S,"ax",@progbits
.global R_X86_64_32S .global R_X86_64_32S
@ -50,4 +50,4 @@ R_X86_64_64:
.quad R_X86_64_64 .quad R_X86_64_64
// CHECK: Contents of section .R_X86_64_64: // CHECK: Contents of section .R_X86_64_64:
// CHECK-NEXT: 14000 00400100 00000000 // CHECK-NEXT: 12000 00200100 00000000

View File

@ -12,7 +12,7 @@
// SO-NEXT: Flags [ // SO-NEXT: Flags [
// SO-NEXT: ] // SO-NEXT: ]
// SO-NEXT: Address: // SO-NEXT: Address:
// SO-NEXT: Offset: 0x400C // SO-NEXT: Offset: 0x300C
// SO-NEXT: Size: // SO-NEXT: Size:
// SO-NEXT: Link: // SO-NEXT: Link:
// SO-NEXT: Info: // SO-NEXT: Info:
@ -44,7 +44,7 @@
// CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ] // CHECK-NEXT: ]
// CHECK-NEXT: Address: [[DYNSYMADDR:.*]] // CHECK-NEXT: Address: [[DYNSYMADDR:.*]]
// CHECK-NEXT: Offset: 0x3000 // CHECK-NEXT: Offset: 0x201C
// CHECK-NEXT: Size: 48 // CHECK-NEXT: Size: 48
// CHECK-NEXT: Link: [[DYNSTR:.*]] // CHECK-NEXT: Link: [[DYNSTR:.*]]
// CHECK-NEXT: Info: 1 // CHECK-NEXT: Info: 1