diff options
author | Lai Wei-Chih <Robert.Lai@mediatek.com> | 2013-11-20 13:38:38 +0800 |
---|---|---|
committer | Andrew Hsieh <andrewhsieh@google.com> | 2015-03-24 11:54:19 -0700 |
commit | 6d1a6d60118b36d4823eb339f76a0f8108b5c9f5 (patch) | |
tree | a4b88b45834d30c51a04eab0bd6c1aa29a470894 | |
parent | 05fc2631081d6bfcaab1be6ac1eee6ce2ecde9bd (diff) | |
download | llvm-6d1a6d60118b36d4823eb339f76a0f8108b5c9f5.tar.gz |
[ndk][pndk] Add bitcode link tool ndk-link.
Author: Lai Wei-Chih <Robert.Lai@mediatek.com>;
Author: WenHan Gu <Wenhan.gu@mediatek.com>;
Author: Logan Chien <loganchien@google.com>;
-rw-r--r-- | tools/Makefile | 3 | ||||
-rw-r--r-- | tools/ndk-link/AndroidBitcodeLinker.cpp | 451 | ||||
-rw-r--r-- | tools/ndk-link/AndroidBitcodeLinker.h | 217 | ||||
-rw-r--r-- | tools/ndk-link/Archive.cpp | 253 | ||||
-rw-r--r-- | tools/ndk-link/Archive.h | 510 | ||||
-rw-r--r-- | tools/ndk-link/ArchiveInternals.h | 87 | ||||
-rw-r--r-- | tools/ndk-link/ArchiveReader.cpp | 544 | ||||
-rw-r--r-- | tools/ndk-link/ArchiveWriter.cpp | 291 | ||||
-rw-r--r-- | tools/ndk-link/CMakeLists.txt | 18 | ||||
-rw-r--r-- | tools/ndk-link/Makefile | 14 | ||||
-rw-r--r-- | tools/ndk-link/ndk-link.cpp | 576 |
11 files changed, 2963 insertions, 1 deletions
diff --git a/tools/Makefile b/tools/Makefile index b559edacd52..4667a79bf75 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -33,7 +33,8 @@ PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ macho-dump llvm-objdump llvm-readobj llvm-rtdyld \ llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \ llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \ - llvm-vtabledump verify-uselistorder dsymutil + llvm-vtabledump verify-uselistorder dsymutil \ + ndk-link # If Intel JIT Events support is configured, build an extra tool to test it. ifeq ($(USE_INTEL_JITEVENTS), 1) diff --git a/tools/ndk-link/AndroidBitcodeLinker.cpp b/tools/ndk-link/AndroidBitcodeLinker.cpp new file mode 100644 index 00000000000..028d50ef927 --- /dev/null +++ b/tools/ndk-link/AndroidBitcodeLinker.cpp @@ -0,0 +1,451 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AndroidBitcodeLinker.h" +#include "Archive.h" + +#include "llvm/ADT/SetOperations.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Linker/Linker.h" +#include "llvm/PassManager.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Wrap/BitcodeWrapper.h" + +#include <memory> +#include <set> +#include <system_error> +#include <vector> + +using namespace llvm; + +// Generate current module to std::string +std::string* AndroidBitcodeLinker::GenerateBitcode() { + std::string *BCString = new std::string; + Module *M = linker->getModule(); + + PassManager PM; + raw_string_ostream Bitcode(*BCString); + + PM.add(createVerifierPass()); + PM.add(new DataLayoutPass()); + + if (!Config.isDisableOpt()) { + PassManagerBuilder PMBuilder; + PMBuilder.Inliner = createFunctionInliningPass(); + PMBuilder.populateLTOPassManager(PM); + } + // Doing clean up passes + if (!Config.isDisableOpt()) + { + PM.add(createInstructionCombiningPass()); + PM.add(createCFGSimplificationPass()); + PM.add(createAggressiveDCEPass()); + PM.add(createGlobalDCEPass()); + } + + // Make sure everything is still good + PM.add(createVerifierPass()); + + // Strip debug info and symbols. + if (Config.isStripAll() || Config.isStripDebug()) + PM.add(createStripSymbolsPass(Config.isStripDebug() && !Config.isStripAll())); + + PM.add(createBitcodeWriterPass(Bitcode)); + PM.run(*M); + Bitcode.flush(); + + // Re-compute defined and undefined symbols + UpdateSymbolList(M); + + delete M; + delete linker; + linker = 0; + + return BCString; +} + +Module * +AndroidBitcodeLinker::LoadAndroidBitcode(AndroidBitcodeItem &Item) { + const StringRef &FN = Item.getFile(); + + ErrorOr<std::unique_ptr<MemoryBuffer> > BufferOrErr = + MemoryBuffer::getFileOrSTDIN(FN.data()); + if (!BufferOrErr) { + Error = "Error reading file '" + FN.str() + "'" + ": " + + BufferOrErr.getError().message(); + return NULL; + } + + std::unique_ptr<MemoryBuffer> &Buffer = BufferOrErr.get(); + BitcodeWrapper *wrapper = new BitcodeWrapper(Buffer->getBufferStart(), + Buffer->getBufferSize()); + Item.setWrapper(wrapper); + assert(Item.getWrapper() != 0); + ErrorOr<Module*> Result = parseBitcodeFile(Buffer->getMemBufferRef(), + Config.getContext()); + if (!Result) { + Error = "Bitcode file '" + FN.str() + "' could not be loaded." + + Result.getError().message(); + errs() << Error << '\n'; + } + + return Result.get(); +} + +void +AndroidBitcodeLinker::UpdateSymbolList(Module *M) { + std::set<std::string> UndefinedSymbols; + std::set<std::string> DefinedSymbols; + GetAllSymbols(M, UndefinedSymbols, DefinedSymbols); + + // Update global undefined/defined symbols + set_union(GlobalDefinedSymbols, DefinedSymbols); + set_union(GlobalUndefinedSymbols, UndefinedSymbols); + set_subtract(GlobalUndefinedSymbols, GlobalDefinedSymbols); + + verbose("Dump global defined symbols:"); + for (std::set<std::string>::iterator I = DefinedSymbols.begin(); + I != DefinedSymbols.end(); ++I) + verbose("D:" + *I); + + verbose("Dump global undefined symbols:"); + for (std::set<std::string>::iterator I = GlobalUndefinedSymbols.begin(); + I != GlobalUndefinedSymbols.end(); ++I) + verbose("U:" + *I); +} + +bool +AndroidBitcodeLinker::LinkInAndroidBitcodes(ABCItemList& Items, + std::vector<std::string*> &BCStrings) { + // Create llvm::Linker + linker = new Linker(new Module(Config.getModuleName(), Config.getContext())); + + for (ABCItemList::iterator I = Items.begin(), E = Items.end(); + I != E; ++I) { + if (LinkInAndroidBitcode(*I)) + return true; + } + + if (linker != 0) + BCStrings.push_back(GenerateBitcode()); + + return false; +} + +// +// Link in bitcode relocatables and bitcode archive +// +bool +AndroidBitcodeLinker::LinkInAndroidBitcode(AndroidBitcodeItem &Item) { + const StringRef &File = Item.getFile(); + + if (File.str() == "-") { + return error("Not supported!"); + } + + if (!sys::fs::exists(File)) + return error("Cannot find linker input '" + File.str() + "'"); + + sys::fs::file_magic Magic; + if (sys::fs::identify_magic(File, Magic)) + return error("Cannot find linker input '" + File.str() + "'"); + + switch (Magic) { + case sys::fs::file_magic::archive: { + if (Item.isWholeArchive()) { + verbose("Link whole archive" + File.str()); + if (LinkInWholeArchive(Item)) + return true; + } + else { + verbose("Link no-whole archive" + File.str()); + if (LinkInArchive(Item)) + return true; + } + break; + } + + case sys::fs::file_magic::bitcode: { + + verbose("Linking bitcode file '" + File.str() + "'"); + + std::unique_ptr<Module> M(LoadAndroidBitcode(Item)); + + int BCFileType = Item.getWrapper()->getBCFileType(); + int BitcodeType = -1; + + if (BCFileType == BC_RAW) + BitcodeType = BCHeaderField::BC_Relocatable; + else if (BCFileType == BC_WRAPPER) + BitcodeType = Item.getWrapper()->getBitcodeType(); + else + return error("Invalid bitcode file type" + File.str()); + + if (M.get() == 0) + return error("Cannot load file '" + File.str() + "': " + Error); + + Triple triple(M.get()->getTargetTriple()); + + if (triple.getArch() != Triple::le32 || triple.getOS() != Triple::NDK) { + Item.setNative(true); + return error("Cannot link '" + File.str() + "', triple:" + M.get()->getTargetTriple()); + } + + switch (BitcodeType) { + default: + return error("Unknown android bitcode type"); + + case BCHeaderField::BC_Relocatable: + assert(linker != 0); + if (linker->linkInModule(M.get())) + return error("Cannot link file '" + File.str() + "'"); + break; + + case BCHeaderField::BC_SharedObject: + break; + + case BCHeaderField::BC_Executable: + return error("Cannot link bitcode executable: " + File.str()); + } + break; + } + case sys::fs::file_magic::elf_shared_object: { + Item.setNative(true); + if (!Config.isLinkNativeBinary()) { + return error("Cannot link native binaries with bitcode" + File.str()); + } + break; + } + case sys::fs::file_magic::elf_relocatable: { + return error("Cannot link ELF relocatable:" + File.str()); + } + case sys::fs::file_magic::elf_executable: { + return error("Cannot link ELF executable:" + File.str()); + } + default: { + return error("Ignoring file '" + File.str() + + "' because does not contain bitcode."); + } + } + return false; +} + +bool +AndroidBitcodeLinker::LinkInWholeArchive(AndroidBitcodeItem &Item) { +const StringRef &Filename = Item.getFile(); + + // Open the archive file + verbose("Linking archive file '" + Filename.str() + "'"); + + std::string ErrMsg; + std::unique_ptr<Archive> AutoArch( + Archive::OpenAndLoad(Filename, Config.getContext(), &ErrMsg)); + Archive* arch = AutoArch.get(); + + // possible empty archive? + if (!arch) { + return false; + } + + if (!arch->isBitcodeArchive()) { + Item.setNative(true); + if (Config.isLinkNativeBinary()) { + return false; + } + else { + return error("Cannot link native binaries with bitcode" + Filename.str()); + } + } + + std::vector<Module*> Modules; + + if (arch->getAllModules(Modules, &ErrMsg)) + return error("Cannot read modules in '" + Filename.str() + "': " + ErrMsg); + + if (Modules.empty()) { + return false; + } + + // Loop over all the Modules + for (std::vector<Module*>::iterator I=Modules.begin(), E=Modules.end(); + I != E; ++I) { + // Get the module we must link in. + Module* aModule = *I; + if (aModule != NULL) { + if (std::error_code ec = aModule->materializeAll()) + return error("Could not load a module: " + ec.message()); + + verbose(" Linking in module: " + aModule->getModuleIdentifier()); + + assert(linker != 0); + // Link it in + if (linker->linkInModule(aModule)) + return error("Cannot link in module '" + + aModule->getModuleIdentifier() + "'"); + delete aModule; + } + } + + /* Success! */ + return false; +} + +void +AndroidBitcodeLinker::GetAllSymbols(Module *M, + std::set<std::string> &UndefinedSymbols, + std::set<std::string> &DefinedSymbols) { + + UndefinedSymbols.clear(); + DefinedSymbols.clear(); + + Function *Main = M->getFunction("main"); + if (Main == 0 || Main->isDeclaration()) + UndefinedSymbols.insert("main"); + + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + if (I->hasName()) { + if (I->isDeclaration()) + UndefinedSymbols.insert(I->getName()); + else if (!I->hasLocalLinkage()) { + assert(!I->hasDLLImportStorageClass() + && "Found dllimported non-external symbol!"); + DefinedSymbols.insert(I->getName()); + } + } + + for (Module::global_iterator I = M->global_begin(), E = M->global_end(); + I != E; ++I) + if (I->hasName()) { + if (I->isDeclaration()) + UndefinedSymbols.insert(I->getName()); + else if (!I->hasLocalLinkage()) { + assert(!I->hasDLLImportStorageClass() + && "Found dllimported non-external symbol!"); + DefinedSymbols.insert(I->getName()); + } + } + + for (Module::alias_iterator I = M->alias_begin(), E = M->alias_end(); + I != E; ++I) + if (I->hasName()) + DefinedSymbols.insert(I->getName()); + + for (std::set<std::string>::iterator I = UndefinedSymbols.begin(); + I != UndefinedSymbols.end(); ) + if (DefinedSymbols.count(*I)) + UndefinedSymbols.erase(I++); + else + ++I; +} + +bool +AndroidBitcodeLinker::LinkInArchive(AndroidBitcodeItem &Item) { +const StringRef &Filename = Item.getFile(); + + verbose("Linking archive file '" + Filename.str() + "'"); + + std::set<std::string> UndefinedSymbols; + std::set<std::string> DefinedSymbols; + GetAllSymbols(linker->getModule(), UndefinedSymbols, DefinedSymbols); + + // Update list + set_union(UndefinedSymbols, GlobalUndefinedSymbols); + set_union(DefinedSymbols, GlobalDefinedSymbols); + set_subtract(UndefinedSymbols, DefinedSymbols); + + if (UndefinedSymbols.empty()) { + verbose("No symbols undefined, skipping library '" + Filename.str() + "'"); + return false; // No need to link anything in! + } + + std::string ErrMsg; + std::unique_ptr<Archive> AutoArch( + Archive::OpenAndLoadSymbols(Filename, Config.getContext(), &ErrMsg)); + + Archive* arch = AutoArch.get(); + + // possible empty archive? + if (!arch) { + return false; + } + + if (!arch->isBitcodeArchive()) { + Item.setNative(true); + if (Config.isLinkNativeBinary()) { + return false; + } + else { + return error("Cannot link native binaries with bitcode" + Filename.str()); + } + } + + std::set<std::string> NotDefinedByArchive; + + std::set<std::string> CurrentlyUndefinedSymbols; + + do { + CurrentlyUndefinedSymbols = UndefinedSymbols; + + SmallVector<Module*, 16> Modules; + if (!arch->findModulesDefiningSymbols(UndefinedSymbols, Modules, &ErrMsg)) + return error("Cannot find symbols in '" + Filename.str() + + "': " + ErrMsg); + + if (Modules.empty()) + break; + + NotDefinedByArchive.insert(UndefinedSymbols.begin(), + UndefinedSymbols.end()); + + for (SmallVectorImpl<Module*>::iterator I=Modules.begin(), E=Modules.end(); + I != E; ++I) { + + Module* aModule = *I; + if (aModule != NULL) { + if (std::error_code ec = aModule->materializeAll()) + return error("Could not load a module: " + ec.message()); + + verbose(" Linking in module: " + aModule->getModuleIdentifier()); + + // Link it in + std::string moduleErrorMsg; + if (linker->linkInModule(aModule)) + return error("Cannot link in module '" + + aModule->getModuleIdentifier() + "'"); + } + } + + GetAllSymbols(linker->getModule(), UndefinedSymbols, DefinedSymbols); + + set_subtract(UndefinedSymbols, NotDefinedByArchive); + + if (UndefinedSymbols.empty()) + break; + } while (CurrentlyUndefinedSymbols != UndefinedSymbols); + + return false; +} diff --git a/tools/ndk-link/AndroidBitcodeLinker.h b/tools/ndk-link/AndroidBitcodeLinker.h new file mode 100644 index 00000000000..16d6b75f4ad --- /dev/null +++ b/tools/ndk-link/AndroidBitcodeLinker.h @@ -0,0 +1,217 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BITCODE_LINKER_H +#define ANDROID_BITCODE_LINKER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Linker/Linker.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Wrap/BitcodeWrapper.h" +#include <cstdio> +#include <cstring> +#include <set> +#include <string> +#include <vector> + +namespace llvm { + +class AndroidBitcodeItem { + + public: + AndroidBitcodeItem(std::string FileName, bool isWhole = false) : + File(FileName), WholeArchive(isWhole), NativeBinary(false), + Wrapper(0) { + } + + ~AndroidBitcodeItem() { + if (Wrapper != 0) + delete Wrapper; + } + + void setWholeArchive(bool whole) { WholeArchive = whole; } + + void setNative(bool native) { NativeBinary = native; } + + void setWrapper(BitcodeWrapper *wrapper) { + if (Wrapper != 0) + delete Wrapper; + + Wrapper = wrapper; + parseLDFlags(Wrapper->getLDFlags()); + } + + int getBitcodeType() { + if (Wrapper != 0) + return Wrapper->getBitcodeType(); + return 0; + } + + std::string getSOName() { + return SOName; + } + + bool isWholeArchive() { return WholeArchive; } + + bool isNative() { return NativeBinary; } + + BitcodeWrapper* getWrapper() { return Wrapper; } + + const StringRef getFile() { return File; } + + private: + + void parseLDFlags(const std::string &ldflags) { + char* str = strdup(ldflags.c_str()); + char* input; + std::vector<char *> inputs; + input = strtok (str, " "); + + while (input != NULL) { + inputs.push_back(input); + input = strtok (NULL, " "); + } + + for (unsigned i = 0; i < inputs.size(); i++) { + if (!strcmp(inputs[i],"-soname")) + SOName = inputs[i+1]; + // if -lx + // push -lx to DepLibs + } + free(str); + } + + private: + std::string File; + bool WholeArchive; + bool NativeBinary; + std::string SOName; + std::vector<std::string> DepLibs; + BitcodeWrapper *Wrapper; +}; + +class LinkerConfig { + public: + /// This enumeration is used to control various optional features of the + /// linker. + enum ControlFlags { + Verbose = 1, ///< Print to stderr what steps the linker is taking + QuietWarnings = 2, ///< Don't print warnings to stderr. + QuietErrors = 4 ///< Don't print errors to stderr. + }; + + public: + LinkerConfig(LLVMContext& context, StringRef progname, + StringRef modulename, unsigned flags, + bool disableopt, bool stripall, bool stripdebug, bool nativebinary) : + C(context), ProgName(progname), ModuleName(modulename), + Flags(flags), DisableOpt(disableopt), StripAll(stripall), + StripDebug(stripdebug), LinkNativeBinary(nativebinary) { + } + + StringRef& getProgName() { return ProgName; } + + StringRef& getModuleName() { return ModuleName; } + + LLVMContext& getContext() { return C; } + + unsigned getFlags() { return Flags; } + + bool isDisableOpt() { return DisableOpt; } + + bool isStripAll() { return StripAll; } + + bool isStripDebug() { return StripDebug; } + + bool isLinkNativeBinary() { return LinkNativeBinary; } + + private: + LLVMContext &C; + StringRef ProgName; + StringRef ModuleName; + unsigned Flags; + bool DisableOpt; + bool StripAll; + bool StripDebug; + bool LinkNativeBinary; +}; + +class AndroidBitcodeLinker { + public: + typedef std::vector<AndroidBitcodeItem> ABCItemList; + + AndroidBitcodeLinker(LinkerConfig &config) : + Config(config), linker(0) { + } + + ~AndroidBitcodeLinker() { + if (linker != 0) + delete linker; + } + + // main procedure to link bitcodes + bool LinkInAndroidBitcodes(ABCItemList& Item, std::vector<std::string*> &BCStrings); + + private: + + bool LinkInAndroidBitcode(AndroidBitcodeItem& Item); + + bool LinkInArchive(AndroidBitcodeItem &Item); + + bool LinkInWholeArchive(AndroidBitcodeItem &Item); + + Module* LoadAndroidBitcode(AndroidBitcodeItem &Item); + + std::string* GenerateBitcode(); + + void UpdateSymbolList(Module* M); + + void GetAllSymbols(Module *M, std::set<std::string> &UndefinedSymbols, + std::set<std::string> &DefinedSymbols); + + bool warning(StringRef message) { + Error = message; + if (!(Config.getFlags() & LinkerConfig::QuietWarnings)) + errs() << Config.getProgName() << ": warning: " << message << "\n"; + return false; + } + + bool error(StringRef message) { + Error = message; + if (!(Config.getFlags() & LinkerConfig::QuietErrors)) + errs() << Config.getProgName() << ": error: " << message << "\n"; + return true; + } + + void verbose(StringRef message) { + if (Config.getFlags() & LinkerConfig::Verbose) + errs() << " " << message << "\n"; + } + + private: + std::set<std::string> GlobalUndefinedSymbols; + std::set<std::string> GlobalDefinedSymbols; + LinkerConfig& Config; + Linker* linker; + std::string Error; +}; + +} // end namespace llvm + +#endif diff --git a/tools/ndk-link/Archive.cpp b/tools/ndk-link/Archive.cpp new file mode 100644 index 00000000000..02cd0aa37ba --- /dev/null +++ b/tools/ndk-link/Archive.cpp @@ -0,0 +1,253 @@ +//===-- Archive.cpp - Generic LLVM archive functions ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of the Archive and ArchiveMember +// classes that is common to both reading and writing archives.. +// +//===----------------------------------------------------------------------===// + +#include "Archive.h" +#include "ArchiveInternals.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" +#include <cstring> +#include <memory> +#include <system_error> +using namespace llvm; + +// getMemberSize - compute the actual physical size of the file member as seen +// on disk. This isn't the size of member's payload. Use getSize() for that. +unsigned +ArchiveMember::getMemberSize() const { + // Basically its the file size plus the header size + unsigned result = Size + sizeof(ArchiveMemberHeader); + + // If it has a long filename, include the name length + if (hasLongFilename()) + result += path.length() + 1; + + // If its now odd lengthed, include the padding byte + if (result % 2 != 0 ) + result++; + + return result; +} + +// This default constructor is only use by the ilist when it creates its +// sentry node. We give it specific static values to make it stand out a bit. +ArchiveMember::ArchiveMember() + : parent(0), path("--invalid--"), flags(0), data(0) +{ + User = 65536; + Group = 65536; + Mode = 0777; + Size = 0; + ModTime = sys::TimeValue::now(); +} + +// This is the constructor that the Archive class uses when it is building or +// reading an archive. It just defaults a few things and ensures the parent is +// set for the iplist. The Archive class fills in the ArchiveMember's data. +// This is required because correctly setting the data may depend on other +// things in the Archive. +ArchiveMember::ArchiveMember(Archive* PAR) + : parent(PAR), path(), flags(0), data(0) +{ +} + +// This method allows an ArchiveMember to be replaced with the data for a +// different file, presumably as an update to the member. It also makes sure +// the flags are reset correctly. +bool ArchiveMember::replaceWith(StringRef newFile, std::string* ErrMsg) { + if (!sys::fs::exists(newFile)) { + if (ErrMsg) + *ErrMsg = "Can not replace an archive member with a non-existent file"; + return true; + } + + data = 0; + path = newFile.str(); + + // SVR4 symbol tables have an empty name + if (path == ARFILE_SVR4_SYMTAB_NAME) + flags |= SVR4SymbolTableFlag; + else + flags &= ~SVR4SymbolTableFlag; + + // BSD4.4 symbol tables have a special name + if (path == ARFILE_BSD4_SYMTAB_NAME) + flags |= BSD4SymbolTableFlag; + else + flags &= ~BSD4SymbolTableFlag; + + // String table name + if (path == ARFILE_STRTAB_NAME) + flags |= StringTableFlag; + else + flags &= ~StringTableFlag; + + // If it has a slash or its over 15 chars then its a long filename format + if (path.length() > 15) + flags |= HasLongFilenameFlag; + else + flags &= ~HasLongFilenameFlag; + + // Get the file status info + sys::fs::file_status Status; + if (sys::fs::status(path, Status)) { + return true; + } + + User = Status.getUser(); + Group = Status.getGroup(); + Mode = Status.permissions(); + ModTime = Status.getLastModificationTime(); + Size = Status.getSize(); + + // Determine what kind of file it is. + sys::fs::file_magic magic = sys::fs::file_magic::unknown; + if (sys::fs::identify_magic(path, magic)) { + return true; + } + if (magic == sys::fs::file_magic::bitcode) + flags |= BitcodeFlag; + else + flags &= ~BitcodeFlag; + + return false; +} + +// Archive constructor - this is the only constructor that gets used for the +// Archive class. Everything else (default,copy) is deprecated. This just +// initializes and maps the file into memory, if requested. +Archive::Archive(StringRef filename, LLVMContext &C) + : archPath(filename), members(), mapfile(0), base(0), symTab(), strtab(), + symTabSize(0), firstFileOffset(0), modules(), Context(C) {} + +bool +Archive::mapToMemory(std::string* ErrMsg) { + ErrorOr<std::unique_ptr<MemoryBuffer> > FileOrErr = + MemoryBuffer::getFile(archPath.str()); + if (!FileOrErr) { + if (ErrMsg) + *ErrMsg = FileOrErr.getError().message(); + return true; + } + mapfile = FileOrErr.get().release(); + base = mapfile->getBufferStart(); + return false; +} + +void Archive::cleanUpMemory() { + // Shutdown the file mapping + delete mapfile; + mapfile = 0; + base = 0; + + // Forget the entire symbol table + symTab.clear(); + symTabSize = 0; + + firstFileOffset = 0; + + // Delete any Modules and ArchiveMember's we've allocated as a result of + // symbol table searches. + for (ModuleMap::iterator I=modules.begin(), E=modules.end(); I != E; ++I ) { + delete I->second.first; + delete I->second.second; + } +} + +// Archive destructor - just clean up memory +Archive::~Archive() { + cleanUpMemory(); +} + + + +static void getSymbols(Module*M, std::vector<std::string>& symbols) { + // Loop over global variables + for (Module::global_iterator GI = M->global_begin(), GE=M->global_end(); GI != GE; ++GI) + if (!GI->isDeclaration() && !GI->hasLocalLinkage()) + if (!GI->getName().empty()) + symbols.push_back(GI->getName()); + + // Loop over functions + for (Module::iterator FI = M->begin(), FE = M->end(); FI != FE; ++FI) + if (!FI->isDeclaration() && !FI->hasLocalLinkage()) + if (!FI->getName().empty()) + symbols.push_back(FI->getName()); + + // Loop over aliases + for (Module::alias_iterator AI = M->alias_begin(), AE = M->alias_end(); + AI != AE; ++AI) { + if (AI->hasName()) + symbols.push_back(AI->getName()); + } +} + +// Get just the externally visible defined symbols from the bitcode +bool llvm::GetBitcodeSymbols(StringRef FileName, + LLVMContext& Context, + std::vector<std::string>& symbols, + std::string* ErrMsg) { + ErrorOr<std::unique_ptr<MemoryBuffer> > BufferOrErr = + MemoryBuffer::getFileOrSTDIN(FileName); + if (!BufferOrErr) { + if (ErrMsg) { + *ErrMsg = "Could not open file '" + FileName.str() + "'" + ": " + + BufferOrErr.getError().message(); + } + return true; + } + + ErrorOr<Module*> M = + parseBitcodeFile(BufferOrErr.get()->getMemBufferRef(), Context); + if (!M) { + if (ErrMsg) { + *ErrMsg = M.getError().message(); + } + return true; + } + + // Get the symbols + getSymbols(M.get(), symbols); + + // Done with the module. + delete M.get(); + return true; +} + +Module* +llvm::GetBitcodeSymbols(const char *BufPtr, unsigned Length, + const std::string& ModuleID, + LLVMContext& Context, + std::vector<std::string>& symbols, + std::string* ErrMsg) { + // Get the module. + MemoryBufferRef Buffer(StringRef(BufPtr, Length), ModuleID.c_str()); + + llvm::ErrorOr<Module*> M = parseBitcodeFile(Buffer, Context); + if (!M) { + if (ErrMsg) { + *ErrMsg = M.getError().message(); + } + return 0; + } + + // Get the symbols + getSymbols(M.get(), symbols); + + // Done with the module. Note that it's the caller's responsibility to delete + // the Module. + return M.get(); +} diff --git a/tools/ndk-link/Archive.h b/tools/ndk-link/Archive.h new file mode 100644 index 00000000000..920c2f7ac34 --- /dev/null +++ b/tools/ndk-link/Archive.h @@ -0,0 +1,510 @@ +//===-- llvm/Bitcode/Archive.h - LLVM Bitcode Archive -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This header file declares the Archive and ArchiveMember classes that provide +// manipulation of LLVM Archive files. The implementation is provided by the +// lib/Bitcode/Archive library. This library is used to read and write +// archive (*.a) files that contain LLVM bitcode files (or others). +// +//===----------------------------------------------------------------------===// + +#ifndef TOOLS_LLVM_AR_ARCHIVE_H +#define TOOLS_LLVM_AR_ARCHIVE_H + +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/TimeValue.h" +#include <map> +#include <set> +#include <vector> + +namespace llvm { + class MemoryBuffer; + +// Forward declare classes +class Module; // From VMCore +class Archive; // Declared below +class ArchiveMemberHeader; // Internal implementation class +class LLVMContext; // Global data + +/// This class is the main class manipulated by users of the Archive class. It +/// holds information about one member of the Archive. It is also the element +/// stored by the Archive's ilist, the Archive's main abstraction. Because of +/// the special requirements of archive files, users are not permitted to +/// construct ArchiveMember instances. You should obtain them from the methods +/// of the Archive class instead. +/// @brief This class represents a single archive member. +class ArchiveMember : public ilist_node<ArchiveMember> { + /// @name Types + /// @{ + public: + /// These flags are used internally by the archive member to specify various + /// characteristics of the member. The various "is" methods below provide + /// access to the flags. The flags are not user settable. + enum Flags { + SVR4SymbolTableFlag = 1, ///< Member is a SVR4 symbol table + BSD4SymbolTableFlag = 2, ///< Member is a BSD4 symbol table + BitcodeFlag = 4, ///< Member is bitcode + HasLongFilenameFlag = 8, ///< Member uses the long filename syntax + StringTableFlag = 16 ///< Member is an ar(1) format string table + }; + + /// @} + /// @name Accessors + /// @{ + public: + /// @returns the parent Archive instance + /// @brief Get the archive associated with this member + Archive* getArchive() const { return parent; } + + /// @returns the path to the Archive's file + /// @brief Get the path to the archive member + StringRef getPath() const { return path; } + + /// The "user" is the owner of the file per Unix security. This may not + /// have any applicability on non-Unix systems but is a required component + /// of the "ar" file format. + /// @brief Get the user associated with this archive member. + unsigned getUser() const { return User; } + + /// The "group" is the owning group of the file per Unix security. This + /// may not have any applicability on non-Unix systems but is a required + /// component of the "ar" file format. + /// @brief Get the group associated with this archive member. + unsigned getGroup() const { return Group; } + + /// The "mode" specifies the access permissions for the file per Unix + /// security. This may not have any applicability on non-Unix systems but is + /// a required component of the "ar" file format. + /// @brief Get the permission mode associated with this archive member. + unsigned getMode() const { return Mode; } + + /// This method returns the time at which the archive member was last + /// modified when it was not in the archive. + /// @brief Get the time of last modification of the archive member. + sys::TimeValue getModTime() const { return ModTime; } + + /// @returns the size of the archive member in bytes. + /// @brief Get the size of the archive member. + uint64_t getSize() const { return Size; } + + /// This method returns the total size of the archive member as it + /// appears on disk. This includes the file content, the header, the + /// long file name if any, and the padding. + /// @brief Get total on-disk member size. + unsigned getMemberSize() const; + + /// This method will return a pointer to the in-memory content of the + /// archive member, if it is available. If the data has not been loaded + /// into memory, the return value will be null. + /// @returns a pointer to the member's data. + /// @brief Get the data content of the archive member + const char* getData() const { return data; } + + /// @returns true iff the member is a SVR4 (non-LLVM) symbol table + /// @brief Determine if this member is a SVR4 symbol table. + bool isSVR4SymbolTable() const { return flags&SVR4SymbolTableFlag; } + + /// @returns true iff the member is a BSD4.4 (non-LLVM) symbol table + /// @brief Determine if this member is a BSD4.4 symbol table. + bool isBSD4SymbolTable() const { return flags&BSD4SymbolTableFlag; } + + /// @returns true iff the archive member is the ar(1) string table + /// @brief Determine if this member is the ar(1) string table. + bool isStringTable() const { return flags&StringTableFlag; } + + /// @returns true iff the archive member is a bitcode file. + /// @brief Determine if this member is a bitcode file. + bool isBitcode() const { return flags&BitcodeFlag; } + + /// Long filenames are an artifact of the ar(1) file format which allows + /// up to sixteen characters in its header and doesn't allow a path + /// separator character (/). To avoid this, a "long format" member name is + /// allowed that doesn't have this restriction. This method determines if + /// that "long format" is used for this member. + /// @returns true iff the file name uses the long form + /// @brief Determine if the member has a long file name + bool hasLongFilename() const { return flags&HasLongFilenameFlag; } + + /// This method causes the archive member to be replaced with the contents + /// of the file specified by \p File. The contents of \p this will be + /// updated to reflect the new data from \p File. The \p File must exist and + /// be readable on entry to this method. + /// @returns true if an error occurred, false otherwise + /// @brief Replace contents of archive member with a new file. + bool replaceWith(StringRef aFile, std::string* ErrMsg); + + /// @} + /// @name Data + /// @{ + private: + Archive *parent; ///< Pointer to parent archive + std::string path; ///< Path of file containing the member + uint32_t User; + uint32_t Group; + uint32_t Mode; + sys::TimeValue ModTime; + uint64_t Size; + unsigned flags; ///< Flags about the archive member + const char *data; ///< Data for the member + + /// @} + /// @name Constructors + /// @{ + public: + /// The default constructor is only used by the Archive's iplist when it + /// constructs the list's sentry node. + ArchiveMember(); + + private: + /// Used internally by the Archive class to construct an ArchiveMember. + /// The contents of the ArchiveMember are filled out by the Archive class. + explicit ArchiveMember(Archive *PAR); + + // So Archive can construct an ArchiveMember + friend class llvm::Archive; + /// @} +}; + +/// This class defines the interface to LLVM Archive files. The Archive class +/// presents the archive file as an ilist of ArchiveMember objects. The members +/// can be rearranged in any fashion either by directly editing the ilist or by +/// using editing methods on the Archive class (recommended). The Archive +/// class also provides several ways of accessing the archive file for various +/// purposes such as editing and linking. Full symbol table support is provided +/// for loading only those files that resolve symbols. Note that read +/// performance of this library is _crucial_ for performance of JIT type +/// applications and the linkers. Consequently, the implementation of the class +/// is optimized for reading. +class Archive { + + /// @name Types + /// @{ + public: + /// This is the ilist type over which users may iterate to examine + /// the contents of the archive + /// @brief The ilist type of ArchiveMembers that Archive contains. + typedef iplist<ArchiveMember> MembersList; + + /// @brief Forward mutable iterator over ArchiveMember + typedef MembersList::iterator iterator; + + /// @brief Forward immutable iterator over ArchiveMember + typedef MembersList::const_iterator const_iterator; + + /// @brief Reverse mutable iterator over ArchiveMember + typedef std::reverse_iterator<iterator> reverse_iterator; + + /// @brief Reverse immutable iterator over ArchiveMember + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + /// @brief The in-memory version of the symbol table + typedef std::map<std::string,unsigned> SymTabType; + + /// @} + /// @name ilist accessor methods + /// @{ + public: + inline iterator begin() { return members.begin(); } + inline const_iterator begin() const { return members.begin(); } + inline iterator end () { return members.end(); } + inline const_iterator end () const { return members.end(); } + + inline reverse_iterator rbegin() { return members.rbegin(); } + inline const_reverse_iterator rbegin() const { return members.rbegin(); } + inline reverse_iterator rend () { return members.rend(); } + inline const_reverse_iterator rend () const { return members.rend(); } + + inline size_t size() const { return members.size(); } + inline bool empty() const { return members.empty(); } + inline const ArchiveMember& front() const { return members.front(); } + inline ArchiveMember& front() { return members.front(); } + inline const ArchiveMember& back() const { return members.back(); } + inline ArchiveMember& back() { return members.back(); } + + /// @} + /// @name ilist mutator methods + /// @{ + public: + /// This method splices a \p src member from an archive (possibly \p this), + /// to a position just before the member given by \p dest in \p this. When + /// the archive is written, \p src will be written in its new location. + /// @brief Move a member to a new location + inline void splice(iterator dest, Archive& arch, iterator src) + { return members.splice(dest,arch.members,src); } + + /// This method erases a \p target member from the archive. When the + /// archive is written, it will no longer contain \p target. The associated + /// ArchiveMember is deleted. + /// @brief Erase a member. + inline iterator erase(iterator target) { return members.erase(target); } + + /// @} + /// @name Constructors + /// @{ + public: + /// Create an empty archive file and associate it with the \p Filename. This + /// method does not actually create the archive disk file. It creates an + /// empty Archive object. If the writeToDisk method is called, the archive + /// file \p Filename will be created at that point, with whatever content + /// the returned Archive object has at that time. + /// @returns An Archive* that represents the new archive file. + /// @brief Create an empty Archive. + static Archive *CreateEmpty( + StringRef Filename, ///< Name of the archive to (eventually) create. + LLVMContext &C ///< Context to use for global information + ); + + /// Open an existing archive and load its contents in preparation for + /// editing. After this call, the member ilist is completely populated based + /// on the contents of the archive file. You should use this form of open if + /// you intend to modify the archive or traverse its contents (e.g. for + /// printing). + /// @brief Open and load an archive file + static Archive* OpenAndLoad( + StringRef filePath, ///< The file path to open and load + LLVMContext& C, ///< The context to use for global information + std::string* ErrorMessage ///< An optional error string + ); + + /// This method opens an existing archive file from \p Filename and reads in + /// its symbol table without reading in any of the archive's members. This + /// reduces both I/O and cpu time in opening the archive if it is to be used + /// solely for symbol lookup (e.g. during linking). The \p Filename must + /// exist and be an archive file or an error will be returned. This form + /// of opening the archive is intended for read-only operations that need to + /// locate members via the symbol table for link editing. Since the archve + /// members are not read by this method, the archive will appear empty upon + /// return. If editing operations are performed on the archive, they will + /// completely replace the contents of the archive! It is recommended that + /// if this form of opening the archive is used that only the symbol table + /// lookup methods (getSymbolTable, findModuleDefiningSymbol, and + /// findModulesDefiningSymbols) be used. + /// @returns an Archive* that represents the archive file, or null on error. + /// @brief Open an existing archive and load its symbols. + static Archive* OpenAndLoadSymbols( + StringRef filePath, ///< The file path to open and load + LLVMContext &C, ///< The context to use for global information + std::string *ErrorMessage ///< An optional error string + ); + + /// This destructor cleans up the Archive object, releases all memory, and + /// closes files. It does nothing with the archive file on disk. If you + /// haven't used the writeToDisk method by the time the destructor is + /// called, all changes to the archive will be lost. + /// @brief Destruct in-memory archive + ~Archive(); + + /// @} + /// @name Accessors + /// @{ + public: + /// @returns the path to the archive file. + /// @brief Get the archive path. + StringRef getPath() { return archPath; } + + /// This method is provided so that editing methods can be invoked directly + /// on the Archive's iplist of ArchiveMember. However, it is recommended + /// that the usual STL style iterator interface be used instead. + /// @returns the iplist of ArchiveMember + /// @brief Get the iplist of the members + MembersList& getMembers() { return members; } + + /// This method allows direct query of the Archive's symbol table. The + /// symbol table is a std::map of std::string (the symbol) to unsigned (the + /// file offset). Note that for efficiency reasons, the offset stored in + /// the symbol table is not the actual offset. It is the offset from the + /// beginning of the first "real" file member (after the symbol table). Use + /// the getFirstFileOffset() to obtain that offset and add this value to the + /// offset in the symbol table to obtain the real file offset. Note that + /// there is purposefully no interface provided by Archive to look up + /// members by their offset. Use the findModulesDefiningSymbols and + /// findModuleDefiningSymbol methods instead. + /// @returns the Archive's symbol table. + /// @brief Get the archive's symbol table + const SymTabType& getSymbolTable() { return symTab; } + + /// This method returns the offset in the archive file to the first "real" + /// file member. Archive files, on disk, have a signature and might have a + /// symbol table that precedes the first actual file member. This method + /// allows you to determine what the size of those fields are. + /// @returns the offset to the first "real" file member in the archive. + /// @brief Get the offset to the first "real" file member in the archive. + unsigned getFirstFileOffset() { return firstFileOffset; } + + /// This method will scan the archive for bitcode modules, interpret them + /// and return a vector of the instantiated modules in \p Modules. If an + /// error occurs, this method will return true. If \p ErrMessage is not null + /// and an error occurs, \p *ErrMessage will be set to a string explaining + /// the error that occurred. + /// @returns true if an error occurred + /// @brief Instantiate all the bitcode modules located in the archive + bool getAllModules(std::vector<Module*>& Modules, std::string* ErrMessage); + + /// This accessor looks up the \p symbol in the archive's symbol table and + /// returns the associated module that defines that symbol. This method can + /// be called as many times as necessary. This is handy for linking the + /// archive into another module based on unresolved symbols. Note that the + /// Module returned by this accessor should not be deleted by the caller. It + /// is managed internally by the Archive class. It is possible that multiple + /// calls to this accessor will return the same Module instance because the + /// associated module defines multiple symbols. + /// @returns The Module* found or null if the archive does not contain a + /// module that defines the \p symbol. + /// @brief Look up a module by symbol name. + Module* findModuleDefiningSymbol( + const std::string& symbol, ///< Symbol to be sought + std::string* ErrMessage ///< Error message storage, if non-zero + ); + + /// This method is similar to findModuleDefiningSymbol but allows lookup of + /// more than one symbol at a time. If \p symbols contains a list of + /// undefined symbols in some module, then calling this method is like + /// making one complete pass through the archive to resolve symbols but is + /// more efficient than looking at the individual members. Note that on + /// exit, the symbols resolved by this method will be removed from \p + /// symbols to ensure they are not re-searched on a subsequent call. If + /// you need to retain the list of symbols, make a copy. + /// @brief Look up multiple symbols in the archive. + bool findModulesDefiningSymbols( + std::set<std::string>& symbols, ///< Symbols to be sought + SmallVectorImpl<Module*>& modules, ///< The modules matching \p symbols + std::string* ErrMessage ///< Error msg storage, if non-zero + ); + + /// This method determines whether the archive is a properly formed llvm + /// bitcode archive. It first makes sure the symbol table has been loaded + /// and has a non-zero size. If it does, then it is an archive. If not, + /// then it tries to load all the bitcode modules of the archive. Finally, + /// it returns whether it was successful. + /// @returns true if the archive is a proper llvm bitcode archive + /// @brief Determine whether the archive is a proper llvm bitcode archive. + bool isBitcodeArchive(); + + /// @} + /// @name Mutators + /// @{ + public: + /// This method is the only way to get the archive written to disk. It + /// creates or overwrites the file specified when \p this was created + /// or opened. The arguments provide options for writing the archive. If + /// \p CreateSymbolTable is true, the archive is scanned for bitcode files + /// and a symbol table of the externally visible function and global + /// variable names is created. If \p TruncateNames is true, the names of the + /// archive members will have their path component stripped and the file + /// name will be truncated at 15 characters. If \p Compress is specified, + /// all archive members will be compressed before being written. If + /// \p PrintSymTab is true, the symbol table will be printed to std::cout. + /// @returns true if an error occurred, \p error set to error message; + /// returns false if the writing succeeded. + /// @brief Write (possibly modified) archive contents to disk + bool writeToDisk( + std::string* ErrMessage=0 ///< If non-null, where error msg is set + ); + + /// This method adds a new file to the archive. The \p filename is examined + /// to determine just enough information to create an ArchiveMember object + /// which is then inserted into the Archive object's ilist at the location + /// given by \p where. + /// @returns true if an error occurred, false otherwise + /// @brief Add a file to the archive. + bool addFileBefore(StringRef filename, ///< The file to be added + iterator where, ///< Insertion point + std::string *ErrMsg ///< Optional error message location + ); + + /// @} + /// @name Implementation + /// @{ + protected: + /// @brief Construct an Archive for \p filename and optionally map it + /// into memory. + explicit Archive(StringRef filename, LLVMContext& C); + + /// @returns A fully populated ArchiveMember or 0 if an error occurred. + /// @brief Parse the header of a member starting at \p At + ArchiveMember* parseMemberHeader( + const char*&At, ///< The pointer to the location we're parsing + const char*End, ///< The pointer to the end of the archive + std::string* error ///< Optional error message catcher + ); + + /// @param ErrMessage Set to address of a std::string to get error messages + /// @returns false on error + /// @brief Check that the archive signature is correct + bool checkSignature(std::string* ErrMessage); + + /// @param ErrMessage Set to address of a std::string to get error messages + /// @returns false on error + /// @brief Load the entire archive. + bool loadArchive(std::string* ErrMessage); + + /// @param ErrMessage Set to address of a std::string to get error messages + /// @returns false on error + /// @brief Load just the symbol table. + bool loadSymbolTable(std::string* ErrMessage); + + /// Writes one ArchiveMember to an ofstream. If an error occurs, returns + /// false, otherwise true. If an error occurs and error is non-null then + /// it will be set to an error message. + /// @returns false if writing member succeeded, + /// returns true if writing member failed, \p error set to error message. + bool writeMember( + const ArchiveMember& member, ///< The member to be written + raw_fd_ostream& ARFile, ///< The file to write member onto + std::string* ErrMessage ///< If non-null, place were error msg is set + ); + + /// @brief Fill in an ArchiveMemberHeader from ArchiveMember. + bool fillHeader(const ArchiveMember&mbr, + ArchiveMemberHeader& hdr,int sz) const; + + /// @brief Maps archive into memory + bool mapToMemory(std::string* ErrMsg); + + /// @brief Frees all the members and unmaps the archive file. + void cleanUpMemory(); + + /// This type is used to keep track of bitcode modules loaded from the + /// symbol table. It maps the file offset to a pair that consists of the + /// associated ArchiveMember and the Module. + /// @brief Module mapping type + typedef std::map<unsigned,std::pair<Module*,ArchiveMember*> > + ModuleMap; + + + /// @} + /// @name Data + /// @{ + protected: + StringRef archPath; ///< Path to the archive file we read/write + MembersList members; ///< The ilist of ArchiveMember + MemoryBuffer *mapfile; ///< Raw Archive contents mapped into memory + const char* base; ///< Base of the memory mapped file data + SymTabType symTab; ///< The symbol table + std::string strtab; ///< The string table for long file names + unsigned symTabSize; ///< Size in bytes of symbol table + unsigned firstFileOffset; ///< Offset to first normal file. + ModuleMap modules; ///< The modules loaded via symbol lookup. + LLVMContext& Context; ///< This holds global data. + /// @} + /// @name Hidden + /// @{ + private: + Archive() LLVM_DELETED_FUNCTION; + Archive(const Archive&) LLVM_DELETED_FUNCTION; + Archive& operator=(const Archive&) LLVM_DELETED_FUNCTION; + /// @} +}; + +} // End llvm namespace + +#endif diff --git a/tools/ndk-link/ArchiveInternals.h b/tools/ndk-link/ArchiveInternals.h new file mode 100644 index 00000000000..f09b856a718 --- /dev/null +++ b/tools/ndk-link/ArchiveInternals.h @@ -0,0 +1,87 @@ +//===-- lib/Archive/ArchiveInternals.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Internal implementation header for LLVM Archive files. +// +//===----------------------------------------------------------------------===// + +#ifndef TOOLS_LLVM_AR_ARCHIVEINTERNALS_H +#define TOOLS_LLVM_AR_ARCHIVEINTERNALS_H + +#include "Archive.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/TimeValue.h" +#include <cstring> + +#define ARFILE_MAGIC "!<arch>\n" ///< magic string +#define ARFILE_MAGIC_LEN (sizeof(ARFILE_MAGIC)-1) ///< length of magic string +#define ARFILE_SVR4_SYMTAB_NAME "/ " ///< SVR4 symtab entry name +#define ARFILE_BSD4_SYMTAB_NAME "__.SYMDEF SORTED" ///< BSD4 symtab entry name +#define ARFILE_STRTAB_NAME "// " ///< Name of string table +#define ARFILE_PAD "\n" ///< inter-file align padding +#define ARFILE_MEMBER_MAGIC "`\n" ///< fmag field magic # + +namespace llvm { + + class LLVMContext; + + /// The ArchiveMemberHeader structure is used internally for bitcode + /// archives. + /// The header precedes each file member in the archive. This structure is + /// defined using character arrays for direct and correct interpretation + /// regardless of the endianess of the machine that produced it. + /// @brief Archive File Member Header + class ArchiveMemberHeader { + /// @name Data + /// @{ + public: + char name[16]; ///< Name of the file member. + char date[12]; ///< File date, decimal seconds since Epoch + char uid[6]; ///< user id in ASCII decimal + char gid[6]; ///< group id in ASCII decimal + char mode[8]; ///< file mode in ASCII octal + char size[10]; ///< file size in ASCII decimal + char fmag[2]; ///< Always contains ARFILE_MAGIC_TERMINATOR + + /// @} + /// @name Methods + /// @{ + public: + void init() { + memset(name,' ',16); + memset(date,' ',12); + memset(uid,' ',6); + memset(gid,' ',6); + memset(mode,' ',8); + memset(size,' ',10); + fmag[0] = '`'; + fmag[1] = '\n'; + } + + bool checkSignature() const { + return 0 == memcmp(fmag, ARFILE_MEMBER_MAGIC,2); + } + }; + + // Get just the externally visible defined symbols from the bitcode + bool GetBitcodeSymbols(StringRef fName, + LLVMContext& Context, + std::vector<std::string>& symbols, + std::string* ErrMsg); + + Module* GetBitcodeSymbols(const char *Buffer, unsigned Length, + const std::string& ModuleID, + LLVMContext& Context, + std::vector<std::string>& symbols, + std::string* ErrMsg); +} + +#endif + +// vim: sw=2 ai diff --git a/tools/ndk-link/ArchiveReader.cpp b/tools/ndk-link/ArchiveReader.cpp new file mode 100644 index 00000000000..877155bbed8 --- /dev/null +++ b/tools/ndk-link/ArchiveReader.cpp @@ -0,0 +1,544 @@ +//===-- ArchiveReader.cpp - Read LLVM archive files -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up standard unix archive files (.a) containing LLVM bitcode. +// +//===----------------------------------------------------------------------===// + +#include "Archive.h" +#include "ArchiveInternals.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include <cstdio> +#include <cstdlib> +#include <memory> +using namespace llvm; + +#if 0 +/// Read a variable-bit-rate encoded unsigned integer +static inline unsigned readInteger(const char*&At, const char*End) { + unsigned Shift = 0; + unsigned Result = 0; + + do { + if (At == End) + return Result; + Result |= (unsigned)((*At++) & 0x7F) << Shift; + Shift += 7; + } while (At[-1] & 0x80); + return Result; +} +#endif + +// This member parses an ArchiveMemberHeader that is presumed to be pointed to +// by At. The At pointer is updated to the byte just after the header, which +// can be variable in size. +ArchiveMember* +Archive::parseMemberHeader(const char*& At, const char* End, std::string* error) { + if (At + sizeof(ArchiveMemberHeader) >= End) { + if (error) + *error = "Unexpected end of file"; + return 0; + } + + // Cast archive member header + const ArchiveMemberHeader* Hdr = (const ArchiveMemberHeader*)At; + At += sizeof(ArchiveMemberHeader); + + int flags = 0; + int MemberSize = atoi(Hdr->size); + assert(MemberSize >= 0); + + // Check the size of the member for sanity + if (At + MemberSize > End) { + if (error) + *error = "invalid member length in archive file"; + return 0; + } + + // Check the member signature + if (!Hdr->checkSignature()) { + if (error) + *error = "invalid file member signature"; + return 0; + } + + // Convert and check the member name + // The empty name ( '/' and 15 blanks) is for a foreign (non-LLVM) symbol + // table. The special name "//" and 14 blanks is for a string table, used + // for long file names. This library doesn't generate either of those but + // it will accept them. If the name starts with #1/ and the remainder is + // digits, then those digits specify the length of the name that is + // stored immediately following the header. Anything else is a regular, short + // filename that is terminated with a '/' and blanks. + + std::string pathname; + switch (Hdr->name[0]) { + case '#': + if (Hdr->name[1] == '1' && Hdr->name[2] == '/') { + if (isdigit(Hdr->name[3])) { + unsigned len = atoi(&Hdr->name[3]); + const char *nulp = (const char *)memchr(At, '\0', len); + pathname.assign(At, nulp != 0 ? (uintptr_t)(nulp - At) : len); + At += len; + MemberSize -= len; + flags |= ArchiveMember::HasLongFilenameFlag; + } else { + if (error) + *error = "invalid long filename"; + return 0; + } + } + break; + case '/': + if (Hdr->name[1]== '/') { + if (0 == memcmp(Hdr->name, ARFILE_STRTAB_NAME, 16)) { + pathname.assign(ARFILE_STRTAB_NAME); + flags |= ArchiveMember::StringTableFlag; + } else { + if (error) + *error = "invalid string table name"; + return 0; + } + } else if (Hdr->name[1] == ' ') { + if (0 == memcmp(Hdr->name, ARFILE_SVR4_SYMTAB_NAME, 16)) { + pathname.assign(ARFILE_SVR4_SYMTAB_NAME); + flags |= ArchiveMember::SVR4SymbolTableFlag; + } else { + if (error) + *error = "invalid SVR4 symbol table name"; + return 0; + } + } else if (isdigit(Hdr->name[1])) { + unsigned index = atoi(&Hdr->name[1]); + if (index < strtab.length()) { + const char* namep = strtab.c_str() + index; + const char* endp = strtab.c_str() + strtab.length(); + const char* p = namep; + const char* last_p = p; + while (p < endp) { + if (*p == '\n' && *last_p == '/') { + pathname.assign(namep, last_p - namep); + flags |= ArchiveMember::HasLongFilenameFlag; + break; + } + last_p = p; + p++; + } + if (p >= endp) { + if (error) + *error = "missing name terminator in string table"; + return 0; + } + } else { + if (error) + *error = "name index beyond string table"; + return 0; + } + } + break; + case '_': + if (Hdr->name[1] == '_' && + (0 == memcmp(Hdr->name, ARFILE_BSD4_SYMTAB_NAME, 16))) { + pathname.assign(ARFILE_BSD4_SYMTAB_NAME); + flags |= ArchiveMember::BSD4SymbolTableFlag; + break; + } + /* FALL THROUGH */ + + default: + const char* slash = (const char*) memchr(Hdr->name, '/', 16); + if (slash == 0) + slash = Hdr->name + 16; + pathname.assign(Hdr->name, slash - Hdr->name); + break; + } + + // Determine if this is a bitcode file + if (sys::fs::identify_magic(StringRef(At, 4)) == + sys::fs::file_magic::bitcode) + flags |= ArchiveMember::BitcodeFlag; + else + flags &= ~ArchiveMember::BitcodeFlag; + + // Instantiate the ArchiveMember to be filled + ArchiveMember* member = new ArchiveMember(this); + + // Fill in fields of the ArchiveMember + member->parent = this; + member->path = pathname; + member->Size = MemberSize; + member->ModTime.fromEpochTime(atoi(Hdr->date)); + unsigned int mode; + sscanf(Hdr->mode, "%o", &mode); + member->Mode = mode; + member->User = atoi(Hdr->uid); + member->Group = atoi(Hdr->gid); + member->flags = flags; + member->data = At; + + return member; +} + +bool +Archive::checkSignature(std::string* error) { + // Check the magic string at file's header + if (mapfile->getBufferSize() < 8 || memcmp(base, ARFILE_MAGIC, 8)) { + if (error) + *error = "invalid signature for an archive file"; + return false; + } + return true; +} + +// This function loads the entire archive and fully populates its ilist with +// the members of the archive file. This is typically used in preparation for +// editing the contents of the archive. +bool +Archive::loadArchive(std::string* error) { + + // Set up parsing + members.clear(); + symTab.clear(); + const char *At = base; + const char *End = mapfile->getBufferEnd(); + + if (!checkSignature(error)) + return false; + + At += 8; // Skip the magic string. + + bool foundFirstFile = false; + while (At < End) { + // parse the member header + const char* Save = At; + std::unique_ptr<ArchiveMember> mbr(parseMemberHeader(At, End, error)); + if (!mbr) + return false; + + // check if this is the foreign symbol table + if (mbr->isSVR4SymbolTable() || mbr->isBSD4SymbolTable()) { + At += mbr->getSize(); + if ((intptr_t(At) & 1) == 1) + At++; + } else if (mbr->isStringTable()) { + // Simply suck the entire string table into a string + // variable. This will be used to get the names of the + // members that use the "/ddd" format for their names + // (SVR4 style long names). + strtab.assign(At, mbr->getSize()); + At += mbr->getSize(); + if ((intptr_t(At) & 1) == 1) + At++; + } else { + // This is just a regular file. If its the first one, save its offset. + // Otherwise just push it on the list and move on to the next file. + if (!foundFirstFile) { + firstFileOffset = Save - base; + foundFirstFile = true; + } + At += mbr->getSize(); + members.push_back(mbr.release()); + if ((intptr_t(At) & 1) == 1) + At++; + } + } + return true; +} + +// Open and completely load the archive file. +Archive* +Archive::OpenAndLoad(StringRef File, LLVMContext& C, + std::string* ErrorMessage) { + std::unique_ptr<Archive> result(new Archive(File, C)); + if (result->mapToMemory(ErrorMessage)) + return NULL; + if (!result->loadArchive(ErrorMessage)) + return NULL; + return result.release(); +} + +// Get all the bitcode modules from the archive +bool Archive::getAllModules(std::vector<Module*>& Modules, + std::string* ErrMessage) { + for (iterator I = begin(), E = end(); I != E; ++I) { + if (I->isBitcode()) { + std::string FullMemberName = + archPath.str() + "(" + I->getPath().str() + ")"; + MemoryBufferRef Buffer(StringRef(I->getData(), I->getSize()), + FullMemberName); + ErrorOr<Module*> M = parseBitcodeFile(Buffer, Context); + if (!M) { + if (ErrMessage) { + *ErrMessage = M.getError().message(); + } + return true; + } + + Modules.push_back(M.get()); + } + } + return false; +} + +// Load just the symbol table from the archive file +bool +Archive::loadSymbolTable(std::string* ErrorMsg) { + + // Set up parsing + members.clear(); + const char *At = base; + const char *End = mapfile->getBufferEnd(); + + // Make sure we're dealing with an archive + if (!checkSignature(ErrorMsg)) + return false; + + At += 8; // Skip signature + + // Parse the first file member header + const char* FirstFile = At; + std::unique_ptr<ArchiveMember> mbr(parseMemberHeader(At, End, ErrorMsg)); + if (!mbr) + return false; + + if (mbr->isSVR4SymbolTable() || mbr->isBSD4SymbolTable()) { + // Skip the foreign symbol table, we don't do anything with it + At += mbr->getSize(); + if ((intptr_t(At) & 1) == 1) + At++; + + // Read the next one + FirstFile = At; + mbr.reset(parseMemberHeader(At, End, ErrorMsg)); + if (!mbr) + return false; + } + + if (mbr->isStringTable()) { + // Process the string table entry + strtab.assign((const char*)mbr->getData(), mbr->getSize()); + At += mbr->getSize(); + if ((intptr_t(At) & 1) == 1) + At++; + + // Get the next one + FirstFile = At; + mbr.reset(parseMemberHeader(At, End, ErrorMsg)); + if (!mbr) + return false; + } + + // There's no symbol table in the file. We have to rebuild it from scratch + // because the intent of this method is to get the symbol table loaded so + // it can be searched efficiently. + // Add the member to the members list + members.push_back(mbr.release()); + + firstFileOffset = FirstFile - base; + return true; +} + +// Open the archive and load just the symbol tables +Archive* Archive::OpenAndLoadSymbols(StringRef File, + LLVMContext& C, + std::string* ErrorMessage) { + std::unique_ptr<Archive> result ( new Archive(File, C) ); + if (result->mapToMemory(ErrorMessage)) + return NULL; + if (!result->loadSymbolTable(ErrorMessage)) + return NULL; + return result.release(); +} + +// Look up one symbol in the symbol table and return the module that defines +// that symbol. +Module* +Archive::findModuleDefiningSymbol(const std::string& symbol, + std::string* ErrMsg) { + SymTabType::iterator SI = symTab.find(symbol); + if (SI == symTab.end()) + return 0; + + // The symbol table was previously constructed assuming that the members were + // written without the symbol table header. Because VBR encoding is used, the + // values could not be adjusted to account for the offset of the symbol table + // because that could affect the size of the symbol table due to VBR encoding. + // We now have to account for this by adjusting the offset by the size of the + // symbol table and its header. + unsigned fileOffset = + SI->second + // offset in symbol-table-less file + firstFileOffset; // add offset to first "real" file in archive + + // See if the module is already loaded + ModuleMap::iterator MI = modules.find(fileOffset); + if (MI != modules.end()) + return MI->second.first; + + // Module hasn't been loaded yet, we need to load it + const char* modptr = base + fileOffset; + ArchiveMember* mbr = parseMemberHeader(modptr, mapfile->getBufferEnd(), + ErrMsg); + if (!mbr) + return 0; + + // Now, load the bitcode module to get the Module. + std::string FullMemberName = + archPath.str() + "(" + mbr->getPath().str() + ")"; + + std::unique_ptr<MemoryBuffer> Buffer = + MemoryBuffer::getMemBufferCopy(StringRef(mbr->getData(), mbr->getSize()), + FullMemberName.c_str()); + + ErrorOr<Module*> m = getLazyBitcodeModule(std::move(Buffer), Context); + if (!m) { + if (ErrMsg) { + *ErrMsg = m.getError().message(); + } + return 0; + } + + modules.insert(std::make_pair(fileOffset, std::make_pair(m.get(), mbr))); + + return m.get(); +} + +// Look up multiple symbols in the symbol table and return a set of +// Modules that define those symbols. +bool +Archive::findModulesDefiningSymbols(std::set<std::string>& symbols, + SmallVectorImpl<Module*>& result, + std::string* error) { + if (!mapfile || !base) { + if (error) + *error = "Empty archive invalid for finding modules defining symbols"; + return false; + } + + if (symTab.empty()) { + // We don't have a symbol table, so we must build it now but lets also + // make sure that we populate the modules table as we do this to ensure + // that we don't load them twice when findModuleDefiningSymbol is called + // below. + + // Get a pointer to the first file + const char* At = base + firstFileOffset; + const char* End = mapfile->getBufferEnd(); + + while ( At < End) { + // Compute the offset to be put in the symbol table + unsigned offset = At - base - firstFileOffset; + + // Parse the file's header + ArchiveMember* mbr = parseMemberHeader(At, End, error); + if (!mbr) + return false; + + // If it contains symbols + if (mbr->isBitcode()) { + // Get the symbols + std::vector<std::string> symbols; + std::string FullMemberName = archPath.str() + "(" + + mbr->getPath().str() + ")"; + Module* M = + GetBitcodeSymbols(At, mbr->getSize(), FullMemberName, Context, + symbols, error); + + if (M) { + // Insert the module's symbols into the symbol table + for (std::vector<std::string>::iterator I = symbols.begin(), + E=symbols.end(); I != E; ++I ) { + symTab.insert(std::make_pair(*I, offset)); + } + // Insert the Module and the ArchiveMember into the table of + // modules. + modules.insert(std::make_pair(offset, std::make_pair(M, mbr))); + } else { + if (error) + *error = "Can't parse bitcode member: " + + mbr->getPath().str() + ": " + *error; + delete mbr; + return false; + } + } + + // Go to the next file location + At += mbr->getSize(); + if ((intptr_t(At) & 1) == 1) + At++; + } + } + + // At this point we have a valid symbol table (one way or another) so we + // just use it to quickly find the symbols requested. + + SmallPtrSet<Module*, 16> Added; + for (std::set<std::string>::iterator I=symbols.begin(), + Next = I, + E=symbols.end(); I != E; I = Next) { + // Increment Next before we invalidate it. + ++Next; + + // See if this symbol exists + Module* m = findModuleDefiningSymbol(*I,error); + if (!m) + continue; + auto NewMember = Added.insert(m); + if (!NewMember.second) + continue; + + // The symbol exists, insert the Module into our result. + result.push_back(m); + + // Remove the symbol now that its been resolved. + symbols.erase(I); + } + return true; +} + +bool Archive::isBitcodeArchive() { + // Make sure the symTab has been loaded. In most cases this should have been + // done when the archive was constructed, but still, this is just in case. + if (symTab.empty()) + if (!loadSymbolTable(0)) + return false; + + // Now that we know it's been loaded, return true + // if it has a size + if (symTab.size()) return true; + + // We still can't be sure it isn't a bitcode archive + if (!loadArchive(0)) + return false; + + std::vector<Module *> Modules; + std::string ErrorMessage; + + // Scan the archive, trying to load a bitcode member. We only load one to + // see if this works. + for (iterator I = begin(), E = end(); I != E; ++I) { + if (!I->isBitcode()) + continue; + + std::string FullMemberName = + archPath.str() + "(" + I->getPath().str() + ")"; + MemoryBufferRef Buffer(StringRef(I->getData(), I->getSize()), + FullMemberName); + ErrorOr<Module*> M = parseBitcodeFile(Buffer, Context); + if (!M) + return false; // Couldn't parse bitcode, not a bitcode archive. + delete M.get(); + return true; + } + + return false; +} diff --git a/tools/ndk-link/ArchiveWriter.cpp b/tools/ndk-link/ArchiveWriter.cpp new file mode 100644 index 00000000000..12196efdb4e --- /dev/null +++ b/tools/ndk-link/ArchiveWriter.cpp @@ -0,0 +1,291 @@ +//===-- ArchiveWriter.cpp - Write LLVM archive files ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up an LLVM archive file (.a) containing LLVM bitcode. +// +//===----------------------------------------------------------------------===// + +#include "Archive.h" +#include "ArchiveInternals.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include <fstream> +#include <iomanip> +#include <memory> +#include <ostream> +#include <system_error> +using namespace llvm; + +#if 0 +// Write an integer using variable bit rate encoding. This saves a few bytes +// per entry in the symbol table. +static inline void writeInteger(unsigned num, std::ofstream& ARFile) { + while (1) { + if (num < 0x80) { // done? + ARFile << (unsigned char)num; + return; + } + + // Nope, we are bigger than a character, output the next 7 bits and set the + // high bit to say that there is more coming... + ARFile << (unsigned char)(0x80 | ((unsigned char)num & 0x7F)); + num >>= 7; // Shift out 7 bits now... + } +} + +// Compute how many bytes are taken by a given VBR encoded value. This is needed +// to pre-compute the size of the symbol table. +static inline unsigned numVbrBytes(unsigned num) { + + // Note that the following nested ifs are somewhat equivalent to a binary + // search. We split it in half by comparing against 2^14 first. This allows + // most reasonable values to be done in 2 comparisons instead of 1 for + // small ones and four for large ones. We expect this to access file offsets + // in the 2^10 to 2^24 range and symbol lengths in the 2^0 to 2^8 range, + // so this approach is reasonable. + if (num < 1<<14) { + if (num < 1<<7) + return 1; + else + return 2; + } + if (num < 1<<21) + return 3; + + if (num < 1<<28) + return 4; + return 5; // anything >= 2^28 takes 5 bytes +} +#endif + +// Create an empty archive. +Archive* Archive::CreateEmpty(StringRef FilePath, LLVMContext& C) { + Archive* result = new Archive(FilePath, C); + return result; +} + +// Fill the ArchiveMemberHeader with the information from a member. If +// TruncateNames is true, names are flattened to 15 chars or less. The sz field +// is provided here instead of coming from the mbr because the member might be +// stored compressed and the compressed size is not the ArchiveMember's size. +// Furthermore compressed files have negative size fields to identify them as +// compressed. +bool +Archive::fillHeader(const ArchiveMember &mbr, ArchiveMemberHeader& hdr, + int sz) const { + + // Set the permissions mode, uid and gid + hdr.init(); + char buffer[32]; + sprintf(buffer, "%-8o", mbr.getMode()); + memcpy(hdr.mode,buffer,8); + sprintf(buffer, "%-6u", mbr.getUser()); + memcpy(hdr.uid,buffer,6); + sprintf(buffer, "%-6u", mbr.getGroup()); + memcpy(hdr.gid,buffer,6); + + // Set the last modification date + uint64_t secondsSinceEpoch = mbr.getModTime().toEpochTime(); + sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch)); + memcpy(hdr.date,buffer,12); + + std::string mbrPath = sys::path::filename(mbr.getPath()); + + // Set the name field in one of its various flavors. + bool writeLongName = false; + if (mbr.isStringTable()) { + memcpy(hdr.name,ARFILE_STRTAB_NAME,16); + } else if (mbr.isSVR4SymbolTable()) { + memcpy(hdr.name,ARFILE_SVR4_SYMTAB_NAME,16); + } else if (mbr.isBSD4SymbolTable()) { + memcpy(hdr.name,ARFILE_BSD4_SYMTAB_NAME,16); + } else if (mbrPath.length() < 16 && mbrPath.find('/') == std::string::npos) { + memcpy(hdr.name,mbrPath.c_str(),mbrPath.length()); + hdr.name[mbrPath.length()] = '/'; + } else { + std::string nm = "#1/"; + nm += utostr(mbrPath.length()); + memcpy(hdr.name,nm.data(),nm.length()); + if (sz < 0) + sz -= mbrPath.length(); + else + sz += mbrPath.length(); + writeLongName = true; + } + + // Set the size field + if (sz < 0) { + buffer[0] = '-'; + sprintf(&buffer[1],"%-9u",(unsigned)-sz); + } else { + sprintf(buffer, "%-10u", (unsigned)sz); + } + memcpy(hdr.size,buffer,10); + + return writeLongName; +} + +// Insert a file into the archive before some other member. This also takes care +// of extracting the necessary flags and information from the file. +bool Archive::addFileBefore(StringRef filePath, iterator where, + std::string *ErrMsg) { + if (!sys::fs::exists(filePath)) { + if (ErrMsg) + *ErrMsg = "Can not add a non-existent file to archive"; + return true; + } + + ArchiveMember* mbr = new ArchiveMember(this); + + mbr->data = 0; + mbr->path = filePath; + sys::fs::file_status Status; + std::error_code EC = sys::fs::status(filePath, Status); + if (EC) { + delete mbr; + return true; + } + mbr->User = Status.getUser(); + mbr->Group = Status.getGroup(); + mbr->Mode = Status.permissions(); + mbr->ModTime = Status.getLastModificationTime(); + // FIXME: On posix this is a second stat. + EC = sys::fs::file_size(filePath, mbr->Size); + if (EC) { + delete mbr; + return true; + } + + unsigned flags = 0; + if (sys::path::filename(filePath).size() > 15) + flags |= ArchiveMember::HasLongFilenameFlag; + + sys::fs::file_magic type; + if (sys::fs::identify_magic(mbr->path, type)) + type = sys::fs::file_magic::unknown; + mbr->flags = flags; + members.insert(where,mbr); + return false; +} + +// Write one member out to the file. +bool +Archive::writeMember( + const ArchiveMember& member, + raw_fd_ostream& ARFile, + std::string* ErrMsg +) { + + uint64_t filepos = ARFile.tell(); + filepos -= 8; + + // Get the data and its size either from the + // member's in-memory data or directly from the file. + size_t fSize = member.getSize(); + const char *data = (const char*)member.getData(); + MemoryBuffer *mFile = 0; + if (!data) { + ErrorOr<std::unique_ptr<MemoryBuffer> > FileOrErr = + MemoryBuffer::getFile(member.getPath()); + if (!FileOrErr) { + if (ErrMsg) + *ErrMsg = FileOrErr.getError().message(); + return true; + } + mFile = FileOrErr.get().release(); + data = mFile->getBufferStart(); + fSize = mFile->getBufferSize(); + } + + int hdrSize = fSize; + + // Compute the fields of the header + ArchiveMemberHeader Hdr; + bool writeLongName = fillHeader(member,Hdr,hdrSize); + + // Write header to archive file + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + // Write the long filename if its long + if (writeLongName) { + StringRef Name = sys::path::filename(member.getPath()); + ARFile.write(Name.data(), Name.size()); + } + + // Write the (possibly compressed) member's content to the file. + ARFile.write(data,fSize); + + // Make sure the member is an even length + if ((ARFile.tell() & 1) == 1) + ARFile << ARFILE_PAD; + + // Close the mapped file if it was opened + delete mFile; + return false; +} + +// Write the entire archive to the file specified when the archive was created. +// This writes to a temporary file first. Options are for creating a symbol +// table, flattening the file names (no directories, 15 chars max) and +// compressing each archive member. +bool Archive::writeToDisk(std::string *ErrMsg) { + // Make sure they haven't opened up the file, not loaded it, + // but are now trying to write it which would wipe out the file. + if (members.empty() && mapfile && mapfile->getBufferSize() > 8) { + if (ErrMsg) + *ErrMsg = "Can't write an archive not opened for writing"; + return true; + } + + // Create a temporary file to store the archive in + int TmpArchiveFD; + SmallString<128> TmpArchive; + std::error_code EC = sys::fs::createUniqueFile( + archPath + ".temp-archive-%%%%%%%.a", TmpArchiveFD, TmpArchive); + if (EC) + return true; + + // Make sure the temporary gets removed if we crash + sys::RemoveFileOnSignal(TmpArchive); + + // Create archive file for output. + raw_fd_ostream ArchiveFile(TmpArchiveFD, true); + + // Write magic string to archive. + ArchiveFile << ARFILE_MAGIC; + + // Loop over all member files, and write them out. Note that this also + // builds the symbol table, symTab. + for (MembersList::iterator I = begin(), E = end(); I != E; ++I) { + if (writeMember(*I, ArchiveFile, ErrMsg)) { + sys::fs::remove(Twine(TmpArchive)); + ArchiveFile.close(); + return true; + } + } + + // Close archive file. + ArchiveFile.close(); + + // Before we replace the actual archive, we need to forget all the + // members, since they point to data in that old archive. We need to do + // this because we cannot replace an open file on Windows. + cleanUpMemory(); + + if (sys::fs::rename(Twine(TmpArchive), archPath)) { + *ErrMsg = EC.message(); + return true; + } + + return false; +} diff --git a/tools/ndk-link/CMakeLists.txt b/tools/ndk-link/CMakeLists.txt new file mode 100644 index 00000000000..e83e768acae --- /dev/null +++ b/tools/ndk-link/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + asmparser + bitreader + bitwriter + codegen + instrumentation + ipo + linker + selectiondag + ) + +add_llvm_tool(ndk-link + AndroidBitcodeLinker.cpp + Archive.cpp + ArchiveReader.cpp + ArchiveWriter.cpp + ndk-link.cpp + ) diff --git a/tools/ndk-link/Makefile b/tools/ndk-link/Makefile new file mode 100644 index 00000000000..c57aa2b9518 --- /dev/null +++ b/tools/ndk-link/Makefile @@ -0,0 +1,14 @@ +LEVEL := ../.. + +TOOLNAME = ndk-link + +LINK_COMPONENTS := asmparser bitreader bitwriter codegen \ + instrumentation ipo linker selectiondag + +USEDLIBS := LLVMWrap.a + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common + diff --git a/tools/ndk-link/ndk-link.cpp b/tools/ndk-link/ndk-link.cpp new file mode 100644 index 00000000000..1673dcbc67f --- /dev/null +++ b/tools/ndk-link/ndk-link.cpp @@ -0,0 +1,576 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <list> +#include <cstring> +#include <utility> + +#include "AndroidBitcodeLinker.h" +#include "Archive.h" + +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Wrap/BitcodeWrapper.h" + +using namespace llvm; + +static cl::list<std::string> +InputFilenames(cl::Positional, cl::OneOrMore, + cl::desc("<input bitcode files>")); + +static cl::opt<bool> +Shared("shared", cl::ZeroOrMore, cl::desc("Generate shared bitcode library")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Override output filename"), + cl::value_desc("output bitcode file")); + +static cl::opt<std::string> Sysroot("sysroot", + cl::desc("Specify sysroot")); + +static cl::list<std::string> +LibPaths("L", cl::Prefix, + cl::desc("Specify a library search path"), + cl::value_desc("directory")); + +static cl::list<std::string> +Libraries("l", cl::Prefix, + cl::desc("Specify libraries to link to"), + cl::value_desc("library name")); + +static cl::opt<bool> +Verbose("v", cl::desc("Print verbose information")); + +static cl::opt<bool> +DisableOptimizations("disable-opt", + cl::desc("Do not run any optimization passes")); + +static cl::opt<std::string> +SOName("soname", cl::desc("Set the DT_SONAME field to the specified name")); + +static cl::list<bool> WholeArchive("whole-archive", + cl::desc("include every bitcode in the archive after --whole-archive")); + +static cl::list<bool> NoWholeArchive("no-whole-archive", + cl::desc("Turn off of the --whole-archive option for for subsequent archive files")); + +static cl::opt<bool> +LinkNativeBinary("link-native-binary", + cl::ZeroOrMore, + cl::Hidden, + cl::desc("Allow to link native binaries, this is only for testing purpose")); + +// Strip options +static cl::opt<bool> +Strip("strip-all", cl::desc("Strip all symbol info")); + +static cl::opt<bool> +StripDebug("strip-debug", cl::desc("Strip debugger symbol info")); + +static cl::alias A0("s", cl::desc("Alias for --strip-all"), + cl::aliasopt(Strip)); + +static cl::alias A1("S", cl::desc("Alias for --strip-debug"), + cl::aliasopt(StripDebug)); + +static cl::opt<bool> +NoUndefined("no-undefined", cl::desc("-z defs")); + +static cl::list<std::string> +ZOptions("z", cl::desc("-z keyword"), cl::value_desc("keyword")); + +static cl::list<std::string> CO1("Wl", cl::Prefix, + cl::desc("Compatibility option: ignored")); + +static cl::opt<std::string> CO3("exclude-libs", + cl::desc("Compatibility option: ignored")); + +static cl::opt<std::string> CO4("icf", + cl::desc("Compatibility option: ignored")); + +static cl::opt<std::string> CO5("dynamic-linker", + cl::desc("Compatibility option: ignored")); + +static cl::opt<bool> CO6("gc-sections", + cl::ZeroOrMore, + cl::desc("Compatibility option: ignored")); + +static cl::list<std::string> CO7("B", cl::Prefix, + cl::desc("Compatibility option: ignored")); + +// TODO: Support --start-group and --end-group + +static cl::list<bool> CO8("start-group", + cl::desc("Compatibility option: ignored")); + +static cl::list<bool> CO9("end-group", + cl::desc("Compatibility option: ignored")); + +static cl::opt<bool> CO10("eh-frame-hdr", + cl::ZeroOrMore, + cl::desc("Compatibility option: ignored")); + +static cl::opt<bool> CO11("no-warn-mismatch", + cl::ZeroOrMore, + cl::desc("Compatibility option: ignored")); + +static cl::list<unsigned int> OptimizationLevel("O", + cl::Prefix, + cl::desc("Optimization level for bitcode compiler")); + +static std::string progname; + +// Implied library dependency for specific libraries +static std::map<std::string, std::string> ImpliedLibs; + +// FileRemover objects to clean up output files on the event of an error. +static FileRemover OutputRemover; + +static void InitImpliedLibs() { + ImpliedLibs.clear(); + ImpliedLibs.insert(std::make_pair("stlport_shared","gabi++_static")); + ImpliedLibs.insert(std::make_pair("stlport_static","gabi++_static")); + ImpliedLibs.insert(std::make_pair("c++_shared", "gabi++_static")); + ImpliedLibs.insert(std::make_pair("c++_static", "gabi++_static")); +} + +static void PrintAndExit(const std::string &Message, int errcode = 1) { + errs() << progname << ": " << Message << "\n"; + llvm_shutdown(); + exit(errcode); +} + +static void WriteInt32(uint8_t *mem, unsigned offset, uint32_t value) { + mem[offset ] = value & 0x000000ff; + mem[offset+1] = (value & 0x0000ff00) >> 8; + mem[offset+2] = (value & 0x00ff0000) >> 16; + mem[offset+3] = (value & 0xff000000) >> 24; +} + +static std::string getLibName(std::string LibPath) { + std::string libname = sys::path::stem(LibPath); + if (!libname.empty() && libname.substr(0,3) == "lib") + return libname.substr(3,libname.length()-3); + return ""; +} + +static std::string getImpliedLibName(std::string LibPath) { + std::string libname = getLibName(LibPath); + if (ImpliedLibs.count(libname) != 0) + return ImpliedLibs.find(libname)->second; + return ""; +} + +// Helper functions to determine file type + +static bool isBitcode(StringRef FilePath) { +sys::fs::file_magic Magic; + + if (sys::fs::identify_magic(FilePath, Magic)) + return false; + + return Magic == sys::fs::file_magic::bitcode; +} + +static bool isArchive(StringRef FilePath) { +sys::fs::file_magic Magic; + + if (sys::fs::identify_magic(FilePath, Magic)) + return false; + + return Magic == sys::fs::file_magic::archive; +} + +static bool isDynamicLibrary(StringRef FilePath) { +sys::fs::file_magic Magic; + + if (sys::fs::identify_magic(FilePath, Magic)) + return false; + + return Magic == sys::fs::file_magic::elf_shared_object; +} + +static bool isBitcodeArchive(StringRef FilePath) { + if (!isArchive(FilePath)) + return false; + + std::string ErrMsg; + std::auto_ptr<Archive> AutoArch( + Archive::OpenAndLoad(FilePath, llvm::getGlobalContext(), &ErrMsg)); + Archive* arch = AutoArch.get(); + + if (!arch) { + return false; + } + + return arch->isBitcodeArchive(); +} + +static StringRef IsLibrary(StringRef Name, + StringRef Directory) { + SmallString<256> FullPath = Directory; + sys::path::append(FullPath, "lib"+Name); + + // 1. Try bitcode archives + sys::path::replace_extension(FullPath, "a"); + if (isBitcodeArchive(FullPath)) + return FullPath; + + // 2. Try libX.so + sys::path::replace_extension(FullPath, "so"); + + if (LinkNativeBinary && isDynamicLibrary(FullPath)) + return FullPath; + if (isBitcode(FullPath)) + return FullPath; + + // 3. Try libX.bc + sys::path::replace_extension(FullPath, "bc"); + if (isBitcode(FullPath)) + return FullPath; + + // 4. Try native archives + sys::path::replace_extension(FullPath, "a"); + if (LinkNativeBinary && isArchive(FullPath)) + return FullPath; + + // Not found + FullPath.clear(); + return FullPath; +} + +static StringRef FindLib(StringRef Filename) { + if (isArchive(Filename) || isDynamicLibrary(Filename)) + return Filename; + + for (unsigned Index = 0; Index != LibPaths.size(); ++Index) { + StringRef Directory(LibPaths[Index]); + StringRef FullPath = IsLibrary(Filename, Directory); + if (sys::fs::exists(FullPath)) + return FullPath; + } + return ""; +} + +static std::string getSOName(const std::string& Filename, + AndroidBitcodeLinker::ABCItemList& Items) { + for (unsigned i = 0; i < Items.size(); ++i) { + if (Items[i].getFile().str() == Filename && + Items[i].getBitcodeType() == BCHeaderField::BC_SharedObject) { + return Items[i].getSOName(); + } + } + return ""; +} + +static std::string* ProcessArgv(int argc, char **argv, + AndroidBitcodeLinker::ABCItemList& Items) { + std::string *ArgvString = new std::string; + raw_string_ostream Output(*ArgvString); + + for (int i = 1 ; i < argc ; ++i) { + // option + if (argv[i][0] == '-') { + // ignore "-" or "--" + char *c = argv[i]; + while (*c == '-') + ++c; + + // skip -o and -soname, we will add it back later + if (!strcmp (c,"o") || !strcmp(c,"soname")) { + i++; + continue; + } + + // ignore these option that doesn't need + if (!strncmp (c,"sysroot",7) || + !strncmp(c,"L",1) || + !strcmp(c,"disable-opt") || + !strcmp(c,"link-native-binary") || + (c[0] == 'O')) + continue; + + Output << argv[i] << " "; + } + else { // file or directory + StringRef file(argv[i]); + + if (!sys::fs::is_regular_file(file)) { + Output << argv[i] << " "; + continue; + } + + if (!isBitcodeArchive(file)) { + if (!isBitcode(file)) { + if (LinkNativeBinary) { + Output << argv[i] << " "; + } + else { + std::string libname = getLibName(argv[i]); + if (!libname.empty()) + Output << "-l" << libname << " "; + } + } + else { // bitcode or bitcode wrapper + std::string soname = getLibName(getSOName(file.str(), Items)); + + if (!soname.empty()) { + Output << "-l" << soname << " "; + } + } + } + + // Check implied libs + std::string implied_lib = getImpliedLibName(file.str()); + if (!implied_lib.empty()) + Output << "-l" << implied_lib << " "; + } + } + + // Add the implied lib + for (unsigned i = 0 ; i < Items.size(); i++) { + std::string implied_lib = getImpliedLibName(Items[i].getSOName()); + if (!implied_lib.empty()) + Output << "-l" << implied_lib << " "; + } + + // Convert .bc into .so + std::string NativeFileName; + if (Shared) { + if (SOName.empty()) { + NativeFileName = sys::path::stem(OutputFilename); + } + else { + NativeFileName = sys::path::stem(SOName); + } + NativeFileName += ".so"; + Output << "-soname " << NativeFileName << " "; + } + else { + NativeFileName = sys::path::stem(OutputFilename); + } + + std::string implied_lib = getImpliedLibName(NativeFileName); + if (!implied_lib.empty()) + Output << "-l" << implied_lib << " "; + + Output << "-o " << NativeFileName; + Output.flush(); + return ArgvString; +} + +static void WrapAndroidBitcode(std::vector<std::string*> &BCStrings, std::string& LDFlags, raw_ostream &Output) { + std::vector<BCHeaderField> header_fields; + std::vector<uint8_t *> field_data; + size_t variable_header_size = 0; + + // shared object or executable + uint32_t BitcodeType = (Shared) ? BCHeaderField::BC_SharedObject : BCHeaderField::BC_Executable; + field_data.push_back(new uint8_t[sizeof(uint32_t)]); + WriteInt32(field_data.back(), 0, BitcodeType); + BCHeaderField BitcodeTypeField(BCHeaderField::kAndroidBitcodeType, + sizeof(uint32_t), field_data.back()); + header_fields.push_back(BitcodeTypeField); + variable_header_size += BitcodeTypeField.GetTotalSize(); + + // ldflags + field_data.push_back(new uint8_t[LDFlags.size()+1]); + strcpy((char *) field_data.back(), LDFlags.c_str()); + BCHeaderField LDFlagsField(BCHeaderField::kAndroidLDFlags, + LDFlags.size()+1, field_data.back()); + header_fields.push_back(LDFlagsField); + variable_header_size += LDFlagsField.GetTotalSize(); + + // Compute bitcode size + uint32_t totalBCSize = 0; + for (unsigned i = 0; i < BCStrings.size(); ++i) { + uint32_t BCSize = BCStrings[i]->size(); + totalBCSize += BCSize; + } + + AndroidBitcodeWrapper wrapper; + uint32_t opt_lv = 0; + if (OptimizationLevel.size() > 0) + opt_lv = OptimizationLevel[OptimizationLevel.size()-1]; + size_t actualWrapperLen = writeAndroidBitcodeWrapper(&wrapper, + totalBCSize, + 14, /* FIXME: TargetAPI */ + 3400, /* llvm-3.4 */ + opt_lv); /* OptimizationLevel */ + wrapper.BitcodeOffset += variable_header_size; + + // Write fixed fields + Output.write(reinterpret_cast<char*>(&wrapper), actualWrapperLen); + + // Write variable fields + for (unsigned i = 0 ; i < header_fields.size(); ++i) { + const uint32_t buffer_size = 1024; + uint8_t buffer[buffer_size]; + header_fields[i].Write(buffer, buffer_size); + Output.write(reinterpret_cast<char*>(buffer), header_fields[i].GetTotalSize()); + } + + // Delete field data + for (unsigned i = 0 ; i < field_data.size(); ++i) { + delete [] field_data[i]; + } + + for (unsigned i = 0 ; i < BCStrings.size(); ++i) { + Output.write(BCStrings[i]->c_str(), BCStrings[i]->size()); + delete BCStrings[i]; + } +} + +void GenerateBitcode(std::vector<std::string*> &BCStrings, std::string& LDFlags, const std::string& FileName) { + + if (Verbose) + errs() << "Generating Bitcode To " << FileName << '\n'; + + // Create the output file. + std::error_code EC; + tool_output_file Out(FileName.c_str(), EC, sys::fs::F_None); + if (EC) { + PrintAndExit(EC.message()); + return; + } + + WrapAndroidBitcode(BCStrings, LDFlags, Out.os()); + Out.keep(); +} + +static void BuildLinkItems( + AndroidBitcodeLinker::ABCItemList& Items, + const cl::list<std::string>& Files) { + cl::list<bool>::const_iterator wholeIt = WholeArchive.begin(); + cl::list<bool>::const_iterator noWholeIt = NoWholeArchive.begin(); + int wholePos = -1, noWholePos = -1; + std::vector<std::pair<int,int> > wholeRange; + + while (wholeIt != WholeArchive.end()) { + wholePos = WholeArchive.getPosition(wholeIt - WholeArchive.begin()); + if (noWholeIt != NoWholeArchive.end()) + noWholePos = NoWholeArchive.getPosition(noWholeIt - NoWholeArchive.begin()); + else + noWholePos = -1; + + if (wholePos < noWholePos) { + wholeRange.push_back(std::make_pair(wholePos, noWholePos)); + ++wholeIt; + ++noWholeIt; + } + else if (noWholePos <= 0) { + wholeRange.push_back(std::make_pair(wholePos, -1)); + break; + } + else { + noWholeIt++; + } + } + + cl::list<std::string>::const_iterator fileIt = Files.begin(); + while ( fileIt != Files.end() ) { + bool isWhole = false; + int filePos = Files.getPosition(fileIt - Files.begin()); + for(unsigned i = 0 ; i < wholeRange.size() ; ++i) { + if (filePos > wholeRange[i].first && + (filePos < wholeRange[i].second || wholeRange[i].second == -1)) { + isWhole = true; + break; + } + } + if (Verbose) + errs() << *fileIt << ":" << isWhole << '\n'; + Items.push_back(AndroidBitcodeItem(*fileIt++, isWhole)); + } + + // Find libaries in search path + for (cl::list<std::string>::const_iterator lib_iter = Libraries.begin(), + lib_end = Libraries.end(); lib_iter != lib_end; ++lib_iter) { + std::string p = FindLib(*lib_iter); + + if (!p.empty()) { + bool isWhole = false; + int filePos = Libraries.getPosition(lib_iter - Libraries.begin()); + for (unsigned i = 0 ; i < wholeRange.size(); ++i) { + if (filePos > wholeRange[i].first && + (filePos < wholeRange[i].second || wholeRange[i].second == -1)) { + isWhole = true; + break; + } + } + Items.push_back(AndroidBitcodeItem(p, isWhole)); + } + else { + PrintAndExit("cannot find -l" + *lib_iter); + } + } +} + +int main(int argc, char** argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + llvm_shutdown_obj _ShutdownObj; + LLVMContext& Ctx = llvm::getGlobalContext(); + + progname = sys::path::stem(argv[0]); + + cl::ParseCommandLineOptions(argc, argv, "Bitcode link tool\n"); + + // Arrange for the output file to be delete on any errors. + OutputRemover.setFile(OutputFilename); + sys::RemoveFileOnSignal(OutputFilename); + + // Add default search path + if (!Sysroot.empty()) + LibPaths.insert(LibPaths.begin(), Sysroot + "/usr/lib"); + + InitImpliedLibs(); + + // Build a list of the items from our command line + AndroidBitcodeLinker::ABCItemList Items; + BuildLinkItems(Items, InputFilenames); + + // Save each bitcode in strings + std::vector<std::string*> BCStrings; + + LinkerConfig Config(Ctx, progname, OutputFilename, + Verbose, DisableOptimizations, + Strip, StripDebug, LinkNativeBinary); + + AndroidBitcodeLinker linker(Config); + + if (linker.LinkInAndroidBitcodes(Items, BCStrings)) + return 1; + + // Output processed argv + std::string *LDFlags = ProcessArgv(argc, argv, Items); + // Write linked bitcode + GenerateBitcode(BCStrings, *LDFlags, OutputFilename); + + // Operation complete + delete LDFlags; + OutputRemover.releaseFile(); + return 0; +} |