infinite loop stopping at "pop" insn on x64-windows
authorJoel Brobecker <brobecker@adacore.com>
Mon, 23 Nov 2015 17:53:31 +0000 (09:53 -0800)
committerJoel Brobecker <brobecker@adacore.com>
Mon, 23 Nov 2015 17:53:31 +0000 (09:53 -0800)
We noticed the following hang trying to run a program where one
of the subroutines we built without debugging info (opaque_routine):

    $ gdb my_program
    (gdb) break opaque_routine
    (gdb) run
    [...hangs...]

The problem comes from the fact that, at the breakpoint's address,
we have the following code:

    => 0x0000000000401994 <+4>:     pop    %rbp

At some point after hitting the breakpoint and stopping, GDB calls
amd64_windows_frame_decode_epilogue, which then gets stuck in the
following infinite loop:

| /* We don't care about the instruction deallocating the frame:
|    if it hasn't been executed, the pc is still in the body,
|    if it has been executed, the following epilog decoding will work.  */
|
| /* First decode:
|    -  pop reg                 [41 58-5f] or [58-5f].  */
|
| while (1)
|   {
|     /* Read opcode. */
|     if (target_read_memory (pc, &op, 1) != 0)
|       return -1;
|
|     if (op >= 0x40 && op <= 0x4f)
|       {
|         /* REX prefix.  */
|         rex = op;
|
|         /* Read opcode. */
|         if (target_read_memory (pc + 1, &op, 1) != 0)
|           return -1;
|       }
|     else
|       rex = 0;
|
|     if (op >= 0x58 && op <= 0x5f)
|       {
|         /* pop reg  */
|         gdb_byte reg = (op & 0x0f) | ((rex & 1) << 3);
|
|         cache->prev_reg_addr[amd64_windows_w2gdb_regnum[reg]] = cur_sp;
|         cur_sp += 8;
|       }
|     else
|       break;
|
|     /* Allow the user to break this loop.  This shouldn't happen as the
|        number of consecutive pop should be small.  */
|     QUIT;
|   }

Nothing in that loop updates PC, and therefore, because the instruction
we stopped at is a "pop", we keep looping forever doing the same thing
over and over!

This patch fixes the issue by advancing PC to the beginning of
the next instruction if the current one is a "pop reg" instruction.

gdb/ChangeLog:

        * amd64-windows-tdep.c (amd64_windows_frame_decode_epilogue):
        Increment PC in while loop skipping "pop reg" instructions.

gdb/ChangeLog
gdb/amd64-windows-tdep.c

index 5655ccb..ffcac03 100644 (file)
@@ -1,5 +1,10 @@
 2015-11-23  Joel Brobecker  <brobecker@adacore.com>
 
+       * amd64-windows-tdep.c (amd64_windows_frame_decode_epilogue):
+       Increment PC in while loop skipping "pop reg" instructions.
+
+2015-11-23  Joel Brobecker  <brobecker@adacore.com>
+
        * arm-tdep.c (arm_exidx_unwind_sniffer): Do not check for a frame
        stuck on a system call if the given frame is the innermost frame.
 
index 296bdb2..c04b730 100644 (file)
@@ -488,6 +488,7 @@ amd64_windows_frame_decode_epilogue (struct frame_info *this_frame,
 
          cache->prev_reg_addr[amd64_windows_w2gdb_regnum[reg]] = cur_sp;
          cur_sp += 8;
+         pc += rex ? 2 : 1;
        }
       else
        break;