Proposal: Use new JDK_EXPORT decorator instead of JNIEXPORT

David Holmes david.holmes at oracle.com
Mon Dec 17 03:07:48 UTC 2018


On 17/12/2018 12:51 pm, dean.long at oracle.com wrote:
> If we are adding JDK_EXPORT, does it make sense to add JDK_CALL as well?

It should never be necessary. JNICALL is only needed to ensure caller 
and callee use the same calling convention. For JDK_EXPORT the caller 
and callee are part of the same thing - the JDK - and will always use 
the same (platform default) calling convention.

David

> dl
> 
> On 12/16/18 4:57 PM, David Holmes wrote:
>> Hi Magnus,
>>
>> Thanks for explaining how addition of JNIEXPORT may have started this 
>> problem.
>>
>> One follow up:
>>
>>>> This will also need a CSR request due to the change in linking 
>>>> behaviour.
>>> I'm not sure what you mean by this..? My entire intention here is 
>>> that we should make no changes to documented interfaces; this is 
>>> strictly an internal change, so no CSR should be needed. Also, I 
>>> don't understand what you mean by "linking behavior"? 
>>
>> A CSR request is also required for behavioural changes - which this 
>> is. Someone linking an "internal only" function today may get an error 
>> if JNICALL is removed tomorrow. This may be acceptable but that is 
>> what the CSR request establishes.
>>
>> Thanks,
>> David
>>
>>
>> On 13/12/2018 8:37 pm, Magnus Ihse Bursie wrote:
>>> On 2018-12-12 13:17, David Holmes wrote:
>>>> Okay I went away and did some homework ...
>>>>
>>>> Let me back up a bit and see if I have the evolution of this correctly.
>>>>
>>>> The native implementation of Java methods should be declared and 
>>>> defined with JNIEXPORT and JNICALL.
>>>>
>>>> JNIEXPORT controls the export visibility so they can be linked.
>>>>
>>>> JNICALL controls the calling convention and is needed so that the 
>>>> JVM's calling convention matches the native code. [This part was 
>>>> unclear to me.]
>>> Yes. And JNICALL is empty on all platforms except Windows 32, that's 
>>> why we're only seeing issues about mismatch there.
>>>>
>>>> Other native methods exported from the same (or different) native 
>>>> libraries may also be decorated with JNIEXPORT and JNICALL. But in 
>>>> places there is a mismatch between the declaration in the header and 
>>>> the definition leading to errors.
>>> Yes; this mismatch has typically happened when the linking has not 
>>> been done by simply including the relevant header file, but either 
>>> duplicating the definition, or looking up the symbol dynamically. 
>>> Otherwise things should basically work out.
>>>>
>>>> There are two main types of such native functions:
>>>>
>>>> a) those publicly defined in the various native APIs: JNI itself 
>>>> (jni.h), JVM TI (jvmti.h), AWT (jawt.h) ...
>>>>
>>>> b) those intended only for use by other native code within the JDK 
>>>> libraries (JLI_* - though I note Alan's comment re javafxpackager, 
>>>> others??)
>>>>
>>>> and a possible third type are callback functions like the JPLISAgent 
>>>> event handlers (e.g. eventHandlerVMInit).
>>>
>>> I'm not sure I understand what the third type is, but if it is a 
>>> publically defined API (which, at a first look, the JPLISAgent API 
>>> seems to be), then it's part of catagory a, otherwise it's part of 
>>> category b.
>>>
>>> I must also state that my initial proposal didn't separate these two 
>>> cases. I had in mind only the b cases -- I have no intention of 
>>> changing public specifications, but I did not express this in my 
>>> proposal. That might have been one source of confusion. I apologize 
>>> for this omission.
>>>>
>>>> There is a view that no native method that isn't the native-half of 
>>>> a Java method should use JNICALL. [Okay I can see how that makes 
>>>> sense - regardless of the actual calling convention used marking 
>>>> such methods as "must use the same calling convention as the VM 
>>>> native method call" is somewhat meaningless. Yet obviously the 
>>>> public native APIs do specify JNICALL so this is not a hard and fast 
>>>> rule. Further the callbacks must also follow a convention known to 
>>>> the VM doing the calling!]
>>> Yes, or rather the rule is "only native half Java methods should use 
>>> JNICALL, and also all public APIs where so is specified".
>>>
>>>>
>>>> Where we have (introduced?) a discrepancy between the definition and 
>>>> declaration the approach has been (because of the previous view) to 
>>>> remove JNICALL. [This should only relate to methods of kind (b) above.]
>>> Actually, it's not so much as we have *removed* JNICALL, as that we 
>>> have *introduced* JNIEXPORT. When I removed the map files, I also 
>>> removed the .def files and /export command lines for Windows. As a 
>>> result, I needed to add JNIEXPORT to a lot of places. Initially, I 
>>> followed the rule of adding JNICALL to those calls -- but I can 
>>> surely have missed a couple of places, since things will work fine 
>>> anyway, as long as caller and callee agree on the calling convention; 
>>> and a mismatch can only happen on Windows 32, which we do not build 
>>> and test. So there is a risk that even with the initial patch, not 
>>> all newly added JNIEXPORTs had JNICALL.
>>>
>>> Then, it turned out, that a lot of this code did not compile with 
>>> Windows 32. With no deeper analysis of the flaw, the blame was put on 
>>> the absence or presence of JNICALL, and a series of patches were made 
>>> where JNICALL was more or less arbitrarily added or removed, until 
>>> things "worked". This should have been a warning sign, and I was 
>>> increasingly uneasy about all these changes, but I hadn't spent 
>>> enough time to realize what the core issue was or how to resolve it 
>>> properly.
>>>
>>>>
>>>> That leaves those methods with JNIEXPORT only.
>>>>
>>>> That seems wrong to you because they are not "JNI methods", so you 
>>>> want to replace with JDK_EXPORT to make it clear. [Ok - this seems 
>>>> reasonable.]
>>> Yes. And given the fact that we have a bunch of "non-JNI methods" 
>>> that use the JNIEXPORT...JNICALL pattern, another way to interpret 
>>> the JDK_EXPORT semantics is that this function is exported for usage 
>>> *in the JDK*, but not for public consumption.
>>>>
>>>> If JNICALL is removed from those native methods and they are 
>>>> currently linked by external applications, those applications may 
>>>> stop working. But this only affects win32 (as its the only platform 
>>>> where JNICALL is different to the default C/C++ calling convention?) 
>>>> so your position is this is acceptable breakage - and would only 
>>>> affect unsupported/undocumented APIs.
>>> Yes. In fact, it's possible that at least some of the breakage that 
>>> occurred was due to new *addition* of JNICALL, which was not present 
>>> before. We might have had something like (I'm making this specific 
>>> example up) a function "void ZIP_OpenFile(char* name)" with no 
>>> decoration at all, and a "/export:ZIP_OpenFile" on the command line, 
>>> and a ZIP_OpenFile entry in the unix map file. And I converted this 
>>> to "JNIEXPORT void JNICALL ZIP_OpenFile(char* name)", which de facto 
>>> -- although I didn't fully realize this at the time, changed the 
>>> calling convention and name decoration on Windows 32. When something 
>>> broke, perhaps because the user of ZIP_OpenFile did not include the 
>>> proper header file with the JNICALL modifier, the solution was to 
>>> remove the JNICALL part.
>>>
>>> And of all the bug reports I've seen on this, the issues has been 
>>> internal linking only, i.e. problems building the JDK, not complaints 
>>> that external tools tried to use internal interfaces and failed. So 
>>> yes, my position is if this should break things, tough shit. That, of 
>>> courses, requires that we do not change public APIs, so we need to be 
>>> careful not to mess with those.
>>>>
>>>> Does that sum it up?
>>> Yep, I think so.
>>>>
>>>> We still need to be sure that we're only changing functions of type 
>>>> (b) above.
>>> Yes, definitely.
>>>>
>>>> And I note that this has been driven by the choice to remove JNICALL 
>>>> where there was a discrepancy - that leads to the visibility of the 
>>>> two kinds of methods. If it had been chosen to add JNICALL where 
>>>> missing instead, then we may not have been having this conversation.
>>> Actually, as I said, this has more been the result of a lot of added 
>>> JNICALL where previously there was none.
>>>
>>> An alternative course of action is the make sure that *all* calls use 
>>> the JNIEXPORT...JNICALL pattern, even type b ones, and that we fix 
>>> all parts of code to actually accept the decorated names on Windows 
>>> 32. This will lead to a lot more code changes, like the fix for 
>>> JDK-8214122 (JDWP is broken on 32 bit Windows: transport library 
>>> missing onLoad entry). And all this special case handling will be 
>>> needed only on Windows 32, which is a platform we do not want to 
>>> spend to much time or effort on. And finally, I think doing so would 
>>> make us miss out on an opportunity to make the semantics clearer.
>>>>
>>>> This will also need a CSR request due to the change in linking 
>>>> behaviour.
>>> I'm not sure what you mean by this..? My entire intention here is 
>>> that we should make no changes to documented interfaces; this is 
>>> strictly an internal change, so no CSR should be needed. Also, I 
>>> don't understand what you mean by "linking behavior"?
>>>
>>> /Magnus
>>>>
>>>> Cheers,
>>>> David
>>>> -----
>>>>
>>>> On 12/12/2018 9:03 pm, Magnus Ihse Bursie wrote:
>>>>>
>>>>>
>>>>> On 2018-12-11 23:47, David Holmes wrote:
>>>>>> On 12/12/2018 12:34 am, Magnus Ihse Bursie wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 2018-12-11 00:23, David Holmes wrote:
>>>>>>>> Hi Magnus,
>>>>>>>>
>>>>>>>> On 10/12/2018 11:19 pm, Magnus Ihse Bursie wrote:
>>>>>>>>> I propose that we introduce a new define, available to all JDK 
>>>>>>>>> native files (Hotspot included), called JDK_EXPORT. The 
>>>>>>>>> behavior of this symbol will be very similar (as of now, in 
>>>>>>>>> fact identical) to JNIEXPORT; however, the semantics will not.
>>>>>>>>>
>>>>>>>>> Currently, we "mis-use" the JNIEXPORT define to mark a function 
>>>>>>>>> for exporting from the library. The problem with this is that 
>>>>>>>>> JNIEXPORT is part of the JNI interface, and is supposed to be 
>>>>>>>>> used when C programs interact with Java. And, when doing this, 
>>>>>>>>> the function should be fully decorated like this: "JNIEXPORT 
>>>>>>>>> foo JNICALL".
>>>>>>>>
>>>>>>>> I've seen a lot of the emails on this issue and I don't fully 
>>>>>>>> understand what has been going wrong. But the intent is 
>>>>>>>> obviously the JNIEXPORT represents what is needed to export this 
>>>>>>>> function for use by JNI, while JNICALL defines the calling 
>>>>>>>> convention. I agree there may be some mistmatch when functions 
>>>>>>>> are actually not intended for general export outside the JDK but 
>>>>>>>> are only for internal JDK use.
>>>>>>>>
>>>>>>>>> We do have many such JNI exports in our native libraries, but 
>>>>>>>>> we also have a lot of other, non-JNI exports, where one native 
>>>>>>>>> library just provides an interface to other libraries. In these 
>>>>>>>>> cases, we have still used JNIEXPORT for the functionality of 
>>>>>>>>> getting the function exported, but we have not been consistent 
>>>>>>>>> in our use of JNICALL. This has caused us way too much trouble 
>>>>>>>>> for something that should Just Work<tm>.
>>>>>>>>
>>>>>>>> Are you suggesting that the interface between different 
>>>>>>>> libraries in the JDK should not be a JNI interface? Is this 
>>>>>>>> because you think the functions in these libraries are only for 
>>>>>>>> JDK internal use or ... ??
>>>>>>>>
>>>>>>>>> I therefore propose that we define "JDK_EXPORT", with the same 
>>>>>>>>> behavior as JNIEXPORT (that is, flagging the function for 
>>>>>>>>> external visibility in the resulting native library), but which 
>>>>>>>>> is *not* supposed to be exported to Java code using JNI, nor 
>>>>>>>>> supposed to be decorated with 
>>>>>>>>
>>>>>>>> Just a clarification there. JNI functions are not exported to 
>>>>>>>> Java code, they are exported to native code. Java code can 
>>>>>>>> declare native methods and those native methods must be written 
>>>>>>>> as JNI functions, but that's not what we are discussing. 
>>>>>>>> Libraries expose a JNI interface (a set of functions in the 
>>>>>>>> library) that can be called by application native code, using JNI.
>>>>>>> We're apparently looking at "JNI" and "exporting" from two 
>>>>>>> opposite sides here. :-) Just to make everything clear: If I have 
>>>>>>> a Java class
>>>>>>> class MyClass {
>>>>>>>    public static void native myNativeFunc();
>>>>>>> }
>>>>>>>
>>>>>>> then I have one half of the JNI function, the Java half. This 
>>>>>>> must be matched by a corresponding implementation in native, like 
>>>>>>> this:
>>>>>>> JNIEXPORT void JNICALL
>>>>>>> Java_MyClass_myNativeFunc(void) {
>>>>>>> // ... do stuff
>>>>>>> }
>>>>>>>
>>>>>>> And this is the native half of the JNI function. Right? Let's 
>>>>>>> leave aside which side is "exporting" to the other for now. :-)
>>>>>>>
>>>>>>> This way of setting up native functions that can be called from 
>>>>>>> Java is what I refer to as JNI. And when you declare a native JNI 
>>>>>>> function, you *must* use both JNIEXPORT and JNICALL. Alright?
>>>>>>>
>>>>>>> We do have a lot of those functions in our native libraries. And 
>>>>>>> they are correct just the way they are.
>>>>>>
>>>>>> Yep all well and good. A function declared native in Java must 
>>>>>> have an implementation as you describe. But not all native 
>>>>>> functions exist to provide the native-half of a Java native function!
>>>>>>
>>>>>>> However, we also have *other* native functions, that are 
>>>>>>> exported, not as JNI functions that should be called from Java, 
>>>>>>> but as normal native library functions that should be called by 
>>>>>>> other native code. Okay so far? And *those* functions have been 
>>>>>>> problematic in how we decorate 
>>>>>>
>>>>>> But there are again two cases. Those functions exported from a 
>>>>>> library that are expected to be called from external code using 
>>>>>> the JNI interface mechanism - such as all the JNI functions and 
>>>>>> JVM TI functions we export from the JVM - and those "exported" for 
>>>>>> access between libraries within the JDK (such as all the JVM_* 
>>>>>> functions in libjvm).
>>>>>>
>>>>>> I think it is only the second group that should be addressed by 
>>>>>> your JDK_EXPORT proposal - though I'm not completely clear exactly 
>>>>>> how to identify them.
>>>>> Alright, now I think we're on the same page again. :)
>>>>>
>>>>> Yes, this is what I'm saying. I'm not proposing to modify public APIs.
>>>>>
>>>>> You identify external APIs by the fact that they are documented. 
>>>>> And if you are unsure, you ask Jon. ;-)
>>>>>
>>>>>>
>>>>>>> them. My proposal is that we *refrain* from using JNIEXPORT for 
>>>>>>> those functions, and instead use JDK_EXPORT as name for the macro 
>>>>>>> that decorates them as exported. That way, we can clearly see 
>>>>>>> that a function like this:
>>>>>>>
>>>>>>> JDK_EXPORT void
>>>>>>> JLI_ReadEnv(char* env);
>>>>>>>
>>>>>>> is correctly declared, and will be exported to other native 
>>>>>>> libraries, but not to Java.
>>>>>>
>>>>>> The issue is not whether it is "exported to Java"** but whether it 
>>>>>> is exported using the JNI mechanism such that other native code 
>>>>>> calls it using the JNI mechanism.
>>>>>>
>>>>>> ** There is no way to write a native method declaration in Java 
>>>>>> such that it would be linked to the JLI_ReadEnv function. The 
>>>>>> naming is all wrong, as is the signature.
>>>>>>
>>>>>>> Just to clarify, this has nothing to do with if this is a 
>>>>>>> officially supported API or not. In general though, I assume that 
>>>>>>> most (if not all?) of our exported functions (apart from the 
>>>>>>> JNI_* stuff) is supposed to be consumed by other libraries in the 
>>>>>>> JDK, and is not a public API.
>>>>>>
>>>>>> I think it varies library by library. You may need native 
>>>>>> application code that can call directly into native JDK libraries. 
>>>>>> JLI is the Java Launcher Interface - I think it was introduced to 
>>>>>> make it easier for other launchers to be created. Native agents 
>>>>>> may need access to libmanagement or libjdwp functions. Native 
>>>>>> graphics code may need access to the JDK graphics library. Some of 
>>>>>> these accesses may be unsupported and undocumented, but I don't 
>>>>>> think you can just cut them all off.
>>>>> If they are unsupported and undocumented, I sure as h*ll can cut 
>>>>> them all off! :-)
>>>>>
>>>>> Besides, and let me re-iterate this, the only change that will 
>>>>> happen by removing JNICALL, is on Windows 32. No other platform 
>>>>> will be affected. There is no official support for Windows 32 
>>>>> anymore. There's some low-effort community work done on keeping 
>>>>> Windows 32 alive, but it's not backed by any major player. Right 
>>>>> now, they are taking a lot of hits *due to the fact* that we have 
>>>>> not sorted this out, and waste a lot of their precious effort 
>>>>> trying to fix this piecemeal. If we do fix things according to this 
>>>>> proposal, then it's at least clear how things *should* work. If it 
>>>>> turns out that there's some code out there in the wild, running on 
>>>>> Windows 32, that uses an undocumented and unsupported function 
>>>>> call, and it breaks -- well, sucks to be them. They'll just have to 
>>>>> do what everyone does who uses an undocumented interface that 
>>>>> suddenly changes: update their code.
>>>>>
>>>>> /Magnus
>>>>>
>>>>>>
>>>>>> David
>>>>>>
>>>>>>>
>>>>>>> /Magnus
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>> JNICALL. All current instances of JNIEXPORT which is not pure 
>>>>>>>>> JNI native functions should be changed to use JDK_EXPORT instead.
>>>>>>>>>
>>>>>>>>> I further propose that this macro should reside in a new file 
>>>>>>>>> "jdk.h", placed in the new directory 
>>>>>>>>> src/java.base/share/native/include/internal. This header file 
>>>>>>>>> path will automatically be provided to all native libraries, 
>>>>>>>>> but not copied to the JDK being built. (The existence of a 
>>>>>>>>> "include/internal" directory with this behavior has been 
>>>>>>>>> discussed before. There are more files that ought to be moved 
>>>>>>>>> there, if/when it is created.) I believe in many cases the 
>>>>>>>>> #include "jni.h" can be just modified to #include "#jdk.h", 
>>>>>>>>> since most native code will not require "jni.h" unless actually 
>>>>>>>>> doing JNI calls -- most have included this file to get the 
>>>>>>>>> JNIEXPORT macro, which would explain the pervasive use of 
>>>>>>>>> #include "jni.h" in our code base.
>>>>>>>>
>>>>>>>> jni.h also defines all of the types used by the JNI. Those types 
>>>>>>>> are pervsive to the native code used throughout the JDK.
>>>>>>>>
>>>>>>>>> Thoughts?
>>>>>>>>
>>>>>>>> I think we need to understand the problems on Windows that 
>>>>>>>> prompted all this. Then I think we need to look at exactly how 
>>>>>>>> jni.h and JNIEXPORT etc are being used and understand whether 
>>>>>>>> this is truly an exported interface or not.
>>>>>>>>
>>>>>>>> Cheers,
>>>>>>>> David
>>>>>>>>
>>>>>>>>> /Magnus
>>>>>>>>>
>>>>>>>
>>>>>
>>>
> 



More information about the build-dev mailing list