fpc/compiler/dbgstabx.pas
2020-04-26 10:42:07 +00:00

490 lines
18 KiB
ObjectPascal

{
Copyright (c) 2012 by Jonas Maebe
This units contains support for STABX debug info generation
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 dbgstabx;
{$i fpcdefs.inc}
interface
uses
cclasses,globtype,
dbgbase,dbgstabs,cgbase,
symtype,symdef,symsym,symtable,symbase,
aasmtai,aasmdata;
type
TDebugInfoStabx = class(TDebugInfoStabs)
protected
function staticvarsym_mangled_name(sym: tstaticvarsym): string; override;
procedure maybe_add_vmt_sym(list: TAsmList; def: tobjectdef); override;
procedure write_def_stabstr(list:TAsmList;def:tdef;const ss:ansistring);override;
function base_stabs_str(const typ, other, desc, value: ansistring): ansistring;overload;override;
function gen_procdef_startsym_stabs(def: tprocdef): TAsmList; override;
function gen_procdef_endsym_stabs(def: tprocdef): TAsmList; override;
procedure appendsym_label(list: TAsmList; sym: tlabelsym); override;
procedure appendsym_staticvar(list: TAsmList; sym: tstaticvarsym); override;
public
procedure insertlineinfo(list:TAsmList);override;
procedure insertmoduleinfo; override;
procedure referencesections(list: TAsmList); override;
constructor create;override;
end;
implementation
uses
globals,cutils,cfileutl,verbose,
systems,finput,fmodule,
aasmbase,
symconst;
const
STABX_N_GSYM = $80;
STABX_N_LSYM = $81;
STABX_N_PSYM = $82;
STABX_N_RSYM = $83;
STABX_N_RPSYM = $84;
STABX_N_STSYM = $85;
STABX_N_LCSYM = 255;
STABX_N_Function = $8e;
STABX_N_TextLine = 255;
STABX_N_DataLine = 255;
STABX_N_BssLine = 255;
STABX_N_DECL = $8c;
STABX_N_tsym = $86;
STABX_N_SourceFile = 255;
STABX_N_OSO = 255;
STABX_N_IncludeFile = 255;
STABX_N_BINCL = 255;
STABX_N_EINCL = 255;
STABX_N_LBRAC = 255;
STABX_N_EXCL = 255;
STABX_N_RBRAC = 255;
{ TDebugInfoStabx }
function TDebugInfoStabx.base_stabs_str(const typ, other, desc, value: ansistring): ansistring;
begin
{ no other/desc }
result:=value+','+typ+',0';
end;
function TDebugInfoStabx.staticvarsym_mangled_name(sym: tstaticvarsym): string;
begin
{ create reference to the local symbol at the same address as the global
symbol (with same name as unmangled symbol, so GDB can find it) }
Result:=ApplyAsmSymbolRestrictions(sym.name);
end;
procedure TDebugInfoStabx.maybe_add_vmt_sym(list: TAsmList; def: tobjectdef);
begin
(*
if assigned(def.owner) and
def.owner.iscurrentunit then
begin
if (oo_has_vmt in def.objectoptions) and
assigned(def.owner.name) then
list.concat(Tai_stab.create_ansistr(stabsdir,ansistring('"vmt_')+GetSymTableName(def.owner)+tobjectdef(def).objname^+':S'+
def_stab_number(vmttype)+'",'+
base_stabs_str(globalvarsym_inited_stab,'0','0',ApplyAsmSymbolRestrictions(tobjectdef(def).vmt_mangledname)+'.')));
end;
*)
{ do nothing, because creating debug information for a global symbol
defined in another unit is not possible for stabx given the FPC
constraints (namely that the name of the global symbol does not match
the name of the variable). If it's in the same unit, we have to add an
extra symbol for the vmt with the same variable name as what we have
here (ansistring('"vmt_')+GetSymTableName(def.owner)+tobjectdef(def).objname^).
We'd have to do that when that symbol is created, in generic code,
which is not very clean, and moreover these symbols are not really
used for anything currently, afaik }
end;
procedure TDebugInfoStabx.write_def_stabstr(list:TAsmList;def:tdef;const ss:ansistring);
var
stabchar,
symname,
declstabnr,
st : ansistring;
begin
{ type prefix }
if use_tag_prefix(def) then
stabchar := tagtypeprefix
else
stabchar := 't';
{ in case of writing the class record structure, we always have to
use the class name (so it refers both to the struct and the
pointer to the struct), otherwise gdb crashes (see tests/webtbs/tw9766.pp) }
if is_class(def) and
tobjectdef(def).writing_class_record_dbginfo then
begin
declstabnr:=def_stab_classnumber(tobjectdef(def));
symname:='${sym_name}'
end
else
begin
{ Type names for types defined in the current unit are already written in
the typesym }
if (def.owner.symtabletype=globalsymtable) and
not(def.owner.iscurrentunit) then
symname:='${sym_name}'
else
symname:='';
declstabnr:=def_stab_number(def)
end;
if (symname='') or
not(use_tag_prefix(def)) then
begin
st:=def_stabstr_evaluate(def,':$1$2=',[stabchar,declstabnr]);
st:='"'+def_stabstr_evaluate(def,symname,[])+st+ss;
{ line info is set to 0 for all defs, because the def can be in another
unit and then the linenumber is invalid in the current sourcefile }
st:=st+'",'+base_stabs_str(def_stab,'0','0','0');
{ add to list }
list.concat(Tai_stab.create_ansistr(stabsdir,st));
end
else
begin
{ first tag, then type decl }
inc(global_stab_number);
st:=def_stabstr_evaluate(def,':$1$2=',[stabchar,tostr(global_stab_number)]);
st:='"'+st+ss;
st:=st+'",'+base_stabs_str(def_stab,'0','0','0');
list.concat(Tai_stab.create_ansistr(stabsdir,st));
st:='"'+def_stabstr_evaluate(def,symname+':t$1=$2',[declstabnr,tostr(global_stab_number)]);
st:=st+'",'+base_stabs_str(def_stab,'0','0','0');
list.concat(Tai_stab.create_ansistr(stabsdir,st));
end;
end;
function TDebugInfoStabx.gen_procdef_startsym_stabs(def: tprocdef): TAsmList;
var
mangledname: ansistring;
hp, hpp, inclstart: tai;
begin
result:=inherited;
{ can happen for procdefs defined in other units, this code is only for
the place where it is defined }
if not assigned(def.procstarttai) then
exit;
mangledname:=ApplyAsmSymbolRestrictions(def.mangledname);
if target_info.system in systems_dotted_function_names then
mangledname:='.'+mangledname;
result.concat(tai_stab.create(stabx_function,
strpnew(mangledname+','+mangledname+',16,044,LT.'+mangledname+'-'+mangledname)));
{ hoist the already generated ".bf" up right after the function
definition so that all parameter and local variable definitions come
after it -- we have to generate it during lineinfo generation and not
here to make sure it takes into account include files opened right after
the function definition but before the code starts
-- also move include file start if any}
hp:=def.procstarttai;
inclstart:=nil;
while (hp.typ<>ait_symbol_end) and
((hp.typ<>ait_stab) or
(tai_stab(hp).stabtype<>stabx_bf)) do
begin
if (hp.typ=ait_stab) and
(tai_stab(hp).stabtype=stabx_bi) then
inclstart:=hp;
hp:=tai(hp.next);
end;
{ happens for implicit unit init routines and the like, they don't get
line info }
if hp.typ=ait_symbol_end then
exit;
if assigned(inclstart) then
begin
current_asmdata.asmlists[al_procedures].Remove(inclstart);
result.concat(inclstart);
end;
current_asmdata.asmlists[al_procedures].Remove(hp);
result.concat(hp);
{ also hoist up the function start symbol(s) }
hp:=def.procstarttai;
while assigned(hp) and
(hp.typ<>ait_symbol_end) do
begin
if (hp.typ=ait_symbol) and
(tai_symbol(hp).sym.typ=AT_FUNCTION) then
begin
hpp:=tai(hp.next);
if hp=def.procstarttai then
def.procstarttai:=hpp;
current_asmdata.asmlists[al_procedures].Remove(hp);
result.insert(hp);
hp:=hpp;
end
else
hp:=tai(hp.next);
end;
end;
function TDebugInfoStabx.gen_procdef_endsym_stabs(def: tprocdef): TAsmList;
var
procendsymbol: tasmsymbol;
begin
result:=inherited gen_procdef_endsym_stabs(def);
if not assigned(def.procstarttai) then
exit;
procendsymbol:=current_asmdata.DefineAsmSymbol('LT..'+ApplyAsmSymbolRestrictions(def.mangledname),AB_LOCAL,AT_ADDR,voidpointertype);
current_asmdata.asmlists[al_procedures].insertbefore(tai_symbol.create(procendsymbol,0),def.procendtai);
end;
procedure TDebugInfoStabx.appendsym_label(list: TAsmList; sym: tlabelsym);
begin
// ignore, not sure what kind of debug information we could generate for
// this
end;
procedure TDebugInfoStabx.appendsym_staticvar(list: TAsmList; sym: tstaticvarsym);
var
ismem,
isglobal: boolean;
begin
if vo_is_external in sym.varoptions then
exit;
ismem:=not(sym.localloc.loc in [LOC_REGISTER,LOC_CREGISTER,LOC_MMREGISTER,LOC_CMMREGISTER,LOC_FPUREGISTER,LOC_CFPUREGISTER]);
isglobal:=false;
if ismem then
isglobal:=current_asmdata.RefAsmSymbol(sym.mangledname,AT_DATA).bind=AB_GLOBAL;
{ put extra ss/es markers in place }
if ismem then
if isglobal then
list.concat(tai_stab.Create_ansistr(stabx_bs,'.data[RW]'))
else
list.concat(tai_stab.Create_ansistr(stabx_bs,'_data.bss_'));
inherited;
if ismem then
list.concat(tai_stab.Create_ansistr(stabx_es,''));
end;
procedure TDebugInfoStabx.insertlineinfo(list: TAsmList);
var
currfileinfo,
lastfileinfo,
curincludefileinfo,
curfunstartfileinfo: tfileposinfo;
currsectype : TAsmSectiontype;
hp, inclinsertpos, last : tai;
infile : tinputfile;
linenr, stabx_func_level,
nolineinfolevel: longint;
nextlineisfunstart: boolean;
begin
FillChar(currfileinfo,sizeof(currfileinfo),0);
FillChar(lastfileinfo,sizeof(lastfileinfo),0);
FillChar(curincludefileinfo,sizeof(curincludefileinfo),0);
FillChar(curfunstartfileinfo,sizeof(curfunstartfileinfo),0);
currsectype:=sec_code;
hp:=Tai(list.first);
nextlineisfunstart:=false;
nolineinfolevel:=0;
stabx_func_level:=0;
last:=nil;
while assigned(hp) do
begin
case hp.typ of
ait_section :
currsectype:=tai_section(hp).sectype;
ait_force_line :
lastfileinfo.line:=-1;
ait_symbol:
if tai_symbol(hp).sym.typ = AT_FUNCTION then
nextlineisfunstart:=true;
ait_symbol_end:
if tai_symbol_end(hp).sym.typ = AT_FUNCTION then
begin
{ end of function }
if stabx_func_level > 0 then
begin
list.insertbefore(Tai_stab.Create_str(stabx_ef,tostr(currfileinfo.line)),hp);
dec(stabx_func_level);
end;
end;
ait_marker :
begin
case tai_marker(hp).kind of
mark_NoLineInfoStart:
inc(nolineinfolevel);
mark_NoLineInfoEnd:
dec(nolineinfolevel);
else
;
end;
end;
else
;
end;
if (currsectype=sec_code) and
(hp.typ=ait_instruction) then
begin
currfileinfo:=tailineinfo(hp).fileinfo;
inclinsertpos:=hp;
while assigned(inclinsertpos.previous) and
(tai(inclinsertpos.previous).typ in (SkipInstr+[ait_marker])) do
inclinsertpos:=tai(inclinsertpos.previous);
{ file changed ? (must be before line info) }
if (currfileinfo.fileindex<>0) and
((lastfileinfo.fileindex<>currfileinfo.fileindex) or
(lastfileinfo.moduleindex<>currfileinfo.moduleindex)) then
begin
if curincludefileinfo.fileindex<>0 then
begin
infile:=get_module(curincludefileinfo.moduleindex).sourcefiles.get_file(curincludefileinfo.fileindex);
list.insertbefore(Tai_stab.Create_str(stabx_ei,'"'+FixFileName(infile.name)+'"'),inclinsertpos);
curincludefileinfo.fileindex:=0;
end;
if currfileinfo.fileindex<>1 then
begin
infile:=get_module(currfileinfo.moduleindex).sourcefiles.get_file(currfileinfo.fileindex);
if assigned(infile) then
begin
list.insertbefore(Tai_stab.Create_str(stabx_bi,'"'+FixFileName(infile.name)+'"'),inclinsertpos);
curincludefileinfo:=currfileinfo;
{ force new line info }
lastfileinfo.line:=-1;
end;
end
else
lastfileinfo.line:=-1;
if nextlineisfunstart then
begin
curfunstartfileinfo:=currfileinfo;
{ insert here rather than via procdef, because the procdef
may have been created in another file in case the body
is completely declared in an include file }
list.insertbefore(Tai_stab.Create_str(stabx_bf,tostr(currfileinfo.line)),hp);
inc(stabx_func_level);
{ -1 to avoid outputting a relative line 0 in the
function, because that means something different }
dec(curfunstartfileinfo.line);
nextlineisfunstart:=false;
end;
end;
{ implicit functions have no file information }
if nextlineisfunstart then
begin
list.insertbefore(Tai_stab.Create_str(stabx_bf,tostr(currfileinfo.line)),hp);
inc(stabx_func_level);
nextlineisfunstart:=false;
end;
if nolineinfolevel=0 then
begin
{ line changed ? }
if (currfileinfo.line>lastfileinfo.line) and
(currfileinfo.line<>0) then
begin
linenr:=currfileinfo.line;
{ line numbers in AIX are relative to the function start line
(except if they are in a different file then where the
function started!) }
if (currfileinfo.fileindex=curfunstartfileinfo.fileindex) and
(currfileinfo.moduleindex=curfunstartfileinfo.moduleindex) then
dec(linenr,curfunstartfileinfo.line);
{ can be < 0 in case of bugs in the compiler }
if (linenr > 0)
{$ifndef cpu64bitaddr}
{ line numbers are unsigned short in 32 bit xcoff }
and (linenr<=high(word))
{$endif}
then
list.insertbefore(Tai_stab.Create_str(stabx_line,tostr(linenr)),hp);
end;
lastfileinfo:=currfileinfo;
end;
end;
last:=hp;
hp:=tai(hp.next);
end;
{ close include file if still open }
if curincludefileinfo.fileindex<>0 then
begin
infile:=get_module(curincludefileinfo.moduleindex).sourcefiles.get_file(curincludefileinfo.fileindex);
list.insertbefore(Tai_stab.Create_str(stabx_ei,'"'+FixFileName(infile.name)+'"'),last);
curincludefileinfo.fileindex:=0;
end;
end;
procedure TDebugInfoStabx.insertmoduleinfo;
begin
// do nothing
end;
procedure TDebugInfoStabx.referencesections(list: TAsmList);
begin
// do nothing
end;
constructor TDebugInfoStabx.create;
begin
inherited create;
dbgtype:=dbg_stabx;
stabsdir:=stab_stabx;
def_stab:=STABX_N_DECL;
regvar_stab:=STABX_N_RPSYM;
procdef_stab:=STABX_N_Function;
constsym_stab:=STABX_N_GSYM;
typesym_stab:=STABX_N_DECL;
globalvarsym_uninited_stab:=STABX_N_STSYM;
globalvarsym_inited_stab:=STABX_N_STSYM;
staticvarsym_uninited_stab:=STABX_N_STSYM;
staticvarsym_inited_stab:=STABX_N_STSYM;
localvarsymref_stab:=STABX_N_LSYM;
paravarsymref_stab:=STABX_N_PSYM;
tagtypeprefix:='T';
end;
const
dbg_stabx_info : tdbginfo =
(
id : dbg_stabx;
idtxt : 'STABX';
);
initialization
RegisterDebugInfo(dbg_stabx_info,TDebugInfoStabx);
end.