blob: bab84ccb9b08273e14fb0aa9a46ab12d6bc486b7 [file] [log] [blame]
Christopher Ferris3958f802017-02-01 15:44:40 -08001/*
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 "ArmExidx.h"
21#include "ElfInterface.h"
22#include "ElfInterfaceArm.h"
23#include "Machine.h"
24#include "Memory.h"
25#include "Regs.h"
26
27bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
28 if (start_offset_ == 0 || total_entries_ == 0) {
29 return false;
30 }
31
32 // Need to subtract the load_bias from the pc.
33 if (pc < load_bias_) {
34 return false;
35 }
36 pc -= load_bias_;
37
38 size_t first = 0;
39 size_t last = total_entries_;
40 while (first < last) {
41 size_t current = (first + last) / 2;
42 uint32_t addr = addrs_[current];
43 if (addr == 0) {
44 if (!GetPrel31Addr(start_offset_ + current * 8, &addr)) {
45 return false;
46 }
47 addrs_[current] = addr;
48 }
49 if (pc == addr) {
50 *entry_offset = start_offset_ + current * 8;
51 return true;
52 }
53 if (pc < addr) {
54 last = current;
55 } else {
56 first = current + 1;
57 }
58 }
59 if (last != 0) {
60 *entry_offset = start_offset_ + (last - 1) * 8;
61 return true;
62 }
63 return false;
64}
65
66bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) {
67 uint32_t data;
68 if (!memory_->Read32(offset, &data)) {
69 return false;
70 }
71
72 // Sign extend the value if necessary.
73 int32_t value = (static_cast<int32_t>(data) << 1) >> 1;
74 *addr = offset + value;
75 return true;
76}
77
78#if !defined(PT_ARM_EXIDX)
79#define PT_ARM_EXIDX 0x70000001
80#endif
81
82bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
83 if (type != PT_ARM_EXIDX) {
84 return false;
85 }
86
87 Elf32_Phdr phdr;
Christopher Ferrisf447c8e2017-04-03 12:39:47 -070088 if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
Christopher Ferris3958f802017-02-01 15:44:40 -080089 return true;
90 }
Christopher Ferrisf447c8e2017-04-03 12:39:47 -070091 if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
Christopher Ferris3958f802017-02-01 15:44:40 -080092 return true;
93 }
94 // The load_bias_ should always be set by this time.
95 start_offset_ = phdr.p_vaddr - load_bias_;
96 total_entries_ = phdr.p_memsz / 8;
97 return true;
98}
99
100bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700101 // Dwarf unwind information is precise about whether a pc is covered or not,
102 // but arm unwind information only has ranges of pc. In order to avoid
103 // incorrectly doing a bad unwind using arm unwind information for a
104 // different function, always try and unwind with the dwarf information first.
105 return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
Christopher Ferris3958f802017-02-01 15:44:40 -0800106}
107
108bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
109 RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
Christopher Ferris3958f802017-02-01 15:44:40 -0800110 uint64_t entry_offset;
111 if (!FindEntry(pc, &entry_offset)) {
112 return false;
113 }
114 ArmExidx arm(regs_arm, memory_, process_memory);
115 arm.set_cfa(regs_arm->sp());
116 if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
117 // If the pc was not set, then use the LR registers for the PC.
118 if (!arm.pc_set()) {
119 regs_arm->set_pc((*regs_arm)[ARM_REG_LR]);
120 (*regs_arm)[ARM_REG_PC] = regs_arm->pc();
121 } else {
122 regs_arm->set_pc((*regs_arm)[ARM_REG_PC]);
123 }
124 regs_arm->set_sp(arm.cfa());
125 (*regs_arm)[ARM_REG_SP] = regs_arm->sp();
126 return true;
127 }
128 return false;
129}