1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
/*
Copyright (C) 2015 Armen Boursalian
aboursalian@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallStack.h"
#include "Expression.h"
#include "IDebugger.h"
#include "IProcess.h"
#include "IRegion.h"
#include "IState.h"
#include "IThread.h"
#include "MemoryRegions.h"
#include "State.h"
#include "edb.h"
// TODO: This may be specific to x86... Maybe abstract this in the future.
/**
* @brief CallStack::CallStack
*/
CallStack::CallStack() {
getCallStack();
}
/**
* @brief CallStack::get_call_stack
*
* Gets the state of the call stack at the time the object is created.
*/
void CallStack::getCallStack() {
/*
* Is rbp a pointer somewhere in the stack?
* Is the value below rbp a ret addr?
* Are we still scanning within the stack region?
*/
if (IProcess *process = edb::v1::debugger_core->process()) {
if (std::shared_ptr<IThread> thread = process->currentThread()) {
// Get the frame & stack pointers.
State state;
thread->getState(&state);
const edb::address_t rbp = state.framePointer();
const edb::address_t rsp = state.stackPointer();
// Check the alignment. rbp and rsp should be aligned to the stack.
if (rbp % edb::v1::pointer_size() != 0 || rsp % edb::v1::pointer_size() != 0) {
qDebug("It appears that the application is not using frame pointers, call stack unavailable.");
return;
}
// Make sure frame pointer is pointing in the same region as stack pointer.
// If not, then it's being used as a GPR, and we don't have enough info.
// This assumes the stack pointer is always pointing somewhere in the stack.
edb::v1::memory_regions().sync();
std::shared_ptr<IRegion> region_rsp = edb::v1::memory_regions().findRegion(rsp);
std::shared_ptr<IRegion> region_rbp = edb::v1::memory_regions().findRegion(rbp);
if (!region_rsp || !region_rbp || (region_rbp != region_rsp)) {
return;
}
// But if we're good, then scan from rbp downward and look for return addresses.
// Code is largely from CommentServer.cpp. Makes assumption of size of call.
constexpr uint8_t CallMinSize = 2;
constexpr uint8_t CallMaxSize = 7;
uint8_t buffer[edb::Instruction::MaxSize];
for (edb::address_t addr = rbp; region_rbp->contains(addr); addr += edb::v1::pointer_size()) {
// Get the stack value so that we can see if it's a pointer
bool ok;
ExpressionError err;
edb::address_t possible_ret = edb::v1::get_value(addr, &ok, &err);
if (process->readBytes(possible_ret - CallMaxSize, buffer, sizeof(buffer))) { // 0xfffff... if not a ptr.
for (int i = (CallMaxSize - CallMinSize); i >= 0; --i) {
edb::Instruction inst(buffer + i, buffer + sizeof(buffer), 0);
// If it's a call, then make a frame
if (is_call(inst)) {
StackFrame frame;
frame.ret = possible_ret;
frame.caller = possible_ret - CallMaxSize + i;
stackFrames_.push_back(frame);
break;
}
}
}
}
}
}
}
/**
* @brief CallStack::operator []
*
* Provides array-like access to the stack_frames_
*
* @param index
* @return
*/
CallStack::StackFrame *CallStack::operator[](size_t index) {
if (index > size()) {
return nullptr;
}
return &stackFrames_[index];
}
/**
* @brief CallStack::size
* @return the number of frames in the call stack.
*/
size_t CallStack::size() const {
return stackFrames_.size();
}
/**
* @brief CallStack::top
* @return a pointer to the frame at the top of the call stack or nullptr
* if there are no frames on the stack
*/
CallStack::StackFrame *CallStack::top() {
if (!size()) {
return nullptr;
}
return &stackFrames_.front();
}
/**
* @brief CallStack::bottom
* @return a pointer to the frame at the bottom of the call stack or nullptr
* if there are no frames on the stack
*/
CallStack::StackFrame *CallStack::bottom() {
if (!size()) {
return nullptr;
}
return &stackFrames_.back();
}
/**
* @brief CallStack::push
*
* Pushes a stack frame onto the top of the call stack.
*
* @param frame
*/
void CallStack::push(StackFrame frame) {
stackFrames_.push_front(frame);
}
|