1016 lines
27 KiB
C
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;
|
|
}
|