//===- 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 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 rawContent() const { return llvm::ArrayRef(); } 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&) { return false; } // give platform a chance to resolve platform-specific undefs virtual bool getPlatformAtoms(llvm::StringRef undefined, std::vector&) { 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&) { 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 &undefs, const std::vector &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 &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; if (kindName.getAsInteger(0, k)) k = 0; 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& f) : _files(f) { } // InputFiles interface virtual void forEachInitialAtom(InputFiles::Handler& handler) const { for (std::vector::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& _files; }; } llvm::cl::opt gInputFilePath(llvm::cl::Positional, llvm::cl::desc(""), llvm::cl::init("-")); llvm::cl::opt gOutputFilePath("o", llvm::cl::desc("Specify output filename"), llvm::cl::value_desc("filename")); llvm::cl::opt 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 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 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; }