libunwind-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [libunwind] unwinding through dynamically modified code?


From: David Mosberger
Subject: Re: [libunwind] unwinding through dynamically modified code?
Date: Thu, 25 Mar 2004 12:21:39 -0800

>>>>> On Wed, 24 Mar 2004 13:05:41 -0600 (CST), Todd L Miller <address@hidden> 
>>>>> said:

  Todd> /* The unw_dyn_info_t which points to regionZero (whose 'next' is
  Todd> regionOne) has a start_ip of 0x60000ffffffe9f80. */

Looks fine.

  Todd> 0x60000ffffffe9f80:     [MLX]       nop.m 0x0
  Todd> 0x60000ffffffe9f81:                 brl.few 0x60000ffffffea1e0;;

  Todd> /* This jump is because this code fragment is currently configured
  Todd> not to do anything; it will be later replaced with a bundle
  Todd> of no-ops. */

OK.

  Todd> 0x60000ffffffe9f90:     [MII]       alloc r43=ar.pfs,61,53,0
  Todd> 0x60000ffffffe9f91:                 nop.i 0x0
  Todd> 0x60000ffffffe9f92:                 nop.i 0x0;;

  Todd> /* op_count was initialized to 0, and insn_count is 3. */

  Todd> _U_dyn_op_save_reg( & regionOne->op[ regionOne->op_count++ ],
  Todd>                    _U_QP_TRUE, regionOne->insn_count++,
  Todd>                    UNW_IA64_AR_PFS, UNW_IA64_GR + 43 );

This looks wrong: the "when" field must be set to the index of the
instruction it is describing.  In C, this index is calculated like
this:

  insn_index = (Ib - Rb)/16*3 + Is - Rs

where:

        Ib = instruction's bundle address
        Rb = bundle address of start of region
        Is = instruction's slot number
        Rs = slot number at which region starts

Note: customarily, the slot number is encoded in the least-significant
2 bits of an instruction-pointer.

Apart from the "when" field, the directive looks fine.

  Todd> 0x60000ffffffe9fa0:     [MII]       mov r44=r1

  Todd> /* Is this still superflous if I later change the gp to make a
  Todd> function call? */

Not just superfluous, but actually illegal.  Libunwind would complain
at the time this descriptor was interpreted (for performance-reasons,
no checking is done at the time _U_dyn_register() is called).

  Todd> _U_dyn_op_save_reg( & regionOne->op[ regionOne->op_count++ ],
  Todd> _U_QP_TRUE, regionOne->insn_count++,
  Todd> UNW_IA64_GR + 1, UNW_IA64_GR + 44 );

  Todd> 0x60000ffffffe9fa1:                 mov r45=r2
  Todd> 0x60000ffffffe9fa2:                 mov r46=r3
  Todd> ... // the other unstacked scratch registers
  Todd> 0x60000ffffffea020:     [MII]       mov r68=r31

Eh, r4-r7 are "preserved" registers.  If you save those, you'll need
to add describe the saving instructions.

  Todd> 0x60000ffffffea051:                 mov r75=pr

  Todd> _U_dyn_op_save_reg( & regionOne->op[ regionOne->op_count++ ],
  Todd>                     _U_QP_TRUE, regionOne->insn_count++,
  Todd>                     UNW_IA64_PR, UNW_IA64_GR + 75 );

Apart from the "when" field, this looks fine.

  Todd> 0x60000ffffffea061:                 mov r76=r12

  Todd> _U_dyn_op_save_reg( & regionOne->op[ regionOne->op_count++ ],
  Todd>                     _U_QP_TRUE, regionOne->insn_count++,
  Todd>                     UNW_IA64_SP, UNW_IA64_GR + 76 );

Likewise.

  Todd> 0x60000ffffffea062:                 adds r12=-32,r12;;

This one:

  Todd> 0x60000ffffffea1b2:                 mov r12=r76

needs to be described with:

        _U_dyn_op_pop_frames(op, qp, when, 1)

because you're popping 1 stack-frame.

  Todd> 0x60000ffffffea1c0:     [MII]       nop.m 0x0
  Todd> 0x60000ffffffea1c1:                 mov.i ar.pfs=r43
  Todd> 0x60000ffffffea1c2:                 nop.i 0x0;;

The second region must end here, so the unwinder knows that
after this point, the existing frame-info is no longer valid.

  Todd> 0x60000ffffffea1d0:     [MII]       alloc r43=ar.pfs,12,3,0

  Todd> /* At this point, the machine state, excepting the extra output
  Todd> register and the pc, is the same as it was before the jump
  Todd> to this code fragment.  After this bundle finishes its
  Todd> nops, */

You'll need to open up a new region.  Assuming that the code matches
_exactly_ as the code at the original location, you can use ALIAS
to ensure that the unwinder will find the right unwind info.

  Todd> /* More importantly, however, after this alloc, the registers
  Todd> in which I'd been preserving ar.pfs, the gp, the stack pointer,
  Todd> and the predicate registers are no longer accessible.

That's why the second region needs to end before the "alloc".

  Todd> Given that my code fragment is only half-way done, do I insert
  Todd> another alias operation, or _U_dyn_op_stop() the current region
  Todd> and start another one?

Yup.

  Todd> Immediately after the emulated instructions, I insert another
  Todd> large alloc and repeat the entire process.  If I used more
  Todd> than two regions, would the third be an aliased region
  Todd> covering the emulated instruction(s), and then the fourth
  Todd> effectively a duplicate of the second?  Or do the label
  Todd> state/copy state operations come into play here? */

If I understand you correctly, label_state and copy_state would be
what you'd want to use here.

  Todd> I then have to copy the unw_dyn_info_t and its linked list of
  Todd> regions into the remote process (diddling the pointers as I
  Todd> go) and link it into the dynamic unwind information list
  Todd> there.

Yes, that sounds about right.

Just for your info: the tests/test-ptrace program can single-step over
a (region) of a program and unwind at each step.  That's a good way to
test whether the unwind info is correct.  It's not a _complete_ test,
because it only tests what is needed to unwind to the root function,
but especially when combined with --enable-debug and UNW_DEBUG_LEVEL,
it's a very good way to verify that things look sane.

Thanks,

        --david


reply via email to

[Prev in Thread] Current Thread [Next in Thread]