[Ltrace-devel] [RFC] add xtensa architecture support to ltrace

Petr Machata pmachata at redhat.com
Thu Jun 19 13:15:47 UTC 2014


Max Filippov <jcmvbkbc at gmail.com> writes:

> It's an RFC because currently there's an issue I'm not sure how to deal with:
> code blocks that correspond to PLT entries on other architectures are only
> used at most once on xtensa, after initial resolution of imported symbol
> address importing module calls it directly.

I don't know the details of operation of xtensa (and I didn't look into
your code yet), but these sorts of things can be generally solved as
follows.

- Put a breakpoint to PLT, catching the first call, and then stop all
  threads and singlestep through the dynamic linker, and waiting for the
  relevant token (such as GOT entry or whatever) to be updated.  Then
  you start the threads again.

- From the updated token, you figure out what the address is where the
  symbol got resolved ("final address" below), and store it at struct
  breakpoint.arch.  Then you undo what the dynamic linker did
  ("unresolve" the token).

- On every next call, when you already know the destination address you
  just change PC to that address via ptrace right away, and let the
  process continue.  You do this after the first call as well.

- If the binary was prelinked, or if you attach to a running process,
  you need to unresolve any hitherto-resolved tokens.

This is fairly involved, but PowerPC backend does this, with some added
twists.  There's however a fair amount of wiggle-room depending on what
you are willing to compromise:

- Instead of single-stepping through dynamic linker, you can simply
  install an on_hit handler to a return breakpoint, figure out the final
  address after return, and then undo it and proceed as above.  The
  downside is that then there's a window between the call and a return,
  where ltrace will miss calls to the same symbol (be they calls from
  other threads or reentrant calls through the same PLT from the same
  thread).

- Or yet simpler, do the on_return thing from previous paragraph, figure
  out the final address, and then just relocate the breakpoint there.
  In addition to the disadvantage above, this one also can't distinguish
  local calls from PLT ones (except for the first one).  If I recall
  correctly, this is what the MIPS backend does.

If you want to implement the full thing, then perhaps looking into
sysdeps/linux-gnu/ppc/plt.c could provide some insight, though with four
ABI's to support, there's a lot of code, and disentangling the relevant
bits may be challenging.  The relevant functions are ppc_plt_bp_continue
and cb_keep_stepping_p.  There's also an extensive comment at the top of
the file explaining some of the complexities that the PowerPC backend
needs to handle and how it does that, but I'm not sure it's enlightening
for anyone working on a non-PowerPC architecture.

The stopping and singlestepping thing is handled by
process_install_stopping_handler.  You call this in PLT breakpoint's
on_continue handler, and provide a keep_stepping_p predicate that
returns CBS_CONT until the dynamic linker updated the relevant token
(then it return CBS_STOP).  You also need to note the final address
somewhere (presumably in breakpoint.arch), and possibly mark a flag in
the breakpoint, so that next time you get to on_continue, you know
you've already done all this, and can just jump to the known final
address right away.

Hopefully this was helpful.  Don't be afraid to ask if you need
anything.

Thanks,
PM



More information about the Ltrace-devel mailing list