1 /*
   2  * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.debugger.linux.amd64;
  26 
  27 import sun.jvm.hotspot.debugger.*;
  28 import sun.jvm.hotspot.debugger.amd64.*;
  29 import sun.jvm.hotspot.debugger.linux.*;
  30 import sun.jvm.hotspot.debugger.cdbg.*;
  31 import sun.jvm.hotspot.debugger.cdbg.basic.*;
  32 
  33 final public class LinuxAMD64CFrame extends BasicCFrame {
  34 
  35    public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, Address rip, ThreadContext context) {
  36       Address libptr = dbg.findLibPtrByAddress(rip);
  37       Address cfa = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
  38       DwarfParser dwarf = null;
  39 
  40       if (libptr != null) { // Native frame
  41         try {
  42           dwarf = new DwarfParser(libptr);
  43           dwarf.processDwarf(rip);
  44           cfa = ((dwarf.getCFARegister() == AMD64ThreadContext.RBP) &&
  45                  !dwarf.isBPOffsetAvailable())
  46                     ? context.getRegisterAsAddress(AMD64ThreadContext.RBP)
  47                     : context.getRegisterAsAddress(dwarf.getCFARegister())
  48                              .addOffsetTo(dwarf.getCFAOffset());
  49         } catch (DebuggerException e) {
  50           // Bail out to Java frame case
  51         }
  52       }
  53 
  54       return (cfa == null) ? null
  55                            : new LinuxAMD64CFrame(dbg, cfa, rip, dwarf);
  56    }
  57 
  58    private LinuxAMD64CFrame(LinuxDebugger dbg, Address cfa, Address rip, DwarfParser dwarf) {
  59       super(dbg.getCDebugger());
  60       this.cfa = cfa;
  61       this.rip = rip;
  62       this.dbg = dbg;
  63       this.dwarf = dwarf;
  64    }
  65 
  66    // override base class impl to avoid ELF parsing
  67    public ClosestSymbol closestSymbolToPC() {
  68       // try native lookup in debugger.
  69       return dbg.lookup(dbg.getAddressValue(pc()));
  70    }
  71 
  72    public Address pc() {
  73       return rip;
  74    }
  75 
  76    public Address localVariableBase() {
  77       return cfa;
  78    }
  79 
  80    private Address getNextPC(boolean useDwarf) {
  81      try {
  82        long offs = useDwarf ? dwarf.getReturnAddressOffsetFromCFA()
  83                             : ADDRESS_SIZE;
  84        return cfa.getAddressAt(offs);
  85      } catch (UnmappedAddressException | UnalignedAddressException e) {
  86        return null;
  87      }
  88    }
  89 
  90    private boolean isValidFrame(Address nextCFA, ThreadContext context) {
  91      return (nextCFA != null) &&
  92              !nextCFA.lessThan(context.getRegisterAsAddress(AMD64ThreadContext.RSP));
  93    }
  94 
  95    private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context) {
  96      Address nextCFA;
  97 
  98      if (nextDwarf == null) { // Next frame is Java
  99        nextCFA = (dwarf == null) ? cfa.getAddressAt(0) // Current frame is Java (Use RBP)
 100                                  : cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()); // Current frame is Native
 101      } else { // Next frame is Native
 102        if (dwarf == null) { // Current frame is Java (Use RBP)
 103          nextCFA = cfa.getAddressAt(0);
 104        } else { // Current frame is Native
 105          int nextCFAReg = nextDwarf.getCFARegister();
 106          if (!dwarf.isBPOffsetAvailable() && // Use RBP as CFA
 107              (nextCFAReg == AMD64ThreadContext.RBP) &&
 108              (nextCFAReg != dwarf.getCFARegister())) {
 109            nextCFA = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
 110            if (nextCFA == null) {
 111              return null;
 112            }
 113            nextCFA = nextCFA.getAddressAt(0);
 114          } else {
 115            nextCFA = cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA());
 116          }
 117        }
 118        if (nextCFA != null) {
 119          nextCFA = nextCFA.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA());
 120        }
 121      }
 122 
 123      return isValidFrame(nextCFA, context) ? nextCFA : null;
 124    }
 125 
 126    private DwarfParser getNextDwarf(Address nextPC) {
 127      DwarfParser nextDwarf = null;
 128 
 129      if ((dwarf != null) && dwarf.isIn(nextPC)) {
 130        nextDwarf = dwarf;
 131      } else {
 132        Address libptr = dbg.findLibPtrByAddress(nextPC);
 133        if (libptr != null) {
 134          try {
 135            nextDwarf = new DwarfParser(libptr);
 136          } catch (DebuggerException e) {
 137            // Bail out to Java frame
 138          }
 139        }
 140      }
 141 
 142      if (nextDwarf != null) {
 143        nextDwarf.processDwarf(nextPC);
 144      }
 145 
 146      return nextDwarf;
 147    }
 148 
 149    @Override
 150    public CFrame sender(ThreadProxy thread) {
 151      ThreadContext context = thread.getContext();
 152 
 153      Address nextPC = getNextPC(dwarf != null);
 154      if (nextPC == null) {
 155        return null;
 156      }
 157 
 158      DwarfParser nextDwarf = getNextDwarf(nextPC);
 159      Address nextCFA = getNextCFA(nextDwarf, context);
 160      return isValidFrame(nextCFA, context) ? new LinuxAMD64CFrame(dbg, nextCFA, nextPC, nextDwarf)
 161                                            : null;
 162    }
 163 
 164    // package/class internals only
 165    private static final int ADDRESS_SIZE = 8;
 166    private Address rip;
 167    private Address cfa;
 168    private LinuxDebugger dbg;
 169    private DwarfParser dwarf;
 170 }