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. *

* This class is not intended to be exposed in the public namespace * and solely exist to solve initialization interdependencies.
* Please use {@link Platform} to access the public fields! *

*/ public abstract class PlatformPropsImpl { static final boolean DEBUG = Debug.debug("Platform"); /** Selected {@link Platform.OSType#MACOS} {@link VersionNumber}s. */ public static class OSXVersion { /** OSX Tiger, i.e. 10.4.0 */ public static final VersionNumber Tiger = new VersionNumber(10,4,0); /** OSX Lion, i.e. 10.7.0 */ public static final VersionNumber Lion = new VersionNumber(10,7,0); /** OSX Mavericks, i.e. 10.9.0 */ public static final VersionNumber Mavericks = new VersionNumber(10,9,0); } // // static initialization order: // /** Version 1.6. As a JVM version, it enables certain JVM 1.6 features. */ public static final VersionNumber Version16; /** Version 1.7. As a JVM version, it enables certain JVM 1.7 features. */ public static final VersionNumber Version17; 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 int JAVA_VERSION_UPDATE; 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}. Note: We claim Android is compatible. */ public static final boolean JAVA_6; public static final String NEWLINE; public static final boolean LITTLE_ENDIAN; /* pp */ static final CPUType sCpuType; 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); Version17 = new VersionNumber(1, 7, 0); // We don't seem to need an AccessController.doPrivileged() block // here as these system properties are visible even to unsigned Applets. final boolean isAndroid = AndroidVersion.isAvailable; // also triggers it's static initialization 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); { int usIdx = JAVA_VERSION.lastIndexOf("-u"); // OpenJDK update notation int usOff; if( 0 < usIdx ) { usOff = 2; } else { usIdx = JAVA_VERSION.lastIndexOf("_"); // Oracle update notation usOff = 1; } if( 0 < usIdx ) { final String buildS = PlatformPropsImpl.JAVA_VERSION.substring(usIdx+usOff); final VersionNumber update = new VersionNumber(buildS); JAVA_VERSION_UPDATE = update.getMajor(); } else { JAVA_VERSION_UPDATE = 0; } } JAVA_VM_NAME = System.getProperty("java.vm.name"); JAVA_RUNTIME_NAME = getJavaRuntimeNameImpl(); JAVA_SE = initIsJavaSE(); JAVA_6 = JAVA_SE && ( isAndroid || JAVA_VERSION_NUMBER.compareTo(Version16) >= 0 ) ; NEWLINE = System.getProperty("line.separator"); OS = System.getProperty("os.name"); OS_lower = OS.toLowerCase(); OS_VERSION = System.getProperty("os.version"); OS_VERSION_NUMBER = new VersionNumber(OS_VERSION); OS_TYPE = getOSTypeImpl(OS_lower, isAndroid); LITTLE_ENDIAN = queryIsLittleEndianImpl(); // Soft values, i.e. w/o probing binaries final String sARCH = System.getProperty("os.arch"); final String sARCH_lower = sARCH.toLowerCase(); sCpuType = getCPUTypeImpl(sARCH_lower); if( DEBUG ) { System.err.println("Platform.Soft: str "+sARCH+", cpuType "+sCpuType); } // Hard values, i.e. w/ probing binaries // // FIXME / HACK: // We use sCPUType for MachineDescriptionRuntime.getStatic() // until we have determined the final CPU_TYPE, etc. // MachineDescriptionRuntime gets notified via MachineDescriptionRuntime.notifyPropsInitialized() below. // // We could use Elf Ehdr's machine value to determine the bit-size // used for it's offset table! // However, 'os.arch' should be a good guess for this task. final CPUType ehCpuType; final ABIType ehAbiType; final boolean ehValid; { final CPUType[] _ehCpuType = { null }; final ABIType[] _ehAbiType = { null }; final ElfHeader eh = queryABITypeImpl(OS_TYPE, _ehCpuType, _ehAbiType); if( null != eh && null != _ehCpuType[0] && null != _ehAbiType[0] ) { ehCpuType = _ehCpuType[0]; ehAbiType = _ehAbiType[0]; if( isAndroid ) { if( DEBUG ) { System.err.println("Android: CPU_ABI1 str "+AndroidVersion.CPU_ABI+", cpu "+AndroidVersion.CPU_TYPE+", abi "+AndroidVersion.ABI_TYPE); System.err.println("Android: CPU_ABI2 str "+AndroidVersion.CPU_ABI2+", cpu "+AndroidVersion.CPU_TYPE2+", abi "+AndroidVersion.ABI_TYPE2); } final CPUFamily aCpuFamily1 = null != AndroidVersion.CPU_TYPE ? AndroidVersion.CPU_TYPE.family : null; final CPUFamily aCpuFamily2 = null != AndroidVersion.CPU_TYPE2 ? AndroidVersion.CPU_TYPE2.family : null; if( ehCpuType.family != aCpuFamily1 && ehCpuType.family != aCpuFamily2 ) { // Ooops ! ehValid = false; } else { ehValid = true; } } else { if( ehCpuType.family != sCpuType.family ) { // Ooops ! ehValid = false; } else { ehValid = true; } } if( DEBUG ) { System.err.println("Platform.Elf: cpuType "+ehCpuType+", abiType "+ehAbiType+", valid "+ehValid); } } else { ehCpuType = null; ehAbiType = null; ehValid = false; if( DEBUG ) { System.err.println("Platform.Elf: n/a"); } } } if( isAndroid ) { if( ehValid ) { if( ehCpuType.family == AndroidVersion.CPU_TYPE.family ) { ARCH = AndroidVersion.CPU_ABI; CPU_ARCH = AndroidVersion.CPU_TYPE; } else { ARCH = AndroidVersion.CPU_ABI2; CPU_ARCH = AndroidVersion.CPU_TYPE2; } ABI_TYPE = ehAbiType; } else { // default if( AndroidVersion.CPU_TYPE.family == CPUFamily.ARM || null == AndroidVersion.CPU_TYPE2 ) { ARCH = AndroidVersion.CPU_ABI; CPU_ARCH = AndroidVersion.CPU_TYPE; ABI_TYPE = AndroidVersion.ABI_TYPE; } else { ARCH = AndroidVersion.CPU_ABI2; CPU_ARCH = AndroidVersion.CPU_TYPE2; ABI_TYPE = AndroidVersion.ABI_TYPE2; } } ARCH_lower = ARCH; } else { ARCH = sARCH; ARCH_lower = sARCH_lower; if( ehValid && CPUFamily.ARM == ehCpuType.family ) { // Use Elf for ARM CPU_ARCH = ehCpuType; ABI_TYPE = ehAbiType; } else { // Otherwise trust detailed os.arch (?) CPU_ARCH = sCpuType; ABI_TYPE = ABIType.GENERIC_ABI; } } MachineDescriptionRuntime.notifyPropsInitialized(); 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() { @Override 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(final ClassNotFoundException ex) { // continue with Java SE check } return false; } private static final boolean queryIsLittleEndianImpl() { final ByteBuffer tst_b = Buffers.newDirectByteBuffer(Buffers.SIZEOF_INT); // 32bit in native order final IntBuffer tst_i = tst_b.asIntBuffer(); final ShortBuffer tst_s = tst_b.asShortBuffer(); tst_i.put(0, 0x0A0B0C0D); return 0x0C0D == tst_s.get(0); } private static final CPUType getCPUTypeImpl(final String archLower) { if( archLower.equals("x86") || // jvm + android 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") || archLower.equals("armeabi") || // android archLower.equals("armeabi-v7a") ) { // android 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 if( archLower.equals("mips") ) { // android return CPUType.MIPS_32; } else { throw new RuntimeException("Please port CPU detection to your platform (" + OS_lower + "/" + archLower + ")"); } } @SuppressWarnings("unused") private static final boolean contains(final String data, final String[] search) { if(null != data && null != search) { for(int i=0; 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. *

* For Elf parsing one of the following binaries is used: *

*

*

* Elf ARM Tags are read using {@link ElfHeader}, .. and {@link SectionArmAttributes#abiVFPArgsAcceptsVFPVariant(byte)}. *

*/ private static final ElfHeader queryABITypeImpl(final OSType osType, final CPUType[] cpuType, final ABIType[] abiType) { return AccessController.doPrivileged(new PrivilegedAction() { @Override public ElfHeader run() { ElfHeader res = null; try { File file = null; if( OSType.ANDROID == osType ) { file = new File(NativeLibrary.findLibrary("gluegen-rt", PlatformPropsImpl.class.getClassLoader())); } else { if( OSType.LINUX == osType ) { file = new File("/proc/self/exe"); if( !checkFileReadAccess(file) ) { file = null; } } if( null == file ) { file = findSysLib("java"); } if( null == file ) { file = findSysLib("jvm"); } } if( null != file ) { res = queryABITypeImpl(file, cpuType, abiType); } } catch(final Throwable t) { if(DEBUG) { t.printStackTrace(); } } return res; } } ); } private static final ElfHeader queryABITypeImpl(final File file, final CPUType[] cpuType, final ABIType[] abiType) { ElfHeader res = null; RandomAccessFile in = null; try { in = new RandomAccessFile(file, "r"); final ElfHeader eh = ElfHeader.read(in); if(DEBUG) { System.err.println("ELF: Got HDR "+file+": "+eh); } if( eh.isArm() ) { boolean abiVFPArgsAcceptsVFPVariant = false; 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()); } } cpuType[0] = CPUType.ARM; // lowest denominator, ok for us abiType[0] = abiVFPArgsAcceptsVFPVariant ? ABIType.EABI_GNU_ARMHF : ABIType.EABI_GNU_ARMEL; if(DEBUG) { System.err.println("ELF: abiARM, abiVFPArgsAcceptsVFPVariant "+abiVFPArgsAcceptsVFPVariant); } } else if ( eh.isX86_64() ) { cpuType[0] = CPUType.X86_64; abiType[0] = ABIType.GENERIC_ABI; } else if ( eh.isX86_32() ) { cpuType[0] = CPUType.X86_32; abiType[0] = ABIType.GENERIC_ABI; } else if ( eh.isIA64() ) { cpuType[0] = CPUType.IA64; abiType[0] = ABIType.GENERIC_ABI; } else if ( eh.isMips() ) { cpuType[0] = CPUType.MIPS_32; // FIXME abiType[0] = ABIType.GENERIC_ABI; } res = eh; } catch(final Throwable t) { if(DEBUG) { System.err.println("Caught: "+t.getMessage()); t.printStackTrace(); } } finally { if(null != in) { try { in.close(); } catch (final IOException e) { } } } if(DEBUG) { System.err.println("ELF: res "+res+", cpuType "+cpuType[0]+", abiType "+abiType[0]); } return res; } private static boolean checkFileReadAccess(final File file) { try { return file.isFile() && file.canRead(); } catch (final Throwable t) { } return false; } private static File findSysLib(final String libName) { final ClassLoader cl = PlatformPropsImpl.class.getClassLoader(); final List possibleLibPaths = NativeLibrary.enumerateLibraryPaths(libName, libName, libName, true, cl); for(int i=0; iplatform property information */ 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'.
* * A list of currently supported os.and.arch strings: *
    *
  • freebsd-i586
  • *
  • freebsd-amd64
  • *
  • hpux-hppa
  • *
  • linux-amd64
  • *
  • linux-ia64
  • *
  • linux-i586
  • *
  • linux-armv6
  • *
  • linux-armv6hf
  • *
  • android-armv6
  • *
  • macosx-universal
  • *
  • solaris-sparc
  • *
  • solaris-sparcv9
  • *
  • solaris-amd64
  • *
  • solaris-i586
  • *
  • windows-amd64
  • *
  • windows-i586
  • *
* @return The os.and.arch value. */ public static final String getOSAndArch(final OSType osType, final CPUType cpuType, final ABIType abiType) { final String os_; final String _and_arch_tmp, _and_arch_final; switch( cpuType ) { case X86_32: _and_arch_tmp = "i586"; break; case ARM: case ARMv5: case ARMv6: case ARMv7: if( ABIType.EABI_GNU_ARMHF == abiType ) { _and_arch_tmp = "armv6hf" ; // TODO: sync with gluegen-cpptasks-base.xml } else { _and_arch_tmp = "armv6"; // TODO: sync with gluegen-cpptasks-base.xml } break; case SPARC_32: _and_arch_tmp = "sparc"; break; case PPC: _and_arch_tmp = "ppc"; // TODO: sync with gluegen-cpptasks-base.xml break; case X86_64: _and_arch_tmp = "amd64"; break; case IA64: _and_arch_tmp = "ia64"; break; case SPARCV9_64: _and_arch_tmp = "sparcv9"; break; case PA_RISC2_0: _and_arch_tmp = "risc2.0"; // TODO: sync with gluegen-cpptasks-base.xml break; default: throw new InternalError("Complete case block"); } switch( osType ) { case ANDROID: os_ = "android"; _and_arch_final = _and_arch_tmp; break; case MACOS: os_ = "macosx"; _and_arch_final = "universal"; break; case WINDOWS: os_ = "windows"; _and_arch_final = _and_arch_tmp; break; case OPENKODE: os_ = "openkode"; // TODO: think about that _and_arch_final = _and_arch_tmp; break; case LINUX: os_ = "linux"; _and_arch_final = _and_arch_tmp; break; case FREEBSD: os_ = "freebsd"; _and_arch_final = _and_arch_tmp; break; case SUNOS: os_ = "solaris"; _and_arch_final = _and_arch_tmp; break; case HPUX: os_ = "hpux"; _and_arch_final = "hppa"; // TODO: really only hppa ? break; default: throw new InternalError("Complete case block"); } return os_ + "-" + _and_arch_final; } }