如何用ASM编写Java本地代码?参数接收异常求助
Hey there! As someone who's dug into JNI assembly on Windows, I totally get where you're stuck—this is a super common gotcha because JNI doesn't exactly follow the standard Windows x64 calling convention without extra context. Let's break down what's going wrong and how to fix it.
The Key Difference: JNI's Hidden Prefix Parameters
When Java calls a native method, it doesn't just pass your defined arguments first. JNI always prepends two mandatory parameters to the call stack/registers:
JNIEnv*(passed viarcxin Windows x64)- Either
jclass(for static native methods) orjobject(for instance methods, passed viardx)
Your actual user-defined parameters start after these two. So if you were expecting your first parameter in rcx, that's why it's missing—rcx is holding the JNI environment pointer instead!
Example Parameter Mapping
Let's say your Java native method looks like this:
public native int calculate(int num1, String text); // Instance method
In Windows x64 assembly, the parameters will be mapped like this:
rcx:JNIEnv*(required JNI context)rdx:jobject(the instance of your class calling the method)r8:jint num1(your first user parameter)r9:jstring text(your second user parameter)
If it were a static method, rdx would hold the jclass instead of an instance object.
Don't Forget the Shadow Space!
Windows x64 requires callers to allocate 32 bytes of "shadow space" on the stack for the first four register-passed parameters. Even if you don't use this space, you need to reserve it to maintain proper stack alignment.
In assembly, this means you should start your function with:
sub rsp, 28h ; Reserves 32 bytes of shadow space + ensures 16-byte stack alignment
(Note: After a call instruction, the stack is 8 bytes off alignment, so subtracting 28h brings it back to 16-byte alignment while providing the required shadow space.)
Quick Working Example
Here's a minimal assembly function that returns the first user-defined integer parameter from the example above:
; JNI function name: Java_com_yourpackage_YourClass_calculate ; (Replace with your actual package/class/method name, using underscores for dots) Java_com_yourpackage_YourClass_calculate proc ; Reserve shadow space and align stack sub rsp, 28h ; Our user parameter num1 is in r8 (jint is 32-bit, so use eax) mov eax, r8d ; Move the lower 32 bits of r8 into eax (return register for int) ; Clean up stack add rsp, 28h ret Java_com_yourpackage_YourClass_calculate endp
Critical Checks to Debug Your Code
- Verify Function Naming: JNI enforces strict naming rules. For a class
com.foo.Barwith methoddoThing, the function name must beJava_com_foo_Bar_doThing. If your method name has underscores, escape them with another underscore (e.g.,do_thingbecomesdo__thing). - Stack Alignment: Windows x64 requires the stack to be 16-byte aligned at all times. Forgetting shadow space or misaligning the stack will cause unpredictable behavior.
- Debug Register Values: Use a tool like WinDbg to break into your assembly function and inspect the values in
rcx,rdx,r8,r9—you'll see the JNIEnv and class/instance pointers first, followed by your parameters.
Hope this clears up the confusion! It's easy to overlook those hidden JNI parameters when you're used to standard C++ calling conventions.
内容的提问来源于stack exchange,提问作者Penelope Hubble

