package com.sun.jna;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
/**
* Analyse an ELF file for platform specific attributes.
*
* <p>Primary use-case: Detect whether the java binary is arm hardfloat or softfloat.</p>
*/
class ELFAnalyser {
/**
* Generic ELF header
*/
private static final byte[] ELF_MAGIC = new byte[]{(byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F'};
/**
* e_flags mask if executable file conforms to hardware floating-point
* procedure-call standard (arm ABI version 5)
*/
private static final int EF_ARM_ABI_FLOAT_HARD = 0x00000400;
/**
* e_flags mask if executable file conforms to software floating-point
* procedure-call standard (arm ABI version 5)
*/
private static final int EF_ARM_ABI_FLOAT_SOFT = 0x00000200;
private static final int EI_DATA_BIG_ENDIAN = 2;
private static final int E_MACHINE_ARM = 0x28;
private static final int EI_CLASS_64BIT = 2;
public static ELFAnalyser analyse(String filename) throws IOException {
ELFAnalyser res = new ELFAnalyser(filename);
res.runDetection();
return res;
}
private final String filename;
private boolean ELF = false;
private boolean _64Bit = false;
private boolean bigEndian = false;
private boolean armHardFloat = false;
private boolean armSoftFloat = false;
private boolean arm = false;
/**
* @return true if the parsed file was detected to be an ELF file
*/
public boolean isELF() {
return ELF;
}
/**
* @return true if the parsed file was detected to be for a 64bit architecture
* and pointers are expected to be 8byte wide
*/
public boolean is64Bit() {
return _64Bit;
}
/**
* @return true if the parsed file is detected to be big endian, false if
* the file is little endian
*/
public boolean isBigEndian() {
return bigEndian;
}
/**
* @return filename of the parsed file
*/
public String getFilename() {
return filename;
}
/**
* @return true if file was detected to conform to the hardware floating-point
* procedure-call standard
*/
public boolean isArmHardFloat() {
return armHardFloat;
}
/**
* @return true if file was detected to conform to the software floating-point
* procedure-call standard
*/
public boolean isArmSoftFloat() {
return armSoftFloat;
}
/**
* @return true if the parsed file was detected to be build for the arm
* architecture
*/
public boolean isArm() {
return arm;
}
private ELFAnalyser(String filename) {
this.filename = filename;
}
private void runDetection() throws IOException {
// run precheck - only of if the file at least hold an ELF header parsing
// runs further.
RandomAccessFile raf = new RandomAccessFile(filename, "r");
if (raf.length() > 4) {
byte[] magic = new byte[4];
raf.seek(0);
raf.read(magic);
if (Arrays.equals(magic, ELF_MAGIC)) {
ELF = true;
}
}
if (!ELF) {
return;
}
raf.seek(4);
// The total header size depends on the pointer size of the platform
// so before the header is loaded the pointer size has to be determined
byte sizeIndicator = raf.readByte();
_64Bit = sizeIndicator == EI_CLASS_64BIT;
raf.seek(0);
ByteBuffer headerData = ByteBuffer.allocate(_64Bit ? 64 : 52);
raf.getChannel().read(headerData, 0);
bigEndian = headerData.get(5) == EI_DATA_BIG_ENDIAN;
headerData.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
arm = headerData.get(0x12) == E_MACHINE_ARM;
if(arm) {
int flags = headerData.getInt(_64Bit ? 0x30 : 0x24);
armSoftFloat = (flags & EF_ARM_ABI_FLOAT_SOFT) == EF_ARM_ABI_FLOAT_SOFT;
armHardFloat = (flags & EF_ARM_ABI_FLOAT_HARD) == EF_ARM_ABI_FLOAT_HARD;
}
}
}