I've been writing a java wrapper to a c library, and need to use asm to push the parameters onto the stack to call the library function.

an extract of the c code:

extern void asm_dispatch(void *func, int nwords, char *arg_types, long *args,
							  int res_type, long *resP,int conv);

/********************************************************************/
/*		     Native methods of class CFunction		    */
/********************************************************************/

/* These are the set of types CFunction can handle now */
typedef enum
{
    TY_CPTR = 0,
    TY_INTEGER,
    TY_FLOAT,
    TY_DOUBLE,
    TY_DOUBLE2,
    TY_STRING
} ty_t;

/* represent a machine word */
typedef union
{
    jint i;
    jfloat f;
    void* p;
} word_t;

/* invoke the real native function */
static void
dispatch(JNIEnv *env,
	 jobject self,
	 jobjectArray arr,
	 ty_t res_ty,
	 jvalue *resP)
{
#define MAX_NARGS 32
    int i, nargs, nwords;
    void *func;
    char argTypes[MAX_NARGS];
    word_t *c_args[MAX_NARGS * 2];
    int conv;

    nargs = env->GetArrayLength(arr);
    if (nargs > MAX_NARGS)
    {
        JNU_ThrowByName(env, "java/lang/IllegalArgumentException",
		    "too many arguments");
		return;
    }

    func = (void *)env->GetLongField(self, FID_CPointer_peer);

    for (nwords = 0, i = 0; i < nargs; i++)
    {
        jobject arg = env->GetObjectArrayElement(arr, i);

		if (arg == NULL)
        {
			c_args[nwords]->p = NULL;
			argTypes[nwords++] = TY_CPTR;
		}
        else if (env->IsInstanceOf(arg, Class_Integer))
        {
			c_args[nwords]->i = env->GetIntField(arg, FID_Integer_value);
			argTypes[nwords++] = TY_INTEGER;
		}
        else if (env->IsInstanceOf(arg, Class_CPointer))
        {
			c_args[nwords]->p = (void *)env->GetLongField(arg, FID_CPointer_peer);
			argTypes[nwords++] = TY_CPTR;
			printf("pointer before asm: %p\n", c_args[nwords]->p);
		}
        else if (env->IsInstanceOf(arg, Class_String))
        {
			if ( (c_args[nwords]->p = JNU_GetStringNativeChars(env, (jstring)arg)) == 0 )
            {
				goto cleanup;
			}
			printf("string pointer before asm: %p\n", (char *)c_args[nwords]->p);
			printf("string before asm: %s\n", (char *)c_args[nwords]->p);
			argTypes[nwords++] = TY_STRING;
		}
        else if (env->IsInstanceOf(arg, Class_Float))
        {
			c_args[nwords]->f = env->GetFloatField(arg, FID_Float_value);
			argTypes[nwords++] = TY_FLOAT;
		}
        else if (env->IsInstanceOf(arg, Class_Double))
        {
			*(jdouble *)(c_args + nwords) = env->GetDoubleField(arg, FID_Double_value);
			argTypes[nwords] = TY_DOUBLE;
			/* harmless with 64-bit machines*/
			argTypes[nwords + 1] = TY_DOUBLE2;
			/* make sure things work on 64-bit machines */
			nwords += sizeof(jdouble) / sizeof(word_t);
		}
        else
        {
			JNU_ThrowByName(env, "java/lang/IllegalArgumentException", "unrecognized argument type");
			goto cleanup;
		}
		env->DeleteLocalRef(arg);
    }

    conv = env->GetIntField(self, FID_CFunction_conv);
    asm_dispatch(func, nwords, argTypes, (long*)c_args, res_ty, (long*)resP, conv);

cleanup:
    for (i = 0; i < nwords; i++)
    {
        if (argTypes[i] == TY_STRING)
        {
			free(c_args[i]->p);
		}
    }
    return;
}

and the asm_dispatch function

void asm_dispatch(void *func,
		  int nwords,
		  char *arg_types,
		  long *args,
		  int res_type,
		  long *resP,
		  int conv)
{
    __asm {
	mov esi, args
	mov edx, nwords
	// word address -> byte address
	shl edx, 2
	sub edx, 4
	jc args_done

	// Push the last argument first.
	args_loop:
		mov eax, DWORD PTR [esi+edx]
		push eax
		sub edx, 4
		jge SHORT args_loop

	args_done:
		call func

	mov edx, conv
	or edx, edx
	jnz is_stdcall

	// pop arguments
	mov edx, nwords
        shl edx, 2
        add esp, edx

is_stdcall:
	mov esi, resP
	mov edx, res_type
	dec edx
	jge not_p64

	// p64
	mov [esi], eax
	mov [esi+4], 0
	jmp done

	not_p64:
	dec edx
	jge not_i32

	// i32
	mov [esi], eax
	jmp done

    not_i32:
		dec edx
		jge not_f32

        // f32
		fstp DWORD PTR [esi]
		jmp done

	not_f32:
        // f64
		fstp QWORD PTR [esi]

    done:
    }
}

the c dispatch code takes the java types, converts them and places them in an array of union in the order that the library function should receive them.

the output that i get from my test app

pointer before asm: 7C97E1A0
string pointer before asm: 02E224E0
string before asm: maint
String pointer after asm: 7C97E178
Pointer after asm: 02E20280


the first 3 lines print the pointer address of the 2 parameters ( long*, char* ) and the string stored by the char*, these prints are located in the c function, everything is correct there, but when the data has passed through the asm dispatch function, they seem to get changed alittle, the causes my app to fail. ( the 2 final prints are in the function pointed to by func )

i appear that pointer address are slightly modified and swap over, which is not the behave your i'm looking for.

this has me stumped, been trying to solve the issue for the last day with no success.

i hope this is the appropriate sub-forum

regards, jason

Recommended Answers

All 6 Replies

In this case is long 32-bit or 64-bit?
You're treating it as 32-bit!

I'm not sure what calling convention Java uses but please revise your code and repost!

You can reduce your pointer addressing a tad, use scaling.


I've tweaked a function but I noted a problem
You are essentially looping for (nwords+1)
I've reviewed the c code briefly (really needs decent indentation by the way) and that is a mistake and made changes for looping for count of nwords.

A carry set following a subtraction indicates a borrow occured! In essence the result went negative! That can only happen if you did a count of (nwords+1).

mov esi, args
mov edx, nwords
;;	// word address -> byte address
;;	shl edx, 2
;;	sub edx, 4
;;	jc args_done
               test edx,edx
               jz args_done

	// Push the last argument first.
args_loop:
     dec edx
;;   mov eax, DWORD PTR [esi+edx]
     mov eax, DWORD PTR [esi+edx * 4]
     push eax
;    sub edx, 4
;     jge args_loop
     jnz args_loop

Then you call func, but how does func know how many arguments are on the stack?

Your argument conv doesn't appear to make sense!
You pushed <nwords> onto the stack you need to counteract them to correct the stack pointer

args_done:
; ??? Don't we need to push number of arguments, since its variable or zero?
   call func

; this doesn't belong??? Args WERE pushed on stack so definitely need to adjust stack!
;   mov edx, conv
;   test edx,edx
;   jnz is_stdcall	

     // pop arguments
   mov edx, nwords
   shl edx, 2
   add esp, edx         ; Offset args on stack

For code clarity use something as follows. it's called a vector table, kind of like a case statement!
Also something funky about your <conv> You push arguments, bypass the pops, and then process? review!

is_stdcall:
   mov esi, resP        ; long pointer

;    TY_CPTR = 0,
;    TY_INTEGER,
;    TY_FLOAT,
;    TY_DOUBLE,
;    TY_DOUBLE2,
;    TY_STRING

;;; You could put a jump vector table

    ; Note: eax contains result of function call!

 mov edx, res_type    ; resource type?
 jmp Near Ptr FuncTbl[ edx * 4 ]

FuncTbl:: dw offset FuncPtr
               dw offset FuncInt
               dw offset FuncFlt32
               dw offset FuncDbl
               dw offset FuncDbl2
               dw offset FuncStr


  FuncCptr:
	mov [esi], eax
	mov [esi+4], 0
    jmp done

  FuncInt:
	mov [esi], eax
    jmp done
    
  FuncFlt32:
  FuncDbl:
  FuncDbl2:
	fstp DWORD PTR [esi]
    jmp done

  FuncStr:
    jmp done;

done:
    }

thanks for the assistance wildgoose.

i still get the same issue

should the arguments be pass in the other order, as the pointers seem to swap

pointer before asm: 7C97E1A0
string pointer before asm: 02E222B8
string before asm: maint
String pointer after asm: 7C97E178
Pointer after asm: 02E204A8

when using the revised code you posted.

void asm_dispatch(void *func,
		  int nwords,
		  char *arg_types,
		  long *args,
		  int res_type,
		  long *resP,
		  int conv)
{
    __asm {
	mov esi, args
	mov edx, nwords
	test edx, edx
	jz args_done

	// Push the last argument first.
args_loop:
	dec edx
	mov eax, DWORD PTR [esi+(edx * 4)]
	push eax
	jnz args_loop

args_done:
	call func

	// pop arguments
	mov edx, nwords
	shl edx, 2
	add esp, edx

is_stdcall:
	mov esi, resP
	mov edx, res_type
	dec edx
	jge not_p64

	// p64
	mov [esi], eax
	mov [esi+4], 0
	jmp done

not_p64:
	dec edx
	jge not_i32

	// i32
	mov [esi], eax
	jmp done

not_i32:
	dec edx
	jge not_f32

	// f32
	fstp DWORD PTR [esi]
	jmp done

not_f32:
	// f64
	fstp QWORD PTR [esi]

done:
    }
}

i did not write the original code but got it from http://java.sun.com/docs/books/jni/ chapter 9.

the pointers should be 64bit, and the calling convention is __cdec as junc points to a library function in a .dll.

sorry about the formatting of the earlier code, seemed to get messed up by the copy/paste

the help is much appreciated
regard, jason

ps will fix the indentation, well i would if the edit button appeared

#include <stdlib.h>
#include <stdio.h>
#include "tdapi.h"

#ifdef WIN32
#include <windows.h>
#define LOAD_LIBRARY(name) LoadLibrary(name)
#define FIND_ENTRY(lib, name) GetProcAddress(lib, name)
#endif

/* These are the set of types CFunction can handle now */
typedef enum
{
    TY_CPTR = 0,
    TY_INTEGER,
    TY_FLOAT,
    TY_DOUBLE,
    TY_DOUBLE2,
    TY_STRING
} ty_t;

/* represent a machine word */
typedef union
{
    int i;
    float f;
    void* p;
} word_t;

void asm_dispatch(void *func,
		  int nwords,
		  char *arg_types,
		  long *args,
		  int res_type,
		  long *resP,
		  int conv)
{
__asm {
	mov esi, args
	mov edx, nwords
	test edx, edx
	jz args_done

	// Push the last argument first.
args_loop:
	dec edx
	mov eax, DWORD PTR [esi+(edx * 4)]
	push eax
	jnz args_loop

args_done:
	call func

	// pop arguments
	mov edx, nwords
	shl edx, 2
	add esp, edx

is_stdcall:
	mov esi, resP
	mov edx, res_type
	dec edx
	jge not_p64

	// p64
	mov [esi], eax
	mov [esi+4], 0
	jmp done

not_p64:
	dec edx
	jge not_i32

	// i32
	mov [esi], eax
	jmp done

not_i32:
	dec edx
	jge not_f32

	// f32
	fstp DWORD PTR [esi]
	jmp done

not_f32:
	// f64
	fstp QWORD PTR [esi]

done:
    }
}
int main( int argc, char* argv[] )
{
	#define MAX_NARGS 32
	#define NUM_CARDS 8
	/* card states */
	#define CARD_NIU 0
	#define CARD_REQUIRED 1
	#define CARD_PRESENT 2

	#define NUM_SS7_TRUNKS 16
	#define NUM_SS7_LINKS 2
	#define NUM_TIMESLOTS 32
	typedef int (*FNPTR)(int);
	typedef void (*FNPTR2)(char*);

	int nargs = 2;
	int nwords = 1;
        void *func;
	FNPTR InitM;
	FNPTR2 SetHost;
        char argTypes[MAX_NARGS];
        word_t c_args[MAX_NARGS * 2];
        int conv;
	long pointer = 10;
	long result = 0;
	HMODULE hDLL;
	int stat;
	int cardState[NUM_CARDS];

	if( ( hDLL = LoadLibrary("C:\\ngntdapi.dll")) == NULL )
	{
		printf ("LoadLibrary failed\n");
		return 1;
	}

	func = GetProcAddress( hDLL, "TDapi_GetServiceProvider" );
	InitM = (FNPTR)GetProcAddress( hDLL, "TDapi_InitM" );
	SetHost = (FNPTR2)GetProcAddress( hDLL, "TDapi_SetHost" );

	for(int i=0; i<NUM_CARDS; i++)
	{
		cardState[i] = CARD_NIU;
	}

	if( ( stat = InitM(4000)) != TDAPI_SUCCESS )
	{
		printf("TDapi_Init() failed, status %d", stat);
		return 1;
	}

	if(argc > 1)
	{
		SetHost(argv[1]);
	}

	c_args[0].p = &pointer;
	argTypes[0] = TY_CPTR;

	c_args[1].p = "maint";
	argTypes[1] = TY_STRING;

	printf("pointer before asm: %p\n", c_args[0].p);
	printf("string pointer before asm: %p\n", (char *)c_args[1].p);
	printf("string before asm: %s\n", (char *)c_args[1].p);

	asm_dispatch(func, 2, argTypes, (long*)c_args, TY_INTEGER, (long*)result, 0);
	return 0;
}

i just run that i every worked fine, seems like the issue is not directly related to the asm, problem solved (ish)

any extra comment on the asm code would still be appreciated

regards, jason

I see the C code a bit clearer now!

What is sizeof(word_t)

Is it 16-bit ? 32-bit? It's being passed like its two halves.

Win32 int and long are both 32-bit but the assembly doesn't treat it as such!

The initial loop in assembly only pushs <nwords> 32-bit values onto the stack. If 16-bit is being paired into 32-bit then its fine. But if word_t is 32-bit not 16-bit then only 1/2 the args are being pushed. If that's the case then...

mov edx,nwords
shl edx,1
dec edx

Ah, a fresh shower and everything is clearer!

word_t is a union declaration and currently it is 32-bits.
They haven't included the other data types such as double.

It's unclear what the function being called does, or why it doesn't need everything! It does seem the assembly isn't appropriate for the call, only like its unfinished!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.