#ifndef _WIN64
/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    symhelp.c

Abstract:

    Symbol Helper for debugging

Author:

    Steve Wood (stevewo) 11-Mar-1994

Revision History:

--*/


#define _SYMHELP_SOURCE_

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <imagehlp.h>
#include <debug.h>
#include <stdio.h>
#include <stdlib.h>
#include "symhelp.h"

//
// Primitives to access symbolic debug information in an image file
//

typedef struct _RTL_SYMBOL_INFORMATION {
    ULONG Type;
    ULONG SectionNumber;
    ULONG Value;
    STRING Name;
} RTL_SYMBOL_INFORMATION, *PRTL_SYMBOL_INFORMATION;

NTSTATUS
RtlLookupSymbolByAddress(
    IN PVOID ImageBase,
    IN PVOID MappedBase OPTIONAL,
    IN PVOID Address,
    IN ULONG ClosenessLimit,
    OUT PRTL_SYMBOL_INFORMATION SymbolInformation,
    OUT PRTL_SYMBOL_INFORMATION NextSymbolInformation OPTIONAL
    );

typedef struct _PROCESS_DEBUG_INFORMATION {
    LIST_ENTRY List;
    HANDLE UniqueProcess;
    DWORD ImageBase;
    DWORD EndOfImage;
    PIMAGE_DEBUG_INFORMATION DebugInfo;
    UCHAR ImageFilePath[ MAX_PATH ];
} PROCESS_DEBUG_INFORMATION, *PPROCESS_DEBUG_INFORMATION;


PLOAD_SYMBOLS_FILTER_ROUTINE LoadSymbolsFilterRoutine;

RTL_CRITICAL_SECTION LoadedImageDebugInfoListCritSect;
LIST_ENTRY LoadedImageDebugInfoListHead;
LIST_ENTRY LoadedProcessDebugInfoListHead;

LPSTR SymbolSearchPath;

// This variable tracks how many times InitializeImageDebugInformation has been
//  called. Certain operations are performed only on the first call (as
//  NumInitCalls transitions from -1 to 0).
LONG NumInitCalls = -1;

LPSTR
GetEnvVariable(
    IN LPSTR VariableName
    )
{
    NTSTATUS Status;
    STRING Name, Value;
    UNICODE_STRING UnicodeName, UnicodeValue;

    RtlInitString( &Name, VariableName );
    RtlInitUnicodeString( &UnicodeValue, NULL );
    Status = RtlAnsiStringToUnicodeString( &UnicodeName, &Name, TRUE );
    if (!NT_SUCCESS( Status )) {
        return NULL;
        }

    Status = RtlQueryEnvironmentVariable_U( NULL, &UnicodeName, &UnicodeValue );
    if (Status != STATUS_BUFFER_TOO_SMALL) {
        RtlFreeHeap( RtlProcessHeap(), 0, UnicodeName.Buffer );
        return NULL;
        }

    UnicodeValue.MaximumLength = UnicodeValue.Length + sizeof( UNICODE_NULL );
    UnicodeValue.Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, UnicodeValue.MaximumLength );
    if (UnicodeValue.Buffer == NULL) {
        RtlFreeHeap( RtlProcessHeap(), 0, UnicodeName.Buffer );
        return NULL;
        }

    Status = RtlQueryEnvironmentVariable_U( NULL, &UnicodeName, &UnicodeValue );
    if (!NT_SUCCESS( Status )) {
        RtlFreeHeap( RtlProcessHeap(), 0, UnicodeValue.Buffer );
        RtlFreeHeap( RtlProcessHeap(), 0, UnicodeName.Buffer );
        return NULL;
        }

    Status = RtlUnicodeStringToAnsiString( &Value, &UnicodeValue, TRUE );
    RtlFreeHeap( RtlProcessHeap(), 0, UnicodeValue.Buffer );
    RtlFreeHeap( RtlProcessHeap(), 0, UnicodeName.Buffer );
    if (!NT_SUCCESS( Status )) {
        return NULL;
        }

    Value.Buffer[ Value.Length ] = '\0';
    return Value.Buffer;
}

LPSTR
SetSymbolSearchPath( )
{
    ULONG Size, i, Attributes, NumberOfSymbolPaths;
    LPSTR s, SymbolPaths[ 4 ];

    if (SymbolSearchPath != NULL) {
        return SymbolSearchPath;
        }

    Size = 0;
    NumberOfSymbolPaths = 0;
    if (s = GetEnvVariable( "_NT_SYMBOL_PATH" )) {
        SymbolPaths[ NumberOfSymbolPaths++ ] = s;
        }

    if (s = GetEnvVariable( "_NT_ALT_SYMBOL_PATH" )) {
        SymbolPaths[ NumberOfSymbolPaths++ ] = s;
        }

    if (s = GetEnvVariable( "SystemRoot" )) {
        SymbolPaths[ NumberOfSymbolPaths++ ] = s;
        }

    SymbolPaths[ NumberOfSymbolPaths++ ] = ".";

    Size = 1;
    for (i=0; i<NumberOfSymbolPaths; i++) {
        Attributes = GetFileAttributesA( SymbolPaths[ i ] );
        if ( Attributes != 0xffffffff && (Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
            Size += 1 + strlen( SymbolPaths[ i ] );
            }
        else {
            SymbolPaths[ i ] = NULL;
            }
        }

    SymbolSearchPath = RtlAllocateHeap( RtlProcessHeap(), 0, Size );
    if (SymbolSearchPath == NULL) {
        return NULL;
        }
    *SymbolSearchPath = '\0';
    for (i=0; i<NumberOfSymbolPaths; i++) {
        if (s = SymbolPaths[ i ]) {
            if (*SymbolSearchPath != '\0') {
                strcat( SymbolSearchPath, ";" );
                }
            strcat( SymbolSearchPath, s );
            }
        }

    return SymbolSearchPath;
}

BOOL
InitializeImageDebugInformation(
    IN PLOAD_SYMBOLS_FILTER_ROUTINE LoadSymbolsFilter,
    IN HANDLE TargetProcess,
    IN BOOL NewProcess,
    IN BOOL GetKernelSymbols
    )
{
    PPEB Peb;
    NTSTATUS Status;
    PROCESS_BASIC_INFORMATION ProcessInformation;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    LDR_DATA_TABLE_ENTRY LdrEntryData;
    PLIST_ENTRY LdrHead, LdrNext;
    PPEB_LDR_DATA Ldr;
    UNICODE_STRING UnicodeString;
    ANSI_STRING AnsiString;
    LPSTR ImageFilePath;
    PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
    RTL_PROCESS_MODULES ModuleInfoBuffer;
    PRTL_PROCESS_MODULES ModuleInfo;
    PRTL_PROCESS_MODULE_INFORMATION ModuleInfo1;
    ULONG RequiredLength, ModuleNumber;

    // Is this the first call?
    if ( InterlockedIncrement ( &NumInitCalls ) == 0 )
    {
        // Yes
        SetSymbolSearchPath();
        InitializeListHead( &LoadedImageDebugInfoListHead );
        InitializeListHead( &LoadedProcessDebugInfoListHead );
        Status = RtlInitializeCriticalSection( &LoadedImageDebugInfoListCritSect );
        if (!NT_SUCCESS(Status)) {
            return FALSE;
        }
    }

    // The filter routine can be superceded at any time.
    LoadSymbolsFilterRoutine = LoadSymbolsFilter;

    if (GetKernelSymbols) {
        ModuleInfo = &ModuleInfoBuffer;
        RequiredLength = sizeof( *ModuleInfo );
        Status = NtQuerySystemInformation( SystemModuleInformation,
                                           ModuleInfo,
                                           RequiredLength,
                                           &RequiredLength
                                         );
        if (Status == STATUS_INFO_LENGTH_MISMATCH) {
            ModuleInfo = NULL;
            Status = NtAllocateVirtualMemory( NtCurrentProcess(),
                                              &ModuleInfo,
                                              0,
                                              &RequiredLength,
                                              MEM_COMMIT,
                                              PAGE_READWRITE
                                            );
            if (NT_SUCCESS( Status )) {
                Status = NtQuerySystemInformation( SystemModuleInformation,
                                                   ModuleInfo,
                                                   RequiredLength,
                                                   &RequiredLength
                                                 );
                if (NT_SUCCESS( Status )) {
                    ModuleInfo1 = &ModuleInfo->Modules[ 0 ];
                    for (ModuleNumber=0; ModuleNumber<ModuleInfo->NumberOfModules; ModuleNumber++) {
                        if ((DWORD)(ModuleInfo1->ImageBase) & 0x80000000) {
                            if (ImageFilePath = strchr( ModuleInfo1->FullPathName, ':')) {
                                ImageFilePath -= 1;
                                }
                            else {
                                ImageFilePath = ModuleInfo1->FullPathName +
                                                strlen( ModuleInfo1->FullPathName );
                                while (ImageFilePath > ModuleInfo1->FullPathName) {
                                    if (ImageFilePath[ -1 ] == '\\') {
                                        break;
                                        }
                                    else {
                                        ImageFilePath -= 1;
                                        }
                                    }
                                }

                            AddImageDebugInformation( NULL,
                                                      ImageFilePath,
                                                      (DWORD)ModuleInfo1->ImageBase,
                                                      ModuleInfo1->ImageSize
                                                    );
                            }

                        ModuleInfo1++;
                        }
                    }

                NtFreeVirtualMemory( NtCurrentProcess(),
                                     &ModuleInfo,
                                     &RequiredLength,
                                     MEM_RELEASE
                                   );
                }
            }
        }

    if (TargetProcess == NULL) {

        // Load module information for this process.

        TargetProcess = GetCurrentProcess();
        }

    Status = NtQueryInformationProcess( TargetProcess,
                                        ProcessBasicInformation,
                                        &ProcessInformation,
                                        sizeof( ProcessInformation ),
                                        NULL
                                      );
    if (!NT_SUCCESS( Status )) {
        return FALSE;
        }

    Peb = ProcessInformation.PebBaseAddress;

    if (NewProcess) {
        return TRUE;
        }

    //
    // Ldr = Peb->Ldr
    //

    Status = NtReadVirtualMemory( TargetProcess,
                                  &Peb->Ldr,
                                  &Ldr,
                                  sizeof( Ldr ),
                                  NULL
                                );
    if (!NT_SUCCESS( Status )) {
        return FALSE;
        }

    LdrHead = &Ldr->InMemoryOrderModuleList;

    //
    // LdrNext = Head->Flink;
    //

    Status = NtReadVirtualMemory( TargetProcess,
                                  &LdrHead->Flink,
                                  &LdrNext,
                                  sizeof( LdrNext ),
                                  NULL
                                );
    if (!NT_SUCCESS( Status )) {
        return FALSE;
        }

    while (LdrNext != LdrHead) {
        LdrEntry = CONTAINING_RECORD( LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks );
        Status = NtReadVirtualMemory( TargetProcess,
                                      LdrEntry,
                                      &LdrEntryData,
                                      sizeof( LdrEntryData ),
                                      NULL
                                    );
        if (!NT_SUCCESS( Status )) {
            return FALSE;
            }

        UnicodeString.Length = LdrEntryData.FullDllName.Length;
        UnicodeString.MaximumLength = LdrEntryData.FullDllName.MaximumLength;
        UnicodeString.Buffer = RtlAllocateHeap( RtlProcessHeap(),
                                                0,
                                                UnicodeString.MaximumLength
                                              );
        if (!UnicodeString.Buffer) {
            return FALSE;
            }
        Status = NtReadVirtualMemory( TargetProcess,
                                      LdrEntryData.FullDllName.Buffer,
                                      UnicodeString.Buffer,
                                      UnicodeString.MaximumLength,
                                      NULL
                                    );
        if (!NT_SUCCESS( Status )) {
            RtlFreeHeap( RtlProcessHeap(), 0, UnicodeString.Buffer );
            return FALSE;
            }

        RtlUnicodeStringToAnsiString( &AnsiString,
                                      &UnicodeString,
                                      TRUE
                                    );
        RtlFreeHeap( RtlProcessHeap(), 0, UnicodeString.Buffer );
        if (ImageFilePath = strchr( AnsiString.Buffer, ':')) {
            ImageFilePath -= 1;
            }
        else {
            ImageFilePath = AnsiString.Buffer;
            }

        AddImageDebugInformation( (HANDLE)ProcessInformation.UniqueProcessId,
                                  ImageFilePath,
                                  (DWORD)LdrEntryData.DllBase,
                                  LdrEntryData.SizeOfImage
                                );

        RtlFreeAnsiString( &AnsiString );

        LdrNext = LdrEntryData.InMemoryOrderLinks.Flink;
        }

    return TRUE;
}


BOOL
AddImageDebugInformation(
    IN HANDLE UniqueProcess,
    IN LPSTR ImageFilePath,
    IN DWORD ImageBase,
    IN DWORD ImageSize
    )
{
    NTSTATUS Status;
    PLIST_ENTRY Head, Next;
    PIMAGE_DEBUG_INFORMATION DebugInfo;
    PPROCESS_DEBUG_INFORMATION ProcessInfo;
    HANDLE FileHandle;
    UCHAR PathBuffer[ MAX_PATH ];

    FileHandle = FindExecutableImage( ImageFilePath, SymbolSearchPath, PathBuffer );
    if (FileHandle == NULL) {
        if (LoadSymbolsFilterRoutine != NULL) {
            (*LoadSymbolsFilterRoutine)( UniqueProcess,
                                         ImageFilePath,
                                         ImageBase,
                                         ImageSize,
                                         LoadSymbolsPathNotFound
                                       );
            }

        return FALSE;
        }
    CloseHandle( FileHandle );
    if (LoadSymbolsFilterRoutine != NULL) {
        (*LoadSymbolsFilterRoutine)( UniqueProcess,
                                     PathBuffer,
                                     ImageBase,
                                     ImageSize,
                                     LoadSymbolsDeferredLoad
                                   );
        }

    Status = RtlEnterCriticalSection( &LoadedImageDebugInfoListCritSect );
    if (!NT_SUCCESS( Status )) {
        return FALSE;
        }

    Head = &LoadedImageDebugInfoListHead;
    Next = Head->Flink;
    while (Next != Head) {
        DebugInfo = CONTAINING_RECORD( Next, IMAGE_DEBUG_INFORMATION, List );
        if (DebugInfo->ImageBase == ImageBase &&
            !_stricmp( PathBuffer, DebugInfo->ImageFilePath )
           ) {
            break;
            }

        Next = Next->Flink;
        }

    if (Next == Head) {
        DebugInfo = NULL;
        }

    Head = &LoadedProcessDebugInfoListHead;
    Next = Head->Flink;
    while (Next != Head) {
        ProcessInfo = CONTAINING_RECORD( Next, PROCESS_DEBUG_INFORMATION, List );
        if (ProcessInfo->UniqueProcess == UniqueProcess &&
            !_stricmp( PathBuffer, ProcessInfo->ImageFilePath )
           ) {
            return TRUE;
            }

        Next = Next->Flink;
        }

    ProcessInfo = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( *ProcessInfo ) );
    if (ProcessInfo == NULL) {
        return FALSE;
        }
    ProcessInfo->ImageBase = ImageBase;
    ProcessInfo->EndOfImage = ImageBase + ImageSize;
    ProcessInfo->UniqueProcess = UniqueProcess;
    ProcessInfo->DebugInfo = DebugInfo;
    strcpy( ProcessInfo->ImageFilePath, PathBuffer );
    InsertTailList( &LoadedProcessDebugInfoListHead, &ProcessInfo->List );

    RtlLeaveCriticalSection( &LoadedImageDebugInfoListCritSect );
    return TRUE;
}


BOOL
RemoveImageDebugInformation(
    IN HANDLE UniqueProcess,
    IN LPSTR ImageFilePath,
    IN DWORD ImageBase
    )
{
    PLIST_ENTRY Head, Next;
    PPROCESS_DEBUG_INFORMATION ProcessInfo;

    Head = &LoadedProcessDebugInfoListHead;
    Next = Head->Flink;
    while (Next != Head) {
        ProcessInfo = CONTAINING_RECORD( Next, PROCESS_DEBUG_INFORMATION, List );
        if (ProcessInfo->UniqueProcess == UniqueProcess &&
            (!ARGUMENT_PRESENT( ImageFilePath ) ||
             !_stricmp( ImageFilePath, ProcessInfo->ImageFilePath )
            )
           ) {
            if (LoadSymbolsFilterRoutine != NULL) {
                (*LoadSymbolsFilterRoutine)( UniqueProcess,
                                             ProcessInfo->ImageFilePath,
                                             ProcessInfo->ImageBase,
                                             ProcessInfo->EndOfImage - ProcessInfo->ImageBase,
                                             LoadSymbolsUnload
                                           );
                }

            Next = Next->Blink;
            RemoveEntryList( &ProcessInfo->List );
            RtlFreeHeap( RtlProcessHeap(), 0, ProcessInfo );
            if (ARGUMENT_PRESENT( ImageFilePath )) {
                break;
                }
            }

        Next = Next->Flink;
        }

    return TRUE;
}

PIMAGE_DEBUG_INFORMATION
FindImageDebugInformation(
    IN HANDLE UniqueProcess,
    IN DWORD Address
    )
{
    NTSTATUS Status;
    PLIST_ENTRY Head, Next;
    PIMAGE_DEBUG_INFORMATION DebugInfo;
    PPROCESS_DEBUG_INFORMATION ProcessInfo;

    Status = RtlEnterCriticalSection( &LoadedImageDebugInfoListCritSect );
    if (!NT_SUCCESS( Status )) {
        return NULL;
        }

    if (Address & 0x80000000) {
        UniqueProcess = NULL;
        }

    Head = &LoadedProcessDebugInfoListHead;
    Next = Head->Flink;
    while (Next != Head) {
        ProcessInfo = CONTAINING_RECORD( Next, PROCESS_DEBUG_INFORMATION, List );
        if (ProcessInfo->UniqueProcess == UniqueProcess &&
            Address >= ProcessInfo->ImageBase &&
            Address < ProcessInfo->EndOfImage
           ) {
            break;
            }

        Next = Next->Flink;
        }

    if (Next == Head) {
        RtlLeaveCriticalSection( &LoadedImageDebugInfoListCritSect );
        return NULL;
        }

    DebugInfo = ProcessInfo->DebugInfo;
    if (DebugInfo == NULL) {
        DebugInfo = MapDebugInformation( NULL, ProcessInfo->ImageFilePath, SymbolSearchPath, ProcessInfo->ImageBase );
        if (DebugInfo != NULL) {
            DebugInfo->ImageBase = ProcessInfo->ImageBase;
            ProcessInfo->DebugInfo = DebugInfo;
            if (LoadSymbolsFilterRoutine != NULL) {
                (*LoadSymbolsFilterRoutine)( UniqueProcess,
                                             ProcessInfo->ImageFilePath,
                                             ProcessInfo->ImageBase,
                                             ProcessInfo->EndOfImage - ProcessInfo->ImageBase,
                                             LoadSymbolsLoad
                                           );
                }

            InsertTailList( &LoadedImageDebugInfoListHead, &DebugInfo->List );
            }
        else {
            if (LoadSymbolsFilterRoutine != NULL) {
                (*LoadSymbolsFilterRoutine)( UniqueProcess,
                                             ProcessInfo->ImageFilePath,
                                             ProcessInfo->ImageBase,
                                             ProcessInfo->EndOfImage - ProcessInfo->ImageBase,
                                             LoadSymbolsUnableToLoad
                                           );
                }
            }
        }

    RtlLeaveCriticalSection( &LoadedImageDebugInfoListCritSect );
    return DebugInfo;
}


ULONG
GetSymbolicNameForAddress(
    IN HANDLE UniqueProcess,
    IN ULONG Address,
    OUT LPSTR Name,
    IN ULONG MaxNameLength
    )
{
    NTSTATUS Status;
    PIMAGE_DEBUG_INFORMATION DebugInfo;
    RTL_SYMBOL_INFORMATION SymbolInformation;
    ULONG i, ModuleNameLength, Result, Offset;
    LPSTR s;

    DebugInfo = FindImageDebugInformation( UniqueProcess,
                                           Address
                                         );
    if (DebugInfo != NULL) {
        if (s = strchr( DebugInfo->ImageFileName, '.' )) {
            ModuleNameLength = s - DebugInfo->ImageFileName;
            }
        else {
            ModuleNameLength = strlen( DebugInfo->ImageFileName );
            }

        // [mikese] RtlLookupSymbolByAddress will fault if there is
        //  no COFF symbol information.
        if ( DebugInfo->CoffSymbols != NULL ) {
            Status = RtlLookupSymbolByAddress( (PVOID)DebugInfo->ImageBase,
                                       DebugInfo->CoffSymbols,
                                       (PVOID)Address,
                                       0x4000,
                                       &SymbolInformation,
                                       NULL
                                     );

            }
        else {
            Status = STATUS_UNSUCCESSFUL;
             }
        }
    else {
        ModuleNameLength = 0;
        Status = STATUS_UNSUCCESSFUL;
        }

    if (NT_SUCCESS( Status )) {
        s = SymbolInformation.Name.Buffer;
        i = 1;
        while (SymbolInformation.Name.Length > i &&
               isdigit( s[ SymbolInformation.Name.Length - i ] )
              ) {
            i += 1;
            }

        if (s[ SymbolInformation.Name.Length - i ] == '@') {
            SymbolInformation.Name.Length = (USHORT)(SymbolInformation.Name.Length - i);
            }

        s = Name;
        Result = _snprintf( s, MaxNameLength,
                            "%.*s!%Z",
                            ModuleNameLength,
                            DebugInfo->ImageFileName,
                            &SymbolInformation.Name
                          );
        Offset = Address - DebugInfo->ImageBase - SymbolInformation.Value;
        if (Offset != 0) {
            Result += _snprintf( s + Result, MaxNameLength - Result, "+0x%x", Offset );
            }
        }
    else {
        if (ModuleNameLength != 0) {
            Result = _snprintf( Name, MaxNameLength,
                                "%.*s!0x%08x",
                                ModuleNameLength,
                                DebugInfo->ImageFileName,
                                Address
                              );
            }
        else {
            Result = _snprintf( Name, MaxNameLength, "0x%08x", Address );
            }
        }

    return Result;
}

ULONG
TranslateAddress (
    IN ULONG Address,
    OUT LPSTR Name,
    IN ULONG MaxNameLength )
{
    PRTL_DEBUG_INFORMATION p;
    NTSTATUS Status;
    DWORD ProcessId;
    ULONG Result = 0;
    ULONG Attempts = 0;

    // We need to call Initialize once to ensure that GetSymbolicNameForAddress
    //  does not fault.
    if ( NumInitCalls == -1 )
    {
        InitializeImageDebugInformation( LoadSymbolsFilterRoutine,
                                         NULL, FALSE, FALSE );
    }

    ProcessId = GetCurrentProcessId();

    while ( Result == 0 )
    {
        Result = GetSymbolicNameForAddress ( (HANDLE)ProcessId, Address,
                                             Name, MaxNameLength );
        if ( Result == 0 )
        {
            if ( ++Attempts < 2 )
            {
                // Try reintialising, to load any modules we missed on a previous
                //  occasion (or if we haven't initialised yet).
                // I don't need a load-symbols-filter, so just use whatever is
                //  already there, if any
                InitializeImageDebugInformation( LoadSymbolsFilterRoutine,
                                                 NULL, FALSE, FALSE );
            }
            else
            {
                // Apparently we are unable to do the right thing, so just return
                //  the address as hex.
                Result = _snprintf( Name, MaxNameLength, "0x%08x", Address );
            }
        }
    }

    return Result;
}


NTSTATUS
RtlpCaptureSymbolInformation(
    IN PIMAGE_SYMBOL SymbolEntry,
    IN PCHAR StringTable,
    OUT PRTL_SYMBOL_INFORMATION SymbolInformation
    );

PIMAGE_COFF_SYMBOLS_HEADER
RtlpGetCoffDebugInfo(
    IN PVOID ImageBase,
    IN PVOID MappedBase OPTIONAL
    );

NTSTATUS
RtlLookupSymbolByAddress(
    IN PVOID ImageBase,
    IN PVOID MappedBase OPTIONAL,
    IN PVOID Address,
    IN ULONG ClosenessLimit,
    OUT PRTL_SYMBOL_INFORMATION SymbolInformation,
    OUT PRTL_SYMBOL_INFORMATION NextSymbolInformation OPTIONAL
    )
/*++

Routine Description:

    Given a code address, this routine returns the nearest symbol
    name and the offset from the symbol to that name.  If the
    nearest symbol is not within ClosenessLimit of the location,
    STATUS_ENTRYPOINT_NOT_FOUND is returned.

Arguments:

    ImageBase - Supplies the base address of the image containing
                Address

    MappedBase - Optional parameter, that if specified means the image
                 was mapped as a data file and the MappedBase gives the
                 location it was mapped.  If this parameter does not
                 point to an image file base, then it is assumed that
                 this is a pointer to the coff debug info.

    ClosenessLimit - Specifies the maximum distance that Address can be
                     from the value of a symbol to be considered
                     "found".  Symbol's whose value is further away then
                     this are not "found".

    SymbolInformation - Points to a structure that is filled in by
                        this routine if a symbol table entry is found.

    NextSymbolInformation - Optional parameter, that if specified, is
                            filled in with information about these
                            symbol whose value is the next address above
                            Address


Return Value:

    Status of operation.

--*/

{
    NTSTATUS Status;
    ULONG AddressOffset, i;
    PIMAGE_SYMBOL PreviousSymbolEntry;
    PIMAGE_SYMBOL SymbolEntry;
    IMAGE_SYMBOL Symbol;
    PUCHAR StringTable;
    BOOLEAN SymbolFound;
    PIMAGE_COFF_SYMBOLS_HEADER DebugInfo;

    DebugInfo = RtlpGetCoffDebugInfo( ImageBase, MappedBase );
    if (DebugInfo == NULL) {
        return STATUS_INVALID_IMAGE_FORMAT;
    }

    //
    // Crack the symbol table.
    //

    SymbolEntry = (PIMAGE_SYMBOL)
        ((ULONG)DebugInfo + DebugInfo->LvaToFirstSymbol);

    StringTable = (PUCHAR)
        ((ULONG)SymbolEntry + DebugInfo->NumberOfSymbols * (ULONG)IMAGE_SIZEOF_SYMBOL);


    //
    // Find the "header" symbol (skipping all the section names)
    //

    for (i = 0; i < DebugInfo->NumberOfSymbols; i++) {
        if (!strcmp( &SymbolEntry->N.ShortName[ 0 ], "header" )) {
            break;
            }

        SymbolEntry = (PIMAGE_SYMBOL)((ULONG)SymbolEntry +
                        IMAGE_SIZEOF_SYMBOL);
        }

    //
    // If no "header" symbol found, just start at the first symbol.
    //

    if (i >= DebugInfo->NumberOfSymbols) {
        SymbolEntry = (PIMAGE_SYMBOL)((ULONG)DebugInfo + DebugInfo->LvaToFirstSymbol);
        i = 0;
        }

    //
    // Loop through all symbols in the symbol table.  For each symbol,
    // if it is within the code section, subtract off the bias and
    // see if there are any hits within the profile buffer for
    // that symbol.
    //

    AddressOffset = (ULONG)Address - (ULONG)ImageBase;
    SymbolFound = FALSE;
    for (; i < DebugInfo->NumberOfSymbols; i++) {

        //
        // Skip over any Auxilliary entries.
        //
        try {
            while (SymbolEntry->NumberOfAuxSymbols) {
                i = i + 1 + SymbolEntry->NumberOfAuxSymbols;
                SymbolEntry = (PIMAGE_SYMBOL)
                    ((ULONG)SymbolEntry + IMAGE_SIZEOF_SYMBOL +
                     SymbolEntry->NumberOfAuxSymbols * IMAGE_SIZEOF_SYMBOL
                    );

                }

            RtlMoveMemory( &Symbol, SymbolEntry, IMAGE_SIZEOF_SYMBOL );
            }
        except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
            }

        //
        // If this symbol value is less than the value we are looking for.
        //

        if (Symbol.Value <= AddressOffset) {
            //
            // Then remember this symbol entry.
            //

            PreviousSymbolEntry = SymbolEntry;
            SymbolFound = TRUE;
            }
        else {
            //
            // All done looking if value of symbol is greater than
            // what we are looking for, as symbols are in address order
            //

            break;
            }

        SymbolEntry = (PIMAGE_SYMBOL)
            ((ULONG)SymbolEntry + IMAGE_SIZEOF_SYMBOL);

        }

    if (!SymbolFound || (AddressOffset - PreviousSymbolEntry->Value) > ClosenessLimit) {
        return STATUS_ENTRYPOINT_NOT_FOUND;
        }

    Status = RtlpCaptureSymbolInformation( PreviousSymbolEntry, StringTable, SymbolInformation );
    if (NT_SUCCESS( Status ) && ARGUMENT_PRESENT( NextSymbolInformation )) {
        Status = RtlpCaptureSymbolInformation( SymbolEntry, StringTable, NextSymbolInformation );
        }

    return Status;
}


NTSTATUS
RtlpCaptureSymbolInformation(
    IN PIMAGE_SYMBOL SymbolEntry,
    IN PCHAR StringTable,
    OUT PRTL_SYMBOL_INFORMATION SymbolInformation
    )
{
    USHORT MaximumLength;
    PCHAR s;

    SymbolInformation->SectionNumber = SymbolEntry->SectionNumber;
    SymbolInformation->Type = SymbolEntry->Type;
    SymbolInformation->Value = SymbolEntry->Value;

    if (SymbolEntry->N.Name.Short) {
        MaximumLength = 8;
        s = &SymbolEntry->N.ShortName[ 0 ];
        }

    else {
        MaximumLength = 64;
        s = &StringTable[ SymbolEntry->N.Name.Long ];
        }

#if i386
    if (*s == '_') {
        s++;
        MaximumLength--;
        }
#endif

    SymbolInformation->Name.Buffer = s;
    SymbolInformation->Name.Length = 0;
    while (*s && MaximumLength--) {
        SymbolInformation->Name.Length++;
        s++;
        }

    SymbolInformation->Name.MaximumLength = SymbolInformation->Name.Length;
    return( STATUS_SUCCESS );
}


PIMAGE_COFF_SYMBOLS_HEADER
RtlpGetCoffDebugInfo(
    IN PVOID ImageBase,
    IN PVOID MappedBase OPTIONAL
    )
{
    PIMAGE_COFF_SYMBOLS_HEADER DebugInfo;
    PIMAGE_DOS_HEADER DosHeader;
    PIMAGE_DEBUG_DIRECTORY DebugDirectory;
    ULONG DebugSize;
    ULONG NumberOfDebugDirectories;

    DosHeader = (PIMAGE_DOS_HEADER)MappedBase;
    if ( !DosHeader || DosHeader->e_magic == IMAGE_DOS_SIGNATURE ) {
        //
        // Locate debug section.
        //

        DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)
            RtlImageDirectoryEntryToData( (PVOID)(MappedBase == NULL ? ImageBase : MappedBase),
                                          (BOOLEAN)(MappedBase == NULL ? TRUE : FALSE),
                                          IMAGE_DIRECTORY_ENTRY_DEBUG,
                                          &DebugSize
                                        );

        if (!DebugDirectory ||
            (DebugSize < sizeof(IMAGE_DEBUG_DIRECTORY)) ||
            ((DebugSize % sizeof(IMAGE_DEBUG_DIRECTORY)) != 0)) {
            return NULL;
        }
        //
        // point debug directory at coff debug directory
        //
        NumberOfDebugDirectories = DebugSize / sizeof(*DebugDirectory);

        while ( NumberOfDebugDirectories-- ) {
            if ( DebugDirectory->Type == IMAGE_DEBUG_TYPE_COFF ) {
                break;
            }
            DebugDirectory++;
        }

        if (DebugDirectory->Type != IMAGE_DEBUG_TYPE_COFF ) {
            return NULL;
        }

        if (MappedBase == NULL) {
            if (DebugDirectory->AddressOfRawData == 0) {
                return(NULL);
            }
            DebugInfo = (PIMAGE_COFF_SYMBOLS_HEADER)
                        ((ULONG) ImageBase + DebugDirectory->AddressOfRawData);
        } else {
            DebugInfo = (PIMAGE_COFF_SYMBOLS_HEADER)
                        ((ULONG) MappedBase + DebugDirectory->PointerToRawData);
        }
    } else {
        DebugInfo = (PIMAGE_COFF_SYMBOLS_HEADER)MappedBase;
    }
    return DebugInfo;
}
#endif