From: John Gilmore Date: Tue, 12 Nov 1991 15:50:47 +0000 (+0000) Subject: * rs6000-pinsn.c, rs6000-tdep.c, rs6000-xdep.c, tm-rs6000.h, X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=41abdfbd2de07837ba8088092765154eaa66351d;p=binutils-gdb.git * rs6000-pinsn.c, rs6000-tdep.c, rs6000-xdep.c, tm-rs6000.h, xm-rs6000.h: New files. * xcoffexec.c: New file for handling AIX shared libraries. --- diff --git a/gdb/rs6000-pinsn.c b/gdb/rs6000-pinsn.c new file mode 100644 index 00000000000..d1e239906a6 --- /dev/null +++ b/gdb/rs6000-pinsn.c @@ -0,0 +1,377 @@ +/* Print rs6000 instructions for objdump. + This file is part of the binutils. +*/ + + +#include +#include "defs.h" +#include "rs6k-opcode.h" + + +/* Print the rs6k instruction at address MEMADDR in debugged memory, + on STREAM. Returns length of the instruction, in bytes. */ + +int +print_insn (memaddr, stream) + CORE_ADDR memaddr; + FILE *stream; +{ + int pop, eop; /* primary and extended opcodes */ + int min, max; + int best = -1; /* found best opcode index */ + int oldbest = -1; + unsigned int the_insn; + + read_memory (memaddr, &the_insn, sizeof (the_insn)); + pop = (unsigned)(the_insn >> 26); + eop = ((the_insn) >> 1) & 0x3ff; + min = 0, max = NOPCODES-1; + + while (min < max) { + best = (min + max) / 2; + + /* see if we are running in loops */ + if (best == oldbest) + goto not_found; + oldbest = best; + + if (pop < rs6k_ops [best].p_opcode) + max = best; + + else if (pop > rs6k_ops [best].p_opcode) + min = best; + + else { + /* opcode matched, check extended opcode. */ + + if (rs6k_ops [best].e_opcode == -1) { + /* there is no valid extended opcode, what we've got is + just fine. */ + goto insn_found; + } + + else if (eop < rs6k_ops [best].e_opcode) { + + while (pop == rs6k_ops [best].p_opcode) { + if (eop == rs6k_ops [best].e_opcode) /* found it! */ + goto insn_found; + --best; + } + goto not_found; + } + + else if (eop > rs6k_ops [best].e_opcode) { + + while (pop == rs6k_ops [best].p_opcode) { + if (eop == rs6k_ops [best].e_opcode) /* found it! */ + goto insn_found; + ++best; + } + goto not_found; + } + + else /* eop == rs6k_ops [best].e_opcode */ + goto insn_found; + } + } + + best = min; + if (pop == rs6k_ops [best].p_opcode && + (rs6k_ops [best].e_opcode == -1 || rs6k_ops [best].e_opcode == eop)) + goto insn_found; + + else + goto not_found; + + +insn_found: + print_operator (stream, memaddr, the_insn, best); + return 4; + +not_found: + fprintf (stream, "0x%08x", the_insn); + return 4; +} + + + +/* condition code names */ +static char *cond_code [] = { + "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", "nl", "ng", "z", "nz" }; + + +print_operator (stream, memaddr, insn_word, insn_no) +FILE *stream; +long memaddr; +long insn_word; +int insn_no; +{ + char buf [BUFSIZ]; + char *qq = buf; + char *pp = rs6k_ops[insn_no].opr_ext; + int tmp; + int nocomma = 0; /* true if no comma needed */ + + *qq = '\0'; + if (pp) { + while (*pp) { + + switch ( *pp ) { + case '.': + if (insn_word & 0x1) + *qq++ = '.'; + break; + + case 'l': + if (insn_word & 0x1) + *qq++ = 'l'; + break; + + case 't': + if ((insn_word & 0x03e00000) == 0x01800000) + *qq++ = 't'; + break; + + case 'f': + if ((insn_word & 0x03e00000) == 0x00800000) + *qq++ = 'f'; + break; + + case 'a': + if (insn_word & 0x2) + *qq++ = 'a'; + break; + + case 'o': + if (insn_word & 0x4000) + *qq++ = 'o'; + break; + + case '1': /* exception #1 for bb/bc ambiguity */ + tmp = (insn_word >> 21) & 0x1f; /* extract BO */ + if (tmp != 0xc && tmp != 0x4) { + /* you can't use `bb' now. switch to `bc' */ + *(qq-1) = 'c'; + ++insn_no; + pp = rs6k_ops[insn_no].opr_ext; + continue; + } + break; + + default: + abort (); + } + ++pp; + } + } + + /* tab between orerator and operand */ + *qq++ = '\t'; + + /* parse the operand now. */ + pp = rs6k_ops[insn_no].oprnd_format; + + while (1) { + switch (*pp) { + case TO : + sprintf (qq, "%d", (insn_word >> 21) & 0x1f); + break; + + case RT : + case RS : + sprintf (qq, "r%d", (insn_word >> 21) & 0x1f); + break; + + case LI : + tmp = (insn_word >> 16) & 0x1f; + if (tmp > 11) { + fprintf (stderr, "Internal error: unknown cond code: 0x%x\n", insn_word); + tmp = 0; + } + sprintf (qq, "%s", cond_code [tmp]); + break; + +#if 0 + case A2 : + tmp = (insn_word >> 2) & 0x3fff; + if (tmp & 0x2000) + tmp -= 0x4000; + sprintf (qq, "0x%x", tmp * 4 + memaddr); + break; +#endif + case A2 : + case TA14 : + tmp = (insn_word & 0xfffc); + if (tmp & 0x8000) /* fix sign extension */ + tmp -= 0x10000; + + if ((insn_word & 0x2) == 0) /* if AA not set */ + tmp += memaddr; + + sprintf (qq, "0x%x", tmp); + break; + + case TA24 : + tmp = insn_word & 0x03fffffc; + if (tmp & 0x2000000) + tmp -= 0x4000000; + + if ((insn_word & 0x2) == 0) /* if no AA bit set */ + tmp += memaddr; + + sprintf (qq, "0x%x", tmp); + break; + + case LEV : /* for svc only */ + if (insn_word & 0x2) { /* SA is set */ + nocomma = 1; + *qq = '\0'; + } + else + sprintf (qq, "%d", (insn_word >> 5) & 0x7f); + break; + + case FL1 : /* for svc only */ + if (insn_word & 0x2) { /* SA is set */ + nocomma = 1; + *qq = '\0'; + } + else + sprintf (qq, "%d", (insn_word >> 12) & 0xf); + break; + + case FL2 : /* for svc only */ + nocomma = 0; + if (insn_word & 0x2) /* SA is set */ + sprintf (qq, "%d", (insn_word >> 2) & 0x3fff); + else + sprintf (qq, "%d", (insn_word >> 2) & 0x7); + break; + + case RA : + if (nocomma) { + sprintf (qq, "r%d)", (insn_word >> 16) & 0x1f); + nocomma = 0; + } + else + sprintf (qq, "r%d", (insn_word >> 16) & 0x1f); + break; + + case RB : + sprintf (qq, "r%d", (insn_word >> 11) & 0x1f); + break; + + case SI : + tmp = insn_word & 0xffff; + if (tmp & 0x8000) + tmp -= 0x10000; + sprintf (qq, "%d", tmp); + break; + + case UI : + sprintf (qq, "%d", insn_word & 0xffff); + break; + + case BF : + sprintf (qq, "%d", (insn_word >> 23) & 0x7); + break; + + case BFA : + sprintf (qq, "%d", (insn_word >> 18) & 0x7); + break; + + case BT : + sprintf (qq, "%d", (insn_word >> 21) & 0x1f); + break; + + case BA : + sprintf (qq, "%d", (insn_word >> 16) & 0x1f); + break; + + case BB : + sprintf (qq, "%d", (insn_word >> 11) & 0x1f); + break; + + case BO : + sprintf (qq, "%d", (insn_word >> 21) & 0x1f); + break; + + case BI : + sprintf (qq, "%d", (insn_word >> 16) & 0x1f); + break; + + case SH : + sprintf (qq, "%d", (insn_word >> 11) & 0x1f); + break; + + case MB : + sprintf (qq, "0x%x", (insn_word >> 6) & 0x1f); + break; + + case ME : + sprintf (qq, "0x%x", (insn_word >> 1) & 0x1f); + break; + + case SPR : + sprintf (qq, "%d", (insn_word >> 16) & 0x1f); + break; + + case DIS : + nocomma = 1; + tmp = insn_word & 0xffff; + if (tmp & 0x8000) + tmp -= 0x10000; + sprintf (qq, "%d(", tmp); + break; + + case FXM : + sprintf (qq, "0x%x", (insn_word >> 12) & 0xff); + break; + + case FRT : + case FRS : + sprintf (qq, "f%d", (insn_word >> 21) & 0x1f); + break; + + case FRA : + sprintf (qq, "f%d", (insn_word >> 16) & 0x1f); + break; + + case FRB : + sprintf (qq, "f%d", (insn_word >> 11) & 0x1f); + break; + + case FRC : + sprintf (qq, "f%d", (insn_word >> 6) & 0x1f); + break; + + case FLM : + sprintf (qq, "0x%x", (insn_word >> 17) & 0xff); + break; + + case NB : + sprintf (qq, "%d", (insn_word >> 11) & 0x1f); + break; + + case I : + sprintf (qq, "%d", (insn_word >> 12) & 0xf); + break; + + default : + sprintf (qq, "Unknown operand format identifier????"); + abort (); + } + while (*qq) ++qq; + ++pp; + + if (*pp == '\0') + break; + else if (!nocomma) + *qq++ = ','; + } + *qq = '\0'; + + fprintf (stream, "0x%08x\t%s%s", + insn_word, rs6k_ops[insn_no].operator, buf); +} + diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c new file mode 100644 index 00000000000..4003de07daa --- /dev/null +++ b/gdb/rs6000-tdep.c @@ -0,0 +1,975 @@ +/* Target-dependent code for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989, 1991 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include + +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "symtab.h" +#include "target.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +extern int errno; +extern int attach_flag; + +/* Nonzero if we just simulated a single step break. */ +int one_stepped; + +#if 0 + +/* This is Damon's implementation of single step simulation. It suffers the + following program: + + 1 main () { + 2 char buf[10]; + 3 puts ("test"); + 4 strcmp (buf, "test"); puts ("test"); + 5 exit (0); + 6 } + + You cannot `next' on line 4 in the above program. gdb puts a breakpoint + to the return address of `strcmp', and when execution arrives that point, + it is still in the line range and gdb attemps to resume it with single + steps. At that point the breakpoint at step_resume_break_address (return + address of strcmp) and single step's breakpoint mixes up and we end up + with a breakpoint which its shadow and itself are identical. + + Fix that problem and use this version. FIXMEmgo. +*/ + + +static struct sstep_breaks { + int address; + int data; +} tbreak[2]; + + +/* + * branch_dest - calculate all places the current instruction may go + */ +static +branch_dest(tb) + register struct sstep_breaks *tb; +{ + register ulong opcode, iar; + long instr; + int immediate, absolute;; + + iar = read_pc(); /* current IAR */ + target_read_memory(iar, &instr, sizeof (instr)); /* current inst */ + + opcode = instr >> 26; + absolute = instr & 2; + + tb[1].address = -1; + + switch (opcode) { + case 0x10: /* branch conditional */ + immediate = ((instr & ~3) << 16) >> 16; + + /* + * two possible locations for next instruction + */ + tb[0].address = iar + 4; + tb[1].address = immediate + (absolute ? 0 : iar); + + break; + + case 0x12: /* branch unconditional */ + immediate = ((instr & ~3) << 6) >> 6; + + /* + * only one possible location for next instr + */ + tb[0].address = immediate + (absolute ? 0 : iar); + + break; + + case 0x13: /* branch conditional register */ + /* + * WE NEED TO CHECK THE CR HERE, TO SEE IF THIS IS + * REALLY UNCONDITIONAL. + */ + tb++->address = iar + 4; + + switch ((instr >> 1) & 0x3ff) { + case 0x10: /* branch conditional register */ + tb->address = read_register(LR_REGNUM) & ~3; + sigtramp_chk(tb); /* return from sig handler? */ + break; + + case 0x210: /* branch cond to CTR */ + tb->address = read_register(CTR_REGNUM) & ~3; + sigtramp_chk(tb); /* return from sig handler? */ + break; + + default: + /* + * not a branch. + */ + tb->address = iar + 4; + break; + } + break; + + default: + /* + * not a branch, flow proceeds normally + */ + tb->address = iar + 4; + break; + } +} + +/* + * sigtramp_chk - heuristic check to see if we think we are returning + * from a signal handler. + * + * Input: + * tb - ^ to a single step branch location + * + * Note: + * When we are at the "br" instruction returning to a signal handler, + * we return in user mode to an address in the kernel. If the + * segment of the branch target is 0, we may very well be in a + * signal handler. From scrounging through this code, we note that + * register 29 has the signal context pointer, from which we can + * determine where we will end up next. + */ +sigtramp_chk(tb) +register struct sstep_breaks *tb; { + struct sigcontext sc; + + if (tb->address & 0xf0000000) + return; /* can't have been sigtramp */ + + if (target_read_memory(read_register(GPR29), &sc, sizeof (sc))) + return; /* read fails, heuristic fails */ + + if ((sc.sc_jmpbuf.jmp_context.iar & 0xf0000000) == 0x10000000) { + /* + * looks like it might be ok..... + */ + tb->address = sc.sc_jmpbuf.jmp_context.iar; + } +} + + +/* + * single_step - no trace mode harware support, or software support. + * sigh. + */ +single_step(signal) { + register i; + + if (!one_stepped) { + /* + * need to set breakpoints for single step. + * figure out all places the current instruction could go. + */ + branch_dest(&tbreak[0]); + + /* + * always at least one place to go to + */ + target_insert_breakpoint(tbreak[0].address, &tbreak[0].data); + + /* + * if there is another possible location, set a breakpoint there + * as well. + */ + if (tbreak[1].address != -1) + target_insert_breakpoint(tbreak[1].address, &tbreak[1].data); + + one_stepped = 1; + ptrace(PT_CONTINUE, inferior_pid, 1, signal, 0); + } else { + /* + * need to clear the breakpoints. + */ + for (i = 0; i < 2; ++i) + if (tbreak[i].address != -1) + target_remove_breakpoint(tbreak[i].address, &tbreak[i].data); + + one_stepped = 0; + } + + return 1; +} + +#else /* !DAMON'S VERSION */ + +/* Breakpoint shadows for the single step instructions will be kept here. */ + +static struct sstep_breaks { + int address; + int data; +} stepBreaks[2]; + + +/* + * Calculate the destination of a branch/jump. Return -1 if not a branch. + */ +static int +branch_dest (opcode, instr, pc, safety) + int opcode, instr, pc, safety; +{ + register long offset; + unsigned dest; + int immediate; + int absolute; + int ext_op; + + absolute = (int) ((instr >> 1) & 1); + + switch (opcode) { + case 18 : + immediate = ((instr & ~3) << 6) >> 6; /* br unconditionl */ + + case 16 : + if (opcode != 18) /* br conditional */ + immediate = ((instr & ~3) << 16) >> 16; + if (absolute) + dest = immediate; + else + dest = pc + immediate; + break; + + case 19 : + ext_op = (instr>>1) & 0x3ff; + + if (ext_op == 16) /* br conditional register */ + dest = read_register (LR_REGNUM) & ~3; + + else if (ext_op == 528) /* br cond to count reg */ + dest = read_register (CTR_REGNUM) & ~3; + + else return -1; + break; + + default: return -1; + } + return (dest < 0x10000000) ? safety : dest; +} + + + +/* AIX does not support PT_STEP. Simulate it. */ + +int +single_step (signal) +int signal; +{ +#define INSNLEN(OPCODE) 4 + + static char breakp[] = BREAKPOINT; + int ii, insn, ret, loc; + int breaks[2], opcode; + + if (!one_stepped) { + extern CORE_ADDR text_start; + loc = read_pc (); + + ret = read_memory (loc, &insn, sizeof (int)); + if (ret) + printf ("Error in single_step()!!\n"); + + breaks[0] = loc + INSNLEN(insn); + opcode = insn >> 26; + breaks[1] = branch_dest (opcode, insn, loc, breaks[0]); + + stepBreaks[1].address = -1; + + for (ii=0; ii < 2; ++ii) { + + /* ignore invalid breakpoint. */ + if ( breaks[ii] == -1) + continue; + + read_memory (breaks[ii], &(stepBreaks[ii].data), sizeof(int)); + + ret = write_memory (breaks[ii], breakp, sizeof(int)); + stepBreaks[ii].address = breaks[ii]; + } + + one_stepped = 1; + ptrace (PT_CONTINUE, inferior_pid, 1, signal); + } + else { + + /* remove step breakpoints. */ + for (ii=0; ii < 2; ++ii) + if (stepBreaks[ii].address != -1) + write_memory + (stepBreaks[ii].address, &(stepBreaks[ii].data), sizeof(int)); + + one_stepped = 0; + } + return 1; +} +#endif /* !DAMON's version of single step. */ + + + +/* return pc value after skipping a function prologue. */ + +skip_prologue (pc) +int pc; +{ + unsigned int tmp; + unsigned int op; + + if (target_read_memory (pc, (char *)&op, sizeof (op))) + return pc; /* Can't access it -- assume no prologue. */ + SWAP_TARGET_AND_HOST (&op, sizeof (op)); + + /* Assume that subsequent fetches can fail with low probability. */ + + if (op == 0x7c0802a6) { /* mflr r0 */ + pc += 4; + op = read_memory_integer (pc, 4); + } + else /* else, this is a frameless invocation */ + return pc; + + if ((op & 0xfc00003e) == 0x7c000026) { /* mfcr Rx */ + pc += 4; + op = read_memory_integer (pc, 4); + } + + if ((op & 0xfc000000) == 0x48000000) { /* bl foo, to save fprs??? */ + pc += 4; + op = read_memory_integer (pc, 4); + } + + if ((op & 0xfc1f0000) == 0xd8010000) { /* stfd Rx,NUM(r1) */ + pc += 4; /* store floating register double */ + op = read_memory_integer (pc, 4); + } + + if ((op & 0xfc1f0000) == 0xbc010000) { /* stm Rx, NUM(r1) */ + pc += 4; + op = read_memory_integer (pc, 4); + } + + while (((tmp = op >> 16) == 0x9001) || /* st r0, NUM(r1) */ + (tmp == 0x9421) || /* stu r1, NUM(r1) */ + (op == 0x93e1fffc)) /* st r31,-4(r1) */ + { + pc += 4; + op = read_memory_integer (pc, 4); + } + + while ((tmp = (op >> 22)) == 0x20f) { /* l r31, ... or */ + pc += 4; /* l r30, ... */ + op = read_memory_integer (pc, 4); + } + + while ((op & 0xfc1f0000) == 0x90010000) { /* st r?, NUM(r1) */ + pc += 4; + op = read_memory_integer (pc, 4); + } + + if (op == 0x603f0000) { /* oril r31, r1, 0x0 */ + pc += 4; /* this happens if r31 is used as */ + op = read_memory_integer (pc, 4); /* frame ptr. (gcc does that) */ + + if ((op >> 16) == 0x907f) { /* st r3, NUM(r31) */ + pc += 4; + op = read_memory_integer (pc, 4); + } + } + return pc; +} + +/* text start and end addresses in virtual memory. */ + +CORE_ADDR text_start; +CORE_ADDR text_end; + + +/************************************************************************* + Support for creating pushind a dummy frame into the stack, and popping + frames, etc. +*************************************************************************/ + +#define DUMMY_FRAME_ADDR_SIZE 10 + +/* Make sure you initialize these in somewhere, in case gdb gives up what it + was debugging and starts debugging something else. FIXMEmgo */ + +static int dummy_frame_count = 0; +static int dummy_frame_size = 0; +static CORE_ADDR *dummy_frame_addr = 0; + +extern int stop_stack_dummy; + +/* push a dummy frame into stack, save all register. Currently we are saving + only gpr's and fpr's, which is not good enough! FIXMEmgo */ + +push_dummy_frame () +{ + int sp, pc; /* stack pointer and link register */ + int ii; + + if (dummy_frame_count >= dummy_frame_size) { + dummy_frame_size += DUMMY_FRAME_ADDR_SIZE; + if (dummy_frame_addr) + dummy_frame_addr = (CORE_ADDR*) xrealloc + (dummy_frame_addr, sizeof(CORE_ADDR) * (dummy_frame_size)); + else + dummy_frame_addr = (CORE_ADDR*) + xmalloc (sizeof(CORE_ADDR) * (dummy_frame_size)); + } + + sp = read_register(SP_REGNUM); + pc = read_register(PC_REGNUM); + + dummy_frame_addr [dummy_frame_count++] = sp; + + /* Be careful! If the stack pointer is not decremented first, then kernel + thinks he is free to use the sapce underneath it. And kernel actually + uses that area for IPC purposes when executing ptrace(2) calls. So + before writing register values into the new frame, decrement and update + %sp first in order to secure your frame. */ + + write_register (SP_REGNUM, sp-408); + +#if 1 + /* gdb relies on the state of current_frame. We'd better update it, + otherwise things like do_registers_info() wouldn't work properly! */ + + flush_cached_frames (); + set_current_frame (create_new_frame (sp-408, pc)); +#endif /* 0 */ + + /* save program counter in link register's space. */ + write_memory (sp+8, &pc, 4); + + /* save full floating point registers here. They will be from F14..F31 + for know. I am not sure if we need to save everything here! */ + + /* fpr's, f0..f31 */ + for (ii = 0; ii < 32; ++ii) + write_memory (sp-8-(ii*8), ®isters[REGISTER_BYTE (31-ii+FP0_REGNUM)], 8); + + /* gpr's r0..r31 */ + for (ii=1; ii <=32; ++ii) + write_memory (sp-256-(ii*4), ®isters[REGISTER_BYTE (32-ii)], 4); + + /* so far, 32*2 + 32 words = 384 bytes have been written. We need 6 words + (24 bytes) for the rest of the registers. It brings the total to 408 + bytes. + save sp or so call back chain right here. */ + write_memory (sp-408, &sp, 4); + sp -= 408; + + /* And finally, this is the back chain. */ + write_memory (sp+8, &pc, 4); +} + + +/* Pop a dummy frame. + + In rs6000 when we push a dummy frame, we save all of the registers. This + is usually done before user calls a function explicitly. + + After a dummy frame is pushed, some instructions are copied into stack, and + stack pointer is decremented even more. Since we don't have a frame pointer to + get back to the parent frame of the dummy, we start having trouble poping it. + Therefore, we keep a dummy frame stack, keeping addresses of dummy frames as + such. When poping happens and when we detect that was a dummy frame, we pop + it back to its parent by using dummy frame stack (`dummy_frame_addr' array). + */ + +pop_dummy_frame () +{ + CORE_ADDR sp, pc; + int ii; + sp = dummy_frame_addr [--dummy_frame_count]; + + /* restore all fpr's. */ + for (ii = 1; ii <= 32; ++ii) + read_memory (sp-(ii*8), ®isters[REGISTER_BYTE (32-ii+FP0_REGNUM)], 8); + + /* restore all gpr's */ + for (ii=1; ii <= 32; ++ii) { + read_memory (sp-256-(ii*4), ®isters[REGISTER_BYTE (32-ii)], 4); + } + + read_memory (sp-400, ®isters [REGISTER_BYTE(PC_REGNUM)], 4); + + /* when a dummy frame was being pushed, we had to decrement %sp first, in + order to secure astack space. Thus, saved %sp (or %r1) value, is not the + one we should restore. Change it with the one we need. */ + + *(int*)®isters [REGISTER_BYTE(FP_REGNUM)] = sp; + + /* Now we can restore all registers. */ + + store_inferior_registers (-1); + pc = read_pc (); + flush_cached_frames (); + set_current_frame (create_new_frame (sp, pc)); +} + + +/* pop the innermost frame, go back to the caller. */ + +pop_frame () +{ + int pc, lr, sp, prev_sp; /* %pc, %lr, %sp */ + FRAME fr = get_current_frame (); + int offset = 0; + int frameless = 0; /* TRUE if function is frameless */ + int addr, ii; + int saved_gpr, saved_fpr; /* # of saved gpr's and fpr's */ + + pc = read_pc (); + sp = FRAME_FP (fr); + + if (stop_stack_dummy && dummy_frame_count) { + pop_dummy_frame (); + return; + } + + /* figure out previous %pc value. If the function is frameless, it is + still in the link register, otherwise walk the frames and retrieve the + saved %pc value in the previous frame. */ + + addr = get_pc_function_start (fr->pc) + FUNCTION_START_OFFSET; + function_frame_info (addr, &frameless, &offset, &saved_gpr, &saved_fpr); + + read_memory (sp, &prev_sp, 4); + if (frameless) + lr = read_register (LR_REGNUM); + else + read_memory (prev_sp+8, &lr, 4); + + /* reset %pc value. */ + write_register (PC_REGNUM, lr); + + /* reset register values if any was saved earlier. */ + addr = prev_sp - offset; + + if (saved_gpr != -1) + for (ii=saved_gpr; ii <= 31; ++ii) { + read_memory (addr, ®isters [REGISTER_BYTE (ii)], 4); + addr += sizeof (int); + } + + if (saved_fpr != -1) + for (ii=saved_fpr; ii <= 31; ++ii) { + read_memory (addr, ®isters [REGISTER_BYTE (ii+FP0_REGNUM)], 8); + addr += 8; + } + + write_register (SP_REGNUM, prev_sp); + store_inferior_registers (-1); + flush_cached_frames (); + set_current_frame (create_new_frame (prev_sp, lr)); +} + + +/* fixup the call sequence of a dummy function, with the real function address. + its argumets will be passed by gdb. */ + +fix_call_dummy(dummyname, pc, fun, nargs, type) + char *dummyname; + int pc; + int fun; + int nargs; /* not used */ + int type; /* not used */ + +{ +#define TOC_ADDR_OFFSET 20 +#define TARGET_ADDR_OFFSET 28 + + int ii; + unsigned long target_addr; + unsigned long tocvalue; + + target_addr = fun; + tocvalue = find_toc_address (target_addr); + + ii = *(int*)((char*)dummyname + TOC_ADDR_OFFSET); + ii = (ii & 0xffff0000) | (tocvalue >> 16); + *(int*)((char*)dummyname + TOC_ADDR_OFFSET) = ii; + + ii = *(int*)((char*)dummyname + TOC_ADDR_OFFSET+4); + ii = (ii & 0xffff0000) | (tocvalue & 0x0000ffff); + *(int*)((char*)dummyname + TOC_ADDR_OFFSET+4) = ii; + + ii = *(int*)((char*)dummyname + TARGET_ADDR_OFFSET); + ii = (ii & 0xffff0000) | (target_addr >> 16); + *(int*)((char*)dummyname + TARGET_ADDR_OFFSET) = ii; + + ii = *(int*)((char*)dummyname + TARGET_ADDR_OFFSET+4); + ii = (ii & 0xffff0000) | (target_addr & 0x0000ffff); + *(int*)((char*)dummyname + TARGET_ADDR_OFFSET+4) = ii; +} + + + +/* return information about a function frame. + - frameless is TRUE, if function does not save %pc value in its frame. + - offset is the number of bytes used in the frame to save registers. + - saved_gpr is the number of the first saved gpr. + - saved_fpr is the number of the first saved fpr. + */ +function_frame_info (pc, frameless, offset, saved_gpr, saved_fpr) + int pc; + int *frameless, *offset, *saved_gpr, *saved_fpr; +{ + unsigned int tmp; + register unsigned int op; + + *offset = 0; + *saved_gpr = *saved_fpr = -1; + + if (!inferior_pid) + return; + + op = read_memory_integer (pc, 4); + if (op == 0x7c0802a6) { /* mflr r0 */ + pc += 4; + op = read_memory_integer (pc, 4); + *frameless = 0; + } + else /* else, this is a frameless invocation */ + *frameless = 1; + + + if ((op & 0xfc00003e) == 0x7c000026) { /* mfcr Rx */ + pc += 4; + op = read_memory_integer (pc, 4); + } + + if ((op & 0xfc000000) == 0x48000000) { /* bl foo, to save fprs??? */ + pc += 4; + op = read_memory_integer (pc, 4); + } + + if ((op & 0xfc1f0000) == 0xd8010000) { /* stfd Rx,NUM(r1) */ + pc += 4; /* store floating register double */ + op = read_memory_integer (pc, 4); + } + + if ((op & 0xfc1f0000) == 0xbc010000) { /* stm Rx, NUM(r1) */ + int tmp2; + *saved_gpr = (op >> 21) & 0x1f; + tmp2 = op & 0xffff; + if (tmp2 > 0x7fff) + tmp2 = 0xffff0000 | tmp2; + + if (tmp2 < 0) { + tmp2 = tmp2 * -1; + *saved_fpr = (tmp2 - ((32 - *saved_gpr) * 4)) / 8; + if ( *saved_fpr > 0) + *saved_fpr = 32 - *saved_fpr; + else + *saved_fpr = -1; + } + *offset = tmp2; + } +} + + +/* Pass the arguments in either registers, or in the stack. In RS6000, the first + eight words of the argument list (that might be less than eight parameters if + some parameters occupy more than one word) are passed in r3..r11 registers. + float and double parameters are passed in fpr's, in addition to that. Rest of + the parameters if any are passed in user stack. There might be cases in which + half of the parameter is copied into registers, the other half is pushed into + stack. + + If the function is returning a structure, then the return address is passed + in r3, then the first 7 words of the parametes can be passed in registers, + starting from r4. */ + +CORE_ADDR +push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value *args; + CORE_ADDR sp; + int struct_return; + CORE_ADDR struct_addr; +{ + int ii, len; + int argno; /* current argument number */ + int argbytes; /* current argument byte */ + char tmp_buffer [50]; + value arg; + int f_argno = 0; /* current floating point argno */ + + CORE_ADDR saved_sp, pc; + + if ( dummy_frame_count <= 0) + printf ("FATAL ERROR -push_arguments()! frame not found!!\n"); + + /* The first eight words of ther arguments are passed in registers. Copy + them appropriately. + + If the function is returning a `struct', then the first word (which + will be passed in r3) is used for struct return address. In that + case we should advance one word and start from r4 register to copy + parameters. */ + + ii = struct_return ? 1 : 0; + + for (argno=0, argbytes=0; argno < nargs && ii<8; ++ii) { + + arg = value_arg_coerce (args[argno]); + len = TYPE_LENGTH (VALUE_TYPE (arg)); + + if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_FLT) { + + /* floating point arguments are passed in fpr's, as well as gpr's. + There are 13 fpr's reserved for passing parameters. At this point + there is no way we would run out of them. */ + + if (len > 8) + printf ( +"Fatal Error: a floating point parameter #%d with a size > 8 is found!\n", argno); + + bcopy (VALUE_CONTENTS (arg), + ®isters[REGISTER_BYTE(FP0_REGNUM + 1 + f_argno)], len); + ++f_argno; + } + + if (len > 4) { + + /* Argument takes more than one register. */ + while (argbytes < len) { + + *(int*)®isters[REGISTER_BYTE(ii+3)] = 0; + bcopy ( ((char*)VALUE_CONTENTS (arg))+argbytes, + ®isters[REGISTER_BYTE(ii+3)], + (len - argbytes) > 4 ? 4 : len - argbytes); + ++ii, argbytes += 4; + + if (ii >= 8) + goto ran_out_of_registers_for_arguments; + } + argbytes = 0; + --ii; + } + else { /* Argument can fit in one register. No problem. */ + *(int*)®isters[REGISTER_BYTE(ii+3)] = 0; + bcopy (VALUE_CONTENTS (arg), ®isters[REGISTER_BYTE(ii+3)], len); + } + ++argno; + } + +ran_out_of_registers_for_arguments: + + /* location for 8 parameters are always reserved. */ + sp -= 4 * 8; + + /* another six words for back chain, TOC register, link register, etc. */ + sp -= 24; + + /* if there are more arguments, allocate space for them in + the stack, then push them starting from the ninth one. */ + + if ((argno < nargs) || argbytes) { + int space = 0, jj; + value val; + + if (argbytes) { + space += ((len - argbytes + 3) & -4); + jj = argno + 1; + } + else + jj = argno; + + for (; jj < nargs; ++jj) { + val = value_arg_coerce (args[jj]); + space += ((TYPE_LENGTH (VALUE_TYPE (val))) + 3) & -4; + } + + /* add location required for the rest of the parameters */ + space = (space + 7) & -8; + sp -= space; + + /* This is another instance we need to be concerned about securing our + stack space. If we write anything underneath %sp (r1), we might conflict + with the kernel who thinks he is free to use this area. So, update %sp + first before doing anything else. */ + + write_register (SP_REGNUM, sp); + +#if 0 + pc = read_pc (); + flush_cached_frames (); + set_current_frame (create_new_frame (sp, pc)); +#endif + + /* if the last argument copied into the registers didn't fit there + completely, push the rest of it into stack. */ + + if (argbytes) { + write_memory ( + sp+24+(ii*4), ((char*)VALUE_CONTENTS (arg))+argbytes, len - argbytes); + ++argno; + ii += ((len - argbytes + 3) & -4) / 4; + } + + /* push the rest of the arguments into stack. */ + for (; argno < nargs; ++argno) { + + arg = value_arg_coerce (args[argno]); + len = TYPE_LENGTH (VALUE_TYPE (arg)); + + + /* float types should be passed in fpr's, as well as in the stack. */ + if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_FLT && f_argno < 13) { + + if (len > 8) + printf ( +"Fatal Error: a floating point parameter #%d with a size > 8 is found!\n", argno); + + bcopy (VALUE_CONTENTS (arg), + ®isters[REGISTER_BYTE(FP0_REGNUM + 1 + f_argno)], len); + ++f_argno; + } + + write_memory (sp+24+(ii*4), VALUE_CONTENTS (arg), len); + ii += ((len + 3) & -4) / 4; + } + } + else { + + /* Secure stack areas first, before doing anything else. */ + write_register (SP_REGNUM, sp); + +#if 0 + pc = read_pc (); + flush_cached_frames (); + set_current_frame (create_new_frame (sp, pc)); +#endif + } + + saved_sp = dummy_frame_addr [dummy_frame_count - 1]; + read_memory (saved_sp, tmp_buffer, 24); + write_memory (sp, tmp_buffer, 24); + + write_memory (sp, &saved_sp, 4); /* set back chain properly */ + + store_inferior_registers (-1); + return sp; +} + +/* a given return value in `regbuf' with a type `valtype', extract and copy its + value into `valbuf' */ + +extract_return_value (valtype, regbuf, valbuf) + struct type *valtype; + char regbuf[REGISTER_BYTES]; + char *valbuf; +{ + + if (TYPE_CODE (valtype) == TYPE_CODE_FLT) { + + double dd; float ff; + /* floats and doubles are returned in fpr1. fpr's have a size of 8 bytes. + We need to truncate the return value into float size (4 byte) if + necessary. */ + + if (TYPE_LENGTH (valtype) > 4) /* this is a double */ + bcopy (®buf[REGISTER_BYTE (FP0_REGNUM + 1)], valbuf, + TYPE_LENGTH (valtype)); + else { /* float */ + bcopy (®buf[REGISTER_BYTE (FP0_REGNUM + 1)], &dd, 8); + ff = (float)dd; + bcopy (&ff, valbuf, sizeof(float)); + } + } + else + /* return value is copied starting from r3. */ + bcopy (®buf[REGISTER_BYTE (3)], valbuf, TYPE_LENGTH (valtype)); +} + + +/* keep keep structure return address in this variable. */ + +CORE_ADDR rs6000_struct_return_address; + + +/* Throw away this debugging code. FIXMEmgo. */ +print_frame(fram) +int fram; +{ + int ii, val; + for (ii=0; ii<40; ++ii) { + if ((ii % 4) == 0) + printf ("\n"); + val = read_memory_integer (fram + ii * 4, 4); + printf ("0x%08x\t", val); + } + printf ("\n"); +} + + + +/* Indirect function calls use a piece of trampoline code do co context switching, + i.e. to set the new TOC table. Skip such code if exists. */ + +skip_trampoline_code (pc) +int pc; +{ + register unsigned int ii, op; + + static unsigned trampoline_code[] = { + 0x800b0000, /* l r0,0x0(r11) */ + 0x90410014, /* st r2,0x14(r1) */ + 0x7c0903a6, /* mtctr r0 */ + 0x804b0004, /* l r2,0x4(r11) */ + 0x816b0008, /* l r11,0x8(r11) */ + 0x4e800420, /* bctr */ + 0x4e800020, /* br */ + 0 + }; + + for (ii=0; trampoline_code[ii]; ++ii) { + op = read_memory_integer (pc + (ii*4), 4); + if (op != trampoline_code [ii]) + return NULL; + } + ii = read_register (11); /* r11 holds destination addr */ + pc = read_memory_integer (ii, 4); /* (r11) value */ + return pc; +} + diff --git a/gdb/rs6000-xdep.c b/gdb/rs6000-xdep.c new file mode 100644 index 00000000000..7d0917b8d34 --- /dev/null +++ b/gdb/rs6000-xdep.c @@ -0,0 +1,364 @@ +/* IBM RS/6000 host-dependent code for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989, 1991 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "symtab.h" +#include "target.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +extern int errno; +extern int attach_flag; + +/* Conversion from gdb-to-system special purpose register numbers.. */ + +static int special_regs[] = { + IAR, /* PC_REGNUM */ + MSR, /* PS_REGNUM */ + CR, /* CR_REGNUM */ + LR, /* LR_REGNUM */ + CTR, /* CTR_REGNUM */ + XER, /* XER_REGNUM */ + MQ /* MQ_REGNUM */ +}; + + +/* Nonzero if we just simulated a single step break. */ +extern int one_stepped; + + +fetch_inferior_registers () +{ + int ii; + extern char registers[]; + + /* read 32 general purpose registers. */ + + for (ii=0; ii < 32; ++ii) + *(int*)®isters[REGISTER_BYTE (ii)] = + ptrace (PT_READ_GPR, inferior_pid, ii, 0, 0); + + /* read general purpose floating point registers. */ + + for (ii=0; ii < 32; ++ii) + ptrace (PT_READ_FPR, inferior_pid, + (int*)®isters [REGISTER_BYTE (FP0_REGNUM+ii)], FPR0+ii, 0); + + /* read special registers. */ + for (ii=0; ii <= LAST_SP_REGNUM-FIRST_SP_REGNUM; ++ii) + *(int*)®isters[REGISTER_BYTE (FIRST_SP_REGNUM+ii)] = + ptrace (PT_READ_GPR, inferior_pid, special_regs[ii], 0, 0); +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +store_inferior_registers (regno) + int regno; +{ + extern char registers[]; + + errno = 0; + + if (regno == -1) { /* for all registers.. */ + int ii; + + /* execute one dummy instruction (which is a breakpoint) in inferior + process. So give kernel a chance to do internal house keeping. + Otherwise the following ptrace(2) calls will mess up user stack + since kernel will get confused about the bottom of the stack (%sp) */ + + exec_one_dummy_insn (); + + /* write general purpose registers first! */ + for ( ii=GPR0; ii<=GPR31; ++ii) { + ptrace (PT_WRITE_GPR, inferior_pid, ii, + *(int*)®isters[REGISTER_BYTE (ii)], 0); + if ( errno ) { + perror ("ptrace write_gpr"); errno = 0; + } + } + + /* write floating point registers now. */ + for ( ii=0; ii < 32; ++ii) { + ptrace (PT_WRITE_FPR, inferior_pid, + (int*)®isters[REGISTER_BYTE (FP0_REGNUM+ii)], FPR0+ii, 0); + if ( errno ) { + perror ("ptrace write_fpr"); errno = 0; + } + } + + /* write special registers. */ + for (ii=0; ii <= LAST_SP_REGNUM-FIRST_SP_REGNUM; ++ii) { + ptrace (PT_WRITE_GPR, inferior_pid, special_regs[ii], + *(int*)®isters[REGISTER_BYTE (FIRST_SP_REGNUM+ii)], 0); + if ( errno ) { + perror ("ptrace write_gpr"); errno = 0; + } + } + } + + /* else, a specific register number is given... */ + + else if (regno < FP0_REGNUM) { /* a GPR */ + + ptrace (PT_WRITE_GPR, inferior_pid, regno, + *(int*)®isters[REGISTER_BYTE (regno)], 0); + } + + else if (regno <= FPLAST_REGNUM) { /* a FPR */ + ptrace (PT_WRITE_FPR, inferior_pid, + (int*)®isters[REGISTER_BYTE (regno)], regno-FP0_REGNUM+FPR0, 0); + } + + else if (regno <= LAST_SP_REGNUM) { /* a special register */ + + ptrace (PT_WRITE_GPR, inferior_pid, special_regs [regno-FIRST_SP_REGNUM], + *(int*)®isters[REGISTER_BYTE (regno)], 0); + } + + else + fprintf (stderr, "Gdb error: register no %d not implemented.\n", regno); + + if ( errno ) { + perror ("ptrace write"); errno = 0; + return -1; + } + return 0; +} + +void +fetch_core_registers (core_reg_sect, core_reg_size, which) + char *core_reg_sect; + unsigned core_reg_size; + int which; +{ + /* fetch GPRs and special registers from the first register section + in core bfd. */ + if (which == 0) { + + /* copy GPRs first. */ + bcopy (core_reg_sect, registers, 32 * 4); + + /* gdb's internal register template and bfd's register section layout + should share a common include file. FIXMEmgo */ + /* then comes special registes. They are supposed to be in the same + order in gdb template and bfd `.reg' section. */ + core_reg_sect += (32 * 4); + bcopy (core_reg_sect, ®isters [REGISTER_BYTE (FIRST_SP_REGNUM)], + (LAST_SP_REGNUM - FIRST_SP_REGNUM + 1) * 4); + } + + /* fetch floating point registers from register section 2 in core bfd. */ + else if (which == 2) + bcopy (core_reg_sect, ®isters [REGISTER_BYTE (FP0_REGNUM)], 32 * 8); + + else + fprintf (stderr, "Gdb error: unknown parameter to fetch_core_registers().\n"); +} + + +frameless_function_invocation (fi) +struct frame_info *fi; +{ + int ret; + CORE_ADDR func_start, after_prologue; + +#if 0 + func_start = (LOAD_ADDR (get_pc_function_start (fi->pc)) + + FUNCTION_START_OFFSET); +#else + func_start = get_pc_function_start (fi->pc) + FUNCTION_START_OFFSET; +#endif + if (func_start) + { + after_prologue = func_start; + SKIP_PROLOGUE (after_prologue); + ret = (after_prologue == func_start); + } + else + /* If we can't find the start of the function, we don't really */ + /* know whether the function is frameless, but we should be */ + /* able to get a reasonable (i.e. best we can do under the */ + /* circumstances) backtrace by saying that it isn't. */ + ret = 0; + + return ret; + +} + + +/* aixcoff_relocate_symtab - hook for symbol table relocation. + also reads shared libraries.. */ + +aixcoff_relocate_symtab (pid) +unsigned int pid; +{ +#define MAX_LOAD_SEGS 64 /* maximum number of load segments */ + + extern int compare_misc_functions (); + struct ld_info *ldi; + int temp; + + ldi = (void *) alloca(MAX_LOAD_SEGS * sizeof (*ldi)); + + /* According to my humble theory, aixcoff has some timing problems and + when the user stack grows, kernel doesn't update stack info in time + and ptrace calls step on user stack. That is why we sleep here a little, + and give kernel to update its internals. */ + + usleep (36000); + + errno = 0; + ptrace(PT_LDINFO, pid, ldi, MAX_LOAD_SEGS * sizeof(*ldi), ldi); + if (errno) + perror_with_name ("ptrace ldinfo"); + + vmap_ldinfo(ldi); + + do { + add_text_to_loadinfo (ldi->ldinfo_textorg, ldi->ldinfo_dataorg); + } while (ldi->ldinfo_next + && (ldi = (void *) (ldi->ldinfo_next + (char *) ldi))); + + /* Now that we've jumbled things around, re-sort them. */ + sort_misc_function_vector (); + + /* relocate the exec and core sections as well. */ + vmap_exec (); +} + + +/* Keep an array of load segment information and their TOC table addresses. + This info will be useful when calling a shared library function by hand. */ + +typedef struct { + unsigned long textorg, dataorg, toc_offset; +} LoadInfo; + +#define LOADINFOLEN 10 + +static LoadInfo *loadInfo = NULL; +static int loadInfoLen = 0; +static int loadInfoTocIndex = 0; +static int loadInfoTextIndex = 0; + + +xcoff_init_loadinfo () +{ + loadInfoTocIndex = 0; + loadInfoTextIndex = 0; + + if (loadInfoLen == 0) { + loadInfo = (void*) xmalloc (sizeof (LoadInfo) * LOADINFOLEN); + loadInfoLen = LOADINFOLEN; + } +} + + +free_loadinfo () +{ + if (loadInfo) + free (loadInfo); + loadInfo = NULL; + loadInfoLen = 0; + loadInfoTocIndex = 0; + loadInfoTextIndex = 0; +} + + +xcoff_add_toc_to_loadinfo (unsigned long tocaddr) +{ + while (loadInfoTocIndex >= loadInfoLen) { + loadInfoLen += LOADINFOLEN; + loadInfo = (void*) xrealloc (loadInfo, sizeof(LoadInfo) * loadInfoLen); + } + loadInfo [loadInfoTocIndex++].toc_offset = tocaddr; +} + + +add_text_to_loadinfo (unsigned long textaddr, unsigned long dataaddr) +{ + while (loadInfoTextIndex >= loadInfoLen) { + loadInfoLen += LOADINFOLEN; + loadInfo = (void*) xrealloc (loadInfo, sizeof(LoadInfo) * loadInfoLen); + } + loadInfo [loadInfoTextIndex].textorg = textaddr; + loadInfo [loadInfoTextIndex].dataorg = dataaddr; + ++loadInfoTextIndex; +} + + +unsigned long +find_toc_address (unsigned long pc) +{ + int ii, toc_entry; + + for (ii=0; ii < loadInfoTextIndex; ++ii) + if (pc > loadInfo [ii].textorg) + toc_entry = ii; + + return loadInfo [toc_entry].dataorg + loadInfo [toc_entry].toc_offset; +} + + +/* execute one dummy breakpoint instruction. This way we give kernel + a chance to do some housekeeping and update inferior's internal data, + including u_area. */ + +exec_one_dummy_insn () +{ +#define DUMMY_INSN_ADDR 0x10000200 + + unsigned long shadow; + unsigned int status, pid; + + target_insert_breakpoint (DUMMY_INSN_ADDR, &shadow); + + errno = 0; + ptrace (PT_CONTINUE, inferior_pid, DUMMY_INSN_ADDR, 0, 0); + if (errno) + perror ("pt_continue"); + + do { + pid = wait (&status); + } while (pid != inferior_pid); + + target_remove_breakpoint (DUMMY_INSN_ADDR, &shadow); +} + diff --git a/gdb/tm-rs6000.h b/gdb/tm-rs6000.h new file mode 100644 index 00000000000..ce47df664ef --- /dev/null +++ b/gdb/tm-rs6000.h @@ -0,0 +1,461 @@ +/* Parameters for target execution on an RS6000, for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989, 1991 Free Software Foundation, Inc. + Contributed by IBM Corporation. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +extern int symtab_relocated; + +/* text addresses in a core file does not necessarily match to symbol table, + if symbol table relocation wasn't done yet. */ + +#define CORE_NEEDS_RELOCATION(PC) \ + if (!symtab_relocated && !inferior_pid && (PC) > 0x10000000) \ + (PC) -= (0x10000000 + text_adjustment (exec_bfd)); + +/* Conversion between a register number in stab string to actual register num. */ + +#define STAB_REG_TO_REGNUM(value) (value) + +/* return true if a given `pc' value is in `call dummy' function. */ + +#define PC_IN_CALL_DUMMY(STOP_PC, STOP_SP, STOP_FRAME_ADDR) \ + (STOP_SP < STOP_PC && STOP_PC < STACK_END_ADDR) + +/* For each symtab, we keep track of which BFD it came from. */ +#define EXTRA_SYMTAB_INFO \ + unsigned nonreloc:1; /* TRUE if non relocatable */ + +#define INIT_EXTRA_SYMTAB_INFO(symtab) \ + symtab->nonreloc = 0; \ + +extern unsigned int text_start, data_start; +extern int inferior_pid; +extern char *corefile; + +/* setpgrp() messes up controling terminal. The other version of it + requires libbsd.a. */ +#define setpgrp(XX,YY) setpgid (XX, YY) + +/* We are missing register descriptions in the system header files. Sigh! */ + +struct regs { + int gregs [32]; /* general purpose registers */ + int pc; /* program conter */ + int ps; /* processor status, or machine state */ +}; + +struct fp_status { + double fpregs [32]; /* floating GP registers */ +}; + +/* Define the byte order of the machine. */ + +#define TARGET_BYTE_ORDER BIG_ENDIAN + +/* Define this if the C compiler puts an underscore at the front + of external names before giving them to the linker. */ + +#undef NAMES_HAVE_UNDERSCORE + +/* Offset from address of function to start of its code. + Zero on most machines. */ + +#define FUNCTION_START_OFFSET 0 + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +#define SKIP_PROLOGUE(pc) pc = skip_prologue (pc) + +/* If PC is in some function-call trampoline code, return the PC + where the function itself actually starts. If not, return NULL. */ + +#define SKIP_TRAMPOLINE_CODE(pc) skip_trampoline_code (pc) + +/* When a child process is just starting, we sneak in and relocate + the symbol table (and other stuff) after the dynamic linker has + figured out where they go. */ + +#define SOLIB_CREATE_INFERIOR_HOOK(PID) aixcoff_relocate_symtab (PID) + +/* When a target process or core-file has been attached, we sneak in + and figure out where the shared libraries have got to. */ + +#define SOLIB_ADD(a, b, c) aixcoff_relocate_symtab (inferior_pid) + +/* Immediately after a function call, return the saved pc. + Can't go through the frames for this because on some machines + the new frame is not set up until the new function executes + some instructions. */ + +extern char registers[]; + +#define SAVED_PC_AFTER_CALL(frame) \ + (*(int*)®isters[REGISTER_BYTE (LR_REGNUM)]) + +/*#define SAVED_PC_AFTER_CALL(frame) saved_pc_after_call(frame) */ + + +/* Address of end of stack space. */ + +#define STACK_END_ADDR 0x2ff80000 + +/* Stack grows downward. */ + +#define INNER_THAN < + +#if 0 +/* No, we shouldn't use this. push_arguments() should leave stack in a + proper alignment! */ +/* Stack has strict alignment. */ + +#define STACK_ALIGN(ADDR) (((ADDR)+7)&-8) +#endif + +/* This is how argumets pushed onto stack or passed in registers. */ + +#define PUSH_ARGUMENTS(nargs, args, sp, struct_return, struct_addr) \ + sp = push_arguments(nargs, args, sp, struct_return, struct_addr) + +/* Sequence of bytes for breakpoint instruction. */ + +#define BREAKPOINT {0x7d, 0x82, 0x10, 0x08} + +/* Amount PC must be decremented by after a breakpoint. + This is often the number of bytes in BREAKPOINT + but not always. */ + +#define DECR_PC_AFTER_BREAK 0 + +/* Nonzero if instruction at PC is a return instruction. */ +/* Allow any of the return instructions, including a trapv and a return + from interrupt. */ + +#define ABOUT_TO_RETURN(pc) \ + ((read_memory_integer (pc, 4) & 0xfe8007ff) == 0x4e800020) + +/* Return 1 if P points to an invalid floating point value. */ + +#define INVALID_FLOAT(p, len) 0 /* Just a first guess; not checked */ + +/* Largest integer type */ + +#define LONGEST long + +/* Name of the builtin type for the LONGEST type above. */ + +#define BUILTIN_TYPE_LONGEST builtin_type_long + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 71 + +/* Initializer for an array of names of registers. + There should be NUM_REGS strings in this initializer. */ + +#define REGISTER_NAMES \ + {"r0", "sp", "toc", "r3", "r4", "r5", "r6", "r7", \ + "r8", "r9", "r10","r11","r12","r13","r14","r15", \ + "r16","r17","r18","r19","r20","r21","r22","r23", \ + "r24","r25","r26","r27","r28","r29","r30","r31", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "f8", "f9", "f10","f11","f12","f13","f14","f15", \ + "f16","f17","f18","f19","f20","f21","f22","f23", \ + "f24","f25","f26","f27","f28","f29","f30","f31", \ + "pc", "ps", "cnd", "lr", "cnt", "xer", "mq" } + +/* Register numbers of various important registers. + Note that some of these values are "real" register numbers, + and correspond to the general registers of the machine, + and some are "phony" register numbers which are too large + to be actual register numbers as far as the user is concerned + but do serve to get the desired values when passed to read_register. */ + +#define FP_REGNUM 1 /* Contains address of executing stack frame */ +#define SP_REGNUM 1 /* Contains address of top of stack */ +#define TOC_REGNUM 2 /* TOC register */ +#define FP0_REGNUM 32 /* Floating point register 0 */ +#define FPLAST_REGNUM 63 /* Last floating point register */ + +/* Special purpose registers... */ +/* P.S. keep these in the same order as in /usr/mstsave.h `mstsave' structure, for + easier processing */ + +#define PC_REGNUM 64 /* Program counter (instruction address %iar) */ +#define PS_REGNUM 65 /* Processor (or machine) status (%msr) */ +#define CR_REGNUM 66 /* Condition register */ +#define LR_REGNUM 67 /* Link register */ +#define CTR_REGNUM 68 /* Count register */ +#define XER_REGNUM 69 /* Fixed point exception registers */ +#define MQ_REGNUM 70 /* Multiply/quotient register */ + +#define FIRST_SP_REGNUM 64 /* first special register number */ +#define LAST_SP_REGNUM 70 /* last special register number */ + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. + + 32 4-byte gpr's + 32 8-byte fpr's + 7 4-byte special purpose registers, + + total 416 bytes. Keep some extra space for now, in case to add more. */ + +#define REGISTER_BYTES 420 + + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) \ + ( \ + ((N) > FPLAST_REGNUM) ? ((((N) - FPLAST_REGNUM -1) * 4) + 384)\ + :((N) >= FP0_REGNUM) ? ((((N) - FP0_REGNUM) * 8) + 128) \ + :((N) * 4) ) + +/* Number of bytes of storage in the actual machine representation + for register N. */ +/* Note that the unsigned cast here forces the result of the + subtractiion to very high positive values if N < FP0_REGNUM */ + +#define REGISTER_RAW_SIZE(N) (((unsigned)(N) - FP0_REGNUM) < 32 ? 8 : 4) + +/* Number of bytes of storage in the program's representation + for register N. On the RS6000, all regs are 4 bytes + except the floating point regs which are 8-byte doubles. */ + +#define REGISTER_VIRTUAL_SIZE(N) (((unsigned)(N) - FP0_REGNUM) < 32 ? 8 : 4) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 8 + +/* Largest value REGISTER_VIRTUAL_SIZE can have. */ + +#define MAX_REGISTER_VIRTUAL_SIZE 8 + +/* convert a dbx stab register number (from `r' declaration) to a gdb REGNUM */ + +#define STAB_REG_TO_REGNUM(value) (value) + +/* Nonzero if register N requires conversion + from raw format to virtual format. */ + +#define REGISTER_CONVERTIBLE(N) ((N) >= FP0_REGNUM && (N) <= FPLAST_REGNUM) + +/* Convert data from raw format for register REGNUM + to virtual format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) \ + bcopy ((FROM), (TO), REGISTER_RAW_SIZE (REGNUM)) + +/* Convert data from virtual format for register REGNUM + to raw format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) \ + bcopy ((FROM), (TO), REGISTER_RAW_SIZE (REGNUM)) + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +#define REGISTER_VIRTUAL_TYPE(N) \ + (((unsigned)(N) - FP0_REGNUM) < 32 ? builtin_type_double : builtin_type_int) + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ +/* in RS6000, struct return addresses are passed as an extra parameter in r3. + In function return, callee is not responsible of returning this address back. + Since gdb needs to find it, we will store in a designated variable + `rs6000_struct_return_address'. */ + +extern unsigned int rs6000_struct_return_address; + +#define STORE_STRUCT_RETURN(ADDR, SP) \ + { write_register (3, (ADDR)); \ + rs6000_struct_return_address = (unsigned int)(ADDR); } + +/* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual format, + into VALBUF. */ + +/* #define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE)) */ + +#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + extract_return_value(TYPE,REGBUF,VALBUF) + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ + +#define STORE_RETURN_VALUE(TYPE,VALBUF) \ + printf ("FIXMEmgo! STORE_RETURN_VALUE not implemented yet!\n") + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) rs6000_struct_return_address + + +/* Do implement the attach and detach commands. */ + +#define ATTACH_DETACH /* FIXMEmgo! Not implemented yet! */ + + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ + +/* FRAME_CHAIN takes a frame's nominal address + and produces the frame's chain-pointer. + + FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address + and produces the nominal address of the caller frame. + + However, if FRAME_CHAIN_VALID returns zero, + it means the given frame is the outermost one and has no caller. + In that case, FRAME_CHAIN_COMBINE is not used. */ + +/* In the case of the RS6000, the frame's nominal address + is the address of a 4-byte word containing the calling frame's address. */ + +#define FRAME_CHAIN(thisframe) \ + (outside_startup_file ((thisframe)->pc) ? \ + read_memory_integer ((thisframe)->frame, 4) :\ + 0) + +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && (outside_startup_file (FRAME_SAVED_PC (thisframe)))) + +#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain) + +/* Define other aspects of the stack frame. */ + +/* A macro that tells us whether the function invocation represented + by FI does not have a frame on the stack associated with it. If it + does not, FRAMELESS is set to 1, else 0. */ + +#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \ + FRAMELESS = frameless_function_invocation (FI) + +/* Frameless function invocation in IBM RS/6000 is half-done. It perfectly + sets up a new frame, e.g. a new frame (in fact stack) pointer, etc, but it + doesn't save the %pc. In the following, even though it is considered a + frameless invocation, we still need to walk one frame up. */ + +#define INIT_EXTRA_FRAME_INFO(fromleaf, fi) \ + if (fromleaf) { \ + int tmp = 0; \ + read_memory ((fi)->frame, &tmp, sizeof (int)); \ + (fi)->frame = tmp; \ + } + +#define FRAME_SAVED_PC(FRAME) \ + read_memory_integer (read_memory_integer ((FRAME)->frame, 4)+8, 4) + +#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame) + +/* Set VAL to the number of args passed to frame described by FI. + Can set VAL to -1, meaning no way to tell. */ + +/* We can't tell how many args there are + now that the C compiler delays popping them. */ + +#define FRAME_NUM_ARGS(val,fi) (val = -1) + +/* Return number of bytes at start of arglist that are not really args. */ + +#define FRAME_ARGS_SKIP 8 /* Not sure on this. FIXMEmgo */ + +/* Put here the code to store, into a struct frame_saved_regs, + the addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: + the address we return for it IS the sp for the next frame. */ + +#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ + printf ("FIXMEmgo! FRAME_FIND_SAVED_REGS() not implemented!\n") + +/* Things needed for making the inferior call functions. */ + +/* Push an empty stack frame, to record the current PC, etc. */ +/* Change these names into rs6k_{push, pop}_frame(). FIXMEmgo. */ + +#define PUSH_DUMMY_FRAME push_dummy_frame () + +/* Discard from the stack the innermost frame, + restoring all saved registers. */ + +#define POP_FRAME pop_frame () + +/* This sequence of words is the instructions: + + mflr r0 // 0x7c0802a6 + // save fpr's + stfd r?, num(r1) // 0xd8010000 there should be 32 of this?? + // save gpr's + stm r0, num(r1) // 0xbc010000 + stu r1, num(r1) // 0x94210000 + + // the function we want to branch might be in a different load + // segment. reset the toc register. Note that the actual toc address + // will be fix by fix_call_dummy () along with function address. + + st r2, 0x14(r1) // 0x90410014 save toc register + liu r2, 0x1234 // 0x3c401234 reset a new toc value 0x12345678 + oril r2, r2,0x5678 // 0x60425678 + + // load absolute address 0x12345678 to r0 + liu r0, 0x1234 // 0x3c001234 + oril r0, r0,0x5678 // 0x60005678 + mtctr r0 // 0x7c0903a6 ctr <- r0 + bctrl // 0x4e800421 jump subroutine 0x12345678 (%ctr) + cror 0xf, 0xf, 0xf // 0x4def7b82 + brpt // 0x7d821008, breakpoint + cror 0xf, 0xf, 0xf // 0x4def7b82 (for 8 byte alignment) + + + We actually start executing by saving the toc register first, since the pushing + of the registers is done by PUSH_DUMMY_FRAME. If this were real code, + the arguments for the function called by the `bctrl' would be pushed + between the `stu' and the `bctrl', and we could allow it to execute through. + But the arguments have to be pushed by GDB after the PUSH_DUMMY_FRAME is done, + and we cannot allow to push the registers again. +*/ + +#define CALL_DUMMY {0x7c0802a6, 0xd8010000, 0xbc010000, 0x94210000, \ + 0x90410014, 0x3c401234, 0x60425678, \ + 0x3c001234, 0x60005678, 0x7c0903a6, 0x4e800421, \ + 0x4def7b82, 0x7d821008, 0x4def7b82 } + + +/* keep this as multiple of 8 (%sp requires 8 byte alignment) */ +#define CALL_DUMMY_LENGTH 56 + +#define CALL_DUMMY_START_OFFSET 16 + +/* Insert the specified number of args and function address + into a call sequence of the above form stored at DUMMYNAME. */ + +#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, args, type, using_gcc) \ + fix_call_dummy(dummyname, pc, fun, nargs, type) diff --git a/gdb/xcoffexec.c b/gdb/xcoffexec.c new file mode 100644 index 00000000000..f1bd3d8ddc3 --- /dev/null +++ b/gdb/xcoffexec.c @@ -0,0 +1,932 @@ +/* Execute AIXcoff files, for GDB. + Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc. + Derived from exec.c. Modified by IBM Corporation. + Donated by IBM Corporation and Cygnus Support. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* xcoff-exec - deal with executing XCOFF files. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "symfile.h" + +#include "libbfd.h" /* BFD internals (sigh!) FIXME */ + +struct section_table *exec_sections, *exec_sections_end; + +#define eq(s0, s1) !strcmp(s0, s1) + +/* Whether to open exec and core files read-only or read-write. */ + +int write_files = 0; + +bfd *exec_bfd; /* needed by core.c */ + +extern char *getenv(); +extern void child_create_inferior (), child_attach (); +extern void add_syms_addr_command (); +extern void symbol_file_command (); +static void exec_files_info(); + +/* + * the vmap struct is used to describe the virtual address space of + * the target we are manipulating. The first entry is always the "exec" + * file. Subsequent entries correspond to other objects that are + * mapped into the address space of a process created from the "exec" file. + * These are either in response to exec()ing the file, in which case all + * shared libraries are loaded, or a "load" system call, followed by the + * user's issuance of a "load" command. + */ +struct vmap { + struct vmap *nxt; /* ^ to next in chain */ + bfd *bfd; /* BFD for mappable object library */ + char *name; /* ^ to object file name */ + char *member; /* ^ to member name */ + CORE_ADDR tstart; /* virtual addr where member is mapped */ + CORE_ADDR tend; /* virtual upper bound of member */ + CORE_ADDR tadj; /* heuristically derived adjustment */ + CORE_ADDR dstart; /* virtual address of data start */ + CORE_ADDR dend; /* vitrual address of data end */ +}; + + +struct vmap_and_bfd { + bfd *pbfd; + struct vmap *pvmap; +}; + +static struct vmap *vmap; /* current vmap */ + +extern struct target_ops exec_ops; + + +/* exec_close - done with exec file, clean up all resources. */ + +void +exec_close(quitting) { + register struct vmap *vp, *nxt; + + for (nxt = vmap; vp = nxt; ) { + nxt = vp->nxt; + bfd_close(vp->bfd); + free_named_symtabs(vp->name, vp->member); /* XXX */ + free(vp); + } + + vmap = 0; + exec_bfd = 0; +} + +/* + * exec_file_command - handle the "exec" command, &c. + */ +void +exec_file_command(filename, from_tty) +char *filename; +{ + bfd *bfd; + + target_preopen(from_tty); + unpush_target(&exec_ops); + + /* Now open and digest the file the user requested, if any. */ + + if (filename) { + char *scratch_pathname; + int scratch_chan; + + filename = tilde_expand(filename); + make_cleanup(free, filename); + + scratch_chan = openp(getenv("PATH"), 1, filename, O_RDONLY, 0 + , &scratch_pathname); + if (scratch_chan < 0) + perror_with_name(filename); + + bfd = bfd_fdopenr(scratch_pathname, NULL, scratch_chan); + if (!bfd) + error("Could not open `%s' as an executable file: %s" + , scratch_pathname, bfd_errmsg(bfd_error)); + + /* make sure we have an object file */ + + if (!bfd_check_format(bfd, bfd_object)) + error("\"%s\": not in executable format: %s." + , scratch_pathname, bfd_errmsg(bfd_error)); + + + /* setup initial vmap */ + + map_vmap (bfd, 0); + if (!vmap) + error("Can't find the file sections in `%s': %s" + , bfd->filename, bfd_errmsg(bfd_error)); + + exec_bfd = bfd; + + if (build_section_table (exec_bfd, &exec_sections, &exec_sections_end)) + error ("Can't find the file sections in `%s': %s", + exec_bfd->filename, bfd_errmsg (bfd_error)); + + /* make sure core, if present, matches */ + validate_files(); + + push_target(&exec_ops); + + /* Tell display code(if any) about the changed file name. */ + + if (exec_file_display_hook) + (*exec_file_display_hook)(filename); + } + else { + exec_close(0); /* just in case */ + if (from_tty) + printf("No exec file now.\n"); + } +} + +/* Set both the exec file and the symbol file, in one command. What a + * novelty. Why did GDB go through four major releases before this + * command was added? + */ +void +file_command(arg, from_tty) +char *arg; { + + exec_file_command(arg, from_tty); + symbol_file_command(arg, from_tty); +} + +/* Locate all mappable sections of a BFD file. + table_pp_char is a char * to get it through bfd_map_over_sections; + we cast it back to its proper type. */ + +void +add_to_section_table (abfd, asect, table_pp_char) + bfd *abfd; + sec_ptr asect; + char *table_pp_char; +{ + struct section_table **table_pp = (struct section_table **)table_pp_char; + flagword aflag; + + aflag = bfd_get_section_flags (abfd, asect); + /* FIXME, we need to handle BSS segment here...it alloc's but doesn't load */ + if (!(aflag & SEC_LOAD)) + return; + (*table_pp)->sec_ptr = asect; + (*table_pp)->addr = bfd_section_vma (abfd, asect); + (*table_pp)->endaddr = (*table_pp)->addr + bfd_section_size (abfd, asect); + (*table_pp)++; +} + +int +build_section_table (some_bfd, start, end) + bfd *some_bfd; + struct section_table **start, **end; +{ + unsigned count; + + count = bfd_count_sections (some_bfd); + if (count == 0) + abort(); /* return 1? */ + if (*start) + free (*start); + *start = (struct section_table *) xmalloc (count * sizeof (**start)); + *end = *start; + bfd_map_over_sections (some_bfd, add_to_section_table, (char *)end); + if (*end > *start + count) + abort(); + /* We could realloc the table, but it probably loses for most files. */ + return 0; +} + +/* + * lookup_symtab_bfd - find if we currently have any symbol tables from bfd + */ +struct objfile * +lookup_objfile_bfd(bfd *bfd) { + register struct objfile *s; + + for (s = object_files; s; s = s->next) + if (s->obfd == bfd) + return s; + return 0; +} + + +void +sex_to_vmap(bfd *bf, sec_ptr sex, struct vmap_and_bfd *vmap_bfd) +{ + register struct vmap *vp, **vpp; + register struct symtab *syms; + bfd *arch = vmap_bfd->pbfd; + vp = vmap_bfd->pvmap; + + if ((bfd_get_section_flags(bf, sex) & SEC_LOAD) == 0) + return; + + if (!strcmp(bfd_section_name(bf, sex), ".text")) { + vp->tstart = 0; + vp->tend = vp->tstart + bfd_section_size(bf, sex); + + /* This is quite a tacky way to recognize the `exec' load segment (rather + than shared libraries. You should use `arch' instead. FIXMEmgo */ + if (!vmap) + vp->tadj = sex->filepos - bfd_section_vma(bf, sex); + else + vp->tadj = 0; + } + + else if (!strcmp(bfd_section_name(bf, sex), ".data")) { + vp->dstart = 0; + vp->dend = vp->dstart + bfd_section_size(bf, sex); + } + + else if (!strcmp(bfd_section_name(bf, sex), ".bss")) /* FIXMEmgo */ + printf ("bss section in exec! Don't know what the heck to do!\n"); +} + +/* Make a vmap for the BFD "bf", which might be a member of the archive + BFD "arch". If we have not yet read in symbols for this file, do so. */ + +map_vmap (bfd *bf, bfd *arch) +{ + struct vmap_and_bfd vmap_bfd; + struct vmap *vp, **vpp; + struct objfile *obj; + char *name; + + vp = (void*) xmalloc (sizeof (*vp)); + vp->nxt = 0; + vp->bfd = bf; + vp->name = bfd_get_filename(arch ? arch : bf); + vp->member = arch ? bfd_get_filename(bf) : ""; + + vmap_bfd.pbfd = arch; + vmap_bfd.pvmap = vp; + bfd_map_over_sections (bf, sex_to_vmap, &vmap_bfd); + + obj = lookup_objfile_bfd (bf); + if (exec_bfd && !obj) { + name = savestring (bfd_get_filename (bf), strlen (bfd_get_filename (bf))); + obj = allocate_objfile (bf, name); + syms_from_objfile (obj, 0, 0); + } + + /* find the end of the list, and append. */ + for (vpp = &vmap; *vpp; vpp = &(*vpp)->nxt) + ; + *vpp = vp; +} + + +/* true, if symbol table and misc_function_vector is relocated. */ + +int symtab_relocated = 0; + + +/* vmap_symtab - handle symbol translation on vmapping */ + +vmap_symtab(vp, old_start, vip) +register struct vmap *vp; +CORE_ADDR old_start; +struct stat *vip; +{ + register struct symtab *s; + + /* + * for each symbol table generated from the vp->bfd + */ + for (s = symtab_list; s; s = s->next) { + + /* skip over if this is not relocatable and doesn't have a line table */ + if (s->nonreloc && !LINETABLE (s)) + continue; + + /* matching the symbol table's BFD and the *vp's BFD is hairy. + exec_file creates a seperate BFD for possibly the + same file as symbol_file.FIXME ALL THIS MUST BE RECTIFIED. */ + + if (s->objfile->obfd == vp->bfd) { + /* if they match, we luck out. */ + ; + } else if (vp->member[0]) { + /* no match, and member present, not this one. */ + continue; + } else { + struct stat si; + FILE *io; + + /* + * no match, and no member. need to be sure. + */ + io = bfd_cache_lookup(s->objfile->obfd); + if (!io) + fatal("cannot find BFD's iostream for sym"); + /* + * see if we are referring to the same file + */ + if (fstat(fileno(io), &si) < 0) + fatal("cannot fstat BFD for sym"); + + if (si.st_dev != vip->st_dev + || si.st_ino != vip->st_ino) + continue; + } + + if (vp->tstart != old_start) + vmap_symtab_1(s, vp, old_start); + } + + if (vp->tstart != old_start) + fixup_misc_vector (vp->tstart - old_start); + + symtab_relocated = 1; +} + + +fixup_misc_vector (int disp) +{ + int ii; + for (ii=0; ii < misc_function_count; ++ii) + if (misc_function_vector[ii].address < 0x10000000) + misc_function_vector[ii].address += disp; +} + + +vmap_symtab_1(s, vp, old_start) +register struct symtab *s; +register struct vmap *vp; +CORE_ADDR old_start; +{ + register int i, j; + int len, blen; + register struct linetable *l; + struct blockvector *bv; + register struct block *b; + int depth; + register ulong reloc, dreloc; + + if ((reloc = vp->tstart - old_start) == 0) + return; + + dreloc = vp->dstart; /* data relocation */ + + /* + * The line table must be relocated. This is only present for + * b.text sections, so only vp->text type maps need be considered. + */ + l = LINETABLE (s); + len = l->nitems; + for (i = 0; i < len; i++) + l->item[i].pc += reloc; + + /* if this symbol table is not relocatable, only line table should + be relocated and the rest ignored. */ + if (s->nonreloc) + return; + + bv = BLOCKVECTOR(s); + len = BLOCKVECTOR_NBLOCKS(bv); + + for (i = 0; i < len; i++) { + b = BLOCKVECTOR_BLOCK(bv, i); + + BLOCK_START(b) += reloc; + BLOCK_END(b) += reloc; + + blen = BLOCK_NSYMS(b); + for (j = 0; j < blen; j++) { + register struct symbol *sym; + + sym = BLOCK_SYM(b, j); + switch (SYMBOL_NAMESPACE(sym)) { + case STRUCT_NAMESPACE: + case UNDEF_NAMESPACE: + continue; + + case LABEL_NAMESPACE: + case VAR_NAMESPACE: + break; + } + + switch (SYMBOL_CLASS(sym)) { + case LOC_CONST: + case LOC_CONST_BYTES: + case LOC_LOCAL: + case LOC_REGISTER: + case LOC_ARG: + case LOC_LOCAL_ARG: + case LOC_REF_ARG: + case LOC_REGPARM: + case LOC_TYPEDEF: + continue; + +#ifdef FIXME + case LOC_EXTERNAL: +#endif + case LOC_LABEL: + SYMBOL_VALUE_ADDRESS(sym) += reloc; + break; + + case LOC_STATIC: + SYMBOL_VALUE_ADDRESS(sym) += dreloc; + break; + + case LOC_BLOCK: + break; + + default: + fatal("botched symbol class %x" + , SYMBOL_CLASS(sym)); + break; + } + } + } +} + +/* + * add_vmap - add a new vmap entry based on ldinfo() information + */ +add_vmap(ldi) +register struct ld_info *ldi; { + bfd *bfd, *last; + register char *mem; + + mem = ldi->ldinfo_filename + strlen(ldi->ldinfo_filename) + 1; + bfd = bfd_fdopenr(ldi->ldinfo_filename, NULL, ldi->ldinfo_fd); + if (!bfd) + error("Could not open `%s' as an executable file: %s" + , ldi->ldinfo_filename, bfd_errmsg(bfd_error)); + + + /* make sure we have an object file */ + + if (bfd_check_format(bfd, bfd_object)) + map_vmap (bfd, 0); + + else if (bfd_check_format(bfd, bfd_archive)) { + last = 0; + /* + * FIXME??? am I tossing BFDs? bfd? + */ + while (last = bfd_openr_next_archived_file(bfd, last)) + if (eq(mem, last->filename)) + break; + + if (!last) { + bfd_close(bfd); +/* FIXME -- should be error */ + warning("\"%s\": member \"%s\" missing.", + bfd->filename, mem); + return; + } + + if (!bfd_check_format(last, bfd_object)) { + bfd_close(last); /* XXX??? */ + goto obj_err; + } + + map_vmap (last, bfd); + } + else { + obj_err: + bfd_close(bfd); +/* FIXME -- should be error */ + warning("\"%s\": not in executable format: %s." + , ldi->ldinfo_filename, bfd_errmsg(bfd_error)); + return; + } +} + + +/* As well as symbol tables, exec_sections need relocation. Otherwise after + the inferior process terminates, symbol table is relocated but there is + no inferior process. Thus, we have to use `exec' bfd, rather than the inferior + process's memory space, when lookipng at symbols. + `exec_sections' need to be relocated only once though, as long as the exec + file was not changed. +*/ +vmap_exec () +{ + static bfd *execbfd; + if (execbfd == exec_bfd) + return; + + execbfd = exec_bfd; + + if (!vmap || !exec_sections) { + printf ("WARNING: vmap not found in vmap_exec()!\n"); + return; + } + /* First exec section is `.text', second is `.data'. If this is changed, + then this routine will choke. Better you should check section names, + FIXMEmgo. */ + exec_sections [0].addr += vmap->tstart; + exec_sections [0].endaddr += vmap->tstart; + exec_sections [1].addr += vmap->dstart; + exec_sections [1].endaddr += vmap->dstart; +} + + +int +text_adjustment (abfd) +bfd *abfd; +{ + static bfd *execbfd; + static int adjustment; + sec_ptr sect; + + if (exec_bfd == execbfd) + return adjustment; + + sect = bfd_get_section_by_name (abfd, ".text"); + if (sect) + adjustment = sect->filepos - sect->vma; + else + adjustment = 0x200; /* just a wild assumption */ + + return adjustment; +} + + +/* + * vmap_ldinfo - update VMAP info with ldinfo() information + * + * Input: + * ldi - ^ to ldinfo() results. + */ +vmap_ldinfo(ldi) +register struct ld_info *ldi; +{ + struct stat ii, vi; + register struct vmap *vp; + register got_one, retried; + CORE_ADDR ostart; + + /* + * for each *ldi, see if we have a corresponding *vp + * if so, update the mapping, and symbol table. + * if not, add an entry and symbol table. + */ + do { + char *name = ldi->ldinfo_filename; + char *memb = name + strlen(name) + 1; + + retried = 0; + + if (fstat(ldi->ldinfo_fd, &ii) < 0) + fatal("cannot fstat(%d) on %s" + , ldi->ldinfo_fd + , name); +retry: + for (got_one = 0, vp = vmap; vp; vp = vp->nxt) { + FILE *io; + + /* The filenames are not always sufficient to match on. */ + if ((name[0] == "/" + && !eq(name, vp->name)) + || (memb[0] && !eq(memb, vp->member))) + continue; + + /* totally opaque! */ + io = bfd_cache_lookup(vp->bfd); + if (!io) + fatal("cannot find BFD's iostream for %s" + , vp->name); + + /* see if we are referring to the same file */ + if (fstat(fileno(io), &vi) < 0) + fatal("cannot fstat BFD for %s", vp->name); + + if (ii.st_dev != vi.st_dev || ii.st_ino != vi.st_ino) + continue; + + if (!retried) + close(ldi->ldinfo_fd); + + ++got_one; + + /* found a corresponding VMAP. remap! */ + ostart = vp->tstart; + + vp->tstart = ldi->ldinfo_textorg; + vp->tend = vp->tstart + ldi->ldinfo_textsize; + vp->dstart = ldi->ldinfo_dataorg; + vp->dend = vp->dstart + ldi->ldinfo_datasize; + + if (vp->tadj) { + vp->tstart += vp->tadj; + vp->tend += vp->tadj; + } + + /* relocate symbol table(s). */ + vmap_symtab(vp, ostart, &vi); + + /* there may be more, so we don't break out of the loop. */ + } + + /* + * if there was no matching *vp, we must perforce create + * the sucker(s) + */ + if (!got_one && !retried) { + add_vmap(ldi); + ++retried; + goto retry; + } + } while (ldi->ldinfo_next + && (ldi = (void *) (ldi->ldinfo_next + (char *) ldi))); + + breakpoint_re_set(); +} + +/* + * vmap_inferior - print VMAP info for inferior + */ +vmap_inferior() { + + if (inferior_pid == 0) + return 0; /* normal processing */ + + exec_files_info(); + + return 1; +} + +/* Read or write the exec file. + + Args are address within exec file, address within gdb address-space, + length, and a flag indicating whether to read or write. + + Result is a length: + + 0: We cannot handle this address and length. + > 0: We have handled N bytes starting at this address. + (If N == length, we did it all.) We might be able + to handle more bytes beyond this length, but no + promises. + < 0: We cannot handle this address, but if somebody + else handles (-N) bytes, we can start from there. + + The same routine is used to handle both core and exec files; + we just tail-call it with more arguments to select between them. */ + +int +xfer_memory (memaddr, myaddr, len, write, abfd, sections, sections_end) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; + bfd *abfd; + struct section_table *sections, *sections_end; +{ + boolean res; + struct section_table *p; + CORE_ADDR nextsectaddr, memend; + boolean (*xfer_fn) (); + + if (len <= 0) + abort(); + + memend = memaddr + len; + xfer_fn = write? bfd_set_section_contents: bfd_get_section_contents; + nextsectaddr = memend; + + for (p = sections; p < sections_end; p++) + { + if (p->addr <= memaddr) + if (p->endaddr >= memend) + { + /* Entire transfer is within this section. */ + res = xfer_fn (abfd, p->sec_ptr, myaddr, memaddr - p->addr, len); + return (res != false)? len: 0; + } + else if (p->endaddr <= memaddr) + { + /* This section ends before the transfer starts. */ + continue; + } + else + { + /* This section overlaps the transfer. Just do half. */ + len = p->endaddr - memaddr; + res = xfer_fn (abfd, p->sec_ptr, myaddr, memaddr - p->addr, len); + return (res != false)? len: 0; + } + else if (p->addr < nextsectaddr) + nextsectaddr = p->addr; + } + + if (nextsectaddr >= memend) + return 0; /* We can't help */ + else + return - (nextsectaddr - memaddr); /* Next boundary where we can help */ +} + +/* The function called by target_xfer_memory via our target_ops */ + +int +exec_xfer_memory (memaddr, myaddr, len, write) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; +{ + return xfer_memory (memaddr, myaddr, len, write, + exec_bfd, exec_sections, exec_sections_end); +} + +/* + * exec_files_info - "info files" command processor + */ +static void +exec_files_info() { + register struct vmap *vp = vmap; + + if (!vp) + return; + + printf("\tMapping info for file `%s'.\n", vp->name); + printf("\t %8.8s %8.8s %8.8s %s\n" + , "start", "end", "section", "file(member)"); + + for (; vp; vp = vp->nxt) + printf("\t0x%8.8x 0x%8.8x %s%s%s%s\n" + , vp->tstart + , vp->tend + , vp->name + , *vp->member ? "(" : "" + , vp->member + , *vp->member ? ")" : ""); +} + +#ifdef DAMON + Damon's implementation of set_section_command! It is based on the sex member + (which is a section pointer from vmap) of vmap. + We will not have multiple vmap entries (one for each section), rather transmit + text and data base offsets and fix them at the same time. Elimination of sex + entry in vmap make this function obsolute, use the one from exec.c. + Need further testing!! FIXMEmgo. + +static void +set_section_command(args, from_tty) +char *args; +{ + register struct vmap *vp = vmap; + char *secname; + unsigned seclen; + unsigned long secaddr; + char secprint[100]; + long offset; + + if (args == 0) + error("Must specify section name and its virtual address"); + + /* Parse out section name */ + for (secname = args; !isspace(*args); args++) + ; + seclen = args - secname; + + /* Parse out new virtual address */ + secaddr = parse_and_eval_address(args); + + for (vp = vmap; vp; vp = vp->nxt) { + if (!strncmp(secname + , bfd_section_name(vp->bfd, vp->sex), seclen) + && bfd_section_name(vp->bfd, vp->sex)[seclen] == '\0') { + offset = secaddr - vp->tstart; + vp->tstart += offset; + vp->tend += offset; + exec_files_info(); + return; + } + } + + if (seclen >= sizeof(secprint)) + seclen = sizeof(secprint) - 1; + strncpy(secprint, secname, seclen); + secprint[seclen] = '\0'; + error("Section %s not found", secprint); +} +#else +static void +set_section_command (args, from_tty) + char *args; + int from_tty; +{ + struct section_table *p; + char *secname; + unsigned seclen; + unsigned long secaddr; + char secprint[100]; + long offset; + + if (args == 0) + error ("Must specify section name and its virtual address"); + + /* Parse out section name */ + for (secname = args; !isspace(*args); args++) ; + seclen = args - secname; + + /* Parse out new virtual address */ + secaddr = parse_and_eval_address (args); + + for (p = exec_sections; p < exec_sections_end; p++) { + if (!strncmp (secname, bfd_section_name (exec_bfd, p->sec_ptr), seclen) + && bfd_section_name (exec_bfd, p->sec_ptr)[seclen] == '\0') { + offset = secaddr - p->addr; + p->addr += offset; + p->endaddr += offset; + exec_files_info(); + return; + } + } + if (seclen >= sizeof (secprint)) + seclen = sizeof (secprint) - 1; + strncpy (secprint, secname, seclen); + secprint[seclen] = '\0'; + error ("Section %s not found", secprint); +} + +#endif /* !DAMON */ + +struct target_ops exec_ops = { + "exec", "Local exec file", + "Use an executable file as a target.\n\ +Specify the filename of the executable file.", + exec_file_command, exec_close, /* open, close */ + child_attach, 0, 0, 0, /* attach, detach, resume, wait, */ + 0, 0, /* fetch_registers, store_registers, */ + 0, 0, 0, /* prepare_to_store, conv_to, conv_from, */ + exec_xfer_memory, exec_files_info, + 0, 0, /* insert_breakpoint, remove_breakpoint, */ + 0, 0, 0, 0, 0, /* terminal stuff */ + 0, 0, /* kill, load */ + 0, 0, /* call fn, lookup sym */ + child_create_inferior, + 0, /* mourn_inferior */ + file_stratum, 0, /* next */ + 0, 1, 0, 0, 0, /* all mem, mem, stack, regs, exec */ + 0, 0, /* section pointers */ + OPS_MAGIC, /* Always the last thing */ +}; + + +void +_initialize_exec() +{ + + add_com("file", class_files, file_command, + "Use FILE as program to be debugged.\n\ +It is read for its symbols, for getting the contents of pure memory,\n\ +and it is the program executed when you use the `run' command.\n\ +If FILE cannot be found as specified, your execution directory path\n\ +($PATH) is searched for a command of that name.\n\ +No arg means to have no executable file and no symbols."); + + add_com("exec-file", class_files, exec_file_command, + "Use FILE as program for getting contents of pure memory.\n\ +If FILE cannot be found as specified, your execution directory path\n\ +is searched for a command of that name.\n\ +No arg means have no executable file."); + + add_com("section", class_files, set_section_command, + "Change the base address of section SECTION of the exec file to ADDR.\n\ +This can be used if the exec file does not contain section addresses,\n\ +(such as in the a.out format), or when the addresses specified in the\n\ +file itself are wrong. Each section must be changed separately. The\n\ +``info files'' command lists all the sections and their addresses."); + + add_target(&exec_ops); +} diff --git a/gdb/xm-rs6000.h b/gdb/xm-rs6000.h new file mode 100644 index 00000000000..2c0c1019489 --- /dev/null +++ b/gdb/xm-rs6000.h @@ -0,0 +1,67 @@ +/* Parameters for hosting on an RS6000, for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989, 1991 Free Software Foundation, Inc. + Contributed by IBM Corporation. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Big end is at the low address */ + +#define HOST_BYTE_ORDER BIG_ENDIAN + +#define HAVE_TERMIO 1 +#define USG 1 +#define HAVE_SIGSETMASK 1 + +/* This system requires that we open a terminal with O_NOCTTY for it to + not become our controlling terminal. */ + +#define USE_O_NOCTTY + +/* Get rid of any system-imposed stack limit if possible. */ + +#define SET_STACK_LIMIT_HUGE + +/* Brain death inherited from PC's pervades. */ +#undef NULL +#define NULL 0 + +/* The IBM compiler requires this in order to properly compile alloca(). */ +#pragma alloca + +#define vfork fork + +/* Do implement the attach and detach commands. */ + +#define ATTACH_DETACH + +/* Override copies of {fetch,store}_inferior_registers in infptrace.c. */ + +#define FETCH_INFERIOR_REGISTERS + +/* Setpgrp() takes arguments, unlike ordinary Sys V's. */ + +#define SETPGRP_ARGS + +/* RS6000/AIXCOFF does not support PT_STEP. Has to be simulated. */ + +#define NO_SINGLE_STEP + +/* Interface between xcoff symbol reading code and AIX shared library + handling code. FIXME, this probably needs generalizing. */ + +#define XCOFF_INIT_LOADINFO() xcoff_init_loadinfo() +#define XCOFF_ADD_TOC_TO_LOADINFO(x) xcoff_add_toc_to_loadinfo (x)