Cipherpack v1.3.0-3-ga29431a
A Cryprographic Stream Processor
Loading...
Searching...
No Matches
PlatformToolkit.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24package org.cipherpack;
25
26import java.io.File;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29import java.nio.IntBuffer;
30import java.nio.ShortBuffer;
31import java.security.PrivilegedAction;
32import java.util.ArrayList;
33import java.util.Iterator;
34import java.util.List;
35import java.util.StringTokenizer;
36
37/**
38 * Miscellaneous platform utilities, allowed to be used within same Java package.
39 */
40final class PlatformToolkit {
41 public enum OSType {
43 }
44 public enum CPUFamily {
45 /** AMD/Intel */
47 /** ARM 32bit */
49 /** ARM 64bit */
51 /** Power PC */
53 /** SPARC */
55 /** Mips */
57 /** PA RISC */
59 /** Itanium */
61 /** Hitachi SuperH */
63 }
64 public enum CPUType {
65 /** ARM 32bit default, usually little endian */
67 /** ARM7EJ, ARM9E, ARM10E, XScale, usually little endian */
69 /** ARM11, usually little endian */
71 /** ARM Cortex, usually little endian */
73 // 4
74
75 /** X86 32bit, little endian */
77 /** PPC 32bit default, usually big endian */
78 PPC( CPUFamily.PPC, true),
79 /** MIPS 32bit, big endian (mips) or little endian (mipsel) */
81 /** Hitachi SuperH 32bit default, ??? endian */
83 /** SPARC 32bit, big endian */
85 // 9
86
87 /** ARM64 default (64bit), usually little endian */
89 /** ARM AArch64 (64bit), usually little endian */
91 /** X86 64bit, little endian */
93 /** PPC 64bit default, usually big endian */
95 /** MIPS 64bit, big endian (mips64) or little endian (mipsel64) ? */
97 /** Itanium 64bit default, little endian */
99 /** SPARC 64bit, big endian */
101 /** PA_RISC2_0 64bit, ??? endian */
103 // 17
104
105 public final CPUFamily family;
106 public final boolean is32Bit;
107
108 CPUType(final CPUFamily type, final boolean is32Bit){
109 this.family = type;
110 this.is32Bit = is32Bit;
111 }
112
113 /**
114 * Returns {@code true} if the given {@link CPUType} is compatible
115 * w/ this one, i.e. at least {@link #family} and {@link #is32Bit} is equal.
116 */
117 public final boolean isCompatible(final CPUType other) {
118 if( null == other ) {
119 return false;
120 } else if( other == this ) {
121 return true;
122 } else {
123 return this.family == other.family &&
124 this.is32Bit == other.is32Bit;
125 }
126 }
127
128 public static final CPUType query(final String cpuABILower) {
129 if( null == cpuABILower ) {
130 throw new IllegalArgumentException("Null cpuABILower arg");
131 }
132 if( cpuABILower.equals("x86") ||
133 cpuABILower.equals("i386") ||
134 cpuABILower.equals("i486") ||
135 cpuABILower.equals("i586") ||
136 cpuABILower.equals("i686") ) {
137 return X86_32;
138 } else if( cpuABILower.equals("x86_64") ||
139 cpuABILower.equals("amd64") ) {
140 return X86_64;
141 } else if( cpuABILower.equals("ia64") ) {
142 return IA64;
143 } else if( cpuABILower.equals("aarch64") ) {
144 return ARM64;
145 } else if( cpuABILower.startsWith("arm") ) {
146 if( cpuABILower.equals("armv8-a") ||
147 cpuABILower.equals("arm-v8-a") ||
148 cpuABILower.equals("arm-8-a") ||
149 cpuABILower.equals("arm64-v8a") ) {
150 return ARMv8_A;
151 } else if( cpuABILower.startsWith("arm64") ) {
152 return ARM64;
153 } else if( cpuABILower.startsWith("armv7") ||
154 cpuABILower.startsWith("arm-v7") ||
155 cpuABILower.startsWith("arm-7") ||
156 cpuABILower.startsWith("armeabi-v7") ) {
157 return ARMv7;
158 } else if( cpuABILower.startsWith("armv5") ||
159 cpuABILower.startsWith("arm-v5") ||
160 cpuABILower.startsWith("arm-5") ) {
161 return ARMv5;
162 } else if( cpuABILower.startsWith("armv6") ||
163 cpuABILower.startsWith("arm-v6") ||
164 cpuABILower.startsWith("arm-6") ) {
165 return ARMv6;
166 } else {
167 return ARM;
168 }
169 } else if( cpuABILower.equals("sparcv9") ) {
170 return SPARCV9_64;
171 } else if( cpuABILower.equals("sparc") ) {
172 return SPARC_32;
173 } else if( cpuABILower.equals("pa_risc2.0") ) {
174 return PA_RISC2_0;
175 } else if( cpuABILower.startsWith("ppc64") ) {
176 return PPC64;
177 } else if( cpuABILower.startsWith("ppc") ) {
178 return PPC;
179 } else if( cpuABILower.startsWith("mips64") ) {
180 return MIPS_64;
181 } else if( cpuABILower.startsWith("mips") ) {
182 return MIPS_32;
183 } else if( cpuABILower.startsWith("superh") ) {
184 return SuperH;
185 } else {
186 throw new RuntimeException("Please port CPUType detection to your platform (CPU_ABI string '" + cpuABILower + "')");
187 }
188 }
189 }
190 public enum ABIType {
191 GENERIC_ABI ( 0x00 ),
192 /** ARM GNU-EABI ARMEL -mfloat-abi=softfp */
194 /** ARM GNU-EABI ARMHF -mfloat-abi=hard */
196 /** ARM EABI AARCH64 (64bit) */
197 EABI_AARCH64 ( 0x03 );
198
199 public final int id;
200
201 ABIType(final int id){
202 this.id = id;
203 }
204
205 /**
206 * Returns {@code true} if the given {@link ABIType} is compatible
207 * w/ this one, i.e. they are equal.
208 */
209 public final boolean isCompatible(final ABIType other) {
210 if( null == other ) {
211 return false;
212 } else {
213 return other == this;
214 }
215 }
216
217 public static final ABIType query(final CPUType cpuType, final String cpuABILower) {
218 if( null == cpuType ) {
219 throw new IllegalArgumentException("Null cpuType");
220 } else if( null == cpuABILower ) {
221 throw new IllegalArgumentException("Null cpuABILower");
222 } else if( CPUFamily.ARM64 == cpuType.family ) {
223 return EABI_AARCH64;
224 } else if( CPUFamily.ARM32 == cpuType.family ) {
225 // FIXME: We only support EABI_GNU_ARMHF on ARM 32bit for now!
226 return EABI_GNU_ARMHF;
227 } else {
228 return GENERIC_ABI;
229 }
230 }
231 }
232
233 /** Lower case system property '{@code os.name}'. */
234 static final String os_name;
235 /** Lower case system property '{@code os.arch}' */
236 static final String os_arch;
237 private static final String user_dir;
238 private static final String java_user_lib_path;
239 private static final String java_boot_lib_path;
240
241 /**
242 * Unique platform denominator composed as '{@link #os_name}' + '-' + '{@link #os_arch}'.
243 */
244 static final String os_and_arch;
245 static final OSType OS_TYPE;
246 private static final boolean isOSX;
247
248 private static final String prefix;
249 private static final String suffix;
250
251 static {
252 {
253 final String[] props =
254 CPFactory.doPrivileged(new PrivilegedAction<String[]>() {
255 @Override
256 public String[] run() {
257 final String[] props = new String[5];
258 int i=0;
259 props[i++] = System.getProperty("os.name").toLowerCase(); // 0
260 props[i++] = System.getProperty("os.arch").toLowerCase(); // 1
261 props[i++] = System.getProperty("user.dir"); // 2
262 props[i++] = System.getProperty("java.library.path"); // 3
263 props[i++] = System.getProperty("sun.boot.library.path"); // 4
264 return props;
265 }
266 });
267 int i=0;
268 os_name = props[i++];
269 final String _os_arch1 = props[i++];
270 user_dir = props[i++];
271 java_user_lib_path = props[i++];
272 java_boot_lib_path = props[i++];
273
274 final boolean LITTLE_ENDIAN = queryIsLittleEndianImpl();
275 final CPUType CPU_TYPE = CPUType.query(_os_arch1);
276 final ABIType ABI_TYPE = ABIType.query(CPU_TYPE, _os_arch1);
277 final String _os_arch2 = getArchName(CPU_TYPE, ABI_TYPE, LITTLE_ENDIAN);
278 os_arch = null != _os_arch2 ? _os_arch2 : _os_arch1;
279 os_and_arch = os_name+"-"+os_arch;
280 if( CPFactory.DEBUG ) {
281 System.err.println("PlatformToolkit: os_name "+os_name+", os_arch ("+_os_arch1+" -> "+_os_arch2+" ->) "+os_arch+" (final), "+
282 "CPU_TYPE "+CPU_TYPE+", ABI_TYPE "+ABI_TYPE+", LITTLE_ENDIAN "+LITTLE_ENDIAN);
283 }
284 }
285
286 if ( os_name.startsWith("mac os x") ||
287 os_name.startsWith("darwin") ) {
288 OS_TYPE = OSType.MACOS;
289 isOSX = true;
290 } else if ( os_name.startsWith("ios") ) {
291 OS_TYPE = OSType.IOS;
292 isOSX = true;
293 } else if ( os_name.startsWith("windows") ) {
294 OS_TYPE = OSType.WINDOWS;
295 isOSX = false;
296 } else {
297 OS_TYPE = OSType.UNIX;
298 isOSX = false;
299 }
300
301 switch (OS_TYPE) {
302 case WINDOWS:
303 prefix = "";
304 suffix = ".dll";
305 break;
306
307 case MACOS:
308 case IOS:
309 prefix = "lib";
310 suffix = ".dylib";
311 break;
312
313 case UNIX:
314 default:
315 prefix = "lib";
316 suffix = ".so";
317 break;
318 }
319 }
320
321 private static final boolean queryIsLittleEndianImpl() {
322 final ByteBuffer tst_b = ByteBuffer.allocateDirect(4 /* SIZEOF_INT */).order(ByteOrder.nativeOrder()); // 32bit in native order
323 final IntBuffer tst_i = tst_b.asIntBuffer();
324 final ShortBuffer tst_s = tst_b.asShortBuffer();
325 tst_i.put(0, 0x0A0B0C0D);
326 return 0x0C0D == tst_s.get(0);
327 }
328 private static final String getArchName(final CPUType cpuType, final ABIType abiType, final boolean littleEndian) {
329 switch( abiType ) {
330 case EABI_GNU_ARMEL:
331 return "arm"; // actually not supported!
332 case EABI_GNU_ARMHF:
333 return "armhf";
334 case EABI_AARCH64:
335 return "arm64";
336 default:
337 break;
338 }
339
340 switch( cpuType ) {
341 case X86_32:
342 return "i386";
343 case PPC:
344 return "ppc";
345 case MIPS_32:
346 return littleEndian ? "mipsel" : "mips";
347 case SuperH:
348 return "superh";
349 case SPARC_32:
350 return "sparc";
351
352 case X86_64:
353 return "amd64";
354 case PPC64:
355 return littleEndian ? "ppc64le" : "ppc64";
356 case MIPS_64:
357 return "mips64";
358 case IA64:
359 return "ia64";
360 case SPARCV9_64:
361 return "sparcv9";
362 case PA_RISC2_0:
363 return "risc2.0";
364 default:
365 return null;
366 }
367 }
368
369 /**
370 * Produces a list of potential full native library pathnames, denoted by its {@code libBaseName}.
371 *
372 * <p>
373 * Basic order of library locations
374 * <pre>
375 * User locations:
376 * - current working directory + {@link #os_and_arch}
377 * - iterate through paths within 'java.library.path', adding {@link #os_and_arch} to each
378 * - current working directory
379 * - iterate through paths within 'java.library.path'
380 *
381 * System locations:
382 * - optional OSX path
383 * - iterate through paths within 'sun.boot.library.path'
384 * </pre>
385 * </p>
386 *
387 * <p>
388 * Example:
389 * <pre>
390 /usr/local/projects/direct_bt/dist-amd64/linux-amd64/libdirect_bt.so (addPath cwd.os_and_arch)
391 /usr/local/projects/direct_bt/dist-amd64/lib/linux-amd64/libdirect_bt.so (addPath java-user-libpath.os_and_arch:0)
392 /usr/local/projects/direct_bt/dist-amd64/libdirect_bt.so (addPath cwd)
393 /usr/local/projects/direct_bt/dist-amd64/lib/libdirect_bt.so (addPath java-user-libpath:0)
394 /usr/lib/jvm/java-14-openjdk-amd64/lib/libdirect_bt.so (addPath java-boot-libpath:0)
395 * </pre>
396 *
397 * @param libBaseName library basename without prefix (like 'lib') or suffix like '.so'.
398 * @param searchSystemPath
399 * @param searchSystemPathFirst
400 * @param loader
401 * @return
402 */
403 private static final List<String> enumerateLibraryPaths(final String libBaseName,
404 final boolean searchSystemPath,
405 final boolean searchSystemPathFirst,
406 final ClassLoader loader) {
407 final List<String> paths = new ArrayList<String>();
408
409 if ( libBaseName == null || libBaseName.length() == 0 ) {
410 return paths;
411 }
412
413 final String libPlatformName = getPlatformName(libBaseName);
414
415 if( searchSystemPath && searchSystemPathFirst ) {
416 // Add probable Mac OS X-specific paths
417 if ( isOSX ) {
418 // Add historical location
419 addPath("osx-1", "/Library/Frameworks/" + libBaseName + ".framework", libPlatformName, paths);
420 // Add current location
421 addPath("osx-2", "/System/Library/Frameworks/" + libBaseName + ".framework", libPlatformName, paths);
422 }
423 addMultiPaths("java-boot-libpath", java_boot_lib_path, libPlatformName, paths);
424 }
425
426 addPath("cwd.os_and_arch", user_dir+File.separator+os_and_arch, libPlatformName, paths);
427 addMultiPaths2("java-user-libpath.os_and_arch", java_user_lib_path, os_and_arch, libPlatformName, paths);
428
429 addPath("cwd", user_dir, libPlatformName, paths);
430 addMultiPaths("java-user-libpath", java_user_lib_path, libPlatformName, paths);
431
432 if( searchSystemPath && !searchSystemPathFirst ) {
433 // Add probable Mac OS X-specific paths
434 if ( isOSX ) {
435 // Add historical location
436 addPath("osx-1", "/Library/Frameworks/" + libBaseName + ".Framework", libPlatformName, paths);
437 // Add current location
438 addPath("osx-2", "/System/Library/Frameworks/" + libBaseName + ".Framework", libPlatformName, paths);
439 }
440 addMultiPaths("java-boot-libpath", java_boot_lib_path, libPlatformName, paths);
441 }
442
443 return paths;
444 }
445
446
447 private static final String getPlatformName(final String libBaseName) {
448 return prefix + libBaseName + suffix;
449 }
450 private static final String getCanonicalPath(final String path) {
451 return CPFactory.doPrivileged(new PrivilegedAction<String>() {
452 @Override
453 public String run() {
454 try {
455 final File f = new File(path);
456 // f.getCanonicalPath() also resolved '.', '..' and symbolic links in contrast to f.getAbsolutePath()
457 return f.getCanonicalPath();
458 } catch (final Throwable t) {
459 if( CPFactory.DEBUG ) {
460 System.err.println("getAbsolutePath("+path+") failed: "+t.getMessage());
461 }
462 return null;
463 }
464 } } );
465 }
466 private static final void addPath(final String msg, final String path, final String platformName, final List<String> paths) {
467 if( null != path && path.length() > 0 ) {
468 final String fullpath = path + File.separator + platformName;
469 final String abspath = getCanonicalPath(fullpath);
470 if( null != abspath ) {
471 final boolean isDup = paths.contains(abspath);
472 if( CPFactory.DEBUG ) {
473 System.err.println(" "+abspath+" (addPath "+msg+", dropped duplicate "+isDup+")");
474 }
475 if( !isDup ) {
476 paths.add(abspath);
477 }
478 }
479 }
480 }
481 private static final void addMultiPaths(final String msg, final String pathList, final String platformName, final List<String> paths) {
482 if( null != pathList && pathList.length() > 0 ) {
483 final StringTokenizer tokenizer = new StringTokenizer(pathList, File.pathSeparator);
484 int i=0;
485 while (tokenizer.hasMoreTokens()) {
486 addPath(msg+":"+i, tokenizer.nextToken(), platformName, paths);
487 i++;
488 }
489 }
490 }
491 private static final void addMultiPaths2(final String msg, final String pathList, final String subDir, final String platformName, final List<String> paths) {
492 if( null != pathList && pathList.length() > 0 && null != subDir && subDir.length() > 0 ) {
493 final StringTokenizer tokenizer = new StringTokenizer(pathList, File.pathSeparator);
494 int i=0;
495 while (tokenizer.hasMoreTokens()) {
496 final String path = tokenizer.nextToken() + File.separator + subDir;
497 addPath(msg+":"+i, path, platformName, paths);
498 i++;
499 }
500 }
501 }
502
503
504 /**
505 * Loads a native library, denoted by its {@code libBaseName}.
506 *
507 * <p>
508 * Basic order of library locations
509 * <pre>
510 * User locations:
511 * - current working directory + {@link #os_and_arch}
512 * - iterate through paths within 'java.library.path', adding {@link #os_and_arch} to each
513 * - current working directory
514 * - iterate through paths within 'java.library.path'
515 *
516 * System locations:
517 * - optional OSX path
518 * - iterate through paths within 'sun.boot.library.path'
519 * </pre>
520 * </p>
521 *
522 * <p>
523 * If the above fails, {@link System#loadLibrary(String)} is called using the plain {@code libBaseName},
524 * exhausting all simple locations and methods.
525 * </p>
526 *
527 * <p>
528 * Example:
529 * <pre>
530 /usr/local/projects/direct_bt/dist-amd64/linux-amd64/libdirect_bt.so (addPath cwd.os_and_arch)
531 /usr/local/projects/direct_bt/dist-amd64/lib/linux-amd64/libdirect_bt.so (addPath java-user-libpath.os_and_arch:0)
532 /usr/local/projects/direct_bt/dist-amd64/libdirect_bt.so (addPath cwd)
533 /usr/local/projects/direct_bt/dist-amd64/lib/libdirect_bt.so (addPath java-user-libpath:0)
534 /usr/lib/jvm/java-14-openjdk-amd64/lib/libdirect_bt.so (addPath java-boot-libpath:0)
535 * </pre>
536 *
537 * @param libBaseName library basename without prefix (like 'lib') or suffix like '.so'.
538 * @param cl
539 * @param t holder to store the last Throwable, if any
540 * @return {@code true} if successful, otherwise {@code false}.
541 */
542 static boolean loadLibrary(final String libBaseName, final ClassLoader cl, final Throwable[] t) {
543 if( CPFactory.DEBUG ) {
544 System.err.println();
545 System.err.println("PlatformToolkit.loadLibrary: libBaseName "+libBaseName+":");
546 }
547 final List<String> possiblePaths = enumerateLibraryPaths(libBaseName, true /* searchSystemPath */, false /* searchSystemPathFirst */, cl);
548 if( CPFactory.DEBUG ) {
549 System.err.println();
550 }
551
552 // Iterate down these and see which one if any we can actually find.
553 for (final Iterator<String> iter = possiblePaths.iterator(); iter.hasNext(); ) {
554 final String path = iter.next();
555 try {
556 System.load(path);
557 if( CPFactory.DEBUG ) {
558 System.err.println(" "+path+" success");
559 }
560 return true;
561 } catch (final Throwable t0) {
562 if( CPFactory.DEBUG ) {
563 System.err.println(" "+path+" failed: "+t0.getMessage());
564 }
565 t[0] = t0;
566 }
567 }
568
569 // Fall back to loadLibrary
570 try {
571 System.loadLibrary(libBaseName);
572 if( CPFactory.DEBUG ) {
573 System.err.println(" "+libBaseName+" success");
574 }
575 return true;
576 } catch (final Throwable t0) {
577 if( CPFactory.DEBUG ) {
578 System.err.println(" "+libBaseName+" failed: "+t0.getMessage());
579 }
580 t[0] = t0;
581 }
582 return false;
583 }
584}
Cipherpack Factory, called by automatically to load the native library.
static< T > T doPrivileged(final PrivilegedAction< T > o)
Deprecated call to java.security.AccessController#doPrivileged(PrivilegedAction) w/o warnings.
EABI_GNU_ARMHF
ARM GNU-EABI ARMHF -mfloat-abi=hard.
final boolean isCompatible(final ABIType other)
Returns true if the given ABIType is compatible w/ this one, i.e.
static final ABIType query(final CPUType cpuType, final String cpuABILower)
EABI_GNU_ARMEL
ARM GNU-EABI ARMEL -mfloat-abi=softfp.
IA64
Itanium 64bit default, little endian.
CPUType(final CPUFamily type, final boolean is32Bit)
ARM
ARM 32bit default, usually little endian.
static final CPUType query(final String cpuABILower)
MIPS_32
MIPS 32bit, big endian (mips) or little endian (mipsel)
ARMv6
ARM11, usually little endian.
MIPS_64
MIPS 64bit, big endian (mips64) or little endian (mipsel64) ?
PPC64
PPC 64bit default, usually big endian.
ARMv7
ARM Cortex, usually little endian.
final boolean isCompatible(final CPUType other)
Returns true if the given CPUType is compatible w/ this one, i.e.
PPC
PPC 32bit default, usually big endian.
SuperH
Hitachi SuperH 32bit default, ?
ARM64
ARM64 default (64bit), usually little endian.
ARMv5
ARM7EJ, ARM9E, ARM10E, XScale, usually little endian.
ARMv8_A
ARM AArch64 (64bit), usually little endian.