/** @file

Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent


**/

#include "Edb.h"

/**

  Load single symbol entry.

  @param  Object          - Symbol file object
  @param  Name            - Symbol name
  @param  ObjName         - Object name
  @param  Address         - Symbol address
  @param  Type            - Symbol type

  @retval EFI_SUCCESS - add single symbol entry successfully

**/
EFI_STATUS
EdbLoadSymbolSingleEntry (
  IN EFI_DEBUGGER_SYMBOL_OBJECT  *Object,
  IN CHAR8                       *Name,
  IN CHAR8                       *ObjName,
  IN UINTN                       Address,
  IN EFI_DEBUGGER_SYMBOL_TYPE    Type
  )
{
  EFI_DEBUGGER_SYMBOL_ENTRY  *Entry;

  //
  // Check Count VS MaxCount
  //
  if (Object->EntryCount >= Object->MaxEntryCount) {
    //
    // reallocate (for codebuffer too)
    // TBD
    //
    return EFI_OUT_OF_RESOURCES;
  }

  Entry = &Object->Entry[Object->EntryCount];

  //
  // Print Debug info
  //
  if (sizeof (UINTN) == sizeof (UINT64)) {
    DEBUG ((DEBUG_ERROR, "  Symbol: %a, Address: 0x%016lx (%d)\n", Name, (UINT64)Address, (UINTN)Type));
  } else {
    DEBUG ((DEBUG_ERROR, "  Symbol: %a, Address: 0x%08x (%d)\n", Name, Address, (UINTN)Type));
  }

  //
  // Fill the entry - name, RVA, type
  //
  AsciiStrnCpyS (Entry->Name, sizeof (Entry->Name), Name, sizeof (Entry->Name) - 1);
  if (ObjName != NULL) {
    AsciiStrnCpyS (Entry->ObjName, sizeof (Entry->ObjName), ObjName, sizeof (Entry->ObjName) - 1);
  }

  Entry->Rva  = Address % EFI_DEBUGGER_DEFAULT_LINK_IMAGEBASE;
  Entry->Type = Type;

  //
  // Increase Count
  //
  Object->EntryCount++;

  //
  // Done
  //
  return EFI_SUCCESS;
}

typedef enum {
  EdbEbcMapParseStateUninitialized,
  EdbEbcMapParseStateSymbolStart,
  EdbEbcMapParseStateSeHandlerSymbol,
  EdbEbcMapParseStateFunctionSymbol,
  EdbEbcMapParseStateVarbssInitSymbol,
  EdbEbcMapParseStateCrtSymbol,
  EdbEbcMapParseStateVariableSymbol,
  EdbEbcMapParseStateStaticFunctionSymbol,
  EdbEbcMapParseStateMax,
} EDB_EBC_MAP_PARSE_STATE;

typedef enum {
  EdbEbcSymbolParseStateUninitialized,
  EdbEbcSymbolParseStateReadyForName,
  EdbEbcSymbolParseStateReadyForRVA,
  EdbEbcSymbolParseStateReadyForType,
  EdbEbcSymbolParseStateReadyForObject,
  EdbEbcSymbolParseStateMax,
} EDB_EBC_SYMBOL_PARSE_STATE;

/**

  The following code depends on the MAP file generated by IEC compiler (actually Microsoft linker).

  Sample as follows: EbcTest.map
===============================================================================
  EbcTest

 Timestamp is 45b02718 (Fri Jan 19 10:04:08 2007)

 Preferred load address is 10000000

 Start         Length     Name                   Class
 0001:00000000 00000370H .text                   CODE
 0002:00000000 00000030H _VARBSS_INIT            CODE
 0003:00000000 00000004H .CRT$TSA                DATA
 0003:00000004 00000004H .CRT$TSC                DATA
 0003:00000008 00000004H .CRT$X                  DATA
 0003:0000000c 00000008H .CRT$XCU                DATA
 0003:00000014 00000004H .CRT$Z                  DATA
 0003:00000020 0000001cH .rdata                  DATA
 0003:0000003c 00000000H .edata                  DATA
 0003:0000003c 00000056H .rdata$debug            DATA
 0004:00000000 00000070H .data                   DATA
 0004:00000070 00000020H .bss                    DATA

  Address         Publics by Value              Rva+Base     Lib:Object

 0000:00000000       ___safe_se_handler_table   00000000     <absolute>
 0000:00000000       ___safe_se_handler_count   00000000     <absolute>
 0001:00000042       TestSubRoutine             10000442 f   EbcTest.obj
 0001:0000011a       EfiMain                    1000051a f   EbcTest.obj
 0001:00000200       TestSubRoutineSub          10000600 f   EbcTestSub.obj
 0001:00000220       EfiStart                   10000620 f   EbcLib:EbcLib.obj
 0002:00000000       varbss_init_C:\efi_src\TIANO\Edk\Sample\Universal\Ebc\Dxe\EbcTest\EbcTest$c45b02717 10000800 f   EbcTest.obj
 0002:00000020       varbss_init_C:\efi_src\TIANO\Edk\Sample\Universal\Ebc\Dxe\EbcTest\EbcTestSub$c45af77f3 10000820 f   EbcTestSub.obj
 0003:00000000       CrtThunkBegin              10000a00     EbcLib:EbcLib.obj
 0003:00000004       CrtThunkEnd                10000a04     EbcLib:EbcLib.obj
 0003:00000008       CrtBegin                   10000a08     EbcLib:EbcLib.obj
 0003:00000014       CrtEnd                     10000a14     EbcLib:EbcLib.obj
 0004:00000070       TestStr                    10000c70     EbcTest.obj
 0004:00000078       TestVariable1              10000c78     EbcTest.obj
 0004:00000080       TestSubVariableSub         10000c80     EbcTestSub.obj

 entry point at        0001:00000220

 Static symbols

 0001:00000000       TestSubRoutine2            10000400 f   EbcTest.obj
===============================================================================

**/

/**

  Load symbol entry by Iec.

  @param  Object          - Symbol file object
  @param  BufferSize      - Symbol file buffer size
  @param  Buffer          - Symbol file buffer

  @retval EFI_SUCCESS - add symbol entry successfully

**/
EFI_STATUS
EdbLoadSymbolEntryByIec (
  IN EFI_DEBUGGER_SYMBOL_OBJECT  *Object,
  IN UINTN                       BufferSize,
  IN VOID                        *Buffer
  )
{
  CHAR8                       *LineBuffer;
  CHAR8                       *FieldBuffer;
  EDB_EBC_MAP_PARSE_STATE     MapParseState;
  EDB_EBC_SYMBOL_PARSE_STATE  SymbolParseState;
  CHAR8                       *Name;
  CHAR8                       *ObjName;
  UINTN                       Address;
  EFI_DEBUGGER_SYMBOL_TYPE    Type;

  //
  // Begin to parse the Buffer
  //
  LineBuffer    = AsciiStrGetNewTokenLine (Buffer, "\n\r");
  MapParseState = EdbEbcMapParseStateUninitialized;
  //
  // Check each line
  //
  while (LineBuffer != NULL) {
    FieldBuffer      = AsciiStrGetNewTokenField (LineBuffer, " ");
    SymbolParseState = EdbEbcSymbolParseStateUninitialized;
    //
    // Init entry value
    //
    Name    = NULL;
    ObjName = NULL;
    Address = 0;
    Type    = EfiDebuggerSymbolTypeMax;
    //
    // Check each field
    //
    while (FieldBuffer != NULL) {
      if (AsciiStrCmp (FieldBuffer, "") == 0) {
        FieldBuffer = AsciiStrGetNextTokenField (" ");
        continue;
      }

      //
      // check "Address"
      //
      if (AsciiStrCmp (FieldBuffer, "Address") == 0) {
        MapParseState = EdbEbcMapParseStateSymbolStart;
        break;
      }

      //
      // check "Static"
      //
      if (AsciiStrCmp (FieldBuffer, "Static") == 0) {
        MapParseState = EdbEbcMapParseStateStaticFunctionSymbol;
        break;
      }

      if (MapParseState == EdbEbcMapParseStateUninitialized) {
        //
        // Do not parse anything until get "Address" or "Static"
        //
        break;
      }

      if (AsciiStrCmp (FieldBuffer, "entry") == 0) {
        //
        // Skip entry point
        //
        break;
      }

      //
      // Now we start to parse this line for Name, Address, and Object
      //
      switch (SymbolParseState) {
        case EdbEbcSymbolParseStateUninitialized:
          //
          // Get the Address
          //
          SymbolParseState = EdbEbcSymbolParseStateReadyForName;
          break;
        case EdbEbcSymbolParseStateReadyForName:
          //
          // Get the Name
          //
          if (AsciiStrnCmp (FieldBuffer, "___safe_se_handler", AsciiStrLen ("___safe_se_handler")) == 0) {
            //
            // skip SeHandler
            //
            MapParseState = EdbEbcMapParseStateSeHandlerSymbol;
            goto ExitFieldParse;
          } else if (AsciiStrnCmp (FieldBuffer, "varbss_init", AsciiStrLen ("varbss_init")) == 0) {
            //
            // check VarbssInit
            //
            MapParseState = EdbEbcMapParseStateVarbssInitSymbol;
            //          goto ExitFieldParse;
            Name             = FieldBuffer;
            SymbolParseState = EdbEbcSymbolParseStateReadyForRVA;
          } else if (AsciiStrnCmp (FieldBuffer, "Crt", AsciiStrLen ("Crt")) == 0) {
            //
            // check Crt
            //
            MapParseState = EdbEbcMapParseStateCrtSymbol;
            //          goto ExitFieldParse;
            Name             = FieldBuffer;
            SymbolParseState = EdbEbcSymbolParseStateReadyForRVA;
          } else {
            //
            // Now, it is normal function
            //
            switch (MapParseState) {
              case EdbEbcMapParseStateSeHandlerSymbol:
                MapParseState = EdbEbcMapParseStateFunctionSymbol;
                break;
              case EdbEbcMapParseStateCrtSymbol:
                MapParseState = EdbEbcMapParseStateVariableSymbol;
                break;
              case EdbEbcMapParseStateFunctionSymbol:
              case EdbEbcMapParseStateVariableSymbol:
              case EdbEbcMapParseStateStaticFunctionSymbol:
                break;
              default:
                ASSERT (FALSE);
                break;
            }

            Name             = FieldBuffer;
            SymbolParseState = EdbEbcSymbolParseStateReadyForRVA;
          }

          break;
        case EdbEbcSymbolParseStateReadyForRVA:
          //
          // Get the RVA
          //
          Address          = AsciiXtoi (FieldBuffer);
          SymbolParseState = EdbEbcSymbolParseStateReadyForType;
          break;
        case EdbEbcSymbolParseStateReadyForType:
          //
          // Get the Type. This is optional, only for "f".
          //
          if (AsciiStrCmp (FieldBuffer, "f") == 0) {
            SymbolParseState = EdbEbcSymbolParseStateReadyForObject;
            switch (MapParseState) {
              case EdbEbcMapParseStateFunctionSymbol:
              case EdbEbcMapParseStateVarbssInitSymbol:
                Type = EfiDebuggerSymbolFunction;
                break;
              case EdbEbcMapParseStateStaticFunctionSymbol:
                Type = EfiDebuggerSymbolStaticFunction;
                break;
              default:
                ASSERT (FALSE);
                break;
            }

            break;
          }

        //
        // Else it should be Object.
        // let it bypass here
        //
        case EdbEbcSymbolParseStateReadyForObject:
          switch (Type) {
            case EfiDebuggerSymbolTypeMax:
              switch (MapParseState) {
                case EdbEbcMapParseStateVariableSymbol:
                case EdbEbcMapParseStateCrtSymbol:
                  Type = EfiDebuggerSymbolGlobalVariable;
                  break;
                case EdbEbcMapParseStateSeHandlerSymbol:
                  //
                  // do nothing here
                  //
                  break;
                default:
                  ASSERT (FALSE);
                  break;
              }

              break;
            case EfiDebuggerSymbolFunction:
            case EfiDebuggerSymbolStaticFunction:
              break;
            default:
              ASSERT (FALSE);
              break;
          }

          //
          // Get the Object
          //
          ObjName          = FieldBuffer;
          SymbolParseState = EdbEbcSymbolParseStateUninitialized;
          break;
        default:
          ASSERT (FALSE);
          break;
      }

      //
      // Get the next field
      //
      FieldBuffer = AsciiStrGetNextTokenField (" ");
    }

    //
    // Add the entry if we get everything.
    //
    if ((Name != NULL) && (Type != EfiDebuggerSymbolTypeMax)) {
      EdbLoadSymbolSingleEntry (Object, Name, ObjName, Address, Type);
    }

ExitFieldParse:
    //
    // Get the next line
    //
    LineBuffer = AsciiStrGetNextTokenLine ("\n\r");
  }

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**

  Load symbol entry.

  @param  Object          - Symbol file object
  @param  BufferSize      - Symbol file buffer size
  @param  Buffer          - Symbol file buffer

  @retval EFI_SUCCESS - add symbol entry successfully

**/
EFI_STATUS
EdbLoadSymbolEntry (
  IN EFI_DEBUGGER_SYMBOL_OBJECT  *Object,
  IN UINTN                       BufferSize,
  IN VOID                        *Buffer
  )
{
  //
  // MAP file format depends on the compiler (actually linker).
  //
  // It is possible to check the different MAP file format in this routine.
  // Now only IEC is supported.
  //
  return EdbLoadSymbolEntryByIec (Object, BufferSize, Buffer);
}

/**

  Find symbol file by name.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  FileName        - Symbol file name
  @param  Index           - Symbol file index

  @return Object

**/
EFI_DEBUGGER_SYMBOL_OBJECT *
EdbFindSymbolFile (
  IN EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN CHAR16                     *FileName,
  IN OUT UINTN                  *Index OPTIONAL
  )
{
  UINTN  ObjectIndex;

  //
  // Check each Object
  //
  for (ObjectIndex = 0; ObjectIndex < DebuggerPrivate->DebuggerSymbolContext.ObjectCount; ObjectIndex++) {
    if (StrCmp (FileName, DebuggerPrivate->DebuggerSymbolContext.Object[ObjectIndex].Name) == 0) {
      //
      // Name match, found it
      //
      if (Index != NULL) {
        *Index = ObjectIndex;
      }

      return &DebuggerPrivate->DebuggerSymbolContext.Object[ObjectIndex];
    }
  }

  //
  // Not found
  //
  return NULL;
}

/**

  Find symbol by address.

  @param  Address         - Symbol address
  @param  Type            - Search type
  @param  RetObject       - Symbol object
  @param  RetEntry        - Symbol entry

  @return Nearest symbol address

**/
UINTN
EbdFindSymbolAddress (
  IN UINTN                        Address,
  IN EDB_MATCH_SYMBOL_TYPE        Type,
  OUT EFI_DEBUGGER_SYMBOL_OBJECT  **RetObject,
  OUT EFI_DEBUGGER_SYMBOL_ENTRY   **RetEntry
  )
{
  UINTN                       Index;
  UINTN                       SubIndex;
  UINTN                       CandidateLowerAddress;
  UINTN                       CandidateUpperAddress;
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  EFI_DEBUGGER_SYMBOL_ENTRY   *Entry;
  EFI_DEBUGGER_SYMBOL_ENTRY   *LowEntry;
  EFI_DEBUGGER_SYMBOL_ENTRY   *UpperEntry;
  EFI_DEBUGGER_SYMBOL_OBJECT  *LowObject;
  EFI_DEBUGGER_SYMBOL_OBJECT  *UpperObject;

  if ((Type < 0) || (Type >= EdbMatchSymbolTypeMax)) {
    return 0;
  }

  //
  // Init
  //
  CandidateLowerAddress = 0;
  CandidateUpperAddress = (UINTN)-1;
  LowEntry              = NULL;
  UpperEntry            = NULL;
  LowObject             = NULL;
  UpperObject           = NULL;

  //
  // Go through each object
  //
  Object = mDebuggerPrivate.DebuggerSymbolContext.Object;
  for (Index = 0; Index < mDebuggerPrivate.DebuggerSymbolContext.ObjectCount; Index++, Object++) {
    if (Object->EntryCount == 0) {
      continue;
    }

    //
    // Go through each entry
    //
    Entry = Object->Entry;
    for (SubIndex = 0; SubIndex < Object->EntryCount; SubIndex++, Entry++) {
      if (Address != Entry->Rva + Object->BaseAddress) {
        //
        // Check for nearest address
        //
        if (Address > Entry->Rva + Object->BaseAddress) {
          //
          // Record it if Current RVA < Address
          //
          if (CandidateLowerAddress < Entry->Rva + Object->BaseAddress) {
            CandidateLowerAddress = Entry->Rva + Object->BaseAddress;
            LowEntry              = Entry;
            LowObject             = Object;
          }
        } else {
          //
          // Record it if Current RVA > Address
          //
          if (CandidateUpperAddress > Entry->Rva + Object->BaseAddress) {
            CandidateUpperAddress = Entry->Rva + Object->BaseAddress;
            UpperEntry            = Entry;
            UpperObject           = Object;
          }
        }

        continue;
      }

      //
      // address match, return directly
      //
      *RetEntry  = Entry;
      *RetObject = Object;
      return Address;
    }
  }

  //
  // No Match, provide latest symbol
  //

  if ((Address - CandidateLowerAddress) < EFI_DEBUGGER_MAX_SYMBOL_ADDRESS_DELTA_VALUE) {
    //
    // Check for lower address
    //
    if (((Type == EdbMatchSymbolTypeNearestAddress) &&
         ((CandidateUpperAddress - Address) > (Address - CandidateLowerAddress))) ||
        (Type == EdbMatchSymbolTypeLowerAddress))
    {
      //
      // return nearest lower address
      //
      *RetEntry  = LowEntry;
      *RetObject = LowObject;
      return CandidateLowerAddress;
    }
  }

  if ((CandidateUpperAddress - Address) < EFI_DEBUGGER_MAX_SYMBOL_ADDRESS_DELTA_VALUE) {
    //
    // Check for upper address
    //
    if (((Type == EdbMatchSymbolTypeNearestAddress) &&
         ((CandidateUpperAddress - Address) < (Address - CandidateLowerAddress))) ||
        (Type == EdbMatchSymbolTypeUpperAddress))
    {
      //
      // return nearest upper address
      //
      *RetEntry  = UpperEntry;
      *RetObject = UpperObject;
      return CandidateUpperAddress;
    }
  }

  //
  // No match and nearest one, return NULL
  //
  return 0;
}

/**

  Unload symbol file by name.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  FileName        - Symbol file name

  @retval EFI_SUCCESS - unload symbol successfully

**/
EFI_STATUS
EdbUnloadSymbol (
  IN EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN CHAR16                     *FileName
  )
{
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  UINTN                       ObjectIndex;
  UINTN                       Index;
  EFI_DEBUGGER_SYMBOL_ENTRY   *OldEntry;
  UINTN                       OldEntryCount;
  UINTN                       MaxEntryCount;
  VOID                        **OldSourceBuffer;

  //
  // Find Symbol
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, FileName, &ObjectIndex);
  if (Object == NULL) {
    EDBPrint (L"SymbolFile is not loaded!\n");
    return EFI_DEBUG_CONTINUE;
  }

  //
  // Record old data
  //
  Object          = DebuggerPrivate->DebuggerSymbolContext.Object;
  OldEntry        = Object->Entry;
  OldSourceBuffer = Object->SourceBuffer;
  MaxEntryCount   = Object->MaxEntryCount;
  OldEntryCount   = Object->EntryCount;

  //
  // Remove the matched Object
  //
  for (Index = ObjectIndex; Index < DebuggerPrivate->DebuggerSymbolContext.ObjectCount - 1; Index++) {
    CopyMem (&Object[Index], &Object[Index + 1], sizeof (EFI_DEBUGGER_SYMBOL_OBJECT));
  }

  ZeroMem (&Object[Index], sizeof (Object[Index]));

  //
  // Move old data to new place
  //
  Object[Index].Entry         = OldEntry;
  Object[Index].SourceBuffer  = OldSourceBuffer;
  Object[Index].MaxEntryCount = MaxEntryCount;
  DebuggerPrivate->DebuggerSymbolContext.ObjectCount--;

  //
  // Clean old entry data
  //
  for (Index = 0; Index < OldEntryCount; Index++) {
    ZeroMem (&OldEntry[Index], sizeof (OldEntry[Index]));
  }

  //
  // Free OldSourceBuffer
  //
  for (Index = 0; OldSourceBuffer[Index] != NULL; Index++) {
    gBS->FreePool (OldSourceBuffer[Index]);
    OldSourceBuffer[Index] = NULL;
  }

  return EFI_SUCCESS;
}

/**

  Load symbol file by name.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  FileName        - Symbol file name
  @param  BufferSize      - Symbol file buffer size
  @param  Buffer          - Symbol file buffer

  @retval EFI_SUCCESS - load symbol successfully

**/
EFI_STATUS
EdbLoadSymbol (
  IN EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN CHAR16                     *FileName,
  IN UINTN                      BufferSize,
  IN VOID                       *Buffer
  )
{
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  EFI_STATUS                  Status;

  //
  // Check duplicated File
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, FileName, NULL);
  if (Object != NULL) {
    Status = EdbUnloadSymbol (DebuggerPrivate, FileName);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "Unload Duplicated Symbol File Error!\n"));
      return Status;
    }
  }

  //
  // Check Count VS MaxCount
  //
  if (DebuggerPrivate->DebuggerSymbolContext.ObjectCount >= DebuggerPrivate->DebuggerSymbolContext.MaxObjectCount) {
    //
    // reallocate
    // TBD
    //
    return EFI_OUT_OF_RESOURCES;
  }

  Object = &DebuggerPrivate->DebuggerSymbolContext.Object[DebuggerPrivate->DebuggerSymbolContext.ObjectCount];

  //
  // Init Object
  //
  Object->EntryCount    = 0;
  Object->MaxEntryCount = EFI_DEBUGGER_SYMBOL_ENTRY_MAX;

  //
  // Load SymbolEntry
  //
  DEBUG ((DEBUG_ERROR, "Symbol File: %s\n", FileName));
  Status = EdbLoadSymbolEntry (Object, BufferSize, Buffer);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Fill Object value
  //
  StrnCpyS (
    Object->Name,
    sizeof (Object->Name) / sizeof (CHAR16),
    FileName,
    (sizeof (Object->Name) / sizeof (CHAR16)) - 1
    );
  Object->BaseAddress = 0;

  //
  // Increase the object count
  //
  DebuggerPrivate->DebuggerSymbolContext.ObjectCount++;

  return EFI_SUCCESS;
}

/**

  Check whether PDB file and MAP file have same name.

  @param  PdbFileName - PDB file name
  @param  MapFileName - MAP file name

  @retval TRUE  - PDB and MAP file name match
  @retval FALSE - PDB and MAP file name not match

**/
BOOLEAN
MatchPdbAndMap (
  IN CONST CHAR8   *PdbFileName,
  IN CONST CHAR16  *MapFileName
  )
{
  UINTN       PdbNameSize;
  UINTN       MapNameSize;
  CONST CHAR8 *PurePdbFileName;
  UINTN       Index;

  //
  // remove dir name
  //
  PurePdbFileName = PdbFileName;
  for (Index = 0; PdbFileName[Index] != 0; Index++) {
    if (PdbFileName[Index] == '\\') {
      PurePdbFileName = &PdbFileName[Index + 1];
    }
  }

  PdbFileName = PurePdbFileName;

  //
  // get size
  //
  PdbNameSize = AsciiStrLen (PdbFileName);
  MapNameSize = StrLen (MapFileName);

  if (PdbNameSize != MapNameSize) {
    return FALSE;
  }

  //
  // check the name
  //
  for (Index = 0; Index < MapNameSize - 4; Index++) {
    if ((PdbFileName[Index] | 0x20) != (MapFileName[Index] | 0x20)) {
      return FALSE;
    }
  }

  return TRUE;
}

//
// BUGBUG: work-around start
//
typedef struct {
  EFI_DEBUG_IMAGE_INFO    *EfiDebugImageInfoTable;
  volatile UINT32         UpdateStatus;
  UINT32                  TableSize;
} EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD;

EFI_DEBUG_IMAGE_INFO_TABLE_HEADER  mDebugImageInfoTableHeader;

/**
For compatibility consideration, we handle 2 cases:

1) IA32:
  Old:                          New:
  +------------------------+    +------------------------+
  | EfiDebugImageInfoTable |    | UpdateStatus           |
  +------------------------+    +------------------------+
  | UpdateStatus           |    | TableSize              |
  +------------------------+    +------------------------+
  | TableSize              |    | EfiDebugImageInfoTable |
  +------------------------+    +------------------------+

2) X64 and IPF:
  Old:                          New:
  +------------------------+    +------------------------+
  | EfiDebugImageInfoTable |    | UpdateStatus           |
  |                        |    +------------------------+
  |                        |    | TableSize              |
  +------------------------+    +------------------------+
  | UpdateStatus           |    | EfiDebugImageInfoTable |
  +------------------------+    |                        |
  | TableSize              |    |                        |
  +------------------------+    +------------------------+

  @param DebugImageInfoTableHeader  Point to the EFI_DEBUG_IMAGE_INFO_TABLE_HEADER structure.

**/
VOID
EdbFixDebugImageInfoTable (
  IN OUT EFI_DEBUG_IMAGE_INFO_TABLE_HEADER  **DebugImageInfoTableHeader
  )
{
  mDebugImageInfoTableHeader.EfiDebugImageInfoTable = ((EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD *)(*DebugImageInfoTableHeader))->EfiDebugImageInfoTable;
  mDebugImageInfoTableHeader.UpdateStatus           = ((EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD *)(*DebugImageInfoTableHeader))->UpdateStatus;
  mDebugImageInfoTableHeader.TableSize              = ((EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_OLD *)(*DebugImageInfoTableHeader))->TableSize;

  if ((*DebugImageInfoTableHeader)->UpdateStatus > 3) {
    *DebugImageInfoTableHeader = &mDebugImageInfoTableHeader;
    return;
  }

  if ((*DebugImageInfoTableHeader)->TableSize % (EFI_PAGE_SIZE / (sizeof (VOID *))) != 0) {
    *DebugImageInfoTableHeader = &mDebugImageInfoTableHeader;
    return;
  }

  return;
}

//
// BUGBUG: work-around end
//

/**

  Patch symbol RVA.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  FileName        - Symbol file name
  @param  SearchType      - Search type for Object

  @retval EFI_SUCCESS   - Patch symbol RVA successfully
  @retval EFI_NOT_FOUND - Symbol RVA base not found

**/
EFI_STATUS
EdbPatchSymbolRVA (
  IN EFI_DEBUGGER_PRIVATE_DATA      *DebuggerPrivate,
  IN CHAR16                         *FileName,
  IN EDB_EBC_IMAGE_RVA_SEARCH_TYPE  SearchType
  )
{
  EFI_STATUS                  Status;
  UINTN                       ImageNumber;
  EFI_DEBUG_IMAGE_INFO        *ImageTable;
  CONST CHAR8           *PdbPath;
  VOID                        *ImageBase;
  VOID                        *CandidateImageBase;
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;

  if ((SearchType < 0) || (SearchType >= EdbEbcImageRvaSearchTypeMax)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Get the related object
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, FileName, NULL);
  if (Object == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Try again to get DebugImageInfoTable
  //
  if (mDebuggerPrivate.DebugImageInfoTableHeader == NULL) {
    Status = EfiGetSystemConfigurationTable (
               &gEfiDebugImageInfoTableGuid,
               (VOID **)&mDebuggerPrivate.DebugImageInfoTableHeader
               );
    if (EFI_ERROR (Status)) {
      EDBPrint (L"DebugImageInfoTable not found!\n");
      return Status;
    }
  }

  DEBUG ((DEBUG_ERROR, "DebugImageInfoTableHeader: %x\n", mDebuggerPrivate.DebugImageInfoTableHeader));

  //
  // BUGBUG: work-around start
  //
  EdbFixDebugImageInfoTable (&mDebuggerPrivate.DebugImageInfoTableHeader);
  //
  // BUGBUG: work-around end
  //

  //
  // Go through DebugImageInfoTable for each Image
  //
  CandidateImageBase = NULL;
  ImageTable         = mDebuggerPrivate.DebugImageInfoTableHeader->EfiDebugImageInfoTable;
  for (ImageNumber = 0; ImageNumber < mDebuggerPrivate.DebugImageInfoTableHeader->TableSize; ImageNumber++) {
    if (ImageTable[ImageNumber].NormalImage2 == NULL) {
      continue;
    }

    ImageBase = ImageTable[ImageNumber].NormalImage2->LoadedImageProtocolInstance->ImageBase;
    //
    // Get PDB path
    //
    PdbPath   = ImageTable[ImageNumber].NormalImage2->PdbPath;
    if (PdbPath == NULL) {
      continue;
    }

    //
    // Check PDB name
    //
    if (!MatchPdbAndMap (PdbPath, FileName)) {
      continue;
    }

    DEBUG ((DEBUG_ERROR, "ImageBase: %x\n", ImageBase));

    //
    // Check SearchType
    //
    if ((SearchType == EdbEbcImageRvaSearchTypeAny) || (SearchType == EdbEbcImageRvaSearchTypeFirst)) {
      //
      // Assign base address and return
      //
      Object->BaseAddress = (UINTN)ImageBase;
      return EFI_SUCCESS;
    }

    //
    // Get CandidateImageBase for EdbEbcImageRvaSearchTypeLast
    //
    CandidateImageBase = ImageBase;
  }

  //
  // Check EdbEbcImageRvaSearchTypeLast
  //
  if (SearchType == EdbEbcImageRvaSearchTypeLast) {
    if (CandidateImageBase == NULL) {
      return EFI_NOT_FOUND;
    }

    //
    // Assign base address and return
    //
    Object->BaseAddress = (UINTN)CandidateImageBase;
    return EFI_SUCCESS;
  }

  //
  // No match
  //
  return EFI_NOT_FOUND;
}

/**

  Check whether OBJ file and COD file have same name.

  @param  ObjFileName - OBJ file name
  @param  CodFileName - COD file name

  @retval TRUE  - OBJ and COD file name match
  @retval FALSE - OBJ and COD file name not match

**/
BOOLEAN
MatchObjAndCod (
  IN CHAR8   *ObjFileName,
  IN CHAR16  *CodFileName
  )
{
  UINTN  ObjNameSize;
  UINTN  CodNameSize;
  CHAR8  *PureObjFileName;
  UINTN  Index;

  //
  // remove library name
  //
  PureObjFileName = ObjFileName;
  for (Index = 0; ObjFileName[Index] != 0; Index++) {
    if (ObjFileName[Index] == ':') {
      PureObjFileName = &ObjFileName[Index + 1];
      break;
    }
  }

  ObjFileName = PureObjFileName;

  //
  // get size
  //
  ObjNameSize = AsciiStrLen (ObjFileName);
  CodNameSize = StrLen (CodFileName);

  if (ObjNameSize != CodNameSize) {
    return FALSE;
  }

  //
  // check the name
  //
  for (Index = 0; Index < CodNameSize - 4; Index++) {
    if ((ObjFileName[Index] | 0x20) != (CodFileName[Index] | 0x20)) {
      return FALSE;
    }
  }

  return TRUE;
}

typedef enum {
  EdbEbcCodParseStateUninitialized,
  EdbEbcCodParseStateSymbolInitialized,
  EdbEbcCodParseStateSymbolStart,
  EdbEbcCodParseStateSymbolEnd,
  EdbEbcCodParseStateMax,
} EDB_EBC_COD_PARSE_STATE;

/**

  The following code depends on the COD file generated by IEC compiler.

**/

/**

  Load code by symbol by Iec.

  @param  Name            - Symbol file name
  @param  Buffer          - Symbol file buffer
  @param  BufferSize      - Symbol file buffer size
  @param  CodeBufferSize  - Code buffer size
  @param  FuncOffset      - Code funcion offset

  @return CodeBuffer

**/
CHAR8 *
EdbLoadCodBySymbolByIec (
  IN CHAR8   *Name,
  IN VOID    *Buffer,
  IN UINTN   BufferSize,
  OUT UINTN  *CodeBufferSize,
  OUT UINTN  *FuncOffset
  )
{
  CHAR8                    *LineBuffer;
  CHAR8                    *FieldBuffer;
  VOID                     *BufferStart;
  VOID                     *BufferEnd;
  UINTN                    Offset;
  EDB_EBC_COD_PARSE_STATE  CodParseState;
  CHAR8                    Char[2];

  //
  // Init
  //
  Char[0]       = 9;
  Char[1]       = 0;
  LineBuffer    = AsciiStrGetNewTokenLine (Buffer, "\n\r");
  Offset        = (UINTN)-1;
  BufferStart   = NULL;
  BufferEnd     = NULL;
  CodParseState = EdbEbcCodParseStateUninitialized;

  //
  // Check each line
  //
  while (LineBuffer != NULL) {
    switch (CodParseState) {
      case EdbEbcCodParseStateUninitialized:
        //
        // check mark_begin, begin to check line after this match
        //
        if (AsciiStrCmp (LineBuffer, "; mark_begin;") == 0) {
          CodParseState = EdbEbcCodParseStateSymbolInitialized;
        }

        LineBuffer = AsciiStrGetNextTokenLine ("\n\r");
        PatchForAsciiStrTokenBefore (LineBuffer, '\n');
        break;

      case EdbEbcCodParseStateSymbolInitialized:
        //
        // check mark_end, not check line after this match
        //
        if (AsciiStrCmp (LineBuffer, "; mark_end;") == 0) {
          CodParseState = EdbEbcCodParseStateUninitialized;
          LineBuffer    = AsciiStrGetNextTokenLine ("\n\r");
          PatchForAsciiStrTokenBefore (LineBuffer, '\n');
          break;
        }

        //
        // not check this line if the first char is as follows
        //
        if ((*LineBuffer == 0)   ||
            (*LineBuffer == '$') ||
            (*LineBuffer == ';') ||
            (*LineBuffer == '_') ||
            (*LineBuffer == ' '))
        {
          LineBuffer = AsciiStrGetNextTokenLine ("\n\r");
          PatchForAsciiStrTokenBefore (LineBuffer, '\n');
          break;
        }

        //
        // get function name, function name is followed by char 0x09.
        //
        FieldBuffer = AsciiStrGetNewTokenField (LineBuffer, Char);
        ASSERT (FieldBuffer != NULL);
        if (AsciiStriCmp (FieldBuffer, Name) == 0) {
          BufferStart   = FieldBuffer;
          CodParseState = EdbEbcCodParseStateSymbolStart;
        }

        PatchForAsciiStrTokenAfter (FieldBuffer, 0x9);

        //
        // Get next line
        //
        LineBuffer = AsciiStrGetNextTokenLine ("\n\r");
        PatchForAsciiStrTokenBefore (LineBuffer, '\n');
        break;

      case EdbEbcCodParseStateSymbolStart:
        //
        // check mark_end, if this match, means the function is found successfully.
        //
        if (AsciiStrCmp (LineBuffer, "; mark_end;") == 0) {
          CodParseState = EdbEbcCodParseStateSymbolEnd;
          //
          // prepare CodeBufferSize, FuncOffset, and FuncStart to return
          //
          BufferEnd       = LineBuffer + sizeof ("; mark_end;") - 1;
          *CodeBufferSize = (UINTN)BufferEnd - (UINTN)BufferStart;
          *FuncOffset     = Offset;
          PatchForAsciiStrTokenAfter (LineBuffer, '\n');
          return BufferStart;
        }

        //
        // Get function offset
        //
        if ((Offset == (UINTN)-1) &&
            (*LineBuffer == ' '))
        {
          FieldBuffer = AsciiStrGetNewTokenField (LineBuffer + 2, " ");
          Offset      = AsciiXtoi (FieldBuffer);
          PatchForAsciiStrTokenAfter (FieldBuffer, ' ');
        }

        //
        // Get next line
        //
        LineBuffer = AsciiStrGetNextTokenLine ("\n\r");
        PatchForAsciiStrTokenBefore (LineBuffer, '\n');
        break;

      case EdbEbcCodParseStateSymbolEnd:
        break;

      default:
        break;
    }
  }

  //
  // no function found
  //
  return NULL;
}

/**

  Load code by symbol.

  @param  Name            - Symbol file name
  @param  Buffer          - Symbol file buffer
  @param  BufferSize      - Symbol file buffer size
  @param  CodeBufferSize  - Code buffer size
  @param  FuncOffset      - Code funcion offset

  @return CodeBuffer

**/
CHAR8 *
EdbLoadCodBySymbol (
  IN CHAR8   *Name,
  IN VOID    *Buffer,
  IN UINTN   BufferSize,
  OUT UINTN  *CodeBufferSize,
  OUT UINTN  *FuncOffset
  )
{
  //
  // COD file format depends on the compiler.
  //
  // It is possible to check the different COD file format in this routine.
  // Now only IEC is supported.
  //
  return EdbLoadCodBySymbolByIec (Name, Buffer, BufferSize, CodeBufferSize, FuncOffset);
}

/**

  Find code from object.

  @param  DebuggerPrivate    EBC Debugger private data structure
  @param  Object          - Symbol object
  @param  FileName        - File name

**/
VOID *
EdbFindCodeFromObject (
  IN EFI_DEBUGGER_PRIVATE_DATA   *DebuggerPrivate,
  IN EFI_DEBUGGER_SYMBOL_OBJECT  *Object,
  IN CHAR16                      *FileName
  )
{
  UINTN  EntryIndex;

  //
  // Go througn each Entry in this Object
  //
  for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) {
    //
    // This check is for Function only
    //
    if ((Object->Entry[EntryIndex].Type != EfiDebuggerSymbolFunction) &&
        (Object->Entry[EntryIndex].Type != EfiDebuggerSymbolStaticFunction))
    {
      continue;
    }

    //
    // Skip match varbss_init function, because they has no source code
    //
    if (AsciiStrnCmp (Object->Entry[EntryIndex].Name, "varbss_init", sizeof ("varbss_init") - 1) == 0) {
      continue;
    }

    //
    // check the name
    //
    if (!MatchObjAndCod (Object->Entry[EntryIndex].ObjName, FileName)) {
      continue;
    }

    //
    // found it, return source buffer
    //
    if (Object->Entry[EntryIndex].CodBuffer != NULL) {
      return Object->Entry[EntryIndex].SourceBuffer;
    }
  }

  //
  // not found
  //
  return NULL;
}

/**

  Load code.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  MapFileName     - Symbol file name
  @param  FileName        - Code file name
  @param  BufferSize      - Code file buffer size
  @param  Buffer          - Code file buffer

  @retval EFI_SUCCESS - Code loaded successfully

**/
EFI_STATUS
EdbLoadCode (
  IN EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN CHAR16                     *MapFileName,
  IN CHAR16                     *FileName,
  IN UINTN                      BufferSize,
  IN VOID                       *Buffer
  )
{
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  UINTN                       ObjectIndex;
  UINTN                       EntryIndex;
  VOID                        *SourceBuffer;
  EFI_STATUS                  Status;

  //
  // Find Symbol
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, &ObjectIndex);
  if (Object == NULL) {
    EDBPrint (L"SymbolFile is not loaded!\n");
    return EFI_NOT_FOUND;
  } else {
    //
    // Check duplicated File
    //
    SourceBuffer = EdbFindCodeFromObject (DebuggerPrivate, Object, FileName);
    if (SourceBuffer != NULL) {
      //
      // unnload duplicated code
      //
      Status = EdbUnloadCode (DebuggerPrivate, MapFileName, FileName, &SourceBuffer);
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_ERROR, "Unload Duplicated Code File Error!\n"));
        return Status;
      }

      Status = EdbDeleteCodeBuffer (DebuggerPrivate, MapFileName, FileName, SourceBuffer);
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_ERROR, "Delete Duplicated Code File Error!\n"));
        return Status;
      }
    }
  }

  //
  // Go through each SymbolEntry
  //
  for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) {
    //
    // load symbol for function only
    //
    if ((Object->Entry[EntryIndex].Type != EfiDebuggerSymbolFunction) &&
        (Object->Entry[EntryIndex].Type != EfiDebuggerSymbolStaticFunction))
    {
      continue;
    }

    //
    // skip varbss_init
    //
    if (AsciiStrnCmp (Object->Entry[EntryIndex].Name, "varbss_init", sizeof ("varbss_init") - 1) == 0) {
      continue;
    }

    //
    // Check the name
    //
    if (!MatchObjAndCod (Object->Entry[EntryIndex].ObjName, FileName)) {
      continue;
    }

    //
    // load code for this symbol
    //
    Object->Entry[EntryIndex].CodBuffer = EdbLoadCodBySymbol (
                                            Object->Entry[EntryIndex].Name,
                                            Buffer,
                                            BufferSize,
                                            &Object->Entry[EntryIndex].CodBufferSize,
                                            &Object->Entry[EntryIndex].FuncOffsetBase
                                            );
    if (Object->Entry[EntryIndex].CodBuffer != NULL) {
      Object->Entry[EntryIndex].SourceBuffer = Buffer;
    }
  }

  //
  // patch end '\0' for each code buffer
  //
  for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) {
    if (Object->Entry[EntryIndex].CodBuffer != NULL) {
      *((UINT8 *)Object->Entry[EntryIndex].CodBuffer + Object->Entry[EntryIndex].CodBufferSize) = 0;
      DEBUG ((DEBUG_ERROR, "  CodeSymbol: %a, FuncOffset: 0x05%x\n", Object->Entry[EntryIndex].Name, Object->Entry[EntryIndex].FuncOffsetBase));
      //      DEBUG ((DEBUG_ERROR, "  [CODE]:\n%a\n", Object->Entry[EntryIndex].CodBuffer));
    }
  }

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**

  Unload code.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  MapFileName     - Symbol file name
  @param  FileName        - Code file name
  @param  Buffer          - Code file buffer

  @retval EFI_SUCCESS - Code unloaded successfully

**/
EFI_STATUS
EdbUnloadCode (
  IN EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN CHAR16                     *MapFileName,
  IN CHAR16                     *FileName,
  OUT VOID                      **Buffer
  )
{
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  UINTN                       ObjectIndex;
  UINTN                       EntryIndex;

  //
  // Find Symbol
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, &ObjectIndex);
  if (Object == NULL) {
    EDBPrint (L"SymbolFile is not loaded!\n");
    return EFI_NOT_FOUND;
  }

  //
  // Find code
  //
  *Buffer = EdbFindCodeFromObject (DebuggerPrivate, Object, FileName);
  if (*Buffer == NULL) {
    EDBPrint (L"CodeFile is not loaded!\n");
    return EFI_NOT_FOUND;
  }

  //
  // go through each entry
  //
  for (EntryIndex = 0; EntryIndex < Object->EntryCount; EntryIndex++) {
    if ((Object->Entry[EntryIndex].Type != EfiDebuggerSymbolFunction) &&
        (Object->Entry[EntryIndex].Type != EfiDebuggerSymbolStaticFunction))
    {
      continue;
    }

    if (AsciiStrnCmp (Object->Entry[EntryIndex].Name, "varbss_init", sizeof ("varbss_init") - 1) == 0) {
      continue;
    }

    if (!MatchObjAndCod (Object->Entry[EntryIndex].ObjName, FileName)) {
      continue;
    }

    //
    // clean up the buffer
    //
    Object->Entry[EntryIndex].CodBuffer      = NULL;
    Object->Entry[EntryIndex].CodBufferSize  = 0;
    Object->Entry[EntryIndex].FuncOffsetBase = 0;
    Object->Entry[EntryIndex].SourceBuffer   = NULL;
  }

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**

  Add code buffer.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  MapFileName     - Symbol file name
  @param  CodeFileName    - Code file name
  @param  SourceBufferSize- Code buffer size
  @param  SourceBuffer    - Code buffer

  @retval EFI_SUCCESS - CodeBuffer added successfully

**/
EFI_STATUS
EdbAddCodeBuffer (
  IN     EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN     CHAR16                     *MapFileName,
  IN     CHAR16                     *CodeFileName,
  IN     UINTN                      SourceBufferSize,
  IN     VOID                       *SourceBuffer
  )
{
  UINTN                       Index;
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;

  //
  // Find Symbol
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, NULL);
  if (Object == NULL) {
    EDBPrint (L"SymbolFile is not loaded!\n");
    return EFI_NOT_FOUND;
  }

  //
  // Add it to last entry
  //
  for (Index = 0; Object->SourceBuffer[Index] != NULL; Index++) {
  }

  Object->SourceBuffer[Index] = SourceBuffer;

  return EFI_SUCCESS;
}

/**

  Delete code buffer.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  MapFileName     - Symbol file name
  @param  CodeFileName    - Code file name
  @param  SourceBuffer    - Code buffer

  @retval EFI_SUCCESS - CodeBuffer deleted successfully

**/
EFI_STATUS
EdbDeleteCodeBuffer (
  IN     EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN     CHAR16                     *MapFileName,
  IN     CHAR16                     *CodeFileName,
  IN     VOID                       *SourceBuffer
  )
{
  UINTN                       Index;
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;

  //
  // Find Symbol
  //
  Object = EdbFindSymbolFile (DebuggerPrivate, MapFileName, NULL);
  if (Object == NULL) {
    EDBPrint (L"SymbolFile is not loaded!\n");
    return EFI_NOT_FOUND;
  }

  for (Index = 0; Object->SourceBuffer[Index] != NULL; Index++) {
    //
    // free the buffer if match
    //
    if (Object->SourceBuffer[Index] == SourceBuffer) {
      gBS->FreePool (SourceBuffer);
      break;
    }
  }

  if (Object->SourceBuffer[Index] == NULL) {
    //
    // not return NOT_FOUND
    //
    return EFI_SUCCESS;
  }

  //
  // remove the entry
  //
  Object->SourceBuffer[Index] = NULL;
  for (Index = Index + 1; Object->SourceBuffer[Index] != NULL; Index++) {
    Object->SourceBuffer[Index - 1] = Object->SourceBuffer[Index];
  }

  Object->SourceBuffer[Index - 1] = NULL;

  return EFI_SUCCESS;
}

/**

  Find the symbol string according to address.

  @param  Address         - Symbol address

  @return Symbol string

**/
CHAR8 *
FindSymbolStr (
  IN UINTN  Address
  )
{
  UINTN                       ObjectIndex;
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  UINTN                       EntryIndex;
  EFI_DEBUGGER_SYMBOL_ENTRY   *Entry;

  //
  // need we display symbol
  //
  if (!mDebuggerPrivate.DebuggerSymbolContext.DisplaySymbol) {
    return NULL;
  }

  //
  // Go through each object and entry
  //
  Object = mDebuggerPrivate.DebuggerSymbolContext.Object;
  for (ObjectIndex = 0; ObjectIndex < mDebuggerPrivate.DebuggerSymbolContext.ObjectCount; ObjectIndex++) {
    Entry = Object[ObjectIndex].Entry;
    for (EntryIndex = 0; EntryIndex < Object[ObjectIndex].EntryCount; EntryIndex++) {
      //
      // if Address match, return Name
      //
      if (Address == (Entry[EntryIndex].Rva + Object[ObjectIndex].BaseAddress)) {
        return Entry[EntryIndex].Name;
      }
    }
  }

  //
  // not found
  //
  return NULL;
}

/**

  Get line number and offset from this line in code file.

  @param  Line            - Line buffer in code file
  @param  Offset          - Offset to functin entry

  @return Line number

**/
UINTN
EdbGetLineNumberAndOffsetFromThisLine (
  IN VOID    *Line,
  OUT UINTN  *Offset
  )
{
  UINTN  LineNumber;
  CHAR8  *LineBuffer;
  CHAR8  *FieldBuffer;

  LineNumber = (UINTN)-1;
  LineBuffer = Line;
  *Offset    = (UINTN)-1;

  while (LineBuffer != NULL) {
    //
    // Check candidate
    //
    if (*LineBuffer != ' ') {
      return (UINTN)-1;
    }

    //
    // Get Offset
    //
    if (*(LineBuffer + 2) != ' ') {
      if (*Offset == (UINTN)-1) {
        FieldBuffer = AsciiStrGetNewTokenField (LineBuffer + 2, " ");
        *Offset     = AsciiXtoi (FieldBuffer);
        PatchForAsciiStrTokenAfter (FieldBuffer, ' ');
      }
    }

    //
    // 1. assembly instruction
    //
    FieldBuffer = AsciiStrGetNewTokenField (LineBuffer, ":");
    //
    // 2. file path
    //
    FieldBuffer = AsciiStrGetNextTokenField (":");
    PatchForAsciiStrTokenBefore (FieldBuffer, ':');
    if (FieldBuffer == NULL) {
      //
      // candidate found
      //
      LineNumber = 0;
      LineBuffer = AsciiStrGetNextTokenLine ("\n");
      PatchForAsciiStrTokenBefore (LineBuffer, '\n');
      continue;
    }

    //
    // 3. line number
    //
    FieldBuffer = AsciiStrGetNextTokenField (":");
    PatchForAsciiStrTokenBefore (FieldBuffer, ':');
    if (FieldBuffer == NULL) {
      //
      // impossible, TBD?
      //
      LineBuffer = AsciiStrGetNextTokenLine ("\n");
      PatchForAsciiStrTokenBefore (LineBuffer, '\n');
      continue;
    }

    LineNumber = AsciiAtoi (FieldBuffer);
    //
    // Not patch after
    //

    return LineNumber;
  }

  return (UINTN)-1;
}

typedef enum {
  EdbEbcLineSearchTypeAny,
  EdbEbcLineSearchTypeFirst,
  EdbEbcLineSearchTypeLast,
  EdbEbcLineSearchTypeMax,
} EDB_EBC_LINE_SEARCH_TYPE;

/**

  Get line number from this code file.

  @param  Entry           - Symbol entry
  @param  FuncOffset      - Offset to functin entry
  @param  SearchType      - Search type for the code

  @return Line number

**/
UINTN
EdbGetLineNumberFromCode (
  IN EFI_DEBUGGER_SYMBOL_ENTRY  *Entry,
  IN UINTN                      FuncOffset,
  IN EDB_EBC_LINE_SEARCH_TYPE   SearchType
  )
{
  CHAR8  *LineBuffer;
  UINTN  LineNumber;
  UINTN  Offset;
  UINTN  CandidateLineNumber;
  UINTN  CandidateOffset;

  if ((SearchType < 0) || (SearchType >= EdbEbcLineSearchTypeMax)) {
    return (UINTN)-1;
  }

  LineNumber          = (UINTN)-1;
  CandidateLineNumber = (UINTN)-1;
  CandidateOffset     = (UINTN)-1;
  LineBuffer          = AsciiStrGetNewTokenLine (Entry->CodBuffer, "\n");
  while (LineBuffer != NULL) {
    if (*LineBuffer != ' ') {
      LineBuffer = AsciiStrGetNextTokenLine ("\n");
      PatchForAsciiStrTokenBefore (LineBuffer, '\n');
      continue;
    }

    //
    // Get Info
    //
    LineNumber = EdbGetLineNumberAndOffsetFromThisLine (LineBuffer, &Offset);

    //
    // Check offset
    //
    if (Offset != FuncOffset) {
      //
      // Check last offset match
      //
      if (CandidateOffset == FuncOffset) {
        if (SearchType == EdbEbcLineSearchTypeLast) {
          PatchForAsciiStrTokenAfter (LineBuffer, '\n');
          if (CandidateLineNumber != LineNumber) {
            return CandidateLineNumber;
          } else {
            return (UINTN)-1;
          }
        } else {
          //
          // impossible, TBD?
          //
        }
      }

      LineBuffer = AsciiStrGetNextTokenLine ("\n");
      PatchForAsciiStrTokenBefore (LineBuffer, '\n');
      CandidateLineNumber = LineNumber;
      continue;
    }

    //
    // Offset match, more check
    //
    if (SearchType == EdbEbcLineSearchTypeAny) {
      PatchForAsciiStrTokenAfter (LineBuffer, '\n');
      return LineNumber;
    }

    if (SearchType == EdbEbcLineSearchTypeFirst) {
      //
      // Check last line
      //
      PatchForAsciiStrTokenAfter (LineBuffer, '\n');
      if (CandidateLineNumber != LineNumber) {
        return LineNumber;
      } else {
        return (UINTN)-1;
      }
    }

    CandidateLineNumber = LineNumber;
    CandidateOffset     = Offset;

    LineBuffer = AsciiStrGetNextTokenLine ("\n");
    PatchForAsciiStrTokenBefore (LineBuffer, '\n');
  }

  //
  // Check last offset match
  //
  if (CandidateOffset == FuncOffset) {
    if (SearchType == EdbEbcLineSearchTypeLast) {
      return CandidateLineNumber;
    }
  }

  return (UINTN)-1;
}

/**

  Get the source string from this code file by line.

  @param  Entry           - Symbol entry
  @param  LineNumber      - line number
  @param  FuncEnd         - Function end

  @return Funtion start

**/
VOID *
EdbGetSourceStrFromCodeByLine (
  IN EFI_DEBUGGER_SYMBOL_ENTRY  *Entry,
  IN UINTN                      LineNumber,
  IN VOID                       **FuncEnd
  )
{
  CHAR8  *LineBuffer;
  CHAR8  *FieldBuffer;
  VOID   *FuncStart;
  UINTN  Number;

  FuncStart  = NULL;
  LineBuffer = AsciiStrGetNewTokenLine (Entry->CodBuffer, "\n");
  while (LineBuffer != NULL) {
    if (*LineBuffer != ';') {
      if (FuncStart != NULL) {
        //
        // Over
        //
        *FuncEnd = LineBuffer - 1;
        PatchForAsciiStrTokenAfter (LineBuffer, '\n');
        return FuncStart;
      }

      LineBuffer = AsciiStrGetNextTokenLine ("\n");
      PatchForAsciiStrTokenBefore (LineBuffer, '\n');
      continue;
    }

    //
    // Check LineNumber
    //
    FieldBuffer = AsciiStrGetNewTokenField (LineBuffer + 1, " ");
    Number      = AsciiAtoi (FieldBuffer);
    PatchForAsciiStrTokenAfter (FieldBuffer, ' ');
    if (Number != LineNumber) {
      LineBuffer = AsciiStrGetNextTokenLine ("\n");
      PatchForAsciiStrTokenBefore (LineBuffer, '\n');
      continue;
    }

    //
    // Line match, get line number
    //
    if (FuncStart == NULL) {
      FuncStart = LineBuffer;
    }

    LineBuffer = AsciiStrGetNextTokenLine ("\n");
    PatchForAsciiStrTokenBefore (LineBuffer, '\n');
  }

  return NULL;
}

/**

  Get source string from this code file.

  @param  Entry           - Symbol entry
  @param  FuncOffset      - Offset to functin entry
  @param  FuncEnd         - Function end

  @retval Funtion start

**/
VOID *
EdbGetSourceStrFromCode (
  IN EFI_DEBUGGER_SYMBOL_ENTRY  *Entry,
  IN UINTN                      FuncOffset,
  IN VOID                       **FuncEnd
  )
{
  UINTN  LineNumber;

  //
  // Only search the last line, then display
  //
  LineNumber = EdbGetLineNumberFromCode (Entry, FuncOffset, EdbEbcLineSearchTypeLast);
  if (LineNumber == (UINTN)-1) {
    return NULL;
  }

  return EdbGetSourceStrFromCodeByLine (Entry, LineNumber, FuncEnd);
}

/**

  Print source.

  @param  Address         - Instruction address
  @param  IsPrint         - Whether need to print

  @retval 1 - find the source
  @retval 0 - not find the source

**/
UINTN
EdbPrintSource (
  IN UINTN    Address,
  IN BOOLEAN  IsPrint
  )
{
  UINTN                       SymbolAddress;
  EFI_DEBUGGER_SYMBOL_OBJECT  *RetObject;
  EFI_DEBUGGER_SYMBOL_ENTRY   *RetEntry;
  UINTN                       FuncOffset;
  UINT8                       *FuncStart;
  UINT8                       *FuncEnd;
  UINT8                       *FuncIndex;
  CHAR8                       Buffer[EFI_DEBUG_MAX_PRINT_BUFFER];
  UINTN                       BufferSize;

  //
  // need we display symbol
  //
  if (!mDebuggerPrivate.DebuggerSymbolContext.DisplaySymbol) {
    return 0;
  }

  //
  // find the symbol address
  //
  SymbolAddress = EbdFindSymbolAddress (
                    Address,
                    EdbMatchSymbolTypeLowerAddress,
                    &RetObject,
                    &RetEntry
                    );
  if ((SymbolAddress == 0) || (RetEntry == NULL)) {
    return 0;
  }

  FuncOffset = Address - SymbolAddress + RetEntry->FuncOffsetBase;

  //
  // Get Func String
  //
  FuncStart = EdbGetSourceStrFromCode (RetEntry, FuncOffset, (VOID **)&FuncEnd);
  if (FuncStart == NULL) {
    return 0;
  }

  //
  // check whether need to real print
  //
  if (!IsPrint) {
    return 1;
  }

  *(UINT8 *)FuncEnd = 0;

  //
  // seperate buffer by \n, so that \r can be added.
  //
  FuncIndex = FuncStart;
  while (*FuncIndex != 0) {
    if (*FuncIndex == '\n') {
      if ((FuncIndex - FuncStart) < (EFI_DEBUG_MAX_PRINT_BUFFER - 3)) {
        BufferSize = FuncIndex - FuncStart;
      } else {
        BufferSize = EFI_DEBUG_MAX_PRINT_BUFFER - 3;
      }

      if (BufferSize != 0) {
        CopyMem (Buffer, FuncStart, BufferSize);
      }

      Buffer[BufferSize] = 0;
      EDBPrint (L"%a\n", Buffer);
      FuncStart = FuncIndex + 1;
      FuncIndex = FuncStart;
    } else {
      FuncIndex++;
    }
  }

  //
  // Patch the end
  //
  *(UINT8 *)FuncEnd = '\n';

  return 1;
}

/**

  Get Mapfile and SymbolName from one symbol format: [MapFileName:]SymbolName.

  @param  Symbol          - whole Symbol name
  @param  MapfileName     - the mapfile name in the symbol
  @param  SymbolName      - the symbol name in the symbol

**/
VOID
GetMapfileAndSymbol (
  IN CHAR16   *Symbol,
  OUT CHAR16  **MapfileName,
  OUT CHAR16  **SymbolName
  )
{
  CHAR16  *Ch;

  *MapfileName = NULL;
  *SymbolName  = Symbol;

  for (Ch = Symbol; *Ch != 0; Ch++) {
    //
    // Find split char
    //
    if (*Ch == L':') {
      *MapfileName = Symbol;
      *Ch          = 0;
      *SymbolName  = Ch + 1;
      break;
    }
  }

  return;
}

/**

  Convert a symbol to an address.

  @param  Symbol          - Symbol name
  @param  Address         - Symbol address

  @retval EFI_SUCCESS    - symbol found and address returned.
  @retval EFI_NOT_FOUND  - symbol not found
  @retval EFI_NO_MAPPING - duplicated symbol not found

**/
EFI_STATUS
Symboltoi (
  IN CHAR16  *Symbol,
  OUT UINTN  *Address
  )
{
  UINTN                       ObjectIndex;
  EFI_DEBUGGER_SYMBOL_OBJECT  *Object;
  UINTN                       EntryIndex;
  EFI_DEBUGGER_SYMBOL_ENTRY   *Entry;
  CHAR16                      *SymbolName;
  CHAR16                      *MapfileName;

  //
  // Split one symbol to mapfile name and symbol name
  //
  GetMapfileAndSymbol (Symbol, &MapfileName, &SymbolName);

  *Address = 0;
  //
  // Go through each object
  //
  Object = mDebuggerPrivate.DebuggerSymbolContext.Object;
  for (ObjectIndex = 0; ObjectIndex < mDebuggerPrivate.DebuggerSymbolContext.ObjectCount; ObjectIndex++) {
    //
    // Check MapfileName
    //
    if ((MapfileName != NULL) && (StriCmp (Object[ObjectIndex].Name, MapfileName) != 0)) {
      continue;
    }

    //
    // Go through each entry
    //
    Entry = Object[ObjectIndex].Entry;
    for (EntryIndex = 0; EntryIndex < Object[ObjectIndex].EntryCount; EntryIndex++) {
      //
      // Check SymbolName (case sensitive)
      //
      if (StrCmpUnicodeAndAscii (SymbolName, Entry[EntryIndex].Name) == 0) {
        if ((*Address != 0) && (MapfileName == NULL)) {
          //
          // Find the duplicated symbol
          //
          EDBPrint (L"Duplicated Symbol found!\n");
          return EFI_NO_MAPPING;
        } else {
          //
          // record Address
          //
          *Address = (Entry[EntryIndex].Rva + Object[ObjectIndex].BaseAddress);
        }
      }
    }
  }

  if (*Address == 0) {
    //
    // Not found
    //
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}
