/* * Copyright (c) 2007, 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.vm.hosted; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.VMConfiguration.*; import java.io.*; import java.lang.reflect.*; import java.nio.*; import java.nio.channels.FileChannel.MapMode; import java.util.*; import com.sun.max.config.*; import com.sun.max.lang.*; import com.sun.max.platform.*; import com.sun.max.program.*; import com.sun.max.unsafe.*; import com.sun.max.util.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.compiler.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.hosted.BootImage.StringInfo.Key; import com.sun.max.vm.tele.*; import com.sun.max.vm.thread.*; import com.sun.max.vm.type.*; /** * The <i>boot image</i> contains the heap objects that represent a Maxine VM, including compiled code. * This class is a utility for both writing and reading a boot image. * * The layout of the boot image as a pseudo-C struct is: * <pre> * * struct BootImage { * struct Header header; // see declaration of image_Header in Native/substrate/image.h * struct StringInfo string_info; // see declaration of image_StringInfo in Native/substrate/image.h * byte[header.relocationDataSize] relocation_data; * byte[] pad; // padding such that next field will be aligned on an OS page-size address * byte[header.heapSize]; // header.heapSize is a multiple of page-size * byte[header.codeSize]; // header.codeSize is a multiple of page-size * struct Trailer trailer; // see declaration of image_Trailer in Native/substrate/image.h * } * * </pre> * * The 'pad' member in the image exists so that in-memory images can be supported. That is, if the * VM obtains the image from a memory location as opposed to loading it from a file, then the * page-alignment requirement for the heap and code sections in the image will be satisfied if the * image itself starts at a page-aligned address. * */ public class BootImage { /** * A special identifier for Maxine boot image files. {@code 0xCAFE4DAD} */ public static final int IDENTIFICATION = 0xcafe4dad; /** * A version number of the boot image file layout, checked against IMAGE_FORMAT_VERSION in Native/substrate/image.c . */ public static final int BOOT_IMAGE_FORMAT_VERSION = 2; /** * A field section in a boot image is described by the {@code public final} and {@code final} * fields in a {@code FieldSection} subclass. That is, every {@code public final} field of the * specified {@linkplain #fieldType() type} declared in a subclass of {@code FieldSection} * describes a section entry. */ public abstract static class FieldSection { private final int offset; protected FieldSection(int offset) { this.offset = offset; } static Field[] fields(Class holder, final Class fieldType) { Field[] declaredFields = holder.getDeclaredFields(); ArrayList<Field> result = new ArrayList<Field>(declaredFields.length); for (Field declaredField : declaredFields) { final int flags = Actor.ACC_FINAL | Actor.ACC_PUBLIC; if ((declaredField.getModifiers() & flags) == flags && declaredField.getType().equals(fieldType)) { result.add(declaredField); } } return result.toArray(new Field[result.size()]); } /** * Gets the fields representing the entries of this section. */ public Field[] fields() { return fields(getClass(), fieldType()); } /** * Gets the type of the fields in this class that describe section entries. */ public abstract Class<?> fieldType(); /** * Gets the size (in bytes) of this section within the boot image. */ public abstract int size(); /** * Gets the offset (in bytes) of this section within the boot image. */ public final int offset() { return offset; } /** * Writes the data in this section to a given stream. */ public abstract void write(OutputStream outputStream, Endianness endian) throws IOException; } /** * A section composed of {@code int} entries. */ public abstract static class IntSection extends FieldSection { private final Endianness endianness; public Endianness endianness() { return endianness; } protected IntSection(Endianness endianness, int offset) { super(offset); this.endianness = endianness; } @Override public int size() { return fields().length * Ints.SIZE; } @Override public void write(OutputStream outputStream, Endianness endian) throws IOException { for (Field field : fields()) { try { endianness.writeInt(outputStream, field.getInt(this)); } catch (IllegalAccessException illegalAccessException) { throw ProgramError.unexpected(); } } } @Override public Class<?> fieldType() { return int.class; } } static int getCriticalEntryPoint(ClassMethodActor classMethodActor, CallEntryPoint callEntryPoint) { return classMethodActor.currentTargetMethod().getEntryPoint(callEntryPoint).toInt(); } /** * The header section in a boot image. * * <b>ATTENTION: this must match 'image_Header' in "com.oracle.max.vm.native/substrate/image.h".</b> */ public static final class Header extends IntSection { public final int isBigEndian; public final int identification; public final int bootImageFormatVersion; public final int randomID; public final int wordSize; public final int cacheAlignment; public final int pageSize; public final int yellowZonePages; public final int redZonePages; public final int vmRunMethodOffset; public final int vmThreadAddMethodOffset; public final int vmThreadRunMethodOffset; public final int vmThreadAttachMethodOffset; public final int vmThreadDetachMethodOffset; public final int classRegistryOffset; public final int stringInfoSize; public final int relocationDataSize; public final int heapSize; public final int codeSize; public final int dynamicHeapRegionsArrayFieldOffset; /** * Instruct the boot image loader to reserve a range of contiguous virtual space of specified size. */ public final int reservedVirtualSpaceSize; /** * Offset to the variable that will hold the address of the virtual space reserved by the boot image loader at boot-load time. * * @see Heap#reservedVirtualSpace */ public final int reservedVirtualSpaceFieldOffset; /** * Instruct the boot image loader to memory map the boot heap region at a specific location: * if 0, just map it anywhere; if 1 (respectively, 2), map it at the beginning (respectively, end),of the reserved virtual space. */ public final int bootRegionMappingConstraint; /** * @see VmThreadMap#ACTIVE */ public final int tlaListHeadOffset; /** * @see MaxineVM#exitCode */ public final int exitCodeOffset; /** * The size of a TLA. */ public final int tlaSize; /** * The indexes of the VM thread locals accessed directly by C code. */ public final int SAFEPOINT_LATCH; public final int ETLA; public final int DTLA; public final int TTLA; public final int NATIVE_THREAD_LOCALS; public final int FORWARD_LINK; public final int BACKWARD_LINK; public final int ID; public final int JNI_ENV; public final int LAST_JAVA_FRAME_ANCHOR; public final int TRAP_NUMBER; public final int TRAP_INSTRUCTION_POINTER; public final int TRAP_FAULT_ADDRESS; public final int TRAP_LATCH_REGISTER; public final int STACK_REFERENCE_MAP; public final int STACK_REFERENCE_MAP_SIZE; public WordWidth wordWidth() { return WordWidth.fromInt(wordSize * 8); } private Header(DataInputStream dataInputStream) throws IOException, BootImageException { super(dataInputStream.readInt() == 0 ? Endianness.LITTLE : Endianness.BIG, 0); final Endianness endian = endianness(); isBigEndian = endian.ordinal(); identification = endian.readInt(dataInputStream); bootImageFormatVersion = endian.readInt(dataInputStream); randomID = endian.readInt(dataInputStream); wordSize = endian.readInt(dataInputStream); cacheAlignment = endian.readInt(dataInputStream); pageSize = endian.readInt(dataInputStream); yellowZonePages = endian.readInt(dataInputStream); redZonePages = endian.readInt(dataInputStream); vmRunMethodOffset = endian.readInt(dataInputStream); vmThreadAddMethodOffset = endian.readInt(dataInputStream); vmThreadRunMethodOffset = endian.readInt(dataInputStream); vmThreadAttachMethodOffset = endian.readInt(dataInputStream); vmThreadDetachMethodOffset = endian.readInt(dataInputStream); classRegistryOffset = endian.readInt(dataInputStream); stringInfoSize = endian.readInt(dataInputStream); relocationDataSize = endian.readInt(dataInputStream); heapSize = endian.readInt(dataInputStream); codeSize = endian.readInt(dataInputStream); dynamicHeapRegionsArrayFieldOffset = endian.readInt(dataInputStream); reservedVirtualSpaceSize = endian.readInt(dataInputStream); reservedVirtualSpaceFieldOffset = endian.readInt(dataInputStream); bootRegionMappingConstraint = endian.readInt(dataInputStream); tlaListHeadOffset = endian.readInt(dataInputStream); exitCodeOffset = endian.readInt(dataInputStream); tlaSize = endian.readInt(dataInputStream); SAFEPOINT_LATCH = endian.readInt(dataInputStream); ETLA = endian.readInt(dataInputStream); DTLA = endian.readInt(dataInputStream); TTLA = endian.readInt(dataInputStream); NATIVE_THREAD_LOCALS = endian.readInt(dataInputStream); FORWARD_LINK = endian.readInt(dataInputStream); BACKWARD_LINK = endian.readInt(dataInputStream); ID = endian.readInt(dataInputStream); JNI_ENV = endian.readInt(dataInputStream); LAST_JAVA_FRAME_ANCHOR = endian.readInt(dataInputStream); TRAP_NUMBER = endian.readInt(dataInputStream); TRAP_INSTRUCTION_POINTER = endian.readInt(dataInputStream); TRAP_FAULT_ADDRESS = endian.readInt(dataInputStream); TRAP_LATCH_REGISTER = endian.readInt(dataInputStream); STACK_REFERENCE_MAP = endian.readInt(dataInputStream); STACK_REFERENCE_MAP_SIZE = endian.readInt(dataInputStream); } private int staticFieldPointerOffset(DataPrototype dataPrototype, Class javaClass, String staticFieldName) { final Pointer staticTupleOrigin = dataPrototype.objectToOrigin(ClassActor.fromJava(javaClass).staticTuple()); final FieldActor fieldActor = ClassActor.fromJava(javaClass).findLocalStaticFieldActor(SymbolTable.makeSymbol(staticFieldName)); return staticTupleOrigin.toInt() + fieldActor.offset(); } private Header(DataPrototype dataPrototype, int stringInfoSize) { super(platform().endianness(), 0); final VMConfiguration vmConfiguration = vmConfig(); isBigEndian = endianness() == Endianness.LITTLE ? 0 : 0xffffffff; identification = IDENTIFICATION; bootImageFormatVersion = BOOT_IMAGE_FORMAT_VERSION; randomID = UUID.randomUUID().hashCode(); wordSize = platform().wordWidth().numberOfBytes; cacheAlignment = platform().dataModel.cacheAlignment; pageSize = platform().pageSize; yellowZonePages = VmThread.YELLOW_ZONE_PAGES; redZonePages = VmThread.RED_ZONE_PAGES; vmRunMethodOffset = getCriticalEntryPoint(ClassRegistry.MaxineVM_run, CallEntryPoint.C_ENTRY_POINT); vmThreadAddMethodOffset = getCriticalEntryPoint(ClassRegistry.VmThread_add, CallEntryPoint.C_ENTRY_POINT); vmThreadRunMethodOffset = getCriticalEntryPoint(ClassRegistry.VmThread_run, CallEntryPoint.C_ENTRY_POINT); vmThreadAttachMethodOffset = getCriticalEntryPoint(ClassRegistry.VmThread_attach, CallEntryPoint.C_ENTRY_POINT); vmThreadDetachMethodOffset = getCriticalEntryPoint(ClassRegistry.VmThread_detach, CallEntryPoint.C_ENTRY_POINT); classRegistryOffset = dataPrototype.objectToOrigin(ClassRegistry.VM_CLASS_REGISTRY).toInt(); this.stringInfoSize = stringInfoSize; relocationDataSize = dataPrototype.relocationData().length; heapSize = dataPrototype.heapData().length; codeSize = dataPrototype.codeData().length; dynamicHeapRegionsArrayFieldOffset = staticFieldPointerOffset(dataPrototype, InspectableHeapInfo.class, "dynamicHeapMemoryRegions"); reservedVirtualSpaceSize = vmConfiguration.heapScheme().reservedVirtualSpaceKB(); reservedVirtualSpaceFieldOffset = staticFieldPointerOffset(dataPrototype, Heap.class, "reservedVirtualSpace"); bootRegionMappingConstraint = vmConfiguration.heapScheme().bootRegionMappingConstraint().ordinal(); tlaListHeadOffset = dataPrototype.objectToOrigin(VmThreadMap.ACTIVE).toInt() + ClassActor.fromJava(VmThreadMap.class).findLocalInstanceFieldActor("tlaListHead").offset(); exitCodeOffset = staticFieldPointerOffset(dataPrototype, MaxineVM.class, "exitCode"); tlaSize = VmThreadLocal.tlaSize().toInt(); SAFEPOINT_LATCH = VmThreadLocal.SAFEPOINT_LATCH.index; ETLA = VmThreadLocal.ETLA.index; DTLA = VmThreadLocal.DTLA.index; TTLA = VmThreadLocal.TTLA.index; NATIVE_THREAD_LOCALS = VmThreadLocal.NATIVE_THREAD_LOCALS.index; FORWARD_LINK = VmThreadLocal.FORWARD_LINK.index; BACKWARD_LINK = VmThreadLocal.BACKWARD_LINK.index; ID = VmThreadLocal.ID.index; JNI_ENV = VmThreadLocal.JNI_ENV.index; LAST_JAVA_FRAME_ANCHOR = VmThreadLocal.LAST_JAVA_FRAME_ANCHOR.index; TRAP_NUMBER = VmThreadLocal.TRAP_NUMBER.index; TRAP_INSTRUCTION_POINTER = VmThreadLocal.TRAP_INSTRUCTION_POINTER.index; TRAP_FAULT_ADDRESS = VmThreadLocal.TRAP_FAULT_ADDRESS.index; TRAP_LATCH_REGISTER = VmThreadLocal.TRAP_LATCH_REGISTER.index; STACK_REFERENCE_MAP = VmThreadLocal.STACK_REFERENCE_MAP.index; STACK_REFERENCE_MAP_SIZE = VmThreadLocal.STACK_REFERENCE_MAP_SIZE.index; } public void check() throws BootImageException { BootImageException.check(identification == IDENTIFICATION, "not a MaxineVM VM boot image file, wrong identification: " + identification); BootImageException.check(bootImageFormatVersion == BOOT_IMAGE_FORMAT_VERSION, "wrong version: " + bootImageFormatVersion); BootImageException.check(wordSize == 4 || wordSize == 8, "illegal word size: " + wordSize); BootImageException.check(cacheAlignment > 4 && Ints.isPowerOfTwoOrZero(cacheAlignment), "implausible alignment size: " + cacheAlignment); BootImageException.check(pageSize >= Longs.K && pageSize % Longs.K == 0, "implausible page size: " + pageSize); BootImageException.check(!(bootRegionMappingConstraint > 0 && reservedVirtualSpaceSize == 0), "invalid boot region mapping constraint"); } @Override public int size() { return fields().length * Ints.SIZE; } @Override public void write(OutputStream outputStream, Endianness endian) throws IOException { for (Field field : fields()) { try { endianness().writeInt(outputStream, field.getInt(this)); } catch (IllegalAccessException illegalAccessException) { throw ProgramError.unexpected(); } } } } /** * The string info section in a boot image. * * <b>ATTENTION: this must match 'image_StringInfo' in "com.oracle.max.vm.native/substrate/image.h".</b> */ public static final class StringInfo extends FieldSection { public enum Key { BUILD(BuildLevel.class), CPU(CPU.class), ISA(ISA.class), OS(OS.class), NSIG(String.class), REFERENCE(BootImagePackage.class), LAYOUT(BootImagePackage.class), HEAP(BootImagePackage.class), MONITOR(BootImagePackage.class), RUN(BootImagePackage.class), OPT(String.class), BASELINE(String.class); Key(Class valueType) { this.valueType = valueType; } public final Class valueType; public static Key toKey(String name) { try { return valueOf(name); } catch (IllegalArgumentException e) { return null; } } } public final Map<String, String> values = new TreeMap<String, String>(); public BuildLevel buildLevel() { return BuildLevel.valueOf(values.get(Key.BUILD.name())); } public CPU cpu() { return CPU.valueOf(values.get(Key.CPU.name())); } public ISA isa() { return ISA.valueOf(values.get(Key.ISA.name())); } public OS os() { return OS.valueOf(values.get(Key.OS.name())); } public int nsig() { return Integer.valueOf(values.get(Key.NSIG.name())); } public BootImagePackage bootImagePackage(Key key) { return BootImagePackage.fromName(values.get(key.name())); } private StringInfo(InputStream inputStream, int offset, Endianness endian) throws IOException, Utf8Exception { super(offset); int count = endian.readInt(inputStream); while (count-- != 0) { String name = Utf8.readString(inputStream); String value = Utf8.readString(inputStream); put(name, value); Key key = Key.toKey(name); if (key == null) { // This is a system property used by one of the schemes. // Set it now so that it is available to the scheme when // it is instantiated. System.setProperty(name, value); } } } private void put(Key key, Object value) { put(key.name(), value); } private void put(String key, Object value) { String oldValue = values.put(key, value.toString()); assert oldValue == null : "Multiple values for " + key + ": " + oldValue + ", " + value; } private StringInfo(VMConfiguration vmConfig, int offset) { super(offset); put(Key.BUILD, vmConfig.buildLevel); put(Key.CPU, platform().cpu); put(Key.ISA, platform().isa); put(Key.OS, platform().os); put(Key.NSIG, platform().nsig); put(Key.REFERENCE, vmConfig.referencePackage); put(Key.LAYOUT, vmConfig.layoutPackage); put(Key.HEAP, vmConfig.heapPackage); put(Key.MONITOR, vmConfig.monitorPackage); put(Key.RUN, vmConfig.runPackage); for (VMScheme scheme : vmConfig.vmSchemes()) { Properties props = scheme.properties(); if (props != null) { for (Object k : props.keySet()) { String key = (String) k; String value = props.getProperty(key); put(key, value); } } } Properties props = MaxineVM.vm().compilationBroker.properties(); if (props != null) { for (Object k : props.keySet()) { String key = (String) k; String value = props.getProperty(key); put(key, value); } } } public void check() throws BootImageException { for (Map.Entry<String, String> e : values.entrySet()) { Key key = Key.toKey(e.getKey()); if (key != null) { String value = e.getValue(); if (Enum.class.isAssignableFrom(key.valueType)) { Enum[] enumConstants = (Enum[]) key.valueType.getEnumConstants(); boolean match = false; if (enumConstants != null) { for (Enum c : enumConstants) { if (c.name().equalsIgnoreCase(value)) { match = true; break; } } } BootImageException.check(match, "No " + key.valueType.getName() + " constant matches " + value); } else { assert key.valueType == BootImagePackage.class || key == Key.NSIG : e; } } else { // must be a system property for one of the schemes } } } @Override public int size() { int size = 4; for (Map.Entry<String, String> e : values.entrySet()) { size += Utf8.utf8Length(e.getKey()) + 1; size += Utf8.utf8Length(e.getValue()) + 1; } return size; } @Override public void write(OutputStream outputStream, Endianness endian) throws IOException { endian.writeInt(outputStream, values.size()); for (Map.Entry<String, String> e : values.entrySet()) { Utf8.writeString(outputStream, e.getKey()); Utf8.writeString(outputStream, e.getValue()); } } @Override public Class<?> fieldType() { return String.class; } } /** * The trailer section in a boot image. * * <b>ATTENTION: this must match 'image_Trailer' in "com.oracle.max.vm.native/substrate/image.h".</b> */ public static final class Trailer extends IntSection { public final int randomID; public final int bootImageFormatVersion; public final int identification; private Trailer(Header header, InputStream inputStream, int offset) throws IOException { super(header.endianness(), offset); randomID = endianness().readInt(inputStream); bootImageFormatVersion = endianness().readInt(inputStream); identification = endianness().readInt(inputStream); } private Trailer(Header header, int offset) { super(header.endianness(), offset); randomID = header.randomID; bootImageFormatVersion = header.bootImageFormatVersion; identification = header.identification; } public void check(Header header) throws BootImageException { BootImageException.check(identification == header.identification, "inconsistent trailer identififcation"); BootImageException.check(bootImageFormatVersion == header.bootImageFormatVersion, "inconsistent trailer version"); BootImageException.check(randomID == header.randomID, "inconsistent trailer random ID"); } } /** * Gets the class method actor for the first method with the specified name found * while traversing all the class method actors declared by a given class and * its super classes. * @param n the method name * @param javaClass the class in which to start the search for a method named "run" * * @return the found method or null */ public static ClassMethodActor getMethodActorFor(String n, Class<?> javaClass) { Utf8Constant name = SymbolTable.makeSymbol(n); final ClassMethodActor runMethodActor = ClassActor.fromJava(javaClass).findLocalClassMethodActor(name, null); if (runMethodActor != null) { return runMethodActor; } return getMethodActorFor(n, javaClass.getSuperclass()); } public final Header header; public final StringInfo stringInfo; public final byte[] relocationData; public final byte[] padding; public final Trailer trailer; public final VMConfiguration vmConfiguration; private ByteBuffer heap; private ByteBuffer code; private ByteBuffer heapAndCode; public final File imageFile; /** * Creates a BootImage object representing the information in a given boot image file. */ public BootImage(File file) throws BootImageException { this.imageFile = file; try { final FileInputStream fileInputStream = new FileInputStream(file); try { DataInputStream dataInputStream = new DataInputStream(fileInputStream); header = new Header(dataInputStream); header.check(); stringInfo = new StringInfo(dataInputStream, header.size(), header.endianness()); stringInfo.check(); BootImageException.check(header.stringInfoSize == stringInfo.size(), "inconsistent string area size"); relocationData = new byte[header.relocationDataSize]; fileInputStream.read(relocationData); this.padding = new byte[deltaToPageAlign(header.size() + stringInfo.size() + relocationData.length)]; fileInputStream.read(padding); for (int i = 0; i < padding.length; ++i) { BootImageException.check(padding[i] == 0, "all padding bytes should be 0"); } BootImageException.check((heapOffset() % header.pageSize) == 0, "heap offset is not page-size aligned"); BootImageException.check((codeOffset() % header.pageSize) == 0, "code offset is not page-size aligned"); final DataModel dataModel = new DataModel(header.wordWidth(), header.endianness(), header.cacheAlignment); final Platform platform = new Platform(stringInfo.cpu(), stringInfo.isa(), dataModel, stringInfo.os(), header.pageSize, stringInfo.nsig()); Platform.set(platform); vmConfiguration = new VMConfiguration(stringInfo.buildLevel(), platform, stringInfo.bootImagePackage(Key.REFERENCE), stringInfo.bootImagePackage(Key.LAYOUT), stringInfo.bootImagePackage(Key.HEAP), stringInfo.bootImagePackage(Key.MONITOR), stringInfo.bootImagePackage(Key.RUN)).gatherBootImagePackages(); fileInputStream.skip(header.heapSize + header.codeSize); int trailerOffset = codeOffset() + header.codeSize; trailer = new Trailer(header, fileInputStream, trailerOffset); trailer.check(header); } catch (Utf8Exception utf8Exception) { throw new BootImageException(utf8Exception); } finally { fileInputStream.close(); } } catch (IOException ioException) { throw new BootImageException(ioException); } } /** * Used when constructing a boot image to be written to a file. */ public BootImage(DataPrototype dataPrototype) throws BootImageException { this.vmConfiguration = vmConfig(); this.stringInfo = new StringInfo(vmConfiguration, new Header(dataPrototype, 0).size()); this.stringInfo.check(); this.header = new Header(dataPrototype, stringInfo.size()); this.header.check(); this.relocationData = dataPrototype.relocationData(); this.padding = new byte[deltaToPageAlign(header.size() + stringInfo.size() + relocationData.length)]; this.heap = ByteBuffer.wrap(dataPrototype.heapData()); this.code = ByteBuffer.wrap(dataPrototype.codeData()); int trailerOffset = codeOffset() + header.codeSize; this.trailer = new Trailer(header, trailerOffset); this.imageFile = null; } public int relocationDataOffset() { return header.size() + stringInfo.size(); } public int heapOffset() { return paddingOffset() + paddingSize(); } public int codeOffset() { return heapOffset() + header.heapSize; } public int paddingOffset() { return relocationDataOffset() + header.relocationDataSize; } public int paddingSize() { return padding.length; } public synchronized ByteBuffer heap() { if (heap == null) { heap = mapSection(heapOffset(), header.heapSize); } ByteBuffer duplicate = heap.duplicate(); duplicate.order(heap.order()); return duplicate; } public synchronized ByteBuffer code() { if (code == null) { code = mapSection(codeOffset(), header.codeSize); } ByteBuffer duplicate = code.duplicate(); duplicate.order(code.order()); return duplicate; } public synchronized ByteBuffer heapAndCode() { if (heapAndCode == null) { heapAndCode = mapSection(heapOffset(), header.heapSize + header.codeSize); } ByteBuffer duplicate = heapAndCode.duplicate(); duplicate.order(heapAndCode.order()); return duplicate; } /** * Writes this image to a given stream. * * @param outputStream * @throws IOException */ public void write(OutputStream outputStream) throws IOException { header.write(outputStream, header.endianness()); stringInfo.write(outputStream, header.endianness()); outputStream.write(relocationData); outputStream.write(padding); write(heap(), outputStream); write(code(), outputStream); trailer.write(outputStream, header.endianness()); } private void write(ByteBuffer buffer, OutputStream outputStream) throws IOException { if (buffer.hasArray()) { outputStream.write(buffer.array(), buffer.arrayOffset(), buffer.limit()); } else { byte[] array = new byte[buffer.limit()]; buffer.get(array); outputStream.write(array); } } /** * Computes the amount to add to a given size to bump it up to the next page-aligned size. */ private int deltaToPageAlign(int size) { final int pageSize = header.pageSize; final int rest = size % pageSize; if (rest == 0) { return 0; } return pageSize - rest; } /** * Maps a section of an image file to memory. * * @param offset the offset within the image file to map * @param size the size of the image file section to map * @return a buffer representing the mapped section */ private ByteBuffer mapSection(int offset, int size) { assert imageFile != null : "Cannot map section of image for which underlying file is not available"; try { final RandomAccessFile raf = new RandomAccessFile(imageFile, "r"); final MappedByteBuffer buffer = raf.getChannel().map(MapMode.READ_ONLY, offset, size); raf.close(); ByteOrder byteOrder = platform().endianness().asByteOrder(); buffer.order(byteOrder); raf.close(); return buffer; } catch (IOException e) { throw new InternalError("Error trying to map section of image file: " + e); } } private static native void nativeRelocate(long heap, long relocatedHeap, byte[] relocationDataPointer, int relocationDataSize, int isBigEndian, int wordSize); /** * Relocates the pointers in the heap and code. All the pointers are assumed to be * canonicalized; their current values assume that the heap and code start address 0. * * @param heap the physical address at which the (contiguous) heap and code reside * @param relocatedHeap the logical address to which the heap and code is being relocated */ public void relocate(long heap, Address relocatedHeap) { nativeRelocate(heap, relocatedHeap.toLong(), relocationData, relocationData.length, header.isBigEndian, header.wordSize); } }