package jogamp.common.os; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.List; import jogamp.common.Debug; import jogamp.common.os.elf.ElfHeader; import jogamp.common.os.elf.SectionArmAttributes; import jogamp.common.os.elf.SectionHeader; import com.jogamp.common.nio.Buffers; import com.jogamp.common.os.AndroidVersion; import com.jogamp.common.os.NativeLibrary; import com.jogamp.common.os.Platform; import com.jogamp.common.os.Platform.ABIType; import com.jogamp.common.os.Platform.CPUFamily; import com.jogamp.common.os.Platform.CPUType; import com.jogamp.common.os.Platform.OSType; import com.jogamp.common.util.VersionNumber; /** * Abstract parent class of {@link Platform} initializing and holding * platform information, which are initialized independent * of other classes. * <p> * This class is not intended to be exposed in the public namespace * and solely exist to solve initialization interdependencies.<br> * Please use {@link Platform} to access the public fields! * </p> */ public abstract class PlatformPropsImpl { static final boolean DEBUG = Debug.debug("Platform"); // // static initialization order: // /** Version 1.6. As a JVM version, it enables certain JVM 1. features. */ public static final VersionNumber Version16; public static final String OS; public static final String OS_lower; public static final String OS_VERSION; public static final VersionNumber OS_VERSION_NUMBER; public static final String ARCH; public static final String ARCH_lower; public static final String JAVA_VENDOR; public static final String JAVA_VENDOR_URL; public static final String JAVA_VERSION; public static final VersionNumber JAVA_VERSION_NUMBER; public static final String JAVA_VM_NAME; public static final String JAVA_RUNTIME_NAME; /** True if having {@link java.nio.LongBuffer} and {@link java.nio.DoubleBuffer} available. */ public static final boolean JAVA_SE; /** True if being compatible w/ language level 6, e.g. JRE 1.6. Implies {@link #JAVA_SE}. <i>Note</i>: We claim Android is compatible. */ public static final boolean JAVA_6; public static final String NEWLINE; public static final boolean LITTLE_ENDIAN; public static final CPUType CPU_ARCH; public static final ABIType ABI_TYPE; public static final OSType OS_TYPE; public static final String os_and_arch; static { Version16 = new VersionNumber(1, 6, 0); // We don't seem to need an AccessController.doPrivileged() block // here as these system properties are visible even to unsigned Applets. OS = System.getProperty("os.name"); OS_lower = OS.toLowerCase(); OS_VERSION = System.getProperty("os.version"); OS_VERSION_NUMBER = new VersionNumber(OS_VERSION); ARCH = System.getProperty("os.arch"); ARCH_lower = ARCH.toLowerCase(); JAVA_VENDOR = System.getProperty("java.vendor"); JAVA_VENDOR_URL = System.getProperty("java.vendor.url"); JAVA_VERSION = System.getProperty("java.version"); JAVA_VERSION_NUMBER = new VersionNumber(JAVA_VERSION); JAVA_VM_NAME = System.getProperty("java.vm.name"); JAVA_RUNTIME_NAME = getJavaRuntimeNameImpl(); JAVA_SE = initIsJavaSE(); JAVA_6 = JAVA_SE && ( AndroidVersion.isAvailable || JAVA_VERSION_NUMBER.compareTo(Version16) >= 0 ) ; NEWLINE = System.getProperty("line.separator"); LITTLE_ENDIAN = queryIsLittleEndianImpl(); CPU_ARCH = getCPUTypeImpl(ARCH_lower); OS_TYPE = getOSTypeImpl(); ABI_TYPE = queryABITypeImpl(OS_TYPE, CPU_ARCH); os_and_arch = getOSAndArch(OS_TYPE, CPU_ARCH, ABI_TYPE); } protected PlatformPropsImpl() {} private static final String getJavaRuntimeNameImpl() { // the fast path, check property Java SE instead of traversing through the ClassLoader return AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("java.runtime.name"); } }); } private static final boolean initIsJavaSE() { if( null != JAVA_RUNTIME_NAME && JAVA_RUNTIME_NAME.indexOf("Java SE") != -1) { return true; } // probe for classes we need on a SE environment try { Class.forName("java.nio.LongBuffer"); Class.forName("java.nio.DoubleBuffer"); return true; } catch(ClassNotFoundException ex) { // continue with Java SE check } return false; } private static final boolean queryIsLittleEndianImpl() { ByteBuffer tst_b = Buffers.newDirectByteBuffer(Buffers.SIZEOF_INT); // 32bit in native order IntBuffer tst_i = tst_b.asIntBuffer(); ShortBuffer tst_s = tst_b.asShortBuffer(); tst_i.put(0, 0x0A0B0C0D); return 0x0C0D == tst_s.get(0); } private static final CPUType getCPUTypeImpl(String archLower) { if( archLower.equals("x86") || archLower.equals("i386") || archLower.equals("i486") || archLower.equals("i586") || archLower.equals("i686") ) { return CPUType.X86_32; } else if( archLower.equals("x86_64") || archLower.equals("amd64") ) { return CPUType.X86_64; } else if( archLower.equals("ia64") ) { return CPUType.IA64; } else if( archLower.equals("arm") ) { return CPUType.ARM; } else if( archLower.equals("armv5l") ) { return CPUType.ARMv5; } else if( archLower.equals("armv6l") ) { return CPUType.ARMv6; } else if( archLower.equals("armv7l") ) { return CPUType.ARMv7; } else if( archLower.equals("sparc") ) { return CPUType.SPARC_32; } else if( archLower.equals("sparcv9") ) { return CPUType.SPARCV9_64; } else if( archLower.equals("pa_risc2.0") ) { return CPUType.PA_RISC2_0; } else if( archLower.equals("ppc") ) { return CPUType.PPC; } else { throw new RuntimeException("Please port CPU detection to your platform (" + OS_lower + "/" + archLower + ")"); } } @SuppressWarnings("unused") private static final boolean contains(String data, String[] search) { if(null != data && null != search) { for(int i=0; i<search.length; i++) { if(data.indexOf(search[i]) >= 0) { return true; } } } return false; } /** * Returns the {@link ABIType} of the current platform using given {@link CPUType cpuType} * and {@link OSType osType} as a hint. * <p> * Note the following queries are performed: * <ul> * <li> not {@link CPUFamily#ARM} -> {@link ABIType#GENERIC_ABI} </li> * <li> else * <ul> * <li> {@link OSType#ANDROID} -> {@link ABIType#EABI_GNU_ARMEL} (due to EACCES, Permission denied)</li> * <li> else * <ul> * <li> Elf ARM Tags -> {@link ABIType#EABI_GNU_ARMEL}, {@link ABIType#EABI_GNU_ARMHF}</li> * <li> On Error -> {@link ABIType#EABI_GNU_ARMEL}</li> * </ul></li> * </ul></li> * </ul> * </p> * <p> * For Elf parsing either the current executable is used (Linux) or a found java/jvm native library. * </p> * <p> * Elf ARM Tags are read using {@link ElfHeader}, .. and {@link SectionArmAttributes#abiVFPArgsAcceptsVFPVariant(byte)}. * </p> * @param osType * @param cpuType * * @return */ private static final ABIType queryABITypeImpl(final OSType osType, final CPUType cpuType) { if( CPUFamily.ARM != cpuType.family ) { return ABIType.GENERIC_ABI; } if( OSType.ANDROID == osType ) { // EACCES (Permission denied) - We assume a not rooted device! return ABIType.EABI_GNU_ARMEL; } return AccessController.doPrivileged(new PrivilegedAction<ABIType>() { private final String GNU_LINUX_SELF_EXE = "/proc/self/exe"; public ABIType run() { boolean abiARM = false; boolean abiVFPArgsAcceptsVFPVariant = false; RandomAccessFile in = null; try { File file = null; if( OSType.LINUX == osType ) { file = new File(GNU_LINUX_SELF_EXE); if( !checkFileReadAccess(file) ) { file = null; } } if( null == file ) { file = findSysLib("java"); } if( null == file ) { file = findSysLib("jvm"); } if( null != file ) { in = new RandomAccessFile(file, "r"); final ElfHeader eh = ElfHeader.read(in); if(DEBUG) { System.err.println("ELF: Got HDR "+GNU_LINUX_SELF_EXE+": "+eh); } abiARM = eh.isArm(); if( abiARM ) { final SectionHeader sh = eh.getSectionHeader(SectionHeader.SHT_ARM_ATTRIBUTES); if( null != sh ) { if(DEBUG) { System.err.println("ELF: Got ARM Attribs Section Header: "+sh); } final SectionArmAttributes sArmAttrs = (SectionArmAttributes) sh.readSection(in); if(DEBUG) { System.err.println("ELF: Got ARM Attribs Section Block : "+sArmAttrs); } final SectionArmAttributes.Attribute abiVFPArgsAttr = sArmAttrs.get(SectionArmAttributes.Tag.ABI_VFP_args); if( null != abiVFPArgsAttr ) { abiVFPArgsAcceptsVFPVariant = SectionArmAttributes.abiVFPArgsAcceptsVFPVariant(abiVFPArgsAttr.getULEB128()); } } } } } catch(Throwable t) { if(DEBUG) { t.printStackTrace(); } } finally { if(null != in) { try { in.close(); } catch (IOException e) { } } } final ABIType res; if( abiARM ) { res = abiVFPArgsAcceptsVFPVariant ? ABIType.EABI_GNU_ARMHF : ABIType.EABI_GNU_ARMEL; } else { res = ABIType.GENERIC_ABI; } if(DEBUG) { System.err.println("ELF: abiARM "+abiARM+", abiVFPArgsAcceptsVFPVariant "+abiVFPArgsAcceptsVFPVariant+" -> "+res); } return res; } } ); } private static boolean checkFileReadAccess(File file) { try { return file.isFile() && file.canRead(); } catch (Throwable t) { } return false; } private static File findSysLib(String libName) { ClassLoader cl = PlatformPropsImpl.class.getClassLoader(); final List<String> possibleLibPaths = NativeLibrary.enumerateLibraryPaths(libName, libName, libName, true, cl); for(int i=0; i<possibleLibPaths.size(); i++) { final String libPath = possibleLibPaths.get(i); final File lib = new File(libPath); if(DEBUG) { System.err.println("findSysLib #"+i+": test "+lib); } if( checkFileReadAccess(lib) ) { return lib; } if(DEBUG) { System.err.println("findSysLib #"+i+": "+lib+" not readable"); } } return null; } private static final OSType getOSTypeImpl() throws RuntimeException { if ( AndroidVersion.isAvailable ) { return OSType.ANDROID; } if ( OS_lower.startsWith("linux") ) { return OSType.LINUX; } if ( OS_lower.startsWith("freebsd") ) { return OSType.FREEBSD; } if ( OS_lower.startsWith("android") ) { return OSType.ANDROID; } if ( OS_lower.startsWith("mac os x") || OS_lower.startsWith("darwin") ) { return OSType.MACOS; } if ( OS_lower.startsWith("sunos") ) { return OSType.SUNOS; } if ( OS_lower.startsWith("hp-ux") ) { return OSType.HPUX; } if ( OS_lower.startsWith("windows") ) { return OSType.WINDOWS; } if ( OS_lower.startsWith("kd") ) { return OSType.OPENKODE; } throw new RuntimeException("Please port OS detection to your platform (" + OS_lower + "/" + ARCH_lower + ")"); } /** * kick off static initialization of <i>platform property information</i> */ public static void initSingleton() { } /** * Returns the GlueGen common name for the given OSType and CPUType * as implemented in the build system in 'gluegen-cpptasks-base.xml'.<br> * * A list of currently supported <code>os.and.arch</code> strings: * <ul> * <li>freebsd-i586</li> * <li>freebsd-amd64</li> * <li>hpux-hppa</li> * <li>linux-amd64</li> * <li>linux-ia64</li> * <li>linux-i586</li> * <li>linux-armv6</li> * <li>linux-armv6hf</li> * <li>android-armv6</li> * <li>macosx-universal</li> * <li>solaris-sparc</li> * <li>solaris-sparcv9</li> * <li>solaris-amd64</li> * <li>solaris-i586</li> * <li>windows-amd64</li> * <li>windows-i586</li> * </ul> * @return */ public static final String getOSAndArch(OSType osType, CPUType cpuType, ABIType abiType) { String _os_and_arch; switch( cpuType ) { case X86_32: _os_and_arch = "i586"; break; case ARM: case ARMv5: case ARMv6: case ARMv7: _os_and_arch = "armv6"; // TODO: sync with gluegen-cpptasks-base.xml break; case SPARC_32: _os_and_arch = "sparc"; break; case PPC: _os_and_arch = "ppc"; // TODO: sync with gluegen-cpptasks-base.xml break; case X86_64: _os_and_arch = "amd64"; break; case IA64: _os_and_arch = "ia64"; break; case SPARCV9_64: _os_and_arch = "sparcv9"; break; case PA_RISC2_0: _os_and_arch = "risc2.0"; // TODO: sync with gluegen-cpptasks-base.xml break; default: throw new InternalError("Complete case block"); } if( ABIType.EABI_GNU_ARMHF == abiType ) { _os_and_arch = _os_and_arch + "hf" ; } switch( osType ) { case ANDROID: _os_and_arch = "android-" + _os_and_arch; break; case MACOS: _os_and_arch = "macosx-universal"; break; case WINDOWS: _os_and_arch = "windows-" + _os_and_arch; break; case OPENKODE: _os_and_arch = "openkode-" + _os_and_arch; // TODO: think about that break; case LINUX: _os_and_arch = "linux-" + _os_and_arch; break; case FREEBSD: _os_and_arch = "freebsd-" + _os_and_arch; break; case SUNOS: _os_and_arch = "solaris-" + _os_and_arch; break; case HPUX: _os_and_arch = "hpux-hppa"; // TODO: really only hppa ? break; default: throw new InternalError("Complete case block"); } return _os_and_arch; } }