Skip to content

Commit

Permalink
[LinkerScript] Don't create unnecessarily large segments
Browse files Browse the repository at this point in the history
When using a linker script expression to change the address of a section, even
if the new address is more than a page of distance from the old address, lld
may put everything in the same segment, forcing it to be unnecessarily large.
This patch changes the logic in Segment::assignVirtualAddress() and
Segment::assignFileOffsets() to allow the segment to be sliced into two or more
if it detects a linker script expression that changes a section address.

Differential Revision: http://reviews.llvm.org/D10952

llvm-svn: 242096
  • Loading branch information
rafaelauler committed Jul 14, 2015
1 parent 418f3ec commit 2c4af3e
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 29 deletions.
70 changes: 41 additions & 29 deletions lld/lib/ReaderWriter/ELF/SegmentChunks.cpp
Expand Up @@ -118,13 +118,23 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
uint64_t lastVirtualAddress = 0;

this->setFileOffset(startOffset);
bool changeOffset = false;
uint64_t newOffset = 0;
for (auto &slice : slices()) {
bool isFirstSection = true;
for (auto section : slice->sections()) {
// Handle linker script expressions, which may change the offset
if (!isFirstSection)
if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section))
fileOffset += expr->virtualAddr() - lastVirtualAddress;
if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) {
if (!isFirstSection) {
changeOffset = true;
newOffset = fileOffset + expr->virtualAddr() - lastVirtualAddress;
}
continue;
}
if (changeOffset) {
changeOffset = false;
fileOffset = newOffset;
}
// Align fileoffset to the alignment of the section.
fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment());
// If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data
Expand All @@ -136,17 +146,14 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
// OutputMagic::NMAGIC/OutputMagic::OMAGIC
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 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) +
(virtualAddress % p_align);
}
// Align according to ELF spec.
// in p75, http://www.sco.com/developers/devspecs/gabi41.pdf
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) +
(virtualAddress % p_align);
} else if (!isDataPageAlignedForNMagic && needAlign(section)) {
fileOffset =
llvm::RoundUpToAlignment(fileOffset, this->_ctx.getPageSize());
Expand All @@ -161,6 +168,7 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
fileOffset += section->fileSize();
lastVirtualAddress = section->virtualAddr() + section->memSize();
}
changeOffset = false;
slice->setFileSize(fileOffset - curSliceFileOffset);
}
this->setFileSize(fileOffset - startOffset);
Expand Down Expand Up @@ -242,6 +250,8 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
++si;
}

uint64_t scriptAddr = 0;
bool forceScriptAddr = false;
for (auto e = _sections.end(); si != e; ++si) {
uint64_t curAddr = curSliceAddress + curSliceSize;
if (!isDataPageAlignedForNMagic && needAlign(*si)) {
Expand All @@ -251,23 +261,25 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
curAddr = llvm::RoundUpToAlignment(curAddr, this->_ctx.getPageSize());
isDataPageAlignedForNMagic = true;
}
uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment());
// Handle linker script expressions, which *may update newAddr* if the
// expression assigns to "."
if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si))
uint64_t newAddr = llvm::RoundUpToAlignment(
forceScriptAddr ? scriptAddr : curAddr, (*si)->alignment());
forceScriptAddr = false;

// Handle linker script expressions, which may force an address change if
// the expression assigns to "."
if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) {
uint64_t oldAddr = newAddr;
expr->evalExpr(newAddr);
Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si);
StringRef curOutputSectionName;
if (sec) {
curOutputSectionName = sec->outputSectionName();
} else {
// If this is a linker script expression, propagate the name of the
// previous section instead
if (isa<ExpressionChunk<ELFT>>(*si))
curOutputSectionName = prevOutputSectionName;
else
curOutputSectionName = (*si)->name();
if (oldAddr != newAddr) {
forceScriptAddr = true;
scriptAddr = newAddr;
}
(*si)->setVirtualAddr(newAddr);
continue;
}
Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si);
StringRef curOutputSectionName =
sec ? sec->outputSectionName() : (*si)->name();
bool autoCreateSlice = true;
if (curOutputSectionName == prevOutputSectionName)
autoCreateSlice = false;
Expand Down
16 changes: 16 additions & 0 deletions lld/test/elf/linkerscript/sections-order.test
Expand Up @@ -94,4 +94,20 @@ CHECKSYMS-NEXT: Value: 0x506038

CHECKSYMS: Name: prog2
CHECKSYMS-NEXT: Value: 0x506050

RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix CHECKPHDRS %s

CHECKPHDRS: Type: PT_LOAD (0x1)
CHECKPHDRS: Offset: 0x1000
CHECKPHDRS-NEXT: VirtualAddress: 0x500000
CHECKPHDRS-NEXT: PhysicalAddress: 0x500000
CHECKPHDRS-NEXT: FileSize: 48
CHECKPHDRS-NEXT: MemSize: 48

CHECKPHDRS: Type: PT_LOAD (0x1)
CHECKPHDRS: Offset: 0x2030
CHECKPHDRS-NEXT: VirtualAddress: 0x506030
CHECKPHDRS-NEXT: PhysicalAddress: 0x506030
CHECKPHDRS-NEXT: FileSize: 168
CHECKPHDRS-NEXT: MemSize: 168
*/

0 comments on commit 2c4af3e

Please sign in to comment.