Index: gdb/tilegx-tdep.c |
diff --git a/gdb/tilegx-tdep.c b/gdb/tilegx-tdep.c |
index 9ec3aaa53fd23201afb21af91d65c8b027b64840..c115f4993307faa1f51e9849b140fab861583f60 100644 |
--- a/gdb/tilegx-tdep.c |
+++ b/gdb/tilegx-tdep.c |
@@ -1,6 +1,6 @@ |
/* Target-dependent code for the Tilera TILE-Gx processor. |
- Copyright (C) 2012 Free Software Foundation, Inc. |
+ Copyright (C) 2012-2013 Free Software Foundation, Inc. |
This file is part of GDB. |
@@ -30,7 +30,7 @@ |
#include "value.h" |
#include "dis-asm.h" |
#include "inferior.h" |
-#include "gdb_string.h" |
+#include <string.h> |
#include "gdb_assert.h" |
#include "arch-utils.h" |
#include "floatformat.h" |
@@ -155,7 +155,7 @@ tilegx_register_name (struct gdbarch *gdbarch, int regnum) |
"r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", |
"r48", "r49", "r50", "r51", "r52", "tp", "sp", "lr", |
"sn", "idn0", "idn1", "udn0", "udn1", "udn2", "udn3", "zero", |
- "pc" |
+ "pc", "faultnum", |
}; |
if (regnum < 0 || regnum >= TILEGX_NUM_REGS) |
@@ -292,7 +292,7 @@ tilegx_push_dummy_call (struct gdbarch *gdbarch, |
int argreg = TILEGX_R0_REGNUM; |
int i, j; |
int typelen, slacklen, alignlen; |
- static const gdb_byte two_zero_words[8] = { 0 }; |
+ static const gdb_byte four_zero_words[16] = { 0 }; |
/* If struct_return is 1, then the struct return address will |
consume one argument-passing register. */ |
@@ -326,38 +326,30 @@ tilegx_push_dummy_call (struct gdbarch *gdbarch, |
/* Align SP. */ |
stack_dest = tilegx_frame_align (gdbarch, stack_dest); |
- /* Loop backwards through arguments to determine stack alignment. */ |
- alignlen = 0; |
- |
- for (j = nargs - 1; j >= i; j--) |
- { |
- typelen = TYPE_LENGTH (value_enclosing_type (args[j])); |
- alignlen += (typelen + 3) & (~3); |
- } |
- |
- if (alignlen & 0x4) |
- stack_dest -= 4; |
- |
/* Loop backwards through remaining arguments and push them on |
the stack, word aligned. */ |
for (j = nargs - 1; j >= i; j--) |
{ |
gdb_byte *val; |
+ struct cleanup *back_to; |
+ const gdb_byte *contents = value_contents (args[j]); |
typelen = TYPE_LENGTH (value_enclosing_type (args[j])); |
- slacklen = ((typelen + 3) & (~3)) - typelen; |
- val = alloca (typelen + slacklen); |
- memcpy (val, value_contents (args[j]), typelen); |
+ slacklen = align_up (typelen, 8) - typelen; |
+ val = xmalloc (typelen + slacklen); |
+ back_to = make_cleanup (xfree, val); |
+ memcpy (val, contents, typelen); |
memset (val + typelen, 0, slacklen); |
/* Now write data to the stack. The stack grows downwards. */ |
stack_dest -= typelen + slacklen; |
write_memory (stack_dest, val, typelen + slacklen); |
+ do_cleanups (back_to); |
} |
- /* Add 2 words for linkage space to the stack. */ |
- stack_dest = stack_dest - 8; |
- write_memory (stack_dest, two_zero_words, 8); |
+ /* Add 16 bytes for linkage space to the stack. */ |
+ stack_dest = stack_dest - 16; |
+ write_memory (stack_dest, four_zero_words, 16); |
/* Update stack pointer. */ |
regcache_cooked_write_unsigned (regcache, TILEGX_SP_REGNUM, stack_dest); |
@@ -401,7 +393,7 @@ tilegx_analyze_prologue (struct gdbarch* gdbarch, |
struct tilegx_reverse_regs |
new_reverse_frame[TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE]; |
int dest_regs[TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE]; |
- int reverse_frame_valid, prolog_done, branch_seen; |
+ int reverse_frame_valid, prolog_done, branch_seen, lr_saved_on_stack_p; |
LONGEST prev_sp_value; |
int i, j; |
@@ -417,6 +409,7 @@ tilegx_analyze_prologue (struct gdbarch* gdbarch, |
prolog_done = 0; |
branch_seen = 0; |
prev_sp_value = 0; |
+ lr_saved_on_stack_p = 0; |
/* To cut down on round-trip overhead, we fetch multiple bundles |
at once. These variables describe the range of memory we have |
@@ -440,6 +433,8 @@ tilegx_analyze_prologue (struct gdbarch* gdbarch, |
if (instbuf_size > size_on_same_page) |
instbuf_size = size_on_same_page; |
+ |
+ instbuf_size = min (instbuf_size, (end_addr - next_addr)); |
instbuf_start = next_addr; |
status = safe_frame_unwind_memory (next_frame, instbuf_start, |
@@ -480,7 +475,11 @@ tilegx_analyze_prologue (struct gdbarch* gdbarch, |
See trad-frame.h. */ |
cache->saved_regs[saved_register].realreg = saved_register; |
cache->saved_regs[saved_register].addr = saved_address; |
- } |
+ } |
+ else if (cache |
+ && (operands[0] == TILEGX_SP_REGNUM) |
+ && (operands[1] == TILEGX_LR_REGNUM)) |
+ lr_saved_on_stack_p = 1; |
break; |
case TILEGX_OPC_ADDI: |
case TILEGX_OPC_ADDLI: |
@@ -733,30 +732,45 @@ tilegx_analyze_prologue (struct gdbarch* gdbarch, |
} |
} |
+ if (lr_saved_on_stack_p) |
+ { |
+ cache->saved_regs[TILEGX_LR_REGNUM].realreg = TILEGX_LR_REGNUM; |
+ cache->saved_regs[TILEGX_LR_REGNUM].addr = |
+ cache->saved_regs[TILEGX_SP_REGNUM].addr; |
+ } |
+ |
return prolog_end; |
} |
/* This is the implementation of gdbarch method skip_prologue. */ |
static CORE_ADDR |
-tilegx_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) |
+tilegx_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc) |
{ |
- struct symtab_and_line sal; |
- CORE_ADDR func_start, func_end; |
+ CORE_ADDR func_start, end_pc; |
+ struct obj_section *s; |
/* This is the preferred method, find the end of the prologue by |
using the debugging information. */ |
- if (find_pc_partial_function (pc, NULL, &func_start, &func_end)) |
+ if (find_pc_partial_function (start_pc, NULL, &func_start, NULL)) |
{ |
- sal = find_pc_line (func_start, 0); |
+ CORE_ADDR post_prologue_pc |
+ = skip_prologue_using_sal (gdbarch, func_start); |
- if (sal.end < func_end && pc <= sal.end) |
- return sal.end; |
+ if (post_prologue_pc != 0) |
+ return max (start_pc, post_prologue_pc); |
} |
+ /* Don't straddle a section boundary. */ |
+ s = find_pc_section (start_pc); |
+ end_pc = start_pc + 8 * TILEGX_BUNDLE_SIZE_IN_BYTES; |
+ if (s != NULL) |
+ end_pc = min (end_pc, obj_section_endaddr (s)); |
+ |
/* Otherwise, try to skip prologue the hard way. */ |
return tilegx_analyze_prologue (gdbarch, |
- pc, pc + 8 * TILEGX_BUNDLE_SIZE_IN_BYTES, |
+ start_pc, |
+ end_pc, |
NULL, NULL); |
} |
@@ -780,6 +794,59 @@ tilegx_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) |
return 0; |
} |
+/* This is the implementation of gdbarch method get_longjmp_target. */ |
+ |
+static int |
+tilegx_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) |
+{ |
+ struct gdbarch *gdbarch = get_frame_arch (frame); |
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
+ CORE_ADDR jb_addr; |
+ gdb_byte buf[8]; |
+ |
+ jb_addr = get_frame_register_unsigned (frame, TILEGX_R0_REGNUM); |
+ |
+ /* TileGX jmp_buf contains 32 elements of type __uint_reg_t which |
+ has a size of 8 bytes. The return address is stored in the 25th |
+ slot. */ |
+ if (target_read_memory (jb_addr + 25 * 8, buf, 8)) |
+ return 0; |
+ |
+ *pc = extract_unsigned_integer (buf, 8, byte_order); |
+ |
+ return 1; |
+} |
+ |
+/* by assigning the 'faultnum' reg in kernel pt_regs with this value, |
+ kernel do_signal will not check r0. see tilegx kernel/signal.c |
+ for details. */ |
+#define INT_SWINT_1_SIGRETURN (~0) |
+ |
+/* Implement the "write_pc" gdbarch method. */ |
+ |
+static void |
+tilegx_write_pc (struct regcache *regcache, CORE_ADDR pc) |
+{ |
+ regcache_cooked_write_unsigned (regcache, TILEGX_PC_REGNUM, pc); |
+ |
+ /* We must be careful with modifying the program counter. If we |
+ just interrupted a system call, the kernel might try to restart |
+ it when we resume the inferior. On restarting the system call, |
+ the kernel will try backing up the program counter even though it |
+ no longer points at the system call. This typically results in a |
+ SIGSEGV or SIGILL. We can prevent this by writing INT_SWINT_1_SIGRETURN |
+ in the "faultnum" pseudo-register. |
+ |
+ Note that "faultnum" is saved when setting up a dummy call frame. |
+ This means that it is properly restored when that frame is |
+ popped, and that the interrupted system call will be restarted |
+ when we resume the inferior on return from a function call from |
+ within GDB. In all other cases the system call will not be |
+ restarted. */ |
+ regcache_cooked_write_unsigned (regcache, TILEGX_FAULTNUM_REGNUM, |
+ INT_SWINT_1_SIGRETURN); |
+} |
+ |
/* This is the implementation of gdbarch method breakpoint_from_pc. */ |
static const unsigned char * |
@@ -817,11 +884,12 @@ tilegx_frame_cache (struct frame_info *this_frame, void **this_cache) |
cache->base = get_frame_register_unsigned (this_frame, TILEGX_SP_REGNUM); |
trad_frame_set_value (cache->saved_regs, TILEGX_SP_REGNUM, cache->base); |
- cache->saved_regs[TILEGX_PC_REGNUM] = cache->saved_regs[TILEGX_LR_REGNUM]; |
if (cache->start_pc) |
tilegx_analyze_prologue (gdbarch, cache->start_pc, current_pc, |
cache, this_frame); |
+ cache->saved_regs[TILEGX_PC_REGNUM] = cache->saved_regs[TILEGX_LR_REGNUM]; |
+ |
return cache; |
} |
@@ -911,7 +979,8 @@ tilegx_cannot_reference_register (struct gdbarch *gdbarch, int regno) |
{ |
if (regno >= 0 && regno < TILEGX_NUM_EASY_REGS) |
return 0; |
- else if (regno == TILEGX_PC_REGNUM) |
+ else if (regno == TILEGX_PC_REGNUM |
+ || regno == TILEGX_FAULTNUM_REGNUM) |
return 0; |
else |
return 1; |
@@ -956,7 +1025,6 @@ tilegx_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
set_gdbarch_register_name (gdbarch, tilegx_register_name); |
set_gdbarch_register_type (gdbarch, tilegx_register_type); |
- set_gdbarch_char_signed (gdbarch, 0); |
set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT); |
set_gdbarch_int_bit (gdbarch, 4 * TARGET_CHAR_BIT); |
set_gdbarch_long_bit (gdbarch, arch_size); |
@@ -994,6 +1062,8 @@ tilegx_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
/* These values and methods are used when gdb calls a target function. */ |
set_gdbarch_push_dummy_call (gdbarch, tilegx_push_dummy_call); |
+ set_gdbarch_get_longjmp_target (gdbarch, tilegx_get_longjmp_target); |
+ set_gdbarch_write_pc (gdbarch, tilegx_write_pc); |
set_gdbarch_breakpoint_from_pc (gdbarch, tilegx_breakpoint_from_pc); |
set_gdbarch_return_value (gdbarch, tilegx_return_value); |