diff options
author | Yabin Cui <yabinc@google.com> | 2020-03-16 19:38:23 -0700 |
---|---|---|
committer | Yi Kong <yikong@google.com> | 2020-04-24 00:47:55 +0800 |
commit | ba4e42a893749a9f16de48c53f9cdfbcfa49d3ae (patch) | |
tree | 0d27a71d1ecfef535b282ec814d4e97ae49724ca | |
parent | 0a3eb7e28bafafce2dcf396fee86f75f7897b9df (diff) | |
download | extras-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.cpp | 14 | ||||
-rw-r--r-- | simpleperf/read_apk.cpp | 6 | ||||
-rw-r--r-- | simpleperf/read_elf.cpp | 120 | ||||
-rw-r--r-- | simpleperf/read_elf.h | 23 | ||||
-rw-r--r-- | simpleperf/read_elf_test.cpp | 9 |
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)); |