From 22c76a5d79ad6ceb4bbf46b6b280dfd10483bade Mon Sep 17 00:00:00 2001 From: Shankar Easwaran Date: Sat, 8 Nov 2014 03:44:49 +0000 Subject: [PATCH] [ELF] Support --no-align-segments. lld generates an ELF by adhering to the ELF spec by aligning vma/fileoffset to a page boundary, but this becomes an issue when dealing with large pages. This adds support so that lld generated executables adheres to the ELF spec with the rule vma % p_align = offset % p_align. This is supported by the flag --no-align-segments. This could be the default in few targets like X86_64 to save space on disk. llvm-svn: 221571 --- .../lld/ReaderWriter/ELFLinkingContext.h | 5 + lld/lib/Driver/GnuLdDriver.cpp | 4 + lld/lib/Driver/GnuLdOptions.td | 3 + .../ReaderWriter/ELF/ELFLinkingContext.cpp | 3 +- lld/lib/ReaderWriter/ELF/SegmentChunks.h | 33 ++++--- lld/test/elf/X86_64/noalignsegments.test | 95 +++++++++++++++++++ lld/test/elf/phdr.test | 2 +- 7 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 lld/test/elf/X86_64/noalignsegments.test diff --git a/lld/include/lld/ReaderWriter/ELFLinkingContext.h b/lld/include/lld/ReaderWriter/ELFLinkingContext.h index 05324aca97de..b7a7ac147c71 100644 --- a/lld/include/lld/ReaderWriter/ELFLinkingContext.h +++ b/lld/include/lld/ReaderWriter/ELFLinkingContext.h @@ -278,6 +278,10 @@ public: bool demangleSymbols() const { return _demangle; } void setDemangleSymbols(bool d) { _demangle = d; } + /// \brief Align segments. + bool alignSegments() const { return _alignSegments; } + void setAlignSegments(bool align) { _alignSegments = align; } + private: ELFLinkingContext() LLVM_DELETED_FUNCTION; @@ -303,6 +307,7 @@ protected: bool _noAllowDynamicLibraries; bool _mergeRODataToTextSegment; bool _demangle; + bool _alignSegments; OutputMagic _outputMagic; StringRefVector _inputSearchPaths; std::unique_ptr _writer; diff --git a/lld/lib/Driver/GnuLdDriver.cpp b/lld/lib/Driver/GnuLdDriver.cpp index 542b1bda3f88..fc191584a779 100644 --- a/lld/lib/Driver/GnuLdDriver.cpp +++ b/lld/lib/Driver/GnuLdDriver.cpp @@ -555,6 +555,10 @@ bool GnuLdDriver::parse(int argc, const char *argv[], ctx->setCreateSeparateROSegment(); break; + case OPT_no_align_segments: + ctx->setAlignSegments(false); + break; + default: break; } // end switch on option ID diff --git a/lld/lib/Driver/GnuLdOptions.td b/lld/lib/Driver/GnuLdOptions.td index c060fe77677d..ca551523fd4e 100644 --- a/lld/lib/Driver/GnuLdOptions.td +++ b/lld/lib/Driver/GnuLdOptions.td @@ -221,6 +221,9 @@ def rosegment: Flag<["--"], "rosegment">, def z : Separate<["-"], "z">, HelpText<"Linker Option extensions">, Group; +def no_align_segments: Flag<["--"], "no-align-segments">, + HelpText<"Don't align ELF segments(virtualaddress/fileoffset) to page boundaries">, + Group; //===----------------------------------------------------------------------===// /// Symbol options diff --git a/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp b/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp index 221a78bfd5ca..0eccf64e1a53 100644 --- a/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ b/lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -62,7 +62,8 @@ ELFLinkingContext::ELFLinkingContext( _mergeCommonStrings(false), _runLayoutPass(true), _useShlibUndefines(true), _dynamicLinkerArg(false), _noAllowDynamicLibraries(false), _mergeRODataToTextSegment(true), - _demangle(true), _outputMagic(OutputMagic::DEFAULT), _sysrootPath("") {} + _demangle(true), _alignSegments(true), _outputMagic(OutputMagic::DEFAULT), + _sysrootPath("") {} void ELFLinkingContext::addPasses(PassManager &pm) { if (_runLayoutPass) diff --git a/lld/lib/ReaderWriter/ELF/SegmentChunks.h b/lld/lib/ReaderWriter/ELF/SegmentChunks.h index 017358df9217..9524f1bc3193 100644 --- a/lld/lib/ReaderWriter/ELF/SegmentChunks.h +++ b/lld/lib/ReaderWriter/ELF/SegmentChunks.h @@ -399,14 +399,12 @@ void Segment::assignFileOffsets(uint64_t startOffset) { uint64_t fileOffset = startOffset; uint64_t curSliceFileOffset = fileOffset; bool isDataPageAlignedForNMagic = false; + bool alignSegments = this->_context.alignSegments(); + uint64_t p_align = this->_context.getPageSize(); this->setFileOffset(startOffset); for (auto &slice : slices()) { - // Align to the slice alignment - fileOffset = llvm::RoundUpToAlignment(fileOffset, slice->align2()); - bool isFirstSection = true; - for (auto section : slice->sections()) { // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data // to a page boundary @@ -415,16 +413,25 @@ void Segment::assignFileOffsets(uint64_t startOffset) { _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { // Align to a page only if the output is not // OutputMagic::NMAGIC/OutputMagic::OMAGIC - fileOffset = - llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize()); - } - if (!isDataPageAlignedForNMagic && needAlign(section)) { + if (alignSegments) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); + else { + // Align according to ELF spec. + // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf + uint64_t padding = 0; + uint64_t virtualAddress = slice->virtualAddr(); + Section *sect = dyn_cast>(section); + if (sect && sect->isLoadableSection() && + ((virtualAddress & (p_align - 1)) != + (fileOffset & (p_align - 1)))) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); + } + } else if (!isDataPageAlignedForNMagic && needAlign(section)) { fileOffset = llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize()); isDataPageAlignedForNMagic = true; - } - // Align the section address - fileOffset = llvm::RoundUpToAlignment(fileOffset, section->align2()); + } else + fileOffset = llvm::RoundUpToAlignment(fileOffset, section->align2()); if (isFirstSection) { slice->setFileOffset(fileOffset); @@ -460,6 +467,7 @@ template void Segment::assignVirtualAddress(uint64_t addr) { uint64_t startAddr = addr; SegmentSlice *slice = nullptr; uint64_t tlsStartAddr = 0; + bool alignSegments = this->_context.alignSegments(); for (auto si = _sections.begin(); si != _sections.end(); ++si) { // If this is first section in the segment, page align the section start @@ -467,7 +475,8 @@ template void Segment::assignVirtualAddress(uint64_t addr) { // only if NMAGIC is set. if (isFirstSection) { isFirstSection = false; - if (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + if (alignSegments && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) // Align to a page only if the output is not // OutputMagic::NMAGIC/OutputMagic::OMAGIC diff --git a/lld/test/elf/X86_64/noalignsegments.test b/lld/test/elf/X86_64/noalignsegments.test new file mode 100644 index 000000000000..8432bda51f00 --- /dev/null +++ b/lld/test/elf/X86_64/noalignsegments.test @@ -0,0 +1,95 @@ +# Checks that segments are aligned as per ELF spec than aligning each +# segment fileoffset / virtual address to a page. + +# Build executable +# RUN: yaml2obj -format=elf -docnum 1 %s -o %t.o +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec +# RUN: llvm-readobj -program-headers %t.exe | FileCheck %s +# +#CHECK: VirtualAddress: 0x400000 +#CHECK: PhysicalAddress: 0x400000 +#CHECK: VirtualAddress: 0x400178 +#CHECK: PhysicalAddress: 0x400178 + +# object +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B864000000C745FC000000005DC366666666662E0F1F840000000000554889E531C05DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000001200000000410E108602430D060000001800000038000000000000000800000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x0000000000000020 + Size: 0x0000000000000008 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000012 + - Name: myval + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + - Name: val + Type: STT_OBJECT + Section: .rodata + Size: 0x0000000000000004 +... diff --git a/lld/test/elf/phdr.test b/lld/test/elf/phdr.test index 3f626aa11790..ba0165c113df 100644 --- a/lld/test/elf/phdr.test +++ b/lld/test/elf/phdr.test @@ -59,7 +59,7 @@ I386-NEXT: Alignment: 4096 I386-NEXT: } I386-NEXT: ProgramHeader { I386-NEXT: Type: PT_LOAD (0x1) -I386-NEXT: Offset: 0x4000 +I386-NEXT: Offset: 0x2000 I386-NEXT: VirtualAddress: 0x4000 I386-NEXT: PhysicalAddress: 0x4000 I386-NEXT: FileSize: 4