Skip to content

Commit

Permalink
Fix x86-64 GNU/Linux crashes
Browse files Browse the repository at this point in the history
Ref: https://sourceware.org/ml/gdb-patches/2017-07/msg00162.html

Debugging x86-64 GNU/Linux programs currently crashes GDB in
tdesc_use_registers during gdbarch initialization:

  Program received signal SIGSEGV, Segmentation fault.
  0x0000000001093eaf in htab_remove_elt_with_hash (htab=0x2ef9fa0, element=0x26af960, hash=557151073) at src/libiberty/hashtab.c:728
  728       if (*slot == HTAB_EMPTY_ENTRY)
  (top-gdb) p slot
  $1 = (void **) 0x0
  (top-gdb) bt
  #0  0x0000000001093eaf in htab_remove_elt_with_hash (htab=0x2ef9fa0, element=0x26af960, hash=557151073) at src/libiberty/hashtab.c:728
  #1  0x0000000001093e79 in htab_remove_elt (htab=0x2ef9fa0, element=0x26af960) at src/libiberty/hashtab.c:714
  #2  0x00000000009121b0 in tdesc_use_registers (gdbarch=0x3001240, target_desc=0x2659cb0, early_data=0x2881cb0)
      at src/gdb/target-descriptions.c:1328
  #3  0x000000000047c93e in i386_gdbarch_init (info=..., arches=0x0) at src/gdb/i386-tdep.c:8634
  #4  0x0000000000818d5f in gdbarch_find_by_info (info=...) at src/gdb/gdbarch.c:5394
  #5  0x00000000007198a8 in set_gdbarch_from_file (abfd=0x2f48250) at src/gdb/arch-utils.c:618
  #6  0x00000000007f21cb in exec_file_attach (filename=0x7fffffffddb0 "/home/pedro/gdb/tests/threads", from_tty=1) at src/gdb/exec.c:380
  #7  0x0000000000865c18 in catch_command_errors_const (command=0x7f1d83 <exec_file_attach(char const*, int)>, arg=0x7fffffffddb0 "/home/pedro/gdb/tests/threads",
      from_tty=1) at src/gdb/main.c:403
  #8  0x00000000008669cf in captured_main_1 (context=0x7fffffffd860) at src/gdb/main.c:1035
  #9  0x0000000000866de2 in captured_main (data=0x7fffffffd860) at src/gdb/main.c:1142
  #10 0x0000000000866e24 in gdb_main (args=0x7fffffffd860) at src/gdb/main.c:1160
  #11 0x000000000041312d in main (argc=3, argv=0x7fffffffd968) at src/gdb/gdb.c:32

The direct cause of the crash is that we tried to remove an element
from the hash which supposedly exists, but does not.  (htab_remove_elt
shouldn't really crash in this case, but that's secondary.)

The real problem is that early_data passed to tdesc_use_registers
includes regs from a target description that is not the target_desc,
which violates its assumptions.  The registers in question are the
fs_base/gs_base registers, added by amd64_init_abi:

      tdesc_numbered_register (feature, tdesc_data_segments,
		       AMD64_FSBASE_REGNUM, "fs_base");
      tdesc_numbered_register (feature, tdesc_data_segments,
		       AMD64_GSBASE_REGNUM, "gs_base");

and that happens because amd64_linux_init_abi uses amd64_init_abi as
helper, but they don't coordinate on which fallback tdesc to use.

amd64_init_abi does:

  if (! tdesc_has_registers (tdesc))
    tdesc = tdesc_amd64;

and then adds the fs_base/gs_base registers of the "tdesc_amd64" tdesc
to the tdesc_arch_data.

After amd64_init_abi returns, amd64_linux_init_abi does:

  if (! tdesc_has_registers (tdesc))
    tdesc = tdesc_amd64_linux;
  tdep->tdesc = tdesc;

and we end up tdesc_amd64_linux installed in tdep->tdesc.

The fix is to make sure that amd64_linux_init_abi and amd64_init_abi
agree on default tdesc, by adding a "default tdesc" parameter to
amd64_init_abi, instead of having amd64_init_abi hardcode a default.
With this, amd64_init_abi creates the fs_base/gs_base registers using
the tdesc_amd64_linux tdesc.

Tested on x86-64 GNU/Linux, -m64.  I don't have an x32 setup handy.

Thanks to John Baldwin, Yao Qi and Simon Marchi for the investigation.

gdb/ChangeLog:
2017-07-13  Pedro Alves  <[email protected]>

	* amd64-darwin-tdep.c (x86_darwin_init_abi_64): Pass tdesc_amd64
	as default tdesc.
	* amd64-dicos-tdep.c (amd64_dicos_init_abi):
	* amd64-fbsd-tdep.c (amd64fbsd_init_abi):
	* amd64-linux-tdep.c (amd64_linux_init_abi): Pass
	tdesc_amd64_linux as default tdesc.  Get final tdesc from the
	tdep.
	(amd64_x32_linux_init_abi): Pass tdesc_x32_linux as default tdesc.
	Get final tdesc from the tdep.
	* amd64-nbsd-tdep.c (amd64nbsd_init_abi): Pass tdesc_amd64 as
	default tdesc.
	* amd64-obsd-tdep.c (amd64obsd_init_abi): Likewise.
	* amd64-sol2-tdep.c (amd64_sol2_init_abi): Likewise.
	* amd64-tdep.c (amd64_init_abi): Add 'default_tdesc' parameter.
	Use it as default tdesc.
	(amd64_x32_init_abi): Add 'default_tdesc' parameter, and pass it
	down to amd_init_abi.  No longer handle fallback tdesc here.
	* amd64-tdep.h (tdesc_x32): Declare.
	(amd64_init_abi, amd64_x32_init_abi): Add 'default_tdesc'
	parameter.
	* amd64-windows-tdep.c (amd64_windows_init_abi): Pass tdesc_amd64
	as default tdesc.
  • Loading branch information
palves committed Jul 13, 2017
1 parent 5d2cbaa commit c55a47e
Show file tree
Hide file tree
Showing 11 changed files with 54 additions and 29 deletions.
25 changes: 25 additions & 0 deletions gdb/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
2017-07-13 Pedro Alves <[email protected]>

* amd64-darwin-tdep.c (x86_darwin_init_abi_64): Pass tdesc_amd64
as default tdesc.
* amd64-dicos-tdep.c (amd64_dicos_init_abi):
* amd64-fbsd-tdep.c (amd64fbsd_init_abi):
* amd64-linux-tdep.c (amd64_linux_init_abi): Pass
tdesc_amd64_linux as default tdesc. Get final tdesc from the
tdep.
(amd64_x32_linux_init_abi): Pass tdesc_x32_linux as default tdesc.
Get final tdesc from the tdep.
* amd64-nbsd-tdep.c (amd64nbsd_init_abi): Pass tdesc_amd64 as
default tdesc.
* amd64-obsd-tdep.c (amd64obsd_init_abi): Likewise.
* amd64-sol2-tdep.c (amd64_sol2_init_abi): Likewise.
* amd64-tdep.c (amd64_init_abi): Add 'default_tdesc' parameter.
Use it as default tdesc.
(amd64_x32_init_abi): Add 'default_tdesc' parameter, and pass it
down to amd_init_abi. No longer handle fallback tdesc here.
* amd64-tdep.h (tdesc_x32): Declare.
(amd64_init_abi, amd64_x32_init_abi): Add 'default_tdesc'
parameter.
* amd64-windows-tdep.c (amd64_windows_init_abi): Pass tdesc_amd64
as default tdesc.

2017-07-13 Andreas Arnez <[email protected]>

* s390-linux-tdep.c (s390_process_record): Add support for
Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-darwin-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ x86_darwin_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);

tdep->struct_return = reg_struct_return;

Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-dicos-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
static void
amd64_dicos_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);

dicos_init_abi (gdbarch);
}
Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-fbsd-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->gregset_num_regs = ARRAY_SIZE (amd64fbsd_r_reg_offset);
tdep->sizeof_gregset = 22 * 8;

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);

tdep->sigtramp_p = amd64fbsd_sigtramp_p;
tdep->sigtramp_start = amd64fbsd_sigtramp_start_addr;
Expand Down
16 changes: 5 additions & 11 deletions gdb/amd64-linux-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -1863,7 +1863,6 @@ static void
amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
const struct target_desc *tdesc = info.target_desc;
struct tdesc_arch_data *tdesc_data
= (struct tdesc_arch_data *) info.tdep_info;
const struct tdesc_feature *feature;
Expand All @@ -1875,15 +1874,13 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset);
tdep->sizeof_gregset = 27 * 8;

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64_linux);

const target_desc *tdesc = tdep->tdesc;

/* Reserve a number for orig_rax. */
set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);

if (! tdesc_has_registers (tdesc))
tdesc = tdesc_amd64_linux;
tdep->tdesc = tdesc;

feature = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.linux");
if (feature == NULL)
return;
Expand Down Expand Up @@ -2080,7 +2077,6 @@ static void
amd64_x32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
const struct target_desc *tdesc = info.target_desc;
struct tdesc_arch_data *tdesc_data
= (struct tdesc_arch_data *) info.tdep_info;
const struct tdesc_feature *feature;
Expand All @@ -2092,14 +2088,12 @@ amd64_x32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset);
tdep->sizeof_gregset = 27 * 8;

amd64_x32_init_abi (info, gdbarch);
amd64_x32_init_abi (info, gdbarch, tdesc_x32_linux);

/* Reserve a number for orig_rax. */
set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);

if (! tdesc_has_registers (tdesc))
tdesc = tdesc_x32_linux;
tdep->tdesc = tdesc;
const target_desc *tdesc = tdep->tdesc;

feature = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.linux");
if (feature == NULL)
Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-nbsd-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ amd64nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->gregset_num_regs = ARRAY_SIZE (amd64nbsd_r_reg_offset);
tdep->sizeof_gregset = 26 * 8;

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);

tdep->jb_pc_offset = 7 * 8;

Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-obsd-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ amd64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);
obsd_init_abi (info, gdbarch);

/* Initialize general-purpose register set details. */
Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-sol2-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ amd64_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->gregset_num_regs = ARRAY_SIZE (amd64_sol2_gregset_reg_offset);
tdep->sizeof_gregset = 28 * 8;

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);

tdep->sigtramp_p = amd64_sol2_sigtramp_p;
tdep->sigcontext_addr = amd64_sol2_mcontext_addr;
Expand Down
15 changes: 6 additions & 9 deletions gdb/amd64-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -3005,7 +3005,8 @@ static const int amd64_record_regmap[] =
};

void
amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
target_desc *default_tdesc)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
const struct target_desc *tdesc = info.target_desc;
Expand All @@ -3022,7 +3023,7 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->fpregset = &amd64_fpregset;

if (! tdesc_has_registers (tdesc))
tdesc = tdesc_amd64;
tdesc = default_tdesc;
tdep->tdesc = tdesc;

tdep->num_core_regs = AMD64_NUM_GREGS + I387_NUM_REGS;
Expand Down Expand Up @@ -3196,16 +3197,12 @@ amd64_x32_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
}

void
amd64_x32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
amd64_x32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
target_desc *default_tdesc)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
const struct target_desc *tdesc = info.target_desc;

amd64_init_abi (info, gdbarch);

if (! tdesc_has_registers (tdesc))
tdesc = tdesc_x32;
tdep->tdesc = tdesc;
amd64_init_abi (info, gdbarch, default_tdesc);

tdep->num_dword_regs = 17;
set_tdesc_pseudo_register_type (gdbarch, amd64_x32_pseudo_register_type);
Expand Down
13 changes: 11 additions & 2 deletions gdb/amd64-tdep.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ enum amd64_regnum
#define AMD64_NUM_REGS (AMD64_GSBASE_REGNUM + 1)

extern struct target_desc *tdesc_amd64;
extern struct target_desc *tdesc_x32;

extern struct displaced_step_closure *amd64_displaced_step_copy_insn
(struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to,
Expand All @@ -97,9 +98,17 @@ extern void amd64_displaced_step_fixup (struct gdbarch *gdbarch,
CORE_ADDR from, CORE_ADDR to,
struct regcache *regs);

extern void amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch);
/* Initialize the ABI for amd64. Uses DEFAULT_TDESC as fallback
tdesc, if INFO does not specify one. */
extern void amd64_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch,
target_desc *default_tdesc);

/* Initialize the ABI for x32. Uses DEFAULT_TDESC as fallback tdesc,
if INFO does not specify one. */
extern void amd64_x32_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch);
struct gdbarch *gdbarch,
target_desc *default_tdesc);
extern const struct target_desc *amd64_target_description (uint64_t xcr0);

/* Fill register REGNUM in REGCACHE with the appropriate
Expand Down
2 changes: 1 addition & 1 deletion gdb/amd64-windows-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,7 @@ amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
*/
frame_unwind_append_unwinder (gdbarch, &amd64_windows_frame_unwind);

amd64_init_abi (info, gdbarch);
amd64_init_abi (info, gdbarch, tdesc_amd64);

windows_init_abi (info, gdbarch);

Expand Down

0 comments on commit c55a47e

Please sign in to comment.