Bug in os::current_frame()/_get_previous_fp() or why parenthesis somtimes really matter
Volker Simonis
volker.simonis at gmail.com
Tue Feb 12 09:15:20 PST 2008
On 2/8/08, steve goldman <Steve.Goldman at sun.com> wrote:
> Volker Simonis wrote:
> > Hi,
> >
> > the implementation of "_get_previous_fp()" which is used by
> > "os::current_frame()" seems to be buggy on Solaris/x86_64.
> > "_get_previous_fp()" is implemented as assembler inline function in
> > "hotspot/src/os_cpu/solaris_amd64/vm/solaris_amd64.il" as follows:
> >
> > // Get the frame pointer from previous frame.
> > .inline _get_previous_fp,0
> > movq %rbp, %rax
> > movq %rax, %rax
> > .end
>
> This seems to be correct. Since no new frame pointer is created, the
> value of rbp is the "fp" of the previous frame, not the previous value
> of rbp. Certainly the definition of previous is ambiguous here but the
> .il file on the 32bit side does say it returns the caller's fp. The
> comment in the 64bit version is more ambiguous.
>
Ok, I agree with you on this. But what is the line:
movq %rax, %rax
good for. Isn't it just a nop?
> >
> > Although I'm not an x86 expert I think that after locking at
> > "generate_get_previous_fp()" in
> > hotspot/src/cpu/amd64/vm/stubGenerator_amd64.cpp which generated a
> > stub that was used previously in "os::current_frame()" to query the
> > frame pointer and looks as follows:
> >
> > address generate_get_previous_fp() {
> > StubCodeMark mark(this, "StubRoutines", "get_previous_fp");
> > const Address old_fp(rbp, 0);
> > const Address older_fp(rax, 0);
> > address start = __ pc();
> >
> > __ enter();
> > __ movq(rax, old_fp); // callers fp
> > __ movq(rax, older_fp); // the frame for ps()
> > __ popq(rbp);
> > __ ret(0);
> >
>
> this code is incorrect. The frame pointer that is desired in
> os::current_frame is the frame pointer that exists for itself. The frame
> it constructs uses "previous fp" (it's own fp) and current sp. That is a
> sensible frame.
>
> The incorrect code would create a frame whose fp was from the call of
> os::current_frame and whose sp was the sp for os::current_frame which is
> not a proper description of the frame.
>
> For what this particular frame is used for it is completely harmless
> since we won't be doing anything with sp. The truth is almost any value
> of fp that is returned that is actually a frame pointer is usable since
> the stack walking the caller's are going to do is dependent on fp being
> a valid frame pointer and that's about it.
>
> > return start;
> > }
> >
> > the correct implementation of "_get_previous_fp()" should read as follows:
> >
> > // Get the frame pointer from previous frame.
> > .inline _get_previous_fp,0
> > movq (%rbp), %rax
> > movq (%rax), %rax
> > .end
> >
> > The funny thing is that although the current implementation is clearly
> > wrong, it still worked in the debug build. I think this has to do with
> > how CC treats inline assembly under optimization. In the debug build,
> > it generated the following code:
> >
> > 185. frame os::current_frame() {
> > <Function: os::current_frame()>
> > [ 185] 780: pushq %rbp
> > [ 185] 781: movq %rsp,%rbp
> > [ 185] 784: subq $0xc0,%rsp
> > [ 185] 78b: movq %r12,-0xb8(%rbp)
> > [ 185] 792: movq %rdi,-8(%rbp)
> > 186. intptr_t* fp = _get_previous_fp();
> > [ 186] 796: movl $0,%eax
> > [ 186] 79b: movq %rbp,%rax
> > [ 186] 79e: movq %rax,%rax
> > [ 186] 7a1: movq %rax,%r8
> > [ 186] 7a4: movq %r8,-0x40(%rbp)
> > 187. frame myframe((intptr_t*)os::current_stack_pointer(),
> >
> > This code returned the wrong, but still a valid fp which is just good
> > enough for "os::current_frame()". However if compiled with '-xO1' as
> > done in the opt build, CC generates the following code:
> >
> > <Function: os::current_frame()>
> > [ ?] 0: pushq %rbp
> > [ ?] 1: movq %rsp,%rbp
> > [ ?] 4: subq $0x60,%rsp
> > [ ?] 8: pushq %r12
> > [ ?] a: pushq %r13
> > [ ?] c: movq %rdi,%r12
> > [ ?] f: movq %rax,%r13
> > [ ?] 12: call os::current_stack_pointer() [
> > 0x17, .+5 ]
> >
> > You can see that it elliminated the code from "_get_previous_fp()" and
> > just used the uninitialized value of %rax as frame pointer, which is
> > usually wrong.
>
> I think this is a CC bug. How is it able to eliminate the read of rbp
> and simply give us a random value in rax? Maybe the .il file needs some
> annotation so that the compiler doesn't decide that rbp is
> "un-initialized" and so any old value in rax is as good as another.
>
I also agree on this one - seems to be a compiler bug. I found Alfred
Huang's weblog at
http://blogs.sun.com/alblog/entry/on_studio_and_gcc_style where he
explains that inline assembler templates get heavily optimzed by the
backend ('ube' on x86). The solution is to use the compiler option
'-Wu,-no_a2lf' for C or "-Qoption ube -no_a2lf" for C++ to disable
inline assembly optimization.
This can be easily added to
'hotspot/build/solaris/makefiles/amd64.make' which already contains
special opt-flags for 'os_solaris_amd64.o' anyway:
OPT_CFLAGS/os_solaris_amd64.o = -xO1 -Qoption ube -no_a2lf
> >
> > Notice that this problem exists in the latest JDK 6 build as well as
> > in OpenJDK (Java 7). It probably hasn't showed up until now because
> > "os::current_frame()" is only called in the following three places:
> >
> > 1.oop::register_oop() in /hotspot/src/share/vm/oops/oopsHierarchy.cpp
> > 2.ps() in hotspot/src/share/vm/utilities/debug.cpp
> > 3. VMError::report(outputStream* st) in
> > /hotspot/src/share/vm/utilities/vmError.cpp
> >
> > The first one is usually disabled, the second one is only called by
> > developers in the debugger (and probably for a long time nobody called
> > "ps()" in the debugger for an opt-build) and the last one is during
> > the creation of an hs_error file where probably nobody noticed the
> > problem.
> >
> > Regards,
> > Volker
>
>
> --
> Steve
>
More information about the hotspot-dev
mailing list