Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1015 lines
27 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. linesym.c
  5. Abstract:
  6. Source file and line support.
  7. Author:
  8. Drew Bliss (drewb) 07-07-1997
  9. Environment:
  10. User Mode
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <ntldr.h>
  16. #include "private.h"
  17. #include "symbols.h"
  18. #include "globals.h"
  19. // private version of qsort used to avoid compat problems on NT4 and win2k.
  20. // code is published from base\crts
  21. extern
  22. void __cdecl dbg_qsort(void *, size_t, size_t,
  23. int (__cdecl *) (const void *, const void *));
  24. // #define DBG_LINES
  25. // #define DBG_COFF_LINES
  26. // #define DBG_ADDR_SEARCH
  27. BOOL
  28. diaAddLinesForAllMod(
  29. PMODULE_ENTRY mi
  30. );
  31. #if defined(DBG_LINES) || defined(DBG_COFF_LINES) || defined(DBG_ADDR_SEARCH)
  32. void __cdecl
  33. DbgOut(PCSTR Format, ...)
  34. {
  35. char Buf[512];
  36. va_list Args;
  37. va_start(Args, Format);
  38. _vsnprintf(Buf, sizeof(Buf), Format, Args);
  39. va_end(Args);
  40. OutputDebugStringA(Buf);
  41. }
  42. #endif
  43. int
  44. __cdecl
  45. CompareLineAddresses(
  46. const void *v1,
  47. const void *v2
  48. )
  49. {
  50. PSOURCE_LINE Line1 = (PSOURCE_LINE)v1;
  51. PSOURCE_LINE Line2 = (PSOURCE_LINE)v2;
  52. if (Line1->Addr < Line2->Addr) {
  53. return -1;
  54. } else if (Line1->Addr > Line2->Addr) {
  55. return 1;
  56. } else {
  57. return 0;
  58. }
  59. }
  60. VOID
  61. AddSourceEntry(
  62. PMODULE_ENTRY mi,
  63. PSOURCE_ENTRY Src
  64. )
  65. {
  66. PSOURCE_ENTRY SrcCur;
  67. // Overlap is currently permitted.
  68. #if 0
  69. // Check for overlap between SOURCE_ENTRY address ranges.
  70. for (SrcCur = mi->SourceFiles;
  71. SrcCur != NULL;
  72. SrcCur = SrcCur->Next)
  73. {
  74. if (!(SrcCur->MinAddr > Src->MaxAddr ||
  75. SrcCur->MaxAddr < Src->MinAddr))
  76. {
  77. DbgOut("SOURCE_ENTRY overlap between %08I64X:%08I64X "
  78. "and %08I64X:%08I64X\n",
  79. Src->MinAddr, Src->MaxAddr,
  80. SrcCur->MinAddr, SrcCur->MaxAddr);
  81. }
  82. }
  83. #endif
  84. // Sort line info by address.
  85. dbg_qsort((PVOID)Src->LineInfo, Src->Lines, sizeof(Src->LineInfo[0]),
  86. CompareLineAddresses);
  87. // Link new source information into list, sorted by address
  88. // range covered by information.
  89. for (SrcCur = mi->SourceFiles;
  90. SrcCur != NULL;
  91. SrcCur = SrcCur->Next) {
  92. if (SrcCur->MinAddr > Src->MinAddr) {
  93. break;
  94. }
  95. }
  96. Src->Next = SrcCur;
  97. if (SrcCur == NULL) {
  98. if (mi->SourceFilesTail == NULL) {
  99. mi->SourceFiles = Src;
  100. } else {
  101. mi->SourceFilesTail->Next = Src;
  102. }
  103. Src->Prev = mi->SourceFilesTail;
  104. mi->SourceFilesTail = Src;
  105. } else {
  106. if (SrcCur->Prev == NULL) {
  107. mi->SourceFiles = Src;
  108. } else {
  109. SrcCur->Prev->Next = Src;
  110. }
  111. Src->Prev = SrcCur->Prev;
  112. SrcCur->Prev = Src;
  113. }
  114. #ifdef DBG_LINES
  115. DbgOut("%08I64X %08I64X: %5d lines, '%s'\n",
  116. Src->MinAddr, Src->MaxAddr, Src->Lines, Src->File);
  117. #endif
  118. }
  119. #define IS_SECTION_SYM(Sym) \
  120. ((Sym)->StorageClass == IMAGE_SYM_CLASS_STATIC && \
  121. (Sym)->Type == IMAGE_SYM_TYPE_NULL && \
  122. (Sym)->NumberOfAuxSymbols == 1)
  123. BOOL
  124. AddLinesForCoff(
  125. PMODULE_ENTRY mi,
  126. PIMAGE_SYMBOL allSymbols,
  127. DWORD numberOfSymbols,
  128. PIMAGE_LINENUMBER LineNumbers
  129. )
  130. {
  131. PIMAGE_LINENUMBER *SecLines;
  132. BOOL Ret = FALSE;
  133. PIMAGE_SECTION_HEADER sh;
  134. ULONG i;
  135. PIMAGE_SYMBOL Symbol;
  136. ULONG LowestPointer;
  137. // Allocate some space for per-section data.
  138. SecLines = (PIMAGE_LINENUMBER *)MemAlloc(sizeof(PIMAGE_LINENUMBER)*mi->NumSections);
  139. if (SecLines == NULL) {
  140. return FALSE;
  141. }
  142. //
  143. // Add line number information for file groups if such
  144. // groups exist.
  145. //
  146. // First locate the lowest file offset for linenumbers. This
  147. // is necessary to be able to compute relative linenumber pointers
  148. // in split images because currently the pointers aren't updated
  149. // during stripping.
  150. sh = mi->SectionHdrs;
  151. LowestPointer = 0xffffffff;
  152. for (i = 0; i < mi->NumSections; i++, sh++) {
  153. if (sh->NumberOfLinenumbers > 0 &&
  154. sh->PointerToLinenumbers != 0 &&
  155. sh->PointerToLinenumbers < LowestPointer)
  156. {
  157. LowestPointer = sh->PointerToLinenumbers;
  158. }
  159. }
  160. if (LowestPointer == 0xffffffff) {
  161. goto EH_FreeSecLines;
  162. }
  163. sh = mi->SectionHdrs;
  164. for (i = 0; i < mi->NumSections; i++, sh++) {
  165. if (sh->NumberOfLinenumbers > 0 &&
  166. sh->PointerToLinenumbers != 0)
  167. {
  168. SecLines[i] = (PIMAGE_LINENUMBER)
  169. (sh->PointerToLinenumbers - LowestPointer + (DWORD_PTR)LineNumbers);
  170. #ifdef DBG_COFF_LINES
  171. DbgOut("Section %d: %d lines at %08X\n",
  172. i, sh->NumberOfLinenumbers, SecLines[i]);
  173. #endif
  174. } else {
  175. SecLines[i] = NULL;
  176. }
  177. }
  178. // Look for a file symbol.
  179. Symbol = allSymbols;
  180. for (i = 0; i < numberOfSymbols; i++) {
  181. if (Symbol->StorageClass == IMAGE_SYM_CLASS_FILE) {
  182. break;
  183. }
  184. i += Symbol->NumberOfAuxSymbols;
  185. Symbol += 1+Symbol->NumberOfAuxSymbols;
  186. }
  187. // If no file symbols were found, don't attempt to add line
  188. // number information. Something could be done with the raw
  189. // linenumber info in the image (if it exists) but this probably
  190. // isn't an important enough case to worry about.
  191. while (i < numberOfSymbols) {
  192. ULONG iNextFile, iAfterFile;
  193. ULONG iCur, iSym;
  194. PIMAGE_SYMBOL SymAfterFile, CurSym;
  195. PIMAGE_AUX_SYMBOL AuxSym;
  196. ULONG Lines;
  197. ULONG MinAddr, MaxAddr;
  198. LPSTR FileName;
  199. ULONG FileNameLen;
  200. #ifdef DBG_COFF_LINES
  201. DbgOut("%3X: '%s', %X\n", i, Symbol+1, Symbol->Value);
  202. #endif
  203. // A file symbol's Value is the index of the next file symbol.
  204. // In between the two file symbols there may be static
  205. // section symbols which give line number counts for all
  206. // the line numbers in the file.
  207. // The file chain can be NULL terminated or a circular list,
  208. // in which case this code assumes the end comes when the
  209. // list wraps around.
  210. if (Symbol->Value == 0 || Symbol->Value <= i) {
  211. iNextFile = numberOfSymbols;
  212. } else {
  213. iNextFile = Symbol->Value;
  214. }
  215. // Compute the index of the first symbol after the current file
  216. // symbol.
  217. iAfterFile = i+1+Symbol->NumberOfAuxSymbols;
  218. SymAfterFile = Symbol+1+Symbol->NumberOfAuxSymbols;
  219. // Look for section symbols and count up the number of linenumber
  220. // references, the min address and the max address.
  221. CurSym = SymAfterFile;
  222. iCur = iAfterFile;
  223. Lines = 0;
  224. MinAddr = 0xffffffff;
  225. MaxAddr = 0;
  226. while (iCur < iNextFile) {
  227. DWORD Addr;
  228. if (IS_SECTION_SYM(CurSym) &&
  229. SecLines[CurSym->SectionNumber-1] != NULL)
  230. {
  231. AuxSym = (PIMAGE_AUX_SYMBOL)(CurSym+1);
  232. Lines += AuxSym->Section.NumberOfLinenumbers;
  233. Addr = (ULONG)(CurSym->Value+mi->BaseOfDll);
  234. #ifdef DBG_COFF_LINES
  235. DbgOut(" Range %08X %08X, min %08X max %08X\n",
  236. Addr, Addr+AuxSym->Section.Length-1,
  237. MinAddr, MaxAddr);
  238. #endif
  239. if (Addr < MinAddr) {
  240. MinAddr = Addr;
  241. }
  242. Addr += AuxSym->Section.Length-1;
  243. if (Addr > MaxAddr) {
  244. MaxAddr = Addr;
  245. }
  246. }
  247. iCur += 1+CurSym->NumberOfAuxSymbols;
  248. CurSym += 1+CurSym->NumberOfAuxSymbols;
  249. }
  250. if (Lines > 0) {
  251. PSOURCE_ENTRY Src;
  252. PSOURCE_LINE SrcLine;
  253. ULONG iLine;
  254. // We have a filename and some linenumber information,
  255. // so create a SOURCE_ENTRY and fill it in.
  256. FileName = (LPSTR)(Symbol+1);
  257. FileNameLen = strlen(FileName);
  258. Src = (PSOURCE_ENTRY)MemAlloc(sizeof(SOURCE_ENTRY)+
  259. sizeof(SOURCE_LINE)*Lines+
  260. FileNameLen+1);
  261. if (Src == NULL) {
  262. goto EH_FreeSecLines;
  263. }
  264. Src->ModuleId = 0;
  265. Src->MinAddr = MinAddr;
  266. Src->MaxAddr = MaxAddr;
  267. Src->Lines = Lines;
  268. SrcLine = (PSOURCE_LINE)(Src+1);
  269. Src->LineInfo = SrcLine;
  270. // Now that we've got a place to put linenumber information,
  271. // retraverse the section symbols and grab COFF linenumbers
  272. // from the appropriate sections and format them into
  273. // the generic format.
  274. CurSym = SymAfterFile;
  275. iCur = iAfterFile;
  276. while (iCur < iNextFile) {
  277. if (IS_SECTION_SYM(CurSym) &&
  278. SecLines[CurSym->SectionNumber-1] != NULL) {
  279. PIMAGE_LINENUMBER CoffLine;
  280. AuxSym = (PIMAGE_AUX_SYMBOL)(CurSym+1);
  281. CoffLine = SecLines[CurSym->SectionNumber-1];
  282. #ifdef DBG_COFF_LINES
  283. DbgOut(" %d lines at %08X\n",
  284. AuxSym->Section.NumberOfLinenumbers,
  285. CoffLine);
  286. #endif
  287. for (iLine = 0;
  288. iLine < AuxSym->Section.NumberOfLinenumbers;
  289. iLine++)
  290. {
  291. SrcLine->Addr = CoffLine->Type.VirtualAddress+
  292. mi->BaseOfDll;
  293. SrcLine->Line = CoffLine->Linenumber;
  294. CoffLine++;
  295. SrcLine++;
  296. }
  297. SecLines[CurSym->SectionNumber-1] = CoffLine;
  298. }
  299. iCur += 1+CurSym->NumberOfAuxSymbols;
  300. CurSym += 1+CurSym->NumberOfAuxSymbols;
  301. }
  302. // Stick file name at the very end of the data block so
  303. // it doesn't interfere with alignment.
  304. Src->File = (LPSTR)SrcLine;
  305. memcpy(Src->File, FileName, FileNameLen+1);
  306. AddSourceEntry(mi, Src);
  307. // This routine is successful as long as it adds at least
  308. // one new source entry.
  309. Ret = TRUE;
  310. }
  311. // After the loops above iCur and CurSym refer to the next
  312. // file symbol, so update the loop counters from them.
  313. i = iCur;
  314. Symbol = CurSym;
  315. }
  316. EH_FreeSecLines:
  317. MemFree(SecLines);
  318. return Ret;
  319. }
  320. BOOL
  321. AddLinesForOmfSourceModule(
  322. PMODULE_ENTRY mi,
  323. BYTE *Base,
  324. OMFSourceModule *OmfSrcMod,
  325. PVOID PdbModule
  326. )
  327. {
  328. BOOL Ret;
  329. ULONG iFile;
  330. Ret = FALSE;
  331. for (iFile = 0; iFile < (ULONG)OmfSrcMod->cFile; iFile++) {
  332. OMFSourceFile *OmfSrcFile;
  333. BYTE OmfFileNameLen;
  334. LPSTR OmfFileName;
  335. PULONG OmfAddrRanges;
  336. OMFSourceLine *OmfSrcLine;
  337. ULONG iSeg;
  338. PSOURCE_ENTRY Src;
  339. PSOURCE_ENTRY Seg0Src;
  340. PSOURCE_LINE SrcLine;
  341. ULONG NameAllocLen;
  342. OmfSrcFile = (OMFSourceFile *)(Base+OmfSrcMod->baseSrcFile[iFile]);
  343. // The baseSrcLn array of offsets is immediately followed by
  344. // SVA pairs which define the address ranges for the segments.
  345. OmfAddrRanges = &OmfSrcFile->baseSrcLn[OmfSrcFile->cSeg];
  346. // The name length and data immediately follows the address
  347. // range information.
  348. OmfFileName = (LPSTR)(OmfAddrRanges+2*OmfSrcFile->cSeg)+1;
  349. OmfFileNameLen = *(BYTE *)(OmfFileName-1);
  350. // The compiler can potentially generate a lot of segments
  351. // per file. The segments within a file have disjoint
  352. // address ranges as long as they are treated as separate
  353. // SOURCE_ENTRYs. If all segments for a particular file get
  354. // combined into one SOURCE_ENTRY it leads to address range overlap
  355. // because of combining non-contiguous segments. Allocating
  356. // a SOURCE_ENTRY per segment isn't that bad, particularly since
  357. // the name information only needs to be allocated in the first
  358. // entry for a file and the rest can share it.
  359. NameAllocLen = OmfFileNameLen+1;
  360. for (iSeg = 0; iSeg < (ULONG)OmfSrcFile->cSeg; iSeg++) {
  361. PULONG Off;
  362. PUSHORT Ln;
  363. ULONG iLine;
  364. PIMAGE_SECTION_HEADER sh;
  365. OmfSrcLine = (OMFSourceLine *)(Base+OmfSrcFile->baseSrcLn[iSeg]);
  366. Src = (PSOURCE_ENTRY)MemAlloc(sizeof(SOURCE_ENTRY)+
  367. sizeof(SOURCE_LINE)*OmfSrcLine->cLnOff+
  368. NameAllocLen);
  369. if (Src == NULL) {
  370. return Ret;
  371. }
  372. Src->ModuleId = (ULONG) (ULONG64) PdbModule;
  373. Src->Lines = OmfSrcLine->cLnOff;
  374. sh = &mi->SectionHdrs[OmfSrcLine->Seg-1];
  375. // Process raw segment limits into current addresses.
  376. Src->MinAddr = mi->BaseOfDll+sh->VirtualAddress+(*OmfAddrRanges++);
  377. Src->MaxAddr = mi->BaseOfDll+sh->VirtualAddress+(*OmfAddrRanges++);
  378. // Retrieve line numbers and offsets from raw data and
  379. // process them into current pointers.
  380. SrcLine = (SOURCE_LINE *)(Src+1);
  381. Src->LineInfo = SrcLine;
  382. Off = (ULONG *)&OmfSrcLine->offset[0];
  383. Ln = (USHORT *)(Off+OmfSrcLine->cLnOff);
  384. for (iLine = 0; iLine < OmfSrcLine->cLnOff; iLine++) {
  385. SrcLine->Line = *Ln++;
  386. SrcLine->Addr = (*Off++)+mi->BaseOfDll+sh->VirtualAddress;
  387. // Line symbol information names the IA64 bundle
  388. // syllables with 0,1,2 whereas the debugger expects
  389. // 0,4,8. Convert.
  390. if (mi->MachineType == IMAGE_FILE_MACHINE_IA64 &&
  391. (SrcLine->Addr & 3)) {
  392. SrcLine->Addr = (SrcLine->Addr & ~3) |
  393. ((SrcLine->Addr & 3) << 2);
  394. }
  395. SrcLine++;
  396. }
  397. if (iSeg == 0) {
  398. // Stick file name at the very end of the data block so
  399. // it doesn't interfere with alignment.
  400. Src->File = (LPSTR)SrcLine;
  401. memcpy(Src->File, OmfFileName, OmfFileNameLen);
  402. Src->File[OmfFileNameLen] = 0;
  403. // Later segments will share this initial name storage
  404. // space so they don't need to alloc their own.
  405. NameAllocLen = 0;
  406. Seg0Src = Src;
  407. } else {
  408. Src->File = Seg0Src->File;
  409. }
  410. AddSourceEntry(mi, Src);
  411. // This routine is successful as long as it adds at least
  412. // one new source entry.
  413. Ret = TRUE;
  414. }
  415. }
  416. return Ret;
  417. }
  418. VOID
  419. FillLineInfo(
  420. PSOURCE_ENTRY Src,
  421. PSOURCE_LINE SrcLine,
  422. PIMAGEHLP_LINE64 Line
  423. )
  424. {
  425. Line->Key = (PVOID)SrcLine;
  426. Line->LineNumber = SrcLine->Line;
  427. Line->FileName = Src->File;
  428. Line->Address = SrcLine->Addr;
  429. }
  430. PSOURCE_LINE
  431. FindLineInSource(
  432. PSOURCE_ENTRY Src,
  433. DWORD64 Addr
  434. )
  435. {
  436. int Low, Middle, High;
  437. PSOURCE_LINE SrcLine;
  438. Low = 0;
  439. High = Src->Lines-1;
  440. while (High >= Low) {
  441. Middle = (High <= Low) ? Low : (Low + High) >> 1;
  442. SrcLine = &Src->LineInfo[Middle];
  443. #ifdef DBG_ADDR_SEARCH
  444. DbgOut(" Checking %4d:%x`%08X\n", Middle,
  445. (ULONG)(SrcLine->Addr>>32), (ULONG)SrcLine->Addr);
  446. #endif
  447. if (Addr < SrcLine->Addr) {
  448. High = Middle-1;
  449. }
  450. else if (Middle < (int)Src->Lines-1 &&
  451. Addr >= (SrcLine+1)->Addr) {
  452. Low = Middle+1;
  453. } else {
  454. PSOURCE_LINE HighLine;
  455. // Find the highest source line with this offset.
  456. // Source lines are sorted by offset so the highest
  457. // source line could be before or after this one.
  458. while (SrcLine > Src->LineInfo &&
  459. (SrcLine - 1)->Addr == SrcLine->Addr) {
  460. SrcLine--;
  461. }
  462. HighLine = SrcLine;
  463. while (SrcLine < Src->LineInfo + Src->Lines - 1 &&
  464. (++SrcLine)->Addr == HighLine->Addr) {
  465. if (SrcLine->Line > HighLine->Line) {
  466. HighLine = SrcLine;
  467. }
  468. }
  469. return HighLine;
  470. }
  471. }
  472. return NULL;
  473. }
  474. PSOURCE_ENTRY
  475. FindNextSourceEntryForAddr(
  476. PMODULE_ENTRY mi,
  477. DWORD64 Addr,
  478. PSOURCE_ENTRY SearchFrom
  479. )
  480. {
  481. PSOURCE_ENTRY Src;
  482. Src = SearchFrom != NULL ? SearchFrom->Next : mi->SourceFiles;
  483. while (Src != NULL) {
  484. if (Addr < Src->MinAddr) {
  485. // Source files are kept sorted by increasing address so this
  486. // means that the address will not be found later and
  487. // we can stop checking.
  488. return NULL;
  489. } else if (Addr <= Src->MaxAddr) {
  490. // Found one.
  491. return Src;
  492. }
  493. Src = Src->Next;
  494. }
  495. return NULL;
  496. }
  497. BOOL
  498. GetLineFromAddr(
  499. PMODULE_ENTRY mi,
  500. DWORD64 Addr,
  501. PDWORD Displacement,
  502. PIMAGEHLP_LINE64 Line
  503. )
  504. {
  505. PSOURCE_ENTRY Src;
  506. DWORD Bias;
  507. DWORD64 srcAddr;
  508. if (mi == NULL) {
  509. return FALSE;
  510. }
  511. if (mi->dia)
  512. return diaGetLineFromAddr(mi, Addr, Displacement, Line);
  513. srcAddr = ConvertOmapToSrc( mi,
  514. Addr,
  515. &Bias,
  516. (g.SymOptions & SYMOPT_OMAP_FIND_NEAREST) != 0
  517. );
  518. if (srcAddr == 0) {
  519. return FALSE;
  520. }
  521. // We have successfully converted
  522. srcAddr += Bias;
  523. for (;;) {
  524. PSOURCE_ENTRY BestSrc;
  525. PSOURCE_LINE BestSrcLine;
  526. DWORD64 BestDisp;
  527. // Search through all the source entries that contain the given
  528. // address, looking for the line with the smallest displacement.
  529. BestDisp = 0xffffffffffffffff;
  530. BestSrc = NULL;
  531. Src = NULL;
  532. while (Src = FindNextSourceEntryForAddr(mi, srcAddr, Src)) {
  533. PSOURCE_LINE SrcLine;
  534. #ifdef DBG_ADDR_SEARCH
  535. DbgOut("Found '%s' %d lines: %08I64X %08I64X for %08I64X\n",
  536. Src->File, Src->Lines, Src->MinAddr, Src->MaxAddr, Addr);
  537. #endif
  538. // Found a matching source entry, so look up the line
  539. // information.
  540. SrcLine = FindLineInSource(Src, srcAddr);
  541. if (SrcLine != NULL &&
  542. Addr-SrcLine->Addr < BestDisp) {
  543. BestDisp = Addr-SrcLine->Addr;
  544. #ifdef DBG_ADDR_SEARCH
  545. DbgOut(" Best disp %I64X\n", BestDisp);
  546. #endif
  547. BestSrc = Src;
  548. BestSrcLine = SrcLine;
  549. if (BestDisp == 0) {
  550. break;
  551. }
  552. }
  553. }
  554. // Only accept displaced answers if there's no more symbol
  555. // information to load.
  556. if (BestSrc != NULL && BestDisp == 0) {
  557. FillLineInfo(BestSrc, BestSrcLine, Line);
  558. *Displacement = (ULONG)BestDisp;
  559. return TRUE;
  560. }
  561. return FALSE;
  562. }
  563. return FALSE;
  564. }
  565. PSOURCE_ENTRY
  566. FindNextSourceEntryForFile(
  567. PMODULE_ENTRY mi,
  568. LPSTR FileStr,
  569. PSOURCE_ENTRY SearchFrom
  570. )
  571. {
  572. PSOURCE_ENTRY Src;
  573. Src = SearchFrom != NULL ? SearchFrom->Next : mi->SourceFiles;
  574. while (Src != NULL)
  575. {
  576. if (SymMatchFileName(Src->File, FileStr, NULL, NULL))
  577. {
  578. return Src;
  579. }
  580. Src = Src->Next;
  581. }
  582. return NULL;
  583. }
  584. PSOURCE_ENTRY
  585. FindPrevSourceEntryForFile(
  586. PMODULE_ENTRY mi,
  587. LPSTR FileStr,
  588. PSOURCE_ENTRY SearchFrom
  589. )
  590. {
  591. PSOURCE_ENTRY Src;
  592. Src = SearchFrom != NULL ? SearchFrom->Prev : mi->SourceFilesTail;
  593. while (Src != NULL)
  594. {
  595. if (SymMatchFileName(Src->File, FileStr, NULL, NULL))
  596. {
  597. return Src;
  598. }
  599. Src = Src->Prev;
  600. }
  601. return NULL;
  602. }
  603. BOOL
  604. FindLineByName(
  605. PMODULE_ENTRY mi,
  606. LPSTR FileName,
  607. DWORD LineNumber,
  608. PLONG Displacement,
  609. PIMAGEHLP_LINE64 Line
  610. )
  611. {
  612. PSOURCE_ENTRY Src;
  613. BOOL TryLoad;
  614. BOOL AtOrGreater;
  615. if (mi == NULL)
  616. {
  617. return FALSE;
  618. }
  619. if (FileName == NULL)
  620. {
  621. // If no file was given then it's assumed that the file
  622. // is the same as for the line information passed in.
  623. FileName = Line->FileName;
  624. }
  625. // If the high bit of the line number is set
  626. // it means that the caller only wants lines at
  627. // or greater than the given line.
  628. AtOrGreater = (LineNumber & 0x80000000) != 0;
  629. LineNumber &= 0x7fffffff;
  630. if (mi->dia)
  631. if (diaGetLineFromName(mi, FileName, LineNumber, Displacement, Line))
  632. return TRUE;
  633. // We only lazy load here for symbols, and only if we're allowed to.
  634. TryLoad = mi->SymType == SymDia &&
  635. (g.SymOptions & SYMOPT_LOAD_LINES) != 0;
  636. for (;;)
  637. {
  638. ULONG Disp;
  639. ULONG BestDisp;
  640. PSOURCE_ENTRY BestSrc;
  641. PSOURCE_LINE BestSrcLine;
  642. //
  643. // Search existing source information for a filename match.
  644. // There can be multiple SOURCE_ENTRYs with the same filename,
  645. // so make sure and search them all for an exact match
  646. // before settling on an approximate match.
  647. //
  648. Src = NULL;
  649. BestDisp = 0x7fffffff;
  650. BestSrcLine = NULL;
  651. while (Src = FindNextSourceEntryForFile(mi, FileName, Src))
  652. {
  653. PSOURCE_LINE SrcLine;
  654. ULONG i;
  655. // Found a matching source entry, so look up the closest
  656. // line. Line number info is sorted by address so the actual
  657. // line numbers can be in any order so we can't optimize
  658. // this linear search.
  659. SrcLine = Src->LineInfo;
  660. for (i = 0; i < Src->Lines; i++)
  661. {
  662. if (LineNumber > SrcLine->Line)
  663. {
  664. if (AtOrGreater)
  665. {
  666. Disp = 0x7fffffff;
  667. }
  668. else
  669. {
  670. Disp = LineNumber-SrcLine->Line;
  671. }
  672. }
  673. else
  674. {
  675. Disp = SrcLine->Line-LineNumber;
  676. }
  677. if (Disp < BestDisp)
  678. {
  679. BestDisp = Disp;
  680. BestSrc = Src;
  681. BestSrcLine = SrcLine;
  682. if (Disp == 0)
  683. {
  684. break;
  685. }
  686. }
  687. SrcLine++;
  688. }
  689. // If we found an exact match we can stop.
  690. if (BestDisp == 0)
  691. {
  692. break;
  693. }
  694. }
  695. // Only accept displaced answers if there's no more symbol
  696. // information to load.
  697. if (BestSrcLine != NULL && (BestDisp == 0 || !TryLoad))
  698. {
  699. FillLineInfo(BestSrc, BestSrcLine, Line);
  700. *Displacement = (LONG)(LineNumber-BestSrcLine->Line);
  701. return TRUE;
  702. }
  703. if (!TryLoad)
  704. {
  705. // There's no more line information to try and load so
  706. // we're out of luck.
  707. return FALSE;
  708. }
  709. TryLoad = FALSE;
  710. // There doesn't seem to be an easy way to look up a module by
  711. // filename. It's possible to query by object filename, but
  712. // that can be much different from the source filename.
  713. // Just load the info all PDB modules.
  714. if (!diaAddLinesForAllMod(mi))
  715. {
  716. return FALSE;
  717. }
  718. }
  719. return FALSE;
  720. }
  721. #define LINE_ERROR 0xffffffff
  722. ULONG
  723. GetFileLineOffsets(
  724. PMODULE_ENTRY mi,
  725. LPSTR FileName,
  726. PDWORD64 Buffer,
  727. ULONG BufferLines
  728. )
  729. {
  730. PSOURCE_ENTRY Src;
  731. ULONG HighestLine = 0;
  732. // This routine collects all line information in one pass so
  733. // there's no opportunity for lazy loading. We have to
  734. // force lines to be loaded up front.
  735. if (mi->dia && (g.SymOptions & SYMOPT_LOAD_LINES) != 0) {
  736. if (!diaAddLinesForAllMod(mi)) {
  737. return LINE_ERROR;
  738. }
  739. }
  740. Src = NULL;
  741. while (Src = FindNextSourceEntryForFile(mi, FileName, Src)) {
  742. PSOURCE_LINE Line;
  743. ULONG i;
  744. ULONG Num;
  745. Line = Src->LineInfo;
  746. for (i = 0; i < Src->Lines; i++) {
  747. if (Line->Line > HighestLine) {
  748. HighestLine = Line->Line;
  749. }
  750. Num = Line->Line - 1;
  751. if (Num < BufferLines) {
  752. Buffer[Num] = Line->Addr;
  753. }
  754. Line++;
  755. }
  756. }
  757. return HighestLine;
  758. }
  759. ULONG
  760. IMAGEAPI
  761. SymGetFileLineOffsets64(
  762. IN HANDLE hProcess,
  763. IN LPSTR ModuleName,
  764. IN LPSTR FileName,
  765. OUT PDWORD64 Buffer,
  766. IN ULONG BufferLines
  767. )
  768. /*++
  769. Routine Description:
  770. This function locates the given file's line information
  771. and fills the given buffer with offsets for each
  772. line. Buffer[Line - 1] is set to the offset for
  773. Line. Buffer entries for lines that do not have information
  774. are left unchanged.
  775. Arguments:
  776. hProcess - Process handle, must have been previously registered
  777. with SymInitialize.
  778. ModuleName - Module name or NULL.
  779. FileName - File name.
  780. Buffer - Array of offsets to fill.
  781. BufferLines - Number of entries in the Buffer array.
  782. Return Value:
  783. 0 - No information was found.
  784. LINE_ERROR - Failure during operation. Call GetLastError to
  785. discover the cause of the failure.
  786. Otherwise the return value is the highest line number found.
  787. --*/
  788. {
  789. PPROCESS_ENTRY ProcessEntry;
  790. PMODULE_ENTRY mi;
  791. ULONG HighestLine = 0;
  792. PLIST_ENTRY Next;
  793. __try {
  794. ProcessEntry = FindProcessEntry( hProcess );
  795. if (!ProcessEntry) {
  796. SetLastError( ERROR_INVALID_HANDLE );
  797. return LINE_ERROR;
  798. }
  799. if (ModuleName != NULL) {
  800. mi = FindModule(hProcess, ProcessEntry, ModuleName, TRUE);
  801. if (mi != NULL) {
  802. return GetFileLineOffsets(mi, FileName, Buffer, BufferLines);
  803. }
  804. SetLastError( ERROR_MOD_NOT_FOUND );
  805. return LINE_ERROR;
  806. }
  807. Next = ProcessEntry->ModuleList.Flink;
  808. if (Next) {
  809. while (Next != &ProcessEntry->ModuleList) {
  810. mi = CONTAINING_RECORD( Next, MODULE_ENTRY, ListEntry );
  811. Next = mi->ListEntry.Flink;
  812. if (!LoadSymbols(hProcess, mi, LS_QUALIFIED | LS_LOAD_LINES)) {
  813. continue;
  814. }
  815. HighestLine = GetFileLineOffsets(mi, FileName, Buffer,
  816. BufferLines);
  817. // This will break on lines found or LINE_ERROR.
  818. if (HighestLine > 0) {
  819. break;
  820. }
  821. }
  822. }
  823. } __except (EXCEPTION_EXECUTE_HANDLER) {
  824. ImagepSetLastErrorFromStatus( GetExceptionCode() );
  825. return LINE_ERROR;
  826. }
  827. return HighestLine;
  828. }