dbgstabx.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. {
  2. Copyright (c) 2012 by Jonas Maebe
  3. This units contains support for STABX debug info generation
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit dbgstabx;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,globtype,
  22. dbgbase,dbgstabs,cgbase,
  23. symtype,symdef,symsym,symtable,symbase,
  24. aasmtai,aasmdata;
  25. type
  26. TDebugInfoStabx = class(TDebugInfoStabs)
  27. protected
  28. function staticvarsym_mangled_name(sym: tstaticvarsym): string; override;
  29. procedure maybe_add_vmt_sym(list: TAsmList; def: tobjectdef); override;
  30. procedure write_def_stabstr(list:TAsmList;def:tdef;const ss:ansistring);override;
  31. function base_stabs_str(const typ, other, desc, value: ansistring): ansistring;overload;override;
  32. function gen_procdef_startsym_stabs(def: tprocdef): TAsmList; override;
  33. function gen_procdef_endsym_stabs(def: tprocdef): TAsmList; override;
  34. procedure appendsym_label(list: TAsmList; sym: tlabelsym); override;
  35. procedure appendsym_staticvar(list: TAsmList; sym: tstaticvarsym); override;
  36. public
  37. procedure insertlineinfo(list:TAsmList);override;
  38. procedure insertmoduleinfo; override;
  39. procedure referencesections(list: TAsmList); override;
  40. constructor create;override;
  41. end;
  42. implementation
  43. uses
  44. globals,cutils,cfileutl,verbose,
  45. systems,finput,fmodule,
  46. aasmbase,
  47. symconst;
  48. const
  49. STABX_N_GSYM = $80;
  50. STABX_N_LSYM = $81;
  51. STABX_N_PSYM = $82;
  52. STABX_N_RSYM = $83;
  53. STABX_N_RPSYM = $84;
  54. STABX_N_STSYM = $85;
  55. STABX_N_LCSYM = 255;
  56. STABX_N_Function = $8e;
  57. STABX_N_TextLine = 255;
  58. STABX_N_DataLine = 255;
  59. STABX_N_BssLine = 255;
  60. STABX_N_DECL = $8c;
  61. STABX_N_tsym = $86;
  62. STABX_N_SourceFile = 255;
  63. STABX_N_OSO = 255;
  64. STABX_N_IncludeFile = 255;
  65. STABX_N_BINCL = 255;
  66. STABX_N_EINCL = 255;
  67. STABX_N_LBRAC = 255;
  68. STABX_N_EXCL = 255;
  69. STABX_N_RBRAC = 255;
  70. { TDebugInfoStabx }
  71. function TDebugInfoStabx.base_stabs_str(const typ, other, desc, value: ansistring): ansistring;
  72. begin
  73. { no other/desc }
  74. result:=value+','+typ+',0';
  75. end;
  76. function TDebugInfoStabx.staticvarsym_mangled_name(sym: tstaticvarsym): string;
  77. begin
  78. { create reference to the local symbol at the same address as the global
  79. symbol (with same name as unmangled symbol, so GDB can find it) }
  80. Result:=ReplaceForbiddenAsmSymbolChars(sym.name);
  81. end;
  82. procedure TDebugInfoStabx.maybe_add_vmt_sym(list: TAsmList; def: tobjectdef);
  83. begin
  84. (*
  85. if assigned(def.owner) and
  86. def.owner.iscurrentunit then
  87. begin
  88. if (oo_has_vmt in def.objectoptions) and
  89. assigned(def.owner.name) then
  90. list.concat(Tai_stab.create_ansistr(stabsdir,ansistring('"vmt_')+GetSymTableName(def.owner)+tobjectdef(def).objname^+':S'+
  91. def_stab_number(vmttype)+'",'+
  92. base_stabs_str(globalvarsym_inited_stab,'0','0',ReplaceForbiddenAsmSymbolChars(tobjectdef(def).vmt_mangledname)+'.')));
  93. end;
  94. *)
  95. { do nothing, because creating debug information for a global symbol
  96. defined in another unit is not possible for stabx given the FPC
  97. constraints (namely that the name of the global symbol does not match
  98. the name of the variable). If it's in the same unit, we have to add an
  99. extra symbol for the vmt with the same variable name as what we have
  100. here (ansistring('"vmt_')+GetSymTableName(def.owner)+tobjectdef(def).objname^).
  101. We'd have to do that when that symbol is created, in generic code,
  102. which is not very clean, and moreover these symbols are not really
  103. used for anything currently, afaik }
  104. end;
  105. procedure TDebugInfoStabx.write_def_stabstr(list:TAsmList;def:tdef;const ss:ansistring);
  106. var
  107. stabchar,
  108. symname,
  109. declstabnr,
  110. st : ansistring;
  111. begin
  112. { type prefix }
  113. if use_tag_prefix(def) then
  114. stabchar := tagtypeprefix
  115. else
  116. stabchar := 't';
  117. { in case of writing the class record structure, we always have to
  118. use the class name (so it refers both to the struct and the
  119. pointer to the struct), otherwise gdb crashes (see tests/webtbs/tw9766.pp) }
  120. if is_class(def) and
  121. tobjectdef(def).writing_class_record_dbginfo then
  122. begin
  123. declstabnr:=def_stab_classnumber(tobjectdef(def));
  124. symname:='${sym_name}'
  125. end
  126. else
  127. begin
  128. { Type names for types defined in the current unit are already written in
  129. the typesym }
  130. if (def.owner.symtabletype=globalsymtable) and
  131. not(def.owner.iscurrentunit) then
  132. symname:='${sym_name}'
  133. else
  134. symname:='';
  135. declstabnr:=def_stab_number(def)
  136. end;
  137. if (symname='') or
  138. not(use_tag_prefix(def)) then
  139. begin
  140. st:=def_stabstr_evaluate(def,':$1$2=',[stabchar,declstabnr]);
  141. st:='"'+def_stabstr_evaluate(def,symname,[])+st+ss;
  142. { line info is set to 0 for all defs, because the def can be in another
  143. unit and then the linenumber is invalid in the current sourcefile }
  144. st:=st+'",'+base_stabs_str(def_stab,'0','0','0');
  145. { add to list }
  146. list.concat(Tai_stab.create_ansistr(stabsdir,st));
  147. end
  148. else
  149. begin
  150. { first tag, then type decl }
  151. inc(global_stab_number);
  152. st:=def_stabstr_evaluate(def,':$1$2=',[stabchar,tostr(global_stab_number)]);
  153. st:='"'+st+ss;
  154. st:=st+'",'+base_stabs_str(def_stab,'0','0','0');
  155. list.concat(Tai_stab.create_ansistr(stabsdir,st));
  156. st:='"'+def_stabstr_evaluate(def,symname+':t$1=$2',[declstabnr,tostr(global_stab_number)]);
  157. st:=st+'",'+base_stabs_str(def_stab,'0','0','0');
  158. list.concat(Tai_stab.create_ansistr(stabsdir,st));
  159. end;
  160. end;
  161. function TDebugInfoStabx.gen_procdef_startsym_stabs(def: tprocdef): TAsmList;
  162. var
  163. mangledname: ansistring;
  164. hp, hpp, inclstart: tai;
  165. begin
  166. result:=inherited;
  167. { can happen for procdefs defined in other units, this code is only for
  168. the place where it is defined }
  169. if not assigned(def.procstarttai) then
  170. exit;
  171. mangledname:=ReplaceForbiddenAsmSymbolChars(def.mangledname);
  172. if target_info.system in systems_dotted_function_names then
  173. mangledname:='.'+mangledname;
  174. result.concat(tai_stab.create(stabx_function,
  175. strpnew(mangledname+','+mangledname+',16,044,LT.'+mangledname+'-'+mangledname)));
  176. { hoist the already generated ".bf" up right after the function
  177. definition so that all parameter and local variable definitions come
  178. after it -- we have to generate it during lineinfo generation and not
  179. here to make sure it takes into account include files opened right after
  180. the function definition but before the code starts
  181. -- also move include file start if any}
  182. hp:=def.procstarttai;
  183. inclstart:=nil;
  184. while (hp.typ<>ait_symbol_end) and
  185. ((hp.typ<>ait_stab) or
  186. (tai_stab(hp).stabtype<>stabx_bf)) do
  187. begin
  188. if (hp.typ=ait_stab) and
  189. (tai_stab(hp).stabtype=stabx_bi) then
  190. inclstart:=hp;
  191. hp:=tai(hp.next);
  192. end;
  193. { happens for implicit unit init routines and the like, they don't get
  194. line info }
  195. if hp.typ=ait_symbol_end then
  196. exit;
  197. if assigned(inclstart) then
  198. begin
  199. current_asmdata.asmlists[al_procedures].Remove(inclstart);
  200. result.concat(inclstart);
  201. end;
  202. current_asmdata.asmlists[al_procedures].Remove(hp);
  203. result.concat(hp);
  204. { also hoist up the function start symbol(s) }
  205. hp:=def.procstarttai;
  206. while assigned(hp) and
  207. (hp.typ<>ait_symbol_end) do
  208. begin
  209. if (hp.typ=ait_symbol) and
  210. (tai_symbol(hp).sym.typ=AT_FUNCTION) then
  211. begin
  212. hpp:=tai(hp.next);
  213. if hp=def.procstarttai then
  214. def.procstarttai:=hpp;
  215. current_asmdata.asmlists[al_procedures].Remove(hp);
  216. result.insert(hp);
  217. hp:=hpp;
  218. end
  219. else
  220. hp:=tai(hp.next);
  221. end;
  222. end;
  223. function TDebugInfoStabx.gen_procdef_endsym_stabs(def: tprocdef): TAsmList;
  224. var
  225. procendsymbol: tasmsymbol;
  226. begin
  227. result:=inherited gen_procdef_endsym_stabs(def);
  228. if not assigned(def.procstarttai) then
  229. exit;
  230. procendsymbol:=current_asmdata.DefineAsmSymbol('LT..'+ReplaceForbiddenAsmSymbolChars(def.mangledname),AB_LOCAL,AT_ADDR,voidpointertype);
  231. current_asmdata.asmlists[al_procedures].insertbefore(tai_symbol.create(procendsymbol,0),def.procendtai);
  232. end;
  233. procedure TDebugInfoStabx.appendsym_label(list: TAsmList; sym: tlabelsym);
  234. begin
  235. // ignore, not sure what kind of debug information we could generate for
  236. // this
  237. end;
  238. procedure TDebugInfoStabx.appendsym_staticvar(list: TAsmList; sym: tstaticvarsym);
  239. var
  240. ismem,
  241. isglobal: boolean;
  242. begin
  243. if vo_is_external in sym.varoptions then
  244. exit;
  245. ismem:=not(sym.localloc.loc in [LOC_REGISTER,LOC_CREGISTER,LOC_MMREGISTER,LOC_CMMREGISTER,LOC_FPUREGISTER,LOC_CFPUREGISTER]);
  246. isglobal:=false;
  247. if ismem then
  248. isglobal:=current_asmdata.RefAsmSymbol(sym.mangledname,AT_DATA).bind=AB_GLOBAL;
  249. { put extra ss/es markers in place }
  250. if ismem then
  251. if isglobal then
  252. list.concat(tai_stab.Create_ansistr(stabx_bs,'.data[RW]'))
  253. else
  254. list.concat(tai_stab.Create_ansistr(stabx_bs,'_data.bss_'));
  255. inherited;
  256. if ismem then
  257. list.concat(tai_stab.Create_ansistr(stabx_es,''));
  258. end;
  259. procedure TDebugInfoStabx.insertlineinfo(list: TAsmList);
  260. var
  261. currfileinfo,
  262. lastfileinfo,
  263. curincludefileinfo,
  264. curfunstartfileinfo: tfileposinfo;
  265. currsectype : TAsmSectiontype;
  266. hp, inclinsertpos, last : tai;
  267. infile : tinputfile;
  268. linenr, stabx_func_level,
  269. nolineinfolevel: longint;
  270. nextlineisfunstart: boolean;
  271. begin
  272. FillChar(currfileinfo,sizeof(currfileinfo),0);
  273. FillChar(lastfileinfo,sizeof(lastfileinfo),0);
  274. FillChar(curincludefileinfo,sizeof(curincludefileinfo),0);
  275. FillChar(curfunstartfileinfo,sizeof(curfunstartfileinfo),0);
  276. currsectype:=sec_code;
  277. hp:=Tai(list.first);
  278. nextlineisfunstart:=false;
  279. nolineinfolevel:=0;
  280. stabx_func_level:=0;
  281. last:=nil;
  282. while assigned(hp) do
  283. begin
  284. case hp.typ of
  285. ait_section :
  286. currsectype:=tai_section(hp).sectype;
  287. ait_force_line :
  288. lastfileinfo.line:=-1;
  289. ait_symbol:
  290. if tai_symbol(hp).sym.typ = AT_FUNCTION then
  291. nextlineisfunstart:=true;
  292. ait_symbol_end:
  293. if tai_symbol_end(hp).sym.typ = AT_FUNCTION then
  294. begin
  295. { end of function }
  296. if stabx_func_level > 0 then
  297. begin
  298. list.insertbefore(Tai_stab.Create_str(stabx_ef,tostr(currfileinfo.line)),hp);
  299. dec(stabx_func_level);
  300. end;
  301. end;
  302. ait_marker :
  303. begin
  304. case tai_marker(hp).kind of
  305. mark_NoLineInfoStart:
  306. inc(nolineinfolevel);
  307. mark_NoLineInfoEnd:
  308. dec(nolineinfolevel);
  309. else
  310. ;
  311. end;
  312. end;
  313. else
  314. ;
  315. end;
  316. if (currsectype=sec_code) and
  317. (hp.typ=ait_instruction) then
  318. begin
  319. currfileinfo:=tailineinfo(hp).fileinfo;
  320. inclinsertpos:=hp;
  321. while assigned(inclinsertpos.previous) and
  322. (tai(inclinsertpos.previous).typ in (SkipInstr+[ait_marker])) do
  323. inclinsertpos:=tai(inclinsertpos.previous);
  324. { file changed ? (must be before line info) }
  325. if (currfileinfo.fileindex<>0) and
  326. ((lastfileinfo.fileindex<>currfileinfo.fileindex) or
  327. (lastfileinfo.moduleindex<>currfileinfo.moduleindex)) then
  328. begin
  329. if curincludefileinfo.fileindex<>0 then
  330. begin
  331. infile:=get_module(curincludefileinfo.moduleindex).sourcefiles.get_file(curincludefileinfo.fileindex);
  332. list.insertbefore(Tai_stab.Create_str(stabx_ei,'"'+FixFileName(infile.name)+'"'),inclinsertpos);
  333. curincludefileinfo.fileindex:=0;
  334. end;
  335. if currfileinfo.fileindex<>1 then
  336. begin
  337. infile:=get_module(currfileinfo.moduleindex).sourcefiles.get_file(currfileinfo.fileindex);
  338. if assigned(infile) then
  339. begin
  340. list.insertbefore(Tai_stab.Create_str(stabx_bi,'"'+FixFileName(infile.name)+'"'),inclinsertpos);
  341. curincludefileinfo:=currfileinfo;
  342. { force new line info }
  343. lastfileinfo.line:=-1;
  344. end;
  345. end
  346. else
  347. lastfileinfo.line:=-1;
  348. if nextlineisfunstart then
  349. begin
  350. curfunstartfileinfo:=currfileinfo;
  351. { insert here rather than via procdef, because the procdef
  352. may have been created in another file in case the body
  353. is completely declared in an include file }
  354. list.insertbefore(Tai_stab.Create_str(stabx_bf,tostr(currfileinfo.line)),hp);
  355. inc(stabx_func_level);
  356. { -1 to avoid outputting a relative line 0 in the
  357. function, because that means something different }
  358. dec(curfunstartfileinfo.line);
  359. nextlineisfunstart:=false;
  360. end;
  361. end;
  362. { implicit functions have no file information }
  363. if nextlineisfunstart then
  364. begin
  365. list.insertbefore(Tai_stab.Create_str(stabx_bf,tostr(currfileinfo.line)),hp);
  366. inc(stabx_func_level);
  367. nextlineisfunstart:=false;
  368. end;
  369. if nolineinfolevel=0 then
  370. begin
  371. { line changed ? }
  372. if (currfileinfo.line>lastfileinfo.line) and
  373. (currfileinfo.line<>0) then
  374. begin
  375. linenr:=currfileinfo.line;
  376. { line numbers in AIX are relative to the function start line
  377. (except if they are in a different file then where the
  378. function started!) }
  379. if (currfileinfo.fileindex=curfunstartfileinfo.fileindex) and
  380. (currfileinfo.moduleindex=curfunstartfileinfo.moduleindex) then
  381. dec(linenr,curfunstartfileinfo.line);
  382. { can be < 0 in case of bugs in the compiler }
  383. if (linenr > 0)
  384. {$ifndef cpu64bitaddr}
  385. { line numbers are unsigned short in 32 bit xcoff }
  386. and (linenr<=high(word))
  387. {$endif}
  388. then
  389. list.insertbefore(Tai_stab.Create_str(stabx_line,tostr(linenr)),hp);
  390. end;
  391. lastfileinfo:=currfileinfo;
  392. end;
  393. end;
  394. last:=hp;
  395. hp:=tai(hp.next);
  396. end;
  397. { close include file if still open }
  398. if curincludefileinfo.fileindex<>0 then
  399. begin
  400. infile:=get_module(curincludefileinfo.moduleindex).sourcefiles.get_file(curincludefileinfo.fileindex);
  401. list.insertbefore(Tai_stab.Create_str(stabx_ei,'"'+FixFileName(infile.name)+'"'),last);
  402. curincludefileinfo.fileindex:=0;
  403. end;
  404. end;
  405. procedure TDebugInfoStabx.insertmoduleinfo;
  406. begin
  407. // do nothing
  408. end;
  409. procedure TDebugInfoStabx.referencesections(list: TAsmList);
  410. begin
  411. // do nothing
  412. end;
  413. constructor TDebugInfoStabx.create;
  414. begin
  415. inherited create;
  416. dbgtype:=dbg_stabx;
  417. stabsdir:=stab_stabx;
  418. def_stab:=STABX_N_DECL;
  419. regvar_stab:=STABX_N_RPSYM;
  420. procdef_stab:=STABX_N_Function;
  421. constsym_stab:=STABX_N_GSYM;
  422. typesym_stab:=STABX_N_DECL;
  423. globalvarsym_uninited_stab:=STABX_N_STSYM;
  424. globalvarsym_inited_stab:=STABX_N_STSYM;
  425. staticvarsym_uninited_stab:=STABX_N_STSYM;
  426. staticvarsym_inited_stab:=STABX_N_STSYM;
  427. localvarsymref_stab:=STABX_N_LSYM;
  428. paravarsymref_stab:=STABX_N_PSYM;
  429. tagtypeprefix:='T';
  430. end;
  431. const
  432. dbg_stabx_info : tdbginfo =
  433. (
  434. id : dbg_stabx;
  435. idtxt : 'STABX';
  436. );
  437. initialization
  438. RegisterDebugInfo(dbg_stabx_info,TDebugInfoStabx);
  439. end.