windows-nt/Source/XPSP1/NT/base/ntos/rtl/lookup.c
2020-09-26 16:20:57 +08:00

711 lines
20 KiB
C

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
lookup.c
Abstract:
This module implements function table lookup for platforms with table
base exception handling.
Author:
David N. Cutler (davec) 30-May-2001
Revision History:
--*/
#include "ntrtlp.h"
//
// Define external data.
//
#if defined(NTOS_KERNEL_RUNTIME)
#if defined(_AMD64_) // ****** temp ******
#pragma alloc_text(INIT, RtlInitializeHistoryTable)
#endif // ****** temp ******
#else
#include "..\ntdll\ldrp.h"
extern PVOID NtDllBase;
#endif
//
// Define global unwind history table to hold the constant unwind entries
// for exception dispatch followed by unwind.
//
UNWIND_HISTORY_TABLE RtlpUnwindHistoryTable = {
0, UNWIND_HISTORY_TABLE_NONE, - 1, 0};
#if defined(_AMD64_) // ****** temp ******
VOID
RtlInitializeHistoryTable (
VOID
)
/*++
Routine Description:
This function initializes the global unwind history table.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG64 BeginAddress;
ULONG64 ControlPc;
ULONG64 EndAddress;
PVOID *FunctionAddressTable;
PRUNTIME_FUNCTION FunctionEntry;
ULONG64 ImageBase;
ULONG Index;
//
// Lookup function entries from the function address table until a NULL
// entry is encountered or the unwind history table is full.
//
FunctionAddressTable = &RtlpFunctionAddressTable[0];
Index = 0;
while ((Index < UNWIND_HISTORY_TABLE_SIZE) &&
(*FunctionAddressTable != NULL)) {
ControlPc = (ULONG64)*FunctionAddressTable++;
FunctionEntry = RtlLookupFunctionEntry(ControlPc,
&ImageBase,
NULL);
ASSERT(FunctionEntry != NULL);
BeginAddress = FunctionEntry->BeginAddress + ImageBase;
EndAddress = FunctionEntry->EndAddress + ImageBase;
RtlpUnwindHistoryTable.Entry[Index].ImageBase = ImageBase;
RtlpUnwindHistoryTable.Entry[Index].FunctionEntry = FunctionEntry;
if (BeginAddress < RtlpUnwindHistoryTable.LowAddress) {
RtlpUnwindHistoryTable.LowAddress = BeginAddress;
}
if (EndAddress > RtlpUnwindHistoryTable.HighAddress) {
RtlpUnwindHistoryTable.HighAddress = EndAddress;
}
Index += 1;
}
RtlpUnwindHistoryTable.Count = Index;
return;
}
#endif // ****** temp ******
PRUNTIME_FUNCTION
RtlpSearchInvertedFunctionTable (
PINVERTED_FUNCTION_TABLE InvertedTable,
PVOID ControlPc,
OUT PVOID *ImageBase,
#if defined(_IA64_)
OUT PULONG64 Gp,
#endif
OUT PULONG SizeOfTable
)
/*++
Routine Description:
This function searches for a matching entry in an inverted function
table using the specified control PC value.
N.B. It is assumed that appropriate locks are held when this routine
is called.
Arguments:
InvertedTable - Supplies a pointer to an inverted function table.
ControlPc - Supplies a PC value to to use in searching the inverted
function table.
ImageBase - Supplies a pointer to a variable that receives the base
address of the corresponding module.
SizeOfTable - Supplies a pointer to a variable that recevies the size
of the function table in bytes.
Return Value:
If a matching entry is located in the specified function table, then
the function table address is returned as the function value. Otherwise,
a value of NULL is returned.
--*/
{
PVOID Bound;
LONG High;
ULONG Index;
PINVERTED_FUNCTION_TABLE_ENTRY InvertedEntry;
LONG Low;
LONG Middle;
//
// If there are any entries in the specified inverted function table,
// then search the table for a matching entry.
//
if (InvertedTable->CurrentSize != 0) {
Low = 0;
High = InvertedTable->CurrentSize - 1;
while (High >= Low) {
//
// Compute next probe index and test entry. If the specified
// control PC is greater than of equal to the beginning address
// and less than the ending address of the inverted function
// table entry, then return the address of the function table.
// Otherwise, continue the search.
//
Middle = (Low + High) >> 1;
InvertedEntry = &InvertedTable->TableEntry[Middle];
Bound = (PVOID)((ULONG_PTR)InvertedEntry->ImageBase + InvertedEntry->SizeOfImage);
if (ControlPc < InvertedEntry->ImageBase) {
High = Middle - 1;
} else if (ControlPc >= Bound) {
Low = Middle + 1;
} else {
*ImageBase = InvertedEntry->ImageBase;
#if defined(_IA64_)
*Gp = InvertedEntry->Gp;
#endif
*SizeOfTable = InvertedEntry->SizeOfTable;
return InvertedEntry->FunctionTable;
}
}
}
return NULL;
}
PRUNTIME_FUNCTION
RtlpLookupFunctionTable (
IN PVOID ControlPc,
OUT PVOID *ImageBase,
#if defined(_IA64_)
OUT PULONG64 Gp,
#endif
OUT PULONG SizeOfTable
)
/*++
Routine Description:
This function looks up the control PC in the loaded module list, and
returns the image base, the size of the function table, and the address
of the function table.
Arguments:
ControlPc - Supplies an address in the module to be looked up.
ImageBase - Supplies a pointer to a variable that receives the base
address of the corresponding module.
SizeOfTable - Supplies a pointer to a variable that recevies the size
of the function table in bytes.
Return Value:
If a module is found that contains the specified control PC value and
that module contains a function table, then the address of the function
table is returned as the function value. Otherwise, NULL is returned.
--*/
{
PVOID Base;
ULONG_PTR Bound;
PLDR_DATA_TABLE_ENTRY Entry;
PRUNTIME_FUNCTION FunctionTable;
PLIST_ENTRY Next;
#if defined(NTOS_KERNEL_RUNTIME)
KIRQL OldIrql;
//
// Acquire the loaded module list spinlock and scan the list for the
// specified PC value if the list has been initialized.
//
OldIrql = KeGetCurrentIrql();
if (OldIrql < DISPATCH_LEVEL) {
KeRaiseIrqlToDpcLevel();
}
ExAcquireSpinLockAtDpcLevel(&PsLoadedModuleSpinLock);
FunctionTable = RtlpSearchInvertedFunctionTable(&PsInvertedFunctionTable,
ControlPc,
&Base,
#if defined(_IA64_)
Gp,
#endif
SizeOfTable);
if ((FunctionTable == NULL) &&
(PsInvertedFunctionTable.Overflow != FALSE)) {
Next = PsLoadedModuleList.Flink;
if (Next != NULL) {
while (Next != &PsLoadedModuleList) {
Entry = CONTAINING_RECORD(Next,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
Next = Next->Flink;
Base = Entry->DllBase;
Bound = (ULONG_PTR)Base + Entry->SizeOfImage;
if (((ULONG_PTR)ControlPc >= (ULONG_PTR)Base) &&
((ULONG_PTR)ControlPc < Bound)) {
//
// Lookup function table address and size.
//
#if defined(_IA64_)
*Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
SizeOfTable));
#endif
FunctionTable = RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
SizeOfTable);
break;
}
}
}
}
//
// Release the loaded module list spin lock.
//
ExReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
#else
BOOLEAN InLdrInit;
MEMORY_BASIC_INFORMATION MemoryInformation;
PLIST_ENTRY ModuleListHead;
PIMAGE_NT_HEADERS NtHeaders;
PPEB Peb;
PTEB Teb;
NTSTATUS Status;
//
// Acquire the Loader lock for the current process and scan the loaded
// module list for the specified PC value if all the data structures
// have been initialized.
//
FunctionTable = NULL;
InLdrInit = LdrpInLdrInit;
if ((InLdrInit == FALSE) &&
(RtlTryEnterCriticalSection(&LdrpLoaderLock) == FALSE)) {
//
// The loader lock could not be acquired. Call the system to find the
// image that contains the control PC.
//
Status = NtQueryVirtualMemory(NtCurrentProcess(),
ControlPc,
MemoryBasicInformation,
&MemoryInformation,
sizeof(MEMORY_BASIC_INFORMATION),
NULL);
if (NT_SUCCESS(Status) &&
(MemoryInformation.Type == MEM_IMAGE)) {
//
// Lookup function table address and size.
//
Base = MemoryInformation.AllocationBase;
#if defined(_IA64_)
*Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
SizeOfTable));
#endif
FunctionTable = RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
SizeOfTable);
}
} else {
//
// The loader lock was acquired or the loader is being initialized.
// Search the loaded module list if it is currently defined. Otherwise,
// set the values for ntdll.
//
Teb = NtCurrentTeb();
if (Teb != NULL) {
Peb = Teb->ProcessEnvironmentBlock;
if (Peb->Ldr != NULL) {
FunctionTable = RtlpSearchInvertedFunctionTable(&LdrpInvertedFunctionTable,
ControlPc,
&Base,
#if defined(_IA64_)
Gp,
#endif
SizeOfTable);
if ((FunctionTable == NULL) &&
((InLdrInit != FALSE) ||
(LdrpInvertedFunctionTable.Overflow != FALSE))) {
ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
Next = ModuleListHead->Flink;
if (Next != NULL) {
while (Next != ModuleListHead) {
Entry = CONTAINING_RECORD(Next,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
Next = Next->Flink;
Base = Entry->DllBase;
Bound = (ULONG_PTR)Base + Entry->SizeOfImage;
if (((ULONG_PTR)ControlPc >= (ULONG_PTR)Base) &&
((ULONG_PTR)ControlPc < Bound)) {
#if defined(_IA64_)
*Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
SizeOfTable));
#endif
FunctionTable = RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
SizeOfTable);
break;
}
}
}
}
} else {
//
// The loaded module list has not been initialized. Therefore,
// the current executing code must be in ntdll. If ntddl base
// is not NULL and the control PC is within the ntdll range,
// then return the information for ntdll.
//
if (NtDllBase != NULL) {
Base = NtDllBase;
NtHeaders = RtlImageNtHeader(Base);
if (NtHeaders != NULL) {
Bound = (ULONG_PTR)Base + NtHeaders->OptionalHeader.SizeOfImage;
if (((ULONG_PTR)ControlPc >= (ULONG_PTR)Base) &&
((ULONG_PTR)ControlPc < Bound)) {
#if defined(_IA64_)
*Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
SizeOfTable));
#endif
FunctionTable = RtlImageDirectoryEntryToData(Base,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
SizeOfTable);
}
}
}
}
}
//
// Release the loader lock if it was acquired.
//
if (InLdrInit == FALSE) {
RtlLeaveCriticalSection(&LdrpLoaderLock);
}
}
#endif
//
// Set the image base address and return the function table address.
//
*ImageBase = Base;
return FunctionTable;
}
VOID
RtlInsertInvertedFunctionTable (
PINVERTED_FUNCTION_TABLE InvertedTable,
PVOID ImageBase,
ULONG SizeOfImage
)
/*++
Routine Description:
This function inserts an entry in an inverted function table if there
is room in the table. Otherwise, no operation is performed.
N.B. It is assumed that appropriate locks are held when this routine
is called.
N.B. If the inverted function table overflows, then it is treated as
a cache. This is unlikely to happen, however.
Arguments:
InvertedTable - Supplies a pointer to the inverted function table in
which the specified entry is to be inserted.
ImageBase - Supplies the base address of the containing image.
SizeOfImage - Supplies the size of the image.
Return Value:
None.
--*/
{
ULONG CurrentSize;
PRUNTIME_FUNCTION FunctionTable;
#if defined(_IA64_)
ULONG64 Gp;
#endif
ULONG Index;
ULONG SizeOfTable;
//
// If the inverted table is not full, then insert the entry in the
// specified inverted table.
//
CurrentSize = InvertedTable->CurrentSize;
if (CurrentSize != InvertedTable->MaximumSize) {
//
// If the inverted table has no entries, then insert the new entry as
// the first entry. Otherwise, search the inverted table for the proper
// insert position, shuffle the table, and insert the new entry.
//
Index = 0;
if (CurrentSize != 0) {
for (Index = 0; Index < CurrentSize; Index += 1) {
if (ImageBase < InvertedTable->TableEntry[Index].ImageBase) {
break;
}
}
//
// If the new entry does not go at the end of the specified table,
// then shuffle the table down to make room for the new entry.
//
if (Index != CurrentSize) {
RtlMoveMemory(&InvertedTable->TableEntry[Index + 1],
&InvertedTable->TableEntry[Index],
(CurrentSize - Index) * sizeof(INVERTED_FUNCTION_TABLE_ENTRY));
}
}
//
// Insert the specified entry in the specified inverted function table.
//
FunctionTable = RtlImageDirectoryEntryToData (ImageBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
&SizeOfTable);
InvertedTable->TableEntry[Index].FunctionTable = FunctionTable;
InvertedTable->TableEntry[Index].ImageBase = ImageBase;
InvertedTable->TableEntry[Index].SizeOfImage = SizeOfImage;
InvertedTable->TableEntry[Index].SizeOfTable = SizeOfTable;
#if defined(_IA64_)
Gp = (ULONG64)RtlImageDirectoryEntryToData (ImageBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
&SizeOfTable);
InvertedTable->TableEntry[Index].Gp = Gp;
#endif
InvertedTable->CurrentSize += 1;
} else {
InvertedTable->Overflow = TRUE;
}
return;
}
VOID
RtlRemoveInvertedFunctionTable (
PINVERTED_FUNCTION_TABLE InvertedTable,
PVOID ImageBase
)
/*++
Routine Description:
This routine removes an entry from an inverted function table.
N.B. It is assumed that appropriate locks are held when this routine
is called.
Arguments:
InvertedTable - Supplies a pointer to the inverted function table from
which the specified entry is to be removed.
ImageBase - Supplies the base address of the containing image.
Return Value:
None.
--*/
{
ULONG CurrentSize;
ULONG Index;
//
// Search for an entry in the specified inverted table that matches the
// image base.
//
// N.B. It is possible a matching entry is not in the inverted table
// the table was full when an attempt was made to insert the
// corresponding entry.
//
CurrentSize = InvertedTable->CurrentSize;
for (Index = 0; Index < CurrentSize; Index += 1) {
if (ImageBase == InvertedTable->TableEntry[Index].ImageBase) {
break;
}
}
//
// If the entry was found in the inverted table, then remove the entry
// and reduce the size of the table.
//
if (Index != CurrentSize) {
//
// If the size of the table is not one, then shuffle the table and
// remove the specified entry.
//
if (CurrentSize != 1) {
RtlCopyMemory(&InvertedTable->TableEntry[Index],
&InvertedTable->TableEntry[Index + 1],
(CurrentSize - Index - 1) * sizeof(INVERTED_FUNCTION_TABLE_ENTRY));
}
//
// Reduce the size of the inverted table.
//
InvertedTable->CurrentSize -= 1;
}
return;
}