From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=65.50.211.136; helo=mail.zytor.com; envelope-from=pcacjr@zytor.com; receiver=edk2-devel@lists.01.org Received: from mail.zytor.com (terminus.zytor.com [65.50.211.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id A0F0A220D4C0A for ; Tue, 14 Nov 2017 07:28:21 -0800 (PST) Received: from [IPv6:2804:7f4:c480:4fed::1] ([IPv6:2804:7f4:c480:4fed:0:0:0:1]) (authenticated bits=0) by mail.zytor.com (8.15.2/8.15.2) with ESMTPSA id vAEFUBDY027616 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NO); Tue, 14 Nov 2017 07:30:13 -0800 To: Andrew Fish Cc: edk2-devel@lists.01.org, Laszlo Ersek , Eric Dong References: <00e14f85d93a2e81ab008f32020f3048fe4857fb.1510662518.git.pcacjr@zytor.com> From: Paulo Alcantara Message-ID: <60b6223a-f88a-5e56-e49a-31f2b1d94278@zytor.com> Date: Tue, 14 Nov 2017 13:30:10 -0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.4.0 MIME-Version: 1.0 In-Reply-To: Subject: Re: [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 Nov 2017 15:28:21 -0000 Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit Hi Andrew, On 14/11/2017 12:01, Andrew Fish wrote: C> Paulo, > > Cool feature. How does this code deal with VC++ that code does not store the frame pointer and requires symbols to unwind. I haven't tested in with MSVC, so I'd hope to get some help from Intel's guys to help supporting and testing it :-) Regarding the symbols, I performed some tests by writing a userspace PE/COFF application [1] and tried to: (a) handle the RUNTIME_FUNCTION entries in exception table (.pdata section) to find the function starting address and then its respective symbol name in COFF symbol table. (b) Walk through UNWIND_INFO entries in .xdata section to figure out which CPU register a function used as a frame pointer or if it didn't use any at all. The problem I had with (a) was that the COFF symbol table is not mapped directory into the image's address space -- that is, the "PointerToSymbolTable" in File Header *should* be 0 as per PE/COFF format specific when handling an executable file rather a object file. Additionally, if it's non-zero, it contains a file offset rather than a RVA address, so impossible to parse it at runtime. In (b), I realized that the CodeView format data in debug directory should be kept in a separate file (PDB file?) and they aren't mapped into image's address space as well. I don't have so much experience with PE/COFF format, so please correct me if I'm mistaken. IMHO, there should be exist a function like AsmGetFrameAddress() and/or AsmGetStackAddress() which would get implemented for both GCC and MSVC toolchains. Thank you very much for your comments! > Also on the page fault you can print the fault address since it is in CR2. Good point! We should also do that. > It should be possible to post process the text file and make a symbolicated backtrace. Yes. Thanks! Paulo > > Thanks, > > Andrew Fish > >> On Nov 14, 2017, at 4:47 AM, Paulo Alcantara wrote: >> >> This patch adds stack trace support during a X64 CPU exception. >> >> It will dump out back trace, stack contents as well as image module >> names that were part of the call stack. >> >> Contributed-under: TianoCore Contribution Agreement 1.1 >> Cc: Eric Dong >> Cc: Laszlo Ersek >> Signed-off-by: Paulo Alcantara >> --- >> UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++- >> 1 file changed, 342 insertions(+), 2 deletions(-) >> >> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c >> index 65f0cff680..7048247be3 100644 >> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c >> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c >> @@ -14,6 +14,11 @@ >> >> #include "CpuExceptionCommon.h" >> >> +// >> +// Unknown PDB file name >> +// >> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????"; >> + >> /** >> Return address map of exception handler template so that C code can generate >> exception tables. >> @@ -243,6 +248,325 @@ DumpCpuContext ( >> } >> >> /** >> + Dump stack contents. >> + >> + @param[in] ImageBase Base address of PE/COFF image. >> + @param[out] PdbAbsoluteFilePath Absolute path of PDB file. >> + @param[out] PdbFileName File name of PDB file. >> +**/ >> +STATIC >> +VOID >> +GetPdbFileName ( >> + IN UINTN ImageBase, >> + OUT CHAR8 **PdbAbsoluteFilePath, >> + OUT CHAR8 **PdbFileName >> + ) >> +{ >> + VOID *PdbPointer; >> + CHAR8 *Str; >> + >> + // >> + // Get PDB file name from PE/COFF image >> + // >> + PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase); >> + if (PdbPointer == NULL) { >> + // >> + // No PDB file name found. Set it to an unknown file name. >> + // >> + *PdbFileName = (CHAR8 *)mUnknownPdbFileName; >> + if (PdbAbsoluteFilePath != NULL) { >> + *PdbAbsoluteFilePath = NULL; >> + } >> + } else { >> + // >> + // Get file name portion out of PDB file in PE/COFF image >> + // >> + Str = (CHAR8 *)((UINTN)PdbPointer + >> + AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str); >> + for (; *Str != '/' && *Str != '\\'; Str--) { >> + ; >> + } >> + >> + // >> + // Set PDB file name (also skip trailing path separator: '/' or '\\') >> + // >> + *PdbFileName = Str + 1; >> + >> + if (PdbAbsoluteFilePath != NULL) { >> + // >> + // Set absolute file path of PDB file >> + // >> + *PdbAbsoluteFilePath = PdbPointer; >> + } >> + } >> +} >> + >> +/** >> + Dump stack contents. >> + >> + @param[in] CurrentRsp Current stack pointer address. >> + @param[in] UnwondStacksCount Count of unwond stack frames. >> +**/ >> +STATIC >> +VOID >> +DumpStackContents ( >> + IN UINT64 CurrentRsp, >> + IN INTN UnwondStacksCount >> + ) >> +{ >> + if (UnwondStacksCount == 0) { >> + return; >> + } >> + >> + // >> + // Dump out stack contents >> + // >> + InternalPrintMessage ("\nStack dump:\n"); >> + while (UnwondStacksCount-- > 0) { >> + InternalPrintMessage ( >> + "0x%016lx: %016lx %016lx\n", >> + CurrentRsp, >> + *(UINT64 *)CurrentRsp, >> + *(UINT64 *)((UINTN)CurrentRsp + 8) >> + ); >> + >> + // >> + // As per Microsoft x64 ABI, the stack pointer must be aligned on a 16 byte >> + // boundary. >> + // >> + CurrentRsp = CurrentRsp + 16; >> + } >> +} >> + >> +/** >> + Dump all image module names from call stack. >> + >> + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. >> +**/ >> +STATIC >> +VOID >> +DumpImageModuleNames ( >> + IN EFI_SYSTEM_CONTEXT SystemContext >> + ) >> +{ >> + EFI_STATUS Status; >> + UINT64 Rip; >> + UINTN ImageBase; >> + VOID *EntryPoint; >> + CHAR8 *PdbAbsoluteFilePath; >> + CHAR8 *PdbFileName; >> + UINT64 Rbp; >> + >> + // >> + // Set current RIP address >> + // >> + Rip = SystemContext.SystemContextX64->Rip; >> + >> + // >> + // Set current frame pointer address >> + // >> + Rbp = SystemContext.SystemContextX64->Rbp; >> + >> + // >> + // Get initial PE/COFF image base address from current RIP >> + // >> + ImageBase = PeCoffSearchImageBase (Rip); >> + if (ImageBase == 0) { >> + InternalPrintMessage ("!!!! Could not find image module names. !!!!"); >> + return; >> + } >> + >> + // >> + // Get initial PE/COFF image's entry point >> + // >> + Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint); >> + if (EFI_ERROR (Status)) { >> + EntryPoint = NULL; >> + } >> + >> + // >> + // Get file name and absolute path of initial PDB file >> + // >> + GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName); >> + >> + // >> + // Print out initial image module name (if any) >> + // >> + if (PdbAbsoluteFilePath != NULL) { >> + InternalPrintMessage ( >> + "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n", >> + PdbFileName, >> + ImageBase, >> + (UINTN)EntryPoint >> + ); >> + InternalPrintMessage ("%a\n", PdbAbsoluteFilePath); >> + } >> + >> + // >> + // Walk through call stack and find next module names >> + // >> + for (;;) { >> + // >> + // Set RIP with return address from current stack frame >> + // >> + Rip = *(UINT64 *)((UINTN)Rbp + 8); >> + >> + // >> + // Check if RIP is within another PE/COFF image base address >> + // >> + if (Rip < ImageBase) { >> + // >> + // Search for the respective PE/COFF image based on RIP >> + // >> + ImageBase = PeCoffSearchImageBase (Rip); >> + if (ImageBase == 0) { >> + // >> + // Stop stack trace >> + // >> + break; >> + } >> + >> + // >> + // Get PE/COFF image's entry point >> + // >> + Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint); >> + if (EFI_ERROR (Status)) { >> + EntryPoint = NULL; >> + } >> + >> + // >> + // Get file name and absolute path of PDB file >> + // >> + GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName); >> + >> + // >> + // Print out image module name (if any) >> + // >> + if (PdbAbsoluteFilePath != NULL) { >> + InternalPrintMessage ( >> + "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n", >> + PdbFileName, >> + ImageBase, >> + (UINTN)EntryPoint >> + ); >> + InternalPrintMessage ("%a\n", PdbAbsoluteFilePath); >> + } >> + } >> + >> + // >> + // Unwind the stack >> + // >> + Rbp = *(UINT64 *)(UINTN)Rbp; >> + } >> +} >> + >> +/** >> + Dump stack trace. >> + >> + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. >> + @param[out] UnwondStacksCount Count of unwond stack frames. >> +**/ >> +STATIC >> +VOID >> +DumpStackTrace ( >> + IN EFI_SYSTEM_CONTEXT SystemContext, >> + OUT INTN *UnwondStacksCount >> + ) >> +{ >> + UINT64 Rip; >> + UINT64 Rbp; >> + UINTN ImageBase; >> + CHAR8 *PdbFileName; >> + >> + // >> + // Initialize count of unwond stacks >> + // >> + *UnwondStacksCount = 0; >> + >> + // >> + // Set current RIP address >> + // >> + Rip = SystemContext.SystemContextX64->Rip; >> + >> + // >> + // Set current frame pointer address >> + // >> + Rbp = SystemContext.SystemContextX64->Rbp; >> + >> + // >> + // Get initial PE/COFF image base address from current RIP >> + // >> + ImageBase = PeCoffSearchImageBase (Rip); >> + if (ImageBase == 0) { >> + InternalPrintMessage ("!!!! Could not find backtrace information. !!!!"); >> + return; >> + } >> + >> + // >> + // Get PDB file name from initial PE/COFF image >> + // >> + GetPdbFileName (ImageBase, NULL, &PdbFileName); >> + >> + // >> + // Print out back trace >> + // >> + InternalPrintMessage ("\nBack trace:\n"); >> + >> + for (;;) { >> + // >> + // Print stack frame in the following format: >> + // >> + // # @ + (RBP) in [ | ????] >> + // >> + InternalPrintMessage ( >> + "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n", >> + *UnwondStacksCount, >> + Rip, >> + ImageBase, >> + Rip - ImageBase - 1, >> + Rbp, >> + PdbFileName >> + ); >> + >> + // >> + // Set RIP with return address from current stack frame >> + // >> + Rip = *(UINT64 *)((UINTN)Rbp + 8); >> + >> + // >> + // Check if RIP is within another PE/COFF image base address >> + // >> + if (Rip < ImageBase) { >> + // >> + // Search for the respective PE/COFF image based on RIP >> + // >> + ImageBase = PeCoffSearchImageBase (Rip); >> + if (ImageBase == 0) { >> + // >> + // Stop stack trace >> + // >> + break; >> + } >> + >> + // >> + // Get PDB file name >> + // >> + GetPdbFileName (ImageBase, NULL, &PdbFileName); >> + } >> + >> + // >> + // Unwind the stack >> + // >> + Rbp = *(UINT64 *)(UINTN)Rbp; >> + >> + // >> + // Increment count of unwond stacks >> + // >> + (*UnwondStacksCount)++; >> + } >> +} >> + >> +/** >> Display CPU information. >> >> @param ExceptionType Exception type. >> @@ -254,9 +578,25 @@ DumpImageAndCpuContent ( >> IN EFI_SYSTEM_CONTEXT SystemContext >> ) >> { >> + INTN UnwondStacksCount; >> + >> + // >> + // Dump CPU context >> + // >> DumpCpuContext (ExceptionType, SystemContext); >> + >> + // >> + // Dump stack trace >> + // >> + DumpStackTrace (SystemContext, &UnwondStacksCount); >> + >> + // >> + // Dump image module names >> + // >> + DumpImageModuleNames (SystemContext); >> + >> // >> - // Dump module image base and module entry point by RIP >> + // Dump stack contents >> // >> - DumpModuleImageInfo (SystemContext.SystemContextX64->Rip); >> + DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount); >> } >> -- >> 2.11.0 >> >> _______________________________________________ >> edk2-devel mailing list >> edk2-devel@lists.01.org >> https://lists.01.org/mailman/listinfo/edk2-devel >