713 lines
22 KiB
C
713 lines
22 KiB
C
//----------------------------------------------------------------------------
|
|
//
|
|
// Function entry cache.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 2000.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntimage.h>
|
|
#define NOEXTAPI
|
|
#include <wdbgexts.h>
|
|
#include <ntdbg.h>
|
|
#include "private.h"
|
|
#include "symbols.h"
|
|
#include "globals.h"
|
|
|
|
#include "fecache.hpp"
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FunctionEntryCache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
FunctionEntryCache::FunctionEntryCache(ULONG ImageDataSize,
|
|
ULONG CacheDataSize,
|
|
ULONG Machine)
|
|
{
|
|
m_ImageDataSize = ImageDataSize;
|
|
m_CacheDataSize = CacheDataSize;
|
|
m_Machine = Machine;
|
|
|
|
m_Entries = NULL;
|
|
}
|
|
|
|
FunctionEntryCache::~FunctionEntryCache(void)
|
|
{
|
|
if (m_Entries != NULL)
|
|
{
|
|
MemFree(m_Entries);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
FunctionEntryCache::Initialize(ULONG MaxEntries, ULONG ReplaceAt)
|
|
{
|
|
// Already initialized.
|
|
if (m_Entries != NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
m_Entries = (FeCacheEntry*)MemAlloc(sizeof(FeCacheEntry) * MaxEntries);
|
|
if (m_Entries == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
m_MaxEntries = MaxEntries;
|
|
m_ReplaceAt = ReplaceAt;
|
|
|
|
m_Used = 0;
|
|
m_Next = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
FeCacheEntry*
|
|
FunctionEntryCache::Find(
|
|
HANDLE Process,
|
|
ULONG64 CodeOffset,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry
|
|
)
|
|
{
|
|
FeCacheEntry* FunctionEntry;
|
|
|
|
FE_DEBUG(("\nFunctionEntryCache::Find(ControlPc=%.8I64x, Machine=%X)\n",
|
|
CodeOffset, m_Machine));
|
|
|
|
// Look for a static or dynamic function entry.
|
|
FunctionEntry = FindDirect( Process, CodeOffset, ReadMemory,
|
|
GetModuleBase, GetFunctionEntry );
|
|
if (FunctionEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// The capability exists for more than one function entry
|
|
// to map to the same function. This permits a function to
|
|
// have discontiguous code segments described by separate
|
|
// function table entries. If the ending prologue address
|
|
// is not within the limits of the begining and ending
|
|
// address of the function table entry, then the prologue
|
|
// ending address is the address of the primary function
|
|
// table entry that accurately describes the ending prologue
|
|
// address.
|
|
//
|
|
|
|
FunctionEntry = SearchForPrimaryEntry(FunctionEntry, Process, ReadMemory,
|
|
GetModuleBase, GetFunctionEntry);
|
|
|
|
#if DBG
|
|
if (tlsvar(DebugFunctionEntries)) {
|
|
if (FunctionEntry == NULL) {
|
|
dbPrint("FunctionEntryCache::Find returning NULL\n");
|
|
} else {
|
|
if (FunctionEntry->Address) {
|
|
dbPrint("FunctionEntryCache::Find returning "
|
|
"FunctionEntry=%.8I64x %s\n",
|
|
FunctionEntry->Address,
|
|
FunctionEntry->Description);
|
|
} else {
|
|
dbPrint("FunctionEntryCache::Find returning "
|
|
"FunctionEntry=%.8I64x %s\n",
|
|
(ULONG64)(LONG64)(LONG_PTR)FunctionEntry,
|
|
FunctionEntry->Description);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return FunctionEntry;
|
|
}
|
|
|
|
FeCacheEntry*
|
|
FunctionEntryCache::FindDirect(
|
|
HANDLE Process,
|
|
ULONG64 CodeOffset,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry
|
|
)
|
|
{
|
|
FeCacheEntry* FunctionEntry;
|
|
ULONG64 ModuleBase;
|
|
|
|
//
|
|
// Look for function entry in static function tables.
|
|
//
|
|
|
|
FunctionEntry = FindStatic( Process, CodeOffset, ReadMemory,
|
|
GetModuleBase, GetFunctionEntry,
|
|
&ModuleBase );
|
|
|
|
FE_DEBUG((" FindDirect: ControlPc=0x%I64x functionEntry=0x%p\n"
|
|
" FindStatic %s\n", CodeOffset, FunctionEntry,
|
|
FunctionEntry != NULL ? "succeeded" : "FAILED"));
|
|
|
|
if (FunctionEntry != NULL) {
|
|
return FunctionEntry;
|
|
}
|
|
|
|
//
|
|
// If not in static image range and no static function entry
|
|
// found use FunctionEntryCallback routine (if present) for
|
|
// dynamic function entry or some other source of pdata (e.g.
|
|
// saved pdata information for ROM images).
|
|
//
|
|
|
|
PPROCESS_ENTRY ProcessEntry = FindProcessEntry( Process );
|
|
if (ProcessEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
PVOID RawEntry;
|
|
|
|
if (!ModuleBase) {
|
|
if (!IsImageMachineType64(m_Machine) &&
|
|
ProcessEntry->pFunctionEntryCallback32) {
|
|
RawEntry = ProcessEntry->pFunctionEntryCallback32
|
|
(Process, (ULONG)CodeOffset,
|
|
(PVOID)ProcessEntry->FunctionEntryUserContext);
|
|
} else if (ProcessEntry->pFunctionEntryCallback64) {
|
|
RawEntry = ProcessEntry->pFunctionEntryCallback64
|
|
(Process, CodeOffset, ProcessEntry->FunctionEntryUserContext);
|
|
if (RawEntry != NULL) {
|
|
FunctionEntry = FillTemporary(Process, RawEntry);
|
|
FE_SET_DESC(FunctionEntry, "from FunctionEntryCallback64");
|
|
}
|
|
}
|
|
|
|
if (FunctionEntry != NULL) {
|
|
FE_DEBUG((" FindDirect: got dynamic entry\n"));
|
|
} else if (GetFunctionEntry != NULL) {
|
|
|
|
// VC 6 didn't supply a GetModuleBase callback so this code is
|
|
// to make stack walking backward compatible.
|
|
//
|
|
// If we don't have a function by now, use the old-style function
|
|
// entry callback and let VC give it to us. Note that MSDN
|
|
// documentation indicates that this callback should return
|
|
// a 3-field IMAGE_FUNCTION_ENTRY structure, but VC 6 actually
|
|
// returns the 5-field IMAGE_RUNTIME_FUNCTION_ENTRY. Since
|
|
// the purpose of this hack is to make VC 6 work just go with the
|
|
// way VC 6 does it rather than what MSDN says.
|
|
|
|
RawEntry = GetFunctionEntry(Process, CodeOffset);
|
|
if (RawEntry != NULL) {
|
|
FunctionEntry = FillTemporary(Process, RawEntry);
|
|
FE_SET_DESC(FunctionEntry, "from GetFunctionEntry");
|
|
FE_DEBUG((" FindDirect: got user entry\n"));
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// Nothing has turned up a function entry but we do have a
|
|
// module base address. One possibility is that this is the
|
|
// kernel debugger and the pdata section is not paged in.
|
|
// The last ditch attempt for a function entry will be an
|
|
// internal dbghelp call to get the pdata entry from the
|
|
// debug info. This is not great because the data in the debug
|
|
// section is incomplete and potentially out of date, but in
|
|
// most cases it works and makes it possible to get user-mode
|
|
// stack traces in the kernel debugger.
|
|
|
|
PIMGHLP_RVA_FUNCTION_DATA RvaEntry =
|
|
GetFunctionEntryFromDebugInfo( ProcessEntry, CodeOffset );
|
|
if (RvaEntry != NULL) {
|
|
FeCacheData Data;
|
|
|
|
TranslateRvaDataToRawData(RvaEntry, ModuleBase, &Data);
|
|
FunctionEntry = FillTemporary(Process, &Data);
|
|
FE_SET_DESC(FunctionEntry, "from GetFunctionEntryFromDebugInfo");
|
|
FE_DEBUG((" FindDirect: got debug info entry\n"));
|
|
}
|
|
}
|
|
|
|
return FunctionEntry;
|
|
}
|
|
|
|
FeCacheEntry*
|
|
FunctionEntryCache::FindStatic(
|
|
HANDLE Process,
|
|
ULONG64 CodeOffset,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
PULONG64 ModuleBase
|
|
)
|
|
{
|
|
ULONG RelCodeOffset;
|
|
|
|
*ModuleBase = GetModuleBase( Process, CodeOffset );
|
|
if (CodeOffset - *ModuleBase > 0xffffffff) {
|
|
return NULL;
|
|
}
|
|
|
|
RelCodeOffset = (ULONG)(CodeOffset - *ModuleBase);
|
|
|
|
FE_DEBUG((" FindStatic: ControlPc=0x%I64x ImageBase=0x%I64x\n"
|
|
" biasedControlPc=0x%lx\n",
|
|
CodeOffset, *ModuleBase, RelCodeOffset));
|
|
|
|
FeCacheEntry* FunctionEntry;
|
|
ULONG Index;
|
|
|
|
//
|
|
// Check the array of recently fetched function entries
|
|
//
|
|
|
|
FunctionEntry = m_Entries;
|
|
for (Index = 0; Index < m_Used; Index++) {
|
|
|
|
if (FunctionEntry->Process == Process &&
|
|
FunctionEntry->ModuleBase == *ModuleBase &&
|
|
RelCodeOffset >= FunctionEntry->RelBegin &&
|
|
RelCodeOffset < FunctionEntry->RelEnd) {
|
|
|
|
FE_DEBUG((" FindStatic: cache hit - index=%ld\n", Index));
|
|
return FunctionEntry;
|
|
}
|
|
|
|
FunctionEntry++;
|
|
}
|
|
|
|
//
|
|
// If an image was found that included the specified code, then locate the
|
|
// function table for the image.
|
|
//
|
|
|
|
if (*ModuleBase == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
ULONG64 FunctionTable;
|
|
ULONG SizeOfFunctionTable;
|
|
|
|
FunctionTable = FunctionTableBase( Process, ReadMemory, *ModuleBase,
|
|
&SizeOfFunctionTable );
|
|
if (FunctionTable == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
FE_DEBUG((" FindStatic: functionTable=0x%I64x "
|
|
"sizeOfFunctionTable=%ld count:%ld\n",
|
|
FunctionTable, SizeOfFunctionTable,
|
|
SizeOfFunctionTable / m_ImageDataSize));
|
|
|
|
LONG High;
|
|
LONG Low;
|
|
LONG Middle;
|
|
|
|
//
|
|
// If a function table is located, then search the function table
|
|
// for a function table entry for the specified code offset.
|
|
//
|
|
|
|
Low = 0;
|
|
High = (SizeOfFunctionTable / m_ImageDataSize) - 1;
|
|
|
|
//
|
|
// Perform binary search on the function table for a function table
|
|
// entry that subsumes the specified code offset.
|
|
//
|
|
|
|
while (High >= Low) {
|
|
|
|
//
|
|
// Compute next probe index and test entry. If the specified PC
|
|
// is greater than of equal to the beginning address and less
|
|
// than the ending address of the function table entry, then
|
|
// return the address of the function table entry. Otherwise,
|
|
// continue the search.
|
|
//
|
|
|
|
Middle = (Low + High) >> 1;
|
|
|
|
ULONG64 NextFunctionTableEntry = FunctionTable +
|
|
Middle * m_ImageDataSize;
|
|
|
|
//
|
|
// Fetch the function entry and bail if there is an error reading it
|
|
//
|
|
|
|
FunctionEntry = ReadImage( Process, NextFunctionTableEntry,
|
|
ReadMemory, GetModuleBase );
|
|
if (FunctionEntry == NULL) {
|
|
FE_DEBUG((" FindStatic: ReadImage "
|
|
"functionEntryAddress=0x%I64x FAILED\n",
|
|
NextFunctionTableEntry));
|
|
return NULL;
|
|
}
|
|
|
|
if (RelCodeOffset < FunctionEntry->RelBegin) {
|
|
High = Middle - 1;
|
|
} else if (RelCodeOffset >= FunctionEntry->RelEnd) {
|
|
Low = Middle + 1;
|
|
} else {
|
|
return Promote( FunctionEntry );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
FeCacheEntry*
|
|
FunctionEntryCache::ReadImage(
|
|
HANDLE Process,
|
|
ULONG64 Address,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase
|
|
)
|
|
{
|
|
FeCacheEntry* FunctionEntry;
|
|
ULONG Index;
|
|
|
|
// Check the array of recently fetched function entries.
|
|
|
|
FunctionEntry = m_Entries;
|
|
for (Index = 0; Index < m_Used; Index++) {
|
|
|
|
if (FunctionEntry->Process == Process &&
|
|
FunctionEntry->Address == Address ) {
|
|
|
|
return FunctionEntry;
|
|
}
|
|
|
|
FunctionEntry++;
|
|
}
|
|
|
|
FeCacheData Data;
|
|
DWORD Done;
|
|
|
|
if (!ReadMemory(Process, Address, &Data, m_ImageDataSize, &Done) ||
|
|
Done != m_ImageDataSize) {
|
|
return NULL;
|
|
}
|
|
|
|
// If not in the cache, replace the entry that m_Next
|
|
// points to. m_Next cycles through the last part of the
|
|
// table and function entries we want to keep are promoted to the first
|
|
// part of the table so they don't get overwritten by new ones being read
|
|
// as part of the binary search through function entry tables.
|
|
|
|
if (m_Used < m_MaxEntries) {
|
|
m_Used++;
|
|
m_Next = m_Used;
|
|
} else {
|
|
m_Next++;
|
|
if (m_Next >= m_MaxEntries) {
|
|
m_Next = m_ReplaceAt + 1;
|
|
}
|
|
}
|
|
|
|
FunctionEntry = m_Entries + (m_Next - 1);
|
|
|
|
FunctionEntry->Data = Data;
|
|
FunctionEntry->Address = Address;
|
|
FunctionEntry->Process = Process;
|
|
FunctionEntry->ModuleBase = GetModuleBase(Process, Address);
|
|
FE_SET_DESC(FunctionEntry, "from target process");
|
|
|
|
// Translate after all other information is filled in so
|
|
// the translation routine can use it.
|
|
TranslateRawData(FunctionEntry);
|
|
|
|
return FunctionEntry;
|
|
}
|
|
|
|
void
|
|
FunctionEntryCache::InvalidateProcessOrModule(HANDLE Process, ULONG64 Base)
|
|
{
|
|
FeCacheEntry* FunctionEntry;
|
|
ULONG Index;
|
|
|
|
FunctionEntry = m_Entries;
|
|
Index = 0;
|
|
while (Index < m_Used) {
|
|
|
|
if (FunctionEntry->Process == Process &&
|
|
(Base == 0 || FunctionEntry->ModuleBase == Base)) {
|
|
|
|
// Pull the last entry down into this slot
|
|
// to keep things packed. There's no need
|
|
// to update m_Next as this will open a
|
|
// new slot for use and m_Next will be reset
|
|
// when it is used.
|
|
*FunctionEntry = m_Entries[--m_Used];
|
|
} else {
|
|
Index++;
|
|
FunctionEntry++;
|
|
}
|
|
}
|
|
}
|
|
|
|
FeCacheEntry*
|
|
FunctionEntryCache::Promote(FeCacheEntry* Entry)
|
|
{
|
|
ULONG Index;
|
|
ULONG Move;
|
|
|
|
Index = (ULONG)(Entry - m_Entries);
|
|
|
|
// Make sure it's promoted out of the temporary area.
|
|
if (Index >= m_ReplaceAt) {
|
|
Move = Index - (m_ReplaceAt - 3);
|
|
} else {
|
|
Move = ( Index >= 3 ) ? 3 : 1;
|
|
}
|
|
|
|
if (Index > Move) {
|
|
FeCacheEntry Temp = *Entry;
|
|
*Entry = m_Entries[Index - Move];
|
|
m_Entries[Index - Move] = Temp;
|
|
Index -= Move;
|
|
}
|
|
|
|
return m_Entries + Index;
|
|
}
|
|
|
|
ULONG64
|
|
FunctionEntryCache::FunctionTableBase(
|
|
HANDLE Process,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
ULONG64 Base,
|
|
PULONG Size
|
|
)
|
|
{
|
|
ULONG64 NtHeaders;
|
|
ULONG64 ExceptionDirectoryEntryAddress;
|
|
IMAGE_DATA_DIRECTORY ExceptionData;
|
|
IMAGE_DOS_HEADER DosHeaderData;
|
|
DWORD Done;
|
|
|
|
// Read DOS header to calculate the address of the NT header.
|
|
|
|
if (!ReadMemory( Process, Base, &DosHeaderData, sizeof(DosHeaderData),
|
|
&Done ) ||
|
|
Done != sizeof(DosHeaderData)) {
|
|
return 0;
|
|
}
|
|
if (DosHeaderData.e_magic != IMAGE_DOS_SIGNATURE) {
|
|
return 0;
|
|
}
|
|
|
|
NtHeaders = Base + DosHeaderData.e_lfanew;
|
|
|
|
if (IsImageMachineType64(m_Machine)) {
|
|
ExceptionDirectoryEntryAddress = NtHeaders +
|
|
FIELD_OFFSET(IMAGE_NT_HEADERS64,OptionalHeader) +
|
|
FIELD_OFFSET(IMAGE_OPTIONAL_HEADER64,DataDirectory) +
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION * sizeof(IMAGE_DATA_DIRECTORY);
|
|
} else {
|
|
ExceptionDirectoryEntryAddress = NtHeaders +
|
|
FIELD_OFFSET(IMAGE_NT_HEADERS32,OptionalHeader) +
|
|
FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32,DataDirectory) +
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION * sizeof(IMAGE_DATA_DIRECTORY);
|
|
}
|
|
|
|
// Read NT header to get the image data directory.
|
|
|
|
if (!ReadMemory( Process, ExceptionDirectoryEntryAddress, &ExceptionData,
|
|
sizeof(IMAGE_DATA_DIRECTORY), &Done ) ||
|
|
Done != sizeof(IMAGE_DATA_DIRECTORY)) {
|
|
return 0;
|
|
}
|
|
|
|
*Size = ExceptionData.Size;
|
|
return Base + ExceptionData.VirtualAddress;
|
|
|
|
}
|
|
|
|
FeCacheEntry*
|
|
FunctionEntryCache::SearchForPrimaryEntry(
|
|
FeCacheEntry* CacheEntry,
|
|
HANDLE Process,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry
|
|
)
|
|
{
|
|
// Assume all entries are primary.
|
|
return CacheEntry;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Ia64FunctionEntryCache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
Ia64FunctionEntryCache::TranslateRawData(FeCacheEntry* Entry)
|
|
{
|
|
Entry->RelBegin = Entry->Data.Ia64.BeginAddress & ~15;
|
|
Entry->RelEnd = (Entry->Data.Ia64.EndAddress + 15) & ~15;
|
|
}
|
|
|
|
void
|
|
Ia64FunctionEntryCache::TranslateRvaDataToRawData
|
|
(PIMGHLP_RVA_FUNCTION_DATA RvaData, ULONG64 ModuleBase,
|
|
FeCacheData* Data)
|
|
{
|
|
Data->Ia64.BeginAddress = RvaData->rvaBeginAddress;
|
|
Data->Ia64.EndAddress = RvaData->rvaEndAddress;
|
|
Data->Ia64.UnwindInfoAddress = RvaData->rvaPrologEndAddress;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
void
|
|
ShowRuntimeFunctionIa64(
|
|
FeCacheEntry* FunctionEntry,
|
|
PSTR Label
|
|
)
|
|
{
|
|
if (!tlsvar(DebugFunctionEntries)) {
|
|
return;
|
|
}
|
|
|
|
if ( FunctionEntry ) {
|
|
if (FunctionEntry->Address) {
|
|
dbPrint(" 0x%I64x: %s\n", FunctionEntry->Address,
|
|
Label ? Label : "" );
|
|
}
|
|
else {
|
|
dbPrint(" %s\n", Label ? Label : "" );
|
|
}
|
|
dbPrint(" BeginAddress = 0x%x\n"
|
|
" EndAddress = 0x%x\n"
|
|
" UnwindInfoAddress = 0x%x\n",
|
|
FunctionEntry->Data.Ia64.BeginAddress,
|
|
FunctionEntry->Data.Ia64.EndAddress,
|
|
FunctionEntry->Data.Ia64.UnwindInfoAddress );
|
|
}
|
|
else {
|
|
dbPrint(" FunctionEntry NULL: %s\n", Label ? Label : "" );
|
|
}
|
|
}
|
|
|
|
#endif // #if DBG
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Amd64FunctionEntryCache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
Amd64FunctionEntryCache::TranslateRawData(FeCacheEntry* Entry)
|
|
{
|
|
Entry->RelBegin = Entry->Data.Amd64.BeginAddress;
|
|
Entry->RelEnd = Entry->Data.Amd64.EndAddress;
|
|
}
|
|
|
|
void
|
|
Amd64FunctionEntryCache::TranslateRvaDataToRawData
|
|
(PIMGHLP_RVA_FUNCTION_DATA RvaData, ULONG64 ModuleBase,
|
|
FeCacheData* Data)
|
|
{
|
|
Data->Amd64.BeginAddress = RvaData->rvaBeginAddress;
|
|
Data->Amd64.EndAddress = RvaData->rvaEndAddress;
|
|
Data->Amd64.UnwindInfoAddress = RvaData->rvaPrologEndAddress;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ArmFunctionEntryCache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
ArmFunctionEntryCache::TranslateRawData(FeCacheEntry* Entry)
|
|
{
|
|
Entry->RelBegin = (ULONG)
|
|
((Entry->Data.Arm.BeginAddress & ~1) - Entry->ModuleBase);
|
|
Entry->RelEnd = (ULONG)
|
|
((Entry->Data.Arm.EndAddress & ~1) - Entry->ModuleBase);
|
|
}
|
|
|
|
void
|
|
ArmFunctionEntryCache::TranslateRvaDataToRawData
|
|
(PIMGHLP_RVA_FUNCTION_DATA RvaData, ULONG64 ModuleBase,
|
|
FeCacheData* Data)
|
|
{
|
|
Data->Arm.BeginAddress = (ULONG)(ModuleBase + RvaData->rvaBeginAddress);
|
|
Data->Arm.EndAddress = (ULONG)(ModuleBase + RvaData->rvaEndAddress);
|
|
Data->Arm.ExceptionHandler = 0;
|
|
Data->Arm.HandlerData = 0;
|
|
Data->Arm.PrologEndAddress =
|
|
(ULONG)(ModuleBase + RvaData->rvaPrologEndAddress);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Functions.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
FunctionEntryCache*
|
|
GetFeCache(ULONG Machine, BOOL Create)
|
|
{
|
|
FunctionEntryCache* Cache;
|
|
|
|
switch(Machine) {
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
if (tlsvar(Amd64FunctionEntries) == NULL && Create) {
|
|
tlsvar(Amd64FunctionEntries) = new Amd64FunctionEntryCache;
|
|
if (tlsvar(Amd64FunctionEntries) == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
Cache = tlsvar(Amd64FunctionEntries);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
if (tlsvar(Ia64FunctionEntries) == NULL && Create) {
|
|
tlsvar(Ia64FunctionEntries) = new Ia64FunctionEntryCache;
|
|
if (tlsvar(Ia64FunctionEntries) == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
Cache = tlsvar(Ia64FunctionEntries);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_ARM:
|
|
if (tlsvar(ArmFunctionEntries) == NULL && Create) {
|
|
tlsvar(ArmFunctionEntries) = new ArmFunctionEntryCache;
|
|
if (tlsvar(ArmFunctionEntries) == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
Cache = tlsvar(ArmFunctionEntries);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (Cache && !Cache->Initialize(60, 40)) {
|
|
return NULL;
|
|
}
|
|
|
|
return Cache;
|
|
}
|
|
|
|
void
|
|
ClearFeCaches(void)
|
|
{
|
|
if (tlsvar(Ia64FunctionEntries)) {
|
|
delete (Ia64FunctionEntryCache*)tlsvar(Ia64FunctionEntries);
|
|
tlsvar(Ia64FunctionEntries) = NULL;
|
|
}
|
|
if (tlsvar(Amd64FunctionEntries)) {
|
|
delete (Amd64FunctionEntryCache*)tlsvar(Amd64FunctionEntries);
|
|
tlsvar(Amd64FunctionEntries) = NULL;
|
|
}
|
|
if (tlsvar(ArmFunctionEntries)) {
|
|
delete (ArmFunctionEntryCache*)tlsvar(ArmFunctionEntries);
|
|
tlsvar(ArmFunctionEntries) = NULL;
|
|
}
|
|
}
|