simpleperf: change interface of read_apk.h.
1. Remove GetBuildIdFromApkFile and ParseSymbolsFromApkFile, because
their function can be supported using ApkInspector::FindElfInApkByName
and read_elf.h.
2. Remove ApkInspector::FindOffsetInApkByName, instead the caller can
use ApkInspector::FindElfInApkByName directly.
3. In ApkInspector::embedded_elf_cache_, add map for entry_name. So
the EmbeddedElfs added by cmd_record.cpp can be used by OfflineUnwinder.
Also support reading min executable virtual address from embedded elfs
in ElfDso.
Also avoid segfault reading build id of elf files with broken section
table, and add test.
Bug: none.
Test: run simpleperf_unit_test.
Change-Id: I2e4f51a5e348138cbf7445ec6dd42dbd6ae1b03d
diff --git a/simpleperf/OfflineUnwinder.cpp b/simpleperf/OfflineUnwinder.cpp
index 8d36677..a288cf7 100644
--- a/simpleperf/OfflineUnwinder.cpp
+++ b/simpleperf/OfflineUnwinder.cpp
@@ -146,16 +146,15 @@
bt_map.offset = map->pgoff;
bt_map.name = map->dso->GetDebugFilePath();
if (bt_map.offset == 0) {
- size_t apk_pos = bt_map.name.find_last_of('!');
- if (apk_pos != std::string::npos) {
+ auto tuple = SplitUrlInApk(bt_map.name);
+ if (std::get<0>(tuple)) {
// The unwinder does not understand the ! format, so change back to
// the previous format (apk, offset).
- std::string shared_lib(bt_map.name.substr(apk_pos + 2));
- bt_map.name = bt_map.name.substr(0, apk_pos);
- uint64_t offset;
- uint32_t length;
- if (ApkInspector::FindOffsetInApkByName(bt_map.name, shared_lib, &offset, &length)) {
- bt_map.offset = offset;
+ EmbeddedElf* elf = ApkInspector::FindElfInApkByName(std::get<1>(tuple),
+ std::get<2>(tuple));
+ if (elf != nullptr) {
+ bt_map.name = elf->filepath();
+ bt_map.offset = elf->entry_offset();
}
}
}
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index cdc2104..0434274 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -374,8 +374,20 @@
BuildId build_id = GetExpectedBuildId();
uint64_t addr;
- ElfStatus result = ReadMinExecutableVirtualAddressFromElfFile(
- GetDebugFilePath(), build_id, &addr);
+ ElfStatus result;
+ auto tuple = SplitUrlInApk(debug_file_path_);
+ if (std::get<0>(tuple)) {
+ EmbeddedElf* elf = ApkInspector::FindElfInApkByName(std::get<1>(tuple),
+ std::get<2>(tuple));
+ if (elf == nullptr) {
+ result = ElfStatus::FILE_NOT_FOUND;
+ } else {
+ result = ReadMinExecutableVirtualAddressFromEmbeddedElfFile(
+ elf->filepath(), elf->entry_offset(), elf->entry_size(), build_id, &addr);
+ }
+ } else {
+ result = ReadMinExecutableVirtualAddressFromElfFile(debug_file_path_, build_id, &addr);
+ }
if (result != ElfStatus::NO_ERROR) {
LOG(WARNING) << "failed to read min virtual address of "
<< GetDebugFilePath() << ": " << result;
@@ -423,8 +435,13 @@
ElfStatus status;
std::tuple<bool, std::string, std::string> tuple = SplitUrlInApk(debug_file_path_);
if (std::get<0>(tuple)) {
- status = ParseSymbolsFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id,
- symbol_callback);
+ EmbeddedElf* elf = ApkInspector::FindElfInApkByName(std::get<1>(tuple), std::get<2>(tuple));
+ if (elf == nullptr) {
+ status = ElfStatus::FILE_NOT_FOUND;
+ } else {
+ status = ParseSymbolsFromEmbeddedElfFile(elf->filepath(), elf->entry_offset(),
+ elf->entry_size(), build_id, symbol_callback);
+ }
} else {
status = ParseSymbolsFromElfFile(debug_file_path_, build_id, symbol_callback);
}
@@ -576,7 +593,13 @@
auto tuple = SplitUrlInApk(dso_path);
ElfStatus result;
if (std::get<0>(tuple)) {
- result = GetBuildIdFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id);
+ EmbeddedElf* elf = ApkInspector::FindElfInApkByName(std::get<1>(tuple), std::get<2>(tuple));
+ if (elf == nullptr) {
+ result = ElfStatus::FILE_NOT_FOUND;
+ } else {
+ result = GetBuildIdFromEmbeddedElfFile(elf->filepath(), elf->entry_offset(),
+ elf->entry_size(), build_id);
+ }
} else {
result = GetBuildIdFromElfFile(dso_path, build_id);
}
diff --git a/simpleperf/dso_test.cpp b/simpleperf/dso_test.cpp
index cc44193..175ce1d 100644
--- a/simpleperf/dso_test.cpp
+++ b/simpleperf/dso_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/test_utils.h>
#include "get_test_data.h"
+#include "read_apk.h"
using namespace simpleperf_dso_impl;
@@ -85,3 +86,18 @@
GTEST_LOG_(INFO) << "This test only runs on linux because of libdexfile";
#endif // defined(__linux__)
}
+
+TEST(dso, embedded_elf) {
+ const std::string file_path = GetUrlInApk(GetTestData(APK_FILE), NATIVELIB_IN_APK);
+ std::unique_ptr<Dso> dso = Dso::CreateDso(DSO_ELF_FILE, file_path);
+ ASSERT_TRUE(dso);
+ ASSERT_EQ(dso->Path(), file_path);
+ ASSERT_EQ(dso->GetDebugFilePath(), file_path);
+ ASSERT_EQ(dso->MinVirtualAddress(), 0u);
+ const Symbol* symbol = dso->FindSymbol(0x9a4);
+ ASSERT_TRUE(symbol != nullptr);
+ ASSERT_STREQ(symbol->Name(), "Java_com_example_hellojni_HelloJni_callFunc1");
+ BuildId build_id;
+ ASSERT_TRUE(GetBuildIdFromDsoPath(file_path, &build_id));
+ ASSERT_EQ(build_id, native_lib_build_id);
+}
diff --git a/simpleperf/read_apk.cpp b/simpleperf/read_apk.cpp
index b3b45c4..31ba28a 100644
--- a/simpleperf/read_apk.cpp
+++ b/simpleperf/read_apk.cpp
@@ -32,23 +32,42 @@
#include "read_elf.h"
#include "utils.h"
-std::map<ApkInspector::ApkOffset, std::unique_ptr<EmbeddedElf>> ApkInspector::embedded_elf_cache_;
+std::unordered_map<std::string, ApkInspector::ApkNode> ApkInspector::embedded_elf_cache_;
EmbeddedElf* ApkInspector::FindElfInApkByOffset(const std::string& apk_path, uint64_t file_offset) {
// Already in cache?
- ApkOffset ami(apk_path, file_offset);
- auto it = embedded_elf_cache_.find(ami);
- if (it != embedded_elf_cache_.end()) {
+ ApkNode& node = embedded_elf_cache_[apk_path];
+ auto it = node.offset_map.find(file_offset);
+ if (it != node.offset_map.end()) {
return it->second.get();
}
std::unique_ptr<EmbeddedElf> elf = FindElfInApkByOffsetWithoutCache(apk_path, file_offset);
EmbeddedElf* result = elf.get();
- embedded_elf_cache_[ami] = std::move(elf);
+ node.offset_map[file_offset] = std::move(elf);
+ if (result != nullptr) {
+ node.name_map[result->entry_name()] = result;
+ }
return result;
}
-std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByOffsetWithoutCache(const std::string& apk_path,
- uint64_t file_offset) {
+EmbeddedElf* ApkInspector::FindElfInApkByName(const std::string& apk_path,
+ const std::string& entry_name) {
+ ApkNode& node = embedded_elf_cache_[apk_path];
+ auto it = node.name_map.find(entry_name);
+ if (it != node.name_map.end()) {
+ return it->second;
+ }
+ std::unique_ptr<EmbeddedElf> elf = FindElfInApkByNameWithoutCache(apk_path, entry_name);
+ EmbeddedElf* result = elf.get();
+ node.name_map[entry_name] = result;
+ if (result != nullptr) {
+ node.offset_map[result->entry_offset()] = std::move(elf);
+ }
+ return result;
+}
+
+std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByOffsetWithoutCache(
+ const std::string& apk_path, uint64_t file_offset) {
// Crack open the apk(zip) file and take a look.
if (!IsValidApkPath(apk_path)) {
return nullptr;
@@ -103,52 +122,37 @@
// Omit files that are not ELF files.
return nullptr;
}
-
- // Elf found: add EmbeddedElf to vector, update cache.
return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, entry_name, zentry.offset,
zentry.uncompressed_length));
}
-bool ApkInspector::FindOffsetInApkByName(const std::string& apk_path,
- const std::string& elf_filename, uint64_t* offset,
- uint32_t* uncompressed_length) {
+std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByNameWithoutCache(
+ const std::string& apk_path, const std::string& entry_name) {
if (!IsValidApkPath(apk_path)) {
- return false;
+ return nullptr;
}
FileHelper fhelper = FileHelper::OpenReadOnly(apk_path);
if (!fhelper) {
- return false;
+ return nullptr;
}
ArchiveHelper ahelper(fhelper.fd(), apk_path);
if (!ahelper) {
- return false;
+ return nullptr;
}
ZipArchiveHandle& handle = ahelper.archive_handle();
ZipEntry zentry;
- int32_t rc = FindEntry(handle, ZipString(elf_filename.c_str()), &zentry);
+ int32_t rc = FindEntry(handle, ZipString(entry_name.c_str()), &zentry);
if (rc != 0) {
- LOG(ERROR) << "failed to find " << elf_filename << " in " << apk_path
+ LOG(ERROR) << "failed to find " << entry_name << " in " << apk_path
<< ": " << ErrorCodeString(rc);
- return false;
- }
- if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
- LOG(ERROR) << "shared library " << elf_filename << " in " << apk_path << " is compressed";
- return false;
- }
- *offset = zentry.offset;
- *uncompressed_length = zentry.uncompressed_length;
- return true;
-}
-
-std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByName(const std::string& apk_path,
- const std::string& elf_filename) {
- uint64_t offset;
- uint32_t uncompressed_length;
- if (!FindOffsetInApkByName(apk_path, elf_filename, &offset, &uncompressed_length)) {
return nullptr;
}
- return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, elf_filename, offset,
- uncompressed_length));
+ if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
+ LOG(ERROR) << "shared library " << entry_name << " in " << apk_path << " is compressed";
+ return nullptr;
+ }
+ return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, entry_name, zentry.offset,
+ zentry.uncompressed_length));
}
bool IsValidApkPath(const std::string& apk_path) {
@@ -182,23 +186,3 @@
}
return std::make_tuple(true, path.substr(0, pos), path.substr(pos + 2));
}
-
-ElfStatus GetBuildIdFromApkFile(const std::string& apk_path, const std::string& elf_filename,
- BuildId* build_id) {
- std::unique_ptr<EmbeddedElf> ee = ApkInspector::FindElfInApkByName(apk_path, elf_filename);
- if (ee == nullptr) {
- return ElfStatus::FILE_NOT_FOUND;
- }
- return GetBuildIdFromEmbeddedElfFile(apk_path, ee->entry_offset(), ee->entry_size(), build_id);
-}
-
-ElfStatus ParseSymbolsFromApkFile(const std::string& apk_path, const std::string& elf_filename,
- const BuildId& expected_build_id,
- const std::function<void(const ElfFileSymbol&)>& callback) {
- std::unique_ptr<EmbeddedElf> ee = ApkInspector::FindElfInApkByName(apk_path, elf_filename);
- if (ee == nullptr) {
- return ElfStatus::FILE_NOT_FOUND;
- }
- return ParseSymbolsFromEmbeddedElfFile(apk_path, ee->entry_offset(), ee->entry_size(),
- expected_build_id, callback);
-}
diff --git a/simpleperf/read_apk.h b/simpleperf/read_apk.h
index 26c266c..484bafe 100644
--- a/simpleperf/read_apk.h
+++ b/simpleperf/read_apk.h
@@ -19,10 +19,10 @@
#include <stdint.h>
-#include <map>
#include <memory>
#include <string>
#include <tuple>
+#include <unordered_map>
#include "read_elf.h"
@@ -68,38 +68,27 @@
// APK inspector helper class
class ApkInspector {
public:
- // Given an APK/ZIP/JAR file and an offset into that file, if the
- // corresponding region of the APK corresponds to an uncompressed
- // ELF file, then return pertinent info on the ELF.
- static bool FindOffsetInApkByName(const std::string& apk_path,
- const std::string& elf_filename,
- uint64_t* offset, uint32_t* uncompressed_length);
static EmbeddedElf* FindElfInApkByOffset(const std::string& apk_path, uint64_t file_offset);
- static std::unique_ptr<EmbeddedElf> FindElfInApkByName(const std::string& apk_path,
- const std::string& elf_filename);
+ static EmbeddedElf* FindElfInApkByName(const std::string& apk_path,
+ const std::string& entry_name);
private:
static std::unique_ptr<EmbeddedElf> FindElfInApkByOffsetWithoutCache(const std::string& apk_path,
uint64_t file_offset);
+ static std::unique_ptr<EmbeddedElf> FindElfInApkByNameWithoutCache(
+ const std::string& apk_path, const std::string& entry_name);
- // First component of pair is APK file path, second is offset into APK.
- typedef std::pair<std::string, uint64_t> ApkOffset;
-
- static std::map<ApkOffset, std::unique_ptr<EmbeddedElf>> embedded_elf_cache_;
+ struct ApkNode {
+ // Map from entry_offset to EmbeddedElf.
+ std::unordered_map<uint64_t, std::unique_ptr<EmbeddedElf>> offset_map;
+ // Map from entry_name to EmbeddedElf.
+ std::unordered_map<std::string, EmbeddedElf*> name_map;
+ };
+ static std::unordered_map<std::string, ApkNode> embedded_elf_cache_;
};
-// Export for test only.
bool IsValidApkPath(const std::string& apk_path);
-
std::string GetUrlInApk(const std::string& apk_path, const std::string& elf_filename);
std::tuple<bool, std::string, std::string> SplitUrlInApk(const std::string& path);
-ElfStatus GetBuildIdFromApkFile(const std::string& apk_path, const std::string& elf_filename,
- BuildId* build_id);
-
-ElfStatus ParseSymbolsFromApkFile(const std::string& apk_path, const std::string& elf_filename,
- const BuildId& expected_build_id,
- const std::function<void(const ElfFileSymbol&)>& callback);
-
-
#endif // SIMPLE_PERF_READ_APK_H_
diff --git a/simpleperf/read_apk_test.cpp b/simpleperf/read_apk_test.cpp
index f9640e8..4d9e2e1 100644
--- a/simpleperf/read_apk_test.cpp
+++ b/simpleperf/read_apk_test.cpp
@@ -41,16 +41,6 @@
ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size());
}
-TEST(read_apk, FindOffsetInApkByName) {
- uint64_t offset;
- uint32_t length;
- ASSERT_FALSE(ApkInspector::FindOffsetInApkByName("/dev/null", "", &offset, &length));
- ASSERT_FALSE(ApkInspector::FindOffsetInApkByName(GetTestData(APK_FILE), "", &offset, &length));
- ASSERT_TRUE(ApkInspector::FindOffsetInApkByName(GetTestData(APK_FILE), NATIVELIB_IN_APK, &offset, &length));
- ASSERT_EQ(NATIVELIB_OFFSET_IN_APK, static_cast<size_t>(offset));
- ASSERT_EQ(NATIVELIB_SIZE_IN_APK, length);
-}
-
TEST(read_apk, FindElfInApkByName) {
ASSERT_TRUE(ApkInspector::FindElfInApkByName("/dev/null", "") == nullptr);
ASSERT_TRUE(ApkInspector::FindElfInApkByName(GetTestData(APK_FILE), "") == nullptr);
@@ -59,17 +49,3 @@
ASSERT_EQ(NATIVELIB_OFFSET_IN_APK, ee->entry_offset());
ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size());
}
-
-TEST(read_apk, GetBuildIdFromApkFile) {
- BuildId build_id;
- ASSERT_EQ(ElfStatus::NO_ERROR, GetBuildIdFromApkFile(GetTestData(APK_FILE), NATIVELIB_IN_APK, &build_id));
- ASSERT_EQ(build_id, native_lib_build_id);
-}
-
-TEST(read_apk, ParseSymbolsFromApkFile) {
- std::map<std::string, ElfFileSymbol> symbols;
- ASSERT_EQ(ElfStatus::NO_SYMBOL_TABLE,
- ParseSymbolsFromApkFile(GetTestData(APK_FILE), NATIVELIB_IN_APK, native_lib_build_id,
- std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
- CheckElfFileSymbols(symbols);
-}
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index 6f88230..e8ce554 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -143,13 +143,18 @@
template <class ELFT>
ElfStatus GetBuildIdFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf, BuildId* build_id) {
+ llvm::StringRef data = elf->getData();
+ const char* binary_start = data.data();
+ const char* binary_end = data.data() + data.size();
for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
const llvm::object::ELFSectionRef& section_ref = *it;
if (section_ref.getType() == llvm::ELF::SHT_NOTE) {
- llvm::StringRef data;
if (it->getContents(data)) {
return ElfStatus::READ_FAILED;
}
+ if (data.data() < binary_start || data.data() + data.size() > binary_end) {
+ return ElfStatus::NO_BUILD_ID;
+ }
if (GetBuildIdFromNoteSection(data.data(), data.size(), build_id)) {
return ElfStatus::NO_ERROR;
}
@@ -521,8 +526,17 @@
if (result != ElfStatus::NO_ERROR) {
return result;
}
+ return ReadMinExecutableVirtualAddressFromEmbeddedElfFile(filename, 0, 0, expected_build_id,
+ min_vaddr);
+}
+
+ElfStatus ReadMinExecutableVirtualAddressFromEmbeddedElfFile(const std::string& filename,
+ uint64_t file_offset,
+ uint32_t file_size,
+ const BuildId& expected_build_id,
+ uint64_t* min_vaddr) {
BinaryWrapper wrapper;
- result = OpenObjectFile(filename, 0, 0, &wrapper);
+ ElfStatus result = OpenObjectFile(filename, file_offset, file_size, &wrapper);
if (result != ElfStatus::NO_ERROR) {
return result;
}
@@ -530,14 +544,12 @@
if (result != ElfStatus::NO_ERROR) {
return result;
}
-
if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(wrapper.obj)) {
return ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
} else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(wrapper.obj)) {
return ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
- } else {
- return ElfStatus::FILE_MALFORMED;
}
+ return ElfStatus::FILE_MALFORMED;
}
ElfStatus ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
diff --git a/simpleperf/read_elf.h b/simpleperf/read_elf.h
index a6e6cfb..9dbe9ac 100644
--- a/simpleperf/read_elf.h
+++ b/simpleperf/read_elf.h
@@ -73,6 +73,11 @@
ElfStatus ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
const BuildId& expected_build_id,
uint64_t* min_addr);
+ElfStatus ReadMinExecutableVirtualAddressFromEmbeddedElfFile(const std::string& filename,
+ uint64_t file_offset,
+ uint32_t file_size,
+ const BuildId& expected_build_id,
+ uint64_t* min_vaddr);
ElfStatus ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
std::string* content);
diff --git a/simpleperf/read_elf_test.cpp b/simpleperf/read_elf_test.cpp
index 599c413..cbc26bf 100644
--- a/simpleperf/read_elf_test.cpp
+++ b/simpleperf/read_elf_test.cpp
@@ -155,8 +155,15 @@
}
TEST(read_elf, read_elf_with_broken_section_table) {
+ std::string elf_path = GetTestData("libsgmainso-6.4.36.so");
std::map<std::string, ElfFileSymbol> symbols;
ASSERT_EQ(ElfStatus::NO_SYMBOL_TABLE,
- ParseSymbolsFromElfFile(GetTestData("libsgmainso-6.4.36.so"), BuildId(),
+ ParseSymbolsFromElfFile(elf_path, BuildId(),
std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
+ BuildId build_id;
+ ASSERT_EQ(ElfStatus::NO_BUILD_ID, GetBuildIdFromElfFile(elf_path, &build_id));
+ uint64_t min_vaddr;
+ ASSERT_EQ(ElfStatus::NO_ERROR, ReadMinExecutableVirtualAddressFromElfFile(elf_path, BuildId(),
+ &min_vaddr));
+ ASSERT_EQ(min_vaddr, 0u);
}