Finally, I have discovered it!
One thing I've always liked about Windows is that if you ship an executable and several DLLs, you just include the DLLs in the same directory as the EXE, and the loader finds them when the program is loaded. However, the Linux runtime loader does not search for SOs in the same way. The way most people work around this (i.e. Mozilla) is by shipping a shell script called 'mozilla' which sets up LD_LIBRARY_PATH -- enabling the loader to find the right SOs -- and then calling mozilla-bin. It works, but it's pretty hacky.
Well, today, I've found a much better way to do it... Apparently ld.so expands $ORIGIN in rpath and such to be the location of the executable. So here's how you specify an rpath relative to the executable:
gcc '-Wl,-rpath=$ORIGIN/bar' -o foo foo.c -Lbar -lbar
Now you can move foo and bar/libbar.so anywhere and they'll still run!
cool stuff!