Passing multiple link libraries in GPR file - linux

Is it possible to pass multiple objects to the linker via an environment variable in GNAT? I have a situation where I have an environment variable (let's call it LINKLIBS) that I have included a space-separated list of extra libraries to be brought into the executable at link-time. In GNU Make, we just define our link line as "$(LD) -o output_file main.o obj1.o obj2.o $(LINKLIBS)", and the linker will bring in everything in LINKLIBS into the object, subject to the usual rules.
LINKLIBS may look like this: "/usr/lib/libsource-highlight.a /usr/lib/x86_64-linux-gnu/libGLU.a /usr/lib/x86_64-linux-gnu/libmenu.a"
When I have a couple of objects explicitly specified in the GPR Linker package as follows, it works fine:
package Linker is
for Default_Switches ("Ada")
use (
"--for-linker=-Map",
"--for-linker=../myprog.map",
"--for-linker=--start-group",
external("LIB_PATH") & "/libabc.a",
external("LIB_PATH") & "/lib123.a",
"--for-linker=--end-group");
end Linker;
However, if I include an environment variable that has a space-separated list of archive files, I instead receive an error:
package Linker is
for Default_Switches ("Ada")
use (
"--for-linker=-Map",
"--for-linker=../myprog.map",
"--for-linker=--start-group",
external("LIB_PATH") & "/libabc.a",
external("LIB_PATH") & "/lib123.a",
external("LINKLIBS"),
"--for-linker=--end-group");
end Linker;
gcc: error: /usr/lib/libsource-highlight.a /usr/lib/x86_64-linux-gnu/libGLU.a /usr/lib/x86_64-linux-gnu/libmenu.a: No such file or directory
So, it appears as though it's interpreting the single argument in the Linker package as a single file that includes spaces. I presume the items need to be comma-separated or something, but how can I do this in a way that would use the same variable for my GCC-created programs and my GNAT-created programs?

You need external_as_list:
...
external_as_list("LINKLIBS", " "),
...
but note that this is a list, so you’ll have to use it as such:
package Linker is
for Default_Switches ("Ada")
use (
"--for-linker=-Map",
"--for-linker=../myprog.map",
"--for-linker=--start-group",
external("LIB_PATH") & "/libabc.a",
external("LIB_PATH") & "/lib123.a")
& external_as_list("LINKLIBS", " ")
& ("--for-linker=--end-group");
end Linker;

Related

How is the -fprofile-prefix-path option supposed to work?

When compiling code for coverage instrumentation (to use with lcov later on), we're compiling from a base directory tree (let's call it A), and we want the .gcda files to be produced at a different place (because the target directory tree is different - let's call it B).
So, the compilation command looked like this:
gcc -O0 -g --coverage -fprofile-dir=B -c -fPIC -Wall -o A/otherpath/to/mySourceFile.o A/path/to/mySourceFile.c
When checking the contents of mySourceFile.o (with the strings command), I saw that the mySourceFile.gcda file was set to be generated in B/A/otherpath/to/mySourceFile.gcda
Which is the mangling of the path given through the -fprofile-dir option with the exact absolute path of the object file created - just as written in the documentation. So far, no problem - except that what I want would be the mySourceFile.gcda file to be generated from the B directory, WITHOUT the A part.
So, the documentation also mentions the -fprofile-prefix-path option, which is supposed to allow you to remove part of the path, so that the mangling doesn't add the old path to the new.
I tried using it in the following way:
gcc -O0 -g --coverage -fprofile-dir=B -fprofile-prefix-path=A -c -fPIC -Wall -o A/otherpath/to/mySourceFile.o A/path/to/mySourceFile.c
However, after checking through strings, once again, in the generated object file, the path was still B/A/otherpath/to/mySourceFile.gcda, whereas I expected it to be B/otherpath/to/mySourceFile.gcda (that is, I expected the A part to have been stripped by the -fprofile-prefix-path option.)
Obviously, it didn't work. Any insight why ?
( Compiler used is GCC 11.2.1, which is a version recent enough to know about the option. )
Ok, after some tinkering, I got results. Maybe not exactly what I was expecting, but close enough.
Let me start by saying that the A and B "directories" I mentioned in my question were absolute paths. And it didn't work well.
However, while keeping the absolute B (target) path, I tried not using the full A (source) path while compiling. More precisely, I didn't use it to specify the OUTPUT file name, for the object. Instead, I went to the base directory (the A path), and then, ran the command by specifying the output file path relative to the current (A) directory
Which would give the following command:
(From directory A)
gcc -O0 -g --coverage -fprofile-dir=B -fprofile-prefix-path=A -c -fPIC -Wall -o otherpath/to/mySourceFile.o path/to/mySourceFile.c
This time, the source command did show an interesting result, for the mySourceFile.gcda file:
B#otherpath#to#mySourceFile.gcda
As you can see, it's not exactly what I wanted (there are # instead of /), but mentions to A disappeared, and overall, I'm confident it should work as intended. Not utterly sure yet (I still have to test it on the target platform, which will need tinkering with the way the makefiles currently work), but confident nonetheless.
Also, if I didn't use the -fprofile-prefix-path in the command, then the string would mention the A path, like this (with the '/' inside the A path being replaced with '#' characters, obviously):
B#A#otherpath#to#mySourceFile.gcda
So, the option works, but only when using relative paths, not when using absolute ones, for the object file. Hope that helps people.
PS: I checked by changing the path to the source (.c) file. Whether specified using absolute, or relative, paths, it didn't change the outcome. What matters is specifying the path to the object file in a relative manner.

Qemu and LD_LIBRARY_PATH variable

When I exec qemu-aarch64 with a binary which is using shared libraries I get the following:
qemu-aarch64 -L /usr/aarch64-linux-gnu ./test
./test: error while loading shared libraries: testlibrary.so.1: cannot open shared object file: No such file or directory
Obviously it is because test does not know where the shared libraries are.
Thus:
qemu-aarch64 -L /usr/aarch64-linux-gnu -E LD_PRELOAD="/home/test/libraries/testlibrary.so.1 /home/test/libraries/testlibrary2.so.1" ./test
hi!
Ok, it works; but when I use LD_LIBRARY_PATH it does not work:
qemu-aarch64 -L /usr/aarch64-linux-gnu -E LD_LIBRARY_PATH="/home/test/libraries ./test
./test: error while loading shared libraries: testlibrary.so.1: cannot open shared object file: No such file or directory
The difference between LD_PRELOAD and LD_LIBRARY_PATH, from ld.so man:
LD_PRELOAD:
A list of additional, user-specified, ELF shared objects
to be loaded before all others. This feature can be used
to selectively override functions in other shared objects.
The items of the list can be separated by spaces or
colons, and there is no support for escaping either
separator. The objects are searched for using the rules
given under DESCRIPTION. Objects are searched for and
added to the link map in the left-to-right order specified
in the list.
In secure-execution mode, preload pathnames containing
slashes are ignored. Furthermore, shared objects are
preloaded only from the standard search directories and
only if they have set-user-ID mode bit enabled (which is
not typical).
Within the names specified in the LD_PRELOAD list, the
dynamic linker understands the tokens $ORIGIN, $LIB, and
$PLATFORM (or the versions using curly braces around the
names) as described above in Dynamic string tokens. (See
also the discussion of quoting under the description of
LD_LIBRARY_PATH.)
There are various methods of specifying libraries to be
preloaded, and these are handled in the following order:
(1) The LD_PRELOAD environment variable.
(2) The --preload command-line option when invoking the
dynamic linker directly.
(3) The /etc/ld.so.preload file (described below).
And,
LD_LIBRARY_PATH:
A list of directories in which to search for ELF libraries
at execution time. The items in the list are separated by
either colons or semicolons, and there is no support for
escaping either separator. A zero-length directory name
indicates the current working directory.
This variable is ignored in secure-execution mode.
Within the pathnames specified in LD_LIBRARY_PATH, the
dynamic linker expands the tokens $ORIGIN, $LIB, and
$PLATFORM (or the versions using curly braces around the
names) as described above in Dynamic string tokens. Thus,
for example, the following would cause a library to be
searched for in either the lib or lib64 subdirectory below
the directory containing the program to be executed:
$ LD_LIBRARY_PATH='$ORIGIN/$LIB' prog
(Note the use of single quotes, which prevent expansion of
$ORIGIN and $LIB as shell variables!)
Why does it work with LD_PRELOAD and not with LD_LIBRARY_PATH?
The library you're opening with LD_PRELOAD is "testlibrary.so.1", but the library that the dynamic loader otherwise looks for is "testlibrary.so.3", which suggests there's a mismatch between the library version you have and the library version the binary is linked against, which maybe the LD_PRELOAD is side-stepping. Does LD_LIBRARY_PATH work if you make sure that you have the file in that directory that the binary is looking for ?

Can not find object file with cmake link_directories

I want to add several .o files to the link process. If I do it like this:
TARGET_LINK_LIBRARIES(FFMPEGTest stdc++fs -pthread /home/stiv2/jsoft/nv-ffmpeg/ffmpeg/libswresample/audioconvert.o ...some more stuff... )
then it finds the file. All of these files are in the same directory, so I want to add them semultaneously:
link_directories(/home/stiv2/jsoft/nv-ffmpeg/ffmpeg/libswresample/)
TARGET_LINK_LIBRARIES(FFMPEGTest stdc++fs -pthread audioconvert.o ...some more stuff... )
but this doesn't work:
/usr/bin/ld: cannot find -laudioconvert.o
how do I fix this?
Documentation for target_link_libraries doesn't allow a relative path (audioconvert.o) to be a parameter to that command. It should be either absolute path (/home/stiv2/jsoft/nv-ffmpeg/ffmpeg/libswresample/audioconvert.o) or a plain library name (like z for libz.a library).
Because the object file audioconvert.o is not a library, it cannot be specified with a plain library name. You have no other choice than specify an absolute path for the object files.
For specify several object files in some directory you may use foreach loop:
foreach(obj audioconvert.o foo.o bar.o)
target_link_libraries(FFMPEGTest /home/stiv2/jsoft/nv-ffmpeg/ffmpeg/libswresample/${obj})
endforeach()
Actually, every parameter <param> to target_link_libraries, which doesn't look like an absolute path (and doesn't corresponds to a library target), is transformed into -l<param> option for the linker.
The linker interprets this parameter as a plain library name, and searches for a file named lib<param>.a or lib<param>.so in the link directories.
So, with parameter -laudioconvert.o the linker searches a file with a name libaudioconvert.o.a - obviously, this is not what do you want.

How to use LD_LIBRARY_PATH in CMakeLists?

On my workstation, I have to load module to increment the LD_LIBRARY_PATH environment variable (module load arpack). It seems that cmake can only access this variable by using $ENV{LD_LIBRARY_PATH}. But when printing this variable I get a list of directories seperated by : and I believe cmake does not understand it as a list of directories to find libraries, as a consequence, the following does not work:
find_library (Arpack_LIBRARY libarpack.a PATH $ENV{LD_LIBRARY_PATH})
and
message(STATUS $ENV{LD_LIBRARY_PATH})
prints
/home/user:/home/user/lib:/usr/lib:/lib
This is how I want to find my library. How can I transform $ENV{LD_LIBRARY_PATH} to an exploitable list of directories ?
In CMake a list is just a string with semicolon-separated parts. For make colon-separated string to be semicolon-separated, use string(REPLACE) command:
string(REPLACE ":" ";" LIBRARY_DIRS $ENV{LD_LIBRARY_PATH})
Resulted variable can be used in find_library call:
find_library (Arpack_LIBRARY libarpack.a PATHS ${LIBRARY_DIRS})

g++ searches /lib/../lib/, then /lib/

According to g++ -print-search-dirs my C++ compiler is searching for libraries in many directories, including ...
/lib/../lib/:
/usr/lib/../lib/:
/lib/:
/usr/lib/
Naively, /lib/../lib/ would appear to be the same directory as /lib/ — lib's parent will have a child named lib, "that man's father's son is my father's son's son" and all that. The same holds for /usr/lib/../lib/ and /usr/lib/
Is there some reason, perhaps having to do with symbolic links, that g++ ought to be configured to search both /lib/../lib/ and /lib/?
If this is unnecessary redundancy, how would one go about fixing it?
If it matters, this was observed on an unmodified install of Ubuntu 9.04.
Edit: More information.
The results are from executing g++ -print-search-dirs with no other switches, from a bash shell.
Neither LIBRARY_PATH nor LPATH are output from printenv, and both echo $LPATH and echo LIBRARY_PATH return blank lines.
An attempt at an answer (which I gathered from a few minutes of looking at the gcc.c driver source and the Makefile environment).
These paths are constructed in runtime from:
GCC exec prefix (see GCC documentation on GCC_EXEC_PREFIX)
The $LIBRARY_PATH environment variable
The $LPATH environment variable (which is treated like $LIBRARY_PATH)
Any values passed to -B command-line switch
Standard executable prefixes (as specified during compilation time)
Tooldir prefix
The last one (tooldir prefix) is usually defined to be a relative path:
From gcc's Makefile.in
# Directory in which the compiler finds libraries etc.
libsubdir = $(libdir)/gcc/$(target_noncanonical)/$(version)
# Directory in which the compiler finds executables
libexecsubdir = $(libexecdir)/gcc/$(target_noncanonical)/$(version)
# Used to produce a relative $(gcc_tooldir) in gcc.o
unlibsubdir = ../../..
....
# These go as compilation flags, so they define the tooldir base prefix
# as ../../../../, and the one of the library search prefixes as ../../../
# These get PREFIX appended, and then machine for which gcc is built
# i.e i484-linux-gnu, to get something like:
# /usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../i486-linux-gnu/lib/../lib/
DRIVER_DEFINES = \
-DSTANDARD_STARTFILE_PREFIX=\"$(unlibsubdir)/\" \
-DTOOLDIR_BASE_PREFIX=\"$(unlibsubdir)/../\" \
However, these are for compiler-version specific paths. Your examples are likely affected by the environment variables that I've listed above (LIBRARY_PATH, LPATH)
Well, theoretically, if /lib was a symlink to /drive2/foo, then /lib/../lib would point to /drive2/lib if I'm not mistaken. Theoretically...
Edit: I just tested and it's not the case - it comes back to /lib. Hrm :(

Resources