1991-03-28 16:28:29 +00:00
|
|
|
|
/* Acorn Risc Machine host machine support.
|
|
|
|
|
Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
This file is part of GDB.
|
|
|
|
|
|
1991-06-04 07:31:55 +00:00
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
1991-03-28 16:28:29 +00:00
|
|
|
|
it under the terms of the GNU General Public License as published by
|
1991-06-04 07:31:55 +00:00
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
1991-06-04 07:31:55 +00:00
|
|
|
|
This program is distributed in the hope that it will be useful,
|
1991-03-28 16:28:29 +00:00
|
|
|
|
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
|
1991-06-04 07:31:55 +00:00
|
|
|
|
along with this program; if not, write to the Free Software
|
1995-08-02 03:41:12 +00:00
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
|
|
|
|
#include "defs.h"
|
|
|
|
|
#include "frame.h"
|
|
|
|
|
#include "inferior.h"
|
|
|
|
|
#include "arm-opcode.h"
|
|
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
#include <sys/dir.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
|
#include <machine/reg.h>
|
|
|
|
|
|
|
|
|
|
#define N_TXTADDR(hdr) 0x8000
|
|
|
|
|
#define N_DATADDR(hdr) (hdr.a_text + 0x8000)
|
|
|
|
|
|
|
|
|
|
#include "gdbcore.h"
|
|
|
|
|
|
|
|
|
|
#include <sys/user.h> /* After a.out.h */
|
|
|
|
|
#include <sys/file.h>
|
* configure.in: Check for working mmap, ansi headers, string.h,
strings.h, and memory.h.
* configure: Regenerated.
* gdb_stat.h: New file, "portable" <sys/stat.h>.
* gdb_string.h: New file, "portable" <string.h>.
* altos-xdep.c, arm-tdep.c, arm-xdep.c, convex-tdep.c,
convex-xdep.c, coredep.c, cxux-nat.c, dbxread.c, exec.c,
gould-xdep.c, hppa-tdep.c, i386aix-nat.c, i386b-nat.c,
i386mach-nat.c, i386v-nat.c, infptrace.c, m88k-nat.c, main.c,
mdebugread.c, objfiles.c, os9kread.c, procfs.c, pyr-xdep.c,
rs6000-nat.c, source.c, standalone.c, stuff.c, sun386-nat.c,
symfile.c, symm-nat.c, symm-tdep.c, symtab.c, top.c, ultra3-nat.c,
ultra3-xdep.c, umax-xdep.c, xcoffread.c: Include "gdb_stat.h"
instead of <sys/stat.h>.
* alpha-tdep.c, breakpoint.c, buildsym.c, c-typeprint.c,
ch-typeprint.c, coffread.c, command.c, core-sol2.c, core-svr4.c,
core.c, corelow.c, cp-valprint.c, dbxread.c, dcache.c, demangle.c,
dpx2-nat.c, dstread.c, dwarfread.c, elfread.c, environ.c, eval.c,
exec.c, f-lang.c, f-typeprint.c, f-valprint.c, findvar.c,
fork-child.c, gdbtypes.c, hpread.c, i386-tdep.c, infcmd.c,
inflow.c, infptrace.c, infrun.c, irix5-nat.c, language.c,
m2-typeprint.c, main.c, mdebugread.c, minsyms.c, mipsread.c,
monitor.c, nlmread.c, objfiles.c, os9kread.c, osfsolib.c, parse.c,
printcmd.c, procfs.c, regex.c, remote-adapt.c, remote-arc.c,
remote-array.c, remote-bug.c, remote-e7000.c, remote-eb.c,
remote-es.c, remote-hms.c, remote-mm.c, remote-os9k.c,
remote-pa.c, remote-sim.c, remote-st.c, remote-udi.c,
remote-utils.c, remote-vx.c, remote-vx29k.c, remote-vx68.c,
remote-vx960.c, remote-vxmips.c, remote-vxsparc.c, remote.c,
solib.c, somread.c, source.c, stabsread.c, stack.c, symfile.c,
symmisc.c, symtab.c, target.c, top.c, typeprint.c, utils.c,
valarith.c, valops.c, valprint.c, values.c, xcoffread.c: Include
"gdb_string.h" instead of <string.h>.
* gdbtk.c: Likewise.
* config/xm-sysv4.h, i386/xm-ptx.h, m68k/xm-sun3os4.h,
sparc/xm-sun4os4.h (HAVE_MMAP): Removed.
* config/xm-lynx.h, config/i386/xm-ptx.h,
config/m68k/nm-apollo68b.h, config/m68k/xm-hp300hpux.h,
config/mips/xm-irix3.h, config/mips/xm-mips.h,
config/mips/xm-news-mips.h, config/mips/xm-riscos.h,
config/pa/hppah.h, config/rs6000/xm-rs6000.h,
config/sparc/xm-sun4os4.h, config/sparc/xm-sun4sol2.h,
config/vax/xm-vaxbsd.h, config/vax/xm-vaxult.h,
config/vax/xm-vaxult2.h (MEM_FNS_DECLARED): Removed.
* config/mips/xm-irix3.h, config/mips/xm-mips.h,
config/pa/xm-hppah.h (memcpy, memset): Removed declarations.
1995-08-01 20:14:27 +00:00
|
|
|
|
#include "gdb_stat.h"
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
fetch_inferior_registers (regno)
|
1992-02-22 01:46:16 +00:00
|
|
|
|
int regno; /* Original value discarded */
|
1991-03-28 16:28:29 +00:00
|
|
|
|
{
|
|
|
|
|
register unsigned int regaddr;
|
|
|
|
|
char buf[MAX_REGISTER_RAW_SIZE];
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
struct user u;
|
|
|
|
|
unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
offset = ptrace (PT_READ_U, inferior_pid, (PTRACE_ARG3_TYPE) offset, 0)
|
|
|
|
|
- KERNEL_U_ADDR;
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
|
|
|
|
registers_fetched ();
|
|
|
|
|
|
|
|
|
|
for (regno = 0; regno < 16; regno++)
|
|
|
|
|
{
|
|
|
|
|
regaddr = offset + regno * 4;
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
*(int *)&buf[0] = ptrace (PT_READ_U, inferior_pid,
|
|
|
|
|
(PTRACE_ARG3_TYPE) regaddr, 0);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
if (regno == PC_REGNUM)
|
|
|
|
|
*(int *)&buf[0] = GET_PC_PART(*(int *)&buf[0]);
|
|
|
|
|
supply_register (regno, buf);
|
|
|
|
|
}
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
*(int *)&buf[0] = ptrace (PT_READ_U, inferior_pid,
|
|
|
|
|
(PTRACE_ARG3_TYPE) (offset + PC*4), 0);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
supply_register (PS_REGNUM, buf); /* set virtual register ps same as pc */
|
|
|
|
|
|
|
|
|
|
/* read the floating point registers */
|
|
|
|
|
offset = (char *) &u.u_fp_regs - (char *)&u;
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
*(int *)buf = ptrace (PT_READ_U, inferior_pid, (PTRACE_ARG3_TYPE) offset, 0);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
supply_register (FPS_REGNUM, buf);
|
|
|
|
|
for (regno = 16; regno < 24; regno++) {
|
|
|
|
|
regaddr = offset + 4 + 12 * (regno - 16);
|
|
|
|
|
for (i = 0; i < 12; i += sizeof(int))
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
*(int *) &buf[i] = ptrace (PT_READ_U, inferior_pid,
|
|
|
|
|
(PTRACE_ARG3_TYPE) (regaddr + i), 0);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
supply_register (regno, buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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). */
|
|
|
|
|
|
1992-02-22 01:46:16 +00:00
|
|
|
|
void
|
1991-03-28 16:28:29 +00:00
|
|
|
|
store_inferior_registers (regno)
|
|
|
|
|
int regno;
|
|
|
|
|
{
|
|
|
|
|
register unsigned int regaddr;
|
|
|
|
|
char buf[80];
|
|
|
|
|
|
|
|
|
|
struct user u;
|
|
|
|
|
unsigned long value;
|
|
|
|
|
unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
offset = ptrace (PT_READ_U, inferior_pid, (PTRACE_ARG3_TYPE) offset, 0)
|
|
|
|
|
- KERNEL_U_ADDR;
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
|
|
|
|
if (regno >= 0) {
|
|
|
|
|
if (regno >= 16) return;
|
|
|
|
|
regaddr = offset + 4 * regno;
|
|
|
|
|
errno = 0;
|
|
|
|
|
value = read_register(regno);
|
|
|
|
|
if (regno == PC_REGNUM)
|
|
|
|
|
value = SET_PC_PART(read_register (PS_REGNUM), value);
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
ptrace (PT_WRITE_U, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, value);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
if (errno != 0)
|
|
|
|
|
{
|
|
|
|
|
sprintf (buf, "writing register number %d", regno);
|
|
|
|
|
perror_with_name (buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else for (regno = 0; regno < 15; regno++)
|
|
|
|
|
{
|
|
|
|
|
regaddr = offset + regno * 4;
|
|
|
|
|
errno = 0;
|
|
|
|
|
value = read_register(regno);
|
|
|
|
|
if (regno == PC_REGNUM)
|
|
|
|
|
value = SET_PC_PART(read_register (PS_REGNUM), value);
|
* Makefile.in (VERSION): Bump to 4.5.6.
* coffread.c (coff_end_symtab): Cast 2nd arg of complain() to
correct type.
* defs.h (NORETURN): Define away for Lucid compiler.
* remote.c (remote_timer, remote_interrupt): Signal handlers
take one int arg.
* ser-termios.c (serial_write, serial_close): Return whatever
value the write/close call returns, rather than falling off end.
* inferior.h (PTRACE_ARG3_TYPE): Third arg to ptrace is int on
more systems than it is "char *". Define PTRACE_ARG3_TYPE to
default to int.
* infptrace.c, hppabsd-xdep.c, hppahpux-xdep.c, i386-xdep.c,
inferior.h (call_ptrace): Use PTRACE_ARG3_TYPE to declare type
of third arg.
* a68v-xdep.c, arm-xdep.c, convex-xdep.c, hp300ux-xdep.c, infrun.c,
m88k-xdep.c, mach386-xdep.c, mips-xdep.c, os68k-xdep.c, pyr-tdep.c,
pyr-xdep.c, rs6000-xdep.c, sparc-xdep.c, sun3-xdep.c, sun386-xdep.c,
symm-xdep.c, ultra3-xdep.c: Use PTRACE_ARG3_TYPE to cast ptrace
argument 3.
* sparc-xdep.c, a68v-xdep.c (fetch_inferior_registers,
store_inferior_registers): Supply missing fourth argument to
ptrace().
1992-06-24 04:49:48 +00:00
|
|
|
|
ptrace (6, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, value);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
if (errno != 0)
|
|
|
|
|
{
|
|
|
|
|
sprintf (buf, "writing all regs, number %d", regno);
|
|
|
|
|
perror_with_name (buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Work with core dump and executable files, for GDB.
|
1995-11-05 00:58:57 +00:00
|
|
|
|
This code would be in corefile.c if it weren't machine-dependent. */
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
|
|
|
|
/* Structure to describe the chain of shared libraries used
|
|
|
|
|
by the execfile.
|
|
|
|
|
e.g. prog shares Xt which shares X11 which shares c. */
|
|
|
|
|
|
|
|
|
|
struct shared_library {
|
|
|
|
|
struct exec_header header;
|
|
|
|
|
char name[SHLIBLEN];
|
|
|
|
|
CORE_ADDR text_start; /* CORE_ADDR of 1st byte of text, this file */
|
|
|
|
|
long data_offset; /* offset of data section in file */
|
|
|
|
|
int chan; /* file descriptor for the file */
|
|
|
|
|
struct shared_library *shares; /* library this one shares */
|
|
|
|
|
};
|
|
|
|
|
static struct shared_library *shlib = 0;
|
|
|
|
|
|
|
|
|
|
/* Hook for `exec_file_command' command to call. */
|
|
|
|
|
|
|
|
|
|
extern void (*exec_file_display_hook) ();
|
|
|
|
|
|
|
|
|
|
static CORE_ADDR unshared_text_start;
|
|
|
|
|
|
|
|
|
|
/* extended header from exec file (for shared library info) */
|
|
|
|
|
|
|
|
|
|
static struct exec_header exec_header;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
core_file_command (filename, from_tty)
|
|
|
|
|
char *filename;
|
|
|
|
|
int from_tty;
|
|
|
|
|
{
|
|
|
|
|
int val;
|
|
|
|
|
extern char registers[];
|
|
|
|
|
|
|
|
|
|
/* Discard all vestiges of any previous core file
|
|
|
|
|
and mark data and stack spaces as empty. */
|
|
|
|
|
|
|
|
|
|
if (corefile)
|
|
|
|
|
free (corefile);
|
|
|
|
|
corefile = 0;
|
|
|
|
|
|
|
|
|
|
if (corechan >= 0)
|
|
|
|
|
close (corechan);
|
|
|
|
|
corechan = -1;
|
|
|
|
|
|
|
|
|
|
data_start = 0;
|
|
|
|
|
data_end = 0;
|
|
|
|
|
stack_start = STACK_END_ADDR;
|
|
|
|
|
stack_end = STACK_END_ADDR;
|
|
|
|
|
|
|
|
|
|
/* Now, if a new core file was specified, open it and digest it. */
|
|
|
|
|
|
|
|
|
|
if (filename)
|
|
|
|
|
{
|
|
|
|
|
filename = tilde_expand (filename);
|
|
|
|
|
make_cleanup (free, filename);
|
|
|
|
|
|
|
|
|
|
if (have_inferior_p ())
|
1993-05-25 16:22:41 +00:00
|
|
|
|
error ("To look at a core file, you must kill the program with \"kill\".");
|
1991-03-28 16:28:29 +00:00
|
|
|
|
corechan = open (filename, O_RDONLY, 0);
|
|
|
|
|
if (corechan < 0)
|
|
|
|
|
perror_with_name (filename);
|
|
|
|
|
/* 4.2-style (and perhaps also sysV-style) core dump file. */
|
|
|
|
|
{
|
|
|
|
|
struct user u;
|
|
|
|
|
|
|
|
|
|
unsigned int reg_offset, fp_reg_offset;
|
|
|
|
|
|
|
|
|
|
val = myread (corechan, &u, sizeof u);
|
|
|
|
|
if (val < 0)
|
|
|
|
|
perror_with_name ("Not a core file: reading upage");
|
|
|
|
|
if (val != sizeof u)
|
|
|
|
|
error ("Not a core file: could only read %d bytes", val);
|
|
|
|
|
|
|
|
|
|
/* We are depending on exec_file_command having been called
|
|
|
|
|
previously to set exec_data_start. Since the executable
|
|
|
|
|
and the core file share the same text segment, the address
|
|
|
|
|
of the data segment will be the same in both. */
|
|
|
|
|
data_start = exec_data_start;
|
|
|
|
|
|
|
|
|
|
data_end = data_start + NBPG * u.u_dsize;
|
|
|
|
|
stack_start = stack_end - NBPG * u.u_ssize;
|
|
|
|
|
data_offset = NBPG * UPAGES;
|
|
|
|
|
stack_offset = NBPG * (UPAGES + u.u_dsize);
|
|
|
|
|
|
|
|
|
|
/* Some machines put an absolute address in here and some put
|
|
|
|
|
the offset in the upage of the regs. */
|
|
|
|
|
reg_offset = (int) u.u_ar0;
|
|
|
|
|
if (reg_offset > NBPG * UPAGES)
|
|
|
|
|
reg_offset -= KERNEL_U_ADDR;
|
|
|
|
|
fp_reg_offset = (char *) &u.u_fp_regs - (char *)&u;
|
|
|
|
|
|
|
|
|
|
/* I don't know where to find this info.
|
|
|
|
|
So, for now, mark it as not available. */
|
|
|
|
|
N_SET_MAGIC (core_aouthdr, 0);
|
|
|
|
|
|
|
|
|
|
/* Read the register values out of the core file and store
|
|
|
|
|
them where `read_register' will find them. */
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
register int regno;
|
|
|
|
|
|
|
|
|
|
for (regno = 0; regno < NUM_REGS; regno++)
|
|
|
|
|
{
|
|
|
|
|
char buf[MAX_REGISTER_RAW_SIZE];
|
|
|
|
|
|
|
|
|
|
if (regno < 16)
|
|
|
|
|
val = lseek (corechan, reg_offset + 4 * regno, 0);
|
|
|
|
|
else if (regno < 24)
|
|
|
|
|
val = lseek (corechan, fp_reg_offset + 4 + 12*(regno - 24), 0);
|
|
|
|
|
else if (regno == 24)
|
|
|
|
|
val = lseek (corechan, fp_reg_offset, 0);
|
|
|
|
|
else if (regno == 25)
|
|
|
|
|
val = lseek (corechan, reg_offset + 4 * PC, 0);
|
|
|
|
|
if (val < 0
|
|
|
|
|
|| (val = myread (corechan, buf, sizeof buf)) < 0)
|
|
|
|
|
{
|
1998-12-10 23:07:24 +00:00
|
|
|
|
char * buffer = (char *) alloca (strlen (REGISTER_NAME (regno))
|
1991-03-28 16:28:29 +00:00
|
|
|
|
+ 30);
|
|
|
|
|
strcpy (buffer, "Reading register ");
|
1998-12-10 23:07:24 +00:00
|
|
|
|
strcat (buffer, REGISTER_NAME (regno));
|
1991-03-28 16:28:29 +00:00
|
|
|
|
|
|
|
|
|
perror_with_name (buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (regno == PC_REGNUM)
|
|
|
|
|
*(int *)buf = GET_PC_PART(*(int *)buf);
|
|
|
|
|
supply_register (regno, buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (filename[0] == '/')
|
|
|
|
|
corefile = savestring (filename, strlen (filename));
|
|
|
|
|
else
|
|
|
|
|
{
|
1991-11-12 17:50:14 +00:00
|
|
|
|
corefile = concat (current_directory, "/", filename, NULL);
|
1991-03-28 16:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
The point of these changes is to avoid reading the frame pointer
and stack pointer during stepping, to speed things up.
A. Changes to not select a frame until we need a selected frame:
* blockframe.c (flush_cached_frames): Call select_frame (NULL, -1).
* infrun.c (wait_for_inferior): Move call to select_frame back to
normal_stop. This reverts a change of 13 Apr 94 (it says Jeff
Law, but the change was my idea); the only reason for that change
was so we could save and restore the selected frame in
wait_for_inferior, and now that flush_cached frames clears the
selected frame, that should work OK now.
B. Changes to not create a current_frame until we need one:
* blockframe.c (get_current_frame): If current_frame is NULL, try
to create an innermost frame.
* sparc-tdep.c (sparc_pop_frame), infcmd.c (run-stack_dummy),
infrun.c (wait_for_inferior), thread.c (thread_switch),
convex-tdep.c (set_thread_command), a29k-tdep.c (pop_frame),
alpha-tdep.c (alpha_pop_frame), convex-xdep.c (core_file_command),
h8300-tdep.c (h8300_pop_frame), h8500-tdep.c (h8300_pop_frame),
hppa-tdep.c (hppa_pop_frame), i386-tdep.c (i386_pop_frame),
i960-tdep.c (pop_frame), m68k-tdep.c
(m68k_pop_frame), mips-tdep.c (mips_pop_frame), rs6000-tdep.c
(push_dummy_frame, pop_dummy_frame, pop_frame), sh-tdep.c
(pop_frame), config/arm/tm-arm.h (POP_FRAME),
config/convex/tm-convex.h (POP_FRAME), config/gould/tm-pn.h
(POP_FRAME), config/ns32k/tm-merlin.h (POP_FRAME),
config/ns32k/tm-umax.h (POP_FRAME), config/tahoe/tm-tahoe.h
(POP_FRAME), config/vax/tm-vax.h (POP_FRAME): Don't
call create_new_frame.
* corelow.c (core_open), altos-xdep.c (core_file_command),
arm-xdep.c (core_file_command), gould-xdep.c (core_file_command),
m3-nat.c (select_thread), sun386-nat.c (core_file_command),
umax-xdep.c (core_file_command): Don't call create_new_frame; do
call flush_cached_frames.
* blockframe.c (reinit_frame_cache): Don't call create_new_frame
or select_frame.
C. Changes to get rid of stop_frame_address and instead only
fetch the frame pointer when we need it.
* breakpoint.c (bpstat_stop_status): Remove argument
frame_address; use FRAME_FP (get_current_frame ()).
* infrun.c (wait_for_inferior): Don't pass frame pointer to
bpstat_stop_status.
* infrun.c (wait_for_inferior): Use FRAME_FP (get_current_frame
()) instead of stop_frame_address.
* infrun.c (save_inferior_status, restore_inferior_status),
inferior.h (struct inferior_status): Don't save and restore
stop_frame_address.
* inferior.h, infcmd.c, thread.c (thread_switch), m3-nat.c
(select_thread): Remove stop_frame_address and uses thereof.
D. Same thing for the stack pointer.
* infrun.c (wait_for_inferior): Remove stop_sp and replace
uses thereof with read_sp ().
E. Change to eliminate one nasty little spot where we were
wanting to know the frame pointer from before the current step
(idea from GDB 3.5, which saved my ass, because my other ideas of
how to fix it were very baroque).
* infrun.c: Remove prev_frame_address.
* infrun.c (wait_for_inferior, step_over_function): Use
step_frame_address instead of prev_frame_address.
F. Same basic idea for the stack pointer.
* inferior.h, infcmd.c: New variable step_sp.
* infcmd.c (step_1, until_next_command): Set it.
* infrun.c: Remove prev_sp and replace uses by step_sp.
* infrun.c (wait_for_inferior): If we get out of the step
range, then set step_sp to the current stack pointer before we
start going again.
1994-10-08 03:41:21 +00:00
|
|
|
|
flush_cached_frames ();
|
1991-03-28 16:28:29 +00:00
|
|
|
|
select_frame (get_current_frame (), 0);
|
|
|
|
|
validate_files ();
|
|
|
|
|
}
|
|
|
|
|
else if (from_tty)
|
|
|
|
|
printf ("No core file now.\n");
|
|
|
|
|
}
|
1996-04-05 04:14:11 +00:00
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Work with core dump and executable files, for GDB.
|
|
|
|
|
This code would be in corefile.c if it weren't machine-dependent. */
|
|
|
|
|
|
|
|
|
|
/* Structure to describe the chain of shared libraries used
|
|
|
|
|
by the execfile.
|
|
|
|
|
e.g. prog shares Xt which shares X11 which shares c. */
|
|
|
|
|
|
|
|
|
|
struct shared_library {
|
|
|
|
|
struct exec_header header;
|
|
|
|
|
char name[SHLIBLEN];
|
|
|
|
|
CORE_ADDR text_start; /* CORE_ADDR of 1st byte of text, this file */
|
|
|
|
|
long data_offset; /* offset of data section in file */
|
|
|
|
|
int chan; /* file descriptor for the file */
|
|
|
|
|
struct shared_library *shares; /* library this one shares */
|
|
|
|
|
};
|
|
|
|
|
static struct shared_library *shlib = 0;
|
|
|
|
|
|
|
|
|
|
/* Hook for `exec_file_command' command to call. */
|
|
|
|
|
|
|
|
|
|
extern void (*exec_file_display_hook) ();
|
|
|
|
|
|
|
|
|
|
static CORE_ADDR unshared_text_start;
|
|
|
|
|
|
|
|
|
|
/* extended header from exec file (for shared library info) */
|
|
|
|
|
|
|
|
|
|
static struct exec_header exec_header;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
exec_file_command (filename, from_tty)
|
|
|
|
|
char *filename;
|
|
|
|
|
int from_tty;
|
|
|
|
|
{
|
|
|
|
|
int val;
|
|
|
|
|
|
|
|
|
|
/* Eliminate all traces of old exec file.
|
|
|
|
|
Mark text segment as empty. */
|
|
|
|
|
|
|
|
|
|
if (execfile)
|
|
|
|
|
free (execfile);
|
|
|
|
|
execfile = 0;
|
|
|
|
|
data_start = 0;
|
|
|
|
|
data_end -= exec_data_start;
|
|
|
|
|
text_start = 0;
|
|
|
|
|
unshared_text_start = 0;
|
|
|
|
|
text_end = 0;
|
|
|
|
|
exec_data_start = 0;
|
|
|
|
|
exec_data_end = 0;
|
|
|
|
|
if (execchan >= 0)
|
|
|
|
|
close (execchan);
|
|
|
|
|
execchan = -1;
|
|
|
|
|
if (shlib) {
|
|
|
|
|
close_shared_library(shlib);
|
|
|
|
|
shlib = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now open and digest the file the user requested, if any. */
|
|
|
|
|
|
|
|
|
|
if (filename)
|
|
|
|
|
{
|
|
|
|
|
filename = tilde_expand (filename);
|
|
|
|
|
make_cleanup (free, filename);
|
|
|
|
|
|
|
|
|
|
execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0,
|
|
|
|
|
&execfile);
|
|
|
|
|
if (execchan < 0)
|
|
|
|
|
perror_with_name (filename);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
struct stat st_exec;
|
|
|
|
|
|
|
|
|
|
#ifdef HEADER_SEEK_FD
|
|
|
|
|
HEADER_SEEK_FD (execchan);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
val = myread (execchan, &exec_header, sizeof exec_header);
|
|
|
|
|
exec_aouthdr = exec_header.a_exec;
|
|
|
|
|
|
|
|
|
|
if (val < 0)
|
|
|
|
|
perror_with_name (filename);
|
|
|
|
|
|
|
|
|
|
text_start = 0x8000;
|
|
|
|
|
|
|
|
|
|
/* Look for shared library if needed */
|
|
|
|
|
if (exec_header.a_exec.a_magic & MF_USES_SL)
|
|
|
|
|
shlib = open_shared_library(exec_header.a_shlibname, text_start);
|
|
|
|
|
|
|
|
|
|
text_offset = N_TXTOFF (exec_aouthdr);
|
|
|
|
|
exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text;
|
|
|
|
|
|
|
|
|
|
if (shlib) {
|
|
|
|
|
unshared_text_start = shared_text_end(shlib) & ~0x7fff;
|
|
|
|
|
stack_start = shlib->header.a_exec.a_sldatabase;
|
|
|
|
|
stack_end = STACK_END_ADDR;
|
|
|
|
|
} else
|
|
|
|
|
unshared_text_start = 0x8000;
|
|
|
|
|
text_end = unshared_text_start + exec_aouthdr.a_text;
|
|
|
|
|
|
|
|
|
|
exec_data_start = unshared_text_start + exec_aouthdr.a_text;
|
|
|
|
|
exec_data_end = exec_data_start + exec_aouthdr.a_data;
|
|
|
|
|
|
|
|
|
|
data_start = exec_data_start;
|
|
|
|
|
data_end += exec_data_start;
|
|
|
|
|
|
|
|
|
|
fstat (execchan, &st_exec);
|
|
|
|
|
exec_mtime = st_exec.st_mtime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_files ();
|
|
|
|
|
}
|
|
|
|
|
else if (from_tty)
|
1998-10-01 11:41:41 +00:00
|
|
|
|
printf ("No executable file now.\n");
|
1996-04-05 04:14:11 +00:00
|
|
|
|
|
|
|
|
|
/* Tell display code (if any) about the changed file name. */
|
|
|
|
|
if (exec_file_display_hook)
|
|
|
|
|
(*exec_file_display_hook) (filename);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Read from the program's memory (except for inferior processes).
|
|
|
|
|
This function is misnamed, since it only reads, never writes; and
|
|
|
|
|
since it will use the core file and/or executable file as necessary.
|
|
|
|
|
|
|
|
|
|
It should be extended to write as well as read, FIXME, for patching files.
|
|
|
|
|
|
|
|
|
|
Return 0 if address could be read, EIO if addresss out of bounds. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
xfer_core_file (memaddr, myaddr, len)
|
|
|
|
|
CORE_ADDR memaddr;
|
|
|
|
|
char *myaddr;
|
|
|
|
|
int len;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
register int val;
|
|
|
|
|
int xferchan;
|
|
|
|
|
char **xferfile;
|
|
|
|
|
int fileptr;
|
|
|
|
|
int returnval = 0;
|
|
|
|
|
|
|
|
|
|
while (len > 0)
|
|
|
|
|
{
|
|
|
|
|
xferfile = 0;
|
|
|
|
|
xferchan = 0;
|
|
|
|
|
|
|
|
|
|
/* Determine which file the next bunch of addresses reside in,
|
|
|
|
|
and where in the file. Set the file's read/write pointer
|
|
|
|
|
to point at the proper place for the desired address
|
|
|
|
|
and set xferfile and xferchan for the correct file.
|
|
|
|
|
|
|
|
|
|
If desired address is nonexistent, leave them zero.
|
|
|
|
|
|
|
|
|
|
i is set to the number of bytes that can be handled
|
|
|
|
|
along with the next address.
|
|
|
|
|
|
|
|
|
|
We put the most likely tests first for efficiency. */
|
|
|
|
|
|
|
|
|
|
/* Note that if there is no core file
|
|
|
|
|
data_start and data_end are equal. */
|
|
|
|
|
if (memaddr >= data_start && memaddr < data_end)
|
|
|
|
|
{
|
|
|
|
|
i = min (len, data_end - memaddr);
|
|
|
|
|
fileptr = memaddr - data_start + data_offset;
|
|
|
|
|
xferfile = &corefile;
|
|
|
|
|
xferchan = corechan;
|
|
|
|
|
}
|
|
|
|
|
/* Note that if there is no core file
|
|
|
|
|
stack_start and stack_end define the shared library data. */
|
|
|
|
|
else if (memaddr >= stack_start && memaddr < stack_end)
|
|
|
|
|
{
|
|
|
|
|
if (corechan < 0) {
|
|
|
|
|
struct shared_library *lib;
|
|
|
|
|
for (lib = shlib; lib; lib = lib->shares)
|
|
|
|
|
if (memaddr >= lib->header.a_exec.a_sldatabase &&
|
|
|
|
|
memaddr < lib->header.a_exec.a_sldatabase +
|
|
|
|
|
lib->header.a_exec.a_data)
|
|
|
|
|
break;
|
|
|
|
|
if (lib) {
|
|
|
|
|
i = min (len, lib->header.a_exec.a_sldatabase +
|
|
|
|
|
lib->header.a_exec.a_data - memaddr);
|
|
|
|
|
fileptr = lib->data_offset + memaddr -
|
|
|
|
|
lib->header.a_exec.a_sldatabase;
|
|
|
|
|
xferfile = execfile;
|
|
|
|
|
xferchan = lib->chan;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
i = min (len, stack_end - memaddr);
|
|
|
|
|
fileptr = memaddr - stack_start + stack_offset;
|
|
|
|
|
xferfile = &corefile;
|
|
|
|
|
xferchan = corechan;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (corechan < 0
|
|
|
|
|
&& memaddr >= exec_data_start && memaddr < exec_data_end)
|
|
|
|
|
{
|
|
|
|
|
i = min (len, exec_data_end - memaddr);
|
|
|
|
|
fileptr = memaddr - exec_data_start + exec_data_offset;
|
|
|
|
|
xferfile = &execfile;
|
|
|
|
|
xferchan = execchan;
|
|
|
|
|
}
|
|
|
|
|
else if (memaddr >= text_start && memaddr < text_end)
|
|
|
|
|
{
|
|
|
|
|
struct shared_library *lib;
|
|
|
|
|
for (lib = shlib; lib; lib = lib->shares)
|
|
|
|
|
if (memaddr >= lib->text_start &&
|
|
|
|
|
memaddr < lib->text_start + lib->header.a_exec.a_text)
|
|
|
|
|
break;
|
|
|
|
|
if (lib) {
|
|
|
|
|
i = min (len, lib->header.a_exec.a_text +
|
|
|
|
|
lib->text_start - memaddr);
|
|
|
|
|
fileptr = memaddr - lib->text_start + text_offset;
|
|
|
|
|
xferfile = &execfile;
|
|
|
|
|
xferchan = lib->chan;
|
|
|
|
|
} else {
|
|
|
|
|
i = min (len, text_end - memaddr);
|
|
|
|
|
fileptr = memaddr - unshared_text_start + text_offset;
|
|
|
|
|
xferfile = &execfile;
|
|
|
|
|
xferchan = execchan;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (memaddr < text_start)
|
|
|
|
|
{
|
|
|
|
|
i = min (len, text_start - memaddr);
|
|
|
|
|
}
|
|
|
|
|
else if (memaddr >= text_end
|
|
|
|
|
&& memaddr < (corechan >= 0? data_start : exec_data_start))
|
|
|
|
|
{
|
|
|
|
|
i = min (len, data_start - memaddr);
|
|
|
|
|
}
|
|
|
|
|
else if (corechan >= 0
|
|
|
|
|
&& memaddr >= data_end && memaddr < stack_start)
|
|
|
|
|
{
|
|
|
|
|
i = min (len, stack_start - memaddr);
|
|
|
|
|
}
|
|
|
|
|
else if (corechan < 0 && memaddr >= exec_data_end)
|
|
|
|
|
{
|
|
|
|
|
i = min (len, - memaddr);
|
|
|
|
|
}
|
|
|
|
|
else if (memaddr >= stack_end && stack_end != 0)
|
|
|
|
|
{
|
|
|
|
|
i = min (len, - memaddr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Address did not classify into one of the known ranges.
|
|
|
|
|
This shouldn't happen; we catch the endpoints. */
|
|
|
|
|
fatal ("Internal: Bad case logic in xfer_core_file.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now we know which file to use.
|
|
|
|
|
Set up its pointer and transfer the data. */
|
|
|
|
|
if (xferfile)
|
|
|
|
|
{
|
|
|
|
|
if (*xferfile == 0)
|
|
|
|
|
if (xferfile == &execfile)
|
|
|
|
|
error ("No program file to examine.");
|
|
|
|
|
else
|
|
|
|
|
error ("No core dump file or running program to examine.");
|
|
|
|
|
val = lseek (xferchan, fileptr, 0);
|
|
|
|
|
if (val < 0)
|
|
|
|
|
perror_with_name (*xferfile);
|
|
|
|
|
val = myread (xferchan, myaddr, i);
|
|
|
|
|
if (val < 0)
|
|
|
|
|
perror_with_name (*xferfile);
|
|
|
|
|
}
|
|
|
|
|
/* If this address is for nonexistent memory,
|
|
|
|
|
read zeros if reading, or do nothing if writing.
|
|
|
|
|
Actually, we never right. */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memset (myaddr, '\0', i);
|
|
|
|
|
returnval = EIO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memaddr += i;
|
|
|
|
|
myaddr += i;
|
|
|
|
|
len -= i;
|
|
|
|
|
}
|
|
|
|
|
return returnval;
|
|
|
|
|
}
|
|
|
|
|
#endif
|