[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
This commit is contained in:
Shankar Easwaran 2014-11-08 03:44:49 +00:00
parent 8da0bf3b7c
commit 22c76a5d79
7 changed files with 131 additions and 14 deletions

View File

@ -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> _writer;

View File

@ -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

View File

@ -221,6 +221,9 @@ def rosegment: Flag<["--"], "rosegment">,
def z : Separate<["-"], "z">,
HelpText<"Linker Option extensions">,
Group<grp_customopts>;
def no_align_segments: Flag<["--"], "no-align-segments">,
HelpText<"Don't align ELF segments(virtualaddress/fileoffset) to page boundaries">,
Group<grp_customopts>;
//===----------------------------------------------------------------------===//
/// Symbol options

View File

@ -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)

View File

@ -399,14 +399,12 @@ void Segment<ELFT>::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<ELFT>::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<ELFT> *sect = dyn_cast<Section<ELFT>>(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 <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
uint64_t startAddr = addr;
SegmentSlice<ELFT> *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 <class ELFT> void Segment<ELFT>::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

View File

@ -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
...

View File

@ -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