lnfodwrf.pp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2006 by Thomas Schatzl, member of the FreePascal
  4. Development team
  5. Parts (c) 2000 Peter Vreman (adapted from original dwarfs line
  6. reader)
  7. Dwarf LineInfo Retriever
  8. See the file COPYING.FPC, included in this distribution,
  9. for details about the copyright.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. **********************************************************************}
  14. {
  15. This unit should not be compiled in objfpc mode, since this would make it
  16. dependent on objpas unit.
  17. }
  18. unit lnfodwrf;
  19. interface
  20. {$S-}
  21. {$IF FPC_VERSION<3}
  22. type
  23. CodePointer = Pointer;
  24. {$ENDIF}
  25. function GetLineInfo(addr:ptruint;var func,source:string;var line:longint) : boolean;
  26. function DwarfBackTraceStr(addr: CodePointer): string;
  27. procedure CloseDwarf;
  28. var
  29. // Allows more efficient operation by reusing previously loaded debug data
  30. // when the target module filename is the same. However, if an invalid memory
  31. // address is supplied then further calls may result in an undefined behaviour.
  32. // In summary: enable for speed, disable for resilience.
  33. AllowReuseOfLineInfoData: Boolean = True;
  34. implementation
  35. uses
  36. exeinfo;
  37. { Current issues:
  38. - ignores DW_LNS_SET_FILE
  39. }
  40. {$MACRO ON}
  41. //{$DEFINE DEBUG_DWARF_PARSER}
  42. {$ifdef DEBUG_DWARF_PARSER}
  43. {$define DEBUG_WRITELN := WriteLn}
  44. {$define DEBUG_COMMENT := }
  45. {$else}
  46. {$define DEBUG_WRITELN := //}
  47. {$define DEBUG_COMMENT := //}
  48. {$endif}
  49. { some type definitions }
  50. type
  51. Bool8 = ByteBool;
  52. const
  53. EBUF_SIZE = 100;
  54. {$WARNING This code is not thread-safe, and needs improvement}
  55. var
  56. { the input file to read DWARF debug info from, i.e. paramstr(0) }
  57. e : TExeFile;
  58. EBuf: Array [0..EBUF_SIZE-1] of Byte;
  59. EBufCnt, EBufPos: Integer;
  60. { the offset and size of the DWARF debug_line section in the file }
  61. DwarfOffset : longint;
  62. DwarfSize : longint;
  63. { DWARF 2 default opcodes}
  64. const
  65. { Extended opcodes }
  66. DW_LNE_END_SEQUENCE = 1;
  67. DW_LNE_SET_ADDRESS = 2;
  68. DW_LNE_DEFINE_FILE = 3;
  69. { Standard opcodes }
  70. DW_LNS_COPY = 1;
  71. DW_LNS_ADVANCE_PC = 2;
  72. DW_LNS_ADVANCE_LINE = 3;
  73. DW_LNS_SET_FILE = 4;
  74. DW_LNS_SET_COLUMN = 5;
  75. DW_LNS_NEGATE_STMT = 6;
  76. DW_LNS_SET_BASIC_BLOCK = 7;
  77. DW_LNS_CONST_ADD_PC = 8;
  78. DW_LNS_FIXED_ADVANCE_PC = 9;
  79. DW_LNS_SET_PROLOGUE_END = 10;
  80. DW_LNS_SET_EPILOGUE_BEGIN = 11;
  81. DW_LNS_SET_ISA = 12;
  82. type
  83. { state record for the line info state machine }
  84. TMachineState = record
  85. address : QWord;
  86. file_id : DWord;
  87. line : QWord;
  88. column : DWord;
  89. is_stmt : Boolean;
  90. basic_block : Boolean;
  91. end_sequence : Boolean;
  92. prolouge_end : Boolean;
  93. epilouge_begin : Boolean;
  94. isa : DWord;
  95. append_row : Boolean;
  96. end;
  97. { DWARF line number program header preceding the line number program, 64 bit version }
  98. TLineNumberProgramHeader64 = packed record
  99. magic : DWord;
  100. unit_length : QWord;
  101. version : Word;
  102. length : QWord;
  103. minimum_instruction_length : Byte;
  104. default_is_stmt : Bool8;
  105. line_base : ShortInt;
  106. line_range : Byte;
  107. opcode_base : Byte;
  108. end;
  109. { DWARF line number program header preceding the line number program, 32 bit version }
  110. TLineNumberProgramHeader32 = packed record
  111. unit_length : DWord;
  112. version : Word;
  113. length : DWord;
  114. minimum_instruction_length : Byte;
  115. default_is_stmt : Bool8;
  116. line_base : ShortInt;
  117. line_range : Byte;
  118. opcode_base : Byte;
  119. end;
  120. {---------------------------------------------------------------------------
  121. I/O utility functions
  122. ---------------------------------------------------------------------------}
  123. var
  124. base, limit : SizeInt;
  125. index : SizeInt;
  126. baseaddr : pointer;
  127. filename,
  128. dbgfn : string;
  129. lastfilename: string; { store last processed file }
  130. lastopendwarf: Boolean; { store last result of processing a file }
  131. function OpenDwarf(addr : pointer) : boolean;
  132. begin
  133. // False by default
  134. OpenDwarf:=false;
  135. // Empty so can test if GetModuleByAddr has worked
  136. filename := '';
  137. // Get filename by address using GetModuleByAddr
  138. GetModuleByAddr(addr,baseaddr,filename);
  139. {$ifdef DEBUG_LINEINFO}
  140. writeln(stderr,filename,' Baseaddr: ',hexstr(ptruint(baseaddr),sizeof(baseaddr)*2));
  141. {$endif DEBUG_LINEINFO}
  142. // Check if GetModuleByAddr has worked
  143. if filename = '' then
  144. exit;
  145. // If target filename same as previous, then re-use previous result
  146. if AllowReuseOfLineInfoData and (filename = lastfilename) then
  147. begin
  148. {$ifdef DEBUG_LINEINFO}
  149. writeln(stderr,'Reusing debug data');
  150. {$endif DEBUG_LINEINFO}
  151. OpenDwarf:=lastopendwarf;
  152. exit;
  153. end;
  154. // Close previously opened Dwarf
  155. CloseDwarf;
  156. // Reset last open dwarf result
  157. lastopendwarf := false;
  158. // Save newly processed filename
  159. lastfilename := filename;
  160. // Open exe file or debug link
  161. if not OpenExeFile(e,filename) then
  162. exit;
  163. if ReadDebugLink(e,dbgfn) then
  164. begin
  165. CloseExeFile(e);
  166. if not OpenExeFile(e,dbgfn) then
  167. exit;
  168. end;
  169. // Find debug data section
  170. e.processaddress:=ptruint(baseaddr)-e.processaddress;
  171. if FindExeSection(e,'.debug_line',dwarfoffset,dwarfsize) then
  172. begin
  173. lastopendwarf:=true;
  174. OpenDwarf:=true;
  175. end
  176. else
  177. CloseExeFile(e);
  178. end;
  179. procedure CloseDwarf;
  180. begin
  181. if e.isopen then
  182. CloseExeFile(e);
  183. // Reset last processed filename
  184. lastfilename := '';
  185. end;
  186. function Init(aBase, aLimit : Int64) : Boolean;
  187. begin
  188. base := aBase;
  189. limit := aLimit;
  190. Init := (aBase + limit) <= e.size;
  191. seek(e.f, base);
  192. EBufCnt := 0;
  193. EBufPos := 0;
  194. index := 0;
  195. end;
  196. function Init(aBase : Int64) : Boolean;
  197. begin
  198. Init := Init(aBase, limit - (aBase - base));
  199. end;
  200. function Pos() : Int64;
  201. begin
  202. Pos := index;
  203. end;
  204. procedure Seek(const newIndex : Int64);
  205. begin
  206. index := newIndex;
  207. system.seek(e.f, base + index);
  208. EBufCnt := 0;
  209. EBufPos := 0;
  210. end;
  211. { Returns the next Byte from the input stream, or -1 if there has been
  212. an error }
  213. function ReadNext() : Longint; inline;
  214. var
  215. bytesread : SizeInt;
  216. begin
  217. ReadNext := -1;
  218. if EBufPos >= EBufCnt then begin
  219. EBufPos := 0;
  220. EBufCnt := EBUF_SIZE;
  221. if EBufCnt > limit - index then
  222. EBufCnt := limit - index;
  223. blockread(e.f, EBuf, EBufCnt, bytesread);
  224. EBufCnt := bytesread;
  225. end;
  226. if EBufPos < EBufCnt then begin
  227. ReadNext := EBuf[EBufPos];
  228. inc(EBufPos);
  229. inc(index);
  230. end
  231. else
  232. ReadNext := -1;
  233. end;
  234. { Reads the next size bytes into dest. Returns true if successful,
  235. false otherwise. Note that dest may be partially overwritten after
  236. returning false. }
  237. function ReadNext(var dest; size : SizeInt) : Boolean;
  238. var
  239. bytesread, totalread : SizeInt;
  240. r: Boolean;
  241. d: PByte;
  242. begin
  243. d := @dest;
  244. totalread := 0;
  245. r := True;
  246. while (totalread < size) and r do begin;
  247. if EBufPos >= EBufCnt then begin
  248. EBufPos := 0;
  249. EBufCnt := EBUF_SIZE;
  250. if EBufCnt > limit - index then
  251. EBufCnt := limit - index;
  252. blockread(e.f, EBuf, EBufCnt, bytesread);
  253. EBufCnt := bytesread;
  254. if bytesread <= 0 then
  255. r := False;
  256. end;
  257. if EBufPos < EBufCnt then begin
  258. bytesread := EBufCnt - EBufPos;
  259. if bytesread > size - totalread then bytesread := size - totalread;
  260. System.Move(EBuf[EBufPos], d[totalread], bytesread);
  261. inc(EBufPos, bytesread);
  262. inc(index, bytesread);
  263. inc(totalread, bytesread);
  264. end;
  265. end;
  266. ReadNext := r;
  267. end;
  268. { Reads an unsigned LEB encoded number from the input stream }
  269. function ReadULEB128() : QWord;
  270. var
  271. shift : Byte;
  272. data : PtrInt;
  273. val : QWord;
  274. begin
  275. shift := 0;
  276. ReadULEB128 := 0;
  277. data := ReadNext();
  278. while (data <> -1) do begin
  279. val := data and $7f;
  280. ReadULEB128 := ReadULEB128 or (val shl shift);
  281. inc(shift, 7);
  282. if ((data and $80) = 0) then
  283. break;
  284. data := ReadNext();
  285. end;
  286. end;
  287. { Reads a signed LEB encoded number from the input stream }
  288. function ReadLEB128() : Int64;
  289. var
  290. shift : Byte;
  291. data : PtrInt;
  292. val : Int64;
  293. begin
  294. shift := 0;
  295. ReadLEB128 := 0;
  296. data := ReadNext();
  297. while (data <> -1) do begin
  298. val := data and $7f;
  299. ReadLEB128 := ReadLEB128 or (val shl shift);
  300. inc(shift, 7);
  301. if ((data and $80) = 0) then
  302. break;
  303. data := ReadNext();
  304. end;
  305. { extend sign. Note that we can not use shl/shr since the latter does not
  306. translate to arithmetic shifting for signed types }
  307. ReadLEB128 := (not ((ReadLEB128 and (1 shl (shift-1)))-1)) or ReadLEB128;
  308. end;
  309. { Reads an address from the current input stream }
  310. function ReadAddress() : PtrUInt;
  311. begin
  312. ReadNext(ReadAddress, sizeof(ReadAddress));
  313. end;
  314. { Reads a zero-terminated string from the current input stream. If the
  315. string is larger than 255 chars (maximum allowed number of elements in
  316. a ShortString, excess characters will be chopped off. }
  317. function ReadString() : ShortString;
  318. var
  319. temp : PtrInt;
  320. i : PtrUInt;
  321. begin
  322. i := 1;
  323. temp := ReadNext();
  324. while (temp > 0) do begin
  325. ReadString[i] := char(temp);
  326. if (i = 255) then begin
  327. { skip remaining characters }
  328. repeat
  329. temp := ReadNext();
  330. until (temp <= 0);
  331. break;
  332. end;
  333. inc(i);
  334. temp := ReadNext();
  335. end;
  336. { unexpected end of file occurred? }
  337. if (temp = -1) then
  338. ReadString := ''
  339. else
  340. Byte(ReadString[0]) := i-1;
  341. end;
  342. { Reads an unsigned Half from the current input stream }
  343. function ReadUHalf() : Word;
  344. begin
  345. ReadNext(ReadUHalf, sizeof(ReadUHalf));
  346. end;
  347. {---------------------------------------------------------------------------
  348. Generic Dwarf lineinfo reader
  349. The line info reader is based on the information contained in
  350. DWARF Debugging Information Format Version 3
  351. Chapter 6.2 "Line Number Information"
  352. from the
  353. DWARF Debugging Information Format Workgroup.
  354. For more information on this document see also
  355. http://dwarf.freestandards.org/
  356. ---------------------------------------------------------------------------}
  357. { initializes the line info state to the default values }
  358. procedure InitStateRegisters(var state : TMachineState; const aIs_Stmt : Bool8);
  359. begin
  360. with state do begin
  361. address := 0;
  362. file_id := 1;
  363. line := 1;
  364. column := 0;
  365. is_stmt := aIs_Stmt;
  366. basic_block := false;
  367. end_sequence := false;
  368. prolouge_end := false;
  369. epilouge_begin := false;
  370. isa := 0;
  371. append_row := false;
  372. end;
  373. end;
  374. { Skips all line info directory entries }
  375. procedure SkipDirectories();
  376. var s : ShortString;
  377. begin
  378. while (true) do begin
  379. s := ReadString();
  380. if (s = '') then break;
  381. DEBUG_WRITELN('Skipping directory : ', s);
  382. end;
  383. end;
  384. { Skips an LEB128 }
  385. procedure SkipLEB128();
  386. {$ifdef DEBUG_DWARF_PARSER}
  387. var temp : QWord;
  388. {$endif}
  389. begin
  390. {$ifdef DEBUG_DWARF_PARSER}temp := {$endif}ReadLEB128();
  391. DEBUG_WRITELN('Skipping LEB128 : ', temp);
  392. end;
  393. { Skips the filename section from the current file stream }
  394. procedure SkipFilenames();
  395. var s : ShortString;
  396. begin
  397. while (true) do begin
  398. s := ReadString();
  399. if (s = '') then break;
  400. DEBUG_WRITELN('Skipping filename : ', s);
  401. SkipLEB128(); { skip the directory index for the file }
  402. SkipLEB128(); { skip last modification time for file }
  403. SkipLEB128(); { skip length of file }
  404. end;
  405. end;
  406. function CalculateAddressIncrement(opcode : Byte; const header : TLineNumberProgramHeader64) : Int64;
  407. begin
  408. CalculateAddressIncrement := (Int64(opcode) - header.opcode_base) div header.line_range * header.minimum_instruction_length;
  409. end;
  410. function GetFullFilename(const filenameStart, directoryStart : Int64; const file_id : DWord) : ShortString;
  411. var
  412. i : DWord;
  413. filename, directory : ShortString;
  414. dirindex : Int64;
  415. begin
  416. filename := '';
  417. directory := '';
  418. i := 1;
  419. Seek(filenameStart);
  420. while (i <= file_id) do begin
  421. filename := ReadString();
  422. DEBUG_WRITELN('Found "', filename, '"');
  423. if (filename = '') then break;
  424. dirindex := ReadLEB128(); { read the directory index for the file }
  425. SkipLEB128(); { skip last modification time for file }
  426. SkipLEB128(); { skip length of file }
  427. inc(i);
  428. end;
  429. { if we could not find the file index, exit }
  430. if (filename = '') then begin
  431. GetFullFilename := '(Unknown file)';
  432. exit;
  433. end;
  434. Seek(directoryStart);
  435. i := 1;
  436. while (i <= dirindex) do begin
  437. directory := ReadString();
  438. if (directory = '') then break;
  439. inc(i);
  440. end;
  441. if (directory<>'') and (directory[length(directory)]<>'/') then
  442. directory:=directory+'/';
  443. GetFullFilename := directory + filename;
  444. end;
  445. function ParseCompilationUnit(const addr : PtrUInt; const file_offset : QWord;
  446. var source : String; var line : longint; var found : Boolean) : QWord;
  447. var
  448. state : TMachineState;
  449. { we need both headers on the stack, although we only use the 64 bit one internally }
  450. header64 : TLineNumberProgramHeader64;
  451. header32 : TLineNumberProgramHeader32;
  452. adjusted_opcode : Int64;
  453. opcode : PtrInt;
  454. extended_opcode : PtrInt;
  455. extended_opcode_length : PtrInt;
  456. i, addrIncrement, lineIncrement : PtrInt;
  457. {$ifdef DEBUG_DWARF_PARSER}
  458. s : ShortString;
  459. {$endif}
  460. numoptable : array[1..255] of Byte;
  461. { the offset into the file where the include directories are stored for this compilation unit }
  462. include_directories : QWord;
  463. { the offset into the file where the file names are stored for this compilation unit }
  464. file_names : Int64;
  465. temp_length : DWord;
  466. unit_length : QWord;
  467. header_length : SizeInt;
  468. first_row : Boolean;
  469. prev_line : QWord;
  470. prev_file : DWord;
  471. begin
  472. prev_line := 0;
  473. prev_file := 0;
  474. first_row := true;
  475. found := false;
  476. ReadNext(temp_length, sizeof(temp_length));
  477. if (temp_length <> $ffffffff) then begin
  478. unit_length := temp_length + sizeof(temp_length)
  479. end else begin
  480. ReadNext(unit_length, sizeof(unit_length));
  481. inc(unit_length, 12);
  482. end;
  483. ParseCompilationUnit := file_offset + unit_length;
  484. Init(file_offset, unit_length);
  485. DEBUG_WRITELN('Unit length: ', unit_length);
  486. if (temp_length <> $ffffffff) then begin
  487. DEBUG_WRITELN('32 bit DWARF detected');
  488. ReadNext(header32, sizeof(header32));
  489. header64.magic := $ffffffff;
  490. header64.unit_length := header32.unit_length;
  491. header64.version := header32.version;
  492. header64.length := header32.length;
  493. header64.minimum_instruction_length := header32.minimum_instruction_length;
  494. header64.default_is_stmt := header32.default_is_stmt;
  495. header64.line_base := header32.line_base;
  496. header64.line_range := header32.line_range;
  497. header64.opcode_base := header32.opcode_base;
  498. header_length :=
  499. sizeof(header32.length) + sizeof(header32.version) +
  500. sizeof(header32.unit_length);
  501. end else begin
  502. DEBUG_WRITELN('64 bit DWARF detected');
  503. ReadNext(header64, sizeof(header64));
  504. header_length :=
  505. sizeof(header64.magic) + sizeof(header64.version) +
  506. sizeof(header64.length) + sizeof(header64.unit_length);
  507. end;
  508. inc(header_length, header64.length);
  509. fillchar(numoptable, sizeof(numoptable), #0);
  510. ReadNext(numoptable, header64.opcode_base-1);
  511. DEBUG_WRITELN('Opcode parameter count table');
  512. for i := 1 to header64.opcode_base-1 do begin
  513. DEBUG_WRITELN('Opcode[', i, '] - ', numoptable[i], ' parameters');
  514. end;
  515. DEBUG_WRITELN('Reading directories...');
  516. include_directories := Pos();
  517. SkipDirectories();
  518. DEBUG_WRITELN('Reading filenames...');
  519. file_names := Pos();
  520. SkipFilenames();
  521. Seek(header_length);
  522. with header64 do begin
  523. InitStateRegisters(state, default_is_stmt);
  524. end;
  525. opcode := ReadNext();
  526. while (opcode <> -1) and (not found) do begin
  527. DEBUG_WRITELN('Next opcode: ');
  528. case (opcode) of
  529. { extended opcode }
  530. 0 : begin
  531. extended_opcode_length := ReadULEB128();
  532. extended_opcode := ReadNext();
  533. case (extended_opcode) of
  534. -1: begin
  535. exit;
  536. end;
  537. DW_LNE_END_SEQUENCE : begin
  538. state.end_sequence := true;
  539. state.append_row := true;
  540. DEBUG_WRITELN('DW_LNE_END_SEQUENCE');
  541. end;
  542. DW_LNE_SET_ADDRESS : begin
  543. state.address := ReadAddress();
  544. DEBUG_WRITELN('DW_LNE_SET_ADDRESS (', hexstr(state.address, sizeof(state.address)*2), ')');
  545. end;
  546. DW_LNE_DEFINE_FILE : begin
  547. {$ifdef DEBUG_DWARF_PARSER}s := {$endif}ReadString();
  548. SkipLEB128();
  549. SkipLEB128();
  550. SkipLEB128();
  551. DEBUG_WRITELN('DW_LNE_DEFINE_FILE (', s, ')');
  552. end;
  553. else begin
  554. DEBUG_WRITELN('Unknown extended opcode (opcode ', extended_opcode, ' length ', extended_opcode_length, ')');
  555. for i := 0 to extended_opcode_length-2 do
  556. if ReadNext() = -1 then
  557. exit;
  558. end;
  559. end;
  560. end;
  561. DW_LNS_COPY : begin
  562. state.basic_block := false;
  563. state.prolouge_end := false;
  564. state.epilouge_begin := false;
  565. state.append_row := true;
  566. DEBUG_WRITELN('DW_LNS_COPY');
  567. end;
  568. DW_LNS_ADVANCE_PC : begin
  569. inc(state.address, ReadULEB128() * header64.minimum_instruction_length);
  570. DEBUG_WRITELN('DW_LNS_ADVANCE_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
  571. end;
  572. DW_LNS_ADVANCE_LINE : begin
  573. // inc(state.line, ReadLEB128()); negative values are allowed
  574. // but those may generate a range check error
  575. state.line := state.line + ReadLEB128();
  576. DEBUG_WRITELN('DW_LNS_ADVANCE_LINE (', state.line, ')');
  577. end;
  578. DW_LNS_SET_FILE : begin
  579. state.file_id := ReadULEB128();
  580. DEBUG_WRITELN('DW_LNS_SET_FILE (', state.file_id, ')');
  581. end;
  582. DW_LNS_SET_COLUMN : begin
  583. state.column := ReadULEB128();
  584. DEBUG_WRITELN('DW_LNS_SET_COLUMN (', state.column, ')');
  585. end;
  586. DW_LNS_NEGATE_STMT : begin
  587. state.is_stmt := not state.is_stmt;
  588. DEBUG_WRITELN('DW_LNS_NEGATE_STMT (', state.is_stmt, ')');
  589. end;
  590. DW_LNS_SET_BASIC_BLOCK : begin
  591. state.basic_block := true;
  592. DEBUG_WRITELN('DW_LNS_SET_BASIC_BLOCK');
  593. end;
  594. DW_LNS_CONST_ADD_PC : begin
  595. inc(state.address, CalculateAddressIncrement(255, header64));
  596. DEBUG_WRITELN('DW_LNS_CONST_ADD_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
  597. end;
  598. DW_LNS_FIXED_ADVANCE_PC : begin
  599. inc(state.address, ReadUHalf());
  600. DEBUG_WRITELN('DW_LNS_FIXED_ADVANCE_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
  601. end;
  602. DW_LNS_SET_PROLOGUE_END : begin
  603. state.prolouge_end := true;
  604. DEBUG_WRITELN('DW_LNS_SET_PROLOGUE_END');
  605. end;
  606. DW_LNS_SET_EPILOGUE_BEGIN : begin
  607. state.epilouge_begin := true;
  608. DEBUG_WRITELN('DW_LNS_SET_EPILOGUE_BEGIN');
  609. end;
  610. DW_LNS_SET_ISA : begin
  611. state.isa := ReadULEB128();
  612. DEBUG_WRITELN('DW_LNS_SET_ISA (', state.isa, ')');
  613. end;
  614. else begin { special opcode }
  615. if (opcode < header64.opcode_base) then begin
  616. DEBUG_WRITELN('Unknown standard opcode $', hexstr(opcode, 2), '; skipping');
  617. for i := 1 to numoptable[opcode] do
  618. SkipLEB128();
  619. end else begin
  620. adjusted_opcode := opcode - header64.opcode_base;
  621. addrIncrement := CalculateAddressIncrement(opcode, header64);
  622. inc(state.address, addrIncrement);
  623. lineIncrement := header64.line_base + (adjusted_opcode mod header64.line_range);
  624. inc(state.line, lineIncrement);
  625. DEBUG_WRITELN('Special opcode $', hexstr(opcode, 2), ' address increment: ', addrIncrement, ' new line: ', lineIncrement);
  626. state.basic_block := false;
  627. state.prolouge_end := false;
  628. state.epilouge_begin := false;
  629. state.append_row := true;
  630. end;
  631. end;
  632. end;
  633. if (state.append_row) then begin
  634. DEBUG_WRITELN('Current state : address = ', hexstr(state.address, sizeof(state.address) * 2),
  635. DEBUG_COMMENT ' file_id = ', state.file_id, ' line = ', state.line, ' column = ', state.column,
  636. DEBUG_COMMENT ' is_stmt = ', state.is_stmt, ' basic_block = ', state.basic_block,
  637. DEBUG_COMMENT ' end_sequence = ', state.end_sequence, ' prolouge_end = ', state.prolouge_end,
  638. DEBUG_COMMENT ' epilouge_begin = ', state.epilouge_begin, ' isa = ', state.isa);
  639. if (first_row) then begin
  640. if (state.address > addr) then
  641. break;
  642. first_row := false;
  643. end;
  644. { when we have found the address we need to return the previous
  645. line because that contains the call instruction }
  646. if (state.address >= addr) then
  647. found:=true
  648. else
  649. begin
  650. { save line information }
  651. prev_file := state.file_id;
  652. prev_line := state.line;
  653. end;
  654. state.append_row := false;
  655. if (state.end_sequence) then begin
  656. InitStateRegisters(state, header64.default_is_stmt);
  657. first_row := true;
  658. end;
  659. end;
  660. opcode := ReadNext();
  661. end;
  662. if (found) then begin
  663. line := prev_line;
  664. source := GetFullFilename(file_names, include_directories, prev_file);
  665. end;
  666. end;
  667. function GetLineInfo(addr : ptruint; var func, source : string; var line : longint) : boolean;
  668. var
  669. current_offset : QWord;
  670. end_offset : QWord;
  671. found : Boolean;
  672. begin
  673. func := '';
  674. source := '';
  675. found := false;
  676. GetLineInfo:=false;
  677. if not OpenDwarf(pointer(addr)) then
  678. exit;
  679. addr := addr - e.processaddress;
  680. current_offset := DwarfOffset;
  681. end_offset := DwarfOffset + DwarfSize;
  682. while (current_offset < end_offset) and (not found) do begin
  683. Init(current_offset, end_offset - current_offset);
  684. current_offset := ParseCompilationUnit(addr, current_offset,
  685. source, line, found);
  686. end;
  687. if not AllowReuseOfLineInfoData then
  688. CloseDwarf;
  689. GetLineInfo:=true;
  690. end;
  691. function DwarfBackTraceStr(addr: CodePointer): string;
  692. var
  693. func,
  694. source : string;
  695. hs : string;
  696. line : longint;
  697. Store : TBackTraceStrFunc;
  698. Success : boolean;
  699. begin
  700. {$ifdef DEBUG_LINEINFO}
  701. writeln(stderr,'DwarfBackTraceStr called');
  702. {$endif DEBUG_LINEINFO}
  703. { reset to prevent infinite recursion if problems inside the code }
  704. Success:=false;
  705. Store := BackTraceStrFunc;
  706. BackTraceStrFunc := @SysBackTraceStr;
  707. Success:=GetLineInfo(ptruint(addr), func, source, line);
  708. { create string }
  709. DwarfBackTraceStr :=' $' + HexStr(ptruint(addr), sizeof(ptruint) * 2);
  710. if Success then
  711. begin
  712. if func<>'' then
  713. DwarfBackTraceStr := DwarfBackTraceStr + ' ' + func;
  714. if source<>'' then
  715. begin
  716. if func<>'' then
  717. DwarfBackTraceStr := DwarfBackTraceStr + ', ';
  718. if line<>0 then
  719. begin
  720. str(line, hs);
  721. DwarfBackTraceStr := DwarfBackTraceStr + ' line ' + hs;
  722. end;
  723. DwarfBackTraceStr := DwarfBackTraceStr + ' of ' + source;
  724. end;
  725. end;
  726. BackTraceStrFunc := Store;
  727. end;
  728. initialization
  729. lastfilename := '';
  730. lastopendwarf := false;
  731. BackTraceStrFunc := @DwarfBacktraceStr;
  732. finalization
  733. CloseDwarf;
  734. end.