blob: 8a691b416b40fb5a9c1715a150399b8d35da9847 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Ilya Berezhniuk
*/
#include "dec_base.h"
#include "port_modules.h"
#include "native_unwind.h"
static unsigned native_dec_instr(UnwindContext* context, void* addr, void** target)
{
Inst inst;
if (!native_is_in_code(context, addr))
return 0;
U_32 len = DecoderBase::decode(addr, &inst);
if (len == 0 ||
inst.mn != Mnemonic_CALL ||
inst.argc != 1)
return 0;
if (target && inst.operands[0].is_imm())
*target = (void*)((U_32)addr + len + inst.operands[0].imm());
return len;
}
static bool native_check_caller(UnwindContext* context, Registers* regs, void** sp)
{
void* target = NULL;
char* ptr = (char*)*sp;
if (native_dec_instr(context, ptr - 2, &target) == 2 || // CALL r/m32 w/o SIB w/o disp
native_dec_instr(context, ptr - 3, &target) == 3 || // CALL r/m32 w/ SIB w/o disp
native_dec_instr(context, ptr - 4, &target) == 4 || // CALL r/m32 w/ SIB w/ disp8
native_dec_instr(context, ptr - 5, &target) == 5 || // CALL rel32
native_dec_instr(context, ptr - 6, &target) == 6 || // CALL r/m32 w/o SIB w/ disp32
native_dec_instr(context, ptr - 7, &target) == 7 || // CALL r/m32 w/ SIB w/ disp32
native_dec_instr(context, ptr - 8, &target) == 8) // CALL r/m32 w/ SIB w/ disp32 + Seg prefix
{
if (!target)
return true;
native_module_t* cur_module =
port_find_module(context->modules, regs->get_ip());
native_module_t* found_module =
port_find_module(context->modules, target);
return (cur_module == found_module);
}
return false;
}
bool native_is_frame_exists(UnwindContext* context, Registers* regs)
{
// Check for frame layout and stack values
if ((regs->ebp < regs->esp) || !native_is_in_stack(context, (void*)regs->ebp))
return false; // Invalid frame
void** frame_ptr = (void**)regs->ebp;
void* eip = frame_ptr[1]; // Return address
// Check return address for meaning
return (native_is_in_code(context, eip) && native_check_caller(context, regs, frame_ptr + 1));
}
bool native_unwind_stack_frame(UnwindContext* context, Registers* regs)
{
void** frame = (void**)regs->ebp;
void* ebp = frame[0];
void* eip = frame[1];
// void* esp = (void*)(frame + 2);
void* esp = &frame[2];
if (native_is_in_stack(context, esp) &&
(native_is_in_code(context, eip)))
{
regs->ebp = (U_32)ebp;
regs->esp = (U_32)esp;
regs->eip = (U_32)eip;
return true;
}
return false;
}
static bool fill_regs_from_sp(UnwindContext* context, Registers* regs, void** sp)
{
regs->esp = (U_32)(sp + 1);
regs->eip = (U_32)*sp;
regs->ebp = native_is_in_stack(context, sp[-1]) ? (U_32)sp[-1] : regs->esp;
return true;
}
// Max search depth for return address
#define MAX_SPECIAL_DEPTH 0x2800
#define NATIVE_STRICT_UNWINDING 1
bool native_unwind_special(UnwindContext* context, Registers* regs)
{
for (void** cur_sp = (void**)regs->esp;
(char*)cur_sp < ((char*)regs->esp + MAX_SPECIAL_DEPTH) && native_is_in_stack(context, cur_sp);
++cur_sp)
{
if (!native_is_in_code(context, *cur_sp))
continue;
#if (!NATIVE_STRICT_UNWINDING)
return fill_regs_from_sp(context, regs, cur_sp);
#else
if (native_check_caller(context, regs, cur_sp))
return fill_regs_from_sp(context, regs, cur_sp);
#endif
}
return false;
}