[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