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.ElfHeaderPart1; import jogamp.common.os.elf.ElfHeaderPart2; import jogamp.common.os.elf.SectionArmAttributes; 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} or {@link Platform.OSType#IOS} {@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); } /** * Returns {@code true} if the given {@link CPUType}s and {@link ABIType}s are compatible. */ public static final boolean isCompatible(final CPUType cpu1, final ABIType abi1, final CPUType cpu2, final ABIType abi2) { return cpu1.isCompatible(cpu2) && abi1.isCompatible(abi2); } // // 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; /** Version 1.8. As a JVM version, it enables certain JVM 1.8 features. */ public static final VersionNumber Version18; /** Version 1.9. As a JVM version, it enables certain JVM 1.9 features. Note the skipped first version number due to JEP 223. */ public static final VersionNumber Version9; 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 only if being compatible w/ language level 6, e.g. JRE 1.6. *

* Implies {@link #isJavaSE()}. *

*

* Note: We claim Android is compatible. *

*/ public static final boolean JAVA_6; /** * True only if being compatible w/ language level 9, e.g. JRE 9. *

* Implies {@link #isJavaSE()} and {@link #JAVA_6}. *

*

* Since JRE 9, the version string has dropped the major release number, * see JEP 223: http://openjdk.java.net/jeps/223 *

*/ public static final boolean JAVA_9; 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; /** * Usually GlueGen and subsequent JogAmp modules are build using dynamic libraries on supported platforms, * hence this boolean is expected to be true. *

* However, on certain systems static libraries are being used on which native JNI library loading is disabled. *

*/ public static final boolean useDynamicLibraries; static { Version16 = new VersionNumber(1, 6, 0); Version17 = new VersionNumber(1, 7, 0); Version18 = new VersionNumber(1, 8, 0); Version9 = new VersionNumber(9, 0, 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_9 = JAVA_SE && JAVA_VERSION_NUMBER.compareTo(Version9) >= 0; JAVA_6 = JAVA_SE && ( isAndroid || JAVA_9 || 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); // Hard values, i.e. w/ probing binaries final String elfCpuName; final CPUType elfCpuType; final ABIType elfABIType; final int elfLittleEndian; final boolean elfValid; { final String[] _elfCpuName = { null }; final CPUType[] _elfCpuType = { null }; final ABIType[] _elfAbiType = { null }; final int[] _elfLittleEndian = { 0 }; // 1 - little, 2 - big final boolean[] _elfValid = { false }; AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { RandomAccessFile in = null; try { final File file = queryElfFile(OS_TYPE); if(DEBUG) { System.err.println("ELF-1: Using "+file); } in = new RandomAccessFile(file, "r"); final ElfHeaderPart1 eh1 = readElfHeaderPart1(OS_TYPE, in); if(DEBUG) { System.err.println("ELF-1: Got "+eh1); } if( null != eh1 ) { final ElfHeaderPart2 eh2 = readElfHeaderPart2(eh1, in); if(DEBUG) { System.err.println("ELF-2: Got "+eh2); } if( null != eh2 ) { _elfCpuName[0] = eh2.cpuName; _elfCpuType[0] = eh2.cpuType; _elfAbiType[0] = eh2.abiType; if( eh1.isLittleEndian() ) { _elfLittleEndian[0] = 1; } else if( eh1.isBigEndian() ) { _elfLittleEndian[0] = 2; } _elfValid[0] = true; } } } catch (final Throwable t) { if(DEBUG) { t.printStackTrace(); } } finally { if(null != in) { try { in.close(); } catch (final IOException e) { } } } return null; } }); elfCpuName = _elfCpuName[0]; elfCpuType = _elfCpuType[0]; elfABIType = _elfAbiType[0]; elfLittleEndian = _elfLittleEndian[0]; elfValid = _elfValid[0]; if( DEBUG ) { System.err.println("Platform.Elf: valid "+elfValid+", elfCpuName "+elfCpuName+", cpuType "+elfCpuType+", abiType "+elfABIType+", elfLittleEndian "+elfLittleEndian); } } // Determine endianess, favor ELF value final boolean littleEndian = queryIsLittleEndianImpl(); if( elfValid ) { switch( elfLittleEndian ) { case 1: LITTLE_ENDIAN = true; break; case 2: LITTLE_ENDIAN = false; break; default: LITTLE_ENDIAN = littleEndian; break; } } else { LITTLE_ENDIAN = littleEndian; } if( DEBUG ) { System.err.println("Platform.Endian: test-little "+littleEndian+", elf[valid "+elfValid+", val "+elfLittleEndian+"] -> LITTLE_ENDIAN "+LITTLE_ENDIAN); } // Property values for comparison // We might take the property values even if ELF values are available, // since the latter only reflect the CPU/ABI version of the binary files! final String propARCH = System.getProperty("os.arch"); final String propARCH_lower = propARCH.toLowerCase(); final CPUType propCpuType = CPUType.query(propARCH_lower); final ABIType propABIType = ABIType.query(propCpuType, propARCH_lower); if( DEBUG ) { System.err.println("Platform.Property: ARCH "+propARCH+", CpuType "+propCpuType+", ABIType "+propABIType); } final int strategy; if( isAndroid ) { if( DEBUG ) { System.err.println("Android: CPU_ABI1 str "+AndroidVersion.CPU_ABI+", CPU_TYPE "+AndroidVersion.CPU_TYPE+", ABI_TYPE "+AndroidVersion.ABI_TYPE); System.err.println("Android: CPU_ABI2 str "+AndroidVersion.CPU_ABI2+", CPU_TYPE2 "+AndroidVersion.CPU_TYPE2+", ABI_TYPE2 "+AndroidVersion.ABI_TYPE2); } if( elfValid ) { if( null != AndroidVersion.CPU_TYPE && isCompatible(elfCpuType, elfABIType, AndroidVersion.CPU_TYPE, AndroidVersion.ABI_TYPE) ) { // ELF matches Android-1 ARCH = AndroidVersion.CPU_ABI; ARCH_lower = ARCH; CPU_ARCH = AndroidVersion.CPU_TYPE; strategy = 110; } else if( null != AndroidVersion.CPU_TYPE2 && isCompatible(elfCpuType, elfABIType, AndroidVersion.CPU_TYPE2, AndroidVersion.ABI_TYPE2) ) { // ELF matches Android-2 ARCH = AndroidVersion.CPU_ABI2; ARCH_lower = ARCH; CPU_ARCH = AndroidVersion.CPU_TYPE2; strategy = 111; } else { // We assume our ELF data beats AndroidVersion info (correctness) ARCH = elfCpuType.toString(); ARCH_lower = ARCH.toLowerCase(); CPU_ARCH = elfCpuType; strategy = 112; } ABI_TYPE = elfABIType; } else { if( AndroidVersion.CPU_TYPE.family == CPUFamily.ARM || AndroidVersion.CPU_TYPE.family == CPUFamily.X86 || null == AndroidVersion.CPU_TYPE2 ) { // Favor Android-1: Either b/c ARM or x86 Family, or no Android-2 ARCH = AndroidVersion.CPU_ABI; ARCH_lower = ARCH; CPU_ARCH = AndroidVersion.CPU_TYPE; ABI_TYPE = AndroidVersion.ABI_TYPE; strategy = 120; } else { // Last resort Android-2 ARCH = AndroidVersion.CPU_ABI2; ARCH_lower = ARCH; CPU_ARCH = AndroidVersion.CPU_TYPE2; ABI_TYPE = AndroidVersion.ABI_TYPE2; strategy = 121; } } } else { if( elfValid ) { if( isCompatible(elfCpuType, elfABIType, propCpuType, propABIType) ) { // Use property ARCH, compatible w/ ELF ARCH = propARCH; ARCH_lower = propARCH_lower; CPU_ARCH = propCpuType; ABI_TYPE = propABIType; strategy = 210; } else { // use ELF ARCH ARCH = elfCpuName; ARCH_lower = elfCpuName; CPU_ARCH = elfCpuType; ABI_TYPE = elfABIType; strategy = 211; } } else { // Last resort: properties ARCH = propARCH; ARCH_lower = propARCH_lower; CPU_ARCH = propCpuType; ABI_TYPE = propABIType; strategy = 220; } } if( OSType.IOS == OS_TYPE ) { useDynamicLibraries = false; } else { useDynamicLibraries = true; } if( DEBUG ) { System.err.println("Platform.Hard: ARCH "+ARCH+", CPU_ARCH "+CPU_ARCH+", ABI_TYPE "+ABI_TYPE+" - strategy "+strategy+"(isAndroid "+isAndroid+", elfValid "+elfValid+"), useDynLibs "+useDynamicLibraries); } os_and_arch = getOSAndArch(OS_TYPE, CPU_ARCH, ABI_TYPE, LITTLE_ENDIAN); } 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); } @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: *

    *
  • Linux: Current executable
  • *
  • Android: Found gluegen_rt library
  • *
  • Other: A found java/jvm native library.
  • *
*

*

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

*/ private static final File queryElfFile(final OSType osType) { File file = null; try { 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"); } } } catch(final Throwable t) { if(DEBUG) { t.printStackTrace(); } } return file; } private static final ElfHeaderPart1 readElfHeaderPart1(final OSType osType, final RandomAccessFile in) { ElfHeaderPart1 res = null; try { res = ElfHeaderPart1.read(osType, in); } catch(final Throwable t) { if(DEBUG) { System.err.println("Caught: "+t.getMessage()); t.printStackTrace(); } } return res; } private static final ElfHeaderPart2 readElfHeaderPart2(final ElfHeaderPart1 eh1, final RandomAccessFile in) { ElfHeaderPart2 res = null; try { res = ElfHeaderPart2.read(eh1, in); } catch(final Throwable t) { if(DEBUG) { System.err.println("Caught: "+t.getMessage()); t.printStackTrace(); } } 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 * {@link OSType}, {@link CPUType}, {@link ABIType} and {@code littleEndian}. *

* Consult 'gluegen/make/gluegen-cpptasks-base.xml' to complete/sync mapping! *

* * An excerpt of supported os.and.arch strings: *
    *
  • android-armv6
  • *
  • android-aarch64
  • *
  • android-x86
  • *
  • linux-armv6
  • *
  • linux-armv6hf
  • *
  • linux-i586
  • *
  • linux-ppc
  • *
  • linux-mips
  • *
  • linux-mipsel
  • *
  • linux-superh
  • *
  • linux-sparc
  • *
  • linux-aarch64
  • *
  • linux-amd64
  • *
  • linux-ppc64
  • *
  • linux-ppc64le
  • *
  • linux-mips64
  • *
  • linux-ia64
  • *
  • linux-sparcv9
  • *
  • linux-risc2.0
  • *
  • freebsd-i586
  • *
  • freebsd-amd64
  • *
  • hpux-hppa
  • *
  • 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 boolean littleEndian) { final String os_; final String _and_arch_tmp, _and_arch_final; switch( cpuType ) { case ARM: case ARMv5: case ARMv6: case ARMv7: if( ABIType.EABI_GNU_ARMHF == abiType ) { _and_arch_tmp = "armv6hf"; } else { _and_arch_tmp = "armv6"; } break; case X86_32: _and_arch_tmp = "i586"; break; case PPC: _and_arch_tmp = "ppc"; break; case MIPS_32: _and_arch_tmp = littleEndian ? "mipsel" : "mips"; break; case SuperH: _and_arch_tmp = "superh"; break; case SPARC_32: _and_arch_tmp = "sparc"; break; case ARM64: case ARMv8_A: _and_arch_tmp = "aarch64"; break; case X86_64: _and_arch_tmp = "amd64"; break; case PPC64: _and_arch_tmp = littleEndian ? "ppc64le" : "ppc64"; break; case MIPS_64: _and_arch_tmp = "mips64"; 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"; break; default: throw new InternalError("Unhandled CPUType: "+cpuType); } switch( osType ) { case ANDROID: os_ = "android"; _and_arch_final = _and_arch_tmp; break; case MACOS: os_ = "macosx"; _and_arch_final = "universal"; break; case IOS: os_ = "ios"; _and_arch_final = _and_arch_tmp; 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("Unhandled OSType: "+osType); } return os_ + "-" + _and_arch_final; } }