forked from OSchip/llvm-project
[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:
parent
8da0bf3b7c
commit
22c76a5d79
|
@ -278,6 +278,10 @@ public:
|
||||||
bool demangleSymbols() const { return _demangle; }
|
bool demangleSymbols() const { return _demangle; }
|
||||||
void setDemangleSymbols(bool d) { _demangle = d; }
|
void setDemangleSymbols(bool d) { _demangle = d; }
|
||||||
|
|
||||||
|
/// \brief Align segments.
|
||||||
|
bool alignSegments() const { return _alignSegments; }
|
||||||
|
void setAlignSegments(bool align) { _alignSegments = align; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ELFLinkingContext() LLVM_DELETED_FUNCTION;
|
ELFLinkingContext() LLVM_DELETED_FUNCTION;
|
||||||
|
|
||||||
|
@ -303,6 +307,7 @@ protected:
|
||||||
bool _noAllowDynamicLibraries;
|
bool _noAllowDynamicLibraries;
|
||||||
bool _mergeRODataToTextSegment;
|
bool _mergeRODataToTextSegment;
|
||||||
bool _demangle;
|
bool _demangle;
|
||||||
|
bool _alignSegments;
|
||||||
OutputMagic _outputMagic;
|
OutputMagic _outputMagic;
|
||||||
StringRefVector _inputSearchPaths;
|
StringRefVector _inputSearchPaths;
|
||||||
std::unique_ptr<Writer> _writer;
|
std::unique_ptr<Writer> _writer;
|
||||||
|
|
|
@ -555,6 +555,10 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
|
||||||
ctx->setCreateSeparateROSegment();
|
ctx->setCreateSeparateROSegment();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPT_no_align_segments:
|
||||||
|
ctx->setAlignSegments(false);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
} // end switch on option ID
|
} // end switch on option ID
|
||||||
|
|
|
@ -221,6 +221,9 @@ def rosegment: Flag<["--"], "rosegment">,
|
||||||
def z : Separate<["-"], "z">,
|
def z : Separate<["-"], "z">,
|
||||||
HelpText<"Linker Option extensions">,
|
HelpText<"Linker Option extensions">,
|
||||||
Group<grp_customopts>;
|
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
|
/// Symbol options
|
||||||
|
|
|
@ -62,7 +62,8 @@ ELFLinkingContext::ELFLinkingContext(
|
||||||
_mergeCommonStrings(false), _runLayoutPass(true),
|
_mergeCommonStrings(false), _runLayoutPass(true),
|
||||||
_useShlibUndefines(true), _dynamicLinkerArg(false),
|
_useShlibUndefines(true), _dynamicLinkerArg(false),
|
||||||
_noAllowDynamicLibraries(false), _mergeRODataToTextSegment(true),
|
_noAllowDynamicLibraries(false), _mergeRODataToTextSegment(true),
|
||||||
_demangle(true), _outputMagic(OutputMagic::DEFAULT), _sysrootPath("") {}
|
_demangle(true), _alignSegments(true), _outputMagic(OutputMagic::DEFAULT),
|
||||||
|
_sysrootPath("") {}
|
||||||
|
|
||||||
void ELFLinkingContext::addPasses(PassManager &pm) {
|
void ELFLinkingContext::addPasses(PassManager &pm) {
|
||||||
if (_runLayoutPass)
|
if (_runLayoutPass)
|
||||||
|
|
|
@ -399,14 +399,12 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
|
||||||
uint64_t fileOffset = startOffset;
|
uint64_t fileOffset = startOffset;
|
||||||
uint64_t curSliceFileOffset = fileOffset;
|
uint64_t curSliceFileOffset = fileOffset;
|
||||||
bool isDataPageAlignedForNMagic = false;
|
bool isDataPageAlignedForNMagic = false;
|
||||||
|
bool alignSegments = this->_context.alignSegments();
|
||||||
|
uint64_t p_align = this->_context.getPageSize();
|
||||||
|
|
||||||
this->setFileOffset(startOffset);
|
this->setFileOffset(startOffset);
|
||||||
for (auto &slice : slices()) {
|
for (auto &slice : slices()) {
|
||||||
// Align to the slice alignment
|
|
||||||
fileOffset = llvm::RoundUpToAlignment(fileOffset, slice->align2());
|
|
||||||
|
|
||||||
bool isFirstSection = true;
|
bool isFirstSection = true;
|
||||||
|
|
||||||
for (auto section : slice->sections()) {
|
for (auto section : slice->sections()) {
|
||||||
// If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data
|
// If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data
|
||||||
// to a page boundary
|
// to a page boundary
|
||||||
|
@ -415,16 +413,25 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
|
||||||
_outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) {
|
_outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) {
|
||||||
// Align to a page only if the output is not
|
// Align to a page only if the output is not
|
||||||
// OutputMagic::NMAGIC/OutputMagic::OMAGIC
|
// OutputMagic::NMAGIC/OutputMagic::OMAGIC
|
||||||
fileOffset =
|
if (alignSegments)
|
||||||
llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize());
|
fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align);
|
||||||
}
|
else {
|
||||||
if (!isDataPageAlignedForNMagic && needAlign(section)) {
|
// 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 =
|
fileOffset =
|
||||||
llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize());
|
llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize());
|
||||||
isDataPageAlignedForNMagic = true;
|
isDataPageAlignedForNMagic = true;
|
||||||
}
|
} else
|
||||||
// Align the section address
|
fileOffset = llvm::RoundUpToAlignment(fileOffset, section->align2());
|
||||||
fileOffset = llvm::RoundUpToAlignment(fileOffset, section->align2());
|
|
||||||
|
|
||||||
if (isFirstSection) {
|
if (isFirstSection) {
|
||||||
slice->setFileOffset(fileOffset);
|
slice->setFileOffset(fileOffset);
|
||||||
|
@ -460,6 +467,7 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
|
||||||
uint64_t startAddr = addr;
|
uint64_t startAddr = addr;
|
||||||
SegmentSlice<ELFT> *slice = nullptr;
|
SegmentSlice<ELFT> *slice = nullptr;
|
||||||
uint64_t tlsStartAddr = 0;
|
uint64_t tlsStartAddr = 0;
|
||||||
|
bool alignSegments = this->_context.alignSegments();
|
||||||
|
|
||||||
for (auto si = _sections.begin(); si != _sections.end(); ++si) {
|
for (auto si = _sections.begin(); si != _sections.end(); ++si) {
|
||||||
// If this is first section in the segment, page align the section start
|
// 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.
|
// only if NMAGIC is set.
|
||||||
if (isFirstSection) {
|
if (isFirstSection) {
|
||||||
isFirstSection = false;
|
isFirstSection = false;
|
||||||
if (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC &&
|
if (alignSegments &&
|
||||||
|
_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC &&
|
||||||
_outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)
|
_outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)
|
||||||
// Align to a page only if the output is not
|
// Align to a page only if the output is not
|
||||||
// OutputMagic::NMAGIC/OutputMagic::OMAGIC
|
// OutputMagic::NMAGIC/OutputMagic::OMAGIC
|
||||||
|
|
|
@ -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
|
||||||
|
...
|
|
@ -59,7 +59,7 @@ I386-NEXT: Alignment: 4096
|
||||||
I386-NEXT: }
|
I386-NEXT: }
|
||||||
I386-NEXT: ProgramHeader {
|
I386-NEXT: ProgramHeader {
|
||||||
I386-NEXT: Type: PT_LOAD (0x1)
|
I386-NEXT: Type: PT_LOAD (0x1)
|
||||||
I386-NEXT: Offset: 0x4000
|
I386-NEXT: Offset: 0x2000
|
||||||
I386-NEXT: VirtualAddress: 0x4000
|
I386-NEXT: VirtualAddress: 0x4000
|
||||||
I386-NEXT: PhysicalAddress: 0x4000
|
I386-NEXT: PhysicalAddress: 0x4000
|
||||||
I386-NEXT: FileSize: 4
|
I386-NEXT: FileSize: 4
|
||||||
|
|
Loading…
Reference in New Issue