blob: 594d96ae3e8d29837e03e615397cde6879bbcf35 [file] [log] [blame]
/*
* Copyright (c) 2016, 2018, 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 jdk.tools.jaotc.binformat;
import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jdk.tools.jaotc.binformat.Symbol.Binding;
import jdk.tools.jaotc.binformat.Symbol.Kind;
import jdk.tools.jaotc.binformat.elf.JELFRelocObject;
import jdk.tools.jaotc.binformat.macho.JMachORelocObject;
import jdk.tools.jaotc.binformat.pecoff.JPECoffRelocObject;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import org.graalvm.compiler.options.OptionValues;
/**
* A format-agnostic container class that holds various components of a binary.
*
* <p>
* This class holds information necessary to create platform-specific binary containers such as
* ELFContainer for Linux and Solaris operating systems or MachOContainer for Mac OS or PEContainer
* for MS Windows operating systems.
*
* <p>
* Method APIs provided by this class are used to construct and populate platform-independent
* contents of a binary as the first step to create a binary representation of code generated by a
* compiler backend such as Graal.
*
* <p>
* Methods to record and access code section contents, symbols and relocations are provided.
*/
public final class BinaryContainer implements SymbolTable {
private final OptionValues graalOptions;
private final int codeSegmentSize;
private final int codeEntryAlignment;
private final boolean threadLocalHandshakes;
/**
* Container holding code bits and any other related information.
*/
private final CodeContainer codeContainer;
/**
* Container holding global offset data for hotspot linkage.
*/
private final ByteContainer extLinkageGOTContainer;
/**
* Patched by HotSpot, contains Klass pointers.
*/
private final ByteContainer klassesGotContainer;
/**
* Patched by HotSpot, contains MethodCounters pointers.
*/
private final ByteContainer countersGotContainer;
/**
* Patched lazily by hotspot, contains klass/method pointers.
*/
private final ByteContainer metadataGotContainer;
/**
* BSS container, contains method state array.
*/
private final ByteContainer methodStateContainer;
/**
* Patched by hotspot, contains java object pointers.
*/
private final ByteContainer oopGotContainer;
// Containers holding read-only data
private final ReadOnlyDataContainer configContainer;
private final ReadOnlyDataContainer metaspaceNamesContainer;
private final ReadOnlyDataContainer methodsOffsetsContainer;
private final ReadOnlyDataContainer klassesOffsetsContainer;
private final ReadOnlyDataContainer klassesDependenciesContainer;
private final HeaderContainer headerContainer;
private final ReadOnlyDataContainer stubsOffsetsContainer;
private final ReadOnlyDataContainer codeSegmentsContainer;
// This cannot be read only since we need to patch the metadata at runtime..
private final ReadOnlyDataContainer methodMetadataContainer;
/**
* Container containing constant data used by code.
*/
private final ReadOnlyDataContainer constantDataContainer;
/**
* Map holding the Strings table.
*/
private final Map<String, Integer> offsetStringTable = new HashMap<>();
private final Map<String, Integer> metaspaceNames = new HashMap<>();
// List of relocation table entries - (symbolName, relocationInfo)
private final Map<String, Symbol> symbolTable = new HashMap<>();
private final Map<Symbol, List<Relocation>> relocationTable = new HashMap<>();
private final Map<Symbol, Relocation> uniqueRelocationTable = new HashMap<>();
/**
* Mapping of local VM function names to known global symbols generated in the output binary.
*/
private static final HashMap<String, String> functionNamesToAOTSymbols = new HashMap<>();
private static final String[][] map = {
//@formatter:off
{"CompilerToVM::Data::SharedRuntime_deopt_blob_unpack", "_aot_deopt_blob_unpack"},
{"CompilerToVM::Data::SharedRuntime_deopt_blob_uncommon_trap", "_aot_deopt_blob_uncommon_trap"},
{"CompilerToVM::Data::SharedRuntime_ic_miss_stub", "_aot_ic_miss_stub"},
{"CompilerToVM::Data::SharedRuntime_handle_wrong_method_stub", "_aot_handle_wrong_method_stub"},
{"SharedRuntime::exception_handler_for_return_address", "_aot_exception_handler_for_return_address"},
{"SharedRuntime::register_finalizer", "_aot_register_finalizer"},
{"SharedRuntime::OSR_migration_end", "_aot_OSR_migration_end"},
{"SharedRuntime::enable_stack_reserved_zone", "_aot_enable_stack_reserved_zone"},
{"CompilerRuntime::resolve_dynamic_invoke", "_aot_resolve_dynamic_invoke"},
{"CompilerRuntime::resolve_string_by_symbol", "_aot_resolve_string_by_symbol"},
{"CompilerRuntime::resolve_klass_by_symbol", "_aot_resolve_klass_by_symbol"},
{"CompilerRuntime::resolve_method_by_symbol_and_load_counters","_aot_resolve_method_by_symbol_and_load_counters"},
{"CompilerRuntime::initialize_klass_by_symbol", "_aot_initialize_klass_by_symbol"},
{"CompilerRuntime::invocation_event", "_aot_invocation_event"},
{"CompilerRuntime::backedge_event", "_aot_backedge_event"},
{"CompilerToVM::Data::dpow", "_aot_shared_runtime_dpow"},
{"CompilerToVM::Data::dexp", "_aot_shared_runtime_dexp"},
{"CompilerToVM::Data::dcos", "_aot_shared_runtime_dcos"},
{"CompilerToVM::Data::dsin", "_aot_shared_runtime_dsin"},
{"CompilerToVM::Data::dtan", "_aot_shared_runtime_dtan"},
{"CompilerToVM::Data::dlog", "_aot_shared_runtime_dlog"},
{"CompilerToVM::Data::dlog10", "_aot_shared_runtime_dlog10"},
{"StubRoutines::_jbyte_arraycopy", "_aot_stub_routines_jbyte_arraycopy"},
{"StubRoutines::_jshort_arraycopy", "_aot_stub_routines_jshort_arraycopy"},
{"StubRoutines::_jint_arraycopy", "_aot_stub_routines_jint_arraycopy"},
{"StubRoutines::_jlong_arraycopy", "_aot_stub_routines_jlong_arraycopy"},
{"StubRoutines::_oop_arraycopy", "_aot_stub_routines_oop_arraycopy"},
{"StubRoutines::_oop_arraycopy_uninit", "_aot_stub_routines_oop_arraycopy_uninit"},
{"StubRoutines::_jbyte_disjoint_arraycopy", "_aot_stub_routines_jbyte_disjoint_arraycopy"},
{"StubRoutines::_jshort_disjoint_arraycopy", "_aot_stub_routines_jshort_disjoint_arraycopy"},
{"StubRoutines::_jint_disjoint_arraycopy", "_aot_stub_routines_jint_disjoint_arraycopy"},
{"StubRoutines::_jlong_disjoint_arraycopy", "_aot_stub_routines_jlong_disjoint_arraycopy"},
{"StubRoutines::_oop_disjoint_arraycopy", "_aot_stub_routines_oop_disjoint_arraycopy"},
{"StubRoutines::_oop_disjoint_arraycopy_uninit", "_aot_stub_routines_oop_disjoint_arraycopy_uninit"},
{"StubRoutines::_arrayof_jbyte_arraycopy", "_aot_stub_routines_arrayof_jbyte_arraycopy"},
{"StubRoutines::_arrayof_jshort_arraycopy", "_aot_stub_routines_arrayof_jshort_arraycopy"},
{"StubRoutines::_arrayof_jint_arraycopy", "_aot_stub_routines_arrayof_jint_arraycopy"},
{"StubRoutines::_arrayof_jlong_arraycopy", "_aot_stub_routines_arrayof_jlong_arraycopy"},
{"StubRoutines::_arrayof_oop_arraycopy", "_aot_stub_routines_arrayof_oop_arraycopy"},
{"StubRoutines::_arrayof_oop_arraycopy_uninit", "_aot_stub_routines_arrayof_oop_arraycopy_uninit"},
{"StubRoutines::_arrayof_jbyte_disjoint_arraycopy", "_aot_stub_routines_arrayof_jbyte_disjoint_arraycopy"},
{"StubRoutines::_arrayof_jshort_disjoint_arraycopy", "_aot_stub_routines_arrayof_jshort_disjoint_arraycopy"},
{"StubRoutines::_arrayof_jint_disjoint_arraycopy", "_aot_stub_routines_arrayof_jint_disjoint_arraycopy"},
{"StubRoutines::_arrayof_jlong_disjoint_arraycopy", "_aot_stub_routines_arrayof_jlong_disjoint_arraycopy"},
{"StubRoutines::_arrayof_oop_disjoint_arraycopy", "_aot_stub_routines_arrayof_oop_disjoint_arraycopy"},
{"StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit", "_aot_stub_routines_arrayof_oop_disjoint_arraycopy_uninit"},
{"StubRoutines::_unsafe_arraycopy", "_aot_stub_routines_unsafe_arraycopy"},
{"StubRoutines::_checkcast_arraycopy", "_aot_stub_routines_checkcast_arraycopy"},
{"StubRoutines::_generic_arraycopy", "_aot_stub_routines_generic_arraycopy"},
{"StubRoutines::_aescrypt_encryptBlock", "_aot_stub_routines_aescrypt_encryptBlock"},
{"StubRoutines::_aescrypt_decryptBlock", "_aot_stub_routines_aescrypt_decryptBlock"},
{"StubRoutines::_cipherBlockChaining_encryptAESCrypt", "_aot_stub_routines_cipherBlockChaining_encryptAESCrypt"},
{"StubRoutines::_cipherBlockChaining_decryptAESCrypt", "_aot_stub_routines_cipherBlockChaining_decryptAESCrypt"},
{"StubRoutines::_electronicCodeBook_encryptAESCrypt", "_aot_stub_routines_electronicCodeBook_encryptAESCrypt"},
{"StubRoutines::_electronicCodeBook_decryptAESCrypt", "_aot_stub_routines_electronicCodeBook_decryptAESCrypt"},
{"StubRoutines::_updateBytesCRC32", "_aot_stub_routines_update_bytes_crc32"},
{"StubRoutines::_crc_table_adr", "_aot_stub_routines_crc_table_adr"},
{"StubRoutines::_sha1_implCompress", "_aot_stub_routines_sha1_implCompress" },
{"StubRoutines::_sha1_implCompressMB", "_aot_stub_routines_sha1_implCompressMB" },
{"StubRoutines::_sha256_implCompress", "_aot_stub_routines_sha256_implCompress" },
{"StubRoutines::_sha256_implCompressMB", "_aot_stub_routines_sha256_implCompressMB" },
{"StubRoutines::_sha512_implCompress", "_aot_stub_routines_sha512_implCompress" },
{"StubRoutines::_sha512_implCompressMB", "_aot_stub_routines_sha512_implCompressMB" },
{"StubRoutines::_multiplyToLen", "_aot_stub_routines_multiplyToLen" },
{"StubRoutines::_counterMode_AESCrypt", "_aot_stub_routines_counterMode_AESCrypt" },
{"StubRoutines::_ghash_processBlocks", "_aot_stub_routines_ghash_processBlocks" },
{"StubRoutines::_base64_encodeBlock", "_aot_stub_routines_base64_encodeBlock" },
{"StubRoutines::_crc32c_table_addr", "_aot_stub_routines_crc32c_table_addr" },
{"StubRoutines::_updateBytesCRC32C", "_aot_stub_routines_updateBytesCRC32C" },
{"StubRoutines::_updateBytesAdler32", "_aot_stub_routines_updateBytesAdler32" },
{"StubRoutines::_squareToLen", "_aot_stub_routines_squareToLen" },
{"StubRoutines::_mulAdd", "_aot_stub_routines_mulAdd" },
{"StubRoutines::_montgomeryMultiply", "_aot_stub_routines_montgomeryMultiply" },
{"StubRoutines::_montgomerySquare", "_aot_stub_routines_montgomerySquare" },
{"StubRoutines::_vectorizedMismatch", "_aot_stub_routines_vectorizedMismatch" },
{"StubRoutines::_throw_delayed_StackOverflowError_entry", "_aot_stub_routines_throw_delayed_StackOverflowError_entry" },
{"os::javaTimeMillis", "_aot_os_javaTimeMillis"},
{"os::javaTimeNanos", "_aot_os_javaTimeNanos"},
{"JVMCIRuntime::monitorenter", "_aot_jvmci_runtime_monitorenter"},
{"JVMCIRuntime::monitorexit", "_aot_jvmci_runtime_monitorexit"},
{"JVMCIRuntime::object_notify", "_aot_object_notify"},
{"JVMCIRuntime::object_notifyAll", "_aot_object_notifyAll"},
{"JVMCIRuntime::log_object", "_aot_jvmci_runtime_log_object"},
{"JVMCIRuntime::log_printf", "_aot_jvmci_runtime_log_printf"},
{"JVMCIRuntime::vm_message", "_aot_jvmci_runtime_vm_message"},
{"JVMCIRuntime::new_instance", "_aot_jvmci_runtime_new_instance"},
{"JVMCIRuntime::log_primitive", "_aot_jvmci_runtime_log_primitive"},
{"JVMCIRuntime::new_multi_array", "_aot_jvmci_runtime_new_multi_array"},
{"JVMCIRuntime::validate_object", "_aot_jvmci_runtime_validate_object"},
{"JVMCIRuntime::dynamic_new_array", "_aot_jvmci_runtime_dynamic_new_array"},
{"JVMCIRuntime::write_barrier_pre", "_aot_jvmci_runtime_write_barrier_pre"},
{"JVMCIRuntime::identity_hash_code", "_aot_jvmci_runtime_identity_hash_code"},
{"JVMCIRuntime::write_barrier_post", "_aot_jvmci_runtime_write_barrier_post"},
{"JVMCIRuntime::dynamic_new_instance", "_aot_jvmci_runtime_dynamic_new_instance"},
{"JVMCIRuntime::thread_is_interrupted", "_aot_jvmci_runtime_thread_is_interrupted"},
{"JVMCIRuntime::exception_handler_for_pc", "_aot_jvmci_runtime_exception_handler_for_pc"},
{"JVMCIRuntime::test_deoptimize_call_int", "_aot_jvmci_runtime_test_deoptimize_call_int"},
{"JVMCIRuntime::throw_and_post_jvmti_exception", "_aot_jvmci_runtime_throw_and_post_jvmti_exception"},
{"JVMCIRuntime::throw_klass_external_name_exception", "_aot_jvmci_runtime_throw_klass_external_name_exception"},
{"JVMCIRuntime::throw_class_cast_exception", "_aot_jvmci_runtime_throw_class_cast_exception"},
{"JVMCIRuntime::vm_error", "_aot_jvmci_runtime_vm_error"},
{"JVMCIRuntime::new_array", "_aot_jvmci_runtime_new_array"}
//@formatter:on
};
static {
for (String[] entry : map) {
functionNamesToAOTSymbols.put(entry[0], entry[1]);
}
}
/**
* Allocates a {@code BinaryContainer} object whose content will be generated in a file with the
* prefix {@code prefix}. It also initializes internal code container, symbol table and
* relocation tables.
*
* @param graalOptions
*/
public BinaryContainer(OptionValues graalOptions, GraalHotSpotVMConfig graalHotSpotVMConfig, GraphBuilderConfiguration graphBuilderConfig, int gc, String jvmVersion) {
this.graalOptions = graalOptions;
this.codeSegmentSize = graalHotSpotVMConfig.codeSegmentSize;
if (codeSegmentSize < 1 || codeSegmentSize > 1024) {
throw new InternalError("codeSegmentSize is not in range [1, 1024] bytes: (" + codeSegmentSize + "), update JPECoffRelocObject");
}
if ((codeSegmentSize & (codeSegmentSize - 1)) != 0) {
throw new InternalError("codeSegmentSize is not power of 2: (" + codeSegmentSize + "), update JPECoffRelocObject");
}
this.codeEntryAlignment = graalHotSpotVMConfig.codeEntryAlignment;
this.threadLocalHandshakes = graalHotSpotVMConfig.threadLocalHandshakes;
// Section unique name is limited to 8 characters due to limitation on Windows.
// Name could be longer but only first 8 characters are stored on Windows.
// read only, code
codeContainer = new CodeContainer(".text", this);
// read only, info
headerContainer = new HeaderContainer(jvmVersion, new ReadOnlyDataContainer(".header", this));
configContainer = new ReadOnlyDataContainer(".config", this);
metaspaceNamesContainer = new ReadOnlyDataContainer(".meta.names", this);
methodsOffsetsContainer = new ReadOnlyDataContainer(".meth.offsets", this);
klassesOffsetsContainer = new ReadOnlyDataContainer(".kls.offsets", this);
klassesDependenciesContainer = new ReadOnlyDataContainer(".kls.dependencies", this);
stubsOffsetsContainer = new ReadOnlyDataContainer(".stubs.offsets", this);
codeSegmentsContainer = new ReadOnlyDataContainer(".code.segments", this);
constantDataContainer = new ReadOnlyDataContainer(".meth.constdata", this);
methodMetadataContainer = new ReadOnlyDataContainer(".meth.metadata", this);
// writable sections
oopGotContainer = new ByteContainer(".oop.got", this);
klassesGotContainer = new ByteContainer(".kls.got", this);
countersGotContainer = new ByteContainer(".cnt.got", this);
metadataGotContainer = new ByteContainer(".meta.got", this);
methodStateContainer = new ByteContainer(".meth.state", this);
extLinkageGOTContainer = new ByteContainer(".got.linkage", this);
addGlobalSymbols();
recordConfiguration(graalHotSpotVMConfig, graphBuilderConfig, gc);
}
private void recordConfiguration(GraalHotSpotVMConfig graalHotSpotVMConfig, GraphBuilderConfiguration graphBuilderConfig, int gc) {
// @formatter:off
boolean[] booleanFlags = { graalHotSpotVMConfig.cAssertions, // Debug VM
graalHotSpotVMConfig.useCompressedOops,
graalHotSpotVMConfig.useCompressedClassPointers,
graalHotSpotVMConfig.compactFields,
graalHotSpotVMConfig.useTLAB,
graalHotSpotVMConfig.useBiasedLocking,
TieredAOT.getValue(graalOptions),
graalHotSpotVMConfig.enableContended,
graalHotSpotVMConfig.restrictContended,
graphBuilderConfig.omitAssertions(),
graalHotSpotVMConfig.threadLocalHandshakes
};
int[] intFlags = { graalHotSpotVMConfig.getOopEncoding().getShift(),
graalHotSpotVMConfig.getKlassEncoding().getShift(),
graalHotSpotVMConfig.contendedPaddingWidth,
graalHotSpotVMConfig.fieldsAllocationStyle,
1 << graalHotSpotVMConfig.logMinObjAlignment(),
graalHotSpotVMConfig.codeSegmentSize,
gc
};
// @formatter:on
byte[] booleanFlagsAsBytes = flagsToByteArray(booleanFlags);
int size0 = configContainer.getByteStreamSize();
// @formatter:off
int computedSize = booleanFlagsAsBytes.length * Byte.BYTES + // size of boolean flags
intFlags.length * Integer.BYTES + // size of int flags
Integer.BYTES; // size of the "computedSize"
configContainer.appendInt(computedSize).
appendInts(intFlags).
appendBytes(booleanFlagsAsBytes);
// @formatter:on
int size = configContainer.getByteStreamSize() - size0;
assert size == computedSize;
}
private static byte[] flagsToByteArray(boolean[] flags) {
byte[] byteArray = new byte[flags.length];
for (int i = 0; i < flags.length; ++i) {
byteArray[i] = boolToByte(flags[i]);
}
return byteArray;
}
private static byte boolToByte(boolean flag) {
return (byte) (flag ? 1 : 0);
}
/**
* Free some memory.
*/
public void freeMemory() {
offsetStringTable.clear();
metaspaceNames.clear();
}
/*
* Global symbol names in generated DSO corresponding to VM's symbols. VM needs to look up this
* symbol in DSO and link it with VM's corresponding symbol: store VM's symbol address or value
* in the named GOT cell.
*/
public static String getCardTableAddressSymbolName() {
return "_aot_card_table_address";
}
public static String getHeapTopAddressSymbolName() {
return "_aot_heap_top_address";
}
public static String getHeapEndAddressSymbolName() {
return "_aot_heap_end_address";
}
public static String getCrcTableAddressSymbolName() {
return "_aot_stub_routines_crc_table_adr";
}
public static String getPollingPageSymbolName() {
return "_aot_polling_page";
}
public static String getResolveStaticEntrySymbolName() {
return "_resolve_static_entry";
}
public static String getResolveVirtualEntrySymbolName() {
return "_resolve_virtual_entry";
}
public static String getResolveOptVirtualEntrySymbolName() {
return "_resolve_opt_virtual_entry";
}
public static String getNarrowKlassBaseAddressSymbolName() {
return "_aot_narrow_klass_base_address";
}
public static String getNarrowOopBaseAddressSymbolName() {
return "_aot_narrow_oop_base_address";
}
public static String getLogOfHeapRegionGrainBytesSymbolName() {
return "_aot_log_of_heap_region_grain_bytes";
}
public static String getInlineContiguousAllocationSupportedSymbolName() {
return "_aot_inline_contiguous_allocation_supported";
}
public int getCodeSegmentSize() {
return codeSegmentSize;
}
public int getCodeEntryAlignment() {
return codeEntryAlignment;
}
public boolean getThreadLocalHandshakes() {
return threadLocalHandshakes;
}
/**
* Gets the global AOT symbol associated with the function name.
*
* @param functionName function name
* @return AOT symbol for the given function name, or null if there is no mapping.
*/
public static String getAOTSymbolForVMFunctionName(String functionName) {
return functionNamesToAOTSymbols.get(functionName);
}
private void addGlobalSymbols() {
// Create global symbols for all containers.
createContainerSymbol(codeContainer);
createContainerSymbol(configContainer);
createContainerSymbol(methodsOffsetsContainer);
createContainerSymbol(klassesOffsetsContainer);
createContainerSymbol(klassesDependenciesContainer);
createContainerSymbol(klassesGotContainer);
createContainerSymbol(countersGotContainer);
createContainerSymbol(metadataGotContainer);
createContainerSymbol(methodStateContainer);
createContainerSymbol(oopGotContainer);
createContainerSymbol(metaspaceNamesContainer);
createContainerSymbol(methodMetadataContainer);
createContainerSymbol(stubsOffsetsContainer);
createContainerSymbol(headerContainer.getContainer());
createContainerSymbol(codeSegmentsContainer);
createGotSymbol(getResolveStaticEntrySymbolName());
createGotSymbol(getResolveVirtualEntrySymbolName());
createGotSymbol(getResolveOptVirtualEntrySymbolName());
createGotSymbol(getCardTableAddressSymbolName());
createGotSymbol(getHeapTopAddressSymbolName());
createGotSymbol(getHeapEndAddressSymbolName());
createGotSymbol(getNarrowKlassBaseAddressSymbolName());
createGotSymbol(getNarrowOopBaseAddressSymbolName());
createGotSymbol(getPollingPageSymbolName());
createGotSymbol(getLogOfHeapRegionGrainBytesSymbolName());
createGotSymbol(getInlineContiguousAllocationSupportedSymbolName());
for (HashMap.Entry<String, String> entry : functionNamesToAOTSymbols.entrySet()) {
createGotSymbol(entry.getValue());
}
}
/**
* Creates a global symbol of the form {@code "A" + container name}. Note, linker on Windows
* does not allow names which start with '.'
*
* @param container container to create a symbol for
*/
private static void createContainerSymbol(ByteContainer container) {
container.createSymbol(0, Kind.OBJECT, Binding.GLOBAL, 0, "A" + container.getContainerName());
}
/**
* Creates a global GOT symbol of the form {@code "got." + name}.
*
* @param name name for the GOT symbol
*/
private void createGotSymbol(String name) {
String s = "got." + name;
Symbol gotSymbol = extLinkageGOTContainer.createGotSymbol(s);
extLinkageGOTContainer.createSymbol(gotSymbol.getOffset(), Kind.OBJECT, Binding.GLOBAL, 8, name);
}
/**
* Create a platform-specific binary file representing the content of the
* {@code BinaryContainer} object.
*
* This method is called after creating and performing any necessary changes to the contents of
* code stream, symbol tables and relocation tables is completely finalized
*
* @param outputFileName name of output file
*
* @throws IOException in case of file creation failure
*/
public void createBinary(String outputFileName) throws IOException {
String osName = System.getProperty("os.name");
switch (osName) {
case "Linux":
case "SunOS":
JELFRelocObject elfobj = JELFRelocObject.newInstance(this, outputFileName);
elfobj.createELFRelocObject(relocationTable, symbolTable.values());
break;
case "Mac OS X":
JMachORelocObject machobj = new JMachORelocObject(this, outputFileName);
machobj.createMachORelocObject(relocationTable, symbolTable.values());
break;
default:
if (osName.startsWith("Windows")) {
JPECoffRelocObject pecoffobj = new JPECoffRelocObject(this, outputFileName);
pecoffobj.createPECoffRelocObject(relocationTable, symbolTable.values());
break;
} else
throw new InternalError("Unsupported platform: " + osName);
}
}
/**
* Add symbol to the symbol table. If the existing symbol is undefined and the specified symbol
* is not undefined, replace the existing symbol information with that specified.
*
* @param symInfo symbol information to be added
*/
public void addSymbol(Symbol symInfo) {
if (symInfo.getName().startsWith("got.") && !(symInfo instanceof GotSymbol)) {
throw new InternalError("adding got. without being GotSymbol");
}
if (symbolTable.containsKey(symInfo.getName())) {
throw new InternalError("Symbol: " + symInfo.getName() + " already exists in SymbolTable");
} else {
// System.out.println("# Symbol [" + name + "] [" + symInfo.getValue() + "] [" +
// symInfo.getSection().getContainerName() + "] [" + symInfo.getSize() + "]");
symbolTable.put(symInfo.getName(), symInfo);
}
}
public boolean addStringOffset(String name, Integer offset) {
offsetStringTable.put(name, offset);
return true;
}
/**
* Add relocation entry for {@code symName}. Multiple relocation entries for a given symbol may
* exist.
*
* @param info relocation information to be added
*/
public void addRelocation(Relocation info) {
// System.out.println("# Relocation [" + info.getSymbol() + "] [" + info.getOffset() + "] [" +
// info.getSection().getContainerName() + "] [" + info.getSymbol().getName() + "] [" +
// info.getSymbol().getOffset() + " @ " + info.getSymbol().getSection().getContainerName() +
// "]");
if (relocationTable.containsKey(info.getSymbol())) {
relocationTable.get(info.getSymbol()).add(info);
} else if (uniqueRelocationTable.containsKey(info.getSymbol())) {
// promote
ArrayList<Relocation> list = new ArrayList<>(2);
list.add(uniqueRelocationTable.get(info.getSymbol()));
list.add(info);
relocationTable.put(info.getSymbol(), list);
uniqueRelocationTable.remove(info.getSymbol());
} else {
uniqueRelocationTable.put(info.getSymbol(), info);
}
}
/**
* Get symbol with name {@code symName}.
*
* @param symName name of symbol for which symbol table information is being queried
* @return success or failure of insertion operation
*/
@Override
public Symbol getSymbol(String symName) {
return symbolTable.get(symName);
}
@Override
public Symbol createSymbol(int offset, Kind kind, Binding binding, int size, String name) {
if (kind != Kind.NATIVE_FUNCTION) {
throw new UnsupportedOperationException("Must be external functions: " + name);
}
Symbol symbol = new Symbol(offset, kind, binding, null, size, name);
addSymbol(symbol);
return symbol;
}
/**
* Get offset in got section with name {@code symName}.
*
* @param name for which String table information is being queried
* @return success or failure of insertion operation
*/
public Integer getStringOffset(String name) {
return offsetStringTable.get(name);
}
/**
* Insert {@code targetCode} to code stream with {@code size} at {@code offset}.
*
* @param targetCode byte array of native code
* @param offset offset at which {@code targetCode} is to be inserted
* @param size size of {@code targetCode}
*/
private static void appendBytes(ByteContainer byteContainer, byte[] targetCode, int offset, int size) {
byteContainer.appendBytes(targetCode, offset, size);
}
public void appendCodeBytes(byte[] targetCode, int offset, int size) {
appendBytes(codeContainer, targetCode, offset, size);
}
public void appendIntToCode(int value) {
codeContainer.appendInt(value);
}
public int appendExtLinkageGotBytes(byte[] bytes, int offset, int size) {
int startOffset = extLinkageGOTContainer.getByteStreamSize();
appendBytes(extLinkageGOTContainer, bytes, offset, size);
return startOffset;
}
public void addMetadataGotEntry(int offset) {
metadataGotContainer.appendLong(offset);
}
public int addMetaspaceName(String name) {
Integer value = metaspaceNames.get(name);
if (value != null) {
return value.intValue();
}
// Get the current length of the stubsNameContainer
// align on 8-byte boundary
int nameOffset = alignUp(metaspaceNamesContainer, 8);
try {
// Add the name of the symbol to the .stubs.names section
// Modify them to sequence of utf8 strings with length:
// "<u2_size>Ljava/lang/ThreadGroup;<u2_size>addUnstarted<u2_size>()V"
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bout);
int len = name.length();
if (name.startsWith("Stub")) { // Stub
out.writeUTF(name);
} else { // Method or Klass
int parenthesesIndex = name.lastIndexOf('(', len - 1);
if (parenthesesIndex > 0) { // Method name
int dotIndex = name.lastIndexOf('.', parenthesesIndex - 1);
assert dotIndex > 0 : "method's full name should have '.' : " + name;
String klassName = name.substring(0, dotIndex);
out.writeUTF(klassName);
String methodName = name.substring(dotIndex + 1, parenthesesIndex);
out.writeUTF(methodName);
String signature = name.substring(parenthesesIndex, len);
out.writeUTF(signature);
} else {
out.writeUTF(name); // Klass
}
}
out.writeShort(0); // Terminate by 0.
byte[] b = bout.toByteArray();
metaspaceNamesContainer.appendBytes(b, 0, b.length);
metaspaceNames.put(name, nameOffset);
return nameOffset;
} catch (IOException e) {
throw new InternalError("Failed to append bytes to stubs sections", e);
}
}
/**
* Add oop symbol by as follows. Extend the oop.got section with another slot for the VM to
* patch.
*
* @param oopName name of the oop symbol
*/
public Integer addOopSymbol(String oopName) {
Integer oopGotOffset = getStringOffset(oopName);
if (oopGotOffset != null) {
return oopGotOffset;
}
return newOopSymbol(oopName);
}
private Integer newOopSymbol(String oopName) {
// Reference to String resolution (ldc).
int offset = oopGotContainer.getByteStreamSize();
String gotName = "got.ldc." + offset;
Symbol relocationSymbol = oopGotContainer.createGotSymbol(gotName);
if (offset != relocationSymbol.getOffset()) {
throw new InternalError("offset must equal! (" + offset + " vs " + relocationSymbol.getOffset());
}
addStringOffset(oopName, relocationSymbol.getOffset());
return relocationSymbol.getOffset();
}
public int addCountersSymbol(String metaspaceName) {
String gotName = "got." + metaspaceName;
Symbol relocationSymbol = getGotSymbol(gotName);
int metaspaceOffset = -1;
if (relocationSymbol == null) {
// Add slots when asked in the .metaspace.got section:
countersGotContainer.createGotSymbol(gotName);
}
return metaspaceOffset;
}
public Symbol getGotSymbol(String name) {
assert name.startsWith("got.");
return symbolTable.get(name);
}
/**
* Add klass symbol by as follows. - Adding the symbol name to the metaspace.names section - Add
* the offset of the name in metaspace.names to metaspace.offsets - Extend the klasses.got
* section with another slot for the VM to patch
*
* @param klassName name of the metaspace symbol
* @return the got offset in the klasses.got of the metaspace symbol
*/
public int addTwoSlotKlassSymbol(String klassName) {
String gotName = "got." + klassName;
Symbol previous = getGotSymbol(gotName);
assert previous == null : "should be called only once for: " + klassName;
// Add slots when asked in the .metaspace.got section:
// First slot
String gotInitName = "got.init." + klassName;
GotSymbol slot1Symbol = klassesGotContainer.createGotSymbol(gotInitName);
GotSymbol slot2Symbol = klassesGotContainer.createGotSymbol(gotName);
slot1Symbol.getIndex(); // check alignment and ignore result
// Get the index (offset/8) to the got in the .metaspace.got section
return slot2Symbol.getIndex();
}
public static int addMethodsCount(int count, ReadOnlyDataContainer container) {
return appendInt(count, container);
}
private static int appendInt(int count, ReadOnlyDataContainer container) {
int offset = container.getByteStreamSize();
container.appendInt(count);
return offset;
}
/**
* Add constant data as follows. - Adding the data to the meth.constdata section
*
* @param data
* @param alignment
* @return the offset in the meth.constdata of the data
*/
public int addConstantData(byte[] data, int alignment) {
// Get the current length of the metaspaceNameContainer
int constantDataOffset = alignUp(constantDataContainer, alignment);
constantDataContainer.appendBytes(data, 0, data.length);
alignUp(constantDataContainer, alignment); // Post alignment
return constantDataOffset;
}
public static int alignUp(ByteContainer container, int alignment) {
if (Integer.bitCount(alignment) != 1) {
throw new IllegalArgumentException("Must be a power of 2");
}
int offset = container.getByteStreamSize();
int aligned = (offset + (alignment - 1)) & -alignment;
if (aligned < offset || (aligned & (alignment - 1)) != 0) {
throw new RuntimeException("Error aligning: " + offset + " -> " + aligned);
}
if (aligned != offset) {
int nullArraySz = aligned - offset;
byte[] nullArray = new byte[nullArraySz];
container.appendBytes(nullArray, 0, nullArraySz);
offset = aligned;
}
return offset;
}
public void addCodeSegments(int start, int end) {
assert (start % codeSegmentSize) == 0 : "not aligned code";
int currentOffset = codeSegmentsContainer.getByteStreamSize();
int offset = start / codeSegmentSize;
int emptySize = offset - currentOffset;
// add empty segments if needed
if (emptySize > 0) {
byte[] emptyArray = new byte[emptySize];
for (int i = 0; i < emptySize; i++) {
emptyArray[i] = (byte) 0xff;
}
appendBytes(codeSegmentsContainer, emptyArray, 0, emptySize);
}
int alignedEnd = (end + (codeSegmentSize - 1)) & -codeSegmentSize;
int segmentsCount = (alignedEnd / codeSegmentSize) - offset;
byte[] segments = new byte[segmentsCount];
int idx = 0;
for (int i = 0; i < segmentsCount; i++) {
segments[i] = (byte) idx;
idx = (idx == 0xfe) ? 1 : (idx + 1);
}
appendBytes(codeSegmentsContainer, segments, 0, segmentsCount);
}
public ByteContainer getExtLinkageGOTContainer() {
return extLinkageGOTContainer;
}
public ReadOnlyDataContainer getMethodMetadataContainer() {
return methodMetadataContainer;
}
public ReadOnlyDataContainer getMetaspaceNamesContainer() {
return metaspaceNamesContainer;
}
public ReadOnlyDataContainer getMethodsOffsetsContainer() {
return methodsOffsetsContainer;
}
public ReadOnlyDataContainer getKlassesOffsetsContainer() {
return klassesOffsetsContainer;
}
public ReadOnlyDataContainer getKlassesDependenciesContainer() {
return klassesDependenciesContainer;
}
public ReadOnlyDataContainer getStubsOffsetsContainer() {
return stubsOffsetsContainer;
}
public ReadOnlyDataContainer getCodeSegmentsContainer() {
return codeSegmentsContainer;
}
public ReadOnlyDataContainer getConstantDataContainer() {
return constantDataContainer;
}
public ByteContainer getKlassesGotContainer() {
return klassesGotContainer;
}
public ByteContainer getCountersGotContainer() {
return countersGotContainer;
}
public ByteContainer getMetadataGotContainer() {
return metadataGotContainer;
}
public ByteContainer getMethodStateContainer() {
return methodStateContainer;
}
public ByteContainer getOopGotContainer() {
return oopGotContainer;
}
public CodeContainer getCodeContainer() {
return codeContainer;
}
public ReadOnlyDataContainer getConfigContainer() {
return configContainer;
}
public Map<Symbol, Relocation> getUniqueRelocationTable() {
return uniqueRelocationTable;
}
public HeaderContainer getHeaderContainer() {
return headerContainer;
}
}