Skip to content

Commit

Permalink
[ELF] - Implemented basic location counter support.
Browse files Browse the repository at this point in the history
This patch implements location counter support. 
It also separates assign addresses for sections to assignAddressesScript() if it scipt exists.

Main testcase is test/ELF/linkerscript-locationcounter.s, It contains some work with location counter. It is basic now.
Implemented location counter assignment and '+' operations.

Patch by myself with LOTS of comments and design suggestions from Rui Ueyama.

Differential revision: http://reviews.llvm.org/D18499

llvm-svn: 266457
  • Loading branch information
George Rimar committed Apr 15, 2016
1 parent 7950b12 commit ea25877
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 28 deletions.
131 changes: 129 additions & 2 deletions lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,55 @@
#include "Config.h"
#include "Driver.h"
#include "InputSection.h"
#include "OutputSections.h"
#include "ScriptParser.h"
#include "SymbolTable.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/StringSaver.h"

using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace lld;
using namespace lld::elf;

LinkerScript *elf::Script;

static uint64_t getInteger(StringRef S) {
uint64_t V;
if (S.getAsInteger(0, V)) {
error("malformed number: " + S);
return 0;
}
return V;
}

// Evaluates the expression given by list of tokens.
uint64_t LinkerScript::evaluate(std::vector<StringRef> &Tokens,
uint64_t LocCounter) {
uint64_t Result = 0;
for (size_t I = 0, E = Tokens.size(); I < E; ++I) {
// Each second token should be '+' as this is the
// only operator we support now.
if (I % 2 == 1) {
if (Tokens[I] == "+")
continue;
error("error in location counter expression");
return 0;
}

StringRef Tok = Tokens[I];
if (Tok == ".")
Result += LocCounter;
else
Result += getInteger(Tok);
}
return Result;
}

template <class ELFT>
SectionRule *LinkerScript::find(InputSectionBase<ELFT> *S) {
for (SectionRule &R : Sections)
Expand All @@ -55,6 +90,66 @@ template <class ELFT> bool LinkerScript::shouldKeep(InputSectionBase<ELFT> *S) {
return R && R->Keep;
}

// This method finalizes the Locations list. Adds neccesary locations for
// orphan sections, what prepares it for futher use without
// changes in LinkerScript::assignAddresses().
template <class ELFT>
void LinkerScript::fixupLocations(std::vector<OutputSectionBase<ELFT> *> &S) {
// Orphan sections are sections present in the input files which
// are not explicitly placed into the output file by the linker
// script. We place orphan sections at end of file. Other linkers places
// them using some heuristics as described in
// https://sourceware.org/binutils/docs/ld/Orphan-Sections.html#Orphan-Sections.
for (OutputSectionBase<ELFT> *Sec : S) {
StringRef Name = Sec->getName();
auto I = std::find(SectionOrder.begin(), SectionOrder.end(), Name);
if (I == SectionOrder.end())
Locations.push_back({Command::Section, {}, {Name}});
}
}

template <class ELFT>
void LinkerScript::assignAddresses(std::vector<OutputSectionBase<ELFT> *> &S) {
typedef typename ELFT::uint uintX_t;

Script->fixupLocations(S);

uintX_t ThreadBssOffset = 0;
uintX_t VA =
Out<ELFT>::ElfHeader->getSize() + Out<ELFT>::ProgramHeaders->getSize();

for (LocationNode &Node : Locations) {
if (Node.Type == Command::Expr) {
VA = evaluate(Node.Expr, VA);
continue;
}

auto I =
std::find_if(S.begin(), S.end(), [&](OutputSectionBase<ELFT> *Sec) {
return Sec->getName() == Node.SectionName;
});
if (I == S.end())
continue;

OutputSectionBase<ELFT> *Sec = *I;
uintX_t Align = Sec->getAlign();
if ((Sec->getFlags() & SHF_TLS) && Sec->getType() == SHT_NOBITS) {
uintX_t TVA = VA + ThreadBssOffset;
TVA = alignTo(TVA, Align);
Sec->setVA(TVA);
ThreadBssOffset = TVA - VA + Sec->getSize();
continue;
}

if (Sec->getFlags() & SHF_ALLOC) {
VA = alignTo(VA, Align);
Sec->setVA(VA);
VA += Sec->getSize();
continue;
}
}
}

ArrayRef<uint8_t> LinkerScript::getFiller(StringRef Name) {
auto I = Filler.find(Name);
if (I == Filler.end())
Expand Down Expand Up @@ -126,6 +221,7 @@ class elf::ScriptParser final : public elf::ScriptParserBase {
void readSearchDir();
void readSections();

void readLocationCounterValue();
void readOutputSectionDescription();
void readSectionPatterns(StringRef OutSec, bool Keep);

Expand Down Expand Up @@ -287,8 +383,13 @@ void ScriptParser::readSearchDir() {

void ScriptParser::readSections() {
expect("{");
while (!Error && !skip("}"))
readOutputSectionDescription();
while (!Error && !skip("}")) {
StringRef Tok = peek();
if (Tok == ".")
readLocationCounterValue();
else
readOutputSectionDescription();
}
}

void ScriptParser::readSectionPatterns(StringRef OutSec, bool Keep) {
Expand All @@ -297,9 +398,25 @@ void ScriptParser::readSectionPatterns(StringRef OutSec, bool Keep) {
Script->Sections.emplace_back(OutSec, next(), Keep);
}

void ScriptParser::readLocationCounterValue() {
expect(".");
expect("=");
Script->Locations.push_back({Command::Expr, {}, {}});
LocationNode &Node = Script->Locations.back();
while (!Error) {
StringRef Tok = next();
if (Tok == ";")
break;
Node.Expr.push_back(Tok);
}
if (Node.Expr.empty())
error("error in location counter expression");
}

void ScriptParser::readOutputSectionDescription() {
StringRef OutSec = next();
Script->SectionOrder.push_back(OutSec);
Script->Locations.push_back({Command::Section, {}, {OutSec}});
expect(":");
expect("{");
while (!Error && !skip("}")) {
Expand Down Expand Up @@ -340,6 +457,7 @@ static bool isUnderSysroot(StringRef Path) {
void LinkerScript::read(MemoryBufferRef MB) {
StringRef Path = MB.getBufferIdentifier();
ScriptParser(&Alloc, MB.getBuffer(), isUnderSysroot(Path)).run();
Exists = true;
}

template StringRef LinkerScript::getOutputSection(InputSectionBase<ELF32LE> *);
Expand All @@ -356,3 +474,12 @@ template bool LinkerScript::shouldKeep(InputSectionBase<ELF32LE> *);
template bool LinkerScript::shouldKeep(InputSectionBase<ELF32BE> *);
template bool LinkerScript::shouldKeep(InputSectionBase<ELF64LE> *);
template bool LinkerScript::shouldKeep(InputSectionBase<ELF64BE> *);

template void
LinkerScript::assignAddresses(std::vector<OutputSectionBase<ELF32LE> *> &);
template void
LinkerScript::assignAddresses(std::vector<OutputSectionBase<ELF32BE> *> &);
template void
LinkerScript::assignAddresses(std::vector<OutputSectionBase<ELF64LE> *> &);
template void
LinkerScript::assignAddresses(std::vector<OutputSectionBase<ELF64BE> *> &);
22 changes: 22 additions & 0 deletions lld/ELF/LinkerScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace elf {

class ScriptParser;
template <class ELFT> class InputSectionBase;
template <class ELFT> class OutputSectionBase;

// This class represents each rule in SECTIONS command.
class SectionRule {
Expand All @@ -40,6 +41,17 @@ class SectionRule {
StringRef SectionPattern;
};

// This enum represents what we can observe in SECTIONS tag of script:
// Expr is a location counter change, like ". = . + 0x1000"
// Section is a description of output section, like ".data :..."
enum class Command { Expr, Section };

struct LocationNode {
Command Type;
std::vector<StringRef> Expr;
StringRef SectionName;
};

// This is a runner of the linker script.
class LinkerScript {
friend class ScriptParser;
Expand All @@ -53,9 +65,16 @@ class LinkerScript {
ArrayRef<uint8_t> getFiller(StringRef Name);
template <class ELFT> bool isDiscarded(InputSectionBase<ELFT> *S);
template <class ELFT> bool shouldKeep(InputSectionBase<ELFT> *S);
template <class ELFT>
void assignAddresses(std::vector<OutputSectionBase<ELFT> *> &S);
int compareSections(StringRef A, StringRef B);

bool Exists = false;

private:
template <class ELFT>
void fixupLocations(std::vector<OutputSectionBase<ELFT> *> &);
uint64_t evaluate(std::vector<StringRef> &Tokens, uint64_t LocCounter);
template <class ELFT> SectionRule *find(InputSectionBase<ELFT> *S);

// SECTIONS commands.
Expand All @@ -67,6 +86,9 @@ class LinkerScript {
// Section fill attribute for each section.
llvm::StringMap<std::vector<uint8_t>> Filler;

// Used to assign addresses to sections.
std::vector<LocationNode> Locations;

llvm::BumpPtrAllocator Alloc;
};

Expand Down
13 changes: 9 additions & 4 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,12 @@ template <class ELFT> void Writer<ELFT>::run() {
} else {
createPhdrs();
fixHeaders();
fixSectionAlignments();
assignAddresses();
if (Script->Exists) {
Script->assignAddresses(OutputSections);
} else {
fixSectionAlignments();
assignAddresses();
}
assignFileOffsets();
setPhdrs();
fixAbsoluteSymbols();
Expand Down Expand Up @@ -1541,10 +1545,11 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
// sections. These are special, we do not include them into output sections
// list, but have them to simplify the code.
template <class ELFT> void Writer<ELFT>::fixHeaders() {
Out<ELFT>::ElfHeader->setVA(Target->getVAStart());
uintX_t BaseVA = Script->Exists ? 0 : Target->getVAStart();
Out<ELFT>::ElfHeader->setVA(BaseVA);
Out<ELFT>::ElfHeader->setFileOffset(0);
uintX_t Off = Out<ELFT>::ElfHeader->getSize();
Out<ELFT>::ProgramHeaders->setVA(Off + Target->getVAStart());
Out<ELFT>::ProgramHeaders->setVA(Off + BaseVA);
Out<ELFT>::ProgramHeaders->setFileOffset(Off);
}

Expand Down
8 changes: 4 additions & 4 deletions lld/test/ELF/end.s
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
// NOBSS-NEXT: SHF_ALLOC
// NOBSS-NEXT: SHF_WRITE
// NOBSS-NEXT: ]
// NOBSS-NEXT: Address: 0x12000
// NOBSS-NEXT: Address: 0x159
// NOBSS-NEXT: Offset:
// NOBSS-NEXT: Size: 2
// NOBSS: ]
// NOBSS: Symbols [
// NOBSS: Name: _end
// NOBSS-NEXT: Value: 0x12002
// NOBSS-NEXT: Value: 0x15B
// NOBSS: ]

// If the layout of the sections is changed, "_end" should point to the end of allocated address space.
Expand All @@ -60,13 +60,13 @@
// TEXTATEND-NEXT: SHF_ALLOC
// TEXTATEND-NEXT: SHF_EXECINSTR
// TEXTATEND-NEXT: ]
// TEXTATEND-NEXT: Address: 0x12000
// TEXTATEND-NEXT: Address: 0x160
// TEXTATEND-NEXT: Offset:
// TEXTATEND-NEXT: Size: 1
// TEXTATEND: ]
// TEXTATEND: Symbols [
// TEXTATEND: Name: _end
// TEXTATEND-NEXT: Value: 0x12001
// TEXTATEND-NEXT: Value: 0x161
// TEXTATEND: ]

.global _start,_end
Expand Down
74 changes: 74 additions & 0 deletions lld/test/ELF/linkerscript-locationcounter.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: echo "SECTIONS { \
# RUN: . = 0x12341; \
# RUN: .data : { *(.data) } \
# RUN: . = . + 0x10000; \
# RUN: .text : { *(.text) } \
# RUN: }" > %t.script

# RUN: ld.lld %t --script %t.script -o %t2
# RUN: llvm-readobj -s %t2 | FileCheck %s

# CHECK: Sections [
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 0
# CHECK-NEXT: Name: (0)
# CHECK-NEXT: Type: SHT_NULL
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
# CHECK-NEXT: Offset: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 0
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 1
# CHECK-NEXT: Name: .data
# CHECK-NEXT: Type: SHT_PROGBITS
# CHECK-NEXT: Flags [
# CHECK-NEXT: SHF_ALLOC
# CHECK-NEXT: SHF_WRITE
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x12341
# CHECK-NEXT: Offset: 0x158
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 2
# CHECK-NEXT: Name: .text
# CHECK-NEXT: Type: SHT_PROGBITS
# CHECK-NEXT: Flags [
# CHECK-NEXT: SHF_ALLOC
# CHECK-NEXT: SHF_EXECINSTR
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x2234C
# CHECK-NEXT: Offset: 0x160
# CHECK-NEXT: Size: 1
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 4
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }

# RUN: echo "SECTIONS { \
# RUN: . = 0x12Q41; \
# RUN: }" > %t.script
# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
# RUN: FileCheck --check-prefix=NUMERR %s
# NUMERR: malformed number: 0x12Q41

.globl _start;
_start:
nop

.section .data
.quad 0

0 comments on commit ea25877

Please sign in to comment.