- /* Linux/i386. */
- xcr0 = i386_linux_core_read_xcr0 (gdbarch, target, abfd);
- if ((xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
- return tdesc_i386_avx_linux;
- else
- return tdesc_i386_linux;
+/* Linux kernel shows PC value after the 'int $0x80' instruction even if
+ inferior is still inside the syscall. On next PTRACE_SINGLESTEP it will
+ finish the syscall but PC will not change.
+
+ Some vDSOs contain 'int $0x80; ret' and during stepping out of the syscall
+ i386_displaced_step_fixup would keep PC at the displaced pad location.
+ As PC is pointing to the 'ret' instruction before the step
+ i386_displaced_step_fixup would expect inferior has just executed that 'ret'
+ and PC should not be adjusted. In reality it finished syscall instead and
+ PC should get relocated back to its vDSO address. Hide the 'ret'
+ instruction by 'nop' so that i386_displaced_step_fixup is not confused.
+
+ It is not fully correct as the bytes in struct displaced_step_closure will
+ not match the inferior code. But we would need some new flag in
+ displaced_step_closure otherwise to keep the state that syscall is finishing
+ for the later i386_displaced_step_fixup execution as the syscall execution
+ is already no longer detectable there. The new flag field would mean
+ i386-linux-tdep.c needs to wrap all the displacement methods of i386-tdep.c
+ which does not seem worth it. The same effect is achieved by patching that
+ 'nop' instruction there instead. */
+
+static struct displaced_step_closure *
+i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
+ CORE_ADDR from, CORE_ADDR to,
+ struct regcache *regs)
+{
+ struct displaced_step_closure *closure;
+
+ closure = i386_displaced_step_copy_insn (gdbarch, from, to, regs);
+
+ if (i386_linux_get_syscall_number_from_regcache (regs) != -1)
+ {
+ /* Since we use simple_displaced_step_copy_insn, our closure is a
+ copy of the instruction. */
+ gdb_byte *insn = (gdb_byte *) closure;
+
+ /* Fake nop. */
+ insn[0] = 0x90;
+ }
+
+ return closure;