undefined symbol: _ZL22__gthrw_pthread_cancelm error - c++

I have a C++/C application which needs to be compiled as a 32 bit application (as there are certain third-party libraries only available for 32 bit). However, the compilation as well as the execution will happen on CentOS 6.4 x86_64 machine.
I am using gnu autotools for building. After doing a lot of googling, finally figured a sets of options to give to ./configure to create 32 bit executables/shared objects. Set the LD_LIBRARY_PATH to search in /lib, /usr/lib/, /usr/lib/gcc/... instead of /lib64, ... Verified that all the generated .so and executable are 32 bit by using file command.
But I get the error: "undefined symbol: _ZL22__gthrw_pthread_cancelm" if I run the executable.
Any clues?

It seems you forgot to link to pthreads with -lpthread.
GCC adds a layer of abstraction over pthreads and this abstraction use weak symbols, so you can build your executable without link error but fail at runtime.

Is there a 32bits pthread library on your target host? If not, I guess you need to get one installed. Also inspect the output of ldd <my-program> on your target host, this might help you find out what is missing.

Related

What determines if a 32-bit library built on a 64-bit machine needs x86_64 or i386 dependencies?

I am updating some old C++ projects to build on a 64-bit Linux machine for the first time, and I don't have much Linux experience. I need to build everything as 32-bit binaries, so I'm building everything with -m32 in the compiler and linker flags. I'm finding that, when linking to their dependencies, some must link with i386 shared objects, and some must link with x86_64 shared objects. If I only include the wrong folder in the linking path (-L/path/to/wrong/folder), it says
/usr/bin/ld: skipping incompatible xxx.so when searching for -lxxx
which I've come to understand means the architecture doesn't match what I'm trying to build.
The makefiles are nearly identical for two such differing projects, so it doesn't seem like I'm doing something obviously wrong there, and -m32 appears in the calls to gcc and g++ in the terminal. What could be causing this difference? Should I be concerned, or is it typical for this to happen?
Let me know if more information is needed to answer; I'm not really sure, due to inexperience with Linux and gcc, so apologies in advance.
Thanks #Wyzard and #duskwuff for the tips. I was indeed able to find my problem by using file on my .o files. It was just a silly mistake; I had inadvertently reverted the changes I made to one of the projects' make files, which included adding the -m32 flag. I think I misunderstood what the "x86_64" libraries are for, and that confused me (I had assumed it meant "32-bit process for 64-bit machine").

Glibc version mismatch

I have created .so library in my Ubuntu and run it on another machine. Got error:
/lib/tls/i686/cmov/libc.so.6: version `GLIBC_2.15' not found
I suppose this is general C++ library. But how to solve such problem? I can't change client configuration and that means I must to do something with my configuration. But what exactly I must do?
UPD
ldd -version returns
my machine:
ldd (Ubuntu EGLIBC 2.19-0ubuntu6.6) 2.19
host machine:
ldd (Ubuntu EGLIBC 2.11.1-0ubuntu7.8) 2.11.1
On the target machine, run ldd --version and check the output which will tell you what version of GLIBC_ they have.
You can then roll yours back to match their version.
Statically link your executable so it doesn't need their Clib.
You can also alter your program to use the older version, once you know what it is, that is.
See this SO solution for how to do that. How can I link to a specific glibc version?
You have to make sure that you are linking to corresponding or older versions of GLIBC. GCC has flag --sysroot which allows you to define which libs are used.
This may help with details: https://sourceware.org/glibc/wiki/Testing/Builds
The point is that creating a shared library necessarily means that you need to link it to the C library (glibc, in your case). That means that calls to C library functions (which the stdc++ library does) get replaced with actual correct symbol locations in the C library.
Now, if the C library on the compiling/linking machine is not the same as on the target machine, this must fail, and hence, libc version gets checked.
Solutions is to either statically link your .so (which honestly doesn't make much sense, usually), or just correctly compile and link it for your target machine.
Beside compiling everything static, which is usually a bad
idea or does not work at all, the only way to solve the isssue
is to recompile your binary for the target platform.
Install a virtual machine or chroot with the same Ubuntu version
as on the target platform and compile there. There are also solutions
like sbuild or pbuilder/cowbuilder which automates this for Debian/Ubuntu packages.

How to use Qt app on tiny210 device?

I want to use a Qt app on a tiny210 device.
I installed Qt ( qt-everywhere-opensource-src.4.8.5 ) downloaded from here. I managed to compile a simple application for use on tiny210. The problem is that now when I try to run the app on the device, I get the following errors:
libc.so.6: version 'GLIBC_2.15' not found (required by libQtCore.so.4)
libc.so.6: version 'GLIBC_2.15' not found (required by libQtNetwork.so.4)
There is a libc.so.6 in /lib/ on the target device, but it is version 2.11.
I should mention that before getting those errors I also got errors for not having libQtCore.so.4, libQtNetwork.so.4 and libQtGui.so.4. I fixed those errors just by copying the compiled libraries from my host PC to the device.
First question is: Would there have been a better way to provide the needed libraries, or copying them is fine?
Second question is: How can I get over the errors mentioned above?
EDIT : I've read something about building it static, but I am not sure how, and what are the downsides of this.
EDIT2 : I managed to get over the above errors thanks to artless noise's answer, but now I get: error loading shared libraries: libQtGui.so.4: cannot open shared object file: No such file or directory.
The issue is the cross-compiler (apt-get install gcc-arm-linux-gnueabi) is ARM based and this cross compiler has a newer glibc than on the ARM device. You can copy the libc from the cross compiler directory to your ARM device. I suggest testing with LD_LIBRARY_PATH, before updating the main libraries. Use ls /var/lib/dpkg/info/*arm-linux*.list to see most packages related to the ARM compiler. You can use grep to figure out where the libraries are (or fancier things like apt-file, etc).
Crosstool-ng has a populate script, but I dont see it in the Ubuntu packages; it is perfect for your issue. If it is present on your Debian version, I would use it.
The glibc 2.15 is backwards compatible with the glibc 2.11 which is currently on your system. Issues may arise if the compiler was configured with different options (different ABI); however if this is the case, you will have many issues with your built Qt besides the library. In this case, you need to find a better compiler which fits your root filesystem.
So to be clear, on the target
mkdir /lib/staging
cp libc.so-2.15 /lib/staging
cd /lib/staging
ln -s libc.so-2.15 libc.so
LD_LIBRARY_PATH=/lib/staging ls # test the library
You may have to copy additional libraries, such as pthread, resolv, rt, crypt, etc. The files are probably in a directory like sysroot/lib. You can copy the whole directory to the /lib/staging to test it. If the above ls functions, then the compilers should be ABI compatible. If you have a crash or not an executable, then the compiler and rootfs may not be compatible.
Would there have been a better way to provide the needed libraries, or copying them is fine?
Copying may be fine as per above. If it is not fine, then either the compiler or the root filesystem must be updated.
How can I get over the errors mentioned above?
Try the above method. As well, you maybe able to leave your root filesystem alone. Set-up a shadow directory and use chroot to run the Qt application with the copied files as another solution. To test this, make a very simple program and put it along the compiler libraries in a test directory, say /lib/staging as above. Then the test code can be run like,
$ LD_LIBRARY_PATH=/lib/staging ./hello_world
If this doesn't work, your compiler and the ARM file system/OS are not compatible. No library magic will help.
I've read something about building it static, but I am not sure how, and what are the downsides of this.
See Linux static linking is dead. I understand this seems like a solution. However, if the compiler is wrong, this won't help. The calling convention between OS, libraries and what registers are saved by the OS will be implicit in the compiled code. You may have to rebuild Qt with -softfp, etc.

How do I build my Linux c++ app to link to an old version of libc?

I have built an app on Ubuntu 12.04 and have tried running it on an embedded system. I ran apt-cache show libc6 on my dev machine which shows (amongst other things)
Package: libc6
Priority: required
Section: libs
Architecture: i386
Source: eglibc
Version: 2.15-0ubuntu10
Replaces: belocs-locales-bin, libc6-i386
Provides: glibc-2.13-1, libc6-i686
The version of libc6 that exists on the embedded device is 2.8.90. In the \lib directory on the device I have 2 libs
libc-2.8.90.so
libc.so.6
When I copy my application onto the embedded device I get the following errors
/usr/lib/libc.so.6: version `GLIBC_2.15` not found (required by ./ServerSocketApp)
I know that if possible I when I build the application on my dev maching I need to force it to link to the same version of libc6 as exists on the embedded device. The problem I have is that I simply do not know how to do this. Any answers that I have found are meaningless to me right now. Is there some option that I need to pass to g++ to get this to link to version 2.8.90 ??
In desperation I am thinking is it possible to copy the libc on my dev machine onto the embedded device in place of what is there already and hope for the best??? I just cannot seem to find any documentation online that explains in simple terms how you even go about this so any advice at all would be really really welcome as I am tearing my hair out here.
OK, here is a somewhat longer explanation, but proceed with care. I still strongly recommend that you setup a chrooted environment to match the one available on the embedded device and use it during the last stage of your build process.
You should understand how dynamically linked ELF executables are loaded and executed. There is something called the run-time link editor (RTLD), also known as the dynamic linker, that takes care of loading all the necessary dynamically linked libraries, fixing relocations and so on. The name of the dynamic linker is /lib/ld-linux.so.2 on 32-bit Linux systems with glibc2 and /lib64/ld-linux-x86-64.so.2 on 64-bit Linux systems with
glibc2. The dynamic linker is very tightly coupled to the glibc2 library and usually can only handle the matching version of that library. Also the path to it is hardcoded into the executable by the linker (usually ld, implicitly called by the compiler to do the linking). You can easily check the validty of the last statement by simply doing ldd some_elf_executable - the run-time link editor shows up with the full path:
$ ldd some_elf_executable
linux-vdso.so.1 => (0x00007fffab59e000)
libm.so.6 => /lib64/libm.so.6 (0x0000003648400000)
libc.so.6 => /lib64/libc.so.6 (0x0000003648800000)
/lib64/ld-linux-x86-64.so.2 (0x0000003648000000) <--- the RTLD
In order to produce a dynamically linked executable that uses a version of glibc2 different from the one installed on the system, where the executable is to be run, you should link your code with the following set of options to ld:
-rpath=/path/to/newer/libs - this one instructs the dynamic linker to first search /path/to/newer/libs when trying to resolve library dependencies. /path/to/newer/libs should match the path where you have copied the newer glibc2 version on the embedded device
-rpath-link=/path/to/newer/libs - this option instructs the linker (not the dynamic linker) to use /path/to/newer/libs when resolving dependencies between shared libraries during link time - this should not be normally necessary in your case
--dynamic-linker=/path/to/newer/libs/ld-linux.so.2 - this one overrides the path to the RTLD that gets embedded into the executable
The way to provide those options to ld is usually via the -Wl option of GCC.
-rpath=/path/to/newer/libs
becomes:
-Wl,-rpath,/path/to/newer/libs
(notice that the = is replaced by ,)
--dynamic-linker=/path/to/newer/libs/ld-linux.so.2
becomes:
-Wl,--dynamic-linker,/path/to/newer/libs/ld-linux.so.2
You should copy /lib/ld-linux.so.2 from your development system to /path/to/newer/libs/ on the embedded device. You should also copy libc.so.6, the mathematical library libm.so.6 and all the other libraries that are used by the executable or that might get loaded indirectly. Note that libc.so.6 and libm.so.6 are actually symbolic links to the real libraries which have names like libc-2.<version>.so. You should copy those library files and create the appropriate symbolic links to make everybody happy.
This is fundamentally incorrect. While you may be able to hack together a way to link in the old libc, the problem is your environment setup.
When you develop applications for an embedded system. You do so on a host. Generally, the host and embedded device are not on the same architecture. For example, your host is usually a desktop/laptop running on an x86 and the embedded system might be on an ARM. If you happen to be on the same architecture as your embedded device, that is sheer coincidence. Standard practice environment setup should still follow:
The host machine should have a tool chain setup to cross build applications to the embedded architecture
The host machine should have a copy of the full rootfs that exists your embedded device. This will contain all of the libraries that your cross tools will use to compile applications for the embedded system
If you have it setup this way. Development will be easy. You will be able to setup simple, clean make files to build your applications and then just scp the binaries over to the embedded system and run.
You might have some luck compiling with the LSB SDK (http://www.linuxfoundation.org/collaborate/workgroups/lsb/download) which restricts the symbols available to the executable.

Linux C++ linker /usr/bin/ld

I wrote a small application on Redhat Linux 6 using g++ 4.4.6. After compilation, I received an error
/usr/bin/ld: cannot find -lcrypto
I did a search for the crypto library and find them here,
[root#STL-DUNKEL01 bin]# find / -name libcrypto*
/usr/lib64/libcrypto.so.0.9.8e
/usr/lib64/libcrypto.so.10
/usr/lib64/libcrypto.so.6
/usr/lib64/libcrypto.so.1.0.0
My question is whether the compilation error is caused by /usr/bin/ld not having /usr/lib64/ in the search path? If yes, how can I add it?
Thanks.
No, you have likely incorrectly diagnosed the cause.
You need a libcrypto.so to link against. This is usually a symlink to one of the actual libraries, whose soname (libcrypto.so.??) will be embedded into the binary. Only that library is needed at runtime, but the symlink is necessary to compile.
See Diego E. Pettenò: Linkers and names for more details.
You have to add -L/usr/lib64 when calling gcc or ld.
Note, you can specify LD_LIBRARY_PATH as well/instead, but it is considered harmful to do so. (The link mentions Solaris specifically, but the issues apply to other OSs as well.)
Quote:
LD_LIBRARY_PATH is used in preference to any run time or default system linker path. If (God forbid) you had it set to something like /dcs/spod/baduser/lib, if there was a hacked version of libc in that directory (for example) your account could be compromised. It is for this reason that set-uid programs completely ignore LD_LIBRARY_PATH.
When code is compiled and depends on this to work, it can cause confusion where different versions of a library are installed in different directories, for example there is a libtiff in /usr/openwin/lib and /usr/local/lib. In this case, the former library is an older one used by some programs that come with Solaris.
Sometimes when using precompiled binaries they may have been built with 3rd party libraries in specific locations; ideally code should either ship with the libraries and install into a certain location or link the code as a pre-installation step. Solaris 7 introduces $ORIGIN which allows for a relative library location to be specified at run time (see the Solaris Linker and Libraries Guide). The alternative is to set LD_LIBRARY_PATH on a per-program basis, either as a wrapper program to the real program or a shell alias. Note however, that LD_LIBRARY_PATH may be inherited by programs called by the wrapped one ...
Add the directory to /etc/ld.so.conf
then run "sudo ldconfig" to make the changes take effect.
You can provide the directories to search for the libraries in as a parameter to gcc like so -L<directory_to_search_in>. And note that there can be multiple parameters to -L. Also, are you trying to build a 32-bit application or a 64-bit one?