optdead.pas 11 KB

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