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