Jump to content

pointer dereference issues during call to exported function


Recommended Posts

Hello,

   I am trying to call a function in a DLL whose signature is as below

 

datablock* foo(datablock db);

 

where datablock is :

typedef struct

{

unsigned char data[10240];

unsigned int no_of_bytes;

unsigned long start;

} datablock;

 

this is actaually a wrapper for a function which return the datablock by value.

 

Labview Import manager chokes and i have to kill labview, when i try to import this function. so i tried to call this function with call library function node and i am unable to get it to work.

 

In the call library function i tried to pass the struct by using

1)adapt to datatype,

2) spliting the struct and passing the elements individually.

 

The call library function returns a pointer which i tried to give it to MoveBlock and tried to move the data but i get Access violation(which leads me to think i'm trespassing labview's memory).

 

i tried using GetVallueByPointer.xnode. This time it did not crash but it does not give proper results. from what i observer only the no_of_bytes seems to be correctly received.

 

basically what i saw is that i can neither pass nor receive these structs by pointer ref and dereference them successfully..

One more thing is that if i try to write the struct argument in the function labview immediately crashes always.

 

i have attached the code (demo.cpp,demo.h), dll(demo.dll), VI that reads this dll.

I used gcc 4.8.1 to generate the dll and LabVIEW 2013.

 

i have searched

(http://lavag.org/forum/25-calling-external-code/?prune_day=100&sort_by=Z-A&sort_key=last_post&topicfilter=all),

(http://lavag.org/topic/17100-problems-with-calling-a-dll/)

(https://decibel.ni.com/content/docs/DOC-9079)

i also searched labview help but am still failing to get this to work

Thanks for the help in advance.

Labview_cpp_dll.zip

Link to comment

Hello,

   I am trying to call a function in a DLL whose signature is as below

 

datablock* foo(datablock db);

 

where datablock is :

typedef struct

{

unsigned char data[10240];

unsigned int no_of_bytes;

unsigned long start;

} datablock;

 

this is actaually a wrapper for a function which return the datablock by value.

 

Labview Import manager chokes and i have to kill labview, when i try to import this function. so i tried to call this function with call library function node and i am unable to get it to work.

 

In the call library function i tried to pass the struct by using

1)adapt to datatype,

2) spliting the struct and passing the elements individually.

 

The call library function returns a pointer which i tried to give it to MoveBlock and tried to move the data but i get Access violation(which leads me to think i'm trespassing labview's memory).

 

i tried using GetVallueByPointer.xnode. This time it did not crash but it does not give proper results. from what i observer only the no_of_bytes seems to be correctly received.

 

basically what i saw is that i can neither pass nor receive these structs by pointer ref and dereference them successfully..

One more thing is that if i try to write the struct argument in the function labview immediately crashes always.

 

i have attached the code (demo.cpp,demo.h), dll(demo.dll), VI that reads this dll.

I used gcc 4.8.1 to generate the dll and LabVIEW 2013.

 

i have searched

(http://lavag.org/forum/25-calling-external-code/?prune_day=100&sort_by=Z-A&sort_key=last_post&topicfilter=all),

(http://lavag.org/topic/17100-problems-with-calling-a-dll/)

(https://decibel.ni.com/content/docs/DOC-9079)

i also searched labview help but am still failing to get this to work

Thanks for the help in advance.

 

This prototype seems very awkward. Passing a cluster by value means usually that all the elements in the cluster are normally passed as individual parameters over the stack. BUT there are several issues with that!

 

First the fixed size string obviously can't be passed over the stack as individual bytes as that would blow your stack immediately. So it's likely that the C compiler would pass it as C string pointer anyhow, but likely means it is just an assumption from my side, I don't really know, and likely different compilers might think different about how to do that, so you are dealing with an API that is most likely not even compiler independent in this way.

 

But the more interesting thing is that the function seems to return a pointer to that structure. However since the structure was never passed into the function as such, the C compiler would have to allocate it somehow. And now you have another problem to know how it does it. Is it a static memory area that is reused with every function call, causing nasty multithreading issues, or is it mallocing the buffer relying on the caller to properly deallocate it after each call?? And if it is using malloc, which one does it use???

 

Basically this function signature causes so many questions, und ambiguous answers that most likely will depend on the used C compiler too, that it is simply a stupid idea to even do in C, lets not talk about trying to call it from anything else. The only way to answer these questions properly is to dissassemble the DLL and check these things out specifically in the assembly code and then hoping that the DLL developer won't release a new DLL version that is compiled with a different C compiler version.

 

If you don't find this worrying enough, I'm not sure there is anything that would worry you in this world! :P

 

Well I now see that it is your DLL!!!!! Forget it and change that signature immediately!!!

 

1) Pass this structure as a pointer parameter, then Adapt to Type will generate the correct parameter passing.

 

2) Do NOT use malloc in a function to return memory buffers to the caller UNLESS you also provide according exported functions to the caller to deallocate those buffers. The malloc/free used by your DLL is only guaranteed to be the same malloc/free used by the caller, if both are compiled with exactly the same C compiler and the same linker settings and those linker settings don't include the static C runtime library into you DLL. Something you generally NEVER can assume when using DLLs, and in your case anyhow not true, since your DLL is generated using gcc while LabVIEW is compiled using Visual C. And various LabVIEW versions are compiled using different Visual C versions, with each having a different C runtime library. But don't believe gcc is better in that respect!

Link to comment

@rolfk thanks for the reply. 

The code that i posted is actually what i wrote as an example that is a stripped down version of original code. the developer has given two functions in which one takes the struct by val and the other returns by val. what is did was write a sample function i.e foo which takes struct by val and returns a copy of it(explicitly malloc'ed). So i will have to deallocate it from labview.(i have written a function for this dll that deallocates it).

 

1) "So it's likely that the C compiler would pass it as C string pointer anyhow"

   the data field is not to be treated as a c-string. they are the flash memory data that has been stored in the byte array form. since as you pointed out that they are large and that they are likely to be passed as strings then it means that if i do a memcpy it will not do a deep copy right?. so the data is lost? the original dev wrote the dll in visual studio and it works there. i write a commandline and call this func it works. but labview is giving me a hard time.

 

2)"But the more interesting thing is that the function seems to return a pointer to that structure"

   The pointer that is returned is to an explicitly malloc'ed location that is done in the wrapper function. so that location should still be available for me when i return it to labview?

 

3) "If you don't find this worrying enough, I'm not sure there is anything that would worry you in this world!"

    its my 6th day tring to beat down on this one function. :angry::throwpc::shifty:

 

Btw this is the BSL430_dll from ti. currently there is only python version. i got this last year. i'm trying to adapt certain functions that are problematic to labview

 

4) Well I now see that it is your DLL!!!!! Forget it and change that signature immediately!!!

  it is my dll which i am using to debug the actual code. this is the isolated part of the code.

5) "you also provide according exported functions to the caller to deallocate those buffers"

Yes i have written a function to deallocate the malloced data and yes i do use free() only.

 

6) The actaul dll is compiled using visual c++ compiler 2013 ed. this dll(demo.dll) that i have attached is mine for test purose

 

the actual scenario is :

the original functions: compiled in visual c++ compiler

void some_func(datablock);

datablock some_other_func();

 

my wrappers to the second(some_other_func())

is datablock* some_other_func_alt();

no wrapper to the first. but as you suggested i will change the parameter and will manage it but the return of the second is what is bothering me.

 

to test if this is ok or not i created this demo.dll in which i use the above foo() function. i just wanted to check if labview can read and write this structs.

but it is failing.

sorry if this is confusing. My apologies

Edited by koushik
Link to comment

Can you post the header file (the function prototypes) for the real functions you want to call?

 

The prototype for the wrapper around the function that returns the datablock struct by value should be something like:

void some_other_func_alt(datablock *db);

 

Then you call that from LabVIEW, pre-allocating the datablock struct as a cluster and passing it (Adapt to Type) to the wrapper. Inside the wrapper, do a memcpy from the returned struct, into the struct that was passed by pointer. While it would be nice to avoid that extra memory copy, I'm not sure there's a way to do so.

Link to comment

If you are going to write a wrapper anyhow I would forget about the stupid cluster and make it something like:

void LV_some_func(char* data, int length, long start)




declare the struct in your code and copy the elements into it then call the API function.


void LV_some_other_func(char* data, int *length, long *start)


Here get the struct with the API function and then copy the contents into the parameters. Make sure to pass in "length" the actually allocated buffer length in the caller (your LabVIEW diagram), and to not copy more than that into the buffer. Then update "length" and "start" and return to LabVIEW.

 

Something along these lines±

void LV_write_block(const char* data, int length, long start)
{
	datablock block;
	block.no_of_bytes = length > 10240 ? 10240 : length;
	block.start = start;
	memcpy(block.data, data, block.no_of_bytes);
	write_block(block);
}

void LV_read_block(char* data, int *length, long *start)
{
	datablock *block = read_block();
	*length = *length > block->no_of_bytes ? block->no_of_bytes : length;
	*start = block->start;
	memcpy(data, block->data, *length);
	deallocate_block(block); // This depends how the block is allocated.
                                 // If it is a static memory then this would be not possible.
}

But as pointed out before, the struct containing fixed size arrays being passed by value is likely a problem when not using the same compiler for your wrapper DLL then what was used for your library DLL you want to interface.

 

 

And just for your information, long is only a 32 bit integer in Windows, even when using 64 Bit Windows and LabVIEW. long is only 64 Bits in 64 Bit Linux (and similar unix environments)!!!

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.