123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- {
- Copyright (c) 2008 by Jonas Maebe
- Optimization information related to dead code removal
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- ****************************************************************************
- }
- unit optdead;
- {$i fpcdefs.inc}
- interface
- uses
- globtype,
- cclasses,
- wpobase;
- type
- { twpodeadcodeinfo }
- twpodeadcodeinfo = class(twpodeadcodehandler)
- private
- { hashtable of symbols which are live }
- fsymbols : tfphashlist;
- procedure documentformat(writer: twposectionwriterintf);
- public
- constructor create; override;
- destructor destroy; override;
- class function getwpotype: twpotype; override;
- class function generatesinfoforwposwitches: twpoptimizerswitches; override;
- class function performswpoforswitches: twpoptimizerswitches; override;
- class function sectionname: shortstring; override;
- class procedure checkoptions; override;
- { information collection }
- procedure storewpofilesection(writer: twposectionwriterintf); override;
- { information providing }
- procedure loadfromwpofilesection(reader: twposectionreaderintf); override;
- function symbolinfinalbinary(const s: shortstring): boolean;override;
- end;
- { tdeadcodeinfofromexternallinker }
- twpodeadcodeinfofromexternallinker = class(twpodeadcodeinfo)
- private
- fsymtypepos,
- fsymnamepos : longint;
- fsymfile : text;
- fsymfilename : tcmdstr;
- aixstrings : TDynStringArray;
- fuseaixextractstrings : boolean;
- function parselinenm(const line: ansistring): boolean;
- function parselineobjdump(const line: ansistring): boolean;
- public
- class procedure checkoptions; override;
- { information collection }
- procedure constructfromcompilerstate; override;
- destructor destroy; override;
- end;
- implementation
- uses
- cutils,cfileutl,
- sysutils,
- globals,systems,fmodule,
- verbose;
- const
- SYMBOL_SECTION_NAME = 'live_symbols';
- { twpodeadcodeinfo }
- constructor twpodeadcodeinfo.create;
- begin
- inherited create;
- fsymbols:=tfphashlist.create;
- end;
- destructor twpodeadcodeinfo.destroy;
- begin
- fsymbols.free;
- fsymbols:=nil;
- inherited destroy;
- end;
- class function twpodeadcodeinfo.getwpotype: twpotype;
- begin
- result:=wpo_live_symbol_information;
- end;
- class function twpodeadcodeinfo.generatesinfoforwposwitches: twpoptimizerswitches;
- begin
- result:=[cs_wpo_symbol_liveness];
- end;
- class function twpodeadcodeinfo.performswpoforswitches: twpoptimizerswitches;
- begin
- result:=[cs_wpo_symbol_liveness];
- end;
- class function twpodeadcodeinfo.sectionname: shortstring;
- begin
- result:=SYMBOL_SECTION_NAME;
- end;
- class procedure twpodeadcodeinfo.checkoptions;
- begin
- { we don't have access to the symbol info if the linking
- hasn't happend
- }
- if (([cs_link_on_target,cs_link_nolink] * init_settings.globalswitches) <> []) then
- begin
- cgmessage(wpo_cannot_extract_live_symbol_info_no_link);
- exit;
- end;
- { without dead code stripping/smart linking, this doesn't make sense }
- if not(cs_link_smart in init_settings.globalswitches) then
- begin
- cgmessage(wpo_symbol_live_info_needs_smart_linking);
- exit;
- end;
- end;
- procedure twpodeadcodeinfo.documentformat(writer: twposectionwriterintf);
- begin
- writer.sectionputline('# section format:');
- writer.sectionputline('# symbol1_that_is_live');
- writer.sectionputline('# symbol2_that_is_live');
- writer.sectionputline('# ...');
- writer.sectionputline('#');
- end;
- procedure twpodeadcodeinfo.storewpofilesection(writer: twposectionwriterintf);
- var
- i: longint;
- begin
- writer.startsection(SYMBOL_SECTION_NAME);
- documentformat(writer);
- for i:=0 to fsymbols.count-1 do
- writer.sectionputline(fsymbols.nameofindex(i));
- end;
- procedure twpodeadcodeinfo.loadfromwpofilesection(reader: twposectionreaderintf);
- var
- symname: shortstring;
- begin
- while reader.sectiongetnextline(symname) do
- fsymbols.add(symname,pointer(1));
- end;
- function twpodeadcodeinfo.symbolinfinalbinary(const s: shortstring): boolean;
- begin
- result:=fsymbols.find(s)<>nil;
- end;
- { twpodeadcodeinfofromexternallinker }
- {$ifdef relaxed_objdump_parsing}
- const
- objdumpcheckstr='.text';
- {$else}
- const
- objdumpcheckstr='F .text';
- {$endif}
- objdumpsearchstr=' '+objdumpcheckstr;
- class procedure twpodeadcodeinfofromexternallinker.checkoptions;
- begin
- inherited checkoptions;
- { we need symbol information }
- if (cs_link_strip in init_settings.globalswitches) then
- begin
- cgmessage(wpo_cannot_extract_live_symbol_info_strip);
- exit;
- end;
- end;
- function twpodeadcodeinfofromexternallinker.parselinenm(const line: ansistring): boolean;
- begin
- if fuseaixextractstrings then
- begin
- result:=true;
- if ExtractStrings([' ',#9],[],pchar(line),aixstrings)>=2 then
- begin
- if (length(aixstrings[1])=1) and
- (aixstrings[1][1] in ['t','T']) and
- (aixstrings[0][1]='.') then
- fsymbols.add(copy(aixstrings[0],2,length(aixstrings[0])),pointer(1));
- end;
- setlength(aixstrings,0);
- end
- else
- begin
- if (length(line) < fsymnamepos) then
- begin
- cgmessage1(wpo_error_reading_symbol_file,'nm');
- close(fsymfile);
- deletefile(fsymfilename);
- result:=false;
- exit;
- end;
- if (line[fsymtypepos] in ['T','t']) and
- (not use_dotted_functions or
- (line[fsymnamepos-1]='.')) then
- fsymbols.add(copy(line,fsymnamepos,length(line)),pointer(1));
- end;
- result:=true;
- end;
- function twpodeadcodeinfofromexternallinker.parselineobjdump(const line: ansistring): boolean;
- begin
- { there are a couple of empty lines at the end }
- if (line='') then
- begin
- result:=true;
- exit;
- end;
- if (length(line) < fsymtypepos) then
- begin
- cgmessage1(wpo_error_reading_symbol_file,'objdump');
- close(fsymfile);
- deletefile(fsymfilename);
- result:=false;
- exit;
- end;
- if (copy(line,fsymtypepos,length(objdumpcheckstr))=objdumpcheckstr) then
- fsymbols.add(copy(line,fsymnamepos,length(line)),pointer(1));
- result:=true;
- end;
- procedure twpodeadcodeinfofromexternallinker.constructfromcompilerstate;
- type
- tparselineproc = function(const line: ansistring): boolean of object;
- var
- nmfullname,
- objdumpfullname,
- symbolprogfullpath : tcmdstr;
- line : ansistring;
- parseline : tparselineproc;
- exitcode : longint;
- symbolprogfound : boolean;
- symbolprogisnm : boolean;
- function findutil(const utilname: string; out fullutilname, fullutilpath: tcmdstr): boolean;
- begin
- result:=false;
- fullutilname:=utilsprefix+changefileext(utilname,source_info.exeext);
- if utilsdirectory<>'' then
- result:=findfile(fullutilname,utilsdirectory,false,fullutilpath);
- if not result then
- result:=findexe(fullutilname,false,fullutilpath);
- end;
- function failiferror(error: boolean): boolean;
- begin
- result:=error;
- if not result then
- exit;
- cgmessage1(wpo_error_reading_symbol_file,symbolprogfullpath);
- {$push}{$i-}
- close(fsymfile);
- {$pop}
- if fileexists(fsymfilename) then
- deletefile(fsymfilename);
- end;
- function setnminfo: boolean;
- begin
- { expected format:
- 0000bce0 T FPC_ABSTRACTERROR
- ...
- }
- result:=false;
- if (source_info.system in systems_aix) and
- (target_info.system in systems_aix) then
- begin
- { check for native aix nm:
- .__start t 268435792 213
- .__start T 268435792
- }
- if not(line[1] in ['0'..'9','a'..'f','A'..'F']) then
- begin
- fuseaixextractstrings:=true;
- setlength(aixstrings,0);
- result:=true;
- exit;
- end;
- end;
- fsymtypepos:=pos(' ',line)+1;
- fsymnamepos:=fsymtypepos+2;
- { on Linux/ppc64, there is an extra '.' at the start
- of public function names
- }
- if use_dotted_functions then
- inc(fsymnamepos);
- if failiferror(fsymtypepos<=0) then
- exit;
- { make sure there's room for the name }
- if failiferror(fsymnamepos>length(line)) then
- exit;
- result:=true;
- end;
- function setobjdumpinfo: boolean;
- begin
- { expected format:
- prog: file format elf32-i386
- SYMBOL TABLE:
- 08048080 l d .text 00000000 .text
- 00000000 l d .stabstr 00000000 .stabstr
- 00000000 l df *ABS* 00000000 nest.pp
- 08048160 l F .text 00000068 SYSTEM_INITSYSCALLINTF
- ...
- }
- result:=false;
- while (pos(objdumpsearchstr,line)<=0) do
- begin
- if failiferror(eof(fsymfile)) then
- exit;
- readln(fsymfile,line)
- end;
- fsymtypepos:=pos(objdumpsearchstr,line)+1;
- { find begin of symbol name }
- fsymnamepos:=(pointer(strrscan(pchar(line),' '))-pointer(@line[1]))+2;
- { sanity check }
- if (fsymnamepos <= fsymtypepos+length(objdumpcheckstr)) then
- exit;
- result:=true;
- end;
- begin { twpodeadcodeinfofromexternallinker }
- objdumpfullname:='';
- fuseaixextractstrings:=false;
- { gnu-nm (e.g., on solaris) }
- symbolprogfound:=findutil('gnm',nmfullname,symbolprogfullpath);
- { regular nm }
- if not symbolprogfound then
- symbolprogfound:=findutil('nm',nmfullname,symbolprogfullpath);
- if not symbolprogfound and
- (target_info.system in systems_linux) then
- begin
- { try objdump }
- symbolprogfound:=findutil('objdump',objdumpfullname,symbolprogfullpath);
- symbolprogfullpath:=symbolprogfullpath+' -t ';
- symbolprogisnm:=false;
- end
- else
- begin
- symbolprogfullpath:=symbolprogfullpath+' -p ';
- { GNU nm shows 64 bit addresses when processing 32 bit binaries on
- a 64 bit platform, but only skips 8 spaces for the address in case
- of undefined symbols -> skip undefined symbols }
- if target_info.system in (systems_linux+systems_windows) then
- symbolprogfullpath:=symbolprogfullpath+'--defined-only ';
- symbolprogisnm:=true;
- end;
- if not symbolprogfound then
- begin
- cgmessage2(wpo_cannot_find_symbol_progs,nmfullname,objdumpfullname);
- exit;
- end;
- { upper case to have the least chance of tripping some long file name
- conversion stuff
- }
- fsymfilename:=outputexedir+'FPCWPO.SYM';
- { -p gives the same kind of output with Solaris nm as
- with GNU nm, and for GNU nm it simply means "unsorted"
- }
- exitcode:=shell(symbolprogfullpath+maybequoted(current_module.exefilename)+' > '+fsymfilename);
- if (exitcode<>0) then
- begin
- cgmessage2(wpo_error_executing_symbol_prog,symbolprogfullpath,tostr(exitcode));
- if fileexists(fsymfilename) then
- deletefile(fsymfilename);
- exit;
- end;
- assign(fsymfile,fsymfilename);
- {$push}{$i-}
- reset(fsymfile);
- {$pop}
- if failiferror((ioresult<>0) or eof(fsymfile)) then
- exit;
- readln(fsymfile, line);
- if (symbolprogisnm) then
- begin
- if not setnminfo then
- exit;
- parseline:=@parselinenm
- end
- else
- begin
- if not setobjdumpinfo then
- exit;
- parseline:=@parselineobjdump;
- end;
- if not parseline(line) then
- exit;
- while not eof(fsymfile) do
- begin
- readln(fsymfile,line);
- if not parseline(line) then
- exit;
- end;
- close(fsymfile);
- deletefile(fsymfilename);
- end;
- destructor twpodeadcodeinfofromexternallinker.destroy;
- begin
- inherited destroy;
- end;
- end.
|