I link my program with -Wl,-rpath=./
but it failed to search the library. the debug shows run path only take affect on some library. log:
find library=libepsverifier.so [0]; searching
search path=./tls/x86_64/x86_64:./tls/x86_64:./tls/x86_64:./tls:./x86_64/x86_64:./x86_64:./x86_64:. (RUNPATH from file ./sim)
trying file=./tls/x86_64/x86_64/libepsverifier.so
trying file=./tls/x86_64/libepsverifier.so
trying file=./libepsverifier.so
find library=libc.so.6 [0]; searching
search path=./tls/x86_64/x86_64:./tls/x86_64:./tls/x86_64:./tls:./x86_64/x86_64:./x86_64:./x86_64:. (RUNPATH from file ./sim)
trying file=./tls/x86_64/x86_64/libc.so.6
...
trying file=./x86_64/libc.so.6
trying file=./libc.so.6
search cache=/etc/ld.so.cache
trying file=/lib/x86_64-linux-gnu/libc.so.6
find library=libepsbase.so [0]; searching
search cache=/etc/ld.so.cache
search path=/lib/x86_64-linux-gnu/tls/x86_64/x86_64:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/x86_64/x86_64:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/x86_64/x86_64:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/x86_64/x86_64:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/x86_64/x86_64:/lib/tls/x86_64:/lib/tls/x86_64:/lib/tls:/lib/x86_64/x86_64:/lib/x86_64:/lib/x86_64:/lib:/usr/lib/tls/x86_64/x86_64:/usr/lib/tls/x86_64:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/x86_64/x86_64:/usr/lib/x86_64:/usr/lib/x86_64:/usr/lib (system search path)
trying file=/lib/x86_64-linux-gnu/tls/x86_64/x86_64/libeps
。。。
trying file=/usr/lib/x86_64/libepsbase.so
trying file=/usr/lib/x86_64/libepsbase.so
trying file=/usr/lib/libepsbase.so
./sim: error while loading shared libraries: libepsbase.so: cannot open shared object file: No such file or directory
why libepsverifier is searched in .
but linker don't search libepsbase under .
I am not entirely sure on what you want to achieve, honestly I never tried rpath in the way you are trying to use it. Am I right if your expect the compiler to scan the entire file system for it? This is not what it is meant for.
Probably the other libraries are found because they are referenced in the LD_LIBRARY_PATH of your environment or because they are in locations recognized by your environment as "the place where libraries are".
The sequence should be
DT_RPATH section of the executable
LD_LIBRARY_PATH
DT_RUNPATH section of the executable
/etc/ld/so/cache
default directories /lib then /usr/lib
To have a proper test, either include the path of the library in the LD_LIBRARY_PATH or pass the rpath with a full path of the library. If it works, this is the reason why it fails.
You should use the same -Wl,-rpath=/some/path option when compiling a shared library if you want that path to be also used when searching the shared libraries that library itself depends upon.
Example:
$ echo 'void bar(){printf("bar\n");}' > bar.c
$ echo 'extern void bar(); void foo(){bar();}' > foo.c
$ echo 'extern void foo(); int main(){foo();}' > main.c
$ mkdir -p dir
$ cc bar.c -shared -o dir/libbar.so -include stdio.h
$ cc foo.c -Ldir -lbar -shared -o dir/libfoo.so
$ cc main.c -Ldir -lfoo -o main -Wl,-rpath=./dir
$ ./main
./main: error while loading shared libraries: libbar.so: cannot open shared object file: No such file or directory
$ cc foo.c -Ldir -lbar -shared -o dir/libfoo.so -Wl,-rpath=./dir
#^^^^^^^^^^^^^^^^
$ ./main
bar
Related
I am trying to generate LLVM bytecode file from a C source file (hello.c) using CMake. And below is my CMakeLists file.
###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-emit-llvm")
project (hello)
add_executable(hello hello.c)
I am new to CMake and not sure if this is the right way. I could not find any rules to make *.bc in the generated MakeFile
. Please correct me here. I also tried "-save-temps"
Considering this for a single .c file. It would be really helpful if you could give me some hints on generating the same for a complete C project.
I think what you ultimately want is to be able to build a C-program
project with CMake and clang in which source files are compiled to LLVM bitcode
and the executable is linked from the bitcode files.
With CMake, asking clang to to link bitcode files means asking it to link in LTO mode,
with the -flto linkage option.
And you can get clang to compile to LLVM bitcode with the -flto compilation
option, or with the -emit-llvm option.
For illustration here is a Hello World project comprising two source files and one header:
$ ls -R
.:
CMakeLists.txt hello.c hello.h main.c
Here is the:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project (hello)
set(CMAKE_C_COMPILER clang)
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello main.c hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)
It will work equally well with:
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)
Make a build directory for CMake and go there:
$ mkdir build
$ cd build
Generate the build system:
$ cmake ..
Build:
$ make
Scanning dependencies of target hello
[ 33%] Building C object CMakeFiles/hello.dir/main.c.o
[ 66%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello
You will not find any *.bc targets in the Makefiles, nor any *.bc files
generated:
$ egrep -r '.*\.bc'; echo Done
Done
$ find -name '*.bc'; echo Done
Done
because the compilation option -flto or -emit-llvm results in an output
file:
CMakeFiles/hello.dir/main.c.o
CMakeFiles/hello.dir/hello.c.o
that adheres to the usual CMake naming convention but is in fact not an object file
but an LLVM bitcode file, as you see:
$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o: LLVM IR bitcode
The program does the usual thing:
$ ./hello
Hello World!
Later
When I try " make hello.o " it should generate the object file right?
the cmd executes successfully but, could not find the generated object file. Am I doing it right?
You are doing it in one way that is right, though not the only way that is right, but
your expectations are wrong. Look again at:
$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o: LLVM IR bitcode
You can see there that the .o files that are made from hello.c and main.c
by the CMake-generated makefile are not called hello.o and main.o but hello.c.o
and main.c.o. CMake prefers a compiled filename to preserve the extension of the
source file, and append .o. That is a fairly common practice. So if you wanted
to use the makefile to compile hello.c, the most obviously right way would be
make hello.c.o.
Let's see what actually happens. In my CMake build directory:
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
make[1]: 'CMakeFiles/hello.dir/hello.c.o' is up to date.
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'
There was nothing to be done, because my hello.c.o was up to date. So I'll
delete it and repeat:
$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang -flto -o CMakeFiles/hello.dir/hello.c.o -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'
Now it has been recompiled.
However, because many people - like you - would expect hello.o to be compiled
from hello.c, CMake helpfully defines hello.o as a .PHONY target
that depends on hello.c.o:
$ egrep -A3 'hello.o.*:.*hello.c.o' Makefile
hello.o: hello.c.o
.PHONY : hello.o
So in fact I can do:
$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang -flto -o CMakeFiles/hello.dir/hello.c.o -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'
make hello.o is another way of making hello.c.o
The problem is that using the -emit-llvm flag does not produce a final binary and stops the configuration tests that CMake performs once that flag is used in them.
Apart from what's already been written about using the LTO infrastructure, you have 3 (or 2 and a half) other alternatives.
One is to use Whole-Program LLVM and use the commands provided to extract the relevant bitcode parts.
The other is to go the manual way of setting up custom targets (see add_custom_target and add_custom_command) on your CMake binary targets, that will get triggered on changes and will reproduce the desired outcome as if executed manually on the command line each time.
Now, on this last point, I had a similar need so I created a CMake project that provides that functionality (llvm-ir-cmake-utils), but allows you to hook up those custom targets on existing ones as you please and see fit without having to rewrite everything from scratch each time.
There are examples in the repo, but in short, it allows you to attach custom targets on already existing CMake targets, e.g.
[...]
add_executable(qux ${SOURCES})
[...]
# this will create a bitcode generating target
# and allow it to depend on the initial target in order to detect source code changes
llvmir_attach_bc_target(qux_bc qux)
add_dependencies(qux_bc qux)
[...]
After make,
$>file CMakeFiles/hello.dir/hello.c.o
CMakeFiles/hello.dir/hello.c.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
if
set(CMAKE_C_FLAGS "-emit-llvm")
written before
project (hello)
In order to obtain IR bitcode, I wrote:
###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
project (hello)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-flto")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
I worked several hours in order to have a Makefile working to compile from IR
code to native using lld, then with cmake it was much more faster.
Then reading at cmake generated Makefile, I was able to correct my Makefile:
clang -flto -flto <hello.c.o> ..
this worked but I do not know why -flto is written twice.
Thanl you very much for this post, showing clang as the centralized front end to various llvm provided commands.
How to set executables search shared library in its same directory in compile,
I've put the executable file and related .so libraries in the same directory, instead of changubg LD_LIBRARY_PATH or /etc/ld.so.conf,
Is it possible to set -rpath as a variable which is the directory where keeps the executalbe file, and the variable is not a certain path , it's changable.
Is it possible to set -rpath as a variable
Yes: you want DT_RPATH to be set to $ORIGIN, like so:
gcc main.c -Wl,-rpath='$ORIGIN' -lfoo -lbar
Note: you need to single-qoute the $ORIGIN to prevent shell from expanding it -- the RPATH must contain the $ sign.
Another option is to link like so:
gcc main.c ./libfoo.so ./libbar.so
This will only work if libfoo.so and libbar.so do not have DT_SONAME set, and generally is an inferior solution. I am only mentioning it here for completeness.
I am trying to build a simple executable that uses boost_serialization and boost_iostreams.
#include <fstream>
#include <iostream>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/device/file.hpp>
int main()
{
using namespace boost::iostreams;
filtering_ostream os;
os.push(boost::iostreams::gzip_compressor());
os.push(boost::iostreams::file_sink("emptyGzipBug.txt.gz"));
}
Unfortunately the system I am working with has a very outdated version of boost_serialization in /usr/lib/, and I have no way to change that.
I am fairly certain when I build the example using
g++ -o main main.cpp -lboost_serialization -lboost_iostreams
that the linker errors result because gcc uses the system version of boost_serialization rather than my locally installed version. Setting LIBRARY_PATH and LD_LIBRARY_PATH to /home/andrew/install/lib doesnt work. When i build using
g++ -o main main.cpp -L/home/andrew/install/lib -lboost_serialization -lboost_iostreams
then everything works.
My questions are:
How can I get gcc to tell me the filenames of the libraries its using?
Is it possible to setup the environment so that I dont have to specify the absolute path to my local boost on the command line of gcc.
PS After typing the below info, I thought I'd be kind and add what you need for your specific case:
g++ -Wl,-rpath,/home/andrew/install/lib -o main main.cpp -I/home/andrew/install/include -L/home/andrew/install/lib -lboost_serialization -lboost_iostreams
gcc itself doesn't care about the libraries. The linker does ;).
Even though the linker needs to find the shared libraries so it can resolve
symbols, it doesn't store the path of those libraries in the executable normally.
So, for a start, lets find out what is actually in the binary after you linked it:
$ readelf -d main | grep 'libboost'
0x0000000000000001 (NEEDED) Shared library: [libboost_serialization.so.1.54.0]
0x0000000000000001 (NEEDED) Shared library: [libboost_iostreams.so.1.54.0]
Just the names thus.
The libraries that are actually used are detemined by /lib/ld-linux.so.*
at run time:
$ ldd main | grep libboost
libboost_serialization.so.1.54.0 => /usr/lib/x86_64-linux-gnu/libboost_serialization.so.1.54.0 (0x00007fd8fa920000)
libboost_iostreams.so.1.54.0 => /usr/lib/x86_64-linux-gnu/libboost_iostreams.so.1.54.0 (0x00007fd8fa700000)
The path is found by looking in /etc/ld.so.cache (which is normally
compiled by running ldconfig). You can print its contents with:
ldconfig -p | grep libboost_iostreams
libboost_iostreams.so.1.54.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libboost_iostreams.so.1.54.0
libboost_iostreams.so.1.49.0 (libc6,x86-64) => /usr/lib/libboost_iostreams.so.1.49.0
libboost_iostreams.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libboost_iostreams.so
but since that is only the cached result of a previous look up,
you are more interested in the output of:
$ ldconfig -v 2>/dev/null | egrep '^[^[:space:]]|libboost_iostreams'
/lib/i386-linux-gnu:
/usr/lib/i386-linux-gnu:
/usr/local/lib:
/lib/x86_64-linux-gnu:
/usr/lib/x86_64-linux-gnu:
libboost_iostreams.so.1.54.0 -> libboost_iostreams.so.1.54.0
/lib32:
/usr/lib32:
/lib:
/usr/lib:
libboost_iostreams.so.1.49.0 -> libboost_iostreams.so.1.49.0
which shows the paths that it looked in before finding a result.
Note if you are linking a 64bit program and it would find a 32bit
library first (or visa versa) then that would be skipped as being
incompatible. Otherwise, the first one found is used.
The paths used to search are specified in /etc/ld.so.conf which is
read (usually at boot time, or after installing something new)
when running ldconfig as root.
However, precedence take paths specified as a colon separated list
of paths in the environment variable LD_LIBRARY_PATH.
For example, if I'd do:
$ export LD_LIBRARY_PATH=/tmp
$ cp /usr/lib/libboost_iostreams.so.1.49.0 /tmp/libboost_iostreams.so.1.54.0
$ ldd main | grep libboost_iostreams
libboost_iostreams.so.1.54.0 => /tmp/libboost_iostreams.so.1.54.0 (0x00007f621add8000)
then it finds 'libboost_iostreams.so.1.54.0' in /tmp (even though it was a libboost_iostreams.so.1.49.0).
Note that you CAN hardcode a path in your executable by passing -rpath to
the linker:
$ unset LD_LIBRARY_PATH
$ g++ -Wl,-rpath,/tmp -o main main.cpp -lboost_serialization -lboost_iostreams
$ ldd main | grep libboost_iostreams
libboost_iostreams.so.1.54.0 => /tmp/libboost_iostreams.so.1.54.0 (0x00007fbd8bcd8000)
which can be made visible with
$ readelf -d main | grep RPATH
0x000000000000000f (RPATH) Library rpath: [/tmp]
Note that LD_LIBRARY_PATH even takes precedence over -rpath, unless
you also passed -Wl,--disable-new-dtags, along with the -rpath and provided that you are linking an executable and your linker supports
this flag.
You can show the search paths that gcc uses during compile(link) time with the -print-search-dirs command line option:
$ g++ -print-search-dirs | grep libraries
libraries: =/usr/lib/gcc/x86_64-linux-gnu/4.7/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/4.7/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/4.7/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib/:/lib/x86_64-linux-gnu/4.7/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/4.7/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/lib/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../:/lib/:/usr/lib/
This can be influenced by adding -L command line options. If a library can't be found in a path specified with the -L option then it looks in paths found through the environment variable GCC_EXEC_PREFIX (see the man page for that) and if that fails it uses the environment variable LIBRARY_PATH.
When you run g++ with the -v option, it will print the LIBRARY_PATH used.
LIBRARY_PATH=/tmp/lib g++ -v -o main main.cpp -lboost_serialization -lboost_iostreams 2>&1 | grep LIBRARY_PATH
LIBRARY_PATH=/tmp/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.7/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/tmp/lib/:/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../:/lib/:/usr/lib/
Finally, note that especially for boost (but in general) you should
use header files that match the correct version! So, if the library that you
link with at run time is version xyz you should have used an -I command line option to get g++ to find the corresponding header files, or things might not link or worse, result in unexplainable crashes.
-nodefaultlibs
Do not use the standard system libraries when linking. Only the
libraries you specify are passed to the linker, and options
specifying linkage of the system libraries, such as
-static-libgcc or -shared-libgcc, are ignored. The standard
startup files are used normally, unless -nostartfiles is used.
The compiler may generate calls to "memcmp", "memset", "memcpy"
and "memmove". These entries are usually resolved by entries in
libc. These entry points should be supplied through some other
mechanism when this option is specified.
Haven't used it myself but it sounds exactly like what was asked for.
I'm a dumb newbie.
I've got a file named file.c with the functions my_putstr(char *) and my_strlen(char *)
my_putstr() writes the parameter with write() (unistd.h)
I wanted to create a library from file.c so I did :
gcc -fPIC -c file.c
gcc -shared -o libfile.so file.o
Then I created a main.c file and called my_putstr() from it.
I tried to compile and link my .so
gcc -L. -lfile main.c -o test
But I got an undefined reference to my_putstr()
I tried to create a .h with my_putstr() and my_strlen() in it, and include it to the main but I got the same error.
Sorry for stupid questions.
Havaniceday.
Your question suffers lack of information, but I can suggest you at first try
gcc main.c ./libfile.so -Wl,-rpath . -o test
If this will fail, you have something wrong with your sources.
If everything is ok at this point, then try
gcc main.c -L . -lfile -Wl,-rpath . -o test
If this will output undefined reference, then probably you already have something like libfile.a without my_putstr(may be from previous experiments) in your lib path.
If everything is ok with it, then your linker is sensible to order in which libraries is supplied to command string, and you must remember, then library always comes after object, that uses this library.
Note: Full working example now below. Original question follows:
I'm having problems using ld's -rpath parameter with $ORIGIN.
As I couldn't find a complete example, I thought I'd try to write one myself, so that I and others can use it later. Once I get it working I'll tidy it up.
I asked about this before, but I think my post was a bit confusing.
The example project builds one shared library and one executable that links to said library.
It's very small (3 files, 22 lines incl buildscript).
You can download the project from here
File structure (before building):
project/
src/
foo.cpp
main.cpp
make.sh
project/src/foo.cpp
int foo()
{ return 3; }
project/src/main.cpp
int foo();
#include <iostream>
int main()
{
std::cout << foo() << std::endl;
return 0;
}
project/make.sh
# Make directories:
mkdir -p -v obj
mkdir -p -v lib
mkdir -p -v run
# Build the library:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/foo.sh obj/foo.o
# Build the executable:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../../lib' -Llib -l:foo.sh
From the project directory, run make.sh (make sure it's executable).
File structure (after building):
project/
src/
foo.cpp
main.cpp
obj/
foo.o
main.o
lib/
foo.so
run/
main.run
make.sh
run/main.run should now load lib/foo.sh on execution, from anywhere.
Problems
Currently, this only partly works.
The files compile and link OK, but it fails to link when run from any directory except project (which is the point of the exercise).
Inspecting main.run with readelf -d shows:
0x0000000000000001 (NEEDED) Shared library: [lib/foo.sh]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../../lib]
Which looks close (I'd rather have [foo.sh] than [lib/foo.sh] but I'll fix that later).
AFAICT the $ORIGIN in -Wl,-rpath,'$ORIGIN/../../lib' means project/run/main.run so this rpath should become project/lib.
I have tried $ORIGIN/.., $ORIGIN/../lib, $ORIGIN/../.., $ORIGIN/../../lib to no avail.
Note: I'm using -l: which requires the complete library filename (amongst other reasons, it's easier to script with variables when all functions take the same name format).
Does anyone know why this isn't working?
Or alternately, does anyone have or know of a complete working example?
(I'd rather have [foo.sh] than [lib/foo.sh] but I'll fix that later).
There's most of your problem: the / in the name stops the dynamic linker from doing the rpath magic.
(Your rpath is wrong too. Think about it: from the shell, if you were currently in the directory where your executable is, how would you get to the directory where your library is? Here, you'd need to cd ../lib. So your rpath should be $ORIGIN/../lib.)
If you built your object as libfoo.so and linked with -Llib -lfoo, the linker would work out what you were intending, and do the right thing. But if you're going to use unusual naming conventions, you'll have to help it out:
Change the link line for the library to explicitly set the SONAME for your library to just foo.sh:
g++ -shared -Wl,-soname,foo.sh -o lib/foo.sh obj/foo.o
Fix the rpath:
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib' -Llib -l:foo.sh
It's useful to run ldd main/main.run to see what's going on. In your original failing case, you'll see something like:
lib/foo.sh (0xNNNNNNNN)
(the lack of any => /some/resolved/path showing that it's not done any path resolution). In the fixed case, you'll see something like:
foo.sh => /your/path/to/run/../lib/foo.sh (0xNNNNNNNN)
This is a example of relative-path linking (with ld) by using $ORIGIN in an rpath.
rpath is a path (or set of paths) embedded in binary files (shared libraries (.so) and executables).
These paths are the foremost search paths for the shared libraries the binary must be linked with at runtime.
$ORIGIN is a potential start directory for an rpath path.
It resolves to the directory containing the executing file. (eg: $ORIGIN/lib)
The example project builds one shared library and one executable that links to said library using rpath and $ORIGIN.
You can download the project from here.
File structure (before building):
project/
src/
foo.cpp
main.cpp
make.sh
project/src/foo.cpp
int foo()
{ return 3; }
project/src/main.cpp
int foo();
#include <iostream>
int main()
{
std::cout << foo() << std::endl;
return 0;
}
project/make.sh
# Make directories:
mkdir -p -v obj
mkdir -p -v lib/dir
mkdir -p -v run
# Build the library:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so -Wl,-soname,foo.so obj/foo.o
# Build the executable:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Llib/dir -l:foo.so
From the project directory, run make.sh (if it won't run, ensure make.sh has execute permissions).
If all went OK, main.run should now load lib/dir/foo.so on execution, regardless of the absolute path to project (you can move it to anywhere), and regardless of the current working directory (you can run it from anywhere).
Notes:
-fPIC instructs the compiler to build relocatable object files (object files build into shared libraries must be relocatable).
-Wl,-soname,<NAME> embeds <NAME> into the generated library. This should match the name you supply for the -l or -l: options when linking to this library.
-Wl,-rpath,'<PATH>' embeds <PATH> into the generated library as a runtime library search path (or rpath - see above).
-L adds a path to the build-time library search path list. (Note: rpath is irrelevant at build-time, -L is irrelevant at runtime).
-l: adds the filename (without path) of a library to link against. (Similar to -l, except -l: requires the complete filename.
File structure (after building):
project/
src/
foo.cpp
main.cpp
obj/
foo.o
main.o
lib/
dir/
foo.so
run/
main.run
make.sh
Note: I'm using -l: which requires the complete library filename (amongst other reasons, it's easier to script with variables when all functions take the same name format).
It is more common to use -l, whereby -l<NAME> denotes lib.so.
Limitations
As far as I'm aware (correct me if I'm wrong) there's no way to add a library inside a subdirectory within a search path (except for adding that directory as a sub-path). This is true for both build-time (-L) and run-time (-rpath) search paths.
So if you have two libraries with the same name but different locations, you won't be able to link them both. (I hope I'm wrong or that this gets fixed).