optdead.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. {
  2. Copyright (c) 2008 by Jonas Maebe
  3. Optimization information related to dead code removal
  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 optdead;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. cclasses,
  23. symtype,
  24. wpobase;
  25. type
  26. { twpodeadcodeinfo }
  27. twpodeadcodeinfo = class(twpodeadcodehandler)
  28. private
  29. { hashtable of symbols which are live }
  30. fsymbols : tfphashlist;
  31. procedure documentformat(writer: twposectionwriterintf);
  32. public
  33. constructor create; override;
  34. destructor destroy; override;
  35. class function getwpotype: twpotype; override;
  36. class function generatesinfoforwposwitches: twpoptimizerswitches; override;
  37. class function performswpoforswitches: twpoptimizerswitches; override;
  38. class function sectionname: shortstring; override;
  39. class procedure checkoptions; override;
  40. { information collection }
  41. procedure storewpofilesection(writer: twposectionwriterintf); override;
  42. { information providing }
  43. procedure loadfromwpofilesection(reader: twposectionreaderintf); override;
  44. function symbolinfinalbinary(const s: shortstring): boolean;override;
  45. end;
  46. { tdeadcodeinfofromexternallinker }
  47. twpodeadcodeinfofromexternallinker = class(twpodeadcodeinfo)
  48. private
  49. fsymtypepos,
  50. fsymnamepos : longint;
  51. fsymfile : text;
  52. fsymfilename : tcmdstr;
  53. function parselinenm(const line: ansistring): boolean;
  54. function parselineobjdump(const line: ansistring): boolean;
  55. public
  56. class procedure checkoptions; override;
  57. { information collection }
  58. procedure constructfromcompilerstate; override;
  59. end;
  60. implementation
  61. uses
  62. cutils,cfileutl,
  63. sysutils,
  64. globals,systems,fmodule,
  65. verbose;
  66. const
  67. SYMBOL_SECTION_NAME = 'live_symbols';
  68. { twpodeadcodeinfo }
  69. constructor twpodeadcodeinfo.create;
  70. begin
  71. inherited create;
  72. fsymbols:=tfphashlist.create;
  73. end;
  74. destructor twpodeadcodeinfo.destroy;
  75. begin
  76. fsymbols.free;
  77. fsymbols:=nil;
  78. inherited destroy;
  79. end;
  80. class function twpodeadcodeinfo.getwpotype: twpotype;
  81. begin
  82. result:=wpo_live_symbol_information;
  83. end;
  84. class function twpodeadcodeinfo.generatesinfoforwposwitches: twpoptimizerswitches;
  85. begin
  86. result:=[cs_wpo_symbol_liveness];
  87. end;
  88. class function twpodeadcodeinfo.performswpoforswitches: twpoptimizerswitches;
  89. begin
  90. result:=[cs_wpo_symbol_liveness];
  91. end;
  92. class function twpodeadcodeinfo.sectionname: shortstring;
  93. begin
  94. result:=SYMBOL_SECTION_NAME;
  95. end;
  96. class procedure twpodeadcodeinfo.checkoptions;
  97. begin
  98. { we don't have access to the symbol info if the linking
  99. hasn't happend
  100. }
  101. if (([cs_link_on_target,cs_link_nolink] * init_settings.globalswitches) <> []) then
  102. begin
  103. cgmessage(wpo_cannot_extract_live_symbol_info_no_link);
  104. exit;
  105. end;
  106. { without dead code stripping/smart linking, this doesn't make sense }
  107. if not(cs_link_smart in init_settings.globalswitches) then
  108. begin
  109. cgmessage(wpo_symbol_live_info_needs_smart_linking);
  110. exit;
  111. end;
  112. end;
  113. procedure twpodeadcodeinfo.documentformat(writer: twposectionwriterintf);
  114. begin
  115. writer.sectionputline('# section format:');
  116. writer.sectionputline('# symbol1_that_is_live');
  117. writer.sectionputline('# symbol2_that_is_live');
  118. writer.sectionputline('# ...');
  119. writer.sectionputline('#');
  120. end;
  121. procedure twpodeadcodeinfo.storewpofilesection(writer: twposectionwriterintf);
  122. var
  123. i: longint;
  124. begin
  125. writer.startsection(SYMBOL_SECTION_NAME);
  126. documentformat(writer);
  127. for i:=0 to fsymbols.count-1 do
  128. writer.sectionputline(fsymbols.nameofindex(i));
  129. end;
  130. procedure twpodeadcodeinfo.loadfromwpofilesection(reader: twposectionreaderintf);
  131. var
  132. symname: shortstring;
  133. begin
  134. while reader.sectiongetnextline(symname) do
  135. fsymbols.add(symname,pointer(1));
  136. end;
  137. function twpodeadcodeinfo.symbolinfinalbinary(const s: shortstring): boolean;
  138. begin
  139. result:=fsymbols.find(s)<>nil;
  140. end;
  141. { twpodeadcodeinfofromexternallinker }
  142. {$ifdef relaxed_objdump_parsing}
  143. const
  144. objdumpcheckstr='.text';
  145. {$else}
  146. const
  147. objdumpcheckstr='F .text';
  148. {$endif}
  149. objdumpsearchstr=' '+objdumpcheckstr;
  150. class procedure twpodeadcodeinfofromexternallinker.checkoptions;
  151. begin
  152. inherited checkoptions;
  153. { we need symbol information }
  154. if (cs_link_strip in init_settings.globalswitches) then
  155. begin
  156. cgmessage(wpo_cannot_extract_live_symbol_info_strip);
  157. exit;
  158. end;
  159. end;
  160. function twpodeadcodeinfofromexternallinker.parselinenm(const line: ansistring): boolean;
  161. begin
  162. if (length(line) < fsymnamepos) then
  163. begin
  164. cgmessage1(wpo_error_reading_symbol_file,'nm');
  165. close(fsymfile);
  166. deletefile(fsymfilename);
  167. result:=false;
  168. exit;
  169. end;
  170. if (line[fsymtypepos] in ['T','t']) then
  171. fsymbols.add(copy(line,fsymnamepos,length(line)),pointer(1));
  172. result:=true;
  173. end;
  174. function twpodeadcodeinfofromexternallinker.parselineobjdump(const line: ansistring): boolean;
  175. begin
  176. { there are a couple of empty lines at the end }
  177. if (line='') then
  178. begin
  179. result:=true;
  180. exit;
  181. end;
  182. if (length(line) < fsymtypepos) then
  183. begin
  184. cgmessage1(wpo_error_reading_symbol_file,'objdump');
  185. close(fsymfile);
  186. deletefile(fsymfilename);
  187. result:=false;
  188. exit;
  189. end;
  190. if (copy(line,fsymtypepos,length(objdumpcheckstr))=objdumpcheckstr) then
  191. fsymbols.add(copy(line,fsymnamepos,length(line)),pointer(1));
  192. result:=true;
  193. end;
  194. procedure twpodeadcodeinfofromexternallinker.constructfromcompilerstate;
  195. type
  196. tparselineproc = function(const line: ansistring): boolean of object;
  197. var
  198. nmfullname,
  199. objdumpfullname,
  200. symbolprogfullpath : tcmdstr;
  201. line : ansistring;
  202. parseline : tparselineproc;
  203. exitcode : longint;
  204. symbolprogfound : boolean;
  205. symbolprogisnm : boolean;
  206. function findutil(const utilname: string; out fullutilname, fullutilpath: tcmdstr): boolean;
  207. begin
  208. result:=false;
  209. fullutilname:=utilsprefix+changefileext(utilname,source_info.exeext);
  210. if utilsdirectory<>'' then
  211. result:=findfile(fullutilname,utilsdirectory,false,fullutilpath);
  212. if not result then
  213. result:=findexe(fullutilname,false,fullutilpath);
  214. end;
  215. function failiferror(error: boolean): boolean;
  216. begin
  217. result:=error;
  218. if not result then
  219. exit;
  220. cgmessage1(wpo_error_reading_symbol_file,symbolprogfullpath);
  221. {$push}{$i-}
  222. close(fsymfile);
  223. {$pop}
  224. if fileexists(fsymfilename) then
  225. deletefile(fsymfilename);
  226. end;
  227. function setnminfo: boolean;
  228. begin
  229. { expected format:
  230. 0000bce0 T FPC_ABSTRACTERROR
  231. ...
  232. }
  233. result:=false;
  234. fsymtypepos:=pos(' ',line)+1;
  235. fsymnamepos:=fsymtypepos+2;
  236. { on Linux/ppc64, there is an extra '.' at the start
  237. of public function names
  238. }
  239. if (target_info.system=system_powerpc64_linux) then
  240. inc(fsymnamepos);
  241. if failiferror(fsymtypepos<=0) then
  242. exit;
  243. { make sure there's room for the name }
  244. if failiferror(fsymnamepos>length(line)) then
  245. exit;
  246. { and that we're not in the middle of some other column }
  247. if failiferror(pos(' ',copy(line,fsymnamepos,length(line)))>0) then
  248. exit;
  249. result:=true;
  250. end;
  251. function setobjdumpinfo: boolean;
  252. begin
  253. { expected format:
  254. prog: file format elf32-i386
  255. SYMBOL TABLE:
  256. 08048080 l d .text 00000000 .text
  257. 00000000 l d .stabstr 00000000 .stabstr
  258. 00000000 l df *ABS* 00000000 nest.pp
  259. 08048160 l F .text 00000068 SYSTEM_INITSYSCALLINTF
  260. ...
  261. }
  262. result:=false;
  263. while (pos(objdumpsearchstr,line)<=0) do
  264. begin
  265. if failiferror(eof(fsymfile)) then
  266. exit;
  267. readln(fsymfile,line)
  268. end;
  269. fsymtypepos:=pos(objdumpsearchstr,line)+1;
  270. { find begin of symbol name }
  271. fsymnamepos:=(pointer(strrscan(pchar(line),' '))-pointer(@line[1]))+2;
  272. { sanity check }
  273. if (fsymnamepos <= fsymtypepos+length(objdumpcheckstr)) then
  274. exit;
  275. result:=true;
  276. end;
  277. begin { twpodeadcodeinfofromexternallinker }
  278. { gnu-nm (e.g., on solaris) }
  279. symbolprogfound:=findutil('gnm',nmfullname,symbolprogfullpath);
  280. { regular nm }
  281. if not symbolprogfound then
  282. symbolprogfound:=findutil('nm',nmfullname,symbolprogfullpath);
  283. if not symbolprogfound then
  284. begin
  285. { try objdump }
  286. symbolprogfound:=findutil('objdump',objdumpfullname,symbolprogfullpath);
  287. symbolprogfullpath:=symbolprogfullpath+' -t ';
  288. symbolprogisnm:=false;
  289. end
  290. else
  291. begin
  292. symbolprogfullpath:=symbolprogfullpath+' -p ';
  293. symbolprogisnm:=true;
  294. end;
  295. if not symbolprogfound then
  296. begin
  297. cgmessage2(wpo_cannot_find_symbol_progs,nmfullname,objdumpfullname);
  298. exit;
  299. end;
  300. { upper case to have the least chance of tripping some long file name
  301. conversion stuff
  302. }
  303. fsymfilename:=outputexedir+'FPCWPO.SYM';
  304. { -p gives the same kind of output with Solaris nm as
  305. with GNU nm, and for GNU nm it simply means "unsorted"
  306. }
  307. exitcode:=shell(symbolprogfullpath+maybequoted(current_module.exefilename^)+' > '+fsymfilename);
  308. if (exitcode<>0) then
  309. begin
  310. cgmessage2(wpo_error_executing_symbol_prog,symbolprogfullpath,tostr(exitcode));
  311. if fileexists(fsymfilename) then
  312. deletefile(fsymfilename);
  313. exit;
  314. end;
  315. assign(fsymfile,fsymfilename);
  316. {$push}{$i-}
  317. reset(fsymfile);
  318. {$pop}
  319. if failiferror((ioresult<>0) or eof(fsymfile)) then
  320. exit;
  321. readln(fsymfile, line);
  322. if (symbolprogisnm) then
  323. begin
  324. if not setnminfo then
  325. exit;
  326. parseline:=@parselinenm
  327. end
  328. else
  329. begin
  330. if not setobjdumpinfo then
  331. exit;
  332. parseline:=@parselineobjdump;
  333. end;
  334. if not parseline(line) then
  335. exit;
  336. while not eof(fsymfile) do
  337. begin
  338. readln(fsymfile,line);
  339. if not parseline(line) then
  340. exit;
  341. end;
  342. close(fsymfile);
  343. deletefile(fsymfilename);
  344. end;
  345. end.