dbgstabx.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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 def.typ in tagtypes 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(def.typ in tagtypes) 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);
  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).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. i,
  269. linenr,
  270. nolineinfolevel: longint;
  271. nextlineisfunstart: boolean;
  272. begin
  273. FillChar(currfileinfo,sizeof(currfileinfo),0);
  274. FillChar(lastfileinfo,sizeof(lastfileinfo),0);
  275. FillChar(curincludefileinfo,sizeof(curincludefileinfo),0);
  276. FillChar(curfunstartfileinfo,sizeof(curfunstartfileinfo),0);
  277. currsectype:=sec_code;
  278. hp:=Tai(list.first);
  279. nextlineisfunstart:=false;
  280. nolineinfolevel:=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. list.insertbefore(Tai_stab.Create_str(stabx_ef,tostr(currfileinfo.line)),hp);
  297. end;
  298. ait_marker :
  299. begin
  300. case tai_marker(hp).kind of
  301. mark_NoLineInfoStart:
  302. inc(nolineinfolevel);
  303. mark_NoLineInfoEnd:
  304. dec(nolineinfolevel);
  305. end;
  306. end;
  307. end;
  308. if (currsectype=sec_code) and
  309. (hp.typ=ait_instruction) then
  310. begin
  311. currfileinfo:=tailineinfo(hp).fileinfo;
  312. inclinsertpos:=hp;
  313. while assigned(inclinsertpos.previous) and
  314. (tai(inclinsertpos.previous).typ in (SkipInstr+[ait_marker])) do
  315. inclinsertpos:=tai(inclinsertpos.previous);
  316. { file changed ? (must be before line info) }
  317. if (currfileinfo.fileindex<>0) and
  318. ((lastfileinfo.fileindex<>currfileinfo.fileindex) or
  319. (lastfileinfo.moduleindex<>currfileinfo.moduleindex)) then
  320. begin
  321. if curincludefileinfo.fileindex<>0 then
  322. begin
  323. infile:=get_module(curincludefileinfo.moduleindex).sourcefiles.get_file(curincludefileinfo.fileindex);
  324. list.insertbefore(Tai_stab.Create_str(stabx_ei,'"'+FixFileName(infile.name)+'"'),inclinsertpos);
  325. curincludefileinfo.fileindex:=0;
  326. end;
  327. if currfileinfo.fileindex<>1 then
  328. begin
  329. infile:=get_module(currfileinfo.moduleindex).sourcefiles.get_file(currfileinfo.fileindex);
  330. if assigned(infile) then
  331. begin
  332. list.insertbefore(Tai_stab.Create_str(stabx_bi,'"'+FixFileName(infile.name)+'"'),inclinsertpos);
  333. curincludefileinfo:=currfileinfo;
  334. { force new line info }
  335. lastfileinfo.line:=-1;
  336. end;
  337. end
  338. else
  339. lastfileinfo.line:=-1;
  340. if nextlineisfunstart then
  341. begin
  342. curfunstartfileinfo:=currfileinfo;
  343. { insert here rather than via procdef, because the procdef
  344. may have been created in another file in case the body
  345. is completely declared in an include file }
  346. list.insertbefore(Tai_stab.Create_str(stabx_bf,tostr(currfileinfo.line)),hp);
  347. { -1 to avoid outputting a relative line 0 in the
  348. function, because that means something different }
  349. dec(curfunstartfileinfo.line);
  350. nextlineisfunstart:=false;
  351. end;
  352. end;
  353. if nolineinfolevel=0 then
  354. begin
  355. { line changed ? }
  356. if (currfileinfo.line>lastfileinfo.line) and
  357. (currfileinfo.line<>0) then
  358. begin
  359. linenr:=currfileinfo.line;
  360. { line numbers in AIX are relative to the function start line
  361. (except if they are in a different file then where the
  362. function started!) }
  363. if (currfileinfo.fileindex=curfunstartfileinfo.fileindex) and
  364. (currfileinfo.moduleindex=curfunstartfileinfo.moduleindex) then
  365. dec(linenr,curfunstartfileinfo.line);
  366. { can be < 0 in case of bugs in the compiler }
  367. if (linenr > 0)
  368. {$ifndef cpu64bitaddr}
  369. { line numbers are unsigned short in 32 bit xcoff }
  370. and (linenr<=high(word))
  371. {$endif}
  372. then
  373. list.insertbefore(Tai_stab.Create_str(stabx_line,tostr(linenr)),hp);
  374. end;
  375. lastfileinfo:=currfileinfo;
  376. end;
  377. end;
  378. last:=hp;
  379. hp:=tai(hp.next);
  380. end;
  381. { close include file if still open }
  382. if curincludefileinfo.fileindex<>0 then
  383. begin
  384. infile:=get_module(curincludefileinfo.moduleindex).sourcefiles.get_file(curincludefileinfo.fileindex);
  385. list.insertbefore(Tai_stab.Create_str(stabx_ei,'"'+FixFileName(infile.name)+'"'),last);
  386. curincludefileinfo.fileindex:=0;
  387. end;
  388. end;
  389. procedure TDebugInfoStabx.insertmoduleinfo;
  390. begin
  391. // do nothing
  392. end;
  393. procedure TDebugInfoStabx.referencesections(list: TAsmList);
  394. begin
  395. // do nothing
  396. end;
  397. constructor TDebugInfoStabx.create;
  398. begin
  399. inherited create;
  400. dbgtype:=dbg_stabx;
  401. stabsdir:=stab_stabx;
  402. def_stab:=STABX_N_DECL;
  403. regvar_stab:=STABX_N_RPSYM;
  404. procdef_stab:=STABX_N_Function;
  405. constsym_stab:=STABX_N_GSYM;
  406. typesym_stab:=STABX_N_DECL;
  407. globalvarsym_uninited_stab:=STABX_N_STSYM;
  408. globalvarsym_inited_stab:=STABX_N_STSYM;
  409. staticvarsym_uninited_stab:=STABX_N_STSYM;
  410. staticvarsym_inited_stab:=STABX_N_STSYM;
  411. localvarsymref_stab:=STABX_N_LSYM;
  412. paravarsymref_stab:=STABX_N_PSYM;
  413. tagtypeprefix:='T';
  414. end;
  415. const
  416. dbg_stabx_info : tdbginfo =
  417. (
  418. id : dbg_stabx;
  419. idtxt : 'STABX';
  420. );
  421. initialization
  422. RegisterDebugInfo(dbg_stabx_info,TDebugInfoStabx);
  423. end.