llvm-project/lld/tools/lld-core/lld-core.cpp

439 lines
12 KiB
C++

//===- tools/lld/lld-core.cpp - Linker Core Test Driver -----------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/InputFiles.h"
#include "lld/Core/Atom.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/UndefinedAtom.h"
#include "lld/Core/Pass.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/YamlReader.h"
#include "lld/Core/YamlWriter.h"
#include "lld/Core/NativeReader.h"
#include "lld/Core/NativeWriter.h"
#include "lld/Platform/Platform.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/system_error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include <vector>
using namespace lld;
static void error(llvm::Twine message) {
llvm::errs() << "lld-core: " << message << ".\n";
}
static bool error(llvm::error_code ec) {
if (ec) {
error(ec.message());
return true;
}
return false;
}
namespace {
//
// Simple atoms created by the stubs pass.
//
class TestingStubAtom : public DefinedAtom {
public:
TestingStubAtom(const File& f, const SharedLibraryAtom& shlib) :
_file(f), _shlib(shlib) {
static uint32_t lastOrdinal = 0;
_ordinal = lastOrdinal++;
}
virtual const File& file() const {
return _file;
}
virtual llvm::StringRef name() const {
return llvm::StringRef();
}
virtual uint64_t ordinal() const {
return _ordinal;
}
virtual uint64_t size() const {
return 0;
}
virtual Scope scope() const {
return DefinedAtom::scopeLinkageUnit;
}
virtual Interposable interposable() const {
return DefinedAtom::interposeNo;
}
virtual Merge merge() const {
return DefinedAtom::mergeNo;
}
virtual ContentType contentType() const {
return DefinedAtom::typeStub;
}
virtual Alignment alignment() const {
return Alignment(0,0);
}
virtual SectionChoice sectionChoice() const {
return DefinedAtom::sectionBasedOnContent;
}
virtual llvm::StringRef customSectionName() const {
return llvm::StringRef();
}
virtual DeadStripKind deadStrip() const {
return DefinedAtom::deadStripNormal;
}
virtual ContentPermissions permissions() const {
return DefinedAtom::permR_X;
}
virtual bool isThumb() const {
return false;
}
virtual bool isAlias() const {
return false;
}
virtual llvm::ArrayRef<uint8_t> rawContent() const {
return llvm::ArrayRef<uint8_t>();
}
virtual reference_iterator referencesBegin() const {
return reference_iterator(*this, NULL);
}
virtual reference_iterator referencesEnd() const {
return reference_iterator(*this, NULL);
}
virtual const Reference* derefIterator(const void* iter) const {
return NULL;
}
virtual void incrementIterator(const void*& iter) const {
}
private:
const File& _file;
const SharedLibraryAtom& _shlib;
uint32_t _ordinal;
};
//
// A simple platform for testing.
//
class TestingPlatform : public Platform {
public:
virtual void initialize() { }
// tell platform object another file has been added
virtual void fileAdded(const File &file) { }
// tell platform object another atom has been added
virtual void atomAdded(const Atom &file) { }
// give platform a chance to change each atom's scope
virtual void adjustScope(const DefinedAtom &atom) { }
// if specified atom needs alternate names, return AliasAtom(s)
virtual bool getAliasAtoms(const Atom &atom,
std::vector<const DefinedAtom *>&) {
return false;
}
// give platform a chance to resolve platform-specific undefs
virtual bool getPlatformAtoms(llvm::StringRef undefined,
std::vector<const DefinedAtom *>&) {
return false;
}
// resolver should remove unreferenced atoms
virtual bool deadCodeStripping() {
return false;
}
// atom must be kept so should be root of dead-strip graph
virtual bool isDeadStripRoot(const Atom &atom) {
return false;
}
// if target must have some atoms, denote here
virtual bool getImplicitDeadStripRoots(std::vector<const DefinedAtom *>&) {
return false;
}
// return entry point for output file (e.g. "main") or NULL
virtual llvm::StringRef entryPointName() {
return NULL;
}
// for iterating must-be-defined symbols ("main" or -u command line option)
typedef llvm::StringRef const *UndefinesIterator;
virtual UndefinesIterator initialUndefinesBegin() const {
return NULL;
}
virtual UndefinesIterator initialUndefinesEnd() const {
return NULL;
}
// if platform wants resolvers to search libraries for overrides
virtual bool searchArchivesToOverrideTentativeDefinitions() {
return false;
}
virtual bool searchSharedLibrariesToOverrideTentativeDefinitions() {
return false;
}
// if platform allows symbol to remain undefined (e.g. -r)
virtual bool allowUndefinedSymbol(llvm::StringRef name) {
return true;
}
// for debugging dead code stripping, -why_live
virtual bool printWhyLive(llvm::StringRef name) {
return false;
}
virtual const Atom& handleMultipleDefinitions(const Atom& def1,
const Atom& def2) {
llvm::report_fatal_error("symbol '"
+ llvm::Twine(def1.name())
+ "' multiply defined");
}
// print out undefined symbol error messages in platform specific way
virtual void errorWithUndefines(const std::vector<const Atom *> &undefs,
const std::vector<const Atom *> &all) {}
// print out undefined can-be-null mismatches
virtual void undefineCanBeNullMismatch(const UndefinedAtom& undef1,
const UndefinedAtom& undef2,
bool& useUndef2) { }
// print out shared library mismatches
virtual void sharedLibrarylMismatch(const SharedLibraryAtom& shLib1,
const SharedLibraryAtom& shLib2,
bool& useShlib2) { }
// last chance for platform to tweak atoms
virtual void postResolveTweaks(std::vector<const Atom *> &all) {}
virtual bool outputUsesStubs() { return true; };
struct KindMapping {
const char* string;
Reference::Kind value;
bool isBranch;
};
static const KindMapping _s_kindMappings[];
virtual Reference::Kind kindFromString(llvm::StringRef kindName) {
for (const KindMapping* p = _s_kindMappings; p->string != NULL; ++p) {
if ( kindName.equals(p->string) )
return p->value;
}
int k;
kindName.getAsInteger(0, k);
return k;
}
virtual llvm::StringRef kindToString(Reference::Kind value) {
for (const KindMapping* p = _s_kindMappings; p->string != NULL; ++p) {
if ( value == p->value)
return p->string;
}
return llvm::StringRef("???");
}
virtual bool isBranch(const Reference* ref) {
Reference::Kind value = ref->kind();
for (const KindMapping* p = _s_kindMappings; p->string != NULL; ++p) {
if ( value == p->value )
return p->isBranch;
}
return false;
}
virtual const Atom* makeStub(const SharedLibraryAtom& shlibAtom, File& file) {
return new TestingStubAtom(file, shlibAtom);
}
};
//
// Table of fixup kinds in YAML documents used for testing
//
const TestingPlatform::KindMapping TestingPlatform::_s_kindMappings[] = {
{ "call32", 1, true },
{ "pcrel32", 2, false },
{ "gotLoad32", 3, false },
{ NULL, 0, false }
};
//
// A simple input files wrapper for testing.
//
class TestingInputFiles : public InputFiles {
public:
TestingInputFiles(std::vector<const File*>& f) : _files(f) { }
// InputFiles interface
virtual void forEachInitialAtom(InputFiles::Handler& handler) const {
for (std::vector<const File *>::iterator fit = _files.begin();
fit != _files.end(); ++fit) {
const File* file = *fit;
handler.doFile(*file);
for(auto it=file->definedAtomsBegin(),end=file->definedAtomsEnd();
it != end; ++it) {
handler.doDefinedAtom(**it);
}
for(auto it=file->undefinedAtomsBegin(), end=file->undefinedAtomsEnd();
it != end; ++it) {
handler.doUndefinedAtom(**it);
}
for(auto it=file->sharedLibraryAtomsBegin(), end=file->sharedLibraryAtomsEnd();
it != end; ++it) {
handler.doSharedLibraryAtom(**it);
}
for(auto it=file->absoluteAtomsBegin(),end=file->absoluteAtomsEnd();
it != end; ++it) {
handler.doAbsoluteAtom(**it);
}
}
}
virtual bool searchLibraries(llvm::StringRef name, bool searchDylibs,
bool searchArchives, bool dataSymbolOnly,
InputFiles::Handler &) const {
return false;
}
private:
std::vector<const File*>& _files;
};
}
llvm::cl::opt<std::string>
gInputFilePath(llvm::cl::Positional,
llvm::cl::desc("<input file>"),
llvm::cl::init("-"));
llvm::cl::opt<std::string>
gOutputFilePath("o",
llvm::cl::desc("Specify output filename"),
llvm::cl::value_desc("filename"));
llvm::cl::opt<bool>
gDoStubsPass("stubs_pass",
llvm::cl::desc("Run pass to create stub atoms"));
int main(int argc, char *argv[]) {
// Print a stack trace if we signal out.
llvm::sys::PrintStackTraceOnErrorSignal();
llvm::PrettyStackTraceProgram X(argc, argv);
llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
// parse options
llvm::cl::ParseCommandLineOptions(argc, argv);
// create platform for testing
TestingPlatform testingPlatform;
// read input YAML doc into object file(s)
std::vector<const File *> files;
if (error(yaml::parseObjectTextFileOrSTDIN(gInputFilePath,
testingPlatform, files))) {
return 1;
}
// create object to mange input files
TestingInputFiles inputFiles(files);
// merge all atom graphs
Resolver resolver(testingPlatform, inputFiles);
resolver.resolve();
// run passes
if ( gDoStubsPass ) {
StubsPass addStubs(resolver.resultFile(), testingPlatform);
addStubs.perform();
}
// write new atom graph out as YAML doc
std::string errorInfo;
const char* outPath = gOutputFilePath.empty() ? "-" : gOutputFilePath.c_str();
llvm::raw_fd_ostream out(outPath, errorInfo);
// yaml::writeObjectText(resolver.resultFile(), out);
// make unique temp .o file to put generated object file
int fd;
llvm::SmallString<128> tempPath;
llvm::sys::fs::unique_file("temp%%%%%.o", fd, tempPath);
llvm::raw_fd_ostream binaryOut(fd, /*shouldClose=*/true);
// write native file
writeNativeObjectFile(resolver.resultFile(), binaryOut);
binaryOut.close(); // manually close so that file can be read next
// out << "native file: " << tempPath.str() << "\n";
// read native file
llvm::OwningPtr<lld::File> natFile;
if ( error(parseNativeObjectFileOrSTDIN(tempPath, natFile)) )
return 1;
// write new atom graph out as YAML doc
yaml::writeObjectText(*natFile, testingPlatform, out);
// delete temp .o file
bool existed;
llvm::sys::fs::remove(tempPath.str(), existed);
return 0;
}