mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
4de283e4b5
Andreas Schwab and John Baldwin pointed out some bugs in the header sorting patch; and I noticed that the output was not correct when limited to a subset of files (a bug in my script). So, I'm reverting the patch. I may try again after fixing the issues pointed out. gdb/ChangeLog 2019-04-05 Tom Tromey <tom@tromey.com> Revert the header-sorting patch. * ft32-tdep.c: Revert. * frv-tdep.c: Revert. * frv-linux-tdep.c: Revert. * frame.c: Revert. * frame-unwind.c: Revert. * frame-base.c: Revert. * fork-child.c: Revert. * findvar.c: Revert. * findcmd.c: Revert. * filesystem.c: Revert. * filename-seen-cache.h: Revert. * filename-seen-cache.c: Revert. * fbsd-tdep.c: Revert. * fbsd-nat.h: Revert. * fbsd-nat.c: Revert. * f-valprint.c: Revert. * f-typeprint.c: Revert. * f-lang.c: Revert. * extension.h: Revert. * extension.c: Revert. * extension-priv.h: Revert. * expprint.c: Revert. * exec.h: Revert. * exec.c: Revert. * exceptions.c: Revert. * event-top.c: Revert. * event-loop.c: Revert. * eval.c: Revert. * elfread.c: Revert. * dwarf2read.h: Revert. * dwarf2read.c: Revert. * dwarf2loc.c: Revert. * dwarf2expr.h: Revert. * dwarf2expr.c: Revert. * dwarf2-frame.c: Revert. * dwarf2-frame-tailcall.c: Revert. * dwarf-index-write.h: Revert. * dwarf-index-write.c: Revert. * dwarf-index-common.c: Revert. * dwarf-index-cache.h: Revert. * dwarf-index-cache.c: Revert. * dummy-frame.c: Revert. * dtrace-probe.c: Revert. * disasm.h: Revert. * disasm.c: Revert. * disasm-selftests.c: Revert. * dictionary.c: Revert. * dicos-tdep.c: Revert. * demangle.c: Revert. * dcache.h: Revert. * dcache.c: Revert. * darwin-nat.h: Revert. * darwin-nat.c: Revert. * darwin-nat-info.c: Revert. * d-valprint.c: Revert. * d-namespace.c: Revert. * d-lang.c: Revert. * ctf.c: Revert. * csky-tdep.c: Revert. * csky-linux-tdep.c: Revert. * cris-tdep.c: Revert. * cris-linux-tdep.c: Revert. * cp-valprint.c: Revert. * cp-support.c: Revert. * cp-namespace.c: Revert. * cp-abi.c: Revert. * corelow.c: Revert. * corefile.c: Revert. * continuations.c: Revert. * completer.h: Revert. * completer.c: Revert. * complaints.c: Revert. * coffread.c: Revert. * coff-pe-read.c: Revert. * cli-out.h: Revert. * cli-out.c: Revert. * charset.c: Revert. * c-varobj.c: Revert. * c-valprint.c: Revert. * c-typeprint.c: Revert. * c-lang.c: Revert. * buildsym.c: Revert. * buildsym-legacy.c: Revert. * build-id.h: Revert. * build-id.c: Revert. * btrace.c: Revert. * bsd-uthread.c: Revert. * breakpoint.h: Revert. * breakpoint.c: Revert. * break-catch-throw.c: Revert. * break-catch-syscall.c: Revert. * break-catch-sig.c: Revert. * blockframe.c: Revert. * block.c: Revert. * bfin-tdep.c: Revert. * bfin-linux-tdep.c: Revert. * bfd-target.c: Revert. * bcache.c: Revert. * ax-general.c: Revert. * ax-gdb.h: Revert. * ax-gdb.c: Revert. * avr-tdep.c: Revert. * auxv.c: Revert. * auto-load.c: Revert. * arm-wince-tdep.c: Revert. * arm-tdep.c: Revert. * arm-symbian-tdep.c: Revert. * arm-pikeos-tdep.c: Revert. * arm-obsd-tdep.c: Revert. * arm-nbsd-tdep.c: Revert. * arm-nbsd-nat.c: Revert. * arm-linux-tdep.c: Revert. * arm-linux-nat.c: Revert. * arm-fbsd-tdep.c: Revert. * arm-fbsd-nat.c: Revert. * arm-bsd-tdep.c: Revert. * arch-utils.c: Revert. * arc-tdep.c: Revert. * arc-newlib-tdep.c: Revert. * annotate.h: Revert. * annotate.c: Revert. * amd64-windows-tdep.c: Revert. * amd64-windows-nat.c: Revert. * amd64-tdep.c: Revert. * amd64-sol2-tdep.c: Revert. * amd64-obsd-tdep.c: Revert. * amd64-obsd-nat.c: Revert. * amd64-nbsd-tdep.c: Revert. * amd64-nbsd-nat.c: Revert. * amd64-nat.c: Revert. * amd64-linux-tdep.c: Revert. * amd64-linux-nat.c: Revert. * amd64-fbsd-tdep.c: Revert. * amd64-fbsd-nat.c: Revert. * amd64-dicos-tdep.c: Revert. * amd64-darwin-tdep.c: Revert. * amd64-bsd-nat.c: Revert. * alpha-tdep.c: Revert. * alpha-obsd-tdep.c: Revert. * alpha-nbsd-tdep.c: Revert. * alpha-mdebug-tdep.c: Revert. * alpha-linux-tdep.c: Revert. * alpha-linux-nat.c: Revert. * alpha-bsd-tdep.c: Revert. * alpha-bsd-nat.c: Revert. * aix-thread.c: Revert. * agent.c: Revert. * addrmap.c: Revert. * ada-varobj.c: Revert. * ada-valprint.c: Revert. * ada-typeprint.c: Revert. * ada-tasks.c: Revert. * ada-lang.c: Revert. * aarch64-tdep.c: Revert. * aarch64-ravenscar-thread.c: Revert. * aarch64-newlib-tdep.c: Revert. * aarch64-linux-tdep.c: Revert. * aarch64-linux-nat.c: Revert. * aarch64-fbsd-tdep.c: Revert. * aarch64-fbsd-nat.c: Revert. * aarch32-linux-nat.c: Revert.
1100 lines
34 KiB
C
1100 lines
34 KiB
C
/* Find a variable's value in memory, for GDB, the GNU debugger.
|
||
|
||
Copyright (C) 1986-2019 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 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include "defs.h"
|
||
#include "symtab.h"
|
||
#include "gdbtypes.h"
|
||
#include "frame.h"
|
||
#include "value.h"
|
||
#include "gdbcore.h"
|
||
#include "inferior.h"
|
||
#include "target.h"
|
||
#include "symfile.h" /* for overlay functions */
|
||
#include "regcache.h"
|
||
#include "user-regs.h"
|
||
#include "block.h"
|
||
#include "objfiles.h"
|
||
#include "language.h"
|
||
#include "dwarf2loc.h"
|
||
#include "common/selftest.h"
|
||
|
||
/* Basic byte-swapping routines. All 'extract' functions return a
|
||
host-format integer from a target-format integer at ADDR which is
|
||
LEN bytes long. */
|
||
|
||
#if TARGET_CHAR_BIT != 8 || HOST_CHAR_BIT != 8
|
||
/* 8 bit characters are a pretty safe assumption these days, so we
|
||
assume it throughout all these swapping routines. If we had to deal with
|
||
9 bit characters, we would need to make len be in bits and would have
|
||
to re-write these routines... */
|
||
you lose
|
||
#endif
|
||
|
||
template<typename T, typename>
|
||
T
|
||
extract_integer (const gdb_byte *addr, int len, enum bfd_endian byte_order)
|
||
{
|
||
typename std::make_unsigned<T>::type retval = 0;
|
||
const unsigned char *p;
|
||
const unsigned char *startaddr = addr;
|
||
const unsigned char *endaddr = startaddr + len;
|
||
|
||
if (len > (int) sizeof (T))
|
||
error (_("\
|
||
That operation is not available on integers of more than %d bytes."),
|
||
(int) sizeof (T));
|
||
|
||
/* Start at the most significant end of the integer, and work towards
|
||
the least significant. */
|
||
if (byte_order == BFD_ENDIAN_BIG)
|
||
{
|
||
p = startaddr;
|
||
if (std::is_signed<T>::value)
|
||
{
|
||
/* Do the sign extension once at the start. */
|
||
retval = ((LONGEST) * p ^ 0x80) - 0x80;
|
||
++p;
|
||
}
|
||
for (; p < endaddr; ++p)
|
||
retval = (retval << 8) | *p;
|
||
}
|
||
else
|
||
{
|
||
p = endaddr - 1;
|
||
if (std::is_signed<T>::value)
|
||
{
|
||
/* Do the sign extension once at the start. */
|
||
retval = ((LONGEST) * p ^ 0x80) - 0x80;
|
||
--p;
|
||
}
|
||
for (; p >= startaddr; --p)
|
||
retval = (retval << 8) | *p;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
/* Explicit instantiations. */
|
||
template LONGEST extract_integer<LONGEST> (const gdb_byte *addr, int len,
|
||
enum bfd_endian byte_order);
|
||
template ULONGEST extract_integer<ULONGEST> (const gdb_byte *addr, int len,
|
||
enum bfd_endian byte_order);
|
||
|
||
/* Sometimes a long long unsigned integer can be extracted as a
|
||
LONGEST value. This is done so that we can print these values
|
||
better. If this integer can be converted to a LONGEST, this
|
||
function returns 1 and sets *PVAL. Otherwise it returns 0. */
|
||
|
||
int
|
||
extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
|
||
enum bfd_endian byte_order, LONGEST *pval)
|
||
{
|
||
const gdb_byte *p;
|
||
const gdb_byte *first_addr;
|
||
int len;
|
||
|
||
len = orig_len;
|
||
if (byte_order == BFD_ENDIAN_BIG)
|
||
{
|
||
for (p = addr;
|
||
len > (int) sizeof (LONGEST) && p < addr + orig_len;
|
||
p++)
|
||
{
|
||
if (*p == 0)
|
||
len--;
|
||
else
|
||
break;
|
||
}
|
||
first_addr = p;
|
||
}
|
||
else
|
||
{
|
||
first_addr = addr;
|
||
for (p = addr + orig_len - 1;
|
||
len > (int) sizeof (LONGEST) && p >= addr;
|
||
p--)
|
||
{
|
||
if (*p == 0)
|
||
len--;
|
||
else
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (len <= (int) sizeof (LONGEST))
|
||
{
|
||
*pval = (LONGEST) extract_unsigned_integer (first_addr,
|
||
sizeof (LONGEST),
|
||
byte_order);
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Treat the bytes at BUF as a pointer of type TYPE, and return the
|
||
address it represents. */
|
||
CORE_ADDR
|
||
extract_typed_address (const gdb_byte *buf, struct type *type)
|
||
{
|
||
if (TYPE_CODE (type) != TYPE_CODE_PTR && !TYPE_IS_REFERENCE (type))
|
||
internal_error (__FILE__, __LINE__,
|
||
_("extract_typed_address: "
|
||
"type is not a pointer or reference"));
|
||
|
||
return gdbarch_pointer_to_address (get_type_arch (type), type, buf);
|
||
}
|
||
|
||
/* All 'store' functions accept a host-format integer and store a
|
||
target-format integer at ADDR which is LEN bytes long. */
|
||
template<typename T, typename>
|
||
void
|
||
store_integer (gdb_byte *addr, int len, enum bfd_endian byte_order,
|
||
T val)
|
||
{
|
||
gdb_byte *p;
|
||
gdb_byte *startaddr = addr;
|
||
gdb_byte *endaddr = startaddr + len;
|
||
|
||
/* Start at the least significant end of the integer, and work towards
|
||
the most significant. */
|
||
if (byte_order == BFD_ENDIAN_BIG)
|
||
{
|
||
for (p = endaddr - 1; p >= startaddr; --p)
|
||
{
|
||
*p = val & 0xff;
|
||
val >>= 8;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (p = startaddr; p < endaddr; ++p)
|
||
{
|
||
*p = val & 0xff;
|
||
val >>= 8;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Explicit instantiations. */
|
||
template void store_integer (gdb_byte *addr, int len,
|
||
enum bfd_endian byte_order,
|
||
LONGEST val);
|
||
|
||
template void store_integer (gdb_byte *addr, int len,
|
||
enum bfd_endian byte_order,
|
||
ULONGEST val);
|
||
|
||
/* Store the address ADDR as a pointer of type TYPE at BUF, in target
|
||
form. */
|
||
void
|
||
store_typed_address (gdb_byte *buf, struct type *type, CORE_ADDR addr)
|
||
{
|
||
if (TYPE_CODE (type) != TYPE_CODE_PTR && !TYPE_IS_REFERENCE (type))
|
||
internal_error (__FILE__, __LINE__,
|
||
_("store_typed_address: "
|
||
"type is not a pointer or reference"));
|
||
|
||
gdbarch_address_to_pointer (get_type_arch (type), type, buf, addr);
|
||
}
|
||
|
||
/* Copy a value from SOURCE of size SOURCE_SIZE bytes to DEST of size DEST_SIZE
|
||
bytes. If SOURCE_SIZE is greater than DEST_SIZE, then truncate the most
|
||
significant bytes. If SOURCE_SIZE is less than DEST_SIZE then either sign
|
||
or zero extended according to IS_SIGNED. Values are stored in memory with
|
||
endianess BYTE_ORDER. */
|
||
|
||
void
|
||
copy_integer_to_size (gdb_byte *dest, int dest_size, const gdb_byte *source,
|
||
int source_size, bool is_signed,
|
||
enum bfd_endian byte_order)
|
||
{
|
||
signed int size_diff = dest_size - source_size;
|
||
|
||
/* Copy across everything from SOURCE that can fit into DEST. */
|
||
|
||
if (byte_order == BFD_ENDIAN_BIG && size_diff > 0)
|
||
memcpy (dest + size_diff, source, source_size);
|
||
else if (byte_order == BFD_ENDIAN_BIG && size_diff < 0)
|
||
memcpy (dest, source - size_diff, dest_size);
|
||
else
|
||
memcpy (dest, source, std::min (source_size, dest_size));
|
||
|
||
/* Fill the remaining space in DEST by either zero extending or sign
|
||
extending. */
|
||
|
||
if (size_diff > 0)
|
||
{
|
||
gdb_byte extension = 0;
|
||
if (is_signed
|
||
&& ((byte_order != BFD_ENDIAN_BIG && source[source_size - 1] & 0x80)
|
||
|| (byte_order == BFD_ENDIAN_BIG && source[0] & 0x80)))
|
||
extension = 0xff;
|
||
|
||
/* Extend into MSBs of SOURCE. */
|
||
if (byte_order == BFD_ENDIAN_BIG)
|
||
memset (dest, extension, size_diff);
|
||
else
|
||
memset (dest + source_size, extension, size_diff);
|
||
}
|
||
}
|
||
|
||
/* Return a `value' with the contents of (virtual or cooked) register
|
||
REGNUM as found in the specified FRAME. The register's type is
|
||
determined by register_type(). */
|
||
|
||
struct value *
|
||
value_of_register (int regnum, struct frame_info *frame)
|
||
{
|
||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||
struct value *reg_val;
|
||
|
||
/* User registers lie completely outside of the range of normal
|
||
registers. Catch them early so that the target never sees them. */
|
||
if (regnum >= gdbarch_num_cooked_regs (gdbarch))
|
||
return value_of_user_reg (regnum, frame);
|
||
|
||
reg_val = value_of_register_lazy (frame, regnum);
|
||
value_fetch_lazy (reg_val);
|
||
return reg_val;
|
||
}
|
||
|
||
/* Return a `value' with the contents of (virtual or cooked) register
|
||
REGNUM as found in the specified FRAME. The register's type is
|
||
determined by register_type(). The value is not fetched. */
|
||
|
||
struct value *
|
||
value_of_register_lazy (struct frame_info *frame, int regnum)
|
||
{
|
||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||
struct value *reg_val;
|
||
struct frame_info *next_frame;
|
||
|
||
gdb_assert (regnum < gdbarch_num_cooked_regs (gdbarch));
|
||
|
||
gdb_assert (frame != NULL);
|
||
|
||
next_frame = get_next_frame_sentinel_okay (frame);
|
||
|
||
/* We should have a valid next frame. */
|
||
gdb_assert (frame_id_p (get_frame_id (next_frame)));
|
||
|
||
reg_val = allocate_value_lazy (register_type (gdbarch, regnum));
|
||
VALUE_LVAL (reg_val) = lval_register;
|
||
VALUE_REGNUM (reg_val) = regnum;
|
||
VALUE_NEXT_FRAME_ID (reg_val) = get_frame_id (next_frame);
|
||
|
||
return reg_val;
|
||
}
|
||
|
||
/* Given a pointer of type TYPE in target form in BUF, return the
|
||
address it represents. */
|
||
CORE_ADDR
|
||
unsigned_pointer_to_address (struct gdbarch *gdbarch,
|
||
struct type *type, const gdb_byte *buf)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
|
||
return extract_unsigned_integer (buf, TYPE_LENGTH (type), byte_order);
|
||
}
|
||
|
||
CORE_ADDR
|
||
signed_pointer_to_address (struct gdbarch *gdbarch,
|
||
struct type *type, const gdb_byte *buf)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
|
||
return extract_signed_integer (buf, TYPE_LENGTH (type), byte_order);
|
||
}
|
||
|
||
/* Given an address, store it as a pointer of type TYPE in target
|
||
format in BUF. */
|
||
void
|
||
unsigned_address_to_pointer (struct gdbarch *gdbarch, struct type *type,
|
||
gdb_byte *buf, CORE_ADDR addr)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
|
||
store_unsigned_integer (buf, TYPE_LENGTH (type), byte_order, addr);
|
||
}
|
||
|
||
void
|
||
address_to_signed_pointer (struct gdbarch *gdbarch, struct type *type,
|
||
gdb_byte *buf, CORE_ADDR addr)
|
||
{
|
||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||
|
||
store_signed_integer (buf, TYPE_LENGTH (type), byte_order, addr);
|
||
}
|
||
|
||
/* See value.h. */
|
||
|
||
enum symbol_needs_kind
|
||
symbol_read_needs (struct symbol *sym)
|
||
{
|
||
if (SYMBOL_COMPUTED_OPS (sym) != NULL)
|
||
return SYMBOL_COMPUTED_OPS (sym)->get_symbol_read_needs (sym);
|
||
|
||
switch (SYMBOL_CLASS (sym))
|
||
{
|
||
/* All cases listed explicitly so that gcc -Wall will detect it if
|
||
we failed to consider one. */
|
||
case LOC_COMPUTED:
|
||
gdb_assert_not_reached (_("LOC_COMPUTED variable missing a method"));
|
||
|
||
case LOC_REGISTER:
|
||
case LOC_ARG:
|
||
case LOC_REF_ARG:
|
||
case LOC_REGPARM_ADDR:
|
||
case LOC_LOCAL:
|
||
return SYMBOL_NEEDS_FRAME;
|
||
|
||
case LOC_UNDEF:
|
||
case LOC_CONST:
|
||
case LOC_STATIC:
|
||
case LOC_TYPEDEF:
|
||
|
||
case LOC_LABEL:
|
||
/* Getting the address of a label can be done independently of the block,
|
||
even if some *uses* of that address wouldn't work so well without
|
||
the right frame. */
|
||
|
||
case LOC_BLOCK:
|
||
case LOC_CONST_BYTES:
|
||
case LOC_UNRESOLVED:
|
||
case LOC_OPTIMIZED_OUT:
|
||
return SYMBOL_NEEDS_NONE;
|
||
}
|
||
return SYMBOL_NEEDS_FRAME;
|
||
}
|
||
|
||
/* See value.h. */
|
||
|
||
int
|
||
symbol_read_needs_frame (struct symbol *sym)
|
||
{
|
||
return symbol_read_needs (sym) == SYMBOL_NEEDS_FRAME;
|
||
}
|
||
|
||
/* Private data to be used with minsym_lookup_iterator_cb. */
|
||
|
||
struct minsym_lookup_data
|
||
{
|
||
/* The name of the minimal symbol we are searching for. */
|
||
const char *name;
|
||
|
||
/* The field where the callback should store the minimal symbol
|
||
if found. It should be initialized to NULL before the search
|
||
is started. */
|
||
struct bound_minimal_symbol result;
|
||
};
|
||
|
||
/* A callback function for gdbarch_iterate_over_objfiles_in_search_order.
|
||
It searches by name for a minimal symbol within the given OBJFILE.
|
||
The arguments are passed via CB_DATA, which in reality is a pointer
|
||
to struct minsym_lookup_data. */
|
||
|
||
static int
|
||
minsym_lookup_iterator_cb (struct objfile *objfile, void *cb_data)
|
||
{
|
||
struct minsym_lookup_data *data = (struct minsym_lookup_data *) cb_data;
|
||
|
||
gdb_assert (data->result.minsym == NULL);
|
||
|
||
data->result = lookup_minimal_symbol (data->name, NULL, objfile);
|
||
|
||
/* The iterator should stop iff a match was found. */
|
||
return (data->result.minsym != NULL);
|
||
}
|
||
|
||
/* Given static link expression and the frame it lives in, look for the frame
|
||
the static links points to and return it. Return NULL if we could not find
|
||
such a frame. */
|
||
|
||
static struct frame_info *
|
||
follow_static_link (struct frame_info *frame,
|
||
const struct dynamic_prop *static_link)
|
||
{
|
||
CORE_ADDR upper_frame_base;
|
||
|
||
if (!dwarf2_evaluate_property (static_link, frame, NULL, &upper_frame_base))
|
||
return NULL;
|
||
|
||
/* Now climb up the stack frame until we reach the frame we are interested
|
||
in. */
|
||
for (; frame != NULL; frame = get_prev_frame (frame))
|
||
{
|
||
struct symbol *framefunc = get_frame_function (frame);
|
||
|
||
/* Stacks can be quite deep: give the user a chance to stop this. */
|
||
QUIT;
|
||
|
||
/* If we don't know how to compute FRAME's base address, don't give up:
|
||
maybe the frame we are looking for is upper in the stace frame. */
|
||
if (framefunc != NULL
|
||
&& SYMBOL_BLOCK_OPS (framefunc) != NULL
|
||
&& SYMBOL_BLOCK_OPS (framefunc)->get_frame_base != NULL
|
||
&& (SYMBOL_BLOCK_OPS (framefunc)->get_frame_base (framefunc, frame)
|
||
== upper_frame_base))
|
||
break;
|
||
}
|
||
|
||
return frame;
|
||
}
|
||
|
||
/* Assuming VAR is a symbol that can be reached from FRAME thanks to lexical
|
||
rules, look for the frame that is actually hosting VAR and return it. If,
|
||
for some reason, we found no such frame, return NULL.
|
||
|
||
This kind of computation is necessary to correctly handle lexically nested
|
||
functions.
|
||
|
||
Note that in some cases, we know what scope VAR comes from but we cannot
|
||
reach the specific frame that hosts the instance of VAR we are looking for.
|
||
For backward compatibility purposes (with old compilers), we then look for
|
||
the first frame that can host it. */
|
||
|
||
static struct frame_info *
|
||
get_hosting_frame (struct symbol *var, const struct block *var_block,
|
||
struct frame_info *frame)
|
||
{
|
||
const struct block *frame_block = NULL;
|
||
|
||
if (!symbol_read_needs_frame (var))
|
||
return NULL;
|
||
|
||
/* Some symbols for local variables have no block: this happens when they are
|
||
not produced by a debug information reader, for instance when GDB creates
|
||
synthetic symbols. Without block information, we must assume they are
|
||
local to FRAME. In this case, there is nothing to do. */
|
||
else if (var_block == NULL)
|
||
return frame;
|
||
|
||
/* We currently assume that all symbols with a location list need a frame.
|
||
This is true in practice because selecting the location description
|
||
requires to compute the CFA, hence requires a frame. However we have
|
||
tests that embed global/static symbols with null location lists.
|
||
We want to get <optimized out> instead of <frame required> when evaluating
|
||
them so return a frame instead of raising an error. */
|
||
else if (var_block == block_global_block (var_block)
|
||
|| var_block == block_static_block (var_block))
|
||
return frame;
|
||
|
||
/* We have to handle the "my_func::my_local_var" notation. This requires us
|
||
to look for upper frames when we find no block for the current frame: here
|
||
and below, handle when frame_block == NULL. */
|
||
if (frame != NULL)
|
||
frame_block = get_frame_block (frame, NULL);
|
||
|
||
/* Climb up the call stack until reaching the frame we are looking for. */
|
||
while (frame != NULL && frame_block != var_block)
|
||
{
|
||
/* Stacks can be quite deep: give the user a chance to stop this. */
|
||
QUIT;
|
||
|
||
if (frame_block == NULL)
|
||
{
|
||
frame = get_prev_frame (frame);
|
||
if (frame == NULL)
|
||
break;
|
||
frame_block = get_frame_block (frame, NULL);
|
||
}
|
||
|
||
/* If we failed to find the proper frame, fallback to the heuristic
|
||
method below. */
|
||
else if (frame_block == block_global_block (frame_block))
|
||
{
|
||
frame = NULL;
|
||
break;
|
||
}
|
||
|
||
/* Assuming we have a block for this frame: if we are at the function
|
||
level, the immediate upper lexical block is in an outer function:
|
||
follow the static link. */
|
||
else if (BLOCK_FUNCTION (frame_block))
|
||
{
|
||
const struct dynamic_prop *static_link
|
||
= block_static_link (frame_block);
|
||
int could_climb_up = 0;
|
||
|
||
if (static_link != NULL)
|
||
{
|
||
frame = follow_static_link (frame, static_link);
|
||
if (frame != NULL)
|
||
{
|
||
frame_block = get_frame_block (frame, NULL);
|
||
could_climb_up = frame_block != NULL;
|
||
}
|
||
}
|
||
if (!could_climb_up)
|
||
{
|
||
frame = NULL;
|
||
break;
|
||
}
|
||
}
|
||
|
||
else
|
||
/* We must be in some function nested lexical block. Just get the
|
||
outer block: both must share the same frame. */
|
||
frame_block = BLOCK_SUPERBLOCK (frame_block);
|
||
}
|
||
|
||
/* Old compilers may not provide a static link, or they may provide an
|
||
invalid one. For such cases, fallback on the old way to evaluate
|
||
non-local references: just climb up the call stack and pick the first
|
||
frame that contains the variable we are looking for. */
|
||
if (frame == NULL)
|
||
{
|
||
frame = block_innermost_frame (var_block);
|
||
if (frame == NULL)
|
||
{
|
||
if (BLOCK_FUNCTION (var_block)
|
||
&& !block_inlined_p (var_block)
|
||
&& SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block)))
|
||
error (_("No frame is currently executing in block %s."),
|
||
SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block)));
|
||
else
|
||
error (_("No frame is currently executing in specified"
|
||
" block"));
|
||
}
|
||
}
|
||
|
||
return frame;
|
||
}
|
||
|
||
/* A default implementation for the "la_read_var_value" hook in
|
||
the language vector which should work in most situations. */
|
||
|
||
struct value *
|
||
default_read_var_value (struct symbol *var, const struct block *var_block,
|
||
struct frame_info *frame)
|
||
{
|
||
struct value *v;
|
||
struct type *type = SYMBOL_TYPE (var);
|
||
CORE_ADDR addr;
|
||
enum symbol_needs_kind sym_need;
|
||
|
||
/* Call check_typedef on our type to make sure that, if TYPE is
|
||
a TYPE_CODE_TYPEDEF, its length is set to the length of the target type
|
||
instead of zero. However, we do not replace the typedef type by the
|
||
target type, because we want to keep the typedef in order to be able to
|
||
set the returned value type description correctly. */
|
||
check_typedef (type);
|
||
|
||
sym_need = symbol_read_needs (var);
|
||
if (sym_need == SYMBOL_NEEDS_FRAME)
|
||
gdb_assert (frame != NULL);
|
||
else if (sym_need == SYMBOL_NEEDS_REGISTERS && !target_has_registers)
|
||
error (_("Cannot read `%s' without registers"), SYMBOL_PRINT_NAME (var));
|
||
|
||
if (frame != NULL)
|
||
frame = get_hosting_frame (var, var_block, frame);
|
||
|
||
if (SYMBOL_COMPUTED_OPS (var) != NULL)
|
||
return SYMBOL_COMPUTED_OPS (var)->read_variable (var, frame);
|
||
|
||
switch (SYMBOL_CLASS (var))
|
||
{
|
||
case LOC_CONST:
|
||
if (is_dynamic_type (type))
|
||
{
|
||
/* Value is a constant byte-sequence and needs no memory access. */
|
||
type = resolve_dynamic_type (type, NULL, /* Unused address. */ 0);
|
||
}
|
||
/* Put the constant back in target format. */
|
||
v = allocate_value (type);
|
||
store_signed_integer (value_contents_raw (v), TYPE_LENGTH (type),
|
||
gdbarch_byte_order (get_type_arch (type)),
|
||
(LONGEST) SYMBOL_VALUE (var));
|
||
VALUE_LVAL (v) = not_lval;
|
||
return v;
|
||
|
||
case LOC_LABEL:
|
||
/* Put the constant back in target format. */
|
||
v = allocate_value (type);
|
||
if (overlay_debugging)
|
||
{
|
||
addr
|
||
= symbol_overlayed_address (SYMBOL_VALUE_ADDRESS (var),
|
||
SYMBOL_OBJ_SECTION (symbol_objfile (var),
|
||
var));
|
||
|
||
store_typed_address (value_contents_raw (v), type, addr);
|
||
}
|
||
else
|
||
store_typed_address (value_contents_raw (v), type,
|
||
SYMBOL_VALUE_ADDRESS (var));
|
||
VALUE_LVAL (v) = not_lval;
|
||
return v;
|
||
|
||
case LOC_CONST_BYTES:
|
||
if (is_dynamic_type (type))
|
||
{
|
||
/* Value is a constant byte-sequence and needs no memory access. */
|
||
type = resolve_dynamic_type (type, NULL, /* Unused address. */ 0);
|
||
}
|
||
v = allocate_value (type);
|
||
memcpy (value_contents_raw (v), SYMBOL_VALUE_BYTES (var),
|
||
TYPE_LENGTH (type));
|
||
VALUE_LVAL (v) = not_lval;
|
||
return v;
|
||
|
||
case LOC_STATIC:
|
||
if (overlay_debugging)
|
||
addr = symbol_overlayed_address (SYMBOL_VALUE_ADDRESS (var),
|
||
SYMBOL_OBJ_SECTION (symbol_objfile (var),
|
||
var));
|
||
else
|
||
addr = SYMBOL_VALUE_ADDRESS (var);
|
||
break;
|
||
|
||
case LOC_ARG:
|
||
addr = get_frame_args_address (frame);
|
||
if (!addr)
|
||
error (_("Unknown argument list address for `%s'."),
|
||
SYMBOL_PRINT_NAME (var));
|
||
addr += SYMBOL_VALUE (var);
|
||
break;
|
||
|
||
case LOC_REF_ARG:
|
||
{
|
||
struct value *ref;
|
||
CORE_ADDR argref;
|
||
|
||
argref = get_frame_args_address (frame);
|
||
if (!argref)
|
||
error (_("Unknown argument list address for `%s'."),
|
||
SYMBOL_PRINT_NAME (var));
|
||
argref += SYMBOL_VALUE (var);
|
||
ref = value_at (lookup_pointer_type (type), argref);
|
||
addr = value_as_address (ref);
|
||
break;
|
||
}
|
||
|
||
case LOC_LOCAL:
|
||
addr = get_frame_locals_address (frame);
|
||
addr += SYMBOL_VALUE (var);
|
||
break;
|
||
|
||
case LOC_TYPEDEF:
|
||
error (_("Cannot look up value of a typedef `%s'."),
|
||
SYMBOL_PRINT_NAME (var));
|
||
break;
|
||
|
||
case LOC_BLOCK:
|
||
if (overlay_debugging)
|
||
addr = symbol_overlayed_address
|
||
(BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (var)),
|
||
SYMBOL_OBJ_SECTION (symbol_objfile (var), var));
|
||
else
|
||
addr = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (var));
|
||
break;
|
||
|
||
case LOC_REGISTER:
|
||
case LOC_REGPARM_ADDR:
|
||
{
|
||
int regno = SYMBOL_REGISTER_OPS (var)
|
||
->register_number (var, get_frame_arch (frame));
|
||
struct value *regval;
|
||
|
||
if (SYMBOL_CLASS (var) == LOC_REGPARM_ADDR)
|
||
{
|
||
regval = value_from_register (lookup_pointer_type (type),
|
||
regno,
|
||
frame);
|
||
|
||
if (regval == NULL)
|
||
error (_("Value of register variable not available for `%s'."),
|
||
SYMBOL_PRINT_NAME (var));
|
||
|
||
addr = value_as_address (regval);
|
||
}
|
||
else
|
||
{
|
||
regval = value_from_register (type, regno, frame);
|
||
|
||
if (regval == NULL)
|
||
error (_("Value of register variable not available for `%s'."),
|
||
SYMBOL_PRINT_NAME (var));
|
||
return regval;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case LOC_COMPUTED:
|
||
gdb_assert_not_reached (_("LOC_COMPUTED variable missing a method"));
|
||
|
||
case LOC_UNRESOLVED:
|
||
{
|
||
struct minsym_lookup_data lookup_data;
|
||
struct minimal_symbol *msym;
|
||
struct obj_section *obj_section;
|
||
|
||
memset (&lookup_data, 0, sizeof (lookup_data));
|
||
lookup_data.name = SYMBOL_LINKAGE_NAME (var);
|
||
|
||
gdbarch_iterate_over_objfiles_in_search_order
|
||
(symbol_arch (var),
|
||
minsym_lookup_iterator_cb, &lookup_data,
|
||
symbol_objfile (var));
|
||
msym = lookup_data.result.minsym;
|
||
|
||
/* If we can't find the minsym there's a problem in the symbol info.
|
||
The symbol exists in the debug info, but it's missing in the minsym
|
||
table. */
|
||
if (msym == NULL)
|
||
{
|
||
const char *flavour_name
|
||
= objfile_flavour_name (symbol_objfile (var));
|
||
|
||
/* We can't get here unless we've opened the file, so flavour_name
|
||
can't be NULL. */
|
||
gdb_assert (flavour_name != NULL);
|
||
error (_("Missing %s symbol \"%s\"."),
|
||
flavour_name, SYMBOL_LINKAGE_NAME (var));
|
||
}
|
||
obj_section = MSYMBOL_OBJ_SECTION (lookup_data.result.objfile, msym);
|
||
/* Relocate address, unless there is no section or the variable is
|
||
a TLS variable. */
|
||
if (obj_section == NULL
|
||
|| (obj_section->the_bfd_section->flags & SEC_THREAD_LOCAL) != 0)
|
||
addr = MSYMBOL_VALUE_RAW_ADDRESS (msym);
|
||
else
|
||
addr = BMSYMBOL_VALUE_ADDRESS (lookup_data.result);
|
||
if (overlay_debugging)
|
||
addr = symbol_overlayed_address (addr, obj_section);
|
||
/* Determine address of TLS variable. */
|
||
if (obj_section
|
||
&& (obj_section->the_bfd_section->flags & SEC_THREAD_LOCAL) != 0)
|
||
addr = target_translate_tls_address (obj_section->objfile, addr);
|
||
}
|
||
break;
|
||
|
||
case LOC_OPTIMIZED_OUT:
|
||
if (is_dynamic_type (type))
|
||
type = resolve_dynamic_type (type, NULL, /* Unused address. */ 0);
|
||
return allocate_optimized_out_value (type);
|
||
|
||
default:
|
||
error (_("Cannot look up value of a botched symbol `%s'."),
|
||
SYMBOL_PRINT_NAME (var));
|
||
break;
|
||
}
|
||
|
||
v = value_at_lazy (type, addr);
|
||
return v;
|
||
}
|
||
|
||
/* Calls VAR's language la_read_var_value hook with the given arguments. */
|
||
|
||
struct value *
|
||
read_var_value (struct symbol *var, const struct block *var_block,
|
||
struct frame_info *frame)
|
||
{
|
||
const struct language_defn *lang = language_def (SYMBOL_LANGUAGE (var));
|
||
|
||
gdb_assert (lang != NULL);
|
||
gdb_assert (lang->la_read_var_value != NULL);
|
||
|
||
return lang->la_read_var_value (var, var_block, frame);
|
||
}
|
||
|
||
/* Install default attributes for register values. */
|
||
|
||
struct value *
|
||
default_value_from_register (struct gdbarch *gdbarch, struct type *type,
|
||
int regnum, struct frame_id frame_id)
|
||
{
|
||
int len = TYPE_LENGTH (type);
|
||
struct value *value = allocate_value (type);
|
||
struct frame_info *frame;
|
||
|
||
VALUE_LVAL (value) = lval_register;
|
||
frame = frame_find_by_id (frame_id);
|
||
|
||
if (frame == NULL)
|
||
frame_id = null_frame_id;
|
||
else
|
||
frame_id = get_frame_id (get_next_frame_sentinel_okay (frame));
|
||
|
||
VALUE_NEXT_FRAME_ID (value) = frame_id;
|
||
VALUE_REGNUM (value) = regnum;
|
||
|
||
/* Any structure stored in more than one register will always be
|
||
an integral number of registers. Otherwise, you need to do
|
||
some fiddling with the last register copied here for little
|
||
endian machines. */
|
||
if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG
|
||
&& len < register_size (gdbarch, regnum))
|
||
/* Big-endian, and we want less than full size. */
|
||
set_value_offset (value, register_size (gdbarch, regnum) - len);
|
||
else
|
||
set_value_offset (value, 0);
|
||
|
||
return value;
|
||
}
|
||
|
||
/* VALUE must be an lval_register value. If regnum is the value's
|
||
associated register number, and len the length of the values type,
|
||
read one or more registers in FRAME, starting with register REGNUM,
|
||
until we've read LEN bytes.
|
||
|
||
If any of the registers we try to read are optimized out, then mark the
|
||
complete resulting value as optimized out. */
|
||
|
||
void
|
||
read_frame_register_value (struct value *value, struct frame_info *frame)
|
||
{
|
||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||
LONGEST offset = 0;
|
||
LONGEST reg_offset = value_offset (value);
|
||
int regnum = VALUE_REGNUM (value);
|
||
int len = type_length_units (check_typedef (value_type (value)));
|
||
|
||
gdb_assert (VALUE_LVAL (value) == lval_register);
|
||
|
||
/* Skip registers wholly inside of REG_OFFSET. */
|
||
while (reg_offset >= register_size (gdbarch, regnum))
|
||
{
|
||
reg_offset -= register_size (gdbarch, regnum);
|
||
regnum++;
|
||
}
|
||
|
||
/* Copy the data. */
|
||
while (len > 0)
|
||
{
|
||
struct value *regval = get_frame_register_value (frame, regnum);
|
||
int reg_len = type_length_units (value_type (regval)) - reg_offset;
|
||
|
||
/* If the register length is larger than the number of bytes
|
||
remaining to copy, then only copy the appropriate bytes. */
|
||
if (reg_len > len)
|
||
reg_len = len;
|
||
|
||
value_contents_copy (value, offset, regval, reg_offset, reg_len);
|
||
|
||
offset += reg_len;
|
||
len -= reg_len;
|
||
reg_offset = 0;
|
||
regnum++;
|
||
}
|
||
}
|
||
|
||
/* Return a value of type TYPE, stored in register REGNUM, in frame FRAME. */
|
||
|
||
struct value *
|
||
value_from_register (struct type *type, int regnum, struct frame_info *frame)
|
||
{
|
||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||
struct type *type1 = check_typedef (type);
|
||
struct value *v;
|
||
|
||
if (gdbarch_convert_register_p (gdbarch, regnum, type1))
|
||
{
|
||
int optim, unavail, ok;
|
||
|
||
/* The ISA/ABI need to something weird when obtaining the
|
||
specified value from this register. It might need to
|
||
re-order non-adjacent, starting with REGNUM (see MIPS and
|
||
i386). It might need to convert the [float] register into
|
||
the corresponding [integer] type (see Alpha). The assumption
|
||
is that gdbarch_register_to_value populates the entire value
|
||
including the location. */
|
||
v = allocate_value (type);
|
||
VALUE_LVAL (v) = lval_register;
|
||
VALUE_NEXT_FRAME_ID (v) = get_frame_id (get_next_frame_sentinel_okay (frame));
|
||
VALUE_REGNUM (v) = regnum;
|
||
ok = gdbarch_register_to_value (gdbarch, frame, regnum, type1,
|
||
value_contents_raw (v), &optim,
|
||
&unavail);
|
||
|
||
if (!ok)
|
||
{
|
||
if (optim)
|
||
mark_value_bytes_optimized_out (v, 0, TYPE_LENGTH (type));
|
||
if (unavail)
|
||
mark_value_bytes_unavailable (v, 0, TYPE_LENGTH (type));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Construct the value. */
|
||
v = gdbarch_value_from_register (gdbarch, type,
|
||
regnum, get_frame_id (frame));
|
||
|
||
/* Get the data. */
|
||
read_frame_register_value (v, frame);
|
||
}
|
||
|
||
return v;
|
||
}
|
||
|
||
/* Return contents of register REGNUM in frame FRAME as address.
|
||
Will abort if register value is not available. */
|
||
|
||
CORE_ADDR
|
||
address_from_register (int regnum, struct frame_info *frame)
|
||
{
|
||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||
struct type *type = builtin_type (gdbarch)->builtin_data_ptr;
|
||
struct value *value;
|
||
CORE_ADDR result;
|
||
int regnum_max_excl = gdbarch_num_cooked_regs (gdbarch);
|
||
|
||
if (regnum < 0 || regnum >= regnum_max_excl)
|
||
error (_("Invalid register #%d, expecting 0 <= # < %d"), regnum,
|
||
regnum_max_excl);
|
||
|
||
/* This routine may be called during early unwinding, at a time
|
||
where the ID of FRAME is not yet known. Calling value_from_register
|
||
would therefore abort in get_frame_id. However, since we only need
|
||
a temporary value that is never used as lvalue, we actually do not
|
||
really need to set its VALUE_NEXT_FRAME_ID. Therefore, we re-implement
|
||
the core of value_from_register, but use the null_frame_id. */
|
||
|
||
/* Some targets require a special conversion routine even for plain
|
||
pointer types. Avoid constructing a value object in those cases. */
|
||
if (gdbarch_convert_register_p (gdbarch, regnum, type))
|
||
{
|
||
gdb_byte *buf = (gdb_byte *) alloca (TYPE_LENGTH (type));
|
||
int optim, unavail, ok;
|
||
|
||
ok = gdbarch_register_to_value (gdbarch, frame, regnum, type,
|
||
buf, &optim, &unavail);
|
||
if (!ok)
|
||
{
|
||
/* This function is used while computing a location expression.
|
||
Complain about the value being optimized out, rather than
|
||
letting value_as_address complain about some random register
|
||
the expression depends on not being saved. */
|
||
error_value_optimized_out ();
|
||
}
|
||
|
||
return unpack_long (type, buf);
|
||
}
|
||
|
||
value = gdbarch_value_from_register (gdbarch, type, regnum, null_frame_id);
|
||
read_frame_register_value (value, frame);
|
||
|
||
if (value_optimized_out (value))
|
||
{
|
||
/* This function is used while computing a location expression.
|
||
Complain about the value being optimized out, rather than
|
||
letting value_as_address complain about some random register
|
||
the expression depends on not being saved. */
|
||
error_value_optimized_out ();
|
||
}
|
||
|
||
result = value_as_address (value);
|
||
release_value (value);
|
||
|
||
return result;
|
||
}
|
||
|
||
#if GDB_SELF_TEST
|
||
namespace selftests {
|
||
namespace findvar_tests {
|
||
|
||
/* Function to test copy_integer_to_size. Store SOURCE_VAL with size
|
||
SOURCE_SIZE to a buffer, making sure no sign extending happens at this
|
||
stage. Copy buffer to a new buffer using copy_integer_to_size. Extract
|
||
copied value and compare to DEST_VALU. Copy again with a signed
|
||
copy_integer_to_size and compare to DEST_VALS. Do everything for both
|
||
LITTLE and BIG target endians. Use unsigned values throughout to make
|
||
sure there are no implicit sign extensions. */
|
||
|
||
static void
|
||
do_cint_test (ULONGEST dest_valu, ULONGEST dest_vals, int dest_size,
|
||
ULONGEST src_val, int src_size)
|
||
{
|
||
for (int i = 0; i < 2 ; i++)
|
||
{
|
||
gdb_byte srcbuf[sizeof (ULONGEST)] = {};
|
||
gdb_byte destbuf[sizeof (ULONGEST)] = {};
|
||
enum bfd_endian byte_order = i ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
|
||
|
||
/* Fill the src buffer (and later the dest buffer) with non-zero junk,
|
||
to ensure zero extensions aren't hidden. */
|
||
memset (srcbuf, 0xaa, sizeof (srcbuf));
|
||
|
||
/* Store (and later extract) using unsigned to ensure there are no sign
|
||
extensions. */
|
||
store_unsigned_integer (srcbuf, src_size, byte_order, src_val);
|
||
|
||
/* Test unsigned. */
|
||
memset (destbuf, 0xaa, sizeof (destbuf));
|
||
copy_integer_to_size (destbuf, dest_size, srcbuf, src_size, false,
|
||
byte_order);
|
||
SELF_CHECK (dest_valu == extract_unsigned_integer (destbuf, dest_size,
|
||
byte_order));
|
||
|
||
/* Test signed. */
|
||
memset (destbuf, 0xaa, sizeof (destbuf));
|
||
copy_integer_to_size (destbuf, dest_size, srcbuf, src_size, true,
|
||
byte_order);
|
||
SELF_CHECK (dest_vals == extract_unsigned_integer (destbuf, dest_size,
|
||
byte_order));
|
||
}
|
||
}
|
||
|
||
static void
|
||
copy_integer_to_size_test ()
|
||
{
|
||
/* Destination is bigger than the source, which has the signed bit unset. */
|
||
do_cint_test (0x12345678, 0x12345678, 8, 0x12345678, 4);
|
||
do_cint_test (0x345678, 0x345678, 8, 0x12345678, 3);
|
||
|
||
/* Destination is bigger than the source, which has the signed bit set. */
|
||
do_cint_test (0xdeadbeef, 0xffffffffdeadbeef, 8, 0xdeadbeef, 4);
|
||
do_cint_test (0xadbeef, 0xffffffffffadbeef, 8, 0xdeadbeef, 3);
|
||
|
||
/* Destination is smaller than the source. */
|
||
do_cint_test (0x5678, 0x5678, 2, 0x12345678, 3);
|
||
do_cint_test (0xbeef, 0xbeef, 2, 0xdeadbeef, 3);
|
||
|
||
/* Destination and source are the same size. */
|
||
do_cint_test (0x8765432112345678, 0x8765432112345678, 8, 0x8765432112345678,
|
||
8);
|
||
do_cint_test (0x432112345678, 0x432112345678, 6, 0x8765432112345678, 6);
|
||
do_cint_test (0xfeedbeaddeadbeef, 0xfeedbeaddeadbeef, 8, 0xfeedbeaddeadbeef,
|
||
8);
|
||
do_cint_test (0xbeaddeadbeef, 0xbeaddeadbeef, 6, 0xfeedbeaddeadbeef, 6);
|
||
|
||
/* Destination is bigger than the source. Source is bigger than 32bits. */
|
||
do_cint_test (0x3412345678, 0x3412345678, 8, 0x3412345678, 6);
|
||
do_cint_test (0xff12345678, 0xff12345678, 8, 0xff12345678, 6);
|
||
do_cint_test (0x432112345678, 0x432112345678, 8, 0x8765432112345678, 6);
|
||
do_cint_test (0xff2112345678, 0xffffff2112345678, 8, 0xffffff2112345678, 6);
|
||
}
|
||
|
||
} // namespace findvar_test
|
||
} // namespace selftests
|
||
|
||
#endif
|
||
|
||
void
|
||
_initialize_findvar (void)
|
||
{
|
||
#if GDB_SELF_TEST
|
||
selftests::register_test
|
||
("copy_integer_to_size",
|
||
selftests::findvar_tests::copy_integer_to_size_test);
|
||
#endif
|
||
}
|