lnfodwrf.pp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  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 stabs 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. unit lnfodwrf;
  15. interface
  16. { disable stack checking }
  17. {$S-}
  18. procedure GetLineInfo(addr:ptruint;var func,source:string;var line:longint);
  19. implementation
  20. { Note to implementors of other OS loaders:
  21. - add a LoadXXX() function which has no parameters and returns a Boolean
  22. in the "OS loaders section" enclosing it using the OS specific define.
  23. This method should set the
  24. DwarfOpened,
  25. DwarfOffset and
  26. DwarfSize
  27. global variables properly (see comments at variable definition for more
  28. information).
  29. Additionally this method should return true if DWARF line info information
  30. could be found.
  31. The file variable which can be used for file I/O is the global "infile"
  32. variable.
  33. - in OpenDwarf(), add a call to this initializer function after the
  34. "run OS specific initializer" comment, again enclosed in the system
  35. specific define.
  36. }
  37. { Current issues:
  38. - ignores DW_LNS_SET_FILE
  39. - slow
  40. }
  41. { some type definitions }
  42. type
  43. {$IFDEF CPU32}
  44. UInt = DWord;
  45. Int = Longint;
  46. {$ENDIF}
  47. {$IFDEF CPU64}
  48. UInt = QWord;
  49. Int = Int64;
  50. {$ENDIF}
  51. Bool8 = ByteBool;
  52. var
  53. { the input file to read DWARF debug info from, i.e. paramstr(0) }
  54. infile : File;
  55. { size of the current file, cached }
  56. DwarfFilesize : Int64;
  57. { these variables should be set by the LoadXXX() methods for proper function }
  58. { set to true if DWARF debug info could be found in the file.
  59. The DwarfOffset and DwarfSize variables must be valid after setting this }
  60. DwarfOpened : Boolean;
  61. { the offset to the DWARF debug_line section in the file }
  62. DwarfOffset : Int64;
  63. { the size of the DWARF .debug_line section in the file in bytes }
  64. DwarfSize : SizeInt;
  65. {$MACRO ON}
  66. //{$DEFINE DEBUG_DWARF_PARSER}
  67. {$ifdef DEBUG_DWARF_PARSER}
  68. {$define DEBUG_WRITELN := WriteLn}
  69. {$else}
  70. {$define DEBUG_WRITELN := //}
  71. {$endif}
  72. {---------------------------------------------------------------------------
  73. I/O utility functions
  74. ---------------------------------------------------------------------------}
  75. var
  76. base, limit : SizeInt;
  77. index : SizeInt;
  78. function Init(aBase, aLimit : Int64) : Boolean;
  79. begin
  80. base := aBase;
  81. limit := aLimit;
  82. Init := (aBase + limit) <= DwarfFilesize;
  83. seek(infile, base);
  84. index := 0;
  85. end;
  86. function Init(aBase : Int64) : Boolean;
  87. begin
  88. Init := Init(aBase, limit - (aBase - base));
  89. end;
  90. function Pos() : Int64;
  91. begin
  92. Pos := index;
  93. end;
  94. procedure Seek(const newIndex : Int64);
  95. begin
  96. index := newIndex;
  97. system.seek(infile, base + index);
  98. end;
  99. { Returns the next Byte from the input stream, or -1 if there has been
  100. an error }
  101. function ReadNext() : Int;
  102. var
  103. bytesread : SizeInt;
  104. b : Byte;
  105. begin
  106. ReadNext := -1;
  107. if (index < limit) then begin
  108. blockread(infile, b, 1, bytesread);
  109. ReadNext := b;
  110. inc(index);
  111. end;
  112. if (bytesread <> 1) then
  113. ReadNext := -1;
  114. end;
  115. { Reads the next size bytes into dest. Returns true if successful,
  116. false otherwise. Note that dest may be partially overwritten after
  117. returning false. }
  118. function ReadNext(var dest; size : SizeInt) : Boolean;
  119. var
  120. bytesread : SizeInt;
  121. begin
  122. bytesread := 0;
  123. if ((index + size) < limit) then begin
  124. blockread(infile, dest, size, bytesread);
  125. inc(index, size);
  126. end;
  127. ReadNext := (bytesread = size);
  128. end;
  129. {---------------------------------------------------------------------------
  130. OS specific loaders
  131. ---------------------------------------------------------------------------}
  132. {$ifdef LINUX}
  133. {$packrecords c}
  134. { ELF Header structures types}
  135. type
  136. Elf32_Half = Word;
  137. Elf64_Half = Word;
  138. { Types for signed and unsigned 32-bit quantities. }
  139. Elf32_Word = DWord;
  140. Elf32_Sword = Longint;
  141. Elf64_Word = DWord;
  142. Elf64_Sword = Longint;
  143. { Types for signed and unsigned 64-bit quantities. }
  144. Elf32_Xword = QWord;
  145. Elf32_Sxword = Int64;
  146. Elf64_Xword = QWord;
  147. Elf64_Sxword = Int64;
  148. { Type of addresses. }
  149. Elf32_Addr = DWord;
  150. Elf64_Addr = QWord;
  151. { Type of file offsets. }
  152. Elf32_Off = DWord;
  153. Elf64_Off = QWord;
  154. { Type for section indices, which are 16-bit quantities. }
  155. Elf32_Section = Word;
  156. Elf64_Section = Word;
  157. { Type for version symbol information. }
  158. Elf32_Versym = Elf32_Half;
  159. Elf64_Versym = Elf64_Half;
  160. { some constants from the corresponding header files }
  161. const
  162. El_NIDENT = 16;
  163. { some important indices into the e_ident signature of an ELF file }
  164. EI_MAG0 = 0;
  165. EI_MAG1 = 1;
  166. EI_MAG2 = 2;
  167. EI_MAG3 = 3;
  168. EI_CLASS = 4;
  169. { the first byte of the e_ident array must be of this value }
  170. ELFMAG0 = $7f;
  171. { the second byte of the e_ident array must be of this value }
  172. ELFMAG1 = Byte('E');
  173. { the third byte of the e_ident array must be of this value }
  174. ELFMAG2 = Byte('L');
  175. { the fourth byte of the e_ident array must be of this value }
  176. ELFMAG3 = Byte('F');
  177. { the fifth byte specifies the bitness of the header; all other values are invalid }
  178. ELFCLASS32 = 1;
  179. ELFCLASS64 = 2;
  180. ELFCLASS = {$IFDEF CPU32}ELFCLASS32{$ENDIF}{$IFDEF CPU64}ELFCLASS64{$ENDIF};
  181. type
  182. { The ELF file header. This appears at the start of every ELF file, 32 bit version }
  183. TElf32_Ehdr = record
  184. e_ident : array[0..El_NIDENT-1] of Byte; { file identification }
  185. e_type : Elf32_Half; { file type }
  186. e_machine : Elf32_Half; { machine architecture }
  187. e_version : Elf32_Word; { ELF format version }
  188. e_entry : Elf32_Addr; { entry point }
  189. e_phoff : Elf32_Off; { program header file offset }
  190. e_shoff : Elf32_Off; { section header file offset }
  191. e_flags : Elf32_Word; { architecture specific flags }
  192. e_ehsize : Elf32_Half; { size of ELF header in bytes }
  193. e_phentsize : Elf32_Half; { size of program header entry }
  194. e_phnum : Elf32_Half; { number of program header entries }
  195. e_shentsize : Elf32_Half; { size of section header entry }
  196. e_shnum : Elf32_Half; { number of section header entry }
  197. e_shstrndx : Elf32_Half; { section name strings section index }
  198. end;
  199. { ELF32 Section header }
  200. TElf32_Shdr = record
  201. sh_name : Elf32_Word; { section name }
  202. sh_type : Elf32_Word; { section type }
  203. sh_flags : Elf32_Word; { section flags }
  204. sh_addr : Elf32_Addr; { virtual address }
  205. sh_offset : Elf32_Off; { file offset }
  206. sh_size : Elf32_Word; { section size }
  207. sh_link : Elf32_Word; { misc info }
  208. sh_info : Elf32_Word; { misc info }
  209. sh_addralign : Elf32_Word; { memory alignment }
  210. sh_entsize : Elf32_Word; { entry size if table }
  211. end;
  212. { The ELF file header. This appears at the start of every ELF file, 64 bit version }
  213. TElf64_Ehdr = record
  214. e_ident : array[0..El_NIDENT-1] of Byte;
  215. e_type : Elf64_Half;
  216. e_machine : Elf64_Half;
  217. e_version : Elf64_Word;
  218. e_entry : Elf64_Addr;
  219. e_phoff : Elf64_Off;
  220. e_shoff : Elf64_Off;
  221. e_flags : Elf64_Word;
  222. e_ehsize : Elf64_Half;
  223. e_phentsize : Elf64_Half;
  224. e_phnum : Elf64_Half;
  225. e_shentsize : Elf64_Half;
  226. e_shnum : Elf64_Half;
  227. e_shstrndx : Elf64_Half;
  228. end;
  229. { ELF64 Section header }
  230. TElf64_Shdr = record
  231. sh_name : Elf64_Word;
  232. sh_type : Elf64_Word;
  233. sh_flags : Elf64_Xword;
  234. sh_addr : Elf64_Addr;
  235. sh_offset : Elf64_Off;
  236. sh_size : Elf64_Xword;
  237. sh_link : Elf64_Word;
  238. sh_info : Elf64_Word;
  239. sh_addralign : Elf64_Xword;
  240. sh_entsize : Elf64_Xword;
  241. end;
  242. TElf_Shdr = {$ifdef cpu32}TElf32_Shdr{$endif}{$ifdef cpu64}TElf64_Shdr{$endif};
  243. TElf_Ehdr = {$ifdef cpu32}TElf32_Ehdr{$endif}{$ifdef cpu64}TElf64_Ehdr{$endif};
  244. { use globals to save stack space }
  245. var
  246. header : TElf_Ehdr;
  247. strtab_header : TElf_Shdr;
  248. cursec_header : TElf_Shdr;
  249. buf : array[0..20] of char;
  250. function LoadLinux() : Boolean;
  251. var
  252. i : Integer;
  253. begin
  254. LoadLinux := false;
  255. Init(0, DwarfFilesize);
  256. if (not ReadNext(header, sizeof(header))) then begin
  257. DEBUG_WRITELN('Could not read header');
  258. exit;
  259. end;
  260. { more paranoia checks }
  261. if ((header.e_ident[EI_MAG0] <> ELFMAG0) or (header.e_ident[EI_MAG1] <> ELFMAG1) or
  262. (header.e_ident[EI_MAG2] <> ELFMAG2) or (header.e_ident[EI_MAG3] <> ELFMAG3)) then begin
  263. DEBUG_WRITELN('Invalid ELF magic header. Exiting');
  264. exit;
  265. end;
  266. if (header.e_ident[EI_CLASS] <> ELFCLASS) then begin
  267. DEBUG_WRITELN('Invalid ELF header bitness. Exiting');
  268. exit;
  269. end;
  270. { check e_version = , e_shentsize > 0, e_shnum > 0 }
  271. { seek to the start of section headers }
  272. { first get string section header }
  273. Init(header.e_shoff + (header.e_shstrndx * header.e_shentsize));
  274. if (not ReadNext(strtab_header, sizeof(strtab_header))) then begin
  275. DEBUG_WRITELN('Could not read string section header');
  276. exit;
  277. end;
  278. for i := 0 to (header.e_shnum-1) do begin
  279. Init(header.e_shoff + (i * header.e_shentsize));
  280. if (not ReadNext(cursec_header, sizeof(cursec_header))) then begin
  281. DEBUG_WRITELN('Could not read next section header');
  282. exit;
  283. end;
  284. { paranoia TODO: check cursec_header.e_shentsize }
  285. Init(strtab_header.sh_offset + cursec_header.sh_name);
  286. if (not ReadNext(buf, sizeof(buf))) then begin
  287. DEBUG_WRITELN('Could not read section name');
  288. exit;
  289. end;
  290. buf[sizeof(buf)-1] := #0;
  291. DEBUG_WRITELN('This section is "', pchar(@buf[0]), '", offset ', cursec_header.sh_offset, ' size ', cursec_header.sh_size);
  292. if (pchar(@buf[0]) = '.debug_line') then begin
  293. DEBUG_WRITELN('.debug_line section found');
  294. DwarfOffset := cursec_header.sh_offset;
  295. DwarfSize := cursec_header.sh_size;
  296. { more checks }
  297. LoadLinux := (DwarfOffset >= 0) and (DwarfSize > 0);
  298. end;
  299. end;
  300. end;
  301. {$endif LINUX}
  302. {---------------------------------------------------------------------------
  303. Generic Dwarf lineinfo reader
  304. The line info reader is based on the information contained in
  305. DWARF Debugging Information Format Version 3
  306. Chapter 6.2 "Line Number Information"
  307. from the
  308. DWARF Debugging Information Format Workgroup.
  309. For more information on this document see also
  310. http://dwarf.freestandards.org/
  311. ---------------------------------------------------------------------------}
  312. procedure CloseDwarf();
  313. begin
  314. if (DwarfOpened) then
  315. close(infile);
  316. DwarfOpened := false;
  317. end;
  318. function OpenDwarf() : Boolean;
  319. var
  320. oldfilemode : Word;
  321. begin
  322. OpenDwarf := false;
  323. { open input file }
  324. assign(infile, paramstr(0));
  325. {$I-}
  326. oldfilemode := filemode;
  327. filemode := $40;
  328. reset(infile, 1);
  329. filemode := oldfilemode;
  330. {$I+}
  331. if (ioresult <> 0) then begin
  332. DEBUG_WRITELN('Could not open file');
  333. exit;
  334. end;
  335. DwarfFilesize := filesize(infile);
  336. DwarfOpened := true;
  337. { run OS specific initializer }
  338. {$ifdef linux}
  339. if (LoadLinux()) then begin
  340. OpenDwarf := true;
  341. exit;
  342. end;
  343. {$endif}
  344. CloseDwarf();
  345. end;
  346. {$packrecords default}
  347. { DWARF 2 default opcodes}
  348. const
  349. { Extended opcodes }
  350. DW_LNE_END_SEQUENCE = 1;
  351. DW_LNE_SET_ADDRESS = 2;
  352. DW_LNE_DEFINE_FILE = 3;
  353. { Standard opcodes }
  354. DW_LNS_COPY = 1;
  355. DW_LNS_ADVANCE_PC = 2;
  356. DW_LNS_ADVANCE_LINE = 3;
  357. DW_LNS_SET_FILE = 4;
  358. DW_LNS_SET_COLUMN = 5;
  359. DW_LNS_NEGATE_STMT = 6;
  360. DW_LNS_SET_BASIC_BLOCK = 7;
  361. DW_LNS_CONST_ADD_PC = 8;
  362. DW_LNS_FIXED_ADVANCE_PC = 9;
  363. DW_LNS_SET_PROLOGUE_END = 10;
  364. DW_LNS_SET_EPILOGUE_BEGIN = 11;
  365. DW_LNS_SET_ISA = 12;
  366. type
  367. { state record for the line info state machine }
  368. TMachineState = record
  369. address : QWord;
  370. file_id : DWord;
  371. line : QWord;
  372. column : DWord;
  373. is_stmt : Boolean;
  374. basic_block : Boolean;
  375. end_sequence : Boolean;
  376. prolouge_end : Boolean;
  377. epilouge_begin : Boolean;
  378. isa : DWord;
  379. append_row : Boolean;
  380. end;
  381. { DWARF line number program header preceding the line number program, 64 bit version }
  382. TLineNumberProgramHeader64 = packed record
  383. magic : DWord;
  384. unit_length : QWord;
  385. version : Word;
  386. length : QWord;
  387. minimum_instruction_length : Byte;
  388. default_is_stmt : Bool8;
  389. line_base : ShortInt;
  390. line_range : Byte;
  391. opcode_base : Byte;
  392. end;
  393. { DWARF line number program header preceding the line number program, 32 bit version }
  394. TLineNumberProgramHeader32 = packed record
  395. unit_length : DWord;
  396. version : Word;
  397. length : DWord;
  398. minimum_instruction_length : Byte;
  399. default_is_stmt : Bool8;
  400. line_base : ShortInt;
  401. line_range : Byte;
  402. opcode_base : Byte;
  403. end;
  404. { initializes the line info state to the default values }
  405. procedure InitStateRegisters(var state : TMachineState; const aIs_Stmt : Bool8);
  406. begin
  407. with state do begin
  408. address := 0;
  409. file_id := 1;
  410. line := 1;
  411. column := 0;
  412. is_stmt := aIs_Stmt;
  413. basic_block := false;
  414. end_sequence := false;
  415. prolouge_end := false;
  416. epilouge_begin := false;
  417. isa := 0;
  418. append_row := false;
  419. end;
  420. end;
  421. { Reads an unsigned LEB encoded number from the input stream }
  422. function ReadULEB128() : QWord;
  423. var
  424. shift : Byte;
  425. data : Int;
  426. val : QWord;
  427. result : QWord;
  428. begin
  429. shift := 0;
  430. result := 0;
  431. data := ReadNext();
  432. while (data <> -1) do begin
  433. val := data and $7f;
  434. result := result or (val shl shift);
  435. inc(shift, 7);
  436. if ((data and $80) = 0) then
  437. break;
  438. data := ReadNext();
  439. end;
  440. ReadULEB128 := result;
  441. end;
  442. { Reads a signed LEB encoded number from the input stream }
  443. function ReadLEB128() : Int64;
  444. var
  445. shift : Byte;
  446. data : Int;
  447. val : Int64;
  448. result : Int64;
  449. begin
  450. shift := 0;
  451. result := 0;
  452. data := ReadNext();
  453. while (data <> -1) do begin
  454. val := data and $7f;
  455. result := result or (val shl shift);
  456. inc(shift, 7);
  457. if ((data and $80) = 0) then
  458. break;
  459. data := ReadNext();
  460. end;
  461. { extend sign. Note that we can not use shl/shr since the latter does not
  462. translate to arithmetic shifting for signed types }
  463. result := (not ((result and (1 shl (shift-1)))-1)) or result;
  464. ReadLEB128 := result;
  465. end;
  466. { Reads an address from the current input stream }
  467. function ReadAddress() : PtrUInt;
  468. var
  469. result : PtrUInt;
  470. begin
  471. ReadNext(result, sizeof(result));
  472. ReadAddress := result;
  473. end;
  474. { Reads a zero-terminated string from the current input stream. If the
  475. string is larger than 255 chars (maximum allowed number of elements in
  476. a ShortString, excess characters will be chopped off. }
  477. function ReadString() : ShortString;
  478. var
  479. temp : Int;
  480. i : UInt;
  481. result : ShortString;
  482. begin
  483. i := 1;
  484. temp := ReadNext();
  485. while (temp > 0) do begin
  486. result[i] := char(temp);
  487. if (i = 255) then begin
  488. { skip remaining characters }
  489. repeat
  490. temp := ReadNext();
  491. until (temp <= 0);
  492. break;
  493. end;
  494. inc(i);
  495. temp := ReadNext();
  496. end;
  497. { unexpected end of file occurred? }
  498. if (temp = -1) then
  499. result := ''
  500. else
  501. Byte(result[0]) := i-1;
  502. ReadString := result;
  503. end;
  504. { Reads an unsigned Half from the current input stream }
  505. function ReadUHalf() : Word;
  506. var
  507. result : Word;
  508. begin
  509. ReadNext(result, sizeof(result));
  510. ReadUHalf := result;
  511. end;
  512. { Skips all line info directory entries }
  513. procedure SkipDirectories();
  514. var s : ShortString;
  515. begin
  516. while (true) do begin
  517. s := ReadString();
  518. if (s = '') then break;
  519. DEBUG_WRITELN('Skipping directory : ', s);
  520. end;
  521. end;
  522. { Skips an LEB128 }
  523. procedure SkipLEB128();
  524. {$ifdef DEBUG_DWARF_PARSER}
  525. var temp : QWord;
  526. {$endif}
  527. begin
  528. {$ifdef DEBUG_DWARF_PARSER}temp := {$endif}ReadLEB128();
  529. DEBUG_WRITELN('Skipping LEB128 : ', temp);
  530. end;
  531. { Skips the filename section from the current file stream }
  532. procedure SkipFilenames();
  533. var s : ShortString;
  534. begin
  535. while (true) do begin
  536. s := ReadString();
  537. if (s = '') then break;
  538. DEBUG_WRITELN('Skipping filename : ', s);
  539. SkipLEB128(); { skip the directory index for the file }
  540. SkipLEB128(); { skip last modification time for file }
  541. SkipLEB128(); { skip length of file }
  542. end;
  543. end;
  544. function CalculateAddressIncrement(opcode : Byte; const header : TLineNumberProgramHeader64) : Int64;
  545. var
  546. result : Int64;
  547. begin
  548. result := (Int64(opcode) - header.opcode_base) div header.line_range * header.minimum_instruction_length;
  549. CalculateAddressIncrement := result;
  550. end;
  551. function GetFullFilename(const filenameStart, directoryStart : Int64; const file_id : DWord) : ShortString;
  552. var
  553. i : DWord;
  554. filename, directory : ShortString;
  555. dirindex : Int64;
  556. begin
  557. filename := '';
  558. directory := '';
  559. i := 1;
  560. Seek(filenameStart);
  561. while (i <= file_id) do begin
  562. filename := ReadString();
  563. DEBUG_WRITELN('Found "', filename, '"');
  564. if (filename = '') then break;
  565. dirindex := ReadLEB128(); { read the directory index for the file }
  566. SkipLEB128(); { skip last modification time for file }
  567. SkipLEB128(); { skip length of file }
  568. inc(i);
  569. end;
  570. { if we could not find the file index, exit }
  571. if (filename = '') then begin
  572. GetFullFilename := '(Unknown file)';
  573. exit;
  574. end;
  575. Seek(directoryStart);
  576. i := 1;
  577. while (i <= dirindex) do begin
  578. directory := ReadString();
  579. if (directory = '') then break;
  580. inc(i);
  581. end;
  582. GetFullFilename := directory + filename;
  583. end;
  584. function ParseCompilationUnit(const addr : PtrUInt; const file_offset : QWord;
  585. var source : String; var line : longint; var found : Boolean) : QWord;
  586. var
  587. state : TMachineState;
  588. { we need both headers on the stack, although we only use the 64 bit one internally }
  589. header64 : TLineNumberProgramHeader64;
  590. header32 : TLineNumberProgramHeader32;
  591. adjusted_opcode : Int64;
  592. opcode : Int;
  593. extended_opcode : Byte;
  594. extended_opcode_length : Int;
  595. i, addrIncrement, lineIncrement : Int;
  596. {$ifdef DEBUG_DWARF_PARSER}
  597. s : ShortString;
  598. {$endif}
  599. numoptable : array[1..255] of Byte;
  600. { the offset into the file where the include directories are stored for this compilation unit }
  601. include_directories : QWord;
  602. { the offset into the file where the file names are stored for this compilation unit }
  603. file_names : Int64;
  604. temp_length : DWord;
  605. unit_length : QWord;
  606. header_length : SizeInt;
  607. first_row : Boolean;
  608. prev_line : QWord;
  609. prev_file : DWord;
  610. begin
  611. prev_line := 0;
  612. prev_file := 0;
  613. first_row := true;
  614. found := false;
  615. ReadNext(temp_length, sizeof(temp_length));
  616. if (temp_length <> $ffffffff) then begin
  617. unit_length := temp_length + sizeof(temp_length)
  618. end else begin
  619. ReadNext(unit_length, sizeof(unit_length));
  620. inc(unit_length, 12);
  621. end;
  622. ParseCompilationUnit := file_offset + unit_length;
  623. Init(file_offset, unit_length);
  624. DEBUG_WRITELN('Unit length: ', unit_length);
  625. if (temp_length <> $ffffffff) then begin
  626. DEBUG_WRITELN('32 bit DWARF detected');
  627. ReadNext(header32, sizeof(header32));
  628. header64.magic := $ffffffff;
  629. header64.unit_length := header32.unit_length;
  630. header64.version := header32.version;
  631. header64.length := header32.length;
  632. header64.minimum_instruction_length := header32.minimum_instruction_length;
  633. header64.default_is_stmt := header32.default_is_stmt;
  634. header64.line_base := header32.line_base;
  635. header64.line_range := header32.line_range;
  636. header64.opcode_base := header32.opcode_base;
  637. header_length :=
  638. sizeof(header32.length) + sizeof(header32.version) +
  639. sizeof(header32.unit_length);
  640. end else begin
  641. DEBUG_WRITELN('64 bit DWARF detected');
  642. ReadNext(header64, sizeof(header64));
  643. header_length :=
  644. sizeof(header64.magic) + sizeof(header64.version) +
  645. sizeof(header64.length) + sizeof(header64.unit_length);
  646. end;
  647. inc(header_length, header64.length);
  648. fillchar(numoptable, sizeof(numoptable), #0);
  649. ReadNext(numoptable, header64.opcode_base-1);
  650. DEBUG_WRITELN('Opcode parameter count table');
  651. for i := 1 to header64.opcode_base-1 do begin
  652. DEBUG_WRITELN('Opcode[', i, '] - ', numoptable[i], ' parameters');
  653. end;
  654. DEBUG_WRITELN('Reading directories...');
  655. include_directories := Pos();
  656. SkipDirectories();
  657. DEBUG_WRITELN('Reading filenames...');
  658. file_names := Pos();
  659. SkipFilenames();
  660. Seek(header_length);
  661. with header64 do begin
  662. InitStateRegisters(state, default_is_stmt);
  663. end;
  664. opcode := ReadNext();
  665. while (opcode <> -1) and (not found) do begin
  666. DEBUG_WRITELN('Next opcode: ');
  667. case (opcode) of
  668. { extended opcode }
  669. 0 : begin
  670. extended_opcode_length := ReadULEB128();
  671. extended_opcode := ReadNext();
  672. case (extended_opcode) of
  673. DW_LNE_END_SEQUENCE : begin
  674. state.end_sequence := true;
  675. state.append_row := true;
  676. DEBUG_WRITELN('DW_LNE_END_SEQUENCE');
  677. end;
  678. DW_LNE_SET_ADDRESS : begin
  679. state.address := ReadAddress();
  680. DEBUG_WRITELN('DW_LNE_SET_ADDRESS (', hexstr(state.address, sizeof(state.address)*2), ')');
  681. end;
  682. DW_LNE_DEFINE_FILE : begin
  683. {$ifdef DEBUG_DWARF_PARSER}s := {$endif}ReadString();
  684. SkipLEB128();
  685. SkipLEB128();
  686. SkipLEB128();
  687. DEBUG_WRITELN('DW_LNE_DEFINE_FILE (', s, ')');
  688. end;
  689. else begin
  690. DEBUG_WRITELN('Unknown extended opcode (opcode ', extended_opcode, ' length ', extended_opcode_length, ')');
  691. for i := 0 to extended_opcode_length-2 do
  692. ReadNext();
  693. end;
  694. end;
  695. end;
  696. DW_LNS_COPY : begin
  697. state.basic_block := false;
  698. state.prolouge_end := false;
  699. state.epilouge_begin := false;
  700. state.append_row := true;
  701. DEBUG_WRITELN('DW_LNS_COPY');
  702. end;
  703. DW_LNS_ADVANCE_PC : begin
  704. inc(state.address, ReadULEB128() * header64.minimum_instruction_length);
  705. DEBUG_WRITELN('DW_LNS_ADVANCE_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
  706. end;
  707. DW_LNS_ADVANCE_LINE : begin
  708. inc(state.line, ReadLEB128());
  709. DEBUG_WRITELN('DW_LNS_ADVANCE_LINE (', state.line, ')');
  710. end;
  711. DW_LNS_SET_FILE : begin
  712. state.file_id := ReadULEB128();
  713. DEBUG_WRITELN('DW_LNS_SET_FILE (', state.file_id, ')');
  714. end;
  715. DW_LNS_SET_COLUMN : begin
  716. state.column := ReadULEB128();
  717. DEBUG_WRITELN('DW_LNS_SET_COLUMN (', state.column, ')');
  718. end;
  719. DW_LNS_NEGATE_STMT : begin
  720. state.is_stmt := not state.is_stmt;
  721. DEBUG_WRITELN('DW_LNS_NEGATE_STMT (', state.is_stmt, ')');
  722. end;
  723. DW_LNS_SET_BASIC_BLOCK : begin
  724. state.basic_block := true;
  725. DEBUG_WRITELN('DW_LNS_SET_BASIC_BLOCK');
  726. end;
  727. DW_LNS_CONST_ADD_PC : begin
  728. inc(state.address, CalculateAddressIncrement(255, header64));
  729. DEBUG_WRITELN('DW_LNS_CONST_ADD_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
  730. end;
  731. DW_LNS_FIXED_ADVANCE_PC : begin
  732. inc(state.address, ReadUHalf());
  733. DEBUG_WRITELN('DW_LNS_FIXED_ADVANCE_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
  734. end;
  735. DW_LNS_SET_PROLOGUE_END : begin
  736. state.prolouge_end := true;
  737. DEBUG_WRITELN('DW_LNS_SET_PROLOGUE_END');
  738. end;
  739. DW_LNS_SET_EPILOGUE_BEGIN : begin
  740. state.epilouge_begin := true;
  741. DEBUG_WRITELN('DW_LNS_SET_EPILOGUE_BEGIN');
  742. end;
  743. DW_LNS_SET_ISA : begin
  744. state.isa := ReadULEB128();
  745. DEBUG_WRITELN('DW_LNS_SET_ISA (', state.isa, ')');
  746. end;
  747. else begin { special opcode }
  748. if (opcode < header64.opcode_base) then begin
  749. DEBUG_WRITELN('Unknown standard opcode $', hexstr(opcode, 2), '; skipping');
  750. for i := 1 to numoptable[opcode] do
  751. SkipLEB128();
  752. end else begin
  753. adjusted_opcode := opcode - header64.opcode_base;
  754. addrIncrement := CalculateAddressIncrement(opcode, header64);
  755. inc(state.address, addrIncrement);
  756. lineIncrement := header64.line_base + (adjusted_opcode mod header64.line_range);
  757. inc(state.line, lineIncrement);
  758. DEBUG_WRITELN('Special opcode $', hexstr(opcode, 2), ' address increment: ', addrIncrement, ' new line: ', lineIncrement);
  759. state.basic_block := false;
  760. state.prolouge_end := false;
  761. state.epilouge_begin := false;
  762. state.append_row := true;
  763. end;
  764. end;
  765. end;
  766. if (state.append_row) then begin
  767. DEBUG_WRITELN('Current state : address = ', hexstr(state.address, sizeof(state.address) * 2), ' file_id = ', state.file_id, ' line = ', state.line, ' column = ', state.column, ' is_stmt = ', state.is_stmt, ' basic_block = ', state.basic_block, ' end_sequence = ', state.end_sequence, ' prolouge_end = ', state.prolouge_end, ' epilouge_begin = ', state.epilouge_begin, ' isa = ', state.isa);
  768. if (first_row) then begin
  769. if (state.address > addr) then
  770. break;
  771. first_row := false;
  772. end;
  773. found := (state.address >= addr);
  774. { use the previous line/file information if the current address is larger
  775. than the requested address }
  776. if (found) and (state.address > addr) then begin
  777. state.line := prev_line;
  778. state.file_id := prev_file;
  779. end;
  780. { save old state information }
  781. prev_file := state.file_id;
  782. prev_line := state.line;
  783. state.append_row := false;
  784. if (state.end_sequence) then begin
  785. InitStateRegisters(state, header64.default_is_stmt);
  786. end;
  787. end;
  788. opcode := ReadNext();
  789. end;
  790. if (found) then begin
  791. line := state.line;
  792. source := GetFullFilename(file_names, include_directories, state.file_id);
  793. end;
  794. end;
  795. procedure GetLineInfo(addr : ptruint; var func, source : string; var line : longint);
  796. var
  797. current_offset : QWord;
  798. end_offset : QWord;
  799. found : Boolean;
  800. begin
  801. func := '';
  802. source := '';
  803. found := false;
  804. if (not DwarfOpened) and (not OpenDwarf()) then
  805. exit;
  806. current_offset := DwarfOffset;
  807. end_offset := DwarfOffset + DwarfSize;
  808. while (current_offset < end_offset) and (not found) do begin
  809. Init(current_offset, end_offset - current_offset);
  810. current_offset := ParseCompilationUnit(addr, current_offset,
  811. source, line, found);
  812. end;
  813. end;
  814. function DwarfBackTraceStr(addr : Pointer) : shortstring;
  815. var
  816. func,
  817. source : string;
  818. hs : string[32];
  819. line : longint;
  820. Store : TBackTraceStrFunc;
  821. begin
  822. { reset to prevent infinite recursion if problems inside the code }
  823. Store := BackTraceStrFunc;
  824. BackTraceStrFunc := @SysBackTraceStr;
  825. GetLineInfo(ptruint(addr), func, source, line);
  826. { create string }
  827. DwarfBackTraceStr :=' $' + HexStr(ptrint(addr), sizeof(ptrint) * 2);
  828. if func<>'' then
  829. DwarfBackTraceStr := DwarfBackTraceStr + ' ' + func;
  830. if source<>'' then begin
  831. if func<>'' then
  832. DwarfBackTraceStr := DwarfBackTraceStr + ', ';
  833. if line<>0 then begin
  834. str(line, hs);
  835. DwarfBackTraceStr := DwarfBackTraceStr + ' line ' + hs;
  836. end;
  837. DwarfBackTraceStr := DwarfBackTraceStr + ' of ' + source;
  838. end;
  839. if (DwarfOpened) then
  840. BackTraceStrFunc := Store;
  841. end;
  842. initialization
  843. DwarfOpened := false;
  844. BackTraceStrFunc := @DwarfBacktraceStr;
  845. finalization
  846. CloseDwarf();
  847. end.