diff options
author | Yabin Cui <yabinc@google.com> | 2016-03-18 18:46:08 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2016-03-29 14:04:34 -0700 |
commit | 41571cab4d8e43f0ce1c465b8d1fca61b4070556 (patch) | |
tree | 18492a277bd15ebf223cab7fcbcb26f41f7b61a5 | |
parent | 371f82002ab713095b098b49a8575431da025579 (diff) | |
download | unwinding-41571cab4d8e43f0ce1c465b8d1fca61b4070556.tar.gz |
libbacktrace_offline: support unwinding of shared libraries in apk file.
Bug: 26962895
Change-Id: I009080f26e7323247c3ab24eea614eec4432ca6a
(cherry picked from commit b791a76ed76442a74c466c6116787c73ceea2170)
-rw-r--r-- | libbacktrace/Android.mk | 28 | ||||
-rw-r--r-- | libbacktrace/BacktraceOffline.cpp | 105 |
2 files changed, 118 insertions, 15 deletions
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index ee56a5e..d5a7e06 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 dc3ed67..9a4f622 100644 --- a/libbacktrace/BacktraceOffline.cpp +++ b/libbacktrace/BacktraceOffline.cpp @@ -29,11 +29,14 @@ extern "C" { #include <ucontext.h> #include <unistd.h> +#include <memory> #include <string> #include <vector> +#include <android-base/file.h> #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> +#include <ziparchive/zip_archive.h> #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 DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) { - if (!IsValidElfPath(filename)) { - return nullptr; +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; } - auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename)); - if (owning_binary.getError()) { - return nullptr; + 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<llvm::object::Binary> OpenEmbeddedElfFile(const std::string& filename) { + llvm::object::OwningBinary<llvm::object::Binary> 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<llvm::object::Binary>(std::move(binary_or_err.get()), + std::move(buffer_or_err.get())); +} + +static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) { + llvm::object::OwningBinary<llvm::object::Binary> 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()); } - llvm::object::Binary* binary = owning_binary.get().getBinary(); + llvm::object::Binary* binary = owning_binary.getBinary(); auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary); if (obj == nullptr) { return nullptr; |