diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d3caa1abf5..53c773eb4d 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,26 @@ +2016-07-01 Pedro Alves + Tom Tromey + + * jit.c (jit_reader_load_command): Call reinit_frame_cache and + jit_inferior_created_hook. + (jit_reader_unload_command): Call reinit_frame_cache and + jit_inferior_exit_hook. + * jit.c (struct jit_unwind_private) : Delete field. + : New field. + (jit_unwind_reg_set_impl): Set the register's value in the + regcache. Free the passed-in gdb_reg_value. + (jit_dealloc_cache): Adjust to free the regcache. + (jit_frame_sniffer): Allocate a regcache instead of an array of + gdb_reg_value pointers. + (jit_frame_this_id): Adjust. + (jit_frame_prev_register): Read raw registers off of the regcache + instead of from the gdb_reg_value pointer array. Use + gdbarch_pseudo_register_read_value to read pseudo registers. + * regcache.c (regcache_raw_set_cached_value): New function, + factored out from ... + (regcache_raw_write): ... here. + * regcache.h (regcache_raw_set_cached_value): Declare. + 2016-07-01 Pedro Alves Antoine Tremblay diff --git a/gdb/jit.c b/gdb/jit.c index 9fd5ae675b..2b6cf77f08 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -51,6 +51,7 @@ static const char *const jit_descriptor_name = "__jit_debug_descriptor"; static const struct program_space_data *jit_program_space_data = NULL; static void jit_inferior_init (struct gdbarch *gdbarch); +static void jit_inferior_exit_hook (struct inferior *inf); /* An unwinder is registered for every gdbarch. This key is used to remember if the unwinder has been registered for a particular @@ -218,6 +219,8 @@ jit_reader_load_command (char *args, int from_tty) prev_cleanup = make_cleanup (xfree, so_name); loaded_jit_reader = jit_reader_load (so_name); + reinit_frame_cache (); + jit_inferior_created_hook (); do_cleanups (prev_cleanup); } @@ -229,6 +232,8 @@ jit_reader_unload_command (char *args, int from_tty) if (!loaded_jit_reader) error (_("No JIT reader loaded.")); + reinit_frame_cache (); + jit_inferior_exit_hook (current_inferior ()); loaded_jit_reader->functions->destroy (loaded_jit_reader->functions); gdb_dlclose (loaded_jit_reader->handle); @@ -1090,7 +1095,7 @@ struct jit_unwind_private { /* Cached register values. See jit_frame_sniffer to see how this works. */ - struct gdb_reg_value **registers; + struct regcache *regcache; /* The frame being unwound. */ struct frame_info *this_frame; @@ -1115,11 +1120,12 @@ jit_unwind_reg_set_impl (struct gdb_unwind_callbacks *cb, int dwarf_regnum, fprintf_unfiltered (gdb_stdlog, _("Could not recognize DWARF regnum %d"), dwarf_regnum); + value->free (value); return; } - gdb_assert (priv->registers); - priv->registers[gdb_reg] = value; + regcache_raw_set_cached_value (priv->regcache, gdb_reg, value->value); + value->free (value); } static void @@ -1162,14 +1168,10 @@ jit_dealloc_cache (struct frame_info *this_frame, void *cache) struct gdbarch *frame_arch; int i; - gdb_assert (priv_data->registers); + gdb_assert (priv_data->regcache != NULL); frame_arch = get_frame_arch (priv_data->this_frame); - for (i = 0; i < gdbarch_num_regs (frame_arch); i++) - if (priv_data->registers[i] && priv_data->registers[i]->free) - priv_data->registers[i]->free (priv_data->registers[i]); - - xfree (priv_data->registers); + regcache_xfree (priv_data->regcache); xfree (priv_data); } @@ -1188,6 +1190,8 @@ jit_frame_sniffer (const struct frame_unwind *self, struct jit_unwind_private *priv_data; struct gdb_unwind_callbacks callbacks; struct gdb_reader_funcs *funcs; + struct address_space *aspace; + struct gdbarch *gdbarch; callbacks.reg_get = jit_unwind_reg_get_impl; callbacks.reg_set = jit_unwind_reg_set_impl; @@ -1200,11 +1204,12 @@ jit_frame_sniffer (const struct frame_unwind *self, gdb_assert (!*cache); + aspace = get_frame_address_space (this_frame); + gdbarch = get_frame_arch (this_frame); + *cache = XCNEW (struct jit_unwind_private); priv_data = (struct jit_unwind_private *) *cache; - priv_data->registers = - XCNEWVEC (struct gdb_reg_value *, - gdbarch_num_regs (get_frame_arch (this_frame))); + priv_data->regcache = regcache_xmalloc (gdbarch, aspace); priv_data->this_frame = this_frame; callbacks.priv_data = priv_data; @@ -1240,7 +1245,7 @@ jit_frame_this_id (struct frame_info *this_frame, void **cache, struct gdb_reader_funcs *funcs; struct gdb_unwind_callbacks callbacks; - priv.registers = NULL; + priv.regcache = NULL; priv.this_frame = this_frame; /* We don't expect the frame_id function to set any registers, so we @@ -1264,17 +1269,25 @@ static struct value * jit_frame_prev_register (struct frame_info *this_frame, void **cache, int reg) { struct jit_unwind_private *priv = (struct jit_unwind_private *) *cache; - struct gdb_reg_value *value; + struct gdbarch *gdbarch; if (priv == NULL) return frame_unwind_got_optimized (this_frame, reg); - gdb_assert (priv->registers); - value = priv->registers[reg]; - if (value && value->defined) - return frame_unwind_got_bytes (this_frame, reg, value->value); + gdbarch = get_regcache_arch (priv->regcache); + if (reg < gdbarch_num_regs (gdbarch)) + { + gdb_byte *buf = (gdb_byte *) alloca (register_size (gdbarch, reg)); + enum register_status status; + + status = regcache_raw_read (priv->regcache, reg, buf); + if (status == REG_VALID) + return frame_unwind_got_bytes (this_frame, reg, buf); + else + return frame_unwind_got_optimized (this_frame, reg); + } else - return frame_unwind_got_optimized (this_frame, reg); + return gdbarch_pseudo_register_read_value (gdbarch, priv->regcache, reg); } /* Relay everything back to the unwinder registered by the JIT debug diff --git a/gdb/regcache.c b/gdb/regcache.c index f0ba0cf552..a5c90a6314 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -890,6 +890,17 @@ regcache_cooked_write_unsigned (struct regcache *regcache, int regnum, regcache_cooked_write (regcache, regnum, buf); } +/* See regcache.h. */ + +void +regcache_raw_set_cached_value (struct regcache *regcache, int regnum, + const gdb_byte *buf) +{ + memcpy (register_buffer (regcache, regnum), buf, + regcache->descr->sizeof_register[regnum]); + regcache->register_status[regnum] = REG_VALID; +} + void regcache_raw_write (struct regcache *regcache, int regnum, const gdb_byte *buf) @@ -917,9 +928,7 @@ regcache_raw_write (struct regcache *regcache, int regnum, inferior_ptid = regcache->ptid; target_prepare_to_store (regcache); - memcpy (register_buffer (regcache, regnum), buf, - regcache->descr->sizeof_register[regnum]); - regcache->register_status[regnum] = REG_VALID; + regcache_raw_set_cached_value (regcache, regnum, buf); /* Register a cleanup function for invalidating the register after it is written, in case of a failure. */ diff --git a/gdb/regcache.h b/gdb/regcache.h index 1a4ff4248a..1bb0ce0ec8 100644 --- a/gdb/regcache.h +++ b/gdb/regcache.h @@ -66,6 +66,14 @@ extern void regcache_raw_write_signed (struct regcache *regcache, extern void regcache_raw_write_unsigned (struct regcache *regcache, int regnum, ULONGEST val); +/* Set a raw register's value in the regcache's buffer. Unlike + regcache_raw_write, this is not write-through. The intention is + allowing to change the buffer contents of a read-only regcache + allocated with regcache_xmalloc. */ + +extern void regcache_raw_set_cached_value + (struct regcache *regcache, int regnum, const gdb_byte *buf); + /* Partial transfer of raw registers. These perform read, modify, write style operations. The read variant returns the status of the register. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index fd943790c5..bcbffaa044 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,21 @@ +2016-07-01 Pedro Alves + + * gdb.base/jit-reader.exp (info_registers_current_frame): New + procedure. + (jit_reader_test): Test the jit reader's unwinder. + * gdb.base/jithost.c (jit_function_00_code): New global. + (main): Use memcpy to fill in the mmapped code, instead of poking + bytes manually here. + * gdb.base/jitreader.c (enum register_mapping) : New + value. + (read_debug_info): Save the function's range. + (read_sp): New function. + (unwind_frame): Use it. Also unwind RBP. + (get_frame_id): Use read_sp. + (gdb_init_reader): Use calloc instead of malloc. + * lib/gdb.exp (get_hexadecimal_valueof): Add optional 'test' + parameter. Use gdb_test_multiple. + 2016-07-01 Pedro Alves Antoine Tremblay diff --git a/gdb/testsuite/gdb.base/jit-reader.exp b/gdb/testsuite/gdb.base/jit-reader.exp index b5e297aaf7..642c257eff 100644 --- a/gdb/testsuite/gdb.base/jit-reader.exp +++ b/gdb/testsuite/gdb.base/jit-reader.exp @@ -57,10 +57,50 @@ if { [gdb_compile_shlib "${srcdir}/${subdir}/${jit_reader_src}" "${jit_reader_bi return -1 } +# Test "info registers" in the current frame, expecting RSP's value to +# be SP. + +proc info_registers_current_frame {sp} { + global hex decimal + + set any "\[^\r\n\]*" + + gdb_test "info registers" \ + [multi_line \ + "rax $hex $decimal" \ + "rbx $hex $decimal" \ + "rcx $hex $decimal" \ + "rdx $hex $decimal" \ + "rsi $hex $decimal" \ + "rdi $hex $decimal" \ + "rbp $hex $hex" \ + "rsp $sp $sp" \ + "r8 $hex $decimal" \ + "r9 $hex $decimal" \ + "r10 $hex $decimal" \ + "r11 $hex $decimal" \ + "r12 $hex $decimal" \ + "r13 $hex $decimal" \ + "r14 $hex $decimal" \ + "r15 $hex $decimal" \ + "rip $hex $hex$any" \ + "eflags $hex \\\[$any\\\]" \ + "cs $hex $decimal" \ + "ss $hex $decimal" \ + "ds $hex $decimal" \ + "es $hex $decimal" \ + "fs $hex $decimal" \ + "gs $hex $decimal" \ + ] +} + proc jit_reader_test {} { global jit_host_bin global jit_reader_bin global verbose + global hex decimal + + set any "\[^\r\n\]*" clean_restart $jit_host_bin gdb_load_shlib $jit_reader_bin @@ -73,7 +113,139 @@ proc jit_reader_test {} { gdb_run_cmd gdb_test "" "Program received signal SIGTRAP, .*" "expect SIGTRAP" - gdb_test "bt" "jit_function_00.*" + # Test the JIT reader unwinder. + with_test_prefix "with jit-reader" { + + with_test_prefix "before mangling" { + gdb_test "bt" \ + [multi_line \ + "#0 ${any} in jit_function_00 ${any}" \ + "#1 ${any} in main ${any}" \ + ] \ + "bt works" + + set sp_before_mangling \ + [get_hexadecimal_valueof "\$sp" 0 "get sp"] + + gdb_test "up" "#1 $any in main $any\r\n$any function $any" \ + "move up to caller" + + set caller_sp \ + [get_hexadecimal_valueof "\$sp" 0 "get caller sp"] + } + + # Step over the instruction that mangles the stack pointer. + # While that confuses GDB's built-in unwinder, the JIT + # reader's unwinder understands the mangling and should thus + # be able to unwind at that location. + with_test_prefix "after mangling" { + gdb_test "si" "in jit_function_00 .*" "step over stack mangling" + + set sp_after_mangling \ + [get_hexadecimal_valueof "\$sp" 0 "get sp"] + + gdb_assert {$sp_before_mangling != $sp_after_mangling} \ + "sp is mangled" + + # Check that the jit unwinder manages to backtrace through + # the mangled stack pointer. + gdb_test "bt" \ + [multi_line \ + "#0 ${any} in jit_function_00 ${any}" \ + "#1 ${any} in main ${any}" \ + ] \ + "bt works" + + with_test_prefix "current frame" { + info_registers_current_frame $sp_after_mangling + + gdb_test "info frame" \ + "Stack level 0, frame at $sp_before_mangling.*in jit_function_00.*" + } + + with_test_prefix "caller frame" { + gdb_test "up" "#1 $any in main $any\r\n$any function $any" \ + "up to caller" + + # Since the JIT unwinder only provides RIP/RSP/RBP, + # all other registers should show as "". + gdb_test "info registers" \ + [multi_line \ + "rax " \ + "rbx " \ + "rcx " \ + "rdx " \ + "rsi " \ + "rdi " \ + "rbp $hex $hex" \ + "rsp $caller_sp $caller_sp" \ + "r8 " \ + "r9 " \ + "r10 " \ + "r11 " \ + "r12 " \ + "r13 " \ + "r14 " \ + "r15 " \ + "rip $hex $hex $any" \ + "eflags " \ + "cs " \ + "ss " \ + "ds " \ + "es " \ + "fs " \ + "gs " \ + ] + + # Make sure that "info frame" doesn't crash. + gdb_test "info frame" "Stack level 1, .*in main.*" + + # ... and that neither does printing a pseudo + # register. + gdb_test "print /x \$ebp" " = $hex" "print pseudo register" + + # There's no way for the JIT reader API to support + # modifyiable values. + gdb_test "print \$rbp = -1" \ + "Attempt to assign to an unmodifiable value\." \ + "cannot assign to register" + } + } + } + + # Now unload the jit reader, and ensure that backtracing really + # doesn't work without it. + with_test_prefix "without jit-reader" { + gdb_test_no_output "jit-reader-unload ${jit_reader_bin}" \ + "jit-reader-unload" + + # Check that we're no longer using the JIT unwinder, and that + # the built-in unwinder cannot backtrace through the mangled + # stack pointer. + gdb_test "bt" \ + [multi_line \ + "Backtrace stopped: Cannot access memory at address $sp_after_mangling" \ + ] \ + "bt shows error" + + gdb_test "info frame" "Cannot access memory at address.*" \ + "info frame shows error" + info_registers_current_frame $sp_after_mangling + gdb_test "up" "Initial frame selected; you cannot go up\\." \ + "cannot go up" + } + + with_test_prefix "with jit-reader again" { + gdb_test_no_output "jit-reader-load ${jit_reader_bin}" "jit-reader-load" + + # Check that the jit unwinder manages to backtrace through + # the mangled stack pointer. + gdb_test "bt" \ + [multi_line \ + "#0 ${any} in jit_function_00 ${any}" \ + "#1 ${any} in main ${any}" \ + ] + } } jit_reader_test diff --git a/gdb/testsuite/gdb.base/jithost.c b/gdb/testsuite/gdb.base/jithost.c index 09ab377b92..afefc98be9 100644 --- a/gdb/testsuite/gdb.base/jithost.c +++ b/gdb/testsuite/gdb.base/jithost.c @@ -33,18 +33,32 @@ struct jit_code_entry only_entry; typedef void (jit_function_t) (); -int main (int argc, char **argv) +/* The code of the jit_function_00 function that is copied into an + mmapped buffer in the inferior at run time. + + The second instruction mangles the stack pointer, meaning that when + stopped at the third instruction, GDB needs assistance from the JIT + unwinder in order to be able to unwind successfully. */ +const unsigned char jit_function_00_code[] = { + 0xcc, /* int3 */ + 0x48, 0x83, 0xf4, 0xff, /* xor $0xffffffffffffffff, %rsp */ + 0x48, 0x83, 0xf4, 0xff, /* xor $0xffffffffffffffff, %rsp */ + 0xc3 /* ret */ +}; + +int +main (int argc, char **argv) { + struct jithost_abi *symfile; char *code = mmap (NULL, getpagesize (), PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); jit_function_t *function = (jit_function_t *) code; - code[0] = 0xcc; /* SIGTRAP */ - code[1] = 0xc3; /* RET */ + memcpy (code, jit_function_00_code, sizeof (jit_function_00_code)); - struct jithost_abi *symfile = malloc (sizeof (struct jithost_abi)); + symfile = malloc (sizeof (struct jithost_abi)); symfile->begin = code; - symfile->end = code + 2; + symfile->end = code + sizeof (jit_function_00_code); only_entry.symfile_addr = symfile; only_entry.symfile_size = sizeof (struct jithost_abi); diff --git a/gdb/testsuite/gdb.base/jitreader.c b/gdb/testsuite/gdb.base/jitreader.c index 850fb46c8d..c18e40d2b5 100644 --- a/gdb/testsuite/gdb.base/jitreader.c +++ b/gdb/testsuite/gdb.base/jitreader.c @@ -28,6 +28,7 @@ GDB_DECLARE_GPL_COMPATIBLE_READER; enum register_mapping { AMD64_RA = 16, + AMD64_RBP = 6, AMD64_RSP = 7, }; @@ -47,6 +48,11 @@ read_debug_info (struct gdb_reader_funcs *self, struct gdb_symtab *symtab = cbs->symtab_open (cbs, object, ""); GDB_CORE_ADDR begin = (GDB_CORE_ADDR) symfile->begin; GDB_CORE_ADDR end = (GDB_CORE_ADDR) symfile->end; + struct reader_state *state = (struct reader_state *) self->priv_data; + + /* Record the function's range, for the unwinder. */ + state->code_begin = begin; + state->code_end = end; cbs->block_open (cbs, symtab, NULL, begin, end, "jit_function_00"); @@ -91,11 +97,36 @@ read_register (struct gdb_unwind_callbacks *callbacks, int dw_reg, return 1; } +/* Read the stack pointer into *VALUE. IP is the address the inferior + is currently stopped at. Takes care of demangling the stack + pointer if necessary. */ + +static int +read_sp (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs, + uintptr_t ip, uintptr_t *value) +{ + struct reader_state *state = (struct reader_state *) self->priv_data; + uintptr_t sp; + + if (!read_register (cbs, AMD64_RSP, &sp)) + return GDB_FAIL; + + /* If stopped at the instruction after the "xor $-1, %rsp", demangle + the stack pointer back. */ + if (ip == state->code_begin + 5) + sp ^= (uintptr_t) -1; + + *value = sp; + return GDB_SUCCESS; +} + static enum gdb_status unwind_frame (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) { const int word_size = sizeof (uintptr_t); - uintptr_t this_sp, this_ip, prev_ip, prev_sp; + uintptr_t prev_sp, this_sp; + uintptr_t prev_ip, this_ip; + uintptr_t prev_bp, this_bp; struct reader_state *state = (struct reader_state *) self->priv_data; if (!read_register (cbs, AMD64_RA, &this_ip)) @@ -104,15 +135,25 @@ unwind_frame (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) if (this_ip >= state->code_end || this_ip < state->code_begin) return GDB_FAIL; - if (!read_register (cbs, AMD64_RSP, &this_sp)) + /* Unwind RBP in order to make the unwinder that tries to unwind + from the just-unwound frame happy. */ + if (!read_register (cbs, AMD64_RBP, &this_bp)) + return GDB_FAIL; + /* RBP is unmodified. */ + prev_bp = this_bp; + + /* Fetch the demangled stack pointer. */ + if (!read_sp (self, cbs, this_ip, &this_sp)) return GDB_FAIL; + /* The return address is saved on the stack. */ if (cbs->target_read (this_sp, &prev_ip, word_size) == GDB_FAIL) return GDB_FAIL; - prev_sp = this_sp + word_size; + write_register (cbs, AMD64_RA, prev_ip); write_register (cbs, AMD64_RSP, prev_sp); + write_register (cbs, AMD64_RBP, prev_bp); return GDB_SUCCESS; } @@ -121,9 +162,11 @@ get_frame_id (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *cbs) { struct reader_state *state = (struct reader_state *) self->priv_data; struct gdb_frame_id frame_id; - + uintptr_t ip; uintptr_t sp; - read_register (cbs, AMD64_RSP, &sp); + + read_register (cbs, AMD64_RA, &ip); + read_sp (self, cbs, ip, &sp); frame_id.code_address = (GDB_CORE_ADDR) state->code_begin; frame_id.stack_address = (GDB_CORE_ADDR) sp; @@ -141,7 +184,7 @@ destroy_reader (struct gdb_reader_funcs *self) struct gdb_reader_funcs * gdb_init_reader (void) { - struct reader_state *state = malloc (sizeof (struct reader_state)); + struct reader_state *state = calloc (1, sizeof (struct reader_state)); struct gdb_reader_funcs *reader_funcs = malloc (sizeof (struct gdb_reader_funcs)); diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index b52fdd9c5d..b7b8fad75c 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -5435,19 +5435,24 @@ proc get_integer_valueof { exp default } { return ${val} } -proc get_hexadecimal_valueof { exp default } { +# Retrieve the value of EXP in the inferior, as an hexadecimal value +# (using "print /x"). DEFAULT is used as fallback if print fails. +# TEST is the test message to use. If can be ommitted, in which case +# a test message is built from EXP. + +proc get_hexadecimal_valueof { exp default {test ""} } { global gdb_prompt - send_gdb "print /x ${exp}\n" - set test "get hexadecimal valueof \"${exp}\"" - gdb_expect { + + if {$test == ""} { + set test "get hexadecimal valueof \"${exp}\"" + } + + set val ${default} + gdb_test_multiple "print /x ${exp}" $test { -re "\\$\[0-9\]* = (0x\[0-9a-zA-Z\]+).*$gdb_prompt $" { set val $expect_out(1,string) pass "$test" } - timeout { - set val ${default} - fail "$test (timeout)" - } } return ${val} }