optdead.pas 13 KB

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