Cipherpack v1.2.0-dirty
A Cryprographic Stream Processor
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 {
42 UNIX, MACOS, IOS, WINDOWS;
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.
Definition: CPFactory.java:47
static< T > T doPrivileged(final PrivilegedAction< T > o)
Deprecated call to java.security.AccessController#doPrivileged(PrivilegedAction) w/o warnings.
Definition: CPFactory.java:95
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, ??? endian.
ARM64
ARM64 default (64bit), usually little endian.
ARMv5
ARM7EJ, ARM9E, ARM10E, XScale, usually little endian.
ARMv8_A
ARM AArch64 (64bit), usually little endian.
PA_RISC2_0
PA_RISC2_0 64bit, ??? endian.