Elf interface for new unwinder.
This cl includes the code to read arm unwind information from a shared
library.
Bug: 23762183
Test: Passes all unit tests. I can dump the arm unwind information
Test: for an arm shared library.
Change-Id: I43501ea2eab843b81de8bd5128401dd1971af8d3
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
new file mode 100644
index 0000000..d59e9d8
--- /dev/null
+++ b/libunwindstack/ElfInterface.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "ElfInterface.h"
+#include "Memory.h"
+#include "Regs.h"
+
+template <typename EhdrType, typename PhdrType, typename ShdrType>
+bool ElfInterface::ReadAllHeaders() {
+ EhdrType ehdr;
+ if (!memory_->Read(0, &ehdr, sizeof(ehdr))) {
+ return false;
+ }
+
+ if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
+ return false;
+ }
+ return ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+}
+
+template <typename EhdrType, typename PhdrType>
+bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
+ uint64_t offset = ehdr.e_phoff;
+ for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
+ PhdrType phdr;
+ if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+ return false;
+ }
+
+ if (HandleType(offset, phdr.p_type)) {
+ continue;
+ }
+
+ switch (phdr.p_type) {
+ case PT_LOAD:
+ {
+ // Get the flags first, if this isn't an executable header, ignore it.
+ if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+ return false;
+ }
+ if ((phdr.p_flags & PF_X) == 0) {
+ continue;
+ }
+
+ if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ return false;
+ }
+ if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ return false;
+ }
+ if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ return false;
+ }
+ pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
+ static_cast<size_t>(phdr.p_memsz)};
+ if (phdr.p_offset == 0) {
+ load_bias_ = phdr.p_vaddr;
+ }
+ break;
+ }
+
+ case PT_GNU_EH_FRAME:
+ if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ return false;
+ }
+ eh_frame_offset_ = phdr.p_offset;
+ if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ return false;
+ }
+ eh_frame_size_ = phdr.p_memsz;
+ break;
+
+ case PT_DYNAMIC:
+ if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ return false;
+ }
+ dynamic_offset_ = phdr.p_offset;
+ if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ return false;
+ }
+ dynamic_size_ = phdr.p_memsz;
+ break;
+ }
+ }
+ return true;
+}
+
+template <typename EhdrType, typename ShdrType>
+bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
+ uint64_t offset = ehdr.e_shoff;
+ uint64_t sec_offset = 0;
+ uint64_t sec_size = 0;
+
+ // Get the location of the section header names.
+ // If something is malformed in the header table data, we aren't going
+ // to terminate, we'll simply ignore this part.
+ ShdrType shdr;
+ if (ehdr.e_shstrndx < ehdr.e_shnum) {
+ uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+ if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
+ && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ sec_offset = shdr.sh_offset;
+ sec_size = shdr.sh_size;
+ }
+ }
+
+ // Skip the first header, it's always going to be NULL.
+ for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
+ if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+ return false;
+ }
+
+ if (shdr.sh_type == SHT_PROGBITS) {
+ // Look for the .debug_frame and .gnu_debugdata.
+ if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+ return false;
+ }
+ if (shdr.sh_name < sec_size) {
+ std::string name;
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ if (name == ".debug_frame") {
+ if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
+ && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ debug_frame_offset_ = shdr.sh_offset;
+ debug_frame_size_ = shdr.sh_size;
+ }
+ } else if (name == ".gnu_debugdata") {
+ if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
+ && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ gnu_debugdata_offset_ = shdr.sh_offset;
+ gnu_debugdata_size_ = shdr.sh_size;
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+template <typename DynType>
+bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
+ if (soname_type_ == SONAME_INVALID) {
+ return false;
+ }
+ if (soname_type_ == SONAME_VALID) {
+ *soname = soname_;
+ return true;
+ }
+
+ soname_type_ = SONAME_INVALID;
+
+ uint64_t soname_offset = 0;
+ uint64_t strtab_offset = 0;
+ uint64_t strtab_size = 0;
+
+ // Find the soname location from the dynamic headers section.
+ DynType dyn;
+ uint64_t offset = dynamic_offset_;
+ uint64_t max_offset = offset + dynamic_size_;
+ for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
+ if (!memory_->Read(offset, &dyn, sizeof(dyn))) {
+ return false;
+ }
+
+ if (dyn.d_tag == DT_STRTAB) {
+ strtab_offset = dyn.d_un.d_ptr;
+ } else if (dyn.d_tag == DT_STRSZ) {
+ strtab_size = dyn.d_un.d_val;
+ } else if (dyn.d_tag == DT_SONAME) {
+ soname_offset = dyn.d_un.d_val;
+ } else if (dyn.d_tag == DT_NULL) {
+ break;
+ }
+ }
+
+ soname_offset += strtab_offset;
+ if (soname_offset >= strtab_offset + strtab_size) {
+ return false;
+ }
+ if (!memory_->ReadString(soname_offset, &soname_)) {
+ return false;
+ }
+ soname_type_ = SONAME_VALID;
+ *soname = soname_;
+ return true;
+}
+
+bool ElfInterface::Step(uint64_t, Regs*, Memory*) {
+ return false;
+}
+
+// Instantiate all of the needed template functions.
+template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
+template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
+
+template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&);
+template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&);
+
+template bool ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
+template bool ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
+
+template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
+template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);