mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-08 23:12:36 +02:00
2465 lines
60 KiB
ObjectPascal
2465 lines
60 KiB
ObjectPascal
{
|
|
Copyright (c) 1998 by Peter Vreman
|
|
|
|
Lowlevel GDB interface which communicates directly with libgdb
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
unit GdbInt;
|
|
interface
|
|
|
|
{$smartlink off}
|
|
|
|
{.$define Verbose}
|
|
{.$define DebugCommand}
|
|
{$define NotImplemented}
|
|
|
|
{ 6.2.x }
|
|
{$ifdef GDB_V602}
|
|
{$define GDB_V6}
|
|
{$endif def GDB_V602}
|
|
|
|
{ 6.3.x }
|
|
{$ifdef GDB_V603}
|
|
{$define GDB_V6}
|
|
{$endif def GDB_V603}
|
|
|
|
{$ifdef GDB_V6}
|
|
{$define GDB_HAS_SYSROOT}
|
|
{$define GDB_SYMTAB_HAS_MACROS}
|
|
{$endif GDB_V6}
|
|
|
|
{ GDB has a simulator for powerpc CPU
|
|
it is integrated into GDB by default }
|
|
{$ifdef cpupowerpc}
|
|
{$define GDB_HAS_SIM}
|
|
{$endif cpupowerpc}
|
|
|
|
{$ifdef go32v2}
|
|
{$undef NotImplemented}
|
|
{$LINKLIB gdb}
|
|
{$ifdef GDB_HAS_SIM}
|
|
{$LINKLIB sim}
|
|
{$endif GDB_HAS_SIM}
|
|
{$LINKLIB bfd}
|
|
{$LINKLIB readline}
|
|
{$LINKLIB opcodes}
|
|
{$LINKLIB history}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB intl}
|
|
{$LINKLIB dbg}
|
|
{$LINKLIB c}
|
|
{$endif go32v2}
|
|
|
|
{$ifdef linux}
|
|
{$undef NotImplemented}
|
|
{$LINKLIB libgdb.a}
|
|
{$ifdef GDB_HAS_SIM}
|
|
{$LINKLIB libsim.a}
|
|
{$endif GDB_HAS_SIM}
|
|
{$LINKLIB libbfd.a}
|
|
{$LINKLIB libreadline.a}
|
|
{$LINKLIB libopcodes.a}
|
|
{$LINKLIB libhistory.a}
|
|
{$LINKLIB libiberty.a}
|
|
{$LINKLIB ncurses}
|
|
{$LINKLIB m}
|
|
{$LINKLIB dl}
|
|
{$LINKLIB c}
|
|
{ don't link explicitly against (FK) $LINKLIB gcc}
|
|
{$endif linux}
|
|
|
|
{$ifdef freebsd}
|
|
{$ifdef FreeBSD5} //5.4+ ?
|
|
{$linklib kvm}
|
|
{$endif}
|
|
{$undef NotImplemented}
|
|
{$LINKLIB gdb}
|
|
{$ifdef GDB_HAS_SIM}
|
|
{$LINKLIB sim}
|
|
{$endif GDB_HAS_SIM}
|
|
{$LINKLIB bfd}
|
|
{$LINKLIB readline}
|
|
{$LINKLIB opcodes}
|
|
{$LINKLIB history}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB ncurses}
|
|
{$LINKLIB m}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB intl} { does not seem to exist on netbsd LINKLIB dl,
|
|
but I use GDB CVS snapshots for the *BSDs}
|
|
{$LINKLIB c}
|
|
{$LINKLIB gcc}
|
|
{$endif freebsd}
|
|
|
|
{$ifdef netbsd}
|
|
{$undef NotImplemented}
|
|
{$LINKLIB gdb}
|
|
{$ifdef GDB_HAS_SIM}
|
|
{$LINKLIB sim}
|
|
{$endif GDB_HAS_SIM}
|
|
{$LINKLIB bfd}
|
|
{$LINKLIB readline}
|
|
{$LINKLIB opcodes}
|
|
{$LINKLIB history}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB ncurses}
|
|
{$LINKLIB m}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB intl}
|
|
{ does not seem to exist on netbsd LINKLIB dl}
|
|
{$LINKLIB c}
|
|
{$LINKLIB gcc}
|
|
{$endif netbsd}
|
|
|
|
{$ifdef openbsd}
|
|
{$undef NotImplemented}
|
|
{$LINKLIB gdb}
|
|
{$ifdef GDB_HAS_SIM}
|
|
{$LINKLIB sim}
|
|
{$endif GDB_HAS_SIM}
|
|
{$LINKLIB bfd}
|
|
{$LINKLIB readline}
|
|
{$LINKLIB opcodes}
|
|
{$LINKLIB history}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB ncurses}
|
|
{$LINKLIB m}
|
|
{$LINKLIB iberty}
|
|
{$LINKLIB intl}
|
|
{ does not seem to exist on netbsd LINKLIB dl}
|
|
{$LINKLIB c}
|
|
{$LINKLIB gcc}
|
|
{$endif netbsd}
|
|
|
|
{$ifdef win32}
|
|
{$undef NotImplemented}
|
|
{$LINKLIB libgdb.a}
|
|
{$ifdef GDB_HAS_SIM}
|
|
{$LINKLIB libsim.a}
|
|
{$endif GDB_HAS_SIM}
|
|
{$LINKLIB libbfd.a}
|
|
{$LINKLIB libreadline.a}
|
|
{$LINKLIB libopcodes.a}
|
|
{$LINKLIB libhistory.a}
|
|
{$LINKLIB libiberty.a}
|
|
{$LINKLIB libintl.a}
|
|
{$LINKLIB libiconv.a}
|
|
{$LINKLIB libncurses.a}
|
|
{$LINKLIB gcc}
|
|
{$LINKLIB cygwin} { alias of libm.a and libc.a }
|
|
{$LINKLIB imagehlp}
|
|
{$LINKLIB kernel32}
|
|
{$LINKLIB user32}
|
|
{$endif win32}
|
|
|
|
{$ifdef go32v2}
|
|
{$define supportexceptions}
|
|
{$endif go32v2}
|
|
{$ifdef unix}
|
|
{$define supportexceptions}
|
|
{$endif unix}
|
|
|
|
{$ifdef CROSSGDB}
|
|
{ do we neeed something special if cross GDB? }
|
|
{$endif CROSSGDB}
|
|
|
|
{$ifdef NotImplemented}
|
|
{$fatal This OS is not yet supported !!!}
|
|
{$endif NotImplemented}
|
|
|
|
{$packrecords C}
|
|
|
|
type
|
|
psyminfo=^tsyminfo;
|
|
tsyminfo=record
|
|
address : ptrint;
|
|
fname : pchar;
|
|
line : longint;
|
|
funcname : pchar;
|
|
offset : ptrint;
|
|
end;
|
|
|
|
tframeentry = object
|
|
file_name : pchar;
|
|
function_name : pchar;
|
|
args : pchar;
|
|
line_number : longint;
|
|
address : ptrint;
|
|
level : longint;
|
|
constructor init;
|
|
destructor done;
|
|
procedure reset;
|
|
procedure clear;
|
|
end;
|
|
pframeentry=^tframeentry;
|
|
ppframeentry=^pframeentry;
|
|
|
|
{ needed for handles }
|
|
{not anymore I textrec.inc}
|
|
|
|
const
|
|
k=1;
|
|
|
|
type
|
|
{$if defined(CPUSPARC) and defined(LINUX)}
|
|
CORE_ADDR = qword;
|
|
{$else}
|
|
CORE_ADDR = ptrint; { might be target dependent PM }
|
|
{$endif}
|
|
streamtype = (afile,astring);
|
|
C_FILE = ptrint; { at least under DJGPP }
|
|
P_C_FILE = ^C_FILE;
|
|
|
|
type
|
|
|
|
pui_file = ^ui_file;
|
|
|
|
ui_file_flush_ftype = procedure(stream : pui_file);cdecl;
|
|
ui_file_write_ftype = procedure(stream : pui_file;buf : pchar;len : longint);cdecl;
|
|
ui_file_fputs_ftype = procedure(buf : pchar; stream : pui_file);cdecl;
|
|
ui_file_delete_ftype = procedure(stream : pui_file);cdecl;
|
|
ui_file_isatty_ftype = function(stream : pui_file) : longbool;cdecl;
|
|
ui_file_rewind_ftype = procedure(stream : pui_file);cdecl;
|
|
ui_file_put_method_ftype = procedure(var _object; buffer : pchar;length_buffer : longint);cdecl;
|
|
ui_file_put_ftype = procedure(stream : pui_file;method : ui_file_put_method_ftype;var context);cdecl;
|
|
{$ifdef GDB_V6}
|
|
ui_file_read_ftype = function (stream : pui_file; buffer : pchar; len : longint):longint;cdecl;
|
|
{$endif}
|
|
|
|
ui_file = record
|
|
magic : plongint;
|
|
to_flush : ui_file_flush_ftype;
|
|
to_write : ui_file_write_ftype;
|
|
to_fputs : ui_file_fputs_ftype;
|
|
{$ifdef GDB_V6}
|
|
to_read : ui_file_read_ftype;
|
|
{$endif}
|
|
to_delete : ui_file_delete_ftype;
|
|
to_isatty : ui_file_isatty_ftype;
|
|
to_rewind : ui_file_rewind_ftype;
|
|
to_put : ui_file_put_ftype;
|
|
to_data : pointer;
|
|
end;
|
|
|
|
{ used to delete stdio_ui_file gdb_stdout and gdb_stderr }
|
|
procedure ui_file_delete(stream : pui_file);cdecl;external;
|
|
|
|
{ used to recreate gdb_stdout and gdb_stderr as memory streams }
|
|
function mem_fileopen : pui_file;cdecl;external;
|
|
|
|
{ used to change the write procvar to ours }
|
|
|
|
procedure set_ui_file_write(stream : pui_file;write : ui_file_write_ftype);cdecl;external;
|
|
|
|
|
|
type
|
|
|
|
(* struct ptid
|
|
{
|
|
/* Process id */
|
|
int pid;
|
|
|
|
/* Lightweight process id */
|
|
long lwp;
|
|
|
|
/* Thread id */
|
|
long tid;
|
|
}; *)
|
|
pinferior_ptid = ^tinferior_ptid;
|
|
tinferior_ptid = record
|
|
pid : longint{C int};
|
|
lwp : ptrint{ C long};
|
|
tid : ptrint{ C long};
|
|
end;
|
|
|
|
{$ifdef win32}
|
|
|
|
type
|
|
{ from sys/reent.h
|
|
real structure is bigger but only std.. are wanted here PM }
|
|
REENT = record
|
|
err : longint;
|
|
stdin,stdout,stderr : P_C_FILE;
|
|
end;
|
|
PREENT = ^REENT;
|
|
|
|
var _impure_ptr : PREENT;cvar;external;
|
|
|
|
{$endif win32}
|
|
|
|
|
|
type
|
|
tgdbbuffer=object
|
|
buf : pchar;
|
|
size,
|
|
idx : longint;
|
|
gdb_file : pui_file;
|
|
constructor Init;
|
|
destructor Done;
|
|
procedure Reset;
|
|
procedure Resize(nsize : longint);
|
|
procedure Append(p:pchar);
|
|
procedure lappend(p:pchar;len : longint);
|
|
end;
|
|
|
|
pgdbinterface=^tgdbinterface;
|
|
tgdbinterface=object
|
|
gdberrorbuf,
|
|
gdboutputbuf : tgdbbuffer;
|
|
got_error,
|
|
reset_command,
|
|
call_reset,
|
|
signaled,
|
|
Debuggee_started : boolean;
|
|
{ frames and frame info while recording a frame }
|
|
frames : ppframeentry;
|
|
frame_size,
|
|
frame_count : longint;
|
|
record_frames,
|
|
frame_begin_seen : boolean;
|
|
frame_level,
|
|
command_level,
|
|
stop_breakpoint_number,
|
|
current_address,
|
|
current_line_number,
|
|
signal_start,
|
|
signal_end,
|
|
signal_name_start,
|
|
signal_name_end,
|
|
error_start,
|
|
error_end,
|
|
function_start,
|
|
function_end,
|
|
args_start,
|
|
args_end,
|
|
file_start,
|
|
file_end,
|
|
line_start,
|
|
line_end : longint;
|
|
signal_name,
|
|
signal_string : pchar;
|
|
current_pc : CORE_ADDR;
|
|
{ breakpoint }
|
|
last_breakpoint_number,
|
|
last_breakpoint_address,
|
|
last_breakpoint_line : longint;
|
|
last_breakpoint_file : pchar;
|
|
invalid_breakpoint_line : boolean;
|
|
{ init }
|
|
constructor init;
|
|
destructor done;
|
|
{ Lowlevel }
|
|
function error:boolean;
|
|
function error_num:longint;
|
|
procedure gdb_command(const s:string);
|
|
procedure gdb__init;
|
|
procedure gdb_done;
|
|
procedure resize_frames;
|
|
function add_frameentry:pframeentry;
|
|
function get_frameentry(level : longint):pframeentry;
|
|
function get_current_frame : ptrint;
|
|
function set_current_frame(level : longint) : boolean;
|
|
procedure clear_frames;
|
|
{ Highlevel }
|
|
user_screen_shown,
|
|
switch_to_user : boolean;
|
|
procedure GetAddrSyminfo(addr:ptrint;var si:tsyminfo);
|
|
procedure SelectSourceline(fn:pchar;line:longint);
|
|
procedure StartSession;
|
|
procedure BreakSession;
|
|
procedure EndSession(code:longint);
|
|
procedure DebuggerScreen;
|
|
procedure UserScreen;
|
|
{ Hooks }
|
|
procedure DoSelectSourceline(const fn:string;line:longint);virtual;
|
|
procedure DoStartSession;virtual;
|
|
procedure DoBreakSession;virtual;
|
|
procedure DoEndSession(code:longint);virtual;
|
|
procedure DoUserSignal;virtual;
|
|
procedure DoDebuggerScreen;virtual;
|
|
procedure DoUserScreen;virtual;
|
|
function AllowQuit : boolean;virtual;
|
|
end;
|
|
|
|
|
|
const
|
|
use_gdb_file : boolean = false;
|
|
|
|
var
|
|
curr_gdb : pgdbinterface;
|
|
gdb_file : text;
|
|
inferior_ptid : tinferior_ptid;cvar;external;
|
|
|
|
function GDBVersion : string;
|
|
function inferior_pid : longint;
|
|
|
|
{$ifdef GDB_V6}
|
|
type
|
|
ui_out = pointer;
|
|
var
|
|
uiout : ui_out;cvar;external;
|
|
function cli_out_new (stream : pui_file):ui_out;cdecl;external;
|
|
{$endif}
|
|
|
|
{$ifdef go32v2}
|
|
{ needed to be sure %fs contains the DOS memory selector
|
|
used in Mem[] code PM }
|
|
procedure reload_fs;
|
|
{$endif go32v2}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$ifdef win32}
|
|
initc,
|
|
{$endif win32}
|
|
{$ifdef unix}
|
|
baseunix,
|
|
{$endif}
|
|
{$ifdef go32v2}
|
|
go32,
|
|
dpmiexcp,
|
|
initc,
|
|
{$endif}
|
|
strings;
|
|
|
|
{*****************************************************************************
|
|
Types used by libgdb.a
|
|
*****************************************************************************}
|
|
|
|
{$ifdef go32v2}
|
|
type
|
|
jmp_buf = dpmi_jmp_buf;
|
|
pjmp_buf = pdpmi_jmp_buf;
|
|
|
|
function setjmp(var rec : jmp_buf) : longint;cdecl;external;
|
|
|
|
procedure longjmp(var rec : jmp_buf;return_value : longint);cdecl;external;
|
|
|
|
procedure reload_fs;assembler;
|
|
asm
|
|
movw dosmemselector,%ax
|
|
movw %ax,%fs
|
|
end['EAX'];
|
|
|
|
{$endif}
|
|
{$ifdef win32}
|
|
type
|
|
jmp_buf = record
|
|
case byte of
|
|
0 :
|
|
{ greatest value found in cygwin machine/setjmp.h for i386 }
|
|
(unknown_field : array [1..13] of longint;);
|
|
1 :
|
|
(eax,ebx,ecx,edx : longint;
|
|
esi,edi,ebp,esp,eip : longint;);
|
|
end;
|
|
|
|
pjmp_buf = ^jmp_buf;
|
|
|
|
function setjmp(var rec : jmp_buf) : longint;cdecl;external;
|
|
|
|
procedure longjmp(var rec : jmp_buf;return_value : longint);cdecl;external;
|
|
|
|
{$ifndef supportexceptions}
|
|
type
|
|
{ I don't think FPC would accept that
|
|
the funcvar return type is the funcvar type itself ! PM }
|
|
SignalHandler = Procedure(Sig : LongInt);cdecl;
|
|
function signal(sig : longint;new_signal : SignalHandler) : SignalHandler;cdecl;external;
|
|
|
|
{define supportexceptions not yet working }
|
|
{$endif now exceptions are supported for win32}
|
|
{$endif win32}
|
|
|
|
type
|
|
pCORE_ADDR = ^CORE_ADDR;
|
|
pblock = ^block;
|
|
|
|
tlanguage = (language_unknown,language_auto,language_c,
|
|
language_cplus,language_java,language_chill,
|
|
language_fortran,language_m2,language_asm,
|
|
language_scm,language_pascal,language_objc);
|
|
|
|
bptype = (bp_breakpoint,bp_hardware_breakpoint,
|
|
bp_until,bp_finish,bp_watchpoint,bp_hardware_watchpoint,
|
|
bp_read_watchpoint,bp_access_watchpoint,
|
|
bp_longjmp,bp_longjmp_resume,bp_step_resume,
|
|
bp_through_sigtramp,bp_watchpoint_scope,
|
|
bp_call_dummy,bp_shlib_event);
|
|
|
|
tenable = (disabled,enabled,shlib_disabled);
|
|
|
|
bpdisp = (del,del_at_next_stop,disable,donttouch);
|
|
|
|
{$PACKRECORDS 4}
|
|
pbreakpoint = ^breakpoint;
|
|
breakpoint = record
|
|
next : pbreakpoint;
|
|
typ : bptype;
|
|
enable : tenable;
|
|
disposition : bpdisp;
|
|
number : longint;
|
|
address : CORE_ADDR;
|
|
line_number : longint;
|
|
source_file : pchar;
|
|
silent : byte;
|
|
ignore_count : longint;
|
|
shadow_contents : array[0..15] of char;
|
|
inserted : char;
|
|
duplicate : char;
|
|
commands : pointer; {^command_line}
|
|
frame : CORE_ADDR;
|
|
cond : pointer; {^expression}
|
|
addr_string : ^char;
|
|
language : tlanguage;
|
|
input_radix : longint;
|
|
cond_string : ^char;
|
|
exp_string : ^char;
|
|
exp : pointer; {^expression}
|
|
exp_valid_block : pblock; {^block;}
|
|
val : pointer; {value_ptr;}
|
|
val_chain : pointer; {value_ptr;}
|
|
related_breakpoint : pbreakpoint;
|
|
watchpoint_frame : CORE_ADDR;
|
|
thread : longint;
|
|
hit_count : longint;
|
|
section : pointer; {^asection}
|
|
end;
|
|
|
|
tfreecode=(free_nothing,free_contents,free_linetable);
|
|
|
|
psymtab = ^symtab;
|
|
symtab = record
|
|
next : psymtab;
|
|
blockvector : pointer; {^blockvector;}
|
|
linetable : pointer; {^linetable;}
|
|
block_line_section : longint;
|
|
primary : longint;
|
|
{$ifdef GDB_SYMTAB_HAS_MACROS}
|
|
{ new field added in the middle :( }
|
|
macro_table : pointer;
|
|
{$endif GDB_SYMTAB_HAS_MACROS}
|
|
filename : pchar;
|
|
dirname : pchar;
|
|
free_code : tfreecode;
|
|
free_ptr : pchar;
|
|
nlines : longint;
|
|
line_charpos : ^longint;
|
|
language : tlanguage;
|
|
Debugformat : pchar;
|
|
version : pchar;
|
|
fullname : pchar;
|
|
objfile : pointer; {^objfile;}
|
|
end;
|
|
|
|
psymtab_and_line = ^symtab_and_line;
|
|
symtab_and_line = record
|
|
symtab : psymtab;
|
|
section : pointer; {^asection;}
|
|
line : longint;
|
|
pc : CORE_ADDR;
|
|
_end : CORE_ADDR;
|
|
end;
|
|
|
|
symtabs_and_lines = record
|
|
sals : ^symtab_and_line;
|
|
nelts : longint;
|
|
end;
|
|
|
|
psymbol = ^symbol;
|
|
pminimal_symbol = ^minimal_symbol;
|
|
|
|
general_symbol_info = record
|
|
(* Name of the symbol. This is a required field. Storage for the name is
|
|
allocated on the psymbol_obstack or symbol_obstack for the associated
|
|
objfile. *)
|
|
|
|
_name : pchar;
|
|
|
|
(* Value of the symbol. Which member of this union to use, and what
|
|
it means, depends on what kind of symbol this is and its
|
|
SYMBOL_CLASS. See comments there for more details. All of these
|
|
are in host byte order (though what they point to might be in
|
|
target byte order, e.g. LOC_CONST_BYTES). *)
|
|
value : record
|
|
case integer of
|
|
(* The fact that this is a long not a LONGEST mainly limits the
|
|
range of a LOC_CONST. Since LOC_CONST_BYTES exists, I'm not
|
|
sure that is a big deal. *)
|
|
0 : (ivalue : longint;);
|
|
|
|
1 : (block : pblock;);
|
|
|
|
2 : (bytes : pchar;);
|
|
|
|
3 : (address : CORE_ADDR;);
|
|
|
|
(* for opaque typedef struct chain *)
|
|
|
|
4 : (chain : psymbol;);
|
|
end;
|
|
|
|
(* Since one and only one language can apply, wrap the language specific
|
|
information inside a union. *)
|
|
|
|
(* union
|
|
{
|
|
struct cplus_specific /* For C++ */
|
|
/* and Java */
|
|
{
|
|
char *demangled_name;
|
|
} cplus_specific;
|
|
struct chill_specific /* For Chill */
|
|
{
|
|
char *demangled_name;
|
|
} chill_specific;
|
|
} language_specific; *)
|
|
demangled_name : pchar;
|
|
|
|
(* Record the source code language that applies to this symbol.
|
|
This is used to select one of the fields from the language specific
|
|
union above. *)
|
|
|
|
language : tlanguage;
|
|
|
|
(* Which section is this symbol in? This is an index into
|
|
section_offsets for this objfile. Negative means that the symbol
|
|
does not get relocated relative to a section.
|
|
Disclaimer: currently this is just used for xcoff, so don't
|
|
expect all symbol-reading code to set it correctly (the ELF code
|
|
also tries to set it correctly). *)
|
|
|
|
section : word;
|
|
|
|
(* The bfd section associated with this symbol. *)
|
|
|
|
bfd_section : pointer {^asection};
|
|
end; { of general_symbol_info record declaration }
|
|
|
|
tminimal_symbol_type =
|
|
(
|
|
mst_unknown := 0, (* Unknown type, the default *)
|
|
mst_text, (* Generally executable instructions *)
|
|
mst_data, (* Generally initialized data *)
|
|
mst_bss, (* Generally uninitialized data *)
|
|
mst_abs, (* Generally absolute (nonrelocatable) *)
|
|
(* GDB uses mst_solib_trampoline for the start address of a shared
|
|
library trampoline entry. Breakpoints for shared library functions
|
|
are put there if the shared library is not yet loaded.
|
|
After the shared library is loaded, lookup_minimal_symbol will
|
|
prefer the minimal symbol from the shared library (usually
|
|
a mst_text symbol) over the mst_solib_trampoline symbol, and the
|
|
breakpoints will be moved to their true address in the shared
|
|
library via breakpoint_re_set. *)
|
|
mst_solib_trampoline, (* Shared library trampoline code *)
|
|
(* For the mst_file* types, the names are only guaranteed to be unique
|
|
within a given .o file. *)
|
|
mst_file_text, (* Static version of mst_text *)
|
|
mst_file_data, (* Static version of mst_data *)
|
|
mst_file_bss (* Static version of mst_bss *)
|
|
);
|
|
|
|
namespace_enum = (
|
|
(* UNDEF_NAMESPACE is used when a namespace has not been discovered or
|
|
none of the following apply. This usually indicates an error either
|
|
in the symbol information or in gdb's handling of symbols. *)
|
|
UNDEF_NAMESPACE,
|
|
|
|
(* VAR_NAMESPACE is the usual namespace. In C, this contains variables,
|
|
function names, typedef names and enum type values. *)
|
|
VAR_NAMESPACE,
|
|
|
|
(* STRUCT_NAMESPACE is used in C to hold struct, union and enum type names.
|
|
Thus, if `struct foo' is used in a C program, it produces a symbol named
|
|
`foo' in the STRUCT_NAMESPACE. *)
|
|
STRUCT_NAMESPACE,
|
|
|
|
(* LABEL_NAMESPACE may be used for names of labels (for gotos);
|
|
currently it is not used and labels are not recorded at all. *)
|
|
LABEL_NAMESPACE,
|
|
|
|
(* Searching namespaces. These overlap with VAR_NAMESPACE, providing
|
|
some granularity with the search_symbols function. *)
|
|
(* Everything in VAR_NAMESPACE minus FUNCTIONS_-, TYPES_-, and
|
|
METHODS_NAMESPACE *)
|
|
VARIABLES_NAMESPACE,
|
|
|
|
(* All functions -- for some reason not methods, though. *)
|
|
FUNCTIONS_NAMESPACE,
|
|
|
|
(* All defined types *)
|
|
TYPES_NAMESPACE,
|
|
|
|
(* All class methods -- why is this separated out? *)
|
|
METHODS_NAMESPACE
|
|
|
|
);
|
|
address_class = (
|
|
(* Not used; catches errors *)
|
|
LOC_UNDEF,
|
|
|
|
(* Value is constant int SYMBOL_VALUE, host byteorder *)
|
|
LOC_CONST,
|
|
|
|
(* Value is at fixed address SYMBOL_VALUE_ADDRESS *)
|
|
LOC_STATIC,
|
|
|
|
(* Value is in register. SYMBOL_VALUE is the register number. *)
|
|
LOC_REGISTER,
|
|
|
|
(* It's an argument; the value is at SYMBOL_VALUE offset in arglist. *)
|
|
LOC_ARG,
|
|
|
|
(* Value address is at SYMBOL_VALUE offset in arglist. *)
|
|
LOC_REF_ARG,
|
|
|
|
(* Value is in register number SYMBOL_VALUE. Just like LOC_REGISTER
|
|
except this is an argument. Probably the cleaner way to handle
|
|
this would be to separate address_class (which would include
|
|
separate ARG and LOCAL to deal with FRAME_ARGS_ADDRESS versus
|
|
FRAME_LOCALS_ADDRESS), and an is_argument flag.
|
|
|
|
For some symbol formats (stabs, for some compilers at least),
|
|
the compiler generates two symbols, an argument and a register.
|
|
In some cases we combine them to a single LOC_REGPARM in symbol
|
|
reading, but currently not for all cases (e.g. it's passed on the
|
|
stack and then loaded into a register). *)
|
|
LOC_REGPARM,
|
|
|
|
(* Value is in specified register. Just like LOC_REGPARM except the
|
|
register holds the address of the argument instead of the argument
|
|
itself. This is currently used for the passing of structs and unions
|
|
on sparc and hppa. It is also used for call by reference where the
|
|
address is in a register, at least by mipsread.c. *)
|
|
LOC_REGPARM_ADDR,
|
|
|
|
(* Value is a local variable at SYMBOL_VALUE offset in stack frame. *)
|
|
LOC_LOCAL,
|
|
|
|
(* Value not used; definition in SYMBOL_TYPE. Symbols in the namespace
|
|
STRUCT_NAMESPACE all have this class. *)
|
|
LOC_TYPEDEF,
|
|
|
|
(* Value is address SYMBOL_VALUE_ADDRESS in the code *)
|
|
LOC_LABEL,
|
|
|
|
(* In a symbol table, value is SYMBOL_BLOCK_VALUE of a `struct block'.
|
|
In a partial symbol table, SYMBOL_VALUE_ADDRESS is the start address
|
|
of the block. Function names have this class. *)
|
|
LOC_BLOCK,
|
|
|
|
(* Value is a constant byte-sequence pointed to by SYMBOL_VALUE_BYTES, in
|
|
target byte order. *)
|
|
LOC_CONST_BYTES,
|
|
|
|
(* Value is arg at SYMBOL_VALUE offset in stack frame. Differs from
|
|
LOC_LOCAL in that symbol is an argument; differs from LOC_ARG in
|
|
that we find it in the frame (FRAME_LOCALS_ADDRESS), not in the
|
|
arglist (FRAME_ARGS_ADDRESS). Added for i960, which passes args
|
|
in regs then copies to frame. *)
|
|
LOC_LOCAL_ARG,
|
|
|
|
(* Value is at SYMBOL_VALUE offset from the current value of
|
|
register number SYMBOL_BASEREG. This exists mainly for the same
|
|
things that LOC_LOCAL and LOC_ARG do; but we need to do this
|
|
instead because on 88k DWARF gives us the offset from the
|
|
frame/stack pointer, rather than the offset from the "canonical
|
|
frame address" used by COFF, stabs, etc., and we don't know how
|
|
to convert between these until we start examining prologues.
|
|
|
|
Note that LOC_BASEREG is much less general than a DWARF expression.
|
|
We don't need the generality (at least not yet), and storing a general
|
|
DWARF expression would presumably take up more space than the existing
|
|
scheme. *)
|
|
LOC_BASEREG,
|
|
|
|
(* Same as LOC_BASEREG but it is an argument. *)
|
|
LOC_BASEREG_ARG,
|
|
|
|
(* Value is at fixed address, but the address of the variable has
|
|
to be determined from the minimal symbol table whenever the
|
|
variable is referenced.
|
|
This happens if debugging information for a global symbol is
|
|
emitted and the corresponding minimal symbol is defined
|
|
in another object file or runtime common storage.
|
|
The linker might even remove the minimal symbol if the global
|
|
symbol is never referenced, in which case the symbol remains
|
|
unresolved. *)
|
|
LOC_UNRESOLVED,
|
|
|
|
(* Value is at a thread-specific location calculated by a
|
|
target-specific method. *)
|
|
LOC_THREAD_LOCAL_STATIC,
|
|
|
|
(* The variable does not actually exist in the program.
|
|
The value is ignored. *)
|
|
LOC_OPTIMIZED_OUT,
|
|
|
|
(* The variable is static, but actually lives at * (address).
|
|
* I.e. do an extra indirection to get to it.
|
|
* This is used on HP-UX to get at globals that are allocated
|
|
* in shared libraries, where references from images other
|
|
* than the one where the global was allocated are done
|
|
* with a level of indirection.
|
|
*)
|
|
LOC_INDIRECT
|
|
);
|
|
|
|
minimal_symbol = record
|
|
(* The general symbol info required for all types of symbols.
|
|
The SYMBOL_VALUE_ADDRESS contains the address that this symbol
|
|
corresponds to. *)
|
|
ginfo : general_symbol_info;
|
|
|
|
(* The info field is available for caching machine-specific information
|
|
so it doesn't have to rederive the info constantly (over a serial line).
|
|
It is initialized to zero and stays that way until target-dependent code
|
|
sets it. Storage for any data pointed to by this field should be allo-
|
|
cated on the symbol_obstack for the associated objfile.
|
|
The type would be "void *" except for reasons of compatibility with older
|
|
compilers. This field is optional.
|
|
|
|
Currently, the AMD 29000 tdep.c uses it to remember things it has decoded
|
|
from the instructions in the function header, and the MIPS-16 code uses
|
|
it to identify 16-bit procedures. *)
|
|
|
|
info : pchar;
|
|
|
|
{$ifdef SOFUN_ADDRESS_MAYBE_MISSING}
|
|
(* Which source file is this symbol in? Only relevant for mst_file_*. *)
|
|
filename : pchar;
|
|
{$endif}
|
|
|
|
(* Classification types for this symbol. These should be taken as "advisory
|
|
only", since if gdb can't easily figure out a classification it simply
|
|
selects mst_unknown. It may also have to guess when it can't figure out
|
|
which is a better match between two types (mst_data versus mst_bss) for
|
|
example. Since the minimal symbol info is sometimes derived from the
|
|
BFD library's view of a file, we need to live with what information bfd
|
|
supplies. *)
|
|
|
|
minimal_symbol_type : tminimal_symbol_type;
|
|
end{ of minimal_symbol};
|
|
|
|
block = record
|
|
(* Addresses in the executable code that are in this block. *)
|
|
startaddr,
|
|
endaddr : CORE_ADDR ;
|
|
|
|
(* The symbol that names this block, if the block is the body of a
|
|
function; otherwise, zero. *)
|
|
_function : psymbol;
|
|
|
|
(* The `struct block' for the containing block, or 0 if none.
|
|
The superblock of a top-level local block (i.e. a function in the
|
|
case of C) is the STATIC_BLOCK. The superblock of the
|
|
STATIC_BLOCK is the GLOBAL_BLOCK. *)
|
|
|
|
superblock : pblock;
|
|
|
|
(* Version of GCC used to compile the function corresponding
|
|
to this block, or 0 if not compiled with GCC. When possible,
|
|
GCC should be compatible with the native compiler, or if that
|
|
is not feasible, the differences should be fixed during symbol
|
|
reading. As of 16 Apr 93, this flag is never used to distinguish
|
|
between gcc2 and the native compiler.
|
|
|
|
If there is no function corresponding to this block, this meaning
|
|
of this flag is undefined. *)
|
|
|
|
gcc_compile_flag : byte;
|
|
|
|
(* Number of local symbols. *)
|
|
nsyms : longint;
|
|
|
|
(* The symbols. If some of them are arguments, then they must be
|
|
in the order in which we would like to print them. *)
|
|
sym : array [0..0] of psymbol;
|
|
end { of block definition };
|
|
|
|
symbol = record
|
|
(* The general symbol info required for all types of symbols. *)
|
|
ginfo : general_symbol_info;
|
|
|
|
(* Data type of value *)
|
|
_type : pointer{ptype};
|
|
|
|
(* Name space code. *)
|
|
namespace : namespace_enum;
|
|
|
|
(* Address class *)
|
|
|
|
aclass : address_class;
|
|
|
|
(* Line number of definition. FIXME: Should we really make the assumption
|
|
that nobody will try to debug files longer than 64K lines? What about
|
|
machine generated programs? *)
|
|
|
|
line : word;
|
|
|
|
(* Some symbols require an additional value to be recorded on a per-
|
|
symbol basis. Stash those values here. *)
|
|
|
|
(*union
|
|
{
|
|
/* Used by LOC_BASEREG and LOC_BASEREG_ARG. */
|
|
short basereg;
|
|
} *)
|
|
aux_value_base_reg : word;
|
|
|
|
(* Link to a list of aliases for this symbol.
|
|
Only a "primary/main symbol may have aliases. *)
|
|
aliases : pointer{palias_list};
|
|
|
|
(* List of ranges where this symbol is active. This is only
|
|
used by alias symbols at the current time. *)
|
|
ranges : pointer{prange_list};
|
|
end;
|
|
|
|
target_signal = (TARGET_SIGNAL_FIRST := 0,
|
|
TARGET_SIGNAL_HUP := 1,TARGET_SIGNAL_INT := 2,
|
|
TARGET_SIGNAL_QUIT := 3,TARGET_SIGNAL_ILL := 4,
|
|
TARGET_SIGNAL_TRAP := 5,TARGET_SIGNAL_ABRT := 6,
|
|
TARGET_SIGNAL_EMT := 7,TARGET_SIGNAL_FPE := 8,
|
|
TARGET_SIGNAL_KILL := 9,TARGET_SIGNAL_BUS := 10,
|
|
TARGET_SIGNAL_SEGV := 11,TARGET_SIGNAL_SYS := 12,
|
|
TARGET_SIGNAL_PIPE := 13,TARGET_SIGNAL_ALRM := 14,
|
|
TARGET_SIGNAL_TERM := 15,TARGET_SIGNAL_URG := 16,
|
|
TARGET_SIGNAL_STOP := 17,TARGET_SIGNAL_TSTP := 18,
|
|
TARGET_SIGNAL_CONT := 19,TARGET_SIGNAL_CHLD := 20,
|
|
TARGET_SIGNAL_TTIN := 21,TARGET_SIGNAL_TTOU := 22,
|
|
TARGET_SIGNAL_IO := 23,TARGET_SIGNAL_XCPU := 24,
|
|
TARGET_SIGNAL_XFSZ := 25,TARGET_SIGNAL_VTALRM := 26,
|
|
TARGET_SIGNAL_PROF := 27,TARGET_SIGNAL_WINCH := 28,
|
|
TARGET_SIGNAL_LOST := 29,TARGET_SIGNAL_USR1 := 30,
|
|
TARGET_SIGNAL_USR2 := 31,TARGET_SIGNAL_PWR := 32,
|
|
TARGET_SIGNAL_POLL := 33,TARGET_SIGNAL_WIND := 34,
|
|
TARGET_SIGNAL_PHONE := 35,TARGET_SIGNAL_WAITING := 36,
|
|
TARGET_SIGNAL_LWP := 37,TARGET_SIGNAL_DANGER := 38,
|
|
TARGET_SIGNAL_GRANT := 39,TARGET_SIGNAL_RETRACT := 40,
|
|
TARGET_SIGNAL_MSG := 41,TARGET_SIGNAL_SOUND := 42,
|
|
TARGET_SIGNAL_SAK := 43,TARGET_SIGNAL_PRIO := 44,
|
|
TARGET_SIGNAL_REALTIME_33 := 45,TARGET_SIGNAL_REALTIME_34 := 46,
|
|
TARGET_SIGNAL_REALTIME_35 := 47,TARGET_SIGNAL_REALTIME_36 := 48,
|
|
TARGET_SIGNAL_REALTIME_37 := 49,TARGET_SIGNAL_REALTIME_38 := 50,
|
|
TARGET_SIGNAL_REALTIME_39 := 51,TARGET_SIGNAL_REALTIME_40 := 52,
|
|
TARGET_SIGNAL_REALTIME_41 := 53,TARGET_SIGNAL_REALTIME_42 := 54,
|
|
TARGET_SIGNAL_REALTIME_43 := 55,TARGET_SIGNAL_REALTIME_44 := 56,
|
|
TARGET_SIGNAL_REALTIME_45 := 57,TARGET_SIGNAL_REALTIME_46 := 58,
|
|
TARGET_SIGNAL_REALTIME_47 := 59,TARGET_SIGNAL_REALTIME_48 := 60,
|
|
TARGET_SIGNAL_REALTIME_49 := 61,TARGET_SIGNAL_REALTIME_50 := 62,
|
|
TARGET_SIGNAL_REALTIME_51 := 63,TARGET_SIGNAL_REALTIME_52 := 64,
|
|
TARGET_SIGNAL_REALTIME_53 := 65,TARGET_SIGNAL_REALTIME_54 := 66,
|
|
TARGET_SIGNAL_REALTIME_55 := 67,TARGET_SIGNAL_REALTIME_56 := 68,
|
|
TARGET_SIGNAL_REALTIME_57 := 69,TARGET_SIGNAL_REALTIME_58 := 70,
|
|
TARGET_SIGNAL_REALTIME_59 := 71,TARGET_SIGNAL_REALTIME_60 := 72,
|
|
TARGET_SIGNAL_REALTIME_61 := 73,TARGET_SIGNAL_REALTIME_62 := 74,
|
|
TARGET_SIGNAL_REALTIME_63 := 75,TARGET_SIGNAL_UNKNOWN,
|
|
TARGET_SIGNAL_DEFAULT,TARGET_SIGNAL_LAST
|
|
);
|
|
|
|
strata = (dummy_stratum,file_stratum,core_stratum,download_stratum,process_stratum);
|
|
|
|
ptarget_ops = ^target_ops;
|
|
target_ops = record
|
|
to_shortname : pchar;
|
|
to_longname : pchar;
|
|
to_doc : pchar;
|
|
to_open : procedure (_para1:pchar; _para2:longint);
|
|
to_close : procedure (_para1:longint);
|
|
to_attach : procedure (_para1:pchar; _para2:longint);
|
|
to_detach : procedure (_para1:pchar; _para2:longint);
|
|
to_resume : procedure (_para1:longint; _para2:longint; _para3:target_signal);
|
|
to_wait : pointer; {function (_para1:longint; _para2:ptarget_waitstatus):longint;}
|
|
to_fetch_registers : procedure (_para1:longint);
|
|
to_store_registers : procedure (_para1:longint);
|
|
to_prepare_to_store : procedure ;
|
|
to_xfer_memory : function (memaddr:CORE_ADDR; myaddr:pchar; len:longint; write:longint; target:ptarget_ops):longint;
|
|
to_files_info : procedure (_para1:ptarget_ops);
|
|
to_insert_breakpoint : function (_para1:CORE_ADDR; _para2:pchar):longint;
|
|
to_remove_breakpoint : function (_para1:CORE_ADDR; _para2:pchar):longint;
|
|
to_terminal_init : procedure ;
|
|
to_terminal_inferior : procedure ;
|
|
to_terminal_ours_for_output : procedure ;
|
|
to_terminal_ours : procedure ;
|
|
to_terminal_info : procedure (_para1:pchar; _para2:longint);
|
|
to_kill : procedure ;
|
|
to_load : procedure (_para1:pchar; _para2:longint);
|
|
to_lookup_symbol : function (_para1:pchar; _para2:pCORE_ADDR):longint;
|
|
to_create_inferior : procedure (_para1:pchar; _para2:pchar; _para3:ppchar);
|
|
to_mourn_inferior : procedure ;
|
|
to_can_run : function :longint;
|
|
to_notice_signals : procedure (pid:longint);
|
|
to_thread_alive : function (pid:longint):longint;
|
|
to_stop : procedure ;
|
|
to_stratum : strata;
|
|
DONT_USE : pointer;
|
|
to_has_all_memory : longint;
|
|
to_has_memory : longint;
|
|
to_has_stack : longint;
|
|
to_has_registers : longint;
|
|
to_has_execution : longint;
|
|
to_sections : pointer; {^section_table}
|
|
to_sections_end : pointer; {^section_table}
|
|
to_magic : longint;
|
|
end;
|
|
|
|
{$PACKRECORDS NORMAL}
|
|
|
|
{*****************************************************************************
|
|
Define external calls to libgdb.a
|
|
*****************************************************************************}
|
|
|
|
var
|
|
{ external variables }
|
|
error_return : jmp_buf;cvar;
|
|
quit_return : jmp_buf;cvar;
|
|
{$ifdef GDB_V6}
|
|
deprecated_create_breakpoint_hook : pointer;cvar;external;
|
|
{$else}
|
|
create_breakpoint_hook : pointer;cvar;external;
|
|
{$endif}
|
|
current_target : target_ops;cvar;external;
|
|
stop_pc : CORE_ADDR;cvar;external;
|
|
{ Only used from GDB 5.01 but doesn't hurst otherwise }
|
|
interpreter_p : pchar;cvar;
|
|
|
|
{ we need also to declare some vars }
|
|
watchdog : longint;cvar;external;
|
|
gdb_error : longint;cvar;public;
|
|
display_time : longbool;cvar;public;
|
|
display_space : longbool;cvar;public;
|
|
|
|
{ Whether this is the command line version or not }
|
|
tui_version : longint;cvar;public;
|
|
|
|
{ Whether xdb commands will be handled }
|
|
xdb_commands : longint;cvar;public;
|
|
|
|
{ Whether dbx commands will be handled }
|
|
dbx_commands : longint;cvar;public;
|
|
|
|
var
|
|
gdb_stdout : pui_file;cvar;public;
|
|
gdb_stderr : pui_file;cvar;public;
|
|
gdb_stdlog : pui_file;cvar;public;
|
|
gdb_stdtarg : pui_file;cvar;public;
|
|
event_loop_p : longint;cvar;public;
|
|
{$ifdef GDB_V6}
|
|
(* target IO streams *)
|
|
gdb_stdtargin : pui_file;cvar;public;
|
|
gdb_stdtargerr : pui_file;cvar;public;
|
|
{$endif}
|
|
|
|
{ used for gdb_stdout and gdb_stderr }
|
|
function xmalloc(size : longint) : pointer;cdecl;external;
|
|
function find_pc_line(i:CORE_ADDR;l:longint):symtab_and_line;cdecl;external;
|
|
function find_pc_function(i:CORE_ADDR):psymbol;cdecl;external;
|
|
function lookup_minimal_symbol_by_pc(i : CORE_ADDR):pminimal_symbol;cdecl;external;
|
|
procedure gdb_init;cdecl;external;
|
|
procedure execute_command(p:pchar;i:longint);cdecl;external;
|
|
procedure target_kill;cdecl;external;
|
|
procedure target_close(i:longint);cdecl;external;
|
|
|
|
|
|
{*****************************************************************************
|
|
Helpers
|
|
*****************************************************************************}
|
|
|
|
procedure Debug(const s:string);
|
|
begin
|
|
if use_gdb_file then
|
|
Writeln(gdb_file,s)
|
|
else
|
|
Writeln(s);
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TFrameEntry
|
|
*****************************************************************************}
|
|
|
|
constructor tframeentry.init;
|
|
begin
|
|
Reset;
|
|
end;
|
|
|
|
destructor tframeentry.done;
|
|
begin
|
|
Clear;
|
|
end;
|
|
|
|
procedure tframeentry.reset;
|
|
begin
|
|
file_name:=nil;
|
|
function_name:=nil;
|
|
args:=nil;
|
|
line_number:=0;
|
|
address:=0;
|
|
end;
|
|
|
|
procedure tframeentry.clear;
|
|
begin
|
|
if assigned(file_name) then
|
|
strdispose(file_name);
|
|
if assigned(function_name) then
|
|
strdispose(function_name);
|
|
if assigned(args) then
|
|
strdispose(args);
|
|
reset;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
tgdbbuffer
|
|
*****************************************************************************}
|
|
|
|
const
|
|
blocksize=2048;
|
|
|
|
constructor tgdbbuffer.init;
|
|
begin
|
|
Buf:=nil;
|
|
gdb_file:=nil;
|
|
Size:=0;
|
|
Resize(blocksize);
|
|
Reset;
|
|
end;
|
|
|
|
|
|
destructor tgdbbuffer.done;
|
|
begin
|
|
if assigned(buf) then
|
|
freemem(buf,size);
|
|
end;
|
|
|
|
|
|
|
|
procedure tgdbbuffer.reset;
|
|
begin
|
|
idx:=0;
|
|
Buf[0]:=#0;
|
|
end;
|
|
|
|
|
|
procedure tgdbbuffer.append(p:pchar);
|
|
var
|
|
len : longint;
|
|
begin
|
|
if not assigned(p) then
|
|
exit;
|
|
len:=Strlen(p);
|
|
if len+1+idx>size then
|
|
Resize(len+1+idx);
|
|
Move(p^,buf[idx],len);
|
|
inc(idx,len);
|
|
buf[idx]:=#0;
|
|
end;
|
|
|
|
|
|
procedure tgdbbuffer.lappend(p:pchar;len : longint);
|
|
begin
|
|
if not assigned(p) then
|
|
exit;
|
|
if len+idx+1>size then
|
|
Resize(len+idx+1);
|
|
Move(p^,buf[idx],len);
|
|
inc(idx,len);
|
|
buf[idx]:=#0;
|
|
end;
|
|
|
|
|
|
procedure tgdbbuffer.resize(nsize : longint);
|
|
var
|
|
np : pchar;
|
|
begin
|
|
nsize:=((nsize+blocksize-1) div blocksize)*blocksize;
|
|
getmem(np,nsize);
|
|
if assigned(buf) then
|
|
begin
|
|
move(buf^,np^,size);
|
|
freemem(buf,size);
|
|
end;
|
|
buf:=np;
|
|
size:=nsize;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
Hook calls from libgdb.a
|
|
*****************************************************************************}
|
|
|
|
{$ifdef go32v2}
|
|
procedure gdbpas_prev_exception_handler;cdecl;public;
|
|
begin
|
|
end;
|
|
{$endif go32v2}
|
|
|
|
procedure init_proc;cdecl;public;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure annotate_signalled;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signalled|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_signal_name;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signal_name|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
signal_name_start:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_signal_name_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signal_name_end|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
signal_name_end:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_signal_string;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signal_string|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
signal_start:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_signal_string_end;cdecl;public;
|
|
var
|
|
c : char;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signal_string_end|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
begin
|
|
signal_end:=gdboutputbuf.idx;
|
|
c:=gdboutputbuf.buf[signal_end];
|
|
gdboutputbuf.buf[signal_end]:=#0;
|
|
if assigned(signal_string) then
|
|
strdispose(signal_string);
|
|
signal_string:=strnew(gdboutputbuf.buf+signal_start);
|
|
gdboutputbuf.buf[signal_end]:=c;
|
|
c:=gdboutputbuf.buf[signal_name_end];
|
|
gdboutputbuf.buf[signal_name_end]:=#0;
|
|
if assigned(signal_name) then
|
|
strdispose(signal_name);
|
|
signal_name:=strnew(gdboutputbuf.buf+signal_name_start);
|
|
gdboutputbuf.buf[signal_name_end]:=c;
|
|
if (user_screen_shown) then
|
|
begin
|
|
DebuggerScreen;
|
|
DoUserSignal;
|
|
UserScreen;
|
|
end
|
|
else
|
|
DoUserSignal;
|
|
call_reset:=true;
|
|
signaled:=false;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure annotate_signal;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signal|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
signaled:=true;
|
|
end;
|
|
|
|
|
|
procedure annotate_exited(exitstatus:longint);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|exited|');
|
|
{$endif}
|
|
{#ifdef __DJGPP__
|
|
/* this is very important. The exit code of a djgpp program
|
|
disables interrupts and after this there is no other interrupt
|
|
called, which enables interrupts with the iret. */
|
|
__dpmi_get_and_enable_virtual_interrupt_state();
|
|
#endif }
|
|
{$ifdef go32v2}
|
|
{$asmmode att}
|
|
asm
|
|
movw $0x901,%ax
|
|
int $0x31
|
|
end;
|
|
{$asmmode default}
|
|
reload_fs;
|
|
{$endif def go32v2}
|
|
|
|
curr_gdb^.DebuggerScreen;
|
|
{ DeleteBreakPoints; }
|
|
curr_gdb^.EndSession(exitstatus);
|
|
end;
|
|
|
|
|
|
procedure annotate_error;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|error|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_error_begin;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|error begin|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
begin
|
|
error_start:=gdboutputbuf.idx+strlen(gdboutputbuf.buf);
|
|
got_error:=true;
|
|
end;
|
|
{$ifdef Verbose}
|
|
Debug('|end of error begin|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_starting;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|starting|');
|
|
{$endif}
|
|
{$ifdef go32v2}
|
|
reload_fs;
|
|
{$endif go32v2}
|
|
curr_gdb^.UserScreen;
|
|
end;
|
|
|
|
|
|
procedure annotate_stopped;cdecl;public;
|
|
var
|
|
sym : symtab_and_line;
|
|
fname : pchar;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|stopped|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
begin
|
|
{$ifdef go32v2}
|
|
reload_fs;
|
|
{$endif go32v2}
|
|
DebuggerScreen;
|
|
current_pc:=stop_pc;
|
|
Debuggee_started:=inferior_pid<>0;
|
|
if not Debuggee_started then exit;
|
|
if reset_command then exit;
|
|
sym:=find_pc_line(stop_pc,0);
|
|
if assigned(sym.symtab) then
|
|
fname:=sym.symtab^.filename
|
|
else
|
|
fname:=nil;
|
|
SelectSourceLine(fname,sym.line);
|
|
end;
|
|
end;
|
|
|
|
|
|
function inferior_pid : longint;
|
|
begin
|
|
inferior_pid:=inferior_ptid.pid;
|
|
end;
|
|
|
|
|
|
procedure proc_remove_foreign(pid:longint);cdecl;public;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure breakpoints_changed;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|breakpoints_changed|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_ignore_count_change;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|annotate_ignore_count_change()|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_breakpoint(num:longint);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|breakpoint(%d)|');
|
|
{$endif}
|
|
With Curr_gdb^ do
|
|
stop_breakpoint_number:=num;
|
|
end;
|
|
|
|
|
|
procedure annotate_watchpoint(num:longint);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|watchpoint(%d)|');
|
|
{$endif}
|
|
With Curr_gdb^ do
|
|
stop_breakpoint_number:=num;
|
|
end;
|
|
|
|
procedure annotate_catchpoint(num:longint);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|catchpoint(%d)|');
|
|
{$endif}
|
|
With Curr_gdb^ do
|
|
stop_breakpoint_number:=num;
|
|
end;
|
|
|
|
|
|
procedure annotate_breakpoints_headers;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|breakpoints_headers|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_breakpoints_table;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|breakpoints_table|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_record;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|record|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_breakpoints_table_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|breakpoints_table_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_frames_invalid;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frames_invalid|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_begin(level:longint;pc:CORE_ADDR);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_begin(%d,%ld)|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
begin
|
|
frame_begin_seen:=true;
|
|
frame_level:=level;
|
|
current_address:=pc;
|
|
current_line_number:=-1;
|
|
function_start:=-1;
|
|
function_end:=-1;
|
|
args_start:=-1;
|
|
args_end:=-1;
|
|
file_start:=-1;
|
|
file_end:=-1;
|
|
line_start:=-1;
|
|
line_end:=-1;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_address;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_address|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_address_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_address_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure annotate_frame_function_name;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_function_name|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
function_start:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_args;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_args|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
begin
|
|
function_end:=gdboutputbuf.idx;
|
|
args_start:=gdboutputbuf.idx;
|
|
end;
|
|
end;
|
|
|
|
procedure annotate_frame_source_begin;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_source_begin|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
args_end:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_source_file;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_source_file|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
file_start:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
procedure annotate_frame_source_file_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_source_file_end|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
file_end:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_source_line;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_source_line|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
line_start:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_source_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_source_end|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
line_end:=gdboutputbuf.idx;
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_where;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_where|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_frame_end;cdecl;public;
|
|
var
|
|
fe : pframeentry;
|
|
c : char;
|
|
err : integer;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|frame_end|');
|
|
{$endif}
|
|
with curr_gdb^ do
|
|
begin
|
|
if (not record_frames) or (not frame_begin_seen) then
|
|
exit;
|
|
{ This can happen, when the function has no Debugging information }
|
|
if (args_start >= 0) and (args_end < 0) then
|
|
args_end:=gdboutputbuf.idx;
|
|
frame_begin_seen:=false;
|
|
fe:=get_frameentry(frame_level);
|
|
fe^.address:=current_address;
|
|
fe^.level:=frame_level;
|
|
if (function_start>=0) then
|
|
begin
|
|
c:=gdboutputbuf.buf[function_end];
|
|
gdboutputbuf.buf[function_end]:=#0;
|
|
fe^.function_name:=strnew(gdboutputbuf.buf+function_start);
|
|
gdboutputbuf.buf[function_end]:=c;
|
|
end;
|
|
if (file_start>=0) then
|
|
begin
|
|
c:=gdboutputbuf.buf[file_end];
|
|
gdboutputbuf.buf[file_end]:=#0;
|
|
fe^.file_name:=strnew(gdboutputbuf.buf+file_start);
|
|
gdboutputbuf.buf[file_end]:=c;
|
|
end;
|
|
if (args_start>=0) then
|
|
begin
|
|
{$warning FIXME} {sometimes the ide crashes here because ars_end is 0, AD}
|
|
if args_end > 0 then
|
|
begin
|
|
if (gdboutputbuf.buf[args_end-1]=#10) then
|
|
dec(args_end);
|
|
c:=gdboutputbuf.buf[args_end];
|
|
gdboutputbuf.buf[args_end]:=#0;
|
|
fe^.args:=strnew(gdboutputbuf.buf+args_start);
|
|
gdboutputbuf.buf[args_end]:=c;
|
|
end;
|
|
end;
|
|
if (line_start>=0) then
|
|
begin
|
|
c:=gdboutputbuf.buf[line_end];
|
|
gdboutputbuf.buf[line_end]:=#0;
|
|
{ sscanf(gdb_output_buffer+line_start,'%d',&fe^.line_number); }
|
|
val(strpas(pchar(@gdboutputbuf.buf[line_start])),fe^.line_number,err);
|
|
gdboutputbuf.buf[line_end]:=c;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure annotate_quit;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|quit|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_arg_begin;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|arg_begin|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_arg_name_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|arg_name_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_arg_value(typ:pointer);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|arg_value|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_arg_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|arg_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure annotate_source(filename:pchar;line,character,mid:longint;pc:CORE_ADDR);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|source|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_function_call;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|function_call|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_signal_handler_caller;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|signal_handler_caller|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_array_section_begin(index:longint;elttype:pointer);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|array_section_begin()|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_elt_rep(repcount:longint);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|elt_rep()|');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure annotate_elt_rep_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|elt_rep_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_elt;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|elt|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_array_section_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|array_section_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure annotate_display_begin;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_begin|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_display_number_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_number_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_display_format;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_format|');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure annotate_display_expression;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_expression|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_display_expression_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_expression_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_display_value;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_value|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_display_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|display_end|');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_field (num:longint);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_field(%d)');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_field_begin(typ:pointer);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_field_begin\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_field_name_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_field_name_end\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_field_value;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_field_value\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_field_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_field_end\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_value_history_begin (histindex:longint;typ:pointer);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_value_history_begin(%d)\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_value_begin (typ:pointer);cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_value_begin\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_value_history_value;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_value_history_value\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_value_history_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_value_history_end\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure annotate_value_end;cdecl;public;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('a_value_end\n');
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure _initialize_annotate;cdecl;public;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure gdbint_ui_file_write(stream : pui_file; p : pchar; len : longint);cdecl;
|
|
begin
|
|
if assigned(curr_gdb) then
|
|
with curr_gdb^ do
|
|
if stream = gdb_stderr then
|
|
gdberrorbuf.lappend(p,len)
|
|
else if stream = gdb_stdout then
|
|
gdboutputbuf.lappend(p,len)
|
|
else
|
|
begin
|
|
gdberrorbuf.append('Unknown gdb ui_file');
|
|
gdberrorbuf.lappend(p,len);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure CreateBreakPointHook(var b:breakpoint);cdecl;
|
|
var
|
|
sym : symtab_and_line;
|
|
|
|
{ this procedure is only here to avoid the problems
|
|
with different version of gcc having different stack
|
|
handling:
|
|
on older versions find_pc_line uses just "ret"
|
|
while on newer gcc version "ret $4" is used
|
|
if this call is within the CreateBreakPointHook function
|
|
it changes %esp and thus the registers are
|
|
not restored correctly PM }
|
|
procedure get_pc_line;
|
|
begin
|
|
sym:=find_pc_line(b.address,0);
|
|
end;
|
|
begin
|
|
get_pc_line;
|
|
with curr_gdb^ do
|
|
begin
|
|
last_breakpoint_number:=b.number;
|
|
{ function breakpoints have zero as file and as line !!
|
|
but they are valid !! }
|
|
invalid_breakpoint_line:=(b.line_number<>sym.line) and (b.line_number<>0);
|
|
last_breakpoint_address:=b.address;
|
|
last_breakpoint_line:=sym.line;
|
|
if assigned(sym.symtab) then
|
|
last_breakpoint_file:=sym.symtab^.filename
|
|
else
|
|
last_breakpoint_file:=nil;
|
|
end;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
tgdbinterface
|
|
*****************************************************************************}
|
|
|
|
constructor tgdbinterface.init;
|
|
begin
|
|
gdboutputbuf.init;
|
|
gdberrorbuf.init;
|
|
record_frames:=true;
|
|
|
|
{ This must be placed before gdb__init is called
|
|
as gdb_init might issue output PM }
|
|
curr_gdb:=@self;
|
|
gdb__init;
|
|
command_level:=0;
|
|
{ set output mode for GDB }
|
|
{ only these values disable filtering
|
|
DONT CHANGE THEM !!! PM }
|
|
gdb_command('set width 0xffffffff');
|
|
gdb_command('set height 0xffffffff');
|
|
{ other standard commands used for fpc debugging }
|
|
gdb_command('set print demangle off');
|
|
gdb_command('set gnutarget auto');
|
|
gdb_command('set language auto');
|
|
gdb_command('set print vtbl on');
|
|
gdb_command('set print object on');
|
|
gdb_command('set print null-stop');
|
|
end;
|
|
|
|
|
|
destructor tgdbinterface.done;
|
|
begin
|
|
clear_frames;
|
|
gdb_done;
|
|
gdboutputbuf.done;
|
|
gdberrorbuf.done;
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.gdb__init;
|
|
begin
|
|
gdboutputbuf.reset;
|
|
gdberrorbuf.reset;
|
|
{$ifdef GDB_V6}
|
|
deprecated_create_breakpoint_hook:=@CreateBreakPointHook;
|
|
{$else}
|
|
create_breakpoint_hook:=@CreateBreakPointHook;
|
|
{$endif}
|
|
signal_string:=nil;
|
|
signal_name:=nil;
|
|
end;
|
|
|
|
|
|
|
|
procedure tgdbinterface.gdb_done;
|
|
begin
|
|
if debuggee_started then
|
|
begin
|
|
current_target.to_kill;
|
|
current_target.to_close(1);
|
|
end;
|
|
{$ifdef GDB_V6}
|
|
deprecated_create_breakpoint_hook:=nil;
|
|
{$else}
|
|
create_breakpoint_hook:=nil;
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
function tgdbinterface.error:boolean;
|
|
begin
|
|
error:=got_error;
|
|
end;
|
|
|
|
function tgdbinterface.error_num:longint;
|
|
begin
|
|
error_num:=gdb_error;
|
|
end;
|
|
|
|
var
|
|
top_level_val : longint;
|
|
|
|
function catch_errors(func : pointer; command : pchar; from_tty,mask : longint) : longint;cdecl;external;
|
|
|
|
function gdbint_execute_command(command : pchar; from_tty,mask : longint) : longint;cdecl;
|
|
begin
|
|
gdbint_execute_command:=1;
|
|
execute_command(command,from_tty);
|
|
gdbint_execute_command:=0;
|
|
end;
|
|
|
|
{$ifdef cpui386}
|
|
type
|
|
tfpustate = word;
|
|
|
|
const
|
|
MaskAllExceptions = $ff;
|
|
{$else}
|
|
type
|
|
tfpustate = longint;
|
|
const
|
|
MaskAllExceptions = 0;
|
|
{$endif}
|
|
|
|
procedure SaveFPUState(var control :TFPUState);
|
|
begin
|
|
{$ifdef cpui386}
|
|
asm
|
|
movl control, %edi
|
|
fstcw (%edi)
|
|
end;
|
|
{$else}
|
|
control:=0;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure SetFPUState(control : TFPUState);
|
|
begin
|
|
{$ifdef cpui386}
|
|
asm
|
|
fnclex
|
|
fldcw control
|
|
end;
|
|
{$else}
|
|
{$endif}
|
|
end;
|
|
|
|
function MaskAllFPUExceptions(control : TFPUState) : TFPUState;
|
|
begin
|
|
{$ifdef cpui386}
|
|
MaskAllFPUExceptions := control or MaskAllExceptions;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure tgdbinterface.gdb_command(const s:string);
|
|
var
|
|
command : array[0..256] of char;
|
|
mask : longint;
|
|
s2 : string;
|
|
old_quit_return,
|
|
old_error_return : jmp_buf;
|
|
control : TFPUState;
|
|
begin
|
|
inc(command_level);
|
|
SaveFPUState(control);
|
|
SetFPUState(MaskAllFPUExceptions(control));
|
|
move(s[1],command,length(s));
|
|
command[length(s)]:=#0;
|
|
old_quit_return:=quit_return;
|
|
old_error_return:=error_return;
|
|
gdb_error:=0;
|
|
got_error:=false;
|
|
stop_breakpoint_number:=0;
|
|
{ Trap quit commands }
|
|
s2:=s;
|
|
while (length(s2)>0) and ((s2[1]=' ') or (s2[1]=#9)) do
|
|
s2:=copy(s2,2,255);
|
|
if (length(s2)>0) and
|
|
(UpCase(s2[1])='Q') and
|
|
((length(s2)=1) or
|
|
(s2[2]=' ') or
|
|
((UpCase(s2[2])='U') and
|
|
((length(s2)=2) or
|
|
(s2[3]=' ') or
|
|
((UpCase(s2[3])='I') and
|
|
((length(s2)=3) or
|
|
(s2[4]=' ') or
|
|
((UpCase(s2[4])='T') and
|
|
((length(s2)=4) or
|
|
(s2[5]=' ')
|
|
))))))) then
|
|
begin
|
|
if not AllowQuit then
|
|
exit;
|
|
end;
|
|
{$ifdef DebugCommand}
|
|
Debug('start of handle_gdb_command ('+s+')');
|
|
{$endif}
|
|
top_level_val:=setjmp(error_return);
|
|
if top_level_val=0 then
|
|
begin
|
|
quit_return:=error_return;
|
|
mask:=longint($ffffffff);
|
|
catch_errors(@gdbint_execute_command,@command,0,mask);
|
|
{$ifdef go32v2}
|
|
reload_fs;
|
|
{$endif go32v2}
|
|
end
|
|
else
|
|
{$ifdef Verbose}
|
|
Debug('error longjmp in handle_gdb_command ('+s+')');
|
|
{$endif}
|
|
;
|
|
{$ifdef DebugCommand}
|
|
Debug('end of handle_gdb_command ('+s+')');
|
|
{$endif}
|
|
quit_return:=old_quit_return;
|
|
error_return:=old_error_return;
|
|
dec(command_level);
|
|
SetFPUState(control);
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.resize_frames;
|
|
var
|
|
i : longint;
|
|
new_frames : ppframeentry;
|
|
begin
|
|
if (frame_count>=frame_size) then
|
|
begin
|
|
getmem(new_frames,sizeof(pointer)*(frame_count+1));
|
|
for i:=0 to frame_size-1 do
|
|
new_frames[i]:=frames[i];
|
|
if assigned(frames) then
|
|
freemem(frames,sizeof(pointer)*frame_size);
|
|
frames:=new_frames;
|
|
frame_size:=frame_count+1;
|
|
for i:=frame_count to frame_size-1 do
|
|
frames[i]:=new(pframeentry,init);
|
|
end;
|
|
end;
|
|
|
|
|
|
function tgdbinterface.add_frameentry:pframeentry;
|
|
begin
|
|
resize_frames;
|
|
add_frameentry:=frames[frame_count];
|
|
inc(frame_count);
|
|
end;
|
|
|
|
function tgdbinterface.get_frameentry(level : longint) : pframeentry;
|
|
begin
|
|
{ only climb values one by one PM }
|
|
if level>=frame_count then
|
|
resize_frames;
|
|
get_frameentry:=frames[level];
|
|
frames[level]^.clear;
|
|
if level>=frame_count then
|
|
inc(frame_count);
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.clear_frames;
|
|
var
|
|
i : longint;
|
|
begin
|
|
for i:=0 to frame_size-1 do
|
|
dispose(frames[i],done);
|
|
freemem(frames,sizeof(pointer)*Frame_size);
|
|
frame_count:=0;
|
|
frame_size:=0;
|
|
end;
|
|
|
|
function tgdbinterface.get_current_frame : ptrint;
|
|
begin
|
|
record_frames:=false;
|
|
gdb_command('f');
|
|
get_current_frame:=frame_level;
|
|
record_frames:=true;
|
|
end;
|
|
|
|
function tgdbinterface.set_current_frame(level : longint) : boolean;
|
|
var
|
|
s : string;
|
|
begin
|
|
record_frames:=false;
|
|
str(level,s);
|
|
gdb_command('f '+s);
|
|
if level=frame_level then
|
|
set_current_frame:=true
|
|
else
|
|
set_current_frame:=false;
|
|
record_frames:=true;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
Highlevel tgdbinterface
|
|
*****************************************************************************}
|
|
|
|
procedure tgdbinterface.GetAddrSyminfo(addr:ptrint;var si:tsyminfo);
|
|
var
|
|
sym : symtab_and_line;
|
|
symbol : psymbol;
|
|
begin
|
|
sym:=find_pc_line(addr,1);
|
|
fillchar(si,sizeof(tsyminfo),0);
|
|
si.address:=addr;
|
|
si.offset:=addr-sym.pc;
|
|
if assigned(sym.symtab) then
|
|
si.fname:=sym.symtab^.filename
|
|
else
|
|
si.fname:=nil;
|
|
si.line:=sym.line;
|
|
symbol:=find_pc_function(addr);
|
|
if assigned(symbol) then
|
|
si.funcname:=symbol^.ginfo._name
|
|
else
|
|
si.funcname:=nil;
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.SelectSourceLine(fn:pchar;line:longint);
|
|
begin
|
|
if assigned(fn) then
|
|
DoSelectSourceLine(StrPas(fn),line)
|
|
else
|
|
DoSelectSourceLine('',line);
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.StartSession;
|
|
begin
|
|
DoStartSession;
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.BreakSession;
|
|
begin
|
|
DoBreakSession;
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.EndSession(code:longint);
|
|
begin
|
|
Debuggee_started:=false;
|
|
inferior_ptid.pid:=0;
|
|
DoEndSession(code);
|
|
if assigned(signal_name) then
|
|
strdispose(signal_name);
|
|
signal_name:=nil;
|
|
if assigned(signal_string) then
|
|
strdispose(signal_string);
|
|
signal_string:=nil;
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.DebuggerScreen;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|DebuggerScreen|');
|
|
{$endif}
|
|
if user_screen_shown then
|
|
DoDebuggerScreen;
|
|
user_screen_shown:=false;
|
|
end;
|
|
|
|
|
|
procedure tgdbinterface.UserScreen;
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|UserScreen|');
|
|
{$endif}
|
|
if switch_to_user then
|
|
begin
|
|
if (not user_screen_shown) then
|
|
DoUserScreen;
|
|
user_screen_shown:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
{---------------------------------------
|
|
Default Hooks
|
|
---------------------------------------}
|
|
|
|
procedure tgdbinterface.DoSelectSourceLine(const fn:string;line:longint);
|
|
{$ifdef Verbose}
|
|
var
|
|
s : string;
|
|
{$endif}
|
|
begin
|
|
{$ifdef Verbose}
|
|
Str(line,S);
|
|
Debug('|SelectSource '+fn+':'+s+'|');
|
|
{$endif}
|
|
end;
|
|
|
|
procedure tgdbinterface.DoStartSession;
|
|
begin
|
|
end;
|
|
|
|
procedure tgdbinterface.DoBreakSession;
|
|
begin
|
|
end;
|
|
|
|
procedure tgdbinterface.DoEndSession(code:longint);
|
|
begin
|
|
end;
|
|
|
|
procedure tgdbinterface.DoUserSignal;
|
|
begin
|
|
end;
|
|
|
|
procedure tgdbinterface.DoDebuggerScreen;
|
|
begin
|
|
end;
|
|
|
|
procedure tgdbinterface.DoUserScreen;
|
|
begin
|
|
end;
|
|
|
|
function tgdbinterface.AllowQuit : boolean;
|
|
begin
|
|
AllowQuit:=true;
|
|
end;
|
|
|
|
var
|
|
version : array[0..0] of char;cvar;external;
|
|
|
|
procedure error_init;cdecl;external;
|
|
|
|
|
|
function GDBVersion : string;
|
|
begin
|
|
GDBVersion:='GDB '+StrPas(version);
|
|
end;
|
|
|
|
|
|
const next_exit : pointer = nil;
|
|
procedure DoneLibGDB;
|
|
begin
|
|
exitproc:=next_exit;
|
|
end;
|
|
|
|
{$ifdef go32v2}
|
|
var
|
|
c_environ : ppchar;external name '_environ';
|
|
c_argc : longint;external name '___crt0_argc';
|
|
c_argv : ppchar;external name '___crt0_argv';
|
|
{$endif def go32v2}
|
|
|
|
procedure InitLibGDB;
|
|
{$ifdef supportexceptions}
|
|
var
|
|
OldSigInt : SignalHandler;
|
|
{$endif supportexceptions}
|
|
begin
|
|
{$ifdef go32v2}
|
|
c_environ:=system.envp;
|
|
c_argc:=system.argc;
|
|
c_argv:=system.argv;
|
|
{$endif def go32v2}
|
|
{$ifdef supportexceptions}
|
|
{$ifdef go32v2}
|
|
OldSigInt:=Signal(SIGINT,SignalHandler(@SIG_DFL));
|
|
{$else}
|
|
{$ifdef Unix}
|
|
OldSigInt:=fpSignal(SIGINT,SignalHandler(SIG_DFL));
|
|
{$else}
|
|
OldSigInt:=Signal(SIGINT,SignalHandler(SIG_DFL));
|
|
{$endif}
|
|
{$endif}
|
|
{$endif supportexceptions}
|
|
|
|
if assigned(gdb_stderr) then
|
|
ui_file_delete(gdb_stderr);
|
|
if assigned(gdb_stdout) then
|
|
ui_file_delete(gdb_stdout);
|
|
gdb_stderr:=mem_fileopen;
|
|
gdb_stdout:=mem_fileopen;
|
|
gdb_stdlog:=gdb_stderr;
|
|
gdb_stdtarg:=gdb_stderr;
|
|
set_ui_file_write(gdb_stdout,@gdbint_ui_file_write);
|
|
set_ui_file_write(gdb_stderr,@gdbint_ui_file_write);
|
|
error_init;
|
|
{$ifdef GDB_V6}
|
|
// gdb_stdtargin := gdb_stdin;
|
|
gdb_stdtargerr := gdb_stderr;
|
|
{$endif}
|
|
|
|
next_exit:=exitproc;
|
|
exitproc:=@DoneLibGDB;
|
|
{$ifdef GDB_V6}
|
|
uiout := cli_out_new (gdb_stdout);
|
|
{$endif}
|
|
gdb_init;
|
|
{$ifdef supportexceptions}
|
|
{$ifdef unix}
|
|
fpsignal(SIGINT,OldSigInt);
|
|
{$else}
|
|
Signal(SIGINT,OldSigInt);
|
|
{$endif}
|
|
{$endif supportexceptions}
|
|
if setjmp(error_return)=0 then
|
|
begin
|
|
quit_return:=error_return;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
{$ifdef Verbose}
|
|
Debug('|LongJump to Init|');
|
|
{$endif}
|
|
{$ifdef go32v2}
|
|
RunError(99);
|
|
{$endif def go32v2}
|
|
end;
|
|
WatchDog:=0;
|
|
end;
|
|
|
|
{$ifdef GDB_HAS_SYSROOT}
|
|
var gdb_sysroot : pchar; export name 'gdb_sysroot';
|
|
gdb_sysrootc : char;
|
|
{$endif}
|
|
|
|
begin
|
|
{$ifdef GDB_HAS_SYSROOT}
|
|
gdb_sysrootc := #0;
|
|
gdb_sysroot := @gdb_sysrootc;
|
|
{$endif}
|
|
InitLibGDB;
|
|
end.
|