/*++ Copyright (c) 2001 Microsoft Corporation Module Name: uwdump.c Abstract: This module implements a program which dumps the function table and unwind data for a specified executable file. It is an AMD64 specific program. Author: David N. Cutler (davec) 6-Feb-2001 Environment: User mode. Revision History: None. --*/ #include #include #include #include #include #include // // Define AMD64 exception handling structures and function prototypes. // // Define unwind operation codes. // typedef enum _UNWIND_OP_CODES { UWOP_PUSH_NONVOL = 0, UWOP_ALLOC_LARGE, UWOP_ALLOC_SMALL, UWOP_SET_FPREG, UWOP_SAVE_NONVOL, UWOP_SAVE_NONVOL_FAR, UWOP_SAVE_XMM, UWOP_SAVE_XMM_FAR, UWOP_SAVE_XMM128, UWOP_SAVE_XMM128_FAR, UWOP_PUSH_MACHFRAME } UNWIND_OP_CODES, *PUNWIND_OP_CODES; // // Define unwind code structure. // typedef union _UNWIND_CODE { struct { UCHAR CodeOffset; UCHAR UnwindOp : 4; UCHAR OpInfo : 4; }; USHORT FrameOffset; } UNWIND_CODE, *PUNWIND_CODE; // // Define unwind information flags. // #define UNW_FLAG_NHANDLER 0x0 #define UNW_FLAG_EHANDLER 0x1 #define UNW_FLAG_UHANDLER 0x2 #define UNW_FLAG_CHAININFO 0x4 // // Define unwind information structure. // typedef struct _UNWIND_INFO { UCHAR Version : 3; UCHAR Flags : 5; UCHAR SizeOfProlog; UCHAR CountOfCodes; UCHAR FrameRegister : 4; UCHAR FrameOffset : 4; UNWIND_CODE UnwindCode[1]; // // The unwind codes are followed by an optional DWORD aligned field that // contains the exception handler address or the address of chained unwind // information. If an exception handler address is specified, then it is // followed by the language specified exception handler data. // // union { // ULONG ExceptionHandler; // ULONG FunctionEntry; // }; // // ULONG ExceptionData[]; // } UNWIND_INFO, *PUNWIND_INFO; // // Define function table entry - a function table entry is generated for // each frame function. // typedef struct _RUNTIME_FUNCTION { ULONG BeginAddress; ULONG EndAddress; ULONG UnwindData; } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; // // Scope table structure definition. // typedef struct _SCOPE_ENTRY { ULONG BeginAddress; ULONG EndAddress; ULONG HandlerAddress; ULONG JumpTarget; } SCOPE_ENTRY; typedef struct _SCOPE_TABLE { ULONG Count; struct { ULONG BeginAddress; ULONG EndAddress; ULONG HandlerAddress; ULONG JumpTarget; } ScopeRecord[1]; } SCOPE_TABLE, *PSCOPE_TABLE; // // Define register names. // PCHAR Register[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xxm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xxm14", "xmm15"}; // // Define the sector size and header buffer. // #define SECTOR_SIZE 512 CHAR LocalBuffer[SECTOR_SIZE * 2]; // // Define input file stream. // FILE * InputFile; // // This gobal indicates whether we are processing an executable or an obj. // BOOLEAN IsObj; // // Define forward referenced prototypes. // VOID DumpPdata ( IN ULONG NumberOfSections, IN PIMAGE_SECTION_HEADER SectionHeaders, IN PIMAGE_SECTION_HEADER PdataHeader ); VOID DumpUData ( IN ULONG NumberOfSections, IN PIMAGE_SECTION_HEADER SectionHeaders, IN ULONG Virtual ); PIMAGE_SECTION_HEADER FindSectionHeader ( IN ULONG NumberOfSections, IN PIMAGE_SECTION_HEADER SectionHeaders, IN PCHAR SectionName ); VOID ReadData ( IN ULONG Position, OUT PVOID Buffer, IN ULONG Count ); USHORT ReadWord ( IN ULONG Position ); ULONG ReadDword ( IN ULONG Position ); // // Main program. // int __cdecl main( int argc, char **argv ) { PIMAGE_FILE_HEADER FileHeader; PCHAR FileName; ULONG Index; PIMAGE_NT_HEADERS NtHeaders; ULONG NumberOfSections; PIMAGE_SECTION_HEADER PDataHeader; PIMAGE_SECTION_HEADER SectionHeaders; if (argc < 2) { printf("no executable file specified\n"); } else { // // Open the input file. // FileName = argv[1]; InputFile = fopen(FileName, "rb"); if (InputFile != NULL) { // // Read the file header. // if (fread(&LocalBuffer[0], sizeof(CHAR), SECTOR_SIZE * 2, InputFile) == (SECTOR_SIZE * 2)) { // // Get the NT header address. // NtHeaders = RtlImageNtHeader(&LocalBuffer[0]); if (NtHeaders != NULL) { IsObj = FALSE; FileHeader = &NtHeaders->FileHeader; } else { IsObj = TRUE; FileHeader = (PIMAGE_FILE_HEADER)LocalBuffer; } printf("FileHeader->Machine %d\n",FileHeader->Machine); if (FileHeader->Machine == IMAGE_FILE_MACHINE_AMD64) { // // Look up the .pdata section. // NumberOfSections = FileHeader->NumberOfSections; SectionHeaders = (PIMAGE_SECTION_HEADER)((PUCHAR)(FileHeader + 1) + FileHeader->SizeOfOptionalHeader); PDataHeader = FindSectionHeader(NumberOfSections, SectionHeaders, ".pdata"); if (PDataHeader != NULL) { printf("Dumping Unwind Information for file %s\n\n", FileName); DumpPdata(NumberOfSections, &SectionHeaders[0], PDataHeader); return 0; } printf("no .pdata section in image\n"); } else { printf("the specified file is not an amd64 executable\n"); } } else { printf("premature end of file encountered on input file\n"); } fclose(InputFile); } else { printf("can't open input file %s\n", FileName); } } return 0; } VOID DumpPdata ( IN ULONG NumberOfSections, IN PIMAGE_SECTION_HEADER SectionHeaders, IN PIMAGE_SECTION_HEADER PdataHeader ) { RUNTIME_FUNCTION Entry; ULONG Number; ULONG Offset; ULONG SectionSize; // // Dump a .pdata function table entry and then dump the associated // unwind data. // if (IsObj == FALSE) { SectionSize = PdataHeader->Misc.VirtualSize; } else { SectionSize = PdataHeader->SizeOfRawData; } Number = 1; Offset = 0; do { // // Read and dump the next function table entry. // ReadData(PdataHeader->PointerToRawData + Offset, &Entry, sizeof(RUNTIME_FUNCTION)); printf(".pdata entry %d 0x%08lX 0x%08lX\n", Number, Entry.BeginAddress, Entry.EndAddress); // // Dump the unwind data assoicated with the function table entry. // DumpUData(NumberOfSections, SectionHeaders, Entry.UnwindData); // // Increment the entry number and update the offset to the next // function table entry. // Number += 1; Offset += sizeof(RUNTIME_FUNCTION); } while (Offset < SectionSize); // // Function offset and size of raw data should be equal if there is // the correct amount of data in the .pdata section. // if (Offset != SectionSize) { printf("incorrect size of raw data in .pdata, 0x%lx\n", PdataHeader->SizeOfRawData); } return; } VOID DumpUData ( IN ULONG NumberOfSections, IN PIMAGE_SECTION_HEADER SectionHeaders, IN ULONG Virtual ) { ULONG Allocation; ULONG Count; ULONG Displacement; ULONG FrameOffset = 0; ULONG FrameRegister = 0; ULONG Handler; ULONG Index; ULONG Offset; SCOPE_ENTRY ScopeEntry; UNWIND_CODE UnwindCode; UNWIND_INFO UnwindInfo; PIMAGE_SECTION_HEADER XdataHeader; // // Locate the section that contains the unwind data. // printf("\n"); printf(" Unwind data: 0x%08lX\n\n", Virtual); if (IsObj == FALSE) { XdataHeader = SectionHeaders; for (Index = 0; Index < NumberOfSections; Index += 1) { if ((XdataHeader->VirtualAddress <= Virtual) && (Virtual < (XdataHeader->VirtualAddress + XdataHeader->Misc.VirtualSize))) { break; } XdataHeader += 1; } if (Index == NumberOfSections) { printf(" unwind data address outside of image\n\n"); return; } Offset = Virtual - XdataHeader->VirtualAddress + XdataHeader->PointerToRawData; } else { // // This is an .obj, so there is only one Xdata header // XdataHeader = FindSectionHeader(NumberOfSections, SectionHeaders, ".xdata"); Offset = Virtual + XdataHeader->PointerToRawData; } // // Read unwind information. // ReadData(Offset, &UnwindInfo, sizeof(UNWIND_INFO) - sizeof(UNWIND_CODE)); // // Dump unwind version. // printf(" Unwind version: %d\n", UnwindInfo.Version); // // Dump unwind flags. // printf(" Unwind Flags: "); if ((UnwindInfo.Flags & UNW_FLAG_EHANDLER) != 0) { printf("EHANDLER "); } if ((UnwindInfo.Flags & UNW_FLAG_UHANDLER) != 0) { printf("UHANDLER "); } if ((UnwindInfo.Flags & UNW_FLAG_CHAININFO) != 0) { printf("CHAININFO"); } if (UnwindInfo.Flags == 0) { printf("None"); } printf("\n"); // // Dump size of prologue. // printf(" Size of prologue: 0x%02lX\n", UnwindInfo.SizeOfProlog); // // Dump number of unwind codes. // printf(" Count of codes: %d\n", UnwindInfo.CountOfCodes); // // Dump frame register if specified. // if (UnwindInfo.FrameRegister != 0) { FrameOffset = UnwindInfo.FrameOffset * 16; FrameRegister = UnwindInfo.FrameRegister; printf(" Frame register: %s\n", Register[FrameRegister]); printf(" Frame offset: 0x%lx\n", FrameOffset); } // // Dump the unwind codes. // Offset += sizeof(UNWIND_INFO) - sizeof(UNWIND_CODE); if (UnwindInfo.CountOfCodes != 0) { printf(" Unwind codes:\n\n"); Count = UnwindInfo.CountOfCodes; do { Count -= 1; UnwindCode.FrameOffset = ReadWord(Offset); Offset += sizeof(USHORT); printf(" Code offset: 0x%02lX, ", UnwindCode.CodeOffset); switch (UnwindCode.UnwindOp) { case UWOP_PUSH_NONVOL: printf("PUSH_NONVOL, register=%s\n", Register[UnwindCode.OpInfo]); break; case UWOP_ALLOC_LARGE: Count -= 1; Allocation = ReadWord(Offset); Offset += sizeof(USHORT); if (UnwindCode.OpInfo == 0) { Allocation *= 8; } else { Count -= 1; Allocation = (Allocation << 16) + ReadWord(Offset); Offset += sizeof(USHORT); } printf("ALLOC_LARGE, size=0x%lX\n", Allocation); break; case UWOP_ALLOC_SMALL: Allocation = (UnwindCode.OpInfo * 8) + 8; printf("ALLOC_SMALL, size=0x%lX\n", Allocation); break; case UWOP_SET_FPREG: printf("SET_FPREG, register=%s, offset=0x%02lX\n", Register[FrameRegister], FrameOffset); break; case UWOP_SAVE_NONVOL: Count -= 1; Displacement = ReadWord(Offset) * 8; Offset += sizeof(USHORT); printf("SAVE_NONVOL, register=%s offset=0x%lX\n", Register[UnwindCode.OpInfo], Displacement); break; case UWOP_SAVE_NONVOL_FAR: Count -= 2; Displacement = ReadWord(Offset) << 16; Offset += sizeof(USHORT); Displacement = Displacement + ReadWord(Offset); Offset += sizeof(USHORT); printf("SAVE_NONVOL_FAR, register=%s offset=0x%lX\n", Register[UnwindCode.OpInfo], Displacement); break; case UWOP_SAVE_XMM: Count -= 1; Displacement = ReadWord(Offset) * 8; Offset += sizeof(USHORT); printf("SAVE_XMM, register=%s offset=0x%lX\n", Register[UnwindCode.OpInfo + 16], Displacement); break; case UWOP_SAVE_XMM_FAR: Count -= 2; Displacement = ReadWord(Offset) << 16; Offset += sizeof(USHORT); Displacement = Displacement + ReadWord(Offset); Offset += sizeof(USHORT); printf("SAVE_XMM_FAR, register=%s offset=0x%lX\n", Register[UnwindCode.OpInfo + 16], Displacement); break; case UWOP_SAVE_XMM128: Count -= 1; Displacement = ReadWord(Offset) * 16; Offset += sizeof(USHORT); printf("SAVE_XMM128, register=%s offset=0x%lX\n", Register[UnwindCode.OpInfo + 16], Displacement); break; case UWOP_SAVE_XMM128_FAR: Count -= 2; Displacement = ReadWord(Offset) << 16; Offset += sizeof(USHORT); Displacement = Displacement + ReadWord(Offset); Offset += sizeof(USHORT); printf("SAVE_XMM128_FAR, register=%s offset=0x%lX\n", Register[UnwindCode.OpInfo + 16], Displacement); break; case UWOP_PUSH_MACHFRAME: if (UnwindCode.OpInfo == 0) { printf("PUSH_MACHFRAME without error code\n"); } else { printf("PUSH_MACHFRAME with error code\n"); } break; } } while (Count != 0); } // // Dump exception data if there is an excpetion or termination // handler. // if (((UnwindInfo.Flags & UNW_FLAG_EHANDLER) != 0) || ((UnwindInfo.Flags & UNW_FLAG_UHANDLER) != 0)) { if ((UnwindInfo.CountOfCodes & 1) != 0) { Offset += sizeof(USHORT); } Handler = ReadDword(Offset); Offset += sizeof(ULONG); Count = ReadDword(Offset); Offset += sizeof(ULONG); printf("\n"); printf(" Language specific handler: 0x%08lX\n", Handler); printf(" Count of scope table entries: %d\n\n", Count); if (Count != 0) { printf(" Begin End Handler Target\n"); do { ReadData(Offset, &ScopeEntry, sizeof(SCOPE_ENTRY)); printf(" 0x%08lX 0x%08lX 0x%08lX 0x%08lX\n", ScopeEntry.BeginAddress, ScopeEntry.EndAddress, ScopeEntry.HandlerAddress, ScopeEntry.JumpTarget); Count -= 1; Offset += sizeof(SCOPE_ENTRY); } while (Count != 0); } } printf("\n"); return; } PIMAGE_SECTION_HEADER FindSectionHeader ( IN ULONG NumberOfSections, IN PIMAGE_SECTION_HEADER SectionHeaders, IN PCHAR SectionName ) { ULONG RemainingSections; PIMAGE_SECTION_HEADER SectionHeader; SectionHeader = SectionHeaders; RemainingSections = NumberOfSections; while (RemainingSections > 0) { if (strncmp(SectionHeader->Name, SectionName, IMAGE_SIZEOF_SHORT_NAME) == 0) { return SectionHeader; } RemainingSections -= 1; SectionHeader += 1; } return NULL; } VOID ReadData ( IN ULONG Position, OUT PVOID Buffer, IN ULONG Count ) { if (fseek(InputFile, Position, SEEK_SET) == 0) { if (fread((PCHAR)Buffer, 1, Count, InputFile) == Count) { return; } } printf("premature end of file encounterd on inpout file\n"); exit(0); } USHORT ReadWord ( IN ULONG Position ) { USHORT Buffer; ReadData(Position, &Buffer, sizeof(USHORT)); return Buffer; } ULONG ReadDword ( IN ULONG Position ) { ULONG Buffer; ReadData(Position, &Buffer, sizeof(ULONG)); return Buffer; }