Shared library not found when compiling a C program - c

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.

Related

How can I set an executable's rpath and check its value after building it?

I'm building the following file:
int main()
{
return 0;
}
with the following flags:
g++ -o main -Wl,-rpath,'$ORIGIN' main.cpp
but the rpath flag is doing nothing. When I execute the following commands:
objdump -x main | grep -i rpath
readelf -a main | grep -i rpath
I obtain nothing (RPATH is not defined).
What I'm doing wrong?
EDIT
I have tried to do the above with a different binary using the following cmake flags:
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
I have moved the executable to a different machine, and placed a dynamic library that it needs to 'dlopen' in the same folder. It has worked (and I'm 100% sure this is because rpath, since before applying the above cmake flags the executable didn't worked).
Still, using the above two commands to check rpath (objdump and readelf) I still don't see anything.
If I didnt miss something here, you are not linking any libs in your build command.
Lets say you want to link libusb.so shared library, which is located in libusb sub-folder of your current folder where is main.cpp.
I will not take any details here, about soname, linkname of lib etc, just to make clear about rpath.
rpath will provide runtime linker path to library, not for linktime, cause even shared library need to be present(accessible) in compile/link time. So, to provide your application loader with possibility to look for needed library in start time, relatively to your app folder, there is $ORIGIN variable, you can see it with readelf but only if you link some library with $ORIGIN in rpath.
Here is example based on your question:
g++ main.cpp -o main -L./libusb -Wl,-rpath,'$ORIGIN/libusb' -lusb
As you see, you need to provide -L directory for compile/link time search, and rpath for runtime linker. Now you will be able to examin all needed libs for your app using readelf and location for search.

Unable to link dynamic library in macOS

I'm trying to use a C library called quirc in my C project. So far, I have generated a libquirc.dylib.1.0 by modifying the Makefile which was using Linux .so files.
quirc/helloquirc.c
#include <quirc.h>
#include <stdio.h>
int main() {
struct quirc *qr;
qr = quirc_new();
if (!qr) {
printf("Failed to allocate memory");
}
quirc_destroy(qr);
return 0;
}
I've created the above source file at the root of the repository. I'm using the following command to compile it:
gcc helloquirc.c -lquirc -L. -Ilib -o helloquirc
To my understanding the -l flag specifies the name of the dynamic library, the -L flag specifies the location of the dynamic library, the -I flag specifies the location of the header files, and -o specifies the name of the executable.
When I run this command I get the following error:
ld: library not found for -lquirc
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I changed the Makefile by using this line
.PHONY: libquirc.dylib
libquirc.dylib: libquirc.$(LIB_VERSION).dylib
libquirc.$(LIB_VERSION).dylib: $(LIB_OBJ)
$(CC) -shared -dynamiclib -o $# $(LIB_OBJ) $(LDFLAGS) -lm
and changing other instances of .so.$(LIB_VERSION) to .$(LIB_VERSION).dylib
Something is wrong with the way quirc was built. The correct library name would be something like libquirc.1.0.dylib with a symlink named libquirc.dylib.
It looks like quirc has a handwritten makefile instead of using something sensible like gyp or cmake. Handwritten makefiles are just fine as long as you're not trying to build shared libraries on multiple platforms.
However, if you are just compiling it yourself, you may find things simpler if you just use a static library instead. There is no point in having a shared library if you are not sharing it with anybody (if no other programs are using the same exact copy of libquirc).

a linker issue when learning static library [duplicate]

When I try to build the following program:
#include <stdio.h>
int main(void)
{
printf("hello world\n");
return 0;
}
On OS X 10.6.4, with the following flags:
gcc -static -o blah blah.c
It returns this:
ld: library not found for -lcrt0.o
collect2: ld returned 1 exit status
Has anyone else encountered this, or is it something that noone else has been affected with yet? Any fixes?
Thanks
This won’t work. From the man page for gcc:
This option will not work on Mac OS X unless all libraries (including libgcc.a) have also been compiled with -static. Since neither a static version of libSystem.dylib nor crt0.o are provided, this option is not useful to most people.
Per Nate's answer, a completely static application is apparently not possible - see also man ld:
-static Produces a mach-o file that does not use the dyld. Only used building the kernel.
The problem in linking with static libraries is that, if both a static and a dynamic version of a library are found in the same directory, the dynamic version will be taken in preference. Three ways of avoiding this are:
Do not attempt to find them via the -L and -l options; instead, specify the full paths, to the libraries you want to use, on the compiler or linker command line.
$ g++ -Wall -Werror -o hi /usr/local/lib/libboost_unit_test_framework.a hi.cpp
Create a separate directory, containing symbolic links to the static libraries, use the -L option to have this directory searched first, and use the -l option to specify the libraries you want to use.
$ g++ -Wall -Werror -L ./staticBoostLib -l boost_unit_test_framework -o hi hi.cpp
Instead of creating a link of the same name in a different directory, create a link of a different name in the same directory, and specify that name in a -l argument.
$ g++ -Wall -Werror -l boost_unit_test_framework_static -o hi hi.cpp
You may also try LLVM LLD linker - I did prebuilt version for my two major OSes - https://github.com/VerKnowSys/Sofin-llds
This one allows me to link for exmple: "Qemu" properly - which is impossible with ld preinstalled by Apple.
And last one is - to build GCC yourself with libstdc++ (don't).

How to make Scons look for libstdc++ in nonstandard directory

I'm trying to use Scons to build a simple project on a server on which I have rights to install stuff only in specific locations (and not in /usr/ ). Since I'm not happy with default compiler the server is offering me, I installed g++4.8 and verified it works just fine. But when I try to use Scons to build a simple project, while it picks up correct g++ (I can get that by checking the version), it's looking for libstdc++ in /usr/ directories instead of the directory where g++4.8 installation resides. E.g. code compiles, but upon execution fails with:
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./main)
Again - this doesn't happen when I call the compiler myself from the terminal.
Even when I add the lib path containing libraries for g++4.8 with LIBPATH option, I get the same error.
Here's my SConscript file:
Import('env')
COMPILER_FLAGS = '-Wall -fopenmp -O3 -std=c++11'
LINK_FLAGS = '-Wall -fopenmp -O3 -std=c++11'
LIB_PATH = 'myfolder/gcc-4.8.2/lib64'
PROGRAM = 'main'
SRC = ['main.cpp', 'Foo.cpp']
env.Append(CPPFLAGS = COMPILER_FLAGS)
env.Append(LINKFLAGS = LINK_FLAGS)
env.Program(target = PROGRAM, source = SRC, LIBPATH = LIB_PATH)
and SConstruct is just
import os
env = Environment(ENV = os.environ)
SConscript('./SConscript', exports=['env'], duplicate=0)
Edit:
I made sure location of my compiler comes in the path before default compiler. But even if I set it explicitly with Environment(CXX=...) it's the same story. Here's the build output:
/mypath/gcc-4.8.2/bin/g++ -o Foo.o -c -Wall -fopenmp -O3 -std=c++11 Foo.cpp
/mypath/gcc-4.8.2/bin/g++ -o main.o -c -Wall -fopenmp -O3 -std=c++11 main.cpp
/mypath/gcc-4.8.2/bin/g++ -o main -Wall -fopenmp -O3 -std=c++11 main.o Foo.o -L/mypath/gcc-4.8.2/lib64
scons: done building targets.
-bash-3.2$
-bash-3.2$
-bash-3.2$ ./main
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./main)
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by ./main)
-bash-3.2$
Yet another edit:
ldd on both manual and scons compile reveal:
linux-vdso.so.1 => (0x00007fff513fd000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6(0x0000003e7f600000)
libm.so.6 => /lib64/libm.so.6 (0x0000003e79600000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003e7de00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003e79200000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e78e00000)
So indeed even manual compile doesn't look for the libs in the right directory (or where I installed the compiler) and the problem isn't with the scons itself, but likely that I didn't configure something right, but then I'm really puzzled as to why the executable runs fine, while it doesn't for scons.
Ok, so my problem wasn't with scons, but with me not giving explicit paths to nonstandard locations of libstdc++ and friends. SO answer over here explains this in more detail:
Linking g++ 4.8 to libstdc++
You're misinterpreting the error. GCC always knows how to find its own libraries, including libstdc++. The problem is that after you've compiled the program the runtime linker (which is not part of GCC, it's part of your OS and comes from glibc) doesn't know how to find the newer libstdc++, so it finds the default system one, which is too old.
The problem and solution are described at in the Libstdc++ FAQ, "How do I insure that the dynamically linked library will be found?", and manual, "Finding Dynamic or Shared Libraries"
This doesn't sound right.
Can you show us what you do to override the compiler?
If you are only doing the above, I don't think your compiler will be overridden with the new version.
You need to do something like
env = Environment(CC='/path/to/gcc')
Or Environment(CXX='/path/to/g++') if you want to override the c++ compiler
Or is your path on your environment setup to have the directory of the custom compiler before the standard compilers directory?
It might help to clean and then run with scons with --debug=presub which will show you the command line used to build each target.
Also your environment is a dictionary, so try printing out different keys to make sure they match what you expect:
print env['CC']
print env['CXX']

Can't load dll in linux gcc

I'm trying to compile my code with a DLL i made and I get the error below when i write ./prog
./prog: error while loading shared libraries: libctest.so.1: cannot open shared object file: No such file or directory
I followed the tutorial here and my mono app has no problem loading the dll and calling the functions. The key parts of the tutorial were
gcc -Wall -fPIC -c *.c
gcc -shared -Wl,-soname,libctest.so.1 -o libctest.so.1.0 *.o
mv libctest.so.1.0 /opt/lib
ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so.1
ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so
My C# code does
[DllImport("path/to/CDLL", CallingConvention = CallingConvention.Cdecl)]
public static extern void test();
I built with
gcc -Wall -L/opt/lib main.c -lctest -o prog
This is the first thing i changed. prog.c to main.c. From there I simply run with ./prog to get the error at the top of this question. I do this as root so there shouldn't be permission issues. I also chmod 755 the so's. What am I doing wrong? This is opensuse. I had to create the /opt/lib so I am thinking this path isn't registered where it should be
The dynamic linker ld.so will not look in /opt/lib by default when attempting to resolve library dependencies. You have to tell the linker about the non-standard library directories or add the /opt/lib path to your prog binary.
eg
LD_LIBRARY_PATH=/opt/lib ./prog
or, link prog with the -rpath linker option. This provides an additional path for the linker to search when resolving locations of shared objects.
gcc -Wall -L/opt/lib -Wl,-rpath,/opt/lib main.c -lctest -o prog
You can use the ldd command on a binary to show the locations of shared libraries.
ldd prog
This will currently show that libctest.so cannot be found. With an additional search path, the following command should show that all libraries are resolved:
LD_LIBRARY_PATH=/opt/lib ldd prog

Resources