windows-nt/Source/XPSP1/NT/sdktools/debuggers/ntsd64/machine.cpp
2020-09-26 16:20:57 +08:00

1033 lines
24 KiB
C++

//----------------------------------------------------------------------------
//
// Abstraction of processor-specific information.
//
// Copyright (C) Microsoft Corporation, 1999-2001.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
ULONG g_EffMachine = IMAGE_FILE_MACHINE_UNKNOWN;
MachineIndex g_EffMachineIndex = MACHIDX_COUNT;
MachineInfo* g_Machine = NULL;
MachineInfo* g_TargetMachine;
// Leave one extra slot at the end so indexing
// MACHIDX_COUNT returns NULL for the undefined case.
MachineInfo* g_AllMachines[MACHIDX_COUNT + 1];
// TRUE when symbol prefixing should be done.
BOOL g_PrefixSymbols;
// TRUE if context changed while processing
BOOL g_ContextChanged;
DEBUG_PROCESSOR_IDENTIFICATION_ALL g_InitProcessorId;
//----------------------------------------------------------------------------
//
// MachineInfo.
//
//----------------------------------------------------------------------------
HRESULT
MachineInfo::InitializeConstants(void)
{
m_TraceMode = TRACE_NONE;
m_ContextState = MCTX_NONE;
m_ContextIsReadOnly = FALSE;
m_NumberRegs = 0;
// Every machine supports basic integer and FP registers.
m_AllMaskBits = DEBUG_REGISTERS_ALL;
m_SymPrefixLen = m_SymPrefix != NULL ? strlen(m_SymPrefix) : 0;
ZeroMemory(m_PageDirectories, sizeof(m_PageDirectories));
m_Translating = FALSE;
ULONG i;
for (i = 0; i < SEGREG_COUNT; i++)
{
m_SegRegDesc[i].Flags = SEGDESC_INVALID;
}
return S_OK;
}
HRESULT
MachineInfo::InitializeForTarget(void)
{
InitializeContextFlags(&m_Context, m_SverCanonicalContext);
return S_OK;
}
HRESULT
MachineInfo::InitializeForProcessor(void)
{
DBG_ASSERT(m_MaxDataBreakpoints <= MAX_DATA_BREAKS);
// Count register definitions.
RegisterGroup* Group;
for (Group = m_Groups; Group != NULL; Group = Group->Next)
{
Group->NumberRegs = 0;
REGDEF* Def = Group->Regs;
while (Def->psz != NULL)
{
Group->NumberRegs++;
Def++;
}
m_NumberRegs += Group->NumberRegs;
REGALLDESC* Desc = Group->AllExtraDesc;
if (Desc != NULL)
{
while (Desc->Bit != 0)
{
m_AllMaskBits |= Desc->Bit;
Desc++;
}
}
}
return S_OK;
}
HRESULT
MachineInfo::GetContextState(ULONG State)
{
if (g_RegContextThread == NULL)
{
// No error message here as this can get hit during
// Reload("NT") initialization when accessing paged-out memory.
// It's also noisy in other failure cases, so rely
// on higher-level error output.
return E_UNEXPECTED;
}
if (State == MCTX_DIRTY)
{
g_ContextChanged = TRUE;
}
if (m_ContextState >= State)
{
return S_OK;
}
HRESULT Status = E_UNEXPECTED;
// Dump support is built into the Ud/Kd routines.
if (IS_USER_TARGET())
{
Status = UdGetContextState(State);
}
else if (IS_KERNEL_TARGET())
{
Status = KdGetContextState(State);
}
if (Status != S_OK)
{
ErrOut("GetContextState failed, 0x%X\n", Status);
return Status;
}
if (State == MCTX_DIRTY)
{
DBG_ASSERT(m_ContextState >= MCTX_FULL);
m_ContextState = State;
}
DBG_ASSERT(State <= m_ContextState);
return S_OK;
}
HRESULT
MachineInfo::SetContext(void)
{
if (m_ContextState != MCTX_DIRTY)
{
// Nothing to write.
return S_OK;
}
if (g_RegContextThread == NULL)
{
ErrOut("No current thread in SetContext\n");
return E_UNEXPECTED;
}
if (m_ContextIsReadOnly)
{
ErrOut("Context cannot be modified\n");
return E_UNEXPECTED;
}
HRESULT Status = E_UNEXPECTED;
if (IS_DUMP_TARGET())
{
ErrOut("Can't set dump file contexts\n");
return E_UNEXPECTED;
}
else if (IS_USER_TARGET())
{
Status = UdSetContext();
}
else if (IS_KERNEL_TARGET())
{
Status = KdSetContext();
}
if (Status != S_OK)
{
ErrOut("SetContext failed, 0x%X\n", Status);
return Status;
}
// No longer dirty.
m_ContextState = MCTX_FULL;
return S_OK;
}
HRESULT
MachineInfo::UdGetContextState(ULONG State)
{
// MCTX_CONTEXT and MCTX_FULL are the same in user mode.
if (State >= MCTX_CONTEXT && m_ContextState < MCTX_FULL)
{
HRESULT Status = g_Target->GetContext(g_RegContextThread->Handle,
&m_Context);
if (Status != S_OK)
{
return Status;
}
Status = g_Target->GetTargetSegRegDescriptors
(g_RegContextThread->Handle, 0, SEGREG_COUNT, m_SegRegDesc);
if (Status != S_OK)
{
return Status;
}
m_ContextState = MCTX_FULL;
}
return S_OK;
}
HRESULT
MachineInfo::UdSetContext(void)
{
return g_Target->SetContext(g_RegContextThread->Handle, &m_Context);
}
void
MachineInfo::InvalidateContext(void)
{
m_ContextState = MCTX_NONE;
g_Target->InvalidateTargetContext();
ULONG i;
for (i = 0; i < SEGREG_COUNT; i++)
{
m_SegRegDesc[i].Flags = SEGDESC_INVALID;
}
}
HRESULT
MachineInfo::GetExdiContext(IUnknown* Exdi, PEXDI_CONTEXT Context)
{
return E_NOTIMPL;
}
HRESULT
MachineInfo::SetExdiContext(IUnknown* Exdi, PEXDI_CONTEXT Context)
{
return E_NOTIMPL;
}
void
MachineInfo::ConvertExdiContextFromContext(PCROSS_PLATFORM_CONTEXT Context,
PEXDI_CONTEXT ExdiContext)
{
// Nothing to do.
}
void
MachineInfo::ConvertExdiContextToContext(PEXDI_CONTEXT ExdiContext,
PCROSS_PLATFORM_CONTEXT Context)
{
// Nothing to do.
}
void
MachineInfo::ConvertExdiContextToSegDescs(PEXDI_CONTEXT ExdiContext,
ULONG Start, ULONG Count,
PDESCRIPTOR64 Descs)
{
// Nothing to do.
}
void
MachineInfo::ConvertExdiContextFromSpecial
(PCROSS_PLATFORM_KSPECIAL_REGISTERS Special,
PEXDI_CONTEXT ExdiContext)
{
// Nothing to do.
}
void
MachineInfo::ConvertExdiContextToSpecial
(PEXDI_CONTEXT ExdiContext,
PCROSS_PLATFORM_KSPECIAL_REGISTERS Special)
{
// Nothing to do.
}
ULONG
MachineInfo::GetSegRegNum(ULONG SegReg)
{
return 0;
}
HRESULT
MachineInfo::GetSegRegDescriptor(ULONG SegReg, PDESCRIPTOR64 Desc)
{
return E_UNEXPECTED;
}
void
MachineInfo::KdUpdateControlSet(PDBGKD_ANY_CONTROL_SET ControlSet)
{
// Nothing to do.
}
void
MachineInfo::KdSaveProcessorState(
void
)
{
m_SavedContext = m_Context;
m_SavedContextState = m_ContextState;
memcpy(m_SavedSegRegDesc, m_SegRegDesc, sizeof(m_SegRegDesc));
m_ContextState = MCTX_NONE;
g_Target->InvalidateTargetContext();
}
void
MachineInfo::KdRestoreProcessorState(
void
)
{
DBG_ASSERT(m_ContextState != MCTX_DIRTY);
m_Context = m_SavedContext;
m_ContextState = m_SavedContextState;
memcpy(m_SegRegDesc, m_SavedSegRegDesc, sizeof(m_SegRegDesc));
g_Target->InvalidateTargetContext();
}
HRESULT
MachineInfo::SetDefaultPageDirectories(ULONG Mask)
{
HRESULT Status;
ULONG i;
ULONG64 OldDirs[PAGE_DIR_COUNT];
memcpy(OldDirs, m_PageDirectories, sizeof(m_PageDirectories));
i = 0;
while (i < PAGE_DIR_COUNT)
{
// Pass on the set to machine-specific code.
if (Mask & (1 << i))
{
if ((Status = SetPageDirectory(i, 0, &i)) != S_OK)
{
memcpy(m_PageDirectories, OldDirs, sizeof(m_PageDirectories));
return Status;
}
}
else
{
i++;
}
}
// Try and validate that the new kernel page directory is
// valid by checking an address that should always
// be available.
if ((Mask & (1 << PAGE_DIR_KERNEL)) &&
IS_KERNEL_TARGET() && KdDebuggerData.PsLoadedModuleList)
{
LIST_ENTRY64 List;
if ((Status = g_Target->
ReadListEntry(this, KdDebuggerData.PsLoadedModuleList,
&List)) != S_OK)
{
// This page directory doesn't seem valid so restore
// the previous setting and fail.
memcpy(m_PageDirectories, OldDirs, sizeof(m_PageDirectories));
}
}
return Status;
}
HRESULT
MachineInfo::NewBreakpoint(DebugClient* Client,
ULONG Type,
ULONG Id,
Breakpoint** RetBp)
{
return E_NOINTERFACE;
}
void
MachineInfo::InsertAllDataBreakpoints(void)
{
// Nothing to do.
}
void
MachineInfo::RemoveAllDataBreakpoints(void)
{
// Nothing to do.
}
ULONG
MachineInfo::IsBreakpointOrStepException(PEXCEPTION_RECORD64 Record,
ULONG FirstChance,
PADDR BpAddr,
PADDR RelAddr)
{
return Record->ExceptionCode == STATUS_BREAKPOINT ?
EXBS_BREAKPOINT_ANY : EXBS_NONE;
}
void
MachineInfo::GetRetAddr(PADDR Addr)
{
DEBUG_STACK_FRAME StackFrame;
if (StackTrace(0, 0, 0, &StackFrame, 1, 0, 0, FALSE) > 0)
{
ADDRFLAT(Addr, StackFrame.ReturnOffset);
}
else
{
ErrOut("StackTrace failed\n");
ADDRFLAT(Addr, 0);
}
}
BOOL
MachineInfo::GetPrefixedSymbolOffset(ULONG64 SymOffset,
ULONG Flags,
PULONG64 PrefixedSymOffset)
{
DBG_ASSERT(m_SymPrefix == NULL);
// This routine should never be called since there's no prefix.
return FALSE;
}
HRESULT
MachineInfo::ReadDynamicFunctionTable(ULONG64 Table,
PULONG64 NextTable,
PULONG64 MinAddress,
PULONG64 MaxAddress,
PULONG64 BaseAddress,
PULONG64 TableData,
PULONG TableSize,
PWSTR OutOfProcessDll,
PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable)
{
// No dynamic function table support.
return E_UNEXPECTED;
}
PVOID
MachineInfo::FindDynamicFunctionEntry(PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE Table,
ULONG64 Address,
PVOID TableData,
ULONG TableSize)
{
// No dynamic function tables so no match.
return NULL;
}
void
MachineInfo::FlushPerExecutionCaches(void)
{
ZeroMemory(m_PageDirectories, sizeof(m_PageDirectories));
m_Translating = FALSE;
}
void
MachineInfo::FormAddr(ULONG SegOrReg, ULONG64 Off,
ULONG Flags, PADDR Address)
{
PDESCRIPTOR64 SegDesc = NULL;
DESCRIPTOR64 Desc;
Address->off = Off;
if (Flags & FORM_SEGREG)
{
ULONG SegRegNum = GetSegRegNum(SegOrReg);
if (SegRegNum)
{
Address->seg = GetReg16(SegRegNum);
}
else
{
Address->seg = 0;
}
}
else
{
Address->seg = (USHORT)SegOrReg;
}
if (Flags & FORM_VM86)
{
Address->type = ADDR_V86;
}
else if (Address->seg == 0)
{
// A segment wasn't used or segmentation doesn't exist.
Address->type = ADDR_FLAT;
}
else
{
HRESULT Status;
if (Flags & FORM_SEGREG)
{
Status = GetSegRegDescriptor(SegOrReg, &Desc);
}
else
{
Status = g_Target->
GetSelDescriptor(this, g_CurrentProcess->CurrentThread->Handle,
SegOrReg, &Desc);
}
if (Status == S_OK)
{
static USHORT MainCodeSeg = 0;
SegDesc = &Desc;
if (((Flags & FORM_CODE) && (Desc.Flags & X86_DESC_LONG_MODE)) ||
((Flags & FORM_CODE) == 0 && g_Amd64InCode64))
{
Address->type = ADDR_1664;
}
else if (Desc.Flags & X86_DESC_DEFAULT_BIG)
{
Address->type = ADDR_1632;
}
else
{
Address->type = ADDR_16;
}
if ((Flags & FORM_CODE) &&
((g_EffMachine == IMAGE_FILE_MACHINE_I386 &&
Address->type == ADDR_1632) ||
(g_EffMachine == IMAGE_FILE_MACHINE_AMD64 &&
Address->type == ADDR_1664)))
{
if ( MainCodeSeg == 0 )
{
if ( Desc.Base == 0 )
{
MainCodeSeg = Address->seg;
}
}
if ( Address->seg == MainCodeSeg )
{
Address->type = ADDR_FLAT;
}
}
}
else
{
Address->type = ADDR_16;
}
}
ComputeFlatAddress(Address, SegDesc);
}
void
MachineInfo::PrintStackFrameAddressesTitle(ULONG Flags)
{
if (!(Flags & DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY))
{
PrintMultiPtrTitle("Child-SP", 1);
}
PrintMultiPtrTitle("RetAddr", 1);
}
void
MachineInfo::PrintStackFrameAddresses(ULONG Flags,
PDEBUG_STACK_FRAME StackFrame)
{
if (!(Flags & DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY))
{
dprintf("%s ", FormatAddr64(StackFrame->StackOffset));
}
dprintf("%s ", FormatAddr64(StackFrame->ReturnOffset));
}
void
MachineInfo::PrintStackArgumentsTitle(ULONG Flags)
{
dprintf(": ");
PrintMultiPtrTitle("Args to Child", 4);
dprintf(": ");
}
void
MachineInfo::PrintStackArguments(ULONG Flags,
PDEBUG_STACK_FRAME StackFrame)
{
dprintf(": %s %s %s %s : ",
FormatAddr64(StackFrame->Params[0]),
FormatAddr64(StackFrame->Params[1]),
FormatAddr64(StackFrame->Params[2]),
FormatAddr64(StackFrame->Params[3]));
}
void
MachineInfo::PrintStackCallSiteTitle(ULONG Flags)
{
dprintf("Call Site");
}
void
MachineInfo::PrintStackCallSite(ULONG Flags,
PDEBUG_STACK_FRAME StackFrame,
CHAR SymBuf[],
DWORD64 Displacement,
USHORT StdCallArgs)
{
if (*SymBuf)
{
dprintf("%s", SymBuf);
if (!(Flags & DEBUG_STACK_PARAMETERS) ||
!ShowFunctionParameters(StackFrame, SymBuf, Displacement))
{
// We dont see the parameters
}
if (Displacement)
{
dprintf("+");
}
}
if (Displacement || !*SymBuf)
{
dprintf("0x%s", FormatDisp64(Displacement));
}
}
void
MachineInfo::PrintStackNonvolatileRegisters(ULONG Flags,
PDEBUG_STACK_FRAME StackFrame,
PCROSS_PLATFORM_CONTEXT Context,
ULONG FrameNum)
{
// Empty base implementation.
}
//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------
HRESULT
InitializeMachines(ULONG TargetMachine)
{
HRESULT Status;
ULONG i;
if (DbgKdApi64 != (g_SystemVersion > NT_SVER_NT4))
{
WarnOut("Debug API version does not match system version\n");
}
if (IsImageMachineType64(TargetMachine) && !DbgKdApi64)
{
WarnOut("64-bit machine not using 64-bit API\n");
}
memset(g_AllMachines, 0, sizeof(g_AllMachines));
// There are several different X86 machines due to
// the emulations available on various systems and CPUs.
switch(TargetMachine)
{
case IMAGE_FILE_MACHINE_IA64:
g_AllMachines[MACHIDX_I386] = &g_X86OnIa64Machine;
break;
default:
g_AllMachines[MACHIDX_I386] = &g_X86Machine;
break;
}
g_AllMachines[MACHIDX_ALPHA] = &g_Axp32Machine;
g_AllMachines[MACHIDX_AXP64] = &g_Axp64Machine;
g_AllMachines[MACHIDX_IA64] = &g_Ia64Machine;
g_AllMachines[MACHIDX_AMD64] = &g_Amd64Machine;
g_TargetMachineType = TargetMachine;
g_TargetMachine = MachineTypeInfo(TargetMachine);
ZeroMemory(&g_InitProcessorId, sizeof(g_InitProcessorId));
for (i = 0; i < MACHIDX_COUNT; i++)
{
DBG_ASSERT(g_AllMachines[i] != NULL);
if ((Status = g_AllMachines[i]->InitializeConstants()) != S_OK)
{
return Status;
}
}
if (!IS_TARGET_SET())
{
return S_OK;
}
for (i = 0; i < MACHIDX_COUNT; i++)
{
if ((Status = g_AllMachines[i]->InitializeForTarget()) != S_OK)
{
return Status;
}
}
if (g_TargetMachineType == IMAGE_FILE_MACHINE_UNKNOWN)
{
return S_OK;
}
// Get the base processor ID for determing what
// kind of features a processor supports. The
// assumption is that the processors in a machine
// will be similar enough that retrieving this
// for one processor is sufficient.
// If this fails we continue on without a processor ID.
if (!IS_DUMP_TARGET())
{
g_Target->GetProcessorId(0, &g_InitProcessorId);
}
for (i = 0; i < MACHIDX_COUNT; i++)
{
if ((Status = g_AllMachines[i]->InitializeForProcessor()) != S_OK)
{
return Status;
}
}
return S_OK;
}
void
SetEffMachine(ULONG Machine, BOOL Notify)
{
BOOL Changed = g_EffMachine != Machine;
if (Changed &&
g_EffMachine != IMAGE_FILE_MACHINE_UNKNOWN &&
g_EffMachine != g_TargetMachineType)
{
// If the previous machine was not the target machine
// it may be an emulated machine that uses the
// target machine's context. In that case we need to
// make sure that any dirty registers it has get flushed
// so that if the new effective machine is the target
// machine it'll show changes due to changes through
// the emulated machine.
if (g_Machine->SetContext() != S_OK)
{
// Error already displayed.
return;
}
}
g_EffMachine = Machine;
g_EffMachineIndex = MachineTypeIndex(Machine);
DBG_ASSERT(g_EffMachineIndex <= MACHIDX_COUNT);
g_Machine = g_AllMachines[g_EffMachineIndex];
if (Changed && Notify)
{
NotifyChangeEngineState(DEBUG_CES_EFFECTIVE_PROCESSOR,
g_EffMachine, TRUE);
}
}
MachineIndex
MachineTypeIndex(ULONG Machine)
{
switch(Machine)
{
case IMAGE_FILE_MACHINE_I386:
return MACHIDX_I386;
case IMAGE_FILE_MACHINE_ALPHA:
return MACHIDX_ALPHA;
case IMAGE_FILE_MACHINE_AXP64:
return MACHIDX_AXP64;
case IMAGE_FILE_MACHINE_IA64:
return MACHIDX_IA64;
case IMAGE_FILE_MACHINE_AMD64:
return MACHIDX_AMD64;
default:
return MACHIDX_COUNT;
}
}
void
CacheReportInstructions(ULONG64 Pc, ULONG Count, PUCHAR Stream)
{
// There was a long-standing bug in the kernel
// where it didn't properly remove all breakpoints
// present in the instruction stream reported to
// the debugger. If this kernel suffers from the
// problem just ignore the stream contents.
if (Count == 0 || g_TargetBuildNumber < 2300)
{
return;
}
g_VirtualCache.Add(Pc, Stream, Count);
}
void
FlushMachinePerExecutionCaches(void)
{
ULONG i;
for (i = 0; i < MACHIDX_COUNT; i++)
{
g_AllMachines[i]->FlushPerExecutionCaches();
}
}
//----------------------------------------------------------------------------
//
// Common code and constants.
//
//----------------------------------------------------------------------------
/* OutputHex - output hex value
*
* Purpose:
* Output the value in outvalue into the buffer
* pointed by *pBuf. The value may be signed
* or unsigned depending on the value fSigned.
*
* Input:
* outvalue - value to output
* length - length in digits
* fSigned - TRUE if signed else FALSE
*
* Output:
* None.
*
***********************************************************************/
UCHAR g_HexDigit[16] =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
void
MachineInfo::BufferHex (
ULONG64 outvalue,
ULONG length,
BOOL fSigned
)
{
UCHAR digit[32];
LONG index = 0;
DBG_ASSERT(length <= 32);
if (fSigned && (LONGLONG)outvalue < 0)
{
*m_Buf++ = '-';
outvalue = - (LONGLONG)outvalue;
}
do
{
digit[index++] = g_HexDigit[outvalue & 0xf];
outvalue >>= 4;
}
while ((fSigned && outvalue) || (!fSigned && index < (LONG)length));
while (--index >= 0)
{
*m_Buf++ = digit[index];
}
}
/* BlankFill - blank-fill buffer
*
* Purpose:
* To fill the buffer at *pBuf with blanks until
* position count is reached.
*
* Input:
* None.
*
* Output:
* None.
*
***********************************************************************/
void
MachineInfo::BufferBlanks(ULONG count)
{
do
{
*m_Buf++ = ' ';
}
while (m_Buf < m_BufStart + count);
}
/* OutputString - output string
*
* Purpose:
* Copy the string into the buffer pointed by pBuf.
*
* Input:
* *pStr - pointer to string
*
* Output:
* None.
*
***********************************************************************/
void
MachineInfo::BufferString(PCSTR String)
{
while (*String)
{
*m_Buf++ = *String++;
}
}
void
MachineInfo::PrintMultiPtrTitle(const CHAR* Title, USHORT PtrNum)
{
size_t PtrLen = (strlen(FormatAddr64(0)) + 1) * PtrNum;
size_t TitleLen = strlen(Title);
if (PtrLen < TitleLen)
{
// Extremly rare case so keep it simple while slow
for (size_t i = 0; i < PtrLen - 1; ++i)
{
dprintf("%c", Title[i]);
}
dprintf(" ");
}
else
{
dprintf(Title);
if (PtrLen > TitleLen)
{
char Format[16];
_snprintf(Format, sizeof(Format) - 1,
"%% %ds", PtrLen - TitleLen);
dprintf(Format, "");
}
}
}
CHAR g_F0[] = "f0";
CHAR g_F1[] = "f1";
CHAR g_F2[] = "f2";
CHAR g_F3[] = "f3";
CHAR g_F4[] = "f4";
CHAR g_F5[] = "f5";
CHAR g_F6[] = "f6";
CHAR g_F7[] = "f7";
CHAR g_F8[] = "f8";
CHAR g_F9[] = "f9";
CHAR g_F10[] = "f10";
CHAR g_F11[] = "f11";
CHAR g_F12[] = "f12";
CHAR g_F13[] = "f13";
CHAR g_F14[] = "f14";
CHAR g_F15[] = "f15";
CHAR g_F16[] = "f16";
CHAR g_F17[] = "f17";
CHAR g_F18[] = "f18";
CHAR g_F19[] = "f19";
CHAR g_F20[] = "f20";
CHAR g_F21[] = "f21";
CHAR g_F22[] = "f22";
CHAR g_F23[] = "f23";
CHAR g_F24[] = "f24";
CHAR g_F25[] = "f25";
CHAR g_F26[] = "f26";
CHAR g_F27[] = "f27";
CHAR g_F28[] = "f28";
CHAR g_F29[] = "f29";
CHAR g_F30[] = "f30";
CHAR g_F31[] = "f31";
CHAR g_R0[] = "r0";
CHAR g_R1[] = "r1";
CHAR g_R2[] = "r2";
CHAR g_R3[] = "r3";
CHAR g_R4[] = "r4";
CHAR g_R5[] = "r5";
CHAR g_R6[] = "r6";
CHAR g_R7[] = "r7";
CHAR g_R8[] = "r8";
CHAR g_R9[] = "r9";
CHAR g_R10[] = "r10";
CHAR g_R11[] = "r11";
CHAR g_R12[] = "r12";
CHAR g_R13[] = "r13";
CHAR g_R14[] = "r14";
CHAR g_R15[] = "r15";
CHAR g_R16[] = "r16";
CHAR g_R17[] = "r17";
CHAR g_R18[] = "r18";
CHAR g_R19[] = "r19";
CHAR g_R20[] = "r20";
CHAR g_R21[] = "r21";
CHAR g_R22[] = "r22";
CHAR g_R23[] = "r23";
CHAR g_R24[] = "r24";
CHAR g_R25[] = "r25";
CHAR g_R26[] = "r26";
CHAR g_R27[] = "r27";
CHAR g_R28[] = "r28";
CHAR g_R29[] = "r29";
CHAR g_R30[] = "r30";
CHAR g_R31[] = "r31";