C program linking with shared library without setting LD_LIBRARY_PATH - c

I was reading Introduction to GCC and it says if a package has both .a and .so. gcc prefer the shared library. By default the loader searches for shared libraries only in a predefined set of system directories, such as /usr/local/lib and /usr/lib. If the library is not located in one of these directories it must be added to the load path, or you need to use -static option to force it to use the .a library. However, I tried the following:
vim hello.c:
#include <gmp.h>
#include <stdio.h>
int main() {
mpz_t x;
mpz_init(x);
return 0;
}
gcc hello.c -I/opt/include -L/opt/lib -lgmp (my gmp library is in opt)
./a.out
And it runs. The book says it should have the following error:
./a.out: error while loading shared libraries:
libgdbm.so.3: cannot open shared object file:
No such file or directory
(well, the book uses GDBM as example but I used GMP, but this won't matter right?)
However, I did not set LD_LIBRARY_PATH=/opt/lib, and as you can see I did not use -static option either, but a.out still runs.
Can you all tell me why and show me how to get the error described in the book? Yes I want the error so I will understand what I misunderstood.

From your response to my comment:
linux-gate.so.1 => (0xb7746000)
libgmp.so.10 => /usr/lib/i386-linux-gnu/libgmp.so.10 (0xb76c5000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7520000)
/lib/ld-linux.so.2 (0xb7747000)
So, your program is picking up the lib from /usr/lib.
What you can try to do is rename the lib in your /opt/lib, and link against the new name.
mv /opt/lib/libgmp.so /opt/lib/libgmp-test.so
gcc hello.c -I/opt/include -L/opt/lib -lgmp-test
Then try running the program. Also, compare the result of ldd against the new a.out against what you got before.

Related

Generate Static Executable with CMake

Please bear with this question, it is rather long.
TLDR: A CMake project with a subdirectory library links successfully, but creates a dynamic executable.
Code Available at: https://github.com/georcon/cmake-issue
Also Note: I have read all related questions/answers, and none answer this question.
I have created the following minimal example:
Create a statically-linked executable (Works correctly)
(Git Tag: SimpleExecutable)
main.c
#include <uuid/uuid.h>
#include <stdio.h>
int main(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
return 0;
}
CMakeLists.txt
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_executable(main-dynamic main.c)
target_link_libraries(main-dynamic uuid)
add_executable(main-static main.c)
target_link_libraries(main-static uuid -static)
Result
ldd main-dynamic
linux-vdso.so.1 (0x00007ffc406bb000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f76781cd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7677fdb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f76781eb000)
ldd main-static
not a dynamic executable
Create a static executable with library
(Git Tag: ExecutableWithLibrary)
lib/lib.h
#ifndef LIB_H
#define LIB_H
void PrintUUID();
#endif //LIB_H
lib/lib.c
#include <uuid/uuid.h>
#include <stdio.h>
void PrintUUID(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
}
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(testlibrary VERSION 1.0 DESCRIPTION "Static target issue - Library" LANGUAGES C)
add_library(testlib lib.c)
target_link_libraries(testlib uuid)
main.c
#include "lib/lib.h"
int main(){
PrintUUID();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_subdirectory(lib ./bin)
link_directories(./lib/ ./bin)
add_executable(main-dynamic main.c)
add_dependencies(main-dynamic testlib)
target_link_libraries(main-dynamic libtestlib.a uuid -static)
link_libraries("-static")
add_executable(main-static main.c)
target_link_libraries(main-static PUBLIC "-static" libtestlib.a uuid)
add_dependencies(main-static testlib)
#target_link_libraries(main-static libtestlib.a uuid -static)
Result
ldd main-static
linux-vdso.so.1 (0x00007ffe6b485000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007fc67edd3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc67ebe1000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007fc67edec000)
Looking at the linker command:
~/cmake_issue$ cat CMakeFiles/main-static.dir/link.txt /usr/bin/cc
CMakeFiles/main-static.dir/main.c.o -o main-static
-L/home/georcon/cmake_issue/./lib -L/home/georcon/cmake_issue/./bin -Wl,-rpath,/home/georcon/cmake_issue/./lib:/home/georcon/cmake_issue/./bin -static -static
-Wl,-Bstatic -ltestlib -Wl,-Bdynamic -luuid
Why does CMake not generate a statically-linked executable in this case?
Long story short: You need to tell CMake that you prefer static linking with the libraries.
This is done by setting property LINK_SEARCH_START_STATIC. Also you need to tell CMake to not reset static linkage at the end of the libraries list.
This is done by setting property LINK_SEARCH_END_STATIC:
set_target_properties(main-static PROPERTIES
LINK_SEARCH_START_STATIC ON
LINK_SEARCH_END_STATIC ON
)
See also that question: CMake and Static Linking.
What is going on
Actually, the linker option -static not only disables PIE, but also affects on further libraries listed in the command... unless -dynamic is specified.
CMake has a notion about "default linking type", which is applied for every library (uuid in your case) for which CMake cannot deduce its type. Moreover, CMake maintains that default linking type after each library it adds into the linker's command line. And CMake expects the same behavior from the user, who manually adds linker flags.
Your first example is wrong, but suddenly works:
You add -static which turns current linkage type to static. And thus you break CMake expectations about current linkage type.
When produce the linker option for link with uuid, CMake expects that current linkage is dynamic. So, CMake doesn't add -dynamic linker switch.
That time CMake expectations doesn't correspond to the reality, which in turn corresponds to your expectations: uuid is linked statically.
But the second example reveals the problem:
When linking with libtestlib.a library, CMake is perfectly aware that this is a static library, and thus adds Wl,-Bstatic option before that library. But CMake need to maintain default linkage after every option, so it adds -Wl,-Bdynamic after the library:
-Wl,-Bstatic -ltestlib -Wl,-Bdynamic
With such options CMake expectations about default dynamic linking corresponds to the reality: uuid is linked dynamically. But now that reality doesn't correspond to yours expectations.

Shared library not found when compiling a C program

So, I have a simple program which looks like so:
#include <amqp.h>
#include <amqp_framing.h>
int main(int argc, char const * const *argv) {
amqp_connection_state_t conn;
conn = amqp_new_connection();
amqp_destroy_connection(conn);
return 0;
}
This program depends on rabbitmq-c library. I compiled it with no errors. So, when I run
$ ls /rabbitmq-c/_install/include/
I get all its header files, that I need:
amqp.h
amqp_framing.h
amqp_tcp_socket.h
And when I run
$ ls /rabbitmq-c/_build/librabbitmq/
I see all needed ".so" files:
CMakeFiles
Makefile
cmake_install.cmake
config.h
librabbitmq.a
librabbitmq.so
librabbitmq.so.4
librabbitmq.so.4.4.1
And finally I compile my own program like so:
$ gcc -I/rabbitmq-c/_install/include/ -g -Wall -c main.c
$ gcc -L/rabbitmq-c/_build/librabbitmq/ -g -Wall -o rabbit main.o -lrabbitmq
It compiles with no errors. However, when I do:
$ ldd ./rabbit
I get this message:
librabbitmq.so.4 => not found
So, what am I missing and how can I fix it?
When you link shared library into an executable, the linker will recorder the library name (in this case librabbitmq.so.4) into the executable. It is the job of the dynamic linker (ld.so), to locate the libraries, and combine them for execution.
To locate the libraries, the dynamic linker constructs a search path (similar to PATH). This include:
LD_LIBRARY_PATH
Hard-coded directories added to the executable.
Default folders (/lib, /usr/lib, etc.).
In the above case, looks like neither #1 nor #2 were used, and the library is not in the default location. Can be fixed using #1 or #2
# Option 1.
# Both gcc, and ldd consult LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/rabbitmq-c/_build/librabbitmq
gcc -g -Wall -o rabbit main.o -lrabbitmq
ldd ./rabbit
# Option #2
# Inject SO directory into the executable with -Wl,-rpath,...
gcc -L/rabbitmq-c/_build/librabbitmq/ -Wl,-rpath,/rabbitmq-c/_build/librabbitmq/ -g -Wall -o rabbit main.o -lrabbitmq
ldd ./rabbit
Consult man ld.so for the full details.
From personal experience, when dealing with 'one-off' libraries, better to use the 'rpath' (#2) approach. Trying to add lot of locations into LD_LIBRARY_PATH can easily result in hard to manage, long, LD_LIBRARY_PATH. Using LD_LIBRARY_PATH works best when a wrapper script is created to launch the program
File: rabbit-run (same folder as executable)
# Prepend rabbitmq SO location to current LD_LIBRARY_PATH
LD_LIBRARY_PATH=LD_LIBRARY_PATH=/rabbitmq-c/_build/librabbitmq${LD_LIBRARY_PATH+:$X}
# Execute the binary, from the same location of the launcher
${0%/*}/./rabbit
If your binary don't find your "librabbitmq.so.4", that means this shared object is not found by ld (the dynamic linker)
First step, do a "ldconfig". Does this solve your problem ?
Yes ? Cool.
if not, then you have to tell ldconfig where to look to find "librabbitmq.so.4".
So either you move it in a knowed folder (LD_LIBRARY_PATH for exemple) or add it so it will be knowed by ld.
echo '/rabbitmq-c/_build/librabbitmq' > '/etc/ld.so.conf.d/name_this_file_yourself.conf'
ldconfig
This should fix your issue.

How to run c program with .so file

I have gone through all the solutions on StackOverflow as well as Ask Ubuntu.
I have a Go program:
package main
import "C"
//export Getint
func Getint() int {
return 2
}
func main() {}
and I have generated .so file for the same with name t.so and header filet.h`
Now I would like to use this function in my C program.
I have written the code but I don't know how to execute it.
#include <stdio.h>
#include <t.h>
int main()
{
int a;
a=Getint();
printf("number : %d",a);
return 0;
}
When I execute it with
gcc c.c t.so
it generates a.out file
but at the time of running a.out with ./a.out it gives an error:
./a.out
Error while loading shared libraries: t.so: can not open shared object file: no such file or directory exists.
then I tried with:
gcc -c c.c -l t.so
So it generates c.o file and it is not executable.
You should use the linker option -rpath, which tells the linker to add information in the executable program where to find runtime libraries like your .so file.
This can be done using the GCC option -Wl which instructs the GCC frontend program to pass an option to the linker:
$ gcc c.c t.so -Wl,-rpath=$(pwd)
This will pass -rpath=$(pwd) to the linker, and $(pwd) causes the shell to call the pwd command to return the current directory.
As long as you don't move the library the program should work.
You can use the environment variable LD_LIBRARY_PATH too, but it's not recommended.
Most probably your loader cannot find the library. Try to put the path to the directory where the libarry is located to LD_LIBRARY_PATH prior to run your binary.
export LD_LIBRARY_PATH=/path/to/my/library
./a.out
.so files are shared object, meaning object that are available to all applications that need them.. that is, shared. Due to this characteristics, they need to be stored in a well known place. Also, they need to be indexed by the dynamic linker.
In linux for instance you typically have a file /etc/ld.so.conf where all directories where shared object are automatically read from are stored
So your options are:
Put your shared object file in a well known place
Put your shared object file in a place of your choice and let the dynamic linker know about it: in linux you can modify ld.so.conf and run ldconfig to update ld indexes
As other suggested write the path of your .so in the env variable LD_LIBRARY_PATH (since dynamic linker reads it before running your application). This must be done at each environment creation
As other suggested use -rpath when compiling. Note that in this way you cannot move your .so file after the compilation
Personally I prefer installing the .so file in a system library path
You should use LD_LIBRARY_PATH to let the dynamic linker find your shared library in the list. Syntax is similar to PATH a list of directories separted by :.
On OSX this environment variable is called DYLD_LIBRARY_PATH.

Detecting unresolved symbols in an ELF executable

Let's say I have two files:
// shared.c (will be compiled to 'shared.so')
#include <stdio.h>
int f() { printf("hello\n"); }
and
// exe.c (will be compiled to 'exe')
#include <stdio.h>
int f();
int main() {
int i;
scanf("%d", &i);
if (i == 5) f();
}
I compile both files as following:
gcc -shared shared.c -o libshared.so
gcc exe.c -o exe -lshared -L.
When I run exe and type 5, it will call f and then exit. However, if I delete f from shared.c and recompile it I will get a runtime symbol lookup error only if I type 5. Is there a way that I can check that exe has all its symbols that will work independent of user input in this case? Preferrably without running it.
You can use ldd -r exe command to list the shared library dependencies.
Here is my output for your example without the f function:
$ LD_LIBRARY_PATH=. ldd -r ./exe
linux-vdso.so.1 (0x00007ffcfa7c3000)
libshared.so => ./libshared.so (0x00007f303a02e000)
libc.so.6 => /lib64/libc.so.6 (0x0000003e26c00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e26400000)
undefined symbol: f (./exe)
(Don't mind the LD_LIBRARY_PATH=. part. It is used to tell to look for shared libraries in the current directory)
#tohava
When you compile the executable and link it with the shared object, ld (linker) checks if all referenced symbols are available in the list of shared objects your executable is dependent on and will throw an error if any symbol was unresolved.
So, I am not sure how you managed to get a runtime error when you removed f() from the shared library and rebuilt the executable. (I did the exercise myself and got the linker error).

gcc static library linking vs dynamic linking

My build environment is CentOS 5. I have a third party library called libcunit. I installed it with autotools and it generates both libcunit.a and libcunit.so. I have my own application that links with a bunch of shared libraries. libcunit.a is in the current directory and libcunit.so and other shared libraries are in /usr/local/lib/. When I compile like:
gcc -o test test.c -L. libcunit.a -L/usr/local/lib -labc -lyz
I get a linkage error:
libcunit.a(Util.o): In function `CU_trim_left':
Util.c:(.text+0x346): undefined reference to `__ctype_b'
libcunit.a(Util.o): In function `CU_trim_right':
Util.c:(.text+0x3fd): undefined reference to `__ctype_b'
But when I compile with .so like:
gcc -o test test.c -L/usr/local/lib -lcunit -labc -lyz
it compiles fine and runs fine too.
Why is it giving error when linked statically with libcunit.a?
Why is it giving error when linked statically with libcunit.a
The problem is that your libcunit.a was built on an ancient Linux system, and depends on symbols which have been removed from libc (these symbols were used in glibc-2.2, and were removed from glibc-2.3 over 10 years ago). More exactly, these symbols have been hidden. They are made available for dynamic linking to old binaries (such as libcunit.so) but no new code can statically link to them (you can't create a new executable or shared library that references them).
You can observe this like so:
readelf -Ws /lib/x86_64-linux-gnu/libc.so.6 | egrep '\W__ctype_b\W'
769: 00000000003b9130 8 OBJECT GLOBAL DEFAULT 31 __ctype_b#GLIBC_2.2.5
readelf -Ws /usr/lib/x86_64-linux-gnu/libc.a | egrep '\W__ctype_b\W'
# no output
Didn't notice that the libcunit.a is actually found in your case and the problem with linakge is rather in the CUnit library itself. Employed Russian is absolutely right, and he's not talking about precompiled binary here. We understand that you've built it yourself. However, CUinit itself seems to be relying on the symbol from glibc which is not available for static linking anymore. As a result you have only 2 options:
File a report to the developers of CUnit about this and ask them to fix it;
Use dynamic linking.
Nevertheless, my recommendation about your style of static linkage still applies. -L. is in general bad practice. CUnit is a 3rd party library and should not be placed into the directory containing source files of your project. It should be rather installed in the same way as the dynamic version, i.e. like you have libcunit.so in /usr/local/lib. If you'd supply prefix to Autotools on the configure stage of CUnit, then Autotools would install everything properly. So if you want to link statically with CUnit, consider doing it in the following form:
gcc -o test test.c -L/usr/local/lib -Wl,-Bstatic -lcunit -Wl,-Bdynamic -labc -lyz

Resources