Non-returning functions and calls to them are not being processed properly 
by Dyninst on ppc/be. As a consequence, in the analysis results produced 
by Dyninst for a binary for AMG 2013, two functions and a long branch 
trampoline are fused into a single function, which leads to confusion in 
HPCToolkit’s hpcstruct.  

The problem report below refers to two binaries: one called ungetc.ppc, 
which is a dynamic ppc/be binary for a main program that contains a single
call to ungetc and a second binary amg2013.gnu.ppc, which is a statically 
linked binary for bg/q (ppc/be).

The binaries can be found here: 
  https://rice.box.com/s/1c6i42lz7dlpxb3fy29rm39zvps5f8br


First a case that works:
 
The call at 0x1001cd0 in the ungetc.ppc binary at the end of ._IO_ungetc
is labeled as non-returning by the following line in Parser.C at line
1012 in my copy of Dyninst source:

  is_nonret = obj().cs()->nonReturning(target);

The function nonReturning resolves the address to a ParseAPI::Function 
and then determines that a mangled name of that function 
(_Unwind_Resume) is in the table of names of non_returning_funcs
defined in  CodeSource.C

         1001cd0:       48 07 20 71     bl      1073d40 <._Unwind_Resume>
         1001cd4:       60 00 00 00     nop
        _IO_ungetc():
         1001cd8:       00 00 00 00     .long 0x0
         1001cdc:       00 00 00 01     .long 0x1
         1001ce0:       80 03 00 00     lwz     r0,0(r3)
        __vmx__sigsetjmp_ent():
         1001ce4:       60 00 00 00     nop
         1001ce8:       60 00 00 00     nop
         1001cec:       60 00 00 00     nop

Now a case that doesn’t work:

In the amg2013.gnu.ppc binary, the branch at 0x158d6b0, which
is at the same spot in  ._IO_ungetc, goes to a ppc long_branch trampoline
that makes a tail call to _Unwind_Resume. This call is not classified as
non-returning.  As a result, there is a fall through edge that joins it
to the branch trampoline for _IO_vfscanf that that follows ._IO_ungetc.

        ...
         158d6b0:       4b ff fb 41     bl      158d1f0 
<00008345.long_branch_r2off._Unwind_Resume+0>
         158d6b4:       e8 41 00 28     ld      r2,40(r1)
        _IO_ungetc():
         158d6b8:       00 00 00 00     .long 0x0
         158d6bc:       00 00 00 01     .long 0x1
         158d6c0:       80 03 00 00     lwz     r0,0(r3)

        000000000158d6c4 <00008368.long_branch_r2off._IO_vfscanf+0>:
        00008368.long_branch_r2off._IO_vfscanf+0():
         158d6c4:       f8 41 00 28     std     r2,40(r1)
         158d6c8:       3c 42 00 01     addis   r2,r2,1
         158d6cc:       38 42 ff f8     addi    r2,r2,-8
         158d6d0:       48 05 a1 00     b       15e77d0 <._IO_vfscanf>
         158d6d4:       60 00 00 00     nop
         158d6d8:       60 00 00 00     nop
         158d6dc:       60 00 00 00     nop

In fact, the same issue at then end of ._IO_setvbuf, which precedes
._IO_ungetc causes a fall through edge between  ._IO_setvbuf and
._IO_ungetc.

The long_branch trampoline that is the target of the call at 0x158d6b0 
is included below:

        000000000158d1f0 <00008345.long_branch_r2off._Unwind_Resume+0>:
        00008345.long_branch_r2off._Unwind_Resume+0():
         158d1f0:       f8 41 00 28     std     r2,40(r1)
         158d1f4:       3c 42 ff ff     addis   r2,r2,-1
         158d1f8:       38 42 00 08     addi    r2,r2,8
         158d1fc:       4b fd b0 f4     b       15682f0 <._Unwind_Resume>

It seems that non-returning status isn't properly being computed for 
functions. When I dump dyninst parseAPI functions and print their 
return status, I find that both _Unwind_Resume is listed as returning
and the long branch trampoline at 0x158d1f0 that goes to ._Unwind_Resume 
is also listed as returning as well. 

function targ158d1f0 RETURN
  [158d1f0,158d200)
    DIRECT --> 15682f0

function _Unwind_Resume RETURN
  [15682f0,15683b4)
    COND_TAKEN --> 15683b8
    COND_NOT_TAKEN --> 15683b4
  [15683b4,15683b8)
    FALLTHROUGH --> 15683b8
  [15683b8,15683d8)
    CALL --> 1567970
    CALL_FT --> 15683d8
...

It seems that the ParseAPI::Function for _Unwind_Resume should be marked
as non-returning since the name is identified as a non-returning function.
If I understand the logic and how this should propagate, 
marking the parseAPI function for  _Unwind_Resume as non-returning should 
cause the long-branch trampoline that ends in a tail call to it to be seen as 
non-returning, 
which would then cause the call to the long-branch trampoline as having no 
CALL_FT edge.

While the branch trampolines may contribute to bad propagation of 
knowledge about non-returning functions on ppc, the fact that the 
ParseAPI::Function for _Unwind_Resume is not marked as non-returning
seems like a problem that transcends architecture.
--
John Mellor-Crummey             Professor
Dept of Computer Science        Rice University
email: joh...@rice.edu          phone: 713-348-5179

_______________________________________________
Dyninst-api mailing list
Dyninst-api@cs.wisc.edu
https://lists.cs.wisc.edu/mailman/listinfo/dyninst-api

Reply via email to