/* Common target dependent code for GDB on ARM systems.
- Copyright 1988, 1989, 1991, 1992, 1993, 1995-1999
+ Copyright 1988, 1989, 1991, 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000
Free Software Foundation, Inc.
This file is part of GDB.
#include "symfile.h"
#include "gdb_string.h"
#include "coff/internal.h" /* Internal format of COFF symbols in BFD */
+#include "dis-asm.h" /* For register flavors. */
extern void _initialize_arm_tdep (void);
-/*
- The following macros are actually wrong. Neither arm nor thumb can
- or should set the lsb on addr.
- The thumb addresses are mod 2, so (addr & 2) would be a good heuristic
- to use when checking for thumb (see arm_pc_is_thumb() below).
- Unfortunately, something else depends on these (incorrect) macros, so
- fixing them actually breaks gdb. I didn't have time to investigate. Z.R.
- */
-/* Thumb function addresses are odd (bit 0 is set). Here are some
- macros to test, set, or clear bit 0 of addresses. */
-#define IS_THUMB_ADDR(addr) ((addr) & 1)
-#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
-#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
+/* Number of different reg name sets (options). */
+static int num_flavor_options;
-/* Default register names as specified by APCS. */
-static char *apcs_register_names[] =
-{"a1", "a2", "a3", "a4", /* 0 1 2 3 */
- "v1", "v2", "v3", "v4", /* 4 5 6 7 */
- "v5", "v6", "sl", "fp", /* 8 9 10 11 */
- "ip", "sp", "lr", "pc", /* 12 13 14 15 */
- "f0", "f1", "f2", "f3", /* 16 17 18 19 */
- "f4", "f5", "f6", "f7", /* 20 21 22 23 */
- "fps", "ps"} /* 24 25 */ ;
-
-/* Alternate set of registers names used by GCC. */
-static char *additional_register_names[] =
-{"r0", "r1", "r2", "r3", /* 0 1 2 3 */
- "r4", "r5", "r6", "r7", /* 4 5 6 7 */
- "r8", "r9", "r10", "r11", /* 8 9 10 11 */
- "r12", "r13", "r14", "pc", /* 12 13 14 15 */
- "f0", "f1", "f2", "f3", /* 16 17 18 19 */
- "f4", "f5", "f6", "f7", /* 20 21 22 23 */
- "fps", "ps"} /* 24 25 */ ;
-
-/* This is the variable that is set with "set disassembly-flavor".
- By default use the APCS registers names. */
-char **arm_register_names = apcs_register_names;
-
-/* Valid register name flavours. */
-static char apcs_flavor[] = "apcs";
-static char r_prefix_flavor[] = "r-prefix";
-static char *valid_flavors[] =
-{
- apcs_flavor,
- r_prefix_flavor,
- NULL
-};
+/* We have more registers than the disassembler as gdb can print the value
+ of special registers as well.
+ The general register names are overwritten by whatever is being used by
+ the disassembler at the moment. We also adjust the case of cpsr and fps. */
+
+/* Initial value: Register names used in ARM's ISA documentation. */
+static char * arm_register_name_strings[] =
+{"r0", "r1", "r2", "r3", /* 0 1 2 3 */
+ "r4", "r5", "r6", "r7", /* 4 5 6 7 */
+ "r8", "r9", "r10", "r11", /* 8 9 10 11 */
+ "r12", "sp", "lr", "pc", /* 12 13 14 15 */
+ "f0", "f1", "f2", "f3", /* 16 17 18 19 */
+ "f4", "f5", "f6", "f7", /* 20 21 22 23 */
+ "fps", "cpsr" }; /* 24 25 */
+char **arm_register_names = arm_register_name_strings;
-/* Disassembly flavor to use. */
-static char *disassembly_flavor = apcs_flavor;
+/* Valid register name flavors. */
+static char **valid_flavors;
+
+/* Disassembly flavor to use. Default to "std" register names. */
+static char *disassembly_flavor;
+static int current_option; /* Index to that option in the opcodes table. */
/* This is used to keep the bfd arch_info in sync with the disassembly
flavor. */
int framereg;
};
+/* Addresses for calling Thumb functions have the bit 0 set.
+ Here are some macros to test, set, or clear bit 0 of addresses. */
+#define IS_THUMB_ADDR(addr) ((addr) & 1)
+#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
+#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
+
+#define SWAP_TARGET_AND_HOST(buffer,len) \
+ do \
+ { \
+ if (TARGET_BYTE_ORDER != HOST_BYTE_ORDER) \
+ { \
+ char tmp; \
+ char *p = (char *)(buffer); \
+ char *q = ((char *)(buffer)) + len - 1; \
+ for (; p < q; p++, q--) \
+ { \
+ tmp = *q; \
+ *q = *p; \
+ *p = tmp; \
+ } \
+ } \
+ } \
+ while (0)
+
/* Will a function return an aggregate type in memory or in a
register? Return 0 if an aggregate type can be returned in a
register, 1 if it must be returned in memory. */
{
CORE_ADDR sp = read_sp ();
- if (PC_IN_CALL_DUMMY (memaddr, sp, sp + 64))
+ /* FIXME: Until we switch for the new call dummy macros, this heuristic
+ is the best we can do. We are trying to determine if the pc is on
+ the stack, which (hopefully) will only happen in a call dummy.
+ We hope the current stack pointer is not so far alway from the dummy
+ frame location (true if we have not pushed large data structures or
+ gone too many levels deep) and that our 1024 is not enough to consider
+ code regions as part of the stack (true for most practical purposes) */
+ if (PC_IN_CALL_DUMMY (memaddr, sp, sp + 1024))
return caller_is_thumb;
else
return 0;
add sp, sp, #-28
add r7, sp, #12
Sometimes the latter instruction may be replaced by:
- mov r7, sp
+ mov r7, sp
+
+ or like this:
+ push {r7, lr}
+ mov r7, sp
+ sub sp, #12
+
+ or, on tpcs, like this:
+ sub sp,#16
+ push {r7, lr}
+ (many instructions)
+ mov r7, sp
+ sub sp, #12
+
+ There is always one instruction of three classes:
+ 1 - push
+ 2 - setting of r7
+ 3 - adjusting of sp
+
+ When we have found at least one of each class we are done with the prolog.
+ Note that the "sub sp, #NN" before the push does not count.
*/
static CORE_ADDR
thumb_skip_prologue (CORE_ADDR pc)
{
CORE_ADDR current_pc;
+ int findmask = 0; /* findmask:
+ bit 0 - push { rlist }
+ bit 1 - mov r7, sp OR add r7, sp, #imm (setting of r7)
+ bit 2 - sub sp, #simm OR add sp, #simm (adjusting of sp)
+ */
- for (current_pc = pc; current_pc < pc + 20; current_pc += 2)
+ for (current_pc = pc; current_pc < pc + 40; current_pc += 2)
{
unsigned short insn = read_memory_unsigned_integer (current_pc, 2);
- if ((insn & 0xfe00) != 0xb400 /* push {..., r7, lr} */
- && (insn & 0xff00) != 0xb000 /* add sp, #simm */
- && (insn & 0xff00) != 0xaf00 /* add r7, sp, #imm */
- && insn != 0x466f /* mov r7, sp */
- && (insn & 0xffc0) != 0x4640) /* mov r0-r7, r8-r15 */
- break;
+ if ((insn & 0xfe00) == 0xb400) /* push { rlist } */
+ {
+ findmask |= 1; /* push found */
+ }
+ else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR sub sp, #simm */
+ {
+ if ((findmask & 1) == 0) /* before push ? */
+ continue;
+ else
+ findmask |= 4; /* add/sub sp found */
+ }
+ else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */
+ {
+ findmask |= 2; /* setting of r7 found */
+ }
+ else if (insn == 0x466f) /* mov r7, sp */
+ {
+ findmask |= 2; /* setting of r7 found */
+ }
+ else
+ continue; /* something in the prolog that we don't care about or some
+ instruction from outside the prolog scheduled here for optimization */
}
return current_pc;
4) the offset from the stack pointer to the frame pointer
This information is stored in the "extra" fields of the frame_info.
- A typical Thumb function prologue might look like this:
- push {r7, lr}
- sub sp, #28,
- add r7, sp, #12
- Which would create this stack frame (offsets relative to FP)
+ A typical Thumb function prologue would create this stack frame
+ (offsets relative to FP)
old SP -> 24 stack parameters
20 LR
16 R7
R7 -> 0 local variables (16 bytes)
SP -> -12 additional stack space (12 bytes)
The frame size would thus be 36 bytes, and the frame offset would be
- 12 bytes. The frame register is R7. */
+ 12 bytes. The frame register is R7.
+
+ The comments for thumb_skip_prolog() describe the algorithm we use to detect
+ the end of the prolog */
/* *INDENT-ON* */
static void
CORE_ADDR prologue_end;
CORE_ADDR current_pc;
int saved_reg[16]; /* which register has been copied to register n? */
+ int findmask = 0; /* findmask:
+ bit 0 - push { rlist }
+ bit 1 - mov r7, sp OR add r7, sp, #imm (setting of r7)
+ bit 2 - sub sp, #simm OR add sp, #simm (adjusting of sp)
+ */
int i;
if (find_pc_partial_function (fi->pc, NULL, &prologue_start, &prologue_end))
saved_reg[i] = i;
/* Search the prologue looking for instructions that set up the
- frame pointer, adjust the stack pointer, and save registers. */
+ frame pointer, adjust the stack pointer, and save registers.
+ Do this until all basic prolog instructions are found. */
fi->framesize = 0;
- for (current_pc = prologue_start; current_pc < prologue_end; current_pc += 2)
+ for (current_pc = prologue_start;
+ (current_pc < prologue_end) && ((findmask & 7) != 7);
+ current_pc += 2)
{
unsigned short insn;
int regno;
if ((insn & 0xfe00) == 0xb400) /* push { rlist } */
{
+ int mask;
+ findmask |= 1; /* push found */
/* Bits 0-7 contain a mask for registers R0-R7. Bit 8 says
whether to save LR (R14). */
- int mask = (insn & 0xff) | ((insn & 0x100) << 6);
+ mask = (insn & 0xff) | ((insn & 0x100) << 6);
/* Calculate offsets of saved R0-R7 and LR. */
for (regno = LR_REGNUM; regno >= 0; regno--)
saved_reg[regno] = regno; /* reset saved register map */
}
}
- else if ((insn & 0xff00) == 0xb000) /* add sp, #simm */
+ else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR sub sp, #simm */
{
+ if ((findmask & 1) == 0) /* before push ? */
+ continue;
+ else
+ findmask |= 4; /* add/sub sp found */
+
offset = (insn & 0x7f) << 2; /* get scaled offset */
- if (insn & 0x80) /* is it signed? */
- offset = -offset;
+ if (insn & 0x80) /* is it signed? (==subtracting) */
+ {
+ fi->frameoffset += offset;
+ offset = -offset;
+ }
fi->framesize -= offset;
}
else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */
{
+ findmask |= 2; /* setting of r7 found */
fi->framereg = THUMB_FP_REGNUM;
fi->frameoffset = (insn & 0xff) << 2; /* get scaled offset */
}
- else if (insn == 0x466f) /* mov r7, sp */
+ else if (insn == 0x466f) /* mov r7, sp */
{
+ findmask |= 2; /* setting of r7 found */
fi->framereg = THUMB_FP_REGNUM;
fi->frameoffset = 0;
saved_reg[THUMB_FP_REGNUM] = SP_REGNUM;
saved_reg[lo_reg] = hi_reg; /* remember hi reg was saved */
}
else
- break; /* anything else isn't prologue */
+ continue; /* something in the prolog that we don't care about or some
+ instruction from outside the prolog scheduled here for optimization */
}
}
calling the function. */
if (TYPE_CODE_FLT == typecode && REGISTER_SIZE == len)
{
- float f = *(float *) val;
- dbl_arg = f;
- val = (char *) &dbl_arg;
+ float f;
+ double d;
+ char * bufo = (char *) &d;
+ char * bufd = (char *) &dbl_arg;
+
len = sizeof (double);
+ f = *(float *) val;
+ SWAP_TARGET_AND_HOST (&f, sizeof (float)); /* adjust endianess */
+ d = f;
+ /* We must revert the longwords so they get loaded into the
+ the right registers. */
+ memcpy (bufd, bufo + len / 2, len / 2);
+ SWAP_TARGET_AND_HOST (bufd, len / 2); /* adjust endianess */
+ memcpy (bufd + len / 2, bufo, len / 2);
+ SWAP_TARGET_AND_HOST (bufd + len / 2, len / 2); /* adjust endianess */
+ val = (char *) &dbl_arg;
}
-#if 0
+#if 1
+ /* I don't know why this code was disable. The only logical use
+ for a function pointer is to call that function, so setting
+ the mode bit is perfectly fine. FN */
/* If the argument is a pointer to a function, and it is a Thumb
function, set the low bit of the pointer. */
if (TYPE_CODE_PTR == typecode
void
arm_pop_frame (void)
{
- struct frame_info *frame = get_current_frame ();
int regnum;
- CORE_ADDR old_SP;
+ struct frame_info *frame = get_current_frame ();
- old_SP = read_register (frame->framereg);
- for (regnum = 0; regnum < NUM_REGS; regnum++)
- if (frame->fsr.regs[regnum] != 0)
- write_register (regnum,
+ if (!PC_IN_CALL_DUMMY(frame->pc, frame->frame, read_fp()))
+ {
+ CORE_ADDR old_SP;
+
+ old_SP = read_register (frame->framereg);
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ if (frame->fsr.regs[regnum] != 0)
+ write_register (regnum,
read_memory_integer (frame->fsr.regs[regnum], 4));
- write_register (PC_REGNUM, FRAME_SAVED_PC (frame));
- write_register (SP_REGNUM, old_SP);
+ write_register (PC_REGNUM, FRAME_SAVED_PC (frame));
+ write_register (SP_REGNUM, old_SP);
+ }
+ else
+ {
+ CORE_ADDR sp;
+
+ sp = read_register (FP_REGNUM);
+ sp -= sizeof(CORE_ADDR); /* we don't care about this first word */
+
+ write_register (PC_REGNUM, read_memory_integer (sp, 4));
+ sp -= sizeof(CORE_ADDR);
+ write_register (SP_REGNUM, read_memory_integer (sp, 4));
+ sp -= sizeof(CORE_ADDR);
+ write_register (FP_REGNUM, read_memory_integer (sp, 4));
+ sp -= sizeof(CORE_ADDR);
+
+ for (regnum = 10; regnum >= 0; regnum--)
+ {
+ write_register (regnum, read_memory_integer (sp, 4));
+ sp -= sizeof(CORE_ADDR);
+ }
+ }
flush_cached_frames ();
}
print_fpu_flags (status);
}
-/* If the disassembly mode is APCS, we have to also switch the
- bfd mach_type. This function is run in the set disassembly_flavor
- command, and does that. */
-
-static void
-set_disassembly_flavor_sfunc (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- set_disassembly_flavor ();
-}
-
-static void
-set_disassembly_flavor (void)
-{
- if (disassembly_flavor == apcs_flavor)
- {
- if (arm_toggle_regnames () == 0)
- arm_toggle_regnames ();
- arm_register_names = apcs_register_names;
- }
- else if (disassembly_flavor == r_prefix_flavor)
- {
- if (arm_toggle_regnames () == 1)
- arm_toggle_regnames ();
- arm_register_names = additional_register_names;
- }
-}
-
-/* arm_othernames implements the "othernames" command. This is kind
- of hacky, and I prefer the set-show disassembly-flavor which is
- also used for the x86 gdb. I will keep this around, however, in
- case anyone is actually using it. */
-
-static void
-arm_othernames (char *names, int n)
-{
- if (disassembly_flavor == r_prefix_flavor)
- {
- disassembly_flavor = apcs_flavor;
- set_disassembly_flavor ();
- }
- else
- {
- disassembly_flavor = r_prefix_flavor;
- set_disassembly_flavor ();
- }
-}
-
#if 0
/* FIXME: The generated assembler works but sucks. Instead of using
r0, r1 it pushes them on the stack, then loads them into r3, r4 and
return 0; /* not a stub */
}
+/* If the user changes the register disassembly flavor used for info register
+ and other commands, we have to also switch the flavor used in opcodes
+ for disassembly output.
+ This function is run in the set disassembly_flavor command, and does that. */
+
+static void
+set_disassembly_flavor_sfunc (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ set_disassembly_flavor ();
+}
+\f
+static void
+set_disassembly_flavor (void)
+{
+ const char *setname, *setdesc, **regnames;
+ int numregs, j;
+
+ /* Find the flavor that the user wants in the opcodes table. */
+ int current = 0;
+ numregs = get_arm_regnames (current, &setname, &setdesc, ®names);
+ while ((disassembly_flavor != setname)
+ && (current < num_flavor_options))
+ get_arm_regnames (++current, &setname, &setdesc, ®names);
+ current_option = current;
+
+ /* Fill our copy. */
+ for (j = 0; j < numregs; j++)
+ arm_register_names[j] = (char *) regnames[j];
+
+ /* Adjust case. */
+ if (isupper (*regnames[PC_REGNUM]))
+ {
+ arm_register_names[FPS_REGNUM] = "FPS";
+ arm_register_names[PS_REGNUM] = "CPSR";
+ }
+ else
+ {
+ arm_register_names[FPS_REGNUM] = "fps";
+ arm_register_names[PS_REGNUM] = "cpsr";
+ }
+
+ /* Synchronize the disassembler. */
+ set_arm_regname_option (current);
+}
+
+/* arm_othernames implements the "othernames" command. This is kind
+ of hacky, and I prefer the set-show disassembly-flavor which is
+ also used for the x86 gdb. I will keep this around, however, in
+ case anyone is actually using it. */
+
+static void
+arm_othernames (char *names, int n)
+{
+ /* Circle through the various flavors. */
+ current_option = (current_option + 1) % num_flavor_options;
+
+ disassembly_flavor = valid_flavors[current_option];
+ set_disassembly_flavor ();
+}
+
void
_initialize_arm_tdep (void)
{
+ struct ui_file *stb;
+ long length;
struct cmd_list_element *new_cmd;
+ const char *setname, *setdesc, **regnames;
+ int numregs, i, j;
+ static char *helptext;
tm_print_insn = gdb_print_insn_arm;
+ /* Get the number of possible sets of register names defined in opcodes. */
+ num_flavor_options = get_arm_regname_num_options ();
+
/* Sync the opcode insn printer with our register viewer: */
+ parse_arm_disassembler_option ("reg-names-std");
- if (arm_toggle_regnames () != 1)
- arm_toggle_regnames ();
+ /* Begin creating the help text. */
+ stb = mem_fileopen ();
+ fprintf_unfiltered (stb, "Set the disassembly flavor.\n\
+The valid values are:\n");
- /* Add the deprecated "othernames" command */
+ /* Initialize the array that will be passed to add_set_enum_cmd(). */
+ valid_flavors = xmalloc ((num_flavor_options + 1) * sizeof (char *));
+ for (i = 0; i < num_flavor_options; i++)
+ {
+ numregs = get_arm_regnames (i, &setname, &setdesc, ®names);
+ valid_flavors[i] = (char *) setname;
+ fprintf_unfiltered (stb, "%s - %s\n", setname,
+ setdesc);
+ /* Copy the default names (if found) and synchronize disassembler. */
+ if (!strcmp (setname, "std"))
+ {
+ disassembly_flavor = (char *) setname;
+ current_option = i;
+ for (j = 0; j < numregs; j++)
+ arm_register_names[j] = (char *) regnames[j];
+ set_arm_regname_option (i);
+ }
+ }
+ /* Mark the end of valid options. */
+ valid_flavors[num_flavor_options] = NULL;
- add_com ("othernames", class_obscure, arm_othernames,
- "Switch to the other set of register names.");
+ /* Finish the creation of the help text. */
+ fprintf_unfiltered (stb, "The default is \"std\".");
+ helptext = ui_file_xstrdup (stb, &length);
+ ui_file_delete (stb);
/* Add the disassembly-flavor command */
-
new_cmd = add_set_enum_cmd ("disassembly-flavor", no_class,
valid_flavors,
(char *) &disassembly_flavor,
- "Set the disassembly flavor, \
-the valid values are \"apcs\" and \"r-prefix\", \
-and the default value is \"apcs\".",
+ helptext,
&setlist);
new_cmd->function.sfunc = set_disassembly_flavor_sfunc;
add_show_from_set (new_cmd, &showlist);
"Set usage of ARM 32-bit mode.\n", &setlist),
&showlist);
+ /* Add the deprecated "othernames" command */
+
+ add_com ("othernames", class_obscure, arm_othernames,
+ "Switch to the next set of register names.");
}
/* Test whether the coff symbol specific value corresponds to a Thumb