summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2020-03-16 19:38:23 -0700
committerYi Kong <yikong@google.com>2020-04-24 00:47:55 +0800
commitba4e42a893749a9f16de48c53f9cdfbcfa49d3ae (patch)
tree0d27a71d1ecfef535b282ec814d4e97ae49724ca
parent0a3eb7e28bafafce2dcf396fee86f75f7897b9df (diff)
downloadextras-ba4e42a893749a9f16de48c53f9cdfbcfa49d3ae.tar.gz
simpleperf: add class interface to read elf files.
This is to keep file mapping memory buffer. And support reading more than one content in one open instance in the future. It also supports embedded elf files in apks as normal elf files. Bug: 153039105 Test: run simpleperf_unit_test. Change-Id: Ia424926d112cbcd9970f11ffa56d047ff6df7872 Merged-In: Ia424926d112cbcd9970f11ffa56d047ff6df7872 (cherry picked from commit 02e2033f9ef34be0189e151bf6fef60b1285afd9)
-rw-r--r--simpleperf/ETMDecoder.cpp14
-rw-r--r--simpleperf/read_apk.cpp6
-rw-r--r--simpleperf/read_elf.cpp120
-rw-r--r--simpleperf/read_elf.h23
-rw-r--r--simpleperf/read_elf_test.cpp9
5 files changed, 112 insertions, 60 deletions
diff --git a/simpleperf/ETMDecoder.cpp b/simpleperf/ETMDecoder.cpp
index 6e65dd59..ae6b6c2f 100644
--- a/simpleperf/ETMDecoder.cpp
+++ b/simpleperf/ETMDecoder.cpp
@@ -210,19 +210,19 @@ class MemAccess : public ITargetMemAccess {
}
llvm::MemoryBuffer* GetMemoryBuffer(Dso* dso) {
- if (auto it = memory_buffers_.find(dso); it != memory_buffers_.end()) {
- return it->second.get();
+ auto it = elf_map_.find(dso);
+ if (it == elf_map_.end()) {
+ ElfStatus status;
+ auto res = elf_map_.emplace(dso, ElfFile::Open(dso->GetDebugFilePath(), &status));
+ it = res.first;
}
- auto buffer_or_err = llvm::MemoryBuffer::getFile(dso->GetDebugFilePath());
- llvm::MemoryBuffer* buffer = buffer_or_err ? buffer_or_err.get().release() : nullptr;
- memory_buffers_.emplace(dso, buffer);
- return buffer;
+ return it->second ? it->second->GetMemoryBuffer() : nullptr;
}
// map from trace id to thread id
std::unordered_map<uint8_t, pid_t> tid_map_;
ThreadTree& thread_tree_;
- std::unordered_map<Dso*, std::unique_ptr<llvm::MemoryBuffer>> memory_buffers_;
+ std::unordered_map<Dso*, std::unique_ptr<ElfFile>> elf_map_;
// cache of the last buffer
uint8_t trace_id_ = 0;
const char* buffer_ = nullptr;
diff --git a/simpleperf/read_apk.cpp b/simpleperf/read_apk.cpp
index a6aae6fe..651a2412 100644
--- a/simpleperf/read_apk.cpp
+++ b/simpleperf/read_apk.cpp
@@ -96,11 +96,7 @@ std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByOffsetWithoutCache(
}
// We found something in the zip file at the right spot. Is it an ELF?
- if (lseek(ahelper->GetFd(), found_entry.offset, SEEK_SET) != found_entry.offset) {
- PLOG(ERROR) << "lseek() failed in " << apk_path << " offset " << found_entry.offset;
- return nullptr;
- }
- if (IsValidElfFile(ahelper->GetFd()) != ElfStatus::NO_ERROR) {
+ if (IsValidElfFile(ahelper->GetFd(), found_entry.offset) != ElfStatus::NO_ERROR) {
// Omit files that are not ELF files.
return nullptr;
}
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index 98cf1937..822d1e3e 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -43,6 +43,8 @@
#define ELF_NOTE_GNU "GNU"
#define NT_GNU_BUILD_ID 3
+using namespace simpleperf;
+
std::ostream& operator<<(std::ostream& os, const ElfStatus& status) {
switch (status) {
case ElfStatus::NO_ERROR:
@@ -78,28 +80,14 @@ bool IsValidElfFileMagic(const char* buf, size_t buf_size) {
return (buf_size >= 4u && memcmp(buf, elf_magic, 4) == 0);
}
-ElfStatus IsValidElfFile(int fd) {
+ElfStatus IsValidElfFile(int fd, uint64_t file_offset) {
char buf[4];
- if (!android::base::ReadFully(fd, buf, 4)) {
+ if (!android::base::ReadFullyAtOffset(fd, buf, 4, file_offset)) {
return ElfStatus::READ_FAILED;
}
return IsValidElfFileMagic(buf, 4) ? ElfStatus::NO_ERROR : ElfStatus::FILE_MALFORMED;
}
-ElfStatus IsValidElfPath(const std::string& filename) {
- if (!IsRegularFile(filename)) {
- return ElfStatus::FILE_NOT_FOUND;
- }
- std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE;
- FILE* fp = fopen(filename.c_str(), mode.c_str());
- if (fp == nullptr) {
- return ElfStatus::READ_FAILED;
- }
- ElfStatus result = IsValidElfFile(fileno(fp));
- fclose(fp);
- return result;
-}
-
bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id) {
const char* p = section;
const char* end = p + section_size;
@@ -173,15 +161,16 @@ static ElfStatus GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId
}
struct BinaryWrapper {
- llvm::object::OwningBinary<llvm::object::Binary> binary;
- llvm::object::ObjectFile* obj;
-
- BinaryWrapper() : obj(nullptr) {
- }
+ std::unique_ptr<llvm::MemoryBuffer> buffer;
+ std::unique_ptr<llvm::object::Binary> binary;
+ llvm::object::ObjectFile* obj = nullptr;
};
static ElfStatus OpenObjectFile(const std::string& filename, uint64_t file_offset,
uint64_t file_size, BinaryWrapper* wrapper) {
+ if (!IsRegularFile(filename)) {
+ return ElfStatus::FILE_NOT_FOUND;
+ }
android::base::unique_fd fd = FileHelper::OpenReadOnly(filename);
if (fd == -1) {
return ElfStatus::READ_FAILED;
@@ -192,6 +181,10 @@ static ElfStatus OpenObjectFile(const std::string& filename, uint64_t file_offse
return ElfStatus::READ_FAILED;
}
}
+ ElfStatus status = IsValidElfFile(fd, file_offset);
+ if (status != ElfStatus::NO_ERROR) {
+ return status;
+ }
auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(fd, filename, file_size, file_offset);
if (!buffer_or_err) {
return ElfStatus::READ_FAILED;
@@ -200,9 +193,9 @@ static ElfStatus OpenObjectFile(const std::string& filename, uint64_t file_offse
if (!binary_or_err) {
return ElfStatus::READ_FAILED;
}
- wrapper->binary = llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
- std::move(buffer_or_err.get()));
- wrapper->obj = llvm::dyn_cast<llvm::object::ObjectFile>(wrapper->binary.getBinary());
+ wrapper->buffer = std::move(buffer_or_err.get());
+ wrapper->binary = std::move(binary_or_err.get());
+ wrapper->obj = llvm::dyn_cast<llvm::object::ObjectFile>(wrapper->binary.get());
if (wrapper->obj == nullptr) {
return ElfStatus::FILE_MALFORMED;
}
@@ -215,9 +208,9 @@ static ElfStatus OpenObjectFileInMemory(const char* data, size_t size, BinaryWra
if (!binary_or_err) {
return ElfStatus::FILE_MALFORMED;
}
- wrapper->binary = llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
- std::move(buffer));
- wrapper->obj = llvm::dyn_cast<llvm::object::ObjectFile>(wrapper->binary.getBinary());
+ wrapper->buffer = std::move(buffer);
+ wrapper->binary = std::move(binary_or_err.get());
+ wrapper->obj = llvm::dyn_cast<llvm::object::ObjectFile>(wrapper->binary.get());
if (wrapper->obj == nullptr) {
return ElfStatus::FILE_MALFORMED;
}
@@ -225,10 +218,6 @@ static ElfStatus OpenObjectFileInMemory(const char* data, size_t size, BinaryWra
}
ElfStatus GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
- ElfStatus result = IsValidElfPath(filename);
- if (result != ElfStatus::NO_ERROR) {
- return result;
- }
return GetBuildIdFromEmbeddedElfFile(filename, 0, 0, build_id);
}
@@ -442,10 +431,6 @@ ElfStatus MatchBuildId(llvm::object::ObjectFile* obj, const BuildId& expected_bu
ElfStatus ParseSymbolsFromElfFile(const std::string& filename,
const BuildId& expected_build_id,
const std::function<void(const ElfFileSymbol&)>& callback) {
- ElfStatus result = IsValidElfPath(filename);
- if (result != ElfStatus::NO_ERROR) {
- return result;
- }
return ParseSymbolsFromEmbeddedElfFile(filename, 0, 0, expected_build_id, callback);
}
@@ -537,10 +522,6 @@ ElfStatus ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename
const BuildId& expected_build_id,
uint64_t* min_vaddr,
uint64_t* file_offset_of_min_vaddr) {
- ElfStatus result = IsValidElfPath(filename);
- if (result != ElfStatus::NO_ERROR) {
- return result;
- }
return ReadMinExecutableVirtualAddressFromEmbeddedElfFile(filename, 0, 0, expected_build_id,
min_vaddr, file_offset_of_min_vaddr);
}
@@ -570,12 +551,8 @@ ElfStatus ReadMinExecutableVirtualAddressFromEmbeddedElfFile(const std::string&
ElfStatus ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
std::string* content) {
- ElfStatus result = IsValidElfPath(filename);
- if (result != ElfStatus::NO_ERROR) {
- return result;
- }
BinaryWrapper wrapper;
- result = OpenObjectFile(filename, 0, 0, &wrapper);
+ ElfStatus result = OpenObjectFile(filename, 0, 0, &wrapper);
if (result != ElfStatus::NO_ERROR) {
return result;
}
@@ -587,3 +564,58 @@ ElfStatus ReadSectionFromElfFile(const std::string& filename, const std::string&
return ElfStatus::FILE_MALFORMED;
}
}
+
+namespace {
+
+template <typename T>
+class ElfFileImpl {};
+
+template <typename ELFT>
+class ElfFileImpl<llvm::object::ELFFile<ELFT>> : public ElfFile {
+ public:
+ ElfFileImpl(BinaryWrapper&& wrapper, const llvm::object::ELFFile<ELFT>* elf)
+ : wrapper_(std::move(wrapper)), elf_(elf) {}
+
+ llvm::MemoryBuffer* GetMemoryBuffer() override {
+ return wrapper_.buffer.get();
+ }
+
+ private:
+ BinaryWrapper wrapper_;
+ const llvm::object::ELFFile<ELFT>* elf_;
+};
+
+} // namespace
+
+namespace simpleperf {
+
+std::unique_ptr<ElfFile> ElfFile::Open(const std::string& filename, ElfStatus* status) {
+ BinaryWrapper wrapper;
+ auto tuple = SplitUrlInApk(filename);
+ if (std::get<0>(tuple)) {
+ EmbeddedElf* elf = ApkInspector::FindElfInApkByName(std::get<1>(tuple), std::get<2>(tuple));
+ if (elf == nullptr) {
+ *status = ElfStatus::FILE_NOT_FOUND;
+ } else {
+ *status = OpenObjectFile(elf->filepath(), elf->entry_offset(), elf->entry_size(), &wrapper);
+ }
+ } else {
+ *status = OpenObjectFile(filename, 0, 0, &wrapper);
+ }
+ if (*status == ElfStatus::NO_ERROR) {
+ if (auto obj = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(wrapper.obj)) {
+ using elf_t = std::decay_t<decltype(*obj->getELFFile())>;
+ return std::unique_ptr<ElfFile>(
+ new ElfFileImpl<elf_t>(std::move(wrapper), obj->getELFFile()));
+ }
+ if (auto obj = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(wrapper.obj)) {
+ using elf_t = std::decay_t<decltype(*obj->getELFFile())>;
+ return std::unique_ptr<ElfFile>(
+ new ElfFileImpl<elf_t>(std::move(wrapper), obj->getELFFile()));
+ }
+ *status = ElfStatus::FILE_MALFORMED;
+ }
+ return nullptr;
+}
+
+} // namespace simpleperf \ No newline at end of file
diff --git a/simpleperf/read_elf.h b/simpleperf/read_elf.h
index 1ab9a1f3..f289ee17 100644
--- a/simpleperf/read_elf.h
+++ b/simpleperf/read_elf.h
@@ -84,11 +84,28 @@ ElfStatus ReadMinExecutableVirtualAddressFromEmbeddedElfFile(const std::string&
ElfStatus ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
std::string* content);
-// Expose the following functions for unit tests.
+namespace llvm {
+class MemoryBuffer;
+}
+
+namespace simpleperf {
+
+class ElfFile {
+ public:
+ static std::unique_ptr<ElfFile> Open(const std::string& filename, ElfStatus* status);
+ virtual ~ElfFile() {}
+
+ virtual llvm::MemoryBuffer* GetMemoryBuffer() = 0;
+
+ protected:
+ ElfFile() {}
+};
+
+} // namespace simpleperf
+
bool IsArmMappingSymbol(const char* name);
-ElfStatus IsValidElfFile(int fd);
+ElfStatus IsValidElfFile(int fd, uint64_t file_offset = 0);
bool IsValidElfFileMagic(const char* buf, size_t buf_size);
-ElfStatus IsValidElfPath(const std::string& filename);
bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id);
#endif // SIMPLE_PERF_READ_ELF_H_
diff --git a/simpleperf/read_elf_test.cpp b/simpleperf/read_elf_test.cpp
index 9f74366c..cc5aa52b 100644
--- a/simpleperf/read_elf_test.cpp
+++ b/simpleperf/read_elf_test.cpp
@@ -29,6 +29,8 @@
#define ELF_NOTE_GNU "GNU"
#define NT_GNU_BUILD_ID 3
+using namespace simpleperf;
+
TEST(read_elf, GetBuildIdFromNoteSection) {
BuildId build_id;
std::vector<char> data;
@@ -137,7 +139,12 @@ TEST(read_elf, arm_mapping_symbol) {
ASSERT_FALSE(IsArmMappingSymbol("$a_no_dot"));
}
-TEST(read_elf, IsValidElfPath) {
+TEST(read_elf, ElfFile_Open) {
+ auto IsValidElfPath = [](const std::string& path) {
+ ElfStatus status;
+ ElfFile::Open(path, &status);
+ return status;
+ };
ASSERT_NE(ElfStatus::NO_ERROR, IsValidElfPath("/dev/zero"));
TemporaryFile tmp_file;
ASSERT_EQ(ElfStatus::READ_FAILED, IsValidElfPath(tmp_file.path));