Making portable Linux executables that Just Work everywhere (x86/64) is definitely possible. I've got games I compiled in 2005 that still runs fine on my up-to-date Linux box with a completely different distro (just tested
).
First, about libraries -- this really isn't any different than the situation in Windows, so I don't know why people make such a fuss about this part. You don't expect everyone to have SDL installed in Windows, so you ship the .dll with the game. Same with Linux. Actually, even with common stuff (like SDL is, on Linux), you should include the .so, since some versions are buggy and distros like to break stuff in a multitude of ways. Rule of thumb: If it's not GLIBC or X11, include it. (There's a few gotchas though, eg with, again, SDL: make sure you've got support for all the various audio APIs included. Dynamic run-time support, so that the support is optional, and not required.)
To make Linux automatically use your included libraries, you can set a special RPATH flag when linking the executable, with the library path relative to the location of the executable. Eg if the executable will end up in ".", and the libs are in a subdirectory "lib", you link with '-Wl,-rpath,$ORIGIN/lib'. (Remember to protect that $ from the shell, and make, if you use that. Check that you got it right with
objdump -x my-exe | grep RPATH, and ldd should also pick up the right lib). Or, if you don't like RPATH for some reason (it does actually have a bug if the executable's path has colons in it, though that's very uncommon and will hopefully get fixed soon), you can use a wrapper that sets LD_LIBRARY_PATH before launching the exectuable. It works like PATH, but for libraries.
To check direct dependencies so you know what to include, you can use objdump again: objdump -x my-exe | grep NEEDED
Note that that only lists direct load-time dependencies. dlopen'd libraries won't get listed since the linker doesn't know about them (I once got burned by this with SDL_mixer dlopening libvorbis), and dependencies of dependencies won't get listed either. (You get the latter with ldd.)
As for binary compatibility, once you dynamically link everything the biggest issue here is GLIBC (and this is not just for the executable, but goes for the libraries as well. This is where the nightmare starts). First, you should never ship glibc with your game because that's actually less compatible. However, glibc uses versioned symbols, so you can't just link to it the normal way when compiling, or your game will most likely require at least the version of glibc you've got on your dev box .. which isn't a problem if it's old, but it's probably not, and if you're like me you'd like people with somewhat older distros to be able to run your game too.
The obvious solution to this problem is to just link against an older glibc. This isn't always possible though, specially if you want to use more a more recent gcc.
Anyway, glibc still has all the older symbol versions included (or else older executables wouldn't work), so all you've got to do is to tell gcc to use these older symbol versions in stead of the most recent ones when compiling. This is unfortunately not entirely straightforward, but it's made a little bit easier with this script I made ages ago:
gensymoverride. First you use that to generate a header that defines which symbols that can be used and which can't (just run it for usage info). Then once you've got that, you force-include it with everything you compile, like this:
gcc -include generated-overrides.h ...
It's easiest to just make a wrapper script to do this for you. I've actually got a gentoo chroot where I made a gcc profile to do this automatically for gcc, g++, etc. Gentoo because it's already got a system to compile everything, and chroot because it means I don't have to worry about this kind of thing at all once it's set up. If it's compiled in there, it'll work. Setting it up is a bit of a pain though.
If your game is C++, you should probably include libstdc++.so with your game as well. An alternative (but only if you're not linking dynamically to any libraries that also use c++ dynamically) is to statically link libstdc++, which is smaller overall since you only get the parts you actually use. Linking that statically requires some trickery though:
g++ -Wl,--as-needed <your-stuff-here> `g++ -print-file-name=libstdc++.a`
(-Wl,--as-needed isn't technically required, but some things depend on libstdc++ despite not actually using anything from it. as-needed makes the executable only depend on actual, used libraries.)
libgcc_s.so.1 used to be a problem with c++ stuff btw, since it got introduced with gcc 3, and it can't be included with your game (doing that is actually guaranteed to prevent it from running at all). Gcc 3 is ancient by now though, so this probably shouldn't be a problem anymore.. If it still bothers you though, you can link it statically with by passing -static-libgcc when linking. This doesn't work if you link libstdc++ dynamically though, so you'll also have to link libstdc++ statically for this.