Valid ARM executable doesn't find libraries - c++

I cross-compiled a small Hello World program for an ARM embedded device (an Asus RT-AC68U router running DD-WRT):
# arm-linux-gnueabi-g++ hello.cpp -mcpu=cortex-a9 -s -o hello
The file seems fine:
# file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.32,
BuildID[sha1]=5099693c31154cdd7f04c16ced5b80b1e35e625b, stripped
It depends on a few libraries:
# readelf -d hello
Dynamic section at offset 0xf08 contains 26 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libstdc++.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
(...)
When I try to execute the file on the target system, I get the following:
# ./hello
-sh: ./hello: not found
If I launch execution from ld-linux.so.3 without any other parameters, everything works:
# /opt/lib/ld-linux.so.3 ./hello
Hello, world.
When listing the libraries, the file seems to be searching for libraries in the /lib folder (they are not there, they are in /opt/lib):
# /opt/lib/ld-linux.so.3 --list ./hello
libstdc++.so.6 => /lib/libstdc++.so.6 (0x76dfb000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x76de0000)
libc.so.6 => /opt/lib/libc.so.6 (0x76cb2000)
libc.so => /lib/libc.so (0x76c35000)
/lib/ld-linux.so.3 => /opt/lib/ld-linux.so.3 (0x76f20000)
I'm guessing the error is due to the fact that the executable when launched on its own is not finding the required shared libraries. All the required libraries are present in the /opt/lib folder and this folder is included in LD_LIBRARY_PATH environment variable:
# echo $LD_LIBRARY_PATH
/lib:/usr/lib:/jffs/lib:/jffs/usr/lib:/jffs/usr/local/lib
:/mmc/lib:/mmc/usr/lib:/opt/lib:/opt/usr/lib
How do I get the executable to find the libraries in the right place?
Edit: I tried linking the libraries statically in the executable and the program runs fine on the target platform. However that is not a workable solution for what I want to do.

Your ld-linux.so.3 appears to be in a nonstandard location (/opt/lib), and it's possible that your gcc is specifying a different program interpreter. You can use readelf -l <program> to check what program interpreter your binary is expecting (under PT_INTERP, "Requesting program interpreter").
If the binary's program interpreter is wrong for your platform, you can specify it manually by passing -Wl,--dynamic-linker=/opt/lib/ld-linux.so.3 to gcc.

Related

Linking shared library dependency not being listing in ldd

There is library which is compiled against -lyaml. But libyaml.so is not getting listed as dependency by ldd. Build is happening successfully using autoconf tool chain.
$ nm libxxxx.so | grep -i yaml
U yaml_document_delete
U yaml_document_get_node
U yaml_parser_delete
U yaml_parser_initialize
U yaml_parser_load
U yaml_parser_set_input_file
$ readelf -d libxxxx.so
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libxxxx.so.0]
There is another shared library which depends depend upon libxxxx.so.
$ ldd lib/libxxxx1.so
libzmq.so.5 => /usr/lib/x86_64-linux-gnu/libzmq.so.5 (0x00007fd45e072000)
libxmaapi.so.0 =>
When I am linking my executable with libxxxx1.so, it is giving undefined symbols error. The question is how do I link against library not found in dependency tree?
This question provides approaches to ignore the problem.
Linking with dynamic library with dependencies
The one approach which I found is disabling optimization using the gcc flag -Wl,--no-as-needed. Since I am already linking using -lyaml, symbols are getting resolved. It works but not efficient.

clang, linux, is there an option to change shared library name at linking?

I'm testing a somewhat non-conventional project layout and rake as make utility. There is a rule to compile binaries from source files in different directories and link them with a shared library. This rule is run from the root directory of the project. For instance the rule does this:
clang -I libs/ -o tests/sourcefile2 tests/sourcefile2.c shared_libs/libFoo.so
And as a result I get the full path shared_libs/libFoo.so in the binary:
readelf -d tests/sourcefile2
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [shared_libs/libFoo.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
I would like to change it to just 'libFoo.so' like this:
readelf -d tests/sourcefile2
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libFoo.so]
...
Then I could set RPATH for dynamic linker as I want and it would give some flexibility. But I cannot find the corresponding option or similar example. Could you suggest how to handle this? Should I just use a temporary directory for the build, copy everything and compile there?
Not sure if it will help you. But when I try compile some shit and I don't know what flags. I use pkg-config.
For example, to compile a program which uses Xlib
pkg-config -cflags -libs x11
and the output is the following
-I/usr/X11R7/include -D_REENTRANT -Wl,-rpath,/usr/X11R7/lib -L/usr/X11R7/lib -lX11
Note this vary on systems, for example NetBSD forces me to link it with rpath, and there are optional arguments in this output.
So I copy the output of pkg-config and it compiles.
if If you use 'ld' as your linker you should be able to use "-Wl,-soname ".

Undefined symbol when loading a shared library

In my program I need to load a shared library dynamically with dlopen(). Both the program and the shared library are successfully cross-compiled for an ARM architecture with the cross-compiler installed on my x86. However, whenever the program tries to load the library at run time on ARM, it fails giving this error:
undefined symbol: _dl_hwcap
I cannot find the culprit of this error.
Let me give details on how the shared library (libmyplugin.so) is built on x86 first. I use the g++ cross-compiler as below:
/home/me/arm/gcc-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -march=armv7-a -mfloat-abi=hard -c -s -fPIC -o build/module1.o module1.cpp
/home/me/arm/gcc-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -march=armv7-a -mfloat-abi=hard -c -s -fPIC -o build/module2.o module2.cpp
/home/me/arm/gcc-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -o dist/libmyplugin.so build/module1.o build/module2.o --sysroot /home/me/arm/sysroot/ -Wl,--no-as-needed -ldl -lX11 -lXext /home/me/arm/libstatic.a -shared -s -fPIC
Please pay attention to the following notes:
module1.cpp and module2.cpp are my source code files.
libstatic.a is a big archive of object .o files implementing the stuff directly invoked/referenced by module1.cpp and module2.cpp. These object files have been compiled by others for the same ARM architecture as mine, with the same compiler flags, but using a slightly more updated g++ compiler (v4.9 instead of my v4.8.3). Unfortunately, I have no control on the building of these objects.
--sysroot /home/me/arm/sysroot/ represents the remote filesystem of my ARM OS from which the local g++ cross-compiler can take the native libraries while linking.
-Wl,--no-as-needed -ldl -lX11 -lXext: these flags are required to force the dynamic loader to load the X11 libraries present on the system when my shared library is loaded by the program. In particular, --no-as-needed is required because the X11 libraries are NOT directly referenced by module1.o and module2.o; on the contrary the X11 libraries are referenced by the static library only.
Note that all the above setup works on x86. It's just that I don't understand what is the reason of the _dl_hwcap symbol not resolved when the program tried to load the library on ARM.
Do you have any idea how to investigate this issue?
There are a myriad of things that could be problematic, but here are four avenues of exploration. I am concentrating on the -shared in your link line, but the last item addresses that as well.
(A nice HOWTO on shared libraries is here:
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
a) Check your environment variable LD_LIBRARY_PATH. Since you aren't using RPATH to the linker (RPATH embeds a full path to the .so so you can find it at runtime), then the only way the linker can find your code is to search the LD_LIBRARY_PATH.
Make sure the .so or .0 you want is in the path.
b) Use the UNIX utility 'nm' to search .so (shared objects) and .a files for that symbol. For example, 'nm -D /usr/lib64/libpython2.6.so' will show all dynamic symbols
in the libpython.so, and you can look for symbols of interest:
For example, Is 'initgc' defined or used in libpython?
% nm -D /usr/lib64/libpython2.6.so | grep initgc
000003404300cf0 T initgc
The 'T' means TEXT or, yes, it is defined there. See if you can find the symbol in the module of interest using grep and nm. (A 'U' means undefined, which means it is defined in another module).
c) Another useful tool is 'ldd'. It shows all dynamic libraries that the library you are looking on depends on. For example:
% ldd /usr/lib64/libpython2.6.so
linux-vdso.so.1 => (0x00007fffa49ff000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00000033f0200000)
libdl.so.2 => /lib64/libdl.so.2 (0x00000033f0600000)
libutil.so.1 => /lib64/libutil.so.1 (0x00000033fea00000)
libm.so.6 => /lib64/libm.so.6 (0x00000033f0a00000)
libc.so.6 => /lib64/libc.so.6 (0x00000033efe00000)
/lib64/ld-linux-x86-64.so.2 (0x00000033efa00000)
If it can't find a library (because it's not on the LD_LIBRARY_PATH or wasn't specified in the RPATH), the library will turn up empty.
d) I am a little worried from your link line of seeing a '.a' file with a -shared option. Some compilers/linkers cannot use a '.a' (archive) file to create a '.so' file. '.so' files usually have to made from other '.so' files or '.o' files that have been compiled with -fPIC.
I would recommend (if you can), recompile /home/me/arm/libstatic.a so that it's a .so. If you can't do, you might have to make your final output a '.a' file as well. (In other words, get rid of the -shared command line option).
In summary: Check your LD_LIBRARY_PATH, use nm and ldd to look around at your .a and .so files, but I think the end result is that you may not be able to combine .so and .a files.
I hope this helps.
I think this symbol may be in the "ld-lsb" library needed by "Xext". On my system the library is a symlink "/lib64/ld-lsb-x86-64.so -> ld-linux-x86-64.so.2", but I am sure that is not the same on the arm. Maybe give it a whirl on your linker line?

Compiling with -static-libgcc -static-libstdc++ still results in dynamic dependency on libc.so

I'm trying to make an executable that's as portable as possible. After removing a few dependencies, I came across the following when running the binary on another system:
/lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.15' not found (required by foob)
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.15' not found (required by foob)
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.14' not found (required by foob)
I'd prefer my binary not to require the user to upgrade their version of libc, so I'd like to remove this dependency as well.
The linker flags that produced the above binary already included -static-libgcc -static-libstdc++. How come the binary still requires on the shared libc.so.6?
I tried adding the -static flag as well, however when I try to run that binary the result is very strange:
$ ls -l foob
-rwxr-xr-x 1 claudiu claudiu 13278191 Oct 10 13:03 foob
$ ./foob
bash: ./foob: No such file or directory
What to do?
EDIT:
$ file foob
foob: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=5adee9a598b9261a29f1c7b0ffdadcfc72197cd7, not stripped
$ strace -f ./foob
execve("./foob", ["./foob"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
write(2, "strace: exec: No such file or di"..., 40strace: exec: No such file or directory
) = 40
exit_group(1) = ?
+++ exited with 1 +++
Interestingly, if I ldd the version without -static, it has two less entries than the version with -static, namely:
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f4f420c1000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f4f41636000)
GNU libc is not designed to be statically linked. Important functions, e.g. gethostbyname and iconv, will malfunction or not work at all in a static binary. Arguably even worse, under some conditions a static binary will attempt to dynamically open and use libc.so.6, even though the whole point of static linkage is to avoid such dependencies.
You should compile your program against uClibc or musl libc instead.
(This has been true for at least 15 years.)
First be aware that static linking of libc might not improve portability of your program, as libc might depend on other parts of your system e.g. kernel version.
If you want to try complete static linking just using -static should the trick. Provided that there are static versions of all used libraries installed.
You can check if your program has only linked static libraries by using:
ldd binary_name
EDIT:
Another way that provides useful information for debugging this problem would be to add --verbose to your linker flags.

How to list exported functions in a shared lib on Ubuntu

I have just built a shared lib on Ubuntu, and when I attempt to use the function, the application that loads the library is reporting 'xxx' symbol not found.
I want to check (i.e. list) the functions that are exported by my library so I can investigate this issue further.
Relevant details:
OS: Ubuntu 9.10
compiler: gcc 4.4.1
linker: GNU ld 2.20
Try the nm utility.
GNU nm lists the symbols from object
files objfile.... If no object files
are listed as arguments, nm assumes
the file a.out. [reference]
nm -D -C -g <library>
works well too.
Is your shared library in the library load path or in the application's run-time search path? It sounds like the dynamic linker can't find your library. Try running ldd on your application to see if the library can be found at run-time, e.g.:
$ ldd /usr/bin/less
linux-gate.so.1 => (0x0072a000)
libncurses.so.5 => /lib/libncurses.so.5 (0x00c68000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x007c7000)
libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0x00286000)
/lib/ld-linux.so.2 (0x002a1000)
See the ld.so(8) man page for additional details on library search paths.