summaryrefslogtreecommitdiff
path: root/gdb/arc-arguments.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/arc-arguments.c')
-rw-r--r--gdb/arc-arguments.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/gdb/arc-arguments.c b/gdb/arc-arguments.c
new file mode 100644
index 00000000000..bd3404a6986
--- /dev/null
+++ b/gdb/arc-arguments.c
@@ -0,0 +1,427 @@
+/* Target dependent code for ARC processor family, for GDB, the GNU debugger.
+
+ Copyright 2008, 2009 Free Software Foundation, Inc.
+
+ Contributed by ARC International (www.arc.com)
+
+ Authors:
+ Richard Stuckey <richard.stuckey@arc.com>
+
+ 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/>. */
+
+/******************************************************************************/
+/* */
+/* Outline: */
+/* This module implements operations for setting up the command line */
+/* arguments to the program which is being debugged. */
+/* */
+/* E.g. if we are passing 4 arguments to the program's 'main' function, */
+/* we must place them on the stack in the layout: */
+/* */
+/* . */
+/* . */
+/* stack[top + A3] <== <arg_3> */
+/* . */
+/* . */
+/* stack[top + A2] <== <arg_2> */
+/* . */
+/* . */
+/* stack[top + A1] <== <arg_1> */
+/* . */
+/* . */
+/* stack[top + A0] <== <arg_0> */
+/* stack[top + 24] <== 0x0 # ? NULL terminator */
+/* stack[top + 20] <== 0x0 # envp NULL terminator */
+/* stack[top + 16] <== 0x0 # argv NULL terminator */
+/* stack[top + 12] <== TOP + A3 # argv[3] */
+/* stack[top + 8] <== TOP + A2 # argv[2] */
+/* stack[top + 4] <== TOP + A1 # argv[1] */
+/* stack[top + 0] <== TOP + A0 # argv[0] */
+/* */
+/* where TOP = &stack[top] */
+/* and A0 .. A3 are the offsets of the stored arguments from the stack */
+/* top. */
+/* */
+/******************************************************************************/
+
+/* system header files */
+#include <byteswap.h>
+
+/* gdb header files */
+#include "defs.h"
+#include "target.h"
+#include "symtab.h"
+#include "regcache.h"
+#include "objfiles.h"
+
+/* ARC header files */
+#include "arc-arguments.h"
+#include "arc-support.h"
+#include "arc-tdep.h"
+
+
+/* -------------------------------------------------------------------------- */
+/* local data */
+/* -------------------------------------------------------------------------- */
+
+#define MINIMUM_INSTRUCTION_SIZE 2
+#define MOV_SP_INSTRUCTION_LE 0x3F80240A
+#define MOV_SP_INSTRUCTION_BE 0x803F0A24
+
+
+/* The address of the top of the target program's stack before the program
+ arguments were pushed onto the stack. */
+static CORE_ADDR old_stack_top;
+
+/* The address in the target program code of the instructions which set up the
+ stack pointer (SP) at program start-up. */
+static CORE_ADDR stack_pointer_setup_code_operand_address;
+
+
+/* -------------------------------------------------------------------------- */
+/* local macros */
+/* -------------------------------------------------------------------------- */
+
+#define TARGET_IS_BIG_ENDIAN (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+
+#define TARGET_ENDIAN(word) ((TARGET_IS_BIG_ENDIAN) ? __bswap_32(word) : (word))
+
+#define MK_OPERAND_LE(x) (ARC_Word) ((((x) & 0xffff0000) >> 16) | \
+ (((x) & 0x0000ffff) << 16))
+
+#define MK_OPERAND_BE(x) (ARC_Word) (__bswap_32(x))
+
+
+/* -------------------------------------------------------------------------- */
+/* local functions */
+/* -------------------------------------------------------------------------- */
+
+/* Find the address in the target program code of the given label.
+ Return 0 if the label is not found. */
+
+static CORE_ADDR
+find_label (const char *label)
+{
+ struct minimal_symbol *msymbol = lookup_minimal_symbol(label, NULL, symfile_objfile);
+ CORE_ADDR address = 0;
+
+ if (msymbol != NULL)
+ address = SYMBOL_VALUE_ADDRESS (msymbol);
+
+ DEBUG("%s = %x\n", label, (unsigned int) address);
+
+ return address;
+}
+
+
+/* Write a word of data to target memory.
+
+ Parameters:
+ address : the address in memory to write the data
+ word : the data to be written
+
+ Returns TRUE if the data is written. FALSE otherwise.
+
+ If the data is written, the given address is incremented to reference the
+ next word in target memory. */
+
+static Boolean
+write_word (CORE_ADDR *address, ARC_Word word)
+{
+ word = TARGET_ENDIAN(word);
+
+ if (target_write_memory(*address, (gdb_byte*) &word, BYTES_IN_WORD) == 0)
+ {
+ *address += BYTES_IN_WORD;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Try to find the address in the target program code of the instructions which
+ set up the program stack pointer (SP).
+
+ Parameter:
+ stack_top: the address which is loaded into SP by the setup code
+
+ Return TRUE if the code is found.
+
+ If the setup code is found, the variable stack_pointer_setup_code_operand_address
+ is set to the code's address. */
+
+static Boolean
+find_stack_top_setup_code (CORE_ADDR stack_top)
+{
+ /* Try to find the start address in the target program. */
+ CORE_ADDR code_start = find_label("__start");
+
+ if (code_start != 0)
+ {
+ CORE_ADDR code = (ARC_Address) code_start;
+ ARC_Word set_sp_insn[2];
+ ARC_Byte buffer[16 * BYTES_IN_WORD];
+
+ DEBUG("setting up arguments: stack_top = %x, code_start = %x\n",
+ (unsigned int) stack_top, (unsigned int) code_start);
+
+ if (TARGET_IS_BIG_ENDIAN)
+ {
+ set_sp_insn[0] = MOV_SP_INSTRUCTION_BE;
+ set_sp_insn[1] = MK_OPERAND_BE(stack_top);
+ }
+ else
+ {
+ set_sp_insn[0] = MOV_SP_INSTRUCTION_LE;
+ set_sp_insn[1] = MK_OPERAND_LE(stack_top);
+ }
+
+ /* Scan through the start code of the program, looking for the code that
+ sets up the program's stack pointer; we recognize this as a 32-bit
+ 'mov sp' instruction followed by a 32-bit operand which is the
+ address of the stack top (which we obtained from the executable file). */
+ while (TRUE)
+ {
+ int result = target_read_memory(code, (gdb_byte*) buffer, (int) sizeof(buffer));
+
+ if (result == 0)
+ {
+ size_t offset = 0;
+
+ while (offset <= sizeof(buffer) - sizeof(set_sp_insn))
+ {
+ if (memcmp(buffer + offset, set_sp_insn, sizeof(set_sp_insn)) == 0)
+ {
+ stack_pointer_setup_code_operand_address = code + (CORE_ADDR) offset + BYTES_IN_WORD;
+
+ DEBUG("found 'mov sp, <stacktop>' instruction operand at address 0x%x\n",
+ (unsigned int) stack_pointer_setup_code_operand_address);
+ return TRUE;
+ }
+
+ offset += MINIMUM_INSTRUCTION_SIZE;
+ }
+ }
+ else
+ {
+ warning(_("can not find read target program start code"));
+ break;
+ }
+
+ code += (CORE_ADDR) (sizeof(buffer) - sizeof(set_sp_insn));
+
+ /* If we haven't found it in the first 100 bytes. */
+ if (code - code_start > 100)
+ {
+ warning(_("can not find 'mov sp, <stacktop>' instruction in start code"));
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* Try to change the setup code in the program so that SP is loaded with a
+ given address.
+
+ Parameters:
+ old_stack_top: the address which is currently loaded into SP by the code
+ new_stack_top: the address which we wish to be loaded into SP by the code
+
+ Return TRUE if the setup code is changed. */
+
+static Boolean
+set_stack_top (CORE_ADDR old_stack_top, CORE_ADDR new_stack_top)
+{
+ ARC_Word operand = (TARGET_IS_BIG_ENDIAN) ? MK_OPERAND_BE(new_stack_top)
+ : MK_OPERAND_LE(new_stack_top);
+
+ /* If we do not yet know the address in the program code at which the
+ program's stack pointer is set up. */
+ if (stack_pointer_setup_code_operand_address == 0)
+ {
+ /* Try to find it. */
+ if (!find_stack_top_setup_code(old_stack_top))
+ return FALSE;
+ }
+
+ DEBUG("set stack top @ 0x%08X to 0x%08X (0x%08X)\n",
+ (unsigned int) stack_pointer_setup_code_operand_address,
+ (unsigned int) new_stack_top,
+ operand);
+
+ return (target_write_memory(stack_pointer_setup_code_operand_address,
+ (gdb_byte*) &operand,
+ BYTES_IN_WORD) == 0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* externally visible functions */
+/* -------------------------------------------------------------------------- */
+
+/* This is called when a program is downloaded to the debug target. */
+
+void
+arc_program_loaded (void)
+{
+ /* The program has just been loaded, so we do not yet know the address in
+ the program code at which the program's stack pointer is set up. */
+ stack_pointer_setup_code_operand_address = 0;
+}
+
+
+/* Store the program's arguments on the stack.
+ Return TRUE if they are stored successfully. */
+
+Boolean
+arc_setup_arguments (char *args)
+{
+ Boolean done = FALSE;
+
+ /* Try to find the top of stack in the target program. */
+ old_stack_top = find_label("__stack_top");
+
+ if (old_stack_top != 0)
+ {
+ char **argv = buildargv (args);
+ char **argp;
+ size_t string_length = 0;
+ unsigned int argc = 0;
+ unsigned int num_pointers;
+ unsigned int total_size;
+ CORE_ADDR new_stack_top;
+
+ if (argv == NULL)
+ nomem (0);
+
+ /* Calculate the space required to hold the args. */
+
+ for (argp = argv; *argp != NULL; argp++)
+ {
+ string_length += strlen (*argp) + 1;
+ argc++;
+ }
+
+ DEBUG("%d arguments\n", argc);
+
+ num_pointers = argc + 3;
+
+ total_size = (unsigned int) string_length + num_pointers * BYTES_IN_WORD;
+
+ /* Round up to a multiple of 32: strlen expects memory to come in chunks
+ * that are at least cache-line (32 bytes) sized. */
+ total_size += 31;
+ total_size &= -32;
+
+ DEBUG("total size: %d\n", total_size);
+
+ new_stack_top = old_stack_top - total_size;
+
+ DEBUG("new stack top: 0x%08x\n", (unsigned int) new_stack_top);
+
+ /* Adjust the setting of the top of the stack in the object code. */
+ if (set_stack_top(old_stack_top, new_stack_top))
+ {
+ struct regcache *regcache = get_current_regcache();
+ CORE_ADDR data_space = new_stack_top + num_pointers * BYTES_IN_WORD;
+ CORE_ADDR stack_top = new_stack_top;
+ unsigned int i;
+
+ DEBUG("data space: 0x%08x\n", (unsigned int) data_space);
+
+ done = TRUE;
+
+ /* Write the args onto the top of the stack. */
+
+ for (i = 0; i < argc; i++)
+ {
+ char *parameter = argv[i];
+ size_t length = strlen(parameter) + 1;
+ int result = target_write_memory(data_space, (gdb_byte*) parameter, (int) length);
+
+ if (result == 0)
+ {
+ DEBUG("written argv[%d] to 0x%08x: \"%s\"\n",
+ i, (unsigned int) data_space, parameter);
+ }
+ else
+ done = FALSE;
+
+ /* Write a pointer to the argument onto the stack. */
+ if (!write_word(&stack_top, (ARC_Word) data_space))
+ done = FALSE;
+
+ data_space += length;
+ }
+
+ /* Try to write the NULLs. */
+ if (!write_word(&stack_top, 0) ||
+ !write_word(&stack_top, 0) ||
+ !write_word(&stack_top, 0))
+ done = FALSE;
+
+ /* Set up the R0 and R1 parameter registers. */
+
+ /* Convert to target byte order if necessary. */
+ if (HOST_AND_TARGET_ENDIANNESS_DIFFER(current_gdbarch))
+ {
+ argc = __bswap_32(argc);
+ new_stack_top = __bswap_32(new_stack_top);
+ }
+
+ regcache_raw_supply (regcache, 0, &argc);
+ regcache_raw_supply (regcache, 1, &new_stack_top);
+ target_store_registers(regcache, 0);
+ target_store_registers(regcache, 1);
+ }
+
+ freeargv(argv);
+ }
+
+ return done;
+}
+
+
+/* This function is called just before disconnection from the debug target. */
+
+void
+arc_restore_stack_top_address (void)
+{
+ /* If we know the address in the program start-up code at which the stack
+ pointer is set up, it must be because we changed the stack top address
+ in the code - so change it back to the original address as read from the
+ excutable file.
+
+ This is done so that if the user disconnects from the target, then
+ reconnects to it in a subsequent debugging session but does NOT download
+ the program to the target again (as it is still in target memory), the
+ mechanism for altering the stack top will still work.
+
+ Note that this has no effect if the target is allowed to resume execution
+ (i.e. a 'detach' is being performed) as we are changing code that has
+ already been executed.
+
+ 0 is passed as the "old" stack top as it is not used in this situation. */
+
+ if (stack_pointer_setup_code_operand_address != 0)
+ (void) set_stack_top(0, old_stack_top);
+}
+
+/******************************************************************************/