lnfodwrf.pp 27 KB

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