#define S390_TDEP
#include <defs.h>
#include "arch-utils.h"
#include "frame.h"
#include "inferior.h"
#include "symtab.h"
#include "target.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "symfile.h"
#include "objfiles.h"
#include "tm.h"
#include "../bfd/bfd.h"
#include "floatformat.h"
#include "regcache.h"
#include "value.h"
#include "gdb_assert.h"
int
s390_register_raw_size (int reg_nr)
{
if (S390_FP0_REGNUM <= reg_nr
&& reg_nr < S390_FP0_REGNUM + S390_NUM_FPRS)
return S390_FPR_SIZE;
else
return 4;
}
int
s390x_register_raw_size (int reg_nr)
{
return (reg_nr == S390_FPC_REGNUM)
|| (reg_nr >= S390_FIRST_ACR && reg_nr <= S390_LAST_ACR) ? 4 : 8;
}
int
s390_cannot_fetch_register (int regno)
{
return (regno >= S390_FIRST_CR && regno < (S390_FIRST_CR + 9)) ||
(regno >= (S390_FIRST_CR + 12) && regno <= S390_LAST_CR);
}
int
s390_register_byte (int reg_nr)
{
if (reg_nr <= S390_GP_LAST_REGNUM)
return reg_nr * S390_GPR_SIZE;
if (reg_nr <= S390_LAST_ACR)
return S390_ACR0_OFFSET + (((reg_nr) - S390_FIRST_ACR) * S390_ACR_SIZE);
if (reg_nr <= S390_LAST_CR)
return S390_CR0_OFFSET + (((reg_nr) - S390_FIRST_CR) * S390_CR_SIZE);
if (reg_nr == S390_FPC_REGNUM)
return S390_FPC_OFFSET;
else
return S390_FP0_OFFSET + (((reg_nr) - S390_FP0_REGNUM) * S390_FPR_SIZE);
}
#ifndef GDBSERVER
#define S390_MAX_INSTR_SIZE (6)
#define S390_SYSCALL_OPCODE (0x0a)
#define S390_SYSCALL_SIZE (2)
#define S390_SIGCONTEXT_SREGS_OFFSET (8)
#define S390X_SIGCONTEXT_SREGS_OFFSET (8)
#define S390_SIGREGS_FP0_OFFSET (144)
#define S390X_SIGREGS_FP0_OFFSET (216)
#define S390_UC_MCONTEXT_OFFSET (256)
#define S390X_UC_MCONTEXT_OFFSET (344)
#define S390_STACK_FRAME_OVERHEAD (GDB_TARGET_IS_ESAME ? 160:96)
#define S390_SIGNAL_FRAMESIZE (GDB_TARGET_IS_ESAME ? 160:96)
#define s390_NR_sigreturn 119
#define s390_NR_rt_sigreturn 173
struct frame_extra_info
{
int initialised;
int good_prologue;
CORE_ADDR function_start;
CORE_ADDR skip_prologue_function_start;
CORE_ADDR saved_pc_valid;
CORE_ADDR saved_pc;
CORE_ADDR sig_fixed_saved_pc_valid;
CORE_ADDR sig_fixed_saved_pc;
CORE_ADDR frame_pointer_saved_pc;
CORE_ADDR stack_bought;
CORE_ADDR sigcontext;
};
static CORE_ADDR s390_frame_saved_pc_nofix (struct frame_info *fi);
int
s390_readinstruction (bfd_byte instr[], CORE_ADDR at,
struct disassemble_info *info)
{
int instrlen;
static int s390_instrlen[] = {
2,
4,
4,
6
};
if ((*info->read_memory_func) (at, &instr[0], 2, info))
return -1;
instrlen = s390_instrlen[instr[0] >> 6];
if (instrlen > 2)
{
if ((*info->read_memory_func) (at + 2, &instr[2], instrlen - 2, info))
return -1;
}
return instrlen;
}
static void
s390_memset_extra_info (struct frame_extra_info *fextra_info)
{
memset (fextra_info, 0, sizeof (struct frame_extra_info));
}
char *
s390_register_name (int reg_nr)
{
static char *register_names[] = {
"pswm", "pswa",
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"acr0", "acr1", "acr2", "acr3", "acr4", "acr5", "acr6", "acr7",
"acr8", "acr9", "acr10", "acr11", "acr12", "acr13", "acr14", "acr15",
"cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
"cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15",
"fpc",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15"
};
if (reg_nr <= S390_LAST_REGNUM)
return register_names[reg_nr];
else
return NULL;
}
int
s390_stab_reg_to_regnum (int regno)
{
return regno >= 64 ? S390_PSWM_REGNUM - 64 :
regno >= 48 ? S390_FIRST_ACR - 48 :
regno >= 32 ? S390_FIRST_CR - 32 :
regno <= 15 ? (regno + 2) :
S390_FP0_REGNUM + ((regno - 16) & 8) + (((regno - 16) & 3) << 1) +
(((regno - 16) & 4) >> 2);
}
static int
is_arg_reg (int regidx)
{
return 2 <= regidx && regidx <= 6;
}
int
s390_get_frame_info (CORE_ADDR pc, struct frame_extra_info *fextra_info,
struct frame_info *fi, int init_extra_info)
{
#define CONST_POOL_REGIDX 13
#define GOT_REGIDX 12
bfd_byte instr[S390_MAX_INSTR_SIZE];
CORE_ADDR test_pc = pc, test_pc2;
CORE_ADDR orig_sp = 0, save_reg_addr = 0, *saved_regs = NULL;
int valid_prologue, good_prologue = 0;
int gprs_saved[S390_NUM_GPRS];
int fprs_saved[S390_NUM_FPRS];
int regidx, instrlen;
int const_pool_state;
int varargs_state;
int loop_cnt, gdb_gpr_store, gdb_fpr_store;
int offset, expected_offset;
int err = 0;
disassemble_info info;
int frame_pointer_found = 0;
int frame_pointer_regidx = 0xf;
int save_link_state = 0;
int save_link_regidx, subtract_sp_regidx;
int got_state= 0;
CORE_ADDR got_load_addr = 0, got_load_len = 0;
const_pool_state = varargs_state = 0;
memset (gprs_saved, 0, sizeof (gprs_saved));
memset (fprs_saved, 0, sizeof (fprs_saved));
info.read_memory_func = dis_asm_read_memory;
save_link_regidx = subtract_sp_regidx = 0;
if (fextra_info)
{
if (fi && fi->frame)
{
orig_sp = fi->frame;
if (! init_extra_info && fextra_info->initialised)
orig_sp += fextra_info->stack_bought;
saved_regs = fi->saved_regs;
}
if (init_extra_info || !fextra_info->initialised)
{
s390_memset_extra_info (fextra_info);
fextra_info->function_start = pc;
fextra_info->initialised = 1;
}
}
instrlen = 0;
do
{
valid_prologue = 0;
test_pc += instrlen;
instrlen = s390_readinstruction (instr, test_pc, &info);
if (instrlen < 0)
{
good_prologue = 0;
err = -1;
break;
}
if (instr[0] == S390_SYSCALL_OPCODE && test_pc == pc)
{
good_prologue = 1;
if (saved_regs && fextra_info && fi->next && fi->next->extra_info
&& fi->next->extra_info->sigcontext)
{
save_reg_addr = fi->next->extra_info->sigcontext +
REGISTER_BYTE (S390_GP0_REGNUM);
for (regidx = 0; regidx < S390_NUM_GPRS; regidx++)
{
saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
save_reg_addr += S390_GPR_SIZE;
}
save_reg_addr = fi->next->extra_info->sigcontext +
(GDB_TARGET_IS_ESAME ? S390X_SIGREGS_FP0_OFFSET :
S390_SIGREGS_FP0_OFFSET);
for (regidx = 0; regidx < S390_NUM_FPRS; regidx++)
{
saved_regs[S390_FP0_REGNUM + regidx] = save_reg_addr;
save_reg_addr += S390_FPR_SIZE;
}
}
break;
}
if (save_link_state == 0)
{
if (((GDB_TARGET_IS_ESAME &&
((instr[0] == 0xeb) && (instr[5] == 0x24))) ||
(instr[0] == 0x90)) && ((instr[2] >> 4) == 0xf))
{
regidx = (instr[1] >> 4);
if (regidx < 6)
varargs_state = 1;
offset = ((instr[2] & 0xf) << 8) + instr[3];
expected_offset =
S390_GPR6_STACK_OFFSET + (S390_GPR_SIZE * (regidx - 6));
if (offset != expected_offset)
{
good_prologue = 0;
break;
}
if (saved_regs)
save_reg_addr = orig_sp + offset;
for (; regidx <= (instr[1] & 0xf); regidx++)
{
if (gprs_saved[regidx])
{
good_prologue = 0;
break;
}
good_prologue = 1;
gprs_saved[regidx] = 1;
if (saved_regs)
{
saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
save_reg_addr += S390_GPR_SIZE;
}
}
valid_prologue = 1;
continue;
}
}
if ((save_link_state == 0 || save_link_state == 3) &&
((GDB_TARGET_IS_ESAME &&
((instr[0] == 0xe3) && (instr[5] == 0x24))) ||
(instr[0] == 0x50)) && ((instr[2] >> 4) == 0xf))
{
regidx = instr[1] >> 4;
offset = ((instr[2] & 0xf) << 8) + instr[3];
if (offset == 0)
{
if (save_link_state == 3 && regidx == save_link_regidx)
{
save_link_state = 4;
valid_prologue = 1;
continue;
}
else
break;
}
if (regidx < 6)
varargs_state = 1;
expected_offset =
S390_GPR6_STACK_OFFSET + (S390_GPR_SIZE * (regidx - 6));
if (offset != expected_offset)
{
good_prologue = 0;
break;
}
if (gprs_saved[regidx])
{
good_prologue = 0;
break;
}
good_prologue = 1;
gprs_saved[regidx] = 1;
if (saved_regs)
{
save_reg_addr = orig_sp + offset;
saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
}
valid_prologue = 1;
continue;
}
if ((save_link_state == 3 || save_link_state == 4)
&& ((instr[0] == 0x50
&& (instr[1] & 0xf) == 0
&& is_arg_reg ((instr[1] >> 4) & 0xf)
&& ((instr[2] >> 4) & 0xf) == frame_pointer_regidx)
|| (instr[0] == 0x90
&& is_arg_reg ((instr[1] >> 4) & 0xf)
&& is_arg_reg (instr[1] & 0xf)
&& ((instr[2] >> 4) & 0xf) == frame_pointer_regidx)))
{
valid_prologue = 1;
continue;
}
if (instr[0] == 0x60 && (instr[2] >> 4) == 0xf)
{
regidx = instr[1] >> 4;
if (regidx == 0 || regidx == 2)
varargs_state = 1;
if (fprs_saved[regidx])
{
good_prologue = 0;
break;
}
fprs_saved[regidx] = 1;
if (saved_regs)
{
save_reg_addr = orig_sp + (((instr[2] & 0xf) << 8) + instr[3]);
saved_regs[S390_FP0_REGNUM + regidx] = save_reg_addr;
}
valid_prologue = 1;
continue;
}
if (const_pool_state == 0)
{
if (GDB_TARGET_IS_ESAME)
{
if ((instr[0] == 0xc0)
&& (instr[1] == (CONST_POOL_REGIDX << 4)))
{
const_pool_state = 2;
valid_prologue = 1;
continue;
}
}
else
{
if (instr[0] == 0xd && (instr[1] & 0xf) == 0
&& ((instr[1] >> 4) == CONST_POOL_REGIDX))
{
const_pool_state = 1;
valid_prologue = 1;
continue;
}
}
if ((instr[0] == 0xa7) && ((instr[1] & 0xf) == 0x5) &&
((instr[1] >> 4) == CONST_POOL_REGIDX)
&& ((instr[2] & 0x80) == 0))
{
const_pool_state = 2;
test_pc +=
(((((instr[2] & 0xf) << 8) + instr[3]) << 1) - instrlen);
valid_prologue = 1;
continue;
}
}
if (const_pool_state == 1 && (instr[0] == 0xa7) &&
((GDB_TARGET_IS_ESAME &&
(instr[1] == ((CONST_POOL_REGIDX << 4) | 0xb))) ||
(instr[1] == ((CONST_POOL_REGIDX << 4) | 0xa))))
{
const_pool_state = 2;
valid_prologue = 1;
continue;
}
if ((GDB_TARGET_IS_ESAME &&
instr[0] == 0xb9 && instr[1] == 0x04 && (instr[3] & 0xf) == 0xf) ||
(instr[0] == 0x18 && (instr[1] & 0xf) == 0xf))
{
if (GDB_TARGET_IS_ESAME)
regidx = instr[3] >> 4;
else
regidx = instr[1] >> 4;
if (save_link_state == 0 && regidx != 0xb)
{
save_link_regidx = regidx;
save_link_state = 1;
valid_prologue = 1;
continue;
}
if (!frame_pointer_found && regidx == 0xb)
{
frame_pointer_regidx = 0xb;
frame_pointer_found = 1;
if (fextra_info)
fextra_info->frame_pointer_saved_pc = test_pc;
valid_prologue = 1;
continue;
}
}
if (save_link_state == 1 && (instr[0] == 0xa7) &&
((GDB_TARGET_IS_ESAME && (instr[1] == 0xfb)) || (instr[1] == 0xfa)))
{
if (fextra_info)
fextra_info->stack_bought =
-extract_signed_integer (&instr[2], 2);
save_link_state = 3;
valid_prologue = 1;
continue;
}
if ((save_link_state == 1) && (instr[0] == 0xa7)
&& ((instr[1] & 0xf) == 0x5) && (instr[2] == 0)
&& (instr[3] == 0x4) && ((instr[1] >> 4) != CONST_POOL_REGIDX))
{
subtract_sp_regidx = instr[1] >> 4;
save_link_state = 2;
if (fextra_info)
target_read_memory (test_pc + instrlen,
(char *) &fextra_info->stack_bought,
sizeof (fextra_info->stack_bought));
test_pc += 4;
valid_prologue = 1;
continue;
}
if (save_link_state == 2 && instr[0] == 0x5b
&& instr[1] == 0xf0 &&
instr[2] == (subtract_sp_regidx << 4) && instr[3] == 0)
{
save_link_state = 3;
valid_prologue = 1;
continue;
}
if ((instr[0] == 0x41) && ((instr[2] >> 4) == 0xf) &&
((instr[1] & 0xf) == 0))
{
if (((instr[1] >> 4) == 7) && (save_link_state == 0) &&
((instr[2] & 0xf) == 0)
&& (instr[3] == S390_STACK_FRAME_OVERHEAD))
{
valid_prologue = 1;
continue;
}
if (varargs_state == 1)
{
varargs_state = 2;
valid_prologue = 1;
continue;
}
}
if (GDB_TARGET_IS_ESAME)
{
if ((got_state == 0) && (instr[0] == 0xc0)
&& (instr[1] == (GOT_REGIDX << 4)))
{
got_state = 2;
valid_prologue = 1;
continue;
}
}
else
{
if (got_state == 0 && const_pool_state == 2 && instr[0] == 0x58
&& (instr[2] == (CONST_POOL_REGIDX << 4))
&& ((instr[1] >> 4) == GOT_REGIDX))
{
got_state = 1;
got_load_addr = test_pc;
got_load_len = instrlen;
valid_prologue = 1;
continue;
}
if (got_state == 1 && instr[0] == 0x1a &&
instr[1] == ((GOT_REGIDX << 4) | CONST_POOL_REGIDX))
{
got_state = 2;
valid_prologue = 1;
continue;
}
}
}
while (valid_prologue && good_prologue);
if (good_prologue)
{
if (got_state == 1
&& got_load_addr + got_load_len == test_pc)
{
test_pc = got_load_addr;
instrlen = got_load_len;
}
good_prologue = (((const_pool_state == 0) || (const_pool_state == 2)) &&
((save_link_state == 0) || (save_link_state == 4)) &&
((varargs_state == 0) || (varargs_state == 2)));
}
if (fextra_info)
{
fextra_info->good_prologue = good_prologue;
fextra_info->skip_prologue_function_start =
(good_prologue ? test_pc : pc);
}
if (saved_regs)
saved_regs[S390_SP_REGNUM] = orig_sp;
return err;
}
int
s390_check_function_end (CORE_ADDR pc)
{
bfd_byte instr[S390_MAX_INSTR_SIZE];
disassemble_info info;
int regidx, instrlen;
info.read_memory_func = dis_asm_read_memory;
instrlen = s390_readinstruction (instr, pc, &info);
if (instrlen < 0)
return -1;
if (instrlen != 2 || instr[0] != 07 || (instr[1] >> 4) != 0xf)
return 0;
regidx = instr[1] & 0xf;
instrlen =
s390_readinstruction (instr, pc - (GDB_TARGET_IS_ESAME ? 6 : 4), &info);
if (instrlen < 0)
return -1;
if (GDB_TARGET_IS_ESAME)
{
if (instrlen != 6 || instr[0] != 0xeb || instr[5] != 0x4)
return 0;
}
else if (instrlen != 4 || instr[0] != 0x98)
{
return 0;
}
if ((instr[2] >> 4) != 0xf)
return 0;
if (regidx == 14)
return 1;
instrlen = s390_readinstruction (instr, pc - (GDB_TARGET_IS_ESAME ? 12 : 8),
&info);
if (instrlen < 0)
return -1;
if (GDB_TARGET_IS_ESAME)
{
if (instrlen != 6 || instr[0] != 0xe3 || instr[5] != 0x4)
return 0;
}
else
{
if (instrlen != 4 || instr[0] != 0x58)
return 0;
}
if (instr[2] >> 4 != 0xf)
return 0;
if (instr[1] >> 4 != regidx)
return 0;
return 1;
}
static CORE_ADDR
s390_sniff_pc_function_start (CORE_ADDR pc, struct frame_info *fi)
{
CORE_ADDR function_start, test_function_start;
int loop_cnt, err, function_end;
struct frame_extra_info fextra_info;
function_start = get_pc_function_start (pc);
if (function_start == 0)
{
test_function_start = pc;
if (test_function_start & 1)
return 0;
loop_cnt = 0;
do
{
err =
s390_get_frame_info (test_function_start, &fextra_info, fi, 1);
loop_cnt++;
test_function_start -= 2;
function_end = s390_check_function_end (test_function_start);
}
while (!(function_end == 1 || err || loop_cnt >= 4096 ||
(fextra_info.good_prologue)));
if (fextra_info.good_prologue)
function_start = fextra_info.function_start;
else if (function_end == 1)
function_start = test_function_start;
}
return function_start;
}
CORE_ADDR
s390_function_start (struct frame_info *fi)
{
CORE_ADDR function_start = 0;
if (fi->extra_info && fi->extra_info->initialised)
function_start = fi->extra_info->function_start;
else if (fi->pc)
function_start = get_pc_function_start (fi->pc);
return function_start;
}
int
s390_frameless_function_invocation (struct frame_info *fi)
{
struct frame_extra_info fextra_info, *fextra_info_ptr;
int frameless = 0;
if (fi->next == NULL)
{
if (fi->extra_info)
fextra_info_ptr = fi->extra_info;
else
{
fextra_info_ptr = &fextra_info;
s390_get_frame_info (s390_sniff_pc_function_start (fi->pc, fi),
fextra_info_ptr, fi, 1);
}
frameless = ((fextra_info_ptr->stack_bought == 0));
}
return frameless;
}
static int
s390_is_sigreturn (CORE_ADDR pc, struct frame_info *sighandler_fi,
CORE_ADDR *sregs, CORE_ADDR *sigcaller_pc)
{
bfd_byte instr[S390_MAX_INSTR_SIZE];
disassemble_info info;
int instrlen;
CORE_ADDR scontext;
int retval = 0;
CORE_ADDR orig_sp;
CORE_ADDR temp_sregs;
scontext = temp_sregs = 0;
info.read_memory_func = dis_asm_read_memory;
instrlen = s390_readinstruction (instr, pc, &info);
if (sigcaller_pc)
*sigcaller_pc = 0;
if (((instrlen == S390_SYSCALL_SIZE) &&
(instr[0] == S390_SYSCALL_OPCODE)) &&
((instr[1] == s390_NR_sigreturn) || (instr[1] == s390_NR_rt_sigreturn)))
{
if (sighandler_fi)
{
if (s390_frameless_function_invocation (sighandler_fi))
orig_sp = sighandler_fi->frame;
else
orig_sp = ADDR_BITS_REMOVE ((CORE_ADDR)
read_memory_integer (sighandler_fi->
frame,
S390_GPR_SIZE));
if (orig_sp && sigcaller_pc)
{
scontext = orig_sp + S390_SIGNAL_FRAMESIZE;
if (pc == scontext && instr[1] == s390_NR_rt_sigreturn)
{
temp_sregs = orig_sp + (GDB_TARGET_IS_ESAME ?
S390X_UC_MCONTEXT_OFFSET :
S390_UC_MCONTEXT_OFFSET);
}
else
{
temp_sregs = ADDR_BITS_REMOVE ((CORE_ADDR)
read_memory_integer (scontext
+
(GDB_TARGET_IS_ESAME
?
S390X_SIGCONTEXT_SREGS_OFFSET
:
S390_SIGCONTEXT_SREGS_OFFSET),
S390_GPR_SIZE));
}
*sigcaller_pc =
ADDR_BITS_REMOVE ((CORE_ADDR)
read_memory_integer (temp_sregs +
REGISTER_BYTE
(S390_PC_REGNUM),
S390_PSW_ADDR_SIZE));
}
}
retval = 1;
}
if (sregs)
*sregs = temp_sregs;
return retval;
}
void
s390_init_frame_pc_first (int next_fromleaf, struct frame_info *fi)
{
CORE_ADDR sigcaller_pc;
fi->pc = 0;
if (next_fromleaf)
{
fi->pc = ADDR_BITS_REMOVE (read_register (S390_RETADDR_REGNUM));
}
else if (fi->next && fi->next->pc)
fi->pc = s390_frame_saved_pc_nofix (fi->next);
if (fi->pc && fi->next && fi->next->frame &&
s390_is_sigreturn (fi->pc, fi->next, NULL, &sigcaller_pc))
{
fi->pc = sigcaller_pc;
}
}
void
s390_init_extra_frame_info (int fromleaf, struct frame_info *fi)
{
fi->extra_info = frame_obstack_alloc (sizeof (struct frame_extra_info));
if (fi->pc)
s390_get_frame_info (s390_sniff_pc_function_start (fi->pc, fi),
fi->extra_info, fi, 1);
else
s390_memset_extra_info (fi->extra_info);
}
void
s390_frame_init_saved_regs (struct frame_info *fi)
{
int quick;
if (fi->saved_regs == NULL)
{
frame_saved_regs_zalloc (fi);
if (fi->pc)
{
quick = (fi->extra_info && fi->extra_info->initialised
&& fi->extra_info->good_prologue);
s390_get_frame_info (quick ? fi->extra_info->function_start :
s390_sniff_pc_function_start (fi->pc, fi),
fi->extra_info, fi, !quick);
}
}
}
CORE_ADDR
s390_frame_args_address (struct frame_info *fi)
{
return fi->frame;
}
static CORE_ADDR
s390_frame_saved_pc_nofix (struct frame_info *fi)
{
if (fi->extra_info && fi->extra_info->saved_pc_valid)
return fi->extra_info->saved_pc;
if (generic_find_dummy_frame (fi->pc, fi->frame))
return generic_read_register_dummy (fi->pc, fi->frame, S390_PC_REGNUM);
s390_frame_init_saved_regs (fi);
if (fi->extra_info)
{
fi->extra_info->saved_pc_valid = 1;
if (fi->extra_info->good_prologue
&& fi->saved_regs[S390_RETADDR_REGNUM])
fi->extra_info->saved_pc
= ADDR_BITS_REMOVE (read_memory_integer
(fi->saved_regs[S390_RETADDR_REGNUM],
S390_GPR_SIZE));
else
fi->extra_info->saved_pc
= ADDR_BITS_REMOVE (read_register (S390_RETADDR_REGNUM));
return fi->extra_info->saved_pc;
}
return 0;
}
CORE_ADDR
s390_frame_saved_pc (struct frame_info *fi)
{
CORE_ADDR saved_pc = 0, sig_pc;
if (fi->extra_info && fi->extra_info->sig_fixed_saved_pc_valid)
return fi->extra_info->sig_fixed_saved_pc;
saved_pc = s390_frame_saved_pc_nofix (fi);
if (fi->extra_info)
{
fi->extra_info->sig_fixed_saved_pc_valid = 1;
if (saved_pc)
{
if (s390_is_sigreturn (saved_pc, fi, NULL, &sig_pc))
saved_pc = sig_pc;
}
fi->extra_info->sig_fixed_saved_pc = saved_pc;
}
return saved_pc;
}
CORE_ADDR
s390_frame_chain (struct frame_info *thisframe)
{
CORE_ADDR prev_fp = 0;
if (thisframe->prev && thisframe->prev->frame)
prev_fp = thisframe->prev->frame;
else if (generic_find_dummy_frame (thisframe->pc, thisframe->frame))
return generic_read_register_dummy (thisframe->pc, thisframe->frame,
S390_SP_REGNUM);
else
{
int sigreturn = 0;
CORE_ADDR sregs = 0;
struct frame_extra_info prev_fextra_info;
memset (&prev_fextra_info, 0, sizeof (prev_fextra_info));
if (thisframe->pc)
{
CORE_ADDR saved_pc, sig_pc;
saved_pc = s390_frame_saved_pc_nofix (thisframe);
if (saved_pc)
{
if ((sigreturn =
s390_is_sigreturn (saved_pc, thisframe, &sregs, &sig_pc)))
saved_pc = sig_pc;
s390_get_frame_info (s390_sniff_pc_function_start
(saved_pc, NULL), &prev_fextra_info, NULL,
1);
}
}
if (sigreturn)
{
prev_fp = read_memory_integer (sregs +
REGISTER_BYTE (S390_GP0_REGNUM +
(prev_fextra_info.
frame_pointer_saved_pc
? 11 : 15)),
S390_GPR_SIZE);
thisframe->extra_info->sigcontext = sregs;
}
else
{
if (thisframe->saved_regs)
{
int regno;
if (prev_fextra_info.frame_pointer_saved_pc
&& thisframe->saved_regs[S390_FRAME_REGNUM])
regno = S390_FRAME_REGNUM;
else
regno = S390_SP_REGNUM;
if (thisframe->saved_regs[regno])
{
if (regno == S390_SP_REGNUM)
prev_fp = thisframe->saved_regs[regno];
else
prev_fp =
read_memory_integer (thisframe->saved_regs[regno],
S390_GPR_SIZE);
}
}
}
}
return ADDR_BITS_REMOVE (prev_fp);
}
void
s390_extract_return_value (struct type *valtype, char *regbuf, char *valbuf)
{
int len = TYPE_LENGTH (valtype);
if (TYPE_CODE (valtype) == TYPE_CODE_FLT)
memcpy (valbuf, ®buf[REGISTER_BYTE (S390_FP0_REGNUM)], len);
else
{
int offset = 0;
if (TYPE_LENGTH (valtype) < S390_GPR_SIZE)
offset = S390_GPR_SIZE - TYPE_LENGTH (valtype);
memcpy (valbuf,
regbuf + REGISTER_BYTE (S390_GP0_REGNUM + 2) + offset,
TYPE_LENGTH (valtype));
}
}
static char *
s390_promote_integer_argument (struct type *valtype, char *valbuf,
char *reg_buff, int *arglen)
{
char *value = valbuf;
int len = TYPE_LENGTH (valtype);
if (len < S390_GPR_SIZE)
{
int idx, diff = S390_GPR_SIZE - len, negative =
(!TYPE_UNSIGNED (valtype) && value[0] & 0x80);
for (idx = 0; idx < S390_GPR_SIZE; idx++)
{
reg_buff[idx] = (idx < diff ? (negative ? 0xff : 0x0) :
value[idx - diff]);
}
value = reg_buff;
*arglen = S390_GPR_SIZE;
}
else
{
if (len & (S390_GPR_SIZE - 1))
{
fprintf_unfiltered (gdb_stderr,
"s390_promote_integer_argument detected an argument not "
"a multiple of S390_GPR_SIZE & greater than S390_GPR_SIZE "
"we might not deal with this correctly.\n");
}
*arglen = len;
}
return (value);
}
void
s390_store_return_value (struct type *valtype, char *valbuf)
{
int arglen;
char *reg_buff = alloca (max (S390_FPR_SIZE, REGISTER_SIZE)), *value;
if (TYPE_CODE (valtype) == TYPE_CODE_FLT)
{
if (TYPE_LENGTH (valtype) == 4
|| TYPE_LENGTH (valtype) == 8)
write_register_bytes (REGISTER_BYTE (S390_FP0_REGNUM), valbuf,
TYPE_LENGTH (valtype));
else
error ("GDB is unable to return `long double' values "
"on this architecture.");
}
else
{
value =
s390_promote_integer_argument (valtype, valbuf, reg_buff, &arglen);
write_register_bytes (REGISTER_BYTE (S390_GP0_REGNUM + 2), value,
arglen);
}
}
static int
gdb_print_insn_s390 (bfd_vma memaddr, disassemble_info * info)
{
bfd_byte instrbuff[S390_MAX_INSTR_SIZE];
int instrlen, cnt;
instrlen = s390_readinstruction (instrbuff, (CORE_ADDR) memaddr, info);
if (instrlen < 0)
{
(*info->memory_error_func) (instrlen, memaddr, info);
return -1;
}
for (cnt = 0; cnt < instrlen; cnt++)
info->fprintf_func (info->stream, "%02X ", instrbuff[cnt]);
for (cnt = instrlen; cnt < S390_MAX_INSTR_SIZE; cnt++)
info->fprintf_func (info->stream, " ");
instrlen = print_insn_s390 (memaddr, info);
return instrlen;
}
int
s390_fp_regnum ()
{
int regno = S390_SP_REGNUM;
struct frame_extra_info fextra_info;
CORE_ADDR pc = ADDR_BITS_REMOVE (read_register (S390_PC_REGNUM));
s390_get_frame_info (s390_sniff_pc_function_start (pc, NULL), &fextra_info,
NULL, 1);
if (fextra_info.frame_pointer_saved_pc)
regno = S390_FRAME_REGNUM;
return regno;
}
CORE_ADDR
s390_read_fp ()
{
return read_register (s390_fp_regnum ());
}
static void
s390_pop_frame_regular (struct frame_info *frame)
{
int regnum;
write_register (S390_PC_REGNUM, FRAME_SAVED_PC (frame));
if (frame->saved_regs)
{
for (regnum = 0; regnum < NUM_REGS; regnum++)
if (frame->saved_regs[regnum] != 0)
{
ULONGEST value;
value = read_memory_unsigned_integer (frame->saved_regs[regnum],
REGISTER_RAW_SIZE (regnum));
write_register (regnum, value);
}
write_register (S390_SP_REGNUM, frame->saved_regs[S390_SP_REGNUM]);
}
flush_cached_frames ();
}
void
s390_pop_frame ()
{
generic_pop_current_frame (s390_pop_frame_regular);
}
static int
is_integer_like (struct type *type)
{
enum type_code code = TYPE_CODE (type);
return (code == TYPE_CODE_INT
|| code == TYPE_CODE_ENUM
|| code == TYPE_CODE_RANGE
|| code == TYPE_CODE_CHAR
|| code == TYPE_CODE_BOOL);
}
static int
is_pointer_like (struct type *type)
{
enum type_code code = TYPE_CODE (type);
return (code == TYPE_CODE_PTR
|| code == TYPE_CODE_REF);
}
static int
is_float_singleton (struct type *type)
{
return (TYPE_CODE (type) == TYPE_CODE_STRUCT
&& TYPE_NFIELDS (type) == 1
&& (TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) == TYPE_CODE_FLT
|| is_float_singleton (TYPE_FIELD_TYPE (type, 0))));
}
static int
is_struct_like (struct type *type)
{
enum type_code code = TYPE_CODE (type);
return (code == TYPE_CODE_UNION
|| (code == TYPE_CODE_STRUCT && ! is_float_singleton (type)));
}
static int
is_float_like (struct type *type)
{
return (TYPE_CODE (type) == TYPE_CODE_FLT
|| is_float_singleton (type));
}
static int
is_double_or_float (struct type *type)
{
return (is_float_like (type)
&& (TYPE_LENGTH (type) == 4
|| TYPE_LENGTH (type) == 8));
}
static int
is_simple_arg (struct type *type)
{
unsigned length = TYPE_LENGTH (type);
return ((is_integer_like (type) && length <= 4)
|| is_pointer_like (type)
|| (is_struct_like (type) && length != 8)
|| (is_float_like (type) && length == 16));
}
static int
pass_by_copy_ref (struct type *type)
{
unsigned length = TYPE_LENGTH (type);
return ((is_struct_like (type) && length != 1 && length != 2 && length != 4)
|| (is_float_like (type) && length == 16));
}
static LONGEST
extend_simple_arg (struct value *arg)
{
struct type *type = VALUE_TYPE (arg);
if (TYPE_UNSIGNED (type))
return extract_unsigned_integer (VALUE_CONTENTS (arg),
TYPE_LENGTH (type));
else
return extract_signed_integer (VALUE_CONTENTS (arg),
TYPE_LENGTH (type));
}
static int
is_double_arg (struct type *type)
{
unsigned length = TYPE_LENGTH (type);
return ((is_integer_like (type)
|| is_struct_like (type))
&& length == 8);
}
static CORE_ADDR
round_up (CORE_ADDR addr, int n)
{
gdb_assert (n && (n & (n-1)) == 0);
return ((addr + n - 1) & -n);
}
static CORE_ADDR
round_down (CORE_ADDR addr, int n)
{
gdb_assert (n && (n & (n-1)) == 0);
return (addr & -n);
}
static int
alignment_of (struct type *type)
{
int alignment;
if (is_integer_like (type)
|| is_pointer_like (type)
|| TYPE_CODE (type) == TYPE_CODE_FLT)
alignment = TYPE_LENGTH (type);
else if (TYPE_CODE (type) == TYPE_CODE_STRUCT
|| TYPE_CODE (type) == TYPE_CODE_UNION)
{
int i;
alignment = 1;
for (i = 0; i < TYPE_NFIELDS (type); i++)
{
int field_alignment = alignment_of (TYPE_FIELD_TYPE (type, i));
if (field_alignment > alignment)
alignment = field_alignment;
}
}
else
alignment = 1;
gdb_assert ((alignment & (alignment - 1)) == 0);
return alignment;
}
CORE_ADDR
s390_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
int struct_return, CORE_ADDR struct_addr)
{
int i;
int pointer_size = (TARGET_PTR_BIT / TARGET_CHAR_BIT);
int num_copies;
CORE_ADDR *copy_addr = alloca (nargs * sizeof (CORE_ADDR));
num_copies = 0;
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *type = VALUE_TYPE (arg);
unsigned length = TYPE_LENGTH (type);
if (is_simple_arg (type)
&& pass_by_copy_ref (type))
{
sp -= length;
sp = round_down (sp, alignment_of (type));
write_memory (sp, VALUE_CONTENTS (arg), length);
copy_addr[i] = sp;
num_copies++;
}
}
{
int i;
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *type = VALUE_TYPE (arg);
int length = TYPE_LENGTH (type);
sp = round_down (sp, alignment_of (type));
if (length < 4) length = 4;
sp -= length;
}
}
sp = round_down (sp, pointer_size);
sp -= num_copies * pointer_size;
sp = round_down (sp, 8);
{
int fr = 0;
int gr = 2;
CORE_ADDR starg = sp;
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *type = VALUE_TYPE (arg);
if (is_double_or_float (type)
&& fr <= 2)
{
write_register_bytes (REGISTER_BYTE (S390_FP0_REGNUM + fr),
VALUE_CONTENTS (arg),
TYPE_LENGTH (type));
fr += 2;
}
else if (is_simple_arg (type)
&& gr <= 6)
{
if (pass_by_copy_ref (type))
write_register (S390_GP0_REGNUM + gr, copy_addr[i]);
else
write_register (S390_GP0_REGNUM + gr, extend_simple_arg (arg));
gr++;
}
else if (is_double_arg (type)
&& gr <= 5)
{
write_register_gen (S390_GP0_REGNUM + gr,
VALUE_CONTENTS (arg));
write_register_gen (S390_GP0_REGNUM + gr + 1,
VALUE_CONTENTS (arg) + 4);
gr += 2;
}
else
{
enum type_code code = TYPE_CODE (type);
unsigned length = TYPE_LENGTH (type);
if (is_double_arg (type) && gr == 6)
gr = 7;
if (is_simple_arg (type))
{
starg = round_up (starg, 4);
if (pass_by_copy_ref (type))
write_memory_signed_integer (starg, pointer_size,
copy_addr[i]);
else
write_memory_signed_integer (starg, 4,
extend_simple_arg (arg));
starg += 4;
}
else
{
starg = round_up (starg, 4);
write_memory (starg, VALUE_CONTENTS (arg), length);
starg += length;
}
}
}
}
sp -= 96;
write_memory_unsigned_integer (sp, (TARGET_PTR_BIT / TARGET_CHAR_BIT),
read_fp ());
return sp;
}
static int
s390_use_struct_convention (int gcc_p, struct type *value_type)
{
enum type_code code = TYPE_CODE (value_type);
return (code == TYPE_CODE_STRUCT
|| code == TYPE_CODE_UNION);
}
struct type *
s390_register_virtual_type (int regno)
{
if (S390_FP0_REGNUM <= regno && regno < S390_FP0_REGNUM + S390_NUM_FPRS)
return builtin_type_double;
else
return builtin_type_int;
}
struct type *
s390x_register_virtual_type (int regno)
{
return (regno == S390_FPC_REGNUM) ||
(regno >= S390_FIRST_ACR && regno <= S390_LAST_ACR) ? builtin_type_int :
(regno >= S390_FP0_REGNUM) ? builtin_type_double : builtin_type_long;
}
void
s390_store_struct_return (CORE_ADDR addr, CORE_ADDR sp)
{
write_register (S390_GP0_REGNUM + 2, addr);
}
static unsigned char *
s390_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
{
static unsigned char breakpoint[] = { 0x0, 0x1 };
*lenptr = sizeof (breakpoint);
return breakpoint;
}
CORE_ADDR
s390_skip_prologue (CORE_ADDR pc)
{
struct frame_extra_info fextra_info;
s390_get_frame_info (pc, &fextra_info, NULL, 1);
return fextra_info.skip_prologue_function_start;
}
CORE_ADDR
s390_saved_pc_after_call (struct frame_info *frame)
{
return ADDR_BITS_REMOVE (read_register (S390_RETADDR_REGNUM));
}
static CORE_ADDR
s390_addr_bits_remove (CORE_ADDR addr)
{
return (addr) & 0x7fffffff;
}
static CORE_ADDR
s390_push_return_address (CORE_ADDR pc, CORE_ADDR sp)
{
write_register (S390_RETADDR_REGNUM, CALL_DUMMY_ADDRESS ());
return sp;
}
struct gdbarch *
s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
static LONGEST s390_call_dummy_words[] = { 0 };
struct gdbarch *gdbarch;
struct gdbarch_tdep *tdep;
int elf_flags;
arches = gdbarch_list_lookup_by_info (arches, &info);
if (arches != NULL)
return arches->gdbarch;
if (info.bfd_arch_info->arch != bfd_arch_s390)
return NULL;
gdbarch = gdbarch_alloc (&info, NULL);
set_gdbarch_believe_pcc_promotion (gdbarch, 0);
set_gdbarch_char_signed (gdbarch, 0);
set_gdbarch_frame_args_skip (gdbarch, 0);
set_gdbarch_frame_args_address (gdbarch, s390_frame_args_address);
set_gdbarch_frame_chain (gdbarch, s390_frame_chain);
set_gdbarch_frame_init_saved_regs (gdbarch, s390_frame_init_saved_regs);
set_gdbarch_frame_locals_address (gdbarch, s390_frame_args_address);
set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown);
set_gdbarch_store_struct_return (gdbarch, s390_store_struct_return);
set_gdbarch_extract_return_value (gdbarch, s390_extract_return_value);
set_gdbarch_store_return_value (gdbarch, s390_store_return_value);
set_gdbarch_decr_pc_after_break (gdbarch, 2);
set_gdbarch_pop_frame (gdbarch, s390_pop_frame);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_function_start_offset (gdbarch, 0);
set_gdbarch_max_register_raw_size (gdbarch, 8);
set_gdbarch_max_register_virtual_size (gdbarch, 8);
set_gdbarch_breakpoint_from_pc (gdbarch, s390_breakpoint_from_pc);
set_gdbarch_skip_prologue (gdbarch, s390_skip_prologue);
set_gdbarch_init_extra_frame_info (gdbarch, s390_init_extra_frame_info);
set_gdbarch_init_frame_pc_first (gdbarch, s390_init_frame_pc_first);
set_gdbarch_read_fp (gdbarch, s390_read_fp);
set_gdbarch_frameless_function_invocation (gdbarch,
s390_frameless_function_invocation);
set_gdbarch_frame_saved_pc (gdbarch, s390_frame_saved_pc);
set_gdbarch_frame_chain (gdbarch, s390_frame_chain);
set_gdbarch_saved_pc_after_call (gdbarch, s390_saved_pc_after_call);
set_gdbarch_register_byte (gdbarch, s390_register_byte);
set_gdbarch_pc_regnum (gdbarch, S390_PC_REGNUM);
set_gdbarch_sp_regnum (gdbarch, S390_SP_REGNUM);
set_gdbarch_fp_regnum (gdbarch, S390_FP_REGNUM);
set_gdbarch_fp0_regnum (gdbarch, S390_FP0_REGNUM);
set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
set_gdbarch_cannot_fetch_register (gdbarch, s390_cannot_fetch_register);
set_gdbarch_cannot_store_register (gdbarch, s390_cannot_fetch_register);
set_gdbarch_get_saved_register (gdbarch, generic_get_saved_register);
set_gdbarch_use_struct_convention (gdbarch, s390_use_struct_convention);
set_gdbarch_frame_chain_valid (gdbarch, func_frame_chain_valid);
set_gdbarch_register_name (gdbarch, s390_register_name);
set_gdbarch_stab_reg_to_regnum (gdbarch, s390_stab_reg_to_regnum);
set_gdbarch_dwarf_reg_to_regnum (gdbarch, s390_stab_reg_to_regnum);
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, s390_stab_reg_to_regnum);
set_gdbarch_extract_struct_value_address
(gdbarch, generic_cannot_extract_struct_value_address);
set_gdbarch_call_dummy_p (gdbarch, 1);
set_gdbarch_use_generic_dummy_frames (gdbarch, 1);
set_gdbarch_call_dummy_length (gdbarch, 0);
set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT);
set_gdbarch_call_dummy_address (gdbarch, entry_point_address);
set_gdbarch_call_dummy_start_offset (gdbarch, 0);
set_gdbarch_pc_in_call_dummy (gdbarch, pc_in_call_dummy_at_entry_point);
set_gdbarch_push_dummy_frame (gdbarch, generic_push_dummy_frame);
set_gdbarch_push_arguments (gdbarch, s390_push_arguments);
set_gdbarch_save_dummy_frame_tos (gdbarch, generic_save_dummy_frame_tos);
set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 1);
set_gdbarch_call_dummy_breakpoint_offset (gdbarch, 0);
set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0);
set_gdbarch_fix_call_dummy (gdbarch, generic_fix_call_dummy);
set_gdbarch_push_return_address (gdbarch, s390_push_return_address);
set_gdbarch_sizeof_call_dummy_words (gdbarch,
sizeof (s390_call_dummy_words));
set_gdbarch_call_dummy_words (gdbarch, s390_call_dummy_words);
set_gdbarch_coerce_float_to_double (gdbarch,
standard_coerce_float_to_double);
switch (info.bfd_arch_info->mach)
{
case bfd_mach_s390_31:
set_gdbarch_register_size (gdbarch, 4);
set_gdbarch_register_raw_size (gdbarch, s390_register_raw_size);
set_gdbarch_register_virtual_size (gdbarch, s390_register_raw_size);
set_gdbarch_register_virtual_type (gdbarch, s390_register_virtual_type);
set_gdbarch_addr_bits_remove (gdbarch, s390_addr_bits_remove);
set_gdbarch_register_bytes (gdbarch, S390_REGISTER_BYTES);
break;
case bfd_mach_s390_64:
set_gdbarch_register_size (gdbarch, 8);
set_gdbarch_register_raw_size (gdbarch, s390x_register_raw_size);
set_gdbarch_register_virtual_size (gdbarch, s390x_register_raw_size);
set_gdbarch_register_virtual_type (gdbarch,
s390x_register_virtual_type);
set_gdbarch_long_bit (gdbarch, 64);
set_gdbarch_long_long_bit (gdbarch, 64);
set_gdbarch_ptr_bit (gdbarch, 64);
set_gdbarch_register_bytes (gdbarch, S390X_REGISTER_BYTES);
break;
}
return gdbarch;
}
void
_initialize_s390_tdep ()
{
register_gdbarch_init (bfd_arch_s390, s390_gdbarch_init);
if (!tm_print_insn)
tm_print_insn = gdb_print_insn_s390;
}
#endif