| /* 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); |
| } |
| |
| /******************************************************************************/ |