[LinkerScript] Process program header in PHDRS command

Add PT_PHDR segment depending on its availability in linker script's
PHDRS command, fallback if no linker script is given.
Handle FILEHDR, PHDRS and FLAGS attributes of program header.

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

llvm-svn: 244743
This commit is contained in:
Denis Protivensky
2015-08-12 12:31:35 +00:00
parent 40caea63c4
commit 0c8beb1c95
16 changed files with 389 additions and 72 deletions

View File

@@ -38,6 +38,9 @@ enum class LinkerScriptReaderError {
unknown_symbol_in_expr,
unrecognized_function_in_expr,
unknown_phdr_ids,
extra_program_phdr,
misplaced_program_phdr,
program_phdr_wrong_phdrs,
};
inline std::error_code make_error_code(LinkerScriptReaderError e) {

View File

@@ -79,7 +79,9 @@ public:
kw_entry,
kw_exclude_file,
kw_extern,
kw_filehdr,
kw_fill,
kw_flags,
kw_group,
kw_hidden,
kw_input,
@@ -818,6 +820,8 @@ public:
StringRef name() const { return _name; }
uint64_t type() const { return _type; }
bool hasFileHdr() const { return _includeFileHdr; }
bool hasPHDRs() const { return _includePHDRs; }
uint64_t flags() const { return _flags; }
bool isNone() const;
@@ -1284,7 +1288,7 @@ public:
/// Prepare our data structures according to the linker scripts currently in
/// our control (control given via addLinkerScript()). Called once all linker
/// scripts have been parsed.
void perform();
std::error_code perform();
/// Answer if we have layout commands (section mapping rules). If we don't,
/// the output file writer can assume there is no linker script special rule
@@ -1326,12 +1330,14 @@ public:
/// has been performed (by calling evalExpr() for all expressions).
uint64_t getLinkerScriptExprValue(StringRef name) const;
/// Check if there are custom headers available.
bool hasPHDRs() const;
/// Retrieve all the headers the given output section is assigned to.
/// Error is returned if the output section is assigned to headers with
/// missing declarations.
std::error_code
getPHDRsForOutputSection(StringRef name,
std::vector<const PHDR *> &phdrs) const;
std::vector<const PHDR *> getPHDRsForOutputSection(StringRef name) const;
/// Retrieve program header if available.
const PHDR *getProgramPHDR() const;
void dump() const;
@@ -1374,9 +1380,14 @@ private:
bool localCompare(int order, const SectionKey &lhs,
const SectionKey &rhs) const;
/// Convert the PHDRS command into map of names to headers.
/// Determine program header during processing.
std::error_code collectPHDRs(const PHDRS *ph,
llvm::StringMap<const PHDR *> &phdrs);
/// Build map that matches output section names to segments they should be
/// put into.
std::error_code buildSectionToPHDR();
std::error_code buildSectionToPHDR(llvm::StringMap<const PHDR *> &phdrs);
/// Our goal with all linearizeAST overloaded functions is to
/// traverse the linker script AST while putting nodes in a vector and
@@ -1433,8 +1444,6 @@ private:
void linearizeAST(const InputSectionsCmd *inputSections);
void linearizeAST(const InputSection *inputSection);
void perform(const LinkerScript *ls);
std::vector<std::unique_ptr<Parser>> _scripts;
std::vector<const Command *> _layoutCommands;
std::unordered_multimap<std::string, int> _memberToLayoutOrder;
@@ -1444,8 +1453,8 @@ private:
llvm::DenseSet<int> _deliveredExprs;
mutable llvm::StringSet<> _definedSymbols;
bool _parsedPHDRS;
llvm::StringMap<llvm::SmallVector<const PHDR *, 2>> _sectionToPHDR;
const PHDR *_programPHDR;
Expression::SymbolTableTy _symbolTable;
};

View File

@@ -58,6 +58,12 @@ public:
"expression";
case LinkerScriptReaderError::unknown_phdr_ids:
return "Unknown header identifiers (missing in PHDRS command) are used";
case LinkerScriptReaderError::extra_program_phdr:
return "Extra program header is found";
case LinkerScriptReaderError::misplaced_program_phdr:
return "Program header must precede load segments";
case LinkerScriptReaderError::program_phdr_wrong_phdrs:
return "Program header has invalid PHDRS attribute";
}
llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a "
"message defined.");

View File

@@ -748,7 +748,10 @@ bool GnuLdDriver::parse(llvm::ArrayRef<const char *> args,
return false;
// Perform linker script semantic actions
ctx->linkerScriptSema().perform();
if (auto ec = ctx->linkerScriptSema().perform()) {
diag << "Error in the linker script's semantics: " << ec.message() << "\n";
return false;
}
context.swap(ctx);
return true;

View File

@@ -423,7 +423,7 @@ template <class ELFT> int64_t Segment<ELFT>::flags() const {
}
template <class ELFT> void Segment<ELFT>::setSegmentFlags(uint64_t flags) {
assert(!_segmentFlags && !_flags && "Flags has already been set");
assert(!_segmentFlags && "Segment flags have already been set");
_segmentFlags = true;
_flags = flags;
_atomflags = toAtomPermsSegment(flags);

View File

@@ -330,12 +330,7 @@ template <class ELFT> void TargetLayout<ELFT>::createOutputSections() {
template <class ELFT>
std::vector<const script::PHDR *>
TargetLayout<ELFT>::getCustomSegments(const OutputSection<ELFT> *sec) const {
std::vector<const script::PHDR *> phdrs;
if (_linkerScriptSema.getPHDRsForOutputSection(sec->name(), phdrs)) {
llvm::report_fatal_error(
"Linker script has wrong segments set for output sections");
}
return phdrs;
return _linkerScriptSema.getPHDRsForOutputSection(sec->name());
}
template <class ELFT>
@@ -466,11 +461,36 @@ template <class ELFT> void TargetLayout<ELFT>::assignSectionsToSegments() {
}
}
}
if (_ctx.isDynamic() && !_ctx.isDynamicLibrary()) {
// Default values if no linker script is available
bool hasProgramSegment = _ctx.isDynamic() && !_ctx.isDynamicLibrary();
bool hasElfHeader = true;
bool hasProgramHeader = true;
uint64_t segmentFlags = 0;
// Check if linker script has PHDRS and program segment defined
if (_linkerScriptSema.hasPHDRs()) {
if (auto p = _linkerScriptSema.getProgramPHDR()) {
hasProgramSegment = true;
hasElfHeader = p->hasFileHdr();
hasProgramHeader = p->hasPHDRs();
segmentFlags = p->flags();
} else {
hasProgramSegment = false;
hasElfHeader = false;
hasProgramHeader = false;
}
}
if (hasProgramSegment) {
Segment<ELFT> *segment = new (_allocator) ProgramHeaderSegment<ELFT>(_ctx);
_segments.push_back(segment);
segment->append(_elfHeader);
segment->append(_programHeader);
if (segmentFlags)
segment->setSegmentFlags(segmentFlags);
if (hasElfHeader)
segment->append(_elfHeader);
if (hasProgramHeader)
segment->append(_programHeader);
}
}

View File

@@ -68,7 +68,9 @@ void Token::dump(raw_ostream &os) const {
CASE(kw_entry)
CASE(kw_exclude_file)
CASE(kw_extern)
CASE(kw_filehdr)
CASE(kw_fill)
CASE(kw_flags)
CASE(kw_group)
CASE(kw_hidden)
CASE(kw_input)
@@ -476,7 +478,9 @@ void Lexer::lex(Token &tok) {
.Case("ENTRY", Token::kw_entry)
.Case("EXCLUDE_FILE", Token::kw_exclude_file)
.Case("EXTERN", Token::kw_extern)
.Case("FILEHDR", Token::kw_filehdr)
.Case("FILL", Token::kw_fill)
.Case("FLAGS", Token::kw_flags)
.Case("GROUP", Token::kw_group)
.Case("HIDDEN", Token::kw_hidden)
.Case("INPUT", Token::kw_input)
@@ -2126,26 +2130,57 @@ const PHDR *Parser::parsePHDR() {
}
uint64_t flags = 0;
const Expression *flagsExpr = nullptr;
bool includeFileHdr = false;
bool includePHDRs = false;
if (_tok._kind == Token::identifier && _tok._range == "FLAGS") {
consumeToken();
if (!expectAndConsume(Token::l_paren, "Expected ("))
return nullptr;
const Expression *flagsExpr = parseExpression();
if (!flagsExpr)
return nullptr;
auto f = flagsExpr->evalExpr();
if (!f)
return nullptr;
flags = *f;
if (!expectAndConsume(Token::r_paren, "Expected )"))
while (_tok._kind != Token::semicolon) {
switch (_tok._kind) {
case Token::kw_filehdr:
if (includeFileHdr) {
error(_tok, "Duplicate FILEHDR attribute");
return nullptr;
}
includeFileHdr = true;
consumeToken();
break;
case Token::kw_phdrs:
if (includePHDRs) {
error(_tok, "Duplicate PHDRS attribute");
return nullptr;
}
includePHDRs = true;
consumeToken();
break;
case Token::kw_flags: {
if (flagsExpr) {
error(_tok, "Duplicate FLAGS attribute");
return nullptr;
}
consumeToken();
if (!expectAndConsume(Token::l_paren, "Expected ("))
return nullptr;
flagsExpr = parseExpression();
if (!flagsExpr)
return nullptr;
auto f = flagsExpr->evalExpr();
if (!f)
return nullptr;
flags = *f;
if (!expectAndConsume(Token::r_paren, "Expected )"))
return nullptr;
} break;
default:
error(_tok, "Unexpected token");
return nullptr;
}
}
if (!expectAndConsume(Token::semicolon, "Expected ;"))
return nullptr;
return new (getAllocator()) PHDR(name, type, false, false, nullptr, flags);
return new (getAllocator())
PHDR(name, type, includeFileHdr, includePHDRs, nullptr, flags);
}
PHDRS *Parser::parsePHDRS() {
@@ -2360,11 +2395,22 @@ Extern *Parser::parseExtern() {
}
// Sema member functions
Sema::Sema() : _parsedPHDRS(false) {}
Sema::Sema() : _programPHDR(nullptr) {}
void Sema::perform() {
for (auto &parser : _scripts)
perform(parser->get());
std::error_code Sema::perform() {
llvm::StringMap<const PHDR *> phdrs;
for (auto &parser : _scripts) {
for (const Command *c : parser->get()->_commands) {
if (const auto *sec = dyn_cast<Sections>(c)) {
linearizeAST(sec);
} else if (const auto *ph = dyn_cast<PHDRS>(c)) {
if (auto ec = collectPHDRs(ph, phdrs))
return ec;
}
}
}
return buildSectionToPHDR(phdrs);
}
bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const {
@@ -2469,18 +2515,15 @@ uint64_t Sema::getLinkerScriptExprValue(StringRef name) const {
return it->second;
}
std::error_code
Sema::getPHDRsForOutputSection(StringRef name,
std::vector<const PHDR *> &phdrs) const {
// Cache results if not done yet.
if (auto ec = const_cast<Sema *>(this)->buildSectionToPHDR())
return ec;
bool Sema::hasPHDRs() const { return !_sectionToPHDR.empty(); }
std::vector<const PHDR *> Sema::getPHDRsForOutputSection(StringRef name) const {
auto vec = _sectionToPHDR.lookup(name);
std::copy(std::begin(vec), std::end(vec), std::back_inserter(phdrs));
return std::error_code();
return std::vector<const PHDR *>(std::begin(vec), std::end(vec));
}
const PHDR *Sema::getProgramPHDR() const { return _programPHDR; }
void Sema::dump() const {
raw_ostream &os = llvm::outs();
os << "Linker script semantics dump\n";
@@ -2717,25 +2760,35 @@ bool Sema::localCompare(int order, const SectionKey &lhs,
return false;
}
std::error_code Sema::buildSectionToPHDR() {
if (_parsedPHDRS)
return std::error_code();
_parsedPHDRS = true;
std::error_code Sema::collectPHDRs(const PHDRS *ph,
llvm::StringMap<const PHDR *> &phdrs) {
bool loadFound = false;
for (auto *p : *ph) {
phdrs[p->name()] = p;
// No scripts - nothing to do.
if (_scripts.empty() || _layoutCommands.empty())
return std::error_code();
// Collect all header declarations.
llvm::StringMap<const PHDR *> phdrs;
for (auto &parser : _scripts) {
for (auto *cmd : parser->get()->_commands) {
if (auto *ph = dyn_cast<PHDRS>(cmd)) {
for (auto *p : *ph)
phdrs[p->name()] = p;
}
switch (p->type()) {
case llvm::ELF::PT_PHDR:
if (_programPHDR != nullptr)
return LinkerScriptReaderError::extra_program_phdr;
if (loadFound)
return LinkerScriptReaderError::misplaced_program_phdr;
if (!p->hasPHDRs())
return LinkerScriptReaderError::program_phdr_wrong_phdrs;
_programPHDR = p;
break;
case llvm::ELF::PT_LOAD:
// Program header, if available, should have program header table
// mapped in the first loadable segment.
if (!loadFound && _programPHDR && !p->hasPHDRs())
return LinkerScriptReaderError::program_phdr_wrong_phdrs;
loadFound = true;
break;
}
}
return std::error_code();
}
std::error_code Sema::buildSectionToPHDR(llvm::StringMap<const PHDR *> &phdrs) {
const bool noPhdrs = phdrs.empty();
// Add NONE header to the map provided there's no user-defined
@@ -2838,12 +2891,5 @@ void Sema::linearizeAST(const Sections *sections) {
}
}
void Sema::perform(const LinkerScript *ls) {
for (const Command *c : ls->_commands) {
if (const Sections *sec = dyn_cast<Sections>(c))
linearizeAST(sec);
}
}
} // End namespace script
} // end namespace lld

View File

@@ -0,0 +1,27 @@
/*
Test extra program header generates error.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error
RUN: FileCheck -check-prefix EXTRA-PROGRAM-PHDR %s < %t1-error
*/
ENTRY(_start)
PHDRS
{
header PT_PHDR PHDRS;
header2 PT_PHDR PHDRS;
text PT_LOAD;
}
SECTIONS
{
.text : { *(.text) } :NONE
.data : { *(.data) }
}
/*
EXTRA-PROGRAM-PHDR: Extra program header is found
*/

View File

@@ -0,0 +1,33 @@
/*
Test when program segment is set it's generated.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1
RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-PHDR %s
*/
ENTRY(_start)
PHDRS
{
header PT_PHDR FILEHDR PHDRS;
text PT_LOAD PHDRS;
}
SECTIONS
{
.text : { *(.text) } :text
.data : { *(.data) }
}
/*
PROGRAM-PHDR: ProgramHeader {
PROGRAM-PHDR: Type: PT_PHDR (0x6)
PROGRAM-PHDR: VirtualAddress: 0x400040
PROGRAM-PHDR: Flags [ (0x5)
PROGRAM-PHDR: PF_R (0x4)
PROGRAM-PHDR: PF_X (0x1)
PROGRAM-PHDR: ]
PROGRAM-PHDR: }
*/

View File

@@ -41,7 +41,7 @@ Test undefined header used when no PHDRS defined.
RUN: not lld -flavor gnu -target x86_64 -T %p/phdrs/undef-no-phdrs.script %t.o -static -o %t1 &> %t1-error
RUN: FileCheck -check-prefix UNDEF-NO-PHDRS %s < %t1-error
UNDEF-NO-PHDRS: Linker script has wrong segments set for output sections
UNDEF-NO-PHDRS: Unknown header identifiers (missing in PHDRS command) are used
*/
/*
@@ -50,7 +50,7 @@ Test undefined header used when PHDRS is empty.
RUN: not lld -flavor gnu -target x86_64 -T %p/phdrs/undef-empty-phdrs.script %t.o -static -o %t2 &> %t2-error
RUN: FileCheck -check-prefix UNDEF-EMPTY-PHDRS %s < %t2-error
UNDEF-EMPTY-PHDRS: Linker script has wrong segments set for output sections
UNDEF-EMPTY-PHDRS: Unknown header identifiers (missing in PHDRS command) are used
*/
/*
@@ -59,5 +59,5 @@ Test undefined header used when PHDRS contains definitions.
RUN: not lld -flavor gnu -target x86_64 -T %p/phdrs/undef-id-phdrs.script %t.o -static -o %t3 &> %t3-error
RUN: FileCheck -check-prefix UNDEF-ID-PHDRS %s < %t3-error
UNDEF-ID-PHDRS: Linker script has wrong segments set for output sections
UNDEF-ID-PHDRS: Unknown header identifiers (missing in PHDRS command) are used
*/

View File

@@ -0,0 +1,26 @@
/*
Test misplaced program header generates error.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error
RUN: FileCheck -check-prefix MISPLACED-PROGRAM-PHDR %s < %t1-error
*/
ENTRY(_start)
PHDRS
{
text PT_LOAD;
header PT_PHDR PHDRS;
}
SECTIONS
{
.text : { *(.text) } :NONE
.data : { *(.data) }
}
/*
MISPLACED-PROGRAM-PHDR: Program header must precede load segments
*/

View File

@@ -0,0 +1,25 @@
/*
Test when no program segment set it's not generated.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1
RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-PHDR %s
*/
ENTRY(_start)
PHDRS
{
text PT_LOAD;
}
SECTIONS
{
.text : { *(.text) } :text
.data : { *(.data) }
}
/*
PROGRAM-PHDR-NOT: Type: PT_PHDR (0x6)
*/

View File

@@ -0,0 +1,33 @@
/*
Test when program segment contains only FLAGS attribute.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1
RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-FLAGS-PHDR %s
*/
ENTRY(_start)
PHDRS
{
header PT_PHDR PHDRS FLAGS(0x7);
text PT_LOAD FILEHDR PHDRS;
}
SECTIONS
{
.text : { *(.text) } :text
.data : { *(.data) }
}
/*
PROGRAM-FLAGS-PHDR: ProgramHeader {
PROGRAM-FLAGS-PHDR: Type: PT_PHDR (0x6)
PROGRAM-FLAGS-PHDR: Flags [ (0x7)
PROGRAM-FLAGS-PHDR: PF_R (0x4)
PROGRAM-FLAGS-PHDR: PF_W (0x2)
PROGRAM-FLAGS-PHDR: PF_X (0x1)
PROGRAM-FLAGS-PHDR: ]
PROGRAM-FLAGS-PHDR: }
*/

View File

@@ -0,0 +1,34 @@
/*
Test when program segment contains only PHDRS attribute.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1
RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-PHDRS-PHDR %s
*/
ENTRY(_start)
PHDRS
{
header PT_PHDR PHDRS;
text PT_LOAD PHDRS;
}
SECTIONS
{
.text : { *(.text) } :text
.data : { *(.data) }
}
/*
PROGRAM-PHDRS-PHDR: ProgramHeader {
PROGRAM-PHDRS-PHDR: Type: PT_PHDR (0x6)
PROGRAM-PHDRS-PHDR: VirtualAddress: 0x400040
PROGRAM-PHDRS-PHDR: MemSize: 168
PROGRAM-PHDRS-PHDR: Flags [ (0x5)
PROGRAM-PHDRS-PHDR: PF_R (0x4)
PROGRAM-PHDRS-PHDR: PF_X (0x1)
PROGRAM-PHDRS-PHDR: ]
PROGRAM-PHDRS-PHDR: }
*/

View File

@@ -0,0 +1,26 @@
/*
Test when program segment doesn't contain PHDRS attribute.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error
RUN: FileCheck -check-prefix PROGRAM-PHDR-NO-PHDRS %s < %t1-error
*/
ENTRY(_start)
PHDRS
{
header PT_PHDR;
text PT_LOAD;
}
SECTIONS
{
.text : { *(.text) } :text
.data : { *(.data) }
}
/*
PROGRAM-PHDR-NO-PHDRS: Program header has invalid PHDRS attribute
*/

View File

@@ -0,0 +1,26 @@
/*
Test when program segment contains PHDRS attribute not mapped to load segment.
RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o
RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error
RUN: FileCheck -check-prefix PROGRAM-PHDR-WRONG-PHDRS %s < %t1-error
*/
ENTRY(_start)
PHDRS
{
header PT_PHDR PHDRS;
text PT_LOAD;
}
SECTIONS
{
.text : { *(.text) } :text
.data : { *(.data) }
}
/*
PROGRAM-PHDR-WRONG-PHDRS: Program header has invalid PHDRS attribute
*/