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);
{
final int usIdx = JAVA_VERSION.lastIndexOf("_");
if( usIdx > 0 ) {
final String buildS = Platform.JAVA_VERSION.substring(usIdx+1);
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(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(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:
*
* - 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 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(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(Throwable t) {
if(DEBUG) {
System.err.println("Catched: "+t.getMessage());
t.printStackTrace();
}
} finally {
if(null != in) {
try {
in.close();
} catch (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 (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
*/
public static final String getOSAndArch(final OSType osType, final CPUType cpuType, final 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;
}
}