All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jna.ELFAnalyser Maven / Gradle / Ivy

The newest version!
/* Copyright (c) 2017 Matthias Bläsing, All Rights Reserved
 *
 * The contents of this file is dual-licensed under 2
 * alternative Open Source/Free licenses: LGPL 2.1 or later and
 * Apache License 2.0. (starting with JNA version 4.0.0).
 *
 * You can freely decide which license you want to apply to
 * the project.
 *
 * You may obtain a copy of the LGPL License at:
 *
 * http://www.gnu.org/licenses/licenses.html
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "LGPL2.1".
 *
 * You may obtain a copy of the Apache License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "AL2.0".
 */
package com.sun.jna;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Analyse an ELF file for platform specific attributes.
 *
 * 

Primary use-case: Detect whether the java binary is arm hardfloat or softfloat.

*/ 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 armHardFloatFlag = false; private boolean armSoftFloatFlag = false; private boolean armEabiAapcsVfp = 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; } public boolean isArmHardFloat() { return isArmEabiAapcsVfp() || isArmHardFloatFlag(); } /** * @return true if file was detected to specify, that FP parameters/result * passing conforms to AAPCS, VFP variant (hardfloat) */ public boolean isArmEabiAapcsVfp() { return armEabiAapcsVfp; } /** * @return true if file was detected to conform to the hardware floating-point * procedure-call standard via ELF flags */ public boolean isArmHardFloatFlag() { return armHardFloatFlag; } /** * @return true if file was detected to conform to the software floating-point * procedure-call standard via ELF flags */ public boolean isArmSoftFloatFlag() { return armSoftFloatFlag; } /** * @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 { RandomAccessFile raf = new RandomAccessFile(filename, "r"); try { // run precheck - only of if the file at least hold an ELF header parsing // runs further. 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(); byte endianessIndicator = raf.readByte(); _64Bit = sizeIndicator == EI_CLASS_64BIT; bigEndian = endianessIndicator == EI_DATA_BIG_ENDIAN; raf.seek(0); // header length ByteBuffer headerData = ByteBuffer.allocate(_64Bit ? 64 : 52); raf.getChannel().read(headerData, 0); headerData.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); // e_machine arm = headerData.get(0x12) == E_MACHINE_ARM; if(arm) { // e_flags int flags = headerData.getInt(_64Bit ? 0x30 : 0x24); armHardFloatFlag = (flags & EF_ARM_ABI_FLOAT_HARD) == EF_ARM_ABI_FLOAT_HARD; armSoftFloatFlag = (flags & EF_ARM_ABI_FLOAT_SOFT) == EF_ARM_ABI_FLOAT_SOFT; parseEabiAapcsVfp(headerData, raf); } } finally { try { raf.close(); } catch (IOException ex) { // Swallow - closing } } } private void parseEabiAapcsVfp(ByteBuffer headerData, RandomAccessFile raf) throws IOException { ELFSectionHeaders sectionHeaders = new ELFSectionHeaders(_64Bit, bigEndian, headerData, raf); for (ELFSectionHeaderEntry eshe : sectionHeaders.getEntries()) { if(".ARM.attributes".equals(eshe.getName())) { ByteBuffer armAttributesBuffer = ByteBuffer.allocate(eshe.getSize()); armAttributesBuffer.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); raf.getChannel().read(armAttributesBuffer, eshe.getOffset()); armAttributesBuffer.rewind(); Map> armAttributes = parseArmAttributes(armAttributesBuffer); Map fileAttributes = armAttributes.get(1); if(fileAttributes == null) { continue; } /** * Tag_ABI_VFP_args, (=28), uleb128 * 0 The user intended FP parameter/result passing to conform to AAPCS, base variant * 1 The user intended FP parameter/result passing to conform to AAPCS, VFP variant * 2 The user intended FP parameter/result passing to conform to tool chain-specific conventions * 3 Code is compatible with both the base and VFP variants; the non-variadic functions to pass FP parameters/results */ Object abiVFPargValue = fileAttributes.get(ArmAeabiAttributesTag.ABI_VFP_args); if(abiVFPargValue instanceof Integer && ((Integer) abiVFPargValue).equals(1)) { armEabiAapcsVfp = true; } else if (abiVFPargValue instanceof BigInteger && ((BigInteger) abiVFPargValue).intValue() == 1) { armEabiAapcsVfp = true; } } } } static class ELFSectionHeaders { private final List entries = new ArrayList(); public ELFSectionHeaders(boolean _64bit, boolean bigEndian, ByteBuffer headerData, RandomAccessFile raf) throws IOException { long shoff; int shentsize; int shnum; short shstrndx; if (_64bit) { shoff = headerData.getLong(0x28); shentsize = headerData.getShort(0x3A); shnum = headerData.getShort(0x3C); shstrndx = headerData.getShort(0x3E); } else { shoff = headerData.getInt(0x20); shentsize = headerData.getShort(0x2E); shnum = headerData.getShort(0x30); shstrndx = headerData.getShort(0x32); } int tableLength = shnum * shentsize; ByteBuffer data = ByteBuffer.allocate(tableLength); data.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); raf.getChannel().read(data, shoff); for(int i = 0; i < shnum; i++) { data.position(i * shentsize); ByteBuffer header = data.slice(); header.order(data.order()); header.limit(shentsize); entries.add(new ELFSectionHeaderEntry(_64bit, header)); } ELFSectionHeaderEntry stringTable = entries.get(shstrndx); ByteBuffer stringBuffer = ByteBuffer.allocate(stringTable.getSize()); stringBuffer.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); raf.getChannel().read(stringBuffer, stringTable.getOffset()); stringBuffer.rewind(); ByteArrayOutputStream baos = new ByteArrayOutputStream(20); for (ELFSectionHeaderEntry eshe : entries) { baos.reset(); ((Buffer) stringBuffer).position(eshe.getNameOffset()); while(stringBuffer.position() < stringBuffer.limit()) { byte b = stringBuffer.get(); if(b == 0) { break; } else { baos.write(b); } } eshe.setName(baos.toString("ASCII")); } } public List getEntries() { return entries; } } static class ELFSectionHeaderEntry { private final int nameOffset; private String name; private final int type; private final int flags; private final int offset; private final int size; public ELFSectionHeaderEntry(boolean _64bit, ByteBuffer sectionHeaderData) { this.nameOffset = sectionHeaderData.getInt(0x0); this.type = sectionHeaderData.getInt(0x4); this.flags = (int) (_64bit ? sectionHeaderData.getLong(0x8) : sectionHeaderData.getInt(0x8)); this.offset = (int) (_64bit ? sectionHeaderData.getLong(0x18) : sectionHeaderData.getInt(0x10)); this.size = (int) (_64bit ? sectionHeaderData.getLong(0x20) : sectionHeaderData.getInt(0x14)); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNameOffset() { return nameOffset; } public int getType() { return type; } public int getFlags() { return flags; } public int getOffset() { return offset; } public int getSize() { return size; } @Override public String toString() { return "ELFSectionHeaderEntry{" + "nameIdx=" + nameOffset + ", name=" + name + ", type=" + type + ", flags=" + flags + ", offset=" + offset + ", size=" + size + '}'; } } static class ArmAeabiAttributesTag { public enum ParameterType { UINT32, NTBS, ULEB128 } private final int value; private final String name; private final ParameterType parameterType; public ArmAeabiAttributesTag(int value, String name, ParameterType parameterType) { this.value = value; this.name = name; this.parameterType = parameterType; } public int getValue() { return value; } public String getName() { return name; } public ParameterType getParameterType() { return parameterType; } @Override public String toString() { return name + " (" + value + ")"; } @Override public int hashCode() { int hash = 7; hash = 67 * hash + this.value; return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ArmAeabiAttributesTag other = (ArmAeabiAttributesTag) obj; if (this.value != other.value) { return false; } return true; } private static final List tags = new LinkedList(); private static final Map valueMap = new HashMap(); private static final Map nameMap = new HashMap(); // Enumerated from ARM IHI 0045E, 2.5 Attributes summary and history public static final ArmAeabiAttributesTag File = addTag(1, "File", ParameterType.UINT32); public static final ArmAeabiAttributesTag Section = addTag(2, "Section", ParameterType.UINT32); public static final ArmAeabiAttributesTag Symbol = addTag(3, "Symbol", ParameterType.UINT32); public static final ArmAeabiAttributesTag CPU_raw_name = addTag(4, "CPU_raw_name", ParameterType.NTBS); public static final ArmAeabiAttributesTag CPU_name = addTag(5, "CPU_name", ParameterType.NTBS); public static final ArmAeabiAttributesTag CPU_arch = addTag(6, "CPU_arch", ParameterType.ULEB128); public static final ArmAeabiAttributesTag CPU_arch_profile = addTag(7, "CPU_arch_profile", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ARM_ISA_use = addTag(8, "ARM_ISA_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag THUMB_ISA_use = addTag(9, "THUMB_ISA_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag FP_arch = addTag(10, "FP_arch", ParameterType.ULEB128); public static final ArmAeabiAttributesTag WMMX_arch = addTag(11, "WMMX_arch", ParameterType.ULEB128); public static final ArmAeabiAttributesTag Advanced_SIMD_arch = addTag(12, "Advanced_SIMD_arch", ParameterType.ULEB128); public static final ArmAeabiAttributesTag PCS_config = addTag(13, "PCS_config", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_PCS_R9_use = addTag(14, "ABI_PCS_R9_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_PCS_RW_data = addTag(15, "ABI_PCS_RW_data", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_PCS_RO_data = addTag(16, "ABI_PCS_RO_data", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_PCS_GOT_use = addTag(17, "ABI_PCS_GOT_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_PCS_wchar_t = addTag(18, "ABI_PCS_wchar_t", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_rounding = addTag(19, "ABI_FP_rounding", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_denormal = addTag(20, "ABI_FP_denormal", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_exceptions = addTag(21, "ABI_FP_exceptions", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_user_exceptions = addTag(22, "ABI_FP_user_exceptions", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_number_model = addTag(23, "ABI_FP_number_model", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_align_needed = addTag(24, "ABI_align_needed", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_align8_preserved = addTag(25, "ABI_align8_preserved", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_enum_size = addTag(26, "ABI_enum_size", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_HardFP_use = addTag(27, "ABI_HardFP_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_VFP_args = addTag(28, "ABI_VFP_args", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_WMMX_args = addTag(29, "ABI_WMMX_args", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_optimization_goals = addTag(30, "ABI_optimization_goals", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_optimization_goals = addTag(31, "ABI_FP_optimization_goals", ParameterType.ULEB128); public static final ArmAeabiAttributesTag compatibility = addTag(32, "compatibility", ParameterType.NTBS); public static final ArmAeabiAttributesTag CPU_unaligned_access = addTag(34, "CPU_unaligned_access", ParameterType.ULEB128); public static final ArmAeabiAttributesTag FP_HP_extension = addTag(36, "FP_HP_extension", ParameterType.ULEB128); public static final ArmAeabiAttributesTag ABI_FP_16bit_format = addTag(38, "ABI_FP_16bit_format", ParameterType.ULEB128); public static final ArmAeabiAttributesTag MPextension_use = addTag(42, "MPextension_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag DIV_use = addTag(44, "DIV_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag nodefaults = addTag(64, "nodefaults", ParameterType.ULEB128); public static final ArmAeabiAttributesTag also_compatible_with = addTag(65, "also_compatible_with", ParameterType.NTBS); public static final ArmAeabiAttributesTag conformance = addTag(67, "conformance", ParameterType.NTBS); public static final ArmAeabiAttributesTag T2EE_use = addTag(66, "T2EE_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag Virtualization_use = addTag(68, "Virtualization_use", ParameterType.ULEB128); public static final ArmAeabiAttributesTag MPextension_use2 = addTag(70, "MPextension_use", ParameterType.ULEB128); private static ArmAeabiAttributesTag addTag(int value, String name, ArmAeabiAttributesTag.ParameterType type) { ArmAeabiAttributesTag tag = new ArmAeabiAttributesTag(value, name, type); if (!valueMap.containsKey(tag.getValue())) { valueMap.put(tag.getValue(), tag); } if (!nameMap.containsKey(tag.getName())) { nameMap.put(tag.getName(), tag); } tags.add(tag); return tag; } public static List getTags() { return Collections.unmodifiableList(tags); } public static ArmAeabiAttributesTag getByName(String name) { return nameMap.get(name); } public static ArmAeabiAttributesTag getByValue(int value) { if (valueMap.containsKey(value)) { return valueMap.get(value); } else { ArmAeabiAttributesTag pseudoTag = new ArmAeabiAttributesTag(value, "Unknown " + value, getParameterType(value)); return pseudoTag; } } private static ArmAeabiAttributesTag.ParameterType getParameterType(int value) { // ARM IHI 0045E, 2.2.6 Coding extensibility and compatibility ArmAeabiAttributesTag tag = getByValue(value); if (tag == null) { if ((value % 2) == 0) { return ArmAeabiAttributesTag.ParameterType.ULEB128; } else { return ArmAeabiAttributesTag.ParameterType.NTBS; } } else { return tag.getParameterType(); } } } private static Map> parseArmAttributes(ByteBuffer bb) { byte format = bb.get(); if (format != 0x41) { // Version A // Not supported return Collections.EMPTY_MAP; } while (bb.position() < bb.limit()) { int posSectionStart = bb.position(); int sectionLength = bb.getInt(); if (sectionLength <= 0) { // Fail! break; } String vendorName = readNTBS(bb, null); if ("aeabi".equals(vendorName)) { return parseAEABI(bb); } ((Buffer) bb).position(posSectionStart + sectionLength); } return Collections.EMPTY_MAP; } private static Map> parseAEABI(ByteBuffer buffer) { Map> data = new HashMap>(); while (buffer.position() < buffer.limit()) { int pos = buffer.position(); int subsectionTag = readULEB128(buffer).intValue(); int length = buffer.getInt(); if (subsectionTag == (byte) 1) { data.put(subsectionTag, parseFileAttribute(buffer)); } ((Buffer) buffer).position(pos + length); } return data; } private static Map parseFileAttribute(ByteBuffer bb) { Map result = new HashMap(); while (bb.position() < bb.limit()) { int tagValue = readULEB128(bb).intValue(); ArmAeabiAttributesTag tag = ArmAeabiAttributesTag.getByValue(tagValue); switch (tag.getParameterType()) { case UINT32: result.put(tag, bb.getInt()); break; case NTBS: result.put(tag, readNTBS(bb, null)); break; case ULEB128: result.put(tag, readULEB128(bb)); break; } } return result; } private static String readNTBS(ByteBuffer buffer, Integer position) { if (position != null) { ((Buffer) buffer).position(position); } int startingPos = buffer.position(); byte currentByte; do { currentByte = buffer.get(); } while (currentByte != '\0' && buffer.position() <= buffer.limit()); int terminatingPosition = buffer.position(); byte[] data = new byte[terminatingPosition - startingPos - 1]; ((Buffer) buffer).position(startingPos); buffer.get(data); ((Buffer) buffer).position(buffer.position() + 1); try { return new String(data, "ASCII"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } } private static BigInteger readULEB128(ByteBuffer buffer) { BigInteger result = BigInteger.ZERO; int shift = 0; while (true) { byte b = buffer.get(); result = result.or(BigInteger.valueOf(b & 127).shiftLeft(shift)); if ((b & 128) == 0) { break; } shift += 7; } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy