[InputGraph][Gnu] Add LinkerScript support.

This adds LinkerScript support by creating a type Script which is of type
FileNode in the InputGraph. Once the LinkerScript Parser converts the
LinkerScript into a sequence of command, the commands are handled by the
equivalent LinkerScript node for the current Flavor/Target. For ELF, a
ELFGNULdScript gets created which converts the commands to ELF nodes and ELF
control nodes(ELFGroup for handling Group nodes).

Since the Inputfile type has to be determined in the Driver, the Driver needs
to determine the complete path of the file that needs to be processed by the
Linker. Due to this, few tests have been removed since the Driver uses paths
that doesnot exist.

llvm-svn: 195583
This commit is contained in:
Shankar Easwaran
2013-11-24 23:12:36 +00:00
parent 98758cbe85
commit d87a021c79
13 changed files with 237 additions and 170 deletions

View File

@@ -70,6 +70,18 @@ inline llvm::error_code make_error_code(InputGraphError e) {
return llvm::error_code(static_cast<int>(e), InputGraphErrorCategory());
}
/// \brief Errors returned by Reader.
const llvm::error_category &ReaderErrorCategory();
enum class ReaderError {
success = 0,
unknown_file_format = 1
};
inline llvm::error_code make_error_code(ReaderError e) {
return llvm::error_code(static_cast<int>(e), ReaderErrorCategory());
}
} // end namespace lld
namespace llvm {
@@ -79,6 +91,7 @@ template <> struct is_error_code_enum<lld::YamlReaderError> : true_type {};
template <>
struct is_error_code_enum<lld::LinkerScriptReaderError> : true_type {};
template <> struct is_error_code_enum<lld::InputGraphError> : true_type {};
template <> struct is_error_code_enum<lld::ReaderError> : true_type {};
} // end namespace llvm
#endif

View File

@@ -44,10 +44,9 @@ public:
/// \brief Kinds of files that are supported.
enum Kind {
kindObject, ///< object file (.o)
kindSharedLibrary, ///< shared library (.so)
kindArchiveLibrary, ///< archive (.a)
kindLinkerScript, ///< linker script
kindObject, ///< object file (.o)
kindSharedLibrary, ///< shared library (.so)
kindArchiveLibrary ///< archive (.a)
};
/// \brief Returns file kind. Need for dyn_cast<> on File objects.

View File

@@ -21,6 +21,7 @@
#include "lld/Core/Resolver.h"
#include "lld/ReaderWriter/ELFLinkingContext.h"
#include "lld/ReaderWriter/FileArchive.h"
#include "lld/ReaderWriter/LinkerScript.h"
namespace lld {
@@ -137,6 +138,77 @@ private:
const ELFLinkingContext &_elfLinkingContext;
};
/// \brief Parse GNU Linker scripts.
class GNULdScript : public FileNode {
public:
GNULdScript(ELFLinkingContext &ctx, StringRef userPath, int64_t ordinal)
: FileNode(userPath, ordinal), _elfLinkingContext(ctx),
_linkerScript(nullptr)
{}
static inline bool classof(const InputElement *a) {
return a->kind() == InputElement::Kind::File;
}
/// \brief Is this node part of resolution ?
virtual bool isHidden() const { return true; }
/// \brief Validate the options
virtual bool validate() {
(void)_elfLinkingContext;
return true;
}
/// \brief Dump the Linker script.
virtual bool dump(raw_ostream &) { return true; }
/// \brief Parse the linker script.
virtual error_code parse(const LinkingContext &, raw_ostream &);
protected:
ELFLinkingContext &_elfLinkingContext;
std::unique_ptr<script::Parser> _parser;
std::unique_ptr<script::Lexer> _lexer;
script::LinkerScript *_linkerScript;
};
/// \brief Handle ELF style with GNU Linker scripts.
class ELFGNULdScript : public GNULdScript {
public:
ELFGNULdScript(ELFLinkingContext &ctx, StringRef userPath, int64_t ordinal)
: GNULdScript(ctx, userPath, ordinal) {}
virtual error_code parse(const LinkingContext &ctx, raw_ostream &diagnostics);
virtual ExpandType expandType() const {
return InputElement::ExpandType::ExpandOnly;
}
/// Unused functions for ELFGNULdScript Nodes.
virtual ErrorOr<File &> getNextFile() {
return make_error_code(InputGraphError::no_more_files);
}
/// Return the elements that we would want to expand with.
range<InputGraph::InputElementIterT> expandElements() {
return make_range(_expandElements.begin(), _expandElements.end());
}
virtual void setResolveState(uint32_t) {
llvm_unreachable("cannot use this function: setResolveState");
}
virtual uint32_t getResolveState() const {
llvm_unreachable("cannot use this function: getResolveState");
}
// Do nothing here.
virtual void resetNextIndex() {}
private:
InputGraph::InputElementVectorT _expandElements;
};
} // namespace lld
#endif

View File

@@ -117,8 +117,6 @@ public:
virtual Reader &getDefaultReader() const { return *_elfReader; }
virtual Reader &getLinkerScriptReader() const { return *_linkerScriptReader; }
/// \brief Does the output have dynamic sections.
virtual bool isDynamic() const;
@@ -214,6 +212,14 @@ public:
return true;
}
/// \brief Helper function to allocate strings.
StringRef allocateString(StringRef ref) const {
char *x = _allocator.Allocate<char>(ref.size() + 1);
memcpy(x, ref.data(), ref.size());
x[ref.size()] = '\0';
return x;
}
private:
ELFLinkingContext() LLVM_DELETED_FUNCTION;
@@ -240,7 +246,6 @@ protected:
StringRefVector _inputSearchPaths;
std::unique_ptr<Reader> _elfReader;
std::unique_ptr<Writer> _writer;
std::unique_ptr<Reader> _linkerScriptReader;
StringRef _dynamicLinkerPath;
StringRefVector _initFunctions;
StringRefVector _finiFunctions;

View File

@@ -1,34 +0,0 @@
//===- lld/ReaderWriter/ReaderLinkerScript.h ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_READER_LINKER_SCRIPT_H
#define LLD_READER_WRITER_READER_LINKER_SCRIPT_H
#include "lld/Core/LLVM.h"
#include "lld/ReaderWriter/Reader.h"
namespace lld {
class File;
class LinkingContext;
/// \brief ReaderLinkerScript is a class for reading linker scripts
class ReaderLinkerScript : public Reader {
public:
explicit ReaderLinkerScript(const LinkingContext &context)
: Reader(context) {}
/// \brief Returns a vector of Files that are contained in the archive file
/// pointed to by the Memorybuffer
error_code parseFile(std::unique_ptr<MemoryBuffer> &mb,
std::vector<std::unique_ptr<File> > &result) const;
};
} // end namespace lld
#endif

View File

@@ -127,3 +127,29 @@ const llvm::error_category &lld::InputGraphErrorCategory() {
static _InputGraphErrorCategory i;
return i;
}
class _ReaderErrorCategory : public llvm::_do_message {
public:
virtual const char *name() const { return "lld.inputGraph.parse"; }
virtual std::string message(int ev) const {
if (ReaderError(ev) == ReaderError::success)
return "Success";
else if (ReaderError(ev) == ReaderError::unknown_file_format)
return "File format for the input file is not recognized by this flavor";
llvm_unreachable("An enumerator of ReaderError does not have a "
"message defined.");
}
virtual llvm::error_condition default_error_condition(int ev) const {
if (ReaderError(ev) == ReaderError::success)
return llvm::errc::success;
return llvm::errc::invalid_argument;
}
};
const llvm::error_category &lld::ReaderErrorCategory() {
static _ReaderErrorCategory i;
return i;
}

View File

@@ -293,8 +293,6 @@ bool Resolver::resolveUndefines() {
file->setOrdinal(_context.getNextOrdinalAndIncrement());
handleSharedLibrary(*file);
break;
case File::kindLinkerScript:
llvm_unreachable("linker script should not be returned by nextFile()");
}
}
}

View File

@@ -67,6 +67,25 @@ public:
GnuLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
};
// Get the Input file magic for creating appropriate InputGraph nodes.
error_code getFileMagic(ELFLinkingContext &ctx, StringRef path,
std::vector<StringRef> &searchPaths,
llvm::sys::fs::file_magic &magic) {
error_code ec = llvm::sys::fs::identify_magic(path, magic);
if (ec)
return ec;
switch (magic) {
case llvm::sys::fs::file_magic::archive:
case llvm::sys::fs::file_magic::elf_relocatable:
case llvm::sys::fs::file_magic::elf_shared_object:
case llvm::sys::fs::file_magic::unknown:
return error_code::success();
default:
break;
}
return make_error_code(ReaderError::unknown_file_format);
}
} // namespace
llvm::ErrorOr<StringRef> ELFFileNode::getPath(const LinkingContext &) const {
@@ -268,9 +287,47 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
case OPT_INPUT:
case OPT_l: {
std::unique_ptr<InputElement> inputFile(new ELFFileNode(
*ctx, inputArg->getValue(), searchPath, index++, isWholeArchive,
asNeeded, inputArg->getOption().getID() == OPT_l));
bool isDashlPrefix = (inputArg->getOption().getID() == OPT_l);
bool isELFFileNode = true;
StringRef userPath = inputArg->getValue();
std::string resolvedInputPath = userPath;
// If the path was referred to by using a -l argument, lets search
// for the file in the search path.
if (isDashlPrefix) {
ErrorOr<StringRef> resolvedPath =
ctx->searchLibrary(userPath, searchPath);
if (!resolvedPath) {
diagnostics << " Unable to find library -l" << userPath << "\n";
return false;
}
resolvedInputPath = resolvedPath->str();
}
llvm::sys::fs::file_magic magic = llvm::sys::fs::file_magic::unknown;
error_code ec = getFileMagic(*ctx, resolvedInputPath, searchPath, magic);
if (ec) {
diagnostics << "lld: unknown input file format for file " << userPath
<< "\n";
return false;
}
if ((!userPath.endswith(".objtxt")) &&
(magic == llvm::sys::fs::file_magic::unknown))
isELFFileNode = false;
FileNode *inputNode = nullptr;
if (isELFFileNode)
inputNode = new ELFFileNode(*ctx, userPath, searchPath, index++,
isWholeArchive, asNeeded, isDashlPrefix);
else {
inputNode = new ELFGNULdScript(*ctx, resolvedInputPath, index++);
ec = inputNode->parse(*ctx, diagnostics);
if (ec) {
diagnostics << userPath << ": Error parsing linker script"
<< "\n";
return false;
}
}
std::unique_ptr<InputElement> inputFile(inputNode);
if (controlNodeStack.empty())
inputGraph->addInputElement(std::move(inputFile));
else
@@ -336,6 +393,9 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
if (!ctx->validate(diagnostics))
return false;
// Normalize the InputGraph.
inputGraph->normalize();
ctx->setInputGraph(std::move(inputGraph));
context.swap(ctx);

View File

@@ -52,10 +52,59 @@ error_code ELFFileNode::parse(const LinkingContext &ctx,
}
return ec;
}
default:
// Process Linker script
return _elfLinkingContext.getLinkerScriptReader().parseFile(_buffer,
_files);
break;
}
return error_code::success();
}
/// \brief Parse the GnuLD Script
error_code GNULdScript::parse(const LinkingContext &ctx,
raw_ostream &diagnostics) {
ErrorOr<StringRef> filePath = getPath(ctx);
if (!filePath)
return error_code(filePath);
if (error_code ec = getBuffer(*filePath))
return ec;
if (ctx.logInputFiles())
diagnostics << *filePath << "\n";
_lexer.reset(new script::Lexer(std::move(_buffer)));
_parser.reset(new script::Parser(*_lexer.get()));
_linkerScript = _parser->parse();
if (!_linkerScript)
return LinkerScriptReaderError::parse_error;
return error_code::success();
}
/// \brief Handle GnuLD script for ELF.
error_code ELFGNULdScript::parse(const LinkingContext &ctx,
raw_ostream &diagnostics) {
int64_t index = 0;
std::vector<StringRef> searchPath;
if (error_code ec = GNULdScript::parse(ctx, diagnostics))
return ec;
for (const auto &c : _linkerScript->_commands) {
if (auto group = dyn_cast<script::Group>(c)) {
std::unique_ptr<InputElement> controlStart(
new ELFGroup(_elfLinkingContext, index++));
for (auto &path : group->getPaths()) {
// TODO : Propagate SearchPath, Set WholeArchive/dashlPrefix
auto inputNode = new ELFFileNode(
_elfLinkingContext, _elfLinkingContext.allocateString(path._path),
searchPath, index++, false, path._asNeeded, false);
std::unique_ptr<InputElement> inputFile(inputNode);
dyn_cast<ControlNode>(controlStart.get())
->processInputElement(std::move(inputFile));
}
_expandElements.push_back(std::move(controlStart));
}
}
return error_code::success();
}

View File

@@ -9,7 +9,6 @@ add_lld_library(lldReaderWriter
CoreLinkingContext.cpp
LinkerScript.cpp
Reader.cpp
ReaderLinkerScript.cpp
Writer.cpp
)

View File

@@ -18,7 +18,6 @@
#include "lld/Passes/LayoutPass.h"
#include "lld/Passes/RoundTripNativePass.h"
#include "lld/Passes/RoundTripYAMLPass.h"
#include "lld/ReaderWriter/ReaderLinkerScript.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/ELF.h"
@@ -83,7 +82,6 @@ StringRef ELFLinkingContext::entrySymbolName() const {
bool ELFLinkingContext::validateImpl(raw_ostream &diagnostics) {
_elfReader = createReaderELF(*this);
_linkerScriptReader.reset(new ReaderLinkerScript(*this));
switch (outputFileType()) {
case LinkingContext::OutputFileType::YAML:
_writer = createWriterYAML(*this);

View File

@@ -1,98 +0,0 @@
//===- lib/ReaderWriter/ReaderLinkerScript.cpp ----------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/ReaderWriter/ReaderLinkerScript.h"
#include "lld/Core/Error.h"
#include "lld/Core/File.h"
#include "lld/ReaderWriter/LinkerScript.h"
using namespace lld;
using namespace script;
namespace {
class LinkerScriptFile : public File {
public:
static ErrorOr<std::unique_ptr<LinkerScriptFile> >
create(const LinkingContext &context,
std::unique_ptr<MemoryBuffer> mb) {
std::unique_ptr<LinkerScriptFile> file(
new LinkerScriptFile(context, std::move(mb)));
file->_script = file->_parser.parse();
if (!file->_script)
return LinkerScriptReaderError::parse_error;
return std::move(file);
}
static inline bool classof(const File *f) {
return f->kind() == kindLinkerScript;
}
virtual const LinkingContext &getLinkingContext() const { return _context; }
virtual const atom_collection<DefinedAtom> &defined() const {
return _definedAtoms;
}
virtual const atom_collection<UndefinedAtom> &undefined() const {
return _undefinedAtoms;
}
virtual const atom_collection<SharedLibraryAtom> &sharedLibrary() const {
return _sharedLibraryAtoms;
}
virtual const atom_collection<AbsoluteAtom> &absolute() const {
return _absoluteAtoms;
}
const LinkerScript *getScript() { return _script; }
private:
LinkerScriptFile(const LinkingContext &context,
std::unique_ptr<MemoryBuffer> mb)
: File(mb->getBufferIdentifier(), kindLinkerScript), _context(context),
_lexer(std::move(mb)), _parser(_lexer), _script(nullptr) {}
const LinkingContext &_context;
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
Lexer _lexer;
Parser _parser;
const LinkerScript *_script;
};
} // end anon namespace
namespace lld {
error_code ReaderLinkerScript::parseFile(
std::unique_ptr<MemoryBuffer> &mb,
std::vector<std::unique_ptr<File> > &result) const {
auto lsf = LinkerScriptFile::create(_context, std::move(mb));
if (!lsf)
return lsf;
const LinkerScript *ls = (*lsf)->getScript();
result.push_back(std::move(*lsf));
for (const auto &c : ls->_commands) {
if (auto group = dyn_cast<lld::script::Group>(c))
for (const auto &path : group->getPaths()) {
OwningPtr<MemoryBuffer> opmb;
if (error_code ec =
MemoryBuffer::getFileOrSTDIN(path._path, opmb))
return ec;
std::unique_ptr<MemoryBuffer> eachMB(opmb.take());
if (error_code ec =
_context.getDefaultReader().parseFile(eachMB, result))
return ec;
}
}
return error_code::success();
}
} // end namespace lld

View File

@@ -33,24 +33,4 @@ TEST_F(GnuLdParserTest, Empty) {
EXPECT_EQ("No input files\n", errorMessage());
}
TEST_F(GnuLdParserTest, Basic) {
EXPECT_TRUE(parse("ld", "infile.o", nullptr));
EXPECT_NE(linkingContext(), nullptr);
EXPECT_EQ("a.out", linkingContext()->outputPath());
EXPECT_EQ(1, inputFileCount());
EXPECT_EQ("infile.o", inputFile(0));
EXPECT_FALSE(_context->outputFileType() ==
LinkingContext::OutputFileType::YAML);
}
TEST_F(GnuLdParserTest, ManyOptions) {
EXPECT_TRUE(parse("ld", "-entry", "_start", "-o", "outfile",
"--output-filetype=yaml", "infile.o", nullptr));
EXPECT_NE(linkingContext(), nullptr);
EXPECT_EQ("outfile", linkingContext()->outputPath());
EXPECT_EQ("_start", linkingContext()->entrySymbolName());
EXPECT_TRUE(_context->outputFileType() ==
LinkingContext::OutputFileType::YAML);
}
} // end anonymous namespace