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       boolean unsupportedDwarf = false;
  40 
  41       if (libptr != null) { // Native frame
  42         try {
  43           dwarf = new DwarfParser(libptr);
  44           dwarf.processDwarf(rip);
  45           cfa = ((dwarf.getCFARegister() == AMD64ThreadContext.RBP) &&
  46                  !dwarf.isBPOffsetAvailable())
  47                     ? context.getRegisterAsAddress(AMD64ThreadContext.RBP)
  48                     : context.getRegisterAsAddress(dwarf.getCFARegister())
  49                              .addOffsetTo(dwarf.getCFAOffset());
  50         } catch (DebuggerException e) {
  51           if (dwarf != null) {
  52             // DWARF processing should succeed when the frame is native
  53             // but it might fail if CIE has language personality routine
  54             // and/or LSDA.
  55             dwarf = null;
  56             unsupportedDwarf = true;
  57           } else {
  58             throw e;
  59           }
  60         }
  61       }
  62 
  63       return (cfa == null) ? null
  64                            : new LinuxAMD64CFrame(dbg, cfa, rip, dwarf, !unsupportedDwarf);
  65    }
  66 
  67    private LinuxAMD64CFrame(LinuxDebugger dbg, Address cfa, Address rip, DwarfParser dwarf, boolean possibleNext) {
  68       super(dbg.getCDebugger());
  69       this.cfa = cfa;
  70       this.rip = rip;
  71       this.dbg = dbg;
  72       this.dwarf = dwarf;
  73       this.possibleNext = possibleNext;
  74    }
  75 
  76    // override base class impl to avoid ELF parsing
  77    public ClosestSymbol closestSymbolToPC() {
  78       // try native lookup in debugger.
  79       return dbg.lookup(dbg.getAddressValue(pc()));
  80    }
  81 
  82    public Address pc() {
  83       return rip;
  84    }
  85 
  86    public Address localVariableBase() {
  87       return cfa;
  88    }
  89 
  90    private Address getNextPC(boolean useDwarf) {
  91      try {
  92        long offs = useDwarf ? dwarf.getReturnAddressOffsetFromCFA()
  93                             : ADDRESS_SIZE;
  94        return cfa.getAddressAt(offs);
  95      } catch (UnmappedAddressException | UnalignedAddressException e) {
  96        return null;
  97      }
  98    }
  99 
 100    private boolean isValidFrame(Address nextCFA, ThreadContext context) {
 101      return (nextCFA != null) &&
 102              !nextCFA.lessThan(context.getRegisterAsAddress(AMD64ThreadContext.RSP));
 103    }
 104 
 105    private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context) {
 106      Address nextCFA;
 107 
 108      if (nextDwarf == null) { // Next frame is Java
 109        nextCFA = (dwarf == null) ? cfa.getAddressAt(0) // Current frame is Java (Use RBP)
 110                                  : cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()); // Current frame is Native
 111      } else { // Next frame is Native
 112        if (dwarf == null) { // Current frame is Java (Use RBP)
 113          nextCFA = cfa.getAddressAt(0);
 114        } else { // Current frame is Native
 115          int nextCFAReg = nextDwarf.getCFARegister();
 116          if (!dwarf.isBPOffsetAvailable() && // Use RBP as CFA
 117              (nextCFAReg == AMD64ThreadContext.RBP) &&
 118              (nextCFAReg != dwarf.getCFARegister())) {
 119            nextCFA = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
 120            if (nextCFA == null) {
 121              return null;
 122            }
 123            nextCFA = nextCFA.getAddressAt(0);
 124          } else {
 125            nextCFA = cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA());
 126          }
 127        }
 128        if (nextCFA != null) {
 129          nextCFA = nextCFA.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA());
 130        }
 131      }
 132 
 133      return isValidFrame(nextCFA, context) ? nextCFA : null;
 134    }
 135 
 136    @Override
 137    public CFrame sender(ThreadProxy thread) {
 138      if (!possibleNext) {
 139        return null;
 140      }
 141 
 142      ThreadContext context = thread.getContext();
 143 
 144      Address nextPC = getNextPC(dwarf != null);
 145      if (nextPC == null) {
 146        return null;
 147      }
 148 
 149      DwarfParser nextDwarf = null;
 150      boolean unsupportedDwarf = false;
 151      if ((dwarf != null) && dwarf.isIn(nextPC)) {
 152        nextDwarf = dwarf;
 153      } else {
 154        Address libptr = dbg.findLibPtrByAddress(nextPC);
 155        if (libptr != null) {
 156          try {
 157            nextDwarf = new DwarfParser(libptr);
 158          } catch (DebuggerException e) {
 159            // Bail out to Java frame
 160          }
 161        }
 162      }
 163 
 164      if (nextDwarf != null) {
 165        try {
 166          nextDwarf.processDwarf(nextPC);
 167        } catch (DebuggerException e) {
 168          // DWARF processing should succeed when the frame is native
 169          // but it might fail if CIE has language personality routine
 170          // and/or LSDA.
 171          nextDwarf = null;
 172          unsupportedDwarf = true;
 173        }
 174      }
 175 
 176      Address nextCFA = getNextCFA(nextDwarf, context);
 177      LinuxAMD64CFrame nextFrame = new LinuxAMD64CFrame(dbg, nextCFA, nextPC, nextDwarf, !unsupportedDwarf);
 178      return isValidFrame(nextCFA, context) ? nextFrame : null;
 179    }
 180 
 181    // package/class internals only
 182    private static final int ADDRESS_SIZE = 8;
 183    private Address rip;
 184    private Address cfa;
 185    private LinuxDebugger dbg;
 186    private DwarfParser dwarf;
 187    private boolean possibleNext;
 188 }