Reduce exported symbols of a third party library - c++

I have code that works fine, but if I link my project to a third party lib libabc.so (source unavailable), then all of the sudden I get a segmentation fault.
I have a main that looks like this
#include <opencv2/imgcodecs.hpp>
#include "Abc.h"
int main(int argc, char **argv)
{
Abc dummyAbc;
auto img = cv::imread("dummy.png");
cv::imwrite("123.png", img);
return 0;
}
The CMakeList.txt is as follows
cmake_minimum_required(VERSION 3.1)
set(CMAKE_C_STANDARD 11)
find_package(OpenCV COMPONENTS core highgui imgcodecs)
include_directories(${OpenCV_INCLUDE_DIR})
add_executable(my_project Main.cpp)
target_link_libraries(my_project ${OpenCV_LIBRARIES} abc)
This compiles well, but segfault when running. If I remove the line
Abc dummyAbc;
then all works fine (i.e. there is no problem with missing file, or opencv).
If I inspect the stack of the segfault, I see that:
Thread 1 "my_project" received signal SIGSEGV, Segmentation fault.
0x00007fdea96836b3 in png_destroy_write_struct () from /usr/local/lib/libabc.so
where png_destroy_write_struct is being called by cv::imwrite.
Both libpng.so and libabc.so (!!) export png_destroy_write_struct it actually exports all of the libpng API (which I assume it was statically linked to?). I assume this is the problem? I do no want openCV to see whatever libabc.so exports... How can I do this?
I tried to use objcopy --prefix-symbols abc_ libabc.so but somehow it did not help, now the crash happens at abc_png_destroy_write_struct.

I assume this is the problem?
Yes: that is very likely: libabc.so has statically linked (probably different version of) libpng, and introduces symbol conflict.
I do no want openCV to see whatever libabc.so exports... How can I do this?
You can't. You must contact libabc.so developer, and tell them to hide libpng symbols.
The only other option (for single process execution) is to dynamically load libabc.so.
This can be done via dlopen("liabc.so.", RTLD_LOCAL), and even that may not work (depending on exactly how libabc.so was linked) -- it may cause libabc.so to bind to your version of libpng, and crash.
On Linux, you could also possibly use dlmopen(LM_ID_NEWLM, "libabc.so", ...) which will completely isolate libabc.so from the rest of your code, and may work if libabc.so was linked to include all of its dependencies (or you can bring them into the new loader namespace explicitly).
Finally, as Eljay commented here, you could use inter-process communication and have a completely separate process load libabc.so. This will have much worse performance than using libabc.so directly, but is better than nothing.

To add to EmployedRussian's answer about using dlmopen-based approach to isolate libabc.so from the rest of your code: to avoid messing with dlsym and function pointers in that case you could automatically generate wrappers for needed library functions via Implib.so:
$ cat mysymbols.txt
foo
bar
$ cat mycallback.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
#endif
// Dlopen callback that loads library to dedicated namespace
void *mycallback() {
void *h = dlmopen(LM_ID_NEWLM, "libabc.so", RTLD_LAZY | RTLD_DEEPBIND);
if (h)
return h;
fprintf(stderr, "dlmopen failed: %s\n", dlerror());
exit(1);
}
$ implib-gen.py --dlopen-callback=mycallback --symbol-list=mysymbols.txt libabc.so
$ ... # Link your app with libabc.tramp.S, libabc.init.c and mycallback.c

I would try strip. See this for details: https://linux.die.net/man/1/strip

Related

How can I link to DLLs from a C++ program built under Cygwin?

Given the following files
RsbbDec.exe
Run_RsbbDec.exe
TRSCompress.dll
TRSCompress.lib
and the following example code:
#include <iostream>
#pragma comment(lib, "TRSCompress.lib.")
extern "C" __declspec(dllexport) int Run_RsbbDec(const char* Input_File, int vbr_en, int bitrate , char *ErrMsg);
#define TEXTLEN 1024
int main()
{
char ErrMsg[TEXTLEN];
if(Run_RsbbDec("audio.pcm", 1, 1, ErrMsg))
{
std::cout <<"ok";
}
else
{
std::cout <<"error";
}
}
How do I make the sample code work? The only instruction I was given was to make sure that the .dll and the 2 exes are in the same directory.
It seems to me that the main point of the demo code is to run the Run_RsbbDec function which I think is located in one of the executables.
I get a linker error when I run the code:
/tmp/ccuvBnWV.o:main.cpp:(.text+0x2b): undefined reference to `Run_RsbbDec'
I am using this command to compile:
g++ main.c RsbbDec.exe Run_RsbbDec.exe
Likely, the third-party supplied DLL was not built under Cygwin, but natively with Visual Studio (or similar).
That's okay, but you have to do some more work to form a compatibility between the two different environments.
You can read about it on the Cygwin website's page "Building and Using DLLs":
If you have an existing DLL already, you need to build a Cygwin-compatible import library. If you have the source to compile the DLL, see the section called “Building DLLs” for details on having gcc build one for you. If you do not have the source or a supplied working import library, you can get most of the way by creating a .def file with these commands (you might need to do this in bash for the quoting to work correctly):
echo EXPORTS > foo.def
nm foo.dll | grep ' T _' | sed 's/.* T _//' >> foo.def
Note that this will only work if the DLL is not stripped. Otherwise you will get an error message: "No symbols in foo.dll".
Once you have the .def file, you can create an import library from it like this:
dlltool --def foo.def --dllname foo.dll --output-lib foo.a
Alternatively, use native tools to build your own program.
Also, the pragma is wrong; there's an errant period at the end of the filename.

Calling a shared library from c++

i have a shared library with some functions stored inside it. i want to Access those functions by calling that library from another program. I have done this earlier in C.
Now i want to do the same using C++. I am pretty new to C++ and any suggestions are very much required. BTW, the shared library is written in C. Is it still possible for me to call this in a C++ program and use all the functions of it.
Please help me. An example program would be very very helpful.
i am using ubuntu 14.04 and the compiler is the native g++ that comes along with it.
Load shared libarary using dlopen, and load given symbol using dlsym. Link with -ldl.
So given a shared library hello.cpp, compile g++ -shared -fPIC -o libhello.so hello.cpp
#include <cstdio>
extern "C" void hello( const char* text ) {
printf("Hello, %s\n", text);
}
(shared libraries should be named lib*.so[.*])
Now calling in main.cpp, compile: g++ -o main main.cpp -ldl
#include <dlfcn.h>
extern "C" typedef void (*hello_t)( const char* text );
int main() {
void* lib = dlopen("./libhello.so", RTLD_LAZY);
hello_t hello = (hello_t)dlsym( lib, "hello" );
hello("World!");
dlclose(lib);
}
See C++ dlopen mini HOWTO.
You said you already did so in C. Actually C++ is based on C, so you still can do it in the same way you did before.
Usually the following steps are required:
Use some method provided by the library in your c++ code
Figure out which header provides that method, and include it in your .cpp file
Compile your C++ code against the headers of the library (compiler flags in the Makefile)
Link against the library ( Linker Flags in the Makefile )

The procedure entry point __gxx_personality_v0 could not be located in the dynamic link library libstdc++-6.dll

I got that error when trying to run my opencv application. I´m using Windows7,CodeBlocks 12.11, opencv2.4.4 and MinGW compiler (the one that comes in CodeBlocks). It compiles and creates the executable but when i try to run it crashes with the procedure entry point error.
I have added C:\programs\CodeBlocks\Mingw\bin to "PATH" variable and i know there is libstdc++-6.dll.
I don´t know what´s hapenning.
This is the simple code:
include <iostream>
include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
cout << "Hello world!" << endl;
namedWindow("window");
Mat image=imread("mustang.jpg",CV_LOAD_IMAGE_COLOR);
imshow("window",image);
waitKey(0);
return 0;
}
The libstdc++-6.dll contains the runtime environment. It is an implementation of fundamental routines, such as the heap manager or the exception handling.
These fundamental routines are used in nearly every program. Thus, it would be a waste of memory to put a copy of them into every program. That is why they are usually packed into a shared library (DLL). The programs can then request the DLL when they need the routines of the runtime.
In your case, the libstdc++-6.dll contains a wrong version of the runtime. There are two possibilities:
Find a libstdc++-6.dll that contains the correct version of the runtime and copy it into the directory of your executable. You can determine whether a DLL is the correct one by running nm libstdc++-6.dll | grep personality. If the __gxx_personality_v0 shows up in the list, then you probably have the correct DLL.
Put a copy of the runtime environment into the executable. You can do this by adding -static-libgcc -static-libstdc++ to your linker parameters.
This question seems to have been answered several times here on stackoverflow.
What is __gxx_personality_v0 for? as one of them

Compile a static binary which code there a function gethostbyname

How to resolve compile a static binary which code include a function gethostbyname and if compiled without warning like this:
warning: Using 'gethostbyname' in statically linked applications
requires at runtime the shared libraries from the glibc version used
for linking
I compile on ubuntu 12.04 with command:
$ gcc -static lookup.c -o lookup
This is code for lookup.c:
/* lookup.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
extern int h_errno;
int main(int argc,char **argv) {
int x, x2;
struct hostent *hp;
for ( x=1; x<argc; ++x ) {
hp = gethostbyname(argv[x]);
if ( !hp ) {
fprintf(stderr,
"%s: host '%s'\n",
hstrerror(h_errno),
argv[x]);
continue;
}
printf("Host %s : \n" ,argv[x]);
printf(" Officially:\t%s\n", hp->h_name);
fputs(" Aliases:\t",stdout);
for ( x2=0; hp->h_aliases[x2]; ++x2 ) {
if ( x2 ) {
fputs(", ",stdout);
}
fputs(hp->h_aliases[x2],stdout);
}
fputc('\n',stdout);
printf(" Type:\t\t%s\n",
hp->h_addrtype == AF_INET
? "AF_INET" : "AF_INET6");
if ( hp->h_addrtype == AF_INET ) {
for ( x2=0; hp->h_addr_list[x2]; ++x2 ) {
printf(" Address:\t%s\n",
inet_ntoa( *(struct in_addr *)
hp->h_addr_list[x2]));
}
}
putchar('\n');
}
return 0;
}
I want to if I check via $ file lookup will get output like this:
lookup: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux),
statically linked, for GNU/Linux 2.6.24,
BuildID[sha1]=0x6fcb2684ad8e5e842036936abb50911cdde47c73, not stripped
Not like this:
lookup: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.24,
BuildID[sha1]=0xf9f18671751927bea80de676d207664abfdcf5dc, not stripped
If you commented with suggested I must use without static because different libc every linux I knew it, I hope you do not need to comment.
Why do I persist in for static?
Because there I need to do to mandatory use static, binary files must be static and not dynamic.
I have more than 2 weeks looking for this but so far have not succeeded.
Thanks for help me to resolve my heavy problem.
What you are asking for is going to be very difficult.
See this StackOverflow question about getaddrinfo. Basically, underneath getaddrinfo/gethostbyname is glibc's NSS layer. This allows a sysadmin to say "use DNS for resolving hostnames to IP addresses", or "use LDAP", or "don't use anything other than /etc/hosts". This control is at runtime; the sysadmin can at any point change the way hostnames are resolved to IPs.
Because of this flexibility, all of the name-resolution calls in glibc use helper libraries (plugins, basically) to do the grunt work of resolution. There's one shared library for LDAP addressing, one for files, one for DNS, one for YP, and so on and so on.
If you want your program to be 100% statically linked, you're going to have to go elsewhere (NOT gethostbyname) to convert a hostname to an IP address. You could do this with a resolver library like uDNS (not this exact one - there are similar tools available), but you should keep in mind that your binary is not going to do the right thing on systems which are configured not to use DNS!
Instead, I would recommend just leaving the program (technically) dynamically linked. If you really want to make sure it will run on any platform, you could even ship glibc with the binary - although doing this would require LGPL conformance. Leaving this one dynamic link in place will only mean you won't work on systems with the wrong glibc version - not a huge compatibility issue.
Speaking of license compliance, it's worth noting that if you statically link glibc, you most likely have to ship the source code for your entire application to comply with glibc's LGPL license. I am not a lawyer, and this is not qualified legal advice, but reading the LGPL makes it very clear that applications statically linking glibc must be open-source. See this StackOverflow question on the topic.
I get the same warning and to fix it I recompiled glibc. Turn on switch --enable-static-nss when configuring to get it to work.
I have 2 answers -
Keep the main part of your program statically linked, and separate out a single function program to just call gethostbyname(). Allow the latter to be dynamically linked. Using fork then exec execute this separate program to get the address for a domain name. Instead of fork then exec you could use system() though it takes longer (a whole millisecond) that should not be of concern since you're searching nameservers on the internet anyway, which takes a time.
Write the source code to do the DNS, as I have done. Compile it into an archive (.a) and have it searched in the static linking.
use ip instead of using domain name, that work for me

[windows] back linking

There is shared class. Declarator is in shared header, implementation is in main program. Main program load DLL and exec some function from it, function create object of shared class.
Test code:
shared_header.h:
#include<stdio.h>
class sharedClass{
public:
sharedClass();
};
plugin.cpp -> libplugin.dll
#include"shared_header.h"
extern "C"
void loader(){
printf("Plugin is loaded!\n");
new sharedClass;
}
base.cpp -> base.exe
#include"shared_header.h"
sharedClass::sharedClass(){
printf("Shared class is loaded!\n");
}
int main(){
/*
some actions to load libplugin.dll and exec function loader
*/
return 0;}
So, I want see
Plugin is loaded!
Shared class is loaded!
And it works on Linux. But while I link libplugin.dll on Windows I have error "undefined refernce to sharedClass::sharedClass()". How I need link program and plugin to use this way?
PS. Mingw, stable version.
PPS. I'm so sorry for my terrible English.
Windows DLLs are non exactly the same thing as UNIX/Linux shared objects.
On Windows, DLLs must be fully linked and have all their references defined. Therefore, as your file plugin.cpp references the sharedClass constructor, the linker will require that this constructor is defined and available to create the DLL. It is not possible to provide it in the executable that loads the DLL.
On UNIX/Linux, shared objects behave differently. Their dependencies are solved when they are loaded by the executable. Therefore, the executable can provide some of the functions needed by the shared object.