| /* |
| * 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(M)); |
| |
| if (!Config.isDisableOpt()) |
| PassManagerBuilder().populateLTOPassManager(PM, |
| false /*Internalize*/, |
| true /*RunInliner*/); |
| // 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 &FileName = Item.getFile(); |
| ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = |
| MemoryBuffer::getFileOrSTDIN(FileName); |
| if (!Buffer) { |
| Error = "Error reading file '" + FileName.str() + "'" + ": " + |
| Buffer.getError().message(); |
| return nullptr; |
| } |
| |
| MemoryBuffer *BufferPtr = Buffer.get().get(); |
| BitcodeWrapper *Wrapper = new BitcodeWrapper(BufferPtr->getBufferStart(), |
| BufferPtr->getBufferSize()); |
| Item.setWrapper(Wrapper); |
| assert(Item.getWrapper() != 0); |
| ErrorOr<Module *> Result = parseBitcodeFile(BufferPtr, Config.getContext()); |
| if (!Result) { |
| Error = "Bitcode file '" + FileName.str() + "' could not be loaded." + |
| Result.getError().message(); |
| errs() << Error << '\n'; |
| return nullptr; |
| } |
| |
| 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.getArch() != Triple::le64) || |
| 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(), &Error)) |
| return error("Cannot link file '" + File.str() + "': " + Error); |
| 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 |
| std::string moduleErrorMsg; |
| if (linker->linkInModule(aModule, &moduleErrorMsg)) |
| return error("Cannot link in module '" + |
| aModule->getModuleIdentifier() + "': " + moduleErrorMsg); |
| 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, &moduleErrorMsg)) |
| return error("Cannot link in module '" + |
| aModule->getModuleIdentifier() + "': " + moduleErrorMsg); |
| } |
| } |
| |
| GetAllSymbols(linker->getModule(), UndefinedSymbols, DefinedSymbols); |
| |
| set_subtract(UndefinedSymbols, NotDefinedByArchive); |
| |
| if (UndefinedSymbols.empty()) |
| break; |
| } while (CurrentlyUndefinedSymbols != UndefinedSymbols); |
| |
| return false; |
| } |