/** * Copyright 2013 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package jogamp.common.os.elf; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import com.jogamp.common.os.Platform.ABIType; import com.jogamp.common.os.Platform.CPUFamily; import com.jogamp.common.os.Platform.CPUType; import static jogamp.common.os.elf.IOUtils.readBytes; import static jogamp.common.os.elf.IOUtils.seek; import static jogamp.common.os.elf.IOUtils.shortToInt; import static jogamp.common.os.elf.IOUtils.toHexString; /** * ELF ABI Header Part-2 * <p> * Part-2 can only be read w/ knowledge of CPUType! * </p> * <p> * References: * <ul> * <li>http://www.sco.com/developers/gabi/latest/contents.html</li> * <li>https://en.wikipedia.org/wiki/Executable_and_Linkable_Format</li> * <li>http://linux.die.net/man/5/elf</li> * <li>http://infocenter.arm.com/ * <ul> * <li>ARM IHI 0044E, current through ABI release 2.09</li> * <li>ARM IHI 0056B: Elf for ARM 64-bit Architecture</li> * </ul></li> * </ul> * </p> */ public class ElfHeaderPart2 { /** * This masks an 8-bit version number, the version of the ABI to which this * ELF file conforms. This ABI is version 5. A value of 0 denotes unknown conformance. * {@value} */ public static final int EF_ARM_ABIMASK = 0xFF000000; public static final int EF_ARM_ABISHIFT = 24; /** * ARM ABI version 5. * {@value} */ public static final int EF_ARM_ABI5 = 0x05000000; /** * The ELF file contains BE-8 code, suitable for execution on an ARM * Architecture v6 processor. This flag must only be set on an executable file. * {@value} */ public static final int EF_ARM_BE8 = 0x00800000; /** * Legacy code (ABI version 4 and earlier) generated by gcc-arm-xxx might * use these bits. * {@value} */ public static final int EF_ARM_GCCMASK = 0x00400FFF; /** * Set in executable file headers (e_type = ET_EXEC or ET_DYN) to note that * the executable file was built to conform to the hardware floating-point * procedure-call standard. * <p> * Compatible with legacy (pre version 5) gcc use as EF_ARM_VFP_FLOAT. * </p> * <p> * Note: This is not used (anymore) * </p> * {@value} */ public static final int EF_ARM_ABI_FLOAT_HARD = 0x00000400; /** * Set in executable file headers (e_type = ET_EXEC or ET_DYN) to note * explicitly that the executable file was built to conform to the software * floating-point procedure-call standard (the base standard). If both * {@link #EF_ARM_ABI_FLOAT_HARD} and {@link #EF_ARM_ABI_FLOAT_SOFT} are clear, * conformance to the base procedure-call standard is implied. * <p> * Compatible with legacy (pre version 5) gcc use as EF_ARM_SOFT_FLOAT. * </p> * <p> * Note: This is not used (anymore) * </p> * {@value} */ public static final int EF_ARM_ABI_FLOAT_SOFT = 0x00000200; /** Public access to the elf header part-1 (CPU/ABI independent read) */ public final ElfHeaderPart1 eh1; /** Public access to the raw elf header part-2 (CPU/ABI dependent read) */ public final Ehdr_p2 raw; /** Lower case CPUType name */ public final String cpuName; public final CPUType cpuType; public final ABIType abiType; /** Public access to the {@link SectionHeader} */ public final SectionHeader[] sht; /** * Note: The input stream shall stay untouch to be able to read sections! * * @param in input stream of a binary file at position zero * @return * @throws IOException if reading from the given input stream fails or less then ELF Header size bytes * @throws IllegalArgumentException if the given input stream does not represent an ELF Header */ public static ElfHeaderPart2 read(final ElfHeaderPart1 eh1, final RandomAccessFile in) throws IOException, IllegalArgumentException { return new ElfHeaderPart2(eh1, in); } /** * @param buf ELF Header bytes * @throws IllegalArgumentException if the given buffer does not represent an ELF Header * @throws IOException */ ElfHeaderPart2(final ElfHeaderPart1 eh1, final RandomAccessFile in) throws IllegalArgumentException, IOException { this.eh1 = eh1; // // Part-2 // { final byte[] buf = new byte[Ehdr_p2.size(eh1.machDesc.ordinal())]; readBytes (in, buf, 0, buf.length); final ByteBuffer eh2Bytes = ByteBuffer.wrap(buf, 0, buf.length); raw = Ehdr_p2.create(eh1.machDesc.ordinal(), eh2Bytes); } sht = readSectionHeaderTable(in); if( CPUFamily.ARM == eh1.cpuType.family && eh1.cpuType.is32Bit ) { // AArch64, has no SHT_ARM_ATTRIBUTES or SHT_AARCH64_ATTRIBUTES SectionHeader defined in our builds! String armCpuName = null; String armCpuRawName = null; boolean abiVFPArgsAcceptsVFPVariant = false; final SectionHeader sh = getSectionHeader(SectionHeader.SHT_ARM_ATTRIBUTES); if(ElfHeaderPart1.DEBUG) { System.err.println("ELF-2: Got ARM Attribs Section Header: "+sh); } if( null != sh ) { final SectionArmAttributes sArmAttrs = (SectionArmAttributes) sh.readSection(in); if(ElfHeaderPart1.DEBUG) { System.err.println("ELF-2: Got ARM Attribs Section Block : "+sArmAttrs); } final SectionArmAttributes.Attribute cpuNameArgsAttr = sArmAttrs.get(SectionArmAttributes.Tag.CPU_name); if( null != cpuNameArgsAttr && cpuNameArgsAttr.isNTBS() ) { armCpuName = cpuNameArgsAttr.getNTBS(); } final SectionArmAttributes.Attribute cpuRawNameArgsAttr = sArmAttrs.get(SectionArmAttributes.Tag.CPU_raw_name); if( null != cpuRawNameArgsAttr && cpuRawNameArgsAttr.isNTBS() ) { armCpuRawName = cpuRawNameArgsAttr.getNTBS(); } final SectionArmAttributes.Attribute abiVFPArgsAttr = sArmAttrs.get(SectionArmAttributes.Tag.ABI_VFP_args); if( null != abiVFPArgsAttr ) { abiVFPArgsAcceptsVFPVariant = SectionArmAttributes.abiVFPArgsAcceptsVFPVariant(abiVFPArgsAttr.getULEB128()); } } { String _cpuName; if( null != armCpuName && armCpuName.length() > 0 ) { _cpuName = armCpuName.toLowerCase().replace(' ', '-'); } else if( null != armCpuRawName && armCpuRawName.length() > 0 ) { _cpuName = armCpuRawName.toLowerCase().replace(' ', '-'); } else { _cpuName = eh1.cpuName; } // 1st-try: native name CPUType _cpuType = queryCPUTypeSafe(_cpuName); if( null == _cpuType ) { // 2nd-try: "arm-" + native name _cpuName = "arm-"+_cpuName; _cpuType = queryCPUTypeSafe(_cpuName); if( null == _cpuType ) { // finally: Use ELF-1 _cpuName = eh1.cpuName; _cpuType = queryCPUTypeSafe(_cpuName); if( null == _cpuType ) { throw new InternalError("XXX: "+_cpuName+", "+eh1); // shall not happen } } } cpuName = _cpuName; cpuType = _cpuType; if(ElfHeaderPart1.DEBUG) { System.err.println("ELF-2: abiARM cpuName "+_cpuName+"[armCpuName "+armCpuName+", armCpuRawName "+armCpuRawName+"] -> "+cpuName+" -> "+cpuType+", abiVFPArgsAcceptsVFPVariant "+abiVFPArgsAcceptsVFPVariant); } } if( cpuType.is32Bit ) { // always true, see above! abiType = abiVFPArgsAcceptsVFPVariant ? ABIType.EABI_GNU_ARMHF : ABIType.EABI_GNU_ARMEL; } else { abiType = eh1.abiType; } } else { cpuName = eh1.cpuName; cpuType = eh1.cpuType; abiType = eh1.abiType; } if(ElfHeaderPart1.DEBUG) { System.err.println("ELF-2: cpuName "+cpuName+" -> "+cpuType+", "+abiType); } } private static CPUType queryCPUTypeSafe(final String cpuName) { CPUType res = null; try { res = CPUType.query(cpuName); } catch (final Throwable t) { if(ElfHeaderPart1.DEBUG) { System.err.println("ELF-2: queryCPUTypeSafe("+cpuName+"): "+t.getMessage()); } } return res; } public final short getSize() { return raw.getE_ehsize(); } /** Returns the processor-specific flags associated with the file. */ public final int getFlags() { return raw.getE_flags(); } /** Returns the ARM EABI version from {@link #getFlags() flags}, maybe 0 if not an ARM EABI. */ public byte getArmABI() { return (byte) ( ( ( EF_ARM_ABIMASK & raw.getE_flags() ) >> EF_ARM_ABISHIFT ) & 0xff ); } /** Returns the ARM EABI legacy GCC {@link #getFlags() flags}, maybe 0 if not an ARM EABI or not having legacy GCC flags. */ public int getArmLegacyGCCFlags() { final int f = raw.getE_flags(); return 0 != ( EF_ARM_ABIMASK & f ) ? ( EF_ARM_GCCMASK & f ) : 0; } /** * Returns the ARM EABI float mode from {@link #getFlags() flags}, * i.e. 1 for {@link #EF_ARM_ABI_FLOAT_SOFT}, 2 for {@link #EF_ARM_ABI_FLOAT_HARD} * or 0 for none. * <p> * Note: This is not used (anymore) * </p> */ public byte getArmFloatMode() { final int f = raw.getE_flags(); if( 0 != ( EF_ARM_ABIMASK & f ) ) { if( ( EF_ARM_ABI_FLOAT_HARD & f ) != 0 ) { return 2; } if( ( EF_ARM_ABI_FLOAT_SOFT & f ) != 0 ) { return 1; } } return 0; } /** Returns the 1st occurence of matching SectionHeader {@link SectionHeader#getType() type}, or null if not exists. */ public final SectionHeader getSectionHeader(final int type) { for(int i=0; i<sht.length; i++) { final SectionHeader sh = sht[i]; if( sh.getType() == type ) { return sh; } } return null; } /** Returns the 1st occurence of matching SectionHeader {@link SectionHeader#getName() name}, or null if not exists. */ public final SectionHeader getSectionHeader(final String name) { for(int i=0; i<sht.length; i++) { final SectionHeader sh = sht[i]; if( sh.getName().equals(name) ) { return sh; } } return null; } @Override public final String toString() { final int armABI = getArmABI(); final String armFlagsS; if( 0 != armABI ) { armFlagsS=", arm[abi "+armABI+", lGCC "+getArmLegacyGCCFlags()+", float "+getArmFloatMode()+"]"; } else { armFlagsS=""; } return "ELF-2["+cpuType+", "+abiType+", flags["+toHexString(getFlags())+armFlagsS+"], sh-num "+sht.length+"]"; } final SectionHeader[] readSectionHeaderTable(final RandomAccessFile in) throws IOException, IllegalArgumentException { // positioning { final long off = raw.getE_shoff(); // absolute offset if( 0 == off ) { return new SectionHeader[0]; } seek(in, off); } final SectionHeader[] sht; final int strndx = raw.getE_shstrndx(); final int size = raw.getE_shentsize(); final int num; int i; if( 0 == raw.getE_shnum() ) { // Read 1st table 1st and use it's sh_size final byte[] buf0 = new byte[size]; readBytes(in, buf0, 0, size); final SectionHeader sh0 = new SectionHeader(this, buf0, 0, size, 0); num = (int) sh0.raw.getSh_size(); if( 0 >= num ) { throw new IllegalArgumentException("EHdr sh_num == 0 and 1st SHdr size == 0"); } sht = new SectionHeader[num]; sht[0] = sh0; i=1; } else { num = raw.getE_shnum(); sht = new SectionHeader[num]; i=0; } for(; i<num; i++) { final byte[] buf = new byte[size]; readBytes(in, buf, 0, size); sht[i] = new SectionHeader(this, buf, 0, size, i); } if( SectionHeader.SHN_UNDEF != strndx ) { // has section name string table if( shortToInt(SectionHeader.SHN_LORESERVE) <= strndx ) { throw new InternalError("TODO strndx: "+SectionHeader.SHN_LORESERVE+" < "+strndx); } final SectionHeader strShdr = sht[strndx]; if( SectionHeader.SHT_STRTAB != strShdr.getType() ) { throw new IllegalArgumentException("Ref. string Shdr["+strndx+"] is of type "+strShdr.raw.getSh_type()); } final Section strS = strShdr.readSection(in); for(i=0; i<num; i++) { sht[i].initName(strS, sht[i].raw.getSh_name()); } } return sht; } }