gdb/
* infrun.c (handle_inferior_event): Move the check for return trampolines ahead of the check for function trampolines. * mips-tdep.h (MIPS_S2_REGNUM, MIPS_GP_REGNUM): New macros. * mips-tdep.c (mips_str_mips16_call_stub): New variable. (mips_str_mips16_ret_stub): Likewise. (mips_str_call_fp_stub): Likewise. (mips_str_call_stub): Likewise. (mips_str_fn_stub): Likewise. (mips_str_pic): Likewise. (mips_in_frame_stub): New function. (mips_unwind_pc): Return the return address rather than the PC if the PC of an intermediate frame is inside a call thunk. (mips_is_stub_suffix): New function. (mips_is_stub_mode): Likewise. (mips_get_mips16_fn_stub_pc): Likewise. (mips_skip_mips16_trampoline_code): Update to handle all the currently generated stub types. Don't recurse into __fn_stub thunks. Remove heuristics to handle stubs beyond etext/_etext. Use cooked register accesses. (mips_in_return_stub): Reintroduce function. (mips_skip_trampoline_code): Traverse trampolines recursively. (mips_gdbarch_init): Handle MIPS16 return trampolines. gdb/testsuite/ * gdb.arch/mips16-thunks-inmain.c: New file. * gdb.arch/mips16-thunks-main.c: New file. * gdb.arch/mips16-thunks-sin.c: New file. * gdb.arch/mips16-thunks-sinfrob.c: New file. * gdb.arch/mips16-thunks-sinfrob16.c: New file. * gdb.arch/mips16-thunks-sinmain.c: New file. * gdb.arch/mips16-thunks-sinmips16.c: New file. * gdb.arch/mips16-thunks.exp: New file.
This commit is contained in:
parent
518f0db5cf
commit
14132e8924
|
@ -1,3 +1,29 @@
|
|||
2012-04-26 Maciej W. Rozycki <macro@mips.com>
|
||||
Maciej W. Rozycki <macro@codesourcery.com>
|
||||
|
||||
* infrun.c (handle_inferior_event): Move the check for return
|
||||
trampolines ahead of the check for function trampolines.
|
||||
* mips-tdep.h (MIPS_S2_REGNUM, MIPS_GP_REGNUM): New macros.
|
||||
* mips-tdep.c (mips_str_mips16_call_stub): New variable.
|
||||
(mips_str_mips16_ret_stub): Likewise.
|
||||
(mips_str_call_fp_stub): Likewise.
|
||||
(mips_str_call_stub): Likewise.
|
||||
(mips_str_fn_stub): Likewise.
|
||||
(mips_str_pic): Likewise.
|
||||
(mips_in_frame_stub): New function.
|
||||
(mips_unwind_pc): Return the return address rather than the PC
|
||||
if the PC of an intermediate frame is inside a call thunk.
|
||||
(mips_is_stub_suffix): New function.
|
||||
(mips_is_stub_mode): Likewise.
|
||||
(mips_get_mips16_fn_stub_pc): Likewise.
|
||||
(mips_skip_mips16_trampoline_code): Update to handle all the
|
||||
currently generated stub types. Don't recurse into __fn_stub
|
||||
thunks. Remove heuristics to handle stubs beyond etext/_etext.
|
||||
Use cooked register accesses.
|
||||
(mips_in_return_stub): Reintroduce function.
|
||||
(mips_skip_trampoline_code): Traverse trampolines recursively.
|
||||
(mips_gdbarch_init): Handle MIPS16 return trampolines.
|
||||
|
||||
2012-04-26 Joel Brobecker <brobecker@adacore.com>
|
||||
|
||||
GDB 7.4.1 released.
|
||||
|
|
81
gdb/infrun.c
81
gdb/infrun.c
|
@ -4814,6 +4814,48 @@ process_event_stop_test:
|
|||
return;
|
||||
}
|
||||
|
||||
/* If we're in the return path from a shared library trampoline,
|
||||
we want to proceed through the trampoline when stepping. */
|
||||
/* macro/2012-04-25: This needs to come before the subroutine
|
||||
call check below as on some targets return trampolines look
|
||||
like subroutine calls (MIPS16 return thunks). */
|
||||
if (gdbarch_in_solib_return_trampoline (gdbarch,
|
||||
stop_pc, ecs->stop_func_name)
|
||||
&& ecs->event_thread->control.step_over_calls != STEP_OVER_NONE)
|
||||
{
|
||||
/* Determine where this trampoline returns. */
|
||||
CORE_ADDR real_stop_pc;
|
||||
|
||||
real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: stepped into solib return tramp\n");
|
||||
|
||||
/* Only proceed through if we know where it's going. */
|
||||
if (real_stop_pc)
|
||||
{
|
||||
/* And put the step-breakpoint there and go until there. */
|
||||
struct symtab_and_line sr_sal;
|
||||
|
||||
init_sal (&sr_sal); /* initialize to zeroes */
|
||||
sr_sal.pc = real_stop_pc;
|
||||
sr_sal.section = find_pc_overlay (sr_sal.pc);
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
|
||||
/* Do not specify what the fp should be when we stop since
|
||||
on some machines the prologue is where the new fp value
|
||||
is established. */
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
|
||||
/* Restart without fiddling with the step ranges or
|
||||
other state. */
|
||||
keep_going (ecs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for subroutine calls. The check for the current frame
|
||||
equalling the step ID is not necessary - the check of the
|
||||
previous frame's ID is sufficient - but it is a common case and
|
||||
|
@ -5024,45 +5066,6 @@ process_event_stop_test:
|
|||
}
|
||||
}
|
||||
|
||||
/* If we're in the return path from a shared library trampoline,
|
||||
we want to proceed through the trampoline when stepping. */
|
||||
if (gdbarch_in_solib_return_trampoline (gdbarch,
|
||||
stop_pc, ecs->stop_func_name)
|
||||
&& ecs->event_thread->control.step_over_calls != STEP_OVER_NONE)
|
||||
{
|
||||
/* Determine where this trampoline returns. */
|
||||
CORE_ADDR real_stop_pc;
|
||||
|
||||
real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: stepped into solib return tramp\n");
|
||||
|
||||
/* Only proceed through if we know where it's going. */
|
||||
if (real_stop_pc)
|
||||
{
|
||||
/* And put the step-breakpoint there and go until there. */
|
||||
struct symtab_and_line sr_sal;
|
||||
|
||||
init_sal (&sr_sal); /* initialize to zeroes */
|
||||
sr_sal.pc = real_stop_pc;
|
||||
sr_sal.section = find_pc_overlay (sr_sal.pc);
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
|
||||
/* Do not specify what the fp should be when we stop since
|
||||
on some machines the prologue is where the new fp value
|
||||
is established. */
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
|
||||
/* Restart without fiddling with the step ranges or
|
||||
other state. */
|
||||
keep_going (ecs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stop_pc_sal = find_pc_line (stop_pc, 0);
|
||||
|
||||
/* NOTE: tausq/2004-05-24: This if block used to be done before all
|
||||
|
|
479
gdb/mips-tdep.c
479
gdb/mips-tdep.c
|
@ -1035,6 +1035,45 @@ mips_pc_is_mips16 (CORE_ADDR memaddr)
|
|||
return is_mips16_addr (memaddr);
|
||||
}
|
||||
|
||||
/* Various MIPS16 thunk (aka stub or trampoline) names. */
|
||||
|
||||
static const char mips_str_mips16_call_stub[] = "__mips16_call_stub_";
|
||||
static const char mips_str_mips16_ret_stub[] = "__mips16_ret_";
|
||||
static const char mips_str_call_fp_stub[] = "__call_stub_fp_";
|
||||
static const char mips_str_call_stub[] = "__call_stub_";
|
||||
static const char mips_str_fn_stub[] = "__fn_stub_";
|
||||
|
||||
/* This is used as a PIC thunk prefix. */
|
||||
|
||||
static const char mips_str_pic[] = ".pic.";
|
||||
|
||||
/* Return non-zero if the PC is inside a call thunk (aka stub or
|
||||
trampoline) that should be treated as a temporary frame. */
|
||||
|
||||
static int
|
||||
mips_in_frame_stub (CORE_ADDR pc)
|
||||
{
|
||||
CORE_ADDR start_addr;
|
||||
const char *name;
|
||||
|
||||
/* Find the starting address of the function containing the PC. */
|
||||
if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0)
|
||||
return 0;
|
||||
|
||||
/* If the PC is in __mips16_call_stub_*, this is a call/return stub. */
|
||||
if (strncmp (name, mips_str_mips16_call_stub,
|
||||
strlen (mips_str_mips16_call_stub)) == 0)
|
||||
return 1;
|
||||
/* If the PC is in __call_stub_*, this is a call/return or a call stub. */
|
||||
if (strncmp (name, mips_str_call_stub, strlen (mips_str_call_stub)) == 0)
|
||||
return 1;
|
||||
/* If the PC is in __fn_stub_*, this is a call stub. */
|
||||
if (strncmp (name, mips_str_fn_stub, strlen (mips_str_fn_stub)) == 0)
|
||||
return 1;
|
||||
|
||||
return 0; /* Not a stub. */
|
||||
}
|
||||
|
||||
/* MIPS believes that the PC has a sign extended value. Perhaps the
|
||||
all registers should be sign extended for simplicity? */
|
||||
|
||||
|
@ -1052,12 +1091,31 @@ mips_read_pc (struct regcache *regcache)
|
|||
static CORE_ADDR
|
||||
mips_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
|
||||
{
|
||||
ULONGEST pc;
|
||||
CORE_ADDR pc;
|
||||
|
||||
pc = frame_unwind_register_signed
|
||||
(next_frame, gdbarch_num_regs (gdbarch) + mips_regnum (gdbarch)->pc);
|
||||
if (is_mips16_addr (pc))
|
||||
pc = unmake_mips16_addr (pc);
|
||||
/* macro/2012-04-20: This hack skips over MIPS16 call thunks as
|
||||
intermediate frames. In this case we can get the caller's address
|
||||
from $ra, or if $ra contains an address within a thunk as well, then
|
||||
it must be in the return path of __mips16_call_stub_{s,d}{f,c}_{0..10}
|
||||
and thus the caller's address is in $s2. */
|
||||
if (frame_relative_level (next_frame) >= 0 && mips_in_frame_stub (pc))
|
||||
{
|
||||
pc = frame_unwind_register_signed
|
||||
(next_frame, gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM);
|
||||
if (is_mips16_addr (pc))
|
||||
pc = unmake_mips16_addr (pc);
|
||||
if (mips_in_frame_stub (pc))
|
||||
{
|
||||
pc = frame_unwind_register_signed
|
||||
(next_frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM);
|
||||
if (is_mips16_addr (pc))
|
||||
pc = unmake_mips16_addr (pc);
|
||||
}
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
|
@ -5624,104 +5682,335 @@ mips_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
|
|||
return bpaddr;
|
||||
}
|
||||
|
||||
/* If PC is in a mips16 call or return stub, return the address of the target
|
||||
PC, which is either the callee or the caller. There are several
|
||||
/* Return non-zero if SUFFIX is one of the numeric suffixes used for MIPS16
|
||||
call stubs, one of 1, 2, 5, 6, 9, 10, or, if ZERO is non-zero, also 0. */
|
||||
|
||||
static int
|
||||
mips_is_stub_suffix (const char *suffix, int zero)
|
||||
{
|
||||
switch (suffix[0])
|
||||
{
|
||||
case '0':
|
||||
return zero && suffix[1] == '\0';
|
||||
case '1':
|
||||
return suffix[1] == '\0' || (suffix[1] == '0' && suffix[2] == '\0');
|
||||
case '2':
|
||||
case '5':
|
||||
case '6':
|
||||
case '9':
|
||||
return suffix[1] == '\0';
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return non-zero if MODE is one of the mode infixes used for MIPS16
|
||||
call stubs, one of sf, df, sc, or dc. */
|
||||
|
||||
static int
|
||||
mips_is_stub_mode (const char *mode)
|
||||
{
|
||||
return ((mode[0] == 's' || mode[0] == 'd')
|
||||
&& (mode[1] == 'f' || mode[1] == 'c'));
|
||||
}
|
||||
|
||||
/* Code at PC is a compiler-generated stub. Such a stub for a function
|
||||
bar might have a name like __fn_stub_bar, and might look like this:
|
||||
|
||||
mfc1 $4, $f13
|
||||
mfc1 $5, $f12
|
||||
mfc1 $6, $f15
|
||||
mfc1 $7, $f14
|
||||
|
||||
followed by (or interspersed with):
|
||||
|
||||
j bar
|
||||
|
||||
or:
|
||||
|
||||
lui $25, %hi(bar)
|
||||
addiu $25, $25, %lo(bar)
|
||||
jr $25
|
||||
|
||||
($1 may be used in old code; for robustness we accept any register)
|
||||
or, in PIC code:
|
||||
|
||||
lui $28, %hi(_gp_disp)
|
||||
addiu $28, $28, %lo(_gp_disp)
|
||||
addu $28, $28, $25
|
||||
lw $25, %got(bar)
|
||||
addiu $25, $25, %lo(bar)
|
||||
jr $25
|
||||
|
||||
In the case of a __call_stub_bar stub, the sequence to set up
|
||||
arguments might look like this:
|
||||
|
||||
mtc1 $4, $f13
|
||||
mtc1 $5, $f12
|
||||
mtc1 $6, $f15
|
||||
mtc1 $7, $f14
|
||||
|
||||
followed by (or interspersed with) one of the jump sequences above.
|
||||
|
||||
In the case of a __call_stub_fp_bar stub, JAL or JALR is used instead
|
||||
of J or JR, respectively, followed by:
|
||||
|
||||
mfc1 $2, $f0
|
||||
mfc1 $3, $f1
|
||||
jr $18
|
||||
|
||||
We are at the beginning of the stub here, and scan down and extract
|
||||
the target address from the jump immediate instruction or, if a jump
|
||||
register instruction is used, from the register referred. Return
|
||||
the value of PC calculated or 0 if inconclusive.
|
||||
|
||||
The limit on the search is arbitrarily set to 20 instructions. FIXME. */
|
||||
|
||||
static CORE_ADDR
|
||||
mips_get_mips16_fn_stub_pc (struct frame_info *frame, CORE_ADDR pc)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
int addrreg = MIPS_ZERO_REGNUM;
|
||||
CORE_ADDR start_pc = pc;
|
||||
CORE_ADDR target_pc = 0;
|
||||
CORE_ADDR addr = 0;
|
||||
CORE_ADDR gp = 0;
|
||||
int status = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0;
|
||||
status == 0 && target_pc == 0 && i < 20;
|
||||
i++, pc += MIPS_INSN32_SIZE)
|
||||
{
|
||||
ULONGEST inst = mips_fetch_instruction (gdbarch, pc);
|
||||
CORE_ADDR imm;
|
||||
int rt;
|
||||
int rs;
|
||||
int rd;
|
||||
|
||||
switch (itype_op (inst))
|
||||
{
|
||||
case 0: /* SPECIAL */
|
||||
switch (rtype_funct (inst))
|
||||
{
|
||||
case 8: /* JR */
|
||||
case 9: /* JALR */
|
||||
rs = rtype_rs (inst);
|
||||
if (rs == MIPS_GP_REGNUM)
|
||||
target_pc = gp; /* Hmm... */
|
||||
else if (rs == addrreg)
|
||||
target_pc = addr;
|
||||
break;
|
||||
|
||||
case 0x21: /* ADDU */
|
||||
rt = rtype_rt (inst);
|
||||
rs = rtype_rs (inst);
|
||||
rd = rtype_rd (inst);
|
||||
if (rd == MIPS_GP_REGNUM
|
||||
&& ((rs == MIPS_GP_REGNUM && rt == MIPS_T9_REGNUM)
|
||||
|| (rs == MIPS_T9_REGNUM && rt == MIPS_GP_REGNUM)))
|
||||
gp += start_pc;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: /* J */
|
||||
case 3: /* JAL */
|
||||
target_pc = jtype_target (inst) << 2;
|
||||
target_pc += ((pc + 4) & ~(CORE_ADDR) 0x0fffffff);
|
||||
break;
|
||||
|
||||
case 9: /* ADDIU */
|
||||
rt = itype_rt (inst);
|
||||
rs = itype_rs (inst);
|
||||
if (rt == rs)
|
||||
{
|
||||
imm = (itype_immediate (inst) ^ 0x8000) - 0x8000;
|
||||
if (rt == MIPS_GP_REGNUM)
|
||||
gp += imm;
|
||||
else if (rt == addrreg)
|
||||
addr += imm;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xf: /* LUI */
|
||||
rt = itype_rt (inst);
|
||||
imm = ((itype_immediate (inst) ^ 0x8000) - 0x8000) << 16;
|
||||
if (rt == MIPS_GP_REGNUM)
|
||||
gp = imm;
|
||||
else if (rt != MIPS_ZERO_REGNUM)
|
||||
{
|
||||
addrreg = rt;
|
||||
addr = imm;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x23: /* LW */
|
||||
rt = itype_rt (inst);
|
||||
rs = itype_rs (inst);
|
||||
imm = (itype_immediate (inst) ^ 0x8000) - 0x8000;
|
||||
if (gp != 0 && rs == MIPS_GP_REGNUM)
|
||||
{
|
||||
gdb_byte buf[4];
|
||||
|
||||
memset (buf, 0, sizeof (buf));
|
||||
status = target_read_memory (gp + imm, buf, sizeof (buf));
|
||||
addrreg = rt;
|
||||
addr = extract_signed_integer (buf, sizeof (buf), byte_order);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return target_pc;
|
||||
}
|
||||
|
||||
/* If PC is in a MIPS16 call or return stub, return the address of the
|
||||
target PC, which is either the callee or the caller. There are several
|
||||
cases which must be handled:
|
||||
|
||||
* If the PC is in __mips16_ret_{d,s}f, this is a return stub and the
|
||||
target PC is in $31 ($ra).
|
||||
* If the PC is in __mips16_ret_{d,s}{f,c}, this is a return stub
|
||||
and the target PC is in $31 ($ra).
|
||||
* If the PC is in __mips16_call_stub_{1..10}, this is a call stub
|
||||
and the target PC is in $2.
|
||||
* If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
|
||||
before the jal instruction, this is effectively a call stub
|
||||
and the target PC is in $2. Otherwise this is effectively
|
||||
a return stub and the target PC is in $18.
|
||||
and the target PC is in $2.
|
||||
* If the PC at the start of __mips16_call_stub_{s,d}{f,c}_{0..10},
|
||||
i.e. before the JALR instruction, this is effectively a call stub
|
||||
and the target PC is in $2. Otherwise this is effectively
|
||||
a return stub and the target PC is in $18.
|
||||
* If the PC is at the start of __call_stub_fp_*, i.e. before the
|
||||
JAL or JALR instruction, this is effectively a call stub and the
|
||||
target PC is buried in the instruction stream. Otherwise this
|
||||
is effectively a return stub and the target PC is in $18.
|
||||
* If the PC is in __call_stub_* or in __fn_stub_*, this is a call
|
||||
stub and the target PC is buried in the instruction stream.
|
||||
|
||||
See the source code for the stubs in gcc/config/mips/mips16.S for
|
||||
See the source code for the stubs in gcc/config/mips/mips16.S, or the
|
||||
stub builder in gcc/config/mips/mips.c (mips16_build_call_stub) for the
|
||||
gory details. */
|
||||
|
||||
static CORE_ADDR
|
||||
mips_skip_mips16_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
const char *name;
|
||||
CORE_ADDR start_addr;
|
||||
const char *name;
|
||||
size_t prefixlen;
|
||||
|
||||
/* Find the starting address and name of the function containing the PC. */
|
||||
if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0)
|
||||
return 0;
|
||||
|
||||
/* If the PC is in __mips16_ret_{d,s}f, this is a return stub and the
|
||||
target PC is in $31 ($ra). */
|
||||
if (strcmp (name, "__mips16_ret_sf") == 0
|
||||
|| strcmp (name, "__mips16_ret_df") == 0)
|
||||
return get_frame_register_signed (frame, MIPS_RA_REGNUM);
|
||||
/* If the PC is in __mips16_ret_{d,s}{f,c}, this is a return stub
|
||||
and the target PC is in $31 ($ra). */
|
||||
prefixlen = strlen (mips_str_mips16_ret_stub);
|
||||
if (strncmp (name, mips_str_mips16_ret_stub, prefixlen) == 0
|
||||
&& mips_is_stub_mode (name + prefixlen)
|
||||
&& name[prefixlen + 2] == '\0')
|
||||
return get_frame_register_signed
|
||||
(frame, gdbarch_num_regs (gdbarch) + MIPS_RA_REGNUM);
|
||||
|
||||
if (strncmp (name, "__mips16_call_stub_", 19) == 0)
|
||||
/* If the PC is in __mips16_call_stub_*, this is one of the call
|
||||
call/return stubs. */
|
||||
prefixlen = strlen (mips_str_mips16_call_stub);
|
||||
if (strncmp (name, mips_str_mips16_call_stub, prefixlen) == 0)
|
||||
{
|
||||
/* If the PC is in __mips16_call_stub_{1..10}, this is a call stub
|
||||
and the target PC is in $2. */
|
||||
if (name[19] >= '0' && name[19] <= '9')
|
||||
return get_frame_register_signed (frame, 2);
|
||||
if (mips_is_stub_suffix (name + prefixlen, 0))
|
||||
return get_frame_register_signed
|
||||
(frame, gdbarch_num_regs (gdbarch) + MIPS_V0_REGNUM);
|
||||
|
||||
/* If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
|
||||
before the jal instruction, this is effectively a call stub
|
||||
/* If the PC at the start of __mips16_call_stub_{s,d}{f,c}_{0..10},
|
||||
i.e. before the JALR instruction, this is effectively a call stub
|
||||
and the target PC is in $2. Otherwise this is effectively
|
||||
a return stub and the target PC is in $18. */
|
||||
else if (name[19] == 's' || name[19] == 'd')
|
||||
else if (mips_is_stub_mode (name + prefixlen)
|
||||
&& name[prefixlen + 2] == '_'
|
||||
&& mips_is_stub_suffix (name + prefixlen + 3, 0))
|
||||
{
|
||||
if (pc == start_addr)
|
||||
{
|
||||
/* Check if the target of the stub is a compiler-generated
|
||||
stub. Such a stub for a function bar might have a name
|
||||
like __fn_stub_bar, and might look like this:
|
||||
mfc1 $4,$f13
|
||||
mfc1 $5,$f12
|
||||
mfc1 $6,$f15
|
||||
mfc1 $7,$f14
|
||||
la $1,bar (becomes a lui/addiu pair)
|
||||
jr $1
|
||||
So scan down to the lui/addi and extract the target
|
||||
address from those two instructions. */
|
||||
|
||||
CORE_ADDR target_pc = get_frame_register_signed (frame, 2);
|
||||
int i;
|
||||
|
||||
/* See if the name of the target function is __fn_stub_*. */
|
||||
if (find_pc_partial_function (target_pc, &name, NULL, NULL) ==
|
||||
0)
|
||||
return target_pc;
|
||||
if (strncmp (name, "__fn_stub_", 10) != 0
|
||||
&& strcmp (name, "etext") != 0
|
||||
&& strcmp (name, "_etext") != 0)
|
||||
return target_pc;
|
||||
|
||||
/* Scan through this _fn_stub_ code for the lui/addiu pair.
|
||||
The limit on the search is arbitrarily set to 20
|
||||
instructions. FIXME. */
|
||||
for (i = 0, pc = 0; i < 20; i++, target_pc += MIPS_INSN32_SIZE)
|
||||
{
|
||||
ULONGEST inst = mips_fetch_instruction (gdbarch, target_pc);
|
||||
CORE_ADDR addr = inst;
|
||||
|
||||
if ((inst & 0xffff0000) == 0x3c010000) /* lui $at */
|
||||
pc = (((addr & 0xffff) ^ 0x8000) - 0x8000) << 16;
|
||||
/* high word */
|
||||
else if ((inst & 0xffff0000) == 0x24210000) /* addiu $at */
|
||||
return pc + ((addr & 0xffff) ^ 0x8000) - 0x8000;
|
||||
/* low word */
|
||||
}
|
||||
|
||||
/* Couldn't find the lui/addui pair, so return stub address. */
|
||||
return target_pc;
|
||||
}
|
||||
/* This is the 'call' part of a call stub. The return
|
||||
address is in $2. */
|
||||
return get_frame_register_signed
|
||||
(frame, gdbarch_num_regs (gdbarch) + MIPS_V0_REGNUM);
|
||||
else
|
||||
/* This is the 'return' part of a call stub. The return
|
||||
address is in $r18. */
|
||||
return get_frame_register_signed (frame, 18);
|
||||
address is in $18. */
|
||||
return get_frame_register_signed
|
||||
(frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM);
|
||||
}
|
||||
else
|
||||
return 0; /* Not a stub. */
|
||||
}
|
||||
return 0; /* not a stub */
|
||||
|
||||
/* If the PC is in __call_stub_* or __fn_stub*, this is one of the
|
||||
compiler-generated call or call/return stubs. */
|
||||
if (strncmp (name, mips_str_fn_stub, strlen (mips_str_fn_stub)) == 0
|
||||
|| strncmp (name, mips_str_call_stub, strlen (mips_str_call_stub)) == 0)
|
||||
{
|
||||
if (pc == start_addr)
|
||||
/* This is the 'call' part of a call stub. Call this helper
|
||||
to scan through this code for interesting instructions
|
||||
and determine the final PC. */
|
||||
return mips_get_mips16_fn_stub_pc (frame, pc);
|
||||
else
|
||||
/* This is the 'return' part of a call stub. The return address
|
||||
is in $18. */
|
||||
return get_frame_register_signed
|
||||
(frame, gdbarch_num_regs (gdbarch) + MIPS_S2_REGNUM);
|
||||
}
|
||||
|
||||
return 0; /* Not a stub. */
|
||||
}
|
||||
|
||||
/* Return non-zero if the PC is inside a return thunk (aka stub or trampoline).
|
||||
This implements the IN_SOLIB_RETURN_TRAMPOLINE macro. */
|
||||
|
||||
static int
|
||||
mips_in_return_stub (struct gdbarch *gdbarch, CORE_ADDR pc, const char *name)
|
||||
{
|
||||
CORE_ADDR start_addr;
|
||||
size_t prefixlen;
|
||||
|
||||
/* Find the starting address of the function containing the PC. */
|
||||
if (find_pc_partial_function (pc, NULL, &start_addr, NULL) == 0)
|
||||
return 0;
|
||||
|
||||
/* If the PC is in __mips16_call_stub_{s,d}{f,c}_{0..10} but not at
|
||||
the start, i.e. after the JALR instruction, this is effectively
|
||||
a return stub. */
|
||||
prefixlen = strlen (mips_str_mips16_call_stub);
|
||||
if (pc != start_addr
|
||||
&& strncmp (name, mips_str_mips16_call_stub, prefixlen) == 0
|
||||
&& mips_is_stub_mode (name + prefixlen)
|
||||
&& name[prefixlen + 2] == '_'
|
||||
&& mips_is_stub_suffix (name + prefixlen + 3, 1))
|
||||
return 1;
|
||||
|
||||
/* If the PC is in __call_stub_fp_* but not at the start, i.e. after
|
||||
the JAL or JALR instruction, this is effectively a return stub. */
|
||||
prefixlen = strlen (mips_str_call_fp_stub);
|
||||
if (pc != start_addr
|
||||
&& strncmp (name, mips_str_call_fp_stub, prefixlen) == 0)
|
||||
return 1;
|
||||
|
||||
/* Consume the .pic. prefix of any PIC stub, this function must return
|
||||
true when the PC is in a PIC stub of a __mips16_ret_{d,s}{f,c} stub
|
||||
or the call stub path will trigger in handle_inferior_event causing
|
||||
it to go astray. */
|
||||
prefixlen = strlen (mips_str_pic);
|
||||
if (strncmp (name, mips_str_pic, prefixlen) == 0)
|
||||
name += prefixlen;
|
||||
|
||||
/* If the PC is in __mips16_ret_{d,s}{f,c}, this is a return stub. */
|
||||
prefixlen = strlen (mips_str_mips16_ret_stub);
|
||||
if (strncmp (name, mips_str_mips16_ret_stub, prefixlen) == 0
|
||||
&& mips_is_stub_mode (name + prefixlen)
|
||||
&& name[prefixlen + 2] == '\0')
|
||||
return 1;
|
||||
|
||||
return 0; /* Not a stub. */
|
||||
}
|
||||
|
||||
/* If the current PC is the start of a non-PIC-to-PIC stub, return the
|
||||
|
@ -5784,21 +6073,41 @@ mips_skip_pic_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
|
|||
static CORE_ADDR
|
||||
mips_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
|
||||
{
|
||||
CORE_ADDR requested_pc = pc;
|
||||
CORE_ADDR target_pc;
|
||||
CORE_ADDR new_pc;
|
||||
|
||||
target_pc = mips_skip_mips16_trampoline_code (frame, pc);
|
||||
if (target_pc)
|
||||
return target_pc;
|
||||
do
|
||||
{
|
||||
target_pc = pc;
|
||||
|
||||
target_pc = find_solib_trampoline_target (frame, pc);
|
||||
if (target_pc)
|
||||
return target_pc;
|
||||
new_pc = mips_skip_mips16_trampoline_code (frame, pc);
|
||||
if (new_pc)
|
||||
{
|
||||
pc = new_pc;
|
||||
if (is_mips16_addr (pc))
|
||||
pc = unmake_mips16_addr (pc);
|
||||
}
|
||||
|
||||
target_pc = mips_skip_pic_trampoline_code (frame, pc);
|
||||
if (target_pc)
|
||||
return target_pc;
|
||||
new_pc = find_solib_trampoline_target (frame, pc);
|
||||
if (new_pc)
|
||||
{
|
||||
pc = new_pc;
|
||||
if (is_mips16_addr (pc))
|
||||
pc = unmake_mips16_addr (pc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
new_pc = mips_skip_pic_trampoline_code (frame, pc);
|
||||
if (new_pc)
|
||||
{
|
||||
pc = new_pc;
|
||||
if (is_mips16_addr (pc))
|
||||
pc = unmake_mips16_addr (pc);
|
||||
}
|
||||
}
|
||||
while (pc != target_pc);
|
||||
|
||||
return pc != requested_pc ? pc : 0;
|
||||
}
|
||||
|
||||
/* Convert a dbx stab register number (from `r' declaration) to a GDB
|
||||
|
@ -6641,6 +6950,16 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
|||
|
||||
set_gdbarch_skip_trampoline_code (gdbarch, mips_skip_trampoline_code);
|
||||
|
||||
/* NOTE drow/2012-04-25: We overload the core solib trampoline code
|
||||
to support MIPS16. This is a bad thing. Make sure not to do it
|
||||
if we have an OS ABI that actually supports shared libraries, since
|
||||
shared library support is more important. If we have an OS someday
|
||||
that supports both shared libraries and MIPS16, we'll have to find
|
||||
a better place for these.
|
||||
macro/2012-04-25: But that applies to return trampolines only and
|
||||
currently no MIPS OS ABI uses shared libraries that have them. */
|
||||
set_gdbarch_in_solib_return_trampoline (gdbarch, mips_in_return_stub);
|
||||
|
||||
set_gdbarch_single_step_through_delay (gdbarch,
|
||||
mips_single_step_through_delay);
|
||||
|
||||
|
|
|
@ -119,7 +119,9 @@ enum
|
|||
MIPS_AT_REGNUM = 1,
|
||||
MIPS_V0_REGNUM = 2, /* Function integer return value. */
|
||||
MIPS_A0_REGNUM = 4, /* Loc of first arg during a subr call. */
|
||||
MIPS_S2_REGNUM = 18, /* Contains return address in MIPS16 thunks. */
|
||||
MIPS_T9_REGNUM = 25, /* Contains address of callee in PIC. */
|
||||
MIPS_GP_REGNUM = 28,
|
||||
MIPS_SP_REGNUM = 29,
|
||||
MIPS_RA_REGNUM = 31,
|
||||
MIPS_PS_REGNUM = 32, /* Contains processor status. */
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
2012-04-26 Maciej W. Rozycki <macro@codesourcery.com>
|
||||
|
||||
* gdb.arch/mips16-thunks-inmain.c: New file.
|
||||
* gdb.arch/mips16-thunks-main.c: New file.
|
||||
* gdb.arch/mips16-thunks-sin.c: New file.
|
||||
* gdb.arch/mips16-thunks-sinfrob.c: New file.
|
||||
* gdb.arch/mips16-thunks-sinfrob16.c: New file.
|
||||
* gdb.arch/mips16-thunks-sinmain.c: New file.
|
||||
* gdb.arch/mips16-thunks-sinmips16.c: New file.
|
||||
* gdb.arch/mips16-thunks.exp: New file.
|
||||
|
||||
2012-04-25 Doug Evans <dje@google.com>
|
||||
|
||||
* configure.ac: Create gdb.go/Makefile.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
int
|
||||
inmain (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
int inmain (void);
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return inmain ();
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
double sinfrob (double d);
|
||||
double sinfrob16 (double d);
|
||||
|
||||
double sinblah (double d);
|
||||
double sinblah16 (double d);
|
||||
|
||||
double sinmips16 (double d);
|
||||
long lsinmips16 (double d);
|
||||
|
||||
extern long i;
|
||||
|
||||
double
|
||||
sinhelper (double d)
|
||||
{
|
||||
i++;
|
||||
d = sin (d);
|
||||
d = sinfrob16 (d);
|
||||
d = sinfrob (d);
|
||||
d = sinmips16 (d);
|
||||
i++;
|
||||
return d;
|
||||
}
|
||||
|
||||
long
|
||||
lsinhelper (double d)
|
||||
{
|
||||
long l;
|
||||
|
||||
i++;
|
||||
d = sin (d);
|
||||
d = sinblah (d);
|
||||
d = sinblah16 (d);
|
||||
l = lsinmips16 (d);
|
||||
i++;
|
||||
return l;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
extern long i;
|
||||
|
||||
double
|
||||
sinfrob (double d)
|
||||
{
|
||||
i++;
|
||||
d = sin (d);
|
||||
i++;
|
||||
return d;
|
||||
}
|
||||
|
||||
double
|
||||
sinblah (double d)
|
||||
{
|
||||
i++;
|
||||
d = sin (d);
|
||||
i++;
|
||||
return d;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
extern long i;
|
||||
|
||||
double
|
||||
sinfrob16 (double d)
|
||||
{
|
||||
i++;
|
||||
d = sin (d);
|
||||
i++;
|
||||
return d;
|
||||
}
|
||||
|
||||
double
|
||||
sinblah16 (double d)
|
||||
{
|
||||
i++;
|
||||
d = sin (d);
|
||||
i++;
|
||||
return d;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
double sinfrob (double d);
|
||||
double sinfrob16 (double d);
|
||||
|
||||
double sinblah (double d);
|
||||
double sinblah16 (double d);
|
||||
|
||||
double sinhelper (double);
|
||||
long lsinhelper (double);
|
||||
|
||||
double (*sinfunc) (double) = sinfrob;
|
||||
double (*sinfunc16) (double) = sinfrob16;
|
||||
|
||||
double f = 1.0;
|
||||
long i = 1;
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
double d = f;
|
||||
long l = i;
|
||||
|
||||
d = sinfrob16 (d);
|
||||
d = sinfrob (d);
|
||||
d = sinhelper (d);
|
||||
|
||||
sinfunc = sinblah;
|
||||
sinfunc16 = sinblah16;
|
||||
|
||||
d = sinblah (d);
|
||||
d = sinblah16 (d);
|
||||
l = lsinhelper (d);
|
||||
|
||||
return l + i;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
double sinfrob (double d);
|
||||
double sinfrob16 (double d);
|
||||
|
||||
double sinblah (double d);
|
||||
double sinblah16 (double d);
|
||||
|
||||
extern double (*sinfunc) (double);
|
||||
extern double (*sinfunc16) (double);
|
||||
|
||||
extern long i;
|
||||
|
||||
double
|
||||
sinmips16 (double d)
|
||||
{
|
||||
i++;
|
||||
d = sin (d);
|
||||
d = sinfrob16 (d);
|
||||
d = sinfrob (d);
|
||||
d = sinfunc16 (d);
|
||||
d = sinfunc (d);
|
||||
i++;
|
||||
return d;
|
||||
}
|
||||
|
||||
long
|
||||
lsinmips16 (double d)
|
||||
{
|
||||
union
|
||||
{
|
||||
double d;
|
||||
long l[2];
|
||||
}
|
||||
u;
|
||||
|
||||
i++;
|
||||
d = sin (d);
|
||||
d = sinblah (d);
|
||||
d = sinblah16 (d);
|
||||
d = sinfunc (d);
|
||||
u.d = sinfunc16 (d);
|
||||
i++;
|
||||
return u.l[0] == 0 && u.l[1] == 0;
|
||||
}
|
|
@ -0,0 +1,543 @@
|
|||
# Copyright 2012 Free Software Foundation, Inc.
|
||||
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Contributed by Mentor Graphics, written by Maciej W. Rozycki.
|
||||
|
||||
# Test MIPS16 thunk support.
|
||||
|
||||
# This should work on any targets that support MIPS16 execution, including
|
||||
# Linux and bare-iron ones, but not all of them do, for example MIPS16
|
||||
# support has been added to Linux relatively late in the game. Also besides
|
||||
# environment support, the target processor has to support the MIPS16 ASE.
|
||||
# Finally as of this writing MIPS16 support has only been implemented in the
|
||||
# toolchain for a subset of ABIs, so we need to check that a MIPS16
|
||||
# executable can be built and run at all before we attempt the actual test.
|
||||
|
||||
if { ![istarget "mips*-*-*"] } then {
|
||||
verbose "Skipping MIPS16 thunk support tests."
|
||||
return
|
||||
}
|
||||
|
||||
# A helper to set caller's SRCFILE and OBJFILE based on FILENAME and SUFFIX.
|
||||
proc set_src_and_obj { filename { suffix "" } } {
|
||||
upvar srcfile srcfile
|
||||
upvar objfile objfile
|
||||
global srcdir
|
||||
global objdir
|
||||
global subdir
|
||||
|
||||
if ![string equal "$suffix" ""] then {
|
||||
set suffix "-$suffix"
|
||||
}
|
||||
set srcfile ${srcdir}/${subdir}/${filename}.c
|
||||
set objfile ${objdir}/${subdir}/${filename}${suffix}.o
|
||||
}
|
||||
|
||||
# First check if a trivial MIPS16 program can be built and debugged. This
|
||||
# verifies environment and processor support, any failure here must be
|
||||
# classed as the lack of support.
|
||||
set testname mips16-thunks-main
|
||||
|
||||
set_src_and_obj mips16-thunks-inmain
|
||||
set options [list debug nowarnings additional_flags=-mips16]
|
||||
set objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set_src_and_obj mips16-thunks-main
|
||||
set options [list debug nowarnings additional_flags=-mips16]
|
||||
lappend objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set binfile ${objdir}/${subdir}/${testname}
|
||||
set options [list debug nowarnings]
|
||||
if { [gdb_compile ${objfiles} ${binfile} executable ${options}] != "" } then {
|
||||
unsupported "No MIPS16 support in the toolchain."
|
||||
return
|
||||
}
|
||||
clean_restart ${testname}
|
||||
gdb_breakpoint inmain
|
||||
gdb_run_cmd
|
||||
gdb_test_multiple "" "check for MIPS16 support in the processor" {
|
||||
-re "Breakpoint 1.*inmain .*$gdb_prompt $" {
|
||||
gdb_test_multiple "finish" \
|
||||
"check for MIPS16 support in the processor" {
|
||||
-re "Value returned is \\\$\[0-9\]+ = 0\[^0-9\].*$gdb_prompt $" {
|
||||
verbose "MIPS16 support check successful."
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
unsupported "No MIPS16 support in the processor."
|
||||
return
|
||||
}
|
||||
default {
|
||||
unsupported "No MIPS16 support in the processor."
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
unsupported "No MIPS16 support in the processor."
|
||||
return
|
||||
}
|
||||
default {
|
||||
unsupported "No MIPS16 support in the processor."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Check if MIPS16 PIC code can be built and debugged. We want to check
|
||||
# PIC and MIPS16 thunks are handled correctly together if possible, but
|
||||
# on targets that do not support PIC code, e.g. bare iron, we still want
|
||||
# to test the rest of functionality.
|
||||
set testname mips16-thunks-pic
|
||||
set picflag ""
|
||||
|
||||
set_src_and_obj mips16-thunks-inmain pic
|
||||
set options [list \
|
||||
debug nowarnings additional_flags=-mips16 additional_flags=-fPIC]
|
||||
set objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set_src_and_obj mips16-thunks-main pic
|
||||
set options [list \
|
||||
debug nowarnings additional_flags=-mips16 additional_flags=-fPIC]
|
||||
lappend objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set binfile ${objdir}/${subdir}/${testname}
|
||||
set options [list debug nowarnings additional_flags=-fPIC]
|
||||
if { [gdb_compile ${objfiles} ${binfile} executable ${options}] == "" } then {
|
||||
clean_restart ${testname}
|
||||
gdb_breakpoint inmain
|
||||
gdb_run_cmd
|
||||
gdb_test_multiple "" "check for PIC support" {
|
||||
-re "Breakpoint 1.*inmain .*$gdb_prompt $" {
|
||||
note "PIC support present, will make additional PIC thunk checks."
|
||||
set picflag additional_flags=-fPIC
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
note "No PIC support, skipping additional PIC thunk checks."
|
||||
}
|
||||
default {
|
||||
note "No PIC support, skipping additional PIC thunk checks."
|
||||
}
|
||||
}
|
||||
} else {
|
||||
note "No PIC support, skipping additional PIC thunk checks."
|
||||
}
|
||||
|
||||
# OK, build the twisted executable. This program contains the following
|
||||
# MIPS16 thunks:
|
||||
# - __call_stub_fp_sin,
|
||||
# - __call_stub_fp_sinblah,
|
||||
# - __call_stub_fp_sinfrob,
|
||||
# - __call_stub_fp_sinhelper,
|
||||
# - __call_stub_lsinhelper,
|
||||
# - __fn_stub_lsinmips16,
|
||||
# - __fn_stub_sinblah16,
|
||||
# - __fn_stub_sinfrob16,
|
||||
# - __fn_stub_sinmips16,
|
||||
# - __mips16_call_stub_df_2,
|
||||
# - __mips16_ret_df.
|
||||
# Additionally, if PIC code is supported, it contains the following PIC thunks:
|
||||
# - .pic.__mips16_call_stub_df_2,
|
||||
# - .pic.__mips16_ret_df,
|
||||
# - .pic.sinblah,
|
||||
# - .pic.sinblah16,
|
||||
# - .pic.sinfrob,
|
||||
# - .pic.sinfrob16.
|
||||
set testname mips16-thunks-sin
|
||||
|
||||
set_src_and_obj mips16-thunks-sinmain
|
||||
set options [list debug nowarnings additional_flags=-mips16]
|
||||
set objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set_src_and_obj mips16-thunks-sin
|
||||
set options [list debug nowarnings additional_flags=-mno-mips16]
|
||||
lappend objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set_src_and_obj mips16-thunks-sinmips16
|
||||
set options [list debug nowarnings additional_flags=-mips16]
|
||||
lappend objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set_src_and_obj mips16-thunks-sinfrob
|
||||
set options [list \
|
||||
debug nowarnings additional_flags=-mno-mips16 ${picflag}]
|
||||
lappend objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set_src_and_obj mips16-thunks-sinfrob16
|
||||
set options [list \
|
||||
debug nowarnings additional_flags=-mips16 ${picflag}]
|
||||
lappend objfiles ${objfile}
|
||||
gdb_compile ${srcfile} ${objfile} object ${options}
|
||||
|
||||
set binfile ${objdir}/${subdir}/${testname}
|
||||
set options [list debug nowarnings]
|
||||
gdb_compile ${objfiles} ${binfile} executable ${options}
|
||||
clean_restart ${testname}
|
||||
if ![runto_main] then {
|
||||
fail "running test program, MIPS16 thunk tests aborted"
|
||||
return
|
||||
}
|
||||
|
||||
# Build some useful regular expressions out of a list of functions FUNCS
|
||||
# to be used to match against backtraces.
|
||||
proc build_frames_re { funcs } {
|
||||
upvar anyframe anyframe
|
||||
upvar frames frames
|
||||
upvar frame frame
|
||||
upvar func func
|
||||
|
||||
set fid 0
|
||||
set argsandsource " +\\\(.*\\\) +at +\[^\r\n\]+\r\n"
|
||||
set addrin "(?:\[^ \]+ +in +)?"
|
||||
set anyframe "#${fid} +${addrin}(\[^ \]+)${argsandsource}"
|
||||
set frame "#${fid} +${addrin}${func}${argsandsource}"
|
||||
set frames "$frame"
|
||||
foreach f [lrange $funcs 1 end] {
|
||||
incr fid
|
||||
append frames "#${fid} +${addrin}${f}${argsandsource}"
|
||||
}
|
||||
}
|
||||
|
||||
# Single-step through the function that is at the head of function list
|
||||
# FUNCS until a different function (frame) is reached. Before each step
|
||||
# check the backtrace against FUNCS. ID is used for reporting, to tell
|
||||
# apart different calls to this procedure for the same function. If
|
||||
# successful, then return the name of the function we have stopped in.
|
||||
proc step_through { id funcs } {
|
||||
global gdb_prompt
|
||||
|
||||
set func [lindex $funcs 0]
|
||||
build_frames_re "$funcs"
|
||||
|
||||
set msg "single-stepping through \"${func}\" ($id)"
|
||||
|
||||
# Arbitrarily limit the maximium number of steps made to avoid looping
|
||||
# indefinitely in the case something goes wrong, increase as (if)
|
||||
# necessary.
|
||||
set count 8
|
||||
while { $count > 0 } {
|
||||
if { [gdb_test_multiple "backtrace" "$msg (backtrace)" {
|
||||
-re "${frames}$gdb_prompt $" {
|
||||
if { [gdb_test_multiple "step" "$msg (step)" {
|
||||
-re "$gdb_prompt $" {
|
||||
if { [gdb_test_multiple "frame" "$msg (frame)" {
|
||||
-re "${frame}.*$gdb_prompt $" {
|
||||
}
|
||||
-re "${anyframe}.*$gdb_prompt $" {
|
||||
pass "$msg"
|
||||
return $expect_out(1,string)
|
||||
}
|
||||
}] != 0 } then {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}] != 0 } then {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}] != 0 } then {
|
||||
return ""
|
||||
}
|
||||
incr count -1
|
||||
}
|
||||
fail "$msg (too many steps)"
|
||||
return ""
|
||||
}
|
||||
|
||||
# Finish the current function that must be one that is at the head of
|
||||
# function list FUNCS. Before that check the backtrace against FUNCS.
|
||||
# ID is used for reporting, to tell apart different calls to this
|
||||
# procedure for the same function. If successful, then return the name
|
||||
# of the function we have stopped in.
|
||||
proc finish_through { id funcs } {
|
||||
global gdb_prompt
|
||||
|
||||
set func [lindex $funcs 0]
|
||||
build_frames_re "$funcs"
|
||||
|
||||
set msg "finishing \"${func}\" ($id)"
|
||||
|
||||
gdb_test_multiple "backtrace" "$msg (backtrace)" {
|
||||
-re "${frames}$gdb_prompt $" {
|
||||
gdb_test_multiple "finish" "$msg (finish)" {
|
||||
-re "Run till exit from ${frame}.*$gdb_prompt $" {
|
||||
gdb_test_multiple "frame" "$msg (frame)" {
|
||||
-re "${anyframe}.*$gdb_prompt $" {
|
||||
pass "$msg"
|
||||
return $expect_out(1,string)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
# Report PASS if VAL is equal to EXP, otherwise report FAIL, using MSG.
|
||||
proc pass_if_eq { val exp msg } {
|
||||
if [string equal "$val" "$exp"] then {
|
||||
pass "$msg"
|
||||
} else {
|
||||
fail "$msg"
|
||||
}
|
||||
}
|
||||
|
||||
# Check if FUNC is equal to WANT. If not, then assume that we have stepped
|
||||
# into a library call. In this case finish it, then step out of the caller.
|
||||
# ID is used for reporting, to tell apart different calls to this procedure
|
||||
# for the same function. If successful, then return the name of the
|
||||
# function we have stopped in.
|
||||
proc finish_if_ne { id func want funcs } {
|
||||
if ![string equal "$func" "$want"] then {
|
||||
set call "$func"
|
||||
set want [lindex $funcs 0]
|
||||
set func [finish_through "$id" [linsert $funcs 0 "$func"]]
|
||||
pass_if_eq "$func" "$want" "\"${call}\" finishing to \"${want}\" ($id)"
|
||||
set func [step_through "$id" $funcs]
|
||||
}
|
||||
return "$func"
|
||||
}
|
||||
|
||||
# Now single-step through the program, making sure all thunks are correctly
|
||||
# stepped over and omitted from backtraces.
|
||||
|
||||
set id 1
|
||||
set func [step_through $id [list main]]
|
||||
pass_if_eq "$func" sinfrob16 "stepping from \"main\" into \"sinfrob16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob16 main]]
|
||||
set func [finish_if_ne $id "$func" main [list sinfrob16 main]]
|
||||
pass_if_eq "$func" main "stepping from \"sinfrob16\" back to \"main\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list main]]
|
||||
pass_if_eq "$func" sinfrob "stepping from \"main\" into \"sinfrob\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob main]]
|
||||
set func [finish_if_ne $id "$func" main [list sinfrob main]]
|
||||
pass_if_eq "$func" main "stepping from \"sinfrob\" back to \"main\" ($id)"
|
||||
|
||||
# 5
|
||||
incr id
|
||||
set func [step_through $id [list main]]
|
||||
pass_if_eq "$func" sinhelper "stepping from \"main\" into \"sinhelper\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinfrob16 [list sinhelper main]]
|
||||
pass_if_eq "$func" sinfrob16 \
|
||||
"stepping from \"sinhelper\" into \"sinfrob16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob16 sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinhelper [list sinfrob16 sinhelper main]]
|
||||
pass_if_eq "$func" sinhelper \
|
||||
"stepping from \"sinfrob16\" back to \"sinhelper\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinhelper main]]
|
||||
pass_if_eq "$func" sinfrob "stepping from \"sinhelper\" into \"sinfrob\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinhelper [list sinfrob sinhelper main]]
|
||||
pass_if_eq "$func" sinhelper \
|
||||
"stepping from \"sinfrob\" back to \"sinhelper\" ($id)"
|
||||
|
||||
# 10
|
||||
incr id
|
||||
set func [step_through $id [list sinhelper main]]
|
||||
pass_if_eq "$func" sinmips16 \
|
||||
"stepping from \"sinhelper\" into \"sinmips16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinmips16 sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinfrob16 [list sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinfrob16 \
|
||||
"stepping from \"sinmips16\" into \"sinfrob16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob16 sinmips16 sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinmips16 \
|
||||
[list sinfrob16 sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinmips16 \
|
||||
"stepping from \"sinfrob16\" back to \"sinmips16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinfrob "stepping from \"sinmips16\" into \"sinfrob\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob sinmips16 sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinhelper \
|
||||
[list sinfrob sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinmips16 \
|
||||
"stepping from \"sinfrob\" back to \"sinmips16\" ($id)"
|
||||
|
||||
# 15
|
||||
incr id
|
||||
set func [step_through $id [list sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinfrob16 \
|
||||
"stepping from \"sinmips16\" into \"sinfrob16\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob16 sinmips16 sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinmips16 \
|
||||
[list sinfrob16 sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinmips16 \
|
||||
"stepping from \"sinfrob16\" back to \"sinmips16\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinfrob \
|
||||
"stepping from \"sinmips16\" into \"sinfrob\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinfrob sinmips16 sinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinhelper \
|
||||
[list sinfrob sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinmips16 \
|
||||
"stepping from \"sinfrob\" back to \"sinmips16\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinmips16 sinhelper main]]
|
||||
pass_if_eq "$func" sinhelper \
|
||||
"stepping from \"sinmips16\" back to \"sinhelper\" ($id)"
|
||||
|
||||
# 20
|
||||
incr id
|
||||
set func [step_through $id [list sinhelper main]]
|
||||
pass_if_eq "$func" main "stepping from \"sinhelper\" back to \"main\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list main]]
|
||||
pass_if_eq "$func" sinblah "stepping from \"main\" into \"sinblah\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah main]]
|
||||
set func [finish_if_ne $id "$func" main [list sinblah main]]
|
||||
pass_if_eq "$func" main "stepping from \"sinblah\" back to \"main\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list main]]
|
||||
pass_if_eq "$func" sinblah16 "stepping from \"main\" into \"sinblah16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah16 main]]
|
||||
set func [finish_if_ne $id "$func" main [list sinblah16 main]]
|
||||
pass_if_eq "$func" main "stepping from \"sinblah16\" back to \"main\" ($id)"
|
||||
|
||||
# 25
|
||||
incr id
|
||||
set func [step_through $id [list main]]
|
||||
pass_if_eq "$func" lsinhelper \
|
||||
"stepping from \"main\" into \"lsinhelper\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinblah [list lsinhelper main]]
|
||||
pass_if_eq "$func" sinblah \
|
||||
"stepping from \"lsinhelper\" into \"sinblah\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" lsinhelper [list sinblah lsinhelper main]]
|
||||
pass_if_eq "$func" lsinhelper \
|
||||
"stepping from \"sinblah\" back to \"lsinhelper\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list lsinhelper main]]
|
||||
pass_if_eq "$func" sinblah16 \
|
||||
"stepping from \"lsinhelper\" into \"sinblah16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah16 lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" lsinhelper [list sinblah16 lsinhelper main]]
|
||||
pass_if_eq "$func" lsinhelper \
|
||||
"stepping from \"sinblah16\" back to \"lsinhelper\" ($id)"
|
||||
|
||||
# 30
|
||||
incr id
|
||||
set func [step_through $id [list lsinhelper main]]
|
||||
pass_if_eq "$func" lsinmips16 \
|
||||
"stepping from \"lsinhelper\" into \"lsinmips16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list lsinmips16 lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" sinblah [list lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" sinblah \
|
||||
"stepping from \"lsinmips16\" into \"sinblah\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah lsinmips16 lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" lsinmips16 \
|
||||
[list sinblah lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" lsinmips16 \
|
||||
"stepping from \"sinblah\" back to \"lsinmips16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" sinblah16 \
|
||||
"stepping from \"lsinmips16\" into \"sinblah16\" ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah16 lsinmips16 lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" lsinhelper \
|
||||
[list sinblah16 lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" lsinmips16 \
|
||||
"stepping from \"sinblah16\" back to \"lsinmips16\" ($id)"
|
||||
|
||||
# 35
|
||||
incr id
|
||||
set func [step_through $id [list lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" sinblah \
|
||||
"stepping from \"lsinmips16\" into \"sinblah\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah lsinmips16 lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" lsinmips16 \
|
||||
[list sinblah lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" lsinmips16 \
|
||||
"stepping from \"sinblah\" back to \"lsinmips16\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" sinblah16 \
|
||||
"stepping from \"lsinmips16\" into \"sinblah16\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list sinblah16 lsinmips16 lsinhelper main]]
|
||||
set func [finish_if_ne $id "$func" lsinhelper \
|
||||
[list sinblah16 lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" lsinmips16 \
|
||||
"stepping from \"sinblah16\" back to \"lsinmips16\" (indirectly) ($id)"
|
||||
|
||||
incr id
|
||||
set func [step_through $id [list lsinmips16 lsinhelper main]]
|
||||
pass_if_eq "$func" lsinhelper \
|
||||
"stepping from \"lsinmips16\" back to \"lsinhelper\" ($id)"
|
||||
|
||||
# 40
|
||||
incr id
|
||||
set func [step_through $id [list lsinhelper main]]
|
||||
pass_if_eq "$func" main "stepping from \"lsinhelper\" back to \"main\" ($id)"
|
Loading…
Reference in New Issue