dbgstabx.pas 18 KB

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