228 lines
5.4 KiB
C++
228 lines
5.4 KiB
C++
|
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||
|
//
|
||
|
// Copyright (C) 2006-2015 AlliedModders LLC
|
||
|
//
|
||
|
// This file is part of SourcePawn. SourcePawn 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 3 of
|
||
|
// the License, or (at your option) any later version.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License along with
|
||
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||
|
//
|
||
|
#include "environment.h"
|
||
|
#include "plugin-runtime.h"
|
||
|
#include "plugin-context.h"
|
||
|
#include "stack-frames.h"
|
||
|
#include "x86/frames-x86.h"
|
||
|
#include "compiled-function.h"
|
||
|
|
||
|
using namespace ke;
|
||
|
using namespace sp;
|
||
|
using namespace SourcePawn;
|
||
|
|
||
|
InvokeFrame::InvokeFrame(PluginContext *cx, cell_t entry_cip)
|
||
|
: prev_(Environment::get()->top()),
|
||
|
cx_(cx),
|
||
|
prev_exit_frame_(Environment::get()->exit_frame()),
|
||
|
entry_cip_(0),
|
||
|
entry_sp_(nullptr)
|
||
|
{
|
||
|
Environment::get()->enterInvoke(this);
|
||
|
}
|
||
|
|
||
|
InvokeFrame::~InvokeFrame()
|
||
|
{
|
||
|
assert(Environment::get()->top() == this);
|
||
|
Environment::get()->leaveInvoke();
|
||
|
}
|
||
|
|
||
|
FrameIterator::FrameIterator()
|
||
|
: ivk_(Environment::get()->top()),
|
||
|
exit_frame_(Environment::get()->exit_frame()),
|
||
|
runtime_(nullptr),
|
||
|
sp_iter_(nullptr),
|
||
|
sp_stop_(nullptr),
|
||
|
function_cip_(kInvalidCip),
|
||
|
cip_(kInvalidCip),
|
||
|
pc_(nullptr)
|
||
|
{
|
||
|
if (!ivk_)
|
||
|
return;
|
||
|
|
||
|
nextInvokeFrame();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
FrameIterator::nextInvokeFrame()
|
||
|
{
|
||
|
assert(exit_frame_.exit_sp());
|
||
|
sp_iter_ = exit_frame_.exit_sp();
|
||
|
|
||
|
// Inside an exit frame, the stack looks like this:
|
||
|
// .. C++ ..
|
||
|
// ----------- <-- entry_sp
|
||
|
// return addr to C++
|
||
|
// entry cip for frame #0
|
||
|
// alignment
|
||
|
// -----------
|
||
|
// return addr to frame #0
|
||
|
// entry cip for frame #1
|
||
|
// alignment
|
||
|
// ----------- <-- exit sp
|
||
|
// saved regs
|
||
|
// return addr
|
||
|
// .. InvokeNativeBoundHelper() ..
|
||
|
//
|
||
|
// We are guaranteed to always have one frame. We subtract one frame from
|
||
|
// the entry sp so we hit the stopping point correctly.
|
||
|
assert(ivk_->entry_sp());
|
||
|
assert(ke::IsAligned(sizeof(JitFrame), sizeof(intptr_t)));
|
||
|
sp_stop_ = ivk_->entry_sp() - (sizeof(JitFrame) / sizeof(intptr_t));
|
||
|
assert(sp_stop_ >= sp_iter_);
|
||
|
|
||
|
runtime_ = ivk_->cx()->runtime();
|
||
|
function_cip_ = -1;
|
||
|
pc_ = nullptr;
|
||
|
cip_ = kInvalidCip;
|
||
|
|
||
|
if (!exit_frame_.has_exit_native()) {
|
||
|
// We have an exit frame, but it's not for natives. automatically advance
|
||
|
// to the most recent scripted frame.
|
||
|
const JitExitFrameForHelper *exit =
|
||
|
JitExitFrameForHelper::FromExitSp(exit_frame_.exit_sp());
|
||
|
exit_frame_ = ExitFrame();
|
||
|
|
||
|
// If we haven't compiled the function yet, but threw an error, then the
|
||
|
// return address will be null.
|
||
|
pc_ = exit->return_address;
|
||
|
assert(pc_ || exit->isCompileFunction());
|
||
|
|
||
|
// The function owning pc_ is in the previous frame.
|
||
|
const JitFrame *frame = exit->prev();
|
||
|
function_cip_ = frame->function_cip;
|
||
|
sp_iter_ = reinterpret_cast<const intptr_t *>(frame);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
FrameIterator::Next()
|
||
|
{
|
||
|
void *pc = nullptr;
|
||
|
if (exit_frame_.has_exit_native()) {
|
||
|
// If we're at an exit frame, the return address will yield the current pc.
|
||
|
const JitExitFrameForNative *exit =
|
||
|
JitExitFrameForNative::FromExitSp(exit_frame_.exit_sp());
|
||
|
exit_frame_ = ExitFrame();
|
||
|
|
||
|
pc_ = exit->return_address;
|
||
|
cip_ = kInvalidCip;
|
||
|
|
||
|
// The function owning pc_ is in the previous frame.
|
||
|
const JitFrame *frame = JitFrame::FromSp(sp_iter_);
|
||
|
function_cip_ = frame->function_cip;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (sp_iter_ >= sp_stop_) {
|
||
|
// Jump to the next invoke frame.
|
||
|
exit_frame_ = ivk_->prev_exit_frame();
|
||
|
ivk_ = ivk_->prev();
|
||
|
if (!ivk_)
|
||
|
return;
|
||
|
|
||
|
nextInvokeFrame();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pc_ = JitFrame::FromSp(sp_iter_)->return_address;
|
||
|
assert(pc_);
|
||
|
|
||
|
// Advance, and find the function cip the pc belongs to.
|
||
|
sp_iter_ = reinterpret_cast<const intptr_t *>(JitFrame::FromSp(sp_iter_) + 1);
|
||
|
function_cip_ = JitFrame::FromSp(sp_iter_)->function_cip;
|
||
|
cip_ = kInvalidCip;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
FrameIterator::Reset()
|
||
|
{
|
||
|
*this = FrameIterator();
|
||
|
}
|
||
|
|
||
|
cell_t
|
||
|
FrameIterator::findCip() const
|
||
|
{
|
||
|
CompiledFunction *fn = runtime_->GetJittedFunctionByOffset(function_cip_);
|
||
|
if (!fn)
|
||
|
return 0;
|
||
|
|
||
|
if (cip_ == kInvalidCip) {
|
||
|
if (pc_)
|
||
|
cip_ = fn->FindCipByPc(pc_);
|
||
|
else
|
||
|
cip_ = function_cip_;
|
||
|
}
|
||
|
return cip_;
|
||
|
}
|
||
|
|
||
|
unsigned
|
||
|
FrameIterator::LineNumber() const
|
||
|
{
|
||
|
cell_t cip = findCip();
|
||
|
if (cip == kInvalidCip)
|
||
|
return 0;
|
||
|
|
||
|
uint32_t line;
|
||
|
if (!runtime_->image()->LookupLine(cip, &line))
|
||
|
return 0;
|
||
|
|
||
|
return line;
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
FrameIterator::FilePath() const
|
||
|
{
|
||
|
cell_t cip = findCip();
|
||
|
if (cip == kInvalidCip)
|
||
|
return runtime_->image()->LookupFile(function_cip_);
|
||
|
|
||
|
return runtime_->image()->LookupFile(cip);
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
FrameIterator::FunctionName() const
|
||
|
{
|
||
|
assert(ivk_);
|
||
|
if (exit_frame_.has_exit_native()) {
|
||
|
uint32_t native_index = exit_frame_.exit_native();
|
||
|
const sp_native_t *native = runtime_->GetNative(native_index);
|
||
|
if (!native)
|
||
|
return nullptr;
|
||
|
return native->name;
|
||
|
}
|
||
|
|
||
|
return runtime_->image()->LookupFunction(function_cip_);
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
FrameIterator::IsNativeFrame() const
|
||
|
{
|
||
|
return exit_frame_.has_exit_native();
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
FrameIterator::IsScriptedFrame() const
|
||
|
{
|
||
|
return !IsNativeFrame() && ivk_;
|
||
|
}
|
||
|
|
||
|
IPluginContext *
|
||
|
FrameIterator::Context() const
|
||
|
{
|
||
|
if (!ivk_)
|
||
|
return nullptr;
|
||
|
return ivk_->cx();
|
||
|
}
|