Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Objective C support #6

Open
paulb777 opened this issue Jun 29, 2013 · 2 comments
Open

Objective C support #6

paulb777 opened this issue Jun 29, 2013 · 2 comments

Comments

@paulb777
Copy link

We have a fork that adds in Objective-C support (although a bit hacky in places) at https://github.com/apportable/gdb. Let me know if you'd like a pull request.

@darchons
Copy link
Owner

darchons commented Jul 2, 2013

Thanks! Are any of the patches good candidates for upstreaming to GDB? If not, I'll be glad to look at them.

@paulb777
Copy link
Author

paulb777 commented Jul 2, 2013

Good question. I'm not sure.

We've been basing from darchons lately, since it seems a better Android starting point than the pure upstream gdb version.

Most of the changes are for Objective C support, along with a few miscellaneous Android and/or ARM bug fixes.

The Objective-C is work in progress, and may have some dependencies on our Clang branch.

A summary of the non-Objective C items:

  • Don't stop thread apply all if one thread has a memory error
  • Fix next command from stepping into file static functions
  • Add timestamp to build label
  • Skip wchar processing that was causing crashes printing uninitialized data
  • Fix crash on continue occurring on some Android devices

darchons pushed a commit that referenced this issue May 9, 2014
…story).

Before the changes starting at
http://sourceware.org/ml/gdb-patches/2012-08/msg00020.html, the 'set
listsize' command only accepted "0" as special value, meaning
"unlimited".  The testsuite actually tried "set listsize -1" and
expected that to mean unlimited too.

If you tried testing list.exp at the time of that patch above,
you'd get:

  (gdb) PASS: gdb.base/list.exp: list line 10 with listsize 100
  set listsize 0
  (gdb) PASS: gdb.base/list.exp: setting listsize to 0 #6
  show listsize
  Number of source lines gdb will list by default is unlimited.
  (gdb) PASS: gdb.base/list.exp: show listsize unlimited #6
  list 1
  1       #include "list0.h"
  2
  ...
  42          /* Not used for anything */
  43      }
  (gdb) PASS: gdb.base/list.exp: listsize of 0 suppresses output
  set listsize -1
  integer 4294967295 out of range
  (gdb) PASS: gdb.base/list.exp: setting listsize to -1 #7
  show listsize
  Number of source lines gdb will list by default is unlimited.
  (gdb) PASS: gdb.base/list.exp: show listsize unlimited #7
  list 1
  1       #include "list0.h"

Notice that "set listsize -1" actually failed with "integer 4294967295
out of range", but we issued a PASS anyway.

(and notice how the "listsize of 0 suppresses output" test passes bogusly too.)

This patch fixes that testsuite problem in the obvious way.

gdb/testsuite/
2013-03-28  Pedro Alves  <palves@redhat.com>

	* gdb.base/list.exp (set_listsize): Use gdb_test_no_output for
	"set listsize".
darchons pushed a commit that referenced this issue May 9, 2014
PR tui/14880 shows a reproducer that triggers this assertion:

  int
  value_available_contents_eq (const struct value *val1, int offset1,
  			     const struct value *val2, int offset2,
  			     int length)
  {
    int idx1 = 0, idx2 = 0;

    /* This routine is used by printing routines, where we should
       already have read the value.  Note that we only know whether a
       value chunk is available if we've tried to read it.  */
    gdb_assert (!val1->lazy && !val2->lazy);

(top-gdb) bt
#0  internal_error (file=0x88a26c "../../src/gdb/value.c", line=549, string=0x88a220 "%s: Assertion `%s' failed.") at ../../src/gdb/utils.c:844
#1  0x000000000057b9cd in value_available_contents_eq (val1=0x10fa900, offset1=0, val2=0x10f9e10, offset2=0, length=8) at ../../src/gdb/value.c:549
#2  0x00000000004fd756 in tui_get_register (frame=0xd5c430, data=0x109a548, regnum=0, changedp=0x109a560) at ../../src/gdb/tui/tui-regs.c:736
#3  0x00000000004fd111 in tui_check_register_values (frame=0xd5c430) at ../../src/gdb/tui/tui-regs.c:521
#4  0x0000000000501884 in tui_check_data_values (frame=0xd5c430) at ../../src/gdb/tui/tui-windata.c:234
#5  0x00000000004f976f in tui_selected_frame_level_changed_hook (level=1) at ../../src/gdb/tui/tui-hooks.c:222
#6  0x00000000006f0681 in select_frame (fi=0xd5c430) at ../../src/gdb/frame.c:1490
#7  0x00000000005dd94b in up_silently_base (count_exp=0x0) at ../../src/gdb/stack.c:2268
#8  0x00000000005dd985 in up_command (count_exp=0x0, from_tty=1) at ../../src/gdb/stack.c:2280
#9  0x00000000004dc5cf in do_cfunc (c=0xd3f720, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:113
#10 0x00000000004df664 in cmd_func (cmd=0xd3f720, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:1888
#11 0x00000000006e43e1 in execute_command (p=0xc7e6c2 "", from_tty=1) at ../../src/gdb/top.c:489

The fix is to fetch the value before comparing the contents.  The
comment additions to value.h explain why it can't be
value_available_contents_eq itself that fetches the contents.

Tested on x86_64 Fedora 17.

gdb/
2013-06-28  Pedro Alves  <palves@redhat.com>

	PR tui/14880
	* tui/tui-regs.c (tui_get_register): Fetch register value contents
	before checking whether they're available.
	* value.c (value_available_contents_eq): Change comment.
	* value.h (value_available_contents_eq): Expand comment.
darchons pushed a commit that referenced this issue May 12, 2014
…story).

Before the changes starting at
<http://sourceware.org/ml/gdb-patches/2012-08/msg00020.html>, the 'set
listsize' command only accepted "0" as special value, meaning
"unlimited".  The testsuite actually tried "set listsize -1" and
expected that to mean unlimited too.

If you tried testing list.exp at the time of that patch above,
you'd get:

  (gdb) PASS: gdb.base/list.exp: list line 10 with listsize 100
  set listsize 0
  (gdb) PASS: gdb.base/list.exp: setting listsize to 0 #6
  show listsize
  Number of source lines gdb will list by default is unlimited.
  (gdb) PASS: gdb.base/list.exp: show listsize unlimited #6
  list 1
  1       #include "list0.h"
  2
  ...
  42          /* Not used for anything */
  43      }
  (gdb) PASS: gdb.base/list.exp: listsize of 0 suppresses output
  set listsize -1
  integer 4294967295 out of range
  (gdb) PASS: gdb.base/list.exp: setting listsize to -1 #7
  show listsize
  Number of source lines gdb will list by default is unlimited.
  (gdb) PASS: gdb.base/list.exp: show listsize unlimited #7
  list 1
  1       #include "list0.h"

Notice that "set listsize -1" actually failed with "integer 4294967295
out of range", but we issued a PASS anyway.

(and notice how the "listsize of 0 suppresses output" test passes bogusly too.)

This patch fixes that testsuite problem in the obvious way.

gdb/testsuite/
2013-03-28  Pedro Alves  <palves@redhat.com>

	* gdb.base/list.exp (set_listsize): Use gdb_test_no_output for
	"set listsize".
darchons pushed a commit that referenced this issue May 12, 2014
This patch does the following:
  - Puts the startfunc and "Sleep func" info on the same line;
  - Renames "Sleep func" into "sleepfunc" to be consistent with
    "startfunc"
  - Avoids the use of a '-' as a separate before the "sleepfunc"
    output, because the '-' looks odd and out of place when the
    "startfunc" field is not printed (ti.ti_startfunc is nul).
  - Use a '=' instead of ':' and avoids the space between
    the name of the value and its value, mostly to help group
    the value with its name.

For the record, this is how the new outout now looks like:

    (gdb) maintenance info sol-threads
    user   thread #1, lwp 1, (active)
    system thread #2, lwp 2, (active)
    system thread #3, lwp 0, (asleep)    sleepfunc=0xff32d9e0
    user   thread #4, lwp 4, (asleep)    startfunc=[...].task_wrapper   sleepfunc=0xff3290f0
    system thread #5, lwp 7, (active)    startfunc=_co_timerset
    user   thread #6, lwp 8, (active)    startfunc=[...].task_wrapper

gdb/ChangeLog:

        * sol-thread.c (info_cb): Rework the output of the "maintenance
        info sol-threads" command a bit.
darchons pushed a commit that referenced this issue May 12, 2014
PR tui/14880 shows a reproducer that triggers this assertion:

  int
  value_available_contents_eq (const struct value *val1, int offset1,
  			     const struct value *val2, int offset2,
  			     int length)
  {
    int idx1 = 0, idx2 = 0;

    /* This routine is used by printing routines, where we should
       already have read the value.  Note that we only know whether a
       value chunk is available if we've tried to read it.  */
    gdb_assert (!val1->lazy && !val2->lazy);

(top-gdb) bt
#0  internal_error (file=0x88a26c "../../src/gdb/value.c", line=549, string=0x88a220 "%s: Assertion `%s' failed.") at ../../src/gdb/utils.c:844
#1  0x000000000057b9cd in value_available_contents_eq (val1=0x10fa900, offset1=0, val2=0x10f9e10, offset2=0, length=8) at ../../src/gdb/value.c:549
#2  0x00000000004fd756 in tui_get_register (frame=0xd5c430, data=0x109a548, regnum=0, changedp=0x109a560) at ../../src/gdb/tui/tui-regs.c:736
#3  0x00000000004fd111 in tui_check_register_values (frame=0xd5c430) at ../../src/gdb/tui/tui-regs.c:521
#4  0x0000000000501884 in tui_check_data_values (frame=0xd5c430) at ../../src/gdb/tui/tui-windata.c:234
#5  0x00000000004f976f in tui_selected_frame_level_changed_hook (level=1) at ../../src/gdb/tui/tui-hooks.c:222
#6  0x00000000006f0681 in select_frame (fi=0xd5c430) at ../../src/gdb/frame.c:1490
#7  0x00000000005dd94b in up_silently_base (count_exp=0x0) at ../../src/gdb/stack.c:2268
#8  0x00000000005dd985 in up_command (count_exp=0x0, from_tty=1) at ../../src/gdb/stack.c:2280
#9  0x00000000004dc5cf in do_cfunc (c=0xd3f720, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:113
#10 0x00000000004df664 in cmd_func (cmd=0xd3f720, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:1888
#11 0x00000000006e43e1 in execute_command (p=0xc7e6c2 "", from_tty=1) at ../../src/gdb/top.c:489

The fix is to fetch the value before comparing the contents.  The
comment additions to value.h explain why it can't be
value_available_contents_eq itself that fetches the contents.

Tested on x86_64 Fedora 17.

gdb/
2013-06-28  Pedro Alves  <palves@redhat.com>

	PR tui/14880
	* tui/tui-regs.c (tui_get_register): Fetch register value contents
	before checking whether they're available.
	* value.c (value_available_contents_eq): Change comment.
	* value.h (value_available_contents_eq): Expand comment.
darchons pushed a commit that referenced this issue May 12, 2014
In entry-values.exp, we have a test where the entry value of 'j' is
unavailable, so it is expected that printing j@entry yields
"<unavailable>".  However, the actual output is:

 (gdb) frame
 #0  0x0000000000400540 in foo (i=0, i@entry=2, j=2, j@entry=<error reading variable: Cannot access memory at address 0x6009e8>)

The error is thrown here:

#0  throw_it (reason=RETURN_ERROR, error=MEMORY_ERROR, fmt=0x8cd550 "Cannot access memory at address %s", ap=0x7fffffffc8e8) at ../../src/gdb/exceptions.c:373
#1  0x00000000005e2f9c in throw_error (error=MEMORY_ERROR, fmt=0x8cd550 "Cannot access memory at address %s") at ../../src/gdb/exceptions.c:422
#2  0x0000000000673a5f in memory_error (status=5, memaddr=6293992) at ../../src/gdb/corefile.c:204
#3  0x0000000000673aea in read_memory (memaddr=6293992, myaddr=0x7fffffffca60 "\200\316\377\377\377\177", len=4) at ../../src/gdb/corefile.c:223
#4  0x00000000006784d1 in dwarf_expr_read_mem (baton=0x7fffffffcd50, buf=0x7fffffffca60 "\200\316\377\377\377\177", addr=6293992, len=4) at ../../src/gdb/dwarf2loc.c:334
#5  0x000000000067645e in execute_stack_op (ctx=0x1409480, op_ptr=0x7fffffffce87 "\237<\005@", op_end=0x7fffffffce88 "<\005@") at ../../src/gdb/dwarf2expr.c:1045
#6  0x0000000000674e29 in dwarf_expr_eval (ctx=0x1409480, addr=0x7fffffffce80 "\003\350\t`", len=8) at ../../src/gdb/dwarf2expr.c:364
#7  0x000000000067c5b2 in dwarf2_evaluate_loc_desc_full (type=0x10876d0, frame=0xd8ecc0, data=0x7fffffffce80 "\003\350\t`", size=8, per_cu=0xf24c40, byte_offset=0)
    at ../../src/gdb/dwarf2loc.c:2236
#8  0x000000000067cc65 in dwarf2_evaluate_loc_desc (type=0x10876d0, frame=0xd8ecc0, data=0x7fffffffce80 "\003\350\t`", size=8, per_cu=0xf24c40)
    at ../../src/gdb/dwarf2loc.c:2407
#9  0x000000000067a5d4 in dwarf_entry_parameter_to_value (parameter=0x13a7960, deref_size=18446744073709551615, type=0x10876d0, caller_frame=0xd8ecc0, per_cu=0xf24c40)
    at ../../src/gdb/dwarf2loc.c:1160
#10 0x000000000067a962 in value_of_dwarf_reg_entry (type=0x10876d0, frame=0xd8de70, kind=CALL_SITE_PARAMETER_DWARF_REG, kind_u=...) at ../../src/gdb/dwarf2loc.c:1310
#11 0x000000000067aaca in value_of_dwarf_block_entry (type=0x10876d0, frame=0xd8de70, block=0xf1c2d4 "Q", block_len=1) at ../../src/gdb/dwarf2loc.c:1363
#12 0x000000000067e7c9 in locexpr_read_variable_at_entry (symbol=0x13a7540, frame=0xd8de70) at ../../src/gdb/dwarf2loc.c:3326
#13 0x00000000005daab6 in read_frame_arg (sym=0x13a7540, frame=0xd8de70, argp=0x7fffffffd0e0, entryargp=0x7fffffffd100) at ../../src/gdb/stack.c:362
#14 0x00000000005db384 in print_frame_args (func=0x13a7470, frame=0xd8de70, num=-1, stream=0xea3890) at ../../src/gdb/stack.c:669
#15 0x00000000005dc338 in print_frame (frame=0xd8de70, print_level=1, print_what=SRC_AND_LOC, print_args=1, sal=...) at ../../src/gdb/stack.c:1199
#16 0x00000000005db8ee in print_frame_info (frame=0xd8de70, print_level=1, print_what=SRC_AND_LOC, print_args=1) at ../../src/gdb/stack.c:851
#17 0x00000000005da2bb in print_stack_frame (frame=0xd8de70, print_level=1, print_what=SRC_AND_LOC) at ../../src/gdb/stack.c:169
#18 0x00000000005de236 in frame_command (level_exp=0x0, from_tty=1) at ../../src/gdb/stack.c:2265

dwarf2_evaluate_loc_desc_full (frame #7) knows to handle
NOT_AVAILABLE_ERROR errors, but read_memory always throws
a generic error.

Presently, only the value machinery knows to handle unavailable
memory.  We need to push the awareness down to the target_xfer layer,
making it return a finer grained error indication.  We can only return
a generic -1 nowadays, which leaves the upper layers with no clue on
why the xfer failed.  Use target_xfer_partial directly, rather than
propagating the error through target_read_memory so as to get a better
address to display in the error message.

(target_read_memory & friends build on top of target_read (thus the
target_xfer machinery), but turn all errors to EIO, an errno value.  I
think this is a mistake, and we'd better convert all these to return a
target_xfer_error too, but that can be done separately.  I looked
around a bit over memory_error calls, and the need to handle random
errno values, other than the EIOs gdb itself hardcodes, probably comes
(only) from deprecated_xfer_memory, which uses errno for error
indication, but I didn't look exhaustively.  We should really get rid
of deprecated_xfer_memory and of passing down errno values as error
indication in target_read & friends methods).

Tested on x86_64 Fedora 17, native and gdbserver.  Fixes the test in
the PR, which will be added to the testsuite later.

gdb/
2013-08-22  Pedro Alves  <palves@redhat.com>

	PR gdb/15871
	* corefile.c (target_xfer_memory_error): New function.
	(memory_error): Defer EIO to target_memory_error.
	(read_memory): Use target_xfer_partial, and handle finer-grained
	target xfer errors.
	* target.c (target_xfer_error_to_string): New function.
	(memory_xfer_partial_1): If memory is known to be
	unavailable, return TARGET_XFER_E_UNAVAILABLE instead of -1.
	(target_xfer_partial): Make extern.
	* target.h (enum target_xfer_error): New enum.
	(target_xfer_error_to_string): Declare function.
	(target_xfer_partial): Declare function.
	(struct target_ops) <xfer_partial>: Adjust describing comment.
darchons pushed a commit that referenced this issue May 12, 2014
…sniffer (move dwarf2_tailcall_sniffer_first elsewhere).

Two rationales, same patch.

TL;DR 1:

 dwarf2_frame_cache recursion is evil.  dwarf2_frame_cache calls
 dwarf2_tailcall_sniffer_first which then recurses into
 dwarf2_frame_cache.

TL;DR 2:

 An unwinder trying to unwind is evil.  dwarf2_frame_sniffer calls
 dwarf2_frame_cache which calls dwarf2_tailcall_sniffer_first which
 then tries to unwind the PC of the previous frame.

Avoid all that by deferring dwarf2_tailcall_sniffer_first until it's
really necessary.

Rationale 1
===========

A frame sniffer should not try to unwind, because that bypasses all
the validation checks done by get_prev_frame.  The UNWIND_SAME_ID
scenario is one such case where GDB is currently broken because (in
part) of this (the next patch adds a test that would fail without
this).

GDB goes into an infinite loop in value_fetch_lazy, here:

      while (VALUE_LVAL (new_val) == lval_register && value_lazy (new_val))
	{
	  frame = frame_find_by_id (VALUE_FRAME_ID (new_val));
...
	  new_val = get_frame_register_value (frame, regnum);
	}

(top-gdb) bt
#0  value_fetch_lazy (val=0x11516d0) at ../../src/gdb/value.c:3510
#1  0x0000000000584bd8 in value_optimized_out (value=0x11516d0) at ../../src/gdb/value.c:1096
#2  0x00000000006fe7a1 in frame_register_unwind (frame=0x1492600, regnum=16, optimizedp=0x7fffffffcdec, unavailablep=0x7fffffffcde8, lvalp=0x7fffffffcdd8, addrp=
    0x7fffffffcde0, realnump=0x7fffffffcddc, bufferp=0x7fffffffce10 "@\316\377\377\377\177") at ../../src/gdb/frame.c:940
#3  0x00000000006fea3a in frame_unwind_register (frame=0x1492600, regnum=16, buf=0x7fffffffce10 "@\316\377\377\377\177") at ../../src/gdb/frame.c:990
#4  0x0000000000473b9b in i386_unwind_pc (gdbarch=0xf54660, next_frame=0x1492600) at ../../src/gdb/i386-tdep.c:1771
#5  0x0000000000601dfa in gdbarch_unwind_pc (gdbarch=0xf54660, next_frame=0x1492600) at ../../src/gdb/gdbarch.c:2870
#6  0x0000000000693db5 in dwarf2_tailcall_sniffer_first (this_frame=0x1492600, tailcall_cachep=0x14926f0, entry_cfa_sp_offsetp=0x7fffffffcf00)
    at ../../src/gdb/dwarf2-frame-tailcall.c:389
#7  0x0000000000690928 in dwarf2_frame_cache (this_frame=0x1492600, this_cache=0x1492618) at ../../src/gdb/dwarf2-frame.c:1245
#8  0x0000000000690f46 in dwarf2_frame_sniffer (self=0x8e4980, this_frame=0x1492600, this_cache=0x1492618) at ../../src/gdb/dwarf2-frame.c:1423
#9  0x000000000070203b in frame_unwind_find_by_frame (this_frame=0x1492600, this_cache=0x1492618) at ../../src/gdb/frame-unwind.c:112
#10 0x00000000006fd681 in get_frame_id (fi=0x1492600) at ../../src/gdb/frame.c:408
#11 0x00000000007006c2 in get_prev_frame_1 (this_frame=0xdc1860) at ../../src/gdb/frame.c:1826
#12 0x0000000000700b7a in get_prev_frame (this_frame=0xdc1860) at ../../src/gdb/frame.c:2056
#13 0x0000000000514588 in frame_info_to_frame_object (frame=0xdc1860) at ../../src/gdb/python/py-frame.c:322
#14 0x000000000051784c in bootstrap_python_frame_filters (frame=0xdc1860, frame_low=0, frame_high=-1) at ../../src/gdb/python/py-framefilter.c:1396
#15 0x0000000000517a6f in apply_frame_filter (frame=0xdc1860, flags=7, args_type=CLI_SCALAR_VALUES, out=0xed7a90, frame_low=0, frame_high=-1)
    at ../../src/gdb/python/py-framefilter.c:1492
#16 0x00000000005e77b0 in backtrace_command_1 (count_exp=0x0, show_locals=0, no_filters=0, from_tty=1) at ../../src/gdb/stack.c:1777
#17 0x00000000005e7c0f in backtrace_command (arg=0x0, from_tty=1) at ../../src/gdb/stack.c:1891
#18 0x00000000004e37a7 in do_cfunc (c=0xda4fa0, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:107
#19 0x00000000004e683c in cmd_func (cmd=0xda4fa0, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:1882
#20 0x00000000006f35ed in execute_command (p=0xcc66c2 "", from_tty=1) at ../../src/gdb/top.c:468
#21 0x00000000005f8853 in command_handler (command=0xcc66c0 "bt") at ../../src/gdb/event-top.c:435
#22 0x00000000005f8e12 in command_line_handler (rl=0xfe05f0 "@") at ../../src/gdb/event-top.c:632
#23 0x000000000074d2c6 in rl_callback_read_char () at ../../src/readline/callback.c:220
#24 0x00000000005f8375 in rl_callback_read_char_wrapper (client_data=0x0) at ../../src/gdb/event-top.c:164
#25 0x00000000005f876a in stdin_event_handler (error=0, client_data=0x0) at ../../src/gdb/event-top.c:375
#26 0x00000000005f72fa in handle_file_event (data=...) at ../../src/gdb/event-loop.c:768
#27 0x00000000005f67a3 in process_event () at ../../src/gdb/event-loop.c:342
#28 0x00000000005f686a in gdb_do_one_event () at ../../src/gdb/event-loop.c:406
#29 0x00000000005f68bb in start_event_loop () at ../../src/gdb/event-loop.c:431
#30 0x00000000005f83a7 in cli_command_loop (data=0x0) at ../../src/gdb/event-top.c:179
#31 0x00000000005eeed3 in current_interp_command_loop () at ../../src/gdb/interps.c:327
#32 0x00000000005ef8ff in captured_command_loop (data=0x0) at ../../src/gdb/main.c:267
#33 0x00000000005ed2f6 in catch_errors (func=0x5ef8e4 <captured_command_loop>, func_args=0x0, errstring=0x8b6554 "", mask=RETURN_MASK_ALL)
    at ../../src/gdb/exceptions.c:524
#34 0x00000000005f0d21 in captured_main (data=0x7fffffffd9e0) at ../../src/gdb/main.c:1067
#35 0x00000000005ed2f6 in catch_errors (func=0x5efb9b <captured_main>, func_args=0x7fffffffd9e0, errstring=0x8b6554 "", mask=RETURN_MASK_ALL)
    at ../../src/gdb/exceptions.c:524
#36 0x00000000005f0d57 in gdb_main (args=0x7fffffffd9e0) at ../../src/gdb/main.c:1076
#37 0x000000000045bb6a in main (argc=4, argv=0x7fffffffdae8) at ../../src/gdb/gdb.c:34
(top-gdb)

GDB is trying to unwind the PC register of the previous frame (frame
#5 above), starting from the frame being sniffed (the THIS frame).
But the THIS frame's unwinder says the PC of the previous frame is
actually the same as the previous's frame's next frame (which is the
same frame we started with, the THIS frame), therefore it returns an
lval_register lazy value with frame set to THIS frame.  And so the
value_fetch_lazy loop never ends.


Rationale 2
===========

As an experiment, I tried making dwarf2-frame.c:read_addr_from_reg use
address_from_register.  That caused a bunch of regressions, but it
actually took me a long while to figure out what was going on.  Turns
out dwarf2-frame.c:read_addr_from_reg is called while computing the
frame's CFA, from within dwarf2_frame_cache.  address_from_register
wants to create a register with frame_id set to the frame being
constructed.  To create the frame id, we again call dwarf2_frame_cache,
which given:

static struct dwarf2_frame_cache *
dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
{
...
  if (*this_cache)
    return *this_cache;

returns an incomplete object to the caller:
static void
dwarf2_frame_this_id (struct frame_info *this_frame, void **this_cache,
		      struct frame_id *this_id)
{
  struct dwarf2_frame_cache *cache =
    dwarf2_frame_cache (this_frame, this_cache);
...
 (*this_id) = frame_id_build (cache->cfa, get_frame_func (this_frame));
}

As cache->cfa is still 0 (we were trying to compute it!), and
get_frame_id recalls this id from here on, we end up with a broken
frame id in recorded for this frame.  Later, when inspecting locals,
the dwarf machinery needs to know the selected frame's base, which
calls get_frame_base:

CORE_ADDR
get_frame_base (struct frame_info *fi)
{
  return get_frame_id (fi).stack_addr;
}

which as seen above then returns 0 ...

So I gave up using address_from_register.

But, the pain of investigating this made me want to have GDB itself
assert that recursion never happens here.  So I wrote a patch to do
that.  But, it triggers on current mainline, because
dwarf2_tailcall_sniffer_first, called from dwarf2_frame_cache, unwinds
the this_frame.

A sniffer shouldn't be trying to unwind, exactly because of this sort
of tricky issue.  The patch defers calling
dwarf2_tailcall_sniffer_first until it's really necessary, in
dwarf2_frame_prev_register (thus actually outside the sniffer path).
As this makes the call to dwarf2_frame_sniffer in dwarf2_frame_cache
unnecessary again, the patch removes that too.

Tested on x86_64 Fedora 17.

gdb/
2013-11-22  Pedro Alves  <palves@redhat.com>

	PR 16155
	* dwarf2-frame.c (struct dwarf2_frame_cache)
	<checked_tailcall_bottom, entry_cfa_sp_offset,
	entry_cfa_sp_offset_p>: New fields.
	(dwarf2_frame_cache): Adjust to use the new cache fields instead
	of locals.  Don't call dwarf2_tailcall_sniffer_first here.
	(dwarf2_frame_prev_register): Call it here, but only once.
darchons pushed a commit that referenced this issue May 12, 2014
…sniffer (move dwarf2_tailcall_sniffer_first elsewhere).

Two rationales, same patch.

TL;DR 1:

 dwarf2_frame_cache recursion is evil.  dwarf2_frame_cache calls
 dwarf2_tailcall_sniffer_first which then recurses into
 dwarf2_frame_cache.

TL;DR 2:

 An unwinder trying to unwind is evil.  dwarf2_frame_sniffer calls
 dwarf2_frame_cache which calls dwarf2_tailcall_sniffer_first which
 then tries to unwind the PC of the previous frame.

Avoid all that by deferring dwarf2_tailcall_sniffer_first until it's
really necessary.

Rationale 1
===========

A frame sniffer should not try to unwind, because that bypasses all
the validation checks done by get_prev_frame.  The UNWIND_SAME_ID
scenario is one such case where GDB is currently broken because (in
part) of this (the next patch adds a test that would fail without
this).

GDB goes into an infinite loop in value_fetch_lazy, here:

      while (VALUE_LVAL (new_val) == lval_register && value_lazy (new_val))
	{
	  frame = frame_find_by_id (VALUE_FRAME_ID (new_val));
...
	  new_val = get_frame_register_value (frame, regnum);
	}

(top-gdb) bt
#0  value_fetch_lazy (val=0x11516d0) at ../../src/gdb/value.c:3510
#1  0x0000000000584bd8 in value_optimized_out (value=0x11516d0) at ../../src/gdb/value.c:1096
#2  0x00000000006fe7a1 in frame_register_unwind (frame=0x1492600, regnum=16, optimizedp=0x7fffffffcdec, unavailablep=0x7fffffffcde8, lvalp=0x7fffffffcdd8, addrp=
    0x7fffffffcde0, realnump=0x7fffffffcddc, bufferp=0x7fffffffce10 "@\316\377\377\377\177") at ../../src/gdb/frame.c:940
#3  0x00000000006fea3a in frame_unwind_register (frame=0x1492600, regnum=16, buf=0x7fffffffce10 "@\316\377\377\377\177") at ../../src/gdb/frame.c:990
#4  0x0000000000473b9b in i386_unwind_pc (gdbarch=0xf54660, next_frame=0x1492600) at ../../src/gdb/i386-tdep.c:1771
#5  0x0000000000601dfa in gdbarch_unwind_pc (gdbarch=0xf54660, next_frame=0x1492600) at ../../src/gdb/gdbarch.c:2870
#6  0x0000000000693db5 in dwarf2_tailcall_sniffer_first (this_frame=0x1492600, tailcall_cachep=0x14926f0, entry_cfa_sp_offsetp=0x7fffffffcf00)
    at ../../src/gdb/dwarf2-frame-tailcall.c:389
#7  0x0000000000690928 in dwarf2_frame_cache (this_frame=0x1492600, this_cache=0x1492618) at ../../src/gdb/dwarf2-frame.c:1245
#8  0x0000000000690f46 in dwarf2_frame_sniffer (self=0x8e4980, this_frame=0x1492600, this_cache=0x1492618) at ../../src/gdb/dwarf2-frame.c:1423
#9  0x000000000070203b in frame_unwind_find_by_frame (this_frame=0x1492600, this_cache=0x1492618) at ../../src/gdb/frame-unwind.c:112
#10 0x00000000006fd681 in get_frame_id (fi=0x1492600) at ../../src/gdb/frame.c:408
#11 0x00000000007006c2 in get_prev_frame_1 (this_frame=0xdc1860) at ../../src/gdb/frame.c:1826
#12 0x0000000000700b7a in get_prev_frame (this_frame=0xdc1860) at ../../src/gdb/frame.c:2056
#13 0x0000000000514588 in frame_info_to_frame_object (frame=0xdc1860) at ../../src/gdb/python/py-frame.c:322
#14 0x000000000051784c in bootstrap_python_frame_filters (frame=0xdc1860, frame_low=0, frame_high=-1) at ../../src/gdb/python/py-framefilter.c:1396
#15 0x0000000000517a6f in apply_frame_filter (frame=0xdc1860, flags=7, args_type=CLI_SCALAR_VALUES, out=0xed7a90, frame_low=0, frame_high=-1)
    at ../../src/gdb/python/py-framefilter.c:1492
#16 0x00000000005e77b0 in backtrace_command_1 (count_exp=0x0, show_locals=0, no_filters=0, from_tty=1) at ../../src/gdb/stack.c:1777
#17 0x00000000005e7c0f in backtrace_command (arg=0x0, from_tty=1) at ../../src/gdb/stack.c:1891
#18 0x00000000004e37a7 in do_cfunc (c=0xda4fa0, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:107
#19 0x00000000004e683c in cmd_func (cmd=0xda4fa0, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:1882
#20 0x00000000006f35ed in execute_command (p=0xcc66c2 "", from_tty=1) at ../../src/gdb/top.c:468
#21 0x00000000005f8853 in command_handler (command=0xcc66c0 "bt") at ../../src/gdb/event-top.c:435
#22 0x00000000005f8e12 in command_line_handler (rl=0xfe05f0 "@") at ../../src/gdb/event-top.c:632
#23 0x000000000074d2c6 in rl_callback_read_char () at ../../src/readline/callback.c:220
#24 0x00000000005f8375 in rl_callback_read_char_wrapper (client_data=0x0) at ../../src/gdb/event-top.c:164
#25 0x00000000005f876a in stdin_event_handler (error=0, client_data=0x0) at ../../src/gdb/event-top.c:375
#26 0x00000000005f72fa in handle_file_event (data=...) at ../../src/gdb/event-loop.c:768
#27 0x00000000005f67a3 in process_event () at ../../src/gdb/event-loop.c:342
#28 0x00000000005f686a in gdb_do_one_event () at ../../src/gdb/event-loop.c:406
#29 0x00000000005f68bb in start_event_loop () at ../../src/gdb/event-loop.c:431
#30 0x00000000005f83a7 in cli_command_loop (data=0x0) at ../../src/gdb/event-top.c:179
#31 0x00000000005eeed3 in current_interp_command_loop () at ../../src/gdb/interps.c:327
#32 0x00000000005ef8ff in captured_command_loop (data=0x0) at ../../src/gdb/main.c:267
#33 0x00000000005ed2f6 in catch_errors (func=0x5ef8e4 <captured_command_loop>, func_args=0x0, errstring=0x8b6554 "", mask=RETURN_MASK_ALL)
    at ../../src/gdb/exceptions.c:524
#34 0x00000000005f0d21 in captured_main (data=0x7fffffffd9e0) at ../../src/gdb/main.c:1067
#35 0x00000000005ed2f6 in catch_errors (func=0x5efb9b <captured_main>, func_args=0x7fffffffd9e0, errstring=0x8b6554 "", mask=RETURN_MASK_ALL)
    at ../../src/gdb/exceptions.c:524
#36 0x00000000005f0d57 in gdb_main (args=0x7fffffffd9e0) at ../../src/gdb/main.c:1076
#37 0x000000000045bb6a in main (argc=4, argv=0x7fffffffdae8) at ../../src/gdb/gdb.c:34
(top-gdb)

GDB is trying to unwind the PC register of the previous frame (frame
#5 above), starting from the frame being sniffed (the THIS frame).
But the THIS frame's unwinder says the PC of the previous frame is
actually the same as the previous's frame's next frame (which is the
same frame we started with, the THIS frame), therefore it returns an
lval_register lazy value with frame set to THIS frame.  And so the
value_fetch_lazy loop never ends.


Rationale 2
===========

As an experiment, I tried making dwarf2-frame.c:read_addr_from_reg use
address_from_register.  That caused a bunch of regressions, but it
actually took me a long while to figure out what was going on.  Turns
out dwarf2-frame.c:read_addr_from_reg is called while computing the
frame's CFA, from within dwarf2_frame_cache.  address_from_register
wants to create a register with frame_id set to the frame being
constructed.  To create the frame id, we again call dwarf2_frame_cache,
which given:

static struct dwarf2_frame_cache *
dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
{
...
  if (*this_cache)
    return *this_cache;

returns an incomplete object to the caller:
static void
dwarf2_frame_this_id (struct frame_info *this_frame, void **this_cache,
		      struct frame_id *this_id)
{
  struct dwarf2_frame_cache *cache =
    dwarf2_frame_cache (this_frame, this_cache);
...
 (*this_id) = frame_id_build (cache->cfa, get_frame_func (this_frame));
}

As cache->cfa is still 0 (we were trying to compute it!), and
get_frame_id recalls this id from here on, we end up with a broken
frame id in recorded for this frame.  Later, when inspecting locals,
the dwarf machinery needs to know the selected frame's base, which
calls get_frame_base:

CORE_ADDR
get_frame_base (struct frame_info *fi)
{
  return get_frame_id (fi).stack_addr;
}

which as seen above then returns 0 ...

So I gave up using address_from_register.

But, the pain of investigating this made me want to have GDB itself
assert that recursion never happens here.  So I wrote a patch to do
that.  But, it triggers on current mainline, because
dwarf2_tailcall_sniffer_first, called from dwarf2_frame_cache, unwinds
the this_frame.

A sniffer shouldn't be trying to unwind, exactly because of this sort
of tricky issue.  The patch defers calling
dwarf2_tailcall_sniffer_first until it's really necessary, in
dwarf2_frame_prev_register (thus actually outside the sniffer path).
As this makes the call to dwarf2_frame_sniffer in dwarf2_frame_cache
unnecessary again, the patch removes that too.

Tested on x86_64 Fedora 17.

gdb/
2013-11-22  Pedro Alves  <palves@redhat.com>

	PR 16155
	* dwarf2-frame.c (struct dwarf2_frame_cache)
	<checked_tailcall_bottom, entry_cfa_sp_offset,
	entry_cfa_sp_offset_p>: New fields.
	(dwarf2_frame_cache): Adjust to use the new cache fields instead
	of locals.  Don't call dwarf2_tailcall_sniffer_first here.
	(dwarf2_frame_prev_register): Call it here, but only once.
darchons pushed a commit that referenced this issue May 12, 2014
Given we already have the frame id stash, which holds the ids of all
frames in the chain, detecting corrupted stacks with wide stack cycles
with non-consecutive dup frame ids is just as cheap as just detecting
cycles in consecutive frames:

 #0 frame_id1
 #1 frame_id2
 #2 frame_id3
 #3 frame_id1
 #4 frame_id2
 #5 frame_id3
 #6 frame_id1
 ... forever ...

We just need to check whether the stash already knows about a given
frame id instead of comparing the ids of the previous/this frames.

Tested on x86_64 Fedora 17.

gdb/
2013-11-22  Pedro Alves  <palves@redhat.com>
	    Tom Tromey  <tromey@redhat.com>

	* frame.c (frame_stash_add): Now returns whether a frame with the
	same ID was already known.
	(compute_frame_id): New function, factored out from get_frame_id.
	(get_frame_id): No longer lazilly compute the frame id here.
	(get_prev_frame_if_no_cycle): New function.  Detects wider stack
	cycles.
	(get_prev_frame_1): Use it instead of get_prev_frame_raw directly,
	and checking for stack cycles here.
darchons pushed a commit that referenced this issue May 12, 2014
Remote servers may cut the connection abruptly since they are not
required to reply to a 'k' (Kill) packet sent from GDB.

This patch addresses any issues arising from such scenario, which
leads to a GDB internal error due to an attempt to pop the target more
than once.  With the patch, this failure is handled gracefully.

Here's the GDB backtrace Maciej got running the testsuite against
QEMU.  Full paths edited out for brevity.

#0  0x55573430 in __kernel_vsyscall ()
#1  0x557a2951 in raise () from /lib32/libc.so.6
#2  0x557a5d82 in abort () from /lib32/libc.so.6
#3  0x0826e2e4 in dump_core ()
    at .../gdb/utils.c:635
#4  0x0826e5b6 in internal_vproblem (problem=0x85200c0,
    file=0x8416be8 ".../gdb/target.c", line=2861,
    fmt=0x84174ac "could not find a target to follow mourn inferior",
    ap=0xffa4796c "\f")
    at .../gdb/utils.c:804
#5  0x0826e5fb in internal_verror (
    file=0x8416be8 ".../gdb/target.c", line=2861,
    fmt=0x84174ac "could not find a target to follow mourn inferior",
    ap=0xffa4796c "\f")
    at .../gdb/utils.c:820
#6  0x0826e633 in internal_error (
    file=0x8416be8 ".../gdb/target.c", line=2861,
    string=0x84174ac "could not find a target to follow mourn inferior")
    at .../gdb/utils.c:830
#7  0x081b4ad0 in target_mourn_inferior ()
    at .../gdb/target.c:2861
#8  0x08082283 in remote_kill (ops=0x85245e0)
    at .../gdb/remote.c:7840
#9  0x081b06d1 in target_kill ()
    at .../gdb/target.c:486
#10 0x081b42f6 in dispose_inferior (inf=0xa501c60, args=0x0)
    at .../gdb/target.c:2570
#11 0x08290cfc in iterate_over_inferiors (
    callback=0x81b42af <dispose_inferior>, data=0x0)
    at .../gdb/inferior.c:396
#12 0x081b435a in target_preopen (from_tty=1)
    at .../gdb/target.c:2591
#13 0x0807c2c6 in remote_open_1 (name=0xa5538b6 "localhost:1237", from_tty=1,
    target=0x85245e0, extended_p=0)
    at .../gdb/remote.c:4292
#14 0x0807b7a8 in remote_open (name=0xa5538b6 "localhost:1237", from_tty=1)
    at .../gdb/remote.c:3655
#15 0x080a23d4 in do_cfunc (c=0xa464f30, args=0xa5538b6 "localhost:1237",
    from_tty=1)
    at .../gdb/cli/cli-decode.c:107
#16 0x080a4c3b in cmd_func (cmd=0xa464f30, args=0xa5538b6 "localhost:1237",
    from_tty=1)
    at .../gdb/cli/cli-decode.c:1882
#17 0x0826bebf in execute_command (p=0xa5538c3 "7", from_tty=1)
    at .../gdb/top.c:467
#18 0x08193f2d in command_handler (command=0xa5538a8 "")
    at .../gdb/event-top.c:435
#19 0x08194463 in command_line_handler (
    rl=0xa778198 "target remote localhost:1237")
    at .../gdb/event-top.c:633
#20 0x082ba92b in rl_callback_read_char ()
    at .../readline/callback.c:220
#21 0x08193adf in rl_callback_read_char_wrapper (client_data=0x0)
    at .../gdb/event-top.c:164
#22 0x08193e57 in stdin_event_handler (error=0, client_data=0x0)
    at .../gdb/event-top.c:375
#23 0x08192f29 in handle_file_event (data=...)
    at .../gdb/event-loop.c:768
#24 0x0819266a in process_event ()
    at .../gdb/event-loop.c:342
#25 0x08192708 in gdb_do_one_event ()
    at .../gdb/event-loop.c:394
#26 0x08192781 in start_event_loop ()
    at .../gdb/event-loop.c:431
#27 0x08193b08 in cli_command_loop (data=0x0)
    at .../gdb/event-top.c:179
#28 0x0818bc26 in current_interp_command_loop ()
    at .../gdb/interps.c:327
#29 0x0818c4e5 in captured_command_loop (data=0x0)
    at .../gdb/main.c:267
#30 0x0818a37f in catch_errors (func=0x818c4d0 <captured_command_loop>,
    func_args=0x0, errstring=0x8402108 "", mask=RETURN_MASK_ALL)
    at .../gdb/exceptions.c:524
#31 0x0818d736 in captured_main (data=0xffa47f10)
    at .../gdb/main.c:1067
#32 0x0818a37f in catch_errors (func=0x818c723 <captured_main>,
    func_args=0xffa47f10, errstring=0x8402108 "", mask=RETURN_MASK_ALL)
    at .../gdb/exceptions.c:524
#33 0x0818d76c in gdb_main (args=0xffa47f10)
    at .../gdb/main.c:1076
#34 0x0804dd1b in main (argc=5, argv=0xffa47fd4)
    at .../gdb/gdb.c:34

The corresponding gdb.log excerpt:

(gdb) PASS: gdb.base/bitfields.exp: bitfield uniqueness (u9)
cont
Continuing.

Breakpoint 1, break1 () at .../gdb/testsuite/gdb.base/bitfields.c:44
44	}
(gdb) PASS: gdb.base/bitfields.exp: continuing to break1 #9
print flags
$10 = {uc = 0 '\000', s1 = 0, u1 = 0, s2 = 0, u2 = 0, s3 = 0, u3 = 0, s9 = 0, u9 = 0, sc = 1 '\001'}
(gdb) PASS: gdb.base/bitfields.exp: bitfield uniqueness (sc)
delete breakpoints
Delete all breakpoints? (y or n) y
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) delete breakpoints
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break break2
Breakpoint 2 at 0x85f8: file .../gdb/testsuite/gdb.base/bitfields.c, line 48.
(gdb) entering gdb_reload
target remote localhost:1235
A program is being debugged already.  Kill it? (y or n) y
Remote connection closed
.../gdb/target.c:2861: internal-error: could not find a target to follow mourn inferior
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) ^Ccontinue
Please answer y or n.
.../gdb/target.c:2861: internal-error: could not find a target to follow mourn inferior
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) Resyncing due to internal error.
n
.../gdb/target.c:2861: internal-error: could not find a target to follow mourn inferior
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Create a core file of GDB? (y or n) y
Command aborted.
(gdb) print/x flags
$11 = {uc = 0x0, s1 = 0x0, u1 = 0x0, s2 = 0x0, u2 = 0x0, s3 = 0x0, u3 = 0x0, s9 = 0x0, u9 = 0x0, sc = 0x0}
(gdb) FAIL: gdb.base/bitfields.exp: bitfield containment #1
cont
The program is not being run.
(gdb) FAIL: gdb.base/bitfields.exp: continuing to break2 (the program is no longer running)
print/x flags
$12 = {uc = 0x0, s1 = 0x0, u1 = 0x0, s2 = 0x0, u2 = 0x0, s3 = 0x0, u3 = 0x0, s9 = 0x0, u9 = 0x0, sc = 0x0}
(gdb) FAIL: gdb.base/bitfields.exp: bitfield containment #2
delete breakpoints
Delete all breakpoints? (y or n) y
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) delete breakpoints
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) break break3
Breakpoint 3 at 0x8604: file .../gdb/testsuite/gdb.base/bitfields.c, line 52.
(gdb) entering gdb_reload
target remote localhost:1236
Remote debugging using localhost:1236
Reading symbols from .../lib/ld-linux.so.3...done.
Loaded symbols for .../lib/ld-linux.so.3
0x41001b80 in _start () from .../lib/ld-linux.so.3
(gdb) continue
Continuing.

Breakpoint 3, break3 () at .../gdb/testsuite/gdb.base/bitfields.c:52
52	}
(gdb) print flags
$13 = {uc = 0 '\000', s1 = 0, u1 = 1, s2 = 0, u2 = 3, s3 = 0, u3 = 7, s9 = 0, u9 = 511, sc = 0 '\000'}
(gdb) PASS: gdb.base/bitfields.exp: unsigned bitfield ranges

gdb/
2013-12-02  Pedro Alves  <pedro@codesourcery.com>
            Maciej W. Rozycki  <macro@codesourcery.com>

	* remote.c (putpkt_for_catch_errors): Remove function.
	(remote_kill): Handle TARGET_CLOSE_ERROR from the kill packet
	gracefully.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants