diff options
author | Christopher Ferris <cferris@google.com> | 2017-03-17 17:01:45 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-03-17 17:01:46 +0000 |
commit | cc404b51fc424cbb786abce8b8f36b1dce1c0ab9 (patch) | |
tree | 62ebb9ce531a818efa51e551179dccad32ea949e /libunwindstack/ElfInterfaceArm.cpp | |
parent | 0add3ca7c47b287bc01b5e738cb9b682d43f6f97 (diff) | |
parent | 3958f8060ac0adccd977c0fab7a53d45f3fce58d (diff) |
Merge "Elf interface for new unwinder."
Diffstat (limited to 'libunwindstack/ElfInterfaceArm.cpp')
-rw-r--r-- | libunwindstack/ElfInterfaceArm.cpp | 127 |
1 files changed, 127 insertions, 0 deletions
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 <elf.h> +#include <stdint.h> + +#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<int32_t>(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<RegsArm*>(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; +} |