jaulib v1.3.0
Jau Support Library (C++, Java, ..)
PlatformProps.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2010-2023 Gothel Software e.K.
4 * Copyright (c) 2010 JogAmp Community.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25package org.jau.sys;
26
27import java.io.File;
28import java.io.IOException;
29import java.io.RandomAccessFile;
30import java.nio.ByteBuffer;
31import java.nio.ByteOrder;
32import java.nio.IntBuffer;
33import java.nio.ShortBuffer;
34import java.nio.file.FileSystems;
35import java.nio.file.Files;
36import java.nio.file.Path;
37import java.security.PrivilegedAction;
38import java.util.List;
39
40import org.jau.io.Buffers;
41import org.jau.sec.SecurityUtil;
42import org.jau.sys.PlatformTypes.ABIType;
43import org.jau.sys.PlatformTypes.CPUType;
44import org.jau.sys.PlatformTypes.OSType;
45import org.jau.sys.elf.ElfHeaderPart1;
46import org.jau.sys.elf.ElfHeaderPart2;
47import org.jau.sys.elf.SectionArmAttributes;
48import org.jau.util.VersionNumber;
49
50/**
51 * Platform Properties derived from Java properties
52 */
53public class PlatformProps {
54 //
55 // static initialization order:
56 //
57
59
60 /**
61 * True only if being compatible w/ language level 9, e.g. JRE 9.
62 * <p>
63 * Since JRE 9, the version string has dropped the major release number,
64 * see JEP 223: http://openjdk.java.net/jeps/223
65 * </p>
66 */
67 public static final boolean JAVA_9;
68
69 /**
70 * True only if being compatible w/ language level 17, e.g. JRE 17 (LTS).
71 * <p>
72 * Implies {@link #JAVA_9}
73 * </p>
74 */
75 public static final boolean JAVA_17;
76
77 /**
78 * True only if being compatible w/ language level 21, e.g. JRE 21 (LTS).
79 * <p>
80 * Implies {@link #JAVA_17}, {@link #JAVA_9}
81 * </p>
82 */
83 public static final boolean JAVA_21;
84
85 public static final String NEWLINE;
86
87 public static final String JAVA_VENDOR;
88 public static final String JAVA_VM_NAME;
89
90 public static final boolean DEBUG;
91 public static final String JAVA_RUNTIME_NAME;
92
93 /** Lower case system property derived from '{@code os.name}'. */
94 public static final String os_name;
95
96 /** Lower case system property derived from ELF or '{@code os.arch}' */
97 public static final String os_arch;
98
99 public static final VersionNumber os_version;
100
101 public static final OSType OS;
102
103 public static final boolean LITTLE_ENDIAN;
104 public static final CPUType CPU;
105 public static final ABIType ABI;
106
107 /** Static (not runtime) determined {@link MachineDataInfo}. */
108 public static final MachineDataInfo MACH_DESC_STAT;
109
110 /**
111 * Unique platform denominator composed as '{@link #os_name}' + '-' + '{@link #os_arch}'.
112 */
113 public static final String os_and_arch;
114
115 static {
116 JAVA_VERSION_NUMBER = new VersionNumber(System.getProperty("java.version"));
117 if( JAVA_VERSION_NUMBER.compareTo(new VersionNumber(21, 0, 0)) >= 0 ) {
118 JAVA_21 = true;
119 JAVA_17 = true;
120 JAVA_9 = true;
121 } else if( JAVA_VERSION_NUMBER.compareTo(new VersionNumber(17, 0, 0)) >= 0 ) {
122 JAVA_21 = false;
123 JAVA_17 = true;
124 JAVA_9 = true;
125 } else if( JAVA_VERSION_NUMBER.compareTo(new VersionNumber(9, 0, 0)) >= 0 ) {
126 JAVA_21 = false;
127 JAVA_17 = false;
128 JAVA_9 = true;
129 } else {
130 // we probably don't support anything below 9
131 JAVA_21 = false;
132 JAVA_17 = false;
133 JAVA_9 = false;
134 }
135 NEWLINE = System.getProperty("line.separator");
136
137 JAVA_VENDOR = System.getProperty("java.vendor");
138 JAVA_VM_NAME = System.getProperty("java.vm.name");
139 }
140
141 static {
142 DEBUG = Debug.debug("Platform");
143
144 final String os_name_prop;
145 final String os_arch_prop;
146 {
147 final String[] props =
148 SecurityUtil.doPrivileged(new PrivilegedAction<String[]>() {
149 @Override
150 public String[] run() {
151 final String[] props = new String[4];
152 int i=0;
153 props[i++] = System.getProperty("java.runtime.name"); // 0
154 props[i++] = System.getProperty("os.name").toLowerCase(); // 1
155 props[i++] = System.getProperty("os.arch").toLowerCase(); // 2
156 props[i++] = System.getProperty("os.version"); // 3
157 return props;
158 }
159 });
160 int i=0;
161 JAVA_RUNTIME_NAME = props[i++];
162 os_name_prop = props[i++];
163 os_arch_prop = props[i++];
164 os_version = new VersionNumber(props[i++]);
165 }
166 OS = OSType.query(os_name_prop);
167
168 // Hard values, i.e. w/ probing binaries
169 final String elfCpuName;
170 final CPUType elfCpuType;
171 final ABIType elfABIType;
172 final int elfLittleEndian;
173 final boolean elfValid;
174 {
175 final String[] _elfCpuName = { null };
176 final CPUType[] _elfCpuType = { null };
177 final ABIType[] _elfAbiType = { null };
178 final int[] _elfLittleEndian = { 0 }; // 1 - little, 2 - big
179 final boolean[] _elfValid = { false };
180 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
181 @Override
182 public Object run() {
183 RandomAccessFile in = null;
184 try {
185 final File file = queryElfFile(OS);
186 if(DEBUG) {
187 System.err.println("ELF-1: Using "+file);
188 }
189 in = new RandomAccessFile(file, "r");
190 final ElfHeaderPart1 eh1 = readElfHeaderPart1(OS, in);
191 if(DEBUG) {
192 System.err.println("ELF-1: Got "+eh1);
193 }
194 if( null != eh1 ) {
195 final ElfHeaderPart2 eh2 = readElfHeaderPart2(eh1, in);
196 if(DEBUG) {
197 System.err.println("ELF-2: Got "+eh2);
198 }
199 if( null != eh2 ) {
200 _elfCpuName[0] = eh2.cpuName;
201 _elfCpuType[0] = eh2.cpuType;
202 _elfAbiType[0] = eh2.abiType;
203 if( eh1.isLittleEndian() ) {
204 _elfLittleEndian[0] = 1;
205 } else if( eh1.isBigEndian() ) {
206 _elfLittleEndian[0] = 2;
207 }
208 _elfValid[0] = true;
209 }
210 }
211 } catch (final Throwable t) {
212 if(DEBUG) {
213 t.printStackTrace();
214 }
215 } finally {
216 if(null != in) {
217 try {
218 in.close();
219 } catch (final IOException e) { }
220 }
221 }
222 return null;
223 } });
224 elfCpuName = _elfCpuName[0];
225 elfCpuType = _elfCpuType[0];
226 elfABIType = _elfAbiType[0];
227 elfLittleEndian = _elfLittleEndian[0];
228 elfValid = _elfValid[0];
229 if( DEBUG ) {
230 System.err.println("Platform.Elf: valid "+elfValid+", elfCpuName "+elfCpuName+", cpuType "+elfCpuType+", abiType "+elfABIType+", elfLittleEndian "+elfLittleEndian);
231 }
232 }
233
234 // Determine endianess, favor ELF value
235 if( elfValid ) {
236 switch( elfLittleEndian ) {
237 case 1:
238 LITTLE_ENDIAN = true;
239 break;
240 case 2:
241 LITTLE_ENDIAN = false;
242 break;
243 default:
244 LITTLE_ENDIAN = queryIsLittleEndianImpl();
245 break;
246 }
247 } else {
248 LITTLE_ENDIAN = queryIsLittleEndianImpl();
249 }
250 if( DEBUG ) {
251 System.err.println("Platform.Endian: test-little "+queryIsLittleEndianImpl()+", elf[valid "+elfValid+", val "+elfLittleEndian+"] -> LITTLE_ENDIAN "+LITTLE_ENDIAN);
252 }
253
254 // Property values for comparison
255 // We might take the property values even if ELF values are available,
256 // since the latter only reflect the CPU/ABI version of the binary files!
257 final CPUType propCpuType = CPUType.query(os_arch_prop);
258 final ABIType propABIType = ABIType.query(propCpuType, os_arch_prop);
259 if( DEBUG ) {
260 System.err.println("Platform.Property: ARCH "+os_arch_prop+", CpuType "+propCpuType+", ABIType "+propABIType);
261 }
262
263 final int strategy;
264 if( elfValid ) {
265 if( isCompatible(elfCpuType, elfABIType, propCpuType, propABIType) ) {
266 // Use property ARCH, compatible w/ ELF
267 CPU = propCpuType;
268 ABI = propABIType;
269 strategy = 210;
270 } else {
271 // use ELF ARCH
272 CPU = elfCpuType;
273 ABI = elfABIType;
274 strategy = 211;
275 }
276 } else {
277 // Last resort: properties
278 CPU = propCpuType;
279 ABI = propABIType;
280 strategy = 220;
281 }
282 {
283 final String _os_name2 = PlatformTypes.getOSName(OS);
284 os_name = null != _os_name2 ? _os_name2 : os_name_prop;
285 }
286 {
287 final String _os_arch2 = PlatformTypes.getArchName(CPU, ABI, LITTLE_ENDIAN);
288 os_arch = null != _os_arch2 ? _os_arch2 : os_arch_prop;
289 }
291
293
294 if( DEBUG ) {
295 System.err.println("Platform.OS: os_name "+os_name+", os_arch "+os_arch+", os_version "+os_version);
296 System.err.println("Platform.Hard: CPU_ARCH "+CPU+", ABI_TYPE "+ABI+" - strategy "+strategy+"(elfValid "+elfValid+"), little "+LITTLE_ENDIAN);
297 System.err.println("Platform.MD.ST: "+MACH_DESC_STAT);
298 }
299 }
300
301 /**
302 * Returns the {@link ABIType} of the current platform using given {@link CPUType cpuType}
303 * and {@link OSType osType} as a hint.
304 * <p>
305 * For Elf parsing one of the following binaries is used:
306 * <ul>
307 * <li>Linux: Current executable</li>
308 * <li>Android: Found gluegen_rt library</li>
309 * <li>Other: A found java/jvm native library.</li>
310 * </ul>
311 * </p>
312 * <p>
313 * Elf ARM Tags are read using {@link ElfHeader}, .. and {@link SectionArmAttributes#abiVFPArgsAcceptsVFPVariant(byte)}.
314 * </p>
315 */
316 private static final File queryElfFile(final OSType osType) {
317 File file = null;
318 try {
319 if( OSType.LINUX == osType ) {
320 /**
321 * Directly using the `/proc/self/exe` via RandomAccessFile within ElfHeaderPart1 leads to a segmentation fault
322 * when using qemu binfmt_misc for armhf or aarch64 (cross build and test)!
323 *
324 * Hence we use the resolved link's exe-file, see below - don't ask ;-)
325 *
326 file = new File("/proc/self/exe");
327 if( !checkFileReadAccess(file) ) {
328 file = null;
329 }
330 */
331 try {
332 final Path exe_symlink = FileSystems.getDefault().getPath("/proc/self/exe");
333 if( Files.isSymbolicLink(exe_symlink) ) {
334 final Path exe_path = Files.readSymbolicLink(exe_symlink);
335 file = exe_path.toFile();
336 if( !checkFileReadAccess(file) ) {
337 file = null;
338 }
339 if(DEBUG) {
340 System.err.println("ElfFile: "+exe_symlink+" -> "+exe_path+" -> "+file);
341 }
342 } else {
343 System.err.println("ElfFile: "+exe_symlink+" -> NULL");
344 }
345 } catch(final Throwable t) {
346 if(DEBUG) {
347 t.printStackTrace();
348 }
349 }
350 }
351 if( null == file ) {
352 file = findSysLib("java");
353 }
354 if( null == file ) {
355 file = findSysLib("jvm");
356 }
357 } catch(final Throwable t) {
358 if(DEBUG) {
359 t.printStackTrace();
360 }
361 }
362 return file;
363 }
364 private static final ElfHeaderPart1 readElfHeaderPart1(final OSType osType, final RandomAccessFile in) {
365 ElfHeaderPart1 res = null;
366 try {
367 res = ElfHeaderPart1.read(osType, in);
368 } catch(final Throwable t) {
369 if(DEBUG) {
370 System.err.println("Caught: "+t.getMessage());
371 t.printStackTrace();
372 }
373 }
374 return res;
375 }
376 private static final ElfHeaderPart2 readElfHeaderPart2(final ElfHeaderPart1 eh1, final RandomAccessFile in) {
377 ElfHeaderPart2 res = null;
378 try {
379 res = ElfHeaderPart2.read(eh1, in);
380 } catch(final Throwable t) {
381 if(DEBUG) {
382 System.err.println("Caught: "+t.getMessage());
383 t.printStackTrace();
384 }
385 }
386 return res;
387 }
388
389 private static boolean checkFileReadAccess(final File file) {
390 try {
391 return file.isFile() && file.canRead();
392 } catch (final Throwable t) { }
393 return false;
394 }
395
396 private static File findSysLib(final String libName) {
397 final ClassLoader cl = PlatformProps.class.getClassLoader();
398 final List<String> possibleLibPaths = JNILibrary.enumerateLibraryPaths(libName,
399 true /* searchSystemPath */, true /* searchSystemPathFirst */, cl);
400 for(int i=0; i<possibleLibPaths.size(); i++) {
401 final String libPath = possibleLibPaths.get(i);
402 final File lib = new File(libPath);
403 if(DEBUG) {
404 System.err.println("findSysLib #"+i+": test "+lib);
405 }
406 if( checkFileReadAccess(lib) ) {
407 return lib;
408 }
409 if(DEBUG) {
410 System.err.println("findSysLib #"+i+": "+lib+" not readable");
411 }
412 }
413 return null;
414 }
415
416 private static final boolean queryIsLittleEndianImpl() {
417 final ByteBuffer tst_b = ByteBuffer.allocate(Buffers.SIZEOF_INT).order(ByteOrder.nativeOrder());
418 // NIO Buffer not required for LE detection: final ByteBuffer tst_b = Buffers.newDirectByteBuffer(Buffers.SIZEOF_INT); // 32bit in native order
419 final IntBuffer tst_i = tst_b.asIntBuffer();
420 final ShortBuffer tst_s = tst_b.asShortBuffer();
421 tst_i.put(0, 0x0A0B0C0D);
422 return 0x0C0D == tst_s.get(0);
423 }
424
425 /**
426 * Returns {@code true} if the given {@link CPUType}s and {@link ABIType}s are compatible.
427 */
428 private static final boolean isCompatible(final CPUType cpu1, final ABIType abi1, final CPUType cpu2, final ABIType abi2) {
429 return cpu1.isCompatible(cpu2) && abi1.isCompatible(abi2);
430 }
431}
static< T > T doPrivileged(final PrivilegedAction< T > o)
Call wrapper for java.security.AccessController#doPrivileged(PrivilegedAction).
Helper routines for logging and debugging.
Definition: Debug.java:35
static final boolean debug(final String subcomponent)
Definition: Debug.java:63
Machine data description for alignment and size onle, see com.jogamp.gluegen.
static StaticConfig guessStaticMachineDataInfo(final PlatformTypes.OSType osType, final PlatformTypes.CPUType cpuType)
Platform Properties derived from Java properties.
static final MachineDataInfo MACH_DESC_STAT
Static (not runtime) determined MachineDataInfo.
static final boolean JAVA_9
True only if being compatible w/ language level 9, e.g.
static final String JAVA_VM_NAME
static final boolean DEBUG
static final VersionNumber os_version
static final String os_and_arch
Unique platform denominator composed as 'os_name' + '-' + 'os_arch'.
static final boolean JAVA_21
True only if being compatible w/ language level 21, e.g.
static final OSType OS
static final String os_name
Lower case system property derived from 'os.name'.
static final String JAVA_VENDOR
static final boolean JAVA_17
True only if being compatible w/ language level 17, e.g.
static final String JAVA_RUNTIME_NAME
static final ABIType ABI
static final CPUType CPU
static final VersionNumber JAVA_VERSION_NUMBER
static final String NEWLINE
static final String os_arch
Lower case system property derived from ELF or 'os.arch'.
static final boolean LITTLE_ENDIAN
Simple version number class containing a version number either being defined explicit or derived from...
final int compareTo(final Object o)
static final OSType query(final String osNameLower)