You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
9.7 KiB
250 lines
9.7 KiB
/*
|
|
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
|
|
*
|
|
* This file is part of the Keystone Java bindings which is released under MIT.
|
|
* See file LICENSE in the Java bindings folder for full license details.
|
|
*/
|
|
|
|
package keystone;
|
|
|
|
import com.sun.jna.Pointer;
|
|
import com.sun.jna.ptr.IntByReference;
|
|
import com.sun.jna.ptr.PointerByReference;
|
|
import keystone.exceptions.AssembleFailedKeystoneException;
|
|
import keystone.exceptions.OpenFailedKeystoneException;
|
|
import keystone.exceptions.SetOptionFailedKeystoneException;
|
|
import keystone.natives.CleanerContainer;
|
|
import keystone.natives.DirectMappingKeystoneNative;
|
|
import keystone.natives.KeystoneCleanerContainer;
|
|
import keystone.utilities.Version;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
/**
|
|
* The Keystone engine.
|
|
*/
|
|
public class Keystone implements AutoCloseable {
|
|
|
|
/**
|
|
* The pointer to the Keystone native resource.
|
|
*/
|
|
private final Pointer ksEngine;
|
|
|
|
/**
|
|
* The cleaner container that frees up the native resource if this object is not properly closed and is
|
|
* candidate for garbage collection.
|
|
*/
|
|
private final CleanerContainer ksEngineCleaner;
|
|
|
|
/**
|
|
* Indicates whether the current instance of Keystone has been closed.
|
|
*/
|
|
private final AtomicBoolean hasBeenClosed;
|
|
|
|
/**
|
|
* The memory retention of the symbol resolver callback, in order to prevent the garbage collector
|
|
* to free up the callback, that would result in a crash, as the native library still has a reference to it.
|
|
*/
|
|
private SymbolResolverCallback symbolResolverCallback;
|
|
|
|
/**
|
|
* Initializes a new instance of the class {@link Keystone}.
|
|
* <p>
|
|
* Some architectures are not supported. Use the static method {@link #isArchitectureSupported} to determine
|
|
* whether the engine support the architecture.
|
|
*
|
|
* @param architecture The architecture of the code generated by Keystone.
|
|
* @param mode The mode type.
|
|
* @throws OpenFailedKeystoneException if the Keystone library cannot be opened properly.
|
|
*/
|
|
public Keystone(KeystoneArchitecture architecture, KeystoneMode mode) {
|
|
ksEngine = initializeKeystoneEngine(architecture, mode);
|
|
ksEngineCleaner = initializeKeystoneCleanerContainer();
|
|
hasBeenClosed = new AtomicBoolean(false);
|
|
}
|
|
|
|
/**
|
|
* Determines whether the given architecture is supported by Keystone.
|
|
*
|
|
* @param architecture The architecture type to check.
|
|
* @return The return value is {@code true} if the architecture is supported, otherwise {@code false}.
|
|
*/
|
|
public static boolean isArchitectureSupported(KeystoneArchitecture architecture) {
|
|
return DirectMappingKeystoneNative.ks_arch_supported(architecture);
|
|
}
|
|
|
|
/**
|
|
* Opens an handle of Keystone.
|
|
*
|
|
* @param architecture The architecture of the code generated by Keystone.
|
|
* @param mode The mode type.
|
|
* @return The return value is a pointer to the handle of Keystone.
|
|
* @throws OpenFailedKeystoneException if the Keystone library cannot be opened properly.
|
|
*/
|
|
private Pointer initializeKeystoneEngine(KeystoneArchitecture architecture, KeystoneMode mode) {
|
|
var pointerToEngine = new PointerByReference();
|
|
var openResult = DirectMappingKeystoneNative.ks_open(architecture, mode, pointerToEngine);
|
|
|
|
if (openResult != KeystoneError.Ok) {
|
|
throw new OpenFailedKeystoneException(openResult);
|
|
}
|
|
|
|
return pointerToEngine.getValue();
|
|
}
|
|
|
|
/**
|
|
* Initializes the cleaner object, that is going to close the native handle of Keystone if
|
|
* the instance is garbage collected.
|
|
*
|
|
* @return The return value is a cleaner container.
|
|
*/
|
|
private CleanerContainer initializeKeystoneCleanerContainer() {
|
|
return new KeystoneCleanerContainer(ksEngine);
|
|
}
|
|
|
|
/**
|
|
* Assembles a string that contains assembly code.
|
|
*
|
|
* @param assembly The assembly instructions. Use ; or \n to separate statements.
|
|
* @return The return value is the machine code of the assembly instructions.
|
|
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
|
|
*/
|
|
public KeystoneEncoded assemble(String assembly) {
|
|
return assemble(assembly, 0);
|
|
}
|
|
|
|
/**
|
|
* Assembles a string that contains assembly code, located at a given address location.
|
|
*
|
|
* @param assembly The assembly instructions. Use ; or \n to separate statements.
|
|
* @param address The address of the first assembly instruction.
|
|
* @return The return value is the machine code of the assembly instructions.
|
|
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
|
|
*/
|
|
public KeystoneEncoded assemble(String assembly, int address) {
|
|
var pointerToMachineCodeBuffer = new PointerByReference();
|
|
var pointerToMachineCodeSize = new IntByReference();
|
|
var pointerToNumberOfStatements = new IntByReference();
|
|
|
|
var result = DirectMappingKeystoneNative.ks_asm(ksEngine, assembly, address, pointerToMachineCodeBuffer,
|
|
pointerToMachineCodeSize, pointerToNumberOfStatements);
|
|
|
|
if (result != 0) {
|
|
var errorCode = DirectMappingKeystoneNative.ks_errno(ksEngine);
|
|
throw new AssembleFailedKeystoneException(errorCode, assembly);
|
|
}
|
|
|
|
var machineCodeBuffer = pointerToMachineCodeBuffer.getValue();
|
|
var machineCode = machineCodeBuffer.getByteArray(0, pointerToMachineCodeSize.getValue());
|
|
|
|
DirectMappingKeystoneNative.ks_free(machineCodeBuffer);
|
|
|
|
return new KeystoneEncoded(machineCode, address, pointerToNumberOfStatements.getValue());
|
|
}
|
|
|
|
/**
|
|
* Assembles a string that contains assembly code.
|
|
*
|
|
* @param assembly A collection of assembly instructions.
|
|
* @return The return value is the machine code of the assembly instructions.
|
|
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
|
|
*/
|
|
public KeystoneEncoded assemble(Iterable<String> assembly) {
|
|
return assemble(assembly, 0);
|
|
}
|
|
|
|
/**
|
|
* Assembles a string that contains assembly code, located at a given address location.
|
|
*
|
|
* @param assembly A collection of assembly instructions.
|
|
* @param address The address of the first assembly instruction.
|
|
* @return The return value is the machine code of the assembly instructions.
|
|
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
|
|
*/
|
|
public KeystoneEncoded assemble(Iterable<String> assembly, int address) {
|
|
return assemble(String.join(";", assembly), address);
|
|
}
|
|
|
|
/**
|
|
* Gets the major and minor version numbers.
|
|
*
|
|
* @return The returned value is an instance of the class {@link Version}, containing the major and minor version numbers.
|
|
*/
|
|
public Version version() {
|
|
var major = new IntByReference();
|
|
var minor = new IntByReference();
|
|
|
|
DirectMappingKeystoneNative.ks_version(major, minor);
|
|
|
|
return new Version(major.getValue(), minor.getValue());
|
|
}
|
|
|
|
/**
|
|
* Sets the syntax of the assembly code used in this instance of Keystone.
|
|
*
|
|
* @param syntax The syntax of the assembly code.
|
|
* @throws SetOptionFailedKeystoneException if the syntax is not supported.
|
|
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
|
|
*/
|
|
public void setAssemblySyntax(KeystoneOptionValue.KeystoneOptionSyntax syntax) {
|
|
setOption(KeystoneOptionType.Syntax, syntax.value());
|
|
}
|
|
|
|
/**
|
|
* Sets an option for Keystone engine at runtime using a not-strongly typed option value.
|
|
* <p>
|
|
* It is suggested to prefer the methods {@link #setAssemblySyntax(KeystoneOptionValue.KeystoneOptionSyntax)},
|
|
* {@link #setSymbolResolver(SymbolResolverCallback)} or {@link #unsetSymbolResolver()}.
|
|
*
|
|
* @param type The type of the option.
|
|
* @param value The value of the option.
|
|
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
|
|
*/
|
|
public void setOption(KeystoneOptionType type, int value) {
|
|
var result = DirectMappingKeystoneNative.ks_option(ksEngine, type, value);
|
|
|
|
if (result != KeystoneError.Ok) {
|
|
throw new SetOptionFailedKeystoneException(result, type, value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a symbol resolver callback, to resolve unrecognized symbols encountered in the assembly code.
|
|
* <p>
|
|
* If the method is called many times, only the last callback will be triggered.
|
|
*
|
|
* @param callback The symbol resolver callback.
|
|
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
|
|
*/
|
|
public void setSymbolResolver(SymbolResolverCallback callback) {
|
|
symbolResolverCallback = callback;
|
|
DirectMappingKeystoneNative.ks_option(ksEngine, KeystoneOptionType.SymbolResolver, callback);
|
|
}
|
|
|
|
/**
|
|
* Unsets the current symbol resolver. The symbol resolver instance can be freely collected afterwards.
|
|
*
|
|
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
|
|
*/
|
|
public void unsetSymbolResolver() {
|
|
DirectMappingKeystoneNative.ks_option(ksEngine, KeystoneOptionType.SymbolResolver, 0);
|
|
symbolResolverCallback = null;
|
|
}
|
|
|
|
/**
|
|
* Closes this resource, relinquishing any underlying resources.
|
|
* This method is invoked automatically on objects managed by the
|
|
* {@code try}-with-resources statement.
|
|
* <p>
|
|
* The call to this method is thread-safe.
|
|
*/
|
|
@Override
|
|
public void close() {
|
|
var hasBeenAlreadyClosed = hasBeenClosed.getAndSet(true);
|
|
|
|
if (!hasBeenAlreadyClosed) {
|
|
ksEngineCleaner.close();
|
|
}
|
|
}
|
|
}
|