+/* Find the address of the instruction that is INST_COUNT instructions before
+ the instruction at ADDR.
+ Since some architectures have variable-length instructions, we can't just
+ simply subtract INST_COUNT * INSN_LEN from ADDR. Instead, we use line
+ number information to locate the nearest known instruction boundary,
+ and disassemble forward from there. If we go out of the symbol range
+ during disassembling, we return the lowest address we've got so far and
+ set the number of instructions read to INST_READ. */
+
+static CORE_ADDR
+find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
+ int inst_count, int *inst_read)
+{
+ /* The vector PCS is used to store instruction addresses within
+ a pc range. */
+ CORE_ADDR loop_start, loop_end, p;
+ VEC (CORE_ADDR) *pcs = NULL;
+ struct symtab_and_line sal;
+ struct cleanup *cleanup = make_cleanup (VEC_cleanup (CORE_ADDR), &pcs);
+
+ *inst_read = 0;
+ loop_start = loop_end = addr;
+
+ /* In each iteration of the outer loop, we get a pc range that ends before
+ LOOP_START, then we count and store every instruction address of the range
+ iterated in the loop.
+ If the number of instructions counted reaches INST_COUNT, return the
+ stored address that is located INST_COUNT instructions back from ADDR.
+ If INST_COUNT is not reached, we subtract the number of counted
+ instructions from INST_COUNT, and go to the next iteration. */
+ do
+ {
+ VEC_truncate (CORE_ADDR, pcs, 0);
+ 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;
+ }
+
+ loop_end = loop_start;
+ loop_start = sal.pc;
+
+ /* This loop pushes instruction addresses in the range from
+ LOOP_START to LOOP_END. */
+ for (p = loop_start; p < loop_end;)
+ {
+ VEC_safe_push (CORE_ADDR, pcs, p);
+ p += gdb_insn_length (gdbarch, p);
+ }
+
+ inst_count -= VEC_length (CORE_ADDR, pcs);
+ *inst_read += VEC_length (CORE_ADDR, pcs);
+ }
+ while (inst_count > 0);
+
+ /* After the loop, the vector PCS has instruction addresses of the last
+ source line we processed, and INST_COUNT has a negative value.
+ We return the address at the index of -INST_COUNT in the vector for
+ the reason below.
+ Let's assume the following instruction addresses and run 'x/-4i 0x400e'.
+ Line X of File
+ 0x4000
+ 0x4001
+ 0x4005
+ Line Y of File
+ 0x4009
+ 0x400c
+ => 0x400e
+ 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
+ 4001 is located at the index 1 of the last iterated line (= Line X),
+ which is simply calculated by -INST_COUNT.
+ The case when the length of PCS is 0 means that we reached an area for
+ which line info is not available. In such case, we return LOOP_START,
+ which was the lowest instruction address that had line info. */
+ p = VEC_length (CORE_ADDR, pcs) > 0
+ ? VEC_index (CORE_ADDR, pcs, -inst_count)
+ : loop_start;
+
+ /* INST_READ includes all instruction addresses in a pc range. Need to
+ exclude the beginning part up to the address we're returning. That
+ is, exclude {0x4000} in the example above. */
+ if (inst_count < 0)
+ *inst_read += inst_count;
+
+ do_cleanups (cleanup);
+ return p;
+}
+
+/* Backward read LEN bytes of target memory from address MEMADDR + LEN,
+ placing the results in GDB's memory from MYADDR + LEN. Returns
+ a count of the bytes actually read. */
+
+static int
+read_memory_backward (struct gdbarch *gdbarch,
+ CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ int errcode;
+ int nread; /* Number of bytes actually read. */
+
+ /* First try a complete read. */
+ errcode = target_read_memory (memaddr, myaddr, len);
+ if (errcode == 0)
+ {
+ /* Got it all. */
+ nread = len;
+ }
+ else
+ {
+ /* Loop, reading one byte at a time until we get as much as we can. */
+ 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;
+ }
+ }
+ }
+ return nread;
+}
+
+/* Returns true if X (which is LEN bytes wide) is the number zero. */
+
+static int
+integer_is_zero (const gdb_byte *x, int len)
+{
+ int i = 0;
+
+ while (i < len && x[i] == 0)
+ ++i;
+ return (i == len);
+}
+
+/* Find the start address of a string in which ADDR is included.
+ Basically we search for '\0' and return the next address,
+ but if OPTIONS->PRINT_MAX is smaller than the length of a string,
+ we stop searching and return the address to print characters as many as
+ PRINT_MAX from the string. */
+
+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)
+{
+ const int chunk_size = 0x20;
+ gdb_byte *buffer = NULL;
+ struct cleanup *cleanup = NULL;
+ int read_error = 0;
+ int chars_read = 0;
+ int chars_to_read = chunk_size;
+ int chars_counted = 0;
+ int count_original = count;
+ CORE_ADDR string_start_addr = addr;
+
+ gdb_assert (char_size == 1 || char_size == 2 || char_size == 4);
+ buffer = (gdb_byte *) xmalloc (chars_to_read * char_size);
+ cleanup = make_cleanup (xfree, buffer);
+ while (count > 0 && read_error == 0)
+ {
+ int i;
+
+ addr -= chars_to_read * char_size;
+ chars_read = read_memory_backward (gdbarch, addr, buffer,
+ 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;
+ }
+ }
+ }
+
+ /* Update STRINGS_COUNTED with the actual number of loaded strings. */
+ *strings_counted = count_original - count;
+
+ 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. */
+ string_start_addr -= chars_counted * char_size;
+ }
+
+ do_cleanups (cleanup);
+ return string_start_addr;
+}
+