Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b791a76e authored by Yabin Cui's avatar Yabin Cui
Browse files

libbacktrace_offline: support unwinding of shared libraries in apk file.

Bug: 26962895
Change-Id: I009080f26e7323247c3ab24eea614eec4432ca6a
parent 80b1b188
Loading
Loading
Loading
Loading
+22 −7
Original line number Diff line number Diff line
@@ -94,18 +94,34 @@ include $(LOCAL_PATH)/Android.build.mk
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 \
@@ -114,9 +130,6 @@ libbacktrace_offline_static_libraries_host := \
	libLLVMSupport \

module := libbacktrace_offline
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
@@ -129,6 +142,8 @@ libbacktrace_offline_static_libraries := \
	liblog \
	libunwind \
	liblzma \
	libziparchive \
	libz \
	libLLVMObject \
	libLLVMBitReader \
	libLLVMMC \
+98 −7
Original line number Diff line number Diff line
@@ -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 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<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 owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
  if (owning_binary.getError()) {
    auto binary_or_err = llvm::object::createBinary(llvm::StringRef(filename));
    if (!binary_or_err) {
      return nullptr;
    }
  llvm::object::Binary* binary = owning_binary.get().getBinary();
    owning_binary = std::move(binary_or_err.get());
  }
  llvm::object::Binary* binary = owning_binary.getBinary();
  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
  if (obj == nullptr) {
    return nullptr;