diff options
author | Yabin Cui <yabinc@google.com> | 2020-04-23 18:10:45 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-04-23 18:10:45 +0000 |
commit | 38568ac7cfd893885b3fc9a69724f4d90d3b745c (patch) | |
tree | 0d27a71d1ecfef535b282ec814d4e97ae49724ca | |
parent | 1b52bd6fb2a70d0b80c93fe5ea49d92ddb94676e (diff) | |
parent | ba4e42a893749a9f16de48c53f9cdfbcfa49d3ae (diff) | |
download | extras-38568ac7cfd893885b3fc9a69724f4d90d3b745c.tar.gz |
simpleperf: add class interface to read elf files. am: ba4e42a893
Change-Id: Ib33f81c7962ab0a61f191a3795abfcc01b2486f3
-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)); |