Specific "cannot open shared object file" error - makefile

I'm a first year computer science student following a course on (amongst others) Makefiles. For our first assignment, we had to create a shared library and link against it.
I have the following setup:
A folder hw1 containing the folders app and lib.
Inside lib is a file called libmine.so, the library I want to link against.
Inside app, there are two files: test.cpp and a Makefile. The former uses the libmine library.
The Makefile is as follows (in the file itself, indentation etc. is correct):
all: test
test: test.cpp
g++ -Wall -o test -L../lib -I../lib/include test.cpp -lmine
However, when running test, I get the infamous `libmine.so: cannot open shared object file' error.
I believe this has something to do with exporting LD_LIBRARY_PATH. I have tried doing so (export LD_LIBRARY_PATH=$[very long relative path to the lib folder]), but I want to do this in my Makefile. Additionally, I don't want the path to be relative, as my teachter should be able to open the file when I send it to him (so I think it should be something like ../lib/libmine.so).
I looked at various StackOverflow posts, such as this one, but none seemed to answer this specific question (either it was a different setup or the solution simply didn't work). By the way: putting the line export LD_LIBRARY_PATH=../lib right under test: test.cpp and before the g++ command did not do anything.
Any help is much appreciated :)

when running test, I get the infamous `libmine.so: cannot open shared object file' error.
This is happening because the -L../lib argument tells the static linker where to find the library, but it doesn't tell anything to the dynamic linker (aka loader), and the problem is that the latter can't find this library.
To solve this, you can use LD_LIBRARY_PATH, but this is usually ill-advised.
What you want is something called RPATH or RUNPATH (assuming you are on a Linux or similar system):
g++ -Wall -o test -L../lib -I../lib/include test.cpp -Wl,-rpath=../lib -lmine
Additionally, I don't want the path to be relative, as my teachter should be able to open the file when I send it to him
Is your teacher going to run your binary on the same system, or on a different one? If the former, you could do this:
g++ -Wall -o test -L../lib -I../lib/include test.cpp -Wl,-rpath=/full/path/to/hw1/lib -lmine
If the latter, /full/path/to/hw1/lib may or may not be available on your teacher's machine, and you need to think about what exactly you are going to send to him.
The usual way to solve this is to package both the application and the library into a tar file:
tar cvf to-send.tar app/test lib/libmine.so
The teacher can then extract the parts of your tar file into arbitrary directory, and try to run it. To make this work, you need RPATH that is relative to the application, regardless of where the application ends up. To achieve that, you want:
g++ -Wall -o test -L../lib -I../lib/include test.cpp -Wl,-rpath='$ORIGIN/../lib' -lmine

Related

Does "-Wl,-soname" work on MinGW or is there an equivalent?

I'm experimenting a bit with building DLLs on windows using MINGW.
A very good summary (in my opinion) can be found at:
https://www.transmissionzero.co.uk/computing/building-dlls-with-mingw/
There is even a basic project which can be used for the purpose of this discussion:
https://github.com/TransmissionZero/MinGW-DLL-Example/releases/tag/rel%2Fv1.1
Note there is a cosmetic mistake in this project which will make it fail out of the box: the Makefile does not create an "obj" directory - Either adjust the Makefile or create it manually.
So here is the real question.
How to change the Windows DLL name so it differs from the actual DLL file name ??
Essentially I'm trying to achieve on Windows, the effect which is very well described here on Linux:
https://www.man7.org/conf/lca2006/shared_libraries/slide4b.html
Initially I tried changing "InternalName" and ""OriginalFilename" in the resource file used to create the DLL but that does not work.
In a second step, I tried adding "-Wl,-soname,SoName.dll" on the command that performs the final link, to change the Windows DLL name.
However, that does not seem to have the expected effect (I'm using MingW 7.3.0, x86_64-posix-seh-rev0).
Two things makes me say that:
1/ The test executable still works (I would expect it to fail, because it tries to locate SoName.dll but can't find it).
2/ "pexports.exe AddLib.dll" produces the output below, where the library name hasn't changed:
LIBRARY "AddLib.dll"
EXPORTS
Add
bar DATA
foo DATA
Am I doing anything wrong ? Are my expectations wrong perhaps ?
Thanks for your help !
David
First of all, I would like to say it's important to use either a .def file for specifying the exported symbols or use __declspec(dllexport) / __declspec(dllimport), but never mix these two methods. There is also another method using the -Wl,--export-all-symbols linker flag, but I think that's ugly and should only be used when quick and dirty is what you want.
It is possible to tell MinGW to use a DLL filename that does not match the library name. In the link step use -o to specify the DLL and use -Wl,--out-implib, to specify the library file.
Let me illustrate by showing how to build chebyshev as a both static and shared library. Its sources consist of only only 2 files: chebyshev.h and chebyshev.c.
Compile
gcc -c -o chebyshev.o chebyshev.c -I. -O3
Create static library
ar cr libchebyshev.a chebyshev.o
Create a .def file (as it wasn't supplied and __declspec(dllexport) / __declspec(dllimport) wasn't used either). Note that this file doesn't contain a line with LIBRARY allowing the linker to specify the DLL filename later.
There are several ways to do this if the .def file wasn't supplied by the project:
3.1. Get the symbols from the .h file(s). This may be hard as sometimes you need to distinguish for example between type definitions (like typedef, enum, struct) and actual functions and variables that need to be exported;
echo "EXPORTS" > chebyshev.def
sed -n -e "s/^.* \**\(chebyshev_.*\) *(.*$/\1/p" chebyshev.h >> chebyshev.def
3.2. Use nm to list symbols in the library file and filter out the type of symbols you need.
echo "EXPORTS" > chebyshev.def
nm -f posix --defined-only -p libchebyshev.a | sed -n -e "s/^_*\([^ ]*\) T .*$/\1/p" >> chebyshev.def
Link the static library into the shared library.
gcc -shared -s -mwindows -def chebyshev.def -o chebyshev-0.dll -Wl,--out-implib,libchebyshev.dll.a libchebyshev.a
If you have a project that uses __declspec(dllexport) / __declspec(dllimport) things are a lot easier. And you can even have the link step generate a .def file using the -Wl,--output-def, linker flag like this:
gcc -shared -s -mwindows -o myproject.dll -Wl,--out-implib,myproject.dll.a -Wl,--output-def,myproject.def myproject.o
This answer is based on my experiences with C. For C++ you really should use __declspec(dllexport) / __declspec(dllimport).
I believe I have found one mechanism to achieve on Windows, the effect described for Linux in https://www.man7.org/conf/lca2006/shared_libraries/slide4b.html
This involves dll_tool
In the example Makefile there was originally this line:
gcc -o AddLib.dll obj/add.o obj/resource.o -shared -s -Wl,--subsystem,windows,--out-implib,libaddlib.a
I simply replaced it with the 2 lines below instead:
dlltool -e obj/exports.o --dllname soname.dll -l libAddLib.a obj/resource.o obj/add.o
gcc -o AddLib.dll obj/resource.o obj/add.o obj/exports.o -shared -s -Wl,--subsystem,windows
Really, the key seems to be the creation with dlltool of an exports file in conjunction with dllname. This exports file is linked with the object files that make up the body of the DLL and it handles the interface between the DLL and the outside world. Note that dlltool also creates the "import library" at the same time
Now I get the expected effect, and I can see that the "Internal DLL name" (not sure what the correct terminology is) has changed:
First evidence:
>> dlltool.exe -I libAddLib.a
soname.dll
Second evidence:
>> pexports.exe AddLib.dll
LIBRARY "soname.dll"
EXPORTS
Add
bar DATA
foo DATA
Third evidence:
>> AddTest.exe
Error: the code execution cannot proceed because soname.dll was not found.
Although the desired effect is achieved, this still seems to be some sort of workaround. My understanding (but I could well be wrong) is that the gcc option "-Wl,-soname" should achieve exactly the same thing. At least it does on Linux, but is this broken on Windows perhaps ??

Can GCC ignore nonexisting directories?

I have two projects: mylib and myproj
mylib compiles into a shared object.
myproj uses mylib.
I linking myproj using:
g++ -L../../mylib/Release myproj.o -lmylib
That works fine on my development machine.
On another machine, mylib.so is located in /usr/local/lib. Therefore there is no need for -L.
It compiles fine without the -L.
But, once I compile it with the same command (with the -L), it responds with
g++: error: ../../mylib/Release: No such file or directory
I would like myproj to compile on both machines with the same command.
Is there a way to make it ignore non existing directories?
It doesn't seem to care whether a full path doesn't exist, it complains only for relative paths.
You want to use Makefiles as often as you can since they make things a lot easier. In a defined Makefile you could check for the directory first. It could be sth like this:
all:
if [ -d "mylib" ]; then
g++ -L../../mylib/Release myproj.o -lmylib
else
g++ myproj.o -lmylib
fi
Of course you can use Variables like CC, etc. This is just a basic example.

Include mpi to make file

I am trying to include MPI compiler to my makefile. The makefile is already prepared such that I only need to include the address of the MPI compiler in a a separate env file. However doing so does not work. I can get the cpp file to run manually by typing:
mpicxx Demo_00.cpp -o aprogram
./aprogram
I test where the mpi compiler is located using:
which mpicxx
/usr/bin/mpicxx
In the env file the corresponding line is:
MPICXX=/usr/bin/mpicxx
However, when I try to 'make' he cpp file I get the following error:
make Demo_00
g++ Demo_00.cpp -o Demo_00
Demo_00.cpp:2:17: fatal error: mpi.h: No such file or directory
compilation terminated.
make: *** [Demo_00] Error 1
The cpp file is in the same folder as the env file and the makefile.
I am not quite sure how to identify the error.
Thank you for your help,
Tartaglia
If you want to change the name of the C++ compiler, you have to change the variable CXX. That's the default variable make uses when it wants to compile C++ code.
This line in your log file:
g++ Demo_00.cpp -o Demo_00
says that you are using g++ compiler instead of mpixx.
Usually in makefiles compiler definition is at the beginnig of the file and looks like this:
CC=g++
just change it to mpixx
CC=mpixx
Thank you all for your responses, I took a closer look into the makefile I thought I was using and it turns out, as you have already suggested, I was not using it at all. The makefile was only able to execute one specific cpp file with one specific name. So whenever I typed in make *.cpp I was using the standard make as you already pointed out.
Thanks again for your help.

Using gfortran with libraries

I have a legacy code written using fortran 77. I'm trying to build it with gfortran. But I seem to be failing at the stage where I include the libraries in the build. The dozens of *.f source files compile fine, but when they are being linked, I get a bunch of "undefined reference" errors all relating to subroutines and functions that are defined in my libraries. I already ran the makefile for the libraries first, so the variables I need should all be exported. I'm playing with the "-L" option, but can't get it to work as desired.
First, here's my syntax of the linking line in my makefile:
29 $(PROGRAM): $(SRCS) $(LIBS)
30 $(FC) $(FLFLAGS) -o $# $+ -L$(DIRLIB)
PROGRAM is the program name, SRCS are all the compiled source files, LIBS is set to two different files - an archive file (file.a) and a file.o file.
FC is gfortran, I don't have any specific linking flags for FLFLAGS as of now, and DIRLIB is the main directory of the libraries.
The thing is that my *.o files that resulted from building my librarires don't reside in just the main directory, DIRLIB. DIRLIB contains several directories, all with their own *.o files that are needed by my code.
I tried adding each individual directory after the -L option (e.g. DIRLIB/DIR1/*.o DIRLIB/DIR2/*.o DIRLIB/DIR3/*.o), but I eventually start getting errors that some subroutines are multiply defined.
All this business of user-defined libraries and archive files just confuses me and I'm pretty new to making makefiles in the first place, so I'm just taking a shot in the dark here that somebody might be able to help me shed some light on this.
Libraries need to come after the .o files that reference them in the linking command.
I'm guessing the object file in LIBS comes after the library, but needs some of the procedures from it. Can you show the command that is actually run (with all variables expanded), to confirm this?
I tried to build this code again using the library. It worked this time. I'm pretty sure I'm doing the same thing in my makefile as I did before, so it must be related to the library I had. Maybe somebody altered it along the way and inadvertently broke it. But I got a fresh clean copy of the library. My steps are to:
1) run the makefile for the library source files; it creates a library.a archive file
2) run my code makefile:
it has a line to specify the location of this archive file and assign it to "DIRLIB"
DIRLIB := ../library
then the linking command of the makefile becomes
$(FC) $(FLFLAGS) -o $# $+ -L$(DIRLIB) -lskit
FC is my compiler, FLFLAGS are my linking flags, -L is the option specifying the location of libraries to be included and -lskit is a crucial option which appears to allow the use of F77 libraries... without the -lskit option, I get many undefined reference errors. It may have been that last time I was not including this -lskit option at the end.

How to set library search path for 64 bit libraries for g++ in Ubuntu?

Trying to compile something for 64 bit unix using Ubuntu. As a disclaimer I only started using linux and gcc a few days ago so still learning my way around. Anyway, getting the following error:
/home/myuser/myproject/myfile.cpp:437: undefined reference to `clock_gettime'
A quick google reveals I need the -lrt option to link with librt.a. So I check my command line ( formatted for readability, different file names and I've remove lists of file names ):
/usr/bin/g++
-Wl,
--gc-sections
-fno-exceptions
-m64
-B/usr/bin
-o
"/home/myuser/myproject"
-Wl,
-Map, "/home/myuser/myproject/myproject.map"
-g
"/home/myuser/myproject/myproject.cpp.obj"
..and some more .objs..
-Xlinker
--start-group
"-lpthread"
"-lrt"
"/home/myuser/myproject/lib/mylib.a"
..and some more .as..
-Xlinker
--end-group
Hmm. Looks like -lrt is already there, maybe I don't have librt.a? Nope searching all files reveals I have /usr/lib/x86_64-linux-gnu/librt.a. I guess g++ is looking in the wrong place. So in the above command line I replace -lrt with /usr/lib/x86_64-linux-gnu/librt.a and bingo! it compiles and links fine. Unfortunately, this is an automated tool and I need this to work on many computers and can't make assumptions about the location of librt.a so I really need it to work with -lrt. So how do I set the local libary search path? First attempt is changing LD_LIBRARY_PATH environment variable but apparently ( from what I can tell from more googling ) this is ignored on ubuntu and instead I should be messing with .conf files in /etc/ld.so.conf.d/, however I already it looks like I already have x86_64-linux-gnu.conf in there with the following lines:
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
From my reading up this point it looks that should be all I need. Kind of stuck as to where to go from here...
Answering my own question just in case someone else has this problem. Turns out the correct librt.a was being linked but the linker is very sensitive to the link order. Putting -lrt and -lpthread at the end of the group fixes the problem.

Resources