29

My LIBRARY_PATH environment variable has a custom directory in it: /cs/public/lib/pkg/opencv/lib.

But, when I use g++ --print-search-dirs, I get this instead:

libraries: =
/cs/public/lib/pkg/opencv/lib/x86_64-suse-linux/4.6/:
/cs/public/lib/pkg/opencv/lib/../lib64/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/../../../../x86_64-suse-linux/lib/x86_64-suse-linux/4.6/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/../../../../x86_64-suse-linux/lib/../lib64/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/../../../x86_64-suse-linux/4.6/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/../../../../lib64/:
/lib/x86_64-suse-linux/4.6/:
/lib/../lib64/:
/usr/lib/x86_64-suse-linux/4.6/:
/usr/lib/../lib64/:
/cs/public/lib/pkg/opencv/lib/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/../../../../x86_64-suse-linux/lib/:
/usr/lib64/gcc/x86_64-suse-linux/4.6/../../../:
/lib/:
/usr/lib/

Why does g++ look in these alternatives and a whole bunch of other system locations before what I explicitly specify in the LIBRARY_PATH variable, and where is this documented?

I would understand if system defaults were searched before LIBRARY_PATH and LIBRARY_PATH/../lib64, etc, but g++ puts LIBRARY_PATH/../lib64, then system paths, then LIBRARY_PATH. Where is this ordering documented?

My g++ version is g++ (SUSE Linux) 4.6.2

My OS is openSUSE 12.1 (x86_64)

  • 2
    Whenever I add a library path to my `LIBRARY_PATH`, and I do `gcc --print-search-dirs` I see my library path, followed by a `x86_64-pc-linux-gnu/4.5.4/`, a `../lib64/` and the path itself. Not *only* the `../lib64/` appended directory. – Jon Lin Sep 16 '12 at 16:09
  • 1
    Yes, mine does too. I just noticed, but much further down the search list than the appended versions, and there are system directories in between. I still don't understand the behaviour and I edited my question. –  Sep 16 '12 at 16:53
  • 1
    I guess because it's 64bit, it puts all of the `../lib64/` and OS libraries (e.g. `x86_64-suse-linux/4.6/`) paths first, with your custom lib in front, then the paths themselves. The same thing happens on my 32bit linux systems, except it doesn't do the `../lib64` part. – Jon Lin Sep 16 '12 at 17:18
  • 2
    I'd like to know where this is documented, though. This is a bit of a complaint, but I would expect that if I explicitly set LIBRARY_PATH, that it would take precedence over /usr/lib64/gcc/x86_64-suse-linux/4.6/, especially since g++ _does_ care to give LIBRARY_PATH/../lib64 precedence over /usr/lib64/gcc/x86_64-suse-linux/4.6/. –  Sep 16 '12 at 17:21
  • lib64 is defined in gcc/config/i386/t-linux when GCC is compiled. The version of GCC that comes with the OS had this file patched before GCC was built. If you want your custom GCC installation to use /cs/public/lib/pkg/opencv/lib first, you have to modify gcc/config/i386/t-linux and rebuild GCC. – facetus Feb 23 '20 at 22:57

7 Answers7

11

A similar question was asked here: g++ searches /lib/../lib/, then /lib/

These scary-looking search paths are determined at least in part when the compiler itself it built, for example during the configure phase. It's clear that it goes beyond environment variables because it's possible to have multiple copies of GCC installed and have each of them give different results for gcc --print-search-dirs. Also noting that g++ --print-search-dirs and gcc --print-search-dirs give different results points out that the g++ wrapper is also affecting the search path. Besides configure/build time differences, GCC is definitely aware of the path where its own executable is, and will search subdirectories of that path. A lot of this alchemy can be found in the GCC documentation:
http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Directory-Options.html#Directory-Options
http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Environment-Variables.html#Environment-Variables

As far as I know, the most forceful thing that you can without compiling your own copy of GCC is to specify your custom libraries using the -L option. The reason I say this is because -L is searched before e.g. LIBRARY_PATH (see the above link on environment variables). In order to make it more tolerable you could add an alias for g++ including the -L option in your .bashrc file.

If you want a definitive answer then downloading a copy of the GCC source code is one way. For example, in gcc.c the following highly suggestive comment appears:

/* Build a list of search directories from PATHS.
   PREFIX is a string to prepend to the list.
   If CHECK_DIR_P is true we ensure the directory exists.
   If DO_MULTI is true, multilib paths are output first, then
   non-multilib paths.
   This is used mostly by putenv_from_prefixes so we use `collect_obstack'.
   It is also used by the --print-search-dirs flag.  */

However the function that follows the comment is not very obvious.

Community
  • 1
  • 1
Douglas B. Staple
  • 10,510
  • 8
  • 31
  • 58
  • This doesn't solve the documentation question, but the different behavior in gcc vs g++ gives some clues. On my system the LIBRARY_PATH entries appear first in the gcc search order, but for g++ they appear in the middle of the search order with system paths on either side, the way you describe. – Douglas B. Staple Sep 18 '12 at 18:47
  • I must say, the linked documentation (and it's latest counterpart) is useless with respect to the question. It describes what options there are, but it completely fails to disclose the *alchemy*. This is a horrible thing to omit from documentation... – Zulan Nov 18 '17 at 11:28
7

This is multilib at work - a mechanism which allows for having libraries (but also the whole compilation and build toolchain) for multiple architectures on a single machine. This Wiki states that "The multilib suffix is appended to all directories searched for libraries by GCC and passed via -L options to the linker. The linker itself does not have any particular knowledge of multilibs, and will continue to consult its default search directories if a library is not found in the -L paths. If multiple orthogonal ABI-changing options are used in a single compilation, multiple multilib suffixes can be used in series.".

So, according to the above description, the architecture marker string or different variants thereof are appended to each library search path the compiler receives since it doesn't differentiate between default and custom paths. Your custom path is first in the row, but it undergoes the same "expansion" process as other paths.

Due to the need to handle i386 compatibility, multilib mechanisms seem to now be used by default on most x64 distros, which in practice means most of the installations out there.

Michał Kosmulski
  • 9,855
  • 1
  • 32
  • 51
  • GCC does this even when built with `--disable-multilib`. Does this make sense? The wiki link seems very useful, but I couldn't tell if it's up to date or not. Such a shame that the official documentation fails to provide this level of detail. – Zulan Nov 18 '17 at 11:30
  • @Zulan That's surprising indeed – Michał Kosmulski Nov 18 '17 at 19:36
2

I have the exact same problem on:

Fedora 17, gcc 4.7 and gcc 4.3
CentOS 6.3, gcc 4.4
Unubuntu 12, gcc 4.6

So it looks like this is a problem with most gcc versions. Probably this strange behavior first appeared in gcc 4.2 at least according to this.

I tried duping the specs and playing with them. It looks like the *multilib spec is used to append specific strings depending on the platform. For example my original spacs looked like:

*multilib:
. !m64 !m32;64:../lib64 m64 !m32;32:../lib !m64 m32;

When I changed 64:../lib64 to 64:../lib then instead of ../lib64 gcc appended ../lib. But I couldn't fully decipher the meaning of *multilib or any of the other specs.

Svetlin Mladenov
  • 4,307
  • 24
  • 31
  • Nice to know I'm not alone. I should try playing around with the spec file, too. I'm starting to be convinced this isn't actually documented anywhere. –  Oct 03 '12 at 19:21
2

This answer attempts to summarize the search path behaviors for both GCC and Clang.

GCC

Include path: for a command-line of the following format:

CPLUS_INCLUDE_PATH=EDIR g++ -IIDIR -isystemSDIR

the following list of directories are used as search paths for #include <...>:

IDIR                                           # '-I' directories.
SDIR                                           # '-isystem' directories.
EDIR                                           # *_INCLUDE_PATH directories.
GCCDIR/include/c++/GCCVER                      # libstdc++ directory (C++).
GCCDIR/include/c++/GCCVER/GCCARCH              # libstdc++ directory (C++).
GCCDIR/include/c++/GCCVER/backward             # libstdc++ directory (C++).
GCCDIR/lib/gcc/GCCARCH/GCCVER/include          # GCC arch-specific directory.
/usr/local/include/GCCARCH                     # Local arch-specific include directory.
/usr/local/include                             # Local include directory.
GCCDIR/include                                 # GCC include directory.
GCCDIR/lib/gcc/GCCARCH/GCCVER/include-fixed    # GCC include-fixed directory.
/usr/include/GCCARCH                           # System include arch-specific directory.
/usr/include                                   # System include directory.

Library path: for a command-line of the following format:

LIBRARY_PATH=EDIR gcc -BBDIR -LLDIR

the following arguments are passed to the linker:

-LLDIR                                         # '-L' directories.
-LBDIR                                         # '-B' directories.
-LEDIR/../libXX                                # Multilib directories from LIBRARY_PATH.
-LGCCDIR/lib/gcc/GCCARCH/GCCVER                # GCC arch-specific library directory.
-LGCCDIR/libXX                                 # GCC multilib library directory.
-L/libXX                                       # System multilib library directory.
-L/usr/libXX                                   # System multilib library directory. 
-LEDIR                                         # LIBRARY_PATH directories.
-LGCCDIR/lib                                   # Other GCC libraries.

Clang

Include path: for a command-line of the following format:

CPLUS_INCLUDE_PATH=EDIR clang++ --gcc-toolchain=GCCDIR -BBDIR -IIDIR -isystemSDIR

the following list of directories are used as search paths for #include <...>:

IDIR                                           # '-I' directories.
SDIR                                           # '-isystem' directories.
EDIR                                           # *_INCLUDE_PATH directories.
 # If -stdlib=libstdc++ is used:
   GCCDIR/include/c++/GCCVER                   # libstdc++ directory from the selected GCC toolchain (C++).
   GCCDIR/include/c++/GCCVER/GCCARCH           # libstdc++ directory from the selected GCC toolchain (C++).
   GCCDIR/include/c++/GCCVER/backward          # libstdc++ directory from the selected GCC toolchain (C++).
 # If -stdlib=libc++ is used:
   CLANGDIR/include/c++/v1                     # libc++ directory (C++).
/usr/local/include                             # Local include directory.
CLANGDIR/lib/clang/CLANGVER/include            # Clang include directory.
/include                                       # System include directory.
/usr/include                                   # System include directory.

Library path: for a command-line of the following format:

LIBRARY_PATH=EDIR clang --gcc-toolchain=GCCDIR -BBDIR -LLDIR    

the following arguments are passed to the linker:

-LLDIR                                         # '-L' directories.
-LGCCDIR/lib/gcc/GCCARCH/GCCVER                # GCC arch-specific library directory.
-LGCCDIR/libXX                                 # GCC multilib library directory.
-L/libXX                                       # System multilib library directory.
-L/usr/libXX                                   # System multilib library directory.
-LGCCDIR/lib                                   # Other GCC libraries.
-LCLANGDIR/lib                                 # Clang libraries.
-L/lib                                         # System library directory.
-L/usr/lib                                     # System library directory.
-LEDIR                                         # LIBRARY_PATH directories.

Summary

The search path for includes are pretty much the same in both GCC and Clang. C++-specific paths are omitted if the C frontend is used in both cases. Library search paths differ substantially between GCC and Clang, notably the presence of -B directories and the odd manipulation of LIBRARY_PATH in the GCC frontend.

The library search paths are the same for both C and C++ frontends. Other library search paths are introduced by the linker itself. The following excerpt comes from the vanilla linker script for GNU Binutils:

# Multilib library directories.
SEARCH_DIR("BINUTILSDIR/BINUTILSARCH/libXX");
SEARCH_DIR("BINUTILSDIR/libXX");
SEARCH_DIR("/usr/local/libXX");
SEARCH_DIR("/libXX");
SEARCH_DIR("/usr/libXX");
# Traditional library directories.
SEARCH_DIR("BINUTILSDIR/BINUTILSARCH/lib");
SEARCH_DIR("BINUTILSDIR/lib");
SEARCH_DIR("/usr/local/lib");
SEARCH_DIR("/lib");
SEARCH_DIR("/usr/lib");

It is also imperative to note that library dependencies are not searched for inside the directories listed above. These exclusively rely on -rpath and -rpath-link options passed to the linker, or else they get resolved from the default system library paths. Thus it might be useful to produce both -L and -rpath-link arguments in order to guarantee that the correct libraries are linked.

Finally, special files (such as the CRT objects) are searched for only in -B directories. In Clang, special files are also searched inside the selected GCC toolchain. Other factors (spec files, distribution-specific configuration) might change some or all of the above.

alecov
  • 4,882
  • 2
  • 29
  • 55
-1

Looks like it's needed for cross-compiling. From the ChangeLog:

  Wed Mar 29 14:53:23 1995  Jim Wilson  <wilson@cygnus.com>

          * gcc.c (process_command): Delete code modifying gcc_exec_prefix.
          (main): Put it here after last use of gcc_exec_prefix.  For cross
          compiler, set startfile_prefixes if gcc_exec_prefix is set and
          standard_startfile_prefix is a relative path.

startfile_prefixes is what is being printed out with the search-dirs flags. From gcc/gcc.c:

    if (print_search_dirs)
      {
        printf (_("install: %s%s\n"), standard_exec_prefix, machine_suffix);
        printf (_("programs: %s\n"), build_search_list (&exec_prefixes, "", 0));
        printf (_("libraries: %s\n"), build_search_list (&startfile_prefixes, "", 0));
        return (0);
      }
eduffy
  • 39,140
  • 13
  • 95
  • 92
  • Okay, but why does it use `LIBRARY_PATH/[arch]`, then `LIBRARY_PATH/../lib64`, then `/usr/lib64/[...]`, then finally `LIBRARY_PATH`. –  Sep 18 '12 at 17:19
-1

The compiler will first look at the default paths then for the others. How does it sorted when you print I don't now, but it is documented here, 3.19 Environment Variables Affecting GCC.

SIFE
  • 5,567
  • 7
  • 32
  • 46
  • The compiler first looks in "/cs/public/lib/pkg/opencv/lib/x86_64-suse-linux/4.6/:", then in "/cs/public/lib/pkg/opencv/lib/../lib64/:", then much later in "/cs/public/lib/pkg/opencv/lib/:". The print-out order from --print-search-dirs is the order they are searched in. –  Sep 16 '12 at 17:18
  • @Sancho I think this also depends on how the compiler is configured. From Ubuntu 12.04 I get this: `libraries: =/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../../i686-linux-gnu/lib/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../../i686-linux-gnu/lib/i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../../i686-linux-gnu/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../../lib/:/lib/i686-linux-gnu/4.6/:/lib/i386-linux-gnu/` – SIFE Sep 16 '12 at 17:25
  • What was your LIBRARY_PATH set to in this case? I don't see any custom paths. –  Sep 16 '12 at 17:26
-1

The paths are defined by the built-in specs. Specs define how the pipeline processes the source code to obtain a result. GCC just drives the compilation.

You can give GCC your own spec file via -spec=, and you can get the built-in specs with -dumpspecs IIRC.

This is probably explained somewhere in the GCC manual.

Ismael Luceno
  • 2,055
  • 15
  • 26
  • Thanks, this is a useful bit of information. I'm still not clear how anything in the specs file determines the ordering of the library search paths that are based on `LIBRARY_PATH`. I couldn't find details in the gcc manual. I found a related question that goes into a bit of detail on this issue: http://stackoverflow.com/questions/11688893/decoding-gcc-specs-file-line –  Sep 20 '12 at 15:30
  • GCC collects the list from various sources (builtin, env, params), passes it around, and the specs expand on it. So in the end the result is determined by the specs. – Ismael Luceno Sep 20 '12 at 17:52
  • I realize that the specs determine the results, but _how_? What spec line results in `LIBRARY_PATH/[arch]`, `LIBRARY_PATH/../lib`, and `/usr/lib64/[...]` coming before `LIBRARY_PATH`? And how would I change it to make that not the case? I can't tell. –  Sep 20 '12 at 18:15
  • As I said before, you can give gcc your own spec, take a look at [the manual](http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Spec-Files.html). – Ismael Luceno Sep 20 '12 at 20:13
  • What I meant was, what line should I put in my custom spec file to make `LIBRARY_PATH` come before `LIBRARY_PATH/[arch]`, and before `/usr/lib64/[...]`? The manual you link to doesn't give this information. If you could answer this, and point to documentation for it, you would get the bounty. –  Sep 21 '12 at 03:07
  • OK, it's not a copy & paste thing, and there could be better documentation, but it's pretty clear, just dump the spec and override the link rule (IIRC `%D` was used only once, so should be pretty easy to identify). – Ismael Luceno Sep 22 '12 at 05:10