/* * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.tele.debug.dump; import static com.oracle.max.elf.ELFProgramHeaderTable.*; import java.io.*; import com.oracle.max.elf.*; import com.sun.max.program.*; import com.sun.max.tele.*; import com.sun.max.tele.channel.*; import com.sun.max.tele.channel.iostream.*; import com.sun.max.tele.debug.*; import com.sun.max.tele.heap.*; import com.sun.max.tele.util.*; import com.sun.max.unsafe.*; import com.sun.max.vm.hosted.*; public class ELFDumpTeleChannelProtocolAdaptor extends TeleChannelDataIOProtocolAdaptor implements TeleChannelProtocol { protected int tlaSize; public boolean bigEndian; protected RandomAccessFile dumpRaf; protected ELFHeader header; protected ELFProgramHeaderTable programHeaderTable; protected ELFSymbolLookup symbolLookup; protected MaxVM teleVM; protected static final String HEAP_SYMBOL_NAME = "theHeap"; // defined in image.c, holds the base address of the boot heap protected ELFDumpTeleChannelProtocolAdaptor(MaxVM teleVM, File vm, File dump) { this.teleVM = teleVM; try { // We actually do need the tele library because we use it to access the OS-specific structs // that are embedded in the NOTE sections of the dump file. Prototype.loadLibrary(TeleVM.TELE_LIBRARY_NAME); dumpRaf = new RandomAccessFile(dump, "r"); this.header = ELFLoader.readELFHeader(dumpRaf); this.programHeaderTable = ELFLoader.readPHT(dumpRaf, header); // This is not needed currently as we cannot look up symbols from shared libraries. //symbolLookup = new ELFSymbolLookup(new File(vm.getParent(), "libjvm.so")); } catch (Exception ex) { TeleError.unexpected("failed to open dump file: " + dump, ex); } } @Override public boolean initialize(int tlaSize, boolean bigEndian) { this.tlaSize = tlaSize; this.bigEndian = bigEndian; return true; } protected static class NoteEntryHandler { /** * OS-specific processing a NOTE entry. * @param type type of NOTE entry * @param name name of NOTE entry * @param desc byte array of NOTE contents */ protected void processNoteEntry(int type, String name, byte[] desc) { } } protected void processNoteSection(NoteEntryHandler entryHandler) { ELFProgramHeaderTable.Entry64 noteSectionEntry = null; // if there are 2 NOTE entries we want the second for (ELFProgramHeaderTable.Entry entry : programHeaderTable.entries) { if (entry.p_type == PT_NOTE) { if (noteSectionEntry != null) { noteSectionEntry = (ELFProgramHeaderTable.Entry64) entry; break; } noteSectionEntry = (ELFProgramHeaderTable.Entry64) entry; } } try { dumpRaf.seek(noteSectionEntry.p_offset); final ELFDataInputStream dis = new ELFDataInputStream(header, dumpRaf); final long size = noteSectionEntry.p_filesz; long readLength = 0; while (readLength < size) { int namesz = dis.read_Elf64_Word(); final int descsz = dis.read_Elf64_Word(); final int type = dis.read_Elf64_Word(); final String name = readNoteString(dis, namesz); readLength += 12 + namesz; while (namesz % 8 != 0) { dis.read_Elf32_byte(); readLength++; namesz++; } final byte[] desc = readNoteDesc(dis, descsz); readLength += descsz; entryHandler.processNoteEntry(type, name, desc); } } catch (IOException ex) { TeleError.unexpected("error reading dump file note section", ex); } } /** * Read a string from a NOTE entry with length length. The returned string is of size length - 1 as java strings are * not null terminated * * @param length */ private String readNoteString(ELFDataInputStream dis, int length) throws IOException { byte[] arr = new byte[length - 1]; for (int i = 0; i < arr.length; i++) { arr[i] = dis.read_Elf64_byte(); } dis.read_Elf64_byte(); return new String(arr); } private byte[] readNoteDesc(ELFDataInputStream dis, int length) throws IOException { byte[] arr = new byte[length]; for (int i = 0; i < length; i++) { arr[i] = dis.read_Elf64_byte(); } return arr; } protected ELFProgramHeaderTable.Entry64 findAddress(long addr) { final Address address = Address.fromLong(addr); for (ELFProgramHeaderTable.Entry entry : programHeaderTable.entries) { ELFProgramHeaderTable.Entry64 entry64 = (ELFProgramHeaderTable.Entry64) entry; if (entry64.p_type == PT_LOAD && entry64.p_filesz != 0) { final Address end = Address.fromLong(entry64.p_vaddr).plus(entry64.p_memsz); if (address.greaterEqual(Address.fromLong(entry64.p_vaddr)) && address.lessThan(end)) { return entry64; } } } return null; } /** * A workaround until this can be done properly by reading it from memory. * We basically look for a segment that is the same size as the sum of the code and heap size * from the {@link BootImage#header}. * @return the base of the boot heap */ private long getBootHeapStartHack() { final BootImage.Header header = teleVM.bootImage().header; final int bootHeapSize = header.codeSize + header.heapSize; for (ELFProgramHeaderTable.Entry entry : programHeaderTable.entries) { if (entry.p_type == PT_LOAD) { ELFProgramHeaderTable.Entry64 entry64 = (ELFProgramHeaderTable.Entry64) entry; if (entry64.p_filesz > 0 && entry64.p_filesz == bootHeapSize) { return entry64.p_vaddr; } } } TeleError.unexpected("failed to find the start of the boot heap"); return 0; } @Override public long getBootHeapStart() { // Check if an option has specified the heap address. final long heapAddress = VmHeapAccess.heapAddressOption(); if (heapAddress != 0) { return heapAddress; } if (true) { return getBootHeapStartHack(); } else { // This is the clean way to do it if you know how to get the absolute address of symbols loaded from shared libraries, // which is not trivial or documented. final long theHeapAddress = getBootHeapStartSymbolAddress(); ELFProgramHeaderTable.Entry64 entry64 = findAddress(theHeapAddress); try { dumpRaf.seek(entry64.p_offset + (theHeapAddress - entry64.p_vaddr)); ELFDataInputStream ds = new ELFDataInputStream(header, dumpRaf); return ds.read_Elf64_Addr(); } catch (Throwable ex) { TeleError.unexpected("failed to get boot heap address", ex); return 0; } } } /** * Return the absolute address of the symbol in image.c whose vcalue is the base address of the boot heap. * Since this is a static, the name may be mangled (e.g. Solaris). */ protected long getBootHeapStartSymbolAddress() { return symbolLookup.lookupSymbolValue(HEAP_SYMBOL_NAME).longValue(); } @Override public int writeBytes(long dst, byte[] src, int srcOffset, int length) { Trace.line(2, "WARNING: Inspector trying to write to " + Long.toHexString(dst)); return length; } @Override public long create(String pathName, String[] commandLineArguments) { inappropriate("create"); return -1; } @Override public boolean attach(int id) { return true; } @Override public boolean detach() { return true; } @Override public int maxByteBufferSize() { return Integer.MAX_VALUE; } @Override public int readBytes(long src, byte[] dst, int dstOffset, int length) { final ELFProgramHeaderTable.Entry64 entry64 = findAddress(src); if (entry64 == null) { return 0; } try { dumpRaf.seek(entry64.p_offset + (src - entry64.p_vaddr)); return dumpRaf.read(dst, dstOffset, length); } catch (IOException ex) { return 0; } } @Override public boolean readRegisters(long threadId, byte[] integerRegisters, int integerRegistersSize, byte[] floatingPointRegisters, int floatingPointRegistersSize, byte[] stateRegisters, int stateRegistersSize) { unimplemented("readRegisters"); return false; } @Override public int gatherThreads(long tlaList) { inappropriate("gatherThreads"); return 0; } @Override public int readThreads(int size, byte[] gatherThreadsData) { inappropriate("readThreads"); return 0; } @Override public boolean setInstructionPointer(long threadId, long ip) { inappropriate("setInstructionPointer"); return false; } @Override public boolean singleStep(long threadId) { inappropriate("setInstructionPointer"); return false; } @Override public boolean resumeAll() { inappropriate("resumeAll"); return false; } @Override public boolean suspendAll() { inappropriate("suspendAll"); return false; } @Override public boolean resume(long threadId) { inappropriate("resume"); return false; } @Override public boolean suspend(long threadId) { inappropriate("suspend"); return false; } @Override public int waitUntilStoppedAsInt() { inappropriate("waitUntilStoppedAsInt"); return 0; } @Override public boolean kill() { return true; } @Override public boolean activateWatchpoint(long start, long size, boolean after, boolean read, boolean write, boolean exec) { inappropriate("activateWatchpoint"); return false; } @Override public boolean deactivateWatchpoint(long start, long size) { inappropriate("deactivateWatchpoint"); return false; } @Override public long readWatchpointAddress() { inappropriate("readWatchpointAddress"); return 0; } @Override public int readWatchpointAccessCode() { inappropriate("readWatchpointAccessCode"); return 0; } @Override public int setTransportDebugLevel(int level) { return 0; } @Override public ProcessState waitUntilStopped() { inappropriate("waitUntilStoppedAsInt"); return null; } protected static void inappropriate(String methodName) { TeleError.unexpected("method: " + methodName + " should not be called in dump mode"); } protected static void unimplemented(String methodName) { TeleError.unimplemented("method: " + methodName); } }