/* Print values for GNU debugger GDB.
- Copyright (C) 1986-2019 Free Software Foundation, Inc.
+ Copyright (C) 1986-2022 Free Software Foundation, Inc.
This file is part of GDB.
#include "source.h"
#include "gdbsupport/byte-vector.h"
#include "gdbsupport/gdb_optional.h"
+#include "safe-ctype.h"
+#include "gdbsupport/rsp-low.h"
+
+/* Chain containing all defined memory-tag subcommands. */
+
+static struct cmd_list_element *memory_tag_list;
/* Last specified output format. */
static int last_count;
+/* Last specified tag-printing option. */
+
+static bool last_print_tags = false;
+
/* Default address to examine next, and associated architecture. */
static struct gdbarch *next_gdbarch;
/* Append the source filename and linenumber of the symbol when
printing a symbolic value as `<symbol at filename:linenum>' if set. */
-static int print_symbol_filename = 0;
+static bool print_symbol_filename = false;
static void
show_print_symbol_filename (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
static int current_display_number;
+/* Last allocated display number. */
+
+static int display_number;
+
struct display
{
- /* Chain link to next auto-display item. */
- struct display *next;
+ display (const char *exp_string_, expression_up &&exp_,
+ const struct format_data &format_, struct program_space *pspace_,
+ const struct block *block_)
+ : exp_string (exp_string_),
+ exp (std::move (exp_)),
+ number (++display_number),
+ format (format_),
+ pspace (pspace_),
+ block (block_),
+ enabled_p (true)
+ {
+ }
/* The expression as the user typed it. */
- char *exp_string;
+ std::string exp_string;
/* Expression to be evaluated and displayed. */
expression_up exp;
const struct block *block;
/* Status of this display (enabled or disabled). */
- int enabled_p;
+ bool enabled_p;
};
-/* Chain of expressions whose values should be displayed
- automatically each time the program stops. */
-
-static struct display *display_chain;
+/* Expressions whose values should be displayed automatically each
+ time the program stops. */
-static int display_number;
-
-/* Walk the following statement or block through all displays.
- ALL_DISPLAYS_SAFE does so even if the statement deletes the current
- display. */
-
-#define ALL_DISPLAYS(B) \
- for (B = display_chain; B; B = B->next)
-
-#define ALL_DISPLAYS_SAFE(B,TMP) \
- for (B = display_chain; \
- B ? (TMP = B->next, 1): 0; \
- B = TMP)
+static std::vector<std::unique_ptr<struct display>> all_displays;
/* Prototypes for local functions. */
val.size = '?';
val.count = 1;
val.raw = 0;
+ val.print_tags = false;
if (*p == '-')
{
val.raw = 1;
p++;
}
+ else if (*p == 'm')
+ {
+ val.print_tags = true;
+ p++;
+ }
else if (*p >= 'a' && *p <= 'z')
val.format = *p++;
else
/* We often wrap here if there are long symbolic names. */
wrap_here (" ");
next_address = (value_address (val)
- + gdb_print_insn (get_type_arch (type),
+ + gdb_print_insn (type->arch (),
value_address (val), stream,
&branch_delay_insns));
return;
}
if (options->format == 0 || options->format == 's'
- || TYPE_CODE (type) == TYPE_CODE_REF
- || TYPE_CODE (type) == TYPE_CODE_ARRAY
- || TYPE_CODE (type) == TYPE_CODE_STRING
- || TYPE_CODE (type) == TYPE_CODE_STRUCT
- || TYPE_CODE (type) == TYPE_CODE_UNION
- || TYPE_CODE (type) == TYPE_CODE_NAMESPACE)
+ || type->code () == TYPE_CODE_VOID
+ || type->code () == TYPE_CODE_REF
+ || type->code () == TYPE_CODE_ARRAY
+ || type->code () == TYPE_CODE_STRING
+ || type->code () == TYPE_CODE_STRUCT
+ || type->code () == TYPE_CODE_UNION
+ || type->code () == TYPE_CODE_NAMESPACE)
value_print (val, stream, options);
else
/* User specified format, so don't look to the type to tell us
what to do. */
- val_print_scalar_formatted (type,
- value_embedded_offset (val),
- val,
- options, size, stream);
+ value_print_scalar_formatted (val, options, size, stream);
}
/* Return builtin floating point type of same length as TYPE.
static struct type *
float_type_from_length (struct type *type)
{
- struct gdbarch *gdbarch = get_type_arch (type);
+ struct gdbarch *gdbarch = type->arch ();
const struct builtin_type *builtin = builtin_type (gdbarch);
if (TYPE_LENGTH (type) == TYPE_LENGTH (builtin->builtin_float))
const struct value_print_options *options,
int size, struct ui_file *stream)
{
- struct gdbarch *gdbarch = get_type_arch (type);
+ struct gdbarch *gdbarch = type->arch ();
unsigned int len = TYPE_LENGTH (type);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ enum bfd_endian byte_order = type_byte_order (type);
/* String printing should go through val_print_scalar_formatted. */
gdb_assert (options->format != 's');
/* If the value is a pointer, and pointers and addresses are not the
same, then at this point, the value's length (in target bytes) is
gdbarch_addr_bit/TARGET_CHAR_BIT, not TYPE_LENGTH (type). */
- if (TYPE_CODE (type) == TYPE_CODE_PTR)
+ if (type->code () == TYPE_CODE_PTR)
len = gdbarch_addr_bit (gdbarch) / TARGET_CHAR_BIT;
/* If we are printing it as unsigned, truncate it in case it is actually
a negative signed value (e.g. "print/u (short)-1" should print 65535
(if shorts are 16 bits) instead of 4294967295). */
if (options->format != 'c'
- && (options->format != 'd' || TYPE_UNSIGNED (type)))
+ && (options->format != 'd' || type->is_unsigned ()))
{
if (len < TYPE_LENGTH (type) && byte_order == BFD_ENDIAN_BIG)
valaddr += TYPE_LENGTH (type) - len;
}
+ /* Allow LEN == 0, and in this case, don't assume that VALADDR is
+ valid. */
+ const gdb_byte zero = 0;
+ if (len == 0)
+ {
+ len = 1;
+ valaddr = &zero;
+ }
+
if (size != 0 && (options->format == 'x' || options->format == 't'))
{
/* Truncate to fit. */
/* Historically gdb has printed floats by first casting them to a
long, and then printing the long. PR cli/16242 suggests changing
- this to using C-style hex float format. */
- gdb::byte_vector converted_float_bytes;
- if (TYPE_CODE (type) == TYPE_CODE_FLT
- && (options->format == 'o'
- || options->format == 'x'
- || options->format == 't'
- || options->format == 'z'
- || options->format == 'd'
- || options->format == 'u'))
- {
- LONGEST val_long = unpack_long (type, valaddr);
- converted_float_bytes.resize (TYPE_LENGTH (type));
- store_signed_integer (converted_float_bytes.data (), TYPE_LENGTH (type),
- byte_order, val_long);
- valaddr = converted_float_bytes.data ();
+ this to using C-style hex float format.
+
+ Biased range types and sub-word scalar types must also be handled
+ here; the value is correctly computed by unpack_long. */
+ gdb::byte_vector converted_bytes;
+ /* Some cases below will unpack the value again. In the biased
+ range case, we want to avoid this, so we store the unpacked value
+ here for possible use later. */
+ gdb::optional<LONGEST> val_long;
+ if (((type->code () == TYPE_CODE_FLT
+ || is_fixed_point_type (type))
+ && (options->format == 'o'
+ || options->format == 'x'
+ || options->format == 't'
+ || options->format == 'z'
+ || options->format == 'd'
+ || options->format == 'u'))
+ || (type->code () == TYPE_CODE_RANGE && type->bounds ()->bias != 0)
+ || type->bit_size_differs_p ())
+ {
+ val_long.emplace (unpack_long (type, valaddr));
+ converted_bytes.resize (TYPE_LENGTH (type));
+ store_signed_integer (converted_bytes.data (), TYPE_LENGTH (type),
+ byte_order, *val_long);
+ valaddr = converted_bytes.data ();
}
/* Printing a non-float type as 'f' will interpret the data as if it were
of a floating-point type of the same length, if that exists. Otherwise,
the data is printed as integer. */
char format = options->format;
- if (format == 'f' && TYPE_CODE (type) != TYPE_CODE_FLT)
+ if (format == 'f' && type->code () != TYPE_CODE_FLT)
{
type = float_type_from_length (type);
- if (TYPE_CODE (type) != TYPE_CODE_FLT)
- format = 0;
+ if (type->code () != TYPE_CODE_FLT)
+ format = 0;
}
switch (format)
print_decimal_chars (stream, valaddr, len, false, byte_order);
break;
case 0:
- if (TYPE_CODE (type) != TYPE_CODE_FLT)
+ if (type->code () != TYPE_CODE_FLT)
{
- print_decimal_chars (stream, valaddr, len, !TYPE_UNSIGNED (type),
+ print_decimal_chars (stream, valaddr, len, !type->is_unsigned (),
byte_order);
break;
}
{
struct value_print_options opts = *options;
- LONGEST val_long = unpack_long (type, valaddr);
+ if (!val_long.has_value ())
+ val_long.emplace (unpack_long (type, valaddr));
opts.format = 0;
- if (TYPE_UNSIGNED (type))
+ if (type->is_unsigned ())
type = builtin_type (gdbarch)->builtin_true_unsigned_char;
- else
+ else
type = builtin_type (gdbarch)->builtin_true_char;
- value_print (value_from_longest (type, val_long), stream, &opts);
+ value_print (value_from_longest (type, *val_long), stream, &opts);
}
break;
case 'a':
{
- CORE_ADDR addr = unpack_pointer (type, valaddr);
-
- print_address (gdbarch, addr, stream);
+ if (!val_long.has_value ())
+ val_long.emplace (unpack_long (type, valaddr));
+ print_address (gdbarch, *val_long, stream);
}
break;
int line = 0;
if (build_address_symbolic (gdbarch, addr, do_demangle, false, &name,
- &offset, &filename, &line, &unmapped))
+ &offset, &filename, &line, &unmapped))
return 0;
fputs_filtered (leadin, stream);
name_location = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (symbol));
if (do_demangle || asm_demangle)
- name_temp = SYMBOL_PRINT_NAME (symbol);
+ name_temp = symbol->print_name ();
else
- name_temp = SYMBOL_LINKAGE_NAME (symbol);
+ name_temp = symbol->linkage_name ();
}
if (msymbol.minsym != NULL
3) The symbol address is not identical to that of the address
under consideration. */
if (symbol == NULL ||
- (!prefer_sym_over_minsym
+ (!prefer_sym_over_minsym
&& BMSYMBOL_VALUE_ADDRESS (msymbol) == addr
&& name_location != addr))
{
symbol = 0;
name_location = BMSYMBOL_VALUE_ADDRESS (msymbol);
if (do_demangle || asm_demangle)
- name_temp = MSYMBOL_PRINT_NAME (msymbol.minsym);
+ name_temp = msymbol.minsym->print_name ();
else
- name_temp = MSYMBOL_LINKAGE_NAME (msymbol.minsym);
+ name_temp = msymbol.minsym->linkage_name ();
}
}
if (symbol == NULL && msymbol.minsym == NULL)
static CORE_ADDR
find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
- int inst_count, int *inst_read)
+ int inst_count, int *inst_read)
{
/* The vector PCS is used to store instruction addresses within
a pc range. */
pcs.clear ();
sal = find_pc_sect_line (loop_start, NULL, 1);
if (sal.line <= 0)
- {
- /* We reach here when line info is not available. In this case,
- we print a message and just exit the loop. The return value
- is calculated after the loop. */
- printf_filtered (_("No line number information available "
- "for address "));
- wrap_here (" ");
- print_address (gdbarch, loop_start - 1, gdb_stdout);
- printf_filtered ("\n");
- break;
- }
+ {
+ /* We reach here when line info is not available. In this case,
+ we print a message and just exit the loop. The return value
+ is calculated after the loop. */
+ printf_filtered (_("No line number information available "
+ "for address "));
+ wrap_here (" ");
+ print_address (gdbarch, loop_start - 1, gdb_stdout);
+ printf_filtered ("\n");
+ break;
+ }
loop_end = loop_start;
loop_start = sal.pc;
/* This loop pushes instruction addresses in the range from
- LOOP_START to LOOP_END. */
+ LOOP_START to LOOP_END. */
for (p = loop_start; p < loop_end;)
- {
+ {
pcs.push_back (p);
- p += gdb_insn_length (gdbarch, p);
- }
+ p += gdb_insn_length (gdbarch, p);
+ }
inst_count -= pcs.size ();
*inst_read += pcs.size ();
the reason below.
Let's assume the following instruction addresses and run 'x/-4i 0x400e'.
Line X of File
- 0x4000
- 0x4001
- 0x4005
+ 0x4000
+ 0x4001
+ 0x4005
Line Y of File
- 0x4009
- 0x400c
+ 0x4009
+ 0x400c
=> 0x400e
- 0x4011
+ 0x4011
find_instruction_backward is called with INST_COUNT = 4 and expected to
return 0x4001. When we reach here, INST_COUNT is set to -1 because
it was subtracted by 2 (from Line Y) and 3 (from Line X). The value
static int
read_memory_backward (struct gdbarch *gdbarch,
- CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+ CORE_ADDR memaddr, gdb_byte *myaddr, int len)
{
int errcode;
int nread; /* Number of bytes actually read. */
memaddr += len;
myaddr += len;
for (nread = 0; nread < len; ++nread)
- {
- errcode = target_read_memory (--memaddr, --myaddr, 1);
- if (errcode != 0)
- {
- /* The read was unsuccessful, so exit the loop. */
- printf_filtered (_("Cannot access memory at address %s\n"),
- paddress (gdbarch, memaddr));
- break;
- }
- }
+ {
+ errcode = target_read_memory (--memaddr, --myaddr, 1);
+ if (errcode != 0)
+ {
+ /* The read was unsuccessful, so exit the loop. */
+ printf_filtered (_("Cannot access memory at address %s\n"),
+ paddress (gdbarch, memaddr));
+ break;
+ }
+ }
}
return nread;
}
static CORE_ADDR
find_string_backward (struct gdbarch *gdbarch,
- CORE_ADDR addr, int count, int char_size,
- const struct value_print_options *options,
- int *strings_counted)
+ CORE_ADDR addr, int count, int char_size,
+ const struct value_print_options *options,
+ int *strings_counted)
{
const int chunk_size = 0x20;
int read_error = 0;
addr -= chars_to_read * char_size;
chars_read = read_memory_backward (gdbarch, addr, buffer.data (),
- chars_to_read * char_size);
+ chars_to_read * char_size);
chars_read /= char_size;
read_error = (chars_read == chars_to_read) ? 0 : 1;
/* Searching for '\0' from the end of buffer in backward direction. */
for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
- {
- int offset = (chars_to_read - i - 1) * char_size;
-
- if (integer_is_zero (&buffer[offset], char_size)
- || chars_counted == options->print_max)
- {
- /* Found '\0' or reached print_max. As OFFSET is the offset to
- '\0', we add CHAR_SIZE to return the start address of
- a string. */
- --count;
- string_start_addr = addr + offset + char_size;
- chars_counted = 0;
- }
- }
+ {
+ int offset = (chars_to_read - i - 1) * char_size;
+
+ if (integer_is_zero (&buffer[offset], char_size)
+ || chars_counted == options->print_max)
+ {
+ /* Found '\0' or reached print_max. As OFFSET is the offset to
+ '\0', we add CHAR_SIZE to return the start address of
+ a string. */
+ --count;
+ string_start_addr = addr + offset + char_size;
+ chars_counted = 0;
+ }
+ }
}
/* Update STRINGS_COUNTED with the actual number of loaded strings. */
if (read_error != 0)
{
/* In error case, STRING_START_ADDR is pointing to the string that
- was last successfully loaded. Rewind the partially loaded string. */
+ was last successfully loaded. Rewind the partially loaded string. */
string_start_addr -= chars_counted * char_size;
}
else if (size == 'w')
char_type = builtin_type (next_gdbarch)->builtin_char32;
if (char_type)
- val_type = char_type;
+ val_type = char_type;
else
- {
+ {
if (size != '\0' && size != 'b')
warning (_("Unable to display strings with "
"size '%c', using 'b' instead."), size);
size = 'b';
val_type = builtin_type (next_gdbarch)->builtin_int8;
- }
+ }
}
maxelts = 8;
if (count < 0)
{
/* This is the negative repeat count case.
- We rewind the address based on the given repeat count and format,
- then examine memory from there in forward direction. */
+ We rewind the address based on the given repeat count and format,
+ then examine memory from there in forward direction. */
count = -count;
if (format == 'i')
- {
- next_address = find_instruction_backward (gdbarch, addr, count,
- &count);
- }
+ {
+ next_address = find_instruction_backward (gdbarch, addr, count,
+ &count);
+ }
else if (format == 's')
- {
- next_address = find_string_backward (gdbarch, addr, count,
- TYPE_LENGTH (val_type),
- &opts, &count);
- }
+ {
+ next_address = find_string_backward (gdbarch, addr, count,
+ TYPE_LENGTH (val_type),
+ &opts, &count);
+ }
else
- {
- next_address = addr - count * TYPE_LENGTH (val_type);
- }
+ {
+ next_address = addr - count * TYPE_LENGTH (val_type);
+ }
/* The following call to print_formatted updates next_address in every
- iteration. In backward case, we store the start address here
- and update next_address with it before exiting the function. */
+ iteration. In backward case, we store the start address here
+ and update next_address with it before exiting the function. */
addr_rewound = (format == 's'
- ? next_address - TYPE_LENGTH (val_type)
- : next_address);
+ ? next_address - TYPE_LENGTH (val_type)
+ : next_address);
need_to_update_next_address = 1;
}
+ /* Whether we need to print the memory tag information for the current
+ address range. */
+ bool print_range_tag = true;
+ uint32_t gsize = gdbarch_memtag_granule_size (gdbarch);
+
/* Print as many objects as specified in COUNT, at most maxelts per line,
with the address of the next one at the start of each line. */
while (count > 0)
{
QUIT;
+
+ CORE_ADDR tag_laddr = 0, tag_haddr = 0;
+
+ /* Print the memory tag information if requested. */
+ if (fmt.print_tags && print_range_tag
+ && target_supports_memory_tagging ())
+ {
+ tag_laddr = align_down (next_address, gsize);
+ tag_haddr = align_down (next_address + gsize, gsize);
+
+ struct value *v_addr
+ = value_from_ulongest (builtin_type (gdbarch)->builtin_data_ptr,
+ tag_laddr);
+
+ if (gdbarch_tagged_address_p (target_gdbarch (), v_addr))
+ {
+ /* Fetch the allocation tag. */
+ struct value *tag
+ = gdbarch_get_memtag (gdbarch, v_addr, memtag_type::allocation);
+ std::string atag
+ = gdbarch_memtag_to_string (gdbarch, tag);
+
+ if (!atag.empty ())
+ {
+ printf_filtered (_("<Allocation Tag %s for range [%s,%s)>\n"),
+ atag.c_str (),
+ paddress (gdbarch, tag_laddr),
+ paddress (gdbarch, tag_haddr));
+ }
+ }
+ print_range_tag = false;
+ }
+
if (format == 'i')
fputs_filtered (pc_prefix (next_address), gdb_stdout);
print_address (next_gdbarch, next_address, gdb_stdout);
/* Display any branch delay slots following the final insn. */
if (format == 'i' && count == 1)
count += branch_delay_insns;
+
+ /* Update the tag range based on the current address being
+ processed. */
+ if (tag_haddr <= next_address)
+ print_range_tag = true;
}
printf_filtered ("\n");
}
{
const char *exp = *expp;
+ /* opts->raw value might already have been set by 'set print raw-values'
+ or by using 'print -raw-values'.
+ So, do not set opts->raw to 0, only set it to 1 if /r is given. */
if (exp && *exp == '/')
{
format_data fmt;
last_format = fmt.format;
opts->format = fmt.format;
- opts->raw = fmt.raw;
+ opts->raw = opts->raw || fmt.raw;
}
else
{
opts->format = 0;
- opts->raw = 0;
}
*expp = exp;
annotate_value_history_end ();
}
-/* Implementation of the "print" and "call" commands. */
+/* Returns true if memory tags should be validated. False otherwise. */
-static void
-print_command_1 (const char *args, int voidprint)
+static bool
+should_validate_memtags (struct value *value)
{
- struct value *val;
- value_print_options print_opts;
+ gdb_assert (value != nullptr && value_type (value) != nullptr);
+
+ if (!target_supports_memory_tagging ())
+ return false;
+
+ enum type_code code = value_type (value)->code ();
+
+ /* Skip non-address values. */
+ if (code != TYPE_CODE_PTR
+ && !TYPE_IS_REFERENCE (value_type (value)))
+ return false;
- get_user_print_options (&print_opts);
+ /* OK, we have an address value. Check we have a complete value we
+ can extract. */
+ if (value_optimized_out (value)
+ || !value_entirely_available (value))
+ return false;
+
+ /* We do. Check whether it includes any tags. */
+ return gdbarch_tagged_address_p (target_gdbarch (), value);
+}
+
+/* Helper for parsing arguments for print_command_1. */
+
+static struct value *
+process_print_command_args (const char *args, value_print_options *print_opts,
+ bool voidprint)
+{
+ get_user_print_options (print_opts);
/* Override global settings with explicit options, if any. */
- auto group = make_value_print_options_def_group (&print_opts);
+ auto group = make_value_print_options_def_group (print_opts);
gdb::option::process_options
(&args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
- print_command_parse_format (&args, "print", &print_opts);
+ print_command_parse_format (&args, "print", print_opts);
const char *exp = args;
if (exp != nullptr && *exp)
{
- expression_up expr = parse_expression (exp);
- val = evaluate_expression (expr.get ());
+ /* VOIDPRINT is true to indicate that we do want to print a void
+ value, so invert it for parse_expression. */
+ expression_up expr = parse_expression (exp, nullptr, !voidprint);
+ return evaluate_expression (expr.get ());
}
- else
- val = access_value_history (0);
+
+ return access_value_history (0);
+}
+
+/* Implementation of the "print" and "call" commands. */
+
+static void
+print_command_1 (const char *args, int voidprint)
+{
+ value_print_options print_opts;
+
+ struct value *val = process_print_command_args (args, &print_opts, voidprint);
if (voidprint || (val && value_type (val) &&
- TYPE_CODE (value_type (val)) != TYPE_CODE_VOID))
- print_value (val, print_opts);
+ value_type (val)->code () != TYPE_CODE_VOID))
+ {
+ /* If memory tagging validation is on, check if the tag is valid. */
+ if (print_opts.memory_tag_violations)
+ {
+ try
+ {
+ if (should_validate_memtags (val)
+ && !gdbarch_memtag_matches_p (target_gdbarch (), val))
+ {
+ /* Fetch the logical tag. */
+ struct value *tag
+ = gdbarch_get_memtag (target_gdbarch (), val,
+ memtag_type::logical);
+ std::string ltag
+ = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+ /* Fetch the allocation tag. */
+ tag = gdbarch_get_memtag (target_gdbarch (), val,
+ memtag_type::allocation);
+ std::string atag
+ = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+ printf_filtered (_("Logical tag (%s) does not match the "
+ "allocation tag (%s).\n"),
+ ltag.c_str (), atag.c_str ());
+ }
+ }
+ catch (gdb_exception_error &ex)
+ {
+ if (ex.error == TARGET_CLOSE_ERROR)
+ throw;
+
+ fprintf_filtered (gdb_stderr,
+ _("Could not validate memory tag: %s\n"),
+ ex.message->c_str ());
+ }
+ }
+
+ print_value (val, print_opts);
+ }
+}
+
+/* Called from command completion function to skip over /FMT
+ specifications, allowing the rest of the line to be completed. Returns
+ true if the /FMT is at the end of the current line and there is nothing
+ left to complete, otherwise false is returned.
+
+ In either case *ARGS can be updated to point after any part of /FMT that
+ is present.
+
+ This function is designed so that trying to complete '/' will offer no
+ completions, the user needs to insert the format specification
+ themselves. Trying to complete '/FMT' (where FMT is any non-empty set
+ of alpha-numeric characters) will cause readline to insert a single
+ space, setting the user up to enter the expression. */
+
+static bool
+skip_over_slash_fmt (completion_tracker &tracker, const char **args)
+{
+ const char *text = *args;
+
+ if (text[0] == '/')
+ {
+ bool in_fmt;
+ tracker.set_use_custom_word_point (true);
+
+ if (text[1] == '\0')
+ {
+ /* The user tried to complete after typing just the '/' character
+ of the /FMT string. Step the completer past the '/', but we
+ don't offer any completions. */
+ in_fmt = true;
+ ++text;
+ }
+ else
+ {
+ /* The user has typed some characters after the '/', we assume
+ this is a complete /FMT string, first skip over it. */
+ text = skip_to_space (text);
+
+ if (*text == '\0')
+ {
+ /* We're at the end of the input string. The user has typed
+ '/FMT' and asked for a completion. Push an empty
+ completion string, this will cause readline to insert a
+ space so the user now has '/FMT '. */
+ in_fmt = true;
+ tracker.add_completion (make_unique_xstrdup (text));
+ }
+ else
+ {
+ /* The user has already typed things after the /FMT, skip the
+ whitespace and return false. Whoever called this function
+ should then try to complete what comes next. */
+ in_fmt = false;
+ text = skip_spaces (text);
+ }
+ }
+
+ tracker.advance_custom_word_point_by (text - *args);
+ *args = text;
+ return in_fmt;
+ }
+
+ return false;
}
/* See valprint.h. */
(tracker, &text, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group))
return;
+ if (skip_over_slash_fmt (tracker, &text))
+ return;
+
const char *word = advance_to_expression_complete_word_point (tracker, text);
expression_completer (ignore, tracker, text, word);
}
static void
print_command (const char *exp, int from_tty)
{
- print_command_1 (exp, 1);
+ print_command_1 (exp, true);
}
/* Same as print, except it doesn't print void results. */
static void
call_command (const char *exp, int from_tty)
{
- print_command_1 (exp, 0);
+ print_command_1 (exp, false);
}
/* Implementation of the "output" command. */
{
expression_up expr = parse_expression (exp);
- if (expr->nelts >= 1)
- switch (expr->elts[0].opcode)
- {
- case UNOP_PREINCREMENT:
- case UNOP_POSTINCREMENT:
- case UNOP_PREDECREMENT:
- case UNOP_POSTDECREMENT:
- case BINOP_ASSIGN:
- case BINOP_ASSIGN_MODIFY:
- case BINOP_COMMA:
- break;
- default:
- warning
- (_("Expression is not an assignment (and might have no effect)"));
- }
+ switch (expr->op->opcode ())
+ {
+ case UNOP_PREINCREMENT:
+ case UNOP_POSTINCREMENT:
+ case UNOP_PREDECREMENT:
+ case UNOP_POSTDECREMENT:
+ case BINOP_ASSIGN:
+ case BINOP_ASSIGN_MODIFY:
+ case BINOP_COMMA:
+ break;
+ default:
+ warning
+ (_("Expression is not an assignment (and might have no effect)"));
+ }
evaluate_expression (expr.get ());
}
sect_addr = overlay_mapped_address (addr, osect);
- if (obj_section_addr (osect) <= sect_addr
- && sect_addr < obj_section_endaddr (osect)
+ if (osect->addr () <= sect_addr && sect_addr < osect->endaddr ()
&& (msymbol
= lookup_minimal_symbol_by_pc_section (sect_addr,
osect).minsym))
offset = sect_addr - MSYMBOL_VALUE_ADDRESS (objfile, msymbol);
mapped = section_is_mapped (osect) ? _("mapped") : _("unmapped");
sec_name = osect->the_bfd_section->name;
- msym_name = MSYMBOL_PRINT_NAME (msymbol);
+ msym_name = msymbol->print_name ();
/* Don't print the offset if it is zero.
We assume there's no need to handle i18n of "sym + offset". */
gdb_assert (osect->objfile && objfile_name (osect->objfile));
obj_name = objfile_name (osect->objfile);
- if (MULTI_OBJFILE_P ())
+ if (current_program_space->multi_objfile_p ())
if (pc_in_unmapped_range (addr, osect))
if (section_is_overlay (osect))
printf_filtered (_("%s in load address range of "
{
struct objfile *objfile = msymbol.objfile;
- gdbarch = get_objfile_arch (objfile);
+ gdbarch = objfile->arch ();
load_addr = BMSYMBOL_VALUE_ADDRESS (msymbol);
printf_filtered ("Symbol \"");
fputs_styled (paddress (gdbarch, load_addr), address_style.style (),
gdb_stdout);
printf_filtered (" in a file compiled without debugging");
- section = MSYMBOL_OBJ_SECTION (objfile, msymbol.minsym);
+ section = msymbol.minsym->obj_section (objfile);
if (section_is_overlay (section))
{
load_addr = overlay_unmapped_address (load_addr, section);
}
printf_filtered ("Symbol \"");
- fprintf_symbol_filtered (gdb_stdout, SYMBOL_PRINT_NAME (sym),
- current_language->la_language, DMGL_ANSI);
+ fputs_filtered (sym->print_name (), gdb_stdout);
printf_filtered ("\" is ");
val = SYMBOL_VALUE (sym);
if (SYMBOL_OBJFILE_OWNED (sym))
- section = SYMBOL_OBJ_SECTION (symbol_objfile (sym), sym);
+ section = sym->obj_section (symbol_objfile (sym));
else
section = NULL;
gdbarch = symbol_arch (sym);
break;
case LOC_COMPUTED:
- gdb_assert_not_reached (_("LOC_COMPUTED variable missing a method"));
+ gdb_assert_not_reached ("LOC_COMPUTED variable missing a method");
case LOC_REGISTER:
/* GDBARCH is the architecture associated with the objfile the symbol
{
struct bound_minimal_symbol msym;
- msym = lookup_bound_minimal_symbol (SYMBOL_LINKAGE_NAME (sym));
+ msym = lookup_bound_minimal_symbol (sym->linkage_name ());
if (msym.minsym == NULL)
printf_filtered ("unresolved");
else
{
- section = MSYMBOL_OBJ_SECTION (msym.objfile, msym.minsym);
+ section = msym.obj_section ();
if (section
&& (section->the_bfd_section->flags & SEC_THREAD_LOCAL) != 0)
struct value *val;
fmt.format = last_format ? last_format : 'x';
+ fmt.print_tags = last_print_tags;
fmt.size = last_size;
fmt.count = 1;
fmt.raw = 0;
{
expression_up expr = parse_expression (exp);
/* Cause expression not to be there any more if this command is
- repeated with Newline. But don't clobber a user-defined
- command's definition. */
+ repeated with Newline. But don't clobber a user-defined
+ command's definition. */
if (from_tty)
set_repeat_arguments ("");
val = evaluate_expression (expr.get ());
if (TYPE_IS_REFERENCE (value_type (val)))
val = coerce_ref (val);
/* In rvalue contexts, such as this, functions are coerced into
- pointers to functions. This makes "x/i main" work. */
- if (/* last_format == 'i' && */
- TYPE_CODE (value_type (val)) == TYPE_CODE_FUNC
+ pointers to functions. This makes "x/i main" work. */
+ if (value_type (val)->code () == TYPE_CODE_FUNC
&& VALUE_LVAL (val) == lval_memory)
next_address = value_address (val);
else
last_size = fmt.size;
last_format = fmt.format;
+ /* Remember tag-printing setting. */
+ last_print_tags = fmt.print_tags;
+
/* Set a couple of internal variables if appropriate. */
if (last_examine_value != nullptr)
{
/* Make last address examined available to the user as $_. Use
- the correct pointer type. */
+ the correct pointer type. */
struct type *pointer_type
= lookup_pointer_type (value_type (last_examine_value.get ()));
set_internalvar (lookup_internalvar ("_"),
set_internalvar (lookup_internalvar ("__"), last_examine_value.get ());
}
}
+
+/* Command completion for the 'display' and 'x' commands. */
+
+static void
+display_and_x_command_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char * /*word*/)
+{
+ if (skip_over_slash_fmt (tracker, &text))
+ return;
+
+ const char *word = advance_to_expression_complete_word_point (tracker, text);
+ expression_completer (ignore, tracker, text, word);
+}
+
\f
/* Add an expression to the auto-display chain.
innermost_block_tracker tracker;
expression_up expr = parse_expression (exp, &tracker);
- newobj = new display ();
-
- newobj->exp_string = xstrdup (exp);
- newobj->exp = std::move (expr);
- newobj->block = tracker.block ();
- newobj->pspace = current_program_space;
- newobj->number = ++display_number;
- newobj->format = fmt;
- newobj->enabled_p = 1;
- newobj->next = NULL;
-
- if (display_chain == NULL)
- display_chain = newobj;
- else
- {
- struct display *last;
-
- for (last = display_chain; last->next != NULL; last = last->next)
- ;
- last->next = newobj;
- }
+ newobj = new display (exp, std::move (expr), fmt,
+ current_program_space, tracker.block ());
+ all_displays.emplace_back (newobj);
if (from_tty)
do_one_display (newobj);
dont_repeat ();
}
-static void
-free_display (struct display *d)
-{
- xfree (d->exp_string);
- delete d;
-}
-
/* Clear out the display_chain. Done when new symtabs are loaded,
since this invalidates the types stored in many expressions. */
void
-clear_displays (void)
+clear_displays ()
{
- struct display *d;
-
- while ((d = display_chain) != NULL)
- {
- display_chain = d->next;
- free_display (d);
- }
+ all_displays.clear ();
}
/* Delete the auto-display DISPLAY. */
static void
delete_display (struct display *display)
{
- struct display *d;
-
gdb_assert (display != NULL);
- if (display_chain == display)
- display_chain = display->next;
-
- ALL_DISPLAYS (d)
- if (d->next == display)
- {
- d->next = display->next;
- break;
- }
-
- free_display (display);
+ auto iter = std::find_if (all_displays.begin (),
+ all_displays.end (),
+ [=] (const std::unique_ptr<struct display> &item)
+ {
+ return item.get () == display;
+ });
+ gdb_assert (iter != all_displays.end ());
+ all_displays.erase (iter);
}
/* Call FUNCTION on each of the displays whose numbers are given in
static void
map_display_numbers (const char *args,
- void (*function) (struct display *,
- void *),
- void *data)
+ gdb::function_view<void (struct display *)> function)
{
int num;
warning (_("bad display number at or near '%s'"), p);
else
{
- struct display *d, *tmp;
-
- ALL_DISPLAYS_SAFE (d, tmp)
- if (d->number == num)
- break;
- if (d == NULL)
- printf_unfiltered (_("No display number %d.\n"), num);
+ auto iter = std::find_if (all_displays.begin (),
+ all_displays.end (),
+ [=] (const std::unique_ptr<display> &item)
+ {
+ return item->number == num;
+ });
+ if (iter == all_displays.end ())
+ printf_filtered (_("No display number %d.\n"), num);
else
- function (d, data);
+ function (iter->get ());
}
}
}
-/* Callback for map_display_numbers, that deletes a display. */
-
-static void
-do_delete_display (struct display *d, void *data)
-{
- delete_display (d);
-}
-
/* "undisplay" command. */
static void
return;
}
- map_display_numbers (args, do_delete_display, NULL);
+ map_display_numbers (args, delete_display);
dont_repeat ();
}
{
int within_current_scope;
- if (d->enabled_p == 0)
+ if (!d->enabled_p)
return;
/* The expression carries the architecture that was used at parse time.
try
{
innermost_block_tracker tracker;
- d->exp = parse_expression (d->exp_string, &tracker);
+ d->exp = parse_expression (d->exp_string.c_str (), &tracker);
d->block = tracker.block ();
}
catch (const gdb_exception &ex)
{
/* Can't re-parse the expression. Disable this display item. */
- d->enabled_p = 0;
+ d->enabled_p = false;
warning (_("Unable to display \"%s\": %s"),
- d->exp_string, ex.what ());
+ d->exp_string.c_str (), ex.what ());
return;
}
}
if (d->block)
{
if (d->pspace == current_program_space)
- within_current_scope = contained_in (get_selected_block (0), d->block);
+ within_current_scope = contained_in (get_selected_block (0), d->block,
+ true);
else
within_current_scope = 0;
}
annotate_display_expression ();
- puts_filtered (d->exp_string);
+ puts_filtered (d->exp_string.c_str ());
annotate_display_expression_end ();
if (d->format.count != 1 || d->format.format == 'i')
annotate_display_value ();
try
- {
+ {
struct value *val;
CORE_ADDR addr;
}
catch (const gdb_exception_error &ex)
{
- fprintf_filtered (gdb_stdout, _("<error: %s>\n"),
- ex.what ());
+ fprintf_filtered (gdb_stdout, _("%p[<error: %s>%p]\n"),
+ metadata_style.style ().ptr (), ex.what (),
+ nullptr);
}
}
else
annotate_display_expression ();
- puts_filtered (d->exp_string);
+ puts_filtered (d->exp_string.c_str ());
annotate_display_expression_end ();
printf_filtered (" = ");
opts.raw = d->format.raw;
try
- {
+ {
struct value *val;
val = evaluate_expression (d->exp.get ());
}
catch (const gdb_exception_error &ex)
{
- fprintf_filtered (gdb_stdout, _("<error: %s>"), ex.what ());
+ fprintf_styled (gdb_stdout, metadata_style.style (),
+ _("<error: %s>"), ex.what ());
}
printf_filtered ("\n");
void
do_displays (void)
{
- struct display *d;
-
- for (d = display_chain; d; d = d->next)
- do_one_display (d);
+ for (auto &d : all_displays)
+ do_one_display (d.get ());
}
/* Delete the auto-display which we were in the process of displaying.
void
disable_display (int num)
{
- struct display *d;
-
- for (d = display_chain; d; d = d->next)
+ for (auto &d : all_displays)
if (d->number == num)
{
- d->enabled_p = 0;
+ d->enabled_p = false;
return;
}
- printf_unfiltered (_("No display number %d.\n"), num);
+ printf_filtered (_("No display number %d.\n"), num);
}
void
static void
info_display_command (const char *ignore, int from_tty)
{
- struct display *d;
-
- if (!display_chain)
- printf_unfiltered (_("There are no auto-display expressions now.\n"));
+ if (all_displays.empty ())
+ printf_filtered (_("There are no auto-display expressions now.\n"));
else
printf_filtered (_("Auto-display expressions now in effect:\n\
Num Enb Expression\n"));
- for (d = display_chain; d; d = d->next)
+ for (auto &d : all_displays)
{
printf_filtered ("%d: %c ", d->number, "ny"[(int) d->enabled_p]);
if (d->format.size)
d->format.format);
else if (d->format.format)
printf_filtered ("/%c ", d->format.format);
- puts_filtered (d->exp_string);
- if (d->block && !contained_in (get_selected_block (0), d->block))
+ puts_filtered (d->exp_string.c_str ());
+ if (d->block && !contained_in (get_selected_block (0), d->block, true))
printf_filtered (_(" (cannot be evaluated in the current context)"));
printf_filtered ("\n");
}
}
-/* Callback fo map_display_numbers, that enables or disables the
- passed in display D. */
-
-static void
-do_enable_disable_display (struct display *d, void *data)
-{
- d->enabled_p = *(int *) data;
-}
-
-/* Implamentation of both the "disable display" and "enable display"
+/* Implementation of both the "disable display" and "enable display"
commands. ENABLE decides what to do. */
static void
-enable_disable_display_command (const char *args, int from_tty, int enable)
+enable_disable_display_command (const char *args, int from_tty, bool enable)
{
if (args == NULL)
{
- struct display *d;
-
- ALL_DISPLAYS (d)
+ for (auto &d : all_displays)
d->enabled_p = enable;
return;
}
- map_display_numbers (args, do_enable_disable_display, &enable);
+ map_display_numbers (args,
+ [=] (struct display *d)
+ {
+ d->enabled_p = enable;
+ });
}
/* The "enable display" command. */
static void
enable_display_command (const char *args, int from_tty)
{
- enable_disable_display_command (args, from_tty, 1);
+ enable_disable_display_command (args, from_tty, true);
}
/* The "disable display" command. */
static void
disable_display_command (const char *args, int from_tty)
{
- enable_disable_display_command (args, from_tty, 0);
+ enable_disable_display_command (args, from_tty, false);
}
/* display_chain items point to blocks and expressions. Some expressions in
static void
clear_dangling_display_expressions (struct objfile *objfile)
{
- struct display *d;
struct program_space *pspace;
/* With no symbol file we cannot have a block or expression from it. */
gdb_assert (objfile->pspace == pspace);
}
- for (d = display_chain; d != NULL; d = d->next)
+ for (auto &d : all_displays)
{
if (d->pspace != pspace)
continue;
- if (lookup_objfile_from_block (d->block) == objfile
+ struct objfile *bl_objf = nullptr;
+ if (d->block != nullptr)
+ {
+ bl_objf = block_objfile (d->block);
+ if (bl_objf->separate_debug_objfile_backlink != nullptr)
+ bl_objf = bl_objf->separate_debug_objfile_backlink;
+ }
+
+ if (bl_objf == objfile
|| (d->exp != NULL && exp_uses_objfile (d->exp.get (), objfile)))
- {
- d->exp.reset ();
- d->block = NULL;
- }
+ {
+ d->exp.reset ();
+ d->block = NULL;
+ }
}
}
\f
{
if (!name)
- name = SYMBOL_PRINT_NAME (var);
+ name = var->print_name ();
- fputs_filtered (n_spaces (2 * indent), stream);
- fputs_styled (name, variable_name_style.style (), stream);
- fputs_filtered (" = ", stream);
+ fprintf_filtered (stream, "%*s%ps = ", 2 * indent, "",
+ styled_string (variable_name_style.style (), name));
try
{
val = read_var_value (var, NULL, frame);
get_user_print_options (&opts);
opts.deref_ref = 1;
- common_val_print (val, stream, indent, &opts, current_language);
+ common_val_print_checked (val, stream, indent, &opts, current_language);
/* common_val_print invalidates FRAME when a pretty printer calls inferior
function. */
}
catch (const gdb_exception_error &except)
{
- fprintf_filtered (stream, "<error reading variable %s (%s)>", name,
- except.what ());
+ fprintf_styled (stream, metadata_style.style (),
+ "<error reading variable %s (%s)>", name,
+ except.what ());
}
fprintf_filtered (stream, "\n");
{
const gdb_byte *str;
- if (VALUE_LVAL (value) == lval_internalvar
+ if (value_type (value)->code () != TYPE_CODE_PTR
+ && VALUE_LVAL (value) == lval_internalvar
&& c_is_string_type_p (value_type (value)))
{
size_t len = TYPE_LENGTH (value_type (value));
null terminated) to be printed without problems. */
gdb_byte *tem_str = (gdb_byte *) alloca (len + 1);
- memcpy (tem_str, value_contents (value), len);
+ memcpy (tem_str, value_contents (value).data (), len);
tem_str [len] = 0;
str = tem_str;
}
{
const gdb_byte *str;
size_t len;
- struct gdbarch *gdbarch = get_type_arch (value_type (value));
- struct type *wctype = lookup_typename (current_language, gdbarch,
+ struct gdbarch *gdbarch = value_type (value)->arch ();
+ struct type *wctype = lookup_typename (current_language,
"wchar_t", NULL, 0);
int wcwidth = TYPE_LENGTH (wctype);
if (VALUE_LVAL (value) == lval_internalvar
&& c_is_string_type_p (value_type (value)))
{
- str = value_contents (value);
+ str = value_contents (value).data ();
len = TYPE_LENGTH (value_type (value));
}
else
{
/* Parameter data. */
struct type *param_type = value_type (value);
- struct gdbarch *gdbarch = get_type_arch (param_type);
+ struct gdbarch *gdbarch = param_type->arch ();
/* Determine target type corresponding to the format string. */
struct type *fmt_type;
In either case, the result of the conversion is a byte buffer
formatted in the target format for the target type. */
- if (TYPE_CODE (fmt_type) == TYPE_CODE_FLT)
+ if (fmt_type->code () == TYPE_CODE_FLT)
{
param_type = float_type_from_length (param_type);
if (param_type != value_type (value))
- value = value_from_contents (param_type, value_contents (value));
+ value = value_from_contents (param_type,
+ value_contents (value).data ());
}
value = value_cast (fmt_type, value);
/* Convert the value to a string and print it. */
std::string str
- = target_float_to_string (value_contents (value), fmt_type, format);
+ = target_float_to_string (value_contents (value).data (), fmt_type, format);
fputs_filtered (str.c_str (), stream);
}
break;
case wide_char_arg:
{
- struct gdbarch *gdbarch
- = get_type_arch (value_type (val_args[i]));
- struct type *wctype = lookup_typename (current_language, gdbarch,
+ struct gdbarch *gdbarch = value_type (val_args[i])->arch ();
+ struct type *wctype = lookup_typename (current_language,
"wchar_t", NULL, 0);
struct type *valtype;
const gdb_byte *bytes;
valtype = value_type (val_args[i]);
if (TYPE_LENGTH (valtype) != TYPE_LENGTH (wctype)
- || TYPE_CODE (valtype) != TYPE_CODE_INT)
+ || valtype->code () != TYPE_CODE_INT)
error (_("expected wchar_t argument for %%lc"));
- bytes = value_contents (val_args[i]);
+ bytes = value_contents (val_args[i]).data ();
auto_obstack output;
DIAGNOSTIC_PUSH
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
fprintf_filtered (stream, current_substring,
- obstack_base (&output));
+ obstack_base (&output));
DIAGNOSTIC_POP
}
break;
DIAGNOSTIC_PUSH
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
- fprintf_filtered (stream, current_substring, val);
+ fprintf_filtered (stream, current_substring, val);
DIAGNOSTIC_POP
break;
}
DIAGNOSTIC_PUSH
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
- fprintf_filtered (stream, current_substring, val);
+ fprintf_filtered (stream, current_substring, val);
DIAGNOSTIC_POP
break;
}
DIAGNOSTIC_PUSH
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
- fprintf_filtered (stream, current_substring, val);
+ fprintf_filtered (stream, current_substring, val);
+ DIAGNOSTIC_POP
+ break;
+ }
+ case size_t_arg:
+ {
+ size_t val = value_as_long (val_args[i]);
+
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+ fprintf_filtered (stream, current_substring, val);
DIAGNOSTIC_POP
break;
}
ui_printf (arg, gdb_stdout);
reset_terminal_style (gdb_stdout);
wrap_here ("");
- gdb_flush (gdb_stdout);
+ gdb_stdout->flush ();
}
/* Implement the "eval" command. */
execute_command (expanded.c_str (), from_tty);
}
+/* Convenience function for error checking in memory-tag commands. */
+
+static void
+show_addr_not_tagged (CORE_ADDR address)
+{
+ error (_("Address %s not in a region mapped with a memory tagging flag."),
+ paddress (target_gdbarch (), address));
+}
+
+/* Convenience function for error checking in memory-tag commands. */
+
+static void
+show_memory_tagging_unsupported (void)
+{
+ error (_("Memory tagging not supported or disabled by the current"
+ " architecture."));
+}
+
+/* Implement the "memory-tag" prefix command. */
+
+static void
+memory_tag_command (const char *arg, int from_tty)
+{
+ help_list (memory_tag_list, "memory-tag ", all_commands, gdb_stdout);
+}
+
+/* Helper for print-logical-tag and print-allocation-tag. */
+
+static void
+memory_tag_print_tag_command (const char *args, enum memtag_type tag_type)
+{
+ if (args == nullptr)
+ error_no_arg (_("address or pointer"));
+
+ /* Parse args into a value. If the value is a pointer or an address,
+ then fetch the logical or allocation tag. */
+ value_print_options print_opts;
+
+ struct value *val = process_print_command_args (args, &print_opts, true);
+
+ /* If the address is not in a region memory mapped with a memory tagging
+ flag, it is no use trying to access/manipulate its allocation tag.
+
+ It is OK to manipulate the logical tag though. */
+ if (tag_type == memtag_type::allocation
+ && !gdbarch_tagged_address_p (target_gdbarch (), val))
+ show_addr_not_tagged (value_as_address (val));
+
+ struct value *tag_value
+ = gdbarch_get_memtag (target_gdbarch (), val, tag_type);
+ std::string tag = gdbarch_memtag_to_string (target_gdbarch (), tag_value);
+
+ if (tag.empty ())
+ printf_filtered (_("%s tag unavailable.\n"),
+ tag_type
+ == memtag_type::logical? "Logical" : "Allocation");
+
+ struct value *v_tag = process_print_command_args (tag.c_str (),
+ &print_opts,
+ true);
+ print_opts.output_format = 'x';
+ print_value (v_tag, print_opts);
+}
+
+/* Implement the "memory-tag print-logical-tag" command. */
+
+static void
+memory_tag_print_logical_tag_command (const char *args, int from_tty)
+{
+ if (!target_supports_memory_tagging ())
+ show_memory_tagging_unsupported ();
+
+ memory_tag_print_tag_command (args, memtag_type::logical);
+}
+
+/* Implement the "memory-tag print-allocation-tag" command. */
+
+static void
+memory_tag_print_allocation_tag_command (const char *args, int from_tty)
+{
+ if (!target_supports_memory_tagging ())
+ show_memory_tagging_unsupported ();
+
+ memory_tag_print_tag_command (args, memtag_type::allocation);
+}
+
+/* Parse ARGS and extract ADDR and TAG.
+ ARGS should have format <expression> <tag bytes>. */
+
+static void
+parse_with_logical_tag_input (const char *args, struct value **val,
+ gdb::byte_vector &tags,
+ value_print_options *print_opts)
+{
+ /* Fetch the address. */
+ std::string address_string = extract_string_maybe_quoted (&args);
+
+ /* Parse the address into a value. */
+ *val = process_print_command_args (address_string.c_str (), print_opts,
+ true);
+
+ /* Fetch the tag bytes. */
+ std::string tag_string = extract_string_maybe_quoted (&args);
+
+ /* Validate the input. */
+ if (address_string.empty () || tag_string.empty ())
+ error (_("Missing arguments."));
+
+ if (tag_string.length () != 2)
+ error (_("Error parsing tags argument. The tag should be 2 digits."));
+
+ tags = hex2bin (tag_string.c_str ());
+}
+
+/* Implement the "memory-tag with-logical-tag" command. */
+
+static void
+memory_tag_with_logical_tag_command (const char *args, int from_tty)
+{
+ if (!target_supports_memory_tagging ())
+ show_memory_tagging_unsupported ();
+
+ if (args == nullptr)
+ error_no_arg (_("<address> <tag>"));
+
+ gdb::byte_vector tags;
+ struct value *val;
+ value_print_options print_opts;
+
+ /* Parse the input. */
+ parse_with_logical_tag_input (args, &val, tags, &print_opts);
+
+ /* Setting the logical tag is just a local operation that does not touch
+ any memory from the target. Given an input value, we modify the value
+ to include the appropriate tag.
+
+ For this reason we need to cast the argument value to a
+ (void *) pointer. This is so we have the right type for the gdbarch
+ hook to manipulate the value and insert the tag.
+
+ Otherwise, this would fail if, for example, GDB parsed the argument value
+ into an int-sized value and the pointer value has a type of greater
+ length. */
+
+ /* Cast to (void *). */
+ val = value_cast (builtin_type (target_gdbarch ())->builtin_data_ptr,
+ val);
+
+ /* Length doesn't matter for a logical tag. Pass 0. */
+ if (!gdbarch_set_memtags (target_gdbarch (), val, 0, tags,
+ memtag_type::logical))
+ printf_filtered (_("Could not update the logical tag data.\n"));
+ else
+ {
+ /* Always print it in hex format. */
+ print_opts.output_format = 'x';
+ print_value (val, print_opts);
+ }
+}
+
+/* Parse ARGS and extract ADDR, LENGTH and TAGS. */
+
+static void
+parse_set_allocation_tag_input (const char *args, struct value **val,
+ size_t *length, gdb::byte_vector &tags)
+{
+ /* Fetch the address. */
+ std::string address_string = extract_string_maybe_quoted (&args);
+
+ /* Parse the address into a value. */
+ value_print_options print_opts;
+ *val = process_print_command_args (address_string.c_str (), &print_opts,
+ true);
+
+ /* Fetch the length. */
+ std::string length_string = extract_string_maybe_quoted (&args);
+
+ /* Fetch the tag bytes. */
+ std::string tags_string = extract_string_maybe_quoted (&args);
+
+ /* Validate the input. */
+ if (address_string.empty () || length_string.empty () || tags_string.empty ())
+ error (_("Missing arguments."));
+
+ errno = 0;
+ const char *trailer = nullptr;
+ LONGEST parsed_length = strtoulst (length_string.c_str (), &trailer, 10);
+
+ if (errno != 0 || (trailer != nullptr && trailer[0] != '\0'))
+ error (_("Error parsing length argument."));
+
+ if (parsed_length <= 0)
+ error (_("Invalid zero or negative length."));
+
+ *length = parsed_length;
+
+ if (tags_string.length () % 2)
+ error (_("Error parsing tags argument. Tags should be 2 digits per byte."));
+
+ tags = hex2bin (tags_string.c_str ());
+
+ /* If the address is not in a region memory mapped with a memory tagging
+ flag, it is no use trying to access/manipulate its allocation tag. */
+ if (!gdbarch_tagged_address_p (target_gdbarch (), *val))
+ show_addr_not_tagged (value_as_address (*val));
+}
+
+/* Implement the "memory-tag set-allocation-tag" command.
+ ARGS should be in the format <address> <length> <tags>. */
+
+static void
+memory_tag_set_allocation_tag_command (const char *args, int from_tty)
+{
+ if (!target_supports_memory_tagging ())
+ show_memory_tagging_unsupported ();
+
+ if (args == nullptr)
+ error_no_arg (_("<starting address> <length> <tag bytes>"));
+
+ gdb::byte_vector tags;
+ size_t length = 0;
+ struct value *val;
+
+ /* Parse the input. */
+ parse_set_allocation_tag_input (args, &val, &length, tags);
+
+ if (!gdbarch_set_memtags (target_gdbarch (), val, length, tags,
+ memtag_type::allocation))
+ printf_filtered (_("Could not update the allocation tag(s).\n"));
+ else
+ printf_filtered (_("Allocation tag(s) updated successfully.\n"));
+}
+
+/* Implement the "memory-tag check" command. */
+
+static void
+memory_tag_check_command (const char *args, int from_tty)
+{
+ if (!target_supports_memory_tagging ())
+ show_memory_tagging_unsupported ();
+
+ if (args == nullptr)
+ error (_("Argument required (address or pointer)"));
+
+ /* Parse the expression into a value. If the value is an address or
+ pointer, then check its logical tag against the allocation tag. */
+ value_print_options print_opts;
+
+ struct value *val = process_print_command_args (args, &print_opts, true);
+
+ /* If the address is not in a region memory mapped with a memory tagging
+ flag, it is no use trying to access/manipulate its allocation tag. */
+ if (!gdbarch_tagged_address_p (target_gdbarch (), val))
+ show_addr_not_tagged (value_as_address (val));
+
+ CORE_ADDR addr = value_as_address (val);
+
+ /* Check if the tag is valid. */
+ if (!gdbarch_memtag_matches_p (target_gdbarch (), val))
+ {
+ struct value *tag
+ = gdbarch_get_memtag (target_gdbarch (), val, memtag_type::logical);
+ std::string ltag
+ = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+ tag = gdbarch_get_memtag (target_gdbarch (), val,
+ memtag_type::allocation);
+ std::string atag
+ = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+ printf_filtered (_("Logical tag (%s) does not match"
+ " the allocation tag (%s) for address %s.\n"),
+ ltag.c_str (), atag.c_str (),
+ paddress (target_gdbarch (), addr));
+ }
+ else
+ {
+ struct value *tag
+ = gdbarch_get_memtag (target_gdbarch (), val, memtag_type::logical);
+ std::string ltag
+ = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+ printf_filtered (_("Memory tags for address %s match (%s).\n"),
+ paddress (target_gdbarch (), addr), ltag.c_str ());
+ }
+}
+
+void _initialize_printcmd ();
void
-_initialize_printcmd (void)
+_initialize_printcmd ()
{
struct cmd_list_element *c;
current_display_number = -1;
- gdb::observers::free_objfile.attach (clear_dangling_display_expressions);
+ gdb::observers::free_objfile.attach (clear_dangling_display_expressions,
+ "printcmd");
add_info ("address", info_address_command,
_("Describe where symbol SYM is stored.\n\
Usage: info symbol ADDR\n\
Only for symbols with fixed locations (global or static scope)."));
- add_com ("x", class_vars, x_command, _("\
+ c = add_com ("x", class_vars, x_command, _("\
Examine memory: x/FMT ADDRESS.\n\
ADDRESS is an expression for the memory address to examine.\n\
FMT is a repeat count followed by a format letter and a size letter.\n\
Defaults for format and size letters are those previously used.\n\
Default count is 1. Default address is following last thing printed\n\
with this command or \"print\"."));
+ set_cmd_completer_handle_brkchars (c, display_and_x_command_completer);
add_info ("display", info_display_command, _("\
Expressions to display when program stops, with code numbers.\n\
Do \"info display\" to see current list of code numbers."),
&cmdlist);
- add_com ("display", class_vars, display_command, _("\
+ c = add_com ("display", class_vars, display_command, _("\
Print value of expression EXP each time the program stops.\n\
Usage: display[/FMT] EXP\n\
/FMT may be used before EXP as in the \"print\" command.\n\
and examining is done as in the \"x\" command.\n\n\
With no argument, display all currently requested auto-display expressions.\n\
Use \"undisplay\" to cancel display requests previously made."));
+ set_cmd_completer_handle_brkchars (c, display_and_x_command_completer);
add_cmd ("display", class_vars, enable_display_command, _("\
Enable some expressions to be displayed when program stops.\n\
This is useful in user-defined commands."));
add_prefix_cmd ("set", class_vars, set_command, _("\
-Evaluate expression EXP and assign result to variable VAR\n\
+Evaluate expression EXP and assign result to variable VAR.\n\
Usage: set VAR = EXP\n\
This uses assignment syntax appropriate for the current language\n\
(VAR = EXP or VAR := EXP for example).\n\
\n\
With a subcommand, this command modifies parts of the gdb environment.\n\
You can see these environment settings with the \"show\" command."),
- &setlist, "set ", 1, &cmdlist);
+ &setlist, 1, &cmdlist);
if (dbx_commands)
add_com ("assign", class_vars, set_command, _("\
-Evaluate expression EXP and assign result to variable VAR\n\
+Evaluate expression EXP and assign result to variable VAR.\n\
Usage: assign VAR = EXP\n\
This uses assignment syntax appropriate for the current language\n\
(VAR = EXP or VAR := EXP for example).\n\
history, if it is not void."));
set_cmd_completer_handle_brkchars (c, print_command_completer);
- add_cmd ("variable", class_vars, set_command, _("\
-Evaluate expression EXP and assign result to variable VAR\n\
+ cmd_list_element *set_variable_cmd
+ = add_cmd ("variable", class_vars, set_command, _("\
+Evaluate expression EXP and assign result to variable VAR.\n\
Usage: set variable VAR = EXP\n\
This uses assignment syntax appropriate for the current language\n\
(VAR = EXP or VAR := EXP for example).\n\
with $), a register (a few standard names starting with $), or an actual\n\
variable in the program being debugged. EXP is any valid expression.\n\
This may usually be abbreviated to simply \"set\"."),
- &setlist);
- add_alias_cmd ("var", "variable", class_vars, 0, &setlist);
+ &setlist);
+ add_alias_cmd ("var", set_variable_cmd, class_vars, 0, &setlist);
const auto print_opts = make_value_print_options_def_group (nullptr);
- static const std::string print_help = gdb::option::build_help (N_("\
+ static const std::string print_help = gdb::option::build_help (_("\
Print value of expression EXP.\n\
Usage: print [[OPTION]... --] [/FMT] [EXP]\n\
\n\
Options:\n\
-%OPTIONS%\
+%OPTIONS%\n\
+\n\
Note: because this command accepts arbitrary expressions, if you\n\
specify any command option, you must use a double dash (\"--\")\n\
to mark the end of option processing. E.g.: \"print -o -- myobj\".\n\
but no count or size letter (see \"x\" command)."),
print_opts);
- c = add_com ("print", class_vars, print_command, print_help.c_str ());
- set_cmd_completer_handle_brkchars (c, print_command_completer);
- add_com_alias ("p", "print", class_vars, 1);
- add_com_alias ("inspect", "print", class_vars, 1);
+ cmd_list_element *print_cmd
+ = add_com ("print", class_vars, print_command, print_help.c_str ());
+ set_cmd_completer_handle_brkchars (print_cmd, print_command_completer);
+ add_com_alias ("p", print_cmd, class_vars, 1);
+ add_com_alias ("inspect", print_cmd, class_vars, 1);
add_setshow_uinteger_cmd ("max-symbolic-offset", no_class,
&max_symbolic_offset, _("\
Usage: eval \"format string\", ARG1, ARG2, ARG3, ..., ARGN\n\
Convert the arguments to a string as \"printf\" would, but then\n\
treat this string as a command line, and evaluate it."));
+
+ /* Memory tagging commands. */
+ add_prefix_cmd ("memory-tag", class_vars, memory_tag_command, _("\
+Generic command for printing and manipulating memory tag properties."),
+ &memory_tag_list, 0, &cmdlist);
+ add_cmd ("print-logical-tag", class_vars,
+ memory_tag_print_logical_tag_command,
+ ("Print the logical tag from POINTER.\n\
+Usage: memory-tag print-logical-tag <POINTER>.\n\
+<POINTER> is an expression that evaluates to a pointer.\n\
+Print the logical tag contained in POINTER. The tag interpretation is\n\
+architecture-specific."),
+ &memory_tag_list);
+ add_cmd ("print-allocation-tag", class_vars,
+ memory_tag_print_allocation_tag_command,
+ _("Print the allocation tag for ADDRESS.\n\
+Usage: memory-tag print-allocation-tag <ADDRESS>.\n\
+<ADDRESS> is an expression that evaluates to a memory address.\n\
+Print the allocation tag associated with the memory address ADDRESS.\n\
+The tag interpretation is architecture-specific."),
+ &memory_tag_list);
+ add_cmd ("with-logical-tag", class_vars, memory_tag_with_logical_tag_command,
+ _("Print a POINTER with a specific logical TAG.\n\
+Usage: memory-tag with-logical-tag <POINTER> <TAG>\n\
+<POINTER> is an expression that evaluates to a pointer.\n\
+<TAG> is a sequence of hex bytes that is interpreted by the architecture\n\
+as a single memory tag."),
+ &memory_tag_list);
+ add_cmd ("set-allocation-tag", class_vars,
+ memory_tag_set_allocation_tag_command,
+ _("Set the allocation tag(s) for a memory range.\n\
+Usage: memory-tag set-allocation-tag <ADDRESS> <LENGTH> <TAG_BYTES>\n\
+<ADDRESS> is an expression that evaluates to a memory address\n\
+<LENGTH> is the number of bytes that is added to <ADDRESS> to calculate\n\
+the memory range.\n\
+<TAG_BYTES> is a sequence of hex bytes that is interpreted by the\n\
+architecture as one or more memory tags.\n\
+Sets the tags of the memory range [ADDRESS, ADDRESS + LENGTH)\n\
+to TAG_BYTES.\n\
+\n\
+If the number of tags is greater than or equal to the number of tag granules\n\
+in the [ADDRESS, ADDRESS + LENGTH) range, only the tags up to the\n\
+number of tag granules are updated.\n\
+\n\
+If the number of tags is less than the number of tag granules, then the\n\
+command is a fill operation. The TAG_BYTES are interpreted as a pattern\n\
+that gets repeated until the number of tag granules in the memory range\n\
+[ADDRESS, ADDRESS + LENGTH) is updated."),
+ &memory_tag_list);
+ add_cmd ("check", class_vars, memory_tag_check_command,
+ _("Validate a pointer's logical tag against the allocation tag.\n\
+Usage: memory-tag check <POINTER>\n\
+<POINTER> is an expression that evaluates to a pointer\n\
+Fetch the logical and allocation tags for POINTER and compare them\n\
+for equality. If the tags do not match, print additional information about\n\
+the tag mismatch."),
+ &memory_tag_list);
}