optdead.pas 13 KB

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