From 02092b36da261c1a411dcf76c00b188e78e21b68 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Fri, 18 Mar 2016 18:46:08 -0700 Subject: [PATCH] libbacktrace_offline: support unwinding of shared libraries in apk file. Bug: 26962895 Change-Id: I009080f26e7323247c3ab24eea614eec4432ca6a (cherry picked from commit b791a76ed76442a74c466c6116787c73ceea2170) --- libbacktrace/Android.mk | 28 +++++--- libbacktrace/BacktraceOffline.cpp | 105 ++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 15 deletions(-) diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index ee56a5e10..d5a7e061d 100644 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -86,18 +86,34 @@ libbacktrace_static_libraries := libbacktrace_offline_src_files := \ BacktraceOffline.cpp \ +# Use shared llvm library on device to save space. libbacktrace_offline_shared_libraries := \ libbacktrace \ + libbase \ liblog \ libunwind \ - -# Use shared llvm library on device to save space. -libbacktrace_offline_shared_libraries_target := \ + libutils \ libLLVM \ +libbacktrace_offline_static_libraries := \ + libziparchive \ + libz \ + +module := libbacktrace_offline +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk + +libbacktrace_offline_shared_libraries := \ + libbacktrace \ + libbase \ + liblog \ + libunwind \ + libziparchive-host \ + # Use static llvm libraries on host to remove dependency on 32-bit llvm shared library # which is not included in the prebuilt. -libbacktrace_offline_static_libraries_host := \ +libbacktrace_offline_static_libraries := \ libLLVMObject \ libLLVMBitReader \ libLLVMMC \ @@ -106,10 +122,6 @@ libbacktrace_offline_static_libraries_host := \ libLLVMSupport \ module := libbacktrace_offline -module_tag := optional -build_type := target -build_target := SHARED_LIBRARY -include $(LOCAL_PATH)/Android.build.mk build_type := host libbacktrace_multilib := both include $(LOCAL_PATH)/Android.build.mk diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp index dc3ed6725..9a4f62224 100644 --- a/libbacktrace/BacktraceOffline.cpp +++ b/libbacktrace/BacktraceOffline.cpp @@ -29,11 +29,14 @@ extern "C" { #include #include +#include #include #include +#include #include #include +#include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" @@ -641,15 +644,103 @@ static bool IsValidElfPath(const std::string& filename) { return memcmp(buf, elf_magic, 4) == 0; } +static bool IsValidApkPath(const std::string& apk_path) { + static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04}; + struct stat st; + if (stat(apk_path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) { + return false; + } + FILE* fp = fopen(apk_path.c_str(), "reb"); + if (fp == nullptr) { + return false; + } + char buf[4]; + if (fread(buf, 4, 1, fp) != 1) { + fclose(fp); + return false; + } + fclose(fp); + return memcmp(buf, zip_preamble, 4) == 0; +} + +class ScopedZiparchiveHandle { + public: + ScopedZiparchiveHandle(ZipArchiveHandle handle) : handle_(handle) { + } + + ~ScopedZiparchiveHandle() { + CloseArchive(handle_); + } + + private: + ZipArchiveHandle handle_; +}; + +llvm::object::OwningBinary OpenEmbeddedElfFile(const std::string& filename) { + llvm::object::OwningBinary nothing; + size_t pos = filename.find("!/"); + if (pos == std::string::npos) { + return nothing; + } + std::string apk_file = filename.substr(0, pos); + std::string elf_file = filename.substr(pos + 2); + if (!IsValidApkPath(apk_file)) { + BACK_LOGW("%s is not a valid apk file", apk_file.c_str()); + return nothing; + } + ZipArchiveHandle handle; + int32_t ret_code = OpenArchive(apk_file.c_str(), &handle); + if (ret_code != 0) { + CloseArchive(handle); + BACK_LOGW("failed to open archive %s: %s", apk_file.c_str(), ErrorCodeString(ret_code)); + return nothing; + } + ScopedZiparchiveHandle scoped_handle(handle); + ZipEntry zentry; + ret_code = FindEntry(handle, ZipString(elf_file.c_str()), &zentry); + if (ret_code != 0) { + BACK_LOGW("failed to find %s in %s: %s", elf_file.c_str(), apk_file.c_str(), + ErrorCodeString(ret_code)); + return nothing; + } + if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) { + BACK_LOGW("%s is compressed in %s, which doesn't support running directly", elf_file.c_str(), + apk_file.c_str()); + return nothing; + } + auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(GetFileDescriptor(handle), apk_file, + zentry.uncompressed_length, + zentry.offset); + if (!buffer_or_err) { + BACK_LOGW("failed to read %s in %s: %s", elf_file.c_str(), apk_file.c_str(), + buffer_or_err.getError().message().c_str()); + return nothing; + } + auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef()); + if (!binary_or_err) { + BACK_LOGW("failed to create binary for %s in %s: %s", elf_file.c_str(), apk_file.c_str(), + binary_or_err.getError().message().c_str()); + return nothing; + } + return llvm::object::OwningBinary(std::move(binary_or_err.get()), + std::move(buffer_or_err.get())); +} + static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) { - if (!IsValidElfPath(filename)) { - return nullptr; + llvm::object::OwningBinary owning_binary; + if (filename.find("!/") != std::string::npos) { + owning_binary = OpenEmbeddedElfFile(filename); + } else { + if (!IsValidElfPath(filename)) { + return nullptr; + } + auto binary_or_err = llvm::object::createBinary(llvm::StringRef(filename)); + if (!binary_or_err) { + return nullptr; + } + owning_binary = std::move(binary_or_err.get()); } - auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename)); - if (owning_binary.getError()) { - return nullptr; - } - llvm::object::Binary* binary = owning_binary.get().getBinary(); + llvm::object::Binary* binary = owning_binary.getBinary(); auto obj = llvm::dyn_cast(binary); if (obj == nullptr) { return nullptr;