/** @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; | |
} | |
/** | |
Located PDB path name in PE image. | |
@param ImageBase - base of PE to search | |
@return Pointer into image at offset of PDB file name if PDB file name is found, | |
Otherwise a pointer to an empty string. | |
**/ | |
CHAR8 * | |
GetPdbPath ( | |
VOID *ImageBase | |
) | |
{ | |
CHAR8 *PdbPath; | |
UINT32 DirCount; | |
EFI_IMAGE_DOS_HEADER *DosHdr; | |
EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHdr; | |
EFI_IMAGE_OPTIONAL_HEADER32 *OptionalHdr32; | |
EFI_IMAGE_OPTIONAL_HEADER64 *OptionalHdr64; | |
EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry; | |
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry; | |
VOID *CodeViewEntryPointer; | |
// | |
// Init value | |
// | |
CodeViewEntryPointer = NULL; | |
PdbPath = NULL; | |
DosHdr = ImageBase; | |
// | |
// Check magic | |
// | |
if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { | |
return NULL; | |
} | |
NtHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)DosHdr + DosHdr->e_lfanew); | |
// | |
// Check Machine, filter for EBC | |
// | |
if (NtHdr->Pe32.FileHeader.Machine != EFI_IMAGE_MACHINE_EBC) { | |
// | |
// If not EBC, return NULL | |
// | |
return NULL; | |
} | |
// | |
// Get DirectoryEntry | |
// EBC spec says PE32+, but implementation uses PE32. So check dynamically here. | |
// | |
if (NtHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | |
OptionalHdr32 = (VOID *)&NtHdr->Pe32.OptionalHeader; | |
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(OptionalHdr32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); | |
} else if (NtHdr->Pe32Plus.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | |
OptionalHdr64 = (VOID *)&NtHdr->Pe32Plus.OptionalHeader; | |
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(OptionalHdr64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); | |
} else { | |
return NULL; | |
} | |
if (DirectoryEntry->VirtualAddress == 0) { | |
return NULL; | |
} | |
// | |
// Go through DirectoryEntry | |
// | |
for (DirCount = 0; | |
(DirCount < DirectoryEntry->Size / sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) && CodeViewEntryPointer == NULL; | |
DirCount++ | |
) | |
{ | |
DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)(DirectoryEntry->VirtualAddress + (UINTN)ImageBase + DirCount * sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)); | |
if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { | |
// | |
// Match DebugEntry, only CODEVIEW_SIGNATURE_NB10 and CODEVIEW_SIGNATURE_RSDS are supported. | |
// | |
CodeViewEntryPointer = (VOID *)((UINTN)DebugEntry->RVA + (UINTN)ImageBase); | |
switch (*(UINT32 *)CodeViewEntryPointer) { | |
case CODEVIEW_SIGNATURE_NB10: | |
PdbPath = (CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY); | |
break; | |
case CODEVIEW_SIGNATURE_RSDS: | |
PdbPath = (CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
// | |
// Done successfully | |
// | |
return PdbPath; | |
} | |
/** | |
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 CHAR8 *PdbFileName, | |
IN CHAR16 *MapFileName | |
) | |
{ | |
UINTN PdbNameSize; | |
UINTN MapNameSize; | |
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; | |
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].NormalImage == NULL) { | |
continue; | |
} | |
ImageBase = ImageTable[ImageNumber].NormalImage->LoadedImageProtocolInstance->ImageBase; | |
// | |
// Get PDB path | |
// | |
PdbPath = GetPdbPath (ImageBase); | |
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; | |
} |