1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <elf.h> 18 #include <stdint.h> 19 20 #include <unwindstack/Memory.h> 21 #include <unwindstack/Regs.h> 22 23 #include "ArmExidx.h" 24 #include "ElfInterfaceArm.h" 25 #include "Machine.h" 26 27 namespace unwindstack { 28 29 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) { 30 if (start_offset_ == 0 || total_entries_ == 0) { 31 return false; 32 } 33 34 // Need to subtract the load_bias from the pc. 35 if (pc < load_bias_) { 36 return false; 37 } 38 pc -= load_bias_; 39 40 size_t first = 0; 41 size_t last = total_entries_; 42 while (first < last) { 43 size_t current = (first + last) / 2; 44 uint32_t addr = addrs_[current]; 45 if (addr == 0) { 46 if (!GetPrel31Addr(start_offset_ + current * 8, &addr)) { 47 return false; 48 } 49 addrs_[current] = addr; 50 } 51 if (pc == addr) { 52 *entry_offset = start_offset_ + current * 8; 53 return true; 54 } 55 if (pc < addr) { 56 last = current; 57 } else { 58 first = current + 1; 59 } 60 } 61 if (last != 0) { 62 *entry_offset = start_offset_ + (last - 1) * 8; 63 return true; 64 } 65 return false; 66 } 67 68 bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) { 69 uint32_t data; 70 if (!memory_->Read32(offset, &data)) { 71 return false; 72 } 73 74 // Sign extend the value if necessary. 75 int32_t value = (static_cast<int32_t>(data) << 1) >> 1; 76 *addr = offset + value; 77 return true; 78 } 79 80 #if !defined(PT_ARM_EXIDX) 81 #define PT_ARM_EXIDX 0x70000001 82 #endif 83 84 bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { 85 if (type != PT_ARM_EXIDX) { 86 return false; 87 } 88 89 Elf32_Phdr phdr; 90 if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { 91 return true; 92 } 93 if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { 94 return true; 95 } 96 // The load_bias_ should always be set by this time. 97 start_offset_ = phdr.p_vaddr - load_bias_; 98 total_entries_ = phdr.p_memsz / 8; 99 return true; 100 } 101 102 bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) { 103 // Dwarf unwind information is precise about whether a pc is covered or not, 104 // but arm unwind information only has ranges of pc. In order to avoid 105 // incorrectly doing a bad unwind using arm unwind information for a 106 // different function, always try and unwind with the dwarf information first. 107 return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory); 108 } 109 110 bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) { 111 RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs); 112 uint64_t entry_offset; 113 if (!FindEntry(pc, &entry_offset)) { 114 return false; 115 } 116 ArmExidx arm(regs_arm, memory_, process_memory); 117 arm.set_cfa(regs_arm->sp()); 118 if (arm.ExtractEntryData(entry_offset) && arm.Eval()) { 119 // If the pc was not set, then use the LR registers for the PC. 120 if (!arm.pc_set()) { 121 regs_arm->set_pc((*regs_arm)[ARM_REG_LR]); 122 (*regs_arm)[ARM_REG_PC] = regs_arm->pc(); 123 } else { 124 regs_arm->set_pc((*regs_arm)[ARM_REG_PC]); 125 } 126 regs_arm->set_sp(arm.cfa()); 127 (*regs_arm)[ARM_REG_SP] = regs_arm->sp(); 128 return true; 129 } 130 return false; 131 } 132 133 } // namespace unwindstack 134