windows-nt/Source/XPSP1/NT/sdktools/debuggers/imagehlp/linesym.c
2020-09-26 16:20:57 +08:00

1016 lines
27 KiB
C

/*++
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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntldr.h>
#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
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;
}
}
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;
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 {
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
FillLineInfo(
PSOURCE_ENTRY Src,
PSOURCE_LINE SrcLine,
PIMAGEHLP_LINE64 Line
)
{
Line->Key = (PVOID)SrcLine;
Line->LineNumber = SrcLine->Line;
Line->FileName = Src->File;
Line->Address = SrcLine->Addr;
}
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(
PMODULE_ENTRY mi,
DWORD64 Addr,
PDWORD Displacement,
PIMAGEHLP_LINE64 Line
)
{
PSOURCE_ENTRY Src;
DWORD Bias;
DWORD64 srcAddr;
if (mi == NULL) {
return FALSE;
}
if (mi->dia)
return diaGetLineFromAddr(mi, Addr, Displacement, Line);
srcAddr = ConvertOmapToSrc( mi,
Addr,
&Bias,
(g.SymOptions & SYMOPT_OMAP_FIND_NEAREST) != 0
);
if (srcAddr == 0) {
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) {
FillLineInfo(BestSrc, BestSrcLine, Line);
*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
FindLineByName(
PMODULE_ENTRY mi,
LPSTR FileName,
DWORD LineNumber,
PLONG Displacement,
PIMAGEHLP_LINE64 Line
)
{
PSOURCE_ENTRY Src;
BOOL TryLoad;
BOOL AtOrGreater;
if (mi == NULL)
{
return FALSE;
}
if (FileName == NULL)
{
// If no file was given then it's assumed that the file
// is the same as for the line information passed in.
FileName = Line->FileName;
}
// 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)
if (diaGetLineFromName(mi, FileName, LineNumber, Displacement, Line))
return TRUE;
// We only lazy load here for symbols, and only if we're allowed to.
TryLoad = mi->SymType == SymDia &&
(g.SymOptions & SYMOPT_LOAD_LINES) != 0;
for (;;)
{
ULONG Disp;
ULONG BestDisp;
PSOURCE_ENTRY BestSrc;
PSOURCE_LINE BestSrcLine;
//
// 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 || !TryLoad))
{
FillLineInfo(BestSrc, BestSrcLine, Line);
*Displacement = (LONG)(LineNumber-BestSrcLine->Line);
return TRUE;
}
if (!TryLoad)
{
// There's no more line information to try and load so
// we're out of luck.
return FALSE;
}
TryLoad = FALSE;
// There doesn't seem to be an easy way to look up a module by
// filename. It's possible to query by object filename, but
// that can be much different from the source filename.
// Just load the info all PDB modules.
if (!diaAddLinesForAllMod(mi))
{
return FALSE;
}
}
return FALSE;
}
#define LINE_ERROR 0xffffffff
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 && (g.SymOptions & SYMOPT_LOAD_LINES) != 0) {
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;
}