<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p>Hi,<br>
Note that here the problem is not the downcall method handle,
which returns just a pointer, so that's portable.</p>
<p>The problem is that the pointer points to a struct whose layout
might vary depending on the platform.</p>
<p>The way one might attack this could be to wrap a bunch of
"shared" accessors for "passwd" inside a class, where the static
initializer for the class determines which platforms we're in, and
sets up the layout accordingly.</p>
<p>For that, I agree that some form of [1] is needed. Note that we
currently have this internal class:</p>
<p><a class="moz-txt-link-freetext" href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/jdk/internal/util/Architecture.java">https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/jdk/internal/util/Architecture.java</a></p>
<p>We have started using this internally more and more, and adding
stuff we know the JDK needs. Ideally, this class will be added in
some shape or form to the public API, because I think that it
could be useful to regularize a lot of ad-hoc code that is written
by 3rd parties (e.g. using system properties and what not). I
think that, realistically, there will be small follow ups such as
this: FFM is a relatively small API, but one that comes with a
relatively big conceptual shift. As some layouts and method
handles are now platform dependent, of course that now puts more
pressure on ways in which to detect said platform differences (and
adapt method/var handles as required).</p>
<p>P.S.</p>
<p>While what you say re. System::loadLibrary is true (e.g. it is
not fully aware of dynamic linker paths), FFM provides a different
"blessed" way of looking up libraries that is very well aware of
what the dynamic linker does. In fact, it's nothing but a thin
wrapper around dlopen:</p>
<p><a class="moz-txt-link-freetext" href="https://download.java.net/java/early_access/jdk22/docs/api/java.base/java/lang/foreign/SymbolLookup.html#libraryLookup(java.lang.String,java.lang.foreign.Arena)">https://download.java.net/java/early_access/jdk22/docs/api/java.base/java/lang/foreign/SymbolLookup.html#libraryLookup(java.lang.String,java.lang.foreign.Arena)</a></p>
<p>On Linux, you can do `loaderLookup("libc.so.6")` and that works
fine - no custom logic is needed. The problem of shipping native
libraries into maven modules, on the other hand, is one that still
requires manual workarounds. But I believe that to be more a
problem of the tool we're "forced" to work with (Maven/Gradle):
there's _no sane way_ to load a shared library from a zip file (or
from some random memory address). Meaning that, to do that, you
need to copy the library _somewhere_ before loading, which can
come with its own set of issues (e.g. no write access, not even to
"/tmp"). A qualitatively better solution would be to use jmods and
custom JDK images (with jlink) which can deal with native
libraries just fine, but unfortunately these things are not
supported by the build tools we have today. This is another of
these follow-up activity that will likely fall out from the
initial FFM addition.<br>
</p>
<p>Cheers<br>
Maurizio<br>
</p>
<div class="moz-cite-prefix">On 17/02/2024 06:53, tison wrote:<br>
</div>
<blockquote type="cite" cite="mid:CALL9TYKhai6KdePs_TtAsk4WeoabQHvZ8Y97u3TYCMDdHFzXZQ@mail.gmail.com">
<div dir="ltr">For example, getpwnam returns passwd struct, but
the struct layout is various on different platform, making the
code snippet below unportable:<br>
<br>
final Linker linker = Linker.nativeLinker();<br>
final SymbolLookup lookup = linker.defaultLookup();<br>
final MemorySegment getpwnam =
lookup.find("getpwnam").orElseThrow();<br>
final MethodHandle getpwnamFn =
linker.downcallHandle(getpwnam,
FunctionDescriptor.of(ValueLayout.ADDRESS,
ValueLayout.ADDRESS));<br>
final StructLayout passwdLayout =
MemoryLayout.structLayout(<br>
ValueLayout.ADDRESS.withName("pw_name"),<br>
ValueLayout.ADDRESS.withName("pw_passwd"),<br>
ValueLayout.JAVA_INT.withName("pw_uid"),<br>
ValueLayout.JAVA_INT.withName("pw_gid"),<br>
ValueLayout.JAVA_LONG.withName("pw_change"),<br>
ValueLayout.ADDRESS.withName("pw_class"),<br>
ValueLayout.ADDRESS.withName("pw_gecos"),<br>
ValueLayout.ADDRESS.withName("pw_dir"),<br>
ValueLayout.ADDRESS.withName("pw_shell"),<br>
ValueLayout.JAVA_LONG.withName("pw_expire"),<br>
ValueLayout.JAVA_INT.withName("pw_fields"));<br>
try (final Arena arena = Arena.ofConfined()) {<br>
final MemorySegment username =
arena.allocateUtf8String(user);<br>
final MemorySegment passwd = ((MemorySegment)
getpwnamFn.invoke(username)).reinterpret(Long.MAX_VALUE);<br>
final MemorySegment dir = passwd.get(<br>
ValueLayout.ADDRESS,<br>
passwdLayout.byteOffset(MemoryLayout.PathElement.groupElement("pw_dir")));<br>
System.out.println(dir.reinterpret(Long.MAX_VALUE).getUtf8String(0));<br>
}
<div><br>
</div>
<div>This code snippet only works <span class="gmail_default" style="font-family:arial,sans-serif">on macOS because the
layout differs on other platforms.</span></div>
<div><font face="arial, sans-serif"><br>
</font></div>
<div><font face="arial, sans-serif"><span class="gmail_default" style="font-family:arial,sans-serif">In Rust, we can use
#[target(os = ..)] to switch the manner, and in Java,
perhaps we can use inheritance or interfaces, but it still
lacks:</span></font></div>
<div><font face="arial, sans-serif"><br>
</font></div>
<div><font face="arial, sans-serif"><span class="gmail_default" style="font-family:arial,sans-serif">* A unified way to
determine current os, arch, toolchain, etc. I made [1]
that can help but it's no more than another incomplete
slang.</span></font></div>
<div><font face="arial, sans-serif"><span class="gmail_default" style="font-family:arial,sans-serif">* A compile-time
dispatch decision. Maybe with static initialization block
it can helps by generating 'static final'
MethodHandle/VarHandle. I don't know.</span><br>
</font><br>
Best,<br>
tison.</div>
<div><br>
</div>
<div>
<div class="gmail_default" style="font-family:arial,sans-serif">[1] <a href="http://github.com/tisonspieces/os-detector" moz-do-not-send="true">github.com/tisonspieces/os-detector</a></div>
<br>
</div>
</div>
</blockquote>
</body>
</html>