/*++ Copyright (c) 1997 Microsoft Corporation Module Name: linesym.c Abstract: Source file and line support. Author: Drew Bliss (drewb) 07-07-1997 Environment: User Mode --*/ #include #include #include #include #include "private.h" #include "symbols.h" #include "globals.h" // private version of qsort used to avoid compat problems on NT4 and win2k. // code is published from base\crts extern void __cdecl dbg_qsort(void *, size_t, size_t, int (__cdecl *) (const void *, const void *)); // #define DBG_LINES // #define DBG_COFF_LINES // #define DBG_ADDR_SEARCH BOOL diaAddLinesForAllMod( PMODULE_ENTRY mi ); #if defined(DBG_LINES) || defined(DBG_COFF_LINES) || defined(DBG_ADDR_SEARCH) void __cdecl DbgOut(PCSTR Format, ...) { char Buf[512]; va_list Args; va_start(Args, Format); _vsnprintf(Buf, sizeof(Buf), Format, Args); va_end(Args); OutputDebugStringA(Buf); } #endif BOOL sci2lline( PMODULE_ENTRY mi, PSRCCODEINFO sci, PIMAGEHLP_LINE64 line64) { assert(mi && line64 && sci); line64->Address = sci->Address; line64->Key = sci->Key; line64->LineNumber = sci->LineNumber; line64->FileName = mi->SrcFile; strcpy(line64->FileName, sci->FileName); // SECURITY: Don't know size of target buffer. return true; } void sciInit(PSRCCODEINFO sci) { ZeroMemory(sci, sizeof(SRCCODEINFO)); sci->SizeOfStruct = sizeof(SRCCODEINFO); } int __cdecl CompareLineAddresses( const void *v1, const void *v2 ) { PSOURCE_LINE Line1 = (PSOURCE_LINE)v1; PSOURCE_LINE Line2 = (PSOURCE_LINE)v2; if (Line1->Addr < Line2->Addr) { return -1; } else if (Line1->Addr > Line2->Addr) { return 1; } else { return 0; } } #ifdef DEBUG void DumpHintList( PPROCESS_ENTRY pe ) { PSOURCE_HINT sh; for (sh = pe->SourceHints; sh; sh = sh->next) dtrace("%s - %s\n", sh->filename, sh->mi->ModuleName); } #define Debug_DumpHintList DumpHintList #else #define Debug_DumpHintList #endif PSOURCE_HINT FindSourceFileInHintList( PPROCESS_ENTRY pe, char *filename ) { PSOURCE_HINT sh; assert(pe && filename && *filename); for (sh = pe->SourceHints; sh; sh = sh->next) { if (!strcmp(sh->filename, filename)) return sh; } return NULL; } BOOL AddSourceFileToHintList( PPROCESS_ENTRY pe, PMODULE_ENTRY mi, LPSTR filename ) { PSOURCE_HINT sh; PSOURCE_HINT psh; assert(pe && mi && filename && *filename); sh = FindSourceFileInHintList(pe,filename); if (sh) { sh->mi = mi; return true; } sh = (PSOURCE_HINT)MemAlloc(sizeof(SOURCE_HINT)); if (!sh) return false; sh->next = NULL; sh->filename = (LPSTR)MemAlloc(strlen(filename) + 1); if (!sh->filename) { MemFree(sh); return false; } strcpy(sh->filename, filename); // SECURITY: Don't know size of target buffer. sh->mi = mi; for (psh = pe->SourceHints; psh; psh = psh->next) { if (!psh->next) break; } if (psh) psh->next = sh; else pe->SourceHints = sh; Debug_DumpHintList(pe); return true; } void RemoveSourceForModuleFromHintList( PPROCESS_ENTRY pe, PMODULE_ENTRY mi ) { PSOURCE_HINT sh; PSOURCE_HINT psh; assert(pe && mi); psh = pe->SourceHints; for (sh = pe->SourceHints; sh; sh = sh->next) { if (sh->mi == mi) { if (psh == sh) psh = pe->SourceHints = sh->next; else psh->next = sh->next; MemFree(sh->filename); MemFree(sh); sh = psh; } psh = sh; if (!sh) return; } Debug_DumpHintList(pe); } void AddSourceEntry( PMODULE_ENTRY mi, PSOURCE_ENTRY Src ) { PSOURCE_ENTRY SrcCur; // Overlap is currently permitted. #if 0 // Check for overlap between SOURCE_ENTRY address ranges. for (SrcCur = mi->SourceFiles; SrcCur != NULL; SrcCur = SrcCur->Next) { if (!(SrcCur->MinAddr > Src->MaxAddr || SrcCur->MaxAddr < Src->MinAddr)) { DbgOut("SOURCE_ENTRY overlap between %08I64X:%08I64X " "and %08I64X:%08I64X\n", Src->MinAddr, Src->MaxAddr, SrcCur->MinAddr, SrcCur->MaxAddr); } } #endif // Sort line info by address. dbg_qsort((PVOID)Src->LineInfo, Src->Lines, sizeof(Src->LineInfo[0]), CompareLineAddresses); // Link new source information into list, sorted by address // range covered by information. for (SrcCur = mi->SourceFiles; SrcCur != NULL; SrcCur = SrcCur->Next) { if (SrcCur->MinAddr > Src->MinAddr) { break; } } Src->Next = SrcCur; if (SrcCur == NULL) { if (mi->SourceFilesTail == NULL) { mi->SourceFiles = Src; } else { mi->SourceFilesTail->Next = Src; } Src->Prev = mi->SourceFilesTail; mi->SourceFilesTail = Src; } else { if (SrcCur->Prev == NULL) { mi->SourceFiles = Src; } else { SrcCur->Prev->Next = Src; } Src->Prev = SrcCur->Prev; SrcCur->Prev = Src; } #ifdef DBG_LINES DbgOut("%08I64X %08I64X: %5d lines, '%s'\n", Src->MinAddr, Src->MaxAddr, Src->Lines, Src->File); #endif } #define IS_SECTION_SYM(Sym) \ ((Sym)->StorageClass == IMAGE_SYM_CLASS_STATIC && \ (Sym)->Type == IMAGE_SYM_TYPE_NULL && \ (Sym)->NumberOfAuxSymbols == 1) BOOL AddLinesForCoff( PMODULE_ENTRY mi, PIMAGE_SYMBOL allSymbols, DWORD numberOfSymbols, PIMAGE_LINENUMBER LineNumbers ) { PIMAGE_LINENUMBER *SecLines; BOOL Ret = false; PIMAGE_SECTION_HEADER sh; ULONG i; PIMAGE_SYMBOL Symbol; ULONG LowestPointer; // Allocate some space for per-section data. SecLines = (PIMAGE_LINENUMBER *)MemAlloc(sizeof(PIMAGE_LINENUMBER)*mi->NumSections); if (SecLines == NULL) { return false; } // // Add line number information for file groups if such // groups exist. // // First locate the lowest file offset for linenumbers. This // is necessary to be able to compute relative linenumber pointers // in split images because currently the pointers aren't updated // during stripping. sh = mi->SectionHdrs; LowestPointer = 0xffffffff; for (i = 0; i < mi->NumSections; i++, sh++) { if (sh->NumberOfLinenumbers > 0 && sh->PointerToLinenumbers != 0 && sh->PointerToLinenumbers < LowestPointer) { LowestPointer = sh->PointerToLinenumbers; } } if (LowestPointer == 0xffffffff) { goto EH_FreeSecLines; } sh = mi->SectionHdrs; for (i = 0; i < mi->NumSections; i++, sh++) { if (sh->NumberOfLinenumbers > 0 && sh->PointerToLinenumbers != 0) { SecLines[i] = (PIMAGE_LINENUMBER) (sh->PointerToLinenumbers - LowestPointer + (DWORD_PTR)LineNumbers); #ifdef DBG_COFF_LINES DbgOut("Section %d: %d lines at %08X\n", i, sh->NumberOfLinenumbers, SecLines[i]); #endif } else { SecLines[i] = NULL; } } // Look for a file symbol. Symbol = allSymbols; for (i = 0; i < numberOfSymbols; i++) { if (Symbol->StorageClass == IMAGE_SYM_CLASS_FILE) { break; } i += Symbol->NumberOfAuxSymbols; Symbol += 1+Symbol->NumberOfAuxSymbols; } // If no file symbols were found, don't attempt to add line // number information. Something could be done with the raw // linenumber info in the image (if it exists) but this probably // isn't an important enough case to worry about. while (i < numberOfSymbols) { ULONG iNextFile, iAfterFile; ULONG iCur, iSym; PIMAGE_SYMBOL SymAfterFile, CurSym; PIMAGE_AUX_SYMBOL AuxSym; ULONG Lines; ULONG MinAddr, MaxAddr; LPSTR FileName; ULONG FileNameLen; #ifdef DBG_COFF_LINES DbgOut("%3X: '%s', %X\n", i, Symbol+1, Symbol->Value); #endif // A file symbol's Value is the index of the next file symbol. // In between the two file symbols there may be static // section symbols which give line number counts for all // the line numbers in the file. // The file chain can be NULL terminated or a circular list, // in which case this code assumes the end comes when the // list wraps around. if (Symbol->Value == 0 || Symbol->Value <= i) { iNextFile = numberOfSymbols; } else { iNextFile = Symbol->Value; } // Compute the index of the first symbol after the current file // symbol. iAfterFile = i+1+Symbol->NumberOfAuxSymbols; SymAfterFile = Symbol+1+Symbol->NumberOfAuxSymbols; // Look for section symbols and count up the number of linenumber // references, the min address and the max address. CurSym = SymAfterFile; iCur = iAfterFile; Lines = 0; MinAddr = 0xffffffff; MaxAddr = 0; while (iCur < iNextFile) { DWORD Addr; if (IS_SECTION_SYM(CurSym) && SecLines[CurSym->SectionNumber-1] != NULL) { AuxSym = (PIMAGE_AUX_SYMBOL)(CurSym+1); Lines += AuxSym->Section.NumberOfLinenumbers; Addr = (ULONG)(CurSym->Value+mi->BaseOfDll); #ifdef DBG_COFF_LINES DbgOut(" Range %08X %08X, min %08X max %08X\n", Addr, Addr+AuxSym->Section.Length-1, MinAddr, MaxAddr); #endif if (Addr < MinAddr) { MinAddr = Addr; } Addr += AuxSym->Section.Length-1; if (Addr > MaxAddr) { MaxAddr = Addr; } } iCur += 1+CurSym->NumberOfAuxSymbols; CurSym += 1+CurSym->NumberOfAuxSymbols; } if (Lines > 0) { PSOURCE_ENTRY Src; PSOURCE_LINE SrcLine; ULONG iLine; // We have a filename and some linenumber information, // so create a SOURCE_ENTRY and fill it in. FileName = (LPSTR)(Symbol+1); FileNameLen = strlen(FileName); Src = (PSOURCE_ENTRY)MemAlloc(sizeof(SOURCE_ENTRY)+ sizeof(SOURCE_LINE)*Lines+ FileNameLen+1); if (Src == NULL) { goto EH_FreeSecLines; } Src->ModuleId = 0; Src->MinAddr = MinAddr; Src->MaxAddr = MaxAddr; Src->Lines = Lines; SrcLine = (PSOURCE_LINE)(Src+1); Src->LineInfo = SrcLine; // Now that we've got a place to put linenumber information, // retraverse the section symbols and grab COFF linenumbers // from the appropriate sections and format them into // the generic format. CurSym = SymAfterFile; iCur = iAfterFile; while (iCur < iNextFile) { if (IS_SECTION_SYM(CurSym) && SecLines[CurSym->SectionNumber-1] != NULL) { PIMAGE_LINENUMBER CoffLine; AuxSym = (PIMAGE_AUX_SYMBOL)(CurSym+1); CoffLine = SecLines[CurSym->SectionNumber-1]; #ifdef DBG_COFF_LINES DbgOut(" %d lines at %08X\n", AuxSym->Section.NumberOfLinenumbers, CoffLine); #endif for (iLine = 0; iLine < AuxSym->Section.NumberOfLinenumbers; iLine++) { SrcLine->Addr = CoffLine->Type.VirtualAddress+ mi->BaseOfDll; SrcLine->Line = CoffLine->Linenumber; CoffLine++; SrcLine++; } SecLines[CurSym->SectionNumber-1] = CoffLine; } iCur += 1+CurSym->NumberOfAuxSymbols; CurSym += 1+CurSym->NumberOfAuxSymbols; } // Stick file name at the very end of the data block so // it doesn't interfere with alignment. Src->File = (LPSTR)SrcLine; memcpy(Src->File, FileName, FileNameLen+1); AddSourceEntry(mi, Src); // This routine is successful as long as it adds at least // one new source entry. Ret = true; } // After the loops above iCur and CurSym refer to the next // file symbol, so update the loop counters from them. i = iCur; Symbol = CurSym; } EH_FreeSecLines: MemFree(SecLines); return Ret; } BOOL AddLinesForOmfSourceModule( PMODULE_ENTRY mi, BYTE *Base, OMFSourceModule *OmfSrcMod, PVOID PdbModule ) { BOOL Ret; ULONG iFile; Ret = false; for (iFile = 0; iFile < (ULONG)OmfSrcMod->cFile; iFile++) { OMFSourceFile *OmfSrcFile; BYTE OmfFileNameLen; LPSTR OmfFileName; PULONG OmfAddrRanges; OMFSourceLine *OmfSrcLine; ULONG iSeg; PSOURCE_ENTRY Src; PSOURCE_ENTRY Seg0Src = NULL; PSOURCE_LINE SrcLine; ULONG NameAllocLen; OmfSrcFile = (OMFSourceFile *)(Base+OmfSrcMod->baseSrcFile[iFile]); // The baseSrcLn array of offsets is immediately followed by // SVA pairs which define the address ranges for the segments. OmfAddrRanges = &OmfSrcFile->baseSrcLn[OmfSrcFile->cSeg]; // The name length and data immediately follows the address // range information. OmfFileName = (LPSTR)(OmfAddrRanges+2*OmfSrcFile->cSeg)+1; OmfFileNameLen = *(BYTE *)(OmfFileName-1); // The compiler can potentially generate a lot of segments // per file. The segments within a file have disjoint // address ranges as long as they are treated as separate // SOURCE_ENTRYs. If all segments for a particular file get // combined into one SOURCE_ENTRY it leads to address range overlap // because of combining non-contiguous segments. Allocating // a SOURCE_ENTRY per segment isn't that bad, particularly since // the name information only needs to be allocated in the first // entry for a file and the rest can share it. NameAllocLen = OmfFileNameLen+1; for (iSeg = 0; iSeg < (ULONG)OmfSrcFile->cSeg; iSeg++) { PULONG Off; PUSHORT Ln; ULONG iLine; PIMAGE_SECTION_HEADER sh; OmfSrcLine = (OMFSourceLine *)(Base+OmfSrcFile->baseSrcLn[iSeg]); Src = (PSOURCE_ENTRY)MemAlloc(sizeof(SOURCE_ENTRY)+ sizeof(SOURCE_LINE)*OmfSrcLine->cLnOff+ NameAllocLen); if (Src == NULL) { return Ret; } Src->ModuleId = (ULONG) (ULONG64) PdbModule; Src->Lines = OmfSrcLine->cLnOff; sh = &mi->SectionHdrs[OmfSrcLine->Seg-1]; // Process raw segment limits into current addresses. Src->MinAddr = mi->BaseOfDll+sh->VirtualAddress+(*OmfAddrRanges++); Src->MaxAddr = mi->BaseOfDll+sh->VirtualAddress+(*OmfAddrRanges++); // Retrieve line numbers and offsets from raw data and // process them into current pointers. SrcLine = (SOURCE_LINE *)(Src+1); Src->LineInfo = SrcLine; Off = (ULONG *)&OmfSrcLine->offset[0]; Ln = (USHORT *)(Off+OmfSrcLine->cLnOff); for (iLine = 0; iLine < OmfSrcLine->cLnOff; iLine++) { SrcLine->Line = *Ln++; SrcLine->Addr = (*Off++)+mi->BaseOfDll+sh->VirtualAddress; // Line symbol information names the IA64 bundle // syllables with 0,1,2 whereas the debugger expects // 0,4,8. Convert. if (mi->MachineType == IMAGE_FILE_MACHINE_IA64 && (SrcLine->Addr & 3)) { SrcLine->Addr = (SrcLine->Addr & ~3) | ((SrcLine->Addr & 3) << 2); } SrcLine++; } if (iSeg == 0) { // Stick file name at the very end of the data block so // it doesn't interfere with alignment. Src->File = (LPSTR)SrcLine; memcpy(Src->File, OmfFileName, OmfFileNameLen); Src->File[OmfFileNameLen] = 0; // Later segments will share this initial name storage // space so they don't need to alloc their own. NameAllocLen = 0; Seg0Src = Src; } else if (Seg0Src) { Src->File = Seg0Src->File; } AddSourceEntry(mi, Src); // This routine is successful as long as it adds at least // one new source entry. Ret = true; } } return Ret; } void srcline2sci( PSOURCE_ENTRY Src, PSOURCE_LINE SrcLine, PSRCCODEINFO sci ) { sci->Key = (PVOID)SrcLine; sci->LineNumber = SrcLine->Line; sci->Address = SrcLine->Addr; CopyStrArray(sci->FileName, Src->File ? Src->File : ""); } PSOURCE_LINE FindLineInSource( PSOURCE_ENTRY Src, DWORD64 Addr ) { int Low, Middle, High; PSOURCE_LINE SrcLine; Low = 0; High = Src->Lines-1; while (High >= Low) { Middle = (High <= Low) ? Low : (Low + High) >> 1; SrcLine = &Src->LineInfo[Middle]; #ifdef DBG_ADDR_SEARCH DbgOut(" Checking %4d:%x`%08X\n", Middle, (ULONG)(SrcLine->Addr>>32), (ULONG)SrcLine->Addr); #endif if (Addr < SrcLine->Addr) { High = Middle-1; } else if (Middle < (int)Src->Lines-1 && Addr >= (SrcLine+1)->Addr) { Low = Middle+1; } else { PSOURCE_LINE HighLine; // Find the highest source line with this offset. // Source lines are sorted by offset so the highest // source line could be before or after this one. while (SrcLine > Src->LineInfo && (SrcLine - 1)->Addr == SrcLine->Addr) { SrcLine--; } HighLine = SrcLine; while (SrcLine < Src->LineInfo + Src->Lines - 1 && (++SrcLine)->Addr == HighLine->Addr) { if (SrcLine->Line > HighLine->Line) { HighLine = SrcLine; } } return HighLine; } } return NULL; } PSOURCE_ENTRY FindNextSourceEntryForAddr( PMODULE_ENTRY mi, DWORD64 Addr, PSOURCE_ENTRY SearchFrom ) { PSOURCE_ENTRY Src; Src = SearchFrom != NULL ? SearchFrom->Next : mi->SourceFiles; while (Src != NULL) { if (Addr < Src->MinAddr) { // Source files are kept sorted by increasing address so this // means that the address will not be found later and // we can stop checking. return NULL; } else if (Addr <= Src->MaxAddr) { // Found one. return Src; } Src = Src->Next; } return NULL; } BOOL GetLineFromAddr( PPROCESS_ENTRY pe, PMODULE_ENTRY mi, DWORD64 Addr, PDWORD Displacement, PSRCCODEINFO sci ) { PSOURCE_ENTRY Src; DWORD Bias; DWORD64 srcAddr; BOOL rc; if (mi == NULL) return false; rc = false; if (mi->dia) { rc = diaGetLineFromAddr(mi, Addr, Displacement, sci); if (rc) AddSourceFileToHintList(pe, mi, sci->FileName); return rc; } srcAddr = ConvertOmapToSrc(mi, Addr, &Bias, option(SYMOPT_OMAP_FIND_NEAREST)); if (!srcAddr) return false; // We have successfully converted srcAddr += Bias; for (;;) { PSOURCE_ENTRY BestSrc; PSOURCE_LINE BestSrcLine; DWORD64 BestDisp; // Search through all the source entries that contain the given // address, looking for the line with the smallest displacement. BestDisp = 0xffffffffffffffff; BestSrc = NULL; Src = NULL; while (Src = FindNextSourceEntryForAddr(mi, srcAddr, Src)) { PSOURCE_LINE SrcLine; #ifdef DBG_ADDR_SEARCH DbgOut("Found '%s' %d lines: %08I64X %08I64X for %08I64X\n", Src->File, Src->Lines, Src->MinAddr, Src->MaxAddr, Addr); #endif // Found a matching source entry, so look up the line // information. SrcLine = FindLineInSource(Src, srcAddr); if (SrcLine != NULL && Addr-SrcLine->Addr < BestDisp) { BestDisp = Addr-SrcLine->Addr; #ifdef DBG_ADDR_SEARCH DbgOut(" Best disp %I64X\n", BestDisp); #endif BestSrc = Src; BestSrcLine = SrcLine; if (BestDisp == 0) { break; } } } // Only accept displaced answers if there's no more symbol // information to load. if (BestSrc != NULL && BestDisp == 0) { srcline2sci(BestSrc, BestSrcLine, sci); *Displacement = (ULONG)BestDisp; return true; } return false; } return false; } PSOURCE_ENTRY FindNextSourceEntryForFile( PMODULE_ENTRY mi, LPSTR FileStr, PSOURCE_ENTRY SearchFrom ) { PSOURCE_ENTRY Src; Src = SearchFrom != NULL ? SearchFrom->Next : mi->SourceFiles; while (Src != NULL) { if (SymMatchFileName(Src->File, FileStr, NULL, NULL)) { return Src; } Src = Src->Next; } return NULL; } PSOURCE_ENTRY FindPrevSourceEntryForFile( PMODULE_ENTRY mi, LPSTR FileStr, PSOURCE_ENTRY SearchFrom ) { PSOURCE_ENTRY Src; Src = SearchFrom != NULL ? SearchFrom->Prev : mi->SourceFilesTail; while (Src != NULL) { if (SymMatchFileName(Src->File, FileStr, NULL, NULL)) { return Src; } Src = Src->Prev; } return NULL; } BOOL GetLineFromName( PPROCESS_ENTRY pe, PMODULE_ENTRY mi, LPSTR FileName, DWORD LineNumber, PLONG Displacement, PSRCCODEINFO sci, DWORD method ) { BOOL rc; PSOURCE_ENTRY Src; BOOL AtOrGreater; ULONG Disp; ULONG BestDisp; PSOURCE_ENTRY BestSrc; PSOURCE_LINE BestSrcLine; if (!mi) return false; // If the high bit of the line number is set // it means that the caller only wants lines at // or greater than the given line. AtOrGreater = (LineNumber & 0x80000000) != 0; LineNumber &= 0x7fffffff; if (mi->dia) { rc = diaGetLineFromName(mi, FileName, LineNumber, Displacement, sci, method); if (rc) AddSourceFileToHintList(pe, mi, FileName); return rc; } // // Search existing source information for a filename match. // There can be multiple SOURCE_ENTRYs with the same filename, // so make sure and search them all for an exact match // before settling on an approximate match. // Src = NULL; BestDisp = 0x7fffffff; BestSrcLine = NULL; while (Src = FindNextSourceEntryForFile(mi, FileName, Src)) { PSOURCE_LINE SrcLine; ULONG i; // Found a matching source entry, so look up the closest // line. Line number info is sorted by address so the actual // line numbers can be in any order so we can't optimize // this linear search. SrcLine = Src->LineInfo; for (i = 0; i < Src->Lines; i++) { if (LineNumber > SrcLine->Line) { if (AtOrGreater) { Disp = 0x7fffffff; } else { Disp = LineNumber-SrcLine->Line; } } else { Disp = SrcLine->Line-LineNumber; } if (Disp < BestDisp) { BestDisp = Disp; BestSrc = Src; BestSrcLine = SrcLine; if (Disp == 0) { break; } } SrcLine++; } // If we found an exact match we can stop. if (BestDisp == 0) { break; } } // Only accept displaced answers if there's no more symbol // information to load. if (BestSrcLine != NULL && (BestDisp == 0)) { srcline2sci(BestSrc, BestSrcLine, sci); *Displacement = (LONG)(LineNumber-BestSrcLine->Line); AddSourceFileToHintList(pe, mi, FileName); return true; } return false; } #define LINE_ERROR 0xffffffff // we need to get rid of this function! ULONG GetFileLineOffsets( PMODULE_ENTRY mi, LPSTR FileName, PDWORD64 Buffer, ULONG BufferLines ) { PSOURCE_ENTRY Src; ULONG HighestLine = 0; // This routine collects all line information in one pass so // there's no opportunity for lazy loading. We have to // force lines to be loaded up front. if (mi->dia && option(SYMOPT_LOAD_LINES)) { if (!diaAddLinesForAllMod(mi)) { return LINE_ERROR; } } Src = NULL; while (Src = FindNextSourceEntryForFile(mi, FileName, Src)) { PSOURCE_LINE Line; ULONG i; ULONG Num; Line = Src->LineInfo; for (i = 0; i < Src->Lines; i++) { if (Line->Line > HighestLine) { HighestLine = Line->Line; } Num = Line->Line - 1; if (Num < BufferLines) { Buffer[Num] = Line->Addr; } Line++; } } return HighestLine; } ULONG IMAGEAPI SymGetFileLineOffsets64( IN HANDLE hProcess, IN LPSTR ModuleName, IN LPSTR FileName, OUT PDWORD64 Buffer, IN ULONG BufferLines ) /*++ Routine Description: This function locates the given file's line information and fills the given buffer with offsets for each line. Buffer[Line - 1] is set to the offset for Line. Buffer entries for lines that do not have information are left unchanged. Arguments: hProcess - Process handle, must have been previously registered with SymInitialize. ModuleName - Module name or NULL. FileName - File name. Buffer - Array of offsets to fill. BufferLines - Number of entries in the Buffer array. Return Value: 0 - No information was found. LINE_ERROR - Failure during operation. Call GetLastError to discover the cause of the failure. Otherwise the return value is the highest line number found. --*/ { PPROCESS_ENTRY ProcessEntry; PMODULE_ENTRY mi; ULONG HighestLine = 0; PLIST_ENTRY Next; __try { ProcessEntry = FindProcessEntry( hProcess ); if (!ProcessEntry) { SetLastError( ERROR_INVALID_HANDLE ); return LINE_ERROR; } if (ModuleName != NULL) { mi = FindModule(hProcess, ProcessEntry, ModuleName, true); if (mi != NULL) { return GetFileLineOffsets(mi, FileName, Buffer, BufferLines); } SetLastError( ERROR_MOD_NOT_FOUND ); return LINE_ERROR; } Next = ProcessEntry->ModuleList.Flink; if (Next) { while (Next != &ProcessEntry->ModuleList) { mi = CONTAINING_RECORD( Next, MODULE_ENTRY, ListEntry ); Next = mi->ListEntry.Flink; if (!LoadSymbols(hProcess, mi, LS_QUALIFIED | LS_LOAD_LINES)) { continue; } HighestLine = GetFileLineOffsets(mi, FileName, Buffer, BufferLines); // This will break on lines found or LINE_ERROR. if (HighestLine > 0) { break; } } } } __except (EXCEPTION_EXECUTE_HANDLER) { ImagepSetLastErrorFromStatus( GetExceptionCode() ); return LINE_ERROR; } return HighestLine; }