Search code examples
linkerclangbundle

Where is the documentation of c lang env variables?


TLDR I tried to read the clang llvm documentation to find a way to pass library path, I wasn't successful. Then I searched and found a solution. I am curious where I can find the documentation of my solution.

Details and why

I was trying to run ruby's bundle on my catalina and I faced this error:

ld: library not found for -lssl

I reinstalled openssl using brew install openssl and then brew link openssl I received the follwoing response from it:

Warning: Refusing to link macOS provided/shadowed software: openssl@1.1
If you need to have openssl@1.1 first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> ~/.zshrc

For compilers to find openssl@1.1 you may need to set:
  export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
  export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"

For pkg-config to find openssl@1.1 you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"

While I appreciated brew's effort to make it work non of those worked. I narrowed the linker issue to this to see what's going on:

clang -dynamic -bundle -o asdf  -L/usr/local/Cellar/mysql/8.0.19/lib -lmysqlclient -lssl -lcrypt -v 

Finally using LIBRARY_PATH I made passed the enviornment variable

export LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib/

Solution

  • You were able to make the clang linkage:

    clang -dynamic -bundle -o asdf  -L/usr/local/Cellar/mysql/8.0.19/lib -lmysqlclient -lssl -lcrypt -v
    

    succeed when it otherwise failed by setting:

    export LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib/
    

    in the environment of the clang command.

    That's evidence that clang is influenced by the LIBRARY_PATH environment variable in the same way as GCC compilers.

    LIBRARY_PATH is not mentioned in the ENVIRONMENT section of man clang, or indeed at all, so this GCC-like behaviour can strictly be viewed as not mandatory. It seems likely however that the omission is an oversight in the manual, since clang generally strives to be an unobtrusive substitute for gcc. If we run a Linux clang linkage in verbose mode:

    $ export LIBRARY_PATH=/wheres/wally; clang -v main.o -L. -lfoo -lbar 
    clang version 10.0.0-4ubuntu1 
    Target: x86_64-pc-linux-gnu
    Thread model: posix
    InstalledDir: /usr/bin
    Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
    Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
    Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
    Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
    Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
    Candidate multilib: .;@m64
    Selected multilib: .;@m64
     "/usr/bin/ld" -z relro --hash-style=gnu --build-id --eh-frame-hdr \
     -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out \
     /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o \
     /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o \
     /usr/bin/../lib/gcc/x86_64-linux-gnu/9/crtbegin.o \
     -L. -L/usr/bin/../lib/gcc/x86_64-linux-gnu/9 \
     -L/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu \
     -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu \
     -L/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../.. \
     -L/usr/lib/llvm-10/bin/../lib -L/lib -L/usr/lib \
     -L/wheres/wally \              # < There's wally!
     main.o -lfoo -lbar -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc \
     --as-needed -lgcc_s --no-as-needed \
     /usr/bin/../lib/gcc/x86_64-linux-gnu/9/crtend.o \
     /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o
    

    we observe clang inserting the expansion of -L${LIBRARY_PATH} into the ld commandline after all of the other -Ldir options. And if we track down LIBRARY_PATH in the clang source tree, we'll see the code that makes it do so.

    In the ENVIRONMENT section of man gcc, LIBRARY_PATH is documented:

    LIBRARY_PATH

    The value of LIBRARY_PATH is a colon-separated list of directories, much like PATH . When configured as a native compiler, GCC tries the directories thus specified when searching for special linker files, if it can't find them using GCC_EXEC_PREFIX . Linking using GCC also uses these directories when searching for ordinary libraries for the -l option (but directories specified with -L come first).

    and the same appears verbatim in GCC online documentation: 3.21 Environment Variables Affecting GCC

    What clang does with LIBRARY_PATH conforms with the sentence I've emphasised.

    It's worth knowing that while using LIBRARY_PATH for this purpose may have got you out of a jam, it is not favoured in regular linkage practice, because it is likely to be an invisible hand when you are studying the output of a linkage: its effect is only revealed in verbose mode (by either gcc or clang). The installer's recommendation:

    export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
    

    is what you'd expect. I've no insight into why it didn't work for you.