The shared library HOWTO explains most of the mechanisms involved, and the dynamic loader manual goes into more detail. Each unix variant has its own way, but most use the same executable format (ELF) and have similar dynamic linkers (derived from Solaris). Below I'll summarize the common behavior with a focus on Linux; check your system's manuals for the complete story.
In a nutshell, when it's looking for a dynamic library (.so file) the linker tries:
- directories listed in the
LD_LIBRARY_PATH environment variable (DYLD_LIBRARY_PATH on OSX);
- directories listed in the executable's rpath;
- directories on the system search path, which (on Linux at least) consists of the entries in
/etc/ld.so.conf plus /lib and /usr/lib.
The rpath is stored in the executable (it's the DT_RPATH or DT_RUNPATH dynamic attribute). It can contain absolute paths or paths starting with $ORIGIN to indicate a path relative to the location of the executable (e.g. if the executable is in /opt/myapp/bin and its rpath is $ORIGIN/../lib:$ORIGIN/../plugins then the dynamic linker will look in /opt/myapp/lib and /opt/myapp/plugins). The rpath is normally determined when the executable is compiled, with the -rpath option to ld, but you can change it afterwards with chrpath.
In the scenario you describe, if you're the developer or packager of the application and intend for it to be installed in a …/bin, …/lib structure, then link with -rpath='$ORIGIN/../lib'. If you're installing a pre-built binary on your system, either put the library in a directory on the search path (/usr/local/lib if you're the system administrator, otherwise a directory that you add to $LD_LIBRARY_PATH), or try chrpath.