mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 22:08:11 +02:00
490 lines
18 KiB
ObjectPascal
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.
|