Skip to content

Commit 997c2c2

Browse files
committed
[GR-71042] Espresso: Introduce --java.NativeBackend=nfi-staticlib on Darwin (default for JVM mode)
PullRequest: graal/22455
2 parents 0fc5386 + 252fdde commit 997c2c2

File tree

8 files changed

+295
-10
lines changed

8 files changed

+295
-10
lines changed

espresso-compiler-stub/mx.espresso-compiler-stub/mx_espresso_compiler_stub.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,15 @@ def create_ni_standalone(base_standalone_name, register_distribution):
6464
f'dependency:espresso:{base_standalone_name}/languages/java/lib/<lib:javavm>'
6565
]
6666
else:
67-
idx = layout['languages/java/lib/'].index('dependency:espresso:com.oracle.truffle.espresso.mokapot/*/<multitarget_libc_selection>/<lib:jvm>')
68-
layout['languages/java/lib/'][idx] = f'dependency:espresso:{base_standalone_name}/languages/java/lib/<lib:jvm>'
67+
idx = layout['languages/java/lib/'].index('dependency:espresso:ESPRESSO_JVM_STANDALONE_MOKAPOT_SUPPORT/*')
68+
if mx.is_darwin():
69+
layout['languages/java/lib/'][idx] = f'dependency:espresso:{base_standalone_name}/languages/java/lib/fatpot/<lib:jvm>'
70+
else:
71+
layout['languages/java/lib/'][idx] = f'dependency:espresso:{base_standalone_name}/languages/java/lib/<lib:jvm>'
72+
6973
idx = layout['bin/'].index('dependency:espresso:espresso')
7074
del layout['bin/'][idx]
75+
7176
layout['bin/<exe:espresso>'] = f'dependency:espresso:{base_standalone}/bin/<exe:espresso>'
7277
layout['bin/<exe:java>'] = 'link:<exe:espresso>'
7378
layout['./'][0]['exclude'].append("bin/<exe:java>")
@@ -86,14 +91,16 @@ def create_ni_standalone(base_standalone_name, register_distribution):
8691
if espresso_java_home.java_home != mx_sdk_vm.base_jdk(stage1=False).home:
8792
mx.abort(f"ESPRESSO_JAVA_HOME(={espresso_java_home.java_home}) must match JAVA_HOME (={mx_sdk_vm.base_jdk(stage1=True).home}) (or FINAL_STAGE_JAVA_HOME (={mx_sdk_vm.base_jdk(stage1=False).home}) if set)")
8893

94+
prefix = '*/Contents/Home/' if mx.is_darwin() else '*/'
95+
8996
# substratevm is available and ESPRESSO_JAVA_HOME is JAVA_HOME, use GraalVM
9097
layout['./'][0]['source_type'] = 'extracted-dependency'
9198
layout['./'][0]['dependency'] = get_final_graalvm_distribution().qualifiedName()
92-
layout['./'][0]['path'] = '*/*'
99+
layout['./'][0]['path'] = prefix + '*'
93100
layout['./'][0]['exclude'] += [
94-
'*/languages/elau',
95-
'*/languages/java',
96-
'*/bin/espresso'
101+
prefix + 'languages/elau',
102+
prefix + 'languages/java',
103+
prefix + 'bin/espresso'
97104
]
98105
else:
99106
layout = None

espresso/docs/hacking.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,14 @@ $ LD_DEBUG=unused mx espresso -cp mxbuild/dists/jdk1.8/espresso-playground.jar c
165165

166166
### macOS
167167

168-
On macOS there is nothing like `dlmopen` available, therefore `jvm-ce` does not work.
168+
Nothing like `dlmopen` is available on macOS. Instead we default to `nfi-staticlib` for the JVM mode, which statically links in (most) of the OpenJDK libraries into `libjvm.dylib`.
169+
A notable exception is `libawt.dylib` and its related libraries, which only work via dynamic loading.
170+
This means in practice only the host _or_ the guest can use AWT, but not both at the same time.
169171

170172

173+
Currently `nfi-staticlib` only works for one Espresso context. Using `nfi-staticlib` with more than one context will likely result in a SIGSEGV in `libtrufflenfi.dylib`.
174+
We are exploring how to implement support for multiple contexts (GR-71082).
175+
171176
## _Espressoⁿ_ Java-ception
172177

173178
Espresso can run itself. **Self-hosting requires a Linux distribution with an up-to-date glibc.**

espresso/mx.espresso/mx_espresso.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import subprocess
2929
import argparse
3030
import sys
31+
import re
3132
from abc import ABCMeta, abstractmethod
3233

3334
import mx
@@ -44,6 +45,7 @@
4445
from os.path import join, exists, dirname, relpath
4546
from import_order import verify_order, validate_format
4647
from mx_truffle import resolve_truffle_dist_names
48+
from mx_native import DefaultNativeProject
4749

4850
_suite = mx.suite('espresso')
4951

@@ -637,6 +639,74 @@ def register_espresso_runtime_resources(register_project, register_distribution,
637639
register_distribution(extra_llvm_java_home_dep)
638640
register_espresso_runtime_resource(extra_java_home_dep, extra_llvm_java_home_dep, register_project, register_distribution, suite, False)
639641

642+
class CustomLibJVMLinking(DefaultNativeProject):
643+
def __init__(self, suite, name, deps, workingSets, **kwargs):
644+
subDir = kwargs.pop('subDir')
645+
d = join(suite.dir, subDir, kwargs.pop('dir'))
646+
super(CustomLibJVMLinking, self).__init__(suite, name, subDir, [], deps, workingSets, d, 'shared_lib', **kwargs)
647+
648+
@staticmethod
649+
def _extract_loaded_by_espresso():
650+
source_path = join(_suite.dir, 'src', 'com.oracle.truffle.espresso', 'src', 'com', 'oracle', 'truffle', 'espresso', 'ffi', 'nfi', 'NFIStaticLibNativeAccess.java')
651+
with open(source_path, "r", encoding="utf-8") as f:
652+
# look for:
653+
# private static final Set<String> LOADED_BY_ESPRESSO = Set.of(...);
654+
m = re.search(r'\bLOADED_BY_ESPRESSO\s*=\s*Set\.of\s*\(([a-z",\s]*)\)', f.read())
655+
if not m:
656+
raise RuntimeError("Could not find LOADED_BY_ESPRESSO = Set.of(")
657+
658+
# collect quoted strings from Set
659+
return set(re.findall(r'\"([a-z]+)\"', m.group(1)))
660+
661+
@property
662+
def ldflags(self):
663+
platform = mx.get_os() + '-' + mx.get_arch()
664+
665+
assert mx.is_darwin(), "not supported yet: " + platform
666+
667+
# Most OpenJDK libraries are suitable for static linking, with the exception of
668+
# libawt and related libs.
669+
#
670+
# This is also the minimum set of libraries required to make this approach work, as HotSpot
671+
# always will load these and it would result into a namespace clash otherwise. Depending
672+
# on the application ran by the host (next to Espresso) and by the guest, other libraries
673+
# such as libmanagement make sense to include as well.
674+
#
675+
# As noted above, libawt only works with dynamic loading and thus only the host or the
676+
# guest will be able to use it.
677+
#
678+
# Some libraries are not included as they are VM implementation specific (e.g. libsaproc).
679+
#
680+
#
681+
# Hints for debugging on Darwin:
682+
# - --log.level=ALL: Prints the reason why a library could not be loaded.
683+
# - DYLD_PRINT_LIBRARIES=1: Verbose output of the dynamic linker, e.g. prints the full
684+
# path of libraries that are attempted to be loaded.
685+
# - breakpoint on dlopen/dlsym
686+
687+
darwin_linked_in_libs = {"attach", "extnet", "freetype", "j2gss", "j2pcsc", "j2pkcs11", "jaas",
688+
"java", "javajpeg", "jimage", "management_agent", "management_ext",
689+
"management", "mlib_image", "net", "nio", "osxkrb5", "prefs", "rmi", "sleef",
690+
"syslookup", "verify", "zip"}
691+
692+
expected_by_VM = CustomLibJVMLinking._extract_loaded_by_espresso()
693+
missing_libs = expected_by_VM - darwin_linked_in_libs
694+
assert not missing_libs, "missing libraries expected by the VM: " + str(missing_libs)
695+
696+
static_lib_dir = os.path.join(get_java_home_dep().java_home, "lib", "static", platform)
697+
ldf = []
698+
699+
for jdk_static_lib in os.listdir(static_lib_dir):
700+
if not jdk_static_lib.endswith('.a'):
701+
continue
702+
703+
if jdk_static_lib[3:-2] not in darwin_linked_in_libs:
704+
continue
705+
706+
ldf.append(f'-Wl,-force_load,{os.path.join(static_lib_dir, jdk_static_lib)}')
707+
708+
return ldf + super(CustomLibJVMLinking, self).ldflags
709+
640710

641711
def register_espresso_runtime_resource(java_home_dep, llvm_java_home_dep, register_project, register_distribution, suite, is_main):
642712
is_ee_suite = suite != _suite

espresso/mx.espresso/suite.py

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,38 @@
405405
},
406406
},
407407

408+
# same as mokapot, but with statically linked OpenJDK libraries.
409+
"com.oracle.truffle.espresso.fatpot": {
410+
"class": "CustomLibJVMLinking",
411+
"subDir": "src",
412+
"dir": "com.oracle.truffle.espresso.mokapot",
413+
"native": "shared_lib",
414+
"deliverable": "jvm",
415+
"platformDependent": True,
416+
"os_arch": {
417+
"darwin": {
418+
"<others>": {
419+
"cflags": ["-Wall", "-Werror", "-std=c11", "-DESPRESSO_NFI_STATIC"],
420+
"ldflags": [
421+
"-Wl,-install_name,@rpath/libjvm.dylib",
422+
"-Wl,-rpath,@loader_path/.",
423+
"-Wl,-rpath,@loader_path/..",
424+
"-Wl,-current_version,1.0.0",
425+
"-Wl,-compatibility_version,1.0.0"
426+
],
427+
"multitarget": {
428+
"compiler": ["host", "*"]
429+
},
430+
},
431+
},
432+
"<others>": {
433+
"<others>": {
434+
"ignore": "GR-66340: Darwin only for now",
435+
},
436+
},
437+
},
438+
},
439+
408440
"com.oracle.truffle.espresso.shadowed.asm" : {
409441
# Shadowed ASM library (org.ow2.asm:asm)
410442
"subDir" : "src",
@@ -654,6 +686,29 @@
654686
"maven": False,
655687
},
656688

689+
"ESPRESSO_JVM_STANDALONE_MOKAPOT_SUPPORT": {
690+
"type": "dir",
691+
"platformDependent": True,
692+
"platforms": "local",
693+
"os": {
694+
"darwin": {
695+
"layout": {
696+
"./fatpot/": [
697+
"dependency:espresso:com.oracle.truffle.espresso.fatpot/*/<multitarget_libc_selection>/<lib:jvm>",
698+
]
699+
},
700+
},
701+
"<others>": {
702+
"layout": {
703+
"./": [
704+
"dependency:espresso:com.oracle.truffle.espresso.mokapot/*/<multitarget_libc_selection>/<lib:jvm>",
705+
]
706+
},
707+
},
708+
},
709+
"maven": False,
710+
},
711+
657712
"ESPRESSO_JVM_STANDALONE": {
658713
"type": "dir",
659714
"pruning_mode": "optional",
@@ -683,7 +738,7 @@
683738
],
684739
"languages/java/lib/": [
685740
# Copy of libjvm.so, accessible by Sulong via the default Truffle file system.
686-
"dependency:espresso:com.oracle.truffle.espresso.mokapot/*/<multitarget_libc_selection>/<lib:jvm>",
741+
"dependency:espresso:ESPRESSO_JVM_STANDALONE_MOKAPOT_SUPPORT/*",
687742
],
688743
"languages/java/": [
689744
{
@@ -765,11 +820,20 @@
765820
"darwin-aarch64",
766821
"windows-amd64",
767822
],
823+
"pruning_mode": "optional",
768824
"layout": {
769825
"META-INF/resources/java/espresso-libs/<os>/<arch>/lib/": [
770826
# Copy of libjvm.so, accessible by Sulong via the default Truffle file system.
771827
"dependency:espresso:com.oracle.truffle.espresso.mokapot/*/<multitarget_libc_selection>/<lib:jvm>",
772828
],
829+
"META-INF/resources/java/espresso-libs/<os>/<arch>/lib/fatpot/": [
830+
{
831+
'source_type': 'dependency',
832+
'dependency': 'espresso:com.oracle.truffle.espresso.fatpot',
833+
'path': '*/<multitarget_libc_selection>/<lib:jvm>',
834+
'optional': True,
835+
},
836+
],
773837
"META-INF/resources/java/espresso-libs/<os>/<arch>/": "dependency:espresso:ESPRESSO_SUPPORT/*",
774838
},
775839
"maven": False,
@@ -841,7 +905,15 @@
841905
"lib/": [
842906
# Copy of libjvm.so, accessible by Sulong via the default Truffle file system.
843907
"dependency:espresso:com.oracle.truffle.espresso.mokapot/*/<multitarget_libc_selection>/<lib:jvm>",
844-
]
908+
],
909+
"lib/fatpot/": [
910+
{
911+
'source_type': 'dependency',
912+
'dependency': 'espresso:com.oracle.truffle.espresso.fatpot',
913+
'path': '*/<multitarget_libc_selection>/<lib:jvm>',
914+
'optional': True,
915+
},
916+
],
845917
},
846918
"maven": False,
847919
},
@@ -854,6 +926,14 @@
854926
"truffle/": [
855927
"dependency:espresso:com.oracle.truffle.espresso.mokapot/*/<multitarget_libc_selection>/<lib:jvm>",
856928
],
929+
"truffle/fatpot/": [
930+
{
931+
'source_type': 'dependency',
932+
'dependency': 'espresso:com.oracle.truffle.espresso.fatpot',
933+
'path': '*/<multitarget_libc_selection>/<lib:jvm>',
934+
'optional': True,
935+
},
936+
],
857937
},
858938
"maven": False,
859939
},

espresso/src/com.oracle.truffle.espresso.mokapot/src/mokapot.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,7 +1730,11 @@ JNIEXPORT jboolean JNICALL JVM_IsForeignLinkerSupported(void) {
17301730
JNIEXPORT jboolean JNICALL
17311731
JVM_IsStaticallyLinked(void) {
17321732
IMPLEMENTED(JVM_IsStaticallyLinked);
1733+
#ifdef ESPRESSO_NFI_STATIC
1734+
return JNI_TRUE;
1735+
#else
17331736
return JNI_FALSE;
1737+
#endif
17341738
}
17351739

17361740
JNIEXPORT void JNICALL JVM_VirtualThreadStart(JNIEnv* env, jobject vthread) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import com.oracle.truffle.espresso.ffi.NoNativeAccess;
7272
import com.oracle.truffle.espresso.ffi.nfi.NFIIsolatedNativeAccess;
7373
import com.oracle.truffle.espresso.ffi.nfi.NFINativeAccess;
74+
import com.oracle.truffle.espresso.ffi.nfi.NFIStaticLibNativeAccess;
7475
import com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess;
7576
import com.oracle.truffle.espresso.impl.EspressoType;
7677
import com.oracle.truffle.espresso.impl.SuppressFBWarnings;
@@ -345,6 +346,8 @@ private static String setNativeBackendId(final TruffleLanguage.Env env) {
345346
if (isInPreInit || !EspressoOptions.RUNNING_ON_SVM) {
346347
if (OS.getCurrent() == OS.Linux) {
347348
nativeBackend = NFIIsolatedNativeAccess.Provider.ID;
349+
} else if (OS.getCurrent() == OS.Darwin) {
350+
nativeBackend = NFIStaticLibNativeAccess.Provider.ID;
348351
} else {
349352
nativeBackend = NFISulongNativeAccess.Provider.ID;
350353
}

0 commit comments

Comments
 (0)