5 changes: 4 additions & 1 deletion lld/lib/Driver/GnuLdDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ std::error_code GnuLdDriver::evalLinkerScript(ELFLinkingContext &ctx,
}
}
// Transfer ownership of the script to the linking context
ctx.addLinkerScript(std::move(parser));
ctx.linkerScriptSema().addLinkerScript(std::move(parser));
return std::error_code();
}

Expand Down Expand Up @@ -734,6 +734,9 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
if (!ctx->validate(diag))
return false;

// Perform linker script semantic actions
ctx->linkerScriptSema().perform();

context.swap(ctx);
return true;
}
Expand Down
3 changes: 2 additions & 1 deletion lld/lib/ReaderWriter/ELF/Chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class Chunk {
SectionHeader, ///< Section header
ELFSegment, ///< Segment
ELFSection, ///< Section
AtomSection ///< A section containing atoms.
AtomSection, ///< A section containing atoms.
Expression ///< A linker script expression
};
/// \brief the ContentType of the chunk
enum ContentType : uint8_t{ Unknown, Header, Code, Data, Note, TLS };
Expand Down
150 changes: 130 additions & 20 deletions lld/lib/ReaderWriter/ELF/DefaultLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ class DefaultLayout : public Layout {

typedef llvm::DenseSet<const Atom *> AtomSetT;

DefaultLayout(const ELFLinkingContext &context) : _context(context) {}
DefaultLayout(ELFLinkingContext &context)
: _context(context), _linkerScriptSema(context.linkerScriptSema()) {}

/// \brief Return the section order for a input section
SectionOrder getSectionOrder(StringRef name, int32_t contentType,
Expand All @@ -180,13 +181,15 @@ class DefaultLayout : public Layout {
virtual StringRef getInputSectionName(const DefinedAtom *da) const;

/// \brief Return the name of the output section from the input section.
virtual StringRef getOutputSectionName(StringRef inputSectionName) const;
virtual StringRef getOutputSectionName(StringRef archivePath,
StringRef memberPath,
StringRef inputSectionName) const;

/// \brief Gets or creates a section.
AtomSection<ELFT> *
getSection(StringRef name, int32_t contentType,
DefinedAtom::ContentPermissions contentPermissions,
StringRef path);
const DefinedAtom *da);

/// \brief Gets the segment for a output section
virtual Layout::SegmentType getSegmentType(Section<ELFT> *section) const;
Expand Down Expand Up @@ -215,6 +218,18 @@ class DefaultLayout : public Layout {
// Output sections with the same name into a OutputSection
void createOutputSections();

/// \brief Sort the sections by their order as defined by the layout,
/// preparing all sections to be assigned to a segment.
virtual void sortInputSections();

/// \brief Add extra chunks to a segment just before including the input
/// section given by <archivePath, memberPath, sectionName>. This
/// is used to add linker script expressions before each section.
virtual void addExtraChunksToSegment(Segment<ELFT> *segment,
StringRef archivePath,
StringRef memberPath,
StringRef sectionName);

void assignSectionsToSegments() override;

void assignVirtualAddress() override;
Expand Down Expand Up @@ -323,7 +338,8 @@ class DefaultLayout : public Layout {
std::vector<lld::AtomLayout *> _absoluteAtoms;
AtomSetT _referencedDynAtoms;
llvm::StringSet<> _copiedDynSymNames;
const ELFLinkingContext &_context;
ELFLinkingContext &_context;
script::Sema &_linkerScriptSema;
};

template <class ELFT>
Expand Down Expand Up @@ -415,7 +431,15 @@ DefaultLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const {
/// \brief This maps the input sections to the output section names.
template <class ELFT>
StringRef
DefaultLayout<ELFT>::getOutputSectionName(StringRef inputSectionName) const {
DefaultLayout<ELFT>::getOutputSectionName(StringRef archivePath,
StringRef memberPath,
StringRef inputSectionName) const {
StringRef outputSectionName;
if (_linkerScriptSema.hasLayoutCommands() &&
!(outputSectionName = _linkerScriptSema.getOutputSection(
{archivePath, memberPath, inputSectionName})).empty())
return outputSectionName;

return llvm::StringSwitch<StringRef>(inputSectionName)
.StartsWith(".text", ".text")
.StartsWith(".ctors", ".ctors")
Expand Down Expand Up @@ -533,17 +557,20 @@ template <class ELFT>
AtomSection<ELFT> *
DefaultLayout<ELFT>::getSection(StringRef sectionName, int32_t contentType,
DefinedAtom::ContentPermissions permissions,
StringRef path) {
const SectionKey sectionKey(sectionName, permissions, path);
SectionOrder sectionOrder =
getSectionOrder(sectionName, contentType, permissions);
const DefinedAtom *da) {
const SectionKey sectionKey(sectionName, permissions, da->file().path());
SectionOrder sectionOrder = getSectionOrder(sectionName, contentType, permissions);
auto sec = _sectionMap.find(sectionKey);
if (sec != _sectionMap.end())
return sec->second;
AtomSection<ELFT> *newSec =
createSection(sectionName, contentType, permissions, sectionOrder);
newSec->setOutputSectionName(getOutputSectionName(sectionName));

newSec->setOutputSectionName(getOutputSectionName(
da->file().archivePath(), da->file().memberPath(), sectionName));
newSec->setOrder(sectionOrder);
newSec->setArchiveNameOrPath(da->file().archivePath());
newSec->setMemberNameOrPath(da->file().memberPath());
_sections.push_back(newSec);
_sectionMap.insert(std::make_pair(sectionKey, newSec));
return newSec;
Expand All @@ -563,8 +590,8 @@ DefaultLayout<ELFT>::addAtom(const Atom *atom) {
const DefinedAtom::ContentType contentType = definedAtom->contentType();

StringRef sectionName = getInputSectionName(definedAtom);
AtomSection<ELFT> *section = getSection(
sectionName, contentType, permissions, definedAtom->file().path());
AtomSection<ELFT> *section =
getSection(sectionName, contentType, permissions, definedAtom);

// Add runtime relocations to the .rela section.
for (const auto &reloc : *definedAtom) {
Expand Down Expand Up @@ -632,10 +659,7 @@ template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() {
ScopedTask task(getDefaultDomain(), "assignSectionsToSegments");
ELFLinkingContext::OutputMagic outputMagic = _context.getOutputMagic();
// sort the sections by their order as defined by the layout
std::stable_sort(_sections.begin(), _sections.end(),
[](Chunk<ELFT> *A, Chunk<ELFT> *B) {
return A->order() < B->order();
});
sortInputSections();
// Create output sections.
createOutputSections();
// Set the ordinal after sorting the sections
Expand Down Expand Up @@ -686,8 +710,8 @@ template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() {
if (!additionalSegmentInsert.second) {
segment = additionalSegmentInsert.first->second;
} else {
segment = new (_allocator) Segment<ELFT>(_context, segmentName,
segmentType);
segment = new (_allocator)
Segment<ELFT>(_context, segmentName, segmentType);
additionalSegmentInsert.first->second = segment;
_segments.push_back(segment);
}
Expand All @@ -713,11 +737,16 @@ template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() {
if (!segmentInsert.second) {
segment = segmentInsert.first->second;
} else {
segment = new (_allocator) Segment<ELFT>(_context, "PT_LOAD",
llvm::ELF::PT_LOAD);
segment = new (_allocator)
Segment<ELFT>(_context, "PT_LOAD", llvm::ELF::PT_LOAD);
segmentInsert.first->second = segment;
_segments.push_back(segment);
}
// Insert chunks with linker script expressions that occur at this
// point, just before appending a new input section
addExtraChunksToSegment(segment, section->archivePath(),
section->memberPath(),
section->inputSectionName());
segment->append(section);
}
}
Expand Down Expand Up @@ -754,6 +783,7 @@ DefaultLayout<ELFT>::assignVirtualAddress() {
break;
}
}
assert(firstLoadSegment != nullptr && "No loadable segment!");
firstLoadSegment->prepend(_programHeader);
firstLoadSegment->prepend(_elfHeader);
bool newSegmentHeaderAdded = true;
Expand Down Expand Up @@ -870,6 +900,86 @@ void DefaultLayout<ELFT>::assignFileOffsetsForMiscSections() {
fileoffset += si->fileSize();
}
}

template <class ELFT> void DefaultLayout<ELFT>::sortInputSections() {
// First, sort according to default layout's order
std::stable_sort(
_sections.begin(), _sections.end(),
[](Chunk<ELFT> *A, Chunk<ELFT> *B) { return A->order() < B->order(); });

if (!_linkerScriptSema.hasLayoutCommands())
return;

// Sort the sections by their order as defined by the linker script
std::stable_sort(this->_sections.begin(), this->_sections.end(),
[this](Chunk<ELFT> *A, Chunk<ELFT> *B) {
auto *a = dyn_cast<Section<ELFT>>(A);
auto *b = dyn_cast<Section<ELFT>>(B);

if (a == nullptr)
return false;
if (b == nullptr)
return true;

return _linkerScriptSema.less(
{a->archivePath(), a->memberPath(),
a->inputSectionName()},
{b->archivePath(), b->memberPath(),
b->inputSectionName()});
});
// Now try to arrange sections with no mapping rules to sections with
// similar content
auto p = this->_sections.begin();
// Find first section that has no assigned rule id
while (p != this->_sections.end()) {
auto *sect = dyn_cast<AtomSection<ELFT>>(*p);
if (!sect)
break;

if (!_linkerScriptSema.hasMapping({sect->archivePath(),
sect->memberPath(),
sect->inputSectionName()}))
break;

++p;
}
// For all sections that have no assigned rule id, try to move them near a
// section with similar contents
if (p != this->_sections.begin()) {
for (; p != this->_sections.end(); ++p) {
auto q = p;
--q;
while (q != this->_sections.begin() &&
(*q)->getContentType() != (*p)->getContentType())
--q;
if ((*q)->getContentType() != (*p)->getContentType())
continue;
++q;
for (auto i = p; i != q;) {
auto next = i--;
std::iter_swap(i, next);
}
}
}
}

template <class ELFT>
void DefaultLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment,
StringRef archivePath,
StringRef memberPath,
StringRef sectionName) {
if (!_linkerScriptSema.hasLayoutCommands())
return;

std::vector<const script::SymbolAssignment *> exprs =
_linkerScriptSema.getExprs({archivePath, memberPath, sectionName});
for (auto expr : exprs) {
auto expChunk =
new (this->_allocator) ExpressionChunk<ELFT>(this->_context, expr);
segment->append(expChunk);
}
}

} // end namespace elf
} // end namespace lld

Expand Down
2 changes: 1 addition & 1 deletion lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ ELFLinkingContext::ELFLinkingContext(
_mergeRODataToTextSegment(true), _demangle(true),
_stripSymbols(false), _alignSegments(true), _collectStats(false),
_outputMagic(OutputMagic::DEFAULT), _initFunction("_init"),
_finiFunction("_fini"), _sysrootPath("") {}
_finiFunction("_fini"), _sysrootPath(""), _linkerScriptSema() {}

void ELFLinkingContext::addPasses(PassManager &pm) {
pm.add(llvm::make_unique<elf::OrderPass>());
Expand Down
2 changes: 1 addition & 1 deletion lld/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class HexagonTargetLayout final : public TargetLayout<HexagonELFType> {
ORDER_SDATA = 205
};

HexagonTargetLayout(const HexagonLinkingContext &hti)
HexagonTargetLayout(HexagonLinkingContext &hti)
: TargetLayout<HexagonELFType>(hti), _sdataSection(nullptr),
_gotSymAtom(nullptr), _cachedGotSymAtom(false) {
_sdataSection = new (_alloc) SDataSection<HexagonELFType>(hti);
Expand Down
2 changes: 1 addition & 1 deletion lld/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace elf {
template <class ELFType>
class MipsTargetLayout final : public TargetLayout<ELFType> {
public:
MipsTargetLayout(const MipsLinkingContext &ctx)
MipsTargetLayout(MipsLinkingContext &ctx)
: TargetLayout<ELFType>(ctx),
_gotSection(new (_alloc) MipsGOTSection<ELFType>(ctx)),
_pltSection(new (_alloc) MipsPLTSection<ELFType>(ctx)) {}
Expand Down
10 changes: 10 additions & 0 deletions lld/lib/ReaderWriter/ELF/SectionChunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ template <class ELFT> class Section : public Chunk<ELFT> {
_outputSectionName = outputSectionName;
}

void setArchiveNameOrPath(StringRef name) { _archivePath = name; }

void setMemberNameOrPath(StringRef name) { _memberPath = name; }

StringRef archivePath() { return _archivePath; }

StringRef memberPath() { return _memberPath; }

protected:
/// \brief OutputSection this Section is a member of, or nullptr.
OutputSection<ELFT> *_outputSection;
Expand All @@ -144,6 +152,8 @@ template <class ELFT> class Section : public Chunk<ELFT> {
StringRef _inputSectionName;
/// \brief Output section name.
StringRef _outputSectionName;
StringRef _archivePath;
StringRef _memberPath;
};

/// \brief A section containing atoms.
Expand Down
60 changes: 57 additions & 3 deletions lld/lib/ReaderWriter/ELF/SegmentChunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,37 @@ class Segment : public Chunk<ELFT> {
llvm::BumpPtrAllocator _segmentAllocate;
};

/// This chunk represents a linker script expression that needs to be calculated
/// at the time the virtual addresses for the parent segment are being assigned.
template <class ELFT> class ExpressionChunk : public Chunk<ELFT> {
public:
ExpressionChunk(ELFLinkingContext &ctx, const script::SymbolAssignment *expr)
: Chunk<ELFT>(StringRef(), Chunk<ELFT>::Kind::Expression, ctx),
_expr(expr), _linkerScriptSema(ctx.linkerScriptSema()) {
this->_alignment = 1;
}

static bool classof(const Chunk<ELFT> *c) {
return c->kind() == Chunk<ELFT>::Kind::Expression;
}

int getContentType() const override {
return Chunk<ELFT>::ContentType::Unknown;
}
void write(ELFWriter *, TargetLayout<ELFT> &,
llvm::FileOutputBuffer &) override {}
void doPreFlight() override {}
void finalize() override {}

std::error_code evalExpr(uint64_t &curPos) {
return _linkerScriptSema.evalExpr(_expr, curPos);
}

private:
const script::SymbolAssignment *_expr;
script::Sema &_linkerScriptSema;
};

/// \brief A Program Header segment contains a set of chunks instead of sections
/// The segment doesn't contain any slice
template <class ELFT> class ProgramHeaderSegment : public Segment<ELFT> {
Expand Down Expand Up @@ -390,11 +421,16 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
bool isDataPageAlignedForNMagic = false;
bool alignSegments = this->_context.alignSegments();
uint64_t p_align = this->_context.getPageSize();
uint64_t lastVirtualAddress = 0;

this->setFileOffset(startOffset);
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;
// 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 Down Expand Up @@ -429,6 +465,7 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
}
section->setFileOffset(fileOffset);
fileOffset += section->fileSize();
lastVirtualAddress = section->virtualAddr() + section->memSize();
}
slice->setFileSize(fileOffset - curSliceFileOffset);
}
Expand Down Expand Up @@ -457,7 +494,7 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
SegmentSlice<ELFT> *slice = nullptr;
uint64_t tlsStartAddr = 0;
bool alignSegments = this->_context.alignSegments();
StringRef prevOutputSectionName;
StringRef prevOutputSectionName = StringRef();

for (auto si = _sections.begin(); si != _sections.end(); ++si) {
// If this is first section in the segment, page align the section start
Expand All @@ -481,6 +518,10 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
}
// align the startOffset to the section alignment
uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment());
// Handle linker script expressions, which *may update newAddr* if the
// expression assigns to "."
if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si))
expr->evalExpr(newAddr);
curSliceAddress = newAddr;
sliceAlign = (*si)->alignment();
(*si)->setVirtualAddr(curSliceAddress);
Expand Down Expand Up @@ -513,9 +554,22 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
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))
expr->evalExpr(newAddr);
Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si);
StringRef curOutputSectionName =
sec ? sec->outputSectionName() : (*si)->name();
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();
}
bool autoCreateSlice = true;
if (curOutputSectionName == prevOutputSectionName)
autoCreateSlice = false;
Expand Down
3 changes: 1 addition & 2 deletions lld/lib/ReaderWriter/ELF/TargetLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ namespace elf {
/// be changed in the final layout
template <class ELFT> class TargetLayout : public DefaultLayout<ELFT> {
public:
TargetLayout(const ELFLinkingContext &context)
: DefaultLayout<ELFT>(context) {}
TargetLayout(ELFLinkingContext &context) : DefaultLayout<ELFT>(context) {}
};
} // end namespace elf
} // end namespace lld
Expand Down
409 changes: 403 additions & 6 deletions lld/lib/ReaderWriter/LinkerScript.cpp

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions lld/unittests/DriverTests/GnuLdDriverTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,10 @@ TEST_F(LinkerScriptTest, ExprEval) {
parse("SECTIONS { symbol = 0x4000 + 0x40; \n"
". = (symbol >= 0x4040)? (0x5001 * 2 & 0xFFF0) << 1 : 0}");

EXPECT_EQ((size_t)1, _ctx->scripts().size());
EXPECT_EQ((size_t)1, _ctx->linkerScriptSema().getLinkerScripts().size());

script::LinkerScript *ls = _ctx->scripts()[0]->get();
script::LinkerScript *ls =
_ctx->linkerScriptSema().getLinkerScripts()[0]->get();
EXPECT_EQ((size_t)1, ls->_commands.size());

auto *secs = dyn_cast<const script::Sections>(*ls->_commands.begin());
Expand Down