From 3958f8060ac0adccd977c0fab7a53d45f3fce58d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 1 Feb 2017 15:44:40 -0800 Subject: 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 --- libunwindstack/ElfInterfaceArm.cpp | 127 +++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 libunwindstack/ElfInterfaceArm.cpp (limited to 'libunwindstack/ElfInterfaceArm.cpp') diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp new file mode 100644 index 000000000..e15732073 --- /dev/null +++ b/libunwindstack/ElfInterfaceArm.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 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 +#include + +#include "ArmExidx.h" +#include "ElfInterface.h" +#include "ElfInterfaceArm.h" +#include "Machine.h" +#include "Memory.h" +#include "Regs.h" + +bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) { + if (start_offset_ == 0 || total_entries_ == 0) { + return false; + } + + // Need to subtract the load_bias from the pc. + if (pc < load_bias_) { + return false; + } + pc -= load_bias_; + + size_t first = 0; + size_t last = total_entries_; + while (first < last) { + size_t current = (first + last) / 2; + uint32_t addr = addrs_[current]; + if (addr == 0) { + if (!GetPrel31Addr(start_offset_ + current * 8, &addr)) { + return false; + } + addrs_[current] = addr; + } + if (pc == addr) { + *entry_offset = start_offset_ + current * 8; + return true; + } + if (pc < addr) { + last = current; + } else { + first = current + 1; + } + } + if (last != 0) { + *entry_offset = start_offset_ + (last - 1) * 8; + return true; + } + return false; +} + +bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) { + uint32_t data; + if (!memory_->Read32(offset, &data)) { + return false; + } + + // Sign extend the value if necessary. + int32_t value = (static_cast(data) << 1) >> 1; + *addr = offset + value; + return true; +} + +#if !defined(PT_ARM_EXIDX) +#define PT_ARM_EXIDX 0x70000001 +#endif + +bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { + if (type != PT_ARM_EXIDX) { + return false; + } + + Elf32_Phdr phdr; + if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { + return true; + } + if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { + return true; + } + // The load_bias_ should always be set by this time. + start_offset_ = phdr.p_vaddr - load_bias_; + total_entries_ = phdr.p_memsz / 8; + return true; +} + +bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) { + return StepExidx(pc, regs, process_memory) || + ElfInterface32::Step(pc, regs, process_memory); +} + +bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) { + RegsArm* regs_arm = reinterpret_cast(regs); + // First try arm, then try dwarf. + uint64_t entry_offset; + if (!FindEntry(pc, &entry_offset)) { + return false; + } + ArmExidx arm(regs_arm, memory_, process_memory); + arm.set_cfa(regs_arm->sp()); + if (arm.ExtractEntryData(entry_offset) && arm.Eval()) { + // If the pc was not set, then use the LR registers for the PC. + if (!arm.pc_set()) { + regs_arm->set_pc((*regs_arm)[ARM_REG_LR]); + (*regs_arm)[ARM_REG_PC] = regs_arm->pc(); + } else { + regs_arm->set_pc((*regs_arm)[ARM_REG_PC]); + } + regs_arm->set_sp(arm.cfa()); + (*regs_arm)[ARM_REG_SP] = regs_arm->sp(); + return true; + } + return false; +} -- cgit v1.2.3