| /* |
| * 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 <cassert> |
| #include <cstdlib> |
| #include <fcntl.h> |
| #include <fstream> |
| #include <sstream> |
| #include <stdint.h> |
| #include <unistd.h> |
| #include "Abcc.h" |
| |
| #if !defined(_WIN32) |
| #include <sys/mman.h> |
| #else |
| #include "mman.h" |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| using namespace abcc; |
| |
| TargetAbi::TargetAbi(const std::string &abi) { |
| if (abi == "armeabi-v7a" || abi == "armeabi-v7a-hard") //ToDo: support armeabi-v7a-hard |
| mAbi = ARMEABI_V7A; |
| else if (abi == "armeabi") |
| mAbi = ARMEABI; |
| else if (abi == "x86") |
| mAbi = X86; |
| else if (abi == "mips") |
| mAbi = MIPS; |
| else if (abi == "arm64-v8a") |
| mAbi = ARM64_V8A; |
| else if (abi == "x86_64") |
| mAbi = X86_64; |
| else if (abi == "mips64") |
| mAbi = MIPS64; |
| else { |
| assert (false && "Unknown abi for abcc. Check your --abi flag."); |
| exit (1); |
| } |
| } |
| |
| |
| BitcodeInfo::BitcodeInfo(const std::string &bc) |
| : mShared(false), mBCPath(bc) { |
| std::string stem = mBCPath.substr(0, mBCPath.rfind(".")); |
| mTargetBCPath = stem + "-target.bc"; |
| mObjPath = stem + ".o"; |
| mOutPath = stem; // If shared, we will add .so after readWrapper |
| mSOName = mBCPath.substr(mBCPath.rfind("/") + 1); |
| } |
| |
| int BitcodeInfo::readWrapper(BitcodeCompiler &compiler) { |
| int fd = open(mBCPath.c_str(), O_RDONLY); |
| |
| if (fd < 0) { |
| return -1; |
| } |
| |
| unsigned char *buf, *p; |
| struct stat st; |
| int bc_offset; |
| |
| fstat (fd, &st); |
| buf = (unsigned char *) mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
| close (fd); |
| |
| bc_offset = transferBytesToNumLe (buf+ 8, 4); |
| p = buf + 4 * 7; // Offset to tag fields. |
| |
| while (p < buf + bc_offset) { |
| uint16_t tag, length; |
| |
| tag = transferBytesToNumLe (p, 2); |
| length = transferBytesToNumLe (p + 2, 2); |
| p += 4; |
| switch (tag) { |
| case 0x4002: // Optimization Level,. e.g., 2 for -O2 |
| mOptimizationLevel = transferBytesToNumLe (p, 4); |
| LOGV("Wrapper field: -O%d", mOptimizationLevel); |
| break; |
| |
| case 0x5002: // LDFLAGS string |
| LOGV("Wrapper field: %s", p); |
| if (compiler.parseLDFlags (*this, reinterpret_cast<const char *>(p)) != 0) { |
| LOGE("Cannot parse ldflags from wrapper"); |
| close(fd); |
| return -1; |
| } |
| break; |
| |
| case 0x4001: // Compiler Version, e.g., 3300 for llvm-3.3. |
| case 0x5001: // Bitcode Type, e.g., rel, shared, or exec. |
| default: |
| // Some field maybe useful, but we use wrapper to encode command line, |
| // this is not necessary for now. |
| break; |
| } |
| |
| p += (length + 3) & ~3; // Data are always padding to 4-byte boundary. |
| } |
| |
| munmap (buf, st.st_size); |
| return 0; |
| } |
| |
| void BitcodeInfo::dropExternalLDLibs(SONameMap &map) { |
| for (SONameMap::iterator i = map.begin(), e = map.end(); i != e; ++i) { |
| BitcodeInfo &info = i->second; |
| for (std::list<std::string>::iterator i_libs = info.mLDLibs.begin(), |
| e_libs = info.mLDLibs.end(); i_libs != e_libs; ) { |
| std::list<std::string>::iterator cur_libs = i_libs++; |
| std::string full_soname = std::string("lib") + *cur_libs + ".so"; |
| if (map.find(full_soname) == map.end()) { |
| LOGV("Drop -l%s from %s for linking order decision", cur_libs->c_str(), info.mSOName.c_str()); |
| info.mLDLibs.erase(cur_libs); |
| } |
| } |
| } |
| } |
| |
| // This function reads N bytes from BUFFER in little endian. |
| int BitcodeInfo::transferBytesToNumLe(const unsigned char *buffer, size_t n) { |
| int ret = 0; |
| const unsigned char *p = buffer + n; |
| |
| while (--p >= buffer) |
| ret = ret * 0x100 + *p; |
| |
| return ret; |
| } |
| |
| BitcodeCompiler::BitcodeCompiler(const std::string &abi, const std::string &sysroot, const std::string &working_dir, const bool savetemps) |
| : mAbi(abi), mSysroot(sysroot), mWorkingDir(working_dir), mRet(RET_OK), mSaveTemps(savetemps) { |
| // CFlags |
| mGlobalCFlags = kGlobalTargetAttrs[mAbi].mBaseCFlags; |
| mGlobalCFlags += std::string(" -mtriple=") + kGlobalTargetAttrs[mAbi].mTriple; |
| mGlobalCFlags += " -filetype=obj -relocation-model=pic -code-model=small"; |
| mGlobalCFlags += " -use-init-array -mc-relax-all -ffunction-sections"; |
| |
| // LDFlags |
| mGlobalLDFlags = kGlobalTargetAttrs[mAbi].mBaseLDFlags; |
| mGlobalLDFlags += std::string(" -Bsymbolic -X -m ") + kGlobalTargetAttrs[mAbi].mLinkEmulation; |
| mGlobalLDFlags += std::string(" --sysroot=") + mSysroot; |
| mGlobalLDFlags += " --build-id --eh-frame-hdr"; |
| |
| // LDLibs |
| mGlobalLDLibs = " "; |
| } |
| |
| BitcodeCompiler::~BitcodeCompiler() { |
| } |
| |
| void BitcodeCompiler::translate() { |
| for (std::vector<BitcodeInfo>::const_iterator i = mBitcodeFiles.begin(), |
| e = mBitcodeFiles.end(); i != e; ++i) { |
| const BitcodeInfo &bc = *i; |
| LOGV("Translate bitcode: %s -> %s", bc.mBCPath.c_str(), bc.mTargetBCPath.c_str()); |
| std::string cmd = mExecutableToolsPath[(unsigned)CMD_TRANSLATE]; |
| cmd += std::string(" -arch=") + kGlobalTargetAttrs[mAbi].mArch; |
| cmd += " " + bc.mBCPath + " -o " + bc.mTargetBCPath; |
| runCmd(cmd, /*dump=*/true); |
| if (returnCode() != RET_OK) { |
| mRet = RET_FAIL_TRANSLATE; |
| return; |
| } |
| if (!mSaveTemps) |
| removeIntermediateFile(bc.mBCPath); |
| } |
| } |
| |
| void BitcodeCompiler::compile() { |
| for (std::vector<BitcodeInfo>::const_iterator i = mBitcodeFiles.begin(), |
| e = mBitcodeFiles.end(); i != e; ++i) { |
| const BitcodeInfo &bc = *i; |
| LOGV("Compile bitcode: %s -> %s", bc.mTargetBCPath.c_str(), bc.mObjPath.c_str()); |
| std::ostringstream os; |
| |
| os << mExecutableToolsPath[(unsigned)CMD_COMPILE] |
| << " " << mGlobalCFlags |
| << " -O" << bc.mOptimizationLevel |
| << " " << bc.mTargetBCPath |
| << " -o " << bc.mObjPath; |
| |
| #if ON_DEVICE && VERBOSE |
| Timer t_llc; |
| t_llc.start(); |
| #endif |
| runCmd(os.str(), /*dump=*/true); |
| #if ON_DEVICE && VERBOSE |
| llc_usec += t_llc.stop(); |
| #endif |
| if (returnCode() != RET_OK) { |
| mRet = RET_FAIL_COMPILE; |
| return; |
| } |
| if (!mSaveTemps) |
| removeIntermediateFile(bc.mTargetBCPath); |
| } |
| } |
| |
| void BitcodeCompiler::link() { |
| BitcodeInfo::dropExternalLDLibs(mSonameMap); |
| |
| while (!mSonameMap.empty()) { |
| SONameMap::iterator i = mSonameMap.begin(), e = mSonameMap.end(); |
| for (; i != e; ++i) { |
| const BitcodeInfo &bc = i->second; |
| |
| if (bc.mLDLibs.empty()) { |
| // No internal dependency for this bitcode |
| LOGV("Link: %s -> %s", bc.mObjPath.c_str(), bc.mSOName.c_str()); |
| std::string cmd = mExecutableToolsPath[(unsigned)CMD_LINK]; |
| std::string libdir = (mAbi == TargetAbi::X86_64) ? "lib64" : "lib"; |
| cmd += " " + mGlobalLDFlags; |
| cmd += " " + bc.mLDFlags; |
| if (bc.mShared) { |
| cmd += std::string(" ") + mSysroot + "/usr/" + libdir + "/crtbegin_so.o"; |
| cmd += " -shared " + bc.mObjPath + " -o " + bc.mOutPath; |
| cmd += " -soname " + bc.mSOName; |
| } else { |
| cmd += std::string(" ") + mSysroot + "/usr/" + libdir + "/crtbegin_dynamic.o"; |
| cmd += " " + bc.mObjPath + " -o " + bc.mOutPath; |
| } |
| // Add ldlibs |
| cmd += " " + bc.mLDLocalLibsStr; |
| cmd += " " + mGlobalLDLibs; |
| cmd += " " + bc.mLDLibsStr; |
| cmd += " " + mExecutableToolsPath[(unsigned)CMD_LINK_RUNTIME]; |
| if (bc.mShared) |
| cmd += std::string(" ") + mSysroot + "/usr/" + libdir + "/crtend_so.o"; |
| else |
| cmd += std::string(" ") + mSysroot + "/usr/" + libdir + "/crtend_android.o"; |
| runCmd(cmd, /*dump=*/true); |
| if (returnCode() != RET_OK) |
| return; |
| |
| copyRuntime(bc); |
| if (!mSaveTemps) |
| removeIntermediateFile(bc.mObjPath); |
| |
| mSonameMap.erase(i); |
| BitcodeInfo::dropExternalLDLibs(mSonameMap); |
| break; // Re-compute |
| } |
| } // for |
| |
| if (i == e) { |
| LOGE("Failed to compute linking order: Internal cyclic dependency!"); |
| mRet = RET_FAIL_LINK; |
| return; |
| } |
| } // while |
| } |
| |
| void BitcodeCompiler::runCmd(std::string cmd, bool dump) { |
| LOGV("Command: %s", cmd.c_str()); |
| std::string logfilename = mWorkingDir + "/compile_log"; |
| if (dump) { |
| cmd += " > " + logfilename + " 2>&1"; |
| } |
| int ret = system(cmd.c_str()); |
| if (ret != 0) { |
| mRet = RET_FAIL_RUN_CMD; |
| if (dump) { |
| std::ifstream ifs(logfilename.c_str()); |
| std::stringstream sstr; |
| sstr << ifs.rdbuf(); |
| LOGE("Error message: %s", sstr.str().c_str()); |
| std::fstream fout; |
| std::string file = mWorkingDir + "/compile_error"; |
| fout.open(file.c_str(), std::fstream::out | std::fstream::app); |
| fout << "Failed command: " << cmd << "\n"; |
| fout << "Error message: " << sstr.str() << "\n"; |
| fout.close(); |
| } |
| return; |
| } |
| mRet = RET_OK; |
| } |
| |
| void BitcodeCompiler::prepareBitcodes() { |
| getBitcodeFiles(); |
| createSONameMapping(); |
| } |
| |
| void BitcodeCompiler::createSONameMapping() { |
| for (std::vector<BitcodeInfo>::const_iterator i = mBitcodeFiles.begin(), |
| e = mBitcodeFiles.end(); i != e; ++i) { |
| const BitcodeInfo &info = *i; |
| LOGV("Map soname %s -> %s", info.mSOName.c_str(), info.mBCPath.c_str()); |
| mSonameMap[info.mSOName] = info; |
| } |
| } |