From d33321e4520d2a1d656655794b914486050ddf6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Wed, 16 May 2018 20:49:22 +0200 Subject: [PATCH] Use Arm EABI section to detect hard-/softfloat usage This is an update to PR 863: Old toolchains produce binaries without hard-/softfloat markers. Rasbian is missinng the markers and the oracle JDK is also affected. For hardfloat detection now also the Arm EABI section is also considered. If either the elf hardfloat flag is set or the Arm EABI section indicates hardfloat the binary is reported as hardfloat. --- CHANGES.md | 1 + .../sun/jna/BuildArmSoftFloatDetector.java | 2 +- src/com/sun/jna/ELFAnalyser.java | 450 +++++++++++++++++- src/com/sun/jna/Platform.java | 2 +- test/com/sun/jna/ELFAnalyserTest.java | 57 ++- 5 files changed, 477 insertions(+), 35 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e46a394df9..c8767e1a25 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -44,6 +44,7 @@ Bug Fixes
  • Change into the expanded directory and run `bash build.sh`.
  • The resulting `sunos-x86.jar` is copied back to the original build system to `lib/native/sunos-x86.jar`
  • - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#958](https://github.com/java-native-access/jna/issues/958): Update for PR 863: Old toolchains produce binaries without hard-/softfloat markers. Rasbian is missinng the markers and the oracle JDK is also affected. For hardfloat detection now also the Arm EABI section is also considered - [@matthiasblaesing](https://github.com/matthiasblaesing). Breaking Changes ---------------- diff --git a/ant-tools-src/com/sun/jna/BuildArmSoftFloatDetector.java b/ant-tools-src/com/sun/jna/BuildArmSoftFloatDetector.java index 6fa141dd15..fa851e9a7a 100644 --- a/ant-tools-src/com/sun/jna/BuildArmSoftFloatDetector.java +++ b/ant-tools-src/com/sun/jna/BuildArmSoftFloatDetector.java @@ -60,7 +60,7 @@ public void execute() throws IOException { // The self.getCanonicalPath() resolves the symblink to the backing // realfile and passes that to the detection routines ELFAnalyser ahfd = ELFAnalyser.analyse(self.getCanonicalPath()); - result = ahfd.isArmSoftFloat(); + result = ! ahfd.isArmHardFloat(); } catch (IOException ex) { result = false; } diff --git a/src/com/sun/jna/ELFAnalyser.java b/src/com/sun/jna/ELFAnalyser.java index ba843821c7..85f4aceb2f 100644 --- a/src/com/sun/jna/ELFAnalyser.java +++ b/src/com/sun/jna/ELFAnalyser.java @@ -1,15 +1,25 @@ 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 { @@ -30,19 +40,20 @@ class ELFAnalyser { 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 armHardFloatFlag = false; + private boolean armSoftFloatFlag = false; + private boolean armEabiAapcsVfp = false; private boolean arm = false; /** @@ -75,20 +86,32 @@ 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 + * procedure-call standard via ELF flags */ - public boolean isArmHardFloat() { - return armHardFloat; + public boolean isArmHardFloatFlag() { + return armHardFloatFlag; } /** * @return true if file was detected to conform to the software floating-point - * procedure-call standard + * procedure-call standard via ELF flags */ - public boolean isArmSoftFloat() { - return armSoftFloat; + public boolean isArmSoftFloatFlag() { + return armSoftFloatFlag; } /** @@ -123,26 +146,423 @@ private void runDetection() throws IOException { // 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); - bigEndian = headerData.get(5) == EI_DATA_BIG_ENDIAN; + 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); - armHardFloat = (flags & EF_ARM_ABI_FLOAT_HARD) == EF_ARM_ABI_FLOAT_HARD; - armSoftFloat = !armHardFloat; + 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 + // 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; } } diff --git a/src/com/sun/jna/Platform.java b/src/com/sun/jna/Platform.java index 7a5a29c19b..7bd94799a7 100644 --- a/src/com/sun/jna/Platform.java +++ b/src/com/sun/jna/Platform.java @@ -268,7 +268,7 @@ static boolean isSoftFloat() { File self = new File("/proc/self/exe"); if (self.exists()) { ELFAnalyser ahfd = ELFAnalyser.analyse(self.getCanonicalPath()); - return ahfd.isArmSoftFloat(); + return ! ahfd.isArmHardFloat(); } } catch (IOException ex) { // asume hardfloat diff --git a/test/com/sun/jna/ELFAnalyserTest.java b/test/com/sun/jna/ELFAnalyserTest.java index 18c74da91e..29c47bbb27 100644 --- a/test/com/sun/jna/ELFAnalyserTest.java +++ b/test/com/sun/jna/ELFAnalyserTest.java @@ -10,31 +10,33 @@ public class ELFAnalyserTest { - + private static final File TEST_RESOURCES = new File("build/test-resources"); private static final File WIN32_LIB = new File(TEST_RESOURCES, "win32-x86-64.dll"); private static final File LINUX_ARMEL_LIB = new File(TEST_RESOURCES, "linux-armel.so"); private static final File LINUX_ARMEL_NOFLAG_LIG = new File(TEST_RESOURCES, "linux-armel-noflag.so"); + private static final File LINUX_ARMHF_NOFLAG_LIG = new File(TEST_RESOURCES, "linux-armhf-noflag.so"); private static final File LINUX_ARMHF_LIB = new File(TEST_RESOURCES, "linux-armhf.so"); private static final File LINUX_AMD64_LIB = new File(TEST_RESOURCES, "linux-amd64.so"); - + @BeforeClass public static void initClass() throws IOException { TEST_RESOURCES.mkdirs(); - + extractTestFile(WIN32_LIB); extractTestFile(LINUX_ARMEL_LIB); extractTestFile(LINUX_ARMHF_LIB); extractTestFile(LINUX_AMD64_LIB); - makeLinuxArmelNoflagLib(LINUX_ARMEL_LIB, LINUX_ARMEL_NOFLAG_LIG); + makeLinuxArmNoflagLib(LINUX_ARMEL_LIB, LINUX_ARMEL_NOFLAG_LIG); + makeLinuxArmNoflagLib(LINUX_ARMHF_LIB, LINUX_ARMHF_NOFLAG_LIG); } - + @Test public void testNonELF() throws IOException { ELFAnalyser ahfd = ELFAnalyser.analyse(WIN32_LIB.getAbsolutePath()); assertFalse(ahfd.isELF()); } - + @Test public void testNonArm() throws IOException { ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_AMD64_LIB.getAbsolutePath()); @@ -42,24 +44,28 @@ public void testNonArm() throws IOException { assertFalse(ahfd.isArm()); assertTrue(ahfd.is64Bit()); } - + @Test public void testArmhf() throws IOException { ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_ARMHF_LIB.getAbsolutePath()); assertTrue(ahfd.isELF()); assertTrue(ahfd.isArm()); assertFalse(ahfd.is64Bit()); - assertFalse(ahfd.isArmSoftFloat()); + assertFalse(ahfd.isArmSoftFloatFlag()); + assertTrue(ahfd.isArmHardFloatFlag()); + assertTrue(ahfd.isArmEabiAapcsVfp()); assertTrue(ahfd.isArmHardFloat()); } - + @Test public void testArmel() throws IOException { ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_ARMEL_LIB.getAbsolutePath()); assertTrue(ahfd.isELF()); assertTrue(ahfd.isArm()); assertFalse(ahfd.is64Bit()); - assertTrue(ahfd.isArmSoftFloat()); + assertTrue(ahfd.isArmSoftFloatFlag()); + assertFalse(ahfd.isArmHardFloatFlag()); + assertFalse(ahfd.isArmEabiAapcsVfp()); assertFalse(ahfd.isArmHardFloat()); } @@ -69,10 +75,24 @@ public void testArmelNoflag() throws IOException { assertTrue(ahfd.isELF()); assertTrue(ahfd.isArm()); assertFalse(ahfd.is64Bit()); - assertTrue(ahfd.isArmSoftFloat()); + assertFalse(ahfd.isArmSoftFloatFlag()); + assertFalse(ahfd.isArmHardFloatFlag()); + assertFalse(ahfd.isArmEabiAapcsVfp()); assertFalse(ahfd.isArmHardFloat()); } - + + @Test + public void testArmhfNoflag() throws IOException { + ELFAnalyser ahfd = ELFAnalyser.analyse(LINUX_ARMHF_NOFLAG_LIG.getAbsolutePath()); + assertTrue(ahfd.isELF()); + assertTrue(ahfd.isArm()); + assertFalse(ahfd.is64Bit()); + assertFalse(ahfd.isArmSoftFloatFlag()); + assertFalse(ahfd.isArmHardFloatFlag()); + assertTrue(ahfd.isArmEabiAapcsVfp()); + assertTrue(ahfd.isArmHardFloat()); + } + @AfterClass public static void afterClass() throws IOException { LINUX_AMD64_LIB.delete(); @@ -80,9 +100,10 @@ public static void afterClass() throws IOException { LINUX_ARMEL_LIB.delete(); WIN32_LIB.delete(); LINUX_ARMEL_NOFLAG_LIG.delete(); + LINUX_ARMHF_NOFLAG_LIG.delete(); TEST_RESOURCES.delete(); } - + private static void extractTestFile(File outputFile) throws IOException { String inputPath = "/com/sun/jna/data/" + outputFile.getName(); InputStream is = ELFAnalyserTest.class.getResourceAsStream(inputPath); @@ -100,10 +121,10 @@ private static void extractTestFile(File outputFile) throws IOException { // The e_flags for elf arm binaries begin at an offset of 0x24 bytes. // The procedure call standard is coded on the second byte. - private static void makeLinuxArmelNoflagLib(File sourceFile, File outputFile) throws IOException { + private static void makeLinuxArmNoflagLib(File sourceFile, File outputFile) throws IOException { final int POS_ABI_FLOAT_BIT = (byte) 0x25; copyFile(sourceFile, outputFile); - + RandomAccessFile out = new RandomAccessFile(outputFile, "rw"); out.seek(POS_ABI_FLOAT_BIT); @@ -111,7 +132,7 @@ private static void makeLinuxArmelNoflagLib(File sourceFile, File outputFile) th out.close(); } - + private static void copyFile(File sourceFile, File outputFile) throws IOException { InputStream inputStream = null; OutputStream outputStream = null; @@ -124,7 +145,7 @@ private static void copyFile(File sourceFile, File outputFile) throws IOExceptio closeSilently(outputStream); } } - + private static void copyStream(InputStream is, OutputStream os) throws IOException { int read; byte[] buffer = new byte[1024 * 1024]; @@ -132,7 +153,7 @@ private static void copyStream(InputStream is, OutputStream os) throws IOExcepti os.write(buffer, 0, read); } } - + private static void closeSilently(Closeable closeable) { if(closeable == null) { return;