NI Linux Real-Time Discussions

Showing results for 
Search instead for 
Did you mean: 
Reply

Calling a .so generated in LabVIEW from a C application

Solved!
Go to Solution
Highlighted

Calling a .so generated in LabVIEW from a C application

[ Edited ]

Hi,

 

I have a cRIO 9039 running NI Linux RT.

I have created a .so shared library compiling it in LabVIEW RT (Build Specifications -> New -> Shared Library).

The .so is quite simple: it contains just a function that swith the state of the user led on the CRIO 9039.

I tested it in LabVIEW by implementing a VI that calls the .so, by using a Call Library Function Node, and it works.

Let's call the shared library libMySharedLibrary.so

 

Now, I would like to implement a C that calls the function setLed() of the .so library. Something like this:

 

#include "MySharedLibraryHeader.h"

void main() {
	setLed(1);
	return;
}

setLed() is defined like this:

 

void setLed(uint8 ledStatus);

 

The problem is that when I try to cross compile the code (using the compiler provided by NI for NI Linux RT) the compilation fails due to the unresolved dependecies of libMySharedLibrary.so.

Indeed, it seems that the library I have created calls another .so library, probably liblvrt.so. If I include this library in the compilation others dependecies pop up.

This is the call to the compiler:

/usr/local/oecore-x86_64/sysroots/x86_64-nilrtsdk-linux/usr/bin/x86_64-nilrt-linux/x86_64-nilrt-linux-gcc -I./path-to-header -L./path-to-library -lMySharedLibrary main.c -o main.exe

And this is the output:

D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `ReleaseEPDS'
D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `InitLVClient'
D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `GetSharedLibPath'
D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `WaitLVDLLReady'
D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `UninitLVClient'
D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `ReserveEPDS'
D:\prj\Work\LabVIEW\Java-DLL\lnx64\LabVIEW-example\labview/libjavadllLabVIEW.so: undefined reference to `CallVIFromDll'
collect2.exe: error: ld returned 1 exit status

 

Resuming my main questions are:

- Is it possible to use a .so generated by LabVIEW in a C application?

- If yes, how am I supposed to compile it? What is the complete list of dependecies?

 

Thanks

 

PS: if you are wondering why I want to do this, I have a C application and I am trying to port it on the cRIO.

0 Kudos
Message 1 of 5 (186 Views)
Reply
0 Kudos

Re: Calling a .so generated in LabVIEW from a C application

Hi Cthulhu-IK4,

 

I'll take a closer look at the resulting library following the basic steps that you outlined, but, if you don't mind, can you try using dlopen()/dlsym()/dlclose() in your C application to dynamically load the generated binary? It's not quite as straight-forward, but it may be a workaround, and it's certainly a useful datapoint if that works/fails.

0 Kudos
Message 2 of 5 (155 Views)
Reply
0 Kudos
Solution
Accepted by topic author Cthulhu-IK4
‎03-06-2017 01:10 AM

Re: Calling a .so generated in LabVIEW from a C application

[ Edited ]

On a quick check, it seems that you need to link to libdl anway (the generated .so I made had an undefined reference to those symbols)

 

// Check for unresolved symbols (functions defined in other libraries)
~/share/My_Shared_Library/home/lvuser/natinst/bin $ nm -u SharedLib.so | grep -E "dl(open|sym|close)"
U dlclose
U dlopen
U dlsym

Now, the next issue that I ran into is that the libraries (the exported library from LV as well as libraries that the LV runtime tries to load) don't exist on the normal library paths that the system will check (and will populate the shared library cache /etc/ld.so.cache).

 

I was able to work around that by liberal (mis)use of the LD_LIBRARY_PATH variable when calling the resulting binary that uses the library that comes from LV. Nonetheless, I was able to get the binary to run and function correctly.

 

Here's the steps that I took:

 

Create a simple VI that does nothing but... add two numbers.

simple_vi.jpg

(note: if you're passing arguments to/from the VI, be sure to assign terminals to the controls/indicators)

 

Right-click the "Build Specifications" item under the RT target and select "New»Shared Library"

In the configuration dialog, make sure to make the following selections:

  • Source Files
    • Select the VI that you want to use
      • In my case, I made sure to define the VI prototype to match what I wanted from the VI: pass in values from my C code and retrieve results from the VI to use in my C code
  • Advanced
    • Check "Include additional LabVIEW header files
      • This is needed if you #include the generated header file, but is not required if you dlopen your generated shared library

LibConfig.jpg

 

With these selections made to the default values, build the shared library. This results in the following files being created (where you've specified the results be dropped on disk)

BuiltProject.jpg

 

Now, here, I cheated a little. I already use a Linux desktop for my development, so I just copied the files over to my development machine and hacked together a quick C file to use the generated library file from LV.

 

test.c:

#include"SharedLib.h"

int main() {
     return Add(1,2);
}

 

Now, when compiling the thing, I used the following commands (since my dev machine is an x64 Linux machine, and I'm targeting an x64 Linux machine, I used used my resident toolchain to compile, ideally you'd use the cross-compile toolchain, but the idea works fine using the cross-compile toolchain and/or can be configured in Eclipse's project settings if you're using that)

 

~/share/My_Shared_Library/home/lvuser/natinst/bin $ gcc -o test -ldl -lstdc++ test.c SharedLib.so

I needed to pass along -ldl -lstdc++ since SharedLib.so actually has unresolved symbols that are provided by both of these libraries. The reason that I simply pass SharedLib.so  as an additional parameter is due to the fact that gcc (actually ld which is called by gcc) assumes that any parameter passed with a -l argument will be prefixed with lib (and therefore, using -L -lSharedLib.so fails to find the library. I could've simply renamed the library, but why not use it as an excuse to share information Smiley Happygcc will dutifully pass along objects (.o), object archives (.a) and shared objects (.so) files to the underlying linker ld for final binary creation, so by passing SharedLib.so, the Add() symbol (which is not defined in test.c) is resolved when ld examines SharedLib.so.

 

This results in a binary that seems all well and fine, but I know that it will attempt to use LVRT libraries and such, things that I don't have on my desktop system, so I don't even attempt to run on my desktop and instead just transfer the files to my target.

 

When I attempt to run the resulting binary on my target (you also need to transfer over SharedLib.so, at least), I am greeted with the following error:

upandatthem:~/My_Shared_Library/home/lvuser/natinst/bin$ ./test
./test: error while loading shared libraries: SharedLib.so: cannot open shared object file: No such file or directory

This error message is pretty clear, but if you're not familiar with how Linux library loading works, it can be confusing ("What do you mean 'No such file or directory'? It's right there beside you!")

 

When you have a library (.so) that exists in a location other than the normal search locations (defaults are /lib,/usr/lib, many distributions, including NI Linux RT, will add /usr/local/lib, NI also adds /usr/local/natinst/lib).

 

You can either drop SharedLib.so in one of the seach paths, or direct the library loading mechanism to look elsewhere for libraries. One such method (a quick-n-dirty fix) is to set the LD_LIBRARY_PATH environment variable prior to launching your binary. This variable will direct the library loading system to check the paths defined in the variable first, before checking the normal locations (including the cache of locations for libraries in the normal search paths). The more correct way to solve this particular issue is either to put libraries in searched paths or to pass gcc the -Wl,-rpath,/additional/path/to/search

 

If I set the variable to include just the location where I have put SharedLib.so, I will be greeted by errors indicating that the binary is unable to find some NI libraries. These libraries are included in the directory /usr/local/natinst/labview, a path that is excluded from the library search path. If I change the LD_LIBRARY_PATH variable to include both paths, I see that the binary executes:

 

upandatthem:~/My_Shared_Library/home/lvuser/natinst/bin$ LD_LIBRARY_PATH=$(pwd):/usr/local/natinst/labview/ ./test
mlockall returned -1
Welcome to LabVIEW Real-Time

upandatthem:~/My_Shared_Library/home/lvuser/natinst/bin$ echo $?
3

Here, you can see that the binary ran, complained that mlockall() failed (expected, since I'm running the binary as lvuser who's a less-privileged user), and it exits. When I wrote the wrapper source code, I simply had it pass x=1y=2, and return the result from the VI on the x_plus_y terminal. This does not print, but rather is the return code of the binary itself, and can be seen by checking bash's special $? variable.

 

So, there you have it. Hopefully this gets you unstuck and on your way.

Message 3 of 5 (152 Views)

Re: Calling a .so generated in LabVIEW from a C application

A few things to keep in mind:

  • You may need to use setcap to add capabilities to your binary if you intend on running your binary as the lvuser user (which I would recommend for security reasons, also it's been more thoroughly tested this way).
    • If your G code does things like opening network ports below 1000, does sysapi "set" calls, reboots the system, etc., you'll probably need to set capabilities. Add sufficient error handling to your application to check for this to help you figure out the capabilities you need.
  • As soon as you set capabilities on a binary, the OS will ignore LD_LIBRARY_PATH, so you'll have to build the binary with rpath settings, at least for the LV library location.
Message 4 of 5 (120 Views)

Re: Calling a .so generated in LabVIEW from a C application

Also-also, in the fervor of posting all of that, I neglected to mention something extremely important:

 

THIS IS NOT (CURRENTLY) SUPPORTED BY NI

It has not been extensively tested, there are many likely issues or pitfalls, and it may kill small kittens in the process. See: How Can I Use LabVIEW Built NI Linux Real-Time Shared Libraries

Message 5 of 5 (115 Views)